From daa43e7209b1453003017edca0d4178c6507be51 Mon Sep 17 00:00:00 2001 From: elforjani3 Date: Sun, 14 Mar 2021 15:40:04 +0200 Subject: [PATCH 1/2] feat(bill): add quick payment made. --- .../Purchases/Bills/BillsLanding/BillsTable.js | 11 +++++++++++ .../Purchases/Bills/BillsLanding/components.js | 9 ++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/client/src/containers/Purchases/Bills/BillsLanding/BillsTable.js b/client/src/containers/Purchases/Bills/BillsLanding/BillsTable.js index 367d2f4ef..cbf514012 100644 --- a/client/src/containers/Purchases/Bills/BillsLanding/BillsTable.js +++ b/client/src/containers/Purchases/Bills/BillsLanding/BillsTable.js @@ -13,6 +13,7 @@ import withBills from './withBills'; import withBillActions from './withBillsActions'; import withSettings from 'containers/Settings/withSettings'; import withAlertsActions from 'containers/Alert/withAlertActions'; +import withDialogActions from 'containers/Dialog/withDialogActions'; import { useBillsTableColumns, ActionsMenu } from './components'; import { useBillsListContext } from './BillsListProvider'; @@ -28,6 +29,9 @@ function BillsDataTable({ // #withAlerts openAlert, + + // #withDialogActions + openDialog, }) { // Bills list context. const { @@ -69,6 +73,11 @@ function BillsDataTable({ openAlert('bill-open', { billId: bill.id }); }; + // Handle quick payment made action. + const handleQuickPaymentMade = ({ id }) => { + openDialog('quick-payment-made', { billId: id }); + }; + if (isEmptyStatus) { return ; } @@ -95,6 +104,7 @@ function BillsDataTable({ onDelete: handleDeleteBill, onEdit: handleEditBill, onOpen: handleOpenBill, + onQuick: handleQuickPaymentMade, }} /> ); @@ -104,6 +114,7 @@ export default compose( withBills(({ billsTableState }) => ({ billsTableState })), withBillActions, withAlertsActions, + withDialogActions, withSettings(({ organizationSettings }) => ({ baseCurrency: organizationSettings?.baseCurrency, })), diff --git a/client/src/containers/Purchases/Bills/BillsLanding/components.js b/client/src/containers/Purchases/Bills/BillsLanding/components.js index 31ca5750c..8b51e6550 100644 --- a/client/src/containers/Purchases/Bills/BillsLanding/components.js +++ b/client/src/containers/Purchases/Bills/BillsLanding/components.js @@ -18,7 +18,7 @@ import moment from 'moment'; * Actions menu. */ export function ActionsMenu({ - payload: { onEdit, onOpen, onDelete }, + payload: { onEdit, onOpen, onDelete, onQuick }, row: { original }, }) { const { formatMessage } = useIntl(); @@ -43,6 +43,13 @@ export function ActionsMenu({ onClick={safeCallback(onOpen, original)} /> + + } + text={formatMessage({ id: 'add_payment' })} + onClick={safeCallback(onQuick, original)} + /> + Date: Sun, 14 Mar 2021 15:45:27 +0200 Subject: [PATCH 2/2] feat(quick payment made): quick payment made dialog. --- client/src/components/DialogsContainer.js | 5 +- .../QuickPaymentMade.schema.js | 34 +++ .../QuickPaymentMadeFloatingActions.js | 43 ++++ .../QuickPaymentMadeForm.js | 85 ++++++++ .../QuickPaymentMadeFormContent.js | 15 ++ .../QuickPaymentMadeFormDialogContent.js | 21 ++ .../QuickPaymentMadeFormFields.js | 197 ++++++++++++++++++ .../QuickPaymentMadeFormProvider.js | 46 ++++ .../QuickPaymentMadeFormDialog/index.js | 38 ++++ .../QuickPaymentMadeFormDialog/utils.js | 24 +++ .../PaymentMades/PaymentForm/utils.js | 2 +- .../Invoices/InvoicesLanding/components.js | 2 +- .../PaymentReceiveForm.schema.js | 2 +- client/src/lang/en/index.js | 1 + 14 files changed, 511 insertions(+), 4 deletions(-) create mode 100644 client/src/containers/Dialogs/QuickPaymentMadeFormDialog/QuickPaymentMade.schema.js create mode 100644 client/src/containers/Dialogs/QuickPaymentMadeFormDialog/QuickPaymentMadeFloatingActions.js create mode 100644 client/src/containers/Dialogs/QuickPaymentMadeFormDialog/QuickPaymentMadeForm.js create mode 100644 client/src/containers/Dialogs/QuickPaymentMadeFormDialog/QuickPaymentMadeFormContent.js create mode 100644 client/src/containers/Dialogs/QuickPaymentMadeFormDialog/QuickPaymentMadeFormDialogContent.js create mode 100644 client/src/containers/Dialogs/QuickPaymentMadeFormDialog/QuickPaymentMadeFormFields.js create mode 100644 client/src/containers/Dialogs/QuickPaymentMadeFormDialog/QuickPaymentMadeFormProvider.js create mode 100644 client/src/containers/Dialogs/QuickPaymentMadeFormDialog/index.js create mode 100644 client/src/containers/Dialogs/QuickPaymentMadeFormDialog/utils.js diff --git a/client/src/components/DialogsContainer.js b/client/src/components/DialogsContainer.js index 41789dd99..1dfdd0998 100644 --- a/client/src/components/DialogsContainer.js +++ b/client/src/components/DialogsContainer.js @@ -11,6 +11,8 @@ import PaymentViaVoucherDialog from 'containers/Dialogs/PaymentViaVoucherDialog' import KeyboardShortcutsDialog from 'containers/Dialogs/keyboardShortcutsDialog'; import ContactDuplicateDialog from 'containers/Dialogs/ContactDuplicateDialog'; import QuickPaymentReceiveFormDialog from 'containers/Dialogs/QuickPaymentReceiveFormDialog'; +import QuickPaymentMadeFormDialog from 'containers/Dialogs/QuickPaymentMadeFormDialog'; + /** * Dialogs container. */ @@ -26,7 +28,8 @@ export default function DialogsContainer() { - + + ); } diff --git a/client/src/containers/Dialogs/QuickPaymentMadeFormDialog/QuickPaymentMade.schema.js b/client/src/containers/Dialogs/QuickPaymentMadeFormDialog/QuickPaymentMade.schema.js new file mode 100644 index 000000000..7139ab69c --- /dev/null +++ b/client/src/containers/Dialogs/QuickPaymentMadeFormDialog/QuickPaymentMade.schema.js @@ -0,0 +1,34 @@ +import * as Yup from 'yup'; +import { formatMessage } from 'services/intl'; +import { DATATYPES_LENGTH } from 'common/dataTypes'; + +const Schema = Yup.object().shape({ + vendor_id: Yup.string() + .label(formatMessage({ id: 'vendor_name_' })) + .required(), + payment_date: Yup.date() + .required() + .label(formatMessage({ id: 'payment_date_' })), + payment_number: Yup.string() + .nullable() + .max(DATATYPES_LENGTH.STRING) + .label(formatMessage({ id: 'payment_no_' })), + payment_account_id: Yup.number() + .required() + .label(formatMessage({ id: 'payment_account_' })), + reference: Yup.string().min(1).max(DATATYPES_LENGTH.STRING).nullable(), + // statement: Yup.string().nullable().max(DATATYPES_LENGTH.TEXT), + entries: Yup.array().of( + Yup.object().shape({ + payment_amount: Yup.number().nullable(), + bill_id: Yup.number() + .nullable() + .when(['payment_amount'], { + is: (payment_amount) => payment_amount, + then: Yup.number().required(), + }), + }), + ), +}); + +export const CreateQuickPaymentMadeFormSchema = Schema; diff --git a/client/src/containers/Dialogs/QuickPaymentMadeFormDialog/QuickPaymentMadeFloatingActions.js b/client/src/containers/Dialogs/QuickPaymentMadeFormDialog/QuickPaymentMadeFloatingActions.js new file mode 100644 index 000000000..ea2413200 --- /dev/null +++ b/client/src/containers/Dialogs/QuickPaymentMadeFormDialog/QuickPaymentMadeFloatingActions.js @@ -0,0 +1,43 @@ +import React from 'react'; +import { Intent, Button, Classes } from '@blueprintjs/core'; +import { useFormikContext } from 'formik'; +import { FormattedMessage as T } from 'react-intl'; + +import { useQuickPaymentMadeContext } from './QuickPaymentMadeFormProvider'; +import withDialogActions from 'containers/Dialog/withDialogActions'; +import { compose } from 'utils'; + +function QuickPaymentMadeFloatingActions({ + // #withDialogActions + closeDialog, +}) { + // Formik context. + const { isSubmitting } = useFormikContext(); + + const { dialogName } = useQuickPaymentMadeContext(); + + // Handle close button click. + const handleCancelBtnClick = () => { + closeDialog(dialogName); + }; + + return ( +
+
+ + +
+
+ ); +} + +export default compose(withDialogActions)(QuickPaymentMadeFloatingActions); diff --git a/client/src/containers/Dialogs/QuickPaymentMadeFormDialog/QuickPaymentMadeForm.js b/client/src/containers/Dialogs/QuickPaymentMadeFormDialog/QuickPaymentMadeForm.js new file mode 100644 index 000000000..b2bd5198e --- /dev/null +++ b/client/src/containers/Dialogs/QuickPaymentMadeFormDialog/QuickPaymentMadeForm.js @@ -0,0 +1,85 @@ +import React from 'react'; +import { Formik } from 'formik'; +import { Intent } from '@blueprintjs/core'; +import { FormattedMessage as T, useIntl } from 'react-intl'; +import { pick } from 'lodash'; + +import { AppToaster } from 'components'; +import { CreateQuickPaymentMadeFormSchema } from './QuickPaymentMade.schema'; +import { useQuickPaymentMadeContext } from './QuickPaymentMadeFormProvider'; +import QuickPaymentMadeFormContent from './QuickPaymentMadeFormContent'; + +import withDialogActions from 'containers/Dialog/withDialogActions'; +import { defaultPaymentMade, transformErrors } from './utils'; +import { compose } from 'utils'; + +/** + * Quick payment made form. + */ +function QuickPaymentMadeForm({ + // #withDialogActions + closeDialog, +}) { + const { formatMessage } = useIntl(); + const { + bill, + dialogName, + createPaymentMadeMutate, + } = useQuickPaymentMadeContext(); + + // Initial form values + const initialValues = { + ...defaultPaymentMade, + ...bill, + }; + + // Handles the form submit. + const handleFormSubmit = (values, { setSubmitting, setFieldError }) => { + const entries = [values] + .filter((entry) => entry.id && entry.payment_amount) + .map((entry) => ({ + bill_id: entry.id, + ...pick(entry, ['payment_amount']), + })); + + const form = { + ...values, + vendor_id: values?.vendor?.id, + entries, + }; + + // Handle request response success. + const onSuccess = () => { + AppToaster.show({ + message: formatMessage({ + id: 'the_payment_made_has_been_created_successfully', + }), + intent: Intent.SUCCESS, + }); + closeDialog(dialogName); + }; + // Handle request response errors. + const onError = ({ + response: { + data: { errors }, + }, + }) => { + if (errors) { + transformErrors(errors, { setFieldError }); + } + setSubmitting(false); + }; + createPaymentMadeMutate(form).then(onSuccess).catch(onError); + }; + + return ( + + ); +} + +export default compose(withDialogActions)(QuickPaymentMadeForm); diff --git a/client/src/containers/Dialogs/QuickPaymentMadeFormDialog/QuickPaymentMadeFormContent.js b/client/src/containers/Dialogs/QuickPaymentMadeFormDialog/QuickPaymentMadeFormContent.js new file mode 100644 index 000000000..f2bbd8615 --- /dev/null +++ b/client/src/containers/Dialogs/QuickPaymentMadeFormDialog/QuickPaymentMadeFormContent.js @@ -0,0 +1,15 @@ +import React from 'react'; +import { Form } from 'formik'; +import QuickPaymentMadeFormFields from './QuickPaymentMadeFormFields'; +import QuickPaymentMadeFloatingActions from './QuickPaymentMadeFloatingActions'; +/** + * Quick payment made form content. + */ +export default function QuickPaymentMadeFormContent() { + return ( +
+ + + + ); +} diff --git a/client/src/containers/Dialogs/QuickPaymentMadeFormDialog/QuickPaymentMadeFormDialogContent.js b/client/src/containers/Dialogs/QuickPaymentMadeFormDialog/QuickPaymentMadeFormDialogContent.js new file mode 100644 index 000000000..bfc35be0c --- /dev/null +++ b/client/src/containers/Dialogs/QuickPaymentMadeFormDialog/QuickPaymentMadeFormDialogContent.js @@ -0,0 +1,21 @@ +import React from 'react'; + +import 'style/pages/PaymentReceive/QuickPaymentReceiveDialog.scss'; + +import { QuickPaymentMadeFormProvider } from './QuickPaymentMadeFormProvider'; +import QuickPaymentMadeForm from './QuickPaymentMadeForm'; + +/** + * Quick payment made form dialog content. + */ +export default function QuickPaymentMadeFormDialogContent({ + // #ownProps + dialogName, + bill, +}) { + return ( + + + + ); +} diff --git a/client/src/containers/Dialogs/QuickPaymentMadeFormDialog/QuickPaymentMadeFormFields.js b/client/src/containers/Dialogs/QuickPaymentMadeFormDialog/QuickPaymentMadeFormFields.js new file mode 100644 index 000000000..361923570 --- /dev/null +++ b/client/src/containers/Dialogs/QuickPaymentMadeFormDialog/QuickPaymentMadeFormFields.js @@ -0,0 +1,197 @@ +import React from 'react'; +import { FastField, ErrorMessage } from 'formik'; +import { FormattedMessage as T } from 'react-intl'; +import { + Classes, + FormGroup, + InputGroup, + TextArea, + Position, + ControlGroup, +} from '@blueprintjs/core'; +import { useAutofocus } from 'hooks'; +import classNames from 'classnames'; +import { CLASSES } from 'common/classes'; +import { DateInput } from '@blueprintjs/datetime'; +import { FieldRequiredHint, Col, Row } from 'components'; +import { + AccountsSelectList, + InputPrependText, + MoneyInputGroup, + Icon, +} from 'components'; +import { + inputIntent, + momentFormatter, + tansformDateValue, + handleDateChange, +} from 'utils'; +import { useQuickPaymentMadeContext } from './QuickPaymentMadeFormProvider'; + +/** + * Quick payment made form fields. + */ +export default function QuickPaymentMadeFormFields() { + const { accounts } = useQuickPaymentMadeContext(); + const paymentMadeFieldRef = useAutofocus(); + + return ( +
+ + + {/* ------------- Vendor name ------------- */} + + {({ from, field, meta: { error, touched } }) => ( + } + className={classNames('form-group--select-list', CLASSES.FILL)} + labelInfo={} + intent={inputIntent({ error, touched })} + helperText={} + > + + + )} + + + + {/* ------------ Payment number. ------------ */} + + {({ form, field, meta: { error, touched } }) => ( + } + className={('form-group--payment_number', CLASSES.FILL)} + intent={inputIntent({ error, touched })} + helperText={} + > + + + )} + + + + {/*------------ Amount Received -----------*/} + + {({ + form: { values, setFieldValue }, + field: { value }, + meta: { error, touched }, + }) => ( + } + labelInfo={} + className={classNames('form-group--payment_amount', CLASSES.FILL)} + intent={inputIntent({ error, touched })} + helperText={} + > + + + + { + setFieldValue('payment_amount', amount); + }} + intent={inputIntent({ error, touched })} + inputRef={(ref) => (paymentMadeFieldRef.current = ref)} + /> + + + )} + + + + {/* ------------- Payment date ------------- */} + + {({ form, field: { value }, meta: { error, touched } }) => ( + } + labelInfo={} + className={classNames('form-group--select-list', CLASSES.FILL)} + intent={inputIntent({ error, touched })} + helperText={} + > + { + form.setFieldValue('payment_date', formattedDate); + })} + popoverProps={{ position: Position.BOTTOM, minimal: true }} + inputProps={{ + leftIcon: , + }} + /> + + )} + + + + {/* ------------ payment account ------------ */} + + {({ form, field: { value }, meta: { error, touched } }) => ( + } + className={classNames( + 'form-group--payment_account_id', + 'form-group--select-list', + CLASSES.FILL, + )} + labelInfo={} + intent={inputIntent({ error, touched })} + helperText={} + > + } + onAccountSelected={(account) => { + form.setFieldValue('payment_account_id', account.id); + }} + selectedAccountId={value} + /> + + )} + + + + {/* ------------ Reference No. ------------ */} + + {({ form, field, meta: { error, touched } }) => ( + } + className={classNames('form-group--reference', CLASSES.FILL)} + intent={inputIntent({ error, touched })} + helperText={} + > + + + )} + + {/* --------- Statement --------- */} + + {({ form, field, meta: { error, touched } }) => ( + } + className={'form-group--statement'} + > +