feat(nestjs): migrate to NestJS

This commit is contained in:
Ahmed Bouhuolia
2025-04-07 11:51:24 +02:00
parent f068218a16
commit 55fcc908ef
3779 changed files with 631 additions and 195332 deletions

View File

@@ -0,0 +1,138 @@
import { Knex } from 'knex';
import { Inject, Injectable } from '@nestjs/common';
import { castArray, isEmpty } from 'lodash';
import { PromisePool } from '@supercharge/promise-pool';
import { bankRulesMatchTransaction } from '../_utils';
import { RecognizeTransactionsCriteria } from '../_types';
import { BankRule } from '@/modules/BankRules/models/BankRule';
import { RecognizedBankTransaction } from '../models/RecognizedBankTransaction';
import { UncategorizedBankTransaction } from '@/modules/BankingTransactions/models/UncategorizedBankTransaction';
import { transformToMapBy } from '@/utils/transform-to-map-by';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
@Injectable()
export class RecognizeTranasctionsService {
constructor(
@Inject(UncategorizedBankTransaction.name)
private readonly uncategorizedCashflowTransactionModel: TenantModelProxy<
typeof UncategorizedBankTransaction
>,
@Inject(RecognizedBankTransaction.name)
private readonly recognizedBankTransactionModel: TenantModelProxy<
typeof RecognizedBankTransaction
>,
@Inject(BankRule.name)
private readonly bankRuleModel: TenantModelProxy<typeof BankRule>,
) {}
/**
* Marks the uncategorized transaction as recognized from the given bank rule.
* @param {BankRule} bankRule -
* @param {UncategorizedCashflowTransaction} transaction -
* @param {Knex.Transaction} trx -
*/
private async markBankRuleAsRecognized(
bankRule: BankRule,
transaction: UncategorizedBankTransaction,
trx?: Knex.Transaction,
) {
const recognizedTransaction = await this.recognizedBankTransactionModel()
.query(trx)
.insert({
bankRuleId: bankRule.id,
uncategorizedTransactionId: transaction.id,
assignedCategory: bankRule.assignCategory,
assignedAccountId: bankRule.assignAccountId,
assignedPayee: bankRule.assignPayee,
assignedMemo: bankRule.assignMemo,
});
await this.uncategorizedCashflowTransactionModel()
.query(trx)
.findById(transaction.id)
.patch({
recognizedTransactionId: recognizedTransaction.id,
});
}
/**
* Recognized the uncategorized transactions.
* @param {number|Array<number>} ruleId - The target rule id/ids.
* @param {RecognizeTransactionsCriteria}
* @param {Knex.Transaction} trx -
*/
public async recognizeTransactions(
ruleId?: number | Array<number>,
transactionsCriteria?: RecognizeTransactionsCriteria,
trx?: Knex.Transaction,
) {
const uncategorizedTranasctions =
await this.uncategorizedCashflowTransactionModel()
.query(trx)
.onBuild((query) => {
query.modify('notRecognized');
query.modify('notCategorized');
// Filter the transactions based on the given criteria.
if (transactionsCriteria?.batch) {
query.where('batch', transactionsCriteria.batch);
}
if (transactionsCriteria?.accountId) {
query.where('accountId', transactionsCriteria.accountId);
}
});
const bankRules = await this.bankRuleModel()
.query(trx)
.onBuild((q) => {
const rulesIds = !isEmpty(ruleId) ? castArray(ruleId) : [];
if (rulesIds?.length > 0) {
q.whereIn('id', rulesIds);
}
q.withGraphFetched('conditions');
});
const bankRulesByAccountId = transformToMapBy(
bankRules,
'applyIfAccountId',
);
// Try to recognize the transaction.
const regonizeTransaction = async (
transaction: UncategorizedBankTransaction,
) => {
const allAccountsBankRules = bankRulesByAccountId.get(`null`);
const accountBankRules = bankRulesByAccountId.get(
`${transaction.accountId}`,
);
const recognizedBankRule = bankRulesMatchTransaction(
transaction,
accountBankRules,
);
if (recognizedBankRule) {
await this.markBankRuleAsRecognized(
recognizedBankRule,
transaction,
trx,
);
}
};
const result = await PromisePool.withConcurrency(MIGRATION_CONCURRENCY)
.for(uncategorizedTranasctions)
.process((transaction: UncategorizedBankTransaction, index, pool) => {
return regonizeTransaction(transaction);
});
}
/**
*
* @param {number} uncategorizedTransaction
*/
public async regonizeTransaction(
uncategorizedTransaction: UncategorizedBankTransaction,
) {}
}
const MIGRATION_CONCURRENCY = 10;

View File

@@ -0,0 +1,77 @@
import { castArray } from 'lodash';
import { Knex } from 'knex';
import { Inject, Injectable } from '@nestjs/common';
import { RevertRecognizedTransactionsCriteria } from '../_types';
import { RecognizedBankTransaction } from '../models/RecognizedBankTransaction';
import { UncategorizedBankTransaction } from '@/modules/BankingTransactions/models/UncategorizedBankTransaction';
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
@Injectable()
export class RevertRecognizedTransactionsService {
constructor(
private readonly uow: UnitOfWork,
@Inject(RecognizedBankTransaction.name)
private readonly recognizedBankTransactionModel: TenantModelProxy<
typeof RecognizedBankTransaction
>,
@Inject(UncategorizedBankTransaction.name)
private readonly uncategorizedBankTransactionModel: TenantModelProxy<
typeof UncategorizedBankTransaction
>,
) {}
/**
* Revert and unlinks the recognized transactions based on the given bank rule
* and transactions criteria..
* @param {number|Array<number>} bankRuleId - Bank rule id.
* @param {RevertRecognizedTransactionsCriteria} transactionsCriteria -
* @param {Knex.Transaction} trx - Knex transaction.
* @returns {Promise<void>}
*/
public async revertRecognizedTransactions(
ruleId?: number | Array<number>,
transactionsCriteria?: RevertRecognizedTransactionsCriteria,
trx?: Knex.Transaction,
): Promise<void> {
const rulesIds = castArray(ruleId);
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
// Retrieves all the recognized transactions of the banbk rule.
const uncategorizedTransactions =
await this.uncategorizedBankTransactionModel()
.query(trx)
.onBuild((q) => {
q.withGraphJoined('recognizedTransaction');
q.whereNotNull('recognizedTransaction.id');
if (rulesIds.length > 0) {
q.whereIn('recognizedTransaction.bankRuleId', rulesIds);
}
if (transactionsCriteria?.accountId) {
q.where('accountId', transactionsCriteria.accountId);
}
if (transactionsCriteria?.batch) {
q.where('batch', transactionsCriteria.batch);
}
});
const uncategorizedTransactionIds = uncategorizedTransactions.map(
(r) => r.id,
);
// Unlink the recognized transactions out of un-categorized transactions.
await this.uncategorizedBankTransactionModel()
.query(trx)
.whereIn('id', uncategorizedTransactionIds)
.patch({
recognizedTransactionId: null,
});
// Delete the recognized bank transactions that associated to bank rule.
await this.recognizedBankTransactionModel()
.query(trx)
.whereIn('uncategorizedTransactionId', uncategorizedTransactionIds)
.delete();
}, trx);
}
}