From 385d84d6542bab70a861152f5f14fbf707e42c29 Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Mon, 6 Jan 2025 18:10:24 +0200 Subject: [PATCH] refactor: bank rules e2e test cases --- packages/server-nest/src/models/Model.ts | 25 +++--- .../modules/Accounts/models/Account.model.ts | 2 +- .../server-nest/src/modules/App/App.module.ts | 6 +- .../modules/BankRules/BankRules.controller.ts | 5 +- .../modules/BankRules/BankRulesApplication.ts | 5 +- .../commands/CreateBankRule.service.ts | 13 +-- .../BankRules/queries/GetBankRule.service.ts | 3 +- .../BankingPlaid/command/PlaidSyncDB.ts | 9 +- .../src/modules/BankingPlaid/utils.ts | 48 +++++----- .../models/RecognizedBankTransaction.ts | 12 +-- .../BankingTransactions.controller.ts | 2 + .../models/BankTransaction.ts | 25 ++++-- .../models/BankTransactionLine.ts | 6 +- .../models/UncategorizedBankTransaction.ts | 8 +- .../queries/BankTransactionTransformer.ts | 2 +- .../queries/GetUncategorizedTransactions.ts | 2 +- .../BillPayments/types/BillPayments.types.ts | 6 +- .../src/modules/Bills/models/Bill.ts | 14 ++- .../src/modules/Import/interfaces.ts | 1 + .../src/modules/Import/models/Import.ts | 87 ++++++++++++++++++ .../src/modules/Items/ItemsEntries.service.ts | 2 +- .../types/PaymentReceived.types.ts | 8 +- .../TransactionItemEntry/ItemEntry.types.ts | 3 + .../src/utils/transform-to-map-by.ts | 2 +- .../server-nest/test/bank-rules.e2e-spec.ts | 88 +++++++++++++++++++ .../test/banking-transactions.e2e-spec.ts | 61 +++++++++++++ .../src/services/Banking/Rules/GetBankRule.ts | 3 +- 27 files changed, 357 insertions(+), 91 deletions(-) create mode 100644 packages/server-nest/src/modules/Import/models/Import.ts create mode 100644 packages/server-nest/test/bank-rules.e2e-spec.ts create mode 100644 packages/server-nest/test/banking-transactions.e2e-spec.ts diff --git a/packages/server-nest/src/models/Model.ts b/packages/server-nest/src/models/Model.ts index 3d1208539..45d09b2f3 100644 --- a/packages/server-nest/src/models/Model.ts +++ b/packages/server-nest/src/models/Model.ts @@ -1,5 +1,4 @@ -import { QueryBuilder } from 'objection'; -import Objection, { Model, Page } from 'objection'; +import { QueryBuilder, Model } from 'objection'; interface PaginationResult { results: M[]; @@ -10,13 +9,19 @@ interface PaginationResult { }; } -class PaginationQueryBuilder< - M extends Model, - R = M[], -> extends Model.QueryBuilder { - pagination(page: number, pageSize: number): QueryBuilder> { - // @ts-ignore - return super.page(page, pageSize).runAfter(({ results, total }) => { +export type PaginationQueryBuilderType = QueryBuilder< + M, + PaginationResult +>; + +class PaginationQueryBuilder extends QueryBuilder< + M, + R +> { + pagination(page: number, pageSize: number): PaginationQueryBuilderType { + const query = super.page(page, pageSize); + + return query.runAfter(({ results, total }) => { return { results, pagination: { @@ -25,7 +30,7 @@ class PaginationQueryBuilder< pageSize, }, }; - }); + }) as unknown as PaginationQueryBuilderType; } } diff --git a/packages/server-nest/src/modules/Accounts/models/Account.model.ts b/packages/server-nest/src/modules/Accounts/models/Account.model.ts index 103e5cd1b..c4287d3eb 100644 --- a/packages/server-nest/src/modules/Accounts/models/Account.model.ts +++ b/packages/server-nest/src/modules/Accounts/models/Account.model.ts @@ -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 { /** diff --git a/packages/server-nest/src/modules/App/App.module.ts b/packages/server-nest/src/modules/App/App.module.ts index e7f59a105..6dfb72f4f 100644 --- a/packages/server-nest/src/modules/App/App.module.ts +++ b/packages/server-nest/src/modules/App/App.module.ts @@ -140,10 +140,10 @@ import { BankingTransactionsModule } from '../BankingTransactions/BankingTransac LedgerModule, BankAccountsModule, BankRulesModule, - BankingTransactionsExcludeModule, - BankingTransactionsRegonizeModule, - BankingMatchingModule, BankingTransactionsModule, + // BankingTransactionsExcludeModule, + // BankingTransactionsRegonizeModule, + // BankingMatchingModule, ], controllers: [AppController], providers: [ diff --git a/packages/server-nest/src/modules/BankRules/BankRules.controller.ts b/packages/server-nest/src/modules/BankRules/BankRules.controller.ts index 1979f19f2..4cd92c6f2 100644 --- a/packages/server-nest/src/modules/BankRules/BankRules.controller.ts +++ b/packages/server-nest/src/modules/BankRules/BankRules.controller.ts @@ -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 { + ): Promise { return this.bankRulesApplication.createBankRule(createRuleDTO); } diff --git a/packages/server-nest/src/modules/BankRules/BankRulesApplication.ts b/packages/server-nest/src/modules/BankRules/BankRulesApplication.ts index 5db9fa678..44242013a 100644 --- a/packages/server-nest/src/modules/BankRules/BankRulesApplication.ts +++ b/packages/server-nest/src/modules/BankRules/BankRulesApplication.ts @@ -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} */ - public createBankRule(createRuleDTO: ICreateBankRuleDTO): Promise { + public createBankRule( + createRuleDTO: ICreateBankRuleDTO, + ): Promise { return this.createBankRuleService.createBankRule(createRuleDTO); } diff --git a/packages/server-nest/src/modules/BankRules/commands/CreateBankRule.service.ts b/packages/server-nest/src/modules/BankRules/commands/CreateBankRule.service.ts index 04adcbd83..f770545e9 100644 --- a/packages/server-nest/src/modules/BankRules/commands/CreateBankRule.service.ts +++ b/packages/server-nest/src/modules/BankRules/commands/CreateBankRule.service.ts @@ -22,9 +22,8 @@ export class CreateBankRuleService { /** * Transforms the DTO to model. * @param {ICreateBankRuleDTO} createDTO - * @returns */ - private transformDTO(createDTO: ICreateBankRuleDTO): Partial { + private transformDTO(createDTO: ICreateBankRuleDTO) { return { ...createDTO, }; @@ -33,19 +32,21 @@ export class CreateBankRuleService { /** * Creates a new bank rule. * @param {ICreateBankRuleDTO} createRuleDTO - * @returns {Promise} + * @returns {Promise} */ - public async createBankRule(createRuleDTO: ICreateBankRuleDTO): Promise { + public async createBankRule( + createRuleDTO: ICreateBankRuleDTO, + ): Promise { 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. diff --git a/packages/server-nest/src/modules/BankRules/queries/GetBankRule.service.ts b/packages/server-nest/src/modules/BankRules/queries/GetBankRule.service.ts index f31344833..49181e729 100644 --- a/packages/server-nest/src/modules/BankRules/queries/GetBankRule.service.ts +++ b/packages/server-nest/src/modules/BankRules/queries/GetBankRule.service.ts @@ -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, diff --git a/packages/server-nest/src/modules/BankingPlaid/command/PlaidSyncDB.ts b/packages/server-nest/src/modules/BankingPlaid/command/PlaidSyncDB.ts index 50fa0f19c..26dab195c 100644 --- a/packages/server-nest/src/modules/BankingPlaid/command/PlaidSyncDB.ts +++ b/packages/server-nest/src/modules/BankingPlaid/command/PlaidSyncDB.ts @@ -80,10 +80,9 @@ export class PlaidSyncDb { item: PlaidItem, trx?: Knex.Transaction, ): Promise { - 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 = diff --git a/packages/server-nest/src/modules/BankingPlaid/utils.ts b/packages/server-nest/src/modules/BankingPlaid/utils.ts index 12b6addf2..1b468ebf2 100644 --- a/packages/server-nest/src/modules/BankingPlaid/utils.ts +++ b/packages/server-nest/src/modules/BankingPlaid/utils.ts @@ -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, }; - } -); + }; diff --git a/packages/server-nest/src/modules/BankingTranasctionsRegonize/models/RecognizedBankTransaction.ts b/packages/server-nest/src/modules/BankingTranasctionsRegonize/models/RecognizedBankTransaction.ts index 308cdbe51..cf4381332 100644 --- a/packages/server-nest/src/modules/BankingTranasctionsRegonize/models/RecognizedBankTransaction.ts +++ b/packages/server-nest/src/modules/BankingTranasctionsRegonize/models/RecognizedBankTransaction.ts @@ -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', diff --git a/packages/server-nest/src/modules/BankingTransactions/BankingTransactions.controller.ts b/packages/server-nest/src/modules/BankingTransactions/BankingTransactions.controller.ts index 0fd6e1e5d..09a527a10 100644 --- a/packages/server-nest/src/modules/BankingTransactions/BankingTransactions.controller.ts +++ b/packages/server-nest/src/modules/BankingTransactions/BankingTransactions.controller.ts @@ -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, diff --git a/packages/server-nest/src/modules/BankingTransactions/models/BankTransaction.ts b/packages/server-nest/src/modules/BankingTransactions/models/BankTransaction.ts index 46e9bdef5..168e4c907 100644 --- a/packages/server-nest/src/modules/BankingTransactions/models/BankTransaction.ts +++ b/packages/server-nest/src/modules/BankingTransactions/models/BankTransaction.ts @@ -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', diff --git a/packages/server-nest/src/modules/BankingTransactions/models/BankTransactionLine.ts b/packages/server-nest/src/modules/BankingTransactions/models/BankTransactionLine.ts index d1538f361..70b69b8ab 100644 --- a/packages/server-nest/src/modules/BankingTransactions/models/BankTransactionLine.ts +++ b/packages/server-nest/src/modules/BankingTransactions/models/BankTransactionLine.ts @@ -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', diff --git a/packages/server-nest/src/modules/BankingTransactions/models/UncategorizedBankTransaction.ts b/packages/server-nest/src/modules/BankingTransactions/models/UncategorizedBankTransaction.ts index 73fdcee63..cfcb15fef 100644 --- a/packages/server-nest/src/modules/BankingTransactions/models/UncategorizedBankTransaction.ts +++ b/packages/server-nest/src/modules/BankingTransactions/models/UncategorizedBankTransaction.ts @@ -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', diff --git a/packages/server-nest/src/modules/BankingTransactions/queries/BankTransactionTransformer.ts b/packages/server-nest/src/modules/BankingTransactions/queries/BankTransactionTransformer.ts index 592174953..5b7d6d945 100644 --- a/packages/server-nest/src/modules/BankingTransactions/queries/BankTransactionTransformer.ts +++ b/packages/server-nest/src/modules/BankingTransactions/queries/BankTransactionTransformer.ts @@ -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); }; /** diff --git a/packages/server-nest/src/modules/BankingTransactions/queries/GetUncategorizedTransactions.ts b/packages/server-nest/src/modules/BankingTransactions/queries/GetUncategorizedTransactions.ts index af5909057..67c8334c7 100644 --- a/packages/server-nest/src/modules/BankingTransactions/queries/GetUncategorizedTransactions.ts +++ b/packages/server-nest/src/modules/BankingTransactions/queries/GetUncategorizedTransactions.ts @@ -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 { diff --git a/packages/server-nest/src/modules/BillPayments/types/BillPayments.types.ts b/packages/server-nest/src/modules/BillPayments/types/BillPayments.types.ts index 15bae9f28..f91c65cf6 100644 --- a/packages/server-nest/src/modules/BillPayments/types/BillPayments.types.ts +++ b/packages/server-nest/src/modules/BillPayments/types/BillPayments.types.ts @@ -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[]; diff --git a/packages/server-nest/src/modules/Bills/models/Bill.ts b/packages/server-nest/src/modules/Bills/models/Bill.ts index c6b56ebfa..d0cb162db 100644 --- a/packages/server-nest/src/modules/Bills/models/Bill.ts +++ b/packages/server-nest/src/modules/Bills/models/Bill.ts @@ -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)); diff --git a/packages/server-nest/src/modules/Import/interfaces.ts b/packages/server-nest/src/modules/Import/interfaces.ts index 5fb89f3fe..1bdcf0fbb 100644 --- a/packages/server-nest/src/modules/Import/interfaces.ts +++ b/packages/server-nest/src/modules/Import/interfaces.ts @@ -1,4 +1,5 @@ import { IModelMetaField2 } from "@/interfaces/Model"; +import { Import } from "./models/Import"; export interface ImportMappingAttr { from: string; diff --git a/packages/server-nest/src/modules/Import/models/Import.ts b/packages/server-nest/src/modules/Import/models/Import.ts new file mode 100644 index 000000000..2d9d4f468 --- /dev/null +++ b/packages/server-nest/src/modules/Import/models/Import.ts @@ -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; diff --git a/packages/server-nest/src/modules/Items/ItemsEntries.service.ts b/packages/server-nest/src/modules/Items/ItemsEntries.service.ts index badbea497..b35c2572c 100644 --- a/packages/server-nest/src/modules/Items/ItemsEntries.service.ts +++ b/packages/server-nest/src/modules/Items/ItemsEntries.service.ts @@ -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); diff --git a/packages/server-nest/src/modules/PaymentReceived/types/PaymentReceived.types.ts b/packages/server-nest/src/modules/PaymentReceived/types/PaymentReceived.types.ts index 646b9109a..dc45eeeb8 100644 --- a/packages/server-nest/src/modules/PaymentReceived/types/PaymentReceived.types.ts +++ b/packages/server-nest/src/modules/PaymentReceived/types/PaymentReceived.types.ts @@ -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; diff --git a/packages/server-nest/src/modules/TransactionItemEntry/ItemEntry.types.ts b/packages/server-nest/src/modules/TransactionItemEntry/ItemEntry.types.ts index 772fd223d..6607bc303 100644 --- a/packages/server-nest/src/modules/TransactionItemEntry/ItemEntry.types.ts +++ b/packages/server-nest/src/modules/TransactionItemEntry/ItemEntry.types.ts @@ -7,6 +7,9 @@ export interface IItemEntryDTO { landedCost?: boolean; warehouseId?: number; + sellAccountId?: number; + costAccountId?: number; + projectRefId?: number; projectRefType?: ProjectLinkRefType; projectRefInvoicedAmount?: number; diff --git a/packages/server-nest/src/utils/transform-to-map-by.ts b/packages/server-nest/src/utils/transform-to-map-by.ts index 39fb3edda..37370de42 100644 --- a/packages/server-nest/src/utils/transform-to-map-by.ts +++ b/packages/server-nest/src/utils/transform-to-map-by.ts @@ -1,5 +1,5 @@ import { groupBy } from 'lodash'; -export const transformToMapBy = (collection, key) => { +export const transformToMapBy = (collection: T[], key: keyof T): Map => { return new Map(Object.entries(groupBy(collection, key))); }; diff --git a/packages/server-nest/test/bank-rules.e2e-spec.ts b/packages/server-nest/test/bank-rules.e2e-spec.ts new file mode 100644 index 000000000..f02fc246e --- /dev/null +++ b/packages/server-nest/test/bank-rules.e2e-spec.ts @@ -0,0 +1,88 @@ +import * as request from 'supertest'; +import { faker } from '@faker-js/faker'; +import { app } from './init-app-test'; + +const requestBankRule = () => ({ + name: faker.company.name(), + order: 1, + applyIfAccountId: 1001, + applyIfTransactionType: 'deposit', + conditions: [ + { + field: 'description', + comparator: 'contains', + value: faker.finance.transactionDescription(), + }, + ], + assignCategory: 'Deposit', + assignAccountId: 1002, + assignPayee: faker.company.name(), + assignMemo: faker.lorem.sentence(), +}); + +describe('Bank Rules (e2e)', () => { + it('/banking/rules (POST)', () => { + return request(app.getHttpServer()) + .post('/banking/rules') + .set('organization-id', '4064541lv40nhca') + .send(requestBankRule()) + .expect(201); + }); + + it('/banking/rules/:id (PUT)', async () => { + const response = await request(app.getHttpServer()) + .post('/banking/rules') + .set('organization-id', '4064541lv40nhca') + .send(requestBankRule()); + + const ruleId = response.body.id; + + return request(app.getHttpServer()) + .put(`/banking/rules/${ruleId}`) + .set('organization-id', '4064541lv40nhca') + .send(requestBankRule()) + .expect(200); + }); + + it('/banking/rules/:id (DELETE)', async () => { + const response = await request(app.getHttpServer()) + .post('/banking/rules') + .set('organization-id', '4064541lv40nhca') + .send(requestBankRule()); + + const ruleId = response.body.id; + + return request(app.getHttpServer()) + .delete(`/banking/rules/${ruleId}`) + .set('organization-id', '4064541lv40nhca') + .expect(200); + }); + + it('/banking/rules/:id (GET)', async () => { + const response = await request(app.getHttpServer()) + .post('/banking/rules') + .set('organization-id', '4064541lv40nhca') + .send(requestBankRule()); + + const ruleId = response.body.id; + + return request(app.getHttpServer()) + .get(`/banking/rules/${ruleId}`) + .set('organization-id', '4064541lv40nhca') + .expect(200); + }); + + it('/banking/rules (GET)', async () => { + const response = await request(app.getHttpServer()) + .post('/banking/rules') + .set('organization-id', '4064541lv40nhca') + .send(requestBankRule()); + + const ruleId = response.body.id; + + return request(app.getHttpServer()) + .get(`/banking/rules/${ruleId}`) + .set('organization-id', '4064541lv40nhca') + .expect(200); + }); +}); diff --git a/packages/server-nest/test/banking-transactions.e2e-spec.ts b/packages/server-nest/test/banking-transactions.e2e-spec.ts new file mode 100644 index 000000000..61c5a3f02 --- /dev/null +++ b/packages/server-nest/test/banking-transactions.e2e-spec.ts @@ -0,0 +1,61 @@ +import * as request from 'supertest'; +import { faker } from '@faker-js/faker'; +import { app } from './init-app-test'; + +const createOwnerContributionTransaction = () => ({ + date: '2024-01-01', + transactionNumber: faker.string.alphanumeric(10), + referenceNo: faker.string.alphanumeric(8), + transactionType: 'owner_contribution', + description: faker.lorem.sentence(), + amount: faker.number.float({ min: 100, max: 10000, precision: 2 }), + // exchangeRate: 1, + // currencyCode: 'USD', + creditAccountId: 1014, + cashflowAccountId: 1000, + publish: true, + branchId: 1, + // plaidTransactionId: faker.string.uuid() +}); + +describe('Banking Transactions (e2e)', () => { + it('/banking/transactions (POST)', () => { + return request(app.getHttpServer()) + .post('/banking/transactions') + .set('organization-id', '4064541lv40nhca') + .send(createOwnerContributionTransaction()) + .expect(201); + }); + + it('/banking/transactions/:id (GET)', async () => { + const transaction = createOwnerContributionTransaction(); + const response = await request(app.getHttpServer()) + .post('/banking/transactions') + .set('organization-id', '4064541lv40nhca') + .send(transaction) + .expect(201); + + const transactionId = response.body.id; + + return request(app.getHttpServer()) + .get(`/banking/transactions/${transactionId}`) + .set('organization-id', '4064541lv40nhca') + .expect(200); + }); + + it('/banking/transactions/:id (DELETE)', async () => { + const transaction = createOwnerContributionTransaction(); + const response = await request(app.getHttpServer()) + .post('/banking/transactions') + .set('organization-id', '4064541lv40nhca') + .send(transaction) + .expect(201); + + const transactionId = response.body.id; + + return request(app.getHttpServer()) + .delete(`/banking/transactions/${transactionId}`) + .set('organization-id', '4064541lv40nhca') + .expect(200); + }); +}); diff --git a/packages/server/src/services/Banking/Rules/GetBankRule.ts b/packages/server/src/services/Banking/Rules/GetBankRule.ts index 67a26e5a1..b06b4f15d 100644 --- a/packages/server/src/services/Banking/Rules/GetBankRule.ts +++ b/packages/server/src/services/Banking/Rules/GetBankRule.ts @@ -23,7 +23,8 @@ export class GetBankRuleService { const bankRule = await BankRule.query() .findById(ruleId) - .withGraphFetched('conditions'); + .withGraphFetched('conditions') + .withGraphFetched('assignAccount'); return this.transformer.transform( tenantId,