refactor(nestjs): banking module

This commit is contained in:
Ahmed Bouhuolia
2025-06-02 15:41:41 +02:00
parent deadd5ac80
commit 7247b52fe5
22 changed files with 573 additions and 106 deletions

View File

@@ -0,0 +1,26 @@
import { Controller, Get, Param, Query } from '@nestjs/common';
import { BankingTransactionsApplication } from '../BankingTransactions/BankingTransactionsApplication.service';
import { ApiTags } from '@nestjs/swagger';
import { RecognizedTransactionsApplication } from './RecognizedTransactions.application';
@Controller('banking/recognized')
@ApiTags('banking-recognized')
export class BankingRecognizedTransactionsController {
constructor(
private readonly recognizedTransactionsApplication: RecognizedTransactionsApplication,
) {}
@Get(':recognizedTransactionId')
async getRecognizedTransaction(
@Param('recognizedTransactionId') recognizedTransactionId: number,
) {
return this.recognizedTransactionsApplication.getRecognizedTransaction(
Number(recognizedTransactionId),
);
}
@Get()
async getRecognizedTransactions(@Query() query: any) {
return this.recognizedTransactionsApplication.getRecognizedTransactions(query);
}
}

View File

@@ -7,6 +7,10 @@ import { RecognizeTranasctionsService } from './commands/RecognizeTranasctions.s
import { TriggerRecognizedTransactionsSubscriber } from './events/TriggerRecognizedTransactions';
import { BankingTransactionsModule } from '../BankingTransactions/BankingTransactions.module';
import { BankRulesModule } from '../BankRules/BankRules.module';
import { BankingRecognizedTransactionsController } from './BankingRecognizedTransactions.controller';
import { RecognizedTransactionsApplication } from './RecognizedTransactions.application';
import { GetRecognizedTransactionsService } from './GetRecongizedTransactions';
import { GetRecognizedTransactionService } from './queries/GetRecognizedTransaction.service';
const models = [RegisterTenancyModel(RecognizedBankTransaction)];
@@ -17,10 +21,13 @@ const models = [RegisterTenancyModel(RecognizedBankTransaction)];
...models,
],
providers: [
RecognizedTransactionsApplication,
GetRecognizedTransactionsService,
GetAutofillCategorizeTransactionService,
RevertRecognizedTransactionsService,
RecognizeTranasctionsService,
TriggerRecognizedTransactionsSubscriber,
GetRecognizedTransactionService,
],
exports: [
...models,
@@ -28,5 +35,6 @@ const models = [RegisterTenancyModel(RecognizedBankTransaction)];
RevertRecognizedTransactionsService,
RecognizeTranasctionsService,
],
controllers: [BankingRecognizedTransactionsController],
})
export class BankingTransactionsRegonizeModule {}

View File

@@ -0,0 +1,68 @@
import { Inject, Injectable } from '@nestjs/common';
import { GetRecognizedTransactionTransformer } from './queries/GetRecognizedTransactionTransformer';
import { UncategorizedBankTransaction } from '../BankingTransactions/models/UncategorizedBankTransaction';
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
import { IGetRecognizedTransactionsQuery } from '../BankingTransactions/types/BankingTransactions.types';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
@Injectable()
export class GetRecognizedTransactionsService {
constructor(
private readonly transformer: TransformerInjectable,
@Inject(UncategorizedBankTransaction.name)
private readonly uncategorizedBankTransactionModel: TenantModelProxy<typeof UncategorizedBankTransaction>,
) {}
/**
* Retrieves the recognized transactions of the given account.
* @param {number} tenantId
* @param {IGetRecognizedTransactionsQuery} filter -
*/
async getRecognizedTranactions(filter?: IGetRecognizedTransactionsQuery) {
const _query = {
page: 1,
pageSize: 20,
...filter,
};
const { results, pagination } =
await this.uncategorizedBankTransactionModel().query()
.onBuild((q) => {
q.withGraphFetched('recognizedTransaction.assignAccount');
q.withGraphFetched('recognizedTransaction.bankRule');
q.whereNotNull('recognizedTransactionId');
// Exclude the excluded transactions.
q.modify('notExcluded');
// Exclude the pending transactions.
q.modify('notPending');
if (_query.accountId) {
q.where('accountId', _query.accountId);
}
if (_query.minDate) {
q.modify('fromDate', _query.minDate);
}
if (_query.maxDate) {
q.modify('toDate', _query.maxDate);
}
if (_query.minAmount) {
q.modify('minAmount', _query.minAmount);
}
if (_query.maxAmount) {
q.modify('maxAmount', _query.maxAmount);
}
if (_query.accountId) {
q.where('accountId', _query.accountId);
}
})
.pagination(_query.page - 1, _query.pageSize);
const data = await this.transformer.transform(
results,
new GetRecognizedTransactionTransformer(),
);
return { data, pagination };
}
}

