// @ts-nocheck import { isEmpty } from 'lodash'; import * as R from 'ramda'; import { useEffect, useState } from 'react'; import { AnchorButton, Button, Intent, Tag, Text } from '@blueprintjs/core'; import { FastField, FastFieldProps, Formik, useFormikContext } from 'formik'; import { AppToaster, Box, FormatNumber, Group, Stack } from '@/components'; import { MatchingTransactionBoot, useMatchingTransactionBoot, } from './MatchingTransactionBoot'; import { MatchTransactionCheckbox, MatchTransactionCheckboxProps, } from './MatchTransactionCheckbox'; import { useMatchUncategorizedTransaction } from '@/hooks/query/bank-rules'; import { MatchingTransactionFormValues } from './types'; import { transformToReq, useGetPendingAmountMatched, useIsShowReconcileTransactionLink, } from './utils'; import { useCategorizeTransactionTabsBoot } from './CategorizeTransactionTabsBoot'; import { WithBankingActionsProps, withBankingActions, } from '../withBankingActions'; import styles from './CategorizeTransactionAside.module.scss'; import { MatchingReconcileTransactionForm } from './MatchingReconcileTransactionAside/MatchingReconcileTransactionForm'; import { withBanking } from '../withBanking'; const initialValues = { matched: {}, }; /** * Renders the bank transaction matching form. * @returns {React.ReactNode} */ function MatchingBankTransactionRoot({ // #withBankingActions closeMatchingTransactionAside, }) { const { uncategorizedTransactionId } = useCategorizeTransactionTabsBoot(); const { mutateAsync: matchTransaction } = useMatchUncategorizedTransaction(); // Handles the form submitting. const handleSubmit = ( values: MatchingTransactionFormValues, { setSubmitting }: FormikHelpers, ) => { const _values = transformToReq(values); if (_values.matchedTransactions?.length === 0) { AppToaster.show({ message: 'You should select at least one transaction for matching.', intent: Intent.DANGER, }); return; } setSubmitting(true); matchTransaction({ id: uncategorizedTransactionId, value: _values }) .then(() => { AppToaster.show({ intent: Intent.SUCCESS, message: 'The bank transaction has been matched successfully.', }); setSubmitting(false); 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.', }); setSubmitting(false); }); }; return ( ); } export const MatchingBankTransaction = R.compose(withBankingActions)( MatchingBankTransactionRoot, ); /** * Matching bank transaction form content. * @returns {React.ReactNode} */ const MatchingBankTransactionFormContent = R.compose( withBankingActions, withBanking(({ openReconcileMatchingTransaction }) => ({ openReconcileMatchingTransaction, })), )( ({ // #withBanking openReconcileMatchingTransaction, }) => { const { isMatchingTransactionsFetching, isMatchingTransactionsSuccess, matches, } = useMatchingTransactionBoot(); const [pending, setPending] = useState(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 ( <> {openReconcileMatchingTransaction && ( )} {!openReconcileMatchingTransaction && } ); }, ); function MatchingBankTransactionContent() { return ( ); } /** * Renders the perfect match transactions. * @returns {React.ReactNode} */ function PerfectMatchingTransactions() { const { perfectMatches, perfectMatchesCount } = useMatchingTransactionBoot(); // Can't continue if the perfect matches is empty. if (isEmpty(perfectMatches)) { return null; } return ( <>

Perfect Matchines

{perfectMatchesCount}
{perfectMatches.map((match, index) => ( ))} ); } /** * Renders the possible match transactions. * @returns {React.ReactNode} */ function PossibleMatchingTransactions() { const { possibleMatches } = useMatchingTransactionBoot(); // Can't continue if the possible matches is emoty. if (isEmpty(possibleMatches)) { return null; } return ( <>

Possible Matches

{possibleMatches.map((match, index) => ( {`${match.transsactionTypeFormatted} for `} {match.amountFormatted} } date={match.dateFormatted} transactionId={match.referenceId} transactionType={match.referenceType} /> ))} ); } interface MatchTransactionFieldProps extends Omit< MatchTransactionCheckboxProps, 'onChange' | 'active' | 'initialActive' > { transactionId: number; transactionType: string; } function MatchTransactionField({ transactionId, transactionType, ...props }: MatchTransactionFieldProps) { const name = `matched.${transactionType}-${transactionId}`; return ( {({ form, field: { value } }: FastFieldProps) => ( { form.setFieldValue(name, state); }} /> )} ); } interface MatchTransctionFooterProps extends WithBankingActionsProps {} /** * Renders the match transactions footer. * @returns {React.ReactNode} */ const MatchTransactionFooter = R.compose(withBankingActions)( ({ closeMatchingTransactionAside, openReconcileMatchingTransaction, }: MatchTransctionFooterProps) => { const { submitForm, isSubmitting } = useFormikContext(); const totalPending = useGetPendingAmountMatched(); const showReconcileLink = useIsShowReconcileTransactionLink(); const submitDisabled = totalPending !== 0; const handleCancelBtnClick = () => { closeMatchingTransactionAside(); }; const handleSubmitBtnClick = () => { submitForm(); }; const handleReconcileTransaction = () => { openReconcileMatchingTransaction(totalPending); }; return ( {showReconcileLink && ( Add Reconcile Transaction + )} Pending ); }, ); MatchTransactionFooter.displayName = 'MatchTransactionFooter';