feat: Schedule to compute items cost.

This commit is contained in:
Ahmed Bouhuolia
2020-08-19 01:30:12 +02:00
parent 52d01b4ed8
commit c2a60e6ba5
4 changed files with 175 additions and 175 deletions

View File

@@ -4,13 +4,14 @@ import InventoryService from '@/services/Inventory/Inventory';
export default class ComputeItemCostJob { export default class ComputeItemCostJob {
public async handler(job, done: Function): Promise<void> { public async handler(job, done: Function): Promise<void> {
const Logger = Container.get('logger'); const Logger = Container.get('logger');
const { startingDate, itemId, costMethod } = job.attrs.data; const { startingDate, itemId, costMethod = 'FIFO' } = job.attrs.data;
try { try {
await InventoryService.computeItemCost(startingDate, itemId, costMethod); await InventoryService.computeItemCost(startingDate, itemId, costMethod);
Logger.log(`Compute item cost: ${job.attrs.data}`); Logger.log(`Compute item cost: ${job.attrs.data}`);
done(); done();
} catch(e) { } catch(e) {
console.log(e);
Logger.error(`Compute item cost: ${job.attrs.data}, error: ${e}`); Logger.error(`Compute item cost: ${job.attrs.data}, error: ${e}`);
done(e); done(e);
} }

View File

@@ -1,3 +1,4 @@
import { Container } from 'typedi';
import { import {
InventoryTransaction, InventoryTransaction,
Item, Item,
@@ -5,7 +6,6 @@ import {
} from '@/models'; } from '@/models';
import InventoryAverageCost from '@/services/Inventory/InventoryAverageCost'; import InventoryAverageCost from '@/services/Inventory/InventoryAverageCost';
import InventoryCostLotTracker from '@/services/Inventory/InventoryCostLotTracker'; import InventoryCostLotTracker from '@/services/Inventory/InventoryCostLotTracker';
import { option } from 'commander';
type TCostMethod = 'FIFO' | 'LIFO' | 'AVG'; type TCostMethod = 'FIFO' | 'LIFO' | 'AVG';
@@ -33,6 +33,23 @@ export default class InventoryService {
await costMethodComputer.computeItemCost() 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. * Records the inventory transactions.
* @param {Bill} bill * @param {Bill} bill
@@ -89,10 +106,6 @@ export default class InventoryService {
.delete(); .delete();
} }
revertInventoryLotsCost(fromDate?: Date) {
}
/** /**
* Retrieve the lot number after the increment. * Retrieve the lot number after the increment.
*/ */
@@ -102,7 +115,7 @@ export default class InventoryService {
.where('key', LOT_NUMBER_KEY) .where('key', LOT_NUMBER_KEY)
.increment('value', 1); .increment('value', 1);
if (effectRows) { if (effectRows === 0) {
await Option.tenant().query() await Option.tenant().query()
.insert({ .insert({
key: LOT_NUMBER_KEY, key: LOT_NUMBER_KEY,

View File

@@ -1,6 +1,5 @@
import { omit, sumBy, pick } from 'lodash'; import { omit, sumBy, pick } from 'lodash';
import moment from 'moment'; import moment from 'moment';
import { Container } from 'typedi';
import { import {
Account, Account,
Bill, Bill,
@@ -85,40 +84,6 @@ export default class BillsService {
return storedBill; 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. * Edits details of the given bill id with associated entries.
* *
@@ -185,6 +150,72 @@ export default class BillsService {
await this.scheduleComputeItemsCost(bill); 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. * Records the bill journal transactions.
* @async * @async
@@ -254,48 +285,21 @@ export default class BillsService {
} }
/** /**
* Deletes the bill with associated entries. * Schedule a job to re-compute the bill's items based on cost method
* @param {Integer} billId * of the each one.
* @return {void} * @param {Bill} bill
*/ */
static async deleteBill(billId) { static scheduleComputeItemsCost(bill) {
const bill = await Bill.tenant().query() const asyncOpers = [];
.where('id', billId)
.withGraphFetched('entries')
.first();
// Delete all associated bill entries. bill.entries.forEach((entry) => {
const deleteBillEntriesOper = ItemEntry.tenant() const oper = InventoryService.scheduleComputeItemCost(
.query() entry.item_id || entry.itemId,
.where('reference_type', 'Bill') bill.bill_date || bill.billDate,
.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. asyncOpers.push(oper);
const deleteInventoryTransOper = InventoryService.deleteInventoryTransactions( });
billId, 'Bill' return Promise.all(asyncOpers);
);
// 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);
} }
/** /**

View File

@@ -1,5 +1,4 @@
import { omit, sumBy, difference, pick } from 'lodash'; import { omit, sumBy, difference, pick } from 'lodash';
import { Container } from 'typedi';
import { import {
SaleInvoice, SaleInvoice,
AccountTransaction, AccountTransaction,
@@ -74,56 +73,6 @@ export default class SaleInvoicesService {
return storedInvoice; 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<Agenda>}
*/
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. * Edit the given sale invoice.
* @async * @async
@@ -179,42 +128,6 @@ export default class SaleInvoicesService {
await this.scheduleComputeItemsCost(saleInvoice); 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 * Deletes the given sale invoice with associated entries
* and journal transactions. * and journal transactions.
@@ -271,13 +184,82 @@ export default class SaleInvoicesService {
} }
/** /**
* Records the journal entries of sale invoice. * Records the inventory transactions from the givne sale invoice input.
* @async * @param {SaleInvoice} saleInvoice -
* @param {ISaleInvoice} saleInvoice * @param {number} saleInvoiceId -
* @return {void} * @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<Agenda>}
*/
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);
} }
/** /**