mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-15 12:20:31 +00:00
refactor(nestjs): banking modules
This commit is contained in:
@@ -90,6 +90,8 @@ import { MiscellaneousModule } from '../Miscellaneous/Miscellaneous.module';
|
||||
import { UsersModule } from '../UsersModule/Users.module';
|
||||
import { ContactsModule } from '../Contacts/Contacts.module';
|
||||
import { BankingPlaidModule } from '../BankingPlaid/BankingPlaid.module';
|
||||
import { BankingCategorizeModule } from '../BankingCategorize/BankingCategorize.module';
|
||||
import { TenantModelsInitializeModule } from '../Tenancy/TenantModelsInitialize.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -151,6 +153,7 @@ import { BankingPlaidModule } from '../BankingPlaid/BankingPlaid.module';
|
||||
ScheduleModule.forRoot(),
|
||||
TenancyDatabaseModule,
|
||||
TenancyModelsModule,
|
||||
TenantModelsInitializeModule,
|
||||
AuthModule,
|
||||
TenancyModule,
|
||||
ChromiumlyTenancyModule,
|
||||
@@ -188,6 +191,7 @@ import { BankingPlaidModule } from '../BankingPlaid/BankingPlaid.module';
|
||||
BankingTransactionsModule,
|
||||
BankingMatchingModule,
|
||||
BankingPlaidModule,
|
||||
BankingCategorizeModule,
|
||||
TransactionsLockingModule,
|
||||
SettingsModule,
|
||||
FeaturesModule,
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
import { Knex } from 'knex';
|
||||
import { CategorizeBankTransaction } from './commands/CategorizeBankTransaction';
|
||||
import { UncategorizeBankTransactionService } from './commands/UncategorizeBankTransaction.service';
|
||||
import { UncategorizeBankTransactionsBulk } from './commands/UncategorizeBankTransactionsBulk.service';
|
||||
import { UncategorizedBankTransactionDto } from './dtos/CreateUncategorizedBankTransaction.dto';
|
||||
import { CategorizeBankTransactionDto } from './dtos/CategorizeBankTransaction.dto';
|
||||
import { CategorizeTransactionAsExpense } from './commands/CategorizeTransactionAsExpense';
|
||||
import { CreateUncategorizedTransactionService } from './commands/CreateUncategorizedTransaction.service';
|
||||
import { ICategorizeCashflowTransactioDTO } from './types/BankingCategorize.types';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class BankingCategorizeApplication {
|
||||
constructor(
|
||||
private readonly categorizeBankTransaction: CategorizeBankTransaction,
|
||||
private readonly uncategorizeBankTransaction: UncategorizeBankTransactionService,
|
||||
private readonly uncategorizeBankTransactionsBulk: UncategorizeBankTransactionsBulk,
|
||||
private readonly categorizeTransactionAsExpense: CategorizeTransactionAsExpense,
|
||||
private readonly createUncategorizedTransaction: CreateUncategorizedTransactionService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Categorize a bank transaction with the given ID and categorization data.
|
||||
* @param {number | Array<number>} uncategorizedTransactionId - The ID(s) of the uncategorized transaction(s) to categorize.
|
||||
* @param {CategorizeBankTransactionDto} categorizeDTO - Data for categorization.
|
||||
* @returns {Promise<any>} The result of the categorization operation.
|
||||
*/
|
||||
public categorizeTransaction(
|
||||
uncategorizedTransactionId: number | Array<number>,
|
||||
categorizeDTO: CategorizeBankTransactionDto,
|
||||
) {
|
||||
return this.categorizeBankTransaction.categorize(
|
||||
uncategorizedTransactionId,
|
||||
categorizeDTO,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uncategorize a bank transaction with the given ID.
|
||||
* @param {number} uncategorizedTransactionId - The ID of the transaction to uncategorize.
|
||||
* @returns {Promise<Array<number>>} Array of affected transaction IDs.
|
||||
*/
|
||||
public uncategorizeTransaction(
|
||||
uncategorizedTransactionId: number,
|
||||
): Promise<Array<number>> {
|
||||
return this.uncategorizeBankTransaction.uncategorize(
|
||||
uncategorizedTransactionId,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uncategorize multiple bank transactions in bulk.
|
||||
* @param {number | Array<number>} uncategorizedTransactionIds - The ID(s) of the transaction(s) to uncategorize.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public uncategorizeTransactionsBulk(
|
||||
uncategorizedTransactionIds: number | Array<number>,
|
||||
) {
|
||||
return this.uncategorizeBankTransactionsBulk.uncategorizeBulk(
|
||||
uncategorizedTransactionIds,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Categorize a transaction as an expense.
|
||||
* @param {number} cashflowTransactionId - The ID of the cashflow transaction to categorize.
|
||||
* @param {ICategorizeCashflowTransactioDTO} transactionDTO - Data for categorization.
|
||||
* @returns {Promise<any>} The result of the categorization operation.
|
||||
*/
|
||||
public categorizeTransactionAsExpenseType(
|
||||
cashflowTransactionId: number,
|
||||
transactionDTO: ICategorizeCashflowTransactioDTO,
|
||||
) {
|
||||
return this.categorizeTransactionAsExpense.categorize(
|
||||
cashflowTransactionId,
|
||||
transactionDTO,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new uncategorized bank transaction.
|
||||
* @param {UncategorizedBankTransactionDto} createDTO - Data for creating the uncategorized transaction.
|
||||
* @param {Knex.Transaction} [trx] - Optional Knex transaction.
|
||||
* @returns {Promise<any>} The created uncategorized transaction.
|
||||
*/
|
||||
public createUncategorizedBankTransaction(
|
||||
createDTO: UncategorizedBankTransactionDto,
|
||||
trx?: Knex.Transaction,
|
||||
) {
|
||||
return this.createUncategorizedTransaction.create(createDTO, trx);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import { Body, Controller, Delete, Param, Post, Query } from '@nestjs/common';
|
||||
import { castArray, omit } from 'lodash';
|
||||
import { BankingCategorizeApplication } from './BankingCategorize.application';
|
||||
import { CategorizeBankTransactionRouteDto } from './dtos/CategorizeBankTransaction.dto';
|
||||
|
||||
@Controller('banking/categorize')
|
||||
export class BankingCategorizeController {
|
||||
constructor(
|
||||
private readonly bankingCategorizeApplication: BankingCategorizeApplication,
|
||||
) {}
|
||||
|
||||
@Post()
|
||||
public categorizeTransaction(
|
||||
@Body() body: CategorizeBankTransactionRouteDto,
|
||||
) {
|
||||
return this.bankingCategorizeApplication.categorizeTransaction(
|
||||
castArray(body.uncategorizedTransactionIds),
|
||||
omit(body, 'uncategorizedTransactionIds'),
|
||||
);
|
||||
}
|
||||
|
||||
@Delete('/bulk')
|
||||
public uncategorizeTransactionsBulk(
|
||||
@Query() uncategorizedTransactionIds: number[] | number,
|
||||
) {
|
||||
return this.bankingCategorizeApplication.uncategorizeTransactionsBulk(
|
||||
castArray(uncategorizedTransactionIds),
|
||||
);
|
||||
}
|
||||
|
||||
@Delete('/:id')
|
||||
public uncategorizeTransaction(
|
||||
@Param('id') uncategorizedTransactionId: number,
|
||||
) {
|
||||
return this.bankingCategorizeApplication.uncategorizeTransaction(
|
||||
Number(uncategorizedTransactionId),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,38 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { forwardRef, Module } from '@nestjs/common';
|
||||
import { CreateUncategorizedTransactionService } from './commands/CreateUncategorizedTransaction.service';
|
||||
import { CategorizeTransactionAsExpense } from './commands/CategorizeTransactionAsExpense';
|
||||
import { BankingTransactionsModule } from '../BankingTransactions/BankingTransactions.module';
|
||||
import { ExpensesModule } from '../Expenses/Expenses.module';
|
||||
import { UncategorizedTransactionsImportable } from './commands/UncategorizedTransactionsImportable';
|
||||
import { BankingCategorizeController } from './BankingCategorize.controller';
|
||||
import { BankingCategorizeApplication } from './BankingCategorize.application';
|
||||
import { CategorizeBankTransaction } from './commands/CategorizeBankTransaction';
|
||||
import { UncategorizeBankTransactionService } from './commands/UncategorizeBankTransaction.service';
|
||||
import { UncategorizeBankTransactionsBulk } from './commands/UncategorizeBankTransactionsBulk.service';
|
||||
|
||||
@Module({
|
||||
imports: [BankingTransactionsModule, ExpensesModule],
|
||||
imports: [
|
||||
BankingTransactionsModule,
|
||||
ExpensesModule,
|
||||
forwardRef(() => BankingTransactionsModule),
|
||||
],
|
||||
providers: [
|
||||
CreateUncategorizedTransactionService,
|
||||
CategorizeTransactionAsExpense,
|
||||
UncategorizedTransactionsImportable
|
||||
UncategorizedTransactionsImportable,
|
||||
BankingCategorizeApplication,
|
||||
CategorizeBankTransaction,
|
||||
UncategorizeBankTransactionService,
|
||||
UncategorizeBankTransactionsBulk,
|
||||
],
|
||||
exports: [
|
||||
CreateUncategorizedTransactionService,
|
||||
CategorizeTransactionAsExpense,
|
||||
BankingCategorizeApplication,
|
||||
CategorizeBankTransaction,
|
||||
UncategorizeBankTransactionService,
|
||||
UncategorizeBankTransactionsBulk,
|
||||
],
|
||||
controllers: [BankingCategorizeController],
|
||||
})
|
||||
export class BankingCategorizeModule {}
|
||||
|
||||
@@ -5,7 +5,6 @@ import { Knex } from 'knex';
|
||||
import {
|
||||
ICashflowTransactionCategorizedPayload,
|
||||
ICashflowTransactionUncategorizingPayload,
|
||||
ICategorizeCashflowTransactioDTO,
|
||||
} from '../types/BankingCategorize.types';
|
||||
import {
|
||||
transformCategorizeTransToCashflow,
|
||||
@@ -17,9 +16,10 @@ import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
|
||||
import { UncategorizedBankTransaction } from '@/modules/BankingTransactions/models/UncategorizedBankTransaction';
|
||||
import { events } from '@/common/events/events';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
import { CategorizeBankTransactionDto } from '../dtos/CategorizeBankTransaction.dto';
|
||||
|
||||
@Injectable()
|
||||
export class CategorizeCashflowTransaction {
|
||||
export class CategorizeBankTransaction {
|
||||
constructor(
|
||||
private readonly eventPublisher: EventEmitter2,
|
||||
private readonly uow: UnitOfWork,
|
||||
@@ -38,7 +38,7 @@ export class CategorizeCashflowTransaction {
|
||||
*/
|
||||
public async categorize(
|
||||
uncategorizedTransactionId: number | Array<number>,
|
||||
categorizeDTO: ICategorizeCashflowTransactioDTO,
|
||||
categorizeDTO: CategorizeBankTransactionDto,
|
||||
) {
|
||||
const uncategorizedTransactionIds = castArray(uncategorizedTransactionId);
|
||||
|
||||
@@ -68,7 +68,6 @@ export class CategorizeCashflowTransaction {
|
||||
await this.eventPublisher.emitAsync(
|
||||
events.cashflow.onTransactionCategorizing,
|
||||
{
|
||||
// tenantId,
|
||||
oldUncategorizedTransactions,
|
||||
trx,
|
||||
} as ICashflowTransactionUncategorizingPayload,
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Knex } from 'knex';
|
||||
import {
|
||||
CreateUncategorizedTransactionDTO,
|
||||
IUncategorizedTransactionCreatedEventPayload,
|
||||
IUncategorizedTransactionCreatingEventPayload,
|
||||
} from '../types/BankingCategorize.types';
|
||||
@@ -10,6 +9,7 @@ import { UncategorizedBankTransaction } from '../../BankingTransactions/models/U
|
||||
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
|
||||
import { events } from '@/common/events/events';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
import { UncategorizedBankTransactionDto } from '../dtos/CreateUncategorizedBankTransaction.dto';
|
||||
|
||||
@Injectable()
|
||||
export class CreateUncategorizedTransactionService {
|
||||
@@ -30,7 +30,7 @@ export class CreateUncategorizedTransactionService {
|
||||
* @returns {Promise<UncategorizedBankTransaction>}
|
||||
*/
|
||||
public create(
|
||||
createUncategorizedTransactionDTO: CreateUncategorizedTransactionDTO,
|
||||
createUncategorizedTransactionDTO: UncategorizedBankTransactionDto,
|
||||
trx?: Knex.Transaction,
|
||||
) {
|
||||
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
||||
|
||||
@@ -12,7 +12,7 @@ import { UncategorizedBankTransaction } from '../../BankingTransactions/models/U
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
|
||||
@Injectable()
|
||||
export class UncategorizeCashflowTransactionService {
|
||||
export class UncategorizeBankTransactionService {
|
||||
constructor(
|
||||
private readonly eventPublisher: EventEmitter2,
|
||||
private readonly uow: UnitOfWork,
|
||||
@@ -1,18 +1,17 @@
|
||||
import { castArray } from 'lodash';
|
||||
import { PromisePool } from '@supercharge/promise-pool';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { UncategorizeCashflowTransactionService } from './UncategorizeCashflowTransaction.service';
|
||||
import { UncategorizeBankTransactionService } from './UncategorizeBankTransaction.service';
|
||||
|
||||
@Injectable()
|
||||
export class UncategorizeCashflowTransactionsBulk {
|
||||
export class UncategorizeBankTransactionsBulk {
|
||||
constructor(
|
||||
private readonly uncategorizeTransactionService: UncategorizeCashflowTransactionService
|
||||
private readonly uncategorizeTransactionService: UncategorizeBankTransactionService
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Uncategorize the given bank transactions in bulk.
|
||||
* @param {number} tenantId
|
||||
* @param {number} uncategorizedTransactionId
|
||||
* @param {number | Array<number>} uncategorizedTransactionId
|
||||
*/
|
||||
public async uncategorizeBulk(
|
||||
uncategorizedTransactionId: number | Array<number>
|
||||
@@ -0,0 +1,55 @@
|
||||
import { ToNumber } from '@/common/decorators/Validators';
|
||||
import {
|
||||
IsArray,
|
||||
IsDateString,
|
||||
IsInt,
|
||||
IsNotEmpty,
|
||||
IsNumber,
|
||||
IsOptional,
|
||||
IsString,
|
||||
} from 'class-validator';
|
||||
|
||||
export class CategorizeBankTransactionDto {
|
||||
@IsDateString()
|
||||
@IsNotEmpty()
|
||||
date: Date;
|
||||
|
||||
@IsInt()
|
||||
@ToNumber()
|
||||
@IsNotEmpty()
|
||||
creditAccountId: number;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
referenceNo: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
transactionNumber: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
transactionType: string;
|
||||
|
||||
@IsNumber()
|
||||
@ToNumber()
|
||||
@IsOptional()
|
||||
exchangeRate: number = 1;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
currencyCode: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
description: string;
|
||||
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
branchId: number;
|
||||
}
|
||||
|
||||
export class CategorizeBankTransactionRouteDto extends CategorizeBankTransactionDto {
|
||||
@IsArray()
|
||||
uncategorizedTransactionIds: Array<number>;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { IsBoolean, IsDateString, IsNumber, IsString } from 'class-validator';
|
||||
|
||||
export class UncategorizedBankTransactionDto {
|
||||
@IsDateString()
|
||||
date: Date | string;
|
||||
|
||||
@IsNumber()
|
||||
accountId: number;
|
||||
|
||||
@IsNumber()
|
||||
amount: number;
|
||||
|
||||
@IsString()
|
||||
currencyCode: string;
|
||||
|
||||
@IsString()
|
||||
payee?: string;
|
||||
|
||||
@IsString()
|
||||
description?: string;
|
||||
|
||||
@IsString()
|
||||
referenceNo?: string | null;
|
||||
|
||||
@IsString()
|
||||
plaidTransactionId?: string | null;
|
||||
|
||||
@IsBoolean()
|
||||
pending?: boolean;
|
||||
|
||||
@IsString()
|
||||
pendingPlaidTransactionId?: string | null;
|
||||
|
||||
@IsString()
|
||||
batch?: string;
|
||||
}
|
||||
@@ -1,47 +1,48 @@
|
||||
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||
import { Body, Controller, Get, Param, Post, Query } from '@nestjs/common';
|
||||
import { BankingMatchingApplication } from './BankingMatchingApplication';
|
||||
import { GetMatchedTransactionsFilter, IMatchTransactionDTO } from './types';
|
||||
import { GetMatchedTransactionsFilter } from './types';
|
||||
import { MatchBankTransactionDto } from './dtos/MatchBankTransaction.dto';
|
||||
|
||||
@Controller('banking/matching')
|
||||
@ApiTags('banking-transactions-matching')
|
||||
export class BankingMatchingController {
|
||||
constructor(
|
||||
private readonly bankingMatchingApplication: BankingMatchingApplication
|
||||
private readonly bankingMatchingApplication: BankingMatchingApplication,
|
||||
) {}
|
||||
|
||||
@Get('matched')
|
||||
@ApiOperation({ summary: 'Retrieves the matched transactions.' })
|
||||
async getMatchedTransactions(
|
||||
@Query('uncategorizedTransactionIds') uncategorizedTransactionIds: number[],
|
||||
@Query() filter: GetMatchedTransactionsFilter
|
||||
@Query() filter: GetMatchedTransactionsFilter,
|
||||
) {
|
||||
return this.bankingMatchingApplication.getMatchedTransactions(
|
||||
uncategorizedTransactionIds,
|
||||
filter
|
||||
filter,
|
||||
);
|
||||
}
|
||||
|
||||
@Post('/match/:uncategorizedTransactionId')
|
||||
@ApiOperation({ summary: 'Match the given uncategorized transaction.' })
|
||||
async matchTransaction(
|
||||
@Param('uncategorizedTransactionId') uncategorizedTransactionId: number | number[],
|
||||
@Body() matchedTransactions: MatchBankTransactionDto
|
||||
@Param('uncategorizedTransactionId')
|
||||
uncategorizedTransactionId: number | number[],
|
||||
@Body() matchedTransactions: MatchBankTransactionDto,
|
||||
) {
|
||||
return this.bankingMatchingApplication.matchTransaction(
|
||||
uncategorizedTransactionId,
|
||||
matchedTransactions
|
||||
matchedTransactions,
|
||||
);
|
||||
}
|
||||
|
||||
@Post('/unmatch/:uncategorizedTransactionId')
|
||||
@ApiOperation({ summary: 'Unmatch the given uncategorized transaction.' })
|
||||
async unmatchMatchedTransaction(
|
||||
@Param('uncategorizedTransactionId') uncategorizedTransactionId: number
|
||||
@Param('uncategorizedTransactionId') uncategorizedTransactionId: number,
|
||||
) {
|
||||
return this.bankingMatchingApplication.unmatchMatchedTransaction(
|
||||
uncategorizedTransactionId
|
||||
uncategorizedTransactionId,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Knex } from 'knex';
|
||||
import { GetMatchedTransactionsByType } from './GetMatchedTransactionsByType';
|
||||
import { GetMatchedTransactionCashflowTransformer } from './GetMatchedTransactionCashflowTransformer';
|
||||
import { GetMatchedTransactionsFilter } from '../types';
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { GetMatchedTransactionsFilter, MatchedTransactionPOJO } from '../types';
|
||||
import { GetMatchedTransactionsFilter, MatchedTransactionPOJO, MatchedTransactionsPOJO } from '../types';
|
||||
import { GetMatchedTransactionsByType } from './GetMatchedTransactionsByType';
|
||||
import { GetMatchedTransactionExpensesTransformer } from './GetMatchedTransactionExpensesTransformer';
|
||||
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
|
||||
import { Expense } from '@/modules/Expenses/models/Expense.model';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
import { Knex } from 'knex';
|
||||
import { TENANCY_DB_CONNECTION } from '@/modules/Tenancy/TenancyDB/TenancyDB.constants';
|
||||
import { initialize } from 'objection';
|
||||
|
||||
@Injectable()
|
||||
export class GetMatchedTransactionsByExpenses extends GetMatchedTransactionsByType {
|
||||
@@ -13,17 +16,26 @@ export class GetMatchedTransactionsByExpenses extends GetMatchedTransactionsByTy
|
||||
|
||||
@Inject(Expense.name)
|
||||
protected readonly expenseModel: TenantModelProxy<typeof Expense>,
|
||||
|
||||
@Inject(TENANCY_DB_CONNECTION)
|
||||
private readonly tenantDb: () => Knex,
|
||||
|
||||
@Inject('TENANT_MODELS_INIT')
|
||||
private readonly tenantModelsInit: () => Promise<boolean>,
|
||||
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the matched transactions of expenses.
|
||||
* @param {number} tenantId
|
||||
* @param {GetMatchedTransactionsFilter} filter
|
||||
* @returns
|
||||
*/
|
||||
async getMatchedTransactions(filter: GetMatchedTransactionsFilter) {
|
||||
async getMatchedTransactions(
|
||||
filter: GetMatchedTransactionsFilter,
|
||||
): Promise<MatchedTransactionsPOJO> {
|
||||
// await this.tenantModelsInit();
|
||||
// Retrieve the expense matches.
|
||||
const expenses = await this.expenseModel()
|
||||
.query()
|
||||
@@ -49,6 +61,7 @@ export class GetMatchedTransactionsByExpenses extends GetMatchedTransactionsByTy
|
||||
}
|
||||
query.orderBy('paymentDate', 'DESC');
|
||||
});
|
||||
|
||||
return this.transformer.transform(
|
||||
expenses,
|
||||
new GetMatchedTransactionExpensesTransformer(),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { Knex } from 'knex';
|
||||
import { first } from 'lodash';
|
||||
import { GetMatchedTransactionInvoicesTransformer } from './GetMatchedTransactionInvoicesTransformer';
|
||||
@@ -9,7 +10,6 @@ import {
|
||||
} from '../types';
|
||||
import { GetMatchedTransactionsByType } from './GetMatchedTransactionsByType';
|
||||
import { CreatePaymentReceivedService } from '@/modules/PaymentReceived/commands/CreatePaymentReceived.serivce';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { SaleInvoice } from '@/modules/SaleInvoices/models/SaleInvoice';
|
||||
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
|
||||
import { UncategorizedBankTransaction } from '@/modules/BankingTransactions/models/UncategorizedBankTransaction';
|
||||
@@ -86,7 +86,6 @@ export class GetMatchedTransactionsByInvoices extends GetMatchedTransactionsByTy
|
||||
|
||||
/**
|
||||
* Creates the common matched transaction.
|
||||
* @param {number} tenantId
|
||||
* @param {Array<number>} uncategorizedTransactionIds
|
||||
* @param {IMatchTransactionDTO} matchTransactionDTO
|
||||
* @param {Knex.Transaction} trx
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import { Knex } from 'knex';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { initialize } from 'objection';
|
||||
import { GetMatchedTransactionManualJournalsTransformer } from './GetMatchedTransactionManualJournalsTransformer';
|
||||
import { GetMatchedTransactionsByType } from './GetMatchedTransactionsByType';
|
||||
import { GetMatchedTransactionsFilter } from '../types';
|
||||
import { ManualJournal } from '@/modules/ManualJournals/models/ManualJournal';
|
||||
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
import { TENANCY_DB_CONNECTION } from '@/modules/Tenancy/TenancyDB/TenancyDB.constants';
|
||||
|
||||
@Injectable()
|
||||
export class GetMatchedTransactionsByManualJournals extends GetMatchedTransactionsByType {
|
||||
|
||||
@@ -12,7 +12,7 @@ import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
|
||||
export abstract class GetMatchedTransactionsByType {
|
||||
@Inject(MatchedBankTransaction.name)
|
||||
private readonly matchedBankTransactionModel: TenantModelProxy<
|
||||
matchedBankTransactionModel: TenantModelProxy<
|
||||
typeof MatchedBankTransaction
|
||||
>;
|
||||
|
||||
|
||||
@@ -76,6 +76,11 @@ const models = [
|
||||
GetPendingBankAccountTransactions,
|
||||
GetAutofillCategorizeTransactionService,
|
||||
],
|
||||
exports: [...models, RemovePendingUncategorizedTransaction],
|
||||
exports: [
|
||||
...models,
|
||||
RemovePendingUncategorizedTransaction,
|
||||
CommandBankTransactionValidator,
|
||||
CreateBankTransactionService
|
||||
],
|
||||
})
|
||||
export class BankingTransactionsModule {}
|
||||
|
||||
@@ -15,6 +15,7 @@ import { GetUncategorizedTransactionsQueryDto } from './dtos/GetUncategorizedTra
|
||||
import { GetPendingBankAccountTransactions } from './queries/GetPendingBankAccountTransaction.service';
|
||||
import { GetPendingTransactionsQueryDto } from './dtos/GetPendingTransactionsQuery.dto';
|
||||
import { GetAutofillCategorizeTransactionService } from './queries/GetAutofillCategorizeTransaction/GetAutofillCategorizeTransaction.service';
|
||||
import { GetBankTransactionsQueryDto } from './dtos/GetBankTranasctionsQuery.dto';
|
||||
|
||||
@Injectable()
|
||||
export class BankingTransactionsApplication {
|
||||
@@ -54,7 +55,7 @@ export class BankingTransactionsApplication {
|
||||
* Retrieves the bank transactions of the given bank id.
|
||||
* @param {ICashflowAccountTransactionsQuery} query
|
||||
*/
|
||||
public getBankAccountTransactions(query: ICashflowAccountTransactionsQuery) {
|
||||
public getBankAccountTransactions(query: GetBankTransactionsQueryDto) {
|
||||
return this.getBankAccountTransactionsService.bankAccountTransactions(
|
||||
query,
|
||||
);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { includes, camelCase, upperFirst, sumBy } from 'lodash';
|
||||
import { getCashflowTransactionType } from '../utils';
|
||||
import {
|
||||
@@ -6,7 +7,6 @@ import {
|
||||
ERRORS,
|
||||
} from '../constants';
|
||||
import { Account } from '@/modules/Accounts/models/Account.model';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ServiceError } from '@/modules/Items/ServiceError';
|
||||
import { BankTransaction } from '../models/BankTransaction';
|
||||
import { UncategorizedBankTransaction } from '../models/UncategorizedBankTransaction';
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { pick } from 'lodash';
|
||||
import { Knex } from 'knex';
|
||||
import * as R from 'ramda';
|
||||
import * as composeAsync from 'async/compose';
|
||||
import { CASHFLOW_TRANSACTION_TYPE } from '../constants';
|
||||
import { transformCashflowTransactionType } from '../utils';
|
||||
@@ -14,12 +13,12 @@ import { events } from '@/common/events/events';
|
||||
import { Account } from '@/modules/Accounts/models/Account.model';
|
||||
import { BankTransaction } from '../models/BankTransaction';
|
||||
import {
|
||||
ICashflowNewCommandDTO,
|
||||
ICommandCashflowCreatedPayload,
|
||||
ICommandCashflowCreatingPayload,
|
||||
} from '../types/BankingTransactions.types';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
import { CreateBankTransactionDto } from '../dtos/CreateBankTransaction.dto';
|
||||
import { formatDateFields } from '@/utils/format-date-fields';
|
||||
|
||||
@Injectable()
|
||||
export class CreateBankTransactionService {
|
||||
@@ -42,7 +41,7 @@ export class CreateBankTransactionService {
|
||||
* @param {ICashflowNewCommandDTO} newCashflowTransactionDTO
|
||||
*/
|
||||
public authorize = async (
|
||||
newCashflowTransactionDTO: ICashflowNewCommandDTO,
|
||||
newCashflowTransactionDTO: CreateBankTransactionDto,
|
||||
creditAccount: Account,
|
||||
) => {
|
||||
const transactionType = transformCashflowTransactionType(
|
||||
@@ -60,7 +59,7 @@ export class CreateBankTransactionService {
|
||||
|
||||
/**
|
||||
* Transformes owner contribution DTO to cashflow transaction.
|
||||
* @param {ICashflowNewCommandDTO} newCashflowTransactionDTO - New transaction DTO.
|
||||
* @param {CreateBankTransactionDto} newCashflowTransactionDTO - New transaction DTO.
|
||||
* @returns {ICashflowTransactionInput} - Cashflow transaction object.
|
||||
*/
|
||||
private transformCashflowTransactionDTO = async (
|
||||
@@ -91,7 +90,7 @@ export class CreateBankTransactionService {
|
||||
|
||||
const initialDTO = {
|
||||
amount,
|
||||
...fromDTO,
|
||||
...formatDateFields(fromDTO, ['date']),
|
||||
transactionNumber,
|
||||
currencyCode: cashflowAccount.currencyCode,
|
||||
exchangeRate: fromDTO?.exchangeRate || 1,
|
||||
@@ -117,7 +116,7 @@ export class CreateBankTransactionService {
|
||||
* @returns {Promise<ICashflowTransaction>}
|
||||
*/
|
||||
public newCashflowTransaction = async (
|
||||
newTransactionDTO: ICashflowNewCommandDTO,
|
||||
newTransactionDTO: CreateBankTransactionDto,
|
||||
userId?: number,
|
||||
): Promise<BankTransaction> => {
|
||||
// Retrieves the cashflow account or throw not found error.
|
||||
|
||||
@@ -1,47 +1,64 @@
|
||||
import { ToNumber } from '@/common/decorators/Validators';
|
||||
import {
|
||||
IsBoolean,
|
||||
IsDate,
|
||||
IsDateString,
|
||||
IsInt,
|
||||
IsNotEmpty,
|
||||
IsNumber,
|
||||
IsOptional,
|
||||
IsString,
|
||||
} from 'class-validator';
|
||||
|
||||
export class CreateBankTransactionDto {
|
||||
@IsDate()
|
||||
@IsDateString()
|
||||
@IsNotEmpty()
|
||||
date: Date;
|
||||
|
||||
@IsString()
|
||||
transactionNumber: string;
|
||||
@IsOptional()
|
||||
transactionNumber?: string;
|
||||
|
||||
@IsString()
|
||||
referenceNo: string;
|
||||
@IsOptional()
|
||||
referenceNo?: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
transactionType: string;
|
||||
|
||||
@IsString()
|
||||
description: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@ToNumber()
|
||||
@IsNumber()
|
||||
amount: number;
|
||||
|
||||
@ToNumber()
|
||||
@IsNumber()
|
||||
exchangeRate: number;
|
||||
exchangeRate: number = 1;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
currencyCode: string;
|
||||
|
||||
@IsNumber()
|
||||
@IsNotEmpty()
|
||||
@ToNumber()
|
||||
@IsInt()
|
||||
creditAccountId: number;
|
||||
|
||||
@IsNumber()
|
||||
@IsNotEmpty()
|
||||
@ToNumber()
|
||||
@IsInt()
|
||||
cashflowAccountId: number;
|
||||
|
||||
@IsBoolean()
|
||||
publish: boolean;
|
||||
@IsOptional()
|
||||
publish: boolean = true;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@ToNumber()
|
||||
@IsInt()
|
||||
branchId?: number;
|
||||
|
||||
@IsOptional()
|
||||
@@ -53,6 +70,6 @@ export class CreateBankTransactionDto {
|
||||
plaidAccountId?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
@IsInt()
|
||||
uncategorizedTransactionId?: number;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common';
|
||||
import { getBankAccountTransactionsDefaultQuery } from './_utils';
|
||||
import { GetBankAccountTransactionsRepository } from './GetBankAccountTransactionsRepo.service';
|
||||
import { GetBankAccountTransactions } from './GetBankAccountTransactions';
|
||||
import { ICashflowAccountTransactionsQuery } from '../../types/BankingTransactions.types';
|
||||
import { GetBankTransactionsQueryDto } from '../../dtos/GetBankTranasctionsQuery.dto';
|
||||
|
||||
@Injectable()
|
||||
export class GetBankAccountTransactionsService {
|
||||
@@ -16,7 +16,7 @@ export class GetBankAccountTransactionsService {
|
||||
* @return {Promise<IInvetoryItemDetailDOO>}
|
||||
*/
|
||||
public async bankAccountTransactions(
|
||||
query: ICashflowAccountTransactionsQuery,
|
||||
query: GetBankTransactionsQueryDto,
|
||||
) {
|
||||
const parsedQuery = {
|
||||
...getBankAccountTransactionsDefaultQuery(),
|
||||
|
||||
@@ -1,35 +1,23 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { initialize } from 'objection';
|
||||
import { Knex } from 'knex';
|
||||
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
|
||||
import { UncategorizedBankTransaction } from '../models/UncategorizedBankTransaction';
|
||||
import { UncategorizedTransactionTransformer } from '../../BankingCategorize/commands/UncategorizedTransaction.transformer';
|
||||
import { GetUncategorizedTransactionsQueryDto } from '../dtos/GetUncategorizedTransactionsQuery.dto';
|
||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||
import { TENANCY_DB_CONNECTION } from '@/modules/Tenancy/TenancyDB/TenancyDB.constants';
|
||||
import { Account } from '@/modules/Accounts/models/Account.model';
|
||||
import { RecognizedBankTransaction } from '@/modules/BankingTranasctionsRegonize/models/RecognizedBankTransaction';
|
||||
import { MatchedBankTransaction } from '@/modules/BankingMatching/models/MatchedBankTransaction';
|
||||
|
||||
@Injectable()
|
||||
export class GetUncategorizedTransactions {
|
||||
/**
|
||||
* @param {TransformerInjectable} transformer
|
||||
* @param {UncategorizedBankTransaction.name} uncategorizedBankTransactionModel
|
||||
*/
|
||||
constructor(
|
||||
private readonly transformer: TransformerInjectable,
|
||||
|
||||
@Inject(TENANCY_DB_CONNECTION)
|
||||
private readonly tenantDb: () => Knex,
|
||||
|
||||
@Inject(UncategorizedBankTransaction.name)
|
||||
private readonly uncategorizedBankTransactionModel: TenantModelProxy<typeof UncategorizedBankTransaction>,
|
||||
|
||||
@Inject(Account.name)
|
||||
private readonly accountModel: TenantModelProxy<typeof Account>,
|
||||
|
||||
@Inject(RecognizedBankTransaction.name)
|
||||
private readonly recognizedTransactionModel: TenantModelProxy<typeof RecognizedBankTransaction>,
|
||||
|
||||
@Inject(MatchedBankTransaction.name)
|
||||
private readonly matchedTransactionModel: TenantModelProxy<typeof MatchedBankTransaction>,
|
||||
private readonly uncategorizedBankTransactionModel: TenantModelProxy<
|
||||
typeof UncategorizedBankTransaction
|
||||
>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -39,7 +27,7 @@ export class GetUncategorizedTransactions {
|
||||
*/
|
||||
public async getTransactions(
|
||||
accountId: number,
|
||||
query: GetUncategorizedTransactionsQueryDto
|
||||
query: GetUncategorizedTransactionsQueryDto,
|
||||
) {
|
||||
// Parsed query with default values.
|
||||
const _query = {
|
||||
@@ -47,16 +35,9 @@ export class GetUncategorizedTransactions {
|
||||
pageSize: 20,
|
||||
...query,
|
||||
};
|
||||
|
||||
await initialize(this.tenantDb(), [
|
||||
this.accountModel(),
|
||||
this.uncategorizedBankTransactionModel(),
|
||||
this.recognizedTransactionModel(),
|
||||
this.matchedTransactionModel(),
|
||||
]);
|
||||
|
||||
const { results, pagination } =
|
||||
await this.uncategorizedBankTransactionModel().query()
|
||||
await this.uncategorizedBankTransactionModel()
|
||||
.query()
|
||||
.onBuild((q) => {
|
||||
q.where('accountId', accountId);
|
||||
q.where('categorized', false);
|
||||
@@ -89,7 +70,7 @@ export class GetUncategorizedTransactions {
|
||||
|
||||
const data = await this.transformer.transform(
|
||||
results,
|
||||
new UncategorizedTransactionTransformer()
|
||||
new UncategorizedTransactionTransformer(),
|
||||
);
|
||||
return {
|
||||
data,
|
||||
|
||||
@@ -3,7 +3,10 @@ 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';
|
||||
import {
|
||||
ICommandCashflowCreatedPayload,
|
||||
ICommandCashflowDeletedPayload,
|
||||
} from '../types/BankingTransactions.types';
|
||||
|
||||
@Injectable()
|
||||
export class BankingTransactionGLEntriesSubscriber {
|
||||
@@ -56,5 +59,5 @@ export class BankingTransactionGLEntriesSubscriber {
|
||||
cashflowTransactionId,
|
||||
trx,
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -492,7 +492,7 @@ export class Bill extends TenantBaseModel {
|
||||
TaxRateTransaction,
|
||||
} = require('../../TaxRates/models/TaxRateTransaction.model');
|
||||
const { Document } = require('../../ChromiumlyTenancy/models/Document');
|
||||
// const { MatchedBankTransaction } = require('models/MatchedBankTransaction');
|
||||
const { MatchedBankTransaction } = require('../../BankingMatching/models/MatchedBankTransaction');
|
||||
|
||||
return {
|
||||
vendor: {
|
||||
@@ -590,17 +590,17 @@ export class Bill extends TenantBaseModel {
|
||||
/**
|
||||
* Bill may belongs to matched bank transaction.
|
||||
*/
|
||||
// matchedBankTransaction: {
|
||||
// relation: Model.HasManyRelation,
|
||||
// modelClass: MatchedBankTransaction,
|
||||
// join: {
|
||||
// from: 'bills.id',
|
||||
// to: 'matched_bank_transactions.referenceId',
|
||||
// },
|
||||
// filter(query) {
|
||||
// query.where('reference_type', 'Bill');
|
||||
// },
|
||||
// },
|
||||
matchedBankTransaction: {
|
||||
relation: Model.HasManyRelation,
|
||||
modelClass: MatchedBankTransaction,
|
||||
join: {
|
||||
from: 'bills.id',
|
||||
to: 'matched_bank_transactions.referenceId',
|
||||
},
|
||||
filter(query) {
|
||||
query.where('reference_type', 'Bill');
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as R from 'ramda';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import validator from 'is-my-json-valid';
|
||||
import * as validator from 'is-my-json-valid';
|
||||
import { IFilterRole } from './DynamicFilter/DynamicFilter.types';
|
||||
import { DynamicFilterAdvancedFilter } from './DynamicFilter/DynamicFilterAdvancedFilter';
|
||||
import { DynamicFilterRoleAbstractor } from './DynamicFilter/DynamicFilterRoleAbstractor';
|
||||
@@ -21,7 +21,7 @@ export class DynamicListFilterRoles extends DynamicFilterRoleAbstractor {
|
||||
properties: {
|
||||
condition: { type: 'string' },
|
||||
fieldKey: { type: 'string' },
|
||||
value: { type: 'string' },
|
||||
// value: { type: ['number', 'string'] },
|
||||
},
|
||||
});
|
||||
const invalidFields = filterRoles.filter((filterRole) => {
|
||||
|
||||
@@ -202,7 +202,7 @@ export class Expense extends TenantBaseModel {
|
||||
const { ExpenseCategory } = require('./ExpenseCategory.model');
|
||||
const { Document } = require('../../ChromiumlyTenancy/models/Document');
|
||||
const { Branch } = require('../../Branches/models/Branch.model');
|
||||
// const { MatchedBankTransaction } = require('models/MatchedBankTransaction');
|
||||
const { MatchedBankTransaction } = require('../../BankingMatching/models/MatchedBankTransaction');
|
||||
|
||||
return {
|
||||
/**
|
||||
@@ -263,20 +263,20 @@ export class Expense extends TenantBaseModel {
|
||||
},
|
||||
},
|
||||
|
||||
// /**
|
||||
// * Expense may belongs to matched bank transaction.
|
||||
// */
|
||||
// matchedBankTransaction: {
|
||||
// relation: Model.HasManyRelation,
|
||||
// modelClass: MatchedBankTransaction,
|
||||
// join: {
|
||||
// from: 'expenses_transactions.id',
|
||||
// to: 'matched_bank_transactions.referenceId',
|
||||
// },
|
||||
// filter(query) {
|
||||
// query.where('reference_type', 'Expense');
|
||||
// },
|
||||
// },
|
||||
/**
|
||||
* Expense may belongs to matched bank transaction.
|
||||
*/
|
||||
matchedBankTransaction: {
|
||||
relation: Model.HasManyRelation,
|
||||
modelClass: MatchedBankTransaction,
|
||||
join: {
|
||||
from: 'expenses_transactions.id',
|
||||
to: 'matched_bank_transactions.referenceId',
|
||||
},
|
||||
filter(query) {
|
||||
query.where('reference_type', 'Expense');
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -123,7 +123,7 @@ export class ManualJournal extends TenantBaseModel {
|
||||
const { AccountTransaction } = require('../../Accounts/models/AccountTransaction.model');
|
||||
const { ManualJournalEntry } = require('./ManualJournalEntry');
|
||||
const { Document } = require('../../ChromiumlyTenancy/models/Document');
|
||||
// const { MatchedBankTransaction } = require('models/MatchedBankTransaction');
|
||||
const { MatchedBankTransaction } = require('../../BankingMatching/models/MatchedBankTransaction');
|
||||
|
||||
return {
|
||||
entries: {
|
||||
@@ -171,17 +171,17 @@ export class ManualJournal extends TenantBaseModel {
|
||||
/**
|
||||
* Manual journal may belongs to matched bank transaction.
|
||||
*/
|
||||
// matchedBankTransaction: {
|
||||
// relation: Model.BelongsToOneRelation,
|
||||
// modelClass: MatchedBankTransaction,
|
||||
// join: {
|
||||
// from: 'manual_journals.id',
|
||||
// to: 'matched_bank_transactions.referenceId',
|
||||
// },
|
||||
// filter(query) {
|
||||
// query.where('reference_type', 'ManualJournal');
|
||||
// },
|
||||
// },
|
||||
matchedBankTransaction: {
|
||||
relation: Model.BelongsToOneRelation,
|
||||
modelClass: MatchedBankTransaction,
|
||||
join: {
|
||||
from: 'manual_journals.id',
|
||||
to: 'matched_bank_transactions.referenceId',
|
||||
},
|
||||
filter(query) {
|
||||
query.where('reference_type', 'ManualJournal');
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -512,7 +512,7 @@ export class SaleInvoice extends TenantBaseModel{
|
||||
TaxRateTransaction,
|
||||
} = require('../../TaxRates/models/TaxRateTransaction.model');
|
||||
const { Document } = require('../../ChromiumlyTenancy/models/Document');
|
||||
// const { MatchedBankTransaction } = require('models/MatchedBankTransaction');
|
||||
const { MatchedBankTransaction } = require('../../BankingMatching/models/MatchedBankTransaction');
|
||||
const {
|
||||
TransactionPaymentServiceEntry,
|
||||
} = require('../../PaymentServices/models/TransactionPaymentServiceEntry.model');
|
||||
@@ -667,17 +667,17 @@ export class SaleInvoice extends TenantBaseModel{
|
||||
/**
|
||||
* Sale invocie may belongs to matched bank transaction.
|
||||
*/
|
||||
// matchedBankTransaction: {
|
||||
// relation: Model.HasManyRelation,
|
||||
// modelClass: MatchedBankTransaction,
|
||||
// join: {
|
||||
// from: 'sales_invoices.id',
|
||||
// to: 'matched_bank_transactions.referenceId',
|
||||
// },
|
||||
// filter(query) {
|
||||
// query.where('reference_type', 'SaleInvoice');
|
||||
// },
|
||||
// },
|
||||
matchedBankTransaction: {
|
||||
relation: Model.HasManyRelation,
|
||||
modelClass: MatchedBankTransaction,
|
||||
join: {
|
||||
from: 'sales_invoices.id',
|
||||
to: 'matched_bank_transactions.referenceId',
|
||||
},
|
||||
filter(query) {
|
||||
query.where('reference_type', 'SaleInvoice');
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Sale invoice may belongs to payment methods entries.
|
||||
|
||||
@@ -5,6 +5,7 @@ import { EnsureTenantIsSeededGuard } from "./EnsureTenantIsSeeded.guards";
|
||||
import { APP_GUARD } from "@nestjs/core";
|
||||
import { TenancyContext } from "./TenancyContext.service";
|
||||
import { TenantController } from "./Tenant.controller";
|
||||
import { TenancyInitializeModelsGuard } from "./TenancyInitializeModels.guard";
|
||||
|
||||
|
||||
@Module({
|
||||
@@ -23,6 +24,10 @@ import { TenantController } from "./Tenant.controller";
|
||||
{
|
||||
provide: APP_GUARD,
|
||||
useClass: EnsureTenantIsSeededGuard
|
||||
},
|
||||
{
|
||||
provide: APP_GUARD,
|
||||
useClass: TenancyInitializeModelsGuard
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import {
|
||||
CanActivate,
|
||||
ExecutionContext,
|
||||
Inject,
|
||||
Injectable,
|
||||
SetMetadata,
|
||||
} from '@nestjs/common';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
import { IS_PUBLIC_ROUTE } from '../Auth/Auth.constants';
|
||||
import { TENANT_MODELS_INIT } from './TenantModelsInitialize.module';
|
||||
|
||||
export const IGNORE_TENANT_MODELS_INITIALIZE =
|
||||
'IGNORE_TENANT_MODELS_INITIALIZE';
|
||||
export const IgnoreTenantModelsInitialize = () =>
|
||||
SetMetadata(IGNORE_TENANT_MODELS_INITIALIZE, true);
|
||||
|
||||
@Injectable()
|
||||
export class TenancyInitializeModelsGuard implements CanActivate {
|
||||
constructor(
|
||||
@Inject(TENANT_MODELS_INIT)
|
||||
private readonly tenantModelsInit: () => Promise<boolean>,
|
||||
private reflector: Reflector,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Initialize tenant models if the route is decorated with TriggerTenantModelsInitialize.
|
||||
* @param {ExecutionContext} context
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const isPublic = this.reflector.getAllAndOverride<boolean>(
|
||||
IS_PUBLIC_ROUTE,
|
||||
[context.getHandler(), context.getClass()],
|
||||
);
|
||||
// Skip initialization for public routes
|
||||
if (isPublic) {
|
||||
return true;
|
||||
}
|
||||
const shouldIgnoreInitialization =
|
||||
this.reflector.getAllAndOverride<boolean>(
|
||||
IGNORE_TENANT_MODELS_INITIALIZE,
|
||||
[context.getHandler(), context.getClass()],
|
||||
);
|
||||
// Initialize models unless the route is decorated with IgnoreTenantModelsInitialize
|
||||
if (!shouldIgnoreInitialization) {
|
||||
try {
|
||||
await this.tenantModelsInit();
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize tenant models:', error);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import { ContextIdFactory, ModuleRef } from '@nestjs/core';
|
||||
import { ClsModule } from 'nestjs-cls';
|
||||
import { Global, Module } from '@nestjs/common';
|
||||
import { Knex } from 'knex';
|
||||
import { initialize } from 'objection';
|
||||
import { TENANCY_DB_CONNECTION } from './TenancyDB/TenancyDB.constants';
|
||||
|
||||
const RegisteredModels = [
|
||||
'SaleInvoice',
|
||||
'Bill',
|
||||
'Expense',
|
||||
'BankTransaction',
|
||||
'MatchedBankTransaction',
|
||||
'ManualJournalEntry',
|
||||
'Account',
|
||||
'UncategorizedBankTransaction',
|
||||
'RecognizedBankTransaction',
|
||||
];
|
||||
export const TENANT_MODELS_INIT = 'TENANT_MODELS_INIT';
|
||||
|
||||
const provider = ClsModule.forFeatureAsync({
|
||||
provide: TENANT_MODELS_INIT,
|
||||
inject: [TENANCY_DB_CONNECTION, ModuleRef],
|
||||
useFactory: (tenantKnex: () => Knex, moduleRef: ModuleRef) => async () => {
|
||||
const knexInstance = tenantKnex();
|
||||
const contextId = ContextIdFactory.create();
|
||||
const models = await Promise.all(
|
||||
RegisteredModels.map((model) => {
|
||||
return moduleRef.resolve(model, contextId, { strict: false });
|
||||
}),
|
||||
);
|
||||
const modelsInstances = models.map((model) => model());
|
||||
|
||||
if (modelsInstances.length > 0) {
|
||||
try {
|
||||
// Initialize all models with the knex instance
|
||||
await initialize(knexInstance, modelsInstances);
|
||||
} catch (error) {
|
||||
console.error('Error initializing models:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
strict: true,
|
||||
type: 'function',
|
||||
});
|
||||
|
||||
@Module({
|
||||
imports: [provider],
|
||||
exports: [provider],
|
||||
})
|
||||
@Global()
|
||||
export class TenantModelsInitializeModule {}
|
||||
@@ -248,7 +248,7 @@ export function useCategorizeTransaction(props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
(values) => apiRequest.post(`banking/transactions/categorize`, values),
|
||||
(values) => apiRequest.post(`banking/categorize`, values),
|
||||
{
|
||||
onSuccess: (res, id) => {
|
||||
// Invalidate queries.
|
||||
@@ -274,7 +274,7 @@ export function useUncategorizeTransaction(props) {
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(
|
||||
(id: number) => apiRequest.post(`banking/transactions/${id}/uncategorize`),
|
||||
(id: number) => apiRequest.delete(`banking/categorize/${id}`),
|
||||
{
|
||||
onSuccess: (res, id) => {
|
||||
// Invalidate queries.
|
||||
|
||||
Reference in New Issue
Block a user