diff --git a/src/components/DialogsContainer.js b/src/components/DialogsContainer.js index 871e38a58..656c7d9d8 100644 --- a/src/components/DialogsContainer.js +++ b/src/components/DialogsContainer.js @@ -26,6 +26,10 @@ import NotifyEstimateViaSMSDialog from '../containers/Dialogs/NotifyEstimateViaS import NotifyPaymentReceiveViaSMSDialog from '../containers/Dialogs/NotifyPaymentReceiveViaSMSDialog'; import SMSMessageDialog from '../containers/Dialogs/SMSMessageDialog'; import TransactionsLockingDialog from '../containers/Dialogs/TransactionsLockingDialog'; +import RefundCreditNoteDialog from '../containers/Dialogs/RefundCreditNoteDialog'; +import RefundVendorCreditDialog from '../containers/Dialogs/RefundVendorCreditDialog'; +import ReconcileCreditNoteDialog from '../containers/Dialogs/ReconcileCreditNoteDialog'; +import ReconcileVendorCreditDialog from '../containers/Dialogs/ReconcileVendorCreditDialog'; /** * Dialogs container. @@ -60,6 +64,10 @@ export default function DialogsContainer() { + + + + ); } diff --git a/src/containers/Alerts/CreditNotes/CreditNoteDeleteAlert.js b/src/containers/Alerts/CreditNotes/CreditNoteDeleteAlert.js index be664372a..5c70206cd 100644 --- a/src/containers/Alerts/CreditNotes/CreditNoteDeleteAlert.js +++ b/src/containers/Alerts/CreditNotes/CreditNoteDeleteAlert.js @@ -9,6 +9,7 @@ import withAlertActions from 'containers/Alert/withAlertActions'; import withDrawerActions from 'containers/Drawer/withDrawerActions'; import { useDeleteCreditNote } from 'hooks/query'; +import { handleDeleteErrors } from '../../Sales/CreditNotes/CreditNotesLanding/utils'; import { compose } from 'utils'; /** @@ -48,7 +49,9 @@ function CreditNoteDeleteAlert({ response: { data: { errors }, }, - }) => {}, + }) => { + handleDeleteErrors(errors); + }, ) .finally(() => { closeAlert(name); diff --git a/src/containers/Alerts/CreditNotes/CreditNoteOpenedAlert.js b/src/containers/Alerts/CreditNotes/CreditNoteOpenedAlert.js new file mode 100644 index 000000000..58b9e8554 --- /dev/null +++ b/src/containers/Alerts/CreditNotes/CreditNoteOpenedAlert.js @@ -0,0 +1,68 @@ +import React from 'react'; +import { FormattedMessage as T } from 'components'; +import intl from 'react-intl-universal'; +import { Intent, Alert } from '@blueprintjs/core'; + +import { useOpenCreditNote } from 'hooks/query'; +import { AppToaster } from 'components'; + +import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect'; +import withAlertActions from 'containers/Alert/withAlertActions'; + +import { compose } from 'utils'; + +/** + * Credit note opened alert. + */ +function CreditNoteOpenedAlert({ + name, + + // #withAlertStoreConnect + isOpen, + payload: { creditNoteId }, + + // #withAlertActions + closeAlert, +}) { + const { mutateAsync: openCreditNoteMutate, isLoading } = useOpenCreditNote(); + + // Handle cancel opened credit note alert. + const handleAlertCancel = () => { + closeAlert(name); + }; + + // Handle confirm credit note opened. + const handleAlertConfirm = () => { + openCreditNoteMutate(creditNoteId) + .then(() => { + AppToaster.show({ + message: intl.get('credit_note_opened.alert.success_message'), + intent: Intent.SUCCESS, + }); + }) + .catch((error) => {}) + .finally(() => { + closeAlert(name); + }); + }; + + return ( + } + confirmButtonText={} + intent={Intent.WARNING} + isOpen={isOpen} + onCancel={handleAlertCancel} + onConfirm={handleAlertConfirm} + loading={isLoading} + > +

+ +

+
+ ); +} +export default compose( + withAlertStoreConnect(), + withAlertActions, +)(CreditNoteOpenedAlert); diff --git a/src/containers/Alerts/CreditNotes/ReconcileCreditNoteDeleteAlert.js b/src/containers/Alerts/CreditNotes/ReconcileCreditNoteDeleteAlert.js new file mode 100644 index 000000000..1d84cdbb6 --- /dev/null +++ b/src/containers/Alerts/CreditNotes/ReconcileCreditNoteDeleteAlert.js @@ -0,0 +1,88 @@ +import React from 'react'; +import intl from 'react-intl-universal'; +import { FormattedMessage as T, FormattedHTMLMessage } from 'components'; +import { Intent, Alert } from '@blueprintjs/core'; +import { AppToaster } from 'components'; + +import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect'; +import withAlertActions from 'containers/Alert/withAlertActions'; +import withDrawerActions from 'containers/Drawer/withDrawerActions'; + +import { useDeleteReconcileCredit } from 'hooks/query'; +import { handleDeleteErrors } from '../../Sales/CreditNotes/CreditNotesLanding/utils'; +import { compose } from 'utils'; + +/** + * Reconcile credit note delete alert. + */ +function ReconcileCreditNoteDeleteAlert({ + name, + + // #withAlertStoreConnect + isOpen, + payload: { creditNoteId }, + + // #withAlertActions + closeAlert, + + // #withDrawerActions + closeDrawer, +}) { + const { isLoading, mutateAsync: deleteReconcileCreditMutate } = + useDeleteReconcileCredit(); + + // handle cancel delete credit note alert. + const handleCancelDeleteAlert = () => { + closeAlert(name); + }; + + const handleConfirmVendorCreditDelete = () => { + deleteReconcileCreditMutate(creditNoteId) + .then(() => { + AppToaster.show({ + message: intl.get('reconcile_credit_note.alert.success_message'), + intent: Intent.SUCCESS, + }); + closeDrawer('credit-note-detail-drawer'); + }) + .catch( + ({ + response: { + data: { errors }, + }, + }) => { + // handleDeleteErrors(errors); + }, + ) + .finally(() => { + closeAlert(name); + }); + }; + + return ( + } + confirmButtonText={} + icon="trash" + intent={Intent.DANGER} + isOpen={isOpen} + onCancel={handleCancelDeleteAlert} + onConfirm={handleConfirmVendorCreditDelete} + loading={isLoading} + > +

+ +

+
+ ); +} + +export default compose( + withAlertStoreConnect(), + withAlertActions, + withDrawerActions, +)(ReconcileCreditNoteDeleteAlert); diff --git a/src/containers/Alerts/CreditNotes/RefundCreditNoteDeleteAlert.js b/src/containers/Alerts/CreditNotes/RefundCreditNoteDeleteAlert.js new file mode 100644 index 000000000..9fe3774ad --- /dev/null +++ b/src/containers/Alerts/CreditNotes/RefundCreditNoteDeleteAlert.js @@ -0,0 +1,68 @@ +import React from 'react'; +import intl from 'react-intl-universal'; +import { FormattedMessage as T, FormattedHTMLMessage } from 'components'; +import { Intent, Alert } from '@blueprintjs/core'; +import { AppToaster } from 'components'; +import { useDeleteRefundCreditNote } from 'hooks/query'; + +import withAlertActions from 'containers/Alert/withAlertActions'; +import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect'; + +import { compose } from 'utils'; + +/** + * Refund credit transactions delete alert + */ +function RefundCreditNoteDeleteAlert({ + name, + // #withAlertStoreConnect + isOpen, + payload: { creditNoteId }, + // #withAlertActions + closeAlert, +}) { + const { mutateAsync: deleteRefundCreditMutate, isLoading } = + useDeleteRefundCreditNote(); + + // Handle cancel delete. + const handleCancelAlert = () => { + closeAlert(name); + }; + + // Handle confirm delete . + const handleConfirmRefundCreditDelete = () => { + deleteRefundCreditMutate(creditNoteId) + .then(() => { + AppToaster.show({ + message: intl.get('refund_credit_transactions.alert.delete_message'), + intent: Intent.SUCCESS, + }); + closeAlert(name); + }) + .catch(() => {}); + }; + + return ( + } + confirmButtonText={} + icon="trash" + intent={Intent.DANGER} + isOpen={isOpen} + onCancel={handleCancelAlert} + onConfirm={handleConfirmRefundCreditDelete} + loading={isLoading} + > +

+ +

+
+ ); +} + +export default compose( + withAlertStoreConnect(), + withAlertActions, +)(RefundCreditNoteDeleteAlert); diff --git a/src/containers/Alerts/VendorCeditNotes/RefundVendorCreditDeleteAlert.js b/src/containers/Alerts/VendorCeditNotes/RefundVendorCreditDeleteAlert.js new file mode 100644 index 000000000..526d92bfb --- /dev/null +++ b/src/containers/Alerts/VendorCeditNotes/RefundVendorCreditDeleteAlert.js @@ -0,0 +1,70 @@ +import React from 'react'; +import intl from 'react-intl-universal'; +import { FormattedMessage as T, FormattedHTMLMessage } from 'components'; +import { Intent, Alert } from '@blueprintjs/core'; +import { AppToaster } from 'components'; +import { useDeleteRefundVendorCredit } from 'hooks/query'; + +import withAlertActions from 'containers/Alert/withAlertActions'; +import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect'; + +import { compose } from 'utils'; + +/** + * Refund Vendor transactions delete alert. + */ +function RefundVendorCreditDeleteAlert({ + name, + // #withAlertStoreConnect + isOpen, + payload: { vendorCreditId }, + // #withAlertActions + closeAlert, +}) { + const { mutateAsync: deleteRefundVendorCreditMutate, isLoading } = + useDeleteRefundVendorCredit(); + + // Handle cancel delete. + const handleCancelAlert = () => { + closeAlert(name); + }; + + // Handle confirm delete . + const handleConfirmRefundVendorCreditDelete = () => { + deleteRefundVendorCreditMutate(vendorCreditId) + .then(() => { + AppToaster.show({ + message: intl.get( + 'refund_vendor_credit_transactions.alert.delete_message', + ), + intent: Intent.SUCCESS, + }); + closeAlert(name); + }) + .catch(() => {}); + }; + + return ( + } + confirmButtonText={} + icon="trash" + intent={Intent.DANGER} + isOpen={isOpen} + onCancel={handleCancelAlert} + onConfirm={handleConfirmRefundVendorCreditDelete} + loading={isLoading} + > +

+ +

+
+ ); +} + +export default compose( + withAlertStoreConnect(), + withAlertActions, +)(RefundVendorCreditDeleteAlert); diff --git a/src/containers/Alerts/VendorCeditNotes/VendorCreditOpenedAlert.js b/src/containers/Alerts/VendorCeditNotes/VendorCreditOpenedAlert.js new file mode 100644 index 000000000..56c0fe5bf --- /dev/null +++ b/src/containers/Alerts/VendorCeditNotes/VendorCreditOpenedAlert.js @@ -0,0 +1,69 @@ +import React from 'react'; +import { FormattedMessage as T } from 'components'; +import intl from 'react-intl-universal'; +import { Intent, Alert } from '@blueprintjs/core'; + +import { useOpenVendorCredit } from 'hooks/query'; +import { AppToaster } from 'components'; + +import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect'; +import withAlertActions from 'containers/Alert/withAlertActions'; + +import { compose } from 'utils'; + +/** + * Vendor credit opened alert. + */ +function VendorCreditOpenedAlert({ + name, + + // #withAlertStoreConnect + isOpen, + payload: { vendorCreditId }, + + // #withAlertActions + closeAlert, +}) { + const { mutateAsync: openVendorCreditMutate, isLoading } = + useOpenVendorCredit(); + + // Handle cancel opened credit note alert. + const handleAlertCancel = () => { + closeAlert(name); + }; + + // Handle confirm vendor credit as opened. + const handleAlertConfirm = () => { + openVendorCreditMutate(vendorCreditId) + .then(() => { + AppToaster.show({ + message: intl.get('vendor_credit_opened.alert.success_message'), + intent: Intent.SUCCESS, + }); + }) + .catch((error) => {}) + .finally(() => { + closeAlert(name); + }); + }; + + return ( + } + confirmButtonText={} + intent={Intent.WARNING} + isOpen={isOpen} + onCancel={handleAlertCancel} + onConfirm={handleAlertConfirm} + loading={isLoading} + > +

+ +

+
+ ); +} +export default compose( + withAlertStoreConnect(), + withAlertActions, +)(VendorCreditOpenedAlert); diff --git a/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteDialogContent.js b/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteDialogContent.js new file mode 100644 index 000000000..62b18d718 --- /dev/null +++ b/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteDialogContent.js @@ -0,0 +1,21 @@ +import React from 'react'; +import { ReconcileCreditNoteFormProvider } from './ReconcileCreditNoteFormProvider'; +import ReconcileCreditNoteForm from './ReconcileCreditNoteForm'; + +/** + * Reconcile credit note dialog content. + */ +export default function ReconcileCreditNoteDialogContent({ + // #ownProps + dialogName, + creditNoteId, +}) { + return ( + + + + ); +} diff --git a/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteEntriesTable.js b/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteEntriesTable.js new file mode 100644 index 000000000..c268c7e00 --- /dev/null +++ b/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteEntriesTable.js @@ -0,0 +1,75 @@ +import React from 'react'; +import intl from 'react-intl-universal'; +import { MoneyFieldCell, DataTableEditable, FormatDateCell } from 'components'; +import { compose, updateTableCell } from 'utils'; + +/** + * Reconcile credit note entries table. + */ +export default function ReconcileCreditNoteEntriesTable({ + onUpdateData, + entries, + errors, +}) { + const columns = React.useMemo( + () => [ + { + Header: intl.get('invoice_date'), + accessor: 'formatted_invoice_date', + Cell: FormatDateCell, + disableSortBy: true, + width: '120', + }, + { + Header: intl.get('invoice_no'), + accessor: 'invoice_no', + disableSortBy: true, + width: '100', + }, + { + Header: intl.get('amount'), + accessor: 'formatted_amount', + disableSortBy: true, + align: 'right', + width: '100', + }, + { + Header: intl.get('reconcile_credit_note.column.remaining_amount'), + accessor: 'formatted_due_amount', + disableSortBy: true, + align: 'right', + width: '150', + }, + { + Header: intl.get('reconcile_credit_note.column.amount_to_credit'), + accessor: 'amount', + Cell: MoneyFieldCell, + disableSortBy: true, + width: '150', + }, + ], + [], + ); + + // Handle update data. + const handleUpdateData = React.useCallback( + (rowIndex, columnId, value) => { + const newRows = compose(updateTableCell(rowIndex, columnId, value))( + entries, + ); + onUpdateData(newRows); + }, + [onUpdateData, entries], + ); + + return ( + + ); +} diff --git a/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteForm.js b/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteForm.js new file mode 100644 index 000000000..6811e2170 --- /dev/null +++ b/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteForm.js @@ -0,0 +1,99 @@ +import React from 'react'; +import { Formik } from 'formik'; +import { Intent } from '@blueprintjs/core'; +import intl from 'react-intl-universal'; + +import '../../../style/pages/ReconcileCreditNote/ReconcileCreditNoteForm.scss'; +import { AppToaster } from 'components'; +import { CreateReconcileCreditNoteFormSchema } from './ReconcileCreditNoteForm.schema'; +import { useReconcileCreditNoteContext } from './ReconcileCreditNoteFormProvider'; +import ReconcileCreditNoteFormContent from './ReconcileCreditNoteFormContent'; +import withDialogActions from 'containers/Dialog/withDialogActions'; +import { compose, transformToForm } from 'utils'; +import { transformErrors } from './utils'; + +// Default form initial values. +const defaultInitialValues = { + entries: [ + { + invoice_id: '', + amount: '', + }, + ], +}; + +/** + * Reconcile credit note form. + */ +function ReconcileCreditNoteForm({ + // #withDialogActions + closeDialog, +}) { + const { + dialogName, + creditNoteId, + reconcileCreditNotes, + createReconcileCreditNoteMutate, + } = useReconcileCreditNoteContext(); + + // Initial form values. + const initialValues = { + entries: reconcileCreditNotes.map((entry) => ({ + ...entry, + invoice_id: entry.id, + amount: '', + })), + }; + + // Handle form submit. + const handleFormSubmit = (values, { setSubmitting, setErrors }) => { + setSubmitting(false); + + // Filters the entries. + const entries = values.entries + .filter((entry) => entry.invoice_id && entry.amount) + .map((entry) => transformToForm(entry, defaultInitialValues.entries[0])); + + const form = { + ...values, + entries: entries, + }; + + // Handle the request success. + const onSuccess = (response) => { + AppToaster.show({ + message: intl.get('reconcile_credit_note.success_message'), + intent: Intent.SUCCESS, + }); + setSubmitting(false); + closeDialog(dialogName); + }; + + // Handle the request error. + const onError = ({ + response: { + data: { errors }, + }, + }) => { + if (errors) { + transformErrors(errors, { setErrors }); + } + setSubmitting(false); + }; + + createReconcileCreditNoteMutate([creditNoteId, form]) + .then(onSuccess) + .catch(onError); + }; + + return ( + + ); +} + +export default compose(withDialogActions)(ReconcileCreditNoteForm); diff --git a/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteForm.schema.js b/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteForm.schema.js new file mode 100644 index 000000000..a6cd9dc09 --- /dev/null +++ b/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteForm.schema.js @@ -0,0 +1,12 @@ +import * as Yup from 'yup'; + +const Schema = Yup.object().shape({ + entries: Yup.array().of( + Yup.object().shape({ + invoice_id: Yup.number().required(), + amount: Yup.number().nullable(), + }), + ), +}); + +export const CreateReconcileCreditNoteFormSchema = Schema; diff --git a/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteFormContent.js b/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteFormContent.js new file mode 100644 index 000000000..1c1e12d21 --- /dev/null +++ b/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteFormContent.js @@ -0,0 +1,28 @@ +import React from 'react'; +import { Form } from 'formik'; +import { Choose } from 'components'; + +import ReconcileCreditNoteFormFields from './ReconcileCreditNoteFormFields'; +import ReconcileCreditNoteFormFloatingActions from './ReconcileCreditNoteFormFloatingActions'; +import { EmptyStatuCallout } from './utils'; +import { useReconcileCreditNoteContext } from './ReconcileCreditNoteFormProvider'; + +/** + * Reconcile credit note form content. + */ +export default function ReconcileCreditNoteFormContent() { + const { isEmptyStatus } = useReconcileCreditNoteContext(); + return ( + + + + + +
+ + + +
+
+ ); +} diff --git a/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteFormFields.js b/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteFormFields.js new file mode 100644 index 000000000..a035fd95d --- /dev/null +++ b/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteFormFields.js @@ -0,0 +1,60 @@ +import React from 'react'; +import { FastField, useFormikContext } from 'formik'; +import { Classes } from '@blueprintjs/core'; +import { T, TotalLines, TotalLine } from 'components'; +import { getEntriesTotal } from 'containers/Entries/utils'; +import ReconcileCreditNoteEntriesTable from './ReconcileCreditNoteEntriesTable'; +import { useReconcileCreditNoteContext } from './ReconcileCreditNoteFormProvider'; +import { formattedAmount } from 'utils'; + +/** + * Reconcile credit note form fields. + */ +export default function ReconcileCreditNoteFormFields() { + const { + creditNote: { formatted_credits_remaining, currency_code }, + } = useReconcileCreditNoteContext(); + + const { values } = useFormikContext(); + + // Calculate the total amount. + const totalAmount = React.useMemo( + () => getEntriesTotal(values.entries), + [values.entries], + ); + + return ( +
+ {/*------------ Reconcile credit entries table -----------*/} + + {({ + form: { setFieldValue, values }, + field: { value }, + meta: { error, touched }, + }) => ( + { + setFieldValue('entries', newEntries); + }} + /> + )} + +
+ + + } + value={formattedAmount(totalAmount, currency_code)} + /> + } + value={formatted_credits_remaining} + /> + +
+
+ ); +} diff --git a/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteFormFloatingActions.js b/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteFormFloatingActions.js new file mode 100644 index 000000000..9fb415500 --- /dev/null +++ b/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteFormFloatingActions.js @@ -0,0 +1,47 @@ +import React from 'react'; +import { useFormikContext } from 'formik'; +import { Intent, Button, Classes } from '@blueprintjs/core'; +import { FormattedMessage as T } from 'components'; + +import { useReconcileCreditNoteContext } from './ReconcileCreditNoteFormProvider'; +import withDialogActions from 'containers/Dialog/withDialogActions'; +import { compose } from 'utils'; + +/** + * Reconcile credit note floating actions. + */ +function ReconcileCreditNoteFormFloatingActions({ + // #withDialogActions + closeDialog, +}) { + // Formik context. + const { isSubmitting } = useFormikContext(); + + const { dialogName } = useReconcileCreditNoteContext(); + + // Handle cancel button click. + const handleCancelBtnClick = (event) => { + closeDialog(dialogName); + }; + + return ( +
+
+ + +
+
+ ); +} +export default compose(withDialogActions)( + ReconcileCreditNoteFormFloatingActions, +); diff --git a/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteFormProvider.js b/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteFormProvider.js new file mode 100644 index 000000000..92cc90bd1 --- /dev/null +++ b/src/containers/Dialogs/ReconcileCreditNoteDialog/ReconcileCreditNoteFormProvider.js @@ -0,0 +1,64 @@ +import React from 'react'; +import { DialogContent } from 'components'; +import { + useCreditNote, + useReconcileCreditNote, + useCreateReconcileCreditNote, +} from 'hooks/query'; +import { isEmpty } from 'lodash'; + +const ReconcileCreditNoteDialogContext = React.createContext(); + +/** + * Reconcile credit note provider. + */ +function ReconcileCreditNoteFormProvider({ + creditNoteId, + dialogName, + ...props +}) { + // Handle fetch reconcile credit note details. + const { isLoading: isReconcileCreditLoading, data: reconcileCreditNotes } = + useReconcileCreditNote(creditNoteId, { + enabled: !!creditNoteId, + }); + + // Handle fetch vendor credit details. + const { data: creditNote, isLoading: isCreditNoteLoading } = useCreditNote( + creditNoteId, + { + enabled: !!creditNoteId, + }, + ); + + // Create reconcile credit note mutations. + const { mutateAsync: createReconcileCreditNoteMutate } = + useCreateReconcileCreditNote(); + + // Detarmines the datatable empty status. + const isEmptyStatus = isEmpty(reconcileCreditNotes); + + // provider payload. + const provider = { + dialogName, + reconcileCreditNotes, + createReconcileCreditNoteMutate, + isEmptyStatus, + creditNote, + creditNoteId, + }; + + return ( + + + + ); +} + +const useReconcileCreditNoteContext = () => + React.useContext(ReconcileCreditNoteDialogContext); + +export { ReconcileCreditNoteFormProvider, useReconcileCreditNoteContext }; diff --git a/src/containers/Dialogs/ReconcileCreditNoteDialog/index.js b/src/containers/Dialogs/ReconcileCreditNoteDialog/index.js new file mode 100644 index 000000000..217d93b32 --- /dev/null +++ b/src/containers/Dialogs/ReconcileCreditNoteDialog/index.js @@ -0,0 +1,36 @@ +import React from 'react'; +import { FormattedMessage as T, Dialog, DialogSuspense } from 'components'; +import withDialogRedux from 'components/DialogReduxConnect'; +import { compose } from 'utils'; + +const ReconcileCreditNoteDialogContent = React.lazy(() => + import('./ReconcileCreditNoteDialogContent'), +); + +/** + * Reconcile credit note dialog. + */ +function ReconcileCreditNoteDialog({ + dialogName, + payload: { creditNoteId }, + isOpen, +}) { + return ( + } + canEscapeKeyClose={true} + isOpen={isOpen} + className="dialog--reconcile-credit-form" + > + + + + + ); +} + +export default compose(withDialogRedux())(ReconcileCreditNoteDialog); diff --git a/src/containers/Dialogs/ReconcileCreditNoteDialog/utils.js b/src/containers/Dialogs/ReconcileCreditNoteDialog/utils.js new file mode 100644 index 000000000..4f9b6a8f1 --- /dev/null +++ b/src/containers/Dialogs/ReconcileCreditNoteDialog/utils.js @@ -0,0 +1,35 @@ +import React from 'react'; +import intl from 'react-intl-universal'; +import { Callout, Intent, Classes } from '@blueprintjs/core'; + +import { AppToaster, T } from 'components'; + +export const transformErrors = (errors, { setErrors }) => { + if (errors.some((e) => e.type === 'INVOICES_HAS_NO_REMAINING_AMOUNT')) { + AppToaster.show({ + message: 'INVOICES_HAS_NO_REMAINING_AMOUNT', + intent: Intent.DANGER, + }); + } + + if ( + errors.find((error) => error.type === 'CREDIT_NOTE_HAS_NO_REMAINING_AMOUNT') + ) { + AppToaster.show({ + message: 'CREDIT_NOTE_HAS_NO_REMAINING_AMOUNT', + intent: Intent.DANGER, + }); + } +}; + +export function EmptyStatuCallout() { + return ( +
+ +

+ +

+
+
+ ); +} diff --git a/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditDialogContent.js b/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditDialogContent.js new file mode 100644 index 000000000..651b33756 --- /dev/null +++ b/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditDialogContent.js @@ -0,0 +1,18 @@ +import React from 'react'; +import { ReconcileVendorCreditFormProvider } from './ReconcileVendorCreditFormProvider'; +import ReconcileVendorCreditForm from './ReconcileVendorCreditForm'; + +export default function ReconcileVendorCreditDialogContent({ + // #ownProps + dialogName, + vendorCreditId, +}) { + return ( + + + + ); +} diff --git a/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditEntriesTable.js b/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditEntriesTable.js new file mode 100644 index 000000000..ee6e23eb7 --- /dev/null +++ b/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditEntriesTable.js @@ -0,0 +1,72 @@ +import React from 'react'; +import intl from 'react-intl-universal'; +import { MoneyFieldCell, DataTableEditable, FormatDateCell } from 'components'; +import { compose, updateTableCell } from 'utils'; + +export default function ReconcileVendorCreditEntriesTable({ + onUpdateData, + entries, + errors, +}) { + const columns = React.useMemo( + () => [ + { + Header: intl.get('bill_date'), + accessor: 'formatted_bill_date', + Cell: FormatDateCell, + disableSortBy: true, + width: '120', + }, + { + Header: intl.get('reconcile_vendor_credit.column.bill_number'), + accessor: 'bill_number', + disableSortBy: true, + width: '100', + }, + { + Header: intl.get('amount'), + accessor: 'formatted_amount', + disableSortBy: true, + align: 'right', + width: '100', + }, + { + Header: intl.get('reconcile_vendor_credit.column.remaining_amount'), + accessor: 'formatted_due_amount', + disableSortBy: true, + align: 'right', + width: '150', + }, + { + Header: intl.get('reconcile_vendor_credit.column.amount_to_credit'), + accessor: 'amount', + Cell: MoneyFieldCell, + disableSortBy: true, + width: '150', + }, + ], + [], + ); + + // Handle update data. + const handleUpdateData = React.useCallback( + (rowIndex, columnId, value) => { + const newRows = compose(updateTableCell(rowIndex, columnId, value))( + entries, + ); + onUpdateData(newRows); + }, + [onUpdateData, entries], + ); + + return ( + + ); +} diff --git a/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditFloatingActions.js b/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditFloatingActions.js new file mode 100644 index 000000000..95dc83c65 --- /dev/null +++ b/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditFloatingActions.js @@ -0,0 +1,41 @@ +import React from 'react'; +import { useFormikContext } from 'formik'; +import { Intent, Button, Classes } from '@blueprintjs/core'; +import { FormattedMessage as T } from 'components'; + +import { useReconcileVendorCreditContext } from './ReconcileVendorCreditFormProvider'; +import withDialogActions from 'containers/Dialog/withDialogActions'; +import { compose } from 'utils'; + +function ReconcileVendorCreditFloatingActions({ + // #withDialogActions + closeDialog, +}) { + // Formik context. + const { isSubmitting } = useFormikContext(); + + const { dialogName } = useReconcileVendorCreditContext(); + + // Handle cancel button click. + const handleCancelBtnClick = (event) => { + closeDialog(dialogName); + }; + return ( +
+
+ + +
+
+ ); +} +export default compose(withDialogActions)(ReconcileVendorCreditFloatingActions); diff --git a/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditForm.js b/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditForm.js new file mode 100644 index 000000000..175e0ccb3 --- /dev/null +++ b/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditForm.js @@ -0,0 +1,98 @@ +import React from 'react'; +import { Formik } from 'formik'; +import { Intent } from '@blueprintjs/core'; +import intl from 'react-intl-universal'; + +import '../../../style/pages/ReconcileVendorCredit/ReconcileVendorCreditForm.scss'; + +import { AppToaster } from 'components'; +import { CreateReconcileVendorCreditFormSchema } from './ReconcileVendorCreditForm.schema'; +import { useReconcileVendorCreditContext } from './ReconcileVendorCreditFormProvider'; +import ReconcileVendorCreditFormContent from './ReconcileVendorCreditFormContent'; +import withDialogActions from 'containers/Dialog/withDialogActions'; +import { compose, transformToForm } from 'utils'; + +// Default form initial values. +const defaultInitialValues = { + entries: [ + { + bill_id: '', + amount: '', + }, + ], +}; + +/** + * Reconcile vendor credit form. + */ +function ReconcileVendorCreditForm({ + // #withDialogActions + closeDialog, +}) { + const { + dialogName, + reconcileVendorCredits, + createReconcileVendorCreditMutate, + vendorCredit, + } = useReconcileVendorCreditContext(); + + // Initial form values. + const initialValues = { + entries: reconcileVendorCredits.map((entry) => ({ + ...entry, + bill_id: entry.id, + amount: '', + })), + }; + + // Handle form submit. + const handleFormSubmit = (values, { setSubmitting, setErrors }) => { + setSubmitting(false); + + // Filters the entries. + const entries = values.entries + .filter((entry) => entry.bill_id && entry.amount) + .map((entry) => transformToForm(entry, defaultInitialValues.entries[0])); + + const form = { + ...values, + entries: entries, + }; + + // Handle the request success. + const onSuccess = (response) => { + AppToaster.show({ + message: intl.get('reconcile_vendor_credit.dialog.success_message'), + intent: Intent.SUCCESS, + }); + setSubmitting(false); + closeDialog(dialogName); + }; + + // Handle the request error. + const onError = ({ + response: { + data: { errors }, + }, + }) => { + // if (errors) { + // transformErrors(errors, { setErrors }); + // } + setSubmitting(false); + }; + + createReconcileVendorCreditMutate([vendorCredit.id, form]) + .then(onSuccess) + .catch(onError); + }; + + return ( + + ); +} +export default compose(withDialogActions)(ReconcileVendorCreditForm); diff --git a/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditForm.schema.js b/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditForm.schema.js new file mode 100644 index 000000000..d15b472d9 --- /dev/null +++ b/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditForm.schema.js @@ -0,0 +1,12 @@ +import * as Yup from 'yup'; + +const Schema = Yup.object().shape({ + entries: Yup.array().of( + Yup.object().shape({ + bill_id: Yup.number().required(), + amount: Yup.number().nullable(), + }), + ), +}); + +export const CreateReconcileVendorCreditFormSchema = Schema; diff --git a/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditFormContent.js b/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditFormContent.js new file mode 100644 index 000000000..c88b1de20 --- /dev/null +++ b/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditFormContent.js @@ -0,0 +1,26 @@ +import React from 'react'; +import { Form } from 'formik'; +import { Choose } from 'components'; + +import { EmptyStatuCallout } from './utils'; +import ReconcileVendorCreditFormFields from './ReconcileVendorCreditFormFields'; +import ReconcileVendorCreditFloatingActions from './ReconcileVendorCreditFloatingActions'; +import { useReconcileVendorCreditContext } from './ReconcileVendorCreditFormProvider'; + +export default function ReconcileVendorCreditFormContent() { + const { isEmptyStatus } = useReconcileVendorCreditContext(); + + return ( + + + + + +
+ + + +
+
+ ); +} diff --git a/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditFormFields.js b/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditFormFields.js new file mode 100644 index 000000000..531e18b82 --- /dev/null +++ b/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditFormFields.js @@ -0,0 +1,56 @@ +import React from 'react'; +import { FastField, useFormikContext } from 'formik'; +import { Classes } from '@blueprintjs/core'; +import { T, TotalLines, TotalLine } from 'components'; +import { getEntriesTotal } from 'containers/Entries/utils'; +import ReconcileVendorCreditEntriesTable from './ReconcileVendorCreditEntriesTable'; +import { useReconcileVendorCreditContext } from './ReconcileVendorCreditFormProvider'; +import { formattedAmount } from 'utils'; + +export default function ReconcileVendorCreditFormFields() { + const { vendorCredit } = useReconcileVendorCreditContext(); + + const { values } = useFormikContext(); + + // Calculate the total amount. + const totalAmount = React.useMemo( + () => getEntriesTotal(values.entries), + [values.entries], + ); + + return ( +
+ + {({ + form: { setFieldValue, values }, + field: { value }, + meta: { error, touched }, + }) => ( + { + setFieldValue('entries', newEntries); + }} + /> + )} + +
+ + + } + value={formattedAmount(totalAmount, vendorCredit.currency_code)} + /> + + } + value={vendorCredit.formatted_credits_remaining} + /> + +
+
+ ); +} diff --git a/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditFormProvider.js b/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditFormProvider.js new file mode 100644 index 000000000..0b85497f7 --- /dev/null +++ b/src/containers/Dialogs/ReconcileVendorCreditDialog/ReconcileVendorCreditFormProvider.js @@ -0,0 +1,64 @@ +import React from 'react'; +import { DialogContent } from 'components'; +import { + useVendorCredit, + useReconcileVendorCredit, + useCreateReconcileVendorCredit, +} from 'hooks/query'; +import { isEmpty } from 'lodash'; + +const ReconcileVendorCreditFormContext = React.createContext(); + +/** + * Reconcile vendor credit provider. + */ +function ReconcileVendorCreditFormProvider({ + vendorCreditId, + dialogName, + ...props +}) { + + // Handle fetch reconcile + const { + isLoading: isReconcileVendorCreditLoading, + data: reconcileVendorCredits, + } = useReconcileVendorCredit(vendorCreditId, { + enabled: !!vendorCreditId, + }); + + // Handle fetch vendor credit details. + const { data: vendorCredit, isLoading: isVendorCreditLoading } = + useVendorCredit(vendorCreditId, { + enabled: !!vendorCreditId, + }); + + // Create reconcile vendor credit mutations. + const { mutateAsync: createReconcileVendorCreditMutate } = + useCreateReconcileVendorCredit(); + + // Detarmines the datatable empty status. + const isEmptyStatus = isEmpty(reconcileVendorCredits); + + // provider. + const provider = { + dialogName, + reconcileVendorCredits, + createReconcileVendorCreditMutate, + isEmptyStatus, + vendorCredit, + }; + + return ( + + + + ); +} + +const useReconcileVendorCreditContext = () => + React.useContext(ReconcileVendorCreditFormContext); + +export { ReconcileVendorCreditFormProvider, useReconcileVendorCreditContext }; diff --git a/src/containers/Dialogs/ReconcileVendorCreditDialog/index.js b/src/containers/Dialogs/ReconcileVendorCreditDialog/index.js new file mode 100644 index 000000000..8dc713917 --- /dev/null +++ b/src/containers/Dialogs/ReconcileVendorCreditDialog/index.js @@ -0,0 +1,36 @@ +import React from 'react'; +import { FormattedMessage as T, Dialog, DialogSuspense } from 'components'; +import withDialogRedux from 'components/DialogReduxConnect'; +import { compose } from 'utils'; + +const ReconcileVendorCreditDialogContent = React.lazy(() => + import('./ReconcileVendorCreditDialogContent'), +); + +/** + * Reconcile vendor credit dialog. + */ +function ReconcileVendorCreditDialog({ + dialogName, + payload: { vendorCreditId }, + isOpen, +}) { + return ( + } + canEscapeKeyClose={true} + isOpen={isOpen} + className="dialog--reconcile-vendor-credit-form" + > + + + + + ); +} + +export default compose(withDialogRedux())(ReconcileVendorCreditDialog); diff --git a/src/containers/Dialogs/ReconcileVendorCreditDialog/utils.js b/src/containers/Dialogs/ReconcileVendorCreditDialog/utils.js new file mode 100644 index 000000000..8439dd634 --- /dev/null +++ b/src/containers/Dialogs/ReconcileVendorCreditDialog/utils.js @@ -0,0 +1,17 @@ +import React from 'react'; +import { Callout, Intent, Classes } from '@blueprintjs/core'; +import { AppToaster, T } from 'components'; + +export const transformErrors = (errors, { setErrors }) => {}; + +export function EmptyStatuCallout() { + return ( +
+ +

+ +

+
+
+ ); +} diff --git a/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteDialogContent.js b/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteDialogContent.js new file mode 100644 index 000000000..4cc99ae85 --- /dev/null +++ b/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteDialogContent.js @@ -0,0 +1,23 @@ +import React from 'react'; + +import 'style/pages/RefundCreditNote/RefundCreditNote.scss'; +import { RefundCreditNoteFormProvider } from './RefundCreditNoteFormProvider'; +import RefundCreditNoteForm from './RefundCreditNoteForm'; + +/** + * Refund credit note dialog content. + */ +export default function RefundCreditNoteDialogContent({ + // #ownProps + dialogName, + creditNoteId, +}) { + return ( + + + + ); +} diff --git a/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteFloatingActions.js b/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteFloatingActions.js new file mode 100644 index 000000000..32cb154aa --- /dev/null +++ b/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteFloatingActions.js @@ -0,0 +1,45 @@ +import React from 'react'; +import { Intent, Button, Classes } from '@blueprintjs/core'; +import { useFormikContext } from 'formik'; +import { FormattedMessage as T } from 'components'; + +import { useRefundCreditNoteContext } from './RefundCreditNoteFormProvider'; +import withDialogActions from 'containers/Dialog/withDialogActions'; +import { compose } from 'utils'; + +/** + * Refund credit note floating actions. + */ +function RefundCreditNoteFloatingActions({ + // #withDialogActions + closeDialog, +}) { + // Formik context. + const { isSubmitting, values, errors } = useFormikContext(); + + // refund credit note dialog context. + const { dialogName } = useRefundCreditNoteContext(); + + // Handle close button click. + const handleCancelBtnClick = () => { + closeDialog(dialogName); + }; + + return ( +
+
+ +
+
+ ); +} +export default compose(withDialogActions)(RefundCreditNoteFloatingActions); diff --git a/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteForm.js b/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteForm.js new file mode 100644 index 000000000..05600768f --- /dev/null +++ b/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteForm.js @@ -0,0 +1,77 @@ +import React from 'react'; +import { Formik } from 'formik'; +import { Intent } from '@blueprintjs/core'; +import intl from 'react-intl-universal'; +import moment from 'moment'; +import { omit, defaultTo } from 'lodash'; + +import { AppToaster } from 'components'; +import { useRefundCreditNoteContext } from './RefundCreditNoteFormProvider'; +import { CreateRefundCreditNoteFormSchema } from './RefundCreditNoteForm.schema'; +import RefundCreditNoteFormContent from './RefundCreditNoteFormContent'; + +import withSettings from 'containers/Settings/withSettings'; +import withDialogActions from 'containers/Dialog/withDialogActions'; +import { compose, transactionNumber } from 'utils'; + +const defaultInitialValues = { + from_account_id: '', + date: moment(new Date()).format('YYYY-MM-DD'), + reference_no: '', + description: '', + amount: '', +}; + +/** + * Refund credit note form. + */ +function RefundCreditNoteForm({ + // #withDialogActions + closeDialog, +}) { + const { dialogName, creditNote, createRefundCreditNoteMutate } = + useRefundCreditNoteContext(); + + // Initial form values + const initialValues = { + ...defaultInitialValues, + ...creditNote, + }; + + // Handles the form submit. + const handleFormSubmit = (values, { setSubmitting, setFieldError }) => { + const form = { + ...omit(values, ['currency_code', 'credits_remaining']), + }; + + // Handle request response success. + const onSaved = (response) => { + AppToaster.show({ + message: intl.get('refund_credit_note.dialog.success_message'), + intent: Intent.SUCCESS, + }); + closeDialog(dialogName); + }; + // Handle request response errors. + const onError = ({ + response: { + data: { errors }, + }, + }) => { + setSubmitting(false); + }; + createRefundCreditNoteMutate([creditNote.id, form]) + .then(onSaved) + .catch(onError); + }; + + return ( + + ); +} +export default compose(withDialogActions)(RefundCreditNoteForm); diff --git a/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteForm.schema.js b/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteForm.schema.js new file mode 100644 index 000000000..8ada6b557 --- /dev/null +++ b/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteForm.schema.js @@ -0,0 +1,12 @@ +import * as Yup from 'yup'; +import intl from 'react-intl-universal'; +import { DATATYPES_LENGTH } from 'common/dataTypes'; + +const Schema = Yup.object().shape({ + date: Yup.date().required().label(intl.get('date')), + amount: Yup.number().required(), + reference_no: Yup.string().min(1).max(DATATYPES_LENGTH.STRING).nullable(), + from_account_id: Yup.number().required().label(intl.get('deposit_account_')), + description: Yup.string().nullable().max(DATATYPES_LENGTH.TEXT), +}); +export const CreateRefundCreditNoteFormSchema = Schema; diff --git a/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteFormContent.js b/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteFormContent.js new file mode 100644 index 000000000..d763729bb --- /dev/null +++ b/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteFormContent.js @@ -0,0 +1,16 @@ +import React from 'react'; +import { Form } from 'formik'; +import RefundCreditNoteFormFields from './RefundCreditNoteFormFields'; +import RefundCreditNoteFloatingActions from './RefundCreditNoteFloatingActions'; + +/** + * Refund credit note form content. + */ +export default function RefundCreditNoteFormContent() { + return ( +
+ + + + ); +} diff --git a/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteFormFields.js b/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteFormFields.js new file mode 100644 index 000000000..a8e9eb100 --- /dev/null +++ b/src/containers/Dialogs/RefundCreditNoteDialog/RefundCreditNoteFormFields.js @@ -0,0 +1,166 @@ +import React from 'react'; +import intl from 'react-intl-universal'; +import { FastField, ErrorMessage } from 'formik'; +import { + Classes, + FormGroup, + InputGroup, + TextArea, + Position, + ControlGroup, +} from '@blueprintjs/core'; +import classNames from 'classnames'; +import { CLASSES } from 'common/classes'; +import { DateInput } from '@blueprintjs/datetime'; +import { + Icon, + FieldRequiredHint, + AccountsSuggestField, + InputPrependText, + MoneyInputGroup, + FormattedMessage as T, +} from 'components'; +import { + inputIntent, + momentFormatter, + tansformDateValue, + handleDateChange, + compose, +} from 'utils'; +import { useAutofocus } from 'hooks'; +import { ACCOUNT_TYPE } from 'common/accountTypes'; +import { useRefundCreditNoteContext } from './RefundCreditNoteFormProvider'; +import withSettings from 'containers/Settings/withSettings'; + +/** + * Refund credit note form fields. + */ +function RefundCreditNoteFormFields() { + const { accounts } = useRefundCreditNoteContext(); + const amountFieldRef = useAutofocus(); + return ( +
+ {/* ------------- Refund date ------------- */} + + {({ form, field: { value }, meta: { error, touched } }) => ( + } + labelInfo={} + className={classNames('form-group--select-list', CLASSES.FILL)} + intent={inputIntent({ error, touched })} + helperText={} + inline={true} + > + { + form.setFieldValue('date', formattedDate); + })} + popoverProps={{ position: Position.BOTTOM, minimal: true }} + inputProps={{ + leftIcon: , + }} + /> + + )} + + {/* ------------- Amount ------------- */} + + {({ + form: { values, setFieldValue }, + field: { value }, + meta: { error, touched }, + }) => ( + } + labelInfo={} + className={classNames('form-group--amount', CLASSES.FILL)} + intent={inputIntent({ error, touched })} + helperText={} + inline={true} + > + + + { + setFieldValue('amount', amount); + }} + intent={inputIntent({ error, touched })} + inputRef={(ref) => (amountFieldRef.current = ref)} + /> + + + )} + + {/* ------------ Reference No. ------------ */} + + {({ form, field, meta: { error, touched } }) => ( + } + className={classNames('form-group--reference', CLASSES.FILL)} + intent={inputIntent({ error, touched })} + helperText={} + inline={true} + > + + + )} + + + {/* ------------ Form account ------------ */} + + {({ form, field: { value }, meta: { error, touched } }) => ( + } + className={classNames( + 'form-group--from_account_id', + 'form-group--select-list', + CLASSES.FILL, + )} + labelInfo={} + intent={inputIntent({ error, touched })} + helperText={} + inline={true} + > + + form.setFieldValue('from_account_id', id) + } + inputProps={{ + placeholder: intl.get('select_account'), + }} + filterByTypes={[ + ACCOUNT_TYPE.BANK, + ACCOUNT_TYPE.CASH, + ACCOUNT_TYPE.FIXED_ASSET, + ]} + /> + + )} + + {/* --------- Statement --------- */} + + {({ form, field, meta: { error, touched } }) => ( + } + className={'form-group--description'} + inline={true} + > +