fix: group matches to get possible and perfect matches

This commit is contained in:
Ahmed Bouhuolia
2024-06-29 13:21:59 +02:00
parent cb1f587637
commit b01528c06b
8 changed files with 132 additions and 29 deletions

View File

@@ -36,8 +36,6 @@ export class BankTransactionsMatchingController extends BaseController {
this.validationResult,
this.unmatchMatchedBankTransaction.bind(this)
);
router.get('/', this.getMatchedTransactions.bind(this));
return router;
}

View File

@@ -6,18 +6,27 @@ import { ServiceError } from '@/exceptions';
import CheckPolicies from '@/api/middleware/CheckPolicies';
import { AbilitySubject, CashflowAction } from '@/interfaces';
import { CashflowApplication } from '@/services/Cashflow/CashflowApplication';
import { GetMatchedTransactionsFilter } from '@/services/Banking/Matching/types';
import { MatchBankTransactionsApplication } from '@/services/Banking/Matching/MatchBankTransactionsApplication';
@Service()
export default class GetCashflowAccounts extends BaseController {
@Inject()
private cashflowApplication: CashflowApplication;
@Inject()
private bankTransactionsMatchingApp: MatchBankTransactionsApplication;
/**
* Controller router.
*/
public router() {
const router = Router();
router.get(
'/transactions/:transactionId/matches',
this.getMatchedTransactions.bind(this)
);
router.get(
'/transactions/:transactionId',
CheckPolicies(CashflowAction.View, AbilitySubject.Cashflow),
@@ -47,7 +56,6 @@ export default class GetCashflowAccounts extends BaseController {
tenantId,
transactionId
);
return res.status(200).send({
cashflow_transaction: this.transfromToResponse(cashflowTransaction),
});
@@ -56,6 +64,34 @@ export default class GetCashflowAccounts extends BaseController {
}
};
/**
* Retrieves the matched transactions.
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
private async getMatchedTransactions(
req: Request<{ transactionId: number }>,
res: Response,
next: NextFunction
) {
const { tenantId } = req;
const { transactionId } = req.params;
const filter = this.matchedQueryData(req) as GetMatchedTransactionsFilter;
try {
const data =
await this.bankTransactionsMatchingApp.getMatchedTransactions(
tenantId,
transactionId,
filter
);
return res.status(200).send(data);
} catch (error) {
next(error);
}
}
/**
* Catches the service errors.
* @param {Error} error - Error.

View File

@@ -1,13 +1,18 @@
import { Inject, Service } from 'typedi';
import * as R from 'ramda';
import moment from 'moment';
import { PromisePool } from '@supercharge/promise-pool';
import { GetMatchedTransactionsFilter, MatchedTransactionsPOJO } from './types';
import { GetMatchedTransactionsByExpenses } from './GetMatchedTransactionsByExpenses';
import { GetMatchedTransactionsByBills } from './GetMatchedTransactionsByBills';
import { GetMatchedTransactionsByManualJournals } from './GetMatchedTransactionsByManualJournals';
import HasTenancyService from '@/services/Tenancy/TenancyService';
@Service()
export class GetMatchedTransactions {
@Inject()
private tenancy: HasTenancyService;
@Inject()
private getMatchedInvoicesService: GetMatchedTransactionsByExpenses;
@@ -36,11 +41,20 @@ export class GetMatchedTransactions {
* Retrieves the matched transactions.
* @param {number} tenantId -
* @param {GetMatchedTransactionsFilter} filter -
* @returns {Promise<MatchedTransactionsPOJO>}
*/
public async getMatchedTransactions(
tenantId: number,
uncategorizedTransactionId: number,
filter: GetMatchedTransactionsFilter
): Promise<MatchedTransactionsPOJO> {
const { UncategorizedCashflowTransaction } = this.tenancy.models(tenantId);
const uncategorizedTransaction =
await UncategorizedCashflowTransaction.query()
.findById(uncategorizedTransactionId)
.throwIfNotFound();
const filtered = filter.transactionType
? this.registered.filter((item) => item.type === filter.transactionType)
: this.registered;
@@ -50,6 +64,39 @@ export class GetMatchedTransactions {
.process(async ({ type, service }) => {
return service.getMatchedTransactions(tenantId, filter);
});
return R.compose(R.flatten)(matchedTransactions?.results);
const { perfectMatches, possibleMatches } = this.groupMatchedResults(
uncategorizedTransaction,
matchedTransactions
);
return {
perfectMatches,
possibleMatches,
};
}
/**
* Groups the given results for getting perfect and possible matches
* based on the given uncategorized transaction.
* @param uncategorizedTransaction
* @param matchedTransactions
* @returns {MatchedTransactionsPOJO}
*/
private groupMatchedResults(
uncategorizedTransaction,
matchedTransactions
): MatchedTransactionsPOJO {
const results = R.compose(R.flatten)(matchedTransactions?.results);
const perfectMatches = R.filter(
(match) =>
match.amount === uncategorizedTransaction.amount &&
moment(match.date).isSame(uncategorizedTransaction.date, 'day'),
results
);
const possibleMatches = R.difference(results, perfectMatches);
return { perfectMatches, possibleMatches };
}
}

View File

@@ -23,10 +23,12 @@ export class MatchBankTransactionsApplication {
*/
public getMatchedTransactions(
tenantId: number,
uncategorizedTransactionId: number,
filter: GetMatchedTransactionsFilter
) {
return this.getMatchedTransactionsService.getMatchedTransactions(
tenantId,
uncategorizedTransactionId,
filter
);
}

View File

@@ -49,7 +49,10 @@ export interface MatchedTransactionPOJO {
transactionId: number;
}
export type MatchedTransactionsPOJO = Array<MatchedTransactionPOJO[]>;
export type MatchedTransactionsPOJO = {
perfectMatches: Array<MatchedTransactionPOJO[]>;
possibleMatches: Array<MatchedTransactionPOJO[]>;
};
export const ERRORS = {
RESOURCE_TYPE_MATCHING_TRANSACTION_INVALID:
@@ -59,5 +62,5 @@ export const ERRORS = {
TOTAL_MATCHING_TRANSACTIONS_INVALID: 'TOTAL_MATCHING_TRANSACTIONS_INVALID',
TRANSACTION_ALREADY_MATCHED: 'TRANSACTION_ALREADY_MATCHED',
CANNOT_MATCH_EXCLUDED_TRANSACTION: 'CANNOT_MATCH_EXCLUDED_TRANSACTION',
CANNOT_DELETE_TRANSACTION_MATCHED: 'CANNOT_DELETE_TRANSACTION_MATCHED'
CANNOT_DELETE_TRANSACTION_MATCHED: 'CANNOT_DELETE_TRANSACTION_MATCHED',
};