mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-18 13:50:31 +00:00
fix: writing the bill journal entries.
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
import { sumBy, chain } from 'lodash';
|
||||
import moment from 'moment';
|
||||
import { IBill } from 'interfaces';
|
||||
import JournalPoster from "./JournalPoster";
|
||||
import JournalEntry from "./JournalEntry";
|
||||
import { AccountTransaction } from 'models';
|
||||
@@ -7,6 +9,7 @@ import {
|
||||
IManualJournal,
|
||||
IExpense,
|
||||
IExpenseCategory,
|
||||
IItem,
|
||||
} from 'interfaces';
|
||||
|
||||
interface IInventoryCostEntity {
|
||||
@@ -57,6 +60,68 @@ export default class JournalCommands{
|
||||
this.models = this.journal.models;
|
||||
}
|
||||
|
||||
/**
|
||||
* Records the bill journal entries.
|
||||
* @param {IBill} bill
|
||||
* @param {boolean} override - Override the old bill entries.
|
||||
*/
|
||||
async bill(bill: IBill, override: boolean = false): Promise<void> {
|
||||
const { transactionsRepository, accountRepository } = this.repositories;
|
||||
const { Item, ItemEntry } = this.models;
|
||||
|
||||
const entriesItemsIds = bill.entries.map((entry) => entry.itemId);
|
||||
|
||||
// Retrieve the bill transaction items.
|
||||
const storedItems = await Item.query().whereIn('id', entriesItemsIds);
|
||||
|
||||
const storedItemsMap = new Map(storedItems.map((item) => [item.id, item]));
|
||||
const payableAccount = await accountRepository.findOne({ slug: 'accounts-payable' });
|
||||
const formattedDate = moment(bill.billDate).format('YYYY-MM-DD');
|
||||
|
||||
const commonJournalMeta = {
|
||||
debit: 0,
|
||||
credit: 0,
|
||||
referenceId: bill.id,
|
||||
referenceType: 'Bill',
|
||||
date: formattedDate,
|
||||
userId: bill.userId,
|
||||
};
|
||||
// Overrides the old bill entries.
|
||||
if (override) {
|
||||
const entries = await transactionsRepository.journal({
|
||||
referenceType: ['Bill'],
|
||||
referenceId: [bill.id],
|
||||
});
|
||||
this.journal.fromTransactions(entries);
|
||||
this.journal.removeEntries();
|
||||
}
|
||||
const payableEntry = new JournalEntry({
|
||||
...commonJournalMeta,
|
||||
credit: bill.amount,
|
||||
account: payableAccount.id,
|
||||
contactId: bill.vendorId,
|
||||
contactType: 'Vendor',
|
||||
index: 1,
|
||||
});
|
||||
this.journal.credit(payableEntry);
|
||||
|
||||
bill.entries.forEach((entry, index) => {
|
||||
const item: IItem = storedItemsMap.get(entry.itemId);
|
||||
const amount = ItemEntry.calcAmount(entry);
|
||||
|
||||
const debitEntry = new JournalEntry({
|
||||
...commonJournalMeta,
|
||||
debit: amount,
|
||||
account:
|
||||
['inventory'].indexOf(item.type) !== -1
|
||||
? item.inventoryAccountId
|
||||
: item.costAccountId,
|
||||
index: index + 2,
|
||||
});
|
||||
this.journal.debit(debitEntry);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Customer opening balance journals.
|
||||
* @param {number} customerId
|
||||
|
||||
@@ -21,7 +21,7 @@ export default class JournalPoster implements IJournalPoster {
|
||||
deletedEntriesIds: number[] = [];
|
||||
entries: IJournalEntry[] = [];
|
||||
balancesChange: IAccountsChange = {};
|
||||
accountsDepGraph: IAccountsChange = {};
|
||||
accountsDepGraph: IAccountsChange;
|
||||
|
||||
accountsBalanceTable: { [key: number]: number; } = {};
|
||||
|
||||
@@ -250,12 +250,12 @@ export default class JournalPoster implements IJournalPoster {
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public async saveEntries() {
|
||||
const { AccountTransaction } = this.models;
|
||||
const { transactionsRepository } = this.repositories;
|
||||
const saveOperations: Promise<void>[] = [];
|
||||
|
||||
this.entries.forEach((entry) => {
|
||||
const oper = AccountTransaction.query()
|
||||
.insert({
|
||||
const oper = transactionsRepository
|
||||
.create({
|
||||
accountId: entry.account,
|
||||
...omit(entry, ['account']),
|
||||
});
|
||||
@@ -309,12 +309,10 @@ export default class JournalPoster implements IJournalPoster {
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
public async deleteEntries() {
|
||||
const { AccountTransaction } = this.models;
|
||||
const { transactionsRepository } = this.repositories;
|
||||
|
||||
if (this.deletedEntriesIds.length > 0) {
|
||||
await AccountTransaction.query()
|
||||
.whereIn('id', this.deletedEntriesIds)
|
||||
.delete();
|
||||
await transactionsRepository.deleteWhereIdIn(this.deletedEntriesIds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -332,7 +330,6 @@ export default class JournalPoster implements IJournalPoster {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculates the entries balance change.
|
||||
* @public
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
import { ServiceError } from 'exceptions';
|
||||
import ItemsService from 'services/Items/ItemsService';
|
||||
import ItemsEntriesService from 'services/Items/ItemsEntriesService';
|
||||
import JournalCommands from 'services/Accounting/JournalCommands';
|
||||
|
||||
const ERRORS = {
|
||||
BILL_NOT_FOUND: 'BILL_NOT_FOUND',
|
||||
@@ -139,8 +140,8 @@ export default class BillsService extends SalesInvoicesCost {
|
||||
private async billDTOToModel(
|
||||
tenantId: number,
|
||||
billDTO: IBillDTO | IBillEditDTO,
|
||||
oldBill?: IBill,
|
||||
authorizedUser: ISystemUser,
|
||||
oldBill?: IBill,
|
||||
) {
|
||||
const { ItemEntry } = this.tenancy.models(tenantId);
|
||||
let invLotNumber = oldBill?.invLotNumber;
|
||||
@@ -156,7 +157,7 @@ export default class BillsService extends SalesInvoicesCost {
|
||||
|
||||
return {
|
||||
...formatDateFields(
|
||||
omit(billDTO, ['open']),
|
||||
omit(billDTO, ['open', 'entries']),
|
||||
['billDate', 'dueDate']
|
||||
),
|
||||
amount,
|
||||
@@ -165,7 +166,6 @@ export default class BillsService extends SalesInvoicesCost {
|
||||
reference_type: 'Bill',
|
||||
...omit(entry, ['amount', 'id']),
|
||||
})),
|
||||
|
||||
// Avoid rewrite the open date in edit mode when already opened.
|
||||
...(billDTO.open && (!oldBill?.openedAt)) && ({
|
||||
openedAt: moment().toMySqlDateTime(),
|
||||
@@ -197,7 +197,7 @@ export default class BillsService extends SalesInvoicesCost {
|
||||
const { Bill } = this.tenancy.models(tenantId);
|
||||
|
||||
this.logger.info('[bill] trying to create a new bill', { tenantId, billDTO });
|
||||
const billObj = await this.billDTOToModel(tenantId, billDTO, null, authorizedUser);
|
||||
const billObj = await this.billDTOToModel(tenantId, billDTO, authorizedUser, null);
|
||||
|
||||
// Retrieve vendor or throw not found service error.
|
||||
await this.getVendorOrThrowError(tenantId, billDTO.vendorId);
|
||||
@@ -253,7 +253,7 @@ export default class BillsService extends SalesInvoicesCost {
|
||||
const oldBill = await this.getBillOrThrowError(tenantId, billId);
|
||||
|
||||
// Transforms the bill DTO object to model object.
|
||||
const billObj = await this.billDTOToModel(tenantId, billDTO, oldBill, authorizedUser);
|
||||
const billObj = await this.billDTOToModel(tenantId, billDTO, authorizedUser, oldBill);
|
||||
|
||||
// Retrieve vendor details or throw not found service error.
|
||||
await this.getVendorOrThrowError(tenantId, billDTO.vendorId);
|
||||
@@ -342,62 +342,16 @@ export default class BillsService extends SalesInvoicesCost {
|
||||
* @param {IBill} bill
|
||||
* @param {Integer} billId
|
||||
*/
|
||||
public async recordJournalTransactions(tenantId: number, bill: IBill, billId?: number) {
|
||||
const { AccountTransaction, Item, ItemEntry } = this.tenancy.models(tenantId);
|
||||
const { accountRepository } = this.tenancy.repositories(tenantId);
|
||||
|
||||
const entriesItemsIds = bill.entries.map((entry) => entry.itemId);
|
||||
const formattedDate = moment(bill.billDate).format('YYYY-MM-DD');
|
||||
|
||||
const storedItems = await Item.query().whereIn('id', entriesItemsIds);
|
||||
|
||||
const storedItemsMap = new Map(storedItems.map((item) => [item.id, item]));
|
||||
const payableAccount = await accountRepository.find({ slug: 'accounts-payable' });
|
||||
|
||||
public async recordJournalTransactions(
|
||||
tenantId: number,
|
||||
bill: IBill,
|
||||
override: boolean = false,
|
||||
) {
|
||||
const journal = new JournalPoster(tenantId);
|
||||
const journalCommands = new JournalCommands(journal);
|
||||
|
||||
const commonJournalMeta = {
|
||||
debit: 0,
|
||||
credit: 0,
|
||||
referenceId: bill.id,
|
||||
referenceType: 'Bill',
|
||||
date: formattedDate,
|
||||
userId: bill.userId,
|
||||
};
|
||||
if (billId) {
|
||||
const transactions = await AccountTransaction.query()
|
||||
.whereIn('reference_type', ['Bill'])
|
||||
.whereIn('reference_id', [billId])
|
||||
.withGraphFetched('account.type');
|
||||
await journalCommands.bill(bill, override)
|
||||
|
||||
journal.loadEntries(transactions);
|
||||
journal.removeEntries();
|
||||
}
|
||||
const payableEntry = new JournalEntry({
|
||||
...commonJournalMeta,
|
||||
credit: bill.amount,
|
||||
account: payableAccount.id,
|
||||
contactId: bill.vendorId,
|
||||
contactType: 'Vendor',
|
||||
index: 1,
|
||||
});
|
||||
journal.credit(payableEntry);
|
||||
|
||||
bill.entries.forEach((entry, index) => {
|
||||
const item: IItem = storedItemsMap.get(entry.itemId);
|
||||
const amount = ItemEntry.calcAmount(entry);
|
||||
|
||||
const debitEntry = new JournalEntry({
|
||||
...commonJournalMeta,
|
||||
debit: amount,
|
||||
account:
|
||||
['inventory'].indexOf(item.type) !== -1
|
||||
? item.inventoryAccountId
|
||||
: item.costAccountId,
|
||||
index: index + 2,
|
||||
});
|
||||
journal.debit(debitEntry);
|
||||
});
|
||||
return Promise.all([
|
||||
journal.deleteEntries(),
|
||||
journal.saveEntries(),
|
||||
|
||||
Reference in New Issue
Block a user