From b6deb842ff04369f37b8eef2fad97a7c7e07c0a0 Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Thu, 20 Jun 2024 10:20:18 +0200 Subject: [PATCH] feat: retrieve the matching transactions --- packages/server/src/models/ManualJournal.ts | 16 +++ .../GetMatchedTransactionBillsTransformer.ts | 76 ++++++++-- ...etMatchedTransactionExpensesTransformer.ts | 107 ++++++++++++-- ...etMatchedTransactionInvoicesTransformer.ts | 89 +++++++++++- ...hedTransactionManualJournalsTransformer.ts | 103 ++++++++++++-- .../Matching/GetMatchedTransactions.ts | 130 ++++-------------- .../Matching/GetMatchedTransactionsByBills.ts | 35 +++++ .../GetMatchedTransactionsByExpenses.ts | 35 +++++ .../GetMatchedTransactionsByInvoices.ts | 34 +++++ .../GetMatchedTransactionsByManualJournals.ts | 29 ++++ .../Banking/Matching/MatchTransactions.ts | 2 +- 11 files changed, 507 insertions(+), 149 deletions(-) create mode 100644 packages/server/src/services/Banking/Matching/GetMatchedTransactionsByBills.ts create mode 100644 packages/server/src/services/Banking/Matching/GetMatchedTransactionsByExpenses.ts create mode 100644 packages/server/src/services/Banking/Matching/GetMatchedTransactionsByInvoices.ts create mode 100644 packages/server/src/services/Banking/Matching/GetMatchedTransactionsByManualJournals.ts diff --git a/packages/server/src/models/ManualJournal.ts b/packages/server/src/models/ManualJournal.ts index 3d25b377f..945af2aad 100644 --- a/packages/server/src/models/ManualJournal.ts +++ b/packages/server/src/models/ManualJournal.ts @@ -97,6 +97,7 @@ export default class ManualJournal extends mixin(TenantModel, [ const AccountTransaction = require('models/AccountTransaction'); const ManualJournalEntry = require('models/ManualJournalEntry'); const Document = require('models/Document'); + const { MatchedBankTransaction } = require('models/MatchedBankTransaction'); return { entries: { @@ -140,6 +141,21 @@ export default class ManualJournal extends mixin(TenantModel, [ query.where('model_ref', 'ManualJournal'); }, }, + + /** + * 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'); + }, + }, }; } diff --git a/packages/server/src/services/Banking/Matching/GetMatchedTransactionBillsTransformer.ts b/packages/server/src/services/Banking/Matching/GetMatchedTransactionBillsTransformer.ts index fdc2a38ed..1bb71ffa0 100644 --- a/packages/server/src/services/Banking/Matching/GetMatchedTransactionBillsTransformer.ts +++ b/packages/server/src/services/Banking/Matching/GetMatchedTransactionBillsTransformer.ts @@ -6,35 +6,81 @@ export class GetMatchedTransactionBillsTransformer extends Transformer { * @returns {Array} */ public includeAttributes = (): string[] => { - return ['referenceNo']; + return [ + 'referenceNo', + 'amount', + 'amountFormatted', + 'transactionNo', + 'date', + 'dateFromatted', + 'transactionId', + 'transactionNo', + 'transactionType', + 'transsactionTypeFormatted', + ]; }; + /** + * Exclude all attributes. + * @returns {Array} + */ public excludeAttributes = (): string[] => { return ['*']; }; - protected referenceNo(invoice) { - return invoice.referenceNo; + /** + * Retrieve the reference number of the bill. + * @param {Object} bill - The bill object. + * @returns {string} + */ + protected referenceNo(bill) { + return bill.referenceNo; } - amount(invoice) { - return 1; + /** + * Retrieve the amount of the bill. + * @param {Object} bill - The bill object. + * @returns {number} + */ + protected amount(bill) { + return bill.amount; } - amountFormatted() { + /** + * Retrieve the formatted amount of the bill. + * @param {Object} bill - The bill object. + * @returns {string} + */ + protected amountFormatted(bill) { + return this.formatNumber(bill.totalAmount, { + currencyCode: bill.currencyCode, + }); } - date() { + /** + * Retrieve the date of the bill. + * @param {Object} bill - The bill object. + * @returns {string} + */ + protected date(bill) { + return bill.date; } - dateFromatted() { + /** + * Retrieve the formatted date of the bill. + * @param {Object} bill - The bill object. + * @returns {string} + */ + protected dateFromatted(bill) { + return this.formatDate(bill.date); } - transactionId(invoice) { - return invoice.id; + + /** + * Retrieve the transcation id of the bill. + * @param {Object} bill - The bill object. + * @returns {number} + */ + protected transactionId(bill) { + return bill.id; } - transactionNo() { - - } - transactionType() {} - transsactionTypeFormatted() {} } diff --git a/packages/server/src/services/Banking/Matching/GetMatchedTransactionExpensesTransformer.ts b/packages/server/src/services/Banking/Matching/GetMatchedTransactionExpensesTransformer.ts index b8f16ae7a..a00dd6d65 100644 --- a/packages/server/src/services/Banking/Matching/GetMatchedTransactionExpensesTransformer.ts +++ b/packages/server/src/services/Banking/Matching/GetMatchedTransactionExpensesTransformer.ts @@ -6,27 +6,108 @@ export class GetMatchedTransactionExpensesTransformer extends Transformer { * @returns {Array} */ public includeAttributes = (): string[] => { - return ['referenceNo']; + return [ + 'referenceNo', + 'amount', + 'amountFormatted', + 'transactionNo', + 'date', + 'dateFromatted', + 'transactionId', + 'transactionNo', + 'transactionType', + 'transsactionTypeFormatted', + ]; }; + /** + * Exclude all attributes. + * @returns {Array} + */ public excludeAttributes = (): string[] => { return ['*']; }; - protected referenceNo(invoice) { - return invoice.referenceNo; + /** + * Retrieves the expense reference number. + * @param expense + * @returns {string} + */ + protected referenceNo(expense) { + return expense.referenceNo; } - amount(invoice) { - return 1; + /** + * Retrieves the expense amount. + * @param expense + * @returns {number} + */ + protected amount(expense) { + return expense.totalAmount; } - amountFormatted() {} - date() {} - dateFromatted() {} - transactionId(invoice) { - return invoice.id; + + /** + * Formats the amount of the expense. + * @param expense + * @returns {string} + */ + protected amountFormatted(expense) { + return this.formatNumber(expense.totalAmount, { + currencyCode: expense.currencyCode, + }); + } + + /** + * Retrieves the date of the expense. + * @param expense + * @returns {Date} + */ + protected date(expense) { + return expense.paymentDate; + } + + /** + * Formats the date of the expense. + * @param expense + * @returns {string} + */ + protected dateFromatted(expense) { + return this.formatDate(expense.paymentDate); + } + + /** + * Retrieves the transaction ID of the expense. + * @param expense + * @returns {number} + */ + protected transactionId(expense) { + return expense.id; + } + + /** + * Retrieves the expense transaction number. + * @param expense + * @returns {string} + */ + protected transactionNo(expense) { + return expense.expenseNo; + } + + /** + * Retrieves the expense transaction type. + * @param expense + * @returns {String} + */ + protected transactionType() { + return 'Expense'; + } + + /** + * Retrieves the formatted transaction type of the expense. + * @param expense + * @returns {string} + */ + protected transsactionTypeFormatted() { + return 'Expense'; } - transactionNo() {} - transactionType() {} - transsactionTypeFormatted() {} } diff --git a/packages/server/src/services/Banking/Matching/GetMatchedTransactionInvoicesTransformer.ts b/packages/server/src/services/Banking/Matching/GetMatchedTransactionInvoicesTransformer.ts index e3326a873..dd49e3eba 100644 --- a/packages/server/src/services/Banking/Matching/GetMatchedTransactionInvoicesTransformer.ts +++ b/packages/server/src/services/Banking/Matching/GetMatchedTransactionInvoicesTransformer.ts @@ -6,18 +6,105 @@ export class GetMatchedTransactionInvoicesTransformer extends Transformer { * @returns {Array} */ public includeAttributes = (): string[] => { - return ['referenceNo', 'transactionNo']; + return [ + 'referenceNo', + 'amount', + 'amountFormatted', + 'transactionNo', + 'date', + 'dateFromatted', + 'transactionId', + 'transactionNo', + 'transactionType', + 'transsactionTypeFormatted', + ]; }; + /** + * Exclude all attributes. + * @returns {Array} + */ public excludeAttributes = (): string[] => { return ['*']; }; + /** + * Retrieve the invoice reference number. + * @returns {string} + */ protected referenceNo(invoice) { return invoice.referenceNo; } + /** + * Retrieve the invoice amount. + * @param invoice + * @returns {number} + */ + protected amount(invoice) { + return invoice.dueAmount; + } + /** + * Format the amount of the invoice. + * @param invoice + * @returns {string} + */ + protected formatAmount(invoice) { + return this.formatNumber(invoice.dueAmount, { + currencyCode: invoice.currencyCode, + }); + } + + /** + * Retrieve the date of the invoice. + * @param invoice + * @returns {Date} + */ + protected getDate(invoice) { + return invoice.invoiceDate; + } + + /** + * Format the date of the invoice. + * @param invoice + * @returns {string} + */ + protected formatDate(invoice) { + return this.formatDate(invoice.invoiceDate); + } + + /** + * Retrieve the transaction ID of the invoice. + * @param invoice + * @returns {number} + */ + protected getTransactionId(invoice) { + return invoice.id; + } + /** + * Retrieve the invoice transaction number. + * @param invoice + * @returns {string} + */ protected transactionNo(invoice) { return invoice.invoiceNo; } + + /** + * Retrieve the invoice transaction type. + * @param invoice + * @returns {String} + */ + protected transactionType(invoice) { + return 'SaleInvoice'; + } + + /** + * Retrieve the invoice formatted transaction type. + * @param invoice + * @returns {string} + */ + protected transsactionTypeFormatted(invoice) { + return 'Sale invoice'; + } } diff --git a/packages/server/src/services/Banking/Matching/GetMatchedTransactionManualJournalsTransformer.ts b/packages/server/src/services/Banking/Matching/GetMatchedTransactionManualJournalsTransformer.ts index 530dfbfa7..d43307700 100644 --- a/packages/server/src/services/Banking/Matching/GetMatchedTransactionManualJournalsTransformer.ts +++ b/packages/server/src/services/Banking/Matching/GetMatchedTransactionManualJournalsTransformer.ts @@ -6,27 +6,104 @@ export class GetMatchedTransactionManualJournalsTransformer extends Transformer * @returns {Array} */ public includeAttributes = (): string[] => { - return ['referenceNo']; + return [ + 'referenceNo', + 'amount', + 'amountFormatted', + 'transactionNo', + 'date', + 'dateFromatted', + 'transactionId', + 'transactionNo', + 'transactionType', + 'transsactionTypeFormatted', + ]; }; + /** + * Exclude all attributes. + * @returns {Array} + */ public excludeAttributes = (): string[] => { return ['*']; }; - protected referenceNo(invoice) { - return invoice.referenceNo; + /** + * Retrieves the manual journal reference no. + * @param manualJournal + * @returns {string} + */ + protected referenceNo(manualJournal) { + return manualJournal.referenceNo; } - amount(invoice) { - return 1; + /** + * Retrieves the manual journal amount. + * @param manualJournal + * @returns {number} + */ + protected amount(manualJournal) { + return manualJournal.totalAmount; } - amountFormatted() {} - date() {} - dateFromatted() {} - transactionId(invoice) { - return invoice.id; + + /** + * Retrieves the manual journal formatted amount. + * @param manualJournal + * @returns {string} + */ + protected amountFormatted(manualJournal) { + return this.formatNumber(manualJournal.totalAmount, { + currencyCode: manualJournal.currencyCode, + }); + } + + /** + * Retreives the manual journal date. + * @param manualJournal + * @returns {Date} + */ + protected date(manualJournal) { + return manualJournal.date; + } + + /** + * Retrieves the manual journal formatted date. + * @param manualJournal + * @returns {string} + */ + protected dateFromatted(manualJournal) { + return this.formatDate(manualJournal.date); + } + + /** + * Retrieve the manual journal transaction id. + * @returns {number} + */ + protected transactionId(manualJournal) { + return manualJournal.id; + } + + /** + * Retrieve the manual journal transaction number. + * @param manualJournal + */ + protected transactionNo(manualJournal) { + return manualJournal.journalNumber; + } + + /** + * Retrieve the manual journal transaction type. + * @returns {string} + */ + protected transactionType() { + return 'ManualJournal'; + } + + /** + * Retrieves the manual journal formatted transaction type. + * @returns {string} + */ + protected transsactionTypeFormatted() { + return 'Manual Journal'; } - transactionNo() {} - transactionType() {} - transsactionTypeFormatted() {} } diff --git a/packages/server/src/services/Banking/Matching/GetMatchedTransactions.ts b/packages/server/src/services/Banking/Matching/GetMatchedTransactions.ts index b98d2cef1..ead394c97 100644 --- a/packages/server/src/services/Banking/Matching/GetMatchedTransactions.ts +++ b/packages/server/src/services/Banking/Matching/GetMatchedTransactions.ts @@ -2,20 +2,32 @@ import { Inject, Service } from 'typedi'; import * as R from 'ramda'; import { PromisePool } from '@supercharge/promise-pool'; import { GetMatchedTransactionsFilter } from './types'; -import HasTenancyService from '@/services/Tenancy/TenancyService'; -import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable'; -import { GetMatchedTransactionInvoicesTransformer } from './GetMatchedTransactionInvoicesTransformer'; -import { GetMatchedTransactionBillsTransformer } from './GetMatchedTransactionBillsTransformer'; -import { GetMatchedTransactionExpensesTransformer } from './GetMatchedTransactionExpensesTransformer'; -import { GetMatchedTransactionManualJournalsTransformer } from './GetMatchedTransactionManualJournalsTransformer'; +import { GetMatchedTransactionsByExpenses } from './GetMatchedTransactionsByExpenses'; +import { GetMatchedTransactionsByBills } from './GetMatchedTransactionsByBills'; +import { GetMatchedTransactionsByManualJournals } from './GetMatchedTransactionsByManualJournals'; @Service() export class GetMatchedTransactions { @Inject() - private tenancy: HasTenancyService; + private getMatchedInvoicesService: GetMatchedTransactionsByExpenses; @Inject() - private transformer: TransformerInjectable; + private getMatchedBillsService: GetMatchedTransactionsByBills; + + @Inject() + private getMatchedManualJournalService: GetMatchedTransactionsByManualJournals; + + @Inject() + private getMatchedExpensesService: GetMatchedTransactionsByExpenses; + + get registered() { + return [ + { type: 'SaleInvoice', service: this.getMatchedInvoicesService }, + { type: 'Bill', service: this.getMatchedBillsService }, + { type: 'Expense', service: this.getMatchedExpensesService }, + { type: 'ManualJournal', service: this.getMatchedManualJournalService }, + ]; + } /** * Retrieves the matched transactions. @@ -26,111 +38,17 @@ export class GetMatchedTransactions { tenantId: number, filter: GetMatchedTransactionsFilter ) { - const registered = [ - { - type: 'SaleInvoice', - callback: this.getSaleInvoicesMatchedTransactions.bind(this), - }, - { - type: 'Bill', - callback: this.getBillsMatchedTransactions.bind(this), - }, - { - type: 'Expense', - callback: this.getExpensesMatchedTransactions.bind(this), - }, - { - type: 'ManualJournal', - callback: this.getManualJournalsMatchedTransactions.bind(this), - }, - ]; const filtered = filter.transactionType - ? registered.filter((item) => item.type === filter.transactionType) - : registered; + ? this.registered.filter((item) => item.type === filter.transactionType) + : this.registered; const matchedTransactions = await PromisePool.withConcurrency(2) .for(filtered) - .process(async ({ type, callback }) => { - return callback(tenantId, filter); + .process(async ({ type, service }) => { + return service.getMatchedTransactions(tenantId, filter); }); return R.compose(R.flatten)(matchedTransactions?.results); } - - /** - * - * @param {number} tenantId - - * @param {GetMatchedTransactionsFilter} filter - - */ - async getSaleInvoicesMatchedTransactions( - tenantId: number, - filter: GetMatchedTransactionsFilter - ) { - const { SaleInvoice } = this.tenancy.models(tenantId); - - const invoices = await SaleInvoice.query(); - - return this.transformer.transform( - tenantId, - invoices, - new GetMatchedTransactionInvoicesTransformer() - ); - } - - /** - * - * @param {number} tenantId - - * @param {GetMatchedTransactionsFilter} filter - - */ - async getBillsMatchedTransactions( - tenantId: number, - filter: GetMatchedTransactionsFilter - ) { - const { Bill } = this.tenancy.models(tenantId); - - const bills = await Bill.query(); - - return this.transformer.transform( - tenantId, - bills, - new GetMatchedTransactionBillsTransformer() - ); - } - - /** - * - * @param {number} tenantId - * @param {GetMatchedTransactionsFilter} filter - * @returns - */ - async getExpensesMatchedTransactions( - tenantId: number, - filter: GetMatchedTransactionsFilter - ) { - const { Expense } = this.tenancy.models(tenantId); - - const expenses = await Expense.query(); - - return this.transformer.transform( - tenantId, - expenses, - new GetMatchedTransactionManualJournalsTransformer() - ); - } - - async getManualJournalsMatchedTransactions( - tenantId: number, - filter: GetMatchedTransactionsFilter - ) { - const { ManualJournal } = this.tenancy.models(tenantId); - - const manualJournals = await ManualJournal.query(); - - return this.transformer.transform( - tenantId, - manualJournals, - new GetMatchedTransactionManualJournalsTransformer() - ); - } } interface MatchedTransaction { diff --git a/packages/server/src/services/Banking/Matching/GetMatchedTransactionsByBills.ts b/packages/server/src/services/Banking/Matching/GetMatchedTransactionsByBills.ts new file mode 100644 index 000000000..3e62bf319 --- /dev/null +++ b/packages/server/src/services/Banking/Matching/GetMatchedTransactionsByBills.ts @@ -0,0 +1,35 @@ +import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable'; +import { GetMatchedTransactionBillsTransformer } from './GetMatchedTransactionBillsTransformer'; +import { GetMatchedTransactionInvoicesTransformer } from './GetMatchedTransactionInvoicesTransformer'; +import { GetMatchedTransactionsFilter } from './types'; +import HasTenancyService from '@/services/Tenancy/TenancyService'; +import { Inject, Service } from 'typedi'; + +@Service() +export class GetMatchedTransactionsByBills { + @Inject() + private tenancy: HasTenancyService; + + @Inject() + private transformer: TransformerInjectable; + + /** + * Retrieves the matched transactions. + * @param {number} tenantId - + * @param {GetMatchedTransactionsFilter} filter - + */ + public async getMatchedTransactions( + tenantId: number, + filter: GetMatchedTransactionsFilter + ) { + const { Bill } = this.tenancy.models(tenantId); + + const bills = await Bill.query(); + + return this.transformer.transform( + tenantId, + bills, + new GetMatchedTransactionBillsTransformer() + ); + } +} diff --git a/packages/server/src/services/Banking/Matching/GetMatchedTransactionsByExpenses.ts b/packages/server/src/services/Banking/Matching/GetMatchedTransactionsByExpenses.ts new file mode 100644 index 000000000..c28ce6900 --- /dev/null +++ b/packages/server/src/services/Banking/Matching/GetMatchedTransactionsByExpenses.ts @@ -0,0 +1,35 @@ +import { Inject, Service } from 'typedi'; +import { GetMatchedTransactionManualJournalsTransformer } from './GetMatchedTransactionManualJournalsTransformer'; +import { GetMatchedTransactionsFilter } from './types'; +import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable'; +import HasTenancyService from '@/services/Tenancy/TenancyService'; + +@Service() +export class GetMatchedTransactionsByExpenses { + @Inject() + private tenancy: HasTenancyService; + + @Inject() + private transformer: TransformerInjectable; + + /** + * + * @param {number} tenantId + * @param {GetMatchedTransactionsFilter} filter + * @returns + */ + async getMatchedTransactions( + tenantId: number, + filter: GetMatchedTransactionsFilter + ) { + const { Expense } = this.tenancy.models(tenantId); + + const expenses = await Expense.query(); + + return this.transformer.transform( + tenantId, + expenses, + new GetMatchedTransactionManualJournalsTransformer() + ); + } +} diff --git a/packages/server/src/services/Banking/Matching/GetMatchedTransactionsByInvoices.ts b/packages/server/src/services/Banking/Matching/GetMatchedTransactionsByInvoices.ts new file mode 100644 index 000000000..73185e776 --- /dev/null +++ b/packages/server/src/services/Banking/Matching/GetMatchedTransactionsByInvoices.ts @@ -0,0 +1,34 @@ +import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable'; +import { GetMatchedTransactionInvoicesTransformer } from './GetMatchedTransactionInvoicesTransformer'; +import { GetMatchedTransactionsFilter } from './types'; +import HasTenancyService from '@/services/Tenancy/TenancyService'; +import { Inject, Service } from 'typedi'; + +@Service() +export class GetMatchedTransactionsByInvoices { + @Inject() + private tenancy: HasTenancyService; + + @Inject() + private transformer: TransformerInjectable; + + /** + * Retrieves the matched transactions. + * @param {number} tenantId - + * @param {GetMatchedTransactionsFilter} filter - + */ + public async getMatchedTransactions( + tenantId: number, + filter: GetMatchedTransactionsFilter + ) { + const { SaleInvoice } = this.tenancy.models(tenantId); + + const invoices = await SaleInvoice.query(); + + return this.transformer.transform( + tenantId, + invoices, + new GetMatchedTransactionInvoicesTransformer() + ); + } +} diff --git a/packages/server/src/services/Banking/Matching/GetMatchedTransactionsByManualJournals.ts b/packages/server/src/services/Banking/Matching/GetMatchedTransactionsByManualJournals.ts new file mode 100644 index 000000000..ec33732eb --- /dev/null +++ b/packages/server/src/services/Banking/Matching/GetMatchedTransactionsByManualJournals.ts @@ -0,0 +1,29 @@ +import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable'; +import { GetMatchedTransactionManualJournalsTransformer } from './GetMatchedTransactionManualJournalsTransformer'; +import { Inject, Service } from 'typedi'; +import HasTenancyService from '@/services/Tenancy/TenancyService'; +import { GetMatchedTransactionsFilter } from './types'; + +@Service() +export class GetMatchedTransactionsByManualJournals { + @Inject() + private tenancy: HasTenancyService; + + @Inject() + private transformer: TransformerInjectable; + + async getMatchedTransactions( + tenantId: number, + filter: GetMatchedTransactionsFilter + ) { + const { ManualJournal } = this.tenancy.models(tenantId); + + const manualJournals = await ManualJournal.query(); + + return this.transformer.transform( + tenantId, + manualJournals, + new GetMatchedTransactionManualJournalsTransformer() + ); + } +} diff --git a/packages/server/src/services/Banking/Matching/MatchTransactions.ts b/packages/server/src/services/Banking/Matching/MatchTransactions.ts index 496ba204e..8f9ac6098 100644 --- a/packages/server/src/services/Banking/Matching/MatchTransactions.ts +++ b/packages/server/src/services/Banking/Matching/MatchTransactions.ts @@ -45,7 +45,7 @@ export class MatchBankTransactions { trx, } as IBankTransactionMatchingEventPayload); - // + // Matches the given transactions under promise pool concurrency controlling. await PromisePool.withConcurrency(10) .for(matchedTransactions) .process(async (matchedTransaction) => {