Files
bigcapital/packages/server/src/modules/BankingTransactions/queries/GetBankAccountTransactions/GetBankAccountTransactions.ts
2025-06-15 15:22:19 +02:00

207 lines
6.6 KiB
TypeScript

// @ts-nocheck
import * as R from 'ramda';
import * as moment from 'moment';
import { first, isEmpty } from 'lodash';
import {
ICashflowAccountTransaction,
ICashflowAccountTransactionsQuery,
} from '../../types/BankingTransactions.types';
import { BankTransactionStatus } from './_constants';
import { FinancialSheet } from '@/modules/FinancialStatements/common/FinancialSheet';
import { formatBankTransactionsStatus } from './_utils';
import { GetBankAccountTransactionsRepository } from './GetBankAccountTransactionsRepo.service';
import { runningBalance } from '@/utils/running-balance';
import { I18nService } from 'nestjs-i18n';
export class GetBankAccountTransactions extends FinancialSheet {
private runningBalance: any;
private query: ICashflowAccountTransactionsQuery;
private repo: GetBankAccountTransactionsRepository;
private i18n: I18nService;
/**
* Constructor method.
* @param {IAccountTransaction[]} transactions -
* @param {number} openingBalance -
* @param {ICashflowAccountTransactionsQuery} query -
*/
constructor(
repo: GetBankAccountTransactionsRepository,
query: ICashflowAccountTransactionsQuery,
i18n: I18nService,
) {
super();
this.repo = repo;
this.query = query;
this.i18n = i18n;
this.runningBalance = runningBalance(this.repo.openingBalance);
}
/**
* Retrieves the transaction status.
* @param {} transaction
* @returns {BankTransactionStatus}
*/
private getTransactionStatus(transaction: any): BankTransactionStatus {
const categorizedTrans = this.repo.uncategorizedTransactionsMapByRef.get(
`${transaction.referenceType}-${transaction.referenceId}`,
);
const matchedTrans = this.repo.matchedBankTransactionsMapByRef.get(
`${transaction.referenceType}-${transaction.referenceId}`,
);
if (!isEmpty(categorizedTrans)) {
return BankTransactionStatus.Categorized;
} else if (!isEmpty(matchedTrans)) {
return BankTransactionStatus.Matched;
} else {
return BankTransactionStatus.Manual;
}
}
/**
* Retrieves the uncategoized transaction id from the given transaction.
* @param transaction
* @returns {number|null}
*/
private getUncategorizedTransId(transaction: any): number {
// The given transaction would be categorized, matched or not, so we'd take a look at
// the categorized transaction first to get the id if not exist, then should look at the matched
// transaction if not exist too, so the given transaction has no uncategorized transaction id.
const categorizedTrans = this.repo.uncategorizedTransactionsMapByRef.get(
`${transaction.referenceType}-${transaction.referenceId}`,
);
const matchedTrans = this.repo.matchedBankTransactionsMapByRef.get(
`${transaction.referenceType}-${transaction.referenceId}`,
);
// Relation between the transaction and matching always been one-to-one.
const firstCategorizedTrans = first(categorizedTrans);
const firstMatchedTrans = first(matchedTrans);
return (
firstCategorizedTrans?.id ||
firstMatchedTrans?.uncategorizedTransactionId ||
null
);
}
/**
*Transformes the account transaction to to cashflow transaction node.
* @param {IAccountTransaction} transaction
* @returns {ICashflowAccountTransaction}
*/
private transactionNode = (transaction: any): ICashflowAccountTransaction => {
const status = this.getTransactionStatus(transaction);
const uncategorizedTransactionId =
this.getUncategorizedTransId(transaction);
return {
date: transaction.date,
formattedDate: moment(transaction.date).format('YYYY-MM-DD'),
withdrawal: transaction.credit,
deposit: transaction.debit,
formattedDeposit: this.formatNumber(transaction.debit),
formattedWithdrawal: this.formatNumber(transaction.credit),
referenceId: transaction.referenceId,
referenceType: transaction.referenceType,
formattedTransactionType: this.i18n.t(transaction.referenceTypeFormatted),
transactionNumber: transaction.transactionNumber,
referenceNumber: transaction.referenceNumber,
runningBalance: this.runningBalance.amount(),
formattedRunningBalance: this.formatNumber(this.runningBalance.amount()),
balance: 0,
formattedBalance: '',
status,
formattedStatus: formatBankTransactionsStatus(status),
uncategorizedTransactionId,
};
};
/**
* Associate cashflow transaction node with running balance attribute.
* @param {IAccountTransaction} transaction
* @returns {ICashflowAccountTransaction}
*/
private transactionRunningBalance = (
transaction: ICashflowAccountTransaction,
): ICashflowAccountTransaction => {
const amount = transaction.deposit - transaction.withdrawal;
const biggerThanZero = R.lt(0, amount);
const lowerThanZero = R.gt(0, amount);
const absAmount = Math.abs(amount);
R.when(R.always(biggerThanZero), this.runningBalance.decrement)(absAmount);
R.when(R.always(lowerThanZero), this.runningBalance.increment)(absAmount);
const runningBalance = this.runningBalance.amount();
return {
...transaction,
runningBalance,
formattedRunningBalance: this.formatNumber(runningBalance),
};
};
/**
* Associate to balance attribute to cashflow transaction node.
* @param {ICashflowAccountTransaction} transaction
* @returns {ICashflowAccountTransaction}
*/
private transactionBalance = (
transaction: ICashflowAccountTransaction,
): ICashflowAccountTransaction => {
const balance =
transaction.runningBalance +
transaction.withdrawal * -1 +
transaction.deposit;
return {
...transaction,
balance,
formattedBalance: this.formatNumber(balance),
};
};
/**
* Transformes the given account transaction to cashflow report transaction.
* @param {ICashflowAccountTransaction} transaction
* @returns {ICashflowAccountTransaction}
*/
private transactionTransformer = (
transaction,
): ICashflowAccountTransaction => {
return R.compose(
this.transactionBalance,
this.transactionRunningBalance,
this.transactionNode,
)(transaction);
};
/**
* Retrieve the report transactions node.
* @param {} transactions
* @returns {ICashflowAccountTransaction[]}
*/
private transactionsNode = (transactions): ICashflowAccountTransaction[] => {
return R.map(this.transactionTransformer)(transactions);
};
/**
* Retrieve the reprot data node.
* @returns {ICashflowAccountTransaction[]}
*/
public reportData(): ICashflowAccountTransaction[] {
return this.transactionsNode(this.repo.transactions);
}
}