mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-19 22:30:31 +00:00
- fix: Schedule write journal entries after item compute cost.
- fix: active vouchers query. - fix: remove babel loader in server-side.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { omit, sumBy, difference, pick } from 'lodash';
|
||||
import { omit, sumBy, difference, pick, chain } from 'lodash';
|
||||
import {
|
||||
SaleInvoice,
|
||||
AccountTransaction,
|
||||
@@ -6,12 +6,14 @@ import {
|
||||
Account,
|
||||
ItemEntry,
|
||||
Customer,
|
||||
Item,
|
||||
} from '@/models';
|
||||
import JournalPoster from '@/services/Accounting/JournalPoster';
|
||||
import HasItemsEntries from '@/services/Sales/HasItemsEntries';
|
||||
import CustomerRepository from '@/repositories/CustomerRepository';
|
||||
import InventoryService from '@/services/Inventory/Inventory';
|
||||
import SalesInvoicesCost from '@/services/Sales/SalesInvoicesCost';
|
||||
import { ISaleInvoice, ISaleInvoiceOTD, IItemEntry } from '@/interfaces';
|
||||
import { formatDateFields } from '@/utils';
|
||||
|
||||
/**
|
||||
@@ -26,11 +28,11 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
||||
* @param {ISaleInvoice}
|
||||
* @return {ISaleInvoice}
|
||||
*/
|
||||
static async createSaleInvoice(saleInvoiceDTO: any) {
|
||||
static async createSaleInvoice(saleInvoiceDTO: ISaleInvoiceOTD) {
|
||||
const balance = sumBy(saleInvoiceDTO.entries, 'amount');
|
||||
const invLotNumber = await InventoryService.nextLotNumber();
|
||||
const saleInvoice = {
|
||||
...formatDateFields(saleInvoiceDTO, ['invoice_date', 'due_date']),
|
||||
const saleInvoice: ISaleInvoice = {
|
||||
...formatDateFields(saleInvoiceDTO, ['invoiceDate', 'dueDate']),
|
||||
balance,
|
||||
paymentAmount: 0,
|
||||
invLotNumber,
|
||||
@@ -70,7 +72,7 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
||||
);
|
||||
// Schedule sale invoice re-compute based on the item cost
|
||||
// method and starting date.
|
||||
await this.scheduleComputeInvoiceItemsCost(saleInvoice);
|
||||
await this.scheduleComputeInvoiceItemsCost(storedInvoice.id);
|
||||
|
||||
return storedInvoice;
|
||||
}
|
||||
@@ -92,7 +94,7 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
||||
balance,
|
||||
invLotNumber: oldSaleInvoice.invLotNumber,
|
||||
};
|
||||
const updatedSaleInvoices = await SaleInvoice.tenant()
|
||||
const updatedSaleInvoices: ISaleInvoice = await SaleInvoice.tenant()
|
||||
.query()
|
||||
.where('id', saleInvoiceId)
|
||||
.update({
|
||||
@@ -124,10 +126,9 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
||||
changeCustomerBalanceOper,
|
||||
recordInventoryTransOper,
|
||||
]);
|
||||
|
||||
// Schedule sale invoice re-compute based on the item cost
|
||||
// method and starting date.
|
||||
await this.scheduleComputeInvoiceItemsCost(saleInvoice);
|
||||
await this.scheduleComputeInvoiceItemsCost(saleInvoiceId, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -313,10 +314,51 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
||||
* @param {ISaleInvoice} saleInvoice
|
||||
* @return {Promise}
|
||||
*/
|
||||
static scheduleComputeInvoiceItemsCost(saleInvoice) {
|
||||
return this.scheduleComputeItemsCost(
|
||||
saleInvoice.entries.map((e) => e.item_id),
|
||||
saleInvoice.invoice_date,
|
||||
);
|
||||
static async scheduleComputeInvoiceItemsCost(saleInvoiceId: number, override?: boolean) {
|
||||
const saleInvoice: ISaleInvoice = await SaleInvoice.tenant()
|
||||
.query()
|
||||
.findById(saleInvoiceId)
|
||||
.withGraphFetched('entries.item');
|
||||
|
||||
const inventoryItemsIds = chain(saleInvoice.entries)
|
||||
.filter((entry: IItemEntry) => entry.item.type === 'inventory')
|
||||
.map((entry: IItemEntry) => entry.itemId)
|
||||
.uniq().value();
|
||||
|
||||
if (inventoryItemsIds.length === 0) {
|
||||
await this.writeNonInventoryInvoiceJournals(saleInvoice, override);
|
||||
} else {
|
||||
await this.scheduleComputeItemsCost(
|
||||
inventoryItemsIds,
|
||||
saleInvoice.invoice_date,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the sale invoice journal entries.
|
||||
* @param {SaleInvoice} saleInvoice -
|
||||
*/
|
||||
static async writeNonInventoryInvoiceJournals(saleInvoice: ISaleInvoice, override: boolean) {
|
||||
const accountsDepGraph = await Account.tenant().depGraph().query();
|
||||
const journal = new JournalPoster(accountsDepGraph);
|
||||
|
||||
if (override) {
|
||||
const oldTransactions = await AccountTransaction.tenant()
|
||||
.query()
|
||||
.where('reference_type', 'SaleInvoice')
|
||||
.where('reference_id', saleInvoice.id)
|
||||
.withGraphFetched('account.type');
|
||||
|
||||
journal.loadEntries(oldTransactions);
|
||||
journal.removeEntries();
|
||||
}
|
||||
this.saleInvoiceJournal(saleInvoice, journal);
|
||||
|
||||
await Promise.all([
|
||||
journal.deleteEntries(),
|
||||
journal.saveEntries(),
|
||||
journal.saveBalance(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,31 +9,27 @@ import JournalPoster from '@/services/Accounting/JournalPoster';
|
||||
import JournalEntry from '@/services/Accounting/JournalEntry';
|
||||
import InventoryService from '@/services/Inventory/Inventory';
|
||||
import { ISaleInvoice, IItemEntry, IItem } from '@/interfaces';
|
||||
import { ISaleInvoice } from '../../interfaces';
|
||||
|
||||
export default class SaleInvoicesCost {
|
||||
/**
|
||||
* Schedule sale invoice re-compute based on the item
|
||||
* cost method and starting date.
|
||||
* @param {number[]} itemIds -
|
||||
* @param {Date} startingDate -
|
||||
* @param {number[]} itemIds - Inventory items ids.
|
||||
* @param {Date} startingDate - Starting compute cost date.
|
||||
* @return {Promise<Agenda>}
|
||||
*/
|
||||
static async scheduleComputeItemsCost(itemIds: number[], startingDate: Date) {
|
||||
const items: IItem[] = await Item.tenant().query().whereIn('id', itemIds);
|
||||
|
||||
const inventoryItems: IItem[] = items.filter((item: IItem) => item.type === 'inventory');
|
||||
static async scheduleComputeItemsCost(inventoryItemsIds: number[], startingDate: Date) {
|
||||
const asyncOpers: Promise<[]>[] = [];
|
||||
|
||||
inventoryItems.forEach((item: IItem) => {
|
||||
inventoryItemsIds.forEach((inventoryItemId: number) => {
|
||||
const oper: Promise<[]> = InventoryService.scheduleComputeItemCost(
|
||||
item.id,
|
||||
inventoryItemId,
|
||||
startingDate,
|
||||
);
|
||||
asyncOpers.push(oper);
|
||||
});
|
||||
const writeJEntriesOper: Promise<any> = this.scheduleWriteJournalEntries(startingDate);
|
||||
|
||||
return Promise.all([...asyncOpers, writeJEntriesOper]);
|
||||
return Promise.all([...asyncOpers]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,7 +59,6 @@ export default class SaleInvoicesCost {
|
||||
builder.withGraphFetched('entries.item')
|
||||
builder.withGraphFetched('costTransactions(groupedEntriesCost)');
|
||||
});
|
||||
|
||||
const accountsDepGraph = await Account.tenant().depGraph().query();
|
||||
const journal = new JournalPoster(accountsDepGraph);
|
||||
|
||||
@@ -79,62 +74,9 @@ export default class SaleInvoicesCost {
|
||||
journal.loadEntries(oldTransactions);
|
||||
journal.removeEntries();
|
||||
}
|
||||
const receivableAccount = { id: 10 };
|
||||
|
||||
salesInvoices.forEach((saleInvoice: ISaleInvoice) => {
|
||||
let inventoryTotal: number = 0;
|
||||
const commonEntry = {
|
||||
referenceType: 'SaleInvoice',
|
||||
referenceId: saleInvoice.id,
|
||||
date: saleInvoice.invoiceDate,
|
||||
};
|
||||
const costTransactions: Map<number, number> = new Map(
|
||||
saleInvoice.costTransactions.map((trans: IItemEntry) => [
|
||||
trans.entryId, trans.cost,
|
||||
]),
|
||||
);
|
||||
// XXX Debit - Receivable account.
|
||||
const receivableEntry = new JournalEntry({
|
||||
...commonEntry,
|
||||
debit: saleInvoice.balance,
|
||||
account: receivableAccount.id,
|
||||
});
|
||||
journal.debit(receivableEntry);
|
||||
|
||||
saleInvoice.entries.forEach((entry: IItemEntry) => {
|
||||
const cost: number = costTransactions.get(entry.id);
|
||||
const income: number = entry.quantity * entry.rate;
|
||||
|
||||
if (entry.item.type === 'inventory' && cost) {
|
||||
// XXX Debit - Cost account.
|
||||
const costEntry = new JournalEntry({
|
||||
...commonEntry,
|
||||
debit: cost,
|
||||
account: entry.item.costAccountId,
|
||||
note: entry.description,
|
||||
});
|
||||
journal.debit(costEntry);
|
||||
inventoryTotal += cost;
|
||||
}
|
||||
// XXX Credit - Income account.
|
||||
const incomeEntry = new JournalEntry({
|
||||
...commonEntry,
|
||||
credit: income,
|
||||
account: entry.item.sellAccountId,
|
||||
note: entry.description,
|
||||
});
|
||||
journal.credit(incomeEntry);
|
||||
|
||||
if (inventoryTotal > 0) {
|
||||
// XXX Credit - Inventory account.
|
||||
const inventoryEntry = new JournalEntry({
|
||||
...commonEntry,
|
||||
credit: inventoryTotal,
|
||||
account: entry.item.inventoryAccountId,
|
||||
});
|
||||
journal.credit(inventoryEntry);
|
||||
}
|
||||
});
|
||||
salesInvoices.forEach((saleInvoice: ISaleInvoice) => {
|
||||
this.saleInvoiceJournal(saleInvoice, journal);
|
||||
});
|
||||
return Promise.all([
|
||||
journal.deleteEntries(),
|
||||
@@ -142,4 +84,66 @@ export default class SaleInvoicesCost {
|
||||
journal.saveBalance(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes journal entries for given sale invoice.
|
||||
* @param {ISaleInvoice} saleInvoice
|
||||
* @param {JournalPoster} journal
|
||||
*/
|
||||
static saleInvoiceJournal(saleInvoice: ISaleInvoice, journal: JournalPoster) {
|
||||
let inventoryTotal: number = 0;
|
||||
const receivableAccount = { id: 10 };
|
||||
const commonEntry = {
|
||||
referenceType: 'SaleInvoice',
|
||||
referenceId: saleInvoice.id,
|
||||
date: saleInvoice.invoiceDate,
|
||||
};
|
||||
const costTransactions: Map<number, number> = new Map(
|
||||
saleInvoice?.costTransactions?.map((trans: IItemEntry) => [
|
||||
trans.entryId, trans.cost,
|
||||
]),
|
||||
);
|
||||
// XXX Debit - Receivable account.
|
||||
const receivableEntry = new JournalEntry({
|
||||
...commonEntry,
|
||||
debit: saleInvoice.balance,
|
||||
account: receivableAccount.id,
|
||||
});
|
||||
journal.debit(receivableEntry);
|
||||
|
||||
saleInvoice.entries.forEach((entry: IItemEntry) => {
|
||||
const cost: number = costTransactions.get(entry.id);
|
||||
const income: number = entry.quantity * entry.rate;
|
||||
|
||||
if (entry.item.type === 'inventory' && cost) {
|
||||
// XXX Debit - Cost account.
|
||||
const costEntry = new JournalEntry({
|
||||
...commonEntry,
|
||||
debit: cost,
|
||||
account: entry.item.costAccountId,
|
||||
note: entry.description,
|
||||
});
|
||||
journal.debit(costEntry);
|
||||
inventoryTotal += cost;
|
||||
}
|
||||
// XXX Credit - Income account.
|
||||
const incomeEntry = new JournalEntry({
|
||||
...commonEntry,
|
||||
credit: income,
|
||||
account: entry.item.sellAccountId,
|
||||
note: entry.description,
|
||||
});
|
||||
journal.credit(incomeEntry);
|
||||
|
||||
if (inventoryTotal > 0) {
|
||||
// XXX Credit - Inventory account.
|
||||
const inventoryEntry = new JournalEntry({
|
||||
...commonEntry,
|
||||
credit: inventoryTotal,
|
||||
account: entry.item.inventoryAccountId,
|
||||
});
|
||||
journal.credit(inventoryEntry);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user