View File

@@ -0,0 +1,70 @@
import { Knex } from 'knex';
import { Injectable } from '@nestjs/common';
import { GetRecognizedTransactionsService } from './GetRecongizedTransactions';
import { GetRecognizedTransactionService } from './queries/GetRecognizedTransaction.service';
import { RevertRecognizedTransactionsService } from './commands/RevertRecognizedTransactions.service';
import { GetAutofillCategorizeTransactionService } from './queries/GetAutofillCategorizeTransaction.service';
import { IGetRecognizedTransactionsQuery } from '../BankingTransactions/types/BankingTransactions.types';
import { RevertRecognizedTransactionsCriteria } from './_types';
@Injectable()
export class RecognizedTransactionsApplication {
constructor(
private readonly getRecognizedTransactionsService: GetRecognizedTransactionsService,
private readonly getRecognizedTransactionService: GetRecognizedTransactionService,
private readonly revertRecognizedTransactionsService: RevertRecognizedTransactionsService,
private readonly getAutofillCategorizeTransactionService: GetAutofillCategorizeTransactionService,
) {}
/**
* Retrieves the recognized transactions based on the provided filter.
* @param {IGetRecognizedTransactionsQuery} filter - Filter criteria.
* @returns {Promise<{ data: any[], pagination: any }>}
*/
public getRecognizedTransactions(filter: IGetRecognizedTransactionsQuery) {
return this.getRecognizedTransactionsService.getRecognizedTranactions(
filter,
);
}
/**
* Retrieves a specific recognized transaction by ID.
* @param {number} recognizedTransactionId - The ID of the recognized transaction.
* @returns {Promise<any>}
*/
public getRecognizedTransaction(recognizedTransactionId: number) {
return this.getRecognizedTransactionService.getRecognizedTransaction(
recognizedTransactionId,
);
}
/**
* Reverts a recognized transaction.
* @param {number} ruleId - The ID of the recognized transaction to revert.
* @param {RevertRecognizedTransactionsCriteria} transactionsCriteria - The criteria for the recognized transaction to revert.
* @param {Knex.Transaction} trx - The transaction to use for the revert operation.
* @returns {Promise<any>}
*/
public revertRecognizedTransactions(
ruleId?: number | Array<number>,
transactionsCriteria?: RevertRecognizedTransactionsCriteria,
trx?: Knex.Transaction,
) {
return this.revertRecognizedTransactionsService.revertRecognizedTransactions(
ruleId,
transactionsCriteria,
trx,
);
}
/**
* Gets autofill categorize suggestions for a transaction.
* @param {number} uncategorizedTransactionId - The ID of the uncategorized transaction.
* @returns {Promise<any>}
*/
public getAutofillCategorizeTransaction(uncategorizedTransactionId: number) {
return this.getAutofillCategorizeTransactionService.getAutofillCategorizeTransaction(
uncategorizedTransactionId,
);
}
}

View File

@@ -0,0 +1,39 @@
import { Inject, Injectable } from '@nestjs/common';
import { GetRecognizedTransactionTransformer } from './GetRecognizedTransactionTransformer';
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
import { UncategorizedBankTransaction } from '../../BankingTransactions/models/UncategorizedBankTransaction';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
@Injectable()
export class GetRecognizedTransactionService {
constructor(
private readonly transformer: TransformerInjectable,
@Inject(UncategorizedBankTransaction.name)
private readonly uncategorizedBankTransactionModel: TenantModelProxy<
typeof UncategorizedBankTransaction
>,
) {}
/**
* Retrieves the recognized transaction of the given uncategorized transaction.
* @param {number} tenantId
* @param {number} uncategorizedTransactionId
*/
public async getRecognizedTransaction(uncategorizedTransactionId: number) {
const uncategorizedTransaction =
await this.uncategorizedBankTransactionModel()
.query()
.findById(uncategorizedTransactionId)
.withGraphFetched('matchedBankTransactions')
.withGraphFetched('recognizedTransaction.assignAccount')
.withGraphFetched('recognizedTransaction.bankRule')
.withGraphFetched('account')
.throwIfNotFound();
return this.transformer.transform(
uncategorizedTransaction,
new GetRecognizedTransactionTransformer(),
);
}
}

View File

