diff --git a/server/src/jobs/ComputeItemCost.ts b/server/src/jobs/ComputeItemCost.ts index 09b069e03..e296c051f 100644 --- a/server/src/jobs/ComputeItemCost.ts +++ b/server/src/jobs/ComputeItemCost.ts @@ -4,13 +4,14 @@ import InventoryService from '@/services/Inventory/Inventory'; export default class ComputeItemCostJob { public async handler(job, done: Function): Promise { const Logger = Container.get('logger'); - const { startingDate, itemId, costMethod } = job.attrs.data; + const { startingDate, itemId, costMethod = 'FIFO' } = job.attrs.data; try { await InventoryService.computeItemCost(startingDate, itemId, costMethod); Logger.log(`Compute item cost: ${job.attrs.data}`); done(); } catch(e) { + console.log(e); Logger.error(`Compute item cost: ${job.attrs.data}, error: ${e}`); done(e); } diff --git a/server/src/services/Inventory/Inventory.ts b/server/src/services/Inventory/Inventory.ts index 381b37a4f..d82b34970 100644 --- a/server/src/services/Inventory/Inventory.ts +++ b/server/src/services/Inventory/Inventory.ts @@ -1,3 +1,4 @@ +import { Container } from 'typedi'; import { InventoryTransaction, Item, @@ -5,7 +6,6 @@ import { } from '@/models'; import InventoryAverageCost from '@/services/Inventory/InventoryAverageCost'; import InventoryCostLotTracker from '@/services/Inventory/InventoryCostLotTracker'; -import { option } from 'commander'; type TCostMethod = 'FIFO' | 'LIFO' | 'AVG'; @@ -33,6 +33,23 @@ export default class InventoryService { await costMethodComputer.computeItemCost() } + /** + * SChedule item cost compute job. + * @param {number} itemId + * @param {Date} startingDate + */ + static async scheduleComputeItemCost(itemId: number, startingDate: Date|string) { + const agenda = Container.get('agenda'); + + // Delete the scheduled job in case has the same given data. + await agenda.cancel({ + name: 'compute-item-cost', + }); + return agenda.schedule('in 3 seconds', 'compute-item-cost', { + startingDate, itemId, + }); + } + /** * Records the inventory transactions. * @param {Bill} bill @@ -89,10 +106,6 @@ export default class InventoryService { .delete(); } - revertInventoryLotsCost(fromDate?: Date) { - - } - /** * Retrieve the lot number after the increment. */ @@ -102,7 +115,7 @@ export default class InventoryService { .where('key', LOT_NUMBER_KEY) .increment('value', 1); - if (effectRows) { + if (effectRows === 0) { await Option.tenant().query() .insert({ key: LOT_NUMBER_KEY, diff --git a/server/src/services/Purchases/Bills.js b/server/src/services/Purchases/Bills.js index b115b732c..c44d9dd3d 100644 --- a/server/src/services/Purchases/Bills.js +++ b/server/src/services/Purchases/Bills.js @@ -1,6 +1,5 @@ import { omit, sumBy, pick } from 'lodash'; import moment from 'moment'; -import { Container } from 'typedi'; import { Account, Bill, @@ -36,7 +35,7 @@ export default class BillsService { * @param {IBill} bill - * @return {void} */ - static async createBill(billDTO) { + static async createBill(billDTO) { const invLotNumber = await InventoryService.nextLotNumber(); const bill = { ...formatDateFields(billDTO, ['bill_date', 'due_date']), @@ -85,40 +84,6 @@ export default class BillsService { return storedBill; } - /** - * Schedule a job to re-compute the bill's items based on cost method - * of the each one. - * @param {Bill} bill - */ - static scheduleComputeItemsCost(bill) { - const agenda = Container.get('agenda'); - - return agenda.schedule('in 1 second', 'compute-item-cost', { - startingDate: bill.bill_date || bill.billDate, - itemId: bill.entries[0].item_id || bill.entries[0].itemId, - costMethod: 'FIFO', - }); - } - - /** - * Records the inventory transactions from the given bill input. - * @param {Bill} bill - * @param {number} billId - */ - static recordInventoryTransactions(bill, billId, override) { - const inventoryTransactions = bill.entries - .map((entry) => ({ - ...pick(entry, ['item_id', 'quantity', 'rate']), - lotNumber: bill.invLotNumber, - transactionType: 'Bill', - transactionId: billId, - direction: 'IN', - date: bill.bill_date, - })); - - return InventoryService.recordInventoryTransactions(inventoryTransactions, override); - } - /** * Edits details of the given bill id with associated entries. * @@ -185,6 +150,72 @@ export default class BillsService { await this.scheduleComputeItemsCost(bill); } + /** + * Deletes the bill with associated entries. + * @param {Integer} billId + * @return {void} + */ + static async deleteBill(billId) { + const bill = await Bill.tenant().query() + .where('id', billId) + .withGraphFetched('entries') + .first(); + + // Delete all associated bill entries. + const deleteBillEntriesOper = ItemEntry.tenant() + .query() + .where('reference_type', 'Bill') + .where('reference_id', billId) + .delete(); + + // Delete the bill transaction. + const deleteBillOper = Bill.tenant().query().where('id', billId).delete(); + + // Delete associated bill journal transactions. + const deleteTransactionsOper = JournalPosterService.deleteJournalTransactions( + billId, + 'Bill' + ); + // Delete bill associated inventory transactions. + const deleteInventoryTransOper = InventoryService.deleteInventoryTransactions( + billId, 'Bill' + ); + // Revert vendor balance. + const revertVendorBalance = Vendor.changeBalance(bill.vendorId, bill.amount * -1); + + await Promise.all([ + deleteBillOper, + deleteBillEntriesOper, + deleteTransactionsOper, + deleteInventoryTransOper, + revertVendorBalance, + ]); + // Schedule sale invoice re-compute based on the item cost + // method and starting date. + await this.scheduleComputeItemsCost(bill); + } + + /** + * Records the inventory transactions from the given bill input. + * @param {Bill} bill + * @param {number} billId + */ + static recordInventoryTransactions(bill, billId, override) { + const inventoryTransactions = bill.entries + .map((entry) => ({ + ...pick(entry, ['item_id', 'quantity', 'rate']), + lotNumber: bill.invLotNumber, + transactionType: 'Bill', + transactionId: billId, + direction: 'IN', + date: bill.bill_date, + })); + + return InventoryService.recordInventoryTransactions( + inventoryTransactions, override + ); + } + /** * Records the bill journal transactions. * @async @@ -254,48 +285,21 @@ export default class BillsService { } /** - * Deletes the bill with associated entries. - * @param {Integer} billId - * @return {void} + * Schedule a job to re-compute the bill's items based on cost method + * of the each one. + * @param {Bill} bill */ - static async deleteBill(billId) { - const bill = await Bill.tenant().query() - .where('id', billId) - .withGraphFetched('entries') - .first(); + static scheduleComputeItemsCost(bill) { + const asyncOpers = []; - // Delete all associated bill entries. - const deleteBillEntriesOper = ItemEntry.tenant() - .query() - .where('reference_type', 'Bill') - .where('reference_id', billId) - .delete(); - - // Delete the bill transaction. - const deleteBillOper = Bill.tenant().query().where('id', billId).delete(); - - // Delete associated bill journal transactions. - const deleteTransactionsOper = JournalPosterService.deleteJournalTransactions( - billId, - 'Bill' - ); - // Delete bill associated inventory transactions. - const deleteInventoryTransOper = InventoryService.deleteInventoryTransactions( - billId, 'Bill' - ); - // Revert vendor balance. - const revertVendorBalance = Vendor.changeBalance(bill.vendorId, bill.amount * -1); - - await Promise.all([ - deleteBillOper, - deleteBillEntriesOper, - deleteTransactionsOper, - deleteInventoryTransOper, - revertVendorBalance, - ]); - // Schedule sale invoice re-compute based on the item cost - // method and starting date. - await this.scheduleComputeItemsCost(bill); + bill.entries.forEach((entry) => { + const oper = InventoryService.scheduleComputeItemCost( + entry.item_id || entry.itemId, + bill.bill_date || bill.billDate, + ); + asyncOpers.push(oper); + }); + return Promise.all(asyncOpers); } /** diff --git a/server/src/services/Sales/SalesInvoices.ts b/server/src/services/Sales/SalesInvoices.ts index 913fcca43..837ac7fe7 100644 --- a/server/src/services/Sales/SalesInvoices.ts +++ b/server/src/services/Sales/SalesInvoices.ts @@ -1,5 +1,4 @@ import { omit, sumBy, difference, pick } from 'lodash'; -import { Container } from 'typedi'; import { SaleInvoice, AccountTransaction, @@ -74,56 +73,6 @@ export default class SaleInvoicesService { return storedInvoice; } - /** - * Records the sale invoice journal entries and calculate the items cost - * based on the given cost method in the options FIFO, LIFO or average cost rate. - * - * @param {SaleInvoice} saleInvoice - - * @param {Array} inventoryTransactions - - */ - static async recordJournalEntries(saleInvoice: any, inventoryTransactions: array[]) { - - } - - /** - * Records the inventory transactions from the givne sale invoice input. - * @param {SaleInvoice} saleInvoice - - * @param {number} saleInvoiceId - - * @param {boolean} override - - */ - static recordInventoryTranscactions(saleInvoice, saleInvoiceId: number, override?: boolean){ - 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, - })); - - return InventoryService.recordInventoryTransactions( - inventortyTransactions, override, - ); - } - - /** - * Schedule sale invoice re-compute based on the item - * cost method and starting date - * - * @param {SaleInvoice} saleInvoice - - * @return {Promise} - */ - static scheduleComputeItemsCost(saleInvoice) { - const agenda = Container.get('agenda'); - - return agenda.schedule('in 1 second', 'compute-item-cost', { - startingDate: saleInvoice.invoice_date || saleInvoice.invoiceDate, - itemId: saleInvoice.entries[0].item_id || saleInvoice.entries[0].itemId, - costMethod: 'FIFO', - }); - } - /** * Edit the given sale invoice. * @async @@ -179,42 +128,6 @@ export default class SaleInvoicesService { await this.scheduleComputeItemsCost(saleInvoice); } - /** - * Deletes the inventory transactions. - * @param {string} transactionType - * @param {number} transactionId - */ - static async revertInventoryTransactions(inventoryTransactions: array) { - const opers: Promise<[]>[] = []; - - inventoryTransactions.forEach((trans: any) => { - switch(trans.direction) { - case 'OUT': - if (trans.inventoryTransactionId) { - const revertRemaining = InventoryTransaction.tenant() - .query() - .where('id', trans.inventoryTransactionId) - .where('direction', 'OUT') - .increment('remaining', trans.quanitity); - - opers.push(revertRemaining); - } - break; - case 'IN': - const removeRelationOper = InventoryTransaction.tenant() - .query() - .where('inventory_transaction_id', trans.id) - .where('direction', 'IN') - .update({ - inventory_transaction_id: null, - }); - opers.push(removeRelationOper); - break; - } - }); - return Promise.all(opers); - } - /** * Deletes the given sale invoice with associated entries * and journal transactions. @@ -271,13 +184,82 @@ export default class SaleInvoicesService { } /** - * Records the journal entries of sale invoice. - * @async - * @param {ISaleInvoice} saleInvoice - * @return {void} + * Records the inventory transactions from the givne sale invoice input. + * @param {SaleInvoice} saleInvoice - + * @param {number} saleInvoiceId - + * @param {boolean} override - */ - async recordJournalEntries(saleInvoice: any) { + static recordInventoryTranscactions(saleInvoice, saleInvoiceId: number, override?: boolean){ + 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, + })); + return InventoryService.recordInventoryTransactions( + inventortyTransactions, override, + ); + } + + /** + * Schedule sale invoice re-compute based on the item + * cost method and starting date + * + * @private + * @param {SaleInvoice} saleInvoice - + * @return {Promise} + */ + private static scheduleComputeItemsCost(saleInvoice: any) { + const asyncOpers: Promise<[]>[] = []; + + saleInvoice.entries.forEach((entry: any) => { + const oper: Promise<[]> = InventoryService.scheduleComputeItemCost( + entry.item_id || entry.itemId, + saleInvoice.bill_date || saleInvoice.billDate, + ); + asyncOpers.push(oper); + }); + return Promise.all(asyncOpers); + } + + /** + * Deletes the inventory transactions. + * @param {string} transactionType + * @param {number} transactionId + */ + static async revertInventoryTransactions(inventoryTransactions: array) { + const opers: Promise<[]>[] = []; + + inventoryTransactions.forEach((trans: any) => { + switch(trans.direction) { + case 'OUT': + if (trans.inventoryTransactionId) { + const revertRemaining = InventoryTransaction.tenant() + .query() + .where('id', trans.inventoryTransactionId) + .where('direction', 'OUT') + .increment('remaining', trans.quanitity); + + opers.push(revertRemaining); + } + break; + case 'IN': + const removeRelationOper = InventoryTransaction.tenant() + .query() + .where('inventory_transaction_id', trans.id) + .where('direction', 'IN') + .update({ + inventory_transaction_id: null, + }); + opers.push(removeRelationOper); + break; + } + }); + return Promise.all(opers); } /**