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,60 @@
import { Injectable } from '@nestjs/common';
import { OnEvent } from '@nestjs/event-emitter';
import { BankTransactionAutoIncrement } from '../commands/BankTransactionAutoIncrement.service';
import { BankTransactionGLEntriesService } from '../commands/BankTransactionGLEntries';
import { events } from '@/common/events/events';
import { ICommandCashflowCreatedPayload, ICommandCashflowDeletedPayload } from '../types/BankingTransactions.types';
@Injectable()
export class BankingTransactionGLEntriesSubscriber {
/**
* @param {BankTransactionGLEntriesService} bankTransactionGLEntries - Bank transaction GL entries service.
* @param {BankTransactionAutoIncrement} cashflowTransactionAutoIncrement - Cashflow transaction auto increment service.
*/
constructor(
private readonly bankTransactionGLEntries: BankTransactionGLEntriesService,
private readonly cashflowTransactionAutoIncrement: BankTransactionAutoIncrement,
) {}
/**
* Writes the journal entries once the cashflow transaction create.
* @param {ICommandCashflowCreatedPayload} payload -
*/
@OnEvent(events.cashflow.onTransactionCreated)
public async writeJournalEntriesOnceTransactionCreated({
cashflowTransaction,
trx,
}: ICommandCashflowCreatedPayload) {
// Can't write GL entries if the transaction not published yet.
if (!cashflowTransaction.isPublished) return;
await this.bankTransactionGLEntries.writeJournalEntries(
cashflowTransaction.id,
trx,
);
}
/**
* Increment the cashflow transaction number once the transaction created.
* @param {ICommandCashflowCreatedPayload} payload -
*/
@OnEvent(events.cashflow.onTransactionCreated)
public async incrementTransactionNumberOnceTransactionCreated({}: ICommandCashflowCreatedPayload) {
this.cashflowTransactionAutoIncrement.incrementNextTransactionNumber();
}
/**
* Deletes the GL entries once the cashflow transaction deleted.
* @param {ICommandCashflowDeletedPayload} payload -
*/
@OnEvent(events.cashflow.onTransactionDeleted)
public async revertGLEntriesOnceTransactionDeleted({
cashflowTransactionId,
trx,
}: ICommandCashflowDeletedPayload) {
await this.bankTransactionGLEntries.revertJournalEntries(
cashflowTransactionId,
trx,
);
};
}

View File

@@ -0,0 +1,26 @@
import { Injectable } from '@nestjs/common';
import { events } from '@/common/events/events';
import { ValidateDeleteBankAccountTransactions } from '../commands/ValidateDeleteBankAccountTransactions.service';
import { OnEvent } from '@nestjs/event-emitter';
import { IAccountEventDeletePayload } from '@/interfaces/Account';
@Injectable()
export class CashflowWithAccountSubscriber {
constructor(
private readonly validateDeleteBankAccount: ValidateDeleteBankAccountTransactions,
) {}
/**
* Validate chart account has no associated cashflow transactions on delete.
* @param {IAccountEventDeletePayload} payload -
*/
@OnEvent(events.accounts.onDelete)
public async validateAccountHasNoCashflowTransactionsOnDelete({
oldAccount,
}: IAccountEventDeletePayload) {
await this.validateDeleteBankAccount.validateAccountHasNoCashflowEntries(
oldAccount.id
);
};
}

View File

@@ -0,0 +1,88 @@
import PromisePool from '@supercharge/promise-pool';
import {
ICashflowTransactionCategorizedPayload,
ICashflowTransactionUncategorizedPayload,
} from '../types/BankingTransactions.types';
import { OnEvent } from '@nestjs/event-emitter';
import { Inject, Injectable } from '@nestjs/common';
import { events } from '@/common/events/events';
import { Account } from '@/modules/Accounts/models/Account.model';
import { UncategorizedBankTransaction } from '../models/UncategorizedBankTransaction';
@Injectable()
export class DecrementUncategorizedTransactionOnCategorizeSubscriber {
constructor(
@Inject(Account.name)
private readonly accountModel: typeof Account,
) {}
/**
* Decrement the uncategoirzed transactions on the account once categorizing.
* @param {ICashflowTransactionCategorizedPayload}
*/
@OnEvent(events.cashflow.onTransactionCategorized)
public async decrementUnCategorizedTransactionsOnCategorized({
uncategorizedTransactions,
trx,
}: ICashflowTransactionCategorizedPayload) {
await PromisePool.withConcurrency(1)
.for(uncategorizedTransactions)
.process(
async (uncategorizedTransaction: UncategorizedBankTransaction) => {
// Cannot continue if the transaction is still pending.
if (uncategorizedTransaction.isPending) {
return;
}
await this.accountModel
.query(trx)
.findById(uncategorizedTransaction.accountId)
.decrement('uncategorizedTransactions', 1);
},
);
}
/**
* Increment the uncategorized transaction on the given account on uncategorizing.
* @param {IManualJournalDeletingPayload}
*/
@OnEvent(events.cashflow.onTransactionUncategorized)
public async incrementUnCategorizedTransactionsOnUncategorized({
uncategorizedTransactions,
trx,
}: ICashflowTransactionUncategorizedPayload) {
await PromisePool.withConcurrency(1)
.for(uncategorizedTransactions)
.process(
async (uncategorizedTransaction: UncategorizedBankTransaction) => {
// Cannot continue if the transaction is still pending.
if (uncategorizedTransaction.isPending) {
return;
}
await this.accountModel
.query(trx)
.findById(uncategorizedTransaction.accountId)
.increment('uncategorizedTransactions', 1);
},
);
}
/**
* Increments uncategorized transactions count once creating a new transaction.
* @param {ICommandCashflowCreatedPayload} payload -
*/
@OnEvent(events.cashflow.onTransactionUncategorizedCreated)
public async incrementUncategoirzedTransactionsOnCreated({
uncategorizedTransaction,
trx,
}: any) {
if (!uncategorizedTransaction.accountId) return;
// Cannot continue if the transaction is still pending.
if (uncategorizedTransaction.isPending) return;
await this.accountModel
.query(trx)
.findById(uncategorizedTransaction.accountId)
.increment('uncategorizedTransactions', 1);
}
}

View File

@@ -0,0 +1,35 @@
import { Inject, Injectable } from '@nestjs/common';
import { OnEvent } from '@nestjs/event-emitter';
import { PromisePool } from '@supercharge/promise-pool';
import { DeleteCashflowTransaction } from '../commands/DeleteCashflowTransaction.service';
import { events } from '@/common/events/events';
import { ICashflowTransactionUncategorizedPayload } from '@/modules/BankingCategorize/types/BankingCategorize.types';
@Injectable()
export class DeleteCashflowTransactionOnUncategorizeSubscriber {
constructor(
private readonly deleteCashflowTransactionService: DeleteCashflowTransaction,
) {}
/**
* Deletes the cashflow transaction once uncategorize the bank transaction.
* @param {ICashflowTransactionUncategorizedPayload} payload
*/
@OnEvent(events.cashflow.onTransactionUncategorized)
public async deleteCashflowTransactionOnUncategorize({
oldMainUncategorizedTransaction,
trx,
}: ICashflowTransactionUncategorizedPayload) {
// Cannot continue if the main transaction does not reference to cashflow type.
if (
oldMainUncategorizedTransaction.categorizeRefType !==
'CashflowTransaction'
) {
return;
}
await this.deleteCashflowTransactionService.deleteCashflowTransaction(
oldMainUncategorizedTransaction.categorizeRefId,
trx
);
}
}

View File

@@ -0,0 +1,42 @@
import { events } from '@/common/events/events';
import { ERRORS } from '../constants';
import { OnEvent } from '@nestjs/event-emitter';
import { ServiceError } from '@/modules/Items/ServiceError';
import { Inject, Injectable } from '@nestjs/common';
import { UncategorizedBankTransaction } from '../models/UncategorizedBankTransaction';
import { ICommandCashflowDeletingPayload } from '../types/BankingTransactions.types';
@Injectable()
export class PreventDeleteTransactionOnDeleteSubscriber {
constructor(
@Inject(UncategorizedBankTransaction.name)
private readonly uncategorizedBankTransactionModel: typeof UncategorizedBankTransaction,
) {}
/**
* Prevent delete cashflow transaction has converted from uncategorized transaction.
* @param {ICommandCashflowDeletingPayload} payload
*/
@OnEvent(events.cashflow.onTransactionDeleting)
public async preventDeleteCashflowTransactionHasUncategorizedTransaction({
oldCashflowTransaction,
trx,
}: ICommandCashflowDeletingPayload) {
if (oldCashflowTransaction.uncategorizedTransactionId) {
const foundTransactions = await this.uncategorizedBankTransactionModel
.query(trx)
.where({
categorized: true,
categorizeRefId: oldCashflowTransaction.id,
categorizeRefType: 'CashflowTransaction',
});
// Throw the error if the cashflow transaction still linked to uncategorized transaction.
if (foundTransactions.length > 0) {
throw new ServiceError(
ERRORS.CANNOT_DELETE_TRANSACTION_CONVERTED_FROM_UNCATEGORIZED,
'Cannot delete cashflow transaction converted from uncategorized transaction.',
);
}
}
}
}