mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-19 14:20:31 +00:00
fix: design flow of writing invoice journal entries.
This commit is contained in:
@@ -272,10 +272,12 @@ export default class SaleInvoicesController extends BaseController {
|
|||||||
next: NextFunction
|
next: NextFunction
|
||||||
) {
|
) {
|
||||||
const { tenantId } = req;
|
const { tenantId } = req;
|
||||||
const filter: ISalesInvoicesFilter = {
|
const filter = {
|
||||||
filterRoles: [],
|
filterRoles: [],
|
||||||
sortOrder: 'asc',
|
sortOrder: 'asc',
|
||||||
columnSortBy: 'created_at',
|
columnSortBy: 'created_at',
|
||||||
|
page: 1,
|
||||||
|
pageSize: 12,
|
||||||
...this.matchedQueryData(req),
|
...this.matchedQueryData(req),
|
||||||
};
|
};
|
||||||
if (filter.stringifiedFilterRoles) {
|
if (filter.stringifiedFilterRoles) {
|
||||||
|
|||||||
@@ -164,4 +164,5 @@ export default {
|
|||||||
|
|
||||||
protocol: '',
|
protocol: '',
|
||||||
hostname: '',
|
hostname: '',
|
||||||
|
scheduleComputeItemCost: 'in 5 seconds'
|
||||||
};
|
};
|
||||||
@@ -9,7 +9,8 @@ export interface ISaleInvoice {
|
|||||||
dueAmount: number,
|
dueAmount: number,
|
||||||
customerId: number,
|
customerId: number,
|
||||||
entries: IItemEntry[],
|
entries: IItemEntry[],
|
||||||
deliveredAt: string|Date,
|
deliveredAt: string | Date,
|
||||||
|
userId: number,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ISaleInvoiceDTO {
|
export interface ISaleInvoiceDTO {
|
||||||
|
|||||||
@@ -2,12 +2,11 @@ import { Container } from 'typedi';
|
|||||||
import SalesInvoicesCost from 'services/Sales/SalesInvoicesCost';
|
import SalesInvoicesCost from 'services/Sales/SalesInvoicesCost';
|
||||||
|
|
||||||
export default class WriteInvoicesJournalEntries {
|
export default class WriteInvoicesJournalEntries {
|
||||||
|
|
||||||
constructor(agenda) {
|
constructor(agenda) {
|
||||||
agenda.define(
|
agenda.define(
|
||||||
'rewrite-invoices-journal-entries',
|
'rewrite-invoices-journal-entries',
|
||||||
{ priority: 'normal', concurrency: 1, },
|
{ priority: 'normal', concurrency: 1 },
|
||||||
this.handler.bind(this),
|
this.handler.bind(this)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17,14 +16,23 @@ export default class WriteInvoicesJournalEntries {
|
|||||||
|
|
||||||
const salesInvoicesCost = Container.get(SalesInvoicesCost);
|
const salesInvoicesCost = Container.get(SalesInvoicesCost);
|
||||||
|
|
||||||
Logger.info(`Write sales invoices journal entries - started: ${job.attrs.data}`);
|
Logger.info(
|
||||||
|
`Write sales invoices journal entries - started: ${job.attrs.data}`
|
||||||
|
);
|
||||||
try {
|
try {
|
||||||
await salesInvoicesCost.writeJournalEntries(tenantId, startingDate, true);
|
await salesInvoicesCost.writeInventoryCostJournalEntries(
|
||||||
Logger.info(`Write sales invoices journal entries - completed: ${job.attrs.data}`);
|
tenantId,
|
||||||
|
startingDate,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
Logger.info(
|
||||||
|
`Write sales invoices journal entries - completed: ${job.attrs.data}`
|
||||||
|
);
|
||||||
done();
|
done();
|
||||||
} catch(e) {
|
} catch (e) {
|
||||||
Logger.info(`Write sales invoices journal entries: ${job.attrs.data}, error: ${e}`);
|
Logger.info(
|
||||||
|
`Write sales invoices journal entries: ${job.attrs.data}, error: ${e}`
|
||||||
|
);
|
||||||
done(e);
|
done(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import dbManagerFactory from 'loaders/dbManager';
|
|||||||
import i18n from 'loaders/i18n';
|
import i18n from 'loaders/i18n';
|
||||||
import repositoriesLoader from 'loaders/systemRepositories';
|
import repositoriesLoader from 'loaders/systemRepositories';
|
||||||
import Cache from 'services/Cache';
|
import Cache from 'services/Cache';
|
||||||
import redisLoader from './redisLoader';
|
|
||||||
import rateLimiterLoaders from './rateLimiterLoader';
|
import rateLimiterLoaders from './rateLimiterLoader';
|
||||||
|
|
||||||
export default ({ mongoConnection, knex }) => {
|
export default ({ mongoConnection, knex }) => {
|
||||||
|
|||||||
@@ -23,14 +23,13 @@ export default class InventoryCostLotTracker extends TenantModel {
|
|||||||
static get modifiers() {
|
static get modifiers() {
|
||||||
return {
|
return {
|
||||||
groupedEntriesCost(query) {
|
groupedEntriesCost(query) {
|
||||||
query.select(['entry_id', 'transaction_id', 'transaction_type']);
|
query.select(['date', 'item_id', 'transaction_id', 'transaction_type']);
|
||||||
|
query.sum('cost as cost');
|
||||||
|
|
||||||
query.groupBy('item_id');
|
|
||||||
query.groupBy('entry_id');
|
|
||||||
query.groupBy('transaction_id');
|
query.groupBy('transaction_id');
|
||||||
query.groupBy('transaction_type');
|
query.groupBy('transaction_type');
|
||||||
|
query.groupBy('date');
|
||||||
query.sum('cost as cost');
|
query.groupBy('item_id');
|
||||||
},
|
},
|
||||||
filterDateRange(query, startDate, endDate, type = 'day') {
|
filterDateRange(query, startDate, endDate, type = 'day') {
|
||||||
const dateFormat = 'YYYY-MM-DD HH:mm:ss';
|
const dateFormat = 'YYYY-MM-DD HH:mm:ss';
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ interface IJournalTransactionsFilter {
|
|||||||
contactType?: string,
|
contactType?: string,
|
||||||
referenceType?: string[],
|
referenceType?: string[],
|
||||||
referenceId?: number[],
|
referenceId?: number[],
|
||||||
|
index: number|number[]
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class AccountTransactionsRepository extends TenantRepository {
|
export default class AccountTransactionsRepository extends TenantRepository {
|
||||||
@@ -50,6 +51,13 @@ export default class AccountTransactionsRepository extends TenantRepository {
|
|||||||
if (filter.referenceId && filter.referenceId.length > 0) {
|
if (filter.referenceId && filter.referenceId.length > 0) {
|
||||||
query.whereIn('reference_id', filter.referenceId);
|
query.whereIn('reference_id', filter.referenceId);
|
||||||
}
|
}
|
||||||
|
if (filter.index) {
|
||||||
|
if (Array.isArray(filter.index)) {
|
||||||
|
query.whereIn('index', filter.index);
|
||||||
|
} else {
|
||||||
|
query.where('index', filter.index);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { sumBy, chain } from 'lodash';
|
import { sumBy, chain } from 'lodash';
|
||||||
import moment from 'moment';
|
import moment, { LongDateFormatKey } from 'moment';
|
||||||
import { IBill, IManualJournalEntry, ISystemUser } from 'interfaces';
|
import { IBill, IManualJournalEntry, ISystemUser } from 'interfaces';
|
||||||
import JournalPoster from './JournalPoster';
|
import JournalPoster from './JournalPoster';
|
||||||
import JournalEntry from './JournalEntry';
|
import JournalEntry from './JournalEntry';
|
||||||
@@ -183,7 +183,7 @@ export default class JournalCommands {
|
|||||||
async vendorOpeningBalance(
|
async vendorOpeningBalance(
|
||||||
vendorId: number,
|
vendorId: number,
|
||||||
openingBalance: number,
|
openingBalance: number,
|
||||||
openingBalanceAt: Date|string,
|
openingBalanceAt: Date | string,
|
||||||
authorizedUserId: ISystemUser
|
authorizedUserId: ISystemUser
|
||||||
) {
|
) {
|
||||||
const { accountRepository } = this.repositories;
|
const { accountRepository } = this.repositories;
|
||||||
@@ -225,10 +225,7 @@ export default class JournalCommands {
|
|||||||
* Writes journal entries of expense model object.
|
* Writes journal entries of expense model object.
|
||||||
* @param {IExpense} expense
|
* @param {IExpense} expense
|
||||||
*/
|
*/
|
||||||
expense(
|
expense(expense: IExpense, userId: number) {
|
||||||
expense: IExpense,
|
|
||||||
userId: number,
|
|
||||||
) {
|
|
||||||
const mixinEntry = {
|
const mixinEntry = {
|
||||||
referenceType: 'Expense',
|
referenceType: 'Expense',
|
||||||
referenceId: expense.id,
|
referenceId: expense.id,
|
||||||
@@ -279,14 +276,50 @@ export default class JournalCommands {
|
|||||||
this.journal.removeEntries();
|
this.journal.removeEntries();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverts the sale invoice cost journal entries.
|
||||||
|
* @param {Date|string} startingDate
|
||||||
|
* @return {Promise<void>}
|
||||||
|
*/
|
||||||
|
async revertInventoryCostJournalEntries(
|
||||||
|
startingDate: Date | string
|
||||||
|
): Promise<void> {
|
||||||
|
const { transactionsRepository } = this.repositories;
|
||||||
|
|
||||||
|
const transactions = await transactionsRepository.journal({
|
||||||
|
fromDate: startingDate,
|
||||||
|
referenceType: ['SaleInvoice'],
|
||||||
|
index: [3, 4],
|
||||||
|
});
|
||||||
|
console.log(transactions);
|
||||||
|
|
||||||
|
this.journal.fromTransactions(transactions);
|
||||||
|
this.journal.removeEntries();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverts sale invoice the income journal entries.
|
||||||
|
* @param {number} saleInvoiceId
|
||||||
|
*/
|
||||||
|
async revertInvoiceIncomeEntries(
|
||||||
|
saleInvoiceId: number,
|
||||||
|
) {
|
||||||
|
const { transactionsRepository } = this.repositories;
|
||||||
|
|
||||||
|
const transactions = await transactionsRepository.journal({
|
||||||
|
referenceType: ['SaleInvoice'],
|
||||||
|
referenceId: [saleInvoiceId],
|
||||||
|
});
|
||||||
|
this.journal.fromTransactions(transactions);
|
||||||
|
this.journal.removeEntries();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes journal entries from manual journal model object.
|
* Writes journal entries from manual journal model object.
|
||||||
* @param {IManualJournal} manualJournalObj
|
* @param {IManualJournal} manualJournalObj
|
||||||
* @param {number} manualJournalId
|
* @param {number} manualJournalId
|
||||||
*/
|
*/
|
||||||
async manualJournal(
|
async manualJournal(manualJournalObj: IManualJournal) {
|
||||||
manualJournalObj: IManualJournal,
|
|
||||||
) {
|
|
||||||
manualJournalObj.entries.forEach((entry: IManualJournalEntry) => {
|
manualJournalObj.entries.forEach((entry: IManualJournalEntry) => {
|
||||||
const jouranlEntry = new JournalEntry({
|
const jouranlEntry = new JournalEntry({
|
||||||
debit: entry.debit,
|
debit: entry.debit,
|
||||||
@@ -310,261 +343,68 @@ export default class JournalCommands {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes and revert accounts balance journal entries that associated
|
|
||||||
* to the given inventory transactions.
|
|
||||||
* @param {IInventoryTransaction[]} inventoryTransactions
|
|
||||||
* @param {Journal} journal
|
|
||||||
*/
|
|
||||||
revertEntriesFromInventoryTransactions(
|
|
||||||
inventoryTransactions: IInventoryTransaction[]
|
|
||||||
) {
|
|
||||||
const groupedInvTransactions = chain(inventoryTransactions)
|
|
||||||
.groupBy(
|
|
||||||
(invTransaction: IInventoryTransaction) =>
|
|
||||||
invTransaction.transactionType
|
|
||||||
)
|
|
||||||
.map((groupedTrans: IInventoryTransaction[], transType: string) => [
|
|
||||||
groupedTrans,
|
|
||||||
transType,
|
|
||||||
])
|
|
||||||
.value();
|
|
||||||
|
|
||||||
return Promise.all(
|
|
||||||
groupedInvTransactions.map(
|
|
||||||
async (grouped: [IInventoryTransaction[], string]) => {
|
|
||||||
const [invTransGroup, referenceType] = grouped;
|
|
||||||
const referencesIds = invTransGroup.map(
|
|
||||||
(trans: IInventoryTransaction) => trans.transactionId
|
|
||||||
);
|
|
||||||
|
|
||||||
const _transactions = await AccountTransaction.tenant()
|
|
||||||
.query()
|
|
||||||
.where('reference_type', referenceType)
|
|
||||||
.whereIn('reference_id', referencesIds)
|
|
||||||
.withGraphFetched('account.type');
|
|
||||||
|
|
||||||
if (_transactions.length > 0) {
|
|
||||||
this.journal.loadEntries(_transactions);
|
|
||||||
this.journal.removeEntries(_transactions.map((t: any) => t.id));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async nonInventoryEntries(transactions: NonInventoryJEntries[]) {
|
|
||||||
const receivableAccount = { id: 10 };
|
|
||||||
const payableAccount = { id: 11 };
|
|
||||||
|
|
||||||
transactions.forEach((trans: NonInventoryJEntries) => {
|
|
||||||
const commonEntry = {
|
|
||||||
date: trans.date,
|
|
||||||
referenceId: trans.referenceId,
|
|
||||||
referenceType: trans.referenceType,
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (trans.referenceType) {
|
|
||||||
case 'Bill':
|
|
||||||
const payableEntry: JournalEntry = new JournalEntry({
|
|
||||||
...commonEntry,
|
|
||||||
credit: trans.payable,
|
|
||||||
account: payableAccount.id,
|
|
||||||
});
|
|
||||||
const costEntry: JournalEntry = new JournalEntry({
|
|
||||||
...commonEntry,
|
|
||||||
});
|
|
||||||
this.journal.credit(payableEntry);
|
|
||||||
this.journal.debit(costEntry);
|
|
||||||
break;
|
|
||||||
case 'SaleInvoice':
|
|
||||||
const receivableEntry: JournalEntry = new JournalEntry({
|
|
||||||
...commonEntry,
|
|
||||||
debit: trans.receivable,
|
|
||||||
account: receivableAccount.id,
|
|
||||||
});
|
|
||||||
const saleIncomeEntry: JournalEntry = new JournalEntry({
|
|
||||||
...commonEntry,
|
|
||||||
credit: trans.income,
|
|
||||||
account: trans.incomeAccountId,
|
|
||||||
});
|
|
||||||
this.journal.debit(receivableEntry);
|
|
||||||
this.journal.credit(saleIncomeEntry);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param {string} referenceType -
|
|
||||||
* @param {number} referenceId -
|
|
||||||
* @param {ISaleInvoice[]} sales -
|
|
||||||
*/
|
|
||||||
public async inventoryEntries(transactions: IInventoryCostEntity[]) {
|
|
||||||
const receivableAccount = { id: 10 };
|
|
||||||
const payableAccount = { id: 11 };
|
|
||||||
|
|
||||||
transactions.forEach((sale: IInventoryCostEntity) => {
|
|
||||||
const commonEntry = {
|
|
||||||
date: sale.date,
|
|
||||||
referenceId: sale.referenceId,
|
|
||||||
referenceType: sale.referenceType,
|
|
||||||
};
|
|
||||||
switch (sale.referenceType) {
|
|
||||||
case 'Bill':
|
|
||||||
const inventoryDebit: JournalEntry = new JournalEntry({
|
|
||||||
...commonEntry,
|
|
||||||
debit: sale.inventory,
|
|
||||||
account: sale.inventoryAccount,
|
|
||||||
});
|
|
||||||
const payableEntry: JournalEntry = new JournalEntry({
|
|
||||||
...commonEntry,
|
|
||||||
credit: sale.inventory,
|
|
||||||
account: payableAccount.id,
|
|
||||||
});
|
|
||||||
this.journal.debit(inventoryDebit);
|
|
||||||
this.journal.credit(payableEntry);
|
|
||||||
break;
|
|
||||||
case 'SaleInvoice':
|
|
||||||
const receivableEntry: JournalEntry = new JournalEntry({
|
|
||||||
...commonEntry,
|
|
||||||
debit: sale.income,
|
|
||||||
account: receivableAccount.id,
|
|
||||||
});
|
|
||||||
const incomeEntry: JournalEntry = new JournalEntry({
|
|
||||||
...commonEntry,
|
|
||||||
credit: sale.income,
|
|
||||||
account: sale.incomeAccount,
|
|
||||||
});
|
|
||||||
// Cost journal transaction.
|
|
||||||
const costEntry: JournalEntry = new JournalEntry({
|
|
||||||
...commonEntry,
|
|
||||||
debit: sale.cost,
|
|
||||||
account: sale.costAccount,
|
|
||||||
});
|
|
||||||
const inventoryCredit: JournalEntry = new JournalEntry({
|
|
||||||
...commonEntry,
|
|
||||||
credit: sale.cost,
|
|
||||||
account: sale.inventoryAccount,
|
|
||||||
});
|
|
||||||
this.journal.debit(receivableEntry);
|
|
||||||
this.journal.debit(costEntry);
|
|
||||||
|
|
||||||
this.journal.credit(incomeEntry);
|
|
||||||
this.journal.credit(inventoryCredit);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes journal entries for given sale invoice.
|
* Writes journal entries for given sale invoice.
|
||||||
* ----------
|
* -------
|
||||||
* - Receivable accounts -> Debit -> XXXX
|
|
||||||
* - Income -> Credit -> XXXX
|
|
||||||
*
|
|
||||||
* - Cost of goods sold -> Debit -> YYYY
|
* - Cost of goods sold -> Debit -> YYYY
|
||||||
* - Inventory assets -> YYYY
|
* - Inventory assets -> Credit -> YYYY
|
||||||
*
|
*
|
||||||
* @param {ISaleInvoice} saleInvoice
|
* @param {ISaleInvoice} saleInvoice
|
||||||
* @param {JournalPoster} journal
|
* @param {JournalPoster} journal
|
||||||
*/
|
*/
|
||||||
saleInvoice(
|
saleInvoiceInventoryCost(
|
||||||
saleInvoice: ISaleInvoice & {
|
inventoryCostLot: IInventoryLotCost & { item: IItem }
|
||||||
costTransactions: IInventoryLotCost[];
|
|
||||||
entries: IItemEntry & { item: IItem };
|
|
||||||
},
|
|
||||||
receivableAccountsId: number
|
|
||||||
) {
|
) {
|
||||||
let inventoryTotal: number = 0;
|
|
||||||
|
|
||||||
const commonEntry = {
|
const commonEntry = {
|
||||||
referenceType: 'SaleInvoice',
|
referenceType: 'SaleInvoice',
|
||||||
referenceId: saleInvoice.id,
|
referenceId: inventoryCostLot.transactionId,
|
||||||
date: saleInvoice.invoiceDate,
|
date: inventoryCostLot.date,
|
||||||
};
|
};
|
||||||
const costTransactions: Map<number, number> = new Map(
|
// XXX Debit - Cost account.
|
||||||
saleInvoice.costTransactions.map((trans: IInventoryLotCost) => [
|
const costEntry = new JournalEntry({
|
||||||
trans.entryId,
|
|
||||||
trans.cost,
|
|
||||||
])
|
|
||||||
);
|
|
||||||
// XXX Debit - Receivable account.
|
|
||||||
const receivableEntry = new JournalEntry({
|
|
||||||
...commonEntry,
|
...commonEntry,
|
||||||
debit: saleInvoice.balance,
|
debit: inventoryCostLot.cost,
|
||||||
account: receivableAccountsId,
|
account: inventoryCostLot.item.costAccountId,
|
||||||
index: 1,
|
index: 3,
|
||||||
});
|
});
|
||||||
this.journal.debit(receivableEntry);
|
// XXX Credit - Inventory account.
|
||||||
|
const inventoryEntry = new JournalEntry({
|
||||||
saleInvoice.entries.forEach(
|
...commonEntry,
|
||||||
(entry: IItemEntry & { item: IItem }, index) => {
|
credit: inventoryCostLot.cost,
|
||||||
const cost: number = costTransactions.get(entry.id);
|
account: inventoryCostLot.item.inventoryAccountId,
|
||||||
const income: number = entry.quantity * entry.rate;
|
index: 4,
|
||||||
|
});
|
||||||
if (entry.item.type === 'inventory' && cost) {
|
this.journal.credit(inventoryEntry);
|
||||||
// XXX Debit - Cost account.
|
this.journal.debit(costEntry);
|
||||||
const costEntry = new JournalEntry({
|
|
||||||
...commonEntry,
|
|
||||||
debit: cost,
|
|
||||||
account: entry.item.costAccountId,
|
|
||||||
note: entry.description,
|
|
||||||
index: index + 3,
|
|
||||||
});
|
|
||||||
this.journal.debit(costEntry);
|
|
||||||
inventoryTotal += cost;
|
|
||||||
}
|
|
||||||
// XXX Credit - Income account.
|
|
||||||
const incomeEntry = new JournalEntry({
|
|
||||||
...commonEntry,
|
|
||||||
credit: income,
|
|
||||||
account: entry.item.sellAccountId,
|
|
||||||
note: entry.description,
|
|
||||||
index: index + 2,
|
|
||||||
});
|
|
||||||
this.journal.credit(incomeEntry);
|
|
||||||
|
|
||||||
if (inventoryTotal > 0) {
|
|
||||||
// XXX Credit - Inventory account.
|
|
||||||
const inventoryEntry = new JournalEntry({
|
|
||||||
...commonEntry,
|
|
||||||
credit: inventoryTotal,
|
|
||||||
account: entry.item.inventoryAccountId,
|
|
||||||
index: index + 4,
|
|
||||||
});
|
|
||||||
this.journal.credit(inventoryEntry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Writes the sale invoice income journal entries.
|
||||||
|
* -----
|
||||||
|
* - Receivable accounts -> Debit -> XXXX
|
||||||
|
* - Income -> Credit -> XXXX
|
||||||
*
|
*
|
||||||
* @param {ISaleInvoice} saleInvoice
|
* @param {ISaleInvoice} saleInvoice
|
||||||
* @param {number} receivableAccountsId
|
* @param {number} receivableAccountsId
|
||||||
* @param {number} authorizedUserId
|
* @param {number} authorizedUserId
|
||||||
*/
|
*/
|
||||||
saleInvoiceNonInventory(
|
async saleInvoiceIncomeEntries(
|
||||||
saleInvoice: ISaleInvoice & {
|
saleInvoice: ISaleInvoice & {
|
||||||
entries: IItemEntry & { item: IItem };
|
entries: IItemEntry & { item: IItem };
|
||||||
},
|
},
|
||||||
receivableAccountsId: number,
|
receivableAccountId: number
|
||||||
authorizedUserId: number,
|
): Promise<void> {
|
||||||
) {
|
|
||||||
const commonEntry = {
|
const commonEntry = {
|
||||||
referenceType: 'SaleInvoice',
|
referenceType: 'SaleInvoice',
|
||||||
referenceId: saleInvoice.id,
|
referenceId: saleInvoice.id,
|
||||||
date: saleInvoice.invoiceDate,
|
date: saleInvoice.invoiceDate,
|
||||||
userId: authorizedUserId,
|
userId: saleInvoice.userId,
|
||||||
};
|
};
|
||||||
|
|
||||||
// XXX Debit - Receivable account.
|
// XXX Debit - Receivable account.
|
||||||
const receivableEntry = new JournalEntry({
|
const receivableEntry = new JournalEntry({
|
||||||
...commonEntry,
|
...commonEntry,
|
||||||
debit: saleInvoice.balance,
|
debit: saleInvoice.balance,
|
||||||
account: receivableAccountsId,
|
account: receivableAccountId,
|
||||||
index: 1,
|
index: 1,
|
||||||
});
|
});
|
||||||
this.journal.debit(receivableEntry);
|
this.journal.debit(receivableEntry);
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import {
|
|||||||
IContactEditDTO,
|
IContactEditDTO,
|
||||||
IContact,
|
IContact,
|
||||||
ISaleInvoice,
|
ISaleInvoice,
|
||||||
ISystemService,
|
|
||||||
ISystemUser,
|
ISystemUser,
|
||||||
} from 'interfaces';
|
} from 'interfaces';
|
||||||
import { ServiceError } from 'exceptions';
|
import { ServiceError } from 'exceptions';
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
import { Container, Service, Inject } from 'typedi';
|
import { Container, Service, Inject } from 'typedi';
|
||||||
import { pick } from 'lodash';
|
import { pick } from 'lodash';
|
||||||
|
import config from 'config';
|
||||||
import {
|
import {
|
||||||
EventDispatcher,
|
EventDispatcher,
|
||||||
EventDispatcherInterface,
|
EventDispatcherInterface,
|
||||||
} from 'decorators/eventDispatcher';
|
} from 'decorators/eventDispatcher';
|
||||||
import { IInventoryLotCost, IInventoryTransaction, IItem, IItemEntry } from 'interfaces'
|
import {
|
||||||
|
IInventoryLotCost,
|
||||||
|
IInventoryTransaction,
|
||||||
|
IItem,
|
||||||
|
IItemEntry,
|
||||||
|
} from 'interfaces';
|
||||||
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 TenancyService from 'services/Tenancy/TenancyService';
|
import TenancyService from 'services/Tenancy/TenancyService';
|
||||||
@@ -27,9 +33,9 @@ export default class InventoryService {
|
|||||||
itemEntries: IItemEntry[],
|
itemEntries: IItemEntry[],
|
||||||
transactionType: string,
|
transactionType: string,
|
||||||
transactionId: number,
|
transactionId: number,
|
||||||
direction: 'IN'|'OUT',
|
direction: 'IN' | 'OUT',
|
||||||
date: Date|string,
|
date: Date | string,
|
||||||
lotNumber: number,
|
lotNumber: number
|
||||||
) {
|
) {
|
||||||
return itemEntries.map((entry: IItemEntry) => ({
|
return itemEntries.map((entry: IItemEntry) => ({
|
||||||
...pick(entry, ['itemId', 'quantity', 'rate']),
|
...pick(entry, ['itemId', 'quantity', 'rate']),
|
||||||
@@ -62,13 +68,21 @@ export default class InventoryService {
|
|||||||
let costMethodComputer: IInventoryCostMethod;
|
let costMethodComputer: IInventoryCostMethod;
|
||||||
|
|
||||||
// Switch between methods based on the item cost method.
|
// Switch between methods based on the item cost method.
|
||||||
switch('AVG') {
|
switch ('AVG') {
|
||||||
case 'FIFO':
|
case 'FIFO':
|
||||||
case 'LIFO':
|
case 'LIFO':
|
||||||
costMethodComputer = new InventoryCostLotTracker(tenantId, fromDate, itemId);
|
costMethodComputer = new InventoryCostLotTracker(
|
||||||
|
tenantId,
|
||||||
|
fromDate,
|
||||||
|
itemId
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case 'AVG':
|
case 'AVG':
|
||||||
costMethodComputer = new InventoryAverageCost(tenantId, fromDate, itemId);
|
costMethodComputer = new InventoryAverageCost(
|
||||||
|
tenantId,
|
||||||
|
fromDate,
|
||||||
|
itemId
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return costMethodComputer.computeItemCost();
|
return costMethodComputer.computeItemCost();
|
||||||
@@ -80,7 +94,11 @@ export default class InventoryService {
|
|||||||
* @param {number} itemId
|
* @param {number} itemId
|
||||||
* @param {Date} startingDate
|
* @param {Date} startingDate
|
||||||
*/
|
*/
|
||||||
async scheduleComputeItemCost(tenantId: number, itemId: number, startingDate: Date|string) {
|
async scheduleComputeItemCost(
|
||||||
|
tenantId: number,
|
||||||
|
itemId: number,
|
||||||
|
startingDate: Date | string
|
||||||
|
) {
|
||||||
const agenda = Container.get('agenda');
|
const agenda = Container.get('agenda');
|
||||||
|
|
||||||
// Cancel any `compute-item-cost` in the queue has upper starting date
|
// Cancel any `compute-item-cost` in the queue has upper starting date
|
||||||
@@ -90,7 +108,7 @@ export default class InventoryService {
|
|||||||
nextRunAt: { $ne: null },
|
nextRunAt: { $ne: null },
|
||||||
'data.tenantId': tenantId,
|
'data.tenantId': tenantId,
|
||||||
'data.itemId': itemId,
|
'data.itemId': itemId,
|
||||||
'data.startingDate': { "$gt": startingDate }
|
'data.startingDate': { $gt: startingDate },
|
||||||
});
|
});
|
||||||
|
|
||||||
// Retrieve any `compute-item-cost` in the queue has lower starting date
|
// Retrieve any `compute-item-cost` in the queue has lower starting date
|
||||||
@@ -100,17 +118,23 @@ export default class InventoryService {
|
|||||||
nextRunAt: { $ne: null },
|
nextRunAt: { $ne: null },
|
||||||
'data.tenantId': tenantId,
|
'data.tenantId': tenantId,
|
||||||
'data.itemId': itemId,
|
'data.itemId': itemId,
|
||||||
'data.startingDate': { "$lte": startingDate }
|
'data.startingDate': { $lte: startingDate },
|
||||||
});
|
});
|
||||||
if (dependsJobs.length === 0) {
|
if (dependsJobs.length === 0) {
|
||||||
await agenda.schedule('in 30 seconds', 'compute-item-cost', {
|
await agenda.schedule(
|
||||||
startingDate, itemId, tenantId,
|
config.scheduleComputeItemCost,
|
||||||
});
|
'compute-item-cost',
|
||||||
|
{
|
||||||
|
startingDate,
|
||||||
|
itemId,
|
||||||
|
tenantId,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// Triggers `onComputeItemCostJobScheduled` event.
|
// Triggers `onComputeItemCostJobScheduled` event.
|
||||||
await this.eventDispatcher.dispatch(
|
await this.eventDispatcher.dispatch(
|
||||||
events.inventory.onComputeItemCostJobScheduled,
|
events.inventory.onComputeItemCostJobScheduled,
|
||||||
{ startingDate, itemId, tenantId },
|
{ startingDate, itemId, tenantId }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -125,14 +149,10 @@ export default class InventoryService {
|
|||||||
async recordInventoryTransactions(
|
async recordInventoryTransactions(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
inventoryEntries: IInventoryTransaction[],
|
inventoryEntries: IInventoryTransaction[],
|
||||||
deleteOld: boolean,
|
deleteOld: boolean
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
inventoryEntries.forEach(async (entry: IInventoryTransaction) => {
|
inventoryEntries.forEach(async (entry: IInventoryTransaction) => {
|
||||||
await this.recordInventoryTransaction(
|
await this.recordInventoryTransaction(tenantId, entry, deleteOld);
|
||||||
tenantId,
|
|
||||||
entry,
|
|
||||||
deleteOld,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,7 +165,7 @@ export default class InventoryService {
|
|||||||
async recordInventoryTransaction(
|
async recordInventoryTransaction(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
inventoryEntry: IInventoryTransaction,
|
inventoryEntry: IInventoryTransaction,
|
||||||
deleteOld: boolean = false,
|
deleteOld: boolean = false
|
||||||
): Promise<IInventoryTransaction> {
|
): Promise<IInventoryTransaction> {
|
||||||
const { InventoryTransaction, Item } = this.tenancy.models(tenantId);
|
const { InventoryTransaction, Item } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
@@ -153,7 +173,7 @@ export default class InventoryService {
|
|||||||
await this.deleteInventoryTransactions(
|
await this.deleteInventoryTransactions(
|
||||||
tenantId,
|
tenantId,
|
||||||
inventoryEntry.transactionId,
|
inventoryEntry.transactionId,
|
||||||
inventoryEntry.transactionType,
|
inventoryEntry.transactionType
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return InventoryTransaction.query().insert({
|
return InventoryTransaction.query().insert({
|
||||||
@@ -172,7 +192,7 @@ export default class InventoryService {
|
|||||||
async deleteInventoryTransactions(
|
async deleteInventoryTransactions(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
transactionId: number,
|
transactionId: number,
|
||||||
transactionType: string,
|
transactionType: string
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const { InventoryTransaction } = this.tenancy.models(tenantId);
|
const { InventoryTransaction } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
@@ -190,7 +210,7 @@ export default class InventoryService {
|
|||||||
*/
|
*/
|
||||||
async recordInventoryCostLotTransaction(
|
async recordInventoryCostLotTransaction(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
inventoryLotEntry: IInventoryLotCost,
|
inventoryLotEntry: IInventoryLotCost
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const { InventoryCostLotTracker } = this.tenancy.models(tenantId);
|
const { InventoryCostLotTracker } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
@@ -209,8 +229,9 @@ export default class InventoryService {
|
|||||||
const LOT_NUMBER_KEY = 'lot_number_increment';
|
const LOT_NUMBER_KEY = 'lot_number_increment';
|
||||||
const storedLotNumber = settings.find({ key: LOT_NUMBER_KEY });
|
const storedLotNumber = settings.find({ key: LOT_NUMBER_KEY });
|
||||||
|
|
||||||
return (storedLotNumber && storedLotNumber.value) ?
|
return storedLotNumber && storedLotNumber.value
|
||||||
parseInt(storedLotNumber.value, 10) : 1;
|
? parseInt(storedLotNumber.value, 10)
|
||||||
|
: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export default class JournalPosterService {
|
|||||||
tenantId: number,
|
tenantId: number,
|
||||||
referenceId: number|number[],
|
referenceId: number|number[],
|
||||||
referenceType: string
|
referenceType: string
|
||||||
) {
|
): Promise<void> {
|
||||||
const journal = new JournalPoster(tenantId);
|
const journal = new JournalPoster(tenantId);
|
||||||
const journalCommand = new JournalCommands(journal);
|
const journalCommand = new JournalCommands(journal);
|
||||||
|
|
||||||
|
|||||||
@@ -13,10 +13,13 @@ import {
|
|||||||
IPaginationMeta,
|
IPaginationMeta,
|
||||||
IFilterMeta,
|
IFilterMeta,
|
||||||
ISystemUser,
|
ISystemUser,
|
||||||
|
IItem,
|
||||||
|
IItemEntry,
|
||||||
} from 'interfaces';
|
} from 'interfaces';
|
||||||
|
import JournalPoster from 'services/Accounting/JournalPoster';
|
||||||
|
import JournalCommands from 'services/Accounting/JournalCommands';
|
||||||
import events from 'subscribers/events';
|
import events from 'subscribers/events';
|
||||||
import InventoryService from 'services/Inventory/Inventory';
|
import InventoryService from 'services/Inventory/Inventory';
|
||||||
import SalesInvoicesCost from 'services/Sales/SalesInvoicesCost';
|
|
||||||
import TenancyService from 'services/Tenancy/TenancyService';
|
import TenancyService from 'services/Tenancy/TenancyService';
|
||||||
import DynamicListingService from 'services/DynamicListing/DynamicListService';
|
import DynamicListingService from 'services/DynamicListing/DynamicListService';
|
||||||
import { formatDateFields } from 'utils';
|
import { formatDateFields } from 'utils';
|
||||||
@@ -25,6 +28,7 @@ import ItemsService from 'services/Items/ItemsService';
|
|||||||
import ItemsEntriesService from 'services/Items/ItemsEntriesService';
|
import ItemsEntriesService from 'services/Items/ItemsEntriesService';
|
||||||
import CustomersService from 'services/Contacts/CustomersService';
|
import CustomersService from 'services/Contacts/CustomersService';
|
||||||
import SaleEstimateService from 'services/Sales/SalesEstimate';
|
import SaleEstimateService from 'services/Sales/SalesEstimate';
|
||||||
|
import JournalPosterService from './JournalPosterService';
|
||||||
|
|
||||||
const ERRORS = {
|
const ERRORS = {
|
||||||
INVOICE_NUMBER_NOT_UNIQUE: 'INVOICE_NUMBER_NOT_UNIQUE',
|
INVOICE_NUMBER_NOT_UNIQUE: 'INVOICE_NUMBER_NOT_UNIQUE',
|
||||||
@@ -42,7 +46,7 @@ const ERRORS = {
|
|||||||
* @service
|
* @service
|
||||||
*/
|
*/
|
||||||
@Service()
|
@Service()
|
||||||
export default class SaleInvoicesService extends SalesInvoicesCost {
|
export default class SaleInvoicesService {
|
||||||
@Inject()
|
@Inject()
|
||||||
tenancy: TenancyService;
|
tenancy: TenancyService;
|
||||||
|
|
||||||
@@ -70,6 +74,9 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
|||||||
@Inject()
|
@Inject()
|
||||||
saleEstimatesService: SaleEstimateService;
|
saleEstimatesService: SaleEstimateService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
journalService: JournalPosterService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate whether sale invoice number unqiue on the storage.
|
* Validate whether sale invoice number unqiue on the storage.
|
||||||
*/
|
*/
|
||||||
@@ -101,6 +108,28 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the sale invoice has no payment entries.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {number} saleInvoiceId
|
||||||
|
*/
|
||||||
|
async validateInvoiceHasNoPaymentEntries(
|
||||||
|
tenantId: number,
|
||||||
|
saleInvoiceId: number
|
||||||
|
) {
|
||||||
|
const { PaymentReceiveEntry } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
|
// Retrieve the sale invoice associated payment receive entries.
|
||||||
|
const entries = await PaymentReceiveEntry.query().where(
|
||||||
|
'invoice_id',
|
||||||
|
saleInvoiceId
|
||||||
|
);
|
||||||
|
if (entries.length > 0) {
|
||||||
|
throw new ServiceError(ERRORS.INVOICE_HAS_ASSOCIATED_PAYMENT_ENTRIES);
|
||||||
|
}
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate whether sale invoice exists on the storage.
|
* Validate whether sale invoice exists on the storage.
|
||||||
* @param {Request} req
|
* @param {Request} req
|
||||||
@@ -148,7 +177,7 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
|||||||
balance,
|
balance,
|
||||||
paymentAmount: 0,
|
paymentAmount: 0,
|
||||||
entries: saleInvoiceDTO.entries.map((entry) => ({
|
entries: saleInvoiceDTO.entries.map((entry) => ({
|
||||||
reference_type: 'SaleInvoice',
|
referenceType: 'SaleInvoice',
|
||||||
...omit(entry, ['amount', 'id']),
|
...omit(entry, ['amount', 'id']),
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
@@ -208,7 +237,7 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
|||||||
});
|
});
|
||||||
this.logger.info('[sale_invoice] successfully inserted.', {
|
this.logger.info('[sale_invoice] successfully inserted.', {
|
||||||
tenantId,
|
tenantId,
|
||||||
saleInvoice,
|
saleInvoiceId: saleInvoice.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
return saleInvoice;
|
return saleInvoice;
|
||||||
@@ -238,19 +267,19 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
|||||||
const saleInvoiceObj = this.transformDTOToModel(
|
const saleInvoiceObj = this.transformDTOToModel(
|
||||||
tenantId,
|
tenantId,
|
||||||
saleInvoiceDTO,
|
saleInvoiceDTO,
|
||||||
oldSaleInvoice,
|
oldSaleInvoice
|
||||||
);
|
);
|
||||||
// Validate customer existance.
|
// Validate customer existance.
|
||||||
await this.customersService.getCustomerByIdOrThrowError(
|
await this.customersService.getCustomerByIdOrThrowError(
|
||||||
tenantId,
|
tenantId,
|
||||||
saleInvoiceDTO.customerId,
|
saleInvoiceDTO.customerId
|
||||||
);
|
);
|
||||||
// Validate sale invoice number uniquiness.
|
// Validate sale invoice number uniquiness.
|
||||||
if (saleInvoiceDTO.invoiceNo) {
|
if (saleInvoiceDTO.invoiceNo) {
|
||||||
await this.validateInvoiceNumberUnique(
|
await this.validateInvoiceNumberUnique(
|
||||||
tenantId,
|
tenantId,
|
||||||
saleInvoiceDTO.invoiceNo,
|
saleInvoiceDTO.invoiceNo,
|
||||||
saleInvoiceId,
|
saleInvoiceId
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// Validate items ids existance.
|
// Validate items ids existance.
|
||||||
@@ -261,7 +290,7 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
|||||||
// Validate non-sellable entries items.
|
// Validate non-sellable entries items.
|
||||||
await this.itemsEntriesService.validateNonSellableEntriesItems(
|
await this.itemsEntriesService.validateNonSellableEntriesItems(
|
||||||
tenantId,
|
tenantId,
|
||||||
saleInvoiceDTO.entries,
|
saleInvoiceDTO.entries
|
||||||
);
|
);
|
||||||
// Validate the items entries existance.
|
// Validate the items entries existance.
|
||||||
await this.itemsEntriesService.validateEntriesIdsExistance(
|
await this.itemsEntriesService.validateEntriesIdsExistance(
|
||||||
@@ -270,7 +299,6 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
|||||||
'SaleInvoice',
|
'SaleInvoice',
|
||||||
saleInvoiceDTO.entries
|
saleInvoiceDTO.entries
|
||||||
);
|
);
|
||||||
|
|
||||||
this.logger.info('[sale_invoice] trying to update sale invoice.');
|
this.logger.info('[sale_invoice] trying to update sale invoice.');
|
||||||
const saleInvoice: ISaleInvoice = await SaleInvoice.query().upsertGraphAndFetch(
|
const saleInvoice: ISaleInvoice = await SaleInvoice.query().upsertGraphAndFetch(
|
||||||
{
|
{
|
||||||
@@ -280,10 +308,10 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
|||||||
);
|
);
|
||||||
// Triggers `onSaleInvoiceEdited` event.
|
// Triggers `onSaleInvoiceEdited` event.
|
||||||
await this.eventDispatcher.dispatch(events.saleInvoice.onEdited, {
|
await this.eventDispatcher.dispatch(events.saleInvoice.onEdited, {
|
||||||
saleInvoice,
|
|
||||||
oldSaleInvoice,
|
|
||||||
tenantId,
|
tenantId,
|
||||||
saleInvoiceId,
|
saleInvoiceId,
|
||||||
|
saleInvoice,
|
||||||
|
oldSaleInvoice,
|
||||||
authorizedUser,
|
authorizedUser,
|
||||||
});
|
});
|
||||||
return saleInvoice;
|
return saleInvoice;
|
||||||
@@ -303,51 +331,33 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
|||||||
const { saleInvoiceRepository } = this.tenancy.repositories(tenantId);
|
const { saleInvoiceRepository } = this.tenancy.repositories(tenantId);
|
||||||
|
|
||||||
// Retrieve details of the given sale invoice id.
|
// Retrieve details of the given sale invoice id.
|
||||||
const saleInvoice = await this.getInvoiceOrThrowError(
|
const oldSaleInvoice = await this.getInvoiceOrThrowError(
|
||||||
tenantId,
|
tenantId,
|
||||||
saleInvoiceId
|
saleInvoiceId
|
||||||
);
|
);
|
||||||
|
|
||||||
// Throws error in case the sale invoice already published.
|
// Throws error in case the sale invoice already published.
|
||||||
if (saleInvoice.isDelivered) {
|
if (oldSaleInvoice.isDelivered) {
|
||||||
throw new ServiceError(ERRORS.SALE_INVOICE_ALREADY_DELIVERED);
|
throw new ServiceError(ERRORS.SALE_INVOICE_ALREADY_DELIVERED);
|
||||||
}
|
}
|
||||||
// Record the delivered at on the storage.
|
// Record the delivered at on the storage.
|
||||||
await saleInvoiceRepository.update(
|
await saleInvoiceRepository.update(
|
||||||
{
|
{ deliveredAt: moment().toMySqlDateTime(), },
|
||||||
deliveredAt: moment().toMySqlDateTime(),
|
|
||||||
},
|
|
||||||
{ id: saleInvoiceId }
|
{ id: saleInvoiceId }
|
||||||
);
|
);
|
||||||
}
|
// Triggers `onSaleInvoiceDelivered` event.
|
||||||
|
this.eventDispatcher.dispatch(events.saleInvoice.onDelivered, {
|
||||||
/**
|
tenantId,
|
||||||
* Validate the sale invoice has no payment entries.
|
saleInvoiceId,
|
||||||
* @param {number} tenantId
|
oldSaleInvoice,
|
||||||
* @param {number} saleInvoiceId
|
});
|
||||||
*/
|
|
||||||
async validateInvoiceHasNoPaymentEntries(
|
|
||||||
tenantId: number,
|
|
||||||
saleInvoiceId: number
|
|
||||||
) {
|
|
||||||
const { PaymentReceiveEntry } = this.tenancy.models(tenantId);
|
|
||||||
|
|
||||||
// Retrieve the sale invoice associated payment receive entries.
|
|
||||||
const entries = await PaymentReceiveEntry.query().where(
|
|
||||||
'invoice_id',
|
|
||||||
saleInvoiceId
|
|
||||||
);
|
|
||||||
if (entries.length > 0) {
|
|
||||||
throw new ServiceError(ERRORS.INVOICE_HAS_ASSOCIATED_PAYMENT_ENTRIES);
|
|
||||||
}
|
|
||||||
return entries;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes the given sale invoice with associated entries
|
* Deletes the given sale invoice with associated entries
|
||||||
* and journal transactions.
|
* and journal transactions.
|
||||||
* @async
|
* @param {number} tenantId - Tenant id.
|
||||||
* @param {Number} saleInvoiceId - The given sale invoice id.
|
* @param {Number} saleInvoiceId - The given sale invoice id.
|
||||||
|
* @param {ISystemUser} authorizedUser -
|
||||||
*/
|
*/
|
||||||
public async deleteSaleInvoice(
|
public async deleteSaleInvoice(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
@@ -376,15 +386,15 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
|||||||
tenantId,
|
tenantId,
|
||||||
saleInvoiceId
|
saleInvoiceId
|
||||||
);
|
);
|
||||||
|
|
||||||
this.logger.info('[sale_invoice] delete sale invoice with entries.');
|
this.logger.info('[sale_invoice] delete sale invoice with entries.');
|
||||||
await saleInvoiceRepository.deleteById(saleInvoiceId);
|
|
||||||
|
|
||||||
await ItemEntry.query()
|
await ItemEntry.query()
|
||||||
.where('reference_id', saleInvoiceId)
|
.where('reference_id', saleInvoiceId)
|
||||||
.where('reference_type', 'SaleInvoice')
|
.where('reference_type', 'SaleInvoice')
|
||||||
.delete();
|
.delete();
|
||||||
|
|
||||||
|
await saleInvoiceRepository.deleteById(saleInvoiceId);
|
||||||
|
|
||||||
// Triggers `onSaleInvoiceDeleted` event.
|
// Triggers `onSaleInvoiceDeleted` event.
|
||||||
await this.eventDispatcher.dispatch(events.saleInvoice.onDeleted, {
|
await this.eventDispatcher.dispatch(events.saleInvoice.onDeleted, {
|
||||||
tenantId,
|
tenantId,
|
||||||
@@ -408,7 +418,7 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
|||||||
tenantId: number,
|
tenantId: number,
|
||||||
saleInvoiceId: number,
|
saleInvoiceId: number,
|
||||||
saleInvoiceDate: Date,
|
saleInvoiceDate: Date,
|
||||||
override?: boolean,
|
override?: boolean
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// Gets the next inventory lot number.
|
// Gets the next inventory lot number.
|
||||||
const lotNumber = this.inventoryService.getNextLotNumber(tenantId);
|
const lotNumber = this.inventoryService.getNextLotNumber(tenantId);
|
||||||
@@ -451,41 +461,38 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Records the journal entries of the given sale invoice just
|
* Writes the sale invoice income journal entries.
|
||||||
* in case the invoice has no inventory items entries.
|
* @param {number} tenantId - Tenant id.
|
||||||
*
|
* @param {ISaleInvoice} saleInvoice - Sale invoice id.
|
||||||
* @param {number} tenantId -
|
|
||||||
* @param {number} saleInvoiceId
|
|
||||||
* @param {boolean} override
|
|
||||||
* @return {Promise<void>}
|
|
||||||
*/
|
*/
|
||||||
public async recordNonInventoryJournalEntries(
|
public async writesIncomeJournalEntries(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
saleInvoiceId: number,
|
saleInvoice: ISaleInvoice & {
|
||||||
authorizedUserId: number,
|
entries: IItemEntry & { item: IItem };
|
||||||
|
},
|
||||||
override: boolean = false
|
override: boolean = false
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const { saleInvoiceRepository } = this.tenancy.repositories(tenantId);
|
const { accountRepository } = this.tenancy.repositories(tenantId);
|
||||||
|
|
||||||
// Loads the inventory items entries of the given sale invoice.
|
const journal = new JournalPoster(tenantId);
|
||||||
const inventoryEntries = await this.itemsEntriesService.getInventoryEntries(
|
const journalCommands = new JournalCommands(journal);
|
||||||
tenantId,
|
|
||||||
'SaleInvoice',
|
|
||||||
saleInvoiceId
|
|
||||||
);
|
|
||||||
// Can't continue if the sale invoice has inventory items entries.
|
|
||||||
if (inventoryEntries.length > 0) return;
|
|
||||||
|
|
||||||
const saleInvoice = await saleInvoiceRepository.findOneById(
|
const receivableAccount = await accountRepository.findOne({
|
||||||
saleInvoiceId,
|
slug: 'accounts-receivable',
|
||||||
'entries.item'
|
});
|
||||||
);
|
if (override) {
|
||||||
await this.writeNonInventoryInvoiceEntries(
|
await journalCommands.revertInvoiceIncomeEntries(saleInvoice.id);
|
||||||
tenantId,
|
}
|
||||||
|
// Records the sale invoice journal entries.
|
||||||
|
await journalCommands.saleInvoiceIncomeEntries(
|
||||||
saleInvoice,
|
saleInvoice,
|
||||||
authorizedUserId,
|
receivableAccount.id
|
||||||
override
|
|
||||||
);
|
);
|
||||||
|
await Promise.all([
|
||||||
|
journal.deleteEntries(),
|
||||||
|
journal.saveBalance(),
|
||||||
|
journal.saveEntries()
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -519,6 +526,23 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverting the sale invoice journal entries.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {number} saleInvoiceId
|
||||||
|
* @return {Promise<void>}
|
||||||
|
*/
|
||||||
|
public async revertInvoiceJournalEntries(
|
||||||
|
tenantId: number,
|
||||||
|
saleInvoiceId: number | number[]
|
||||||
|
): Promise<void> {
|
||||||
|
return this.journalService.revertJournalTransactions(
|
||||||
|
tenantId,
|
||||||
|
saleInvoiceId,
|
||||||
|
'SaleInvoice'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve sale invoice with associated entries.
|
* Retrieve sale invoice with associated entries.
|
||||||
* @async
|
* @async
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export default class SaleInvoicesCost {
|
|||||||
async scheduleComputeCostByItemsIds(
|
async scheduleComputeCostByItemsIds(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
inventoryItemsIds: number[],
|
inventoryItemsIds: number[],
|
||||||
startingDate: Date,
|
startingDate: Date
|
||||||
) {
|
) {
|
||||||
const asyncOpers: Promise<[]>[] = [];
|
const asyncOpers: Promise<[]>[] = [];
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ export default class SaleInvoicesCost {
|
|||||||
const oper: Promise<[]> = this.inventoryService.scheduleComputeItemCost(
|
const oper: Promise<[]> = this.inventoryService.scheduleComputeItemCost(
|
||||||
tenantId,
|
tenantId,
|
||||||
inventoryItemId,
|
inventoryItemId,
|
||||||
startingDate,
|
startingDate
|
||||||
);
|
);
|
||||||
asyncOpers.push(oper);
|
asyncOpers.push(oper);
|
||||||
});
|
});
|
||||||
@@ -49,7 +49,7 @@ export default class SaleInvoicesCost {
|
|||||||
*/
|
*/
|
||||||
async scheduleComputeCostByInvoiceId(
|
async scheduleComputeCostByInvoiceId(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
saleInvoiceId: number,
|
saleInvoiceId: number
|
||||||
) {
|
) {
|
||||||
const { SaleInvoice } = this.tenancy.models(tenantId);
|
const { SaleInvoice } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ export default class SaleInvoicesCost {
|
|||||||
return this.scheduleComputeCostByEntries(
|
return this.scheduleComputeCostByEntries(
|
||||||
tenantId,
|
tenantId,
|
||||||
saleInvoice.entries,
|
saleInvoice.entries,
|
||||||
saleInvoice.invoiceDate,
|
saleInvoice.invoiceDate
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ export default class SaleInvoicesCost {
|
|||||||
return this.scheduleComputeCostByEntries(
|
return this.scheduleComputeCostByEntries(
|
||||||
tenantId,
|
tenantId,
|
||||||
bill.entries,
|
bill.entries,
|
||||||
bill.billDate,
|
bill.billDate
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ export default class SaleInvoicesCost {
|
|||||||
async scheduleComputeCostByEntries(
|
async scheduleComputeCostByEntries(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
entries: IItemEntry[],
|
entries: IItemEntry[],
|
||||||
startingDate: Date,
|
startingDate: Date
|
||||||
) {
|
) {
|
||||||
const { Item } = this.tenancy.models(tenantId);
|
const { Item } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
@@ -128,98 +128,50 @@ export default class SaleInvoicesCost {
|
|||||||
const agenda = Container.get('agenda');
|
const agenda = Container.get('agenda');
|
||||||
|
|
||||||
return agenda.schedule('in 3 seconds', 'rewrite-invoices-journal-entries', {
|
return agenda.schedule('in 3 seconds', 'rewrite-invoices-journal-entries', {
|
||||||
startingDate, tenantId,
|
startingDate,
|
||||||
|
tenantId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes journal entries from sales invoices.
|
* Writes journal entries from sales invoices.
|
||||||
* @param {number} tenantId - The tenant id.
|
* @param {number} tenantId - The tenant id.
|
||||||
* @param {Date} startingDate
|
* @param {Date} startingDate - Starting date.
|
||||||
* @param {boolean} override
|
* @param {boolean} override
|
||||||
*/
|
*/
|
||||||
async writeJournalEntries(tenantId: number, startingDate: Date, override: boolean) {
|
async writeInventoryCostJournalEntries(
|
||||||
const { AccountTransaction, SaleInvoice, Account } = this.tenancy.models(tenantId);
|
tenantId: number,
|
||||||
|
startingDate: Date,
|
||||||
|
override: boolean
|
||||||
|
) {
|
||||||
|
const { InventoryCostLotTracker } = this.tenancy.models(tenantId);
|
||||||
const { accountRepository } = this.tenancy.repositories(tenantId);
|
const { accountRepository } = this.tenancy.repositories(tenantId);
|
||||||
|
|
||||||
const receivableAccount = await accountRepository.findOne({
|
const inventoryCostLotTrans = await InventoryCostLotTracker.query()
|
||||||
slug: 'accounts-receivable',
|
.where('direction', 'OUT')
|
||||||
});
|
.modify('groupedEntriesCost')
|
||||||
const salesInvoices = await SaleInvoice.query()
|
.modify('filterDateRange', startingDate)
|
||||||
.onBuild((builder: any) => {
|
.orderBy('date', 'ASC')
|
||||||
builder.modify('filterDateRange', startingDate);
|
.where('cost', '>', 0)
|
||||||
builder.orderBy('invoice_date', 'ASC');
|
.withGraphFetched('item');
|
||||||
|
|
||||||
builder.withGraphFetched('entries.item');
|
|
||||||
builder.withGraphFetched('costTransactions(groupedEntriesCost)');
|
|
||||||
});
|
|
||||||
const accountsDepGraph = await accountRepository.getDependencyGraph();
|
const accountsDepGraph = await accountRepository.getDependencyGraph();
|
||||||
const journal = new JournalPoster(tenantId, accountsDepGraph);
|
|
||||||
|
|
||||||
|
const journal = new JournalPoster(tenantId, accountsDepGraph);
|
||||||
const journalCommands = new JournalCommands(journal);
|
const journalCommands = new JournalCommands(journal);
|
||||||
|
|
||||||
if (override) {
|
if (override) {
|
||||||
const oldTransactions = await AccountTransaction.query()
|
await journalCommands.revertInventoryCostJournalEntries(startingDate);
|
||||||
.whereIn('reference_type', ['SaleInvoice'])
|
|
||||||
.onBuild((builder: any) => {
|
|
||||||
builder.modify('filterDateRange', startingDate);
|
|
||||||
})
|
|
||||||
.withGraphFetched('account.type');
|
|
||||||
|
|
||||||
journal.fromTransactions(oldTransactions);
|
|
||||||
journal.removeEntries();
|
|
||||||
}
|
}
|
||||||
salesInvoices.forEach((saleInvoice: ISaleInvoice & {
|
inventoryCostLotTrans.forEach(
|
||||||
costTransactions: IInventoryLotCost[],
|
(inventoryCostLot: IInventoryLotCost & { item: IItem }) => {
|
||||||
entries: IItemEntry & { item: IItem },
|
journalCommands.saleInvoiceInventoryCost(inventoryCostLot);
|
||||||
}) => {
|
}
|
||||||
journalCommands.saleInvoice(saleInvoice, receivableAccount.id);
|
);
|
||||||
});
|
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
journal.deleteEntries(),
|
journal.deleteEntries(),
|
||||||
journal.saveEntries(),
|
journal.saveEntries(),
|
||||||
journal.saveBalance(),
|
journal.saveBalance()
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes the sale invoice journal entries.
|
|
||||||
*/
|
|
||||||
async writeNonInventoryInvoiceEntries(
|
|
||||||
tenantId: number,
|
|
||||||
saleInvoice: ISaleInvoice,
|
|
||||||
authorizedUserId: number,
|
|
||||||
override: boolean = false,
|
|
||||||
) {
|
|
||||||
const { accountRepository } = this.tenancy.repositories(tenantId);
|
|
||||||
const { AccountTransaction } = this.tenancy.models(tenantId);
|
|
||||||
|
|
||||||
// Receivable account.
|
|
||||||
const receivableAccount = await accountRepository.findOne({
|
|
||||||
slug: 'accounts-receivable',
|
|
||||||
});
|
|
||||||
const journal = new JournalPoster(tenantId);
|
|
||||||
const journalCommands = new JournalCommands(journal);
|
|
||||||
|
|
||||||
if (override) {
|
|
||||||
const oldTransactions = await AccountTransaction.query()
|
|
||||||
.where('reference_type', 'SaleInvoice')
|
|
||||||
.where('reference_id', saleInvoice.id)
|
|
||||||
.withGraphFetched('account.type');
|
|
||||||
|
|
||||||
journal.fromTransactions(oldTransactions);
|
|
||||||
journal.removeEntries();
|
|
||||||
}
|
|
||||||
journalCommands.saleInvoiceNonInventory(
|
|
||||||
saleInvoice,
|
|
||||||
receivableAccount.id,
|
|
||||||
authorizedUserId,
|
|
||||||
);
|
|
||||||
|
|
||||||
await Promise.all([
|
|
||||||
journal.deleteEntries(),
|
|
||||||
journal.saveEntries(),
|
|
||||||
journal.saveBalance(),
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,6 +80,7 @@ export default {
|
|||||||
onEdited: 'onSaleInvoiceEdited',
|
onEdited: 'onSaleInvoiceEdited',
|
||||||
onDelete: 'onSaleInvoiceDelete',
|
onDelete: 'onSaleInvoiceDelete',
|
||||||
onDeleted: 'onSaleInvoiceDeleted',
|
onDeleted: 'onSaleInvoiceDeleted',
|
||||||
|
onDelivered: 'onSaleInvoiceDelivered',
|
||||||
onBulkDelete: 'onSaleInvoiceBulkDeleted',
|
onBulkDelete: 'onSaleInvoiceBulkDeleted',
|
||||||
onPublished: 'onSaleInvoicePublished',
|
onPublished: 'onSaleInvoicePublished',
|
||||||
onInventoryTransactionsCreated: 'onInvoiceInventoryTransactionsCreated',
|
onInventoryTransactionsCreated: 'onInvoiceInventoryTransactionsCreated',
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import SettingsService from 'services/Settings/SettingsService';
|
|||||||
import SaleEstimateService from 'services/Sales/SalesEstimate';
|
import SaleEstimateService from 'services/Sales/SalesEstimate';
|
||||||
import SaleInvoicesService from 'services/Sales/SalesInvoices';
|
import SaleInvoicesService from 'services/Sales/SalesInvoices';
|
||||||
import ItemsEntriesService from 'services/Items/ItemsEntriesService';
|
import ItemsEntriesService from 'services/Items/ItemsEntriesService';
|
||||||
|
import SalesInvoicesCost from 'services/Sales/SalesInvoicesCost';
|
||||||
|
|
||||||
@EventSubscriber()
|
@EventSubscriber()
|
||||||
export default class SaleInvoiceSubscriber {
|
export default class SaleInvoiceSubscriber {
|
||||||
@@ -16,6 +17,7 @@ export default class SaleInvoiceSubscriber {
|
|||||||
saleEstimatesService: SaleEstimateService;
|
saleEstimatesService: SaleEstimateService;
|
||||||
saleInvoicesService: SaleInvoicesService;
|
saleInvoicesService: SaleInvoicesService;
|
||||||
itemsEntriesService: ItemsEntriesService;
|
itemsEntriesService: ItemsEntriesService;
|
||||||
|
salesInvoicesCost: SalesInvoicesCost;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.logger = Container.get('logger');
|
this.logger = Container.get('logger');
|
||||||
@@ -24,6 +26,7 @@ export default class SaleInvoiceSubscriber {
|
|||||||
this.saleEstimatesService = Container.get(SaleEstimateService);
|
this.saleEstimatesService = Container.get(SaleEstimateService);
|
||||||
this.saleInvoicesService = Container.get(SaleInvoicesService);
|
this.saleInvoicesService = Container.get(SaleInvoicesService);
|
||||||
this.itemsEntriesService = Container.get(ItemsEntriesService);
|
this.itemsEntriesService = Container.get(ItemsEntriesService);
|
||||||
|
this.salesInvoicesCost = Container.get(SalesInvoicesCost);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -95,15 +98,38 @@ export default class SaleInvoiceSubscriber {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Records journal entries of the non-inventory invoice.
|
* Handles handle write income journal entries of sale invoice.
|
||||||
*/
|
*/
|
||||||
@On(events.saleInvoice.onCreated)
|
@On(events.saleInvoice.onCreated)
|
||||||
@On(events.saleInvoice.onEdited)
|
public async handleWriteInvoiceIncomeJournalEntries({
|
||||||
public async handleWritingNonInventoryEntries({ tenantId, saleInvoice, authorizedUser }) {
|
tenantId,
|
||||||
await this.saleInvoicesService.recordNonInventoryJournalEntries(
|
saleInvoiceId,
|
||||||
|
saleInvoice,
|
||||||
|
authorizedUser,
|
||||||
|
}) {
|
||||||
|
const { saleInvoiceRepository } = this.tenancy.repositories(tenantId);
|
||||||
|
|
||||||
|
const saleInvoiceWithItems = await saleInvoiceRepository.findOneById(
|
||||||
|
saleInvoiceId,
|
||||||
|
'entries.item'
|
||||||
|
);
|
||||||
|
await this.saleInvoicesService.writesIncomeJournalEntries(
|
||||||
tenantId,
|
tenantId,
|
||||||
saleInvoice.id,
|
saleInvoiceWithItems
|
||||||
authorizedUser.id,
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increments the sale invoice items once the invoice created.
|
||||||
|
*/
|
||||||
|
@On(events.saleInvoice.onCreated)
|
||||||
|
public async handleDecrementSaleInvoiceItemsQuantity({
|
||||||
|
tenantId,
|
||||||
|
saleInvoice,
|
||||||
|
}) {
|
||||||
|
await this.itemsEntriesService.decrementItemsQuantity(
|
||||||
|
tenantId,
|
||||||
|
saleInvoice.entries
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,6 +172,29 @@ export default class SaleInvoiceSubscriber {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Records journal entries of the non-inventory invoice.
|
||||||
|
*/
|
||||||
|
@On(events.saleInvoice.onEdited)
|
||||||
|
public async handleRewriteJournalEntriesOnceInvoiceEdit({
|
||||||
|
tenantId,
|
||||||
|
saleInvoiceId,
|
||||||
|
saleInvoice,
|
||||||
|
authorizedUser,
|
||||||
|
}) {
|
||||||
|
const { saleInvoiceRepository } = this.tenancy.repositories(tenantId);
|
||||||
|
|
||||||
|
const saleInvoiceWithItems = await saleInvoiceRepository.findOneById(
|
||||||
|
saleInvoiceId,
|
||||||
|
'entries.item'
|
||||||
|
);
|
||||||
|
await this.saleInvoicesService.writesIncomeJournalEntries(
|
||||||
|
tenantId,
|
||||||
|
saleInvoiceWithItems,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles customer balance decrement once sale invoice deleted.
|
* Handles customer balance decrement once sale invoice deleted.
|
||||||
*/
|
*/
|
||||||
@@ -166,6 +215,20 @@ export default class SaleInvoiceSubscriber {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle reverting journal entries once sale invoice delete.
|
||||||
|
*/
|
||||||
|
@On(events.saleInvoice.onDelete)
|
||||||
|
public async handleRevertingInvoiceJournalEntriesOnDelete({
|
||||||
|
tenantId,
|
||||||
|
saleInvoiceId,
|
||||||
|
}) {
|
||||||
|
await this.saleInvoicesService.revertInvoiceJournalEntries(
|
||||||
|
tenantId,
|
||||||
|
saleInvoiceId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles deleting the inventory transactions once the invoice deleted.
|
* Handles deleting the inventory transactions once the invoice deleted.
|
||||||
*/
|
*/
|
||||||
@@ -203,7 +266,7 @@ export default class SaleInvoiceSubscriber {
|
|||||||
saleInvoiceId,
|
saleInvoiceId,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
await this.saleInvoicesService.scheduleComputeCostByInvoiceId(
|
await this.salesInvoicesCost.scheduleComputeCostByInvoiceId(
|
||||||
tenantId,
|
tenantId,
|
||||||
saleInvoiceId
|
saleInvoiceId
|
||||||
);
|
);
|
||||||
@@ -222,27 +285,13 @@ export default class SaleInvoiceSubscriber {
|
|||||||
const startingDates = map(oldInventoryTransactions, 'date');
|
const startingDates = map(oldInventoryTransactions, 'date');
|
||||||
const startingDate = head(startingDates);
|
const startingDate = head(startingDates);
|
||||||
|
|
||||||
await this.saleInvoicesService.scheduleComputeCostByItemsIds(
|
await this.salesInvoicesCost.scheduleComputeCostByItemsIds(
|
||||||
tenantId,
|
tenantId,
|
||||||
inventoryItemsIds,
|
inventoryItemsIds,
|
||||||
startingDate
|
startingDate
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Increments the sale invoice items once the invoice created.
|
|
||||||
*/
|
|
||||||
@On(events.saleInvoice.onCreated)
|
|
||||||
public async handleDecrementSaleInvoiceItemsQuantity({
|
|
||||||
tenantId,
|
|
||||||
saleInvoice,
|
|
||||||
}) {
|
|
||||||
await this.itemsEntriesService.decrementItemsQuantity(
|
|
||||||
tenantId,
|
|
||||||
saleInvoice.entries
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decrements the sale invoice items once the invoice deleted.
|
* Decrements the sale invoice items once the invoice deleted.
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user