diff --git a/client/src/components/DialogsContainer.js b/client/src/components/DialogsContainer.js index 6f261d044..2da5cb4ad 100644 --- a/client/src/components/DialogsContainer.js +++ b/client/src/components/DialogsContainer.js @@ -8,13 +8,14 @@ import AccountFormDialog from 'containers/Dialogs/AccountFormDialog'; // import InviteUserDialog from 'containers/Dialogs/InviteUserDialog'; // import ExchangeRateDialog from 'containers/Dialogs/ExchangeRateDialog'; import JournalNumberDialog from 'containers/Dialogs/JournalNumberDialog'; - +import BillNumberDialog from 'containers/Dialogs/BillNumberDialog'; export default function DialogsContainer() { return (
+
); } diff --git a/client/src/containers/Dialogs/BillNumberDialog/BillNumberDialogContent.js b/client/src/containers/Dialogs/BillNumberDialog/BillNumberDialogContent.js new file mode 100644 index 000000000..83f57ecd7 --- /dev/null +++ b/client/src/containers/Dialogs/BillNumberDialog/BillNumberDialogContent.js @@ -0,0 +1,79 @@ +import React from 'react'; +import { DialogContent } from 'components'; +import { useQuery, queryCache } from 'react-query'; + +import ReferenceNumberForm from 'containers/JournalNumber/ReferenceNumberForm'; + +import withDialogActions from 'containers/Dialog/withDialogActions'; +import withSettingsActions from 'containers/Settings/withSettingsActions'; +import withSettings from 'containers/Settings/withSettings'; +import withBillActions from 'containers/Purchases/Bill/withBillActions'; + +import { compose, optionsMapToArray } from 'utils'; + +/** + * bill number dialog's content. + */ + +function BillNumberDialogContent({ + // #withSettings + nextNumber, + numberPrefix, + + // #withSettingsActions + requestFetchOptions, + requestSubmitOptions, + + // #withDialogActions + closeDialog, + + // #withBillActions + setBillNumberChanged, +}) { + const fetchSettings = useQuery(['settings'], () => requestFetchOptions({})); + + const handleSubmitForm = (values, { setSubmitting }) => { + const options = optionsMapToArray(values).map((option) => { + return { key: option.key, ...option, group: 'bills' }; + }); + + requestSubmitOptions({ options }) + .then(() => { + setSubmitting(false); + closeDialog('bill-number-form'); + setBillNumberChanged(true); + + setTimeout(() => { + queryCache.invalidateQueries('settings'); + }, 250); + }) + .catch(() => { + setSubmitting(false); + }); + }; + + const handleClose = () => { + closeDialog('bill-number-form'); + }; + + return ( + + + + ); +} + +export default compose( + withDialogActions, + withSettingsActions, + withSettings(({ billsettings }) => ({ + nextNumber: billsettings?.next_number, + numberPrefix: billsettings?.number_prefix, + })), + withBillActions, +)(BillNumberDialogContent); diff --git a/client/src/containers/Dialogs/BillNumberDialog/index.js b/client/src/containers/Dialogs/BillNumberDialog/index.js new file mode 100644 index 000000000..835dd660e --- /dev/null +++ b/client/src/containers/Dialogs/BillNumberDialog/index.js @@ -0,0 +1,26 @@ +import React, { lazy } from 'react'; +import { FormattedMessage as T } from 'react-intl'; +import { Dialog, DialogSuspense } from 'components'; +import withDialogRedux from 'components/DialogReduxConnect'; +import { compose } from 'utils'; + +const BillNumberDialogContent = lazy(() => import('./BillNumberDialogContent')); + +function BillNumberDialog({ dialogName, payload = { id: null }, isOpen }) { + return ( + } + autoFocus={true} + canEscapeKeyClose={true} + isOpen={isOpen} + className={'dialog--journal-number-settings'} + > + + + + + ); +} + +export default compose(withDialogRedux())(BillNumberDialog); diff --git a/client/src/containers/Purchases/Bill/BillForm.js b/client/src/containers/Purchases/Bill/BillForm.js index 80c412618..123e484e7 100644 --- a/client/src/containers/Purchases/Bill/BillForm.js +++ b/client/src/containers/Purchases/Bill/BillForm.js @@ -20,8 +20,10 @@ import BillFormFooter from './BillFormFooter'; import withDashboardActions from 'containers/Dashboard/withDashboardActions'; import withMediaActions from 'containers/Media/withMediaActions'; +import withBills from './withBills'; import withBillActions from './withBillActions'; import withBillDetail from './withBillDetail'; +import withSettings from 'containers/Settings/withSettings'; import { AppToaster } from 'components'; import Dragzone from 'components/Dragzone'; @@ -29,7 +31,7 @@ import useMedia from 'hooks/useMedia'; import { compose, repeatValue } from 'utils'; -const MIN_LINES_NUMBER = 4; +const MIN_LINES_NUMBER = 5; function BillForm({ //#WithMedia @@ -39,10 +41,17 @@ function BillForm({ //#withBillActions requestSubmitBill, requestEditBill, + setBillNumberChanged, //#withDashboard changePageTitle, - changePageSubtitle, + + // #withBills + nextBillNumberChanged, + + // #withSettings + billNextNumber, + billNumberPrefix, //#withBillDetail bill, @@ -83,7 +92,6 @@ function BillForm({ } }, [changePageTitle, bill, formatMessage]); - // @todo abstruct validation schema to sperated file. const validationSchema = Yup.object().shape({ vendor_id: Yup.number() .required() @@ -94,11 +102,11 @@ function BillForm({ due_date: Yup.date() .required() .label(formatMessage({ id: 'due_date_' })), - bill_number: Yup.number() + bill_number: Yup.string() .required() .label(formatMessage({ id: 'bill_number_' })), - reference_no: Yup.string().min(1).max(255), - status: Yup.string().required().nullable(), + reference_no: Yup.string().nullable().min(1).max(255), + // status: Yup.string().required().nullable(), note: Yup.string() .trim() .min(1) @@ -128,27 +136,34 @@ function BillForm({ [onFormSubmit], ); - const defaultBill = useMemo(() => ({ - index: 0, - item_id: null, - rate: null, - discount: 0, - quantity: null, - description: '', - })); + const defaultBill = useMemo( + () => ({ + index: 0, + item_id: null, + rate: null, + discount: 0, + quantity: null, + description: '', + }), + [], + ); + + const billNumber = billNumberPrefix + ? `${billNumberPrefix}-${billNextNumber}` + : billNextNumber; const defaultInitialValues = useMemo( () => ({ vendor_id: '', - bill_number: '', + bill_number: billNumber, bill_date: moment(new Date()).format('YYYY-MM-DD'), due_date: moment(new Date()).format('YYYY-MM-DD'), - status: 'Bill', + // status: 'Bill', reference_no: '', note: '', entries: [...repeatValue(defaultBill, MIN_LINES_NUMBER)], }), - [defaultBill], + [defaultBill, billNumber], ); const orderingIndex = (_bill) => { @@ -209,9 +224,12 @@ function BillForm({ requestEditBill(bill.id, requestForm) .then((response) => { AppToaster.show({ - message: formatMessage({ - id: 'the_bill_has_been_successfully_edited', - }), + message: formatMessage( + { + id: 'the_bill_has_been_successfully_edited', + }, + { number: values.bill_number }, + ), intent: Intent.SUCCESS, }); setSubmitting(false); @@ -242,10 +260,16 @@ function BillForm({ }, }); + useEffect(() => { + formik.setFieldValue('bill_number', billNumber); + setBillNumberChanged(false); + }, [nextBillNumberChanged, billNumber]); + const handleSubmitClick = useCallback( (payload) => { setPayload(payload); formik.submitForm(); + formik.setSubmitting(false); }, [setPayload, formik], ); @@ -315,6 +339,7 @@ function BillForm({ formik={formik} onSubmitClick={handleSubmitClick} bill={bill} + disabled={formik.isSubmitting} onCancelClick={handleCancelClick} /> @@ -323,7 +348,12 @@ function BillForm({ export default compose( withBillActions, + withBillDetail(), + withBills(({ nextBillNumberChanged }) => ({ nextBillNumberChanged })), withDashboardActions, withMediaActions, - withBillDetail(), + withSettings(({ billsettings }) => ({ + billNextNumber: billsettings?.next_number, + billNumberPrefix: billsettings?.number_prefix, + })), )(BillForm); diff --git a/client/src/containers/Purchases/Bill/BillFormFooter.js b/client/src/containers/Purchases/Bill/BillFormFooter.js index 1835b89ba..1be37d6d9 100644 --- a/client/src/containers/Purchases/Bill/BillFormFooter.js +++ b/client/src/containers/Purchases/Bill/BillFormFooter.js @@ -12,6 +12,7 @@ export default function BillFormFooter({