refactor: banking modules to nestjs

This commit is contained in:
Ahmed Bouhuolia
2025-01-06 11:45:58 +02:00
parent ba176394c8
commit 2bf58d9cb4
22 changed files with 172 additions and 60 deletions

View File

@@ -59,6 +59,7 @@ import { BankAccountsModule } from '../BankingAccounts/BankAccounts.module';
import { BankingTransactionsExcludeModule } from '../BankingTransactionsExclude/BankingTransactionsExclude.module';
import { BankingTransactionsRegonizeModule } from '../BankingTranasctionsRegonize/BankingTransactionsRegonize.module';
import { BankingMatchingModule } from '../BankingMatching/BankingMatching.module';
import { BankingTransactionsModule } from '../BankingTransactions/BankingTransactions.module';
@Module({
imports: [
@@ -141,7 +142,8 @@ import { BankingMatchingModule } from '../BankingMatching/BankingMatching.module
BankRulesModule,
BankingTransactionsExcludeModule,
BankingTransactionsRegonizeModule,
// BankingMatchingModule
BankingMatchingModule,
BankingTransactionsModule,
],
controllers: [AppController],
providers: [

View File

@@ -23,7 +23,7 @@ export class EditBankRuleService {
* @param createDTO
* @returns
*/
private transformDTO(createDTO: IEditBankRuleDTO): Partial<BankRule> {
private transformDTO(createDTO: IEditBankRuleDTO) {
return {
...createDTO,
};
@@ -59,7 +59,6 @@ export class EditBankRuleService {
...tranformDTO,
id: ruleId,
});
// Triggers `onBankRuleEdited` event.
await this.eventPublisher.emitAsync(events.bankRules.onEdited, {
oldBankRule,

View File

@@ -1,10 +1,11 @@
import { BaseModel } from '@/models/Model';
import { BankRuleComparator } from '../types';
export class BankRuleCondition extends BaseModel {
public id!: number;
public bankRuleId!: number;
public field!: string;
public comparator!: string;
public comparator!: BankRuleComparator;
public value!: string;
/**

View File

@@ -11,7 +11,7 @@ export enum BankRuleConditionComparator {
Contains = 'contains',
Equals = 'equals',
Equal = 'equal',
NotContain = 'not_contain',
NotContain = 'not_contains',
Bigger = 'bigger',
BiggerOrEqual = 'bigger_or_equal',
Smaller = 'smaller',
@@ -59,19 +59,21 @@ export enum BankRuleAssignCategory {
OwnerDrawings = 'OwnerDrawings',
}
export type BankRuleComparator =
| 'contains'
| 'equals'
| 'not_contains'
| 'equal'
| 'bigger'
| 'bigger_or_equal'
| 'smaller'
| 'smaller_or_equal';
export interface IBankRuleConditionDTO {
id?: number;
field: string;
comparator:
| 'contains'
| 'equals'
| 'not_contains'
| 'equal'
| 'bigger'
| 'bigger_or_equal'
| 'smaller'
| 'smaller_or_equal';
value: number;
comparator: BankRuleComparator;
value: string;
}
export interface IBankRuleCommonDTO {

View File

@@ -1,6 +1,7 @@
import { Inject, Injectable } from '@nestjs/common';
import { Account } from '@/modules/Accounts/models/Account.model';
import { UncategorizedBankTransaction } from '@/modules/BankingTransactions/models/UncategorizedBankTransaction';
import { BaseModel } from '@/models/Model';
@Injectable()
export class GetBankAccountSummary {
@@ -34,6 +35,10 @@ export class GetBankAccountSummary {
q.modify('notCategorized');
};
interface UncategorizedTransactionsCount {
total: number;
}
// Retrieves the uncategorized transactions count of the given bank account.
const uncategorizedTranasctionsCount =
await this.uncategorizedBankTransactionModel.query().onBuild((q) => {
@@ -79,6 +84,7 @@ export class GetBankAccountSummary {
q.count('uncategorized_cashflow_transactions.id as total');
q.first();
});
// Retrieves the pending transactions count.
const pendingTransactionsCount =
await this.uncategorizedBankTransactionModel.query().onBuild((q) => {
@@ -91,9 +97,13 @@ export class GetBankAccountSummary {
});
const totalUncategorizedTransactions =
// @ts-ignore
uncategorizedTranasctionsCount?.total || 0;
// @ts-ignore
const totalRecognizedTransactions = recognizedTransactionsCount?.total || 0;
// @ts-ignore
const totalExcludedTransactions = excludedTransactionsCount?.total || 0;
// @ts-ignore
const totalPendingTransactions = pendingTransactionsCount?.total || 0;
return {

View File

@@ -1,7 +1,3 @@
import {
CategorizeTransactionAsExpenseDTO,
ICashflowTransactionCategorizedPayload,
} from '@/interfaces';
import { Knex } from 'knex';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { BankTransaction } from '@/modules/BankingTransactions/models/BankTransaction';
@@ -10,7 +6,10 @@ import { Inject } from '@nestjs/common';
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
import { Injectable } from '@nestjs/common';
import { events } from '@/common/events/events';
import { ICashflowTransactionUncategorizedPayload } from '../types/BankingCategorize.types';
import {
ICashflowTransactionCategorizedPayload,
ICategorizeCashflowTransactioDTO,
} from '../types/BankingCategorize.types';
@Injectable()
export class CategorizeTransactionAsExpense {
@@ -30,7 +29,7 @@ export class CategorizeTransactionAsExpense {
*/
public async categorize(
cashflowTransactionId: number,
transactionDTO: CategorizeTransactionAsExpenseDTO,
transactionDTO: ICategorizeCashflowTransactioDTO,
) {
const transaction = await this.bankTransactionModel
.query()
@@ -46,7 +45,12 @@ export class CategorizeTransactionAsExpense {
} as ICashflowTransactionCategorizedPayload,
);
// Creates a new expense transaction.
const expenseTransaction = await this.createExpenseService.newExpense({});
// TODO: the DTO is not complete, we need to add the missing properties.
// @ts-ignore
const expenseTransaction = await this.createExpenseService.newExpense({
// ...transactionDTO,
// publishedAt: transaction.publishedAt,
});
// Updates the item on the storage and fetches the updated once.
const cashflowTransaction = await this.bankTransactionModel
@@ -62,7 +66,7 @@ export class CategorizeTransactionAsExpense {
{
cashflowTransaction,
trx,
} as ICashflowTransactionUncategorizedPayload,
},
);
});
}

View File

@@ -1,9 +1,10 @@
import { BankTransaction } from "@/modules/BankingTransactions/models/BankTransaction";
import { UncategorizedBankTransaction } from "@/modules/BankingTransactions/models/UncategorizedBankTransaction";
import { Knex } from "knex";
export interface ICashflowTransactionCategorizedPayload {
uncategorizedTransactions: Array<UncategorizedBankTransaction>;
cashflowTransaction: UncategorizedBankTransaction;
cashflowTransaction: BankTransaction;
oldUncategorizedTransactions: Array<UncategorizedBankTransaction>;
categorizeDTO: any;
trx: Knex.Transaction;

View File

@@ -0,0 +1,41 @@
import { Body, Controller, Get, Param, Post, Query } from '@nestjs/common';
import { BankingMatchingApplication } from './BankingMatchingApplication';
import { GetMatchedTransactionsFilter, IMatchTransactionDTO } from './types';
@Controller('banking/matching')
export class BankingMatchingController {
constructor(
private readonly bankingMatchingApplication: BankingMatchingApplication
) {}
@Get('matched/transactions')
async getMatchedTransactions(
@Query('uncategorizedTransactionIds') uncategorizedTransactionIds: number[],
@Query() filter: GetMatchedTransactionsFilter
) {
return this.bankingMatchingApplication.getMatchedTransactions(
uncategorizedTransactionIds,
filter
);
}
@Post('/match/:uncategorizedTransactionId')
async matchTransaction(
@Param('uncategorizedTransactionId') uncategorizedTransactionId: number | number[],
@Body() matchedTransactions: IMatchTransactionDTO[]
) {
return this.bankingMatchingApplication.matchTransaction(
uncategorizedTransactionId,
matchedTransactions
);
}
@Post('/unmatch/:uncategorizedTransactionId')
async unmatchMatchedTransaction(
@Param('uncategorizedTransactionId') uncategorizedTransactionId: number
) {
return this.bankingMatchingApplication.unmatchMatchedTransaction(
uncategorizedTransactionId
);
}
}

View File

@@ -19,10 +19,14 @@ import { BankingTransactionsModule } from '../BankingTransactions/BankingTransac
import { PaymentsReceivedModule } from '../PaymentReceived/PaymentsReceived.module';
import { MatchBankTransactions } from './commands/MatchTransactions';
import { MatchTransactionsTypes } from './commands/MatchTransactionsTypes';
import { GetMatchedTransactionsByManualJournals } from './queries/GetMatchedTransactionsByManualJournals.service';
import { ValidateTransactionMatched } from './commands/ValidateTransactionsMatched.service';
import { BankingMatchingController } from './BankingMatching.controller';
const models = [RegisterTenancyModel(MatchedBankTransaction)];
@Module({
controllers: [BankingMatchingController],
imports: [
BillPaymentsModule,
BankingTransactionsModule,
@@ -30,12 +34,14 @@ const models = [RegisterTenancyModel(MatchedBankTransaction)];
],
providers: [
...models,
ValidateTransactionMatched,
MatchBankTransactions,
MatchTransactionsTypes,
GetMatchedTransactionsByBills,
GetMatchedTransactionsByCashflow,
GetMatchedTransactionsByExpenses,
GetMatchedTransactionsByInvoices,
GetMatchedTransactionsByManualJournals,
BankingMatchingApplication,
GetMatchedTransactions,
UnmatchMatchedBankTransaction,

View File

@@ -30,18 +30,15 @@ export class BankingMatchingApplication {
/**
* Matches the given uncategorized transaction with the given system transaction.
* @param {number} tenantId
* @param {number} uncategorizedTransactionId
* @param {IMatchTransactionDTO} matchTransactionsDTO
* @returns {Promise<void>}
*/
public matchTransaction(
tenantId: number,
uncategorizedTransactionId: number | Array<number>,
matchedTransactions: Array<IMatchTransactionDTO>
): Promise<void> {
return this.matchTransactionService.matchTransaction(
tenantId,
uncategorizedTransactionId,
matchedTransactions
);
@@ -49,16 +46,13 @@ export class BankingMatchingApplication {
/**
* Unmatch the given matched transaction.
* @param {number} tenantId
* @param {number} uncategorizedTransactionId
* @param {number} uncategorizedTransactionId - Uncategorized transaction id.
* @returns {Promise<void>}
*/
public unmatchMatchedTransaction(
tenantId: number,
uncategorizedTransactionId: number
) {
return this.unmatchMatchedTransactionService.unmatchMatchedTransaction(
tenantId,
uncategorizedTransactionId
);
}

View File

@@ -108,7 +108,6 @@ export class MatchBankTransactions {
* @returns {Promise<void>}
*/
public async matchTransaction(
tenantId: number,
uncategorizedTransactionId: number | Array<number>,
matchedTransactions: Array<IMatchTransactionDTO>,
): Promise<void> {

View File

@@ -21,12 +21,10 @@ export class UnmatchMatchedBankTransaction {
* @returns {Promise<void>}
*/
public unmatchMatchedTransaction(
tenantId: number,
uncategorizedTransactionId: number,
): Promise<void> {
return this.uow.withTransaction(async (trx) => {
await this.eventPublisher.emitAsync(events.bankMatch.onUnmatching, {
tenantId,
uncategorizedTransactionId,
trx,
} as IBankTransactionUnmatchingEventPayload);
@@ -37,7 +35,6 @@ export class UnmatchMatchedBankTransaction {
.delete();
await this.eventPublisher.emitAsync(events.bankMatch.onUnmatched, {
tenantId,
uncategorizedTransactionId,
trx,
} as IBankTransactionUnmatchingEventPayload);

View File

@@ -6,7 +6,7 @@ import { EventEmitter2 } from '@nestjs/event-emitter';
import { events } from '@/common/events/events';
import { SystemPlaidItem } from '../models/SystemPlaidItem';
import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
import { PlaidItemDTO } from '../types/BankingPlaid.types';
import { IPlaidItemCreatedEventPayload, PlaidItemDTO } from '../types/BankingPlaid.types';
@Injectable()
export class PlaidItemService {

View File

@@ -133,7 +133,7 @@ export class PlaidWebooks {
): Promise<void> {
switch (webhookCode) {
case 'WEBHOOK_UPDATE_ACKNOWLEDGED':
this.serverLogAndEmitSocket('is updated', plaidItemId, error);
this.serverLogAndEmitSocket('is updated', webhookCode, plaidItemId);
break;
case 'ERROR': {
break;

View File

@@ -70,13 +70,15 @@ export class TriggerRecognizedTransactionsSubscriber {
@OnEvent(events.import.onImportCommitted)
private async triggerRecognizeTransactionsOnImportCommitted({
importId,
}: IImportFileCommitedEventPayload) {
const importFile = await Import.query().findOne({ importId });
const batch = importFile.paramsParsed.batch;
const payload = { transactionsCriteria: { batch } };
// Cannot continue if the imported resource is not bank account transactions.
if (importFile.resource !== 'UncategorizedCashflowTransaction') return;
// @ts-ignore
}: IImportFileCommitedEventPayload) {
// const importFile = await Import.query().findOne({ importId });
// const batch = importFile.paramsParsed.batch;
// const payload = { transactionsCriteria: { batch } };
// // Cannot continue if the imported resource is not bank account transactions.
// if (importFile.resource !== 'UncategorizedCashflowTransaction') return;
// await this.agenda.now('recognize-uncategorized-transactions-job', payload);
}

View File

@@ -0,0 +1,31 @@
import { Body, Controller, Delete, Get, Param, Post } from '@nestjs/common';
import { BankingTransactionsApplication } from './BankingTransactionsApplication.service';
import { ICashflowNewCommandDTO } from './types/BankingTransactions.types';
@Controller('banking/transactions')
export class BankingTransactionsController {
constructor(
private readonly bankingTransactionsApplication: BankingTransactionsApplication,
) {}
@Post()
async createTransaction(@Body() transactionDTO: ICashflowNewCommandDTO) {
return this.bankingTransactionsApplication.createTransaction(
transactionDTO,
);
}
@Delete(':id')
async deleteTransaction(@Param('id') transactionId: string) {
return this.bankingTransactionsApplication.deleteTransaction(
Number(transactionId),
);
}
@Get(':id')
async getTransaction(@Param('id') transactionId: string) {
return this.bankingTransactionsApplication.getTransaction(
Number(transactionId),
);
}
}

View File

@@ -20,6 +20,7 @@ import { CommandBankTransactionValidator } from './commands/CommandCasflowValida
import { BranchTransactionDTOTransformer } from '../Branches/integrations/BranchTransactionDTOTransform';
import { BranchesModule } from '../Branches/Branches.module';
import { RemovePendingUncategorizedTransaction } from './commands/RemovePendingUncategorizedTransaction.service';
import { BankingTransactionsController } from './BankingTransactions.controller';
const models = [
RegisterTenancyModel(UncategorizedBankTransaction),
@@ -29,6 +30,7 @@ const models = [
@Module({
imports: [AutoIncrementOrdersModule, LedgerModule, BranchesModule],
controllers: [BankingTransactionsController],
providers: [
BankTransactionAutoIncrement,
BankTransactionGLEntriesService,

View File

@@ -7,8 +7,8 @@ import { Model } from 'objection';
// import { CASHFLOW_DIRECTION } from '@/services/Cashflow/constants';
// import { getCashflowTransactionFormattedType } from '@/utils/transactions-types';
import { BaseModel } from '@/models/Model';
import { getCashflowTransactionType } from '../utils';
import { CASHFLOW_DIRECTION } from '../constants';
import { getCashflowAccountTransactionsTypes, getCashflowTransactionType } from '../utils';
import { CASHFLOW_DIRECTION, CASHFLOW_TRANSACTION_TYPE } from '../constants';
import { BankTransactionLine } from './BankTransactionLine';
import { Account } from '@/modules/Accounts/models/Account.model';
@@ -27,9 +27,15 @@ export class BankTransaction extends BaseModel {
cashflowAccountId: number;
creditAccountId: number;
categorizeRefType: string;
categorizeRefId: number;
uncategorized: boolean;
branchId: number;
userId: number;
publishedAt: Date;
entries: BankTransactionLine[];
cashflowAccount: Account;
creditAccount: Account;
@@ -92,7 +98,9 @@ export class BankTransaction extends BaseModel {
// }
get typeMeta() {
return getCashflowTransactionType(this.transactionType);
return getCashflowTransactionType(
this.transactionType as CASHFLOW_TRANSACTION_TYPE,
);
}
/**

View File

@@ -12,6 +12,7 @@ import { BaseModel } from '@/models/Model';
import { ItemEntry } from '@/modules/TransactionItemEntry/models/ItemEntry';
import { BillLandedCost } from '@/modules/BillLandedCosts/models/BillLandedCost';
import { DiscountType } from '@/common/types/Discount';
import { Knex } from 'knex';
export class Bill extends BaseModel {
public amount: number;
@@ -615,7 +616,7 @@ export class Bill extends BaseModel {
return notFoundBillsIds;
}
static changePaymentAmount(billId, amount, trx) {
static changePaymentAmount(billId, amount, trx: Knex.Transaction) {
const changeMethod = amount > 0 ? 'increment' : 'decrement';
return this.query(trx)
.where('id', billId)

View File

@@ -1,7 +1,6 @@
import moment from 'moment';
import { defaultTo, omit, isEmpty } from 'lodash';
import { Injectable } from '@nestjs/common';
import { Customer } from '../models/Customer';
import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
import { ICustomerEditDTO, ICustomerNewDTO } from '../types/Customers.types';
import { ContactService } from '@/modules/Contacts/types/Contacts.types';
@@ -20,7 +19,7 @@ export class CreateEditCustomerDTO {
*/
private transformCommonDTO = (
customerDTO: ICustomerNewDTO | ICustomerEditDTO,
): Partial<Customer> => {
) => {
return {
...omit(customerDTO, ['customerType']),
contactType: customerDTO.customerType,

View File

@@ -4,7 +4,7 @@ import { PaymentReceived } from '../models/PaymentReceived';
export interface IPaymentReceivedCreateDTO {
customerId: number;
paymentDate: Date;
paymentDate: Date | string;
amount: number;
exchangeRate: number;
referenceNo: string;