mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-18 22:00:31 +00:00
feat: Financial statements dependency graph calculate.
This commit is contained in:
@@ -4,16 +4,21 @@ import JournalEntry from '@/services/Accounting/JournalEntry';
|
||||
import AccountTransaction from '@/models/AccountTransaction';
|
||||
import AccountBalance from '@/models/AccountBalance';
|
||||
import {promiseSerial} from '@/utils';
|
||||
import Account from '../../models/Account';
|
||||
import Account from '@/models/Account';
|
||||
import NestedSet from '../../collection/NestedSet';
|
||||
|
||||
|
||||
export default class JournalPoster {
|
||||
/**
|
||||
* Journal poster constructor.
|
||||
*/
|
||||
constructor() {
|
||||
constructor(accountsGraph) {
|
||||
this.entries = [];
|
||||
this.balancesChange = {};
|
||||
this.deletedEntriesIds = [];
|
||||
|
||||
this.accountsBalanceTable = {};
|
||||
this.accountsGraph = accountsGraph;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -29,7 +34,7 @@ export default class JournalPoster {
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the debit entry for the given account.
|
||||
* Writes the debit entr y for the given account.
|
||||
* @param {JournalEntry} entry -
|
||||
*/
|
||||
debit(entryModel) {
|
||||
@@ -45,18 +50,47 @@ export default class JournalPoster {
|
||||
* @param {JournalEntry} entry
|
||||
* @param {String} type
|
||||
*/
|
||||
setAccountBalanceChange(entry, type) {
|
||||
if (!this.balancesChange[entry.account]) {
|
||||
this.balancesChange[entry.account] = 0;
|
||||
setAccountBalanceChange(entry, entryType) {
|
||||
const depAccountsIds = this.accountsGraph.dependantsOf(entry.account);
|
||||
|
||||
const balanceChangeEntry = {
|
||||
debit: entry.debit,
|
||||
credit: entry.credit,
|
||||
entryType,
|
||||
accountNormal: entry.accountNormal,
|
||||
};
|
||||
this._setAccountBalanceChange({
|
||||
...balanceChangeEntry,
|
||||
accountId: entry.account,
|
||||
});
|
||||
|
||||
// Effect parent accounts of the given account id.
|
||||
depAccountsIds.forEach((accountId) => {
|
||||
this._setAccountBalanceChange({
|
||||
...balanceChangeEntry,
|
||||
accountId,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets account balance change.
|
||||
* @private
|
||||
*/
|
||||
_setAccountBalanceChange({
|
||||
accountId, accountNormal, debit, credit, entryType
|
||||
}) {
|
||||
if (!this.balancesChange[accountId]) {
|
||||
this.balancesChange[accountId] = 0;
|
||||
}
|
||||
let change = 0;
|
||||
|
||||
if (entry.accountNormal === 'credit') {
|
||||
change = (type === 'credit') ? entry.credit : -1 * entry.debit;
|
||||
} else if (entry.accountNormal === 'debit') {
|
||||
change = (type === 'debit') ? entry.debit : -1 * entry.credit;
|
||||
if (accountNormal === 'credit') {
|
||||
change = (entryType === 'credit') ? credit : -1 * debit;
|
||||
} else if (accountNormal === 'debit') {
|
||||
change = (entryType === 'debit') ? debit : -1 * credit;
|
||||
}
|
||||
this.balancesChange[entry.account] += change;
|
||||
this.balancesChange[accountId] += change;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,11 +120,10 @@ export default class JournalPoster {
|
||||
const balanceFindOneOpers = [];
|
||||
let balanceAccounts = [];
|
||||
|
||||
const effectAccountsOpers = [];
|
||||
|
||||
balancesList.forEach((balance) => {
|
||||
const oper = AccountBalance.tenant()
|
||||
.query().findOne('account_id', balance.account_id);
|
||||
.query()
|
||||
.findOne('account_id', balance.account_id);
|
||||
balanceFindOneOpers.push(oper);
|
||||
});
|
||||
balanceAccounts = await Promise.all(balanceFindOneOpers);
|
||||
@@ -102,9 +135,11 @@ export default class JournalPoster {
|
||||
const foundAccBalance = balanceAccounts.some((account) => (
|
||||
account && account.account_id === balance.account_id
|
||||
));
|
||||
|
||||
if (foundAccBalance) {
|
||||
const query = AccountBalance.tenant()
|
||||
.query()[method]('amount', Math.abs(balance.amount))
|
||||
.query()
|
||||
[method]('amount', Math.abs(balance.amount))
|
||||
.where('account_id', balance.account_id);
|
||||
|
||||
balanceUpdateOpers.push(query);
|
||||
@@ -116,67 +151,12 @@ export default class JournalPoster {
|
||||
});
|
||||
balanceInsertOpers.push(query);
|
||||
}
|
||||
|
||||
const effectedAccountsOper = this.effectAssociatedAccountsBalance(
|
||||
balance.accountId, amount, 'USD', method,
|
||||
);
|
||||
effectAccountsOpers.push(effectedAccountsOper);
|
||||
});
|
||||
await Promise.all([
|
||||
...balanceUpdateOpers, ...balanceInsertOpers,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Effect associated descendants and parent accounts
|
||||
* of the given account id.
|
||||
* @param {Number} accountId
|
||||
* @param {Number} amount
|
||||
* @param {String} currencyCode
|
||||
* @param {*} method
|
||||
*/
|
||||
async effectAssociatedAccountsBalance(accountId, amount, currencyCode = 'USD', method) {
|
||||
const accounts = await Account.query().withGraphFetched('balance');
|
||||
|
||||
const accountsDecendences = accounts.getDescendants();
|
||||
|
||||
const asyncOpers = [];
|
||||
const accountsInsertBalance = [];
|
||||
const accountsUpdateBalance = [];
|
||||
|
||||
accounts.forEach((account) => {
|
||||
const accountBalances = account.balance;
|
||||
const currencyBalance = accountBalances
|
||||
.find(balance => balance.currencyCode === currencyCode);
|
||||
|
||||
if (currencyBalance) {
|
||||
accountsInsertBalance.push(account.id);
|
||||
} else {
|
||||
accountsUpdateBalance.push(account.id);
|
||||
}
|
||||
});
|
||||
|
||||
accountsInsertBalance.forEach((accountId) => {
|
||||
const oper = AccountBalance.tenant().query().insert({
|
||||
account_id: accountId,
|
||||
amount: method === 'decrement' ? amount * -1 : amount,
|
||||
currency_code: currencyCode,
|
||||
});
|
||||
asyncOpers.push(oper);
|
||||
});
|
||||
|
||||
if (accountsUpdateBalance.length > 0) {
|
||||
const oper = AccountBalance.tenant().query()
|
||||
.whereIn('account_id', accountsUpdateBalance);
|
||||
[method]('amount', Math.abs(amount))
|
||||
.where('currency_code', currencyCode);
|
||||
|
||||
asyncOpers.push(oper);
|
||||
}
|
||||
await Promise.all(asyncOpers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the stacked journal entries to the storage.
|
||||
*/
|
||||
@@ -236,6 +216,9 @@ export default class JournalPoster {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all the stacked entries.
|
||||
*/
|
||||
async deleteEntries() {
|
||||
if (this.deletedEntriesIds.length > 0) {
|
||||
await AccountTransaction.tenant().query()
|
||||
@@ -269,6 +252,29 @@ export default class JournalPoster {
|
||||
return closingBalance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the given account balance with dependencies accounts.
|
||||
* @param {Number} accountId
|
||||
* @param {Date} closingDate
|
||||
* @param {String} dateType
|
||||
* @return {Number}
|
||||
*/
|
||||
getAccountBalance(accountId, closingDate, dateType) {
|
||||
const accountNode = this.accountsGraph.getNodeData(accountId);
|
||||
const depAccountsIds = this.accountsGraph.dependenciesOf(accountId);
|
||||
const depAccounts = depAccountsIds.map((id) => this.accountsGraph.getNodeData(id));
|
||||
let balance = 0;
|
||||
|
||||
[...depAccounts, accountNode].forEach((account) => {
|
||||
// if (!this.accountsBalanceTable[account.id]) {
|
||||
const closingBalance = this.getClosingBalance(account.id, closingDate, dateType);
|
||||
this.accountsBalanceTable[account.id] = closingBalance;
|
||||
// }
|
||||
balance += this.accountsBalanceTable[account.id];
|
||||
});
|
||||
return balance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the credit/debit sumation for the given account and date.
|
||||
* @param {Number} account -
|
||||
@@ -299,6 +305,30 @@ export default class JournalPoster {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve trial balance of the given account with depends.
|
||||
* @param {Number} accountId
|
||||
* @param {Date} closingDate
|
||||
* @param {String} dateType
|
||||
* @return {Number}
|
||||
*/
|
||||
getTrialBalanceWithDepands(accountId, closingDate, dateType) {
|
||||
const accountNode = this.accountsGraph.getNodeData(accountId);
|
||||
const depAccountsIds = this.accountsGraph.dependenciesOf(accountId);
|
||||
const depAccounts = depAccountsIds.map((id) => this.accountsGraph.getNodeData(id));
|
||||
|
||||
const trialBalance = { credit: 0, debit: 0, balance: 0 };
|
||||
|
||||
[...depAccounts, accountNode].forEach((account) => {
|
||||
const _trialBalance = this.getTrialBalance(account.id, closingDate, dateType);
|
||||
|
||||
trialBalance.credit += _trialBalance.credit;
|
||||
trialBalance.debit += _trialBalance.debit;
|
||||
trialBalance.balance += _trialBalance.balance;
|
||||
});
|
||||
return trialBalance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load fetched accounts journal entries.
|
||||
* @param {Array} entries -
|
||||
@@ -324,8 +354,4 @@ export default class JournalPoster {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static loadAccounts() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user