mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-21 07:10:33 +00:00
feat: cashflow transaction matching
This commit is contained in:
@@ -0,0 +1,123 @@
|
|||||||
|
import { Transformer } from '@/lib/Transformer/Transformer';
|
||||||
|
|
||||||
|
export class GetMatchedTransactionCashflowTransformer extends Transformer {
|
||||||
|
/**
|
||||||
|
* Include these attributes to sale credit note object.
|
||||||
|
* @returns {Array}
|
||||||
|
*/
|
||||||
|
public includeAttributes = (): string[] => {
|
||||||
|
return [
|
||||||
|
'referenceNo',
|
||||||
|
'amount',
|
||||||
|
'amountFormatted',
|
||||||
|
'transactionNo',
|
||||||
|
'date',
|
||||||
|
'dateFormatted',
|
||||||
|
'transactionId',
|
||||||
|
'transactionNo',
|
||||||
|
'transactionType',
|
||||||
|
'transsactionTypeFormatted',
|
||||||
|
'referenceId',
|
||||||
|
'referenceType',
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 transaction amount.
|
||||||
|
* @param transaction
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
protected amount(transaction) {
|
||||||
|
return transaction.amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the transaction formatted amount.
|
||||||
|
* @param transaction
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected amountFormatted(transaction) {
|
||||||
|
return this.formatNumber(transaction.amount, {
|
||||||
|
currencyCode: transaction.currencyCode,
|
||||||
|
money: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the date of the invoice.
|
||||||
|
* @param invoice
|
||||||
|
* @returns {Date}
|
||||||
|
*/
|
||||||
|
protected date(transaction) {
|
||||||
|
return transaction.date;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format the date of the invoice.
|
||||||
|
* @param invoice
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected dateFormatted(transaction) {
|
||||||
|
return this.formatDate(transaction.date);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the transaction ID of the invoice.
|
||||||
|
* @param invoice
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
protected transactionId(transaction) {
|
||||||
|
return transaction.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the invoice transaction number.
|
||||||
|
* @param invoice
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected transactionNo(transaction) {
|
||||||
|
return transaction.transactionNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the invoice transaction type.
|
||||||
|
* @param invoice
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
protected transactionType(transaction) {
|
||||||
|
return transaction.transactionType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the invoice formatted transaction type.
|
||||||
|
* @param invoice
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
protected transsactionTypeFormatted(transaction) {
|
||||||
|
return transaction.transactionTypeFormatted;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected referenceId(transaction) {
|
||||||
|
return transaction.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected referenceType() {
|
||||||
|
return 'CashflowTransaction';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -49,7 +49,7 @@ export class GetMatchedTransactionInvoicesTransformer extends Transformer {
|
|||||||
* @param invoice
|
* @param invoice
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
protected formatAmount(invoice) {
|
protected amountFormatted(invoice) {
|
||||||
return this.formatNumber(invoice.dueAmount, {
|
return this.formatNumber(invoice.dueAmount, {
|
||||||
currencyCode: invoice.currencyCode,
|
currencyCode: invoice.currencyCode,
|
||||||
money: true,
|
money: true,
|
||||||
@@ -79,7 +79,7 @@ export class GetMatchedTransactionInvoicesTransformer extends Transformer {
|
|||||||
* @param invoice
|
* @param invoice
|
||||||
* @returns {number}
|
* @returns {number}
|
||||||
*/
|
*/
|
||||||
protected getTransactionId(invoice) {
|
protected transactionId(invoice) {
|
||||||
return invoice.id;
|
return invoice.id;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { GetMatchedTransactionsByBills } from './GetMatchedTransactionsByBills';
|
|||||||
import { GetMatchedTransactionsByManualJournals } from './GetMatchedTransactionsByManualJournals';
|
import { GetMatchedTransactionsByManualJournals } from './GetMatchedTransactionsByManualJournals';
|
||||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||||
import { sortClosestMatchTransactions } from './_utils';
|
import { sortClosestMatchTransactions } from './_utils';
|
||||||
|
import { GetMatchedTransactionsByCashflow } from './GetMatchedTransactionsByCashflow';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class GetMatchedTransactions {
|
export class GetMatchedTransactions {
|
||||||
@@ -26,6 +27,9 @@ export class GetMatchedTransactions {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private getMatchedExpensesService: GetMatchedTransactionsByExpenses;
|
private getMatchedExpensesService: GetMatchedTransactionsByExpenses;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private getMatchedCashflowService: GetMatchedTransactionsByCashflow;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registered matched transactions types.
|
* Registered matched transactions types.
|
||||||
*/
|
*/
|
||||||
@@ -35,6 +39,7 @@ export class GetMatchedTransactions {
|
|||||||
{ type: 'Bill', service: this.getMatchedBillsService },
|
{ type: 'Bill', service: this.getMatchedBillsService },
|
||||||
{ type: 'Expense', service: this.getMatchedExpensesService },
|
{ type: 'Expense', service: this.getMatchedExpensesService },
|
||||||
{ type: 'ManualJournal', service: this.getMatchedManualJournalService },
|
{ type: 'ManualJournal', service: this.getMatchedManualJournalService },
|
||||||
|
{ type: 'Cashflow', service: this.getMatchedCashflowService },
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import { initialize } from 'objection';
|
||||||
|
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||||
|
import { GetMatchedTransactionsByType } from './GetMatchedTransactionsByType';
|
||||||
|
import { GetMatchedTransactionCashflowTransformer } from './GetMatchedTransactionCashflowTransformer';
|
||||||
|
import { GetMatchedTransactionsFilter } from './types';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class GetMatchedTransactionsByCashflow extends GetMatchedTransactionsByType {
|
||||||
|
@Inject()
|
||||||
|
private transformer: TransformerInjectable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the matched transactions of cash flow.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {GetMatchedTransactionsFilter} filter
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async getMatchedTransactions(
|
||||||
|
tenantId: number,
|
||||||
|
filter: Omit<GetMatchedTransactionsFilter, 'transactionType'>
|
||||||
|
) {
|
||||||
|
const { CashflowTransaction, MatchedBankTransaction } =
|
||||||
|
this.tenancy.models(tenantId);
|
||||||
|
const knex = this.tenancy.knex(tenantId);
|
||||||
|
|
||||||
|
// Initialize the ORM models metadata.
|
||||||
|
await initialize(knex, [CashflowTransaction, MatchedBankTransaction]);
|
||||||
|
|
||||||
|
const transactions = await CashflowTransaction.query()
|
||||||
|
.withGraphJoined('matchedBankTransaction')
|
||||||
|
.whereNull('matchedBankTransaction.id');
|
||||||
|
|
||||||
|
return this.transformer.transform(
|
||||||
|
tenantId,
|
||||||
|
transactions,
|
||||||
|
new GetMatchedTransactionCashflowTransformer()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the matched transaction of cash flow.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {number} transactionId
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async getMatchedTransaction(tenantId: number, transactionId: number) {
|
||||||
|
const { CashflowTransaction, MatchedBankTransaction } =
|
||||||
|
this.tenancy.models(tenantId);
|
||||||
|
const knex = this.tenancy.knex(tenantId);
|
||||||
|
|
||||||
|
// Initialize the ORM models metadata.
|
||||||
|
await initialize(knex, [CashflowTransaction, MatchedBankTransaction]);
|
||||||
|
|
||||||
|
const transactions = await CashflowTransaction.query()
|
||||||
|
.findById(transactionId)
|
||||||
|
.withGraphJoined('matchedBankTransaction')
|
||||||
|
.whereNull('matchedBankTransaction.id')
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
|
return this.transformer.transform(
|
||||||
|
tenantId,
|
||||||
|
transactions,
|
||||||
|
new GetMatchedTransactionCashflowTransformer()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,8 @@ import { GetMatchedTransactionsByBills } from './GetMatchedTransactionsByBills';
|
|||||||
import { GetMatchedTransactionsByManualJournals } from './GetMatchedTransactionsByManualJournals';
|
import { GetMatchedTransactionsByManualJournals } from './GetMatchedTransactionsByManualJournals';
|
||||||
import { MatchTransactionsTypesRegistry } from './MatchTransactionsTypesRegistry';
|
import { MatchTransactionsTypesRegistry } from './MatchTransactionsTypesRegistry';
|
||||||
import { GetMatchedTransactionsByInvoices } from './GetMatchedTransactionsByInvoices';
|
import { GetMatchedTransactionsByInvoices } from './GetMatchedTransactionsByInvoices';
|
||||||
|
import { GetMatchedTransactionCashflowTransformer } from './GetMatchedTransactionCashflowTransformer';
|
||||||
|
import { GetMatchedTransactionsByCashflow } from './GetMatchedTransactionsByCashflow';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class MatchTransactionsTypes {
|
export class MatchTransactionsTypes {
|
||||||
@@ -25,6 +27,10 @@ export class MatchTransactionsTypes {
|
|||||||
type: 'ManualJournal',
|
type: 'ManualJournal',
|
||||||
service: GetMatchedTransactionsByManualJournals,
|
service: GetMatchedTransactionsByManualJournals,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'CashflowTransaction',
|
||||||
|
service: GetMatchedTransactionsByCashflow,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,11 +25,18 @@ import {
|
|||||||
import { useCreateCashflowTransaction } from '@/hooks/query';
|
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 } from './_utils';
|
import { initialValues, transformToReq } from './_utils';
|
||||||
|
|
||||||
|
interface MatchingReconcileTransactionFormProps {
|
||||||
|
onSubmitSuccess?: (values: any) => void;
|
||||||
|
}
|
||||||
|
|
||||||
function MatchingReconcileTransactionFormRoot({
|
function MatchingReconcileTransactionFormRoot({
|
||||||
closeReconcileMatchingTransaction,
|
closeReconcileMatchingTransaction,
|
||||||
}) {
|
|
||||||
|
// #props¿
|
||||||
|
onSubmitSuccess,
|
||||||
|
}: MatchingReconcileTransactionFormProps) {
|
||||||
// Mutation create cashflow transaction.
|
// Mutation create cashflow transaction.
|
||||||
const { mutateAsync: createCashflowTransactionMutate } =
|
const { mutateAsync: createCashflowTransactionMutate } =
|
||||||
useCreateCashflowTransaction();
|
useCreateCashflowTransaction();
|
||||||
@@ -47,7 +54,7 @@ function MatchingReconcileTransactionFormRoot({
|
|||||||
const _values = transformToReq(values, accountId);
|
const _values = transformToReq(values, accountId);
|
||||||
|
|
||||||
createCashflowTransactionMutate(_values)
|
createCashflowTransactionMutate(_values)
|
||||||
.then(() => {
|
.then((res) => {
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
|
|
||||||
AppToaster.show({
|
AppToaster.show({
|
||||||
@@ -55,6 +62,8 @@ function MatchingReconcileTransactionFormRoot({
|
|||||||
intent: Intent.SUCCESS,
|
intent: Intent.SUCCESS,
|
||||||
});
|
});
|
||||||
closeReconcileMatchingTransaction();
|
closeReconcileMatchingTransaction();
|
||||||
|
onSubmitSuccess &&
|
||||||
|
onSubmitSuccess({ id: res.data.id, type: 'CashflowTransaction' });
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
@@ -97,25 +106,6 @@ export const MatchingReconcileTransactionForm = R.compose(withBankingActions)(
|
|||||||
MatchingReconcileTransactionFormRoot,
|
MatchingReconcileTransactionFormRoot,
|
||||||
);
|
);
|
||||||
|
|
||||||
export function MatchingReconcileTransactionFooter() {
|
|
||||||
const { isSubmitting } = useFormikContext();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box className={styles.footer}>
|
|
||||||
<Group>
|
|
||||||
<Button
|
|
||||||
fill
|
|
||||||
type={'submit'}
|
|
||||||
intent={Intent.PRIMARY}
|
|
||||||
loading={isSubmitting}
|
|
||||||
>
|
|
||||||
Submit
|
|
||||||
</Button>
|
|
||||||
</Group>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ReconcileMatchingType() {
|
function ReconcileMatchingType() {
|
||||||
const { setFieldValue, values } =
|
const { setFieldValue, values } =
|
||||||
useFormikContext<MatchingReconcileFormValues>();
|
useFormikContext<MatchingReconcileFormValues>();
|
||||||
@@ -135,7 +125,7 @@ function ReconcileMatchingType() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CreateReconcileTransactionContent() {
|
function CreateReconcileTransactionContent() {
|
||||||
const { accounts, branches } = useMatchingReconcileTransactionBoot();
|
const { accounts, branches } = useMatchingReconcileTransactionBoot();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -197,3 +187,22 @@ export function CreateReconcileTransactionContent() {
|
|||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function MatchingReconcileTransactionFooter() {
|
||||||
|
const { isSubmitting } = useFormikContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box className={styles.footer}>
|
||||||
|
<Group>
|
||||||
|
<Button
|
||||||
|
fill
|
||||||
|
type={'submit'}
|
||||||
|
intent={Intent.PRIMARY}
|
||||||
|
loading={isSubmitting}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
import * as R from 'ramda';
|
import * as R from 'ramda';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
import { AnchorButton, Button, Intent, Tag, Text } from '@blueprintjs/core';
|
import { AnchorButton, Button, Intent, Tag, Text } from '@blueprintjs/core';
|
||||||
import { FastField, FastFieldProps, Formik, useFormikContext } from 'formik';
|
import { FastField, FastFieldProps, Formik, useFormikContext } from 'formik';
|
||||||
import { AppToaster, Box, FormatNumber, Group, Stack } from '@/components';
|
import { AppToaster, Box, FormatNumber, Group, Stack } from '@/components';
|
||||||
@@ -39,9 +40,6 @@ const initialValues = {
|
|||||||
function MatchingBankTransactionRoot({
|
function MatchingBankTransactionRoot({
|
||||||
// #withBankingActions
|
// #withBankingActions
|
||||||
closeMatchingTransactionAside,
|
closeMatchingTransactionAside,
|
||||||
|
|
||||||
// #withBanking
|
|
||||||
openReconcileMatchingTransaction,
|
|
||||||
}) {
|
}) {
|
||||||
const { uncategorizedTransactionId } = useCategorizeTransactionTabsBoot();
|
const { uncategorizedTransactionId } = useCategorizeTransactionTabsBoot();
|
||||||
const { mutateAsync: matchTransaction } = useMatchUncategorizedTransaction();
|
const { mutateAsync: matchTransaction } = useMatchUncategorizedTransaction();
|
||||||
@@ -84,25 +82,89 @@ function MatchingBankTransactionRoot({
|
|||||||
uncategorizedTransactionId={uncategorizedTransactionId}
|
uncategorizedTransactionId={uncategorizedTransactionId}
|
||||||
>
|
>
|
||||||
<Formik initialValues={initialValues} onSubmit={handleSubmit}>
|
<Formik initialValues={initialValues} onSubmit={handleSubmit}>
|
||||||
<>
|
<MatchingBankTransactionFormContent />
|
||||||
<MatchingBankTransactionContent />
|
|
||||||
|
|
||||||
{openReconcileMatchingTransaction && (
|
|
||||||
<MatchingReconcileTransactionForm />
|
|
||||||
)}
|
|
||||||
{!openReconcileMatchingTransaction && <MatchTransactionFooter />}
|
|
||||||
</>
|
|
||||||
</Formik>
|
</Formik>
|
||||||
</MatchingTransactionBoot>
|
</MatchingTransactionBoot>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MatchingBankTransaction = R.compose(
|
export const MatchingBankTransaction = R.compose(withBankingActions)(
|
||||||
|
MatchingBankTransactionRoot,
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matching bank transaction form content.
|
||||||
|
* @returns {React.ReactNode}
|
||||||
|
*/
|
||||||
|
const MatchingBankTransactionFormContent = R.compose(
|
||||||
withBankingActions,
|
withBankingActions,
|
||||||
withBanking(({ openReconcileMatchingTransaction }) => ({
|
withBanking(({ openReconcileMatchingTransaction }) => ({
|
||||||
openReconcileMatchingTransaction,
|
openReconcileMatchingTransaction,
|
||||||
})),
|
})),
|
||||||
)(MatchingBankTransactionRoot);
|
)(
|
||||||
|
({
|
||||||
|
// #withBankingActions
|
||||||
|
closeMatchingTransactionAside,
|
||||||
|
|
||||||
|
// #withBanking
|
||||||
|
openReconcileMatchingTransaction,
|
||||||
|
}) => {
|
||||||
|
const {
|
||||||
|
isMatchingTransactionsFetching,
|
||||||
|
isMatchingTransactionsSuccess,
|
||||||
|
matches,
|
||||||
|
} = useMatchingTransactionBoot();
|
||||||
|
const [pending, setPending] = useState<null | {
|
||||||
|
refId: number;
|
||||||
|
refType: string;
|
||||||
|
}>(null);
|
||||||
|
|
||||||
|
const { setFieldValue } = useFormikContext();
|
||||||
|
|
||||||
|
// This effect is responsible for automatically marking a transaction as matched
|
||||||
|
// when the matching process is successful and not currently fetching.
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
pending &&
|
||||||
|
isMatchingTransactionsSuccess &&
|
||||||
|
!isMatchingTransactionsFetching
|
||||||
|
) {
|
||||||
|
const foundMatch = matches?.find(
|
||||||
|
(m) =>
|
||||||
|
m.referenceType === pending?.refType &&
|
||||||
|
m.referenceId === pending?.refId,
|
||||||
|
);
|
||||||
|
if (foundMatch) {
|
||||||
|
setFieldValue(`matched.${pending.refType}-${pending.refId}`, true);
|
||||||
|
}
|
||||||
|
setPending(null);
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
isMatchingTransactionsFetching,
|
||||||
|
isMatchingTransactionsSuccess,
|
||||||
|
matches,
|
||||||
|
pending,
|
||||||
|
setFieldValue,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const handleReconcileFormSubmitSuccess = (payload) => {
|
||||||
|
setPending({ refId: payload.id, refType: payload.type });
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<MatchingBankTransactionContent />
|
||||||
|
|
||||||
|
{openReconcileMatchingTransaction && (
|
||||||
|
<MatchingReconcileTransactionForm
|
||||||
|
onSubmitSuccess={handleReconcileFormSubmitSuccess}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{!openReconcileMatchingTransaction && <MatchTransactionFooter />}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
function MatchingBankTransactionContent() {
|
function MatchingBankTransactionContent() {
|
||||||
return (
|
return (
|
||||||
@@ -178,8 +240,8 @@ function PossibleMatchingTransactions() {
|
|||||||
key={index}
|
key={index}
|
||||||
label={`${match.transsactionTypeFormatted} for ${match.amountFormatted}`}
|
label={`${match.transsactionTypeFormatted} for ${match.amountFormatted}`}
|
||||||
date={match.dateFormatted}
|
date={match.dateFormatted}
|
||||||
transactionId={match.transactionId}
|
transactionId={match.referenceId}
|
||||||
transactionType={match.transactionType}
|
transactionType={match.referenceType}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import { defaultTo } from 'lodash';
|
|
||||||
import React, { createContext } from 'react';
|
import React, { createContext } from 'react';
|
||||||
|
import { defaultTo } from 'lodash';
|
||||||
|
import * as R from 'ramda';
|
||||||
import { useGetBankTransactionsMatches } from '@/hooks/query/bank-rules';
|
import { useGetBankTransactionsMatches } from '@/hooks/query/bank-rules';
|
||||||
|
|
||||||
interface MatchingTransactionBootValues {
|
interface MatchingTransactionBootValues {
|
||||||
isMatchingTransactionsLoading: boolean;
|
isMatchingTransactionsLoading: boolean;
|
||||||
|
isMatchingTransactionsFetching: boolean;
|
||||||
|
isMatchingTransactionsSuccess: boolean;
|
||||||
possibleMatches: Array<any>;
|
possibleMatches: Array<any>;
|
||||||
perfectMatchesCount: number;
|
perfectMatchesCount: number;
|
||||||
perfectMatches: Array<any>;
|
perfectMatches: Array<any>;
|
||||||
@@ -26,13 +29,24 @@ function MatchingTransactionBoot({
|
|||||||
const {
|
const {
|
||||||
data: matchingTransactions,
|
data: matchingTransactions,
|
||||||
isLoading: isMatchingTransactionsLoading,
|
isLoading: isMatchingTransactionsLoading,
|
||||||
|
isFetching: isMatchingTransactionsFetching,
|
||||||
|
isSuccess: isMatchingTransactionsSuccess,
|
||||||
} = useGetBankTransactionsMatches(uncategorizedTransactionId);
|
} = useGetBankTransactionsMatches(uncategorizedTransactionId);
|
||||||
|
|
||||||
|
const possibleMatches = defaultTo(matchingTransactions?.possibleMatches, []);
|
||||||
|
const perfectMatchesCount = matchingTransactions?.perfectMatches?.length || 0;
|
||||||
|
const perfectMatches = defaultTo(matchingTransactions?.perfectMatches, []);
|
||||||
|
|
||||||
|
const matches = R.concat(perfectMatches, possibleMatches);
|
||||||
|
|
||||||
const provider = {
|
const provider = {
|
||||||
isMatchingTransactionsLoading,
|
isMatchingTransactionsLoading,
|
||||||
possibleMatches: defaultTo(matchingTransactions?.possibleMatches, []),
|
isMatchingTransactionsFetching,
|
||||||
perfectMatchesCount: matchingTransactions?.perfectMatches?.length || 0,
|
isMatchingTransactionsSuccess,
|
||||||
perfectMatches: defaultTo(matchingTransactions?.perfectMatches, []),
|
possibleMatches,
|
||||||
|
perfectMatchesCount,
|
||||||
|
perfectMatches,
|
||||||
|
matches,
|
||||||
} as MatchingTransactionBootValues;
|
} as MatchingTransactionBootValues;
|
||||||
|
|
||||||
return <RuleFormBootContext.Provider value={provider} {...props} />;
|
return <RuleFormBootContext.Provider value={provider} {...props} />;
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export const useGetPendingAmountMatched = () => {
|
|||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
const matchedItems = [...perfectMatches, ...possibleMatches].filter(
|
const matchedItems = [...perfectMatches, ...possibleMatches].filter(
|
||||||
(match) => {
|
(match) => {
|
||||||
const key = `${match.transactionType}-${match.transactionId}`;
|
const key = `${match.referenceType}-${match.referenceId}`;
|
||||||
return values.matched[key];
|
return values.matched[key];
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -58,6 +58,8 @@ export function useCreateCashflowTransaction(props) {
|
|||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
// Invalidate queries.
|
// Invalidate queries.
|
||||||
commonInvalidateQueries(queryClient);
|
commonInvalidateQueries(queryClient);
|
||||||
|
|
||||||
|
queryClient.invalidateQueries('BANK_TRANSACTION_MATCHES');
|
||||||
},
|
},
|
||||||
...props,
|
...props,
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user