feat: general ledger closing balance with accounts row

This commit is contained in:
Ahmed Bouhuolia
2024-06-06 18:42:07 +02:00
parent 5dbfd36415
commit 10fcf94c92
4 changed files with 99 additions and 31 deletions

View File

@@ -56,7 +56,7 @@ export interface IGeneralLedgerSheetAccount {
transactions: IGeneralLedgerSheetAccountTransaction[]; transactions: IGeneralLedgerSheetAccountTransaction[];
openingBalance: IGeneralLedgerSheetAccountBalance; openingBalance: IGeneralLedgerSheetAccountBalance;
closingBalance: IGeneralLedgerSheetAccountBalance; closingBalance: IGeneralLedgerSheetAccountBalance;
closingBalanceSubaccounts: IGeneralLedgerSheetAccountBalance; closingBalanceSubaccounts?: IGeneralLedgerSheetAccountBalance;
children?: IGeneralLedgerSheetAccount[]; children?: IGeneralLedgerSheetAccount[];
} }

View File

@@ -117,10 +117,12 @@ export default class GeneralLedgerSheet extends R.compose(
amount, amount,
runningBalance, runningBalance,
formattedAmount: this.formatNumber(amount), formattedAmount: this.formatNumber(amount, { excerptZero: false }),
formattedCredit: this.formatNumber(entry.credit), formattedCredit: this.formatNumber(entry.credit, { excerptZero: false }),
formattedDebit: this.formatNumber(entry.debit), formattedDebit: this.formatNumber(entry.debit, { excerptZero: false }),
formattedRunningBalance: this.formatNumber(runningBalance), formattedRunningBalance: this.formatNumber(runningBalance, {
excerptZero: false,
}),
currencyCode: this.baseCurrency, currencyCode: this.baseCurrency,
} as IGeneralLedgerSheetAccountTransaction; } as IGeneralLedgerSheetAccountTransaction;
@@ -141,16 +143,20 @@ export default class GeneralLedgerSheet extends R.compose(
return entries return entries
.reduce((prev: Array<[number, ILedgerEntry]>, current: ILedgerEntry) => { .reduce((prev: Array<[number, ILedgerEntry]>, current: ILedgerEntry) => {
const prevEntry = last(prev);
const prevRunningBalance = head(prevEntry) as number;
const amount = this.getEntryRunningBalance( const amount = this.getEntryRunningBalance(
current, current,
openingBalance, openingBalance,
head(last(prev)) as number prevRunningBalance
); );
return new Array([amount, current]); return [...prev, [amount, current]];
}, []) }, [])
.map(([runningBalance, entry]: [number, ILedgerEntry]) => .map((entryPair: [number, ILedgerEntry]) => {
this.entryMapper(entry, runningBalance) const [runningBalance, entry] = entryPair;
);
return this.entryMapper(entry, runningBalance);
});
} }
/** /**
@@ -224,8 +230,6 @@ export default class GeneralLedgerSheet extends R.compose(
const depsAccountsIds = const depsAccountsIds =
this.repository.accountsGraph.dependenciesOf(accountId); this.repository.accountsGraph.dependenciesOf(accountId);
console.log([...depsAccountsIds, accountId]);
const openingBalance = this.repository.openingBalanceTransactionsLedger const openingBalance = this.repository.openingBalanceTransactionsLedger
.whereAccountsIds([...depsAccountsIds, accountId]) .whereAccountsIds([...depsAccountsIds, accountId])
.getClosingBalance(); .getClosingBalance();
@@ -241,7 +245,7 @@ export default class GeneralLedgerSheet extends R.compose(
}; };
/** /**
* * Retrieves the closing balance with subaccounts total node.
* @param {number} accountId * @param {number} accountId
* @returns {IGeneralLedgerSheetAccountBalance} * @returns {IGeneralLedgerSheetAccountBalance}
*/ */
@@ -256,6 +260,31 @@ export default class GeneralLedgerSheet extends R.compose(
return { amount, formattedAmount, currencyCode, date }; return { amount, formattedAmount, currencyCode, date };
}; };
/**
* Detarmines whether the closing balance subaccounts node should be exist.
* @param {number} accountId
* @returns {boolean}
*/
private isAccountNodeIncludesClosingSubaccounts = (accountId: number) => {
// Retrun early if there is no accounts in the filter so
// return closing subaccounts in all cases.
if (isEmpty(this.query.accountsIds)) {
return true;
}
// Returns true if the given account id included in the filter.
const isIncluded = this.query.accountsIds.includes(accountId);
const parentAccountIds =
this.repository.accountsGraph.dependantsOf(accountId);
// Returns true if one of the parent account id exists in the filter.
const accountIdInChildren = R.any(
(parentAccountId) => R.includes(parentAccountId, this.query.accountsIds),
parentAccountIds
);
return isIncluded || accountIdInChildren;
};
/** /**
* Retreive general ledger accounts sections. * Retreive general ledger accounts sections.
* @param {IAccount} account * @param {IAccount} account
@@ -271,7 +300,12 @@ export default class GeneralLedgerSheet extends R.compose(
const closingBalanceSubaccounts = const closingBalanceSubaccounts =
this.accountClosingBalanceWithSubaccountsTotal(account.id); this.accountClosingBalanceWithSubaccountsTotal(account.id);
return { return R.compose(
R.when(
() => this.isAccountNodeIncludesClosingSubaccounts(account.id),
R.assoc('closingBalanceSubaccounts', closingBalanceSubaccounts)
)
)({
id: account.id, id: account.id,
name: account.name, name: account.name,
code: account.code, code: account.code,
@@ -280,8 +314,7 @@ export default class GeneralLedgerSheet extends R.compose(
openingBalance, openingBalance,
transactions, transactions,
closingBalance, closingBalance,
closingBalanceSubaccounts, });
};
}; };
/** /**
@@ -310,19 +343,42 @@ export default class GeneralLedgerSheet extends R.compose(
* @param {IGeneralLedgerSheetAccount[]} nodes * @param {IGeneralLedgerSheetAccount[]} nodes
* @returns {IGeneralLedgerSheetAccount[]} * @returns {IGeneralLedgerSheetAccount[]}
*/ */
private filterAccountNodes = ( private filterAccountNodesByTransactionsFilter = (
nodes: IGeneralLedgerSheetAccount[] nodes: IGeneralLedgerSheetAccount[]
): IGeneralLedgerSheetAccount[] => { ): IGeneralLedgerSheetAccount[] => {
return this.filterNodesDeep( return this.filterNodesDeep(
nodes, nodes,
(generalLedgerAccount: IGeneralLedgerSheetAccount) => (account: IGeneralLedgerSheetAccount) =>
!( !(account.transactions.length === 0 && this.query.noneTransactions)
generalLedgerAccount.transactions.length === 0 &&
this.query.noneTransactions
)
); );
}; };
/**
* Filters account nodes by the acounts filter.
* @param {IAccount[]} nodes
* @returns {IAccount[]}
*/
private filterAccountNodesByAccountsFilter = (
nodes: IAccount[]
): IAccount[] => {
return this.filterNodesDeep(nodes, (node: IGeneralLedgerSheetAccount) => {
if (R.isEmpty(this.query.accountsIds)) {
return true;
}
// Returns true if the given account id exists in the filter.
const isIncluded = this.query.accountsIds?.includes(node.id);
const parentAccountIds = this.repository.accountsGraph.dependantsOf(
node.id
);
// Returns true if one of th parent account ids exist in the filter.
const oneParentAccountIdExistInFilter = parentAccountIds.some((id) =>
this.query.accountsIds?.includes(id)
);
return isIncluded || oneParentAccountIdExistInFilter;
});
};
/** /**
* Retrieves mapped accounts with general ledger transactions and * Retrieves mapped accounts with general ledger transactions and
* opeing/closing balance. * opeing/closing balance.
@@ -331,8 +387,11 @@ export default class GeneralLedgerSheet extends R.compose(
*/ */
private accountsWalker(accounts: IAccount[]): IGeneralLedgerSheetAccount[] { private accountsWalker(accounts: IAccount[]): IGeneralLedgerSheetAccount[] {
return R.compose( return R.compose(
this.filterAccountNodes, R.defaultTo([]),
this.filterAccountNodesByTransactionsFilter,
this.accountNodesDeepMap, this.accountNodesDeepMap,
R.defaultTo([]),
this.filterAccountNodesByAccountsFilter,
this.nestedAccountsNode this.nestedAccountsNode
)(accounts); )(accounts);
} }
@@ -342,6 +401,7 @@ export default class GeneralLedgerSheet extends R.compose(
* @return {IGeneralLedgerSheetAccount[]} * @return {IGeneralLedgerSheetAccount[]}
*/ */
public reportData(): IGeneralLedgerSheetAccount[] { public reportData(): IGeneralLedgerSheetAccount[] {
console.log(this.repository.accounts);
return this.accountsWalker(this.repository.accounts); return this.accountsWalker(this.repository.accounts);
} }
} }

View File

@@ -71,7 +71,9 @@ export class GeneralLedgerRepository {
* Initialize the accounts. * Initialize the accounts.
*/ */
public async initAccounts() { public async initAccounts() {
this.accounts = await this.repositories.accountRepository.all(); this.accounts = await this.repositories.accountRepository
.all()
.orderBy('name', 'ASC');
} }
/** /**
@@ -94,11 +96,13 @@ export class GeneralLedgerRepository {
* Initialize the G/L transactions from/to the given date. * Initialize the G/L transactions from/to the given date.
*/ */
public async initTransactions() { public async initTransactions() {
this.transactions = await this.repositories.transactionsRepository.journal({ this.transactions = await this.repositories.transactionsRepository
fromDate: this.filter.fromDate, .journal({
toDate: this.filter.toDate, fromDate: this.filter.fromDate,
branchesIds: this.filter.branchesIds, toDate: this.filter.toDate,
}); branchesIds: this.filter.branchesIds,
})
.orderBy('date', 'ASC');
// Transform array transactions to journal collection. // Transform array transactions to journal collection.
this.transactionsLedger = Ledger.fromTransactions(this.transactions); this.transactionsLedger = Ledger.fromTransactions(this.transactions);
} }

View File

@@ -272,10 +272,14 @@ export class GeneralLedgerTable extends R.compose(
const closingBalanceWithSubaccounts = const closingBalanceWithSubaccounts =
this.closingBalanceWithSubaccountsMapper(account); this.closingBalanceWithSubaccountsMapper(account);
// Appends the closing balance with sub-accounts row if the account
// has children accounts and the node is define.
const isAppendClosingSubaccounts = () =>
account.children?.length > 0 && !!account.closingBalanceSubaccounts;
const children = R.compose( const children = R.compose(
// Appends the closing balance with sub-accounts row if the account has children accounts.
R.when( R.when(
() => account.children?.length > 0, isAppendClosingSubaccounts,
R.append(closingBalanceWithSubaccounts) R.append(closingBalanceWithSubaccounts)
), ),
R.concat(R.defaultTo([], transactions)), R.concat(R.defaultTo([], transactions)),