diff --git a/server/src/api/index.ts b/server/src/api/index.ts index 08d915910..359803e70 100644 --- a/server/src/api/index.ts +++ b/server/src/api/index.ts @@ -81,7 +81,6 @@ export default () => { dashboard.use(EnsureConfiguredMiddleware); dashboard.use(EnsureTenantIsSeeded); - dashboard.use('/users', Container.get(Users).router()); dashboard.use('/invite', Container.get(InviteUsers).authRouter()); dashboard.use('/currencies', Container.get(Currencies).router()); diff --git a/server/src/interfaces/InventoryTransaction.ts b/server/src/interfaces/InventoryTransaction.ts index 5b1d23bc8..5a896d708 100644 --- a/server/src/interfaces/InventoryTransaction.ts +++ b/server/src/interfaces/InventoryTransaction.ts @@ -9,6 +9,7 @@ export interface IInventoryTransaction { rate: number, transactionType: string, transactionId: string, + lotNumber: string, }; export interface IInventoryLotCost { diff --git a/server/src/services/Inventory/Inventory.ts b/server/src/services/Inventory/Inventory.ts index 54655992a..54f516b94 100644 --- a/server/src/services/Inventory/Inventory.ts +++ b/server/src/services/Inventory/Inventory.ts @@ -1,4 +1,5 @@ import { Container, Service, Inject } from 'typedi'; +import { IInventoryTransaction, IItem } from 'interfaces' import InventoryAverageCost from 'services/Inventory/InventoryAverageCost'; import InventoryCostLotTracker from 'services/Inventory/InventoryCostLotTracker'; import TenancyService from 'services/Tenancy/TenancyService'; @@ -60,27 +61,30 @@ export default class InventoryService { /** * Records the inventory transactions. - * @param {number} tenantId - Tenant id. - * @param {Bill} bill - * @param {number} billId + * @param {number} tenantId - Tenant id. + * @param {Bill} bill - Bill model object. + * @param {number} billId - Bill id. + * @return {Promise} */ async recordInventoryTransactions( tenantId: number, - entries: [], + entries: IInventoryTransaction[], deleteOld: boolean, - ) { + ): Promise { const { InventoryTransaction, Item } = this.tenancy.models(tenantId); - const entriesItemsIds = entries.map((e: any) => e.item_id); + // Mapping the inventory entries items ids. + const entriesItemsIds = entries.map((e: any) => e.itemId); const inventoryItems = await Item.query() .whereIn('id', entriesItemsIds) .where('type', 'inventory'); - const inventoryItemsIds = inventoryItems.map((i: any) => i.id); + // Mapping the inventory items ids. + const inventoryItemsIds = inventoryItems.map((i: IItem) => i.id); // Filter the bill entries that have inventory items. const inventoryEntries = entries.filter( - (entry: any) => inventoryItemsIds.indexOf(entry.item_id) !== -1 + (entry: IInventoryTransaction) => inventoryItemsIds.indexOf(entry.itemId) !== -1 ); inventoryEntries.forEach(async (entry: any) => { if (deleteOld) { diff --git a/server/src/services/Purchases/Bills.ts b/server/src/services/Purchases/Bills.ts index a74c5f3ee..c165cd9a3 100644 --- a/server/src/services/Purchases/Bills.ts +++ b/server/src/services/Purchases/Bills.ts @@ -23,6 +23,7 @@ import { IPaginationMeta, IFilterMeta, IBillsFilter, + IItemEntry, } from 'interfaces'; import { ServiceError } from 'exceptions'; import ItemsService from 'services/Items/ItemsService'; @@ -221,7 +222,6 @@ export default class BillsService extends SalesInvoicesCost { authorizedUser, null ); - // Retrieve vendor or throw not found service error. await this.getVendorOrThrowError(tenantId, billDTO.vendorId); @@ -372,29 +372,42 @@ export default class BillsService extends SalesInvoicesCost { * @param {Bill} bill * @param {number} billId */ - public recordInventoryTransactions( + public async recordInventoryTransactions( tenantId: number, - bill: any, - billId: number, + bill: IBill, override?: boolean - ) { - const inventoryTransactions = bill.entries.map((entry) => ({ - ...pick(entry, ['item_id', 'quantity', 'rate']), + ): Promise { + const invTransactions = bill.entries.map((entry: IItemEntry) => ({ + ...pick(entry, ['itemId', 'quantity', 'rate']), lotNumber: bill.invLotNumber, transactionType: 'Bill', - transactionId: billId, + transactionId: bill.id, direction: 'IN', - date: bill.bill_date, + date: bill.billDate, entryId: entry.id, })); - return this.inventoryService.recordInventoryTransactions( + await this.inventoryService.recordInventoryTransactions( tenantId, - inventoryTransactions, + invTransactions, override ); } + /** + * Reverts the inventory transactions of the given bill id. + * @param {number} tenantId - Tenant id. + * @param {number} billId - Bill id. + * @return {Promise} + */ + public async revertInventoryTransactions(tenantId: number, billId: number) { + await this.inventoryService.deleteInventoryTransactions( + tenantId, + billId, + 'Bill', + ); + } + /** * Records the bill journal transactions. * @async @@ -437,7 +450,6 @@ export default class BillsService extends SalesInvoicesCost { Bill, billsFilter ); - this.logger.info('[bills] trying to get bills data table.', { tenantId, billsFilter, diff --git a/server/src/services/Sales/SalesInvoices.ts b/server/src/services/Sales/SalesInvoices.ts index 8c5c38884..ce4243787 100644 --- a/server/src/services/Sales/SalesInvoices.ts +++ b/server/src/services/Sales/SalesInvoices.ts @@ -7,13 +7,13 @@ import { } from 'decorators/eventDispatcher'; import { ISaleInvoice, - ISaleInvoiceDTO, IItemEntry, + ISaleInvoiceCreateDTO, + ISaleInvoiceEditDTO, + IInventoryTransaction, ISalesInvoicesFilter, IPaginationMeta, IFilterMeta, - ISaleInvoiceCreateDTO, - ISaleInvoiceEditDTO, } from 'interfaces'; import events from 'subscribers/events'; import JournalPoster from 'services/Accounting/JournalPoster'; @@ -351,48 +351,68 @@ export default class SaleInvoicesService extends SalesInvoicesCost { .where('reference_type', 'SaleInvoice') .delete(); + // Triggers `onSaleInvoiceDeleted` event. await this.eventDispatcher.dispatch(events.saleInvoice.onDeleted, { tenantId, oldSaleInvoice, + saleInvoiceId, }); } /** * Records the inventory transactions from the givne sale invoice input. - * @param {SaleInvoice} saleInvoice - - * @param {number} saleInvoiceId - - * @param {boolean} override - + * @parma {number} tenantId - Tenant id. + * @param {SaleInvoice} saleInvoice - Sale invoice DTO. + * @param {number} saleInvoiceId - Sale invoice id. + * @param {boolean} override - Allow to override old transactions. */ - private recordInventoryTranscactions( + public recordInventoryTranscactions( tenantId: number, - saleInvoice, - saleInvoiceId: number, + saleInvoice: ISaleInvoice, override?: boolean ) { this.logger.info('[sale_invoice] saving inventory transactions'); - const inventortyTransactions = saleInvoice.entries.map((entry) => ({ - ...pick(entry, ['item_id', 'quantity', 'rate']), - lotNumber: saleInvoice.invLotNumber, - transactionType: 'SaleInvoice', - transactionId: saleInvoiceId, - direction: 'OUT', - date: saleInvoice.invoice_date, - entryId: entry.id, - })); + const invTransactions: IInventoryTransaction[] = saleInvoice.entries.map( + (entry: IItemEntry) => ({ + ...pick(entry, ['itemId', 'quantity', 'rate']), + lotNumber: 1, + transactionType: 'SaleInvoice', + transactionId: saleInvoice.id, + direction: 'OUT', + date: saleInvoice.invoiceDate, + entryId: entry.id, + }) + ); return this.inventoryService.recordInventoryTransactions( tenantId, - inventortyTransactions, + invTransactions, override ); } + /** + * Reverting the inventory transactions once the invoice deleted. + * @param {number} tenantId - Tenant id. + * @param {number} billId - Bill id. + */ + public revertInventoryTransactions( + tenantId: number, + billId: number + ): Promise { + return this.inventoryService.deleteInventoryTransactions( + tenantId, + billId, + 'SaleInvoice' + ); + } + /** * Deletes the inventory transactions. * @param {string} transactionType * @param {number} transactionId */ - private async revertInventoryTransactions( + private async revertInventoryTransactions_( tenantId: number, inventoryTransactions: array ) { diff --git a/server/src/subscribers/bills.ts b/server/src/subscribers/bills.ts index 99480315e..47ddbc8dc 100644 --- a/server/src/subscribers/bills.ts +++ b/server/src/subscribers/bills.ts @@ -108,4 +108,41 @@ export default class BillSubscriber { oldBill.vendorId ); } + + /** + * Handles writing the inventory transactions once bill created. + */ + @On(events.bill.onCreated) + async handleWritingInventoryTransactions({ tenantId, bill }) { + this.logger.info('[bill] writing the inventory transactions', { tenantId }); + this.billsService.recordInventoryTransactions( + tenantId, + bill, + ); + } + + /** + * Handles the overwriting the inventory transactions once bill edited. + */ + @On(events.bill.onEdited) + async handleOverwritingInventoryTransactions({ tenantId, bill }) { + this.logger.info('[bill] overwriting the inventory transactions.', { tenantId }); + this.billsService.recordInventoryTransactions( + tenantId, + bill, + true, + ); + } + + /** + * Handles the reverting the inventory transactions once the bill deleted. + */ + @On(events.bill.onDeleted) + async handleRevertInventoryTransactions({ tenantId, billId }) { + this.logger.info('[bill] reverting the bill inventory transactions', { tenantId, billId }); + this.billsService.revertInventoryTransactions( + tenantId, + billId, + ); + } } diff --git a/server/src/subscribers/saleInvoices.ts b/server/src/subscribers/saleInvoices.ts index 6fc279471..c035fbe80 100644 --- a/server/src/subscribers/saleInvoices.ts +++ b/server/src/subscribers/saleInvoices.ts @@ -4,6 +4,7 @@ import events from 'subscribers/events'; import TenancyService from 'services/Tenancy/TenancyService'; import SettingsService from 'services/Settings/SettingsService'; import SaleEstimateService from 'services/Sales/SalesEstimate'; +import SaleInvoicesService from 'services/Sales/SalesInvoices'; @EventSubscriber() export default class SaleInvoiceSubscriber { @@ -11,12 +12,14 @@ export default class SaleInvoiceSubscriber { tenancy: TenancyService; settingsService: SettingsService; saleEstimatesService: SaleEstimateService; + saleInvoicesService: SaleInvoicesService; constructor() { this.logger = Container.get('logger'); this.tenancy = Container.get(TenancyService); this.settingsService = Container.get(SettingsService); this.saleEstimatesService = Container.get(SaleEstimateService); + this.saleInvoicesService = Container.get(SaleInvoicesService); } /** @@ -114,4 +117,32 @@ export default class SaleInvoiceSubscriber { group: 'sales_invoices', }); } + + /** + * Handles the writing inventory transactions once the invoice created. + */ + @On(events.saleInvoice.onCreated) + public async handleWritingInventoryTransactions({ tenantId, saleInvoice }) { + this.logger.info('[sale_invoice] trying to write inventory transactions.', { + tenantId, + }); + await this.saleInvoicesService.recordInventoryTranscactions( + tenantId, + saleInvoice, + ); + } + + /** + * Handles deleting the inventory transactions once the invoice deleted. + */ + @On(events.saleInvoice.onDeleted) + public async handleDeletingInventoryTransactions({ tenantId, saleInvoiceId }) { + this.logger.info('[sale_invoice] trying to revert inventory transactions.', { + tenantId, saleInvoiceId, + }); + await this.saleInvoicesService.revertInventoryTransactions( + tenantId, + saleInvoiceId, + ); + } }