refactor: bank rules e2e test cases

This commit is contained in:
Ahmed Bouhuolia
2025-01-06 18:10:24 +02:00
parent 2bf58d9cb4
commit 385d84d654
27 changed files with 357 additions and 91 deletions

View File

@@ -217,7 +217,7 @@ export class Account extends TenantModel {
// const ExpenseEntry = require('models/ExpenseCategory');
// const ItemEntry = require('models/ItemEntry');
// const UncategorizedTransaction = require('models/UncategorizedCashflowTransaction');
const { PlaidItem } = require('../../Banking/models/PlaidItem.model');
const { PlaidItem } = require('../../BankingPlaid/models/PlaidItem');
return {
/**

View File

@@ -140,10 +140,10 @@ import { BankingTransactionsModule } from '../BankingTransactions/BankingTransac
LedgerModule,
BankAccountsModule,
BankRulesModule,
BankingTransactionsExcludeModule,
BankingTransactionsRegonizeModule,
BankingMatchingModule,
BankingTransactionsModule,
// BankingTransactionsExcludeModule,
// BankingTransactionsRegonizeModule,
// BankingMatchingModule,
],
controllers: [AppController],
providers: [

View File

@@ -9,15 +9,18 @@ import {
} from '@nestjs/common';
import { BankRulesApplication } from './BankRulesApplication';
import { ICreateBankRuleDTO, IEditBankRuleDTO } from './types';
import { PublicRoute } from '../Auth/Jwt.guard';
import { BankRule } from './models/BankRule';
@Controller('banking/rules')
@PublicRoute()
export class BankRulesController {
constructor(private readonly bankRulesApplication: BankRulesApplication) {}
@Post()
async createBankRule(
@Body() createRuleDTO: ICreateBankRuleDTO,
): Promise<void> {
): Promise<BankRule> {
return this.bankRulesApplication.createBankRule(createRuleDTO);
}

View File

@@ -5,6 +5,7 @@ import { EditBankRuleService } from './commands/EditBankRule.service';
import { GetBankRuleService } from './queries/GetBankRule.service';
import { GetBankRulesService } from './queries/GetBankRules.service';
import { ICreateBankRuleDTO, IEditBankRuleDTO } from './types';
import { BankRule } from './models/BankRule';
@Injectable()
export class BankRulesApplication {
@@ -21,7 +22,9 @@ export class BankRulesApplication {
* @param {ICreateBankRuleDTO} createRuleDTO - Bank rule data.
* @returns {Promise<void>}
*/
public createBankRule(createRuleDTO: ICreateBankRuleDTO): Promise<void> {
public createBankRule(
createRuleDTO: ICreateBankRuleDTO,
): Promise<BankRule> {
return this.createBankRuleService.createBankRule(createRuleDTO);
}

View File

@@ -22,9 +22,8 @@ export class CreateBankRuleService {
/**
* Transforms the DTO to model.
* @param {ICreateBankRuleDTO} createDTO
* @returns
*/
private transformDTO(createDTO: ICreateBankRuleDTO): Partial<BankRule> {
private transformDTO(createDTO: ICreateBankRuleDTO) {
return {
...createDTO,
};
@@ -33,19 +32,21 @@ export class CreateBankRuleService {
/**
* Creates a new bank rule.
* @param {ICreateBankRuleDTO} createRuleDTO
* @returns {Promise<void>}
* @returns {Promise<BankRule>}
*/
public async createBankRule(createRuleDTO: ICreateBankRuleDTO): Promise<void> {
public async createBankRule(
createRuleDTO: ICreateBankRuleDTO,
): Promise<BankRule> {
const transformDTO = this.transformDTO(createRuleDTO);
await this.uow.withTransaction(async (trx: Knex.Transaction) => {
return this.uow.withTransaction(async (trx: Knex.Transaction) => {
// Triggers `onBankRuleCreating` event.
await this.eventPublisher.emitAsync(events.bankRules.onCreating, {
createRuleDTO,
trx,
} as IBankRuleEventCreatingPayload);
const bankRule = await this.bankRuleModel.query(trx).upsertGraph({
const bankRule = await this.bankRuleModel.query(trx).upsertGraphAndFetch({
...transformDTO,
});
// Triggers `onBankRuleCreated` event.

View File

@@ -20,7 +20,8 @@ export class GetBankRuleService {
const bankRule = await this.bankRuleModel
.query()
.findById(ruleId)
.withGraphFetched('conditions');
.withGraphFetched('conditions')
.withGraphFetched('assignAccount');
return this.transformer.transform(
bankRule,

View File

@@ -80,10 +80,9 @@ export class PlaidSyncDb {
item: PlaidItem,
trx?: Knex.Transaction,
): Promise<void> {
const transformToPlaidAccounts = transformPlaidAccountToCreateAccount(
item,
institution,
);
const transformToPlaidAccounts = R.curry(
transformPlaidAccountToCreateAccount,
)(item, institution);
const accountCreateDTOs = R.map(transformToPlaidAccounts)(plaidAccounts);
await bluebird.map(
@@ -112,7 +111,7 @@ export class PlaidSyncDb {
.throwIfNotFound();
// Transformes the Plaid transactions to cashflow create DTOs.
const transformTransaction = transformPlaidTrxsToCashflowCreate(
const transformTransaction = R.curry(transformPlaidTrxsToCashflowCreate)(
cashflowAccount.id,
);
const uncategorizedTransDTOs =

View File

@@ -16,7 +16,7 @@ import { CreateUncategorizedTransactionDTO } from '../BankingCategorize/types/Ba
* @returns {string}
*/
const getAccountTypeFromPlaidAccountType = (
plaidAccountType: PlaidAccountType
plaidAccountType: PlaidAccountType,
) => {
if (plaidAccountType === PlaidAccountType.Credit) {
return ACCOUNT_TYPE.CREDIT_CARD;
@@ -31,26 +31,24 @@ const getAccountTypeFromPlaidAccountType = (
* @param {PlaidAccount} plaidAccount - Plaid account.
* @returns {IAccountCreateDTO}
*/
export const transformPlaidAccountToCreateAccount = R.curry(
(
item: PlaidItem,
institution: PlaidInstitution,
plaidAccount: PlaidAccount
): IAccountCreateDTO => {
return {
name: `${institution.name} - ${plaidAccount.name}`,
code: '',
description: plaidAccount.official_name,
currencyCode: plaidAccount.balances.iso_currency_code,
accountType: getAccountTypeFromPlaidAccountType(plaidAccount.type),
active: true,
bankBalance: plaidAccount.balances.current,
accountMask: plaidAccount.mask,
plaidAccountId: plaidAccount.account_id,
plaidItemId: item.item_id,
};
}
);
export const transformPlaidAccountToCreateAccount = (
item: PlaidItem,
institution: PlaidInstitution,
plaidAccount: PlaidAccount,
): IAccountCreateDTO => {
return {
name: `${institution.name} - ${plaidAccount.name}`,
code: '',
description: plaidAccount.official_name,
currencyCode: plaidAccount.balances.iso_currency_code,
accountType: getAccountTypeFromPlaidAccountType(plaidAccount.type),
active: true,
bankBalance: plaidAccount.balances.current,
accountMask: plaidAccount.mask,
plaidAccountId: plaidAccount.account_id,
plaidItemId: item.item_id,
};
};
/**
* Transformes the plaid transaction to cashflow create DTO.
@@ -59,10 +57,9 @@ export const transformPlaidAccountToCreateAccount = R.curry(
* @param {PlaidTransaction} plaidTranasction - Plaid transaction.
* @returns {CreateUncategorizedTransactionDTO}
*/
export const transformPlaidTrxsToCashflowCreate = R.curry(
(
export const transformPlaidTrxsToCashflowCreate = (
cashflowAccountId: number,
plaidTranasction: PlaidTransactionBase
plaidTranasction: PlaidTransactionBase,
): CreateUncategorizedTransactionDTO => {
return {
date: plaidTranasction.date,
@@ -81,5 +78,4 @@ export const transformPlaidTrxsToCashflowCreate = R.curry(
pending: plaidTranasction.pending,
pendingPlaidTransactionId: plaidTranasction.pending_transaction_id,
};
}
);
};

View File

@@ -34,9 +34,11 @@ export class RecognizedBankTransaction extends BaseModel {
* Relationship mapping.
*/
static get relationMappings() {
const UncategorizedCashflowTransaction = require('./UncategorizedCashflowTransaction');
const Account = require('./Account');
const { BankRule } = require('./BankRule');
const {
UncategorizedBankTransaction,
} = require('../../BankingTransactions/models/UncategorizedBankTransaction');
const { Account } = require('../../Accounts/models/Account.model');
const { BankRule } = require('../../BankRules/models/BankRule');
return {
/**
@@ -44,7 +46,7 @@ export class RecognizedBankTransaction extends BaseModel {
*/
uncategorizedTransactions: {
relation: Model.HasManyRelation,
modelClass: UncategorizedCashflowTransaction.default,
modelClass: UncategorizedBankTransaction,
join: {
from: 'recognized_bank_transactions.uncategorizedTransactionId',
to: 'uncategorized_cashflow_transactions.id',
@@ -56,7 +58,7 @@ export class RecognizedBankTransaction extends BaseModel {
*/
assignAccount: {
relation: Model.BelongsToOneRelation,
modelClass: Account.default,
modelClass: Account,
join: {
from: 'recognized_bank_transactions.assignedAccountId',
to: 'accounts.id',

View File

@@ -1,8 +1,10 @@
import { Body, Controller, Delete, Get, Param, Post } from '@nestjs/common';
import { BankingTransactionsApplication } from './BankingTransactionsApplication.service';
import { ICashflowNewCommandDTO } from './types/BankingTransactions.types';
import { PublicRoute } from '../Auth/Jwt.guard';
@Controller('banking/transactions')
@PublicRoute()
export class BankingTransactionsController {
constructor(
private readonly bankingTransactionsApplication: BankingTransactionsApplication,

View File

@@ -7,7 +7,10 @@ import { Model } from 'objection';
// import { CASHFLOW_DIRECTION } from '@/services/Cashflow/constants';
// import { getCashflowTransactionFormattedType } from '@/utils/transactions-types';
import { BaseModel } from '@/models/Model';
import { getCashflowAccountTransactionsTypes, getCashflowTransactionType } from '../utils';
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';
@@ -159,10 +162,14 @@ export class BankTransaction extends BaseModel {
* Relationship mapping.
*/
static get relationMappings() {
const CashflowTransactionLine = require('models/CashflowTransactionLine');
const AccountTransaction = require('models/AccountTransaction');
const Account = require('models/Account');
const { MatchedBankTransaction } = require('models/MatchedBankTransaction');
const { BankTransactionLine } = require('./BankTransactionLine');
const {
AccountTransaction,
} = require('../../Accounts/models/AccountTransaction.model');
const { Account } = require('../../Accounts/models/Account.model');
const {
MatchedBankTransaction,
} = require('../../BankingMatching/models/MatchedBankTransaction');
return {
/**
@@ -170,7 +177,7 @@ export class BankTransaction extends BaseModel {
*/
entries: {
relation: Model.HasManyRelation,
modelClass: CashflowTransactionLine.default,
modelClass: BankTransactionLine,
join: {
from: 'cashflow_transactions.id',
to: 'cashflow_transaction_lines.cashflowTransactionId',
@@ -185,7 +192,7 @@ export class BankTransaction extends BaseModel {
*/
transactions: {
relation: Model.HasManyRelation,
modelClass: AccountTransaction.default,
modelClass: AccountTransaction,
join: {
from: 'cashflow_transactions.id',
to: 'accounts_transactions.referenceId',
@@ -200,7 +207,7 @@ export class BankTransaction extends BaseModel {
*/
cashflowAccount: {
relation: Model.BelongsToOneRelation,
modelClass: Account.default,
modelClass: Account,
join: {
from: 'cashflow_transactions.cashflowAccountId',
to: 'accounts.id',
@@ -212,7 +219,7 @@ export class BankTransaction extends BaseModel {
*/
creditAccount: {
relation: Model.BelongsToOneRelation,
modelClass: Account.default,
modelClass: Account,
join: {
from: 'cashflow_transactions.creditAccountId',
to: 'accounts.id',

View File

@@ -22,12 +22,12 @@ export class BankTransactionLine extends BaseModel{
* Relationship mapping.
*/
static get relationMappings() {
const Account = require('models/Account');
const { Account } = require('../../Accounts/models/Account.model');
return {
cashflowAccount: {
relation: Model.BelongsToOneRelation,
modelClass: Account.default,
modelClass: Account,
join: {
from: 'cashflow_transaction_lines.cashflowAccountId',
to: 'accounts.id',
@@ -35,7 +35,7 @@ export class BankTransactionLine extends BaseModel{
},
creditAccount: {
relation: Model.BelongsToOneRelation,
modelClass: Account.default,
modelClass: Account,
join: {
from: 'cashflow_transaction_lines.creditAccountId',
to: 'accounts.id',

View File

@@ -195,11 +195,11 @@ export class UncategorizedBankTransaction extends BaseModel {
* Relationship mapping.
*/
static get relationMappings() {
const Account = require('models/Account');
const { Account } = require('../../Accounts/models/Account.model');
const {
RecognizedBankTransaction,
} = require('models/RecognizedBankTransaction');
const { MatchedBankTransaction } = require('models/MatchedBankTransaction');
} = require('../../BankingTranasctionsRegonize/models/RecognizedBankTransaction');
const { MatchedBankTransaction } = require('../../BankingMatching/models/MatchedBankTransaction');
return {
/**
@@ -207,7 +207,7 @@ export class UncategorizedBankTransaction extends BaseModel {
*/
account: {
relation: Model.BelongsToOneRelation,
modelClass: Account.default,
modelClass: Account,
join: {
from: 'uncategorized_cashflow_transactions.accountId',
to: 'accounts.id',

View File

@@ -32,7 +32,7 @@ export class BankTransactionTransformer extends Transformer {
* @returns {string}
*/
protected transactionTypeFormatted = (transaction) => {
return this.context.i18n.t(transaction.transactionTypeFormatted);
return this.context.i18n.t(transaction.transactionType);
};
/**

View File

@@ -1,8 +1,8 @@
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
import { initialize } from 'objection';
import { UncategorizedBankTransaction } from '../models/UncategorizedBankTransaction';
import { Inject, Injectable } from '@nestjs/common';
import { UncategorizedTransactionTransformer } from '../../BankingCategorize/commands/UncategorizedTransaction.transformer';
import { IGetUncategorizedTransactionsQuery } from '../types/BankingTransactions.types';
@Injectable()
export class GetUncategorizedTransactions {

View File

@@ -9,13 +9,13 @@ export interface IBillPaymentEntryDTO {
export interface IBillPaymentDTO {
vendorId: number;
amount: number;
amount?: number;
paymentAccountId: number;
paymentNumber?: string;
paymentDate: Date | string;
exchangeRate?: number;
statement: string;
reference: string;
statement?: string;
reference?: string;
entries: IBillPaymentEntryDTO[];
branchId?: number;
attachments?: AttachmentLinkDTO[];

View File

@@ -8,11 +8,11 @@ import * as R from 'ramda';
// import CustomViewBaseModel from './CustomViewBaseModel';
// import { DEFAULT_VIEWS } from '@/services/Purchases/Bills/constants';
// import ModelSearchable from './ModelSearchable';
import { BaseModel } from '@/models/Model';
import { BaseModel, PaginationQueryBuilderType } 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';
import type { Knex, QueryBuilder } from 'knex';
export class Bill extends BaseModel {
public amount: number;
@@ -482,6 +482,7 @@ export class Bill extends BaseModel {
const { Branch } = require('../../Branches/models/Branch.model');
const { Warehouse } = require('../../Warehouses/models/Warehouse.model');
const { TaxRateModel } = require('../../TaxRates/models/TaxRate.model');
const { TaxRateTransaction } = require('../../TaxRates/models/TaxRateTransaction.model');
const { Document } = require('../../ChromiumlyTenancy/models/Document');
// const { MatchedBankTransaction } = require('models/MatchedBankTransaction');
@@ -549,7 +550,7 @@ export class Bill extends BaseModel {
*/
taxes: {
relation: Model.HasManyRelation,
modelClass: TaxRateModel,
modelClass: TaxRateTransaction,
join: {
from: 'bills.id',
to: 'tax_rate_transactions.referenceId',
@@ -616,8 +617,13 @@ export class Bill extends BaseModel {
return notFoundBillsIds;
}
static changePaymentAmount(billId, amount, trx: Knex.Transaction) {
static changePaymentAmount(
billId: number,
amount: number,
trx: Knex.Transaction,
) {
const changeMethod = amount > 0 ? 'increment' : 'decrement';
return this.query(trx)
.where('id', billId)
[changeMethod]('payment_amount', Math.abs(amount));

View File

@@ -1,4 +1,5 @@
import { IModelMetaField2 } from "@/interfaces/Model";
import { Import } from "./models/Import";
export interface ImportMappingAttr {
from: string;

View File

@@ -0,0 +1,87 @@
import { Model, ModelObject } from 'objection';
// import SystemModel from './SystemModel';
import { BaseModel } from '@/models/Model';
export class Import extends BaseModel {
resource: string;
tenantId: number;
mapping!: string;
columns!: string;
params!: string;
/**
* Table name.
*/
static get tableName() {
return 'imports';
}
/**
* Virtual attributes.
*/
static get virtualAttributes() {
return ['mappingParsed'];
}
/**
* Timestamps columns.
*/
get timestamps() {
return ['createdAt', 'updatedAt'];
}
/**
* Detarmines whether the import is mapped.
* @returns {boolean}
*/
public get isMapped() {
return Boolean(this.mapping);
}
public get columnsParsed() {
try {
return JSON.parse(this.columns);
} catch {
return [];
}
}
public get paramsParsed() {
try {
return JSON.parse(this.params);
} catch {
return [];
}
}
public get mappingParsed() {
try {
return JSON.parse(this.mapping);
} catch {
return [];
}
}
/**
* Relationship mapping.
*/
static get relationMappings() {
const Tenant = require('system/models/Tenant');
return {
/**
* System user may belongs to tenant model.
*/
tenant: {
relation: Model.BelongsToOneRelation,
modelClass: Tenant.default,
join: {
from: 'imports.tenantId',
to: 'tenants.id',
},
},
};
}
}
export type ImportShape = ModelObject<Import>;

View File

@@ -213,7 +213,7 @@ export class ItemsEntriesService {
/**
* Sets the cost/sell accounts to the invoice entries.
*/
public setItemsEntriesDefaultAccounts = async (entries: ItemEntry[]) => {
public setItemsEntriesDefaultAccounts = async (entries: IItemEntryDTO[]) => {
const entriesItemsIds = entries.map((e) => e.itemId);
const items = await this.itemModel.query().whereIn('id', entriesItemsIds);

View File

@@ -5,12 +5,12 @@ import { PaymentReceived } from '../models/PaymentReceived';
export interface IPaymentReceivedCreateDTO {
customerId: number;
paymentDate: Date | string;
amount: number;
exchangeRate: number;
referenceNo: string;
amount?: number;
exchangeRate?: number;
referenceNo?: string;
depositAccountId: number;
paymentReceiveNo?: string;
statement: string;
statement?: string;
entries: IPaymentReceivedEntryDTO[];
branchId?: number;

View File

@@ -7,6 +7,9 @@ export interface IItemEntryDTO {
landedCost?: boolean;
warehouseId?: number;
sellAccountId?: number;
costAccountId?: number;
projectRefId?: number;
projectRefType?: ProjectLinkRefType;
projectRefInvoicedAmount?: number;