mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 04:40:32 +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
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
"dependencies": {
|
||||
"@bigcapital/email-components": "*",
|
||||
"@bigcapital/pdf-templates": "*",
|
||||
"@bigcapital/server": "*",
|
||||
"@bigcapital/utils": "*",
|
||||
"@nestjs/bull": "^10.2.1",
|
||||
"@nestjs/bullmq": "^10.2.2",
|
||||
|
||||
@@ -230,6 +230,7 @@ export class Account extends TenantBaseModel {
|
||||
to: 'accounts_transactions.accountId',
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* 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 { StripePaymentModule } from '../StripePayment/StripePayment.module';
|
||||
import { FeaturesModule } from '../Features/Features.module';
|
||||
import { InventoryCostModule } from '../InventoryCost/InventoryCost.module';
|
||||
import { WarehousesTransfersModule } from '../WarehousesTransfers/WarehouseTransfers.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -135,6 +137,7 @@ import { FeaturesModule } from '../Features/Features.module';
|
||||
PdfTemplatesModule,
|
||||
BranchesModule,
|
||||
WarehousesModule,
|
||||
WarehousesTransfersModule,
|
||||
CustomersModule,
|
||||
VendorsModule,
|
||||
SaleInvoicesModule,
|
||||
@@ -160,6 +163,7 @@ import { FeaturesModule } from '../Features/Features.module';
|
||||
SettingsModule,
|
||||
FeaturesModule,
|
||||
InventoryAdjustmentsModule,
|
||||
InventoryCostModule,
|
||||
PostHogModule,
|
||||
EventTrackerModule,
|
||||
FinancialStatementsModule,
|
||||
|
||||
@@ -15,7 +15,7 @@ export class ImportAls {
|
||||
* @returns The result of the callback function.
|
||||
*/
|
||||
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 { LedgerModule } from '../Ledger/Ledger.module';
|
||||
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 = [
|
||||
RegisterTenancyModel(InventoryCostLotTracker),
|
||||
@@ -15,14 +27,28 @@ const models = [
|
||||
];
|
||||
|
||||
@Module({
|
||||
imports: [LedgerModule, ...models],
|
||||
imports: [
|
||||
LedgerModule,
|
||||
...models,
|
||||
BullModule.registerQueue({ name: ComputeItemCostQueue }),
|
||||
BullModule.registerQueue({
|
||||
name: WriteInventoryTransactionsGLEntriesQueue,
|
||||
}),
|
||||
],
|
||||
providers: [
|
||||
InventoryCostGLBeforeWriteSubscriber,
|
||||
InventoryCostGLStorage,
|
||||
InventoryItemsQuantitySyncService,
|
||||
InventoryTransactionsService,
|
||||
InventoryComputeCostService,
|
||||
InventoryCostApplication,
|
||||
StoreInventoryLotsCostService,
|
||||
ComputeItemCostProcessor,
|
||||
WriteInventoryTransactionsGLEntriesProcessor,
|
||||
InventoryAverageCostMethodService,
|
||||
InventoryItemCostService,
|
||||
InventoryItemOpeningAvgCostService,
|
||||
],
|
||||
exports: [...models, InventoryTransactionsService],
|
||||
exports: [...models, InventoryTransactionsService, InventoryItemCostService],
|
||||
})
|
||||
export class InventoryCostModule {}
|
||||
|
||||
@@ -3,12 +3,6 @@ import { Knex } from 'knex';
|
||||
import { InventoryTransaction } from '../models/InventoryTransaction';
|
||||
|
||||
export class InventoryAverageCostMethod {
|
||||
/**
|
||||
* Constructor method.
|
||||
* @param {number} tenantId - The given tenant id.
|
||||
* @param {Date} startingDate -
|
||||
* @param {number} itemId - The given inventory item id.
|
||||
*/
|
||||
constructor() {}
|
||||
|
||||
/**
|
||||
|
||||
@@ -72,7 +72,6 @@ export class InventoryComputeCostService {
|
||||
* @param {Date} startingDate
|
||||
*/
|
||||
async scheduleComputeItemCost(
|
||||
tenantId: number,
|
||||
itemId: number,
|
||||
startingDate: Date | string,
|
||||
) {
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { keyBy, get } from 'lodash';
|
||||
import { Knex } from 'knex';
|
||||
import * as R from 'ramda';
|
||||
import { IInventoryItemCostMeta } from '../types/InventoryCost.types';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { InventoryTransaction } from '../models/InventoryTransaction';
|
||||
@@ -22,33 +20,30 @@ export class InventoryItemCostService {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {} INValuationMap -
|
||||
* @param {} OUTValuationMap -
|
||||
* @param {Map<number, IInventoryItemCostMeta>} INValuationMap -
|
||||
* @param {Map<number, IInventoryItemCostMeta>} OUTValuationMap -
|
||||
* @param {number} itemId
|
||||
*/
|
||||
private getItemInventoryMeta = R.curry(
|
||||
(
|
||||
INValuationMap,
|
||||
OUTValuationMap,
|
||||
itemId: number,
|
||||
): IInventoryItemCostMeta => {
|
||||
const INCost = get(INValuationMap, `[${itemId}].cost`, 0);
|
||||
const INQuantity = get(INValuationMap, `[${itemId}].quantity`, 0);
|
||||
private getItemInventoryMeta(
|
||||
INValuationMap: Map<number, IInventoryItemCostMeta>,
|
||||
OUTValuationMap: Map<number, IInventoryItemCostMeta>,
|
||||
itemId: number,
|
||||
) {
|
||||
const INCost = get(INValuationMap, `[${itemId}].cost`, 0);
|
||||
const INQuantity = get(INValuationMap, `[${itemId}].quantity`, 0);
|
||||
|
||||
const OUTCost = get(OUTValuationMap, `[${itemId}].cost`, 0);
|
||||
const OUTQuantity = get(OUTValuationMap, `[${itemId}].quantity`, 0);
|
||||
const OUTCost = get(OUTValuationMap, `[${itemId}].cost`, 0);
|
||||
const OUTQuantity = get(OUTValuationMap, `[${itemId}].quantity`, 0);
|
||||
|
||||
const valuation = INCost - OUTCost;
|
||||
const quantity = INQuantity - OUTQuantity;
|
||||
const average = quantity ? valuation / quantity : 0;
|
||||
const valuation = INCost - OUTCost;
|
||||
const quantity = INQuantity - OUTQuantity;
|
||||
const average = quantity ? valuation / quantity : 0;
|
||||
|
||||
return { itemId, valuation, quantity, average };
|
||||
},
|
||||
);
|
||||
return { itemId, valuation, quantity, average };
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} tenantId
|
||||
* @param {number} itemsId
|
||||
* @param {Date} date
|
||||
* @returns
|
||||
@@ -57,7 +52,7 @@ export class InventoryItemCostService {
|
||||
itemsId: number[],
|
||||
date: Date,
|
||||
): Promise<any> => {
|
||||
const commonBuilder = (builder: Knex.QueryBuilder) => {
|
||||
const commonBuilder = (builder) => {
|
||||
if (date) {
|
||||
builder.where('date', '<', date);
|
||||
}
|
||||
@@ -84,7 +79,6 @@ export class InventoryItemCostService {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} tenantId -
|
||||
* @param {number[]} itemsIds -
|
||||
* @param {Date} date -
|
||||
*/
|
||||
@@ -122,11 +116,10 @@ export class InventoryItemCostService {
|
||||
const [OUTValuationMap, INValuationMap] =
|
||||
await this.getItemsInventoryInOutMap(itemsId, date);
|
||||
|
||||
const getItemValuation = this.getItemInventoryMeta(
|
||||
INValuationMap,
|
||||
OUTValuationMap,
|
||||
);
|
||||
const itemsValuations = inventoryItemsIds.map(getItemValuation);
|
||||
const getItemValuation = (itemId: number) =>
|
||||
this.getItemInventoryMeta(INValuationMap, OUTValuationMap, itemId);
|
||||
|
||||
const itemsValuations = inventoryItemsIds.map((id) => getItemValuation(id));
|
||||
const itemsValuationsMap = new Map(
|
||||
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 { InventoryCostLotTracker } from '../models/InventoryCostLotTracker';
|
||||
|
||||
@Injectable()
|
||||
export class InventoryItemOpeningAvgCostService {
|
||||
constructor(
|
||||
@Inject(InventoryCostLotTracker.name)
|
||||
private readonly inventoryCostLotTrackerModel: TenantModelProxy<
|
||||
typeof InventoryCostLotTracker
|
||||
>,
|
||||
@@ -31,6 +32,10 @@ export class InventoryItemOpeningAvgCostService {
|
||||
builder.sum('cost as cost');
|
||||
builder.first();
|
||||
};
|
||||
interface QueryResult {
|
||||
cost: number;
|
||||
quantity: number;
|
||||
}
|
||||
// Calculates the total inventory total quantity and rate `IN` transactions.
|
||||
const inInvSumationOper = this.inventoryCostLotTrackerModel()
|
||||
.query()
|
||||
@@ -43,10 +48,11 @@ export class InventoryItemOpeningAvgCostService {
|
||||
.onBuild(commonBuilder)
|
||||
.where('direction', 'OUT');
|
||||
|
||||
const [inInvSumation, outInvSumation] = await Promise.all([
|
||||
const [inInvSumation, outInvSumation] = (await Promise.all([
|
||||
inInvSumationOper,
|
||||
outInvSumationOper,
|
||||
]);
|
||||
])) as unknown as [QueryResult, QueryResult];
|
||||
|
||||
return this.computeItemAverageCost(
|
||||
inInvSumation?.cost || 0,
|
||||
inInvSumation?.quantity || 0,
|
||||
|
||||
@@ -33,7 +33,7 @@ export class InventoryTransactionsService {
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
async recordInventoryTransactions(
|
||||
transactions: InventoryTransaction[],
|
||||
transactions: ModelObject<InventoryTransaction>[],
|
||||
override: boolean = false,
|
||||
trx?: Knex.Transaction,
|
||||
): Promise<void> {
|
||||
|
||||
@@ -7,21 +7,20 @@ import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
|
||||
import { InventoryTransactionMeta } from './InventoryTransactionMeta';
|
||||
|
||||
export class InventoryTransaction extends TenantBaseModel {
|
||||
date: Date | string;
|
||||
direction: TInventoryTransactionDirection;
|
||||
itemId: number;
|
||||
quantity: number | null;
|
||||
rate: number;
|
||||
transactionType: string;
|
||||
transactionId: number;
|
||||
date!: Date | string;
|
||||
direction!: TInventoryTransactionDirection;
|
||||
itemId!: number;
|
||||
quantity!: number | null;
|
||||
rate!: number;
|
||||
transactionType!: string;
|
||||
transactionId!: number;
|
||||
costAccountId?: number;
|
||||
entryId: number;
|
||||
entryId!: number;
|
||||
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
|
||||
warehouseId?: number;
|
||||
|
||||
meta?: InventoryTransactionMeta;
|
||||
|
||||
/**
|
||||
@@ -34,7 +33,7 @@ export class InventoryTransaction extends TenantBaseModel {
|
||||
/**
|
||||
* Model timestamps.
|
||||
*/
|
||||
get timestamps() {
|
||||
static get timestamps() {
|
||||
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 { Job } from 'bullmq';
|
||||
import { ClsService } from 'nestjs-cls';
|
||||
import { TenantJobPayload } from '@/interfaces/Tenant';
|
||||
import { InventoryComputeCostService } from '../commands/InventoryComputeCost.service';
|
||||
import { events } from '@/common/events/events';
|
||||
import { ComputeItemCostQueueJob } from '../types/InventoryCost.types';
|
||||
|
||||
interface ComputeItemCostJobPayload extends TenantJobPayload {
|
||||
itemId: number;
|
||||
startingDate: Date;
|
||||
}
|
||||
|
||||
@Processor({
|
||||
name: 'compute-item-cost',
|
||||
name: ComputeItemCostQueueJob,
|
||||
scope: Scope.REQUEST,
|
||||
})
|
||||
export class ComputeItemCostProcessor {
|
||||
export class ComputeItemCostProcessor extends WorkerHost {
|
||||
/**
|
||||
* @param {InventoryComputeCostService} inventoryComputeCostService -
|
||||
* @param {ClsService} clsService -
|
||||
* @param {EventEmitter2} eventEmitter -
|
||||
*/
|
||||
constructor(
|
||||
private readonly inventoryComputeCostService: InventoryComputeCostService,
|
||||
private readonly clsService: ClsService,
|
||||
|
||||
@Inject(JOB_REF)
|
||||
private readonly jobRef: Job<ComputeItemCostJobPayload>,
|
||||
) {}
|
||||
private readonly eventEmitter: EventEmitter2,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle compute item cost job.
|
||||
* Process the compute item cost job.
|
||||
* @param {Job<ComputeItemCostJobPayload>} job - The job to process
|
||||
*/
|
||||
async handleComputeItemCost() {
|
||||
const { itemId, startingDate, organizationId, userId } = this.jobRef.data;
|
||||
async process(job: Job<ComputeItemCostJobPayload>) {
|
||||
const { itemId, startingDate, organizationId, userId } = job.data;
|
||||
|
||||
this.clsService.set('organizationId', organizationId);
|
||||
this.clsService.set('userId', userId);
|
||||
|
||||
await this.inventoryComputeCostService.computeItemCost(
|
||||
startingDate,
|
||||
itemId,
|
||||
);
|
||||
try {
|
||||
await this.inventoryComputeCostService.computeItemCost(
|
||||
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 { InventoryTransaction } from "../models/InventoryTransaction";
|
||||
import { Knex } from 'knex';
|
||||
import { InventoryTransaction } from '../models/InventoryTransaction';
|
||||
|
||||
export const ComputeItemCostQueue = 'ComputeItemCostQueue';
|
||||
export const ComputeItemCostQueueJob = 'ComputeItemCostQueueJob';
|
||||
|
||||
export const WriteInventoryTransactionsGLEntriesQueue =
|
||||
'WriteInventoryTransactionsGLEntriesQueue';
|
||||
export const WriteInventoryTransactionsGLEntriesQueueJob =
|
||||
'WriteInventoryTransactionsGLEntriesQueueJob';
|
||||
|
||||
export interface IInventoryItemCostMeta {
|
||||
itemId: number;
|
||||
@@ -10,8 +17,8 @@ export interface IInventoryItemCostMeta {
|
||||
}
|
||||
|
||||
export interface IInventoryCostLotsGLEntriesWriteEvent {
|
||||
startingDate: Date,
|
||||
trx: Knex.Transaction
|
||||
startingDate: Date;
|
||||
trx: Knex.Transaction;
|
||||
}
|
||||
|
||||
export type TInventoryTransactionDirection = 'IN' | 'OUT';
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Warehouse } from '@/modules/Warehouses/models/Warehouse.model';
|
||||
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 name: string;
|
||||
public readonly active: boolean;
|
||||
@@ -30,4 +31,164 @@ export class Item extends TenantBaseModel{
|
||||
static get tableName() {
|
||||
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 { InventoryTransaction } from '../InventoryCost/models/InventoryTransaction';
|
||||
import { IInventoryCostLotsGLEntriesWriteEvent } from '../InventoryCost/types/InventoryCost.types';
|
||||
import { InventoryComputeCostService } from '../InventoryCost/commands/InventoryComputeCost.service';
|
||||
|
||||
@Injectable()
|
||||
export class SaleInvoicesCost {
|
||||
constructor(
|
||||
private readonly inventoryService: InventoryService,
|
||||
private readonly inventoryService: InventoryComputeCostService,
|
||||
private readonly uow: UnitOfWork,
|
||||
private readonly eventPublisher: EventEmitter2,
|
||||
) {}
|
||||
@@ -112,12 +113,12 @@ export class SaleInvoicesCost {
|
||||
* @return {Promise<agenda>}
|
||||
*/
|
||||
scheduleWriteJournalEntries(startingDate?: Date) {
|
||||
const agenda = Container.get('agenda');
|
||||
// const agenda = Container.get('agenda');
|
||||
|
||||
return agenda.schedule('in 3 seconds', 'rewrite-invoices-journal-entries', {
|
||||
startingDate,
|
||||
tenantId,
|
||||
});
|
||||
// return agenda.schedule('in 3 seconds', 'rewrite-invoices-journal-entries', {
|
||||
// startingDate,
|
||||
// tenantId,
|
||||
// });
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,6 +19,14 @@ import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
|
||||
@Injectable()
|
||||
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(
|
||||
private unlockEstimateFromInvoice: UnlinkConvertedSaleEstimate,
|
||||
private eventPublisher: EventEmitter2,
|
||||
@@ -36,6 +44,9 @@ export class DeleteSaleInvoice {
|
||||
|
||||
@Inject(SaleInvoice.name)
|
||||
private saleInvoiceModel: TenantModelProxy<typeof SaleInvoice>,
|
||||
|
||||
@Inject(ItemEntry.name)
|
||||
private itemEntryModel: TenantModelProxy<typeof ItemEntry>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -113,12 +124,13 @@ export class DeleteSaleInvoice {
|
||||
saleInvoiceId,
|
||||
trx,
|
||||
);
|
||||
await ItemEntry.query(trx)
|
||||
await this.itemEntryModel()
|
||||
.query(trx)
|
||||
.where('reference_id', saleInvoiceId)
|
||||
.where('reference_type', 'SaleInvoice')
|
||||
.delete();
|
||||
|
||||
await SaleInvoice.query(trx).findById(saleInvoiceId).delete();
|
||||
await this.saleInvoiceModel().query(trx).findById(saleInvoiceId).delete();
|
||||
|
||||
// Triggers `onSaleInvoiceDeleted` event.
|
||||
await this.eventPublisher.emitAsync(events.saleInvoice.onDeleted, {
|
||||
|
||||
@@ -17,6 +17,15 @@ import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
|
||||
@Injectable()
|
||||
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(
|
||||
private readonly itemsEntriesService: ItemsEntriesService,
|
||||
private readonly eventPublisher: EventEmitter2,
|
||||
|
||||
@@ -13,9 +13,7 @@ import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
export class SubscriptionGuard implements CanActivate {
|
||||
constructor(
|
||||
@Inject(PlanSubscription.name)
|
||||
private readonly planSubscriptionModel: TenantModelProxy<
|
||||
typeof PlanSubscription
|
||||
>,
|
||||
private readonly planSubscriptionModel: typeof PlanSubscription,
|
||||
private readonly tenancyContext: TenancyContext,
|
||||
) {}
|
||||
|
||||
@@ -30,7 +28,7 @@ export class SubscriptionGuard implements CanActivate {
|
||||
subscriptionSlug: string = 'main', // Default value
|
||||
): Promise<boolean> {
|
||||
const tenant = await this.tenancyContext.getTenant();
|
||||
const subscription = await this.planSubscriptionModel()
|
||||
const subscription = await this.planSubscriptionModel
|
||||
.query()
|
||||
.findOne('slug', subscriptionSlug)
|
||||
.where('tenant_id', tenant.id);
|
||||
|
||||
@@ -17,6 +17,8 @@ import { WriteInvoiceTaxTransactionsSubscriber } from './subscribers/WriteInvoic
|
||||
import { BillTaxRateValidateSubscriber } from './subscribers/BillTaxRateValidateSubscriber';
|
||||
import { SaleInvoiceTaxRateValidateSubscriber } from './subscribers/SaleInvoiceTaxRateValidateSubscriber';
|
||||
import { SyncItemTaxRateOnEditTaxSubscriber } from './subscribers/SyncItemTaxRateOnEditTaxSubscriber';
|
||||
import { WriteTaxTransactionsItemEntries } from './WriteTaxTransactionsItemEntries';
|
||||
import { SyncItemTaxRateOnEditTaxRate } from './SyncItemTaxRateOnEditTaxRate';
|
||||
|
||||
@Module({
|
||||
imports: [],
|
||||
@@ -39,6 +41,8 @@ import { SyncItemTaxRateOnEditTaxSubscriber } from './subscribers/SyncItemTaxRat
|
||||
BillTaxRateValidateSubscriber,
|
||||
SaleInvoiceTaxRateValidateSubscriber,
|
||||
SyncItemTaxRateOnEditTaxSubscriber,
|
||||
WriteTaxTransactionsItemEntries,
|
||||
SyncItemTaxRateOnEditTaxRate
|
||||
],
|
||||
exports: [ItemEntriesTaxTransactions],
|
||||
})
|
||||
|
||||
@@ -6,6 +6,7 @@ import { TaxRateModel } from './models/TaxRate.model';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { ModelObject } from 'objection';
|
||||
import { ItemEntry } from '../TransactionItemEntry/models/ItemEntry';
|
||||
import { TaxRateTransaction } from './models/TaxRateTransaction.model';
|
||||
|
||||
@Injectable()
|
||||
export class WriteTaxTransactionsItemEntries {
|
||||
@@ -40,11 +41,11 @@ export class WriteTaxTransactionsItemEntries {
|
||||
taxRateId: entry.taxRateId,
|
||||
referenceType: entry.referenceType,
|
||||
referenceId: entry.referenceId,
|
||||
rate: entry.taxRate || taxRatesById[entry.taxRateId]?.rate,
|
||||
rate: entry.taxRate || (taxRatesById[entry.taxRateId]?.rate as number),
|
||||
}));
|
||||
await this.taxRateTransactionModel()
|
||||
.query(trx)
|
||||
.upsertGraph(taxTransactions);
|
||||
.upsertGraph(taxTransactions as ModelObject<TaxRateTransaction>[]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,12 +4,12 @@ import { mixin, Model, raw } from 'objection';
|
||||
import { BaseModel } from '@/models/Model';
|
||||
|
||||
export class TaxRateTransaction extends BaseModel {
|
||||
id: number;
|
||||
taxRateId: number;
|
||||
referenceType: string;
|
||||
referenceId: number;
|
||||
rate: number;
|
||||
taxAccountId: number;
|
||||
public id: number;
|
||||
public taxRateId: number;
|
||||
public referenceType: string;
|
||||
public referenceId: string;
|
||||
public rate: number;
|
||||
public taxAccountId?: number;
|
||||
|
||||
/**
|
||||
* Table name
|
||||
|
||||
@@ -8,7 +8,7 @@ import { TENANCY_DB_CONNECTION } from '@/modules/Tenancy/TenancyDB/TenancyDB.con
|
||||
export class UnitOfWork {
|
||||
constructor(
|
||||
@Inject(TENANCY_DB_CONNECTION)
|
||||
private readonly tenantKex: Knex,
|
||||
private readonly tenantKex: () => Knex,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -23,7 +23,7 @@ export class UnitOfWork {
|
||||
trx?: Transaction,
|
||||
isolationLevel: IsolationLevel = IsolationLevel.READ_UNCOMMITTED,
|
||||
): Promise<T> => {
|
||||
const knex = this.tenantKex;
|
||||
const knex = this.tenantKex();
|
||||
let _trx = trx;
|
||||
|
||||
if (!_trx) {
|
||||
|
||||
@@ -1,23 +1,12 @@
|
||||
import { Knex } from 'knex';
|
||||
import { Warehouse } from './models/Warehouse.model';
|
||||
import { WarehouseTransfer } from '../WarehousesTransfers/models/WarehouseTransfer';
|
||||
import { ModelObject } from 'objection';
|
||||
|
||||
export interface IWarehouse {
|
||||
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 {
|
||||
id?: number;
|
||||
index?: number;
|
||||
@@ -118,37 +107,31 @@ export interface IWarehouseTransferCreate {
|
||||
}
|
||||
|
||||
export interface IWarehouseTransferCreated {
|
||||
trx: Knex.Transaction;
|
||||
warehouseTransfer: IWarehouseTransfer;
|
||||
trx?: Knex.Transaction;
|
||||
warehouseTransfer: ModelObject<WarehouseTransfer>;
|
||||
warehouseTransferDTO: ICreateWarehouseTransferDTO;
|
||||
// tenantId: number;
|
||||
}
|
||||
|
||||
export interface IWarehouseTransferEditPayload {
|
||||
// tenantId: number;
|
||||
editWarehouseDTO: IEditWarehouseTransferDTO;
|
||||
oldWarehouseTransfer: IWarehouseTransfer;
|
||||
oldWarehouseTransfer: ModelObject<WarehouseTransfer>;
|
||||
trx: Knex.Transaction;
|
||||
}
|
||||
|
||||
export interface IWarehouseTransferEditedPayload {
|
||||
// tenantId: number;
|
||||
editWarehouseDTO: IEditWarehouseTransferDTO;
|
||||
oldWarehouseTransfer: IWarehouseTransfer;
|
||||
warehouseTransfer: IWarehouseTransfer;
|
||||
oldWarehouseTransfer: ModelObject<WarehouseTransfer>;
|
||||
warehouseTransfer: ModelObject<WarehouseTransfer>;
|
||||
trx: Knex.Transaction;
|
||||
}
|
||||
|
||||
export interface IWarehouseTransferDeletePayload {
|
||||
// tenantId: number;
|
||||
oldWarehouseTransfer: IWarehouseTransfer;
|
||||
oldWarehouseTransfer: ModelObject<WarehouseTransfer>;
|
||||
trx: Knex.Transaction;
|
||||
}
|
||||
|
||||
export interface IWarehouseTransferDeletedPayload {
|
||||
// tenantId: number;
|
||||
warehouseTransfer: IWarehouseTransfer;
|
||||
oldWarehouseTransfer: IWarehouseTransfer;
|
||||
oldWarehouseTransfer: ModelObject<WarehouseTransfer>;
|
||||
trx: Knex.Transaction;
|
||||
}
|
||||
|
||||
@@ -173,40 +156,33 @@ export interface IWarehousesActivatedPayload {
|
||||
}
|
||||
|
||||
export interface IWarehouseMarkAsPrimaryPayload {
|
||||
// tenantId: number;
|
||||
oldWarehouse: Warehouse;
|
||||
trx: Knex.Transaction;
|
||||
}
|
||||
export interface IWarehouseMarkedAsPrimaryPayload {
|
||||
// tenantId: number;
|
||||
oldWarehouse: Warehouse;
|
||||
markedWarehouse: Warehouse;
|
||||
trx: Knex.Transaction;
|
||||
}
|
||||
|
||||
export interface IWarehouseTransferInitiatePayload {
|
||||
// tenantId: number;
|
||||
oldWarehouseTransfer: IWarehouseTransfer;
|
||||
oldWarehouseTransfer: ModelObject<WarehouseTransfer>;
|
||||
trx: Knex.Transaction;
|
||||
}
|
||||
|
||||
|
||||
export interface IWarehouseTransferInitiatedPayload {
|
||||
// tenantId: number;
|
||||
warehouseTransfer: IWarehouseTransfer;
|
||||
oldWarehouseTransfer: IWarehouseTransfer;
|
||||
warehouseTransfer: ModelObject<WarehouseTransfer>;
|
||||
oldWarehouseTransfer: ModelObject<WarehouseTransfer>;
|
||||
trx: Knex.Transaction;
|
||||
}
|
||||
|
||||
export interface IWarehouseTransferTransferingPayload {
|
||||
// tenantId: number;
|
||||
oldWarehouseTransfer: IWarehouseTransfer;
|
||||
oldWarehouseTransfer: ModelObject<WarehouseTransfer>;
|
||||
trx: Knex.Transaction;
|
||||
}
|
||||
|
||||
export interface IWarehouseTransferTransferredPayload {
|
||||
// tenantId: number;
|
||||
warehouseTransfer: IWarehouseTransfer;
|
||||
oldWarehouseTransfer: IWarehouseTransfer;
|
||||
warehouseTransfer: ModelObject<WarehouseTransfer>;
|
||||
oldWarehouseTransfer: ModelObject<WarehouseTransfer>;
|
||||
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,
|
||||
"noFallthroughCasesInSwitch": false,
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
"@/*": ["./src/*"],
|
||||
"@bigcapital/server/*": ["../server/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,11 @@ import ModelSearchable from './ModelSearchable';
|
||||
export default class TaxRateTransaction extends mixin(TenantModel, [
|
||||
ModelSearchable,
|
||||
]) {
|
||||
public rate: number;
|
||||
public referenceId: number;
|
||||
public referenceType: string;
|
||||
public taxRateId: number;
|
||||
|
||||
/**
|
||||
* Table name
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user