mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 06:40:31 +00:00
fix: sync contacts balance with journal entries.
fix: edit invoice amount that has payment transactions.
This commit is contained in:
@@ -17,7 +17,6 @@ import {
|
||||
|
||||
export default class JournalCommands {
|
||||
journal: JournalPoster;
|
||||
|
||||
models: any;
|
||||
repositories: any;
|
||||
|
||||
@@ -77,7 +76,6 @@ export default class JournalCommands {
|
||||
credit: bill.amount,
|
||||
account: payableAccount.id,
|
||||
contactId: bill.vendorId,
|
||||
contactType: 'Vendor',
|
||||
index: 1,
|
||||
});
|
||||
this.journal.credit(payableEntry);
|
||||
@@ -113,8 +111,8 @@ export default class JournalCommands {
|
||||
) {
|
||||
const { accountRepository } = this.repositories;
|
||||
|
||||
const openingBalanceAccount = await accountRepository.findOne({
|
||||
slug: 'opening-balance',
|
||||
const incomeAccount = await accountRepository.findOne({
|
||||
slug: 'other-income',
|
||||
});
|
||||
const receivableAccount = await accountRepository.findOne({
|
||||
slug: 'accounts-receivable',
|
||||
@@ -123,8 +121,6 @@ export default class JournalCommands {
|
||||
const commonEntry = {
|
||||
referenceType: 'CustomerOpeningBalance',
|
||||
referenceId: customerId,
|
||||
contactType: 'Customer',
|
||||
contactId: customerId,
|
||||
date: openingBalanceAt,
|
||||
userId,
|
||||
};
|
||||
@@ -133,13 +129,14 @@ export default class JournalCommands {
|
||||
credit: 0,
|
||||
debit: openingBalance,
|
||||
account: receivableAccount.id,
|
||||
contactId: customerId,
|
||||
index: 1,
|
||||
});
|
||||
const creditEntry = new JournalEntry({
|
||||
...commonEntry,
|
||||
credit: openingBalance,
|
||||
debit: 0,
|
||||
account: openingBalanceAccount.id,
|
||||
account: incomeAccount.id,
|
||||
index: 2,
|
||||
});
|
||||
this.journal.debit(debitEntry);
|
||||
@@ -171,8 +168,6 @@ export default class JournalCommands {
|
||||
const commonEntry = {
|
||||
referenceType: 'VendorOpeningBalance',
|
||||
referenceId: vendorId,
|
||||
contactType: 'Vendor',
|
||||
contactId: vendorId,
|
||||
date: openingBalanceAt,
|
||||
userId: authorizedUserId,
|
||||
};
|
||||
@@ -182,6 +177,7 @@ export default class JournalCommands {
|
||||
credit: openingBalance,
|
||||
debit: 0,
|
||||
index: 1,
|
||||
contactId: vendorId,
|
||||
});
|
||||
const debitEntry = new JournalEntry({
|
||||
...commonEntry,
|
||||
@@ -297,7 +293,6 @@ export default class JournalCommands {
|
||||
account: entry.accountId,
|
||||
referenceType: 'Journal',
|
||||
referenceId: manualJournalObj.id,
|
||||
contactType: entry.contactType,
|
||||
contactId: entry.contactId,
|
||||
note: entry.note,
|
||||
date: manualJournalObj.date,
|
||||
@@ -379,6 +374,7 @@ export default class JournalCommands {
|
||||
...commonEntry,
|
||||
debit: saleInvoice.balance,
|
||||
account: receivableAccountId,
|
||||
contactId: saleInvoice.customerId,
|
||||
index: 1,
|
||||
});
|
||||
this.journal.debit(receivableEntry);
|
||||
|
||||
74
server/src/services/Accounting/JournalContacts.ts
Normal file
74
server/src/services/Accounting/JournalContacts.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import async from 'async';
|
||||
|
||||
export default class JournalContacts {
|
||||
saveContactBalanceQueue: any;
|
||||
contactsBalanceTable: {
|
||||
[key: number]: { credit: number; debit: number };
|
||||
} = {};
|
||||
|
||||
constructor(journal) {
|
||||
this.journal = journal;
|
||||
this.saveContactBalanceQueue = async.queue(
|
||||
this.saveContactBalanceChangeTask.bind(this),
|
||||
10
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Sets the contact balance change.
|
||||
*/
|
||||
private getContactsBalanceChanges(entry) {
|
||||
if (!entry.contactId) {
|
||||
return;
|
||||
}
|
||||
const change = {
|
||||
debit: entry.debit,
|
||||
credit: entry.credit,
|
||||
};
|
||||
if (!this.contactsBalanceTable[entry.contactId]) {
|
||||
this.contactsBalanceTable[entry.contactId] = { credit: 0, debit: 0 };
|
||||
}
|
||||
if (change.credit) {
|
||||
this.contactsBalanceTable[entry.contactId].credit += change.credit;
|
||||
}
|
||||
if (change.debit) {
|
||||
this.contactsBalanceTable[entry.contactId].debit += change.debit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save contacts balance change.
|
||||
*/
|
||||
saveContactsBalance() {
|
||||
const balanceChanges = Object.entries(
|
||||
this.contactsBalanceTable
|
||||
).map(([contactId, { credit, debit }]) => ({ contactId, credit, debit }));
|
||||
|
||||
return this.saveContactBalanceQueue.pushAsync(balanceChanges);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves contact balance change task.
|
||||
* @param {number} contactId - Contact id.
|
||||
* @param {number} credit - Credit amount.
|
||||
* @param {number} debit - Debit amount.
|
||||
*/
|
||||
async saveContactBalanceChangeTask({ contactId, credit, debit }, callback) {
|
||||
const { contactRepository } = this.repositories;
|
||||
|
||||
const contact = await contactRepository.findOneById(contactId);
|
||||
let balanceChange = 0;
|
||||
|
||||
if (contact.contactNormal === 'credit') {
|
||||
balanceChange += credit - debit;
|
||||
} else {
|
||||
balanceChange += debit - credit;
|
||||
}
|
||||
// Contact change balance.
|
||||
await contactRepository.changeNumber(
|
||||
{ id: contactId },
|
||||
'balance',
|
||||
balanceChange
|
||||
);
|
||||
callback();
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,4 @@ export default class JournalFinancial {
|
||||
this.journal = journal;
|
||||
this.accountsDepGraph = this.journal.accountsDepGraph;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { omit, get } from 'lodash';
|
||||
import { omit, get, chain } from 'lodash';
|
||||
import moment from 'moment';
|
||||
import { Container } from 'typedi';
|
||||
import async from 'async';
|
||||
import JournalEntry from 'services/Accounting/JournalEntry';
|
||||
import TenancyService from 'services/Tenancy/TenancyService';
|
||||
import {
|
||||
@@ -11,6 +12,19 @@ import {
|
||||
TEntryType,
|
||||
} from 'interfaces';
|
||||
|
||||
const CONTACTS_CONFIG = [
|
||||
{
|
||||
accountBySlug: 'accounts-receivable',
|
||||
contactService: 'customer',
|
||||
assignRequired: true,
|
||||
},
|
||||
{
|
||||
accountBySlug: 'accounts-payable',
|
||||
contactService: 'vendor',
|
||||
assignRequired: true,
|
||||
},
|
||||
];
|
||||
|
||||
export default class JournalPoster implements IJournalPoster {
|
||||
tenantId: number;
|
||||
tenancy: TenancyService;
|
||||
@@ -24,6 +38,10 @@ export default class JournalPoster implements IJournalPoster {
|
||||
accountsDepGraph: IAccountsChange;
|
||||
|
||||
accountsBalanceTable: { [key: number]: number } = {};
|
||||
contactsBalanceTable: {
|
||||
[key: number]: { credit: number; debit: number }[];
|
||||
} = {};
|
||||
saveContactBalanceQueue: any;
|
||||
|
||||
/**
|
||||
* Journal poster constructor.
|
||||
@@ -39,6 +57,10 @@ export default class JournalPoster implements IJournalPoster {
|
||||
if (accountsGraph) {
|
||||
this.accountsDepGraph = accountsGraph;
|
||||
}
|
||||
this.saveContactBalanceQueue = async.queue(
|
||||
this.saveContactBalanceChangeTask.bind(this),
|
||||
10
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,7 +91,7 @@ export default class JournalPoster implements IJournalPoster {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Detarmines the ledger is empty.
|
||||
*/
|
||||
public isEmpty() {
|
||||
return this.entries.length === 0;
|
||||
@@ -85,6 +107,7 @@ export default class JournalPoster implements IJournalPoster {
|
||||
}
|
||||
this.entries.push(entryModel.entry);
|
||||
this.setAccountBalanceChange(entryModel.entry);
|
||||
this.setContactBalanceChange(entryModel.entry);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,6 +120,83 @@ export default class JournalPoster implements IJournalPoster {
|
||||
}
|
||||
this.entries.push(entryModel.entry);
|
||||
this.setAccountBalanceChange(entryModel.entry);
|
||||
this.setContactBalanceChange(entryModel.entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the contact balance change.
|
||||
*/
|
||||
private setContactBalanceChange(entry) {
|
||||
if (!entry.contactId) {
|
||||
return;
|
||||
}
|
||||
const change = {
|
||||
debit: entry.debit || 0,
|
||||
credit: entry.credit || 0,
|
||||
account: entry.account,
|
||||
};
|
||||
if (!this.contactsBalanceTable[entry.contactId]) {
|
||||
this.contactsBalanceTable[entry.contactId] = [];
|
||||
}
|
||||
this.contactsBalanceTable[entry.contactId].push(change);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save contacts balance change.
|
||||
*/
|
||||
async saveContactsBalance() {
|
||||
await this.initAccountsDepGraph();
|
||||
|
||||
const balanceChanges = Object.entries(this.contactsBalanceTable).map(
|
||||
([contactId, entries]) => ({
|
||||
contactId,
|
||||
entries: entries.filter((entry) => {
|
||||
const account = this.accountsDepGraph.getNodeData(entry.account);
|
||||
|
||||
return (
|
||||
account &&
|
||||
CONTACTS_CONFIG.some((config) => {
|
||||
return config.accountBySlug === account.slug;
|
||||
})
|
||||
);
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
const balanceEntries = chain(balanceChanges)
|
||||
.map((change) => change.entries.map(entry => ({
|
||||
...entry,
|
||||
contactId: change.contactId
|
||||
})))
|
||||
.flatten()
|
||||
.value();
|
||||
|
||||
return this.saveContactBalanceQueue.pushAsync(balanceEntries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves contact balance change task.
|
||||
* @param {number} contactId - Contact id.
|
||||
* @param {number} credit - Credit amount.
|
||||
* @param {number} debit - Debit amount.
|
||||
*/
|
||||
async saveContactBalanceChangeTask({ contactId, credit, debit }) {
|
||||
const { contactRepository } = this.repositories;
|
||||
|
||||
const contact = await contactRepository.findOneById(contactId);
|
||||
let balanceChange = 0;
|
||||
|
||||
if (contact.contactNormal === 'credit') {
|
||||
balanceChange += credit - debit;
|
||||
} else {
|
||||
balanceChange += debit - credit;
|
||||
}
|
||||
// Contact change balance.
|
||||
await contactRepository.changeNumber(
|
||||
{ id: contactId },
|
||||
'balance',
|
||||
balanceChange
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -321,7 +421,8 @@ export default class JournalPoster implements IJournalPoster {
|
||||
entry.credit = -1 * entry.credit;
|
||||
entry.debit = -1 * entry.debit;
|
||||
|
||||
this.setAccountBalanceChange(entry, entry.accountNormal);
|
||||
this.setAccountBalanceChange(entry);
|
||||
this.setContactBalanceChange(entry);
|
||||
});
|
||||
this.deletedEntriesIds.push(...removeEntries.map((entry) => entry.id));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user