From 8623b69991eee643b8babd7ebba7910df99d95fa Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Thu, 1 Aug 2024 12:11:40 +0200 Subject: [PATCH] feat: getting matched transactiosn from multi uncategorized transactions --- .../Cashflow/GetCashflowTransaction.ts | 14 ++++++++--- .../Matching/GetMatchedTransactions.ts | 25 ++++++++++--------- .../MatchBankTransactionsApplication.ts | 4 +-- .../Banking/Matching/MatchTransactions.ts | 10 +++++--- 4 files changed, 32 insertions(+), 21 deletions(-) diff --git a/packages/server/src/api/controllers/Cashflow/GetCashflowTransaction.ts b/packages/server/src/api/controllers/Cashflow/GetCashflowTransaction.ts index 6b0be76e2..393cdfcf9 100644 --- a/packages/server/src/api/controllers/Cashflow/GetCashflowTransaction.ts +++ b/packages/server/src/api/controllers/Cashflow/GetCashflowTransaction.ts @@ -1,6 +1,6 @@ import { Service, Inject } from 'typedi'; import { Router, Request, Response, NextFunction } from 'express'; -import { param } from 'express-validator'; +import { param, query } from 'express-validator'; import BaseController from '../BaseController'; import { ServiceError } from '@/exceptions'; import CheckPolicies from '@/api/middleware/CheckPolicies'; @@ -24,7 +24,12 @@ export default class GetCashflowAccounts extends BaseController { const router = Router(); router.get( - '/transactions/:transactionId/matches', + '/transactions/matches', + [ + query('uncategorizeTransactionsIds').exists().isArray({ min: 1 }), + query('uncategorizeTransactionsIds.*').exists().isNumeric().toInt(), + ], + this.validationResult, this.getMatchedTransactions.bind(this) ); router.get( @@ -76,14 +81,15 @@ export default class GetCashflowAccounts extends BaseController { next: NextFunction ) { const { tenantId } = req; - const { transactionId } = req.params; + const uncategorizeTransactionsIds: Array = + req.query.uncategorizeTransactionsIds; const filter = this.matchedQueryData(req) as GetMatchedTransactionsFilter; try { const data = await this.bankTransactionsMatchingApp.getMatchedTransactions( tenantId, - transactionId, + uncategorizeTransactionsIds, filter ); return res.status(200).send(data); diff --git a/packages/server/src/services/Banking/Matching/GetMatchedTransactions.ts b/packages/server/src/services/Banking/Matching/GetMatchedTransactions.ts index f0cfdcaed..3b5f1339a 100644 --- a/packages/server/src/services/Banking/Matching/GetMatchedTransactions.ts +++ b/packages/server/src/services/Banking/Matching/GetMatchedTransactions.ts @@ -1,6 +1,7 @@ import { Inject, Service } from 'typedi'; import * as R from 'ramda'; import moment from 'moment'; +import { first, sumBy } from 'lodash'; import { PromisePool } from '@supercharge/promise-pool'; import { GetMatchedTransactionsFilter, MatchedTransactionsPOJO } from './types'; import { GetMatchedTransactionsByExpenses } from './GetMatchedTransactionsByExpenses'; @@ -47,19 +48,20 @@ export class GetMatchedTransactions { /** * Retrieves the matched transactions. * @param {number} tenantId - + * @param {Array} uncategorizedTransactionIds - Uncategorized transactions ids. * @param {GetMatchedTransactionsFilter} filter - * @returns {Promise} */ public async getMatchedTransactions( tenantId: number, - uncategorizedTransactionId: number, + uncategorizedTransactionIds: Array, filter: GetMatchedTransactionsFilter ): Promise { const { UncategorizedCashflowTransaction } = this.tenancy.models(tenantId); - const uncategorizedTransaction = + const uncategorizedTransactions = await UncategorizedCashflowTransaction.query() - .findById(uncategorizedTransactionId) + .whereIn('id', uncategorizedTransactionIds) .throwIfNotFound(); const filtered = filter.transactionType @@ -71,9 +73,8 @@ export class GetMatchedTransactions { .process(async ({ type, service }) => { return service.getMatchedTransactions(tenantId, filter); }); - const { perfectMatches, possibleMatches } = this.groupMatchedResults( - uncategorizedTransaction, + uncategorizedTransactions, matchedTransactions ); return { @@ -90,20 +91,20 @@ export class GetMatchedTransactions { * @returns {MatchedTransactionsPOJO} */ private groupMatchedResults( - uncategorizedTransaction, + uncategorizedTransactions: Array, matchedTransactions ): MatchedTransactionsPOJO { const results = R.compose(R.flatten)(matchedTransactions?.results); + const firstUncategorized = first(uncategorizedTransactions); + const amount = sumBy(uncategorizedTransactions, 'amount'); + const date = firstUncategorized.date; + // Sort the results based on amount, date, and transaction type - const closestResullts = sortClosestMatchTransactions( - uncategorizedTransaction, - results - ); + const closestResullts = sortClosestMatchTransactions(amount, date, results); const perfectMatches = R.filter( (match) => - match.amount === uncategorizedTransaction.amount && - moment(match.date).isSame(uncategorizedTransaction.date, 'day'), + match.amount === amount && moment(match.date).isSame(date, 'day'), closestResullts ); const possibleMatches = R.difference(closestResullts, perfectMatches); diff --git a/packages/server/src/services/Banking/Matching/MatchBankTransactionsApplication.ts b/packages/server/src/services/Banking/Matching/MatchBankTransactionsApplication.ts index 39702c4bb..dd60c166c 100644 --- a/packages/server/src/services/Banking/Matching/MatchBankTransactionsApplication.ts +++ b/packages/server/src/services/Banking/Matching/MatchBankTransactionsApplication.ts @@ -23,12 +23,12 @@ export class MatchBankTransactionsApplication { */ public getMatchedTransactions( tenantId: number, - uncategorizedTransactionId: number, + uncategorizedTransactionsIds: Array, filter: GetMatchedTransactionsFilter ) { return this.getMatchedTransactionsService.getMatchedTransactions( tenantId, - uncategorizedTransactionId, + uncategorizedTransactionsIds, filter ); } diff --git a/packages/server/src/services/Banking/Matching/MatchTransactions.ts b/packages/server/src/services/Banking/Matching/MatchTransactions.ts index 9ec128831..af0d41f60 100644 --- a/packages/server/src/services/Banking/Matching/MatchTransactions.ts +++ b/packages/server/src/services/Banking/Matching/MatchTransactions.ts @@ -16,6 +16,7 @@ import { MatchTransactionsTypes } from './MatchTransactionsTypes'; import { ServiceError } from '@/exceptions'; import { sumMatchTranasctions, + sumUncategorizedTransactions, validateUncategorizedTransactionsExcluded, validateUncategorizedTransactionsNotMatched, } from './_utils'; @@ -95,11 +96,14 @@ export class MatchBankTransactions { const totalMatchedTranasctions = sumMatchTranasctions( validatationResult.results ); + const totalUncategorizedTransactions = sumUncategorizedTransactions( + uncategorizedTransactions + ); // Validates the total given matching transcations whether is not equal // uncategorized transaction amount. - // if (totalMatchedTranasctions !== uncategorizedTransaction.amount) { - // throw new ServiceError(ERRORS.TOTAL_MATCHING_TRANSACTIONS_INVALID); - // } + if (totalUncategorizedTransactions === totalMatchedTranasctions) { + throw new ServiceError(ERRORS.TOTAL_MATCHING_TRANSACTIONS_INVALID); + } } /**