From fb9d68c2cf40866f8813a0c195d3a6f4e9ed2d11 Mon Sep 17 00:00:00 2001 From: "a.bouhuolia" Date: Sun, 28 Feb 2021 14:45:13 +0200 Subject: [PATCH] fix: sync inventory items quantity with services. --- server/src/interfaces/InventoryTransaction.ts | 7 +- server/src/loaders/events.ts | 3 - .../Inventory/InventoryAdjustmentService.ts | 5 +- .../Inventory/InventoryItemsQuantitySync.ts | 92 +++++++++++++++++++ .../subscribers/Bills/SyncItemsQuantity.ts | 59 ------------ server/src/subscribers/Inventory/Inventory.ts | 47 +++++++++- .../Inventory/InventoryAdjustment.ts | 6 +- .../SaleInvoices/SyncItemsQuantity.ts | 71 -------------- .../SaleReceipt/SyncItemsQuantity.ts | 67 -------------- 9 files changed, 147 insertions(+), 210 deletions(-) create mode 100644 server/src/services/Inventory/InventoryItemsQuantitySync.ts delete mode 100644 server/src/subscribers/Bills/SyncItemsQuantity.ts delete mode 100644 server/src/subscribers/SaleInvoices/SyncItemsQuantity.ts delete mode 100644 server/src/subscribers/SaleReceipt/SyncItemsQuantity.ts diff --git a/server/src/interfaces/InventoryTransaction.ts b/server/src/interfaces/InventoryTransaction.ts index efa3323c5..47b9756e7 100644 --- a/server/src/interfaces/InventoryTransaction.ts +++ b/server/src/interfaces/InventoryTransaction.ts @@ -29,4 +29,9 @@ export interface IInventoryLotCost { transactionType: string, transactionId: number, entryId: number -} \ No newline at end of file +}; + +export interface IItemsQuantityChanges { + itemId: number, + balanceChange: number, +}; \ No newline at end of file diff --git a/server/src/loaders/events.ts b/server/src/loaders/events.ts index b394d510d..2b1f8d323 100644 --- a/server/src/loaders/events.ts +++ b/server/src/loaders/events.ts @@ -7,19 +7,16 @@ import 'subscribers/manualJournals'; import 'subscribers/expenses'; import 'subscribers/Bills'; -import 'subscribers/Bills/SyncItemsQuantity'; import 'subscribers/Bills/SyncVendorsBalances'; import 'subscribers/Bills/WriteJournalEntries'; import 'subscribers/Bills/WriteInventoryTransactions'; import 'subscribers/SaleInvoices'; import 'subscribers/SaleInvoices/SyncCustomersBalance'; -import 'subscribers/SaleInvoices/SyncItemsQuantity'; import 'subscribers/SaleInvoices/WriteInventoryTransactions'; import 'subscribers/SaleInvoices/WriteJournalEntries'; import 'subscribers/SaleReceipt'; -import 'subscribers/SaleReceipt/SyncItemsQuantity'; import 'subscribers/SaleReceipt/WriteInventoryTransactions'; import 'subscribers/SaleReceipt/WriteJournalEntries'; diff --git a/server/src/services/Inventory/InventoryAdjustmentService.ts b/server/src/services/Inventory/InventoryAdjustmentService.ts index a2f07e841..2f1507354 100644 --- a/server/src/services/Inventory/InventoryAdjustmentService.ts +++ b/server/src/services/Inventory/InventoryAdjustmentService.ts @@ -183,7 +183,7 @@ export default class InventoryAdjustmentService { inventoryAdjustmentId: number ): Promise { // Retrieve the inventory adjustment or throw not found service error. - const adjustment = await this.getInventoryAdjustmentOrThrowError( + const oldInventoryAdjustment = await this.getInventoryAdjustmentOrThrowError( tenantId, inventoryAdjustmentId ); @@ -208,6 +208,7 @@ export default class InventoryAdjustmentService { await this.eventDispatcher.dispatch(events.inventoryAdjustment.onDeleted, { tenantId, inventoryAdjustmentId, + oldInventoryAdjustment }); this.logger.info( '[inventory_adjustment] the adjustment deleted successfully.', @@ -317,7 +318,7 @@ export default class InventoryAdjustmentService { }); }); // Saves the given inventory transactions to the storage. - this.inventoryService.recordInventoryTransactions( + await this.inventoryService.recordInventoryTransactions( tenantId, inventoryTransactions, override diff --git a/server/src/services/Inventory/InventoryItemsQuantitySync.ts b/server/src/services/Inventory/InventoryItemsQuantitySync.ts new file mode 100644 index 000000000..642d792a2 --- /dev/null +++ b/server/src/services/Inventory/InventoryItemsQuantitySync.ts @@ -0,0 +1,92 @@ +import { Inject, Service } from 'typedi'; +import { toSafeInteger } from 'lodash'; +import { IInventoryTransaction, IItemsQuantityChanges } from 'interfaces'; +import HasTenancyService from 'services/Tenancy/TenancyService'; + +/** + * Syncs the inventory transactions with inventory items quantity. + */ +@Service() +export default class InventoryItemsQuantitySync { + @Inject() + tenancy: HasTenancyService; + + /** + * Reverse the given inventory transactions. + * @param {IInventoryTransaction[]} inventroyTransactions + * @return {IInventoryTransaction[]} + */ + reverseInventoryTransactions( + inventroyTransactions: IInventoryTransaction[] + ): IInventoryTransaction[] { + return inventroyTransactions.map((transaction) => ({ + ...transaction, + direction: transaction.direction === 'OUT' ? 'IN' : 'OUT', + })); + } + + /** + * Reverses the inventory transactions. + * @param {IInventoryTransaction[]} inventroyTransactions - + * @return {IItemsQuantityChanges[]} + */ + getReverseItemsQuantityChanges( + inventroyTransactions: IInventoryTransaction[] + ): IItemsQuantityChanges[] { + const reversedTransactions = this.reverseInventoryTransactions( + inventroyTransactions + ); + return this.getItemsQuantityChanges(reversedTransactions); + } + + /** + * Retrieve the items quantity changes from the given inventory transactions. + * @param {IInventoryTransaction[]} inventroyTransactions - Inventory transactions. + * @return {IItemsQuantityChanges[]} + */ + getItemsQuantityChanges( + inventroyTransactions: IInventoryTransaction[] + ): IItemsQuantityChanges[] { + const balanceMap: { [itemId: number]: number } = {}; + + inventroyTransactions.forEach( + (inventoryTransaction: IInventoryTransaction) => { + const { itemId, direction, quantity } = inventoryTransaction; + + if (!balanceMap[itemId]) { + balanceMap[itemId] = 0; + } + balanceMap[itemId] += direction === 'IN' ? quantity : 0; + balanceMap[itemId] -= direction === 'OUT' ? quantity : 0; + } + ); + + return Object.entries(balanceMap).map(([itemId, balanceChange]) => ({ + itemId: toSafeInteger(itemId), + balanceChange, + })); + } + + /** + * Changes the items quantity changes. + * @param {IItemsQuantityChanges[]} itemsQuantity - Items quantity changes. + * @return {Promise} + */ + async changeItemsQuantity( + tenantId: number, + itemsQuantity: IItemsQuantityChanges[] + ): Promise { + const { itemRepository } = this.tenancy.repositories(tenantId); + const opers = []; + + itemsQuantity.forEach((itemQuantity: IItemsQuantityChanges) => { + const changeQuantityOper = itemRepository.changeNumber( + { id: itemQuantity.itemId, type: 'inventory' }, + 'quantityOnHand', + itemQuantity.balanceChange + ); + opers.push(changeQuantityOper); + }); + await Promise.all(opers); + } +} diff --git a/server/src/subscribers/Bills/SyncItemsQuantity.ts b/server/src/subscribers/Bills/SyncItemsQuantity.ts deleted file mode 100644 index 28bb86950..000000000 --- a/server/src/subscribers/Bills/SyncItemsQuantity.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { Container } from 'typedi'; -import { EventSubscriber, On } from 'event-dispatch'; -import events from 'subscribers/events'; -import TenancyService from 'services/Tenancy/TenancyService'; -import ItemsEntriesService from 'services/Items/ItemsEntriesService'; - -@EventSubscriber() -export default class BillSubscriber { - tenancy: TenancyService; - logger: any; - itemsEntriesService: ItemsEntriesService; - - /** - * Constructor method. - */ - constructor() { - this.tenancy = Container.get(TenancyService); - this.logger = Container.get('logger'); - this.itemsEntriesService = Container.get(ItemsEntriesService); - } - - /** - * Increments the sale invoice items once the invoice created. - */ - @On(events.bill.onCreated) - public async handleDecrementSaleInvoiceItemsQuantity({ tenantId, bill }) { - await this.itemsEntriesService.incrementItemsEntries( - tenantId, - bill.entries - ); - } - - /** - * Decrements the sale invoice items once the invoice deleted. - */ - @On(events.bill.onDeleted) - public async handleIncrementSaleInvoiceItemsQuantity({ tenantId, oldBill }) { - await this.itemsEntriesService.decrementItemsQuantity( - tenantId, - oldBill.entries - ); - } - - /** - * Handle increment/decrement the different items quantity once the sale invoice be edited. - */ - @On(events.bill.onEdited) - public async handleChangeSaleInvoiceItemsQuantityOnEdit({ - tenantId, - bill, - oldBill, - }) { - await this.itemsEntriesService.changeItemsQuantity( - tenantId, - bill.entries, - oldBill.entries - ); - } -} diff --git a/server/src/subscribers/Inventory/Inventory.ts b/server/src/subscribers/Inventory/Inventory.ts index d346aef43..7caf72859 100644 --- a/server/src/subscribers/Inventory/Inventory.ts +++ b/server/src/subscribers/Inventory/Inventory.ts @@ -3,16 +3,21 @@ import { EventSubscriber, On } from 'event-dispatch'; import { map, head } from 'lodash'; import events from 'subscribers/events'; import SaleInvoicesCost from 'services/Sales/SalesInvoicesCost'; +import InventoryItemsQuantitySync from 'services/Inventory/InventoryItemsQuantitySync'; +import { InventoryTransaction } from 'models'; @EventSubscriber() export class InventorySubscriber { depends: number = 0; startingDate: Date; saleInvoicesCost: SaleInvoicesCost; + + itemsQuantitySync: InventoryItemsQuantitySync; agenda: any; constructor() { this.saleInvoicesCost = Container.get(SaleInvoicesCost); + this.itemsQuantitySync = Container.get(InventoryItemsQuantitySync); this.agenda = Container.get('agenda'); } @@ -30,7 +35,7 @@ export class InventorySubscriber { if (dependsComputeJobs.length === 0) { this.startingDate = null; - await this.saleInvoicesCost.scheduleWriteJournalEntries( + await this.saleInvoicesCost.scheduleWriteJournalEntries( tenantId, startingDate ); @@ -38,12 +43,46 @@ export class InventorySubscriber { } /** - * + * Sync inventory items quantity once inventory transactions created. + */ + @On(events.inventory.onInventoryTransactionsCreated) + async syncItemsQuantityOnceInventoryTransactionsCreated({ + tenantId, + inventoryTransactions, + }) { + const itemsQuantityChanges = this.itemsQuantitySync.getItemsQuantityChanges( + inventoryTransactions + ); + await this.itemsQuantitySync.changeItemsQuantity( + tenantId, + itemsQuantityChanges + ); + } + + /** + * Sync inventory items quantity once inventory transactions deleted. + */ + @On(events.inventory.onInventoryTransactionsDeleted) + async syncItemsQuantityOnceInventoryTransactionsDeleted({ + tenantId, + oldInventoryTransactions, + }) { + const itemsQuantityChanges = this.itemsQuantitySync.getReverseItemsQuantityChanges( + oldInventoryTransactions + ); + await this.itemsQuantitySync.changeItemsQuantity( + tenantId, + itemsQuantityChanges + ); + } + + /** + * */ @On(events.inventory.onInventoryTransactionsCreated) async handleScheduleItemsCostOnInventoryTransactionsCreated({ tenantId, - inventoryTransactions + inventoryTransactions, }) { const inventoryItemsIds = map(inventoryTransactions, 'itemId'); @@ -61,7 +100,7 @@ export class InventorySubscriber { tenantId, transactionType, transactionId, - oldInventoryTransactions + oldInventoryTransactions, }) { // Ignore compute item cost with theses transaction types. const ignoreWithTransactionTypes = ['OpeningItem']; diff --git a/server/src/subscribers/Inventory/InventoryAdjustment.ts b/server/src/subscribers/Inventory/InventoryAdjustment.ts index cd39d39a7..b4b494b5a 100644 --- a/server/src/subscribers/Inventory/InventoryAdjustment.ts +++ b/server/src/subscribers/Inventory/InventoryAdjustment.ts @@ -33,7 +33,7 @@ export default class InventoryAdjustmentsSubscriber { await this.inventoryAdjustment.writeInventoryTransactions( tenantId, inventoryAdjustment - ) + ); } /** @@ -43,10 +43,10 @@ export default class InventoryAdjustmentsSubscriber { async handleRevertInventoryTransactionsOnceDeleted({ tenantId, inventoryAdjustmentId, - oldInventoryTransaction, + oldInventoryAdjustment, }) { // Can't continue if the inventory adjustment is not published. - if (!oldInventoryTransaction.isPublished) { return; } + if (!oldInventoryAdjustment.isPublished) { return; } await this.inventoryAdjustment.revertInventoryTransactions( tenantId, diff --git a/server/src/subscribers/SaleInvoices/SyncItemsQuantity.ts b/server/src/subscribers/SaleInvoices/SyncItemsQuantity.ts deleted file mode 100644 index 2eb8a0c76..000000000 --- a/server/src/subscribers/SaleInvoices/SyncItemsQuantity.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { Container } from 'typedi'; -import { On, EventSubscriber } from 'event-dispatch'; -import events from 'subscribers/events'; -import TenancyService from 'services/Tenancy/TenancyService'; -import ItemsEntriesService from 'services/Items/ItemsEntriesService'; - -@EventSubscriber() -export default class SyncItemsQuantityWithInvoices { - logger: any; - tenancy: TenancyService; - itemsEntriesService: ItemsEntriesService; - - /** - * Constructor method. - */ - constructor() { - this.logger = Container.get('logger'); - this.tenancy = Container.get(TenancyService); - this.itemsEntriesService = Container.get(ItemsEntriesService); - } - - /** - * Increments the sale invoice items once the invoice created. - */ - @On(events.saleInvoice.onCreated) - public async handleDecrementSaleInvoiceItemsQuantity({ - tenantId, - saleInvoice, - }) { - await this.itemsEntriesService.decrementItemsQuantity( - tenantId, - saleInvoice.entries - ); - } - - /** - * Decrements the sale invoice items once the invoice deleted. - */ - @On(events.saleInvoice.onDeleted) - public async handleIncrementSaleInvoiceItemsQuantity({ - tenantId, - oldSaleInvoice, - }) { - await this.itemsEntriesService.incrementItemsEntries( - tenantId, - oldSaleInvoice.entries - ); - } - - /** - * Handle increment/decrement the different items quantity once the sale invoice be edited. - */ - @On(events.saleInvoice.onEdited) - public async handleChangeSaleInvoiceItemsQuantityOnEdit({ - tenantId, - saleInvoice, - oldSaleInvoice, - }) { - await this.itemsEntriesService.changeItemsQuantity( - tenantId, - saleInvoice.entries.map((entry) => ({ - ...entry, - quantity: entry.quantity * -1, - })), - oldSaleInvoice.entries.map((entry) => ({ - ...entry, - quantity: entry.quantity * -1, - })) - ); - } -} diff --git a/server/src/subscribers/SaleReceipt/SyncItemsQuantity.ts b/server/src/subscribers/SaleReceipt/SyncItemsQuantity.ts deleted file mode 100644 index 80f4aec37..000000000 --- a/server/src/subscribers/SaleReceipt/SyncItemsQuantity.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { Container } from 'typedi'; -import { On, EventSubscriber } from 'event-dispatch'; -import events from 'subscribers/events'; -import TenancyService from 'services/Tenancy/TenancyService'; -import ItemsEntriesService from 'services/Items/ItemsEntriesService'; -import SalesInvoicesCost from 'services/Sales/SalesInvoicesCost'; - -@EventSubscriber() -export default class SaleReceiptSubscriber { - logger: any; - tenancy: TenancyService; - itemsEntriesService: ItemsEntriesService; - - constructor() { - this.logger = Container.get('logger'); - this.tenancy = Container.get(TenancyService); - this.itemsEntriesService = Container.get(ItemsEntriesService); - } - - /** - * Increments the sale receipt items once be created. - */ - @On(events.saleReceipt.onCreated) - public async handleDecremenReceiptItemsQuantity({ tenantId, saleReceipt }) { - await this.itemsEntriesService.decrementItemsQuantity( - tenantId, - saleReceipt.entries - ); - } - - /** - * Decrements the sale receipt items once be deleted. - */ - @On(events.saleReceipt.onDeleted) - public async handleIncrementReceiptItemsQuantity({ - tenantId, - oldSaleReceipt, - }) { - await this.itemsEntriesService.incrementItemsEntries( - tenantId, - oldSaleReceipt.entries - ); - } - - /** - * Handle increment/decrement the different items quantity once - * the sale receipt be edited. - */ - @On(events.saleReceipt.onEdited) - public async handleChangeSaleInvoiceItemsQuantityOnEdit({ - tenantId, - saleReceipt, - oldSaleReceipt, - }) { - await this.itemsEntriesService.changeItemsQuantity( - tenantId, - saleReceipt.entries.map((entry) => ({ - ...entry, - quantity: entry.quantity * -1, - })), - oldSaleReceipt.entries.map((entry) => ({ - ...entry, - quantity: entry.quantity * -1, - })) - ); - } -}