mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 23:00:34 +00:00
fix: account balance change with credit/debit entries.
This commit is contained in:
@@ -2,7 +2,6 @@ import { get } from 'lodash';
|
|||||||
import { ACCOUNT_TYPES } from 'data/AccountTypes';
|
import { ACCOUNT_TYPES } from 'data/AccountTypes';
|
||||||
|
|
||||||
export default class AccountTypesUtils {
|
export default class AccountTypesUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve account types list.
|
* Retrieve account types list.
|
||||||
*/
|
*/
|
||||||
@@ -11,15 +10,16 @@ export default class AccountTypesUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Retrieve accounts types by the given root type.
|
||||||
* @param {string} rootType
|
* @param {string} rootType -
|
||||||
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
static getTypesByRootType(rootType: string) {
|
static getTypesByRootType(rootType: string) {
|
||||||
return ACCOUNT_TYPES.filter((type) => type.rootType === rootType);
|
return ACCOUNT_TYPES.filter((type) => type.rootType === rootType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Retrieve account type by the given account type key.
|
||||||
* @param {string} key
|
* @param {string} key
|
||||||
* @param {string} accessor
|
* @param {string} accessor
|
||||||
*/
|
*/
|
||||||
@@ -33,7 +33,7 @@ export default class AccountTypesUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Retrieve accounts types by the parent account type.
|
||||||
* @param {string} parentType
|
* @param {string} parentType
|
||||||
*/
|
*/
|
||||||
static getTypesByParentType(parentType: string) {
|
static getTypesByParentType(parentType: string) {
|
||||||
@@ -41,20 +41,19 @@ export default class AccountTypesUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Retrieve accounts types by the given account normal.
|
||||||
* @param {string} normal
|
* @param {string} normal
|
||||||
*/
|
*/
|
||||||
static getTypesByNormal(normal: string) {
|
static getTypesByNormal(normal: string) {
|
||||||
return ACCOUNT_TYPES.filter((type) => type.normal === normal);
|
return ACCOUNT_TYPES.filter((type) => type.normal === normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Detarmines whether the root type equals the account type.
|
||||||
* @param {string} key
|
* @param {string} key
|
||||||
* @param {string} rootType
|
* @param {string} rootType
|
||||||
*/
|
*/
|
||||||
static isRootTypeEqualsKey(key: string, rootType: string) {
|
static isRootTypeEqualsKey(key: string, rootType: string): boolean {
|
||||||
return ACCOUNT_TYPES.some((type) => {
|
return ACCOUNT_TYPES.some((type) => {
|
||||||
const isType = type.key === key;
|
const isType = type.key === key;
|
||||||
const isRootType = type.rootType === rootType;
|
const isRootType = type.rootType === rootType;
|
||||||
@@ -64,11 +63,11 @@ export default class AccountTypesUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Detarmines whether the parent account type equals the account type key.
|
||||||
* @param {string} key
|
* @param {string} key - Account type key.
|
||||||
* @param {string} parentType
|
* @param {string} parentType - Account parent type.
|
||||||
*/
|
*/
|
||||||
static isParentTypeEqualsKey(key: string, parentType: string) {
|
static isParentTypeEqualsKey(key: string, parentType: string): boolean {
|
||||||
return ACCOUNT_TYPES.some((type) => {
|
return ACCOUNT_TYPES.some((type) => {
|
||||||
const isType = type.key === key;
|
const isType = type.key === key;
|
||||||
const isParentType = type.parentType === parentType;
|
const isParentType = type.parentType === parentType;
|
||||||
@@ -78,10 +77,11 @@ export default class AccountTypesUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Detarmines whether account type has balance sheet.
|
||||||
|
* @param {string} key - Account type key.
|
||||||
*
|
*
|
||||||
* @param {string} key
|
|
||||||
*/
|
*/
|
||||||
static isTypeBalanceSheet(key: string) {
|
static isTypeBalanceSheet(key: string): boolean {
|
||||||
return ACCOUNT_TYPES.some((type) => {
|
return ACCOUNT_TYPES.some((type) => {
|
||||||
const isType = type.key === key;
|
const isType = type.key === key;
|
||||||
return isType && type.balanceSheet;
|
return isType && type.balanceSheet;
|
||||||
@@ -89,10 +89,10 @@ export default class AccountTypesUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Detarmines whether account type has profit/loss sheet.
|
||||||
* @param {string} key
|
* @param {string} key - Account type key.
|
||||||
*/
|
*/
|
||||||
static isTypePLSheet(key: string) {
|
static isTypePLSheet(key: string): boolean {
|
||||||
return ACCOUNT_TYPES.some((type) => {
|
return ACCOUNT_TYPES.some((type) => {
|
||||||
const isType = type.key === key;
|
const isType = type.key === key;
|
||||||
return isType && type.incomeSheet;
|
return isType && type.incomeSheet;
|
||||||
|
|||||||
@@ -23,16 +23,13 @@ export default class JournalPoster implements IJournalPoster {
|
|||||||
balancesChange: IAccountsChange = {};
|
balancesChange: IAccountsChange = {};
|
||||||
accountsDepGraph: IAccountsChange;
|
accountsDepGraph: IAccountsChange;
|
||||||
|
|
||||||
accountsBalanceTable: { [key: number]: number; } = {};
|
accountsBalanceTable: { [key: number]: number } = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Journal poster constructor.
|
* Journal poster constructor.
|
||||||
* @param {number} tenantId -
|
* @param {number} tenantId -
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(tenantId: number, accountsGraph?: any) {
|
||||||
tenantId: number,
|
|
||||||
accountsGraph?: any,
|
|
||||||
) {
|
|
||||||
this.initTenancy();
|
this.initTenancy();
|
||||||
|
|
||||||
this.tenantId = tenantId;
|
this.tenantId = tenantId;
|
||||||
@@ -126,7 +123,9 @@ export default class JournalPoster implements IJournalPoster {
|
|||||||
accountChange: IAccountChange
|
accountChange: IAccountChange
|
||||||
) {
|
) {
|
||||||
this.balancesChange = this.accountBalanceChangeReducer(
|
this.balancesChange = this.accountBalanceChangeReducer(
|
||||||
this.balancesChange, accountId, accountChange,
|
this.balancesChange,
|
||||||
|
accountId,
|
||||||
|
accountChange
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,7 +139,7 @@ export default class JournalPoster implements IJournalPoster {
|
|||||||
private accountBalanceChangeReducer(
|
private accountBalanceChangeReducer(
|
||||||
balancesChange: IAccountsChange,
|
balancesChange: IAccountsChange,
|
||||||
accountId: number,
|
accountId: number,
|
||||||
accountChange: IAccountChange,
|
accountChange: IAccountChange
|
||||||
) {
|
) {
|
||||||
const change = { ...balancesChange };
|
const change = { ...balancesChange };
|
||||||
|
|
||||||
@@ -164,26 +163,32 @@ export default class JournalPoster implements IJournalPoster {
|
|||||||
*/
|
*/
|
||||||
private async convertBalanceChangesToArr(
|
private async convertBalanceChangesToArr(
|
||||||
accountsChange: IAccountsChange
|
accountsChange: IAccountsChange
|
||||||
) : Promise<{ account: number, change: number }[]>{
|
): Promise<{ account: number; change: number }[]> {
|
||||||
const mappedList: { account: number, change: number }[] = [];
|
const mappedList: { account: number; change: number }[] = [];
|
||||||
const accountsIds: number[] = Object.keys(accountsChange).map(id => parseInt(id, 10));
|
const accountsIds: number[] = Object.keys(accountsChange).map((id) =>
|
||||||
|
parseInt(id, 10)
|
||||||
|
);
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
accountsIds.map(async (account: number) => {
|
accountsIds.map(async (account: number) => {
|
||||||
const accountChange = accountsChange[account];
|
const accountChange = accountsChange[account];
|
||||||
const accountNode = this.accountsDepGraph.getNodeData(account);
|
const accountNode = this.accountsDepGraph.getNodeData(account);
|
||||||
|
const normal = accountNode.accountNormal;
|
||||||
|
|
||||||
const { normal }: { normal: TEntryType } = accountNode.accountNormal;
|
|
||||||
let change = 0;
|
let change = 0;
|
||||||
|
|
||||||
if (accountChange.credit) {
|
if (accountChange.credit) {
|
||||||
change = (normal === 'credit') ? accountChange.credit : -1 * accountChange.credit;
|
change =
|
||||||
|
normal === 'credit'
|
||||||
|
? accountChange.credit
|
||||||
|
: -1 * accountChange.credit;
|
||||||
}
|
}
|
||||||
if (accountChange.debit) {
|
if (accountChange.debit) {
|
||||||
change = (normal === 'debit') ? accountChange.debit : -1 * accountChange.debit;
|
change =
|
||||||
|
normal === 'debit' ? accountChange.debit : -1 * accountChange.debit;
|
||||||
}
|
}
|
||||||
mappedList.push({ account, change });
|
mappedList.push({ account, change });
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
return mappedList;
|
return mappedList;
|
||||||
}
|
}
|
||||||
@@ -198,20 +203,26 @@ export default class JournalPoster implements IJournalPoster {
|
|||||||
const { Account } = this.models;
|
const { Account } = this.models;
|
||||||
const accountsChange = this.balanceChangeWithDepends(this.balancesChange);
|
const accountsChange = this.balanceChangeWithDepends(this.balancesChange);
|
||||||
const balancesList = await this.convertBalanceChangesToArr(accountsChange);
|
const balancesList = await this.convertBalanceChangesToArr(accountsChange);
|
||||||
const balancesAccounts = balancesList.map(b => b.account);
|
const balancesAccounts = balancesList.map((b) => b.account);
|
||||||
|
|
||||||
// Ensure the accounts has atleast zero in amount.
|
// Ensure the accounts has atleast zero in amount.
|
||||||
await Account.query().where('amount', null).whereIn('id', balancesAccounts)
|
await Account.query()
|
||||||
|
.where('amount', null)
|
||||||
|
.whereIn('id', balancesAccounts)
|
||||||
.patch({ amount: 0 });
|
.patch({ amount: 0 });
|
||||||
|
|
||||||
const balanceUpdateOpers: Promise<void>[] = [];
|
const balanceUpdateOpers: Promise<void>[] = [];
|
||||||
|
|
||||||
balancesList.forEach((balance: { account: number, change: number }) => {
|
balancesList.forEach((balance: { account: number; change: number }) => {
|
||||||
const method: string = (balance.change < 0) ? 'decrement' : 'increment';
|
const method: string = balance.change < 0 ? 'decrement' : 'increment';
|
||||||
|
|
||||||
this.logger.info('[journal_poster] increment/decrement account balance.', {
|
this.logger.info(
|
||||||
balance, tenantId: this.tenantId,
|
'[journal_poster] increment/decrement account balance.',
|
||||||
})
|
{
|
||||||
|
balance,
|
||||||
|
tenantId: this.tenantId,
|
||||||
|
}
|
||||||
|
);
|
||||||
const query = Account.query()
|
const query = Account.query()
|
||||||
[method]('amount', Math.abs(balance.change))
|
[method]('amount', Math.abs(balance.change))
|
||||||
.where('id', balance.account);
|
.where('id', balance.account);
|
||||||
@@ -228,16 +239,22 @@ export default class JournalPoster implements IJournalPoster {
|
|||||||
* @param {IAccountsChange} accountsChange
|
* @param {IAccountsChange} accountsChange
|
||||||
* @returns {IAccountsChange}
|
* @returns {IAccountsChange}
|
||||||
*/
|
*/
|
||||||
private balanceChangeWithDepends(accountsChange: IAccountsChange): IAccountsChange {
|
private balanceChangeWithDepends(
|
||||||
|
accountsChange: IAccountsChange
|
||||||
|
): IAccountsChange {
|
||||||
const accountsIds = Object.keys(accountsChange);
|
const accountsIds = Object.keys(accountsChange);
|
||||||
let changes: IAccountsChange = {};
|
let changes: IAccountsChange = {};
|
||||||
|
|
||||||
accountsIds.forEach((accountId) => {
|
accountsIds.forEach((accountId) => {
|
||||||
const accountChange = accountsChange[accountId];
|
const accountChange = accountsChange[accountId];
|
||||||
const depAccountsIds = this.accountsDepGraph.dependenciesOf(accountId);
|
const depAccountsIds = this.accountsDepGraph.dependantsOf(accountId);
|
||||||
|
|
||||||
[accountId, ...depAccountsIds].forEach((account) => {
|
[accountId, ...depAccountsIds].forEach((account) => {
|
||||||
changes = this.accountBalanceChangeReducer(changes, account, accountChange);
|
changes = this.accountBalanceChangeReducer(
|
||||||
|
changes,
|
||||||
|
account,
|
||||||
|
accountChange
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return changes;
|
return changes;
|
||||||
@@ -260,11 +277,10 @@ export default class JournalPoster implements IJournalPoster {
|
|||||||
const saveOperations: Promise<void>[] = [];
|
const saveOperations: Promise<void>[] = [];
|
||||||
|
|
||||||
this.entries.forEach((entry) => {
|
this.entries.forEach((entry) => {
|
||||||
const oper = transactionsRepository
|
const oper = transactionsRepository.create({
|
||||||
.create({
|
accountId: entry.account,
|
||||||
accountId: entry.account,
|
...omit(entry, ['account']),
|
||||||
...omit(entry, ['account']),
|
});
|
||||||
});
|
|
||||||
saveOperations.push(oper);
|
saveOperations.push(oper);
|
||||||
});
|
});
|
||||||
await Promise.all(saveOperations);
|
await Promise.all(saveOperations);
|
||||||
@@ -368,7 +384,7 @@ export default class JournalPoster implements IJournalPoster {
|
|||||||
*/
|
*/
|
||||||
getClosingBalance(
|
getClosingBalance(
|
||||||
accountId: number,
|
accountId: number,
|
||||||
closingDate: Date|string,
|
closingDate: Date | string,
|
||||||
dateType: string = 'day'
|
dateType: string = 'day'
|
||||||
): number {
|
): number {
|
||||||
let closingBalance = 0;
|
let closingBalance = 0;
|
||||||
@@ -399,11 +415,16 @@ export default class JournalPoster implements IJournalPoster {
|
|||||||
* @param {String} dateType -
|
* @param {String} dateType -
|
||||||
* @return {Number}
|
* @return {Number}
|
||||||
*/
|
*/
|
||||||
getAccountBalance(accountId: number, closingDate: Date|string, dateType: string) {
|
getAccountBalance(
|
||||||
|
accountId: number,
|
||||||
|
closingDate: Date | string,
|
||||||
|
dateType: string
|
||||||
|
) {
|
||||||
const accountNode = this.accountsDepGraph.getNodeData(accountId);
|
const accountNode = this.accountsDepGraph.getNodeData(accountId);
|
||||||
const depAccountsIds = this.accountsDepGraph.dependenciesOf(accountId);
|
const depAccountsIds = this.accountsDepGraph.dependenciesOf(accountId);
|
||||||
const depAccounts = depAccountsIds
|
const depAccounts = depAccountsIds.map((id) =>
|
||||||
.map((id) => this.accountsDepGraph.getNodeData(id));
|
this.accountsDepGraph.getNodeData(id)
|
||||||
|
);
|
||||||
|
|
||||||
let balance: number = 0;
|
let balance: number = 0;
|
||||||
|
|
||||||
@@ -459,7 +480,11 @@ export default class JournalPoster implements IJournalPoster {
|
|||||||
* @return {Number}
|
* @return {Number}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getTrialBalanceWithDepands(accountId: number, closingDate: Date, dateType: string) {
|
getTrialBalanceWithDepands(
|
||||||
|
accountId: number,
|
||||||
|
closingDate: Date,
|
||||||
|
dateType: string
|
||||||
|
) {
|
||||||
const accountNode = this.accountsDepGraph.getNodeData(accountId);
|
const accountNode = this.accountsDepGraph.getNodeData(accountId);
|
||||||
const depAccountsIds = this.accountsDepGraph.dependenciesOf(accountId);
|
const depAccountsIds = this.accountsDepGraph.dependenciesOf(accountId);
|
||||||
const depAccounts = depAccountsIds.map((id) =>
|
const depAccounts = depAccountsIds.map((id) =>
|
||||||
@@ -485,8 +510,8 @@ export default class JournalPoster implements IJournalPoster {
|
|||||||
accountId: number,
|
accountId: number,
|
||||||
contactId: number,
|
contactId: number,
|
||||||
contactType: string,
|
contactType: string,
|
||||||
closingDate?: Date|string,
|
closingDate?: Date | string,
|
||||||
openingDate?: Date|string,
|
openingDate?: Date | string
|
||||||
) {
|
) {
|
||||||
const momentClosingDate = moment(closingDate);
|
const momentClosingDate = moment(closingDate);
|
||||||
const momentOpeningDate = moment(openingDate);
|
const momentOpeningDate = moment(openingDate);
|
||||||
@@ -534,7 +559,7 @@ export default class JournalPoster implements IJournalPoster {
|
|||||||
contactId: number,
|
contactId: number,
|
||||||
contactType: string,
|
contactType: string,
|
||||||
closingDate: Date,
|
closingDate: Date,
|
||||||
openingDate: Date,
|
openingDate: Date
|
||||||
) {
|
) {
|
||||||
const momentClosingDate = moment(closingDate);
|
const momentClosingDate = moment(closingDate);
|
||||||
let balance = 0;
|
let balance = 0;
|
||||||
|
|||||||
@@ -197,7 +197,7 @@ export default class ManualJournalsService implements IManualJournalsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Validate accounts with contact type.
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
* @param {IManualJournalDTO} manualJournalDTO
|
* @param {IManualJournalDTO} manualJournalDTO
|
||||||
* @param {string} accountBySlug
|
* @param {string} accountBySlug
|
||||||
|
|||||||
Reference in New Issue
Block a user