@@ -0,0 +1,261 @@
import { Transformer } from "@/modules/Transformer/Transformer";
export class GetRecognizedTransactionTransformer extends Transformer {
/**
* Include these attributes to sale credit note object.
* @returns {Array}
*/
public includeAttributes = (): string[] => {
return [
'uncategorizedTransactionId',
'referenceNo',
'description',
'payee',
'amount',
'formattedAmount',
'date',
'formattedDate',
'assignedAccountId',
'assignedAccountName',
'assignedAccountCode',
'assignedPayee',
'assignedMemo',
'assignedCategory',
'assignedCategoryFormatted',
'withdrawal',
'deposit',
'isDepositTransaction',
'isWithdrawalTransaction',
'formattedDepositAmount',
'formattedWithdrawalAmount',
'bankRuleId',
'bankRuleName',
];
};
/**
* Exclude all attributes.
* @returns {Array<string>}
*/
public excludeAttributes = (): string[] => {
return ['*'];
};
/**
* Get the uncategorized transaction id.
* @param transaction
* @returns {number}
*/
public uncategorizedTransactionId = (transaction): number => {
return transaction.id;
}
/**
* Get the reference number of the transaction.
* @param {object} transaction
* @returns {string}
*/
public referenceNo(transaction: any): string {
return transaction.referenceNo;
}
/**
* Get the description of the transaction.
* @param {object} transaction
* @returns {string}
*/
public description(transaction: any): string {
return transaction.description;
}
/**
* Get the payee of the transaction.
* @param {object} transaction
* @returns {string}
*/
public payee(transaction: any): string {
return transaction.payee;
}
/**
* Get the amount of the transaction.
* @param {object} transaction
* @returns {number}
*/
public amount(transaction: any): number {
return transaction.amount;
}
/**
* Get the formatted amount of the transaction.
* @param {object} transaction
* @returns {string}
*/
public formattedAmount(transaction: any): string {
return this.formatNumber(transaction.formattedAmount, {
money: true,
});
}
/**
* Get the date of the transaction.
* @param {object} transaction
* @returns {string}
*/
public date(transaction: any): string {
return transaction.date;
}
/**
* Get the formatted date of the transaction.
* @param {object} transaction
* @returns {string}
*/
public formattedDate(transaction: any): string {
return this.formatDate(transaction.date);
}
/**
* Get the assigned account ID of the transaction.
* @param {object} transaction
* @returns {number}
*/
public assignedAccountId(transaction: any): number {
return transaction.recognizedTransaction.assignedAccountId;
}
/**
* Get the assigned account name of the transaction.
* @param {object} transaction
* @returns {string}
*/
public assignedAccountName(transaction: any): string {
return transaction.recognizedTransaction.assignAccount.name;
}
/**
* Get the assigned account code of the transaction.
* @param {object} transaction
* @returns {string}
*/
public assignedAccountCode(transaction: any): string {
return transaction.recognizedTransaction.assignAccount.code;
}
/**
* Get the assigned payee of the transaction.
* @param {object} transaction
* @returns {string}
*/
public getAssignedPayee(transaction: any): string {
return transaction.recognizedTransaction.assignedPayee;
}
/**
* Get the assigned memo of the transaction.
* @param {object} transaction
* @returns {string}
*/
public assignedMemo(transaction: any): string {
return transaction.recognizedTransaction.assignedMemo;
}
/**
* Get the assigned category of the transaction.
* @param {object} transaction
* @returns {string}
*/
public assignedCategory(transaction: any): string {
return transaction.recognizedTransaction.assignedCategory;
}
/**
*
* @returns {string}
*/
public assignedCategoryFormatted() {
return 'Other Income'
}
/**
* Check if the transaction is a withdrawal.
* @param {object} transaction
* @returns {boolean}
*/
public isWithdrawal(transaction: any): boolean {
return transaction.withdrawal;
}
/**
* Check if the transaction is a deposit.
* @param {object} transaction
* @returns {boolean}
*/
public isDeposit(transaction: any): boolean {
return transaction.deposit;
}
/**
* Check if the transaction is a deposit transaction.
* @param {object} transaction
* @returns {boolean}
*/
public isDepositTransaction(transaction: any): boolean {
return transaction.isDepositTransaction;
}
/**
* Check if the transaction is a withdrawal transaction.
* @param {object} transaction
* @returns {boolean}
*/
public isWithdrawalTransaction(transaction: any): boolean {
return transaction.isWithdrawalTransaction;
}
/**
* Get formatted deposit amount.
* @param {any} transaction
* @returns {string}
*/
protected formattedDepositAmount(transaction) {
if (transaction.isDepositTransaction) {
return this.formatNumber(transaction.deposit, {
currencyCode: transaction.currencyCode,
});
}
return '';
}
/**
* Get formatted withdrawal amount.
* @param transaction
* @returns {string}
*/
protected formattedWithdrawalAmount(transaction) {
if (transaction.isWithdrawalTransaction) {
return this.formatNumber(transaction.withdrawal, {
currencyCode: transaction.currencyCode,
});
}
return '';
}
/**
* Get the transaction bank rule id.
* @param transaction
* @returns {string}
*/
protected bankRuleId(transaction) {
return transaction.recognizedTransaction.bankRuleId;
}
/**
* Get the transaction bank rule name.
* @param transaction
* @returns {string}
*/
protected bankRuleName(transaction) {
return transaction.recognizedTransaction.bankRule.name;
}
}