refactor(nestjs): banking module

This commit is contained in:
Ahmed Bouhuolia
2025-06-02 21:32:53 +02:00
parent 7247b52fe5
commit 5595478e19
18 changed files with 216 additions and 103 deletions

View File

@@ -31,6 +31,7 @@ import { GetUncategorizedBankTransactionService } from './queries/GetUncategoriz
import { BankingUncategorizedTransactionsController } from './controllers/BankingUncategorizedTransactions.controller';
import { BankingPendingTransactionsController } from './controllers/BankingPendingTransactions.controller';
import { GetPendingBankAccountTransactions } from './queries/GetPendingBankAccountTransaction.service';
import { GetAutofillCategorizeTransactionService } from './queries/GetAutofillCategorizeTransaction/GetAutofillCategorizeTransaction.service';
const models = [
RegisterTenancyModel(UncategorizedBankTransaction),
@@ -72,7 +73,8 @@ const models = [
GetBankAccountTransactionsService,
GetUncategorizedTransactions,
GetUncategorizedBankTransactionService,
GetPendingBankAccountTransactions
GetPendingBankAccountTransactions,
GetAutofillCategorizeTransactionService,
],
exports: [...models, RemovePendingUncategorizedTransaction],
})

View File

@@ -14,6 +14,7 @@ import { GetUncategorizedBankTransactionService } from './queries/GetUncategoriz
import { GetUncategorizedTransactionsQueryDto } from './dtos/GetUncategorizedTransactionsQuery.dto';
import { GetPendingBankAccountTransactions } from './queries/GetPendingBankAccountTransaction.service';
import { GetPendingTransactionsQueryDto } from './dtos/GetPendingTransactionsQuery.dto';
import { GetAutofillCategorizeTransactionService } from './queries/GetAutofillCategorizeTransaction/GetAutofillCategorizeTransaction.service';
@Injectable()
export class BankingTransactionsApplication {
@@ -25,7 +26,8 @@ export class BankingTransactionsApplication {
private readonly getBankAccountTransactionsService: GetBankAccountTransactionsService,
private readonly getBankAccountUncategorizedTransitionsService: GetUncategorizedTransactions,
private readonly getBankAccountUncategorizedTransactionService: GetUncategorizedBankTransactionService,
private readonly getPendingBankAccountTransactionsService: GetPendingBankAccountTransactions
private readonly getPendingBankAccountTransactionsService: GetPendingBankAccountTransactions,
private readonly getAutofillCategorizeTransactionService: GetAutofillCategorizeTransactionService,
) {}
/**
@@ -106,7 +108,23 @@ export class BankingTransactionsApplication {
* Retrieves the pending bank account transactions.
* @param {GetPendingTransactionsQueryDto} filter - Pending transactions query.
*/
public getPendingBankAccountTransactions(filter?: GetPendingTransactionsQueryDto) {
return this.getPendingBankAccountTransactionsService.getPendingTransactions(filter);
public getPendingBankAccountTransactions(
filter?: GetPendingTransactionsQueryDto,
) {
return this.getPendingBankAccountTransactionsService.getPendingTransactions(
filter,
);
}
/**
* Retrieves the autofill values of categorize transactions form.
* @param {Array<number> | number} uncategorizeTransactionsId - Uncategorized transactions ids.
*/
public getAutofillCategorizeTransaction(
uncategorizeTransactionsId: Array<number> | number,
) {
return this.getAutofillCategorizeTransactionService.getAutofillCategorizeTransaction(
uncategorizeTransactionsId,
);
}
}

View File

@@ -1,5 +1,11 @@
import { Controller, Get, Param, Query } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiQuery } from '@nestjs/swagger';
import {
ApiTags,
ApiOperation,
ApiResponse,
ApiParam,
ApiQuery,
} from '@nestjs/swagger';
import { GetUncategorizedTransactionsQueryDto } from '../dtos/GetUncategorizedTransactionsQuery.dto';
import { BankingTransactionsApplication } from '../BankingTransactionsApplication.service';
@@ -10,11 +16,41 @@ export class BankingUncategorizedTransactionsController {
private readonly bankingTransactionsApplication: BankingTransactionsApplication,
) {}
@Get('accounts/:accountId')
@ApiOperation({ summary: 'Get uncategorized transactions for a specific bank account' })
@Get('autofill')
@ApiOperation({ summary: 'Get autofill values for categorize transactions' })
@ApiResponse({
status: 200,
description: 'Returns a list of uncategorized transactions for the specified bank account',
description: 'Returns autofill values for categorize transactions',
})
@ApiParam({
name: 'accountId',
required: true,
type: Number,
description: 'Bank account ID',
})
@ApiQuery({
name: 'uncategorizeTransactionsId',
required: true,
type: Number,
description: 'Uncategorize transactions ID',
})
async getAutofillCategorizeTransaction(
@Query('uncategorizedTransactionIds') uncategorizedTransactionIds: Array<number> | number,
) {
console.log(uncategorizedTransactionIds)
return this.bankingTransactionsApplication.getAutofillCategorizeTransaction(
uncategorizedTransactionIds,
);
}
@Get('accounts/:accountId')
@ApiOperation({
summary: 'Get uncategorized transactions for a specific bank account',
})
@ApiResponse({
status: 200,
description:
'Returns a list of uncategorized transactions for the specified bank account',
})
@ApiParam({
name: 'accountId',

View File

@@ -0,0 +1,46 @@
import { Inject, Injectable } from '@nestjs/common';
import { castArray, first, uniq } from 'lodash';
import { GetAutofillCategorizeTransctionTransformer } from './GetAutofillCategorizeTransactionTransformer';
import { UncategorizedBankTransaction } from '@/modules/BankingTransactions/models/UncategorizedBankTransaction';
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
@Injectable()
export class GetAutofillCategorizeTransactionService {
constructor(
private readonly transformer: TransformerInjectable,
@Inject(UncategorizedBankTransaction.name)
private readonly uncategorizedBankTransactionModel: TenantModelProxy<
typeof UncategorizedBankTransaction
>,
) {}
/**
* Retrieves the autofill values of categorize transactions form.
* @param {Array<number> | number} uncategorizeTransactionsId - Uncategorized transactions ids.
*/
public async getAutofillCategorizeTransaction(
uncategorizeTransactionsId: Array<number> | number,
) {
const uncategorizeTransactionsIds = uniq(
castArray(uncategorizeTransactionsId),
);
const uncategorizedTransactions =
await this.uncategorizedBankTransactionModel()
.query()
.whereIn('id', uncategorizeTransactionsIds)
.withGraphFetched('recognizedTransaction.assignAccount')
.withGraphFetched('recognizedTransaction.bankRule')
.throwIfNotFound();
return this.transformer.transform(
{},
new GetAutofillCategorizeTransctionTransformer(),
{
uncategorizedTransactions,
firstUncategorizedTransaction: first(uncategorizedTransactions),
},
);
}
}

View File

@@ -0,0 +1,176 @@
import { sumBy } from 'lodash';
import { Transformer } from '@/modules/Transformer/Transformer';
export class GetAutofillCategorizeTransctionTransformer extends Transformer {
/**
* Included attributes to the object.
* @returns {Array}
*/
public includeAttributes = (): string[] => {
return [
'amount',
'formattedAmount',
'isRecognized',
'date',
'formattedDate',
'creditAccountId',
'debitAccountId',
'referenceNo',
'transactionType',
'recognizedByRuleId',
'recognizedByRuleName',
'isWithdrawalTransaction',
'isDepositTransaction',
];
};
/**
* Detarmines whether the transaction is recognized.
* @returns {boolean}
*/
public isRecognized() {
return !!this.options.firstUncategorizedTransaction?.recognizedTransaction;
}
/**
* Retrieves the total amount of uncategorized transactions.
* @returns {number}
*/
public amount() {
return sumBy(this.options.uncategorizedTransactions, 'amount');
}
/**
* Retrieves the formatted total amount of uncategorized transactions.
* @returns {string}
*/
public formattedAmount() {
return this.formatNumber(this.amount(), {
currencyCode: 'USD',
money: true,
});
}
/**
* Detarmines whether the transaction is deposit.
* @returns {boolean}
*/
public isDepositTransaction() {
const amount = this.amount();
return amount > 0;
}
/**
* Detarmines whether the transaction is withdrawal.
* @returns {boolean}
*/
public isWithdrawalTransaction() {
const amount = this.amount();
return amount < 0;
}
/**
*
* @param {string}
*/
public date() {
return this.options.firstUncategorizedTransaction?.date || null;
}
/**
* Retrieves the formatted date of uncategorized transaction.
* @returns {string}
*/
public formattedDate() {
return this.formatDate(this.date());
}
/**
*
* @param {string}
*/
public referenceNo() {
return this.options.firstUncategorizedTransaction?.referenceNo || null;
}
/**
*
* @returns {number}
*/
public creditAccountId() {
return (
this.options.firstUncategorizedTransaction?.recognizedTransaction
?.assignedAccountId || null
);
}
/**
*
* @returns {number}
*/
public debitAccountId() {
return this.options.firstUncategorizedTransaction?.accountId || null;
}
/**
* Retrieves the assigned category of recognized transaction, if is not recognized
* returns the default transaction type depends on the transaction normal.
* @returns {string}
*/
public transactionType() {
const assignedCategory =
this.options.firstUncategorizedTransaction?.recognizedTransaction
?.assignedCategory;
return (
assignedCategory ||
(this.isDepositTransaction() ? 'other_income' : 'other_expense')
);
}
/**
*
* @returns {string}
*/
public payee() {
return (
this.options.firstUncategorizedTransaction?.recognizedTransaction
?.assignedPayee || null
);
}
/**
*
* @returns {string}
*/
public memo() {
return (
this.options.firstUncategorizedTransaction?.recognizedTransaction
?.assignedMemo || null
);
}
/**
* Retrieves the rule id the transaction recongized by.
* @returns {string}
*/
public recognizedByRuleId() {
return (
this.options.firstUncategorizedTransaction?.recognizedTransaction
?.bankRuleId || null
);
}
/**
* Retrieves the rule name the transaction recongized by.
* @returns {string}
*/
public recognizedByRuleName() {
return (
this.options.firstUncategorizedTransaction?.recognizedTransaction
?.bankRule?.name || null
);
}
}