mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-15 12:20:31 +00:00
feat: Schedule to compute items cost.
This commit is contained in:
@@ -4,13 +4,14 @@ import InventoryService from '@/services/Inventory/Inventory';
|
||||
export default class ComputeItemCostJob {
|
||||
public async handler(job, done: Function): Promise<void> {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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<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.
|
||||
* @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<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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user