mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 06:40:31 +00:00
refactor: inventory transfers to nestjs
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
FROM redis:4.0
|
FROM redis:6.2.0
|
||||||
|
|
||||||
COPY redis.conf /usr/local/etc/redis/redis.conf
|
COPY redis.conf /usr/local/etc/redis/redis.conf
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@bigcapital/email-components": "*",
|
"@bigcapital/email-components": "*",
|
||||||
"@bigcapital/pdf-templates": "*",
|
"@bigcapital/pdf-templates": "*",
|
||||||
|
"@bigcapital/server": "*",
|
||||||
"@bigcapital/utils": "*",
|
"@bigcapital/utils": "*",
|
||||||
"@nestjs/bull": "^10.2.1",
|
"@nestjs/bull": "^10.2.1",
|
||||||
"@nestjs/bullmq": "^10.2.2",
|
"@nestjs/bullmq": "^10.2.2",
|
||||||
|
|||||||
@@ -230,6 +230,7 @@ export class Account extends TenantBaseModel {
|
|||||||
to: 'accounts_transactions.accountId',
|
to: 'accounts_transactions.accountId',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Account may has many items as cost account.
|
* Account may has many items as cost account.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -69,6 +69,8 @@ import { MailModule } from '../Mail/Mail.module';
|
|||||||
import { FinancialStatementsModule } from '../FinancialStatements/FinancialStatements.module';
|
import { FinancialStatementsModule } from '../FinancialStatements/FinancialStatements.module';
|
||||||
import { StripePaymentModule } from '../StripePayment/StripePayment.module';
|
import { StripePaymentModule } from '../StripePayment/StripePayment.module';
|
||||||
import { FeaturesModule } from '../Features/Features.module';
|
import { FeaturesModule } from '../Features/Features.module';
|
||||||
|
import { InventoryCostModule } from '../InventoryCost/InventoryCost.module';
|
||||||
|
import { WarehousesTransfersModule } from '../WarehousesTransfers/WarehouseTransfers.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -135,6 +137,7 @@ import { FeaturesModule } from '../Features/Features.module';
|
|||||||
PdfTemplatesModule,
|
PdfTemplatesModule,
|
||||||
BranchesModule,
|
BranchesModule,
|
||||||
WarehousesModule,
|
WarehousesModule,
|
||||||
|
WarehousesTransfersModule,
|
||||||
CustomersModule,
|
CustomersModule,
|
||||||
VendorsModule,
|
VendorsModule,
|
||||||
SaleInvoicesModule,
|
SaleInvoicesModule,
|
||||||
@@ -160,6 +163,7 @@ import { FeaturesModule } from '../Features/Features.module';
|
|||||||
SettingsModule,
|
SettingsModule,
|
||||||
FeaturesModule,
|
FeaturesModule,
|
||||||
InventoryAdjustmentsModule,
|
InventoryAdjustmentsModule,
|
||||||
|
InventoryCostModule,
|
||||||
PostHogModule,
|
PostHogModule,
|
||||||
EventTrackerModule,
|
EventTrackerModule,
|
||||||
FinancialStatementsModule,
|
FinancialStatementsModule,
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export class ImportAls {
|
|||||||
* @returns The result of the callback function.
|
* @returns The result of the callback function.
|
||||||
*/
|
*/
|
||||||
public run<T>(callback: () => T): T {
|
public run<T>(callback: () => T): T {
|
||||||
return this.als.run<T>(new Map(), callback);
|
return this.als.run<T, any>(new Map(), callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -8,6 +8,18 @@ import { InventoryItemsQuantitySyncService } from './commands/InventoryItemsQuan
|
|||||||
import { InventoryTransactionsService } from './commands/InventoryTransactions.service';
|
import { InventoryTransactionsService } from './commands/InventoryTransactions.service';
|
||||||
import { LedgerModule } from '../Ledger/Ledger.module';
|
import { LedgerModule } from '../Ledger/Ledger.module';
|
||||||
import { InventoryComputeCostService } from './commands/InventoryComputeCost.service';
|
import { InventoryComputeCostService } from './commands/InventoryComputeCost.service';
|
||||||
|
import { InventoryCostApplication } from './InventoryCostApplication';
|
||||||
|
import { StoreInventoryLotsCostService } from './commands/StoreInventortyLotsCost.service';
|
||||||
|
import { ComputeItemCostProcessor } from './processors/ComputeItemCost.processor';
|
||||||
|
import { WriteInventoryTransactionsGLEntriesProcessor } from './processors/WriteInventoryTransactionsGLEntries.processor';
|
||||||
|
import {
|
||||||
|
ComputeItemCostQueue,
|
||||||
|
WriteInventoryTransactionsGLEntriesQueue,
|
||||||
|
} from './types/InventoryCost.types';
|
||||||
|
import { BullModule } from '@nestjs/bullmq';
|
||||||
|
import { InventoryAverageCostMethodService } from './commands/InventoryAverageCostMethod.service';
|
||||||
|
import { InventoryItemCostService } from './commands/InventoryCosts.service';
|
||||||
|
import { InventoryItemOpeningAvgCostService } from './commands/InventoryItemOpeningAvgCost.service';
|
||||||
|
|
||||||
const models = [
|
const models = [
|
||||||
RegisterTenancyModel(InventoryCostLotTracker),
|
RegisterTenancyModel(InventoryCostLotTracker),
|
||||||
@@ -15,14 +27,28 @@ const models = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [LedgerModule, ...models],
|
imports: [
|
||||||
|
LedgerModule,
|
||||||
|
...models,
|
||||||
|
BullModule.registerQueue({ name: ComputeItemCostQueue }),
|
||||||
|
BullModule.registerQueue({
|
||||||
|
name: WriteInventoryTransactionsGLEntriesQueue,
|
||||||
|
}),
|
||||||
|
],
|
||||||
providers: [
|
providers: [
|
||||||
InventoryCostGLBeforeWriteSubscriber,
|
InventoryCostGLBeforeWriteSubscriber,
|
||||||
InventoryCostGLStorage,
|
InventoryCostGLStorage,
|
||||||
InventoryItemsQuantitySyncService,
|
InventoryItemsQuantitySyncService,
|
||||||
InventoryTransactionsService,
|
InventoryTransactionsService,
|
||||||
InventoryComputeCostService,
|
InventoryComputeCostService,
|
||||||
|
InventoryCostApplication,
|
||||||
|
StoreInventoryLotsCostService,
|
||||||
|
ComputeItemCostProcessor,
|
||||||
|
WriteInventoryTransactionsGLEntriesProcessor,
|
||||||
|
InventoryAverageCostMethodService,
|
||||||
|
InventoryItemCostService,
|
||||||
|
InventoryItemOpeningAvgCostService,
|
||||||
],
|
],
|
||||||
exports: [...models, InventoryTransactionsService],
|
exports: [...models, InventoryTransactionsService, InventoryItemCostService],
|
||||||
})
|
})
|
||||||
export class InventoryCostModule {}
|
export class InventoryCostModule {}
|
||||||
|
|||||||
@@ -3,12 +3,6 @@ import { Knex } from 'knex';
|
|||||||
import { InventoryTransaction } from '../models/InventoryTransaction';
|
import { InventoryTransaction } from '../models/InventoryTransaction';
|
||||||
|
|
||||||
export class InventoryAverageCostMethod {
|
export class InventoryAverageCostMethod {
|
||||||
/**
|
|
||||||
* Constructor method.
|
|
||||||
* @param {number} tenantId - The given tenant id.
|
|
||||||
* @param {Date} startingDate -
|
|
||||||
* @param {number} itemId - The given inventory item id.
|
|
||||||
*/
|
|
||||||
constructor() {}
|
constructor() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -72,7 +72,6 @@ export class InventoryComputeCostService {
|
|||||||
* @param {Date} startingDate
|
* @param {Date} startingDate
|
||||||
*/
|
*/
|
||||||
async scheduleComputeItemCost(
|
async scheduleComputeItemCost(
|
||||||
tenantId: number,
|
|
||||||
itemId: number,
|
itemId: number,
|
||||||
startingDate: Date | string,
|
startingDate: Date | string,
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
import { keyBy, get } from 'lodash';
|
import { keyBy, get } from 'lodash';
|
||||||
import { Knex } from 'knex';
|
|
||||||
import * as R from 'ramda';
|
|
||||||
import { IInventoryItemCostMeta } from '../types/InventoryCost.types';
|
import { IInventoryItemCostMeta } from '../types/InventoryCost.types';
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { InventoryTransaction } from '../models/InventoryTransaction';
|
import { InventoryTransaction } from '../models/InventoryTransaction';
|
||||||
@@ -22,33 +20,30 @@ export class InventoryItemCostService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {} INValuationMap -
|
* @param {Map<number, IInventoryItemCostMeta>} INValuationMap -
|
||||||
* @param {} OUTValuationMap -
|
* @param {Map<number, IInventoryItemCostMeta>} OUTValuationMap -
|
||||||
* @param {number} itemId
|
* @param {number} itemId
|
||||||
*/
|
*/
|
||||||
private getItemInventoryMeta = R.curry(
|
private getItemInventoryMeta(
|
||||||
(
|
INValuationMap: Map<number, IInventoryItemCostMeta>,
|
||||||
INValuationMap,
|
OUTValuationMap: Map<number, IInventoryItemCostMeta>,
|
||||||
OUTValuationMap,
|
itemId: number,
|
||||||
itemId: number,
|
) {
|
||||||
): IInventoryItemCostMeta => {
|
const INCost = get(INValuationMap, `[${itemId}].cost`, 0);
|
||||||
const INCost = get(INValuationMap, `[${itemId}].cost`, 0);
|
const INQuantity = get(INValuationMap, `[${itemId}].quantity`, 0);
|
||||||
const INQuantity = get(INValuationMap, `[${itemId}].quantity`, 0);
|
|
||||||
|
|
||||||
const OUTCost = get(OUTValuationMap, `[${itemId}].cost`, 0);
|
const OUTCost = get(OUTValuationMap, `[${itemId}].cost`, 0);
|
||||||
const OUTQuantity = get(OUTValuationMap, `[${itemId}].quantity`, 0);
|
const OUTQuantity = get(OUTValuationMap, `[${itemId}].quantity`, 0);
|
||||||
|
|
||||||
const valuation = INCost - OUTCost;
|
const valuation = INCost - OUTCost;
|
||||||
const quantity = INQuantity - OUTQuantity;
|
const quantity = INQuantity - OUTQuantity;
|
||||||
const average = quantity ? valuation / quantity : 0;
|
const average = quantity ? valuation / quantity : 0;
|
||||||
|
|
||||||
return { itemId, valuation, quantity, average };
|
return { itemId, valuation, quantity, average };
|
||||||
},
|
}
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {number} tenantId
|
|
||||||
* @param {number} itemsId
|
* @param {number} itemsId
|
||||||
* @param {Date} date
|
* @param {Date} date
|
||||||
* @returns
|
* @returns
|
||||||
@@ -57,7 +52,7 @@ export class InventoryItemCostService {
|
|||||||
itemsId: number[],
|
itemsId: number[],
|
||||||
date: Date,
|
date: Date,
|
||||||
): Promise<any> => {
|
): Promise<any> => {
|
||||||
const commonBuilder = (builder: Knex.QueryBuilder) => {
|
const commonBuilder = (builder) => {
|
||||||
if (date) {
|
if (date) {
|
||||||
builder.where('date', '<', date);
|
builder.where('date', '<', date);
|
||||||
}
|
}
|
||||||
@@ -84,7 +79,6 @@ export class InventoryItemCostService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {number} tenantId -
|
|
||||||
* @param {number[]} itemsIds -
|
* @param {number[]} itemsIds -
|
||||||
* @param {Date} date -
|
* @param {Date} date -
|
||||||
*/
|
*/
|
||||||
@@ -122,11 +116,10 @@ export class InventoryItemCostService {
|
|||||||
const [OUTValuationMap, INValuationMap] =
|
const [OUTValuationMap, INValuationMap] =
|
||||||
await this.getItemsInventoryInOutMap(itemsId, date);
|
await this.getItemsInventoryInOutMap(itemsId, date);
|
||||||
|
|
||||||
const getItemValuation = this.getItemInventoryMeta(
|
const getItemValuation = (itemId: number) =>
|
||||||
INValuationMap,
|
this.getItemInventoryMeta(INValuationMap, OUTValuationMap, itemId);
|
||||||
OUTValuationMap,
|
|
||||||
);
|
const itemsValuations = inventoryItemsIds.map((id) => getItemValuation(id));
|
||||||
const itemsValuations = inventoryItemsIds.map(getItemValuation);
|
|
||||||
const itemsValuationsMap = new Map(
|
const itemsValuationsMap = new Map(
|
||||||
itemsValuations.map((i) => [i.itemId, i]),
|
itemsValuations.map((i) => [i.itemId, i]),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { TenantModelProxy } from '../../System/models/TenantBaseModel';
|
import { TenantModelProxy } from '../../System/models/TenantBaseModel';
|
||||||
import { InventoryCostLotTracker } from '../models/InventoryCostLotTracker';
|
import { InventoryCostLotTracker } from '../models/InventoryCostLotTracker';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class InventoryItemOpeningAvgCostService {
|
export class InventoryItemOpeningAvgCostService {
|
||||||
constructor(
|
constructor(
|
||||||
|
@Inject(InventoryCostLotTracker.name)
|
||||||
private readonly inventoryCostLotTrackerModel: TenantModelProxy<
|
private readonly inventoryCostLotTrackerModel: TenantModelProxy<
|
||||||
typeof InventoryCostLotTracker
|
typeof InventoryCostLotTracker
|
||||||
>,
|
>,
|
||||||
@@ -31,6 +32,10 @@ export class InventoryItemOpeningAvgCostService {
|
|||||||
builder.sum('cost as cost');
|
builder.sum('cost as cost');
|
||||||
builder.first();
|
builder.first();
|
||||||
};
|
};
|
||||||
|
interface QueryResult {
|
||||||
|
cost: number;
|
||||||
|
quantity: number;
|
||||||
|
}
|
||||||
// Calculates the total inventory total quantity and rate `IN` transactions.
|
// Calculates the total inventory total quantity and rate `IN` transactions.
|
||||||
const inInvSumationOper = this.inventoryCostLotTrackerModel()
|
const inInvSumationOper = this.inventoryCostLotTrackerModel()
|
||||||
.query()
|
.query()
|
||||||
@@ -43,10 +48,11 @@ export class InventoryItemOpeningAvgCostService {
|
|||||||
.onBuild(commonBuilder)
|
.onBuild(commonBuilder)
|
||||||
.where('direction', 'OUT');
|
.where('direction', 'OUT');
|
||||||
|
|
||||||
const [inInvSumation, outInvSumation] = await Promise.all([
|
const [inInvSumation, outInvSumation] = (await Promise.all([
|
||||||
inInvSumationOper,
|
inInvSumationOper,
|
||||||
outInvSumationOper,
|
outInvSumationOper,
|
||||||
]);
|
])) as unknown as [QueryResult, QueryResult];
|
||||||
|
|
||||||
return this.computeItemAverageCost(
|
return this.computeItemAverageCost(
|
||||||
inInvSumation?.cost || 0,
|
inInvSumation?.cost || 0,
|
||||||
inInvSumation?.quantity || 0,
|
inInvSumation?.quantity || 0,
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export class InventoryTransactionsService {
|
|||||||
* @return {Promise<void>}
|
* @return {Promise<void>}
|
||||||
*/
|
*/
|
||||||
async recordInventoryTransactions(
|
async recordInventoryTransactions(
|
||||||
transactions: InventoryTransaction[],
|
transactions: ModelObject<InventoryTransaction>[],
|
||||||
override: boolean = false,
|
override: boolean = false,
|
||||||
trx?: Knex.Transaction,
|
trx?: Knex.Transaction,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
|||||||
@@ -7,21 +7,20 @@ import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
|
|||||||
import { InventoryTransactionMeta } from './InventoryTransactionMeta';
|
import { InventoryTransactionMeta } from './InventoryTransactionMeta';
|
||||||
|
|
||||||
export class InventoryTransaction extends TenantBaseModel {
|
export class InventoryTransaction extends TenantBaseModel {
|
||||||
date: Date | string;
|
date!: Date | string;
|
||||||
direction: TInventoryTransactionDirection;
|
direction!: TInventoryTransactionDirection;
|
||||||
itemId: number;
|
itemId!: number;
|
||||||
quantity: number | null;
|
quantity!: number | null;
|
||||||
rate: number;
|
rate!: number;
|
||||||
transactionType: string;
|
transactionType!: string;
|
||||||
transactionId: number;
|
transactionId!: number;
|
||||||
costAccountId?: number;
|
costAccountId?: number;
|
||||||
entryId: number;
|
entryId!: number;
|
||||||
|
|
||||||
createdAt?: Date;
|
createdAt?: Date;
|
||||||
updatedAt?: Date;
|
updatedAt?: Date;
|
||||||
|
|
||||||
warehouseId?: number;
|
warehouseId?: number;
|
||||||
|
|
||||||
meta?: InventoryTransactionMeta;
|
meta?: InventoryTransactionMeta;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -34,7 +33,7 @@ export class InventoryTransaction extends TenantBaseModel {
|
|||||||
/**
|
/**
|
||||||
* Model timestamps.
|
* Model timestamps.
|
||||||
*/
|
*/
|
||||||
get timestamps() {
|
static get timestamps() {
|
||||||
return ['createdAt', 'updatedAt'];
|
return ['createdAt', 'updatedAt'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,40 +1,58 @@
|
|||||||
import { JOB_REF, Processor } from '@nestjs/bullmq';
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
|
import { JOB_REF, Processor, WorkerHost } from '@nestjs/bullmq';
|
||||||
import { Inject, Scope } from '@nestjs/common';
|
import { Inject, Scope } from '@nestjs/common';
|
||||||
import { Job } from 'bullmq';
|
import { Job } from 'bullmq';
|
||||||
import { ClsService } from 'nestjs-cls';
|
import { ClsService } from 'nestjs-cls';
|
||||||
import { TenantJobPayload } from '@/interfaces/Tenant';
|
import { TenantJobPayload } from '@/interfaces/Tenant';
|
||||||
import { InventoryComputeCostService } from '../commands/InventoryComputeCost.service';
|
import { InventoryComputeCostService } from '../commands/InventoryComputeCost.service';
|
||||||
|
import { events } from '@/common/events/events';
|
||||||
|
import { ComputeItemCostQueueJob } from '../types/InventoryCost.types';
|
||||||
|
|
||||||
interface ComputeItemCostJobPayload extends TenantJobPayload {
|
interface ComputeItemCostJobPayload extends TenantJobPayload {
|
||||||
itemId: number;
|
itemId: number;
|
||||||
startingDate: Date;
|
startingDate: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Processor({
|
@Processor({
|
||||||
name: 'compute-item-cost',
|
name: ComputeItemCostQueueJob,
|
||||||
scope: Scope.REQUEST,
|
scope: Scope.REQUEST,
|
||||||
})
|
})
|
||||||
export class ComputeItemCostProcessor {
|
export class ComputeItemCostProcessor extends WorkerHost {
|
||||||
|
/**
|
||||||
|
* @param {InventoryComputeCostService} inventoryComputeCostService -
|
||||||
|
* @param {ClsService} clsService -
|
||||||
|
* @param {EventEmitter2} eventEmitter -
|
||||||
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
private readonly inventoryComputeCostService: InventoryComputeCostService,
|
private readonly inventoryComputeCostService: InventoryComputeCostService,
|
||||||
private readonly clsService: ClsService,
|
private readonly clsService: ClsService,
|
||||||
|
private readonly eventEmitter: EventEmitter2,
|
||||||
@Inject(JOB_REF)
|
) {
|
||||||
private readonly jobRef: Job<ComputeItemCostJobPayload>,
|
super();
|
||||||
) {}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle compute item cost job.
|
* Process the compute item cost job.
|
||||||
|
* @param {Job<ComputeItemCostJobPayload>} job - The job to process
|
||||||
*/
|
*/
|
||||||
async handleComputeItemCost() {
|
async process(job: Job<ComputeItemCostJobPayload>) {
|
||||||
const { itemId, startingDate, organizationId, userId } = this.jobRef.data;
|
const { itemId, startingDate, organizationId, userId } = job.data;
|
||||||
|
|
||||||
this.clsService.set('organizationId', organizationId);
|
this.clsService.set('organizationId', organizationId);
|
||||||
this.clsService.set('userId', userId);
|
this.clsService.set('userId', userId);
|
||||||
|
|
||||||
await this.inventoryComputeCostService.computeItemCost(
|
try {
|
||||||
startingDate,
|
await this.inventoryComputeCostService.computeItemCost(
|
||||||
itemId,
|
startingDate,
|
||||||
);
|
itemId,
|
||||||
|
);
|
||||||
|
// Emit job completed event
|
||||||
|
await this.eventEmitter.emitAsync(
|
||||||
|
events.inventory.onComputeItemCostJobCompleted,
|
||||||
|
{ startingDate, itemId, organizationId, userId },
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error computing item cost:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { Process } from '@nestjs/bull';
|
||||||
|
import {
|
||||||
|
WriteInventoryTransactionsGLEntriesQueue,
|
||||||
|
WriteInventoryTransactionsGLEntriesQueueJob,
|
||||||
|
} from '../types/InventoryCost.types';
|
||||||
|
import { Processor, WorkerHost } from '@nestjs/bullmq';
|
||||||
|
import { Scope } from '@nestjs/common';
|
||||||
|
|
||||||
|
@Processor({
|
||||||
|
name: WriteInventoryTransactionsGLEntriesQueue,
|
||||||
|
scope: Scope.REQUEST,
|
||||||
|
})
|
||||||
|
export class WriteInventoryTransactionsGLEntriesProcessor extends WorkerHost {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Process(WriteInventoryTransactionsGLEntriesQueueJob)
|
||||||
|
async process() {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
import { map, head } from 'lodash';
|
||||||
|
import { OnEvent } from '@nestjs/event-emitter';
|
||||||
|
import {
|
||||||
|
IComputeItemCostJobCompletedPayload,
|
||||||
|
IInventoryTransactionsCreatedPayload,
|
||||||
|
IInventoryTransactionsDeletedPayload,
|
||||||
|
} from '../types/InventoryCost.types';
|
||||||
|
import { ImportAls } from '@/modules/Import/ImportALS';
|
||||||
|
import { InventoryItemsQuantitySyncService } from '../commands/InventoryItemsQuantitySync.service';
|
||||||
|
import { SaleInvoicesCost } from '@/modules/SaleInvoices/SalesInvoicesCost';
|
||||||
|
import { events } from '@/common/events/events';
|
||||||
|
import { runAfterTransaction } from '@/modules/Tenancy/TenancyDB/TransactionsHooks';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { InventoryComputeCostService } from '../commands/InventoryComputeCost.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export default class InventorySubscriber {
|
||||||
|
constructor(
|
||||||
|
private readonly saleInvoicesCost: SaleInvoicesCost,
|
||||||
|
private readonly itemsQuantitySync: InventoryItemsQuantitySyncService,
|
||||||
|
private readonly inventoryService: InventoryComputeCostService,
|
||||||
|
private readonly importAls: ImportAls,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sync inventory items quantity once inventory transactions created.
|
||||||
|
* @param {IInventoryTransactionsCreatedPayload} payload -
|
||||||
|
*/
|
||||||
|
@OnEvent(events.inventory.onInventoryTransactionsCreated)
|
||||||
|
async syncItemsQuantityOnceInventoryTransactionsCreated({
|
||||||
|
inventoryTransactions,
|
||||||
|
trx,
|
||||||
|
}: IInventoryTransactionsCreatedPayload) {
|
||||||
|
const itemsQuantityChanges = this.itemsQuantitySync.getItemsQuantityChanges(
|
||||||
|
inventoryTransactions,
|
||||||
|
);
|
||||||
|
await this.itemsQuantitySync.changeItemsQuantity(itemsQuantityChanges, trx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles schedule compute inventory items cost once inventory transactions created.
|
||||||
|
* @param {IInventoryTransactionsCreatedPayload} payload -
|
||||||
|
*/
|
||||||
|
@OnEvent(events.inventory.onInventoryTransactionsCreated)
|
||||||
|
async handleScheduleItemsCostOnInventoryTransactionsCreated({
|
||||||
|
inventoryTransactions,
|
||||||
|
trx,
|
||||||
|
}: IInventoryTransactionsCreatedPayload) {
|
||||||
|
const inImportPreviewScope = this.importAls.isImportPreview;
|
||||||
|
|
||||||
|
// Avoid running the cost items job if the async process is in import preview.
|
||||||
|
if (inImportPreviewScope) return;
|
||||||
|
|
||||||
|
await this.saleInvoicesCost.computeItemsCostByInventoryTransactions(
|
||||||
|
inventoryTransactions,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks items cost compute running state.
|
||||||
|
*/
|
||||||
|
@OnEvent(events.inventory.onInventoryTransactionsCreated)
|
||||||
|
async markGlobalSettingsComputeItems({}) {
|
||||||
|
await this.inventoryService.markItemsCostComputeRunning(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks items cost compute as completed.
|
||||||
|
*/
|
||||||
|
@OnEvent(events.inventory.onInventoryCostEntriesWritten)
|
||||||
|
async markGlobalSettingsComputeItemsCompeted({}) {
|
||||||
|
await this.inventoryService.markItemsCostComputeRunning(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle run writing the journal entries once the compute items jobs completed.
|
||||||
|
*/
|
||||||
|
@OnEvent(events.inventory.onComputeItemCostJobCompleted)
|
||||||
|
async onComputeItemCostJobFinished({
|
||||||
|
itemId,
|
||||||
|
startingDate,
|
||||||
|
}: IComputeItemCostJobCompletedPayload) {
|
||||||
|
// const dependsComputeJobs = await this.agenda.jobs({
|
||||||
|
// name: 'compute-item-cost',
|
||||||
|
// nextRunAt: { $ne: null },
|
||||||
|
// 'data.tenantId': tenantId,
|
||||||
|
// });
|
||||||
|
// // There is no scheduled compute jobs waiting.
|
||||||
|
// if (dependsComputeJobs.length === 0) {
|
||||||
|
// await this.saleInvoicesCost.scheduleWriteJournalEntries(startingDate);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sync inventory items quantity once inventory transactions deleted.
|
||||||
|
*/
|
||||||
|
@OnEvent(events.inventory.onInventoryTransactionsDeleted)
|
||||||
|
async syncItemsQuantityOnceInventoryTransactionsDeleted({
|
||||||
|
oldInventoryTransactions,
|
||||||
|
trx,
|
||||||
|
}: IInventoryTransactionsDeletedPayload) {
|
||||||
|
const itemsQuantityChanges =
|
||||||
|
this.itemsQuantitySync.getReverseItemsQuantityChanges(
|
||||||
|
oldInventoryTransactions,
|
||||||
|
);
|
||||||
|
await this.itemsQuantitySync.changeItemsQuantity(itemsQuantityChanges, trx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedules compute items cost once the inventory transactions deleted.
|
||||||
|
*/
|
||||||
|
@OnEvent(events.inventory.onInventoryTransactionsDeleted)
|
||||||
|
async handleScheduleItemsCostOnInventoryTransactionsDeleted({
|
||||||
|
transactionType,
|
||||||
|
transactionId,
|
||||||
|
oldInventoryTransactions,
|
||||||
|
trx,
|
||||||
|
}: IInventoryTransactionsDeletedPayload) {
|
||||||
|
// Ignore compute item cost with theses transaction types.
|
||||||
|
const ignoreWithTransactionTypes = ['OpeningItem'];
|
||||||
|
|
||||||
|
if (ignoreWithTransactionTypes.indexOf(transactionType) !== -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const inventoryItemsIds = map(oldInventoryTransactions, 'itemId');
|
||||||
|
const startingDates = map(oldInventoryTransactions, 'date');
|
||||||
|
const startingDate: Date = head(startingDates);
|
||||||
|
|
||||||
|
runAfterTransaction(trx, async () => {
|
||||||
|
try {
|
||||||
|
await this.saleInvoicesCost.scheduleComputeCostByItemsIds(
|
||||||
|
inventoryItemsIds,
|
||||||
|
startingDate,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,13 @@
|
|||||||
import { Knex } from "knex";
|
import { Knex } from 'knex';
|
||||||
import { InventoryTransaction } from "../models/InventoryTransaction";
|
import { InventoryTransaction } from '../models/InventoryTransaction';
|
||||||
|
|
||||||
|
export const ComputeItemCostQueue = 'ComputeItemCostQueue';
|
||||||
|
export const ComputeItemCostQueueJob = 'ComputeItemCostQueueJob';
|
||||||
|
|
||||||
|
export const WriteInventoryTransactionsGLEntriesQueue =
|
||||||
|
'WriteInventoryTransactionsGLEntriesQueue';
|
||||||
|
export const WriteInventoryTransactionsGLEntriesQueueJob =
|
||||||
|
'WriteInventoryTransactionsGLEntriesQueueJob';
|
||||||
|
|
||||||
export interface IInventoryItemCostMeta {
|
export interface IInventoryItemCostMeta {
|
||||||
itemId: number;
|
itemId: number;
|
||||||
@@ -10,8 +17,8 @@ export interface IInventoryItemCostMeta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface IInventoryCostLotsGLEntriesWriteEvent {
|
export interface IInventoryCostLotsGLEntriesWriteEvent {
|
||||||
startingDate: Date,
|
startingDate: Date;
|
||||||
trx: Knex.Transaction
|
trx: Knex.Transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TInventoryTransactionDirection = 'IN' | 'OUT';
|
export type TInventoryTransactionDirection = 'IN' | 'OUT';
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { Warehouse } from '@/modules/Warehouses/models/Warehouse.model';
|
import { Warehouse } from '@/modules/Warehouses/models/Warehouse.model';
|
||||||
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
|
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
|
||||||
|
import { Model } from 'objection';
|
||||||
|
|
||||||
export class Item extends TenantBaseModel{
|
export class Item extends TenantBaseModel {
|
||||||
public readonly quantityOnHand: number;
|
public readonly quantityOnHand: number;
|
||||||
public readonly name: string;
|
public readonly name: string;
|
||||||
public readonly active: boolean;
|
public readonly active: boolean;
|
||||||
@@ -30,4 +31,164 @@ export class Item extends TenantBaseModel{
|
|||||||
static get tableName() {
|
static get tableName() {
|
||||||
return 'items';
|
return 'items';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Relationship mapping.
|
||||||
|
*/
|
||||||
|
static get relationMappings() {
|
||||||
|
// const { Media } = require('../../Media/models/Media.model');
|
||||||
|
const { Account } = require('../../Accounts/models/Account.model');
|
||||||
|
const {
|
||||||
|
ItemCategory,
|
||||||
|
} = require('../../ItemCategories/models/ItemCategory.model');
|
||||||
|
const {
|
||||||
|
ItemWarehouseQuantity,
|
||||||
|
} = require('../../Warehouses/models/ItemWarehouseQuantity');
|
||||||
|
const {
|
||||||
|
ItemEntry,
|
||||||
|
} = require('../../TransactionItemEntry/models/ItemEntry');
|
||||||
|
// const WarehouseTransferEntry = require('../../Warehouses/');
|
||||||
|
const {
|
||||||
|
InventoryAdjustmentEntry,
|
||||||
|
} = require('../../InventoryAdjutments/models/InventoryAdjustment');
|
||||||
|
const { TaxRate } = require('../../TaxRates/models/TaxRate.model');
|
||||||
|
|
||||||
|
return {
|
||||||
|
/**
|
||||||
|
* Item may belongs to cateogory model.
|
||||||
|
*/
|
||||||
|
category: {
|
||||||
|
relation: Model.BelongsToOneRelation,
|
||||||
|
modelClass: ItemCategory,
|
||||||
|
join: {
|
||||||
|
from: 'items.categoryId',
|
||||||
|
to: 'items_categories.id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Item may belongs to cost account.
|
||||||
|
*/
|
||||||
|
costAccount: {
|
||||||
|
relation: Model.BelongsToOneRelation,
|
||||||
|
modelClass: Account,
|
||||||
|
join: {
|
||||||
|
from: 'items.costAccountId',
|
||||||
|
to: 'accounts.id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Item may belongs to sell account.
|
||||||
|
*/
|
||||||
|
sellAccount: {
|
||||||
|
relation: Model.BelongsToOneRelation,
|
||||||
|
modelClass: Account,
|
||||||
|
join: {
|
||||||
|
from: 'items.sellAccountId',
|
||||||
|
to: 'accounts.id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Item may belongs to inventory account.
|
||||||
|
*/
|
||||||
|
inventoryAccount: {
|
||||||
|
relation: Model.BelongsToOneRelation,
|
||||||
|
modelClass: Account,
|
||||||
|
join: {
|
||||||
|
from: 'items.inventoryAccountId',
|
||||||
|
to: 'accounts.id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Item has many warehouses quantities.
|
||||||
|
*/
|
||||||
|
itemWarehouses: {
|
||||||
|
relation: Model.HasManyRelation,
|
||||||
|
modelClass: ItemWarehouseQuantity,
|
||||||
|
join: {
|
||||||
|
from: 'items.id',
|
||||||
|
to: 'items_warehouses_quantity.itemId',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Item may has many item entries.
|
||||||
|
*/
|
||||||
|
itemEntries: {
|
||||||
|
relation: Model.HasManyRelation,
|
||||||
|
modelClass: ItemEntry,
|
||||||
|
join: {
|
||||||
|
from: 'items.id',
|
||||||
|
to: 'items_entries.itemId',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Item may has many warehouses transfers entries.
|
||||||
|
*/
|
||||||
|
// warehousesTransfersEntries: {
|
||||||
|
// relation: Model.HasManyRelation,
|
||||||
|
// modelClass: WarehouseTransferEntry,
|
||||||
|
// join: {
|
||||||
|
// from: 'items.id',
|
||||||
|
// to: 'warehouses_transfers_entries.itemId',
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Item has many inventory adjustment entries.
|
||||||
|
*/
|
||||||
|
inventoryAdjustmentsEntries: {
|
||||||
|
relation: Model.HasManyRelation,
|
||||||
|
modelClass: InventoryAdjustmentEntry,
|
||||||
|
join: {
|
||||||
|
from: 'items.id',
|
||||||
|
to: 'inventory_adjustments_entries.itemId',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
// media: {
|
||||||
|
// relation: Model.ManyToManyRelation,
|
||||||
|
// modelClass: Media.default,
|
||||||
|
// join: {
|
||||||
|
// from: 'items.id',
|
||||||
|
// through: {
|
||||||
|
// from: 'media_links.model_id',
|
||||||
|
// to: 'media_links.media_id',
|
||||||
|
// },
|
||||||
|
// to: 'media.id',
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Item may has sell tax rate.
|
||||||
|
*/
|
||||||
|
sellTaxRate: {
|
||||||
|
relation: Model.BelongsToOneRelation,
|
||||||
|
modelClass: TaxRate,
|
||||||
|
join: {
|
||||||
|
from: 'items.sellTaxRateId',
|
||||||
|
to: 'tax_rates.id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Item may has purchase tax rate.
|
||||||
|
*/
|
||||||
|
purchaseTaxRate: {
|
||||||
|
relation: Model.BelongsToOneRelation,
|
||||||
|
modelClass: TaxRate,
|
||||||
|
join: {
|
||||||
|
from: 'items.purchaseTaxRateId',
|
||||||
|
to: 'tax_rates.id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,11 +9,12 @@ import { events } from '@/common/events/events';
|
|||||||
import { ModelObject } from 'objection';
|
import { ModelObject } from 'objection';
|
||||||
import { InventoryTransaction } from '../InventoryCost/models/InventoryTransaction';
|
import { InventoryTransaction } from '../InventoryCost/models/InventoryTransaction';
|
||||||
import { IInventoryCostLotsGLEntriesWriteEvent } from '../InventoryCost/types/InventoryCost.types';
|
import { IInventoryCostLotsGLEntriesWriteEvent } from '../InventoryCost/types/InventoryCost.types';
|
||||||
|
import { InventoryComputeCostService } from '../InventoryCost/commands/InventoryComputeCost.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SaleInvoicesCost {
|
export class SaleInvoicesCost {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly inventoryService: InventoryService,
|
private readonly inventoryService: InventoryComputeCostService,
|
||||||
private readonly uow: UnitOfWork,
|
private readonly uow: UnitOfWork,
|
||||||
private readonly eventPublisher: EventEmitter2,
|
private readonly eventPublisher: EventEmitter2,
|
||||||
) {}
|
) {}
|
||||||
@@ -112,12 +113,12 @@ export class SaleInvoicesCost {
|
|||||||
* @return {Promise<agenda>}
|
* @return {Promise<agenda>}
|
||||||
*/
|
*/
|
||||||
scheduleWriteJournalEntries(startingDate?: Date) {
|
scheduleWriteJournalEntries(startingDate?: Date) {
|
||||||
const agenda = Container.get('agenda');
|
// const agenda = Container.get('agenda');
|
||||||
|
|
||||||
return agenda.schedule('in 3 seconds', 'rewrite-invoices-journal-entries', {
|
// return agenda.schedule('in 3 seconds', 'rewrite-invoices-journal-entries', {
|
||||||
startingDate,
|
// startingDate,
|
||||||
tenantId,
|
// tenantId,
|
||||||
});
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -19,6 +19,14 @@ import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DeleteSaleInvoice {
|
export class DeleteSaleInvoice {
|
||||||
|
/**
|
||||||
|
* @param {UnlinkConvertedSaleEstimate} unlockEstimateFromInvoice - Unlink converted sale estimate service.
|
||||||
|
* @param {EventEmitter2} eventPublisher - Event emitter.
|
||||||
|
* @param {UnitOfWork} uow - Unit of work.
|
||||||
|
* @param {TenantModelProxy<typeof PaymentReceivedEntry>} paymentReceivedEntryModel - Payment received entry model.
|
||||||
|
* @param {TenantModelProxy<typeof CreditNoteAppliedInvoice>} creditNoteAppliedInvoiceModel - Credit note applied invoice model.
|
||||||
|
* @param {TenantModelProxy<typeof SaleInvoice>} saleInvoiceModel - Sale invoice model.
|
||||||
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
private unlockEstimateFromInvoice: UnlinkConvertedSaleEstimate,
|
private unlockEstimateFromInvoice: UnlinkConvertedSaleEstimate,
|
||||||
private eventPublisher: EventEmitter2,
|
private eventPublisher: EventEmitter2,
|
||||||
@@ -36,6 +44,9 @@ export class DeleteSaleInvoice {
|
|||||||
|
|
||||||
@Inject(SaleInvoice.name)
|
@Inject(SaleInvoice.name)
|
||||||
private saleInvoiceModel: TenantModelProxy<typeof SaleInvoice>,
|
private saleInvoiceModel: TenantModelProxy<typeof SaleInvoice>,
|
||||||
|
|
||||||
|
@Inject(ItemEntry.name)
|
||||||
|
private itemEntryModel: TenantModelProxy<typeof ItemEntry>,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -113,12 +124,13 @@ export class DeleteSaleInvoice {
|
|||||||
saleInvoiceId,
|
saleInvoiceId,
|
||||||
trx,
|
trx,
|
||||||
);
|
);
|
||||||
await ItemEntry.query(trx)
|
await this.itemEntryModel()
|
||||||
|
.query(trx)
|
||||||
.where('reference_id', saleInvoiceId)
|
.where('reference_id', saleInvoiceId)
|
||||||
.where('reference_type', 'SaleInvoice')
|
.where('reference_type', 'SaleInvoice')
|
||||||
.delete();
|
.delete();
|
||||||
|
|
||||||
await SaleInvoice.query(trx).findById(saleInvoiceId).delete();
|
await this.saleInvoiceModel().query(trx).findById(saleInvoiceId).delete();
|
||||||
|
|
||||||
// Triggers `onSaleInvoiceDeleted` event.
|
// Triggers `onSaleInvoiceDeleted` event.
|
||||||
await this.eventPublisher.emitAsync(events.saleInvoice.onDeleted, {
|
await this.eventPublisher.emitAsync(events.saleInvoice.onDeleted, {
|
||||||
|
|||||||
@@ -17,6 +17,15 @@ import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class EditSaleInvoice {
|
export class EditSaleInvoice {
|
||||||
|
/**
|
||||||
|
* @param {ItemsEntriesService} itemsEntriesService - Items entries service.
|
||||||
|
* @param {EventEmitter2} eventPublisher - Event emitter.
|
||||||
|
* @param {CommandSaleInvoiceValidators} validators - Command sale invoice validators.
|
||||||
|
* @param {CommandSaleInvoiceDTOTransformer} transformerDTO - Command sale invoice DTO transformer.
|
||||||
|
* @param {UnitOfWork} uow - Unit of work.
|
||||||
|
* @param {TenantModelProxy<typeof SaleInvoice>} saleInvoiceModel - Sale invoice model.
|
||||||
|
* @param {TenantModelProxy<typeof Customer>} customerModel - Customer model.
|
||||||
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
private readonly itemsEntriesService: ItemsEntriesService,
|
private readonly itemsEntriesService: ItemsEntriesService,
|
||||||
private readonly eventPublisher: EventEmitter2,
|
private readonly eventPublisher: EventEmitter2,
|
||||||
|
|||||||
@@ -13,9 +13,7 @@ import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
|||||||
export class SubscriptionGuard implements CanActivate {
|
export class SubscriptionGuard implements CanActivate {
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(PlanSubscription.name)
|
@Inject(PlanSubscription.name)
|
||||||
private readonly planSubscriptionModel: TenantModelProxy<
|
private readonly planSubscriptionModel: typeof PlanSubscription,
|
||||||
typeof PlanSubscription
|
|
||||||
>,
|
|
||||||
private readonly tenancyContext: TenancyContext,
|
private readonly tenancyContext: TenancyContext,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@@ -30,7 +28,7 @@ export class SubscriptionGuard implements CanActivate {
|
|||||||
subscriptionSlug: string = 'main', // Default value
|
subscriptionSlug: string = 'main', // Default value
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
const tenant = await this.tenancyContext.getTenant();
|
const tenant = await this.tenancyContext.getTenant();
|
||||||
const subscription = await this.planSubscriptionModel()
|
const subscription = await this.planSubscriptionModel
|
||||||
.query()
|
.query()
|
||||||
.findOne('slug', subscriptionSlug)
|
.findOne('slug', subscriptionSlug)
|
||||||
.where('tenant_id', tenant.id);
|
.where('tenant_id', tenant.id);
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ import { WriteInvoiceTaxTransactionsSubscriber } from './subscribers/WriteInvoic
|
|||||||
import { BillTaxRateValidateSubscriber } from './subscribers/BillTaxRateValidateSubscriber';
|
import { BillTaxRateValidateSubscriber } from './subscribers/BillTaxRateValidateSubscriber';
|
||||||
import { SaleInvoiceTaxRateValidateSubscriber } from './subscribers/SaleInvoiceTaxRateValidateSubscriber';
|
import { SaleInvoiceTaxRateValidateSubscriber } from './subscribers/SaleInvoiceTaxRateValidateSubscriber';
|
||||||
import { SyncItemTaxRateOnEditTaxSubscriber } from './subscribers/SyncItemTaxRateOnEditTaxSubscriber';
|
import { SyncItemTaxRateOnEditTaxSubscriber } from './subscribers/SyncItemTaxRateOnEditTaxSubscriber';
|
||||||
|
import { WriteTaxTransactionsItemEntries } from './WriteTaxTransactionsItemEntries';
|
||||||
|
import { SyncItemTaxRateOnEditTaxRate } from './SyncItemTaxRateOnEditTaxRate';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [],
|
imports: [],
|
||||||
@@ -39,6 +41,8 @@ import { SyncItemTaxRateOnEditTaxSubscriber } from './subscribers/SyncItemTaxRat
|
|||||||
BillTaxRateValidateSubscriber,
|
BillTaxRateValidateSubscriber,
|
||||||
SaleInvoiceTaxRateValidateSubscriber,
|
SaleInvoiceTaxRateValidateSubscriber,
|
||||||
SyncItemTaxRateOnEditTaxSubscriber,
|
SyncItemTaxRateOnEditTaxSubscriber,
|
||||||
|
WriteTaxTransactionsItemEntries,
|
||||||
|
SyncItemTaxRateOnEditTaxRate
|
||||||
],
|
],
|
||||||
exports: [ItemEntriesTaxTransactions],
|
exports: [ItemEntriesTaxTransactions],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { TaxRateModel } from './models/TaxRate.model';
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { ModelObject } from 'objection';
|
import { ModelObject } from 'objection';
|
||||||
import { ItemEntry } from '../TransactionItemEntry/models/ItemEntry';
|
import { ItemEntry } from '../TransactionItemEntry/models/ItemEntry';
|
||||||
|
import { TaxRateTransaction } from './models/TaxRateTransaction.model';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class WriteTaxTransactionsItemEntries {
|
export class WriteTaxTransactionsItemEntries {
|
||||||
@@ -40,11 +41,11 @@ export class WriteTaxTransactionsItemEntries {
|
|||||||
taxRateId: entry.taxRateId,
|
taxRateId: entry.taxRateId,
|
||||||
referenceType: entry.referenceType,
|
referenceType: entry.referenceType,
|
||||||
referenceId: entry.referenceId,
|
referenceId: entry.referenceId,
|
||||||
rate: entry.taxRate || taxRatesById[entry.taxRateId]?.rate,
|
rate: entry.taxRate || (taxRatesById[entry.taxRateId]?.rate as number),
|
||||||
}));
|
}));
|
||||||
await this.taxRateTransactionModel()
|
await this.taxRateTransactionModel()
|
||||||
.query(trx)
|
.query(trx)
|
||||||
.upsertGraph(taxTransactions);
|
.upsertGraph(taxTransactions as ModelObject<TaxRateTransaction>[]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ import { mixin, Model, raw } from 'objection';
|
|||||||
import { BaseModel } from '@/models/Model';
|
import { BaseModel } from '@/models/Model';
|
||||||
|
|
||||||
export class TaxRateTransaction extends BaseModel {
|
export class TaxRateTransaction extends BaseModel {
|
||||||
id: number;
|
public id: number;
|
||||||
taxRateId: number;
|
public taxRateId: number;
|
||||||
referenceType: string;
|
public referenceType: string;
|
||||||
referenceId: number;
|
public referenceId: string;
|
||||||
rate: number;
|
public rate: number;
|
||||||
taxAccountId: number;
|
public taxAccountId?: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Table name
|
* Table name
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { TENANCY_DB_CONNECTION } from '@/modules/Tenancy/TenancyDB/TenancyDB.con
|
|||||||
export class UnitOfWork {
|
export class UnitOfWork {
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(TENANCY_DB_CONNECTION)
|
@Inject(TENANCY_DB_CONNECTION)
|
||||||
private readonly tenantKex: Knex,
|
private readonly tenantKex: () => Knex,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -23,7 +23,7 @@ export class UnitOfWork {
|
|||||||
trx?: Transaction,
|
trx?: Transaction,
|
||||||
isolationLevel: IsolationLevel = IsolationLevel.READ_UNCOMMITTED,
|
isolationLevel: IsolationLevel = IsolationLevel.READ_UNCOMMITTED,
|
||||||
): Promise<T> => {
|
): Promise<T> => {
|
||||||
const knex = this.tenantKex;
|
const knex = this.tenantKex();
|
||||||
let _trx = trx;
|
let _trx = trx;
|
||||||
|
|
||||||
if (!_trx) {
|
if (!_trx) {
|
||||||
|
|||||||
@@ -1,23 +1,12 @@
|
|||||||
import { Knex } from 'knex';
|
import { Knex } from 'knex';
|
||||||
import { Warehouse } from './models/Warehouse.model';
|
import { Warehouse } from './models/Warehouse.model';
|
||||||
|
import { WarehouseTransfer } from '../WarehousesTransfers/models/WarehouseTransfer';
|
||||||
|
import { ModelObject } from 'objection';
|
||||||
|
|
||||||
export interface IWarehouse {
|
export interface IWarehouse {
|
||||||
id?: number;
|
id?: number;
|
||||||
}
|
}
|
||||||
export interface IWarehouseTransfer {
|
|
||||||
id?: number;
|
|
||||||
date: Date;
|
|
||||||
fromWarehouseId: number;
|
|
||||||
toWarehouseId: number;
|
|
||||||
reason?: string;
|
|
||||||
transactionNumber: string;
|
|
||||||
entries: IWarehouseTransferEntry[];
|
|
||||||
transferInitiatedAt?: Date;
|
|
||||||
transferDeliveredAt?: Date;
|
|
||||||
|
|
||||||
isInitiated?: boolean;
|
|
||||||
isTransferred?: boolean;
|
|
||||||
}
|
|
||||||
export interface IWarehouseTransferEntry {
|
export interface IWarehouseTransferEntry {
|
||||||
id?: number;
|
id?: number;
|
||||||
index?: number;
|
index?: number;
|
||||||
@@ -118,37 +107,31 @@ export interface IWarehouseTransferCreate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface IWarehouseTransferCreated {
|
export interface IWarehouseTransferCreated {
|
||||||
trx: Knex.Transaction;
|
trx?: Knex.Transaction;
|
||||||
warehouseTransfer: IWarehouseTransfer;
|
warehouseTransfer: ModelObject<WarehouseTransfer>;
|
||||||
warehouseTransferDTO: ICreateWarehouseTransferDTO;
|
warehouseTransferDTO: ICreateWarehouseTransferDTO;
|
||||||
// tenantId: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IWarehouseTransferEditPayload {
|
export interface IWarehouseTransferEditPayload {
|
||||||
// tenantId: number;
|
|
||||||
editWarehouseDTO: IEditWarehouseTransferDTO;
|
editWarehouseDTO: IEditWarehouseTransferDTO;
|
||||||
oldWarehouseTransfer: IWarehouseTransfer;
|
oldWarehouseTransfer: ModelObject<WarehouseTransfer>;
|
||||||
trx: Knex.Transaction;
|
trx: Knex.Transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IWarehouseTransferEditedPayload {
|
export interface IWarehouseTransferEditedPayload {
|
||||||
// tenantId: number;
|
|
||||||
editWarehouseDTO: IEditWarehouseTransferDTO;
|
editWarehouseDTO: IEditWarehouseTransferDTO;
|
||||||
oldWarehouseTransfer: IWarehouseTransfer;
|
oldWarehouseTransfer: ModelObject<WarehouseTransfer>;
|
||||||
warehouseTransfer: IWarehouseTransfer;
|
warehouseTransfer: ModelObject<WarehouseTransfer>;
|
||||||
trx: Knex.Transaction;
|
trx: Knex.Transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IWarehouseTransferDeletePayload {
|
export interface IWarehouseTransferDeletePayload {
|
||||||
// tenantId: number;
|
oldWarehouseTransfer: ModelObject<WarehouseTransfer>;
|
||||||
oldWarehouseTransfer: IWarehouseTransfer;
|
|
||||||
trx: Knex.Transaction;
|
trx: Knex.Transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IWarehouseTransferDeletedPayload {
|
export interface IWarehouseTransferDeletedPayload {
|
||||||
// tenantId: number;
|
oldWarehouseTransfer: ModelObject<WarehouseTransfer>;
|
||||||
warehouseTransfer: IWarehouseTransfer;
|
|
||||||
oldWarehouseTransfer: IWarehouseTransfer;
|
|
||||||
trx: Knex.Transaction;
|
trx: Knex.Transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,40 +156,33 @@ export interface IWarehousesActivatedPayload {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface IWarehouseMarkAsPrimaryPayload {
|
export interface IWarehouseMarkAsPrimaryPayload {
|
||||||
// tenantId: number;
|
|
||||||
oldWarehouse: Warehouse;
|
oldWarehouse: Warehouse;
|
||||||
trx: Knex.Transaction;
|
trx: Knex.Transaction;
|
||||||
}
|
}
|
||||||
export interface IWarehouseMarkedAsPrimaryPayload {
|
export interface IWarehouseMarkedAsPrimaryPayload {
|
||||||
// tenantId: number;
|
|
||||||
oldWarehouse: Warehouse;
|
oldWarehouse: Warehouse;
|
||||||
markedWarehouse: Warehouse;
|
markedWarehouse: Warehouse;
|
||||||
trx: Knex.Transaction;
|
trx: Knex.Transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IWarehouseTransferInitiatePayload {
|
export interface IWarehouseTransferInitiatePayload {
|
||||||
// tenantId: number;
|
oldWarehouseTransfer: ModelObject<WarehouseTransfer>;
|
||||||
oldWarehouseTransfer: IWarehouseTransfer;
|
|
||||||
trx: Knex.Transaction;
|
trx: Knex.Transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export interface IWarehouseTransferInitiatedPayload {
|
export interface IWarehouseTransferInitiatedPayload {
|
||||||
// tenantId: number;
|
warehouseTransfer: ModelObject<WarehouseTransfer>;
|
||||||
warehouseTransfer: IWarehouseTransfer;
|
oldWarehouseTransfer: ModelObject<WarehouseTransfer>;
|
||||||
oldWarehouseTransfer: IWarehouseTransfer;
|
|
||||||
trx: Knex.Transaction;
|
trx: Knex.Transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IWarehouseTransferTransferingPayload {
|
export interface IWarehouseTransferTransferingPayload {
|
||||||
// tenantId: number;
|
oldWarehouseTransfer: ModelObject<WarehouseTransfer>;
|
||||||
oldWarehouseTransfer: IWarehouseTransfer;
|
|
||||||
trx: Knex.Transaction;
|
trx: Knex.Transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IWarehouseTransferTransferredPayload {
|
export interface IWarehouseTransferTransferredPayload {
|
||||||
// tenantId: number;
|
warehouseTransfer: ModelObject<WarehouseTransfer>;
|
||||||
warehouseTransfer: IWarehouseTransfer;
|
oldWarehouseTransfer: ModelObject<WarehouseTransfer>;
|
||||||
oldWarehouseTransfer: IWarehouseTransfer;
|
|
||||||
trx: Knex.Transaction;
|
trx: Knex.Transaction;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
import {
|
||||||
|
ICreateWarehouseTransferDTO,
|
||||||
|
IEditWarehouseTransferDTO,
|
||||||
|
IGetWarehousesTransfersFilterDTO,
|
||||||
|
} from '@/modules/Warehouses/Warehouse.types';
|
||||||
|
import { CreateWarehouseTransfer } from './commands/CreateWarehouseTransfer';
|
||||||
|
import { DeleteWarehouseTransfer } from './commands/DeleteWarehouseTransfer';
|
||||||
|
import { EditWarehouseTransfer } from './commands/EditWarehouseTransfer';
|
||||||
|
import { GetWarehouseTransfer } from './queries/GetWarehouseTransfer';
|
||||||
|
import { GetWarehouseTransfers } from './queries/GetWarehouseTransfers';
|
||||||
|
import { InitiateWarehouseTransfer } from './commands/InitiateWarehouseTransfer';
|
||||||
|
import { TransferredWarehouseTransfer } from './commands/TransferredWarehouseTransfer';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { WarehouseTransfer } from './models/WarehouseTransfer';
|
||||||
|
import { ModelObject } from 'objection';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class WarehouseTransferApplication {
|
||||||
|
constructor(
|
||||||
|
private readonly createWarehouseTransferService: CreateWarehouseTransfer,
|
||||||
|
private readonly editWarehouseTransferService: EditWarehouseTransfer,
|
||||||
|
private readonly deleteWarehouseTransferService: DeleteWarehouseTransfer,
|
||||||
|
private readonly getWarehouseTransferService: GetWarehouseTransfer,
|
||||||
|
private readonly getWarehousesTransfersService: GetWarehouseTransfers,
|
||||||
|
private readonly initiateWarehouseTransferService: InitiateWarehouseTransfer,
|
||||||
|
private readonly transferredWarehouseTransferService: TransferredWarehouseTransfer,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a warehouse transfer transaction.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {ICreateWarehouseTransferDTO} createWarehouseTransferDTO
|
||||||
|
* @returns {}
|
||||||
|
*/
|
||||||
|
public createWarehouseTransfer = (
|
||||||
|
createWarehouseTransferDTO: ICreateWarehouseTransferDTO,
|
||||||
|
): Promise<ModelObject<WarehouseTransfer>> => {
|
||||||
|
return this.createWarehouseTransferService.createWarehouseTransfer(
|
||||||
|
createWarehouseTransferDTO,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edits warehouse transfer transaction.
|
||||||
|
* @param {number} tenantId -
|
||||||
|
* @param {number} warehouseTransferId - number
|
||||||
|
* @param {IEditWarehouseTransferDTO} editWarehouseTransferDTO
|
||||||
|
*/
|
||||||
|
public editWarehouseTransfer = (
|
||||||
|
warehouseTransferId: number,
|
||||||
|
editWarehouseTransferDTO: IEditWarehouseTransferDTO,
|
||||||
|
): Promise<ModelObject<WarehouseTransfer>> => {
|
||||||
|
return this.editWarehouseTransferService.editWarehouseTransfer(
|
||||||
|
warehouseTransferId,
|
||||||
|
editWarehouseTransferDTO,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes warehouse transfer transaction.
|
||||||
|
* @param {number} warehouseTransferId
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
public deleteWarehouseTransfer = (
|
||||||
|
warehouseTransferId: number,
|
||||||
|
): Promise<void> => {
|
||||||
|
return this.deleteWarehouseTransferService.deleteWarehouseTransfer(
|
||||||
|
warehouseTransferId,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves warehouse transfer transaction.
|
||||||
|
* @param {number} warehouseTransferId
|
||||||
|
* @returns {Promise<IWarehouseTransfer>}
|
||||||
|
*/
|
||||||
|
public getWarehouseTransfer = (
|
||||||
|
warehouseTransferId: number,
|
||||||
|
): Promise<ModelObject<WarehouseTransfer>> => {
|
||||||
|
return this.getWarehouseTransferService.getWarehouseTransfer(
|
||||||
|
warehouseTransferId,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves warehouses trans
|
||||||
|
* @param {IGetWarehousesTransfersFilterDTO} filterDTO
|
||||||
|
* @returns {Promise<IWarehouseTransfer>}
|
||||||
|
*/
|
||||||
|
public getWarehousesTransfers = (
|
||||||
|
filterDTO: IGetWarehousesTransfersFilterDTO,
|
||||||
|
) => {
|
||||||
|
return this.getWarehousesTransfersService.getWarehouseTransfers(filterDTO);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks the warehouse transfer order as transfered.
|
||||||
|
* @param {number} warehouseTransferId
|
||||||
|
* @returns {Promise<IWarehouseTransfer>}
|
||||||
|
*/
|
||||||
|
public transferredWarehouseTransfer = (
|
||||||
|
warehouseTransferId: number,
|
||||||
|
): Promise<ModelObject<WarehouseTransfer>> => {
|
||||||
|
return this.transferredWarehouseTransferService.transferredWarehouseTransfer(
|
||||||
|
warehouseTransferId,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks the warehouse transfer order as initiated.
|
||||||
|
* @param {number} warehouseTransferId
|
||||||
|
* @returns {Promise<IWarehouseTransfer>}
|
||||||
|
*/
|
||||||
|
public initiateWarehouseTransfer = (
|
||||||
|
warehouseTransferId: number,
|
||||||
|
): Promise<ModelObject<WarehouseTransfer>> => {
|
||||||
|
return this.initiateWarehouseTransferService.initiateWarehouseTransfer(
|
||||||
|
warehouseTransferId,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
import {
|
||||||
|
Controller,
|
||||||
|
Post,
|
||||||
|
Put,
|
||||||
|
Get,
|
||||||
|
Delete,
|
||||||
|
Body,
|
||||||
|
Param,
|
||||||
|
Query,
|
||||||
|
Inject,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { WarehouseTransferApplication } from './WarehouseTransferApplication';
|
||||||
|
import {
|
||||||
|
ICreateWarehouseTransferDTO,
|
||||||
|
IEditWarehouseTransferDTO,
|
||||||
|
} from '@/modules/Warehouses/Warehouse.types';
|
||||||
|
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
||||||
|
import { PublicRoute } from '../Auth/Jwt.guard';
|
||||||
|
|
||||||
|
@Controller('warehouse-transfers')
|
||||||
|
@ApiTags('warehouse-transfers')
|
||||||
|
@PublicRoute()
|
||||||
|
export class WarehouseTransfersController {
|
||||||
|
/**
|
||||||
|
* @param {WarehouseTransferApplication} warehouseTransferApplication - Warehouse transfer application.
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
@Inject(WarehouseTransferApplication)
|
||||||
|
private readonly warehouseTransferApplication: WarehouseTransferApplication,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new warehouse transfer transaction.
|
||||||
|
*/
|
||||||
|
@Post()
|
||||||
|
@ApiOperation({ summary: 'Create a new warehouse transfer transaction.' })
|
||||||
|
@ApiResponse({
|
||||||
|
status: 200,
|
||||||
|
description:
|
||||||
|
'The warehouse transfer transaction has been created successfully.',
|
||||||
|
})
|
||||||
|
async createWarehouseTransfer(
|
||||||
|
@Body() createWarehouseTransferDTO: ICreateWarehouseTransferDTO,
|
||||||
|
) {
|
||||||
|
const warehouse =
|
||||||
|
await this.warehouseTransferApplication.createWarehouseTransfer(
|
||||||
|
createWarehouseTransferDTO,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: warehouse.id,
|
||||||
|
message:
|
||||||
|
'The warehouse transfer transaction has been created successfully.',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edits warehouse transfer transaction.
|
||||||
|
*/
|
||||||
|
@Post(':id')
|
||||||
|
@ApiOperation({ summary: 'Edit the given warehouse transfer transaction.' })
|
||||||
|
@ApiResponse({
|
||||||
|
status: 200,
|
||||||
|
description:
|
||||||
|
'The warehouse transfer transaction has been edited successfully.',
|
||||||
|
})
|
||||||
|
async editWarehouseTransfer(
|
||||||
|
@Param('id') id: number,
|
||||||
|
@Body() editWarehouseTransferDTO: IEditWarehouseTransferDTO,
|
||||||
|
) {
|
||||||
|
const warehouseTransfer =
|
||||||
|
await this.warehouseTransferApplication.editWarehouseTransfer(
|
||||||
|
id,
|
||||||
|
editWarehouseTransferDTO,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: warehouseTransfer.id,
|
||||||
|
message:
|
||||||
|
'The warehouse transfer transaction has been edited successfully.',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiates the warehouse transfer.
|
||||||
|
*/
|
||||||
|
@Put(':id/initiate')
|
||||||
|
@ApiOperation({ summary: 'Initiate the given warehouse transfer.' })
|
||||||
|
@ApiResponse({
|
||||||
|
status: 200,
|
||||||
|
description: 'The warehouse transfer has been initiated successfully.',
|
||||||
|
})
|
||||||
|
async initiateTransfer(@Param('id') id: number) {
|
||||||
|
await this.warehouseTransferApplication.initiateWarehouseTransfer(id);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
message: 'The given warehouse transfer has been initialized.',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks the given warehouse transfer as transferred.
|
||||||
|
*/
|
||||||
|
@Put(':id/transferred')
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'Mark the given warehouse transfer as transferred.',
|
||||||
|
})
|
||||||
|
@ApiResponse({
|
||||||
|
status: 200,
|
||||||
|
description:
|
||||||
|
'The warehouse transfer has been marked as transferred successfully.',
|
||||||
|
})
|
||||||
|
async deliverTransfer(@Param('id') id: number) {
|
||||||
|
await this.warehouseTransferApplication.transferredWarehouseTransfer(id);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
message: 'The given warehouse transfer has been delivered.',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves warehouse transfer transactions with pagination.
|
||||||
|
*/
|
||||||
|
@Get()
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'Retrieve warehouse transfer transactions with pagination.',
|
||||||
|
})
|
||||||
|
@ApiResponse({
|
||||||
|
status: 200,
|
||||||
|
description:
|
||||||
|
'The warehouse transfer transactions have been retrieved successfully.',
|
||||||
|
})
|
||||||
|
async getWarehousesTransfers(@Query() query: any) {
|
||||||
|
const { warehousesTransfers, pagination, filter } =
|
||||||
|
await this.warehouseTransferApplication.getWarehousesTransfers(query);
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: warehousesTransfers,
|
||||||
|
pagination,
|
||||||
|
filter,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves warehouse transfer transaction details.
|
||||||
|
*/
|
||||||
|
@Get(':id')
|
||||||
|
@ApiOperation({ summary: 'Retrieve warehouse transfer transaction details.' })
|
||||||
|
@ApiResponse({
|
||||||
|
status: 200,
|
||||||
|
description:
|
||||||
|
'The warehouse transfer transaction details have been retrieved successfully.',
|
||||||
|
})
|
||||||
|
async getWarehouseTransfer(@Param('id') id: number) {
|
||||||
|
const warehouseTransfer =
|
||||||
|
await this.warehouseTransferApplication.getWarehouseTransfer(id);
|
||||||
|
|
||||||
|
return { data: warehouseTransfer };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the given warehouse transfer transaction.
|
||||||
|
*/
|
||||||
|
@Delete(':id')
|
||||||
|
@ApiOperation({ summary: 'Delete the given warehouse transfer transaction.' })
|
||||||
|
@ApiResponse({
|
||||||
|
status: 200,
|
||||||
|
description:
|
||||||
|
'The warehouse transfer transaction has been deleted successfully.',
|
||||||
|
})
|
||||||
|
async deleteWarehouseTransfer(@Param('id') id: number) {
|
||||||
|
await this.warehouseTransferApplication.deleteWarehouseTransfer(id);
|
||||||
|
|
||||||
|
return {
|
||||||
|
message:
|
||||||
|
'The warehouse transfer transaction has been deleted successfully.',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { CreateWarehouseTransfer } from './commands/CreateWarehouseTransfer';
|
||||||
|
import { EditWarehouseTransfer } from './commands/EditWarehouseTransfer';
|
||||||
|
import { DeleteWarehouseTransfer } from './commands/DeleteWarehouseTransfer';
|
||||||
|
import { GetWarehouseTransfer } from './queries/GetWarehouseTransfer';
|
||||||
|
import { GetWarehouseTransfers } from './queries/GetWarehouseTransfers';
|
||||||
|
import { WarehouseTransferApplication } from './WarehouseTransferApplication';
|
||||||
|
import { WarehouseTransfersController } from './WarehouseTransfers.controller';
|
||||||
|
import { WarehouseTransferInventoryTransactions } from './commands/WarehouseTransferWriteInventoryTransactions';
|
||||||
|
import { WarehouseTransferAutoIncrement } from './commands/WarehouseTransferAutoIncrement';
|
||||||
|
import { WarehouseTransferAutoIncrementSubscriber } from './susbcribers/WarehouseTransferAutoIncrementSubscriber';
|
||||||
|
import { WarehouseTransferInventoryTransactionsSubscriber } from './susbcribers/WarehouseTransferInventoryTransactionsSubscriber';
|
||||||
|
import { InitiateWarehouseTransfer } from './commands/InitiateWarehouseTransfer';
|
||||||
|
import { TransferredWarehouseTransfer } from './commands/TransferredWarehouseTransfer';
|
||||||
|
import { CommandWarehouseTransfer } from './commands/CommandWarehouseTransfer';
|
||||||
|
import { ItemsModule } from '../Items/items.module';
|
||||||
|
import { InventoryCostModule } from '../InventoryCost/InventoryCost.module';
|
||||||
|
import { RegisterTenancyModel } from '../Tenancy/TenancyModels/Tenancy.module';
|
||||||
|
import { WarehouseTransfer } from './models/WarehouseTransfer';
|
||||||
|
import { WarehouseTransferEntry } from './models/WarehouseTransferEntry';
|
||||||
|
import { DynamicListModule } from '../DynamicListing/DynamicList.module';
|
||||||
|
import { AutoIncrementOrdersModule } from '../AutoIncrementOrders/AutoIncrementOrders.module';
|
||||||
|
|
||||||
|
const models = [
|
||||||
|
RegisterTenancyModel(WarehouseTransfer),
|
||||||
|
RegisterTenancyModel(WarehouseTransferEntry),
|
||||||
|
];
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [
|
||||||
|
ItemsModule,
|
||||||
|
InventoryCostModule,
|
||||||
|
DynamicListModule,
|
||||||
|
AutoIncrementOrdersModule,
|
||||||
|
...models,
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
WarehouseTransferApplication,
|
||||||
|
CreateWarehouseTransfer,
|
||||||
|
EditWarehouseTransfer,
|
||||||
|
DeleteWarehouseTransfer,
|
||||||
|
GetWarehouseTransfer,
|
||||||
|
GetWarehouseTransfers,
|
||||||
|
WarehouseTransferInventoryTransactions,
|
||||||
|
WarehouseTransferAutoIncrement,
|
||||||
|
WarehouseTransferAutoIncrementSubscriber,
|
||||||
|
WarehouseTransferInventoryTransactionsSubscriber,
|
||||||
|
TransferredWarehouseTransfer,
|
||||||
|
InitiateWarehouseTransfer,
|
||||||
|
CommandWarehouseTransfer,
|
||||||
|
],
|
||||||
|
exports: [...models],
|
||||||
|
controllers: [WarehouseTransfersController],
|
||||||
|
})
|
||||||
|
export class WarehousesTransfersModule {}
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
import { ERRORS } from '../constants';
|
||||||
|
import { WarehouseTransfer } from '../models/WarehouseTransfer';
|
||||||
|
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||||
|
import { ItemsEntriesService } from '@/modules/Items/ItemsEntries.service';
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { ServiceError } from '@/modules/Items/ServiceError';
|
||||||
|
import { ModelObject } from 'objection';
|
||||||
|
import { Item } from '@/modules/Items/models/Item';
|
||||||
|
import {
|
||||||
|
ICreateWarehouseTransferDTO,
|
||||||
|
IEditWarehouseTransferDTO,
|
||||||
|
} from '@/modules/Warehouses/Warehouse.types';
|
||||||
|
import { Warehouse } from '@/modules/Warehouses/models/Warehouse.model';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CommandWarehouseTransfer {
|
||||||
|
constructor(
|
||||||
|
@Inject(Warehouse.name)
|
||||||
|
private readonly warehouseModel: TenantModelProxy<typeof Warehouse>,
|
||||||
|
|
||||||
|
@Inject(WarehouseTransfer.name)
|
||||||
|
private readonly warehouseTransferModel: TenantModelProxy<
|
||||||
|
typeof WarehouseTransfer
|
||||||
|
>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {WarehouseTransfer} warehouseTransfer
|
||||||
|
*/
|
||||||
|
throwIfTransferNotFound = (warehouseTransfer: WarehouseTransfer) => {
|
||||||
|
if (!warehouseTransfer) {
|
||||||
|
throw new ServiceError(ERRORS.WAREHOUSE_TRANSFER_NOT_FOUND);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {number} branchId
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async getWarehouseTransferOrThrowNotFound(branchId: number) {
|
||||||
|
const foundTransfer = await this.warehouseTransferModel()
|
||||||
|
.query()
|
||||||
|
.findById(branchId);
|
||||||
|
|
||||||
|
if (!foundTransfer) {
|
||||||
|
throw new ServiceError(ERRORS.WAREHOUSE_TRANSFER_NOT_FOUND);
|
||||||
|
}
|
||||||
|
return foundTransfer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the from/to warehouses should not be the same.
|
||||||
|
* @param {ICreateWarehouseTransferDTO|IEditWarehouseTransferDTO} warehouseTransferDTO
|
||||||
|
*/
|
||||||
|
validateWarehouseFromToNotSame(
|
||||||
|
warehouseTransferDTO:
|
||||||
|
| ICreateWarehouseTransferDTO
|
||||||
|
| IEditWarehouseTransferDTO,
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
warehouseTransferDTO.fromWarehouseId ===
|
||||||
|
warehouseTransferDTO.toWarehouseId
|
||||||
|
) {
|
||||||
|
throw new ServiceError(ERRORS.WAREHOUSES_TRANSFER_SHOULD_NOT_BE_SAME);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates entries items should be inventory.
|
||||||
|
* @param {IItem[]} items
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
validateItemsShouldBeInventory = (
|
||||||
|
items: ModelObject<Item>[],
|
||||||
|
): void => {
|
||||||
|
const nonInventoryItems = items.filter((item) => item.type !== 'inventory');
|
||||||
|
|
||||||
|
if (nonInventoryItems.length > 0) {
|
||||||
|
throw new ServiceError(
|
||||||
|
ERRORS.WAREHOUSE_TRANSFER_ITEMS_SHOULD_BE_INVENTORY,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {number} fromWarehouseId
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
getToWarehouseOrThrow = async (fromWarehouseId: number) => {
|
||||||
|
const warehouse = await this.warehouseModel()
|
||||||
|
.query()
|
||||||
|
.findById(fromWarehouseId);
|
||||||
|
|
||||||
|
if (!warehouse) {
|
||||||
|
throw new ServiceError(ERRORS.TO_WAREHOUSE_NOT_FOUND);
|
||||||
|
}
|
||||||
|
return warehouse;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {number} fromWarehouseId
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
getFromWarehouseOrThrow = async (fromWarehouseId: number) => {
|
||||||
|
const warehouse = await this.warehouseModel()
|
||||||
|
.query()
|
||||||
|
.findById(fromWarehouseId);
|
||||||
|
|
||||||
|
if (!warehouse) {
|
||||||
|
throw new ServiceError(ERRORS.FROM_WAREHOUSE_NOT_FOUND);
|
||||||
|
}
|
||||||
|
return warehouse;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,198 @@
|
|||||||
|
import { Knex } from 'knex';
|
||||||
|
import { omit, get, isNumber } from 'lodash';
|
||||||
|
import * as R from 'ramda';
|
||||||
|
import {
|
||||||
|
ICreateWarehouseTransferDTO,
|
||||||
|
IWarehouseTransferCreate,
|
||||||
|
IWarehouseTransferCreated,
|
||||||
|
IWarehouseTransferEntryDTO,
|
||||||
|
} from '@/modules/Warehouses/Warehouse.types';
|
||||||
|
import { CommandWarehouseTransfer } from './CommandWarehouseTransfer';
|
||||||
|
import { WarehouseTransferAutoIncrement } from './WarehouseTransferAutoIncrement';
|
||||||
|
import { WarehouseTransfer } from '../models/WarehouseTransfer';
|
||||||
|
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||||
|
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
|
||||||
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
|
import { ItemsEntriesService } from '@/modules/Items/ItemsEntries.service';
|
||||||
|
import { InventoryItemCostService } from '@/modules/InventoryCost/commands/InventoryCosts.service';
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { events } from '@/common/events/events';
|
||||||
|
import { IInventoryItemCostMeta } from '@/modules/InventoryCost/types/InventoryCost.types';
|
||||||
|
import { ModelObject } from 'objection';
|
||||||
|
import { WarehouseTransferEntry } from '../models/WarehouseTransferEntry';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CreateWarehouseTransfer {
|
||||||
|
/**
|
||||||
|
* @param {UnitOfWork} uow - Unit of work.
|
||||||
|
* @param {EventEmitter2} eventPublisher - Event publisher.
|
||||||
|
* @param {ItemsEntriesService} itemsEntries - Items entries service.
|
||||||
|
* @param {InventoryItemCostService} inventoryItemCost - Inventory item cost service.
|
||||||
|
* @param {WarehouseTransferAutoIncrement} autoIncrementOrders - Warehouse transfer auto increment.
|
||||||
|
* @param {CommandWarehouseTransfer} commandWarehouseTransfer - Command warehouse transfer.
|
||||||
|
* @param {TenantModelProxy<typeof WarehouseTransfer>} warehouseTransferModel - Warehouse transfer model.
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
private readonly uow: UnitOfWork,
|
||||||
|
private readonly eventPublisher: EventEmitter2,
|
||||||
|
private readonly itemsEntries: ItemsEntriesService,
|
||||||
|
private readonly inventoryItemCost: InventoryItemCostService,
|
||||||
|
private readonly autoIncrementOrders: WarehouseTransferAutoIncrement,
|
||||||
|
private readonly commandWarehouseTransfer: CommandWarehouseTransfer,
|
||||||
|
|
||||||
|
@Inject(WarehouseTransfer.name)
|
||||||
|
private readonly warehouseTransferModel: TenantModelProxy<
|
||||||
|
typeof WarehouseTransfer
|
||||||
|
>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transformes the givne new warehouse transfer DTO to model.
|
||||||
|
* @param {ICreateWarehouseTransferDTO} warehouseTransferDTO
|
||||||
|
* @returns {IWarehouseTransfer}
|
||||||
|
*/
|
||||||
|
private transformDTOToModel = async (
|
||||||
|
warehouseTransferDTO: ICreateWarehouseTransferDTO,
|
||||||
|
): Promise<ModelObject<WarehouseTransfer>> => {
|
||||||
|
const entries = await this.transformEntries(
|
||||||
|
warehouseTransferDTO,
|
||||||
|
warehouseTransferDTO.entries,
|
||||||
|
);
|
||||||
|
// Retrieves the auto-increment the warehouse transfer number.
|
||||||
|
const autoNextNumber = this.autoIncrementOrders.getNextTransferNumber();
|
||||||
|
|
||||||
|
// Warehouse transfer order transaction number.
|
||||||
|
const transactionNumber =
|
||||||
|
warehouseTransferDTO.transactionNumber || autoNextNumber;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...omit(warehouseTransferDTO, ['transferDelivered', 'transferInitiated']),
|
||||||
|
transactionNumber,
|
||||||
|
...(warehouseTransferDTO.transferDelivered
|
||||||
|
? {
|
||||||
|
transferDeliveredAt: new Date(),
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
...(warehouseTransferDTO.transferDelivered ||
|
||||||
|
warehouseTransferDTO.transferInitiated
|
||||||
|
? {
|
||||||
|
transferInitiatedAt: new Date(),
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
entries,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assoc average cost to the entry that has no cost.
|
||||||
|
* @param {Promise<Map<number, IInventoryItemCostMeta>} inventoryItemsCostMap -
|
||||||
|
* @param {IWarehouseTransferEntryDTO} entry -
|
||||||
|
*/
|
||||||
|
private transformEntryAssocAverageCost = R.curry(
|
||||||
|
(
|
||||||
|
inventoryItemsCostMap: Map<number, IInventoryItemCostMeta>,
|
||||||
|
entry: IWarehouseTransferEntryDTO,
|
||||||
|
): IWarehouseTransferEntryDTO => {
|
||||||
|
const itemValuation = inventoryItemsCostMap.get(entry.itemId);
|
||||||
|
const itemCost = get(itemValuation, 'average', 0);
|
||||||
|
|
||||||
|
return isNumber(entry.cost) ? entry : R.assoc('cost', itemCost, entry);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transformes warehouse transfer entries.
|
||||||
|
* @param {ICreateWarehouseTransferDTO} warehouseTransferDTO
|
||||||
|
* @param {IWarehouseTransferEntryDTO[]} entries
|
||||||
|
* @returns {Promise<IWarehouseTransferEntryDTO[]>}
|
||||||
|
*/
|
||||||
|
public transformEntries = async (
|
||||||
|
warehouseTransferDTO: ICreateWarehouseTransferDTO,
|
||||||
|
entries: IWarehouseTransferEntryDTO[],
|
||||||
|
): Promise<ModelObject<WarehouseTransferEntry>[]> => {
|
||||||
|
const inventoryItemsIds = warehouseTransferDTO.entries.map((e) => e.itemId);
|
||||||
|
|
||||||
|
// Retrieves the inventory items valuation map.
|
||||||
|
const inventoryItemsCostMap =
|
||||||
|
await this.inventoryItemCost.getItemsInventoryValuation(
|
||||||
|
inventoryItemsIds,
|
||||||
|
warehouseTransferDTO.date,
|
||||||
|
);
|
||||||
|
// Assoc average cost to the entry.
|
||||||
|
const assocAverageCost = this.transformEntryAssocAverageCost(
|
||||||
|
inventoryItemsCostMap,
|
||||||
|
);
|
||||||
|
return entries.map((entry) => assocAverageCost(entry));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authorize warehouse transfer before creating.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {ICreateWarehouseTransferDTO} warehouseTransferDTO
|
||||||
|
*/
|
||||||
|
public authorize = async (
|
||||||
|
warehouseTransferDTO: ICreateWarehouseTransferDTO,
|
||||||
|
) => {
|
||||||
|
// Validate warehouse from and to should not be the same.
|
||||||
|
this.commandWarehouseTransfer.validateWarehouseFromToNotSame(
|
||||||
|
warehouseTransferDTO,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Retrieves the from warehouse or throw not found service error.
|
||||||
|
const fromWarehouse =
|
||||||
|
await this.commandWarehouseTransfer.getFromWarehouseOrThrow(
|
||||||
|
warehouseTransferDTO.fromWarehouseId,
|
||||||
|
);
|
||||||
|
// Retrieves the to warehouse or throw not found service error.
|
||||||
|
const toWarehouse =
|
||||||
|
await this.commandWarehouseTransfer.getToWarehouseOrThrow(
|
||||||
|
warehouseTransferDTO.toWarehouseId,
|
||||||
|
);
|
||||||
|
// Validates the not found entries items ids.
|
||||||
|
const items = await this.itemsEntries.validateItemsIdsExistance(
|
||||||
|
warehouseTransferDTO.entries,
|
||||||
|
);
|
||||||
|
// Validate the items entries should be inventory type.
|
||||||
|
this.commandWarehouseTransfer.validateItemsShouldBeInventory(items);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new warehouse transfer transaction.
|
||||||
|
* @param {ICreateWarehouseTransferDTO} warehouseDTO -
|
||||||
|
* @returns {Promise<ModelObject<WarehouseTransfer>>}
|
||||||
|
*/
|
||||||
|
public createWarehouseTransfer = async (
|
||||||
|
warehouseTransferDTO: ICreateWarehouseTransferDTO,
|
||||||
|
): Promise<ModelObject<WarehouseTransfer>> => {
|
||||||
|
// Authorize warehouse transfer before creating.
|
||||||
|
await this.authorize(warehouseTransferDTO);
|
||||||
|
|
||||||
|
// Transformes the warehouse transfer DTO to model.
|
||||||
|
const warehouseTransferModel =
|
||||||
|
await this.transformDTOToModel(warehouseTransferDTO);
|
||||||
|
|
||||||
|
// Create warehouse transfer under unit-of-work.
|
||||||
|
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
||||||
|
// Triggers `onWarehouseTransferCreate` event.
|
||||||
|
await this.eventPublisher.emitAsync(events.warehouseTransfer.onCreate, {
|
||||||
|
trx,
|
||||||
|
warehouseTransferDTO,
|
||||||
|
} as IWarehouseTransferCreate);
|
||||||
|
|
||||||
|
// Stores the warehouse transfer transaction graph to the storage.
|
||||||
|
const warehouseTransfer = await this.warehouseTransferModel()
|
||||||
|
.query(trx)
|
||||||
|
.upsertGraphAndFetch({
|
||||||
|
...warehouseTransferModel,
|
||||||
|
});
|
||||||
|
// Triggers `onWarehouseTransferCreated` event.
|
||||||
|
await this.eventPublisher.emitAsync(events.warehouseTransfer.onCreated, {
|
||||||
|
trx,
|
||||||
|
warehouseTransfer,
|
||||||
|
warehouseTransferDTO,
|
||||||
|
} as IWarehouseTransferCreated);
|
||||||
|
|
||||||
|
return warehouseTransfer;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
import { Knex } from 'knex';
|
||||||
|
import {
|
||||||
|
IWarehouseTransferDeletedPayload,
|
||||||
|
IWarehouseTransferDeletePayload,
|
||||||
|
} from '@/modules/Warehouses/Warehouse.types';
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
|
||||||
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
|
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||||
|
import { events } from '@/common/events/events';
|
||||||
|
import { WarehouseTransfer } from '../models/WarehouseTransfer';
|
||||||
|
import { WarehouseTransferEntry } from '../models/WarehouseTransferEntry';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class DeleteWarehouseTransfer {
|
||||||
|
/**
|
||||||
|
* @param {UnitOfWork} uow - Unit of work service.
|
||||||
|
* @param {EventEmitter2} eventPublisher - Event emitter service.
|
||||||
|
* @param {TenantModelProxy<WarehouseTransfer>} warehouseTransferModel - Warehouse transfer model.
|
||||||
|
* @param {TenantModelProxy<WarehouseTransferEntry>} warehouseTransferEntryModel - Warehouse transfer entry model.
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
private readonly uow: UnitOfWork,
|
||||||
|
private readonly eventPublisher: EventEmitter2,
|
||||||
|
|
||||||
|
@Inject(WarehouseTransfer.name)
|
||||||
|
private readonly warehouseTransferModel: TenantModelProxy<
|
||||||
|
typeof WarehouseTransfer
|
||||||
|
>,
|
||||||
|
@Inject(WarehouseTransferEntry.name)
|
||||||
|
private readonly warehouseTransferEntryModel: TenantModelProxy<
|
||||||
|
typeof WarehouseTransferEntry
|
||||||
|
>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes warehouse transfer transaction.
|
||||||
|
* @param {number} warehouseTransferId
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
public deleteWarehouseTransfer = async (
|
||||||
|
warehouseTransferId: number,
|
||||||
|
): Promise<void> => {
|
||||||
|
// Retrieve the old warehouse transfer or throw not found service error.
|
||||||
|
const oldWarehouseTransfer = await this.warehouseTransferModel()
|
||||||
|
.query()
|
||||||
|
.findById(warehouseTransferId)
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
|
// Deletes the warehouse transfer under unit-of-work envirement.
|
||||||
|
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
||||||
|
// Triggers `onWarehouseTransferCreate` event.
|
||||||
|
await this.eventPublisher.emitAsync(events.warehouseTransfer.onDelete, {
|
||||||
|
oldWarehouseTransfer,
|
||||||
|
trx,
|
||||||
|
} as IWarehouseTransferDeletePayload);
|
||||||
|
|
||||||
|
// Delete warehouse transfer entries.
|
||||||
|
await this.warehouseTransferEntryModel()
|
||||||
|
.query(trx)
|
||||||
|
.where('warehouseTransferId', warehouseTransferId)
|
||||||
|
.delete();
|
||||||
|
|
||||||
|
// Delete warehouse transfer.
|
||||||
|
await this.warehouseTransferModel()
|
||||||
|
.query(trx)
|
||||||
|
.findById(warehouseTransferId)
|
||||||
|
.delete();
|
||||||
|
|
||||||
|
// Triggers `onWarehouseTransferDeleted` event
|
||||||
|
await this.eventPublisher.emitAsync(events.warehouseTransfer.onDeleted, {
|
||||||
|
oldWarehouseTransfer,
|
||||||
|
trx,
|
||||||
|
} as IWarehouseTransferDeletedPayload);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
import { Knex } from 'knex';
|
||||||
|
import {
|
||||||
|
IEditWarehouseTransferDTO,
|
||||||
|
IWarehouseTransferEditPayload,
|
||||||
|
IWarehouseTransferEditedPayload,
|
||||||
|
} from '@/modules/Warehouses/Warehouse.types';
|
||||||
|
import { CommandWarehouseTransfer } from './CommandWarehouseTransfer';
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { TenantModelProxy } from '../../System/models/TenantBaseModel';
|
||||||
|
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
|
||||||
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
|
import { events } from '@/common/events/events';
|
||||||
|
import { WarehouseTransfer } from '../models/WarehouseTransfer';
|
||||||
|
import { ItemsEntriesService } from '@/modules/Items/ItemsEntries.service';
|
||||||
|
import { ModelObject } from 'objection';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class EditWarehouseTransfer {
|
||||||
|
constructor(
|
||||||
|
private readonly uow: UnitOfWork,
|
||||||
|
private readonly eventPublisher: EventEmitter2,
|
||||||
|
private readonly commandWarehouseTransfer: CommandWarehouseTransfer,
|
||||||
|
private readonly itemsEntries: ItemsEntriesService,
|
||||||
|
|
||||||
|
@Inject(WarehouseTransfer.name)
|
||||||
|
private readonly warehouseTransferModel: TenantModelProxy<
|
||||||
|
typeof WarehouseTransfer
|
||||||
|
>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edits warehouse transfer.
|
||||||
|
* @param {number} warehouseTransferId - Warehouse transfer id.
|
||||||
|
* @param {IEditWarehouseTransferDTO} editWarehouseDTO -
|
||||||
|
* @returns {Promise<ModelObject<WarehouseTransfer>>}
|
||||||
|
*/
|
||||||
|
public editWarehouseTransfer = async (
|
||||||
|
warehouseTransferId: number,
|
||||||
|
editWarehouseDTO: IEditWarehouseTransferDTO,
|
||||||
|
): Promise<ModelObject<WarehouseTransfer>> => {
|
||||||
|
// Retrieves the old warehouse transfer transaction.
|
||||||
|
const oldWarehouseTransfer = await this.warehouseTransferModel()
|
||||||
|
.query()
|
||||||
|
.findById(warehouseTransferId)
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
|
// Validate warehouse from and to should not be the same.
|
||||||
|
this.commandWarehouseTransfer.validateWarehouseFromToNotSame(
|
||||||
|
editWarehouseDTO,
|
||||||
|
);
|
||||||
|
// Retrieves the from warehouse or throw not found service error.
|
||||||
|
const fromWarehouse =
|
||||||
|
await this.commandWarehouseTransfer.getFromWarehouseOrThrow(
|
||||||
|
editWarehouseDTO.fromWarehouseId,
|
||||||
|
);
|
||||||
|
// Retrieves the to warehouse or throw not found service error.
|
||||||
|
const toWarehouse =
|
||||||
|
await this.commandWarehouseTransfer.getToWarehouseOrThrow(
|
||||||
|
editWarehouseDTO.toWarehouseId,
|
||||||
|
);
|
||||||
|
// Validates the not found entries items ids.
|
||||||
|
const items = await this.itemsEntries.validateItemsIdsExistance(
|
||||||
|
editWarehouseDTO.entries,
|
||||||
|
);
|
||||||
|
// Validate the items entries should be inventory type.
|
||||||
|
this.commandWarehouseTransfer.validateItemsShouldBeInventory(items);
|
||||||
|
|
||||||
|
// Edits warehouse transfer transaction under unit-of-work envirement.
|
||||||
|
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
||||||
|
// Triggers `onWarehouseTransferEdit` event.
|
||||||
|
await this.eventPublisher.emitAsync(events.warehouseTransfer.onEdit, {
|
||||||
|
editWarehouseDTO,
|
||||||
|
oldWarehouseTransfer,
|
||||||
|
trx,
|
||||||
|
} as IWarehouseTransferEditPayload);
|
||||||
|
|
||||||
|
// Updates warehouse transfer graph on the storage.
|
||||||
|
const warehouseTransfer = await this.warehouseTransferModel()
|
||||||
|
.query(trx)
|
||||||
|
.upsertGraphAndFetch({
|
||||||
|
id: warehouseTransferId,
|
||||||
|
...editWarehouseDTO,
|
||||||
|
});
|
||||||
|
// Triggers `onWarehouseTransferEdit` event
|
||||||
|
await this.eventPublisher.emitAsync(events.warehouseTransfer.onEdited, {
|
||||||
|
editWarehouseDTO,
|
||||||
|
warehouseTransfer,
|
||||||
|
oldWarehouseTransfer,
|
||||||
|
trx,
|
||||||
|
} as IWarehouseTransferEditedPayload);
|
||||||
|
|
||||||
|
return warehouseTransfer;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
import { Knex } from 'knex';
|
||||||
|
import {
|
||||||
|
IWarehouseTransferEditedPayload,
|
||||||
|
IWarehouseTransferInitiatedPayload,
|
||||||
|
IWarehouseTransferInitiatePayload,
|
||||||
|
} from '@/modules/Warehouses/Warehouse.types';
|
||||||
|
import { ERRORS } from '../constants';
|
||||||
|
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||||
|
import { WarehouseTransfer } from '../models/WarehouseTransfer';
|
||||||
|
import { Inject } from '@nestjs/common';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { ServiceError } from '@/modules/Items/ServiceError';
|
||||||
|
import { events } from '@/common/events/events';
|
||||||
|
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
|
||||||
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
|
import { ModelObject } from 'objection';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class InitiateWarehouseTransfer {
|
||||||
|
constructor(
|
||||||
|
private readonly uow: UnitOfWork,
|
||||||
|
private readonly eventPublisher: EventEmitter2,
|
||||||
|
|
||||||
|
@Inject(WarehouseTransfer.name)
|
||||||
|
private readonly warehouseTransferModel: TenantModelProxy<
|
||||||
|
typeof WarehouseTransfer
|
||||||
|
>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the given warehouse transfer not already initiated.
|
||||||
|
* @param {IWarehouseTransfer} warehouseTransfer
|
||||||
|
*/
|
||||||
|
private validateWarehouseTransferNotAlreadyInitiated = (
|
||||||
|
warehouseTransfer: ModelObject<WarehouseTransfer>,
|
||||||
|
) => {
|
||||||
|
if (warehouseTransfer.transferInitiatedAt) {
|
||||||
|
throw new ServiceError(ERRORS.WAREHOUSE_TRANSFER_ALREADY_INITIATED);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiate warehouse transfer.
|
||||||
|
* @param {number} warehouseTransferId
|
||||||
|
* @returns {Promise<IWarehouseTransfer>}
|
||||||
|
*/
|
||||||
|
public initiateWarehouseTransfer = async (
|
||||||
|
warehouseTransferId: number,
|
||||||
|
): Promise<ModelObject<WarehouseTransfer>> => {
|
||||||
|
// Retrieves the old warehouse transfer transaction.
|
||||||
|
const oldWarehouseTransfer = await this.warehouseTransferModel()
|
||||||
|
.query()
|
||||||
|
.findById(warehouseTransferId)
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
|
// Validate the given warehouse transfer not already initiated.
|
||||||
|
this.validateWarehouseTransferNotAlreadyInitiated(oldWarehouseTransfer);
|
||||||
|
|
||||||
|
// Edits warehouse transfer transaction under unit-of-work envirement.
|
||||||
|
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
||||||
|
// Triggers `onWarehouseTransferInitiate` event.
|
||||||
|
await this.eventPublisher.emitAsync(events.warehouseTransfer.onInitiate, {
|
||||||
|
oldWarehouseTransfer,
|
||||||
|
trx,
|
||||||
|
} as IWarehouseTransferInitiatePayload);
|
||||||
|
|
||||||
|
// Updates warehouse transfer graph on the storage.
|
||||||
|
const warehouseTransferUpdated = await this.warehouseTransferModel()
|
||||||
|
.query(trx)
|
||||||
|
.findById(warehouseTransferId)
|
||||||
|
.patch({
|
||||||
|
transferInitiatedAt: new Date(),
|
||||||
|
});
|
||||||
|
// Fetches the warehouse transfer with entries.
|
||||||
|
const warehouseTransfer = await this.warehouseTransferModel()
|
||||||
|
.query(trx)
|
||||||
|
.findById(warehouseTransferId)
|
||||||
|
.withGraphFetched('entries');
|
||||||
|
|
||||||
|
// Triggers `onWarehouseTransferEdit` event
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.warehouseTransfer.onInitiated,
|
||||||
|
{
|
||||||
|
warehouseTransfer,
|
||||||
|
oldWarehouseTransfer,
|
||||||
|
trx,
|
||||||
|
} as IWarehouseTransferInitiatedPayload,
|
||||||
|
);
|
||||||
|
return warehouseTransfer;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
import { Knex } from 'knex';
|
||||||
|
import {
|
||||||
|
IWarehouseTransferTransferingPayload,
|
||||||
|
IWarehouseTransferTransferredPayload,
|
||||||
|
} from '@/modules/Warehouses/Warehouse.types';
|
||||||
|
import { CommandWarehouseTransfer } from './CommandWarehouseTransfer';
|
||||||
|
import { ERRORS } from '../constants';
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { UnitOfWork } from '../../Tenancy/TenancyDB/UnitOfWork.service';
|
||||||
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
|
import { TenantModelProxy } from '../../System/models/TenantBaseModel';
|
||||||
|
import { WarehouseTransfer } from '../models/WarehouseTransfer';
|
||||||
|
import { ServiceError } from '../../Items/ServiceError';
|
||||||
|
import { events } from '@/common/events/events';
|
||||||
|
import { ModelObject } from 'objection';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class TransferredWarehouseTransfer {
|
||||||
|
constructor(
|
||||||
|
private readonly uow: UnitOfWork,
|
||||||
|
private readonly eventPublisher: EventEmitter2,
|
||||||
|
|
||||||
|
@Inject(WarehouseTransfer.name)
|
||||||
|
private readonly warehouseTransferModel: TenantModelProxy<
|
||||||
|
typeof WarehouseTransfer
|
||||||
|
>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the warehouse transfer not already transferred.
|
||||||
|
* @param {IWarehouseTransfer} warehouseTransfer
|
||||||
|
*/
|
||||||
|
private validateWarehouseTransferNotTransferred = (
|
||||||
|
warehouseTransfer: ModelObject<WarehouseTransfer>,
|
||||||
|
) => {
|
||||||
|
if (warehouseTransfer.transferDeliveredAt) {
|
||||||
|
throw new ServiceError(ERRORS.WAREHOUSE_TRANSFER_ALREADY_TRANSFERRED);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the warehouse transfer should be initiated.
|
||||||
|
* @param {IWarehouseTransfer} warehouseTransfer
|
||||||
|
*/
|
||||||
|
private validateWarehouseTranbsferShouldInitiated = (
|
||||||
|
warehouseTransfer: ModelObject<WarehouseTransfer>,
|
||||||
|
) => {
|
||||||
|
if (!warehouseTransfer.transferInitiatedAt) {
|
||||||
|
throw new ServiceError(ERRORS.WAREHOUSE_TRANSFER_NOT_INITIATED);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transferred warehouse transfer.
|
||||||
|
* @param {number} warehouseTransferId
|
||||||
|
* @returns {Promise<IWarehouseTransfer>}
|
||||||
|
*/
|
||||||
|
public transferredWarehouseTransfer = async (
|
||||||
|
warehouseTransferId: number,
|
||||||
|
): Promise<ModelObject<WarehouseTransfer>> => {
|
||||||
|
// Retrieves the old warehouse transfer transaction.
|
||||||
|
const oldWarehouseTransfer = await this.warehouseTransferModel()
|
||||||
|
.query()
|
||||||
|
.findById(warehouseTransferId)
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
|
// Validate the warehouse transfer not already transferred.
|
||||||
|
this.validateWarehouseTransferNotTransferred(oldWarehouseTransfer);
|
||||||
|
|
||||||
|
// Validate the warehouse transfer should be initiated.
|
||||||
|
this.validateWarehouseTranbsferShouldInitiated(oldWarehouseTransfer);
|
||||||
|
|
||||||
|
// Edits warehouse transfer transaction under unit-of-work envirement.
|
||||||
|
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
||||||
|
// Triggers `onWarehouseTransferInitiate` event.
|
||||||
|
await this.eventPublisher.emitAsync(events.warehouseTransfer.onTransfer, {
|
||||||
|
oldWarehouseTransfer,
|
||||||
|
trx,
|
||||||
|
} as IWarehouseTransferTransferingPayload);
|
||||||
|
|
||||||
|
// Updates warehouse transfer graph on the storage.
|
||||||
|
const warehouseTransferUpdated = await this.warehouseTransferModel()
|
||||||
|
.query(trx)
|
||||||
|
.findById(warehouseTransferId)
|
||||||
|
.patch({
|
||||||
|
transferDeliveredAt: new Date(),
|
||||||
|
});
|
||||||
|
// Fetches the warehouse transfer with entries.
|
||||||
|
const warehouseTransfer = await this.warehouseTransferModel()
|
||||||
|
.query(trx)
|
||||||
|
.findById(warehouseTransferId)
|
||||||
|
.withGraphFetched('entries');
|
||||||
|
|
||||||
|
// Triggers `onWarehouseTransferEdit` event
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.warehouseTransfer.onTransferred,
|
||||||
|
{
|
||||||
|
warehouseTransfer,
|
||||||
|
oldWarehouseTransfer,
|
||||||
|
trx,
|
||||||
|
} as IWarehouseTransferTransferredPayload,
|
||||||
|
);
|
||||||
|
return warehouseTransfer;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { AutoIncrementOrdersService } from '@/modules/AutoIncrementOrders/AutoIncrementOrders.service';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class WarehouseTransferAutoIncrement {
|
||||||
|
constructor(
|
||||||
|
private readonly autoIncrementOrdersService: AutoIncrementOrdersService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the next unique invoice number.
|
||||||
|
* @return {Promise<string>}
|
||||||
|
*/
|
||||||
|
public getNextTransferNumber(): Promise<string> {
|
||||||
|
return this.autoIncrementOrdersService.getNextTransactionNumber(
|
||||||
|
'warehouse_transfers',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increment the invoice next number.
|
||||||
|
*/
|
||||||
|
public incrementNextTransferNumber() {
|
||||||
|
return this.autoIncrementOrdersService.incrementSettingsNextNumber(
|
||||||
|
'warehouse_transfers',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
import { Knex } from 'knex';
|
||||||
|
import { IWarehouseTransferEntry } from '@/modules/Warehouses/Warehouse.types';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { InventoryTransactionsService } from '../../InventoryCost/commands/InventoryTransactions.service';
|
||||||
|
import { ModelObject } from 'objection';
|
||||||
|
import { WarehouseTransfer } from '../models/WarehouseTransfer';
|
||||||
|
import { InventoryTransaction } from '../../InventoryCost/models/InventoryTransaction';
|
||||||
|
import { WarehouseTransferEntry } from '../models/WarehouseTransferEntry';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class WarehouseTransferInventoryTransactions {
|
||||||
|
constructor(private readonly inventory: InventoryTransactionsService) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes all (initiate and transfer) inventory transactions.
|
||||||
|
* @param {ModelObject<WarehouseTransfer>} warehouseTransfer - Warehouse transfer.
|
||||||
|
* @param {Boolean} override - Override the inventory transactions.
|
||||||
|
* @param {Knex.Transaction} trx - Knex transcation.
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
public writeAllInventoryTransactions = async (
|
||||||
|
warehouseTransfer: ModelObject<WarehouseTransfer>,
|
||||||
|
override?: boolean,
|
||||||
|
trx?: Knex.Transaction,
|
||||||
|
): Promise<void> => {
|
||||||
|
const inventoryTransactions =
|
||||||
|
this.getWarehouseTransferInventoryTransactions(warehouseTransfer);
|
||||||
|
|
||||||
|
await this.inventory.recordInventoryTransactions(
|
||||||
|
inventoryTransactions,
|
||||||
|
override,
|
||||||
|
trx,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes initiate inventory transactions of warehouse transfer transaction.
|
||||||
|
* @param {ModelObject<WarehouseTransfer>} warehouseTransfer - Warehouse transfer.
|
||||||
|
* @param {boolean} override - Override the inventory transactions.
|
||||||
|
* @param {Knex.Transaction} trx - Knex transaction.
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
public writeInitiateInventoryTransactions = async (
|
||||||
|
warehouseTransfer: ModelObject<WarehouseTransfer>,
|
||||||
|
override?: boolean,
|
||||||
|
trx?: Knex.Transaction,
|
||||||
|
): Promise<void> => {
|
||||||
|
const inventoryTransactions =
|
||||||
|
this.getWarehouseFromTransferInventoryTransactions(warehouseTransfer);
|
||||||
|
|
||||||
|
await this.inventory.recordInventoryTransactions(
|
||||||
|
inventoryTransactions,
|
||||||
|
override,
|
||||||
|
trx,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes transferred inventory transaction of warehouse transfer transaction.
|
||||||
|
* @param {ModelObject<WarehouseTransfer>} warehouseTransfer - Warehouse transfer.
|
||||||
|
* @param {boolean} override - Override the inventory transactions.
|
||||||
|
* @param {Knex.Transaction} trx - Knex transaction.
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
public writeTransferredInventoryTransactions = async (
|
||||||
|
warehouseTransfer: ModelObject<WarehouseTransfer>,
|
||||||
|
override?: boolean,
|
||||||
|
trx?: Knex.Transaction,
|
||||||
|
): Promise<void> => {
|
||||||
|
const inventoryTransactions =
|
||||||
|
this.getWarehouseToTransferInventoryTransactions(warehouseTransfer);
|
||||||
|
|
||||||
|
await this.inventory.recordInventoryTransactions(
|
||||||
|
inventoryTransactions,
|
||||||
|
override,
|
||||||
|
trx,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverts warehouse transfer inventory transactions.
|
||||||
|
* @param {number} warehouseTransferId - Warehouse transfer id.
|
||||||
|
* @param {Knex.Transaction} trx - Knex transaction.
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
public revertInventoryTransactions = async (
|
||||||
|
warehouseTransferId: number,
|
||||||
|
trx?: Knex.Transaction,
|
||||||
|
): Promise<void> => {
|
||||||
|
await this.inventory.deleteInventoryTransactions(
|
||||||
|
warehouseTransferId,
|
||||||
|
'WarehouseTransfer',
|
||||||
|
trx,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the inventory transactions of the given warehouse transfer.
|
||||||
|
* @param {IWarehouseTransfer} warehouseTransfer
|
||||||
|
* @returns {IInventoryTransaction[]}
|
||||||
|
*/
|
||||||
|
private getWarehouseFromTransferInventoryTransactions = (
|
||||||
|
warehouseTransfer: ModelObject<WarehouseTransfer>,
|
||||||
|
) => {
|
||||||
|
const commonEntry = {
|
||||||
|
date: warehouseTransfer.date,
|
||||||
|
transactionType: 'WarehouseTransfer',
|
||||||
|
transactionId: warehouseTransfer.id,
|
||||||
|
};
|
||||||
|
return warehouseTransfer.entries.map(
|
||||||
|
(entry: ModelObject<WarehouseTransferEntry>) => ({
|
||||||
|
...commonEntry,
|
||||||
|
entryId: entry.id,
|
||||||
|
itemId: entry.itemId,
|
||||||
|
quantity: entry.quantity,
|
||||||
|
rate: entry.cost,
|
||||||
|
direction: 'OUT',
|
||||||
|
warehouseId: warehouseTransfer.fromWarehouseId,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the inventory transactions of the given warehouse transfer.
|
||||||
|
* @param {ModelObject<WarehouseTransfer>} warehouseTransfer - Warehouse transfer.
|
||||||
|
* @returns {IInventoryTransaction[]}
|
||||||
|
*/
|
||||||
|
private getWarehouseToTransferInventoryTransactions = (
|
||||||
|
warehouseTransfer: ModelObject<WarehouseTransfer>,
|
||||||
|
) => {
|
||||||
|
const commonEntry = {
|
||||||
|
date: warehouseTransfer.date,
|
||||||
|
transactionType: 'WarehouseTransfer',
|
||||||
|
transactionId: warehouseTransfer.id,
|
||||||
|
};
|
||||||
|
return warehouseTransfer.entries.map(
|
||||||
|
(entry: ModelObject<WarehouseTransferEntry>) => ({
|
||||||
|
...commonEntry,
|
||||||
|
entryId: entry.id,
|
||||||
|
itemId: entry.itemId,
|
||||||
|
quantity: entry.quantity,
|
||||||
|
rate: entry.cost,
|
||||||
|
direction: 'IN',
|
||||||
|
warehouseId: warehouseTransfer.toWarehouseId,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the inventory transactions of the given warehouse transfer.
|
||||||
|
* @param {ModelObject<WarehouseTransfer>} warehouseTransfer - Warehouse transfer.
|
||||||
|
* @returns {IInventoryTransaction[]}
|
||||||
|
*/
|
||||||
|
private getWarehouseTransferInventoryTransactions = (
|
||||||
|
warehouseTransfer: ModelObject<WarehouseTransfer>,
|
||||||
|
) => {
|
||||||
|
// Retrieve the to inventory transactions of warehouse transfer.
|
||||||
|
const toTransactions =
|
||||||
|
this.getWarehouseToTransferInventoryTransactions(warehouseTransfer);
|
||||||
|
|
||||||
|
// Retrieve the from inventory transactions of warehouse transfer.
|
||||||
|
const fromTransactions =
|
||||||
|
this.getWarehouseFromTransferInventoryTransactions(warehouseTransfer);
|
||||||
|
|
||||||
|
return [...toTransactions, ...fromTransactions];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
export const ERRORS = {
|
||||||
|
WAREHOUSE_TRANSFER_NOT_FOUND: 'WAREHOUSE_TRANSFER_NOT_FOUND',
|
||||||
|
WAREHOUSES_TRANSFER_SHOULD_NOT_BE_SAME:
|
||||||
|
'WAREHOUSES_TRANSFER_SHOULD_NOT_BE_SAME',
|
||||||
|
|
||||||
|
FROM_WAREHOUSE_NOT_FOUND: 'FROM_WAREHOUSE_NOT_FOUND',
|
||||||
|
TO_WAREHOUSE_NOT_FOUND: 'TO_WAREHOUSE_NOT_FOUND',
|
||||||
|
WAREHOUSE_TRANSFER_ITEMS_SHOULD_BE_INVENTORY:
|
||||||
|
'WAREHOUSE_TRANSFER_ITEMS_SHOULD_BE_INVENTORY',
|
||||||
|
|
||||||
|
WAREHOUSE_TRANSFER_ALREADY_TRANSFERRED:
|
||||||
|
'WAREHOUSE_TRANSFER_ALREADY_TRANSFERRED',
|
||||||
|
|
||||||
|
WAREHOUSE_TRANSFER_ALREADY_INITIATED: 'WAREHOUSE_TRANSFER_ALREADY_INITIATED',
|
||||||
|
WAREHOUSE_TRANSFER_NOT_INITIATED: 'WAREHOUSE_TRANSFER_NOT_INITIATED',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Warehouse transfers default views.
|
||||||
|
export const DEFAULT_VIEWS = [
|
||||||
|
{
|
||||||
|
name: 'warehouse_transfer.view.draft.name',
|
||||||
|
slug: 'draft',
|
||||||
|
rolesLogicExpression: '1',
|
||||||
|
roles: [
|
||||||
|
{ index: 1, fieldKey: 'status', comparator: 'equals', value: 'draft' },
|
||||||
|
],
|
||||||
|
columns: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'warehouse_transfer.view.in_transit.name',
|
||||||
|
slug: 'in-transit',
|
||||||
|
rolesLogicExpression: '1',
|
||||||
|
roles: [
|
||||||
|
{
|
||||||
|
index: 1,
|
||||||
|
fieldKey: 'status',
|
||||||
|
comparator: 'equals',
|
||||||
|
value: 'in-transit',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
columns: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'warehouse_transfer.view.transferred.name',
|
||||||
|
slug: 'transferred',
|
||||||
|
rolesLogicExpression: '1',
|
||||||
|
roles: [
|
||||||
|
{
|
||||||
|
index: 1,
|
||||||
|
fieldKey: 'status',
|
||||||
|
comparator: 'equals',
|
||||||
|
value: 'tansferred',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
columns: [],
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
import { Model, mixin } from 'objection';
|
||||||
|
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
|
||||||
|
import { Warehouse } from '@/modules/Warehouses/models/Warehouse.model';
|
||||||
|
import { WarehouseTransferEntry } from './WarehouseTransferEntry';
|
||||||
|
|
||||||
|
export class WarehouseTransfer extends TenantBaseModel {
|
||||||
|
public date!: Date;
|
||||||
|
public transferInitiatedAt!: Date;
|
||||||
|
public transferDeliveredAt!: Date;
|
||||||
|
public fromWarehouseId!: number;
|
||||||
|
public toWarehouseId!: number;
|
||||||
|
|
||||||
|
public entries!: WarehouseTransferEntry[];
|
||||||
|
public fromWarehouse!: Warehouse;
|
||||||
|
public toWarehouse!: Warehouse;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Table name.
|
||||||
|
*/
|
||||||
|
static get tableName() {
|
||||||
|
return 'warehouses_transfers';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timestamps columns.
|
||||||
|
*/
|
||||||
|
get timestamps() {
|
||||||
|
return ['created_at', 'updated_at'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual attributes.
|
||||||
|
*/
|
||||||
|
static get virtualAttributes() {
|
||||||
|
return ['isInitiated', 'isTransferred'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detarmines whether the warehouse transfer initiated.
|
||||||
|
* @retruns {boolean}
|
||||||
|
*/
|
||||||
|
get isInitiated() {
|
||||||
|
return !!this.transferInitiatedAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detarmines whether the warehouse transfer transferred.
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
get isTransferred() {
|
||||||
|
return !!this.transferDeliveredAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model modifiers.
|
||||||
|
*/
|
||||||
|
static get modifiers() {
|
||||||
|
return {
|
||||||
|
filterByDraft(query) {
|
||||||
|
query.whereNull('transferInitiatedAt');
|
||||||
|
query.whereNull('transferDeliveredAt');
|
||||||
|
},
|
||||||
|
filterByInTransit(query) {
|
||||||
|
query.whereNotNull('transferInitiatedAt');
|
||||||
|
query.whereNull('transferDeliveredAt');
|
||||||
|
},
|
||||||
|
filterByTransferred(query) {
|
||||||
|
query.whereNotNull('transferInitiatedAt');
|
||||||
|
query.whereNotNull('transferDeliveredAt');
|
||||||
|
},
|
||||||
|
filterByStatus(query, status) {
|
||||||
|
switch (status) {
|
||||||
|
case 'draft':
|
||||||
|
default:
|
||||||
|
return query.modify('filterByDraft');
|
||||||
|
case 'in-transit':
|
||||||
|
return query.modify('filterByInTransit');
|
||||||
|
case 'transferred':
|
||||||
|
return query.modify('filterByTransferred');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Relationship mapping.
|
||||||
|
*/
|
||||||
|
static get relationMappings() {
|
||||||
|
const { WarehouseTransferEntry } = require('./WarehouseTransferEntry');
|
||||||
|
const { Warehouse } = require('../../Warehouses/models/Warehouse.model');
|
||||||
|
|
||||||
|
return {
|
||||||
|
/**
|
||||||
|
* View model may has many columns.
|
||||||
|
*/
|
||||||
|
entries: {
|
||||||
|
relation: Model.HasManyRelation,
|
||||||
|
modelClass: WarehouseTransferEntry,
|
||||||
|
join: {
|
||||||
|
from: 'warehouses_transfers.id',
|
||||||
|
to: 'warehouses_transfers_entries.warehouseTransferId',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
fromWarehouse: {
|
||||||
|
relation: Model.BelongsToOneRelation,
|
||||||
|
modelClass: Warehouse,
|
||||||
|
join: {
|
||||||
|
from: 'warehouses_transfers.fromWarehouseId',
|
||||||
|
to: 'warehouses.id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
toWarehouse: {
|
||||||
|
relation: Model.BelongsToOneRelation,
|
||||||
|
modelClass: Warehouse,
|
||||||
|
join: {
|
||||||
|
from: 'warehouses_transfers.toWarehouseId',
|
||||||
|
to: 'warehouses.id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model settings.
|
||||||
|
*/
|
||||||
|
// static get meta() {
|
||||||
|
// return WarehouseTransferSettings;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Retrieve the default custom views, roles and columns.
|
||||||
|
// */
|
||||||
|
// static get defaultViews() {
|
||||||
|
// return DEFAULT_VIEWS;
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model search roles.
|
||||||
|
*/
|
||||||
|
static get searchRoles() {
|
||||||
|
return [
|
||||||
|
// { fieldKey: 'name', comparator: 'contains' },
|
||||||
|
// { condition: 'or', fieldKey: 'code', comparator: 'like' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import { Model } from 'objection';
|
||||||
|
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
|
||||||
|
import { WarehouseTransfer } from './WarehouseTransfer';
|
||||||
|
import { Item } from '@/modules/Items/models/Item';
|
||||||
|
|
||||||
|
export class WarehouseTransferEntry extends TenantBaseModel {
|
||||||
|
public warehouseTransferId!: number;
|
||||||
|
public itemId!: number;
|
||||||
|
public quantity!: number;
|
||||||
|
public cost!: number;
|
||||||
|
|
||||||
|
public warehouseTransfer!: WarehouseTransfer;
|
||||||
|
public item!: Item;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Table name.
|
||||||
|
*/
|
||||||
|
static get tableName() {
|
||||||
|
return 'warehouses_transfers_entries';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtual attributes.
|
||||||
|
*/
|
||||||
|
static get virtualAttributes() {
|
||||||
|
return ['total'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoice amount in local currency.
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
get total() {
|
||||||
|
return this.cost * this.quantity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Relationship mapping.
|
||||||
|
*/
|
||||||
|
static get relationMappings() {
|
||||||
|
const { Item } = require('../../Items/models/Item');
|
||||||
|
|
||||||
|
return {
|
||||||
|
item: {
|
||||||
|
relation: Model.BelongsToOneRelation,
|
||||||
|
modelClass: Item,
|
||||||
|
join: {
|
||||||
|
from: 'warehouses_transfers_entries.itemId',
|
||||||
|
to: 'items.id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { WarehouseTransferTransformer } from './WarehouseTransferTransfomer';
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
|
||||||
|
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||||
|
import { WarehouseTransfer } from '../models/WarehouseTransfer';
|
||||||
|
import { ModelObject } from 'objection';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class GetWarehouseTransfer {
|
||||||
|
constructor(
|
||||||
|
private readonly transformer: TransformerInjectable,
|
||||||
|
|
||||||
|
@Inject(WarehouseTransfer.name)
|
||||||
|
private readonly warehouseTransferModel: TenantModelProxy<
|
||||||
|
typeof WarehouseTransfer
|
||||||
|
>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the specific warehouse transfer transaction.
|
||||||
|
* @param {number} warehouseTransferId
|
||||||
|
* @param {IEditWarehouseTransferDTO} editWarehouseDTO
|
||||||
|
* @returns {Promise<IWarehouseTransfer>}
|
||||||
|
*/
|
||||||
|
public getWarehouseTransfer = async (
|
||||||
|
warehouseTransferId: number,
|
||||||
|
): Promise<ModelObject<WarehouseTransfer>> => {
|
||||||
|
// Retrieves the old warehouse transfer transaction.
|
||||||
|
const warehouseTransfer = await this.warehouseTransferModel()
|
||||||
|
.query()
|
||||||
|
.findById(warehouseTransferId)
|
||||||
|
.withGraphFetched('entries.item')
|
||||||
|
.withGraphFetched('fromWarehouse')
|
||||||
|
.withGraphFetched('toWarehouse')
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
|
// Retrieves the transfromed warehouse transfers.
|
||||||
|
return this.transformer.transform(
|
||||||
|
warehouseTransfer,
|
||||||
|
new WarehouseTransferTransformer(),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
import * as R from 'ramda';
|
||||||
|
import { WarehouseTransferTransformer } from './WarehouseTransferTransfomer';
|
||||||
|
import { IGetWarehousesTransfersFilterDTO } from '../../Warehouses/Warehouse.types';
|
||||||
|
import { TransformerInjectable } from '../../Transformer/TransformerInjectable.service';
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { DynamicListService } from '../../DynamicListing/DynamicList.service';
|
||||||
|
import { TenantModelProxy } from '../../System/models/TenantBaseModel';
|
||||||
|
import { WarehouseTransfer } from '../models/WarehouseTransfer';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class GetWarehouseTransfers {
|
||||||
|
constructor(
|
||||||
|
private readonly dynamicListService: DynamicListService,
|
||||||
|
private readonly transformer: TransformerInjectable,
|
||||||
|
|
||||||
|
@Inject(WarehouseTransfer.name)
|
||||||
|
private readonly warehouseTransferModel: TenantModelProxy<
|
||||||
|
typeof WarehouseTransfer
|
||||||
|
>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the sale invoice list filter DTO.
|
||||||
|
* @param filterDTO
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
private parseListFilterDTO(filterDTO) {
|
||||||
|
return R.compose(this.dynamicListService.parseStringifiedFilter)(filterDTO);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves warehouse transfers paginated list.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {IGetWarehousesTransfersFilterDTO} filterDTO
|
||||||
|
* @returns {}
|
||||||
|
*/
|
||||||
|
public getWarehouseTransfers = async (
|
||||||
|
filterDTO: IGetWarehousesTransfersFilterDTO,
|
||||||
|
) => {
|
||||||
|
// Parses stringified filter roles.
|
||||||
|
const filter = this.parseListFilterDTO(filterDTO);
|
||||||
|
|
||||||
|
// Dynamic list service.
|
||||||
|
const dynamicFilter = await this.dynamicListService.dynamicList(
|
||||||
|
this.warehouseTransferModel(),
|
||||||
|
filter,
|
||||||
|
);
|
||||||
|
const { results, pagination } = await this.warehouseTransferModel()
|
||||||
|
.query()
|
||||||
|
.onBuild((query) => {
|
||||||
|
query.withGraphFetched('entries.item');
|
||||||
|
query.withGraphFetched('fromWarehouse');
|
||||||
|
query.withGraphFetched('toWarehouse');
|
||||||
|
|
||||||
|
dynamicFilter.buildQuery()(query);
|
||||||
|
})
|
||||||
|
.pagination(filter.page - 1, filter.pageSize);
|
||||||
|
|
||||||
|
// Retrieves the transformed warehouse transfers
|
||||||
|
const warehousesTransfers = await this.transformer.transform(
|
||||||
|
results,
|
||||||
|
new WarehouseTransferTransformer(),
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
warehousesTransfers,
|
||||||
|
pagination,
|
||||||
|
filter,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { Transformer } from "../../Transformer/Transformer";
|
||||||
|
|
||||||
|
export class WarehouseTransferItemTransformer extends Transformer {
|
||||||
|
/**
|
||||||
|
* Include these attributes to sale invoice object.
|
||||||
|
* @returns {Array}
|
||||||
|
*/
|
||||||
|
public includeAttributes = (): string[] => {
|
||||||
|
return ['formattedQuantity', 'formattedCost', 'formattedTotal'];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats the total.
|
||||||
|
* @param {IWarehouseTransferEntry} entry
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
public formattedTotal = (entry) => {
|
||||||
|
return this.formatMoney(entry.total);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats the quantity.
|
||||||
|
* @param {IWarehouseTransferEntry} entry
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
public formattedQuantity = (entry) => {
|
||||||
|
return this.formatNumber(entry.quantity);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats the cost.
|
||||||
|
* @param {IWarehouseTransferEntry} entry
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
public formattedCost = (entry) => {
|
||||||
|
return this.formatMoney(entry.cost);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { WarehouseTransferItemTransformer } from './WarehouseTransferItemTransformer';
|
||||||
|
import { Transformer } from '@/modules/Transformer/Transformer';
|
||||||
|
export class WarehouseTransferTransformer extends Transformer {
|
||||||
|
/**
|
||||||
|
* Include these attributes to sale invoice object.
|
||||||
|
* @returns {Array}
|
||||||
|
*/
|
||||||
|
public includeAttributes = (): string[] => {
|
||||||
|
return ['formattedDate', 'entries'];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param transfer
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
protected formattedDate = (transfer) => {
|
||||||
|
return this.formatDate(transfer.date);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected entries = (transfer) => {
|
||||||
|
return this.item(transfer.entries, new WarehouseTransferItemTransformer());
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { events } from '@/common/events/events';
|
||||||
|
import { IWarehouseTransferCreated } from '../../Warehouses/Warehouse.types';
|
||||||
|
import { WarehouseTransferAutoIncrement } from '../commands/WarehouseTransferAutoIncrement';
|
||||||
|
import { OnEvent } from '@nestjs/event-emitter';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class WarehouseTransferAutoIncrementSubscriber {
|
||||||
|
constructor(
|
||||||
|
private readonly warehouseTransferAutoIncrement: WarehouseTransferAutoIncrement,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes inventory transactions once warehouse transfer created.
|
||||||
|
* @param {IInventoryTransactionsCreatedPayload} -
|
||||||
|
*/
|
||||||
|
@OnEvent(events.warehouseTransfer.onCreated)
|
||||||
|
async incrementTransferAutoIncrementOnCreated({}: IWarehouseTransferCreated) {
|
||||||
|
await this.warehouseTransferAutoIncrement.incrementNextTransferNumber();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
import {
|
||||||
|
IWarehouseTransferEditedPayload,
|
||||||
|
IWarehouseTransferDeletedPayload,
|
||||||
|
IWarehouseTransferCreated,
|
||||||
|
IWarehouseTransferInitiatedPayload,
|
||||||
|
IWarehouseTransferTransferredPayload,
|
||||||
|
} from '@/modules/Warehouses/Warehouse.types';
|
||||||
|
import { WarehouseTransferInventoryTransactions } from '../commands/WarehouseTransferWriteInventoryTransactions';
|
||||||
|
import { OnEvent } from '@nestjs/event-emitter';
|
||||||
|
import { events } from '@/common/events/events';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class WarehouseTransferInventoryTransactionsSubscriber {
|
||||||
|
constructor(
|
||||||
|
private readonly warehouseTransferInventoryTransactions: WarehouseTransferInventoryTransactions,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes inventory transactions once warehouse transfer created.
|
||||||
|
* @param {IInventoryTransactionsCreatedPayload} -
|
||||||
|
*/
|
||||||
|
@OnEvent(events.warehouseTransfer.onCreated)
|
||||||
|
async writeInventoryTransactionsOnWarehouseTransferCreated({
|
||||||
|
warehouseTransfer,
|
||||||
|
trx,
|
||||||
|
}: IWarehouseTransferCreated) {
|
||||||
|
// Can't continue if the warehouse transfer is not initiated yet.
|
||||||
|
if (!warehouseTransfer.isInitiated) return;
|
||||||
|
|
||||||
|
// Write all inventory transaction if warehouse transfer initiated and transferred.
|
||||||
|
if (warehouseTransfer.isInitiated && warehouseTransfer.isTransferred) {
|
||||||
|
await this.warehouseTransferInventoryTransactions.writeAllInventoryTransactions(
|
||||||
|
warehouseTransfer,
|
||||||
|
false,
|
||||||
|
trx,
|
||||||
|
);
|
||||||
|
// Write initiate inventory transaction if warehouse transfer initited and transferred yet.
|
||||||
|
} else if (warehouseTransfer.isInitiated) {
|
||||||
|
await this.warehouseTransferInventoryTransactions.writeInitiateInventoryTransactions(
|
||||||
|
warehouseTransfer,
|
||||||
|
false,
|
||||||
|
trx,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rewrite inventory transactions once warehouse transfer edited.
|
||||||
|
* @param {IWarehouseTransferEditedPayload} -
|
||||||
|
*/
|
||||||
|
@OnEvent(events.warehouseTransfer.onEdited)
|
||||||
|
async rewriteInventoryTransactionsOnWarehouseTransferEdited({
|
||||||
|
warehouseTransfer,
|
||||||
|
trx,
|
||||||
|
}: IWarehouseTransferEditedPayload) {
|
||||||
|
// Can't continue if the warehouse transfer is not initiated yet.
|
||||||
|
if (!warehouseTransfer.isInitiated) return;
|
||||||
|
|
||||||
|
// Write all inventory transaction if warehouse transfer initiated and transferred.
|
||||||
|
if (warehouseTransfer.isInitiated && warehouseTransfer.isTransferred) {
|
||||||
|
await this.warehouseTransferInventoryTransactions.writeAllInventoryTransactions(
|
||||||
|
warehouseTransfer,
|
||||||
|
true,
|
||||||
|
trx,
|
||||||
|
);
|
||||||
|
// Write initiate inventory transaction if warehouse transfer initited and transferred yet.
|
||||||
|
} else if (warehouseTransfer.isInitiated) {
|
||||||
|
await this.warehouseTransferInventoryTransactions.writeInitiateInventoryTransactions(
|
||||||
|
warehouseTransfer,
|
||||||
|
true,
|
||||||
|
trx,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverts inventory transactions once warehouse transfer deleted.
|
||||||
|
* @parma {IWarehouseTransferDeletedPayload} -
|
||||||
|
*/
|
||||||
|
@OnEvent(events.warehouseTransfer.onDeleted)
|
||||||
|
async revertInventoryTransactionsOnWarehouseTransferDeleted({
|
||||||
|
oldWarehouseTransfer,
|
||||||
|
trx,
|
||||||
|
}: IWarehouseTransferDeletedPayload) {
|
||||||
|
await this.warehouseTransferInventoryTransactions.revertInventoryTransactions(
|
||||||
|
oldWarehouseTransfer.id,
|
||||||
|
trx,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write inventory transactions of warehouse transfer once the transfer initiated.
|
||||||
|
* @param {IWarehouseTransferInitiatedPayload}
|
||||||
|
*/
|
||||||
|
@OnEvent(events.warehouseTransfer.onInitiated)
|
||||||
|
async writeInventoryTransactionsOnTransferInitiated({
|
||||||
|
trx,
|
||||||
|
warehouseTransfer,
|
||||||
|
}: IWarehouseTransferInitiatedPayload) {
|
||||||
|
await this.warehouseTransferInventoryTransactions.writeInitiateInventoryTransactions(
|
||||||
|
warehouseTransfer,
|
||||||
|
false,
|
||||||
|
trx,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write inventory transactions of warehouse transfer once the transfer completed.
|
||||||
|
* @param {IWarehouseTransferTransferredPayload}
|
||||||
|
*/
|
||||||
|
@OnEvent(events.warehouseTransfer.onTransferred)
|
||||||
|
async writeInventoryTransactionsOnTransferred({
|
||||||
|
trx,
|
||||||
|
warehouseTransfer,
|
||||||
|
}: IWarehouseTransferTransferredPayload) {
|
||||||
|
await this.warehouseTransferInventoryTransactions.writeTransferredInventoryTransactions(
|
||||||
|
warehouseTransfer,
|
||||||
|
false,
|
||||||
|
trx,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,7 +18,8 @@
|
|||||||
"forceConsistentCasingInFileNames": false,
|
"forceConsistentCasingInFileNames": false,
|
||||||
"noFallthroughCasesInSwitch": false,
|
"noFallthroughCasesInSwitch": false,
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./src/*"]
|
"@/*": ["./src/*"],
|
||||||
|
"@bigcapital/server/*": ["../server/*"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,11 @@ import ModelSearchable from './ModelSearchable';
|
|||||||
export default class TaxRateTransaction extends mixin(TenantModel, [
|
export default class TaxRateTransaction extends mixin(TenantModel, [
|
||||||
ModelSearchable,
|
ModelSearchable,
|
||||||
]) {
|
]) {
|
||||||
|
public rate: number;
|
||||||
|
public referenceId: number;
|
||||||
|
public referenceType: string;
|
||||||
|
public taxRateId: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Table name
|
* Table name
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user