feat: excess payment alert

This commit is contained in:
Ahmed Bouhuolia
2024-07-23 18:54:08 +02:00
parent 5c3a371e8a
commit 341d47cc7b
11 changed files with 331 additions and 5 deletions

View File

@@ -0,0 +1,9 @@
import { ExcessPaymentDialog } from './dialogs/PaymentMadeExcessDialog';
export function PaymentMadeDialogs() {
return (
<>
<ExcessPaymentDialog dialogName={'payment-made-excessed-payment'} />
</>
);
}

View File

@@ -14,6 +14,7 @@ import PaymentMadeFloatingActions from './PaymentMadeFloatingActions';
import PaymentMadeFooter from './PaymentMadeFooter';
import PaymentMadeFormBody from './PaymentMadeFormBody';
import PaymentMadeFormTopBar from './PaymentMadeFormTopBar';
import { PaymentMadeDialogs } from './PaymentMadeDialogs';
import { PaymentMadeInnerProvider } from './PaymentMadeInnerProvider';
import { usePaymentMadeFormContext } from './PaymentMadeFormProvider';
@@ -21,6 +22,7 @@ import { compose, orderingLinesIndexes } from '@/utils';
import withSettings from '@/containers/Settings/withSettings';
import withCurrentOrganization from '@/containers/Organization/withCurrentOrganization';
import withDialogActions from '@/containers/Dialog/withDialogActions';
import {
EditPaymentMadeFormSchema,
@@ -42,6 +44,9 @@ function PaymentMadeForm({
// #withCurrentOrganization
organization: { base_currency },
// #withDialogActions
openDialog,
}) {
const history = useHistory();
@@ -149,6 +154,7 @@ function PaymentMadeForm({
<PaymentMadeFormBody />
<PaymentMadeFooter />
<PaymentMadeFloatingActions />
<PaymentMadeDialogs />
</PaymentMadeInnerProvider>
</Form>
</Formik>
@@ -163,4 +169,5 @@ export default compose(
preferredPaymentAccount: parseInt(billPaymentSettings?.withdrawalAccount),
})),
withCurrentOrganization(),
withDialogActions,
)(PaymentMadeForm);

View File

@@ -0,0 +1,37 @@
// @ts-nocheck
import React from 'react';
import { Dialog, DialogSuspense } from '@/components';
import withDialogRedux from '@/components/DialogReduxConnect';
import { compose } from '@/utils';
const ExcessPaymentDialogContent = React.lazy(() =>
import('./PaymentMadeExcessDialogContent').then((module) => ({
default: module.ExcessPaymentDialogContent,
})),
);
/**
* Exess payment dialog of the payment made form.
*/
function ExcessPaymentDialogRoot({ dialogName, isOpen }) {
return (
<Dialog
name={dialogName}
title={'Excess Payment'}
isOpen={isOpen}
canEscapeJeyClose={true}
autoFocus={true}
style={{ width: 500 }}
>
<DialogSuspense>
<ExcessPaymentDialogContent dialogName={dialogName} />
</DialogSuspense>
</Dialog>
);
}
export const ExcessPaymentDialog = compose(withDialogRedux())(
ExcessPaymentDialogRoot,
);
ExcessPaymentDialog.displayName = 'ExcessPaymentDialog';

View File

@@ -0,0 +1,113 @@
// @ts-nocheck
import * as R from 'ramda';
import * as Yup from 'yup';
import { useMemo } from 'react';
import { Button, Classes, Intent } from '@blueprintjs/core';
import { Form, Formik, FormikHelpers, useFormikContext } from 'formik';
import { AccountsSelect, FFormGroup } from '@/components';
import { usePaymentMadeFormContext } from '../../PaymentMadeFormProvider';
import withDialogActions from '@/containers/Dialog/withDialogActions';
interface ExcessPaymentValues {
accountId: string;
}
const initialValues = {
accountId: '',
} as ExcessPaymentValues;
const Schema = Yup.object().shape({
accountId: Yup.number().required(),
});
const DEFAULT_ACCOUNT_SLUG = 'depreciation-expense';
function ExcessPaymentDialogContentRoot({ dialogName, closeDialog }) {
const { setFieldValue } = useFormikContext();
const handleSubmit = (
values: ExcessPaymentValues,
{ setSubmitting }: FormikHelpers<ExcessPaymentValues>,
) => {
closeDialog(dialogName);
setFieldValue(values.accountId);
};
// Handle close button click.
const handleCloseBtn = () => {
closeDialog(dialogName);
};
const defaultAccountId = useDefaultExcessPaymentDeposit();
return (
<Formik
initialValues={{
...initialValues,
accountId: defaultAccountId,
}}
validationSchema={Schema}
onSubmit={handleSubmit}
>
<Form>
<ExcessPaymentDialogContentForm onClose={handleCloseBtn} />
</Form>
</Formik>
);
}
export const ExcessPaymentDialogContent = R.compose(withDialogActions)(
ExcessPaymentDialogContentRoot,
);
interface ExcessPaymentDialogContentFormProps {
onClose?: () => void;
}
function ExcessPaymentDialogContentForm({
onClose,
}: ExcessPaymentDialogContentFormProps) {
const { submitForm } = useFormikContext();
const { accounts } = usePaymentMadeFormContext();
const handleCloseBtn = () => {
onClose && onClose();
};
return (
<>
<div className={Classes.DIALOG_BODY}>
<p>
Would you like to record the excess amount of $1000 as advanced
payment from the customer.
</p>
<FFormGroup
name={'accountId'}
label={'The excessed amount will be deposited in the'}
>
<AccountsSelect
name={'accountId'}
items={accounts}
buttonProps={{ small: true }}
/>
</FFormGroup>
</div>
<div className={Classes.DIALOG_FOOTER}>
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
<Button intent={Intent.PRIMARY} onClick={() => submitForm()}>
Continue to Payment
</Button>
<Button onClick={handleCloseBtn}>Cancel</Button>
</div>
</div>
</>
);
}
const useDefaultExcessPaymentDeposit = () => {
const { accounts } = usePaymentMadeFormContext();
return useMemo(() => {
return accounts?.find((a) => a.slug === DEFAULT_ACCOUNT_SLUG)?.id;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
};

View File

@@ -0,0 +1 @@
export * from './PaymentMadeExcessDialog';

View File

@@ -21,6 +21,7 @@ import { PaymentReceiveInnerProvider } from './PaymentReceiveInnerProvider';
import withSettings from '@/containers/Settings/withSettings';
import withCurrentOrganization from '@/containers/Organization/withCurrentOrganization';
import withDialogActions from '@/containers/Dialog/withDialogActions';
import {
EditPaymentReceiveFormSchema,
@@ -51,6 +52,9 @@ function PaymentReceiveForm({
// #withCurrentOrganization
organization: { base_currency },
// #withDialogActions
openDialog
}) {
const history = useHistory();
@@ -102,6 +106,11 @@ function PaymentReceiveForm({
) => {
setSubmitting(true);
if (true) {
openDialog('payment-received-excessed-payment');
return;
}
// Transformes the form values to request body.
const form = transformFormToRequest(values);
@@ -191,4 +200,5 @@ export default compose(
preferredDepositAccount: paymentReceiveSettings?.preferredDepositAccount,
})),
withCurrentOrganization(),
withDialogActions
)(PaymentReceiveForm);

View File

@@ -2,6 +2,7 @@
import React from 'react';
import { useFormikContext } from 'formik';
import PaymentReceiveNumberDialog from '@/containers/Dialogs/PaymentReceiveNumberDialog';
import { ExcessPaymentDialog } from './dialogs/ExcessPaymentDialog';
/**
* Payment receive form dialogs.
@@ -21,9 +22,12 @@ export default function PaymentReceiveFormDialogs() {
};
return (
<PaymentReceiveNumberDialog
dialogName={'payment-receive-number-form'}
onConfirm={handleUpdatePaymentNumber}
/>
<>
<PaymentReceiveNumberDialog
dialogName={'payment-receive-number-form'}
onConfirm={handleUpdatePaymentNumber}
/>
<ExcessPaymentDialog dialogName={'payment-received-excessed-payment'} />
</>
);
}

View File

@@ -0,0 +1,37 @@
// @ts-nocheck
import React from 'react';
import { Dialog, DialogSuspense } from '@/components';
import withDialogRedux from '@/components/DialogReduxConnect';
import { compose } from '@/utils';
const ExcessPaymentDialogContent = React.lazy(() =>
import('./ExcessPaymentDialogContent').then((module) => ({
default: module.ExcessPaymentDialogContent,
})),
);
/**
* Excess payment dialog of the payment received form.
*/
function ExcessPaymentDialogRoot({ dialogName, isOpen }) {
return (
<Dialog
name={dialogName}
title={'Excess Payment'}
isOpen={isOpen}
canEscapeJeyClose={true}
autoFocus={true}
style={{ width: 500 }}
>
<DialogSuspense>
<ExcessPaymentDialogContent dialogName={dialogName} />
</DialogSuspense>
</Dialog>
);
}
export const ExcessPaymentDialog = compose(withDialogRedux())(
ExcessPaymentDialogRoot,
);
ExcessPaymentDialog.displayName = 'ExcessPaymentDialog';

View File

@@ -0,0 +1,106 @@
// @ts-nocheck
import { useMemo } from 'react';
import * as Yup from 'yup';
import * as R from 'ramda';
import { Button, Classes, Intent } from '@blueprintjs/core';
import { Form, Formik, FormikHelpers, useFormikContext } from 'formik';
import { AccountsSelect, FFormGroup } from '@/components';
import { usePaymentReceiveFormContext } from '../../PaymentReceiveFormProvider';
import withDialogActions from '@/containers/Dialog/withDialogActions';
interface ExcessPaymentValues {
accountId: string;
}
const initialValues = {
accountId: '',
} as ExcessPaymentValues;
const Schema = Yup.object().shape({
accountId: Yup.number().required(),
});
const DEFAULT_ACCOUNT_SLUG = 'depreciation-expense';
export function ExcessPaymentDialogContentRoot({ dialogName, closeDialog }) {
const { setFieldValue } = useFormikContext();
const initialAccountId = useDefaultExcessPaymentDeposit();
const handleSubmit = (
values: ExcessPaymentValues,
{ setSubmitting }: FormikHelpers<ExcessPaymentValues>,
) => {
closeDialog(dialogName);
setFieldValue('unearned_revenue_account_id', values.accountId);
};
const handleClose = () => {
closeDialog(dialogName);
};
return (
<Formik
initialValues={{
...initialValues,
accountId: initialAccountId,
}}
validationSchema={Schema}
onSubmit={handleSubmit}
>
<Form>
<ExcessPaymentDialogContentForm onClose={handleClose} />
</Form>
</Formik>
);
}
export const ExcessPaymentDialogContent = R.compose(withDialogActions)(
ExcessPaymentDialogContentRoot,
);
function ExcessPaymentDialogContentForm({ onClose }) {
const { accounts } = usePaymentReceiveFormContext();
const { submitForm } = useFormikContext();
const handleCloseBtn = () => {
onClose && onClose();
};
return (
<>
<div className={Classes.DIALOG_BODY}>
<p>
Would you like to record the excess amount of $1000 as advanced
payment from the customer.
</p>
<FFormGroup
name={'accountId'}
label={'The excessed amount will be deposited in the'}
>
<AccountsSelect
name={'accountId'}
items={accounts}
buttonProps={{ small: true }}
/>
</FFormGroup>
</div>
<div className={Classes.DIALOG_FOOTER}>
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
<Button intent={Intent.PRIMARY} onClick={() => submitForm()}>
Continue to Payment
</Button>
<Button onClick={handleCloseBtn}>Cancel</Button>
</div>
</div>
</>
);
}
const useDefaultExcessPaymentDeposit = () => {
const { accounts } = usePaymentReceiveFormContext();
return useMemo(() => {
return accounts?.find((a) => a.slug === DEFAULT_ACCOUNT_SLUG)?.id;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
};

View File

@@ -0,0 +1 @@
export * from './ExcessPaymentDialog';

View File

@@ -47,7 +47,8 @@ export const defaultPaymentReceive = {
branch_id: '',
exchange_rate: 1,
entries: [],
attachments: []
attachments: [],
unearned_revenue_account_id: '',
};
export const defaultRequestPaymentEntry = {