fix(server): match transactions query

This commit is contained in:
Ahmed Bouhuolia
2024-07-06 16:10:34 +02:00
parent 87f60f7461
commit cd9039fe16
21 changed files with 413 additions and 71 deletions

View File

@@ -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.
*/

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -8,16 +8,26 @@ import {
} from '../withBankingActions';
import { CategorizeTransactionTabsBoot } from './CategorizeTransactionTabsBoot';
import { withBanking } from '../withBanking';
import { useEffect } from 'react';
interface CategorizeTransactionAsideProps extends WithBankingActionsProps {}
function CategorizeTransactionAsideRoot({
// #withBankingActions
closeMatchingTransactionAside,
closeReconcileMatchingTransaction,
// #withBanking
selectedUncategorizedTransactionId,
}: CategorizeTransactionAsideProps) {
//
useEffect(
() => () => {
closeReconcileMatchingTransaction();
},
[closeReconcileMatchingTransaction],
);
const handleClose = () => {
closeMatchingTransactionAside();
};

View File

@@ -1,7 +1,14 @@
// @ts-nocheck
import * as R from 'ramda';
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 {
AccountsSelect,
AppToaster,
@@ -26,6 +33,7 @@ import { useCreateCashflowTransaction } from '@/hooks/query';
import { useAccountTransactionsContext } from '../../AccountTransactions/AccountTransactionsProvider';
import { MatchingReconcileFormSchema } from './MatchingReconcileTransactionForm.schema';
import { initialValues, transformToReq } from './_utils';
import { withBanking } from '../../withBanking';
interface MatchingReconcileTransactionFormProps {
onSubmitSuccess?: (values: any) => void;
@@ -33,6 +41,7 @@ interface MatchingReconcileTransactionFormProps {
function MatchingReconcileTransactionFormRoot({
closeReconcileMatchingTransaction,
reconcileMatchingTransactionPendingAmount,
// #props¿
onSubmitSuccess,
@@ -43,12 +52,17 @@ function MatchingReconcileTransactionFormRoot({
const { accountId } = useAccountTransactionsContext();
// Handles the aside close.
const handleAsideClose = () => {
closeReconcileMatchingTransaction();
};
// Handle the form submitting.
const handleSubmit = (
values: MatchingReconcileTransactionValues,
{ setSubmitting }: FormikValues<MatchingReconcileTransactionValues>,
{
setSubmitting,
setErrors,
}: FormikHelpers<MatchingReconcileTransactionValues>,
) => {
setSubmitting(true);
const _values = transformToReq(values, accountId);
@@ -67,14 +81,31 @@ function MatchingReconcileTransactionFormRoot({
})
.catch((error) => {
setSubmitting(false);
AppToaster.show({
message: 'Something went wrong.',
intent: Intent.DANGER,
});
if (
error.response.data?.errors?.find(
(e) => e.type === 'BRANCH_ID_REQUIRED',
)
) {
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 (
<Aside
title={'Create Reconcile Transactions'}
@@ -84,7 +115,7 @@ function MatchingReconcileTransactionFormRoot({
<MatchingReconcileTransactionBoot>
<Formik
onSubmit={handleSubmit}
initialValues={initialValues}
initialValues={_initialValues}
validationSchema={MatchingReconcileFormSchema}
>
<Form className={styles.form}>
@@ -102,9 +133,12 @@ function MatchingReconcileTransactionFormRoot({
);
}
export const MatchingReconcileTransactionForm = R.compose(withBankingActions)(
MatchingReconcileTransactionFormRoot,
);
export const MatchingReconcileTransactionForm = R.compose(
withBankingActions,
withBanking(({ reconcileMatchingTransactionPendingAmount }) => ({
reconcileMatchingTransactionPendingAmount,
})),
)(MatchingReconcileTransactionFormRoot);
function ReconcileMatchingType() {
const { setFieldValue, values } =
@@ -112,6 +146,7 @@ function ReconcileMatchingType() {
const handleChange = (value: string) => {
setFieldValue('type', value);
setFieldValue('category');
};
return (
<ContentTabs
@@ -126,22 +161,27 @@ function ReconcileMatchingType() {
}
function CreateReconcileTransactionContent() {
const { accounts, branches } = useMatchingReconcileTransactionBoot();
const { branches } = useMatchingReconcileTransactionBoot();
return (
<Box className={styles.content}>
<ReconcileMatchingType />
<FFormGroup label={'Date'} name={'date'}>
<FFormGroup label={'Date'} name={'date'} fastField>
<FDateInput
{...momentFormatter('YYYY/MM/DD')}
name={'date'}
formatDate={(date) => date.toLocaleString()}
popoverProps={{
minimal: false,
position: Position.LEFT,
modifiers: {
preventOverflow: { enabled: true },
},
boundary: 'viewport',
}}
inputProps={{ fill: true }}
fill
fastField
/>
</FFormGroup>
@@ -149,45 +189,79 @@ function CreateReconcileTransactionContent() {
label={'Amount'}
name={'amount'}
labelInfo={<Tag minimal>Required</Tag>}
fastField
>
<FMoneyInputGroup name={'amount'} />
<FMoneyInputGroup name={'amount'} fastField />
</FFormGroup>
<FFormGroup
label={'Category'}
name={'category'}
labelInfo={<Tag minimal>Required</Tag>}
>
<AccountsSelect
name={'category'}
items={accounts}
popoverProps={{ minimal: false, position: Position.LEFT }}
/>
</FFormGroup>
<MatchingReconcileCategoryField />
<FFormGroup
label={'Memo'}
name={'memo'}
labelInfo={<Tag minimal>Required</Tag>}
fastField
>
<FInputGroup name={'memo'} />
<FInputGroup name={'memo'} fastField />
</FFormGroup>
<FFormGroup label={'Reference No.'} name={'reference_no'}>
<FFormGroup label={'Reference No.'} name={'reference_no'} fastField>
<FInputGroup name={'reference_no'} />
</FFormGroup>
<FFormGroup name={'branchId'} label={'Branch'}>
<FFormGroup
name={'branchId'}
label={'Branch'}
labelInfo={<Tag minimal>Required</Tag>}
fastField
>
<BranchSelect
name={'branchId'}
branches={branches}
popoverProps={{ minimal: true }}
popoverProps={{
minimal: false,
position: Position.LEFT,
modifiers: {
preventOverflow: { enabled: true },
},
boundary: 'viewport',
}}
fastField
/>
</FFormGroup>
</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() {
const { isSubmitting } = useFormikContext();

View File

@@ -69,6 +69,18 @@ function MatchingBankTransactionRoot({
closeMatchingTransactionAside();
})
.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({
intent: Intent.DANGER,
message: 'Something went wrong.',
@@ -103,9 +115,6 @@ const MatchingBankTransactionFormContent = R.compose(
})),
)(
({
// #withBankingActions
closeMatchingTransactionAside,
// #withBanking
openReconcileMatchingTransaction,
}) => {
@@ -302,7 +311,7 @@ const MatchTransactionFooter = R.compose(withBankingActions)(
submitForm();
};
const handleReconcileTransaction = () => {
openReconcileMatchingTransaction();
openReconcileMatchingTransaction(totalPending);
};
return (
@@ -334,8 +343,8 @@ const MatchTransactionFooter = R.compose(withBankingActions)(
intent={Intent.PRIMARY}
style={{ minWidth: 85 }}
onClick={handleSubmitBtnClick}
loading={isSubmitting}
disabled={submitDisabled}
// loading={isSubmitting}
// disabled={submitDisabled}
>
Match
</Button>

View File

@@ -29,7 +29,9 @@ export const useGetPendingAmountMatched = () => {
},
);
const totalMatchedAmount = matchedItems.reduce(
(total, item) => total + parseFloat(item.amount),
(total, item) =>
total +
(item.transactionNormal === 'debit' ? 1 : -1) * parseFloat(item.amount),
0,
);
const amount = uncategorizedTransaction.amount;

View File

@@ -9,7 +9,10 @@ export const withBanking = (mapState) => {
selectedUncategorizedTransactionId:
state.plaid.uncategorizedTransactionIdForMatching,
openReconcileMatchingTransaction:
state.plaid.openReconcileMatchingTransaction,
state.plaid.openReconcileMatchingTransaction.isOpen,
reconcileMatchingTransactionPendingAmount:
state.plaid.openReconcileMatchingTransaction.pending,
};
return mapState ? mapState(mapped, state, props) : mapped;
};

View File

@@ -11,7 +11,7 @@ export interface WithBankingActionsProps {
setUncategorizedTransactionIdForMatching: (
uncategorizedTransactionId: number,
) => void;
openReconcileMatchingTransaction: () => void;
openReconcileMatchingTransaction: (pendingAmount: number) => void;
closeReconcileMatchingTransaction: () => void;
}
@@ -24,8 +24,8 @@ const mapDipatchToProps = (dispatch: any): WithBankingActionsProps => ({
dispatch(
setUncategorizedTransactionIdForMatching(uncategorizedTransactionId),
),
openReconcileMatchingTransaction: () =>
dispatch(openReconcileMatchingTransaction()),
openReconcileMatchingTransaction: (pendingAmount: number) =>
dispatch(openReconcileMatchingTransaction({ pending: pendingAmount })),
closeReconcileMatchingTransaction: () =>
dispatch(closeReconcileMatchingTransaction()),
});

View File

@@ -4,7 +4,7 @@ interface StorePlaidState {
plaidToken: string;
openMatchingTransactionAside: boolean;
uncategorizedTransactionIdForMatching: number | null;
openReconcileMatchingTransaction: boolean;
openReconcileMatchingTransaction: { isOpen: boolean; pending: number };
}
export const PlaidSlice = createSlice({
@@ -13,7 +13,10 @@ export const PlaidSlice = createSlice({
plaidToken: '',
openMatchingTransactionAside: false,
uncategorizedTransactionIdForMatching: null,
openReconcileMatchingTransaction: false,
openReconcileMatchingTransaction: {
isOpen: false,
pending: 0,
},
} as StorePlaidState,
reducers: {
setPlaidId: (state: StorePlaidState, action: PayloadAction<string>) => {
@@ -37,12 +40,17 @@ export const PlaidSlice = createSlice({
state.uncategorizedTransactionIdForMatching = null;
},
openReconcileMatchingTransaction: (state: StorePlaidState) => {
state.openReconcileMatchingTransaction = true;
openReconcileMatchingTransaction: (
state: StorePlaidState,
action: PayloadAction<{ pending: number }>,
) => {
state.openReconcileMatchingTransaction.isOpen = true;
state.openReconcileMatchingTransaction.pending = action.payload.pending;
},
closeReconcileMatchingTransaction: (state: StorePlaidState) => {
state.openReconcileMatchingTransaction = false;
state.openReconcileMatchingTransaction.isOpen = false;
state.openReconcileMatchingTransaction.pending = 0;
},
},
});