feat: retrieve the matching transactions

This commit is contained in:
Ahmed Bouhuolia
2024-06-20 10:20:18 +02:00
parent d3230767dd
commit b6deb842ff
11 changed files with 507 additions and 149 deletions

View File

@@ -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');
},
},
};
}

View File

@@ -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<string>}
*/
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() {}
}

View File

@@ -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<string>}
*/
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() {}
}

View File

@@ -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<string>}
*/
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';
}
}

View File

@@ -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<string>}
*/
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() {}
}

View File

@@ -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 {

View File

@@ -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()
);
}
}

View File

@@ -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()
);
}
}

View File

@@ -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()
);
}
}

View File

@@ -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()
);
}
}

View File

@@ -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) => {