mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-15 12:20:31 +00:00
fix(server): match transactions query
This commit is contained in:
@@ -95,6 +95,17 @@ export default class CashflowTransaction extends TenantModel {
|
||||
return !!this.uncategorizedTransaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Model modifiers.
|
||||
*/
|
||||
static get modifiers() {
|
||||
return {
|
||||
published(query) {
|
||||
query.whereNot('published_at', null);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Relationship mapping.
|
||||
*/
|
||||
|
||||
@@ -17,6 +17,9 @@ export class GetMatchedTransactionBillsTransformer extends Transformer {
|
||||
'transactionNo',
|
||||
'transactionType',
|
||||
'transsactionTypeFormatted',
|
||||
'transactionNormal',
|
||||
'referenceId',
|
||||
'referenceType',
|
||||
];
|
||||
};
|
||||
|
||||
@@ -100,4 +103,29 @@ export class GetMatchedTransactionBillsTransformer extends Transformer {
|
||||
protected transsactionTypeFormatted() {
|
||||
return 'Bill';
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the bill transaction normal (debit or credit).
|
||||
* @returns {string}
|
||||
*/
|
||||
protected transactionNormal() {
|
||||
return 'credit';
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the match transaction reference id.
|
||||
* @param bill
|
||||
* @returns {number}
|
||||
*/
|
||||
protected referenceId(bill) {
|
||||
return bill.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the match transaction referenece type.
|
||||
* @returns {string}
|
||||
*/
|
||||
protected referenceType() {
|
||||
return 'Bill';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ export class GetMatchedTransactionCashflowTransformer extends Transformer {
|
||||
'transactionNo',
|
||||
'transactionType',
|
||||
'transsactionTypeFormatted',
|
||||
'transactionNormal',
|
||||
'referenceId',
|
||||
'referenceType',
|
||||
];
|
||||
@@ -113,10 +114,28 @@ export class GetMatchedTransactionCashflowTransformer extends Transformer {
|
||||
return transaction.transactionTypeFormatted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the cashflow transaction normal (credit or debit).
|
||||
* @param transaction
|
||||
* @returns {string}
|
||||
*/
|
||||
protected transactionNormal(transaction) {
|
||||
return transaction.isCashCredit ? 'credit' : 'debit';
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the cashflow transaction reference id.
|
||||
* @param transaction
|
||||
* @returns {number}
|
||||
*/
|
||||
protected referenceId(transaction) {
|
||||
return transaction.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the cashflow transaction reference type.
|
||||
* @returns {string}
|
||||
*/
|
||||
protected referenceType() {
|
||||
return 'CashflowTransaction';
|
||||
}
|
||||
|
||||
@@ -17,6 +17,9 @@ export class GetMatchedTransactionExpensesTransformer extends Transformer {
|
||||
'transactionNo',
|
||||
'transactionType',
|
||||
'transsactionTypeFormatted',
|
||||
'transactionNormal',
|
||||
'referenceType',
|
||||
'referenceId',
|
||||
];
|
||||
};
|
||||
|
||||
@@ -111,4 +114,29 @@ export class GetMatchedTransactionExpensesTransformer extends Transformer {
|
||||
protected transsactionTypeFormatted() {
|
||||
return 'Expense';
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the expense transaction normal (credit or debit).
|
||||
* @returns {string}
|
||||
*/
|
||||
protected transactionNormal() {
|
||||
return 'credit';
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the transaction reference type.
|
||||
* @returns {string}
|
||||
*/
|
||||
protected referenceType() {
|
||||
return 'Expense';
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the transaction reference id.
|
||||
* @param transaction
|
||||
* @returns {number}
|
||||
*/
|
||||
protected referenceId(transaction) {
|
||||
return transaction.id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,9 @@ export class GetMatchedTransactionInvoicesTransformer extends Transformer {
|
||||
'transactionNo',
|
||||
'transactionType',
|
||||
'transsactionTypeFormatted',
|
||||
'transactionNormal',
|
||||
'referenceType',
|
||||
'referenceId'
|
||||
];
|
||||
};
|
||||
|
||||
@@ -108,4 +111,28 @@ export class GetMatchedTransactionInvoicesTransformer extends Transformer {
|
||||
protected transsactionTypeFormatted(invoice) {
|
||||
return 'Sale invoice';
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the transaction normal of invoice (credit or debit).
|
||||
* @returns {string}
|
||||
*/
|
||||
protected transactionNormal() {
|
||||
return 'debit';
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the transaction reference type.
|
||||
* @returns {string}
|
||||
*/ protected referenceType() {
|
||||
return 'SaleInvoice';
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the transaction reference id.
|
||||
* @param transaction
|
||||
* @returns {number}
|
||||
*/
|
||||
protected referenceId(transaction) {
|
||||
return transaction.id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { sumBy } from 'lodash';
|
||||
import { Transformer } from '@/lib/Transformer/Transformer';
|
||||
import { AccountNormal } from '@/interfaces';
|
||||
|
||||
export class GetMatchedTransactionManualJournalsTransformer extends Transformer {
|
||||
/**
|
||||
@@ -17,6 +19,9 @@ export class GetMatchedTransactionManualJournalsTransformer extends Transformer
|
||||
'transactionNo',
|
||||
'transactionType',
|
||||
'transsactionTypeFormatted',
|
||||
'transactionNormal',
|
||||
'referenceType',
|
||||
'referenceId',
|
||||
];
|
||||
};
|
||||
|
||||
@@ -37,13 +42,20 @@ export class GetMatchedTransactionManualJournalsTransformer extends Transformer
|
||||
return manualJournal.referenceNo;
|
||||
}
|
||||
|
||||
protected total(manualJournal) {
|
||||
const credit = sumBy(manualJournal?.entries, 'credit');
|
||||
const debit = sumBy(manualJournal?.entries, 'debit');
|
||||
|
||||
return debit - credit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the manual journal amount.
|
||||
* @param manualJournal
|
||||
* @returns {number}
|
||||
*/
|
||||
protected amount(manualJournal) {
|
||||
return manualJournal.amount;
|
||||
return Math.abs(this.total(manualJournal));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -107,5 +119,31 @@ export class GetMatchedTransactionManualJournalsTransformer extends Transformer
|
||||
protected transsactionTypeFormatted() {
|
||||
return 'Manual Journal';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the manual journal transaction normal (credit or debit).
|
||||
* @returns {string}
|
||||
*/
|
||||
protected transactionNormal(transaction) {
|
||||
const amount = this.total(transaction);
|
||||
|
||||
return amount >= 0 ? AccountNormal.DEBIT : AccountNormal.CREDIT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the manual journal reference type.
|
||||
* @returns {string}
|
||||
*/
|
||||
protected referenceType() {
|
||||
return 'ManualJournal';
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the manual journal reference id.
|
||||
* @param transaction
|
||||
* @returns {number}
|
||||
*/
|
||||
protected referenceId(transaction) {
|
||||
return transaction.id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import { GetMatchedTransactionsByManualJournals } from './GetMatchedTransactions
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { sortClosestMatchTransactions } from './_utils';
|
||||
import { GetMatchedTransactionsByCashflow } from './GetMatchedTransactionsByCashflow';
|
||||
import { GetMatchedTransactionsByInvoices } from './GetMatchedTransactionsByInvoices';
|
||||
|
||||
@Service()
|
||||
export class GetMatchedTransactions {
|
||||
@@ -16,7 +17,7 @@ export class GetMatchedTransactions {
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private getMatchedInvoicesService: GetMatchedTransactionsByExpenses;
|
||||
private getMatchedInvoicesService: GetMatchedTransactionsByInvoices;
|
||||
|
||||
@Inject()
|
||||
private getMatchedBillsService: GetMatchedTransactionsByBills;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { initialize } from 'objection';
|
||||
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||
import { GetMatchedTransactionBillsTransformer } from './GetMatchedTransactionBillsTransformer';
|
||||
import { GetMatchedTransactionsFilter, MatchedTransactionPOJO } from './types';
|
||||
@@ -22,10 +23,25 @@ export class GetMatchedTransactionsByBills extends GetMatchedTransactionsByType
|
||||
tenantId: number,
|
||||
filter: GetMatchedTransactionsFilter
|
||||
) {
|
||||
const { Bill } = this.tenancy.models(tenantId);
|
||||
const { Bill, MatchedBankTransaction } = this.tenancy.models(tenantId);
|
||||
const knex = this.tenancy.knex(tenantId);
|
||||
|
||||
// Initialize the models metadata.
|
||||
await initialize(knex, [Bill, MatchedBankTransaction]);
|
||||
|
||||
// Retrieves the bill matches.
|
||||
const bills = await Bill.query().onBuild((q) => {
|
||||
q.whereNotExists(Bill.relatedQuery('matchedBankTransaction'));
|
||||
q.withGraphJoined('matchedBankTransaction');
|
||||
q.whereNull('matchedBankTransaction.id');
|
||||
q.modify('published');
|
||||
|
||||
if (filter.fromDate) {
|
||||
q.where('billDate', '>=', filter.fromDate);
|
||||
}
|
||||
if (filter.toDate) {
|
||||
q.where('billDate', '<=', filter.toDate);
|
||||
}
|
||||
q.orderBy('billDate', 'DESC');
|
||||
});
|
||||
|
||||
return this.transformer.transform(
|
||||
|
||||
@@ -27,9 +27,22 @@ export class GetMatchedTransactionsByCashflow extends GetMatchedTransactionsByTy
|
||||
// Initialize the ORM models metadata.
|
||||
await initialize(knex, [CashflowTransaction, MatchedBankTransaction]);
|
||||
|
||||
const transactions = await CashflowTransaction.query()
|
||||
.withGraphJoined('matchedBankTransaction')
|
||||
.whereNull('matchedBankTransaction.id');
|
||||
const transactions = await CashflowTransaction.query().onBuild((q) => {
|
||||
// Not matched to bank transaction.
|
||||
q.withGraphJoined('matchedBankTransaction');
|
||||
q.whereNull('matchedBankTransaction.id');
|
||||
|
||||
// Published.
|
||||
q.modify('published');
|
||||
|
||||
if (filter.fromDate) {
|
||||
q.where('date', '>=', filter.fromDate);
|
||||
}
|
||||
if (filter.toDate) {
|
||||
q.where('date', '<=', filter.toDate);
|
||||
}
|
||||
q.orderBy('date', 'DESC');
|
||||
});
|
||||
|
||||
return this.transformer.transform(
|
||||
tenantId,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { initialize } from 'objection';
|
||||
import { GetMatchedTransactionsFilter, MatchedTransactionPOJO } from './types';
|
||||
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
@@ -23,22 +24,34 @@ export class GetMatchedTransactionsByExpenses extends GetMatchedTransactionsByTy
|
||||
tenantId: number,
|
||||
filter: GetMatchedTransactionsFilter
|
||||
) {
|
||||
const { Expense } = this.tenancy.models(tenantId);
|
||||
const { Expense, MatchedBankTransaction } = this.tenancy.models(tenantId);
|
||||
const knex = this.tenancy.knex(tenantId);
|
||||
|
||||
// Initialize the models metadata.
|
||||
await initialize(knex, [Expense, MatchedBankTransaction]);
|
||||
|
||||
// Retrieve the expense matches.
|
||||
const expenses = await Expense.query().onBuild((query) => {
|
||||
query.whereNotExists(Expense.relatedQuery('matchedBankTransaction'));
|
||||
// Filter out the not matched to bank transactions.
|
||||
query.withGraphJoined('matchedBankTransaction');
|
||||
query.whereNull('matchedBankTransaction.id');
|
||||
|
||||
// Filter the published onyl
|
||||
query.modify('filterByPublished');
|
||||
|
||||
if (filter.fromDate) {
|
||||
query.where('payment_date', '>=', filter.fromDate);
|
||||
query.where('paymentDate', '>=', filter.fromDate);
|
||||
}
|
||||
if (filter.toDate) {
|
||||
query.where('payment_date', '<=', filter.toDate);
|
||||
query.where('paymentDate', '<=', filter.toDate);
|
||||
}
|
||||
if (filter.minAmount) {
|
||||
query.where('total_amount', '>=', filter.minAmount);
|
||||
query.where('totalAmount', '>=', filter.minAmount);
|
||||
}
|
||||
if (filter.maxAmount) {
|
||||
query.where('total_amount', '<=', filter.maxAmount);
|
||||
query.where('totalAmount', '<=', filter.maxAmount);
|
||||
}
|
||||
query.orderBy('paymentDate', 'DESC');
|
||||
});
|
||||
return this.transformer.transform(
|
||||
tenantId,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { initialize } from 'objection';
|
||||
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||
import { GetMatchedTransactionInvoicesTransformer } from './GetMatchedTransactionInvoicesTransformer';
|
||||
import {
|
||||
@@ -6,7 +8,6 @@ import {
|
||||
MatchedTransactionsPOJO,
|
||||
} from './types';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { GetMatchedTransactionsByType } from './GetMatchedTransactionsByType';
|
||||
|
||||
@Service()
|
||||
@@ -27,10 +28,27 @@ export class GetMatchedTransactionsByInvoices extends GetMatchedTransactionsByTy
|
||||
tenantId: number,
|
||||
filter: GetMatchedTransactionsFilter
|
||||
): Promise<MatchedTransactionsPOJO> {
|
||||
const { SaleInvoice } = this.tenancy.models(tenantId);
|
||||
const { SaleInvoice, MatchedBankTransaction } =
|
||||
this.tenancy.models(tenantId);
|
||||
const knex = this.tenancy.knex(tenantId);
|
||||
|
||||
// Initialize the models metadata.
|
||||
await initialize(knex, [SaleInvoice, MatchedBankTransaction]);
|
||||
|
||||
// Retrieve the invoices that not matched, unpaid.
|
||||
const invoices = await SaleInvoice.query().onBuild((q) => {
|
||||
q.whereNotExists(SaleInvoice.relatedQuery('matchedBankTransaction'));
|
||||
q.withGraphJoined('matchedBankTransaction');
|
||||
q.whereNull('matchedBankTransaction.id');
|
||||
q.modify('unpaid');
|
||||
q.modify('published');
|
||||
|
||||
if (filter.fromDate) {
|
||||
q.where('invoiceDate', '>=', filter.fromDate);
|
||||
}
|
||||
if (filter.toDate) {
|
||||
q.where('invoiceDate', '<=', filter.toDate);
|
||||
}
|
||||
q.orderBy('invoiceDate', 'DESC');
|
||||
});
|
||||
|
||||
return this.transformer.transform(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { initialize } from 'objection';
|
||||
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||
import { GetMatchedTransactionManualJournalsTransformer } from './GetMatchedTransactionManualJournalsTransformer';
|
||||
import { GetMatchedTransactionsByType } from './GetMatchedTransactionsByType';
|
||||
@@ -19,12 +20,26 @@ export class GetMatchedTransactionsByManualJournals extends GetMatchedTransactio
|
||||
tenantId: number,
|
||||
filter: Omit<GetMatchedTransactionsFilter, 'transactionType'>
|
||||
) {
|
||||
const { ManualJournal } = this.tenancy.models(tenantId);
|
||||
const { ManualJournal, ManualJournalEntry, MatchedBankTransaction } =
|
||||
this.tenancy.models(tenantId);
|
||||
const knex = this.tenancy.knex(tenantId);
|
||||
|
||||
await initialize(knex, [
|
||||
ManualJournal,
|
||||
ManualJournalEntry,
|
||||
MatchedBankTransaction,
|
||||
]);
|
||||
const accountId = 1000;
|
||||
|
||||
const manualJournals = await ManualJournal.query().onBuild((query) => {
|
||||
query.whereNotExists(
|
||||
ManualJournal.relatedQuery('matchedBankTransaction')
|
||||
);
|
||||
query.withGraphJoined('matchedBankTransaction');
|
||||
query.whereNull('matchedBankTransaction.id');
|
||||
|
||||
query.withGraphJoined('entries');
|
||||
query.where('entries.accountId', accountId);
|
||||
|
||||
query.modify('filterByPublished');
|
||||
|
||||
if (filter.fromDate) {
|
||||
query.where('date', '>=', filter.fromDate);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { isEmpty, sumBy } from 'lodash';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { Knex } from 'knex';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { PromisePool } from '@supercharge/promise-pool';
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
} from './types';
|
||||
import { MatchTransactionsTypes } from './MatchTransactionsTypes';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import { sumMatchTranasctions } from './_utils';
|
||||
|
||||
@Service()
|
||||
export class MatchBankTransactions {
|
||||
@@ -90,9 +91,8 @@ export class MatchBankTransactions {
|
||||
throw new ServiceError(error);
|
||||
}
|
||||
// Calculate the total given matching transactions.
|
||||
const totalMatchedTranasctions = sumBy(
|
||||
validatationResult.results,
|
||||
'amount'
|
||||
const totalMatchedTranasctions = sumMatchTranasctions(
|
||||
validatationResult.results
|
||||
);
|
||||
// Validates the total given matching transcations whether is not equal
|
||||
// uncategorized transaction amount.
|
||||
|
||||
@@ -20,3 +20,12 @@ export const sortClosestMatchTransactions = (
|
||||
),
|
||||
])(matches);
|
||||
};
|
||||
|
||||
export const sumMatchTranasctions = (transactions: Array<any>) => {
|
||||
return transactions.reduce(
|
||||
(total, item) =>
|
||||
total +
|
||||
(item.transactionNormal === 'debit' ? 1 : -1) * parseFloat(item.amount),
|
||||
0
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user