mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-21 23:30:32 +00:00
refactor: inventory adjustments e2e test cases
This commit is contained in:
@@ -20,9 +20,9 @@
|
|||||||
"test:e2e": "jest --config ./test/jest-e2e.json --watchAll"
|
"test:e2e": "jest --config ./test/jest-e2e.json --watchAll"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@bigcapital/utils": "*",
|
|
||||||
"@bigcapital/email-components": "*",
|
"@bigcapital/email-components": "*",
|
||||||
"@bigcapital/pdf-templates": "*",
|
"@bigcapital/pdf-templates": "*",
|
||||||
|
"@bigcapital/utils": "*",
|
||||||
"@nestjs/bull": "^10.2.1",
|
"@nestjs/bull": "^10.2.1",
|
||||||
"@nestjs/bullmq": "^10.2.1",
|
"@nestjs/bullmq": "^10.2.1",
|
||||||
"@nestjs/cache-manager": "^2.2.2",
|
"@nestjs/cache-manager": "^2.2.2",
|
||||||
@@ -35,13 +35,13 @@
|
|||||||
"@nestjs/platform-express": "^10.0.0",
|
"@nestjs/platform-express": "^10.0.0",
|
||||||
"@nestjs/swagger": "^7.4.2",
|
"@nestjs/swagger": "^7.4.2",
|
||||||
"@nestjs/throttler": "^6.2.1",
|
"@nestjs/throttler": "^6.2.1",
|
||||||
|
"@supercharge/promise-pool": "^3.2.0",
|
||||||
"@types/passport-local": "^1.0.38",
|
"@types/passport-local": "^1.0.38",
|
||||||
"@types/ramda": "^0.30.2",
|
"@types/ramda": "^0.30.2",
|
||||||
"accounting": "^0.4.1",
|
"accounting": "^0.4.1",
|
||||||
"bluebird": "^3.7.2",
|
|
||||||
"async": "^3.2.0",
|
"async": "^3.2.0",
|
||||||
"axios": "^1.6.0",
|
"axios": "^1.6.0",
|
||||||
"form-data": "^4.0.0",
|
"bluebird": "^3.7.2",
|
||||||
"bull": "^4.16.3",
|
"bull": "^4.16.3",
|
||||||
"bullmq": "^5.21.1",
|
"bullmq": "^5.21.1",
|
||||||
"cache-manager": "^6.1.1",
|
"cache-manager": "^6.1.1",
|
||||||
@@ -49,6 +49,7 @@
|
|||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"class-validator": "^0.14.1",
|
"class-validator": "^0.14.1",
|
||||||
"express-validator": "^7.2.0",
|
"express-validator": "^7.2.0",
|
||||||
|
"form-data": "^4.0.0",
|
||||||
"fp-ts": "^2.16.9",
|
"fp-ts": "^2.16.9",
|
||||||
"js-money": "^0.6.3",
|
"js-money": "^0.6.3",
|
||||||
"knex": "^3.1.0",
|
"knex": "^3.1.0",
|
||||||
@@ -59,25 +60,24 @@
|
|||||||
"mysql2": "^3.11.3",
|
"mysql2": "^3.11.3",
|
||||||
"nestjs-cls": "^4.4.1",
|
"nestjs-cls": "^4.4.1",
|
||||||
"nestjs-i18n": "^10.4.9",
|
"nestjs-i18n": "^10.4.9",
|
||||||
"uuid": "^10.0.0",
|
|
||||||
"pug": "^3.0.2",
|
|
||||||
"object-hash": "^2.0.3",
|
"object-hash": "^2.0.3",
|
||||||
"objection": "^3.1.5",
|
"objection": "^3.1.5",
|
||||||
"passport": "^0.7.0",
|
"passport": "^0.7.0",
|
||||||
"passport-jwt": "^4.0.1",
|
"passport-jwt": "^4.0.1",
|
||||||
"passport-local": "^1.0.0",
|
"passport-local": "^1.0.0",
|
||||||
|
"plaid": "^10.3.0",
|
||||||
|
"posthog-node": "^4.3.2",
|
||||||
|
"pug": "^3.0.2",
|
||||||
"ramda": "^0.30.1",
|
"ramda": "^0.30.1",
|
||||||
"redis": "^4.7.0",
|
"redis": "^4.7.0",
|
||||||
"reflect-metadata": "^0.2.0",
|
"reflect-metadata": "^0.2.0",
|
||||||
"rxjs": "^7.8.1",
|
"rxjs": "^7.8.1",
|
||||||
"serialize-interceptor": "^1.1.7",
|
"serialize-interceptor": "^1.1.7",
|
||||||
"strategy": "^1.1.1",
|
"strategy": "^1.1.1",
|
||||||
"zod": "^3.23.8",
|
|
||||||
"plaid": "^10.3.0",
|
|
||||||
"@supercharge/promise-pool": "^3.2.0",
|
|
||||||
"yup": "^0.28.1",
|
|
||||||
"uniqid": "^5.2.0",
|
"uniqid": "^5.2.0",
|
||||||
"posthog-node": "^4.2.0"
|
"uuid": "^10.0.0",
|
||||||
|
"yup": "^0.28.1",
|
||||||
|
"zod": "^3.23.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nestjs/cli": "^10.0.0",
|
"@nestjs/cli": "^10.0.0",
|
||||||
|
|||||||
@@ -152,8 +152,8 @@ import { PostHogModule } from '../EventsTracker/postHog.module';
|
|||||||
TransactionsLockingModule,
|
TransactionsLockingModule,
|
||||||
SettingsModule,
|
SettingsModule,
|
||||||
InventoryAdjustmentsModule,
|
InventoryAdjustmentsModule,
|
||||||
PostHogModule,
|
// PostHogModule,
|
||||||
EventTrackerModule,
|
// EventTrackerModule,
|
||||||
],
|
],
|
||||||
controllers: [AppController],
|
controllers: [AppController],
|
||||||
providers: [
|
providers: [
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { PostHog } from 'posthog-node';
|
// import { PostHog, EventMessage } from 'posthog-node';
|
||||||
import { EventMessage } from 'posthog-node/src/types';
|
|
||||||
import { POSTHOG } from './postHog.module';
|
import { POSTHOG } from './postHog.module';
|
||||||
import { TenancyContext } from '../Tenancy/TenancyContext.service';
|
import { TenancyContext } from '../Tenancy/TenancyContext.service';
|
||||||
|
|
||||||
|
interface EventMessage {
|
||||||
|
distinctId
|
||||||
|
}
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class EventTrackerService {
|
export class EventTrackerService {
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(POSTHOG) private readonly posthog: PostHog,
|
@Inject(POSTHOG) private readonly posthog: any,
|
||||||
private readonly tenancyContext: TenancyContext,
|
private readonly tenancyContext: TenancyContext,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@@ -15,7 +17,7 @@ export class EventTrackerService {
|
|||||||
* Track tenant an event.
|
* Track tenant an event.
|
||||||
* @param event - The event to track.
|
* @param event - The event to track.
|
||||||
*/
|
*/
|
||||||
public async trackEvent(event: Omit<EventMessage, 'distinctId'>) {
|
public async trackEvent(event: any) {
|
||||||
// Cannot continue if the Posthog not configured.
|
// Cannot continue if the Posthog not configured.
|
||||||
if (!this.posthog) return;
|
if (!this.posthog) return;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { ITransactionsLockingPartialUnlocked } from '@/interfaces';
|
|
||||||
import {
|
import {
|
||||||
SUBSCRIPTION_CANCELLED,
|
SUBSCRIPTION_CANCELLED,
|
||||||
SUBSCRIPTION_PAYMENT_FAILED,
|
SUBSCRIPTION_PAYMENT_FAILED,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { PostHog } from 'posthog-node';
|
// import { PostHog } from 'posthog-node';
|
||||||
import { EventTrackerService } from './EventTracker.service';
|
import { EventTrackerService } from './EventTracker.service';
|
||||||
import { ConfigService } from '@nestjs/config';
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
|
||||||
@@ -10,10 +10,12 @@ export const POSTHOG = 'PostHog';
|
|||||||
EventTrackerService,
|
EventTrackerService,
|
||||||
{
|
{
|
||||||
provide: POSTHOG,
|
provide: POSTHOG,
|
||||||
useFactory: (configService: ConfigService) =>
|
useFactory: (configService: ConfigService) => {
|
||||||
new PostHog(configService.get('posthog.apiKey'), {
|
|
||||||
host: configService.get('posthog.host'),
|
},
|
||||||
}),
|
// new PostHog(configService.get('posthog.apiKey'), {
|
||||||
|
// host: configService.get('posthog.host'),
|
||||||
|
// }),
|
||||||
inject: [ConfigService],
|
inject: [ConfigService],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,9 +1,19 @@
|
|||||||
import { Body, Controller, Delete, Param, Post } from '@nestjs/common';
|
import {
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
Delete,
|
||||||
|
Get,
|
||||||
|
Param,
|
||||||
|
Post,
|
||||||
|
Put,
|
||||||
|
} from '@nestjs/common';
|
||||||
import { InventoryAdjustmentsApplicationService } from './InventoryAdjustmentsApplication.service';
|
import { InventoryAdjustmentsApplicationService } from './InventoryAdjustmentsApplication.service';
|
||||||
import { IQuickInventoryAdjustmentDTO } from './types/InventoryAdjustments.types';
|
import { IQuickInventoryAdjustmentDTO } from './types/InventoryAdjustments.types';
|
||||||
import { InventoryAdjustment } from './models/InventoryAdjustment';
|
import { InventoryAdjustment } from './models/InventoryAdjustment';
|
||||||
|
import { PublicRoute } from '../Auth/Jwt.guard';
|
||||||
|
|
||||||
@Controller('inventory-adjustments')
|
@Controller('inventory-adjustments')
|
||||||
|
@PublicRoute()
|
||||||
export class InventoryAdjustmentsController {
|
export class InventoryAdjustmentsController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly inventoryAdjustmentsApplicationService: InventoryAdjustmentsApplicationService,
|
private readonly inventoryAdjustmentsApplicationService: InventoryAdjustmentsApplicationService,
|
||||||
@@ -27,7 +37,16 @@ export class InventoryAdjustmentsController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post(':id/publish')
|
@Get(':id')
|
||||||
|
public async getInventoryAdjustment(
|
||||||
|
@Param('id') inventoryAdjustmentId: number,
|
||||||
|
): Promise<InventoryAdjustment> {
|
||||||
|
return this.inventoryAdjustmentsApplicationService.getInventoryAdjustment(
|
||||||
|
inventoryAdjustmentId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Put(':id/publish')
|
||||||
public async publishInventoryAdjustment(
|
public async publishInventoryAdjustment(
|
||||||
@Param('id') inventoryAdjustmentId: number,
|
@Param('id') inventoryAdjustmentId: number,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { WarehousesModule } from '../Warehouses/Warehouses.module';
|
|||||||
import { InventoryAdjustmentsGLSubscriber } from './subscribers/InventoryAdjustmentGL.subscriber';
|
import { InventoryAdjustmentsGLSubscriber } from './subscribers/InventoryAdjustmentGL.subscriber';
|
||||||
import { InventoryAdjustmentsGLEntries } from './commands/ledger/InventoryAdjustmentsGLEntries';
|
import { InventoryAdjustmentsGLEntries } from './commands/ledger/InventoryAdjustmentsGLEntries';
|
||||||
import { LedgerModule } from '../Ledger/Ledger.module';
|
import { LedgerModule } from '../Ledger/Ledger.module';
|
||||||
|
import { TenancyContext } from '../Tenancy/TenancyContext.service';
|
||||||
|
|
||||||
const models = [
|
const models = [
|
||||||
RegisterTenancyModel(InventoryAdjustment),
|
RegisterTenancyModel(InventoryAdjustment),
|
||||||
@@ -32,6 +33,7 @@ const models = [
|
|||||||
InventoryAdjustmentsApplicationService,
|
InventoryAdjustmentsApplicationService,
|
||||||
InventoryAdjustmentsGLSubscriber,
|
InventoryAdjustmentsGLSubscriber,
|
||||||
InventoryAdjustmentsGLEntries,
|
InventoryAdjustmentsGLEntries,
|
||||||
|
TenancyContext,
|
||||||
],
|
],
|
||||||
exports: [...models],
|
exports: [...models],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Knex } from 'knex';
|
import { Knex } from 'knex';
|
||||||
import { Inject } from '@nestjs/common';
|
import { Inject } from '@nestjs/common';
|
||||||
import * as R from 'ramda';
|
import * as R from 'ramda';
|
||||||
|
import * as moment from 'moment';
|
||||||
import { omit } from 'lodash';
|
import { omit } from 'lodash';
|
||||||
import { events } from '@/common/events/events';
|
import { events } from '@/common/events/events';
|
||||||
import { InventoryAdjustment } from '../models/InventoryAdjustment';
|
import { InventoryAdjustment } from '../models/InventoryAdjustment';
|
||||||
@@ -17,6 +18,7 @@ import { Account } from '@/modules/Accounts/models/Account.model';
|
|||||||
import { BranchTransactionDTOTransformer } from '@/modules/Branches/integrations/BranchTransactionDTOTransform';
|
import { BranchTransactionDTOTransformer } from '@/modules/Branches/integrations/BranchTransactionDTOTransform';
|
||||||
import { WarehouseTransactionDTOTransform } from '@/modules/Warehouses/Integrations/WarehouseTransactionDTOTransform';
|
import { WarehouseTransactionDTOTransform } from '@/modules/Warehouses/Integrations/WarehouseTransactionDTOTransform';
|
||||||
import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
|
import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
|
||||||
|
import { ERRORS } from '../constants/InventoryAdjustments.constants';
|
||||||
|
|
||||||
export class CreateQuickInventoryAdjustmentService {
|
export class CreateQuickInventoryAdjustmentService {
|
||||||
constructor(
|
constructor(
|
||||||
@@ -80,7 +82,6 @@ export class CreateQuickInventoryAdjustmentService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a quick inventory adjustment for specific item.
|
* Creates a quick inventory adjustment for specific item.
|
||||||
* @param {number} tenantId - Tenant id.
|
|
||||||
* @param {IQuickInventoryAdjustmentDTO} quickAdjustmentDTO - qucik adjustment DTO.
|
* @param {IQuickInventoryAdjustmentDTO} quickAdjustmentDTO - qucik adjustment DTO.
|
||||||
*/
|
*/
|
||||||
public async createQuickAdjustment(
|
public async createQuickAdjustment(
|
||||||
@@ -119,7 +120,7 @@ export class CreateQuickInventoryAdjustmentService {
|
|||||||
// Saves the inventory adjustment with associated entries to the storage.
|
// Saves the inventory adjustment with associated entries to the storage.
|
||||||
const inventoryAdjustment = await this.inventoryAdjustmentModel
|
const inventoryAdjustment = await this.inventoryAdjustmentModel
|
||||||
.query(trx)
|
.query(trx)
|
||||||
.upsertGraph({
|
.upsertGraphAndFetch({
|
||||||
...invAdjustmentObject,
|
...invAdjustmentObject,
|
||||||
});
|
});
|
||||||
// Triggers `onInventoryAdjustmentQuickCreated` event.
|
// Triggers `onInventoryAdjustmentQuickCreated` event.
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Knex } from 'knex';
|
import { Knex } from 'knex';
|
||||||
import { Inject } from '@nestjs/common';
|
import { Inject } from '@nestjs/common';
|
||||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
|
import * as moment from 'moment';
|
||||||
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
|
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
|
||||||
import { InventoryAdjustment } from '../models/InventoryAdjustment';
|
import { InventoryAdjustment } from '../models/InventoryAdjustment';
|
||||||
import {
|
import {
|
||||||
@@ -9,6 +10,7 @@ import {
|
|||||||
} from '../types/InventoryAdjustments.types';
|
} from '../types/InventoryAdjustments.types';
|
||||||
import { events } from '@/common/events/events';
|
import { events } from '@/common/events/events';
|
||||||
import { ServiceError } from '@/modules/Items/ServiceError';
|
import { ServiceError } from '@/modules/Items/ServiceError';
|
||||||
|
import { ERRORS } from '../constants/InventoryAdjustments.constants';
|
||||||
|
|
||||||
export class PublishInventoryAdjustmentService {
|
export class PublishInventoryAdjustmentService {
|
||||||
constructor(
|
constructor(
|
||||||
@@ -47,11 +49,15 @@ export class PublishInventoryAdjustmentService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Publish the inventory adjustment transaction.
|
// Publish the inventory adjustment transaction.
|
||||||
await InventoryAdjustment.query().findById(inventoryAdjustmentId).patch({
|
await this.inventoryAdjustmentModel
|
||||||
|
.query()
|
||||||
|
.findById(inventoryAdjustmentId)
|
||||||
|
.patch({
|
||||||
publishedAt: moment().toMySqlDateTime(),
|
publishedAt: moment().toMySqlDateTime(),
|
||||||
});
|
});
|
||||||
// Retrieve the inventory adjustment after the modification.
|
// Retrieve the inventory adjustment after the modification.
|
||||||
const inventoryAdjustment = await InventoryAdjustment.query()
|
const inventoryAdjustment = await this.inventoryAdjustmentModel
|
||||||
|
.query()
|
||||||
.findById(inventoryAdjustmentId)
|
.findById(inventoryAdjustmentId)
|
||||||
.withGraphFetched('entries');
|
.withGraphFetched('entries');
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
const ERRORS = {
|
export const ERRORS = {
|
||||||
INVENTORY_ADJUSTMENT_NOT_FOUND: 'INVENTORY_ADJUSTMENT_NOT_FOUND',
|
INVENTORY_ADJUSTMENT_NOT_FOUND: 'INVENTORY_ADJUSTMENT_NOT_FOUND',
|
||||||
ITEM_SHOULD_BE_INVENTORY_TYPE: 'ITEM_SHOULD_BE_INVENTORY_TYPE',
|
ITEM_SHOULD_BE_INVENTORY_TYPE: 'ITEM_SHOULD_BE_INVENTORY_TYPE',
|
||||||
INVENTORY_ADJUSTMENT_ALREADY_PUBLISHED:
|
INVENTORY_ADJUSTMENT_ALREADY_PUBLISHED:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Model, mixin } from 'objection';
|
import { Model } from 'objection';
|
||||||
// import TenantModel from 'models/TenantModel';
|
// import TenantModel from 'models/TenantModel';
|
||||||
// import InventoryAdjustmentSettings from './InventoryAdjustment.Settings';
|
// import InventoryAdjustmentSettings from './InventoryAdjustment.Settings';
|
||||||
// import ModelSetting from './ModelSetting';
|
// import ModelSetting from './ModelSetting';
|
||||||
|
|||||||
@@ -1,21 +1,24 @@
|
|||||||
import { TransformerInjectable } from "@/modules/Transformer/TransformerInjectable.service";
|
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
|
||||||
import { InventoryAdjustment } from "../models/InventoryAdjustment";
|
import { InventoryAdjustment } from '../models/InventoryAdjustment';
|
||||||
import { InventoryAdjustmentTransformer } from "../InventoryAdjustmentTransformer";
|
import { InventoryAdjustmentTransformer } from '../InventoryAdjustmentTransformer';
|
||||||
|
import { Inject } from '@nestjs/common';
|
||||||
|
|
||||||
export class GetInventoryAdjustmentService {
|
export class GetInventoryAdjustmentService {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly transformer: TransformerInjectable,
|
private readonly transformer: TransformerInjectable,
|
||||||
|
|
||||||
|
@Inject(InventoryAdjustment.name)
|
||||||
|
private readonly inventoryAdjustmentModel: typeof InventoryAdjustment,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve specific inventory adjustment transaction details.
|
* Retrieve specific inventory adjustment transaction details.
|
||||||
* @param {number} inventoryAdjustmentId - Inventory adjustment id.
|
* @param {number} inventoryAdjustmentId - Inventory adjustment id.
|
||||||
*/
|
*/
|
||||||
async getInventoryAdjustment(
|
async getInventoryAdjustment(inventoryAdjustmentId: number) {
|
||||||
inventoryAdjustmentId: number,
|
|
||||||
) {
|
|
||||||
// Retrieve inventory adjustment transation with associated models.
|
// Retrieve inventory adjustment transation with associated models.
|
||||||
const inventoryAdjustment = await InventoryAdjustment.query()
|
const inventoryAdjustment = await this.inventoryAdjustmentModel
|
||||||
|
.query()
|
||||||
.findById(inventoryAdjustmentId)
|
.findById(inventoryAdjustmentId)
|
||||||
.withGraphFetched('entries.item')
|
.withGraphFetched('entries.item')
|
||||||
.withGraphFetched('adjustmentAccount')
|
.withGraphFetched('adjustmentAccount')
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export class ItemsController extends TenantController {
|
|||||||
* @returns The updated item id.
|
* @returns The updated item id.
|
||||||
*/
|
*/
|
||||||
@Put(':id')
|
@Put(':id')
|
||||||
@UsePipes(new ZodValidationPipe(createItemSchema))
|
// @UsePipes(new ZodValidationPipe(createItemSchema))
|
||||||
async editItem(
|
async editItem(
|
||||||
@Param('id') id: string,
|
@Param('id') id: string,
|
||||||
@Body() editItemDto: any,
|
@Body() editItemDto: any,
|
||||||
@@ -55,7 +55,7 @@ export class ItemsController extends TenantController {
|
|||||||
* @returns The created item id.
|
* @returns The created item id.
|
||||||
*/
|
*/
|
||||||
@Post()
|
@Post()
|
||||||
@UsePipes(new ZodValidationPipe(createItemSchema))
|
// @UsePipes(new ZodValidationPipe(createItemSchema))
|
||||||
async createItem(@Body() createItemDto: any): Promise<number> {
|
async createItem(@Body() createItemDto: any): Promise<number> {
|
||||||
return this.createItemService.createItem(createItemDto);
|
return this.createItemService.createItem(createItemDto);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Model } from 'objection';
|
import { Model } from 'objection';
|
||||||
import { omit, isEmpty } from 'lodash';
|
import { omit, isEmpty } from 'lodash';
|
||||||
import { IMetadata, IMetaQuery, IMetableStore } from './types';
|
import { IMetadata, IMetaQuery, IMetableStore } from './types';
|
||||||
import { itemsStartWith } from 'utils';
|
import { itemsStartWith } from '@/utils/items-start-with';
|
||||||
|
|
||||||
export class MetableStore implements IMetableStore {
|
export class MetableStore implements IMetableStore {
|
||||||
metadata: IMetadata[];
|
metadata: IMetadata[];
|
||||||
@@ -23,7 +23,7 @@ export class MetableStore implements IMetableStore {
|
|||||||
*/
|
*/
|
||||||
setExtraColumns(columns: string[]): void {
|
setExtraColumns(columns: string[]): void {
|
||||||
this.extraColumns = columns;
|
this.extraColumns = columns;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the given metadata key.
|
* Find the given metadata key.
|
||||||
@@ -120,6 +120,7 @@ export class MetableStore implements IMetableStore {
|
|||||||
key,
|
key,
|
||||||
...extraColumns,
|
...extraColumns,
|
||||||
_markAsInserted: true,
|
_markAsInserted: true,
|
||||||
|
group: 'default',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -166,11 +167,11 @@ export class MetableStore implements IMetableStore {
|
|||||||
* Parse the metadata to the collection.
|
* Parse the metadata to the collection.
|
||||||
* @param {Array} collection -
|
* @param {Array} collection -
|
||||||
*/
|
*/
|
||||||
mapMetadataToCollection(metadata: IMetadata[], parseType: string = 'parse') {
|
// mapMetadataToCollection(metadata: IMetadata[], parseType: string = 'parse') {
|
||||||
return metadata.map((model) =>
|
// return metadata.map((model) =>
|
||||||
this.mapMetadataToCollection(model, parseType)
|
// this.mapMetadataToCollection(model, parseType)
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load metadata to the metable collection.
|
* Load metadata to the metable collection.
|
||||||
@@ -198,12 +199,12 @@ export class MetableStore implements IMetableStore {
|
|||||||
* Static method to load metadata to the collection.
|
* Static method to load metadata to the collection.
|
||||||
* @param {Array} meta
|
* @param {Array} meta
|
||||||
*/
|
*/
|
||||||
static from(meta) {
|
// static from(meta) {
|
||||||
const collection = new MetableCollection();
|
// const collection = new MetableCollection();
|
||||||
collection.from(meta);
|
// collection.from(meta);
|
||||||
|
|
||||||
return collection;
|
// return collection;
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset the momerized metadata.
|
* Reset the momerized metadata.
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { IMetadata, IMetableStoreStorage } from './types';
|
import { IMetadata, IMetableStoreStorage } from './types';
|
||||||
import { MetableStore } from './MetableStore';
|
import { MetableStore } from './MetableStore';
|
||||||
import { isBlank, parseBoolean } from 'utils';
|
|
||||||
import { MetableConfig } from './MetableConfig';
|
import { MetableConfig } from './MetableConfig';
|
||||||
import { EntityRepository } from '@/common/repository/EntityRepository';
|
import { EntityRepository } from '@/common/repository/EntityRepository';
|
||||||
|
import { isBlank } from '@/utils/is-blank';
|
||||||
|
import { parseBoolean } from '@/utils/parse-boolean';
|
||||||
|
|
||||||
export class MetableDBStore
|
export class MetableDBStore
|
||||||
extends MetableStore
|
extends MetableStore
|
||||||
@@ -72,10 +73,10 @@ export class MetableDBStore
|
|||||||
/**
|
/**
|
||||||
* Saves the modified, deleted and insert metadata.
|
* Saves the modified, deleted and insert metadata.
|
||||||
*/
|
*/
|
||||||
save() {
|
async save() {
|
||||||
this.validateStoreIsLoaded();
|
this.validateStoreIsLoaded();
|
||||||
|
|
||||||
return Promise.all([
|
await Promise.all([
|
||||||
this.saveUpdated(this.metadata),
|
this.saveUpdated(this.metadata),
|
||||||
this.saveDeleted(this.metadata),
|
this.saveDeleted(this.metadata),
|
||||||
this.saveInserted(this.metadata),
|
this.saveInserted(this.metadata),
|
||||||
|
|||||||
@@ -15,5 +15,6 @@ export const SETTINGS = 'SETTINGS';
|
|||||||
inject: [SettingRepository],
|
inject: [SettingRepository],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
exports: [SETTINGS]
|
||||||
})
|
})
|
||||||
export class SettingsModule {}
|
export class SettingsModule {}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export class SettingsStore extends MetableDBStore {
|
|||||||
constructor(repository: EntityRepository, config: any = SettingsOptions) {
|
constructor(repository: EntityRepository, config: any = SettingsOptions) {
|
||||||
super(config);
|
super(config);
|
||||||
|
|
||||||
// this.setExtraColumns(['group']);
|
this.setExtraColumns(['group']);
|
||||||
this.setRepository(repository);
|
this.setRepository(repository);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,10 @@ import { PurchasesTransactionLockingGuardSubscriber } from './subscribers/Purcha
|
|||||||
import { SalesTransactionLockingGuardSubscriber } from './subscribers/SalesTransactionLockingGuardSubscriber';
|
import { SalesTransactionLockingGuardSubscriber } from './subscribers/SalesTransactionLockingGuardSubscriber';
|
||||||
import { QueryTransactionsLocking } from './queries/QueryTransactionsLocking';
|
import { QueryTransactionsLocking } from './queries/QueryTransactionsLocking';
|
||||||
import { TransactionsLockingController } from './TransactionsLocking.controller';
|
import { TransactionsLockingController } from './TransactionsLocking.controller';
|
||||||
|
import { SettingsModule } from '../Settings/Settings.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
|
imports: [SettingsModule],
|
||||||
providers: [
|
providers: [
|
||||||
TransactionsLockingService,
|
TransactionsLockingService,
|
||||||
FinancialTransactionLocking,
|
FinancialTransactionLocking,
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import {
|
|||||||
TransactionsLockingGroup,
|
TransactionsLockingGroup,
|
||||||
TransactionsLockingType,
|
TransactionsLockingType,
|
||||||
} from './types/TransactionsLocking.types';
|
} from './types/TransactionsLocking.types';
|
||||||
import { parseDate } from 'utils';
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { SettingsStore } from '../Settings/SettingsStore';
|
import { SettingsStore } from '../Settings/SettingsStore';
|
||||||
import { SETTINGS } from '../Settings/Settings.module';
|
import { SETTINGS } from '../Settings/Settings.module';
|
||||||
@@ -159,3 +158,7 @@ export class TransactionsLockingRepository {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const parseDate = (date: string) => {
|
||||||
|
return date ? moment(date).utcOffset(0).format('YYYY-MM-DD') : '';
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
ITransactionsLockingSchema,
|
|
||||||
TransactionsLockingGroup,
|
TransactionsLockingGroup,
|
||||||
} from '@/interfaces';
|
ITransactionsLockingSchema,
|
||||||
|
} from './types/TransactionsLocking.types';
|
||||||
|
|
||||||
export const ERRORS = {
|
export const ERRORS = {
|
||||||
TRANSACTIONS_DATE_LOCKED: 'TRANSACTIONS_DATE_LOCKED',
|
TRANSACTIONS_DATE_LOCKED: 'TRANSACTIONS_DATE_LOCKED',
|
||||||
@@ -30,7 +30,7 @@ export const TRANSACTIONS_LOCKING_SCHEMA = [
|
|||||||
] as ITransactionsLockingSchema[];
|
] as ITransactionsLockingSchema[];
|
||||||
|
|
||||||
export function getTransactionsLockingSchemaMeta(
|
export function getTransactionsLockingSchemaMeta(
|
||||||
module: TransactionsLockingGroup
|
module: TransactionsLockingGroup,
|
||||||
): ITransactionsLockingSchema {
|
): ITransactionsLockingSchema {
|
||||||
return TRANSACTIONS_LOCKING_SCHEMA.find((schema) => schema.module === module);
|
return TRANSACTIONS_LOCKING_SCHEMA.find((schema) => schema.module === module);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { TransactionsLockingGroup } from '../types/TransactionsLocking.types';
|
import { TransactionsLockingGroup } from '../types/TransactionsLocking.types';
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { TransactionsLockingGuard } from './TransactionsLockingGuard';
|
import { TransactionsLockingGuard } from './TransactionsLockingGuard';
|
||||||
|
import { MomentInput } from 'moment';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PurchasesTransactionLockingGuard {
|
export class PurchasesTransactionLockingGuard {
|
||||||
@@ -13,7 +14,7 @@ export class PurchasesTransactionLockingGuard {
|
|||||||
* @param {Date} transactionDate - The transaction date.
|
* @param {Date} transactionDate - The transaction date.
|
||||||
*/
|
*/
|
||||||
public transactionLockingGuard = async (
|
public transactionLockingGuard = async (
|
||||||
transactionDate: Date
|
transactionDate: MomentInput
|
||||||
) => {
|
) => {
|
||||||
this.transactionLockingGuardService.transactionsLockingGuard(
|
this.transactionLockingGuardService.transactionsLockingGuard(
|
||||||
transactionDate,
|
transactionDate,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { TransactionsLockingGroup } from '../types/TransactionsLocking.types';
|
import { TransactionsLockingGroup } from '../types/TransactionsLocking.types';
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { TransactionsLockingGuard } from './TransactionsLockingGuard';
|
import { TransactionsLockingGuard } from './TransactionsLockingGuard';
|
||||||
|
import { MomentInput } from 'moment';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SalesTransactionLockingGuard {
|
export class SalesTransactionLockingGuard {
|
||||||
@@ -13,7 +14,7 @@ export class SalesTransactionLockingGuard {
|
|||||||
* @param {Date} transactionDate - The transaction date.
|
* @param {Date} transactionDate - The transaction date.
|
||||||
*/
|
*/
|
||||||
public transactionLockingGuard = async (
|
public transactionLockingGuard = async (
|
||||||
transactionDate: Date
|
transactionDate: MomentInput
|
||||||
) => {
|
) => {
|
||||||
await this.transactionLockingGuardService.transactionsLockingGuard(
|
await this.transactionLockingGuardService.transactionsLockingGuard(
|
||||||
transactionDate,
|
transactionDate,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import moment from 'moment';
|
import moment, { MomentInput } from 'moment';
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { TransactionsLockingGroup } from '../types/TransactionsLocking.types';
|
import { TransactionsLockingGroup } from '../types/TransactionsLocking.types';
|
||||||
import { TransactionsLockingRepository } from '../TransactionsLockingRepository';
|
import { TransactionsLockingRepository } from '../TransactionsLockingRepository';
|
||||||
@@ -18,7 +18,7 @@ export class TransactionsLockingGuard {
|
|||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
public isTransactionsLocking = (
|
public isTransactionsLocking = (
|
||||||
transactionDate: Date,
|
transactionDate: MomentInput,
|
||||||
lockingGroup: string = TransactionsLockingGroup.All
|
lockingGroup: string = TransactionsLockingGroup.All
|
||||||
): boolean => {
|
): boolean => {
|
||||||
const { isEnabled, unlockFromDate, unlockToDate, lockToDate } =
|
const { isEnabled, unlockFromDate, unlockToDate, lockToDate } =
|
||||||
@@ -49,7 +49,7 @@ export class TransactionsLockingGuard {
|
|||||||
* @throws {ServiceError}
|
* @throws {ServiceError}
|
||||||
*/
|
*/
|
||||||
public validateTransactionsLocking = (
|
public validateTransactionsLocking = (
|
||||||
transactionDate: Date,
|
transactionDate: MomentInput,
|
||||||
lockingGroup: TransactionsLockingGroup
|
lockingGroup: TransactionsLockingGroup
|
||||||
) => {
|
) => {
|
||||||
const isLocked = this.isTransactionsLocking(
|
const isLocked = this.isTransactionsLocking(
|
||||||
@@ -83,7 +83,7 @@ export class TransactionsLockingGuard {
|
|||||||
* @param {Date} transactionDate - The transaction date.
|
* @param {Date} transactionDate - The transaction date.
|
||||||
*/
|
*/
|
||||||
public transactionsLockingGuard = (
|
public transactionsLockingGuard = (
|
||||||
transactionDate: Date,
|
transactionDate: MomentInput,
|
||||||
moduleType: TransactionsLockingGroup
|
moduleType: TransactionsLockingGroup
|
||||||
) => {
|
) => {
|
||||||
const lockingType =
|
const lockingType =
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export class RefundVendorCredit extends BaseModel {
|
|||||||
public depositAccountId!: number;
|
public depositAccountId!: number;
|
||||||
public description!: string;
|
public description!: string;
|
||||||
public branchId!: number;
|
public branchId!: number;
|
||||||
|
public date!: Date;
|
||||||
|
|
||||||
public vendorCredit!: VendorCredit;
|
public vendorCredit!: VendorCredit;
|
||||||
public depositAccount!: Account;
|
public depositAccount!: Account;
|
||||||
|
|||||||
5
packages/server-nest/src/utils/is-blank.ts
Normal file
5
packages/server-nest/src/utils/is-blank.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
export const isBlank = (value) => {
|
||||||
|
return (_.isEmpty(value) && !_.isNumber(value)) || _.isNaN(value);
|
||||||
|
};
|
||||||
3
packages/server-nest/src/utils/items-start-with.ts
Normal file
3
packages/server-nest/src/utils/items-start-with.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export const itemsStartWith = (items: string[], char: string) => {
|
||||||
|
return items.filter((item) => item.indexOf(char) === 0);
|
||||||
|
};
|
||||||
23
packages/server-nest/src/utils/parse-boolean.ts
Normal file
23
packages/server-nest/src/utils/parse-boolean.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { isEmpty } from 'lodash';
|
||||||
|
|
||||||
|
const booleanValuesRepresentingTrue: string[] = ['true', '1'];
|
||||||
|
const booleanValuesRepresentingFalse: string[] = ['false', '0'];
|
||||||
|
|
||||||
|
const normalizeValue = (value: any): string =>
|
||||||
|
value?.toString().trim().toLowerCase();
|
||||||
|
|
||||||
|
const booleanValues: string[] = [
|
||||||
|
...booleanValuesRepresentingTrue,
|
||||||
|
...booleanValuesRepresentingFalse,
|
||||||
|
].map((value) => normalizeValue(value));
|
||||||
|
|
||||||
|
export const parseBoolean = <T>(value: any, defaultValue: T): T | boolean => {
|
||||||
|
if (typeof value === 'boolean') {
|
||||||
|
return value; // Retrun early we have nothing to parse.
|
||||||
|
}
|
||||||
|
const normalizedValue = normalizeValue(value);
|
||||||
|
if (isEmpty(value) || booleanValues.indexOf(normalizedValue) === -1) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
return booleanValuesRepresentingTrue.indexOf(normalizedValue) !== -1;
|
||||||
|
};
|
||||||
113
packages/server-nest/test/inventory-adjustment.e2e-spec.ts
Normal file
113
packages/server-nest/test/inventory-adjustment.e2e-spec.ts
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
import * as request from 'supertest';
|
||||||
|
import { faker } from '@faker-js/faker';
|
||||||
|
import { app } from './init-app-test';
|
||||||
|
|
||||||
|
export const createInventoryAdjustment = ({ itemId }) => ({
|
||||||
|
date: '2020-01-01',
|
||||||
|
type: 'increment',
|
||||||
|
adjustmentAccountId: 1001,
|
||||||
|
reason: faker.lorem.sentence(),
|
||||||
|
description: faker.lorem.paragraph(),
|
||||||
|
referenceNo: faker.string.alphanumeric(10),
|
||||||
|
itemId,
|
||||||
|
quantity: faker.number.int({ min: 1, max: 100 }),
|
||||||
|
cost: faker.number.float({ min: 1, max: 1000 }),
|
||||||
|
publish: true,
|
||||||
|
|
||||||
|
warehouseId: 1,
|
||||||
|
branchId: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
const makeItemRequest = () => ({
|
||||||
|
name: faker.commerce.productName(),
|
||||||
|
type: 'inventory',
|
||||||
|
inventory_account_id: 1007,
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Inventory Adjustments (e2e)', () => {
|
||||||
|
it('/inventory-adjustments/quick (POST)', async () => {
|
||||||
|
const itemResponse = await request(app.getHttpServer())
|
||||||
|
.post('/items')
|
||||||
|
.set('organization-id', '4064541lv40nhca')
|
||||||
|
.send(makeItemRequest())
|
||||||
|
.expect(201);
|
||||||
|
|
||||||
|
const itemId = itemResponse.text;
|
||||||
|
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.post('/inventory-adjustments/quick')
|
||||||
|
.set('organization-id', '4064541lv40nhca')
|
||||||
|
.send(createInventoryAdjustment({ itemId }))
|
||||||
|
.expect(201);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('/inventory-adjustments/:id (DELETE)', async () => {
|
||||||
|
const itemResponse = await request(app.getHttpServer())
|
||||||
|
.post('/items')
|
||||||
|
.set('organization-id', '4064541lv40nhca')
|
||||||
|
.send(makeItemRequest())
|
||||||
|
.expect(201);
|
||||||
|
|
||||||
|
const itemId = itemResponse.text;
|
||||||
|
|
||||||
|
const inventoryAdjustmentResponse = await request(app.getHttpServer())
|
||||||
|
.post('/inventory-adjustments/quick')
|
||||||
|
.set('organization-id', '4064541lv40nhca')
|
||||||
|
.send(createInventoryAdjustment({ itemId }))
|
||||||
|
.expect(201);
|
||||||
|
|
||||||
|
const inventoryAdjustmentId = inventoryAdjustmentResponse.body.id;
|
||||||
|
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.delete(`/inventory-adjustments/${inventoryAdjustmentId}`)
|
||||||
|
.set('organization-id', '4064541lv40nhca')
|
||||||
|
.expect(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('/inventory-adjustments/:id (GET)', async () => {
|
||||||
|
const itemResponse = await request(app.getHttpServer())
|
||||||
|
.post('/items')
|
||||||
|
.set('organization-id', '4064541lv40nhca')
|
||||||
|
.send(makeItemRequest())
|
||||||
|
.expect(201);
|
||||||
|
|
||||||
|
const itemId = itemResponse.text;
|
||||||
|
const inventoryAdjustmentResponse = await request(app.getHttpServer())
|
||||||
|
.post('/inventory-adjustments/quick')
|
||||||
|
.set('organization-id', '4064541lv40nhca')
|
||||||
|
.send(createInventoryAdjustment({ itemId }))
|
||||||
|
.expect(201);
|
||||||
|
|
||||||
|
const inventoryAdjustmentId = inventoryAdjustmentResponse.body.id;
|
||||||
|
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.get(`/inventory-adjustments/${inventoryAdjustmentId}`)
|
||||||
|
.set('organization-id', '4064541lv40nhca')
|
||||||
|
.expect(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('/inventory-adjustments/:id/publish (POST)', async () => {
|
||||||
|
const itemResponse = await request(app.getHttpServer())
|
||||||
|
.post('/items')
|
||||||
|
.set('organization-id', '4064541lv40nhca')
|
||||||
|
.send(makeItemRequest())
|
||||||
|
.expect(201);
|
||||||
|
|
||||||
|
const itemId = itemResponse.text;
|
||||||
|
const inventoryAdjustmentResponse = await request(app.getHttpServer())
|
||||||
|
.post('/inventory-adjustments/quick')
|
||||||
|
.set('organization-id', '4064541lv40nhca')
|
||||||
|
.send({
|
||||||
|
...createInventoryAdjustment({ itemId }),
|
||||||
|
publish: false,
|
||||||
|
})
|
||||||
|
.expect(201);
|
||||||
|
|
||||||
|
const inventoryAdjustmentId = inventoryAdjustmentResponse.body.id;
|
||||||
|
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.put(`/inventory-adjustments/${inventoryAdjustmentId}/publish`)
|
||||||
|
.set('organization-id', '4064541lv40nhca')
|
||||||
|
.expect(200);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -102,7 +102,7 @@
|
|||||||
"objection-unique": "^1.2.2",
|
"objection-unique": "^1.2.2",
|
||||||
"plaid": "^10.3.0",
|
"plaid": "^10.3.0",
|
||||||
"pluralize": "^8.0.0",
|
"pluralize": "^8.0.0",
|
||||||
"posthog-node": "^4.2.0",
|
"posthog-node": "^4.3.2",
|
||||||
"pug": "^3.0.2",
|
"pug": "^3.0.2",
|
||||||
"puppeteer": "^10.2.0",
|
"puppeteer": "^10.2.0",
|
||||||
"qim": "0.0.52",
|
"qim": "0.0.52",
|
||||||
|
|||||||
Reference in New Issue
Block a user