mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-19 14: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;
|
return !!this.uncategorizedTransaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model modifiers.
|
||||||
|
*/
|
||||||
|
static get modifiers() {
|
||||||
|
return {
|
||||||
|
published(query) {
|
||||||
|
query.whereNot('published_at', null);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Relationship mapping.
|
* Relationship mapping.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ export class GetMatchedTransactionBillsTransformer extends Transformer {
|
|||||||
'transactionNo',
|
'transactionNo',
|
||||||
'transactionType',
|
'transactionType',
|
||||||
'transsactionTypeFormatted',
|
'transsactionTypeFormatted',
|
||||||
|
'transactionNormal',
|
||||||
|
'referenceId',
|
||||||
|
'referenceType',
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -100,4 +103,29 @@ export class GetMatchedTransactionBillsTransformer extends Transformer {
|
|||||||
protected transsactionTypeFormatted() {
|
protected transsactionTypeFormatted() {
|
||||||
return 'Bill';
|
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',
|
'transactionNo',
|
||||||
'transactionType',
|
'transactionType',
|
||||||
'transsactionTypeFormatted',
|
'transsactionTypeFormatted',
|
||||||
|
'transactionNormal',
|
||||||
'referenceId',
|
'referenceId',
|
||||||
'referenceType',
|
'referenceType',
|
||||||
];
|
];
|
||||||
@@ -113,10 +114,28 @@ export class GetMatchedTransactionCashflowTransformer extends Transformer {
|
|||||||
return transaction.transactionTypeFormatted;
|
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) {
|
protected referenceId(transaction) {
|
||||||
return transaction.id;
|
return transaction.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the cashflow transaction reference type.
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
protected referenceType() {
|
protected referenceType() {
|
||||||
return 'CashflowTransaction';
|
return 'CashflowTransaction';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ export class GetMatchedTransactionExpensesTransformer extends Transformer {
|
|||||||
'transactionNo',
|
'transactionNo',
|
||||||
'transactionType',
|
'transactionType',
|
||||||
'transsactionTypeFormatted',
|
'transsactionTypeFormatted',
|
||||||
|
'transactionNormal',
|
||||||
|
'referenceType',
|
||||||
|
'referenceId',
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -111,4 +114,29 @@ export class GetMatchedTransactionExpensesTransformer extends Transformer {
|
|||||||
protected transsactionTypeFormatted() {
|
protected transsactionTypeFormatted() {
|
||||||
return 'Expense';
|
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',
|
'transactionNo',
|
||||||
'transactionType',
|
'transactionType',
|
||||||
'transsactionTypeFormatted',
|
'transsactionTypeFormatted',
|
||||||
|
'transactionNormal',
|
||||||
|
'referenceType',
|
||||||
|
'referenceId'
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -108,4 +111,28 @@ export class GetMatchedTransactionInvoicesTransformer extends Transformer {
|
|||||||
protected transsactionTypeFormatted(invoice) {
|
protected transsactionTypeFormatted(invoice) {
|
||||||
return 'Sale 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 { Transformer } from '@/lib/Transformer/Transformer';
|
||||||
|
import { AccountNormal } from '@/interfaces';
|
||||||
|
|
||||||
export class GetMatchedTransactionManualJournalsTransformer extends Transformer {
|
export class GetMatchedTransactionManualJournalsTransformer extends Transformer {
|
||||||
/**
|
/**
|
||||||
@@ -17,6 +19,9 @@ export class GetMatchedTransactionManualJournalsTransformer extends Transformer
|
|||||||
'transactionNo',
|
'transactionNo',
|
||||||
'transactionType',
|
'transactionType',
|
||||||
'transsactionTypeFormatted',
|
'transsactionTypeFormatted',
|
||||||
|
'transactionNormal',
|
||||||
|
'referenceType',
|
||||||
|
'referenceId',
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -37,13 +42,20 @@ export class GetMatchedTransactionManualJournalsTransformer extends Transformer
|
|||||||
return manualJournal.referenceNo;
|
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.
|
* Retrieves the manual journal amount.
|
||||||
* @param manualJournal
|
* @param manualJournal
|
||||||
* @returns {number}
|
* @returns {number}
|
||||||
*/
|
*/
|
||||||
protected amount(manualJournal) {
|
protected amount(manualJournal) {
|
||||||
return manualJournal.amount;
|
return Math.abs(this.total(manualJournal));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -107,5 +119,31 @@ export class GetMatchedTransactionManualJournalsTransformer extends Transformer
|
|||||||
protected transsactionTypeFormatted() {
|
protected transsactionTypeFormatted() {
|
||||||
return 'Manual Journal';
|
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 HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||||
import { sortClosestMatchTransactions } from './_utils';
|
import { sortClosestMatchTransactions } from './_utils';
|
||||||
import { GetMatchedTransactionsByCashflow } from './GetMatchedTransactionsByCashflow';
|
import { GetMatchedTransactionsByCashflow } from './GetMatchedTransactionsByCashflow';
|
||||||
|
import { GetMatchedTransactionsByInvoices } from './GetMatchedTransactionsByInvoices';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class GetMatchedTransactions {
|
export class GetMatchedTransactions {
|
||||||
@@ -16,7 +17,7 @@ export class GetMatchedTransactions {
|
|||||||
private tenancy: HasTenancyService;
|
private tenancy: HasTenancyService;
|
||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
private getMatchedInvoicesService: GetMatchedTransactionsByExpenses;
|
private getMatchedInvoicesService: GetMatchedTransactionsByInvoices;
|
||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
private getMatchedBillsService: GetMatchedTransactionsByBills;
|
private getMatchedBillsService: GetMatchedTransactionsByBills;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
|
import { initialize } from 'objection';
|
||||||
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||||
import { GetMatchedTransactionBillsTransformer } from './GetMatchedTransactionBillsTransformer';
|
import { GetMatchedTransactionBillsTransformer } from './GetMatchedTransactionBillsTransformer';
|
||||||
import { GetMatchedTransactionsFilter, MatchedTransactionPOJO } from './types';
|
import { GetMatchedTransactionsFilter, MatchedTransactionPOJO } from './types';
|
||||||
@@ -22,10 +23,25 @@ export class GetMatchedTransactionsByBills extends GetMatchedTransactionsByType
|
|||||||
tenantId: number,
|
tenantId: number,
|
||||||
filter: GetMatchedTransactionsFilter
|
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) => {
|
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(
|
return this.transformer.transform(
|
||||||
|
|||||||
@@ -27,9 +27,22 @@ export class GetMatchedTransactionsByCashflow extends GetMatchedTransactionsByTy
|
|||||||
// Initialize the ORM models metadata.
|
// Initialize the ORM models metadata.
|
||||||
await initialize(knex, [CashflowTransaction, MatchedBankTransaction]);
|
await initialize(knex, [CashflowTransaction, MatchedBankTransaction]);
|
||||||
|
|
||||||
const transactions = await CashflowTransaction.query()
|
const transactions = await CashflowTransaction.query().onBuild((q) => {
|
||||||
.withGraphJoined('matchedBankTransaction')
|
// Not matched to bank transaction.
|
||||||
.whereNull('matchedBankTransaction.id');
|
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(
|
return this.transformer.transform(
|
||||||
tenantId,
|
tenantId,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
|
import { initialize } from 'objection';
|
||||||
import { GetMatchedTransactionsFilter, MatchedTransactionPOJO } from './types';
|
import { GetMatchedTransactionsFilter, MatchedTransactionPOJO } from './types';
|
||||||
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||||
@@ -23,22 +24,34 @@ export class GetMatchedTransactionsByExpenses extends GetMatchedTransactionsByTy
|
|||||||
tenantId: number,
|
tenantId: number,
|
||||||
filter: GetMatchedTransactionsFilter
|
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) => {
|
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) {
|
if (filter.fromDate) {
|
||||||
query.where('payment_date', '>=', filter.fromDate);
|
query.where('paymentDate', '>=', filter.fromDate);
|
||||||
}
|
}
|
||||||
if (filter.toDate) {
|
if (filter.toDate) {
|
||||||
query.where('payment_date', '<=', filter.toDate);
|
query.where('paymentDate', '<=', filter.toDate);
|
||||||
}
|
}
|
||||||
if (filter.minAmount) {
|
if (filter.minAmount) {
|
||||||
query.where('total_amount', '>=', filter.minAmount);
|
query.where('totalAmount', '>=', filter.minAmount);
|
||||||
}
|
}
|
||||||
if (filter.maxAmount) {
|
if (filter.maxAmount) {
|
||||||
query.where('total_amount', '<=', filter.maxAmount);
|
query.where('totalAmount', '<=', filter.maxAmount);
|
||||||
}
|
}
|
||||||
|
query.orderBy('paymentDate', 'DESC');
|
||||||
});
|
});
|
||||||
return this.transformer.transform(
|
return this.transformer.transform(
|
||||||
tenantId,
|
tenantId,
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import { initialize } from 'objection';
|
||||||
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||||
import { GetMatchedTransactionInvoicesTransformer } from './GetMatchedTransactionInvoicesTransformer';
|
import { GetMatchedTransactionInvoicesTransformer } from './GetMatchedTransactionInvoicesTransformer';
|
||||||
import {
|
import {
|
||||||
@@ -6,7 +8,6 @@ import {
|
|||||||
MatchedTransactionsPOJO,
|
MatchedTransactionsPOJO,
|
||||||
} from './types';
|
} from './types';
|
||||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||||
import { Inject, Service } from 'typedi';
|
|
||||||
import { GetMatchedTransactionsByType } from './GetMatchedTransactionsByType';
|
import { GetMatchedTransactionsByType } from './GetMatchedTransactionsByType';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
@@ -27,10 +28,27 @@ export class GetMatchedTransactionsByInvoices extends GetMatchedTransactionsByTy
|
|||||||
tenantId: number,
|
tenantId: number,
|
||||||
filter: GetMatchedTransactionsFilter
|
filter: GetMatchedTransactionsFilter
|
||||||
): Promise<MatchedTransactionsPOJO> {
|
): 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) => {
|
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(
|
return this.transformer.transform(
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
|
import { initialize } from 'objection';
|
||||||
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||||
import { GetMatchedTransactionManualJournalsTransformer } from './GetMatchedTransactionManualJournalsTransformer';
|
import { GetMatchedTransactionManualJournalsTransformer } from './GetMatchedTransactionManualJournalsTransformer';
|
||||||
import { GetMatchedTransactionsByType } from './GetMatchedTransactionsByType';
|
import { GetMatchedTransactionsByType } from './GetMatchedTransactionsByType';
|
||||||
@@ -19,12 +20,26 @@ export class GetMatchedTransactionsByManualJournals extends GetMatchedTransactio
|
|||||||
tenantId: number,
|
tenantId: number,
|
||||||
filter: Omit<GetMatchedTransactionsFilter, 'transactionType'>
|
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) => {
|
const manualJournals = await ManualJournal.query().onBuild((query) => {
|
||||||
query.whereNotExists(
|
query.withGraphJoined('matchedBankTransaction');
|
||||||
ManualJournal.relatedQuery('matchedBankTransaction')
|
query.whereNull('matchedBankTransaction.id');
|
||||||
);
|
|
||||||
|
query.withGraphJoined('entries');
|
||||||
|
query.where('entries.accountId', accountId);
|
||||||
|
|
||||||
|
query.modify('filterByPublished');
|
||||||
|
|
||||||
if (filter.fromDate) {
|
if (filter.fromDate) {
|
||||||
query.where('date', '>=', 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 { Knex } from 'knex';
|
||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
import { PromisePool } from '@supercharge/promise-pool';
|
import { PromisePool } from '@supercharge/promise-pool';
|
||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
} from './types';
|
} from './types';
|
||||||
import { MatchTransactionsTypes } from './MatchTransactionsTypes';
|
import { MatchTransactionsTypes } from './MatchTransactionsTypes';
|
||||||
import { ServiceError } from '@/exceptions';
|
import { ServiceError } from '@/exceptions';
|
||||||
|
import { sumMatchTranasctions } from './_utils';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class MatchBankTransactions {
|
export class MatchBankTransactions {
|
||||||
@@ -90,9 +91,8 @@ export class MatchBankTransactions {
|
|||||||
throw new ServiceError(error);
|
throw new ServiceError(error);
|
||||||
}
|
}
|
||||||
// Calculate the total given matching transactions.
|
// Calculate the total given matching transactions.
|
||||||
const totalMatchedTranasctions = sumBy(
|
const totalMatchedTranasctions = sumMatchTranasctions(
|
||||||
validatationResult.results,
|
validatationResult.results
|
||||||
'amount'
|
|
||||||
);
|
);
|
||||||
// Validates the total given matching transcations whether is not equal
|
// Validates the total given matching transcations whether is not equal
|
||||||
// uncategorized transaction amount.
|
// uncategorized transaction amount.
|
||||||
|
|||||||
@@ -20,3 +20,12 @@ export const sortClosestMatchTransactions = (
|
|||||||
),
|
),
|
||||||
])(matches);
|
])(matches);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const sumMatchTranasctions = (transactions: Array<any>) => {
|
||||||
|
return transactions.reduce(
|
||||||
|
(total, item) =>
|
||||||
|
total +
|
||||||
|
(item.transactionNormal === 'debit' ? 1 : -1) * parseFloat(item.amount),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -8,16 +8,26 @@ import {
|
|||||||
} from '../withBankingActions';
|
} from '../withBankingActions';
|
||||||
import { CategorizeTransactionTabsBoot } from './CategorizeTransactionTabsBoot';
|
import { CategorizeTransactionTabsBoot } from './CategorizeTransactionTabsBoot';
|
||||||
import { withBanking } from '../withBanking';
|
import { withBanking } from '../withBanking';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
interface CategorizeTransactionAsideProps extends WithBankingActionsProps {}
|
interface CategorizeTransactionAsideProps extends WithBankingActionsProps {}
|
||||||
|
|
||||||
function CategorizeTransactionAsideRoot({
|
function CategorizeTransactionAsideRoot({
|
||||||
// #withBankingActions
|
// #withBankingActions
|
||||||
closeMatchingTransactionAside,
|
closeMatchingTransactionAside,
|
||||||
|
closeReconcileMatchingTransaction,
|
||||||
|
|
||||||
// #withBanking
|
// #withBanking
|
||||||
selectedUncategorizedTransactionId,
|
selectedUncategorizedTransactionId,
|
||||||
}: CategorizeTransactionAsideProps) {
|
}: CategorizeTransactionAsideProps) {
|
||||||
|
//
|
||||||
|
useEffect(
|
||||||
|
() => () => {
|
||||||
|
closeReconcileMatchingTransaction();
|
||||||
|
},
|
||||||
|
[closeReconcileMatchingTransaction],
|
||||||
|
);
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
closeMatchingTransactionAside();
|
closeMatchingTransactionAside();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import * as R from 'ramda';
|
import * as R from 'ramda';
|
||||||
import { Button, Intent, Position, Tag } from '@blueprintjs/core';
|
import { Button, Intent, Position, Tag } from '@blueprintjs/core';
|
||||||
import { Form, Formik, FormikValues, useFormikContext } from 'formik';
|
import {
|
||||||
|
Form,
|
||||||
|
Formik,
|
||||||
|
FormikHelpers,
|
||||||
|
FormikValues,
|
||||||
|
useFormikContext,
|
||||||
|
} from 'formik';
|
||||||
|
import moment from 'moment';
|
||||||
import {
|
import {
|
||||||
AccountsSelect,
|
AccountsSelect,
|
||||||
AppToaster,
|
AppToaster,
|
||||||
@@ -26,6 +33,7 @@ import { useCreateCashflowTransaction } from '@/hooks/query';
|
|||||||
import { useAccountTransactionsContext } from '../../AccountTransactions/AccountTransactionsProvider';
|
import { useAccountTransactionsContext } from '../../AccountTransactions/AccountTransactionsProvider';
|
||||||
import { MatchingReconcileFormSchema } from './MatchingReconcileTransactionForm.schema';
|
import { MatchingReconcileFormSchema } from './MatchingReconcileTransactionForm.schema';
|
||||||
import { initialValues, transformToReq } from './_utils';
|
import { initialValues, transformToReq } from './_utils';
|
||||||
|
import { withBanking } from '../../withBanking';
|
||||||
|
|
||||||
interface MatchingReconcileTransactionFormProps {
|
interface MatchingReconcileTransactionFormProps {
|
||||||
onSubmitSuccess?: (values: any) => void;
|
onSubmitSuccess?: (values: any) => void;
|
||||||
@@ -33,6 +41,7 @@ interface MatchingReconcileTransactionFormProps {
|
|||||||
|
|
||||||
function MatchingReconcileTransactionFormRoot({
|
function MatchingReconcileTransactionFormRoot({
|
||||||
closeReconcileMatchingTransaction,
|
closeReconcileMatchingTransaction,
|
||||||
|
reconcileMatchingTransactionPendingAmount,
|
||||||
|
|
||||||
// #props¿
|
// #props¿
|
||||||
onSubmitSuccess,
|
onSubmitSuccess,
|
||||||
@@ -43,12 +52,17 @@ function MatchingReconcileTransactionFormRoot({
|
|||||||
|
|
||||||
const { accountId } = useAccountTransactionsContext();
|
const { accountId } = useAccountTransactionsContext();
|
||||||
|
|
||||||
|
// Handles the aside close.
|
||||||
const handleAsideClose = () => {
|
const handleAsideClose = () => {
|
||||||
closeReconcileMatchingTransaction();
|
closeReconcileMatchingTransaction();
|
||||||
};
|
};
|
||||||
|
// Handle the form submitting.
|
||||||
const handleSubmit = (
|
const handleSubmit = (
|
||||||
values: MatchingReconcileTransactionValues,
|
values: MatchingReconcileTransactionValues,
|
||||||
{ setSubmitting }: FormikValues<MatchingReconcileTransactionValues>,
|
{
|
||||||
|
setSubmitting,
|
||||||
|
setErrors,
|
||||||
|
}: FormikHelpers<MatchingReconcileTransactionValues>,
|
||||||
) => {
|
) => {
|
||||||
setSubmitting(true);
|
setSubmitting(true);
|
||||||
const _values = transformToReq(values, accountId);
|
const _values = transformToReq(values, accountId);
|
||||||
@@ -67,14 +81,31 @@ function MatchingReconcileTransactionFormRoot({
|
|||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
|
if (
|
||||||
AppToaster.show({
|
error.response.data?.errors?.find(
|
||||||
message: 'Something went wrong.',
|
(e) => e.type === 'BRANCH_ID_REQUIRED',
|
||||||
intent: Intent.DANGER,
|
)
|
||||||
});
|
) {
|
||||||
|
setErrors({
|
||||||
|
branchId: 'The branch is required.',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
AppToaster.show({
|
||||||
|
message: 'Something went wrong.',
|
||||||
|
intent: Intent.DANGER,
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const _initialValues = {
|
||||||
|
...initialValues,
|
||||||
|
amount: Math.abs(reconcileMatchingTransactionPendingAmount) || 0,
|
||||||
|
date: moment().format('YYYY-MM-DD'),
|
||||||
|
type:
|
||||||
|
reconcileMatchingTransactionPendingAmount > 0 ? 'deposit' : 'withdrawal',
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Aside
|
<Aside
|
||||||
title={'Create Reconcile Transactions'}
|
title={'Create Reconcile Transactions'}
|
||||||
@@ -84,7 +115,7 @@ function MatchingReconcileTransactionFormRoot({
|
|||||||
<MatchingReconcileTransactionBoot>
|
<MatchingReconcileTransactionBoot>
|
||||||
<Formik
|
<Formik
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
initialValues={initialValues}
|
initialValues={_initialValues}
|
||||||
validationSchema={MatchingReconcileFormSchema}
|
validationSchema={MatchingReconcileFormSchema}
|
||||||
>
|
>
|
||||||
<Form className={styles.form}>
|
<Form className={styles.form}>
|
||||||
@@ -102,9 +133,12 @@ function MatchingReconcileTransactionFormRoot({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MatchingReconcileTransactionForm = R.compose(withBankingActions)(
|
export const MatchingReconcileTransactionForm = R.compose(
|
||||||
MatchingReconcileTransactionFormRoot,
|
withBankingActions,
|
||||||
);
|
withBanking(({ reconcileMatchingTransactionPendingAmount }) => ({
|
||||||
|
reconcileMatchingTransactionPendingAmount,
|
||||||
|
})),
|
||||||
|
)(MatchingReconcileTransactionFormRoot);
|
||||||
|
|
||||||
function ReconcileMatchingType() {
|
function ReconcileMatchingType() {
|
||||||
const { setFieldValue, values } =
|
const { setFieldValue, values } =
|
||||||
@@ -112,6 +146,7 @@ function ReconcileMatchingType() {
|
|||||||
|
|
||||||
const handleChange = (value: string) => {
|
const handleChange = (value: string) => {
|
||||||
setFieldValue('type', value);
|
setFieldValue('type', value);
|
||||||
|
setFieldValue('category');
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<ContentTabs
|
<ContentTabs
|
||||||
@@ -126,22 +161,27 @@ function ReconcileMatchingType() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function CreateReconcileTransactionContent() {
|
function CreateReconcileTransactionContent() {
|
||||||
const { accounts, branches } = useMatchingReconcileTransactionBoot();
|
const { branches } = useMatchingReconcileTransactionBoot();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box className={styles.content}>
|
<Box className={styles.content}>
|
||||||
<ReconcileMatchingType />
|
<ReconcileMatchingType />
|
||||||
|
|
||||||
<FFormGroup label={'Date'} name={'date'}>
|
<FFormGroup label={'Date'} name={'date'} fastField>
|
||||||
<FDateInput
|
<FDateInput
|
||||||
{...momentFormatter('YYYY/MM/DD')}
|
{...momentFormatter('YYYY/MM/DD')}
|
||||||
name={'date'}
|
name={'date'}
|
||||||
formatDate={(date) => date.toLocaleString()}
|
|
||||||
popoverProps={{
|
popoverProps={{
|
||||||
|
minimal: false,
|
||||||
position: Position.LEFT,
|
position: Position.LEFT,
|
||||||
|
modifiers: {
|
||||||
|
preventOverflow: { enabled: true },
|
||||||
|
},
|
||||||
|
boundary: 'viewport',
|
||||||
}}
|
}}
|
||||||
inputProps={{ fill: true }}
|
inputProps={{ fill: true }}
|
||||||
fill
|
fill
|
||||||
|
fastField
|
||||||
/>
|
/>
|
||||||
</FFormGroup>
|
</FFormGroup>
|
||||||
|
|
||||||
@@ -149,45 +189,79 @@ function CreateReconcileTransactionContent() {
|
|||||||
label={'Amount'}
|
label={'Amount'}
|
||||||
name={'amount'}
|
name={'amount'}
|
||||||
labelInfo={<Tag minimal>Required</Tag>}
|
labelInfo={<Tag minimal>Required</Tag>}
|
||||||
|
fastField
|
||||||
>
|
>
|
||||||
<FMoneyInputGroup name={'amount'} />
|
<FMoneyInputGroup name={'amount'} fastField />
|
||||||
</FFormGroup>
|
</FFormGroup>
|
||||||
|
|
||||||
<FFormGroup
|
<MatchingReconcileCategoryField />
|
||||||
label={'Category'}
|
|
||||||
name={'category'}
|
|
||||||
labelInfo={<Tag minimal>Required</Tag>}
|
|
||||||
>
|
|
||||||
<AccountsSelect
|
|
||||||
name={'category'}
|
|
||||||
items={accounts}
|
|
||||||
popoverProps={{ minimal: false, position: Position.LEFT }}
|
|
||||||
/>
|
|
||||||
</FFormGroup>
|
|
||||||
|
|
||||||
<FFormGroup
|
<FFormGroup
|
||||||
label={'Memo'}
|
label={'Memo'}
|
||||||
name={'memo'}
|
name={'memo'}
|
||||||
labelInfo={<Tag minimal>Required</Tag>}
|
labelInfo={<Tag minimal>Required</Tag>}
|
||||||
|
fastField
|
||||||
>
|
>
|
||||||
<FInputGroup name={'memo'} />
|
<FInputGroup name={'memo'} fastField />
|
||||||
</FFormGroup>
|
</FFormGroup>
|
||||||
|
|
||||||
<FFormGroup label={'Reference No.'} name={'reference_no'}>
|
<FFormGroup label={'Reference No.'} name={'reference_no'} fastField>
|
||||||
<FInputGroup name={'reference_no'} />
|
<FInputGroup name={'reference_no'} />
|
||||||
</FFormGroup>
|
</FFormGroup>
|
||||||
|
|
||||||
<FFormGroup name={'branchId'} label={'Branch'}>
|
<FFormGroup
|
||||||
|
name={'branchId'}
|
||||||
|
label={'Branch'}
|
||||||
|
labelInfo={<Tag minimal>Required</Tag>}
|
||||||
|
fastField
|
||||||
|
>
|
||||||
<BranchSelect
|
<BranchSelect
|
||||||
name={'branchId'}
|
name={'branchId'}
|
||||||
branches={branches}
|
branches={branches}
|
||||||
popoverProps={{ minimal: true }}
|
popoverProps={{
|
||||||
|
minimal: false,
|
||||||
|
position: Position.LEFT,
|
||||||
|
modifiers: {
|
||||||
|
preventOverflow: { enabled: true },
|
||||||
|
},
|
||||||
|
boundary: 'viewport',
|
||||||
|
}}
|
||||||
|
fastField
|
||||||
/>
|
/>
|
||||||
</FFormGroup>
|
</FFormGroup>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function MatchingReconcileCategoryField() {
|
||||||
|
const { accounts } = useMatchingReconcileTransactionBoot();
|
||||||
|
const { values } = useFormikContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FFormGroup
|
||||||
|
label={'Category'}
|
||||||
|
name={'category'}
|
||||||
|
labelInfo={<Tag minimal>Required</Tag>}
|
||||||
|
fastField
|
||||||
|
>
|
||||||
|
<AccountsSelect
|
||||||
|
name={'category'}
|
||||||
|
items={accounts}
|
||||||
|
popoverProps={{
|
||||||
|
minimal: false,
|
||||||
|
position: Position.LEFT,
|
||||||
|
modifiers: {
|
||||||
|
preventOverflow: { enabled: true },
|
||||||
|
},
|
||||||
|
boundary: 'viewport',
|
||||||
|
}}
|
||||||
|
filterByRootTypes={values.type === 'deposit' ? 'income' : 'expense'}
|
||||||
|
fastField
|
||||||
|
/>
|
||||||
|
</FFormGroup>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function MatchingReconcileTransactionFooter() {
|
function MatchingReconcileTransactionFooter() {
|
||||||
const { isSubmitting } = useFormikContext();
|
const { isSubmitting } = useFormikContext();
|
||||||
|
|
||||||
|
|||||||
@@ -69,6 +69,18 @@ function MatchingBankTransactionRoot({
|
|||||||
closeMatchingTransactionAside();
|
closeMatchingTransactionAside();
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
|
if (
|
||||||
|
err.response?.data.errors.find(
|
||||||
|
(e) => e.type === 'TOTAL_MATCHING_TRANSACTIONS_INVALID',
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
AppToaster.show({
|
||||||
|
message: `The total amount does not equal the uncategorized transaction.`,
|
||||||
|
intent: Intent.DANGER,
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
AppToaster.show({
|
AppToaster.show({
|
||||||
intent: Intent.DANGER,
|
intent: Intent.DANGER,
|
||||||
message: 'Something went wrong.',
|
message: 'Something went wrong.',
|
||||||
@@ -103,9 +115,6 @@ const MatchingBankTransactionFormContent = R.compose(
|
|||||||
})),
|
})),
|
||||||
)(
|
)(
|
||||||
({
|
({
|
||||||
// #withBankingActions
|
|
||||||
closeMatchingTransactionAside,
|
|
||||||
|
|
||||||
// #withBanking
|
// #withBanking
|
||||||
openReconcileMatchingTransaction,
|
openReconcileMatchingTransaction,
|
||||||
}) => {
|
}) => {
|
||||||
@@ -302,7 +311,7 @@ const MatchTransactionFooter = R.compose(withBankingActions)(
|
|||||||
submitForm();
|
submitForm();
|
||||||
};
|
};
|
||||||
const handleReconcileTransaction = () => {
|
const handleReconcileTransaction = () => {
|
||||||
openReconcileMatchingTransaction();
|
openReconcileMatchingTransaction(totalPending);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -334,8 +343,8 @@ const MatchTransactionFooter = R.compose(withBankingActions)(
|
|||||||
intent={Intent.PRIMARY}
|
intent={Intent.PRIMARY}
|
||||||
style={{ minWidth: 85 }}
|
style={{ minWidth: 85 }}
|
||||||
onClick={handleSubmitBtnClick}
|
onClick={handleSubmitBtnClick}
|
||||||
loading={isSubmitting}
|
// loading={isSubmitting}
|
||||||
disabled={submitDisabled}
|
// disabled={submitDisabled}
|
||||||
>
|
>
|
||||||
Match
|
Match
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -29,7 +29,9 @@ export const useGetPendingAmountMatched = () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
const totalMatchedAmount = matchedItems.reduce(
|
const totalMatchedAmount = matchedItems.reduce(
|
||||||
(total, item) => total + parseFloat(item.amount),
|
(total, item) =>
|
||||||
|
total +
|
||||||
|
(item.transactionNormal === 'debit' ? 1 : -1) * parseFloat(item.amount),
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
const amount = uncategorizedTransaction.amount;
|
const amount = uncategorizedTransaction.amount;
|
||||||
|
|||||||
@@ -9,7 +9,10 @@ export const withBanking = (mapState) => {
|
|||||||
selectedUncategorizedTransactionId:
|
selectedUncategorizedTransactionId:
|
||||||
state.plaid.uncategorizedTransactionIdForMatching,
|
state.plaid.uncategorizedTransactionIdForMatching,
|
||||||
openReconcileMatchingTransaction:
|
openReconcileMatchingTransaction:
|
||||||
state.plaid.openReconcileMatchingTransaction,
|
state.plaid.openReconcileMatchingTransaction.isOpen,
|
||||||
|
|
||||||
|
reconcileMatchingTransactionPendingAmount:
|
||||||
|
state.plaid.openReconcileMatchingTransaction.pending,
|
||||||
};
|
};
|
||||||
return mapState ? mapState(mapped, state, props) : mapped;
|
return mapState ? mapState(mapped, state, props) : mapped;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export interface WithBankingActionsProps {
|
|||||||
setUncategorizedTransactionIdForMatching: (
|
setUncategorizedTransactionIdForMatching: (
|
||||||
uncategorizedTransactionId: number,
|
uncategorizedTransactionId: number,
|
||||||
) => void;
|
) => void;
|
||||||
openReconcileMatchingTransaction: () => void;
|
openReconcileMatchingTransaction: (pendingAmount: number) => void;
|
||||||
closeReconcileMatchingTransaction: () => void;
|
closeReconcileMatchingTransaction: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,8 +24,8 @@ const mapDipatchToProps = (dispatch: any): WithBankingActionsProps => ({
|
|||||||
dispatch(
|
dispatch(
|
||||||
setUncategorizedTransactionIdForMatching(uncategorizedTransactionId),
|
setUncategorizedTransactionIdForMatching(uncategorizedTransactionId),
|
||||||
),
|
),
|
||||||
openReconcileMatchingTransaction: () =>
|
openReconcileMatchingTransaction: (pendingAmount: number) =>
|
||||||
dispatch(openReconcileMatchingTransaction()),
|
dispatch(openReconcileMatchingTransaction({ pending: pendingAmount })),
|
||||||
closeReconcileMatchingTransaction: () =>
|
closeReconcileMatchingTransaction: () =>
|
||||||
dispatch(closeReconcileMatchingTransaction()),
|
dispatch(closeReconcileMatchingTransaction()),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ interface StorePlaidState {
|
|||||||
plaidToken: string;
|
plaidToken: string;
|
||||||
openMatchingTransactionAside: boolean;
|
openMatchingTransactionAside: boolean;
|
||||||
uncategorizedTransactionIdForMatching: number | null;
|
uncategorizedTransactionIdForMatching: number | null;
|
||||||
openReconcileMatchingTransaction: boolean;
|
openReconcileMatchingTransaction: { isOpen: boolean; pending: number };
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PlaidSlice = createSlice({
|
export const PlaidSlice = createSlice({
|
||||||
@@ -13,7 +13,10 @@ export const PlaidSlice = createSlice({
|
|||||||
plaidToken: '',
|
plaidToken: '',
|
||||||
openMatchingTransactionAside: false,
|
openMatchingTransactionAside: false,
|
||||||
uncategorizedTransactionIdForMatching: null,
|
uncategorizedTransactionIdForMatching: null,
|
||||||
openReconcileMatchingTransaction: false,
|
openReconcileMatchingTransaction: {
|
||||||
|
isOpen: false,
|
||||||
|
pending: 0,
|
||||||
|
},
|
||||||
} as StorePlaidState,
|
} as StorePlaidState,
|
||||||
reducers: {
|
reducers: {
|
||||||
setPlaidId: (state: StorePlaidState, action: PayloadAction<string>) => {
|
setPlaidId: (state: StorePlaidState, action: PayloadAction<string>) => {
|
||||||
@@ -37,12 +40,17 @@ export const PlaidSlice = createSlice({
|
|||||||
state.uncategorizedTransactionIdForMatching = null;
|
state.uncategorizedTransactionIdForMatching = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
openReconcileMatchingTransaction: (state: StorePlaidState) => {
|
openReconcileMatchingTransaction: (
|
||||||
state.openReconcileMatchingTransaction = true;
|
state: StorePlaidState,
|
||||||
|
action: PayloadAction<{ pending: number }>,
|
||||||
|
) => {
|
||||||
|
state.openReconcileMatchingTransaction.isOpen = true;
|
||||||
|
state.openReconcileMatchingTransaction.pending = action.payload.pending;
|
||||||
},
|
},
|
||||||
|
|
||||||
closeReconcileMatchingTransaction: (state: StorePlaidState) => {
|
closeReconcileMatchingTransaction: (state: StorePlaidState) => {
|
||||||
state.openReconcileMatchingTransaction = false;
|
state.openReconcileMatchingTransaction.isOpen = false;
|
||||||
|
state.openReconcileMatchingTransaction.pending = 0;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user