mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 12:50:38 +00:00
Merge branch 'feature/react-query' of https://github.com/abouolia/Bigcapital into feature/react-query
This commit is contained in:
@@ -49,6 +49,7 @@ import ContextMenu from './ContextMenu'
|
||||
import TableFastCell from './Datatable/TableFastCell';
|
||||
import DashboardContentTable from './Dashboard/DashboardContentTable';
|
||||
import DashboardPageContent from './Dashboard/DashboardPageContent';
|
||||
import DashboardInsider from './Dashboard/DashboardInsider';
|
||||
|
||||
const Hint = FieldHint;
|
||||
|
||||
@@ -103,5 +104,6 @@ export {
|
||||
Skeleton,
|
||||
ContextMenu,
|
||||
DashboardContentTable,
|
||||
DashboardPageContent
|
||||
DashboardPageContent,
|
||||
DashboardInsider
|
||||
};
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
import { Alert } from '@blueprintjs/core';
|
||||
|
||||
function ChangingFullAmountAlert() {
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'ok'} />}
|
||||
intent={Intent.DANGER}
|
||||
isOpen={amountChangeAlert}
|
||||
onCancel={handleCancelAmountChangeAlert}
|
||||
onConfirm={handleConfirmAmountChangeAlert}
|
||||
>
|
||||
<p>
|
||||
Changing full amount will change all credit and payment were applied, Is
|
||||
this okay?
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
|
||||
|
||||
|
||||
|
||||
export default function ClearTransactionAlert() {
|
||||
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'ok'} />}
|
||||
intent={Intent.WARNING}
|
||||
isOpen={clearFormAlert}
|
||||
onCancel={handleCancelClearFormAlert}
|
||||
onConfirm={handleConfirmCancelClearFormAlert}
|
||||
>
|
||||
<p>
|
||||
<T id={'are_you_sure_you_want_to_clear_this_transaction'} />
|
||||
</p>
|
||||
</Alert>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||
|
||||
|
||||
|
||||
function ClearningAllLinesAlert() {
|
||||
|
||||
return (
|
||||
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'ok'} />}
|
||||
intent={Intent.DANGER}
|
||||
isOpen={clearLinesAlert}
|
||||
onCancel={handleCancelClearLines}
|
||||
onConfirm={handleConfirmClearLines}
|
||||
>
|
||||
<p>
|
||||
Clearing the table lines will delete all credits and payments were
|
||||
applied. Is this okay?
|
||||
</p>
|
||||
</Alert>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
|
||||
|
||||
|
||||
function ChangingFullAmountAlert() {
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'ok'} />}
|
||||
intent={Intent.WARNING}
|
||||
isOpen={amountChangeAlert}
|
||||
onCancel={handleCancelAmountChangeAlert}
|
||||
onConfirm={handleConfirmAmountChangeAlert}
|
||||
>
|
||||
<p>
|
||||
<T
|
||||
id={'changing_full_amount_will_change_all_credits_and_payment'}
|
||||
/>
|
||||
</p>
|
||||
</Alert>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
|
||||
|
||||
|
||||
|
||||
function ClearTransactionAlert() {
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'ok'} />}
|
||||
intent={Intent.WARNING}
|
||||
isOpen={clearFormAlert}
|
||||
onCancel={handleCancelClearFormAlert}
|
||||
onConfirm={handleConfirmCancelClearFormAlert}
|
||||
>
|
||||
<p>
|
||||
<T id={'are_you_sure_you_want_to_clear_this_transaction'} />
|
||||
</p>
|
||||
</Alert>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
|
||||
|
||||
|
||||
function ClearingAllLinesAlert() {
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'ok'} />}
|
||||
intent={Intent.WARNING}
|
||||
isOpen={clearLinesAlert}
|
||||
onCancel={handleCancelClearLines}
|
||||
onConfirm={handleConfirmClearLines}
|
||||
>
|
||||
<p>
|
||||
<T id={'clearing_the_table_lines_will_delete_all_credits'} />
|
||||
</p>
|
||||
</Alert>
|
||||
)
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import React from 'react';
|
||||
import { FastField } from 'formik';
|
||||
import ItemsEntriesTable from './ItemsEntriesTable';
|
||||
|
||||
import 'style/components/DataTable/DataTableEditable.scss';
|
||||
|
||||
/**
|
||||
* Editable items entries table.
|
||||
*/
|
||||
export default function EditableItemsEntriesTable({
|
||||
items,
|
||||
filterSellableItems = false,
|
||||
filterPurchasableItems = false,
|
||||
}) {
|
||||
|
||||
return (
|
||||
|
||||
);
|
||||
}
|
||||
@@ -1,12 +1,10 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { useItemsCategoriesTableColumns, ActionMenuList } from './components';
|
||||
import DataTable from 'components/DataTable';
|
||||
import TableSkeletonRows from 'components/Datatable/TableSkeletonRows';
|
||||
|
||||
import { useItemsCategoriesContext } from './ItemsCategoriesProvider';
|
||||
import { CLASSES } from 'common/classes';
|
||||
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
@@ -36,8 +34,6 @@ function ItemsCategoryTable({
|
||||
// Table columns.
|
||||
const columns = useItemsCategoriesTableColumns();
|
||||
|
||||
const handleSelectedRowsChange = (selectedRows) => {};
|
||||
|
||||
// Handle delete Item.
|
||||
const handleDeleteCategory = ({ id }) => {
|
||||
openAlert('item-category-delete', { itemCategoryId: id });
|
||||
|
||||
@@ -11,50 +11,42 @@ import {
|
||||
} from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
import { useFormikContext } from 'formik';
|
||||
import { usePaymentMadeFormContext } from './PaymentMadeFormProvider';
|
||||
|
||||
import { CLASSES } from 'common/classes';
|
||||
import { saveInvoke } from 'utils';
|
||||
|
||||
import { Icon } from 'components';
|
||||
|
||||
/**
|
||||
* Payment made floating actions bar.
|
||||
*/
|
||||
export default function PaymentMadeFloatingActions({
|
||||
isSubmitting,
|
||||
onSubmitClick,
|
||||
onCancelClick,
|
||||
onClearBtnClick,
|
||||
onSubmitForm,
|
||||
paymentMadeId,
|
||||
}) {
|
||||
export default function PaymentMadeFloatingActions() {
|
||||
// Formik context.
|
||||
const { isSubmitting } = useFormikContext();
|
||||
|
||||
// Payment made form context.
|
||||
const { setSubmitPayload, paymentMadeId } = usePaymentMadeFormContext();
|
||||
|
||||
// Handle submit button click.
|
||||
const handleSubmitBtnClick = (event) => {
|
||||
saveInvoke(onSubmitClick, event, {
|
||||
redirect: true,
|
||||
});
|
||||
setSubmitPayload({ redirect: true });
|
||||
};
|
||||
|
||||
const handleClearBtnClick = (event) => {
|
||||
onClearBtnClick && onClearBtnClick(event);
|
||||
};
|
||||
// Handle clear button click.
|
||||
const handleClearBtnClick = (event) => {};
|
||||
|
||||
const handleCancelBtnClick = (event) => {
|
||||
onCancelClick && onCancelClick(event);
|
||||
saveInvoke(onCancelClick, event);
|
||||
};
|
||||
// Handle cancel button click.
|
||||
const handleCancelBtnClick = (event) => {};
|
||||
|
||||
// Handle submit & new button click.
|
||||
const handleSubmitAndNewClick = (event) => {
|
||||
onSubmitForm();
|
||||
saveInvoke(onSubmitClick, event, {
|
||||
redirect: false,
|
||||
resetForm: true,
|
||||
});
|
||||
setSubmitPayload({ redirect: false, resetForm: true });
|
||||
};
|
||||
|
||||
// handle submit & continue editing button click.
|
||||
const handleSubmitContinueEditingBtnClick = (event) => {
|
||||
onSubmitForm();
|
||||
saveInvoke(onSubmitClick, event, {
|
||||
redirect: false,
|
||||
publish: true,
|
||||
});
|
||||
setSubmitPayload({ redirect: false, publish: true });
|
||||
};
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_FLOATING_ACTIONS)}>
|
||||
|
||||
@@ -2,31 +2,31 @@ import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { FormGroup, TextArea } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import { FastField } from 'formik';
|
||||
import { Row, Col } from 'components';
|
||||
import { CLASSES } from 'common/classes';
|
||||
|
||||
/**
|
||||
* Payment made form footer.
|
||||
*/
|
||||
export default function PaymentMadeFooter({
|
||||
getFieldProps
|
||||
}) {
|
||||
export default function PaymentMadeFooter({ getFieldProps }) {
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
||||
<Row>
|
||||
<Col md={8}>
|
||||
{/* --------- Statement --------- */}
|
||||
<FormGroup
|
||||
label={<T id={'statement'} />}
|
||||
className={'form-group--statement'}
|
||||
>
|
||||
<TextArea
|
||||
growVertically={true}
|
||||
{...getFieldProps('description')}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FastField name={'customer_name'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'statement'} />}
|
||||
className={'form-group--statement'}
|
||||
>
|
||||
<TextArea growVertically={true} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import React, { useMemo, useState, useCallback, useEffect } from 'react';
|
||||
import * as Yup from 'yup';
|
||||
import { useFormik } from 'formik';
|
||||
import moment from 'moment';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
import { pick, sumBy, omit } from 'lodash';
|
||||
import React, { useMemo } from 'react';
|
||||
import { Formik, Form } from 'formik';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { sumBy, omit } from 'lodash';
|
||||
import classNames from 'classnames';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
@@ -12,113 +10,49 @@ import { CLASSES } from 'common/classes';
|
||||
import { AppToaster } from 'components';
|
||||
import PaymentMadeHeader from './PaymentMadeFormHeader';
|
||||
import PaymentMadeFloatingActions from './PaymentMadeFloatingActions';
|
||||
import PaymentMadeItemsTable from './PaymentMadeItemsTable';
|
||||
import PaymentMadeFooter from './PaymentMadeFooter';
|
||||
|
||||
import withMediaActions from 'containers/Media/withMediaActions';
|
||||
import withPaymentMadeActions from './withPaymentMadeActions';
|
||||
import withPaymentMadeDetail from './withPaymentMadeDetail';
|
||||
import withPaymentMade from './withPaymentMade';
|
||||
import withSettings from 'containers/Settings/withSettings';
|
||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
import {
|
||||
EditPaymentMadeFormSchema,
|
||||
CreatePaymentMadeFormSchema,
|
||||
} from './PaymentMadeForm.schema';
|
||||
import { compose, orderingLinesIndexes } from 'utils';
|
||||
|
||||
const ERRORS = {
|
||||
PAYMENT_NUMBER_NOT_UNIQUE: 'PAYMENT.NUMBER.NOT.UNIQUE',
|
||||
};
|
||||
|
||||
// Default payment made entry values.x
|
||||
const defaultPaymentMadeEntry = {
|
||||
bill_id: '',
|
||||
payment_amount: '',
|
||||
id: null,
|
||||
due_amount: null,
|
||||
};
|
||||
// Default initial values.
|
||||
const defaultInitialValues = {
|
||||
full_amount: '',
|
||||
vendor_id: '',
|
||||
payment_account_id: '',
|
||||
payment_date: moment(new Date()).format('YYYY-MM-DD'),
|
||||
reference: '',
|
||||
payment_number: '',
|
||||
description: '',
|
||||
entries: [],
|
||||
};
|
||||
import { usePaymentMadeFormContext } from './PaymentMadeFormProvider';
|
||||
import { defaultPaymentMade, transformToEditForm, ERRORS } from './utils';
|
||||
|
||||
/**
|
||||
* Payment made form component.
|
||||
*/
|
||||
function PaymentMadeForm({
|
||||
// #withMedia
|
||||
requestSubmitMedia,
|
||||
requestDeleteMedia,
|
||||
|
||||
// #withPaymentMadesActions
|
||||
requestSubmitPaymentMade,
|
||||
requestEditPaymentMade,
|
||||
|
||||
// #withPaymentMadeDetail
|
||||
paymentMade,
|
||||
|
||||
// #withBills
|
||||
paymentMadeEntries,
|
||||
|
||||
// #withDashboardActions
|
||||
changePageSubtitle,
|
||||
|
||||
// #ownProps
|
||||
paymentMadeId,
|
||||
}) {
|
||||
function PaymentMadeForm() {
|
||||
const history = useHistory();
|
||||
const { formatMessage } = useIntl();
|
||||
const isNewMode = !paymentMadeId;
|
||||
const [amountChangeAlert, setAmountChangeAlert] = useState(false);
|
||||
const [clearLinesAlert, setClearLinesAlert] = useState(false);
|
||||
const [clearFormAlert, setClearFormAlert] = useState(false);
|
||||
const [fullAmount, setFullAmount] = useState(null);
|
||||
const [submitPayload, setSubmitPayload] = useState({});
|
||||
|
||||
const [localPaymentEntries, setLocalPaymentEntries] = useState(
|
||||
paymentMadeEntries,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (localPaymentEntries !== paymentMadeEntries) {
|
||||
setLocalPaymentEntries(paymentMadeEntries);
|
||||
}
|
||||
}, [localPaymentEntries, paymentMadeEntries]);
|
||||
|
||||
// Yup validation schema.
|
||||
const validationSchema = isNewMode
|
||||
? CreatePaymentMadeFormSchema
|
||||
: EditPaymentMadeFormSchema;
|
||||
// Payment made form context.
|
||||
const {
|
||||
isNewMode,
|
||||
paymentMade,
|
||||
submitPayload,
|
||||
createPaymentMadeMutate,
|
||||
editPaymentMadeMutate,
|
||||
} = usePaymentMadeFormContext();
|
||||
|
||||
// Form initial values.
|
||||
const initialValues = useMemo(
|
||||
() => ({
|
||||
...(paymentMade
|
||||
...(!isNewMode
|
||||
? {
|
||||
...pick(paymentMade, Object.keys(defaultInitialValues)),
|
||||
full_amount: sumBy(paymentMade.entries, 'payment_amount'),
|
||||
entries: [
|
||||
...paymentMadeEntries.map((paymentMadeEntry) => ({
|
||||
...pick(paymentMadeEntry, Object.keys(defaultPaymentMadeEntry)),
|
||||
})),
|
||||
],
|
||||
...transformToEditForm(paymentMade, []),
|
||||
}
|
||||
: {
|
||||
...defaultInitialValues,
|
||||
entries: orderingLinesIndexes(defaultInitialValues.entries),
|
||||
...defaultPaymentMade,
|
||||
entries: orderingLinesIndexes(defaultPaymentMade.entries),
|
||||
}),
|
||||
}),
|
||||
[paymentMade, paymentMadeEntries],
|
||||
[isNewMode, paymentMade],
|
||||
);
|
||||
|
||||
// Handle the form submit.
|
||||
const handleSubmitForm = (
|
||||
values,
|
||||
{ setSubmitting, resetForm, setFieldError },
|
||||
@@ -149,24 +83,19 @@ function PaymentMadeForm({
|
||||
const onSaved = (response) => {
|
||||
AppToaster.show({
|
||||
message: formatMessage({
|
||||
id: paymentMadeId
|
||||
id: isNewMode
|
||||
? 'the_payment_made_has_been_edited_successfully'
|
||||
: 'the_payment_made_has_been_created_successfully',
|
||||
}),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
setSubmitting(false);
|
||||
// changePageSubtitle('');
|
||||
|
||||
if (submitPayload.redirect) {
|
||||
history.push('/payment-mades');
|
||||
}
|
||||
if (submitPayload.resetForm) {
|
||||
resetForm();
|
||||
}
|
||||
submitPayload.redirect && history.push('/payment-mades');
|
||||
submitPayload.resetForm && resetForm();
|
||||
};
|
||||
|
||||
const onError = (errors) => {
|
||||
const onError = ({ response: { error: { data: errors } } }) => {
|
||||
const getError = (errorType) => errors.find((e) => e.type === errorType);
|
||||
|
||||
if (getError(ERRORS.PAYMENT_NUMBER_NOT_UNIQUE)) {
|
||||
@@ -178,145 +107,15 @@ function PaymentMadeForm({
|
||||
setSubmitting(false);
|
||||
};
|
||||
|
||||
if (paymentMade && paymentMade.id) {
|
||||
requestEditPaymentMade(paymentMade.id, form).then(onSaved).catch(onError);
|
||||
if (!isNewMode) {
|
||||
editPaymentMadeMutate([paymentMade.id, form])
|
||||
.then(onSaved)
|
||||
.catch(onError);
|
||||
} else {
|
||||
requestSubmitPaymentMade(form).then(onSaved).catch(onError);
|
||||
createPaymentMadeMutate(form).then(onSaved).catch(onError);
|
||||
}
|
||||
};
|
||||
|
||||
const {
|
||||
errors,
|
||||
touched,
|
||||
setFieldValue,
|
||||
getFieldProps,
|
||||
setValues,
|
||||
values,
|
||||
handleSubmit,
|
||||
isSubmitting,
|
||||
resetForm,
|
||||
submitForm,
|
||||
} = useFormik({
|
||||
validationSchema,
|
||||
initialValues,
|
||||
onSubmit: handleSubmitForm,
|
||||
});
|
||||
|
||||
const handleFullAmountChange = useCallback(
|
||||
(value) => {
|
||||
if (value !== fullAmount) {
|
||||
setAmountChangeAlert(value);
|
||||
}
|
||||
},
|
||||
[fullAmount, setAmountChangeAlert],
|
||||
);
|
||||
// Handle cancel button of amount change alert.
|
||||
const handleCancelAmountChangeAlert = () => {
|
||||
setAmountChangeAlert(false);
|
||||
};
|
||||
// Handle confirm button of amount change alert.
|
||||
const handleConfirmAmountChangeAlert = () => {
|
||||
setFullAmount(amountChangeAlert);
|
||||
setAmountChangeAlert(false);
|
||||
};
|
||||
|
||||
const transformPaymentEntries = (entries) => {
|
||||
return entries.map((entry) => ({
|
||||
...pick(entry, Object.keys(defaultPaymentMadeEntry)),
|
||||
}));
|
||||
};
|
||||
// Handle update data.
|
||||
const handleUpdataData = useCallback(
|
||||
(entries) => {
|
||||
setFieldValue('entries', transformPaymentEntries(entries));
|
||||
},
|
||||
[setFieldValue],
|
||||
);
|
||||
|
||||
const resetEntriesPaymentAmount = (entries) => {
|
||||
return entries.map((entry) => ({ ...entry, payment_amount: 0 }));
|
||||
};
|
||||
// Handle fetch success of vendor bills entries.
|
||||
const handleFetchEntriesSuccess = useCallback(
|
||||
(entries) => {
|
||||
setFieldValue('entries', transformPaymentEntries(entries));
|
||||
},
|
||||
[setFieldValue],
|
||||
);
|
||||
|
||||
// Handle cancel button click.
|
||||
const handleCancelClick = useCallback(() => {
|
||||
history.goBack();
|
||||
}, [history]);
|
||||
|
||||
// Handle clear all lines button click.
|
||||
const handleClearAllLines = useCallback(() => {
|
||||
setClearLinesAlert(true);
|
||||
},[setClearLinesAlert]);
|
||||
|
||||
const handleCancelClearLines = useCallback(() => {
|
||||
setClearLinesAlert(false);
|
||||
}, [setClearLinesAlert]);
|
||||
|
||||
const handleConfirmClearLines = useCallback(() => {
|
||||
setLocalPaymentEntries(resetEntriesPaymentAmount(localPaymentEntries));
|
||||
setFieldValue('entries', resetEntriesPaymentAmount(values.entries));
|
||||
|
||||
setClearLinesAlert(false);
|
||||
}, [setFieldValue, localPaymentEntries, values.entries]);
|
||||
|
||||
// Handle clear button click.
|
||||
const handleClearBtnClick = useCallback(() => {
|
||||
setClearFormAlert(true);
|
||||
}, []);
|
||||
|
||||
// Handle cancel button clear
|
||||
const handleCancelClearFormAlert = () => {
|
||||
setClearFormAlert(false);
|
||||
};
|
||||
// Handle confirm button click of clear form alert.
|
||||
const handleConfirmCancelClearFormAlert = () => {
|
||||
setValues({
|
||||
...defaultInitialValues,
|
||||
...(paymentMadeId
|
||||
? {
|
||||
vendor_id: values.vendor_id,
|
||||
payment_number: values.payment_number,
|
||||
}
|
||||
: {}),
|
||||
});
|
||||
setClearFormAlert(false);
|
||||
resetForm();
|
||||
};
|
||||
// Payable full amount.
|
||||
const payableFullAmount = useMemo(() => sumBy(values.entries, 'due_amount'), [
|
||||
values.entries,
|
||||
]);
|
||||
|
||||
const handlePaymentNoChanged = (paymentNumber) => {
|
||||
changePageSubtitle(paymentNumber);
|
||||
};
|
||||
|
||||
// Clear page subtitle before once page leave.
|
||||
useEffect(
|
||||
() => () => {
|
||||
changePageSubtitle('');
|
||||
},
|
||||
[changePageSubtitle],
|
||||
);
|
||||
|
||||
const fullAmountPaid = useMemo(
|
||||
() => sumBy(values.entries, 'payment_amount'),
|
||||
[values.entries],
|
||||
);
|
||||
|
||||
const handleSubmitClick = useCallback(
|
||||
(event, payload) => {
|
||||
setSubmitPayload({ ...payload });
|
||||
},
|
||||
[setSubmitPayload],
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
@@ -325,110 +124,30 @@ function PaymentMadeForm({
|
||||
CLASSES.PAGE_FORM_PAYMENT_MADE,
|
||||
)}
|
||||
>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<PaymentMadeHeader
|
||||
paymentMadeId={paymentMadeId}
|
||||
vendorId={values.vendor_id}
|
||||
errors={errors}
|
||||
touched={touched}
|
||||
setFieldValue={setFieldValue}
|
||||
getFieldProps={getFieldProps}
|
||||
values={values}
|
||||
payableFullAmount={payableFullAmount}
|
||||
onFullAmountChanged={handleFullAmountChange}
|
||||
onPaymentNumberChanged={handlePaymentNoChanged}
|
||||
amountPaid={fullAmountPaid}
|
||||
/>
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={
|
||||
isNewMode ? CreatePaymentMadeFormSchema : EditPaymentMadeFormSchema
|
||||
}
|
||||
onSubmit={handleSubmitForm}
|
||||
>
|
||||
<Form>
|
||||
<PaymentMadeHeader />
|
||||
|
||||
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
|
||||
<PaymentMadeItemsTable
|
||||
fullAmount={fullAmount}
|
||||
paymentEntries={localPaymentEntries}
|
||||
vendorId={values.vendor_id}
|
||||
paymentMadeId={paymentMadeId}
|
||||
onUpdateData={handleUpdataData}
|
||||
onClickClearAllLines={handleClearAllLines}
|
||||
errors={errors?.entries}
|
||||
onFetchEntriesSuccess={handleFetchEntriesSuccess}
|
||||
vendorPayableBillsEntrie={[]}
|
||||
/>
|
||||
</div>
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'ok'} />}
|
||||
intent={Intent.DANGER}
|
||||
isOpen={amountChangeAlert}
|
||||
onCancel={handleCancelAmountChangeAlert}
|
||||
onConfirm={handleConfirmAmountChangeAlert}
|
||||
>
|
||||
<p>
|
||||
Changing full amount will change all credit and payment were
|
||||
applied, Is this okay?
|
||||
</p>
|
||||
</Alert>
|
||||
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'ok'} />}
|
||||
intent={Intent.DANGER}
|
||||
isOpen={clearLinesAlert}
|
||||
onCancel={handleCancelClearLines}
|
||||
onConfirm={handleConfirmClearLines}
|
||||
>
|
||||
<p>
|
||||
Clearing the table lines will delete all credits and payments were
|
||||
applied. Is this okay?
|
||||
</p>
|
||||
</Alert>
|
||||
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'ok'} />}
|
||||
intent={Intent.WARNING}
|
||||
isOpen={clearFormAlert}
|
||||
onCancel={handleCancelClearFormAlert}
|
||||
onConfirm={handleConfirmCancelClearFormAlert}
|
||||
>
|
||||
<p>
|
||||
<T id={'are_you_sure_you_want_to_clear_this_transaction'} />
|
||||
</p>
|
||||
</Alert>
|
||||
|
||||
<PaymentMadeFooter getFieldProps={getFieldProps} />
|
||||
|
||||
<PaymentMadeFloatingActions
|
||||
isSubmitting={isSubmitting}
|
||||
paymentMadeId={paymentMadeId}
|
||||
onSubmitClick={handleSubmitClick}
|
||||
onCancelClick={handleCancelClick}
|
||||
onClearBtnClick={handleClearBtnClick}
|
||||
onSubmitForm={submitForm}
|
||||
/>
|
||||
|
||||
{/* <Dragzone
|
||||
initialFiles={initialAttachmentFiles}
|
||||
onDrop={handleDropFiles}
|
||||
onDeleteFile={handleDeleteFile}
|
||||
hint={'Attachments: Maxiumum size: 20MB'}
|
||||
/> */}
|
||||
</form>
|
||||
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
|
||||
|
||||
</div>
|
||||
<PaymentMadeFooter />
|
||||
<PaymentMadeFloatingActions />
|
||||
</Form>
|
||||
</Formik>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withPaymentMadeActions,
|
||||
withMediaActions,
|
||||
withPaymentMadeDetail(),
|
||||
withPaymentMade(
|
||||
({ nextPaymentNumberChanged, paymentMadeEntries }, state, props) => ({
|
||||
nextPaymentNumberChanged,
|
||||
paymentMadeEntries,
|
||||
}),
|
||||
),
|
||||
withSettings(({ billPaymentSettings }) => ({
|
||||
paymentNextNumber: billPaymentSettings?.next_number,
|
||||
paymentNumberPrefix: billPaymentSettings?.number_prefix,
|
||||
})),
|
||||
withDashboardActions,
|
||||
)(PaymentMadeForm);
|
||||
|
||||
@@ -1,250 +1,32 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import {
|
||||
FormGroup,
|
||||
InputGroup,
|
||||
Intent,
|
||||
Position,
|
||||
Classes,
|
||||
ControlGroup,
|
||||
} from '@blueprintjs/core';
|
||||
import { DateInput } from '@blueprintjs/datetime';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import moment from 'moment';
|
||||
import React, { useMemo } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { useFormikContext } from 'formik';
|
||||
import { sumBy } from 'lodash';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import { momentFormatter, compose, tansformDateValue } from 'utils';
|
||||
import { compose } from 'utils';
|
||||
import {
|
||||
AccountsSelectList,
|
||||
ContactSelecetList,
|
||||
ErrorMessage,
|
||||
FieldRequiredHint,
|
||||
InputPrependText,
|
||||
Money,
|
||||
Hint,
|
||||
Icon,
|
||||
} from 'components';
|
||||
|
||||
import withVender from 'containers/Vendors/withVendors';
|
||||
import withAccounts from 'containers/Accounts/withAccounts';
|
||||
import PaymentMadeFormHeaderFields from './PaymentMadeFormHeaderFields';
|
||||
import withSettings from 'containers/Settings/withSettings';
|
||||
|
||||
/**
|
||||
* Payment made header form.
|
||||
*/
|
||||
function PaymentMadeFormHeader({
|
||||
paymentMadeId,
|
||||
payableFullAmount,
|
||||
onPaymentNumberChanged,
|
||||
amountPaid = 0,
|
||||
|
||||
// #useFormik
|
||||
errors,
|
||||
touched,
|
||||
setFieldValue,
|
||||
getFieldProps,
|
||||
values,
|
||||
|
||||
//#withSettings
|
||||
// #withSettings
|
||||
baseCurrency,
|
||||
|
||||
onFullAmountChanged,
|
||||
|
||||
//#withVender
|
||||
vendorsCurrentPage,
|
||||
vendorItems,
|
||||
|
||||
//#withAccouts
|
||||
accountsList,
|
||||
}) {
|
||||
const isNewMode = !paymentMadeId;
|
||||
// Formik form context.
|
||||
const { values } = useFormikContext();
|
||||
|
||||
const handleDateChange = useCallback(
|
||||
(date_filed) => (date) => {
|
||||
const formatted = moment(date).format('YYYY-MM-DD');
|
||||
setFieldValue(date_filed, formatted);
|
||||
},
|
||||
[setFieldValue],
|
||||
);
|
||||
const triggerFullAmountChanged = (value) => {
|
||||
onFullAmountChanged && onFullAmountChanged(value);
|
||||
};
|
||||
|
||||
const handleFullAmountBlur = (event) => {
|
||||
triggerFullAmountChanged(event.currentTarget.value);
|
||||
};
|
||||
|
||||
const onChangeSelect = useCallback(
|
||||
(filedName) => {
|
||||
return (item) => {
|
||||
setFieldValue(filedName, item.id);
|
||||
};
|
||||
},
|
||||
[setFieldValue],
|
||||
);
|
||||
|
||||
const handleReceiveFullAmountClick = () => {
|
||||
setFieldValue('full_amount', payableFullAmount);
|
||||
triggerFullAmountChanged(payableFullAmount);
|
||||
};
|
||||
|
||||
const handlePaymentNumberBlur = (event) => {
|
||||
onPaymentNumberChanged && onPaymentNumberChanged(event.currentTarget.value);
|
||||
};
|
||||
// Calculate the payment amount of the entries.
|
||||
const amountPaid = useMemo(() => sumBy(values, 'payment_amount'), [values]);
|
||||
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
|
||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_PRIMARY)}>
|
||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
|
||||
{/* ------------ Vendor name ------------ */}
|
||||
<FormGroup
|
||||
label={<T id={'vendor_name'} />}
|
||||
inline={true}
|
||||
className={classNames('form-group--select-list', Classes.FILL)}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
intent={errors.vendor_id && touched.vendor_id && Intent.DANGER}
|
||||
helperText={
|
||||
<ErrorMessage name={'vendor_id'} {...{ errors, touched }} />
|
||||
}
|
||||
>
|
||||
<ContactSelecetList
|
||||
contactsList={vendorItems}
|
||||
selectedContactId={values.vendor_id}
|
||||
defaultSelectText={<T id={'select_vender_account'} />}
|
||||
onContactSelected={onChangeSelect('vendor_id')}
|
||||
disabled={!isNewMode}
|
||||
popoverFill={true}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
{/* ------------ Payment date ------------ */}
|
||||
<FormGroup
|
||||
label={<T id={'payment_date'} />}
|
||||
inline={true}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
className={classNames('form-group--select-list', Classes.FILL)}
|
||||
intent={
|
||||
errors.payment_date && touched.payment_date && Intent.DANGER
|
||||
}
|
||||
helperText={
|
||||
<ErrorMessage name="payment_date" {...{ errors, touched }} />
|
||||
}
|
||||
>
|
||||
<DateInput
|
||||
{...momentFormatter('YYYY/MM/DD')}
|
||||
value={tansformDateValue(values.payment_date)}
|
||||
onChange={handleDateChange('payment_date')}
|
||||
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
||||
inputProps={{
|
||||
leftIcon: <Icon icon={'date-range'} />,
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
{/* ------------ Full amount ------------ */}
|
||||
<FormGroup
|
||||
label={<T id={'full_amount'} />}
|
||||
inline={true}
|
||||
className={('form-group--full-amount', Classes.FILL)}
|
||||
intent={errors.full_amount && touched.full_amount && Intent.DANGER}
|
||||
labelInfo={<Hint />}
|
||||
helperText={
|
||||
<ErrorMessage name="full_amount" {...{ errors, touched }} />
|
||||
}
|
||||
>
|
||||
<ControlGroup>
|
||||
<InputPrependText text={baseCurrency} />
|
||||
<InputGroup
|
||||
intent={
|
||||
errors.full_amount && touched.full_amount && Intent.DANGER
|
||||
}
|
||||
minimal={true}
|
||||
value={values.full_amount}
|
||||
{...getFieldProps('full_amount')}
|
||||
onBlur={handleFullAmountBlur}
|
||||
/>
|
||||
</ControlGroup>
|
||||
|
||||
<a
|
||||
onClick={handleReceiveFullAmountClick}
|
||||
href="#"
|
||||
className={'receive-full-amount'}
|
||||
>
|
||||
Receive full amount (
|
||||
<Money amount={payableFullAmount} currency={baseCurrency} />)
|
||||
</a>
|
||||
</FormGroup>
|
||||
|
||||
{/* ------------ Payment number ------------ */}
|
||||
<FormGroup
|
||||
label={<T id={'payment_no'} />}
|
||||
inline={true}
|
||||
className={('form-group--payment_number', Classes.FILL)}
|
||||
intent={
|
||||
errors.payment_number && touched.payment_number && Intent.DANGER
|
||||
}
|
||||
helperText={
|
||||
<ErrorMessage name="payment_number" {...{ errors, touched }} />
|
||||
}
|
||||
>
|
||||
<InputGroup
|
||||
intent={
|
||||
errors.payment_number && touched.payment_number && Intent.DANGER
|
||||
}
|
||||
minimal={true}
|
||||
{...getFieldProps('payment_number')}
|
||||
onBlur={handlePaymentNumberBlur}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
{/* ------------ Payment account ------------ */}
|
||||
<FormGroup
|
||||
label={<T id={'payment_account'} />}
|
||||
className={classNames(
|
||||
'form-group--payment_account_id',
|
||||
'form-group--select-list',
|
||||
Classes.FILL,
|
||||
)}
|
||||
inline={true}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
intent={
|
||||
errors.payment_account_id &&
|
||||
touched.payment_account_id &&
|
||||
Intent.DANGER
|
||||
}
|
||||
helperText={
|
||||
<ErrorMessage
|
||||
name={'payment_account_id'}
|
||||
{...{ errors, touched }}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<AccountsSelectList
|
||||
accounts={accountsList}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
onAccountSelected={onChangeSelect('payment_account_id')}
|
||||
defaultSelectText={<T id={'select_payment_account'} />}
|
||||
selectedAccountId={values.payment_account_id}
|
||||
filterByTypes={['current_asset']}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
{/* ------------ Reference ------------ */}
|
||||
<FormGroup
|
||||
label={<T id={'reference'} />}
|
||||
inline={true}
|
||||
className={classNames('form-group--reference', Classes.FILL)}
|
||||
intent={errors.reference && touched.reference && Intent.DANGER}
|
||||
helperText={
|
||||
<ErrorMessage name="reference" {...{ errors, touched }} />
|
||||
}
|
||||
>
|
||||
<InputGroup
|
||||
intent={errors.reference && touched.reference && Intent.DANGER}
|
||||
minimal={true}
|
||||
{...getFieldProps('reference')}
|
||||
/>
|
||||
</FormGroup>
|
||||
</div>
|
||||
<PaymentMadeFormHeaderFields />
|
||||
|
||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_BIG_NUMBERS)}>
|
||||
<div class="big-amount">
|
||||
@@ -260,13 +42,6 @@ function PaymentMadeFormHeader({
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withVender(({ vendorsCurrentPage, vendorItems }) => ({
|
||||
vendorsCurrentPage,
|
||||
vendorItems,
|
||||
})),
|
||||
withAccounts(({ accountsList }) => ({
|
||||
accountsList,
|
||||
})),
|
||||
withSettings(({ organizationSettings }) => ({
|
||||
baseCurrency: organizationSettings?.baseCurrency,
|
||||
})),
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
FormGroup,
|
||||
InputGroup,
|
||||
Position,
|
||||
Classes,
|
||||
ControlGroup,
|
||||
} from '@blueprintjs/core';
|
||||
import { DateInput } from '@blueprintjs/datetime';
|
||||
import { FastField } from 'formik';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import {
|
||||
AccountsSelectList,
|
||||
ContactSelecetList,
|
||||
ErrorMessage,
|
||||
FieldRequiredHint,
|
||||
InputPrependText,
|
||||
Money,
|
||||
Hint,
|
||||
Icon,
|
||||
} from 'components';
|
||||
import withSettings from 'containers/Settings/withSettings';
|
||||
import { usePaymentMadeFormContext } from './PaymentMadeFormProvider';
|
||||
import {
|
||||
momentFormatter,
|
||||
tansformDateValue,
|
||||
inputIntent,
|
||||
compose,
|
||||
} from 'utils';
|
||||
|
||||
/**
|
||||
* Payment made form header fields.
|
||||
*/
|
||||
function PaymentMadeFormHeaderFields({ baseCurrency }) {
|
||||
const { vendors, accounts, isNewMode } = usePaymentMadeFormContext();
|
||||
|
||||
const payableFullAmount = 0;
|
||||
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
|
||||
{/* ------------ Vendor name ------------ */}
|
||||
<FastField name={'vendor_id'}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'vendor_name'} />}
|
||||
inline={true}
|
||||
className={classNames('form-group--select-list', Classes.FILL)}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'vendor_id'} />}
|
||||
>
|
||||
<ContactSelecetList
|
||||
contactsList={vendors}
|
||||
selectedContactId={value}
|
||||
defaultSelectText={<T id={'select_vender_account'} />}
|
||||
onContactSelected={(contact) => {
|
||||
form.setFieldValue('vendor_id', contact.id);
|
||||
}}
|
||||
disabled={!isNewMode}
|
||||
popoverFill={true}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
{/* ------------ Payment date ------------ */}
|
||||
<FastField name={'customer_id'}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'payment_date'} />}
|
||||
inline={true}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
className={classNames('form-group--select-list', Classes.FILL)}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="payment_date" />}
|
||||
>
|
||||
<DateInput
|
||||
{...momentFormatter('YYYY/MM/DD')}
|
||||
value={tansformDateValue(value)}
|
||||
// onChange={handleDateChange('payment_date')}
|
||||
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
||||
inputProps={{
|
||||
leftIcon: <Icon icon={'date-range'} />,
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
{/* ------------ Full amount ------------ */}
|
||||
<FastField name={'full_amount'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'full_amount'} />}
|
||||
inline={true}
|
||||
className={('form-group--full-amount', Classes.FILL)}
|
||||
intent={inputIntent({ error, touched })}
|
||||
labelInfo={<Hint />}
|
||||
helperText={<ErrorMessage name="full_amount" />}
|
||||
>
|
||||
<ControlGroup>
|
||||
<InputPrependText text={baseCurrency} />
|
||||
<InputGroup
|
||||
intent={inputIntent({ error, touched })}
|
||||
minimal={true}
|
||||
{...field}
|
||||
/>
|
||||
</ControlGroup>
|
||||
|
||||
<a
|
||||
// onClick={handleReceiveFullAmountClick}
|
||||
href="#"
|
||||
className={'receive-full-amount'}
|
||||
>
|
||||
Receive full amount (
|
||||
<Money amount={payableFullAmount} currency={baseCurrency} />)
|
||||
</a>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
{/* ------------ Payment number ------------ */}
|
||||
<FastField name={'payment_number'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'payment_no'} />}
|
||||
inline={true}
|
||||
className={('form-group--payment_number', Classes.FILL)}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="payment_number" />}
|
||||
>
|
||||
<InputGroup
|
||||
intent={inputIntent({ error, touched })}
|
||||
minimal={true}
|
||||
{...field}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
{/* ------------ Payment account ------------ */}
|
||||
<FastField name={'payment_account_id'}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'payment_account'} />}
|
||||
className={classNames(
|
||||
'form-group--payment_account_id',
|
||||
'form-group--select-list',
|
||||
Classes.FILL,
|
||||
)}
|
||||
inline={true}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'payment_account_id'} />}
|
||||
>
|
||||
<AccountsSelectList
|
||||
accounts={accounts}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
onAccountSelected={(account) => {
|
||||
form.setFieldValue('payment_account_id', account.id);
|
||||
}}
|
||||
defaultSelectText={<T id={'select_payment_account'} />}
|
||||
selectedAccountId={value}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
{/* ------------ Reference ------------ */}
|
||||
<FastField name={'reference'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'reference'} />}
|
||||
inline={true}
|
||||
className={classNames('form-group--reference', Classes.FILL)}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="reference" />}
|
||||
>
|
||||
<InputGroup
|
||||
intent={inputIntent({ error, touched })}
|
||||
minimal={true}
|
||||
{...field}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withSettings(({ organizationSettings }) => ({
|
||||
baseCurrency: organizationSettings?.baseCurrency,
|
||||
})),
|
||||
)(PaymentMadeFormHeaderFields);
|
||||
@@ -1,123 +1,20 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import React from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useQuery } from 'react-query';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import PaymentMadeForm from './PaymentMadeForm';
|
||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||
|
||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
import withVenderActions from 'containers/Vendors/withVendorActions';
|
||||
import withAccountsActions from 'containers/Accounts/withAccountsActions';
|
||||
import withItemsActions from 'containers/Items/withItemsActions';
|
||||
import withPaymentMadeActions from './withPaymentMadeActions';
|
||||
import withBillActions from '../Bill/withBillActions';
|
||||
import withSettingsActions from 'containers/Settings/withSettingsActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
import { PaymentMadeFormProvider } from './PaymentMadeFormProvider';
|
||||
|
||||
import 'style/pages/PaymentMade/PageForm.scss'
|
||||
|
||||
/**
|
||||
* Payment made - Page form.
|
||||
*/
|
||||
function PaymentMadeFormPage({
|
||||
//#withAccountsActions
|
||||
requestFetchAccounts,
|
||||
|
||||
//#withVenderActions
|
||||
requestFetchVendorsTable,
|
||||
|
||||
//#withItemsActions
|
||||
requestFetchItems,
|
||||
|
||||
//#withPaymentMadesActions
|
||||
requestFetchPaymentMade,
|
||||
|
||||
// #withSettingsActions
|
||||
requestFetchOptions,
|
||||
|
||||
// #withDashboardActions
|
||||
changePageTitle,
|
||||
setSidebarShrink,
|
||||
resetSidebarPreviousExpand,
|
||||
setDashboardBackLink
|
||||
}) {
|
||||
export default function PaymentMadeFormPage() {
|
||||
const { id: paymentMadeId } = useParams();
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
useEffect(() => {
|
||||
// Shrink the sidebar by foce.
|
||||
setSidebarShrink();
|
||||
// Show the back link on dashboard topbar.
|
||||
setDashboardBackLink(true);
|
||||
|
||||
return () => {
|
||||
// Reset the sidebar to the previous status.
|
||||
resetSidebarPreviousExpand();
|
||||
// Hide the back link on dashboard topbar.
|
||||
setDashboardBackLink(false);
|
||||
};
|
||||
}, [resetSidebarPreviousExpand, setSidebarShrink, setDashboardBackLink]);
|
||||
|
||||
// Handle page title change in new and edit mode.
|
||||
useEffect(() => {
|
||||
if (paymentMadeId) {
|
||||
changePageTitle(formatMessage({ id: 'edit_payment_made' }));
|
||||
} else {
|
||||
changePageTitle(formatMessage({ id: 'payment_made' }));
|
||||
}
|
||||
}, [changePageTitle, paymentMadeId, formatMessage]);
|
||||
|
||||
// Handle fetch accounts data.
|
||||
const fetchAccounts = useQuery('accounts-list', (key) =>
|
||||
requestFetchAccounts(),
|
||||
);
|
||||
|
||||
// Handle fetch Items data table or list.
|
||||
const fetchItems = useQuery('items-list', () => requestFetchItems({}));
|
||||
|
||||
// Handle fetch venders data table or list.
|
||||
const fetchVenders = useQuery('venders-list', () =>
|
||||
requestFetchVendorsTable({}),
|
||||
);
|
||||
|
||||
// Handle fetch specific payment made details.
|
||||
const fetchPaymentMade = useQuery(
|
||||
['payment-made', paymentMadeId],
|
||||
(key, _id) => requestFetchPaymentMade(_id),
|
||||
{ enabled: !!paymentMadeId },
|
||||
);
|
||||
|
||||
// Fetch payment made settings.
|
||||
const fetchSettings = useQuery(['settings'], () => requestFetchOptions({}));
|
||||
|
||||
return (
|
||||
<DashboardInsider
|
||||
loading={
|
||||
fetchVenders.isFetching ||
|
||||
fetchItems.isFetching ||
|
||||
fetchAccounts.isFetching ||
|
||||
fetchPaymentMade.isFetching ||
|
||||
fetchSettings.isFetching
|
||||
}
|
||||
name={'payment-made'}
|
||||
>
|
||||
<PaymentMadeForm
|
||||
paymentMadeId={paymentMadeId}
|
||||
mode={paymentMadeId ? 'edit' : 'new'}
|
||||
/>
|
||||
</DashboardInsider>
|
||||
<PaymentMadeFormProvider paymentMadeId={paymentMadeId}>
|
||||
<PaymentMadeForm />
|
||||
</PaymentMadeFormProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withVenderActions,
|
||||
withItemsActions,
|
||||
withAccountsActions,
|
||||
withBillActions,
|
||||
withPaymentMadeActions,
|
||||
withSettingsActions,
|
||||
withDashboardActions,
|
||||
)(PaymentMadeFormPage);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
import React, { createContext, useContext } from 'react';
|
||||
import {
|
||||
useAccounts,
|
||||
useVendors,
|
||||
useItems,
|
||||
usePaymentMade,
|
||||
useSettings,
|
||||
useCreatePaymentMade,
|
||||
useEditPaymentMade
|
||||
} from 'hooks/query';
|
||||
import { DashboardInsider } from 'components';
|
||||
|
||||
// Payment made form context.
|
||||
const PaymentMadeFormContext = createContext();
|
||||
|
||||
/**
|
||||
* Payment made form provider.
|
||||
*/
|
||||
function PaymentMadeFormProvider({ paymentMadeId, ...props }) {
|
||||
// Handle fetch accounts data.
|
||||
const { data: accounts, isFetching: isAccountsFetching } = useAccounts();
|
||||
|
||||
// Handle fetch Items data table or list.
|
||||
const {
|
||||
data: { items },
|
||||
isFetching: isItemsFetching,
|
||||
isLoading: isItemsLoading,
|
||||
} = useItems();
|
||||
|
||||
// Handle fetch venders data table or list.
|
||||
const {
|
||||
data: { vendors },
|
||||
isFetching: isVendorsFetching,
|
||||
} = useVendors();
|
||||
|
||||
// Handle fetch specific payment made details.
|
||||
const {
|
||||
data: { paymentMade, payableBills, paymentBills },
|
||||
isFetching: isPaymentFetching,
|
||||
isLoading: isPaymentLoading,
|
||||
} = usePaymentMade(paymentMadeId, {
|
||||
enabled: !!paymentMadeId,
|
||||
});
|
||||
|
||||
// Fetch payment made settings.
|
||||
useSettings();
|
||||
|
||||
// Create and edit payment made mutations.
|
||||
const { mutateAsync: createPaymentMadeMutate } = useCreatePaymentMade();
|
||||
const { mutateAsync: editPaymentMadeMutate } = useEditPaymentMade();
|
||||
|
||||
// Provider payload.
|
||||
const provider = {
|
||||
paymentMadeId,
|
||||
accounts,
|
||||
paymentMade,
|
||||
payableBills,
|
||||
paymentBills,
|
||||
vendors,
|
||||
items,
|
||||
|
||||
isAccountsFetching,
|
||||
isItemsFetching,
|
||||
isItemsLoading,
|
||||
isVendorsFetching,
|
||||
isPaymentFetching,
|
||||
isPaymentLoading,
|
||||
|
||||
createPaymentMadeMutate,
|
||||
editPaymentMadeMutate,
|
||||
};
|
||||
|
||||
return (
|
||||
<DashboardInsider
|
||||
loading={
|
||||
isVendorsFetching ||
|
||||
isItemsFetching ||
|
||||
isAccountsFetching ||
|
||||
isPaymentFetching ||
|
||||
isPaymentLoading
|
||||
}
|
||||
name={'payment-made'}
|
||||
>
|
||||
<PaymentMadeFormContext.Provider value={provider} {...props} />
|
||||
</DashboardInsider>
|
||||
);
|
||||
}
|
||||
|
||||
const usePaymentMadeFormContext = () => useContext(PaymentMadeFormContext);
|
||||
|
||||
export { PaymentMadeFormProvider, usePaymentMadeFormContext };
|
||||
@@ -0,0 +1,40 @@
|
||||
import moment from 'moment';
|
||||
import { sumBy } from 'lodash';
|
||||
import { transformToForm } from 'utils';
|
||||
|
||||
export const ERRORS = {
|
||||
PAYMENT_NUMBER_NOT_UNIQUE: 'PAYMENT.NUMBER.NOT.UNIQUE',
|
||||
};
|
||||
|
||||
|
||||
// Default payment made entry values.
|
||||
export const defaultPaymentMadeEntry = {
|
||||
bill_id: '',
|
||||
payment_amount: '',
|
||||
id: null,
|
||||
due_amount: null,
|
||||
};
|
||||
|
||||
// Default initial values of payment made.
|
||||
export const defaultPaymentMade = {
|
||||
full_amount: '',
|
||||
vendor_id: '',
|
||||
payment_account_id: '',
|
||||
payment_date: moment(new Date()).format('YYYY-MM-DD'),
|
||||
reference: '',
|
||||
payment_number: '',
|
||||
description: '',
|
||||
entries: [],
|
||||
};
|
||||
|
||||
export const transformToEditForm = (paymentMade, paymentMadeEntries) => {
|
||||
return {
|
||||
...transformToForm(paymentMade, defaultPaymentMade),
|
||||
full_amount: sumBy(paymentMade.entries, 'payment_amount'),
|
||||
entries: [
|
||||
...paymentMadeEntries.map((paymentMadeEntry) => ({
|
||||
...transformToForm(paymentMadeEntry, defaultPaymentMadeEntry),
|
||||
})),
|
||||
],
|
||||
};
|
||||
};
|
||||
@@ -1,9 +1,8 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
import { CLASSES } from 'common/classes';
|
||||
import { DataTable } from 'components';
|
||||
import PaymentMadesEmptyStatus from './PaymentMadesEmptyStatus';
|
||||
import TableSkeletonRows from 'components/Datatable/TableSkeletonRows';
|
||||
@@ -37,8 +36,13 @@ function PaymentMadesTable({
|
||||
isPaymentsFetching,
|
||||
} = usePaymentMadesListContext();
|
||||
|
||||
// History context.
|
||||
const history = useHistory();
|
||||
|
||||
// Handles the edit payment made action.
|
||||
const handleEditPaymentMade = (paymentMade) => {};
|
||||
const handleEditPaymentMade = (paymentMade) => {
|
||||
history.push(`/payment-mades/${paymentMade.id}/edit`);
|
||||
};
|
||||
|
||||
// Handles the delete payment made action.
|
||||
const handleDeletePaymentMade = (paymentMade) => {
|
||||
|
||||
@@ -1,479 +0,0 @@
|
||||
import React, { useMemo, useCallback, useEffect, useState } from 'react';
|
||||
import { useFormik } from 'formik';
|
||||
import moment from 'moment';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
import { pick, sumBy, omit } from 'lodash';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import classNames from 'classnames';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import { CLASSES } from 'common/classes';
|
||||
import PaymentReceiveHeader from './PaymentReceiveFormHeader';
|
||||
import PaymentReceiveItemsTable from './PaymentReceiveItemsTable';
|
||||
import PaymentReceiveFloatingActions from './PaymentReceiveFloatingActions';
|
||||
import PaymentReceiveFormFooter from './PaymentReceiveFormFooter';
|
||||
|
||||
import withMediaActions from 'containers/Media/withMediaActions';
|
||||
import withPaymentReceivesActions from './withPaymentReceivesActions';
|
||||
import withPaymentReceiveDetail from './withPaymentReceiveDetail';
|
||||
import withPaymentReceives from './withPaymentReceives';
|
||||
import withSettings from 'containers/Settings/withSettings';
|
||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
import {
|
||||
EditPaymentReceiveFormSchema,
|
||||
CreatePaymentReceiveFormSchema,
|
||||
} from './PaymentReceiveForm.schema';
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import { compose, defaultToTransform } from 'utils';
|
||||
|
||||
import 'style/pages/PaymentReceive/PageForm.scss'
|
||||
|
||||
/**
|
||||
* Payment Receive form.
|
||||
*/
|
||||
function PaymentReceiveForm({
|
||||
// #ownProps
|
||||
paymentReceiveId,
|
||||
mode, //edit, new
|
||||
|
||||
//#WithPaymentReceiveActions
|
||||
requestSubmitPaymentReceive,
|
||||
requestEditPaymentReceive,
|
||||
setPaymentReceiveNumberChanged,
|
||||
|
||||
// #withPaymentReceiveDetail
|
||||
paymentReceive,
|
||||
paymentReceiveEntries,
|
||||
|
||||
// #withSettings
|
||||
paymentReceiveNextNumber,
|
||||
paymentReceiveNumberPrefix,
|
||||
|
||||
// #withPaymentReceives
|
||||
paymentReceiveNumberChanged,
|
||||
|
||||
// #withDashboardAction
|
||||
changePageTitle,
|
||||
changePageSubtitle,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
|
||||
const [amountChangeAlert, setAmountChangeAlert] = useState(false);
|
||||
const [clearLinesAlert, setClearLinesAlert] = useState(false);
|
||||
const [fullAmount, setFullAmount] = useState(null);
|
||||
const [clearFormAlert, setClearFormAlert] = useState(false);
|
||||
const [submitPayload, setSubmitPayload] = useState({});
|
||||
|
||||
const { formatMessage } = useIntl();
|
||||
const [localPaymentEntries, setLocalPaymentEntries] = useState(
|
||||
paymentReceiveEntries,
|
||||
);
|
||||
const isNewMode = !paymentReceiveId;
|
||||
|
||||
const paymentReceiveNumber = paymentReceiveNumberPrefix
|
||||
? `${paymentReceiveNumberPrefix}-${paymentReceiveNextNumber}`
|
||||
: paymentReceiveNextNumber;
|
||||
|
||||
useEffect(() => {
|
||||
const transactionNumber = !isNewMode
|
||||
? paymentReceive.payment_receive_no
|
||||
: paymentReceiveNumber;
|
||||
|
||||
if (paymentReceive && paymentReceiveId) {
|
||||
changePageTitle(formatMessage({ id: 'edit_payment_receive' }));
|
||||
changePageSubtitle(`No. ${paymentReceive.payment_receive_no}`);
|
||||
} else {
|
||||
changePageTitle(formatMessage({ id: 'payment_receive' }));
|
||||
}
|
||||
changePageSubtitle(
|
||||
defaultToTransform(transactionNumber, `No. ${transactionNumber}`, ''),
|
||||
);
|
||||
}, [
|
||||
isNewMode,
|
||||
changePageTitle,
|
||||
changePageSubtitle,
|
||||
paymentReceive,
|
||||
paymentReceiveId,
|
||||
formatMessage,
|
||||
paymentReceiveNumber,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (localPaymentEntries !== paymentReceiveEntries) {
|
||||
setLocalPaymentEntries(paymentReceiveEntries);
|
||||
}
|
||||
}, [localPaymentEntries, paymentReceiveEntries]);
|
||||
|
||||
// Form validation schema.
|
||||
const validationSchema = isNewMode
|
||||
? CreatePaymentReceiveFormSchema
|
||||
: EditPaymentReceiveFormSchema;
|
||||
|
||||
// Default payment receive entry.
|
||||
const defaultPaymentReceiveEntry = {
|
||||
id: '',
|
||||
payment_amount: '',
|
||||
invoice_id: '',
|
||||
due_amount: '',
|
||||
};
|
||||
|
||||
// Form initial values.
|
||||
const defaultInitialValues = {
|
||||
customer_id: '',
|
||||
deposit_account_id: '',
|
||||
payment_date: moment(new Date()).format('YYYY-MM-DD'),
|
||||
reference_no: '',
|
||||
payment_receive_no: paymentReceiveNumber,
|
||||
description: '',
|
||||
full_amount: '',
|
||||
entries: [],
|
||||
};
|
||||
|
||||
// Form initial values.
|
||||
const initialValues = useMemo(
|
||||
() => ({
|
||||
...(paymentReceive
|
||||
? {
|
||||
...pick(paymentReceive, Object.keys(defaultInitialValues)),
|
||||
entries: [
|
||||
...paymentReceiveEntries.map((paymentReceiveEntry) => ({
|
||||
...pick(
|
||||
paymentReceiveEntry,
|
||||
Object.keys(defaultPaymentReceiveEntry),
|
||||
),
|
||||
})),
|
||||
],
|
||||
}
|
||||
: {
|
||||
...defaultInitialValues,
|
||||
}),
|
||||
}),
|
||||
[paymentReceive, paymentReceiveEntries],
|
||||
);
|
||||
|
||||
// Handle form submit.
|
||||
const handleSubmitForm = (
|
||||
values,
|
||||
{ setSubmitting, resetForm, setFieldError },
|
||||
) => {
|
||||
setSubmitting(true);
|
||||
|
||||
// Filters entries that have no `invoice_id` and `payment_amount`.
|
||||
const entries = values.entries
|
||||
.filter((entry) => entry.invoice_id && entry.payment_amount)
|
||||
.map((entry) => ({
|
||||
...omit(entry, ['due_amount']),
|
||||
}));
|
||||
|
||||
// Calculates the total payment amount of entries.
|
||||
const totalPaymentAmount = sumBy(entries, 'payment_amount');
|
||||
|
||||
if (totalPaymentAmount <= 0) {
|
||||
AppToaster.show({
|
||||
message: formatMessage({
|
||||
id: 'you_cannot_make_payment_with_zero_total_amount',
|
||||
}),
|
||||
intent: Intent.DANGER,
|
||||
});
|
||||
setSubmitting(false);
|
||||
return;
|
||||
}
|
||||
const form = { ...values, entries };
|
||||
|
||||
// Handle request response success.
|
||||
const onSaved = (response) => {
|
||||
AppToaster.show({
|
||||
message: formatMessage({
|
||||
id: paymentReceiveId
|
||||
? 'the_payment_receive_transaction_has_been_edited'
|
||||
: 'the_payment_receive_transaction_has_been_created',
|
||||
}),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
setSubmitting(false);
|
||||
|
||||
if (submitPayload.redirect) {
|
||||
history.push('/payment-receives');
|
||||
}
|
||||
if (submitPayload.resetForm) {
|
||||
resetForm();
|
||||
}
|
||||
};
|
||||
// Handle request response errors.
|
||||
const onError = (errors) => {
|
||||
const getError = (errorType) => errors.find((e) => e.type === errorType);
|
||||
|
||||
if (getError('PAYMENT_RECEIVE_NO_EXISTS')) {
|
||||
setFieldError(
|
||||
'payment_receive_no',
|
||||
formatMessage({ id: 'payment_number_is_not_unique' }),
|
||||
);
|
||||
}
|
||||
setSubmitting(false);
|
||||
};
|
||||
|
||||
if (paymentReceiveId) {
|
||||
requestEditPaymentReceive(paymentReceiveId, form)
|
||||
.then(onSaved)
|
||||
.catch(onError);
|
||||
} else {
|
||||
requestSubmitPaymentReceive(form).then(onSaved).catch(onError);
|
||||
}
|
||||
};
|
||||
|
||||
const {
|
||||
errors,
|
||||
values,
|
||||
setFieldValue,
|
||||
getFieldProps,
|
||||
setValues,
|
||||
handleSubmit,
|
||||
isSubmitting,
|
||||
touched,
|
||||
resetForm,
|
||||
submitForm,
|
||||
} = useFormik({
|
||||
enableReinitialize: true,
|
||||
validationSchema,
|
||||
initialValues: {
|
||||
...initialValues,
|
||||
},
|
||||
onSubmit: handleSubmitForm,
|
||||
});
|
||||
|
||||
const transformDataTableToEntries = (dataTable) => {
|
||||
return dataTable.map((data) => ({
|
||||
...pick(data, Object.keys(defaultPaymentReceiveEntry)),
|
||||
}));
|
||||
};
|
||||
|
||||
// Handle update data.
|
||||
const handleUpdataData = useCallback(
|
||||
(entries) => {
|
||||
setFieldValue('entries', transformDataTableToEntries(entries));
|
||||
},
|
||||
[setFieldValue],
|
||||
);
|
||||
|
||||
// Handle fetch success of customer invoices entries.
|
||||
const handleFetchEntriesSuccess = useCallback(
|
||||
(entries) => {
|
||||
setFieldValue('entries', transformDataTableToEntries(entries));
|
||||
},
|
||||
[setFieldValue],
|
||||
);
|
||||
|
||||
// Handles full amount change.
|
||||
const handleFullAmountChange = useCallback(
|
||||
(value) => {
|
||||
if (value !== fullAmount) {
|
||||
setAmountChangeAlert(value);
|
||||
}
|
||||
},
|
||||
[fullAmount, setAmountChangeAlert],
|
||||
);
|
||||
|
||||
// Handle clear all lines button click.
|
||||
const handleClearAllLines = useCallback(() => {
|
||||
setClearLinesAlert(true);
|
||||
}, [setClearLinesAlert]);
|
||||
|
||||
// Handle cancel button click of clear lines alert
|
||||
const handleCancelClearLines = useCallback(() => {
|
||||
setClearLinesAlert(false);
|
||||
}, [setClearLinesAlert]);
|
||||
|
||||
// Handle cancel button of amount change alert.
|
||||
const handleCancelAmountChangeAlert = () => {
|
||||
setAmountChangeAlert(false);
|
||||
};
|
||||
// Handle confirm button of amount change alert.
|
||||
const handleConfirmAmountChangeAlert = () => {
|
||||
setFullAmount(amountChangeAlert);
|
||||
setAmountChangeAlert(false);
|
||||
};
|
||||
|
||||
// Reset entries payment amount.
|
||||
const resetEntriesPaymentAmount = (entries) => {
|
||||
return entries.map((entry) => ({ ...entry, payment_amount: 0 }));
|
||||
};
|
||||
// Handle confirm clear all lines.
|
||||
const handleConfirmClearLines = useCallback(() => {
|
||||
setLocalPaymentEntries(resetEntriesPaymentAmount(localPaymentEntries));
|
||||
setFieldValue('entries', resetEntriesPaymentAmount(values.entries));
|
||||
setClearLinesAlert(false);
|
||||
}, [setFieldValue, setClearLinesAlert, values.entries, localPaymentEntries]);
|
||||
|
||||
// Handle footer clear button click.
|
||||
const handleClearBtnClick = () => {
|
||||
setClearFormAlert(true);
|
||||
};
|
||||
// Handle cancel button click of clear form alert.
|
||||
const handleCancelClearFormAlert = () => {
|
||||
setClearFormAlert(false);
|
||||
};
|
||||
// Handle confirm button click of clear form alert.
|
||||
const handleConfirmCancelClearFormAlert = () => {
|
||||
resetForm();
|
||||
setClearFormAlert(false);
|
||||
setFullAmount(null);
|
||||
};
|
||||
|
||||
// Calculates the total receivable amount from due amount.
|
||||
const receivableFullAmount = useMemo(
|
||||
() => sumBy(values.entries, 'due_amount'),
|
||||
[values.entries],
|
||||
);
|
||||
|
||||
const fullAmountReceived = useMemo(
|
||||
() => sumBy(values.entries, 'payment_amount'),
|
||||
[values.entries],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (paymentReceiveNumberChanged) {
|
||||
setFieldValue('payment_receive_no', paymentReceiveNumber);
|
||||
changePageSubtitle(
|
||||
defaultToTransform(
|
||||
paymentReceiveNumber,
|
||||
`No. ${paymentReceiveNumber}`,
|
||||
'',
|
||||
),
|
||||
);
|
||||
setPaymentReceiveNumberChanged(false);
|
||||
}
|
||||
}, [
|
||||
paymentReceiveNumber,
|
||||
paymentReceiveNumberChanged,
|
||||
setFieldValue,
|
||||
changePageSubtitle,
|
||||
setPaymentReceiveNumberChanged,
|
||||
]);
|
||||
|
||||
const handlePaymentReceiveNumberChanged = useCallback(
|
||||
(payment_receive_no) => {
|
||||
changePageSubtitle(
|
||||
defaultToTransform(payment_receive_no, `No.${payment_receive_no}`, ''),
|
||||
);
|
||||
},
|
||||
[changePageSubtitle],
|
||||
);
|
||||
|
||||
const handleSubmitClick = useCallback(
|
||||
(event, payload) => {
|
||||
setSubmitPayload({ ...payload });
|
||||
},
|
||||
[setSubmitPayload],
|
||||
);
|
||||
|
||||
const handleCancelClick = useCallback(() => {
|
||||
history.goBack();
|
||||
}, [history]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
CLASSES.PAGE_FORM,
|
||||
CLASSES.PAGE_FORM_STRIP_STYLE,
|
||||
CLASSES.PAGE_FORM_PAYMENT_RECEIVE,
|
||||
)}
|
||||
>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<PaymentReceiveHeader
|
||||
errors={errors}
|
||||
touched={touched}
|
||||
setFieldValue={setFieldValue}
|
||||
getFieldProps={getFieldProps}
|
||||
values={values}
|
||||
paymentReceiveId={paymentReceiveId}
|
||||
customerId={values.customer_id}
|
||||
onFullAmountChanged={handleFullAmountChange}
|
||||
receivableFullAmount={receivableFullAmount}
|
||||
amountReceived={fullAmountReceived}
|
||||
onPaymentReceiveNumberChanged={handlePaymentReceiveNumberChanged}
|
||||
/>
|
||||
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
|
||||
<PaymentReceiveItemsTable
|
||||
paymentReceiveId={paymentReceiveId}
|
||||
customerId={values.customer_id}
|
||||
fullAmount={fullAmount}
|
||||
onUpdateData={handleUpdataData}
|
||||
paymentReceiveEntries={localPaymentEntries}
|
||||
errors={errors?.entries}
|
||||
onClickClearAllLines={handleClearAllLines}
|
||||
onFetchEntriesSuccess={handleFetchEntriesSuccess}
|
||||
/>
|
||||
</div>
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'ok'} />}
|
||||
intent={Intent.WARNING}
|
||||
isOpen={amountChangeAlert}
|
||||
onCancel={handleCancelAmountChangeAlert}
|
||||
onConfirm={handleConfirmAmountChangeAlert}
|
||||
>
|
||||
<p>
|
||||
<T
|
||||
id={'changing_full_amount_will_change_all_credits_and_payment'}
|
||||
/>
|
||||
</p>
|
||||
</Alert>
|
||||
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'ok'} />}
|
||||
intent={Intent.WARNING}
|
||||
isOpen={clearLinesAlert}
|
||||
onCancel={handleCancelClearLines}
|
||||
onConfirm={handleConfirmClearLines}
|
||||
>
|
||||
<p>
|
||||
<T id={'clearing_the_table_lines_will_delete_all_credits'} />
|
||||
</p>
|
||||
</Alert>
|
||||
|
||||
<Alert
|
||||
cancelButtonText={<T id={'cancel'} />}
|
||||
confirmButtonText={<T id={'ok'} />}
|
||||
intent={Intent.WARNING}
|
||||
isOpen={clearFormAlert}
|
||||
onCancel={handleCancelClearFormAlert}
|
||||
onConfirm={handleConfirmCancelClearFormAlert}
|
||||
>
|
||||
<p>
|
||||
<T id={'are_you_sure_you_want_to_clear_this_transaction'} />
|
||||
</p>
|
||||
</Alert>
|
||||
|
||||
<PaymentReceiveFormFooter getFieldProps={getFieldProps} />
|
||||
|
||||
<PaymentReceiveFloatingActions
|
||||
isSubmitting={isSubmitting}
|
||||
paymentReceiveId={paymentReceiveId}
|
||||
onClearClick={handleClearBtnClick}
|
||||
onSubmitClick={handleSubmitClick}
|
||||
onCancelClick={handleCancelClick}
|
||||
onSubmitForm={submitForm}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withPaymentReceivesActions,
|
||||
withDashboardActions,
|
||||
withMediaActions,
|
||||
withPaymentReceiveDetail(({ paymentReceive, paymentReceiveEntries }) => ({
|
||||
paymentReceive,
|
||||
paymentReceiveEntries,
|
||||
})),
|
||||
withSettings(({ paymentReceiveSettings }) => ({
|
||||
paymentReceiveNextNumber: paymentReceiveSettings?.nextNumber,
|
||||
paymentReceiveNumberPrefix: paymentReceiveSettings?.numberPrefix,
|
||||
})),
|
||||
withPaymentReceives(({ paymentReceiveNumberChanged }) => ({
|
||||
paymentReceiveNumberChanged,
|
||||
})),
|
||||
)(PaymentReceiveForm);
|
||||
@@ -1,304 +0,0 @@
|
||||
import React, { useMemo, useCallback } from 'react';
|
||||
import {
|
||||
FormGroup,
|
||||
InputGroup,
|
||||
Intent,
|
||||
Position,
|
||||
ControlGroup,
|
||||
} from '@blueprintjs/core';
|
||||
|
||||
import { DateInput } from '@blueprintjs/datetime';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import moment from 'moment';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { CLASSES } from 'common/classes';
|
||||
import { momentFormatter, compose, tansformDateValue, saveInvoke } from 'utils';
|
||||
import {
|
||||
AccountsSelectList,
|
||||
ContactSelecetList,
|
||||
ErrorMessage,
|
||||
FieldRequiredHint,
|
||||
Icon,
|
||||
InputPrependButton,
|
||||
MoneyInputGroup,
|
||||
InputPrependText,
|
||||
Hint,
|
||||
Money,
|
||||
} from 'components';
|
||||
|
||||
import withCustomers from 'containers/Customers/withCustomers';
|
||||
import withAccounts from 'containers/Accounts/withAccounts';
|
||||
import withSettings from 'containers/Settings/withSettings';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
|
||||
function PaymentReceiveFormHeader({
|
||||
// #useFormik
|
||||
errors,
|
||||
touched,
|
||||
setFieldValue,
|
||||
getFieldProps,
|
||||
values,
|
||||
onFullAmountChanged,
|
||||
paymentReceiveId,
|
||||
receivableFullAmount,
|
||||
amountReceived,
|
||||
|
||||
//#withCustomers
|
||||
customers,
|
||||
|
||||
//#withAccouts
|
||||
accountsList,
|
||||
|
||||
//#withSettings
|
||||
baseCurrency,
|
||||
|
||||
// #withInvoices
|
||||
receivableInvoices,
|
||||
// #ownProps
|
||||
onPaymentReceiveNumberChanged,
|
||||
|
||||
//#withDialogActions
|
||||
openDialog,
|
||||
}) {
|
||||
const handleDateChange = useCallback(
|
||||
(date_filed) => (date) => {
|
||||
const formatted = moment(date).format('YYYY-MM-DD');
|
||||
setFieldValue(date_filed, formatted);
|
||||
},
|
||||
[setFieldValue],
|
||||
);
|
||||
|
||||
const onChangeSelect = useCallback(
|
||||
(filedName) => {
|
||||
return (item) => {
|
||||
setFieldValue(filedName, item.id);
|
||||
};
|
||||
},
|
||||
[setFieldValue],
|
||||
);
|
||||
|
||||
const triggerFullAmountChanged = (value) => {
|
||||
onFullAmountChanged && onFullAmountChanged(value);
|
||||
};
|
||||
|
||||
// Handle full amount changed event.
|
||||
const handleFullAmountBlur = (event) => {
|
||||
triggerFullAmountChanged(event.currentTarget.value);
|
||||
};
|
||||
// Handle link click of receive full amount.
|
||||
const handleReceiveFullAmountClick = () => {
|
||||
setFieldValue('full_amount', receivableFullAmount);
|
||||
triggerFullAmountChanged(receivableFullAmount);
|
||||
};
|
||||
|
||||
const handlePaymentReceiveNumberChange = useCallback(() => {
|
||||
openDialog('payment-receive-number-form', {});
|
||||
}, [openDialog]);
|
||||
|
||||
const handlePaymentReceiveNumberChanged = (event) => {
|
||||
saveInvoke(onPaymentReceiveNumberChanged, event.currentTarget.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
|
||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_PRIMARY)}>
|
||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
|
||||
{/* ------------- Customer name ------------- */}
|
||||
<FormGroup
|
||||
label={<T id={'customer_name'} />}
|
||||
inline={true}
|
||||
className={classNames('form-group--select-list', CLASSES.FILL)}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
intent={errors.customer_id && touched.customer_id && Intent.DANGER}
|
||||
helperText={
|
||||
<ErrorMessage name={'customer_id'} {...{ errors, touched }} />
|
||||
}
|
||||
>
|
||||
<ContactSelecetList
|
||||
contactsList={customers}
|
||||
selectedContactId={values.customer_id}
|
||||
defaultSelectText={<T id={'select_customer_account'} />}
|
||||
onContactSelected={onChangeSelect('customer_id')}
|
||||
popoverFill={true}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
{/* ------------- Payment date ------------- */}
|
||||
<FormGroup
|
||||
label={<T id={'payment_date'} />}
|
||||
inline={true}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
className={classNames('form-group--select-list', CLASSES.FILL)}
|
||||
intent={
|
||||
errors.payment_date && touched.payment_date && Intent.DANGER
|
||||
}
|
||||
helperText={
|
||||
<ErrorMessage name="payment_date" {...{ errors, touched }} />
|
||||
}
|
||||
>
|
||||
<DateInput
|
||||
{...momentFormatter('YYYY/MM/DD')}
|
||||
value={tansformDateValue(values.payment_date)}
|
||||
onChange={handleDateChange('payment_date')}
|
||||
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
||||
inputProps={{
|
||||
leftIcon: (<Icon icon={'date-range'} />),
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
{/* ------------ Full amount ------------ */}
|
||||
<FormGroup
|
||||
label={<T id={'full_amount'} />}
|
||||
inline={true}
|
||||
className={('form-group--full-amount', CLASSES.FILL)}
|
||||
intent={errors.full_amount && touched.full_amount && Intent.DANGER}
|
||||
labelInfo={<Hint />}
|
||||
helperText={
|
||||
<ErrorMessage name="full_amount" {...{ errors, touched }} />
|
||||
}
|
||||
>
|
||||
<ControlGroup>
|
||||
<InputPrependText text={baseCurrency} />
|
||||
<MoneyInputGroup
|
||||
value={values.full_amount}
|
||||
inputGroupProps={{
|
||||
medium: true,
|
||||
onBlur: { handleFullAmountBlur },
|
||||
...getFieldProps('full_amount'),
|
||||
}}
|
||||
/>
|
||||
</ControlGroup>
|
||||
|
||||
<a
|
||||
onClick={handleReceiveFullAmountClick}
|
||||
href="#"
|
||||
className={'receive-full-amount'}
|
||||
>
|
||||
Receive full amount (
|
||||
<Money amount={receivableFullAmount} currency={baseCurrency} />)
|
||||
</a>
|
||||
</FormGroup>
|
||||
|
||||
{/* ------------ Payment receive no. ------------ */}
|
||||
<FormGroup
|
||||
label={<T id={'payment_receive_no'} />}
|
||||
inline={true}
|
||||
className={('form-group--payment_receive_no', CLASSES.FILL)}
|
||||
intent={
|
||||
errors.payment_receive_no &&
|
||||
touched.payment_receive_no &&
|
||||
Intent.DANGER
|
||||
}
|
||||
helperText={
|
||||
<ErrorMessage
|
||||
name="payment_receive_no"
|
||||
{...{ errors, touched }}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ControlGroup fill={true}>
|
||||
<InputGroup
|
||||
intent={
|
||||
errors.payment_receive_no &&
|
||||
touched.payment_receive_no &&
|
||||
Intent.DANGER
|
||||
}
|
||||
minimal={true}
|
||||
{...getFieldProps('payment_receive_no')}
|
||||
onBlur={handlePaymentReceiveNumberChanged}
|
||||
/>
|
||||
<InputPrependButton
|
||||
buttonProps={{
|
||||
onClick: handlePaymentReceiveNumberChange,
|
||||
icon: <Icon icon={'settings-18'} />,
|
||||
}}
|
||||
tooltip={true}
|
||||
tooltipProps={{
|
||||
content: 'Setting your auto-generated Payment Receive number',
|
||||
position: Position.BOTTOM_LEFT,
|
||||
}}
|
||||
/>
|
||||
</ControlGroup>
|
||||
</FormGroup>
|
||||
|
||||
{/* ------------ Deposit account ------------ */}
|
||||
<FormGroup
|
||||
label={<T id={'deposit_to'} />}
|
||||
className={classNames(
|
||||
'form-group--deposit_account_id',
|
||||
'form-group--select-list',
|
||||
CLASSES.FILL,
|
||||
)}
|
||||
inline={true}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
intent={
|
||||
errors.deposit_account_id &&
|
||||
touched.deposit_account_id &&
|
||||
Intent.DANGER
|
||||
}
|
||||
helperText={
|
||||
<ErrorMessage
|
||||
name={'deposit_account_id'}
|
||||
{...{ errors, touched }}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<AccountsSelectList
|
||||
accounts={accountsList}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
onAccountSelected={onChangeSelect('deposit_account_id')}
|
||||
defaultSelectText={<T id={'select_deposit_account'} />}
|
||||
selectedAccountId={values.deposit_account_id}
|
||||
filterByTypes={['current_asset']}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
{/* ------------ Reference No. ------------ */}
|
||||
<FormGroup
|
||||
label={<T id={'reference'} />}
|
||||
inline={true}
|
||||
className={classNames('form-group--reference', CLASSES.FILL)}
|
||||
intent={
|
||||
errors.reference_no && touched.reference_no && Intent.DANGER
|
||||
}
|
||||
helperText={
|
||||
<ErrorMessage name="reference" {...{ errors, touched }} />
|
||||
}
|
||||
>
|
||||
<InputGroup
|
||||
intent={
|
||||
errors.reference_no && touched.reference_no && Intent.DANGER
|
||||
}
|
||||
minimal={true}
|
||||
{...getFieldProps('reference_no')}
|
||||
/>
|
||||
</FormGroup>
|
||||
</div>
|
||||
|
||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_BIG_NUMBERS)}>
|
||||
<div class="big-amount">
|
||||
<span class="big-amount__label">Amount Received</span>
|
||||
<h1 class="big-amount__number">
|
||||
<Money amount={amountReceived} currency={baseCurrency} />
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withCustomers(({ customers }) => ({
|
||||
customers,
|
||||
})),
|
||||
withAccounts(({ accountsList }) => ({
|
||||
accountsList,
|
||||
})),
|
||||
withSettings(({ organizationSettings }) => ({
|
||||
baseCurrency: organizationSettings?.baseCurrency,
|
||||
})),
|
||||
withDialogActions,
|
||||
)(PaymentReceiveFormHeader);
|
||||
@@ -1,98 +0,0 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { useQuery } from 'react-query';
|
||||
|
||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||
|
||||
import PaymentReceiveForm from './PaymentReceiveForm';
|
||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
import withAccountsActions from 'containers/Accounts/withAccountsActions';
|
||||
import withSettingsActions from 'containers/Settings/withSettingsActions';
|
||||
import withPaymentReceivesActions from './withPaymentReceivesActions';
|
||||
import withCustomersActions from 'containers/Customers/withCustomersActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Payment receive form page.
|
||||
*/
|
||||
function PaymentReceiveFormPage({
|
||||
// #withDashboardAction
|
||||
changePageTitle,
|
||||
|
||||
// #withAccountsActions
|
||||
requestFetchAccounts,
|
||||
|
||||
// #withSettingsActions
|
||||
requestFetchOptions,
|
||||
|
||||
// #withPaymentReceivesActions
|
||||
requestFetchPaymentReceive,
|
||||
|
||||
// #withCustomersActions
|
||||
requestFetchCustomers,
|
||||
|
||||
// #withDashboardActions
|
||||
setSidebarShrink,
|
||||
resetSidebarPreviousExpand,
|
||||
setDashboardBackLink,
|
||||
}) {
|
||||
const { id: paymentReceiveId } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
// Shrink the sidebar by foce.
|
||||
setSidebarShrink();
|
||||
// Show the back link on dashboard topbar.
|
||||
setDashboardBackLink(true);
|
||||
|
||||
return () => {
|
||||
// Reset the sidebar to the previous status.
|
||||
resetSidebarPreviousExpand();
|
||||
// Hide the back link on dashboard topbar.
|
||||
setDashboardBackLink(false);
|
||||
};
|
||||
}, [resetSidebarPreviousExpand, setSidebarShrink, setDashboardBackLink]);
|
||||
|
||||
// Fetches payment recevie details.
|
||||
const fetchPaymentReceive = useQuery(
|
||||
['payment-receive', paymentReceiveId],
|
||||
(key, _id) => requestFetchPaymentReceive(_id),
|
||||
{ enabled: paymentReceiveId },
|
||||
);
|
||||
|
||||
// Handle fetch accounts data.
|
||||
const fetchAccounts = useQuery('accounts-list', (key) =>
|
||||
requestFetchAccounts(),
|
||||
);
|
||||
|
||||
// Fetch payment made settings.
|
||||
const fetchSettings = useQuery(['settings'], () => requestFetchOptions({}));
|
||||
|
||||
// Fetches customers list.
|
||||
const fetchCustomers = useQuery(['customers-list'], () =>
|
||||
requestFetchCustomers(),
|
||||
);
|
||||
|
||||
return (
|
||||
<DashboardInsider
|
||||
loading={
|
||||
fetchPaymentReceive.isFetching ||
|
||||
fetchAccounts.isFetching ||
|
||||
// fetchSettings.isFetching ||
|
||||
fetchCustomers.isFetching
|
||||
}
|
||||
name={'payment-receive-form'}
|
||||
>
|
||||
<PaymentReceiveForm paymentReceiveId={paymentReceiveId} />
|
||||
</DashboardInsider>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withDashboardActions,
|
||||
withAccountsActions,
|
||||
withSettingsActions,
|
||||
withPaymentReceivesActions,
|
||||
withCustomersActions,
|
||||
)(PaymentReceiveFormPage);
|
||||
@@ -10,52 +10,51 @@ import {
|
||||
MenuItem,
|
||||
} from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import classNames from 'classnames';
|
||||
import { useFormikContext } from 'formik';
|
||||
import { CLASSES } from 'common/classes';
|
||||
|
||||
import { saveInvoke } from 'utils';
|
||||
import { Icon } from 'components';
|
||||
import { usePaymentReceiveFormContext } from './PaymentReceiveFormProvider';
|
||||
|
||||
/**
|
||||
* Payment receive floating actions bar.
|
||||
*/
|
||||
export default function PaymentReceiveFormFloatingActions({
|
||||
isSubmitting,
|
||||
onSubmitClick,
|
||||
onCancelClick,
|
||||
onClearClick,
|
||||
onSubmitForm,
|
||||
paymentReceiveId,
|
||||
}) {
|
||||
export default function PaymentReceiveFormFloatingActions() {
|
||||
|
||||
// Payment receive form context.
|
||||
const { setSubmitPayload, isNewMode } = usePaymentReceiveFormContext();
|
||||
|
||||
// Formik form context.
|
||||
const { isSubmitting } = useFormikContext();
|
||||
|
||||
// History context.
|
||||
const history = useHistory();
|
||||
|
||||
// Handle submit button click.
|
||||
const handleSubmitBtnClick = (event) => {
|
||||
saveInvoke(onSubmitClick, event, {
|
||||
redirect: true,
|
||||
});
|
||||
setSubmitPayload({ redirect: true, });
|
||||
};
|
||||
|
||||
// Handle clear button click.
|
||||
const handleClearBtnClick = (event) => {
|
||||
onClearClick && onClearClick(event);
|
||||
|
||||
};
|
||||
|
||||
// Handle cancel button click.
|
||||
const handleCancelBtnClick = (event) => {
|
||||
onCancelClick && onCancelClick(event);
|
||||
saveInvoke(onCancelClick, event);
|
||||
history.goBack();
|
||||
};
|
||||
|
||||
// Handle submit & new button click.
|
||||
const handleSubmitAndNewClick = (event) => {
|
||||
onSubmitForm();
|
||||
saveInvoke(onSubmitClick, event, {
|
||||
redirect: false,
|
||||
resetForm: true,
|
||||
});
|
||||
setSubmitPayload({ redirect: false, resetForm: true, });
|
||||
};
|
||||
|
||||
// Handle submit & continue editing button click.
|
||||
const handleSubmitContinueEditingBtnClick = (event) => {
|
||||
onSubmitForm();
|
||||
saveInvoke(onSubmitClick, event, {
|
||||
redirect: false,
|
||||
publish: true,
|
||||
});
|
||||
setSubmitPayload({ redirect: false, publish: true });
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -67,7 +66,7 @@ export default function PaymentReceiveFormFloatingActions({
|
||||
intent={Intent.PRIMARY}
|
||||
type="submit"
|
||||
onClick={handleSubmitBtnClick}
|
||||
text={paymentReceiveId ? <T id={'edit'} /> : <T id={'save'} />}
|
||||
text={!isNewMode ? <T id={'edit'} /> : <T id={'save'} />}
|
||||
/>
|
||||
<Popover
|
||||
content={
|
||||
@@ -93,13 +92,15 @@ export default function PaymentReceiveFormFloatingActions({
|
||||
/>
|
||||
</Popover>
|
||||
</ButtonGroup>
|
||||
|
||||
{/* ----------- Clear & Reset----------- */}
|
||||
<Button
|
||||
className={'ml1'}
|
||||
disabled={isSubmitting}
|
||||
onClick={handleClearBtnClick}
|
||||
text={paymentReceiveId ? <T id={'reset'} /> : <T id={'clear'} />}
|
||||
text={!isNewMode ? <T id={'reset'} /> : <T id={'clear'} />}
|
||||
/>
|
||||
|
||||
{/* ----------- Cancel ----------- */}
|
||||
<Button
|
||||
className={'ml1'}
|
||||
@@ -108,4 +109,4 @@ export default function PaymentReceiveFormFloatingActions({
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { Formik, Form } from 'formik';
|
||||
|
||||
import { useIntl } from 'react-intl';
|
||||
import { pick, sumBy, omit, isEmpty } from 'lodash';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import classNames from 'classnames';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import { CLASSES } from 'common/classes';
|
||||
import PaymentReceiveHeader from './PaymentReceiveFormHeader';
|
||||
// import PaymentReceiveItemsTable from './PaymentReceiveItemsTable';
|
||||
import PaymentReceiveFloatingActions from './PaymentReceiveFloatingActions';
|
||||
import PaymentReceiveFormFooter from './PaymentReceiveFormFooter';
|
||||
|
||||
import withSettings from 'containers/Settings/withSettings';
|
||||
import {
|
||||
EditPaymentReceiveFormSchema,
|
||||
CreatePaymentReceiveFormSchema,
|
||||
} from './PaymentReceiveForm.schema';
|
||||
import { AppToaster } from 'components';
|
||||
import { compose } from 'utils';
|
||||
|
||||
import { usePaymentReceiveFormContext } from './PaymentReceiveFormProvider';
|
||||
import {
|
||||
defaultPaymentReceive,
|
||||
defaultPaymentReceiveEntry,
|
||||
transformToEditForm,
|
||||
} from './utils';
|
||||
|
||||
import 'style/pages/PaymentReceive/PageForm.scss';
|
||||
|
||||
/**
|
||||
* Payment Receive form.
|
||||
*/
|
||||
function PaymentReceiveForm({
|
||||
// #withSettings
|
||||
paymentReceiveNextNumber,
|
||||
paymentReceiveNumberPrefix,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
// Payment receive form context.
|
||||
const {
|
||||
isNewMode,
|
||||
paymentReceive,
|
||||
paymentReceiveId,
|
||||
submitPayload,
|
||||
editPaymentReceiveMutate,
|
||||
createPaymentReceiveMutate,
|
||||
} = usePaymentReceiveFormContext();
|
||||
|
||||
// Payment receive number.
|
||||
const paymentReceiveNumber = paymentReceiveNumberPrefix
|
||||
? `${paymentReceiveNumberPrefix}-${paymentReceiveNextNumber}`
|
||||
: paymentReceiveNextNumber;
|
||||
|
||||
// Form validation schema.
|
||||
const validationSchema = isNewMode
|
||||
? CreatePaymentReceiveFormSchema
|
||||
: EditPaymentReceiveFormSchema;
|
||||
|
||||
// Form initial values.
|
||||
const initialValues = useMemo(
|
||||
() => ({
|
||||
...(!isEmpty(paymentReceive)
|
||||
? {
|
||||
...transformToEditForm(paymentReceive, []),
|
||||
}
|
||||
: {
|
||||
...paymentReceive,
|
||||
payment_receive_no: paymentReceiveNumber,
|
||||
}),
|
||||
}),
|
||||
[paymentReceive, paymentReceiveNumber],
|
||||
);
|
||||
|
||||
// Handle form submit.
|
||||
const handleSubmitForm = (
|
||||
values,
|
||||
{ setSubmitting, resetForm, setFieldError },
|
||||
) => {
|
||||
setSubmitting(true);
|
||||
|
||||
// Filters entries that have no `invoice_id` and `payment_amount`.
|
||||
const entries = values.entries
|
||||
.filter((entry) => entry.invoice_id && entry.payment_amount)
|
||||
.map((entry) => ({
|
||||
...omit(entry, ['due_amount']),
|
||||
}));
|
||||
|
||||
// Calculates the total payment amount of entries.
|
||||
const totalPaymentAmount = sumBy(entries, 'payment_amount');
|
||||
|
||||
if (totalPaymentAmount <= 0) {
|
||||
AppToaster.show({
|
||||
message: formatMessage({
|
||||
id: 'you_cannot_make_payment_with_zero_total_amount',
|
||||
}),
|
||||
intent: Intent.DANGER,
|
||||
});
|
||||
setSubmitting(false);
|
||||
return;
|
||||
}
|
||||
const form = { ...values, entries };
|
||||
|
||||
// Handle request response success.
|
||||
const onSaved = (response) => {
|
||||
AppToaster.show({
|
||||
message: formatMessage({
|
||||
id: paymentReceiveId
|
||||
? 'the_payment_receive_transaction_has_been_edited'
|
||||
: 'the_payment_receive_transaction_has_been_created',
|
||||
}),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
setSubmitting(false);
|
||||
|
||||
if (submitPayload.redirect) {
|
||||
history.push('/payment-receives');
|
||||
}
|
||||
if (submitPayload.resetForm) {
|
||||
resetForm();
|
||||
}
|
||||
};
|
||||
// Handle request response errors.
|
||||
const onError = (errors) => {
|
||||
const getError = (errorType) => errors.find((e) => e.type === errorType);
|
||||
|
||||
if (getError('PAYMENT_RECEIVE_NO_EXISTS')) {
|
||||
setFieldError(
|
||||
'payment_receive_no',
|
||||
formatMessage({ id: 'payment_number_is_not_unique' }),
|
||||
);
|
||||
}
|
||||
setSubmitting(false);
|
||||
};
|
||||
|
||||
if (paymentReceiveId) {
|
||||
editPaymentReceiveMutate([paymentReceiveId, form])
|
||||
.then(onSaved)
|
||||
.catch(onError);
|
||||
} else {
|
||||
createPaymentReceiveMutate(form).then(onSaved).catch(onError);
|
||||
}
|
||||
};
|
||||
|
||||
const transformDataTableToEntries = (dataTable) => {
|
||||
return dataTable.map((data) => ({
|
||||
...pick(data, Object.keys(defaultPaymentReceiveEntry)),
|
||||
}));
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
CLASSES.PAGE_FORM,
|
||||
CLASSES.PAGE_FORM_STRIP_STYLE,
|
||||
CLASSES.PAGE_FORM_PAYMENT_RECEIVE,
|
||||
)}
|
||||
>
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
onSubmit={handleSubmitForm}
|
||||
validationSchema={validationSchema}
|
||||
>
|
||||
<Form>
|
||||
<PaymentReceiveHeader />
|
||||
<PaymentReceiveFormFooter />
|
||||
<PaymentReceiveFloatingActions />
|
||||
</Form>
|
||||
</Formik>
|
||||
|
||||
{/* <form onSubmit={handleSubmit}> */}
|
||||
{/* <PaymentReceiveHeader
|
||||
errors={errors}
|
||||
touched={touched}
|
||||
setFieldValue={setFieldValue}
|
||||
getFieldProps={getFieldProps}
|
||||
values={values}
|
||||
paymentReceiveId={paymentReceiveId}
|
||||
customerId={values.customer_id}
|
||||
onFullAmountChanged={handleFullAmountChange}
|
||||
receivableFullAmount={receivableFullAmount}
|
||||
amountReceived={fullAmountReceived}
|
||||
onPaymentReceiveNumberChanged={handlePaymentReceiveNumberChanged}
|
||||
/>
|
||||
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
|
||||
<PaymentReceiveItemsTable
|
||||
paymentReceiveId={paymentReceiveId}
|
||||
customerId={values.customer_id}
|
||||
fullAmount={fullAmount}
|
||||
onUpdateData={handleUpdataData}
|
||||
paymentReceiveEntries={localPaymentEntries}
|
||||
errors={errors?.entries}
|
||||
onClickClearAllLines={handleClearAllLines}
|
||||
onFetchEntriesSuccess={handleFetchEntriesSuccess}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<PaymentReceiveFloatingActions
|
||||
isSubmitting={isSubmitting}
|
||||
paymentReceiveId={paymentReceiveId}
|
||||
onClearClick={handleClearBtnClick}
|
||||
onSubmitClick={handleSubmitClick}
|
||||
onCancelClick={handleCancelClick}
|
||||
onSubmitForm={submitForm}
|
||||
/> */}
|
||||
{/* </form> */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withSettings(({ paymentReceiveSettings }) => ({
|
||||
paymentReceiveNextNumber: paymentReceiveSettings?.nextNumber,
|
||||
paymentReceiveNumberPrefix: paymentReceiveSettings?.numberPrefix,
|
||||
})),
|
||||
)(PaymentReceiveForm);
|
||||
@@ -2,31 +2,31 @@ import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { FormGroup, TextArea } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import { FastField } from 'formik';
|
||||
import { Row, Col } from 'components';
|
||||
import { CLASSES } from 'common/classes';
|
||||
|
||||
/**
|
||||
* Payment receive form footer.
|
||||
*/
|
||||
export default function PaymentReceiveFormFooter({
|
||||
getFieldProps,
|
||||
}) {
|
||||
export default function PaymentReceiveFormFooter({ getFieldProps }) {
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
||||
<Row>
|
||||
<Col md={8}>
|
||||
{/* --------- Statement --------- */}
|
||||
<FormGroup
|
||||
label={<T id={'statement'} />}
|
||||
className={'form-group--statement'}
|
||||
>
|
||||
<TextArea
|
||||
growVertically={true}
|
||||
{...getFieldProps('description')}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FastField name={'statement'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'statement'} />}
|
||||
className={'form-group--statement'}
|
||||
>
|
||||
<TextArea growVertically={true} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { sumBy } from 'lodash';
|
||||
import { useFormikContext } from 'formik';
|
||||
import classNames from 'classnames';
|
||||
import { Money } from 'components';
|
||||
|
||||
import { CLASSES } from 'common/classes';
|
||||
import PaymentReceiveHeaderFields from './PaymentReceiveHeaderFields';
|
||||
import withSettings from 'containers/Settings/withSettings';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Payment receive form header.
|
||||
*/
|
||||
function PaymentReceiveFormHeader({
|
||||
// #withSettings
|
||||
baseCurrency,
|
||||
}) {
|
||||
// Formik form context.
|
||||
const { values } = useFormikContext();
|
||||
|
||||
// Calculates the total receivable amount from due amount.
|
||||
const receivableFullAmount = useMemo(
|
||||
() => sumBy(values.entries, 'due_amount'),
|
||||
[values.entries],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
|
||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_PRIMARY)}>
|
||||
<PaymentReceiveHeaderFields />
|
||||
|
||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_BIG_NUMBERS)}>
|
||||
<div class="big-amount">
|
||||
<span class="big-amount__label">Amount Received</span>
|
||||
<h1 class="big-amount__number">
|
||||
<Money amount={receivableFullAmount} currency={baseCurrency} />
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withSettings(({ organizationSettings }) => ({
|
||||
baseCurrency: organizationSettings?.baseCurrency,
|
||||
})),
|
||||
)(PaymentReceiveFormHeader);
|
||||
@@ -0,0 +1,18 @@
|
||||
import React from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
import { PaymentReceiveFormProvider } from './PaymentReceiveFormProvider';
|
||||
import PaymentReceiveForm from './PaymentReceiveForm';
|
||||
|
||||
/**
|
||||
* Payment receive form page.
|
||||
*/
|
||||
export default function PaymentReceiveFormPage() {
|
||||
const { id: paymentReceiveId } = useParams();
|
||||
|
||||
return (
|
||||
<PaymentReceiveFormProvider paymentReceiveId={paymentReceiveId}>
|
||||
<PaymentReceiveForm paymentReceiveId={paymentReceiveId} />
|
||||
</PaymentReceiveFormProvider>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import React, { createContext, useContext } from 'react';
|
||||
import { DashboardInsider } from 'components';
|
||||
import {
|
||||
useSettings,
|
||||
usePaymentReceive,
|
||||
useAccounts,
|
||||
useCustomers,
|
||||
useCreatePaymentReceive,
|
||||
useEditPaymentReceive
|
||||
} from 'hooks/query';
|
||||
|
||||
// Payment receive form context.
|
||||
const PaymentReceiveFormContext = createContext();
|
||||
|
||||
/**
|
||||
* Payment receive form provider.
|
||||
*/
|
||||
function PaymentReceiveFormProvider({ paymentReceiveId, ...props }) {
|
||||
// Fetches payment recevie details.
|
||||
const {
|
||||
data: paymentReceive,
|
||||
isLoading: isPaymentLoading,
|
||||
isFetching: isPaymentFetching,
|
||||
} = usePaymentReceive(paymentReceiveId, {
|
||||
enabled: !!paymentReceiveId,
|
||||
});
|
||||
|
||||
// Handle fetch accounts data.
|
||||
const { data: accounts, isFetching: isAccountsFetching } = useAccounts();
|
||||
|
||||
// Fetch payment made settings.
|
||||
const fetchSettings = useSettings();
|
||||
|
||||
// Fetches customers list.
|
||||
const {
|
||||
data: { customers },
|
||||
isFetching: isCustomersFetching,
|
||||
} = useCustomers();
|
||||
|
||||
const [submitPayload, setSubmitPayload] = React.useState({});
|
||||
const isNewMode = !paymentReceiveId;
|
||||
|
||||
// Create and edit payment receive mutations.
|
||||
const { mutateAsync: editPaymentReceiveMutate } = useEditPaymentReceive();
|
||||
const { mutateAsync: createPaymentReceiveMutate } = useCreatePaymentReceive();
|
||||
|
||||
// Provider payload.
|
||||
const provider = {
|
||||
paymentReceive,
|
||||
accounts,
|
||||
customers,
|
||||
|
||||
isPaymentLoading,
|
||||
isPaymentFetching,
|
||||
isAccountsFetching,
|
||||
isCustomersFetching,
|
||||
|
||||
submitPayload,
|
||||
setSubmitPayload,
|
||||
isNewMode,
|
||||
|
||||
editPaymentReceiveMutate,
|
||||
createPaymentReceiveMutate
|
||||
};
|
||||
|
||||
return (
|
||||
<DashboardInsider
|
||||
loading={
|
||||
isPaymentLoading ||
|
||||
isAccountsFetching ||
|
||||
isCustomersFetching
|
||||
}
|
||||
name={'payment-receive-form'}
|
||||
>
|
||||
<PaymentReceiveFormContext.Provider value={provider} {...props} />
|
||||
</DashboardInsider>
|
||||
);
|
||||
}
|
||||
|
||||
const usePaymentReceiveFormContext = () =>
|
||||
useContext(PaymentReceiveFormContext);
|
||||
|
||||
export { PaymentReceiveFormProvider, usePaymentReceiveFormContext };
|
||||
@@ -0,0 +1,223 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import {
|
||||
FormGroup,
|
||||
InputGroup,
|
||||
Position,
|
||||
ControlGroup,
|
||||
} from '@blueprintjs/core';
|
||||
import { DateInput } from '@blueprintjs/datetime';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import { FastField, useFormikContext } from 'formik';
|
||||
import { sumBy } from 'lodash';
|
||||
|
||||
import { CLASSES } from 'common/classes';
|
||||
import classNames from 'classnames';
|
||||
import { momentFormatter, tansformDateValue, inputIntent } from 'utils';
|
||||
import {
|
||||
AccountsSelectList,
|
||||
ContactSelecetList,
|
||||
ErrorMessage,
|
||||
FieldRequiredHint,
|
||||
Icon,
|
||||
InputPrependButton,
|
||||
MoneyInputGroup,
|
||||
InputPrependText,
|
||||
Hint,
|
||||
Money,
|
||||
} from 'components';
|
||||
import { usePaymentReceiveFormContext } from './PaymentReceiveFormProvider';
|
||||
|
||||
import withSettings from 'containers/Settings/withSettings';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Payment receive header fields.
|
||||
*/
|
||||
function PaymentReceiveHeaderFields({ baseCurrency }) {
|
||||
// Payment receive form context.
|
||||
const { customers, accounts, isNewMode } = usePaymentReceiveFormContext();
|
||||
|
||||
// Formik form context.
|
||||
const { values } = useFormikContext();
|
||||
|
||||
const fullAmountReceived = useMemo(
|
||||
() => sumBy(values.entries, 'payment_amount'),
|
||||
[values.entries],
|
||||
);
|
||||
|
||||
const handleReceiveFullAmountClick = () => {};
|
||||
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
|
||||
{/* ------------- Customer name ------------- */}
|
||||
<FastField name={'customer_id'}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'customer_name'} />}
|
||||
inline={true}
|
||||
className={classNames('form-group--select-list', CLASSES.FILL)}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'customer_id'} />}
|
||||
>
|
||||
<ContactSelecetList
|
||||
contactsList={customers}
|
||||
selectedContactId={value}
|
||||
defaultSelectText={<T id={'select_customer_account'} />}
|
||||
onContactSelected={(value) => {
|
||||
form.setFieldValue('customer_id', value);
|
||||
}}
|
||||
popoverFill={true}
|
||||
disabled={!isNewMode}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
{/* ------------- Payment date ------------- */}
|
||||
<FastField name={'payment_date'}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'payment_date'} />}
|
||||
inline={true}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
className={classNames('form-group--select-list', CLASSES.FILL)}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="payment_date" />}
|
||||
>
|
||||
<DateInput
|
||||
{...momentFormatter('YYYY/MM/DD')}
|
||||
value={tansformDateValue(value)}
|
||||
// onChange={handleDateChange('payment_date')}
|
||||
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
||||
inputProps={{
|
||||
leftIcon: <Icon icon={'date-range'} />,
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
{/* ------------ Full amount ------------ */}
|
||||
<FastField name={'customer_name'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'full_amount'} />}
|
||||
inline={true}
|
||||
className={('form-group--full-amount', CLASSES.FILL)}
|
||||
intent={inputIntent({ error, touched })}
|
||||
labelInfo={<Hint />}
|
||||
helperText={<ErrorMessage name="full_amount" />}
|
||||
>
|
||||
<ControlGroup>
|
||||
<InputPrependText text={baseCurrency} />
|
||||
<MoneyInputGroup
|
||||
inputGroupProps={{
|
||||
medium: true,
|
||||
...field,
|
||||
}}
|
||||
/>
|
||||
</ControlGroup>
|
||||
|
||||
<a
|
||||
onClick={handleReceiveFullAmountClick}
|
||||
href="#"
|
||||
className={'receive-full-amount'}
|
||||
>
|
||||
Receive full amount (
|
||||
<Money amount={fullAmountReceived} currency={baseCurrency} />)
|
||||
</a>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
{/* ------------ Payment receive no. ------------ */}
|
||||
<FastField name={'payment_receive_no'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'payment_receive_no'} />}
|
||||
inline={true}
|
||||
className={('form-group--payment_receive_no', CLASSES.FILL)}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="payment_receive_no" />}
|
||||
>
|
||||
<ControlGroup fill={true}>
|
||||
<InputGroup
|
||||
intent={inputIntent({ error, touched })}
|
||||
minimal={true}
|
||||
{...field}
|
||||
/>
|
||||
<InputPrependButton
|
||||
buttonProps={{
|
||||
// onClick: handlePaymentReceiveNumberChange,
|
||||
icon: <Icon icon={'settings-18'} />,
|
||||
}}
|
||||
tooltip={true}
|
||||
tooltipProps={{
|
||||
content: 'Setting your auto-generated Payment Receive number',
|
||||
position: Position.BOTTOM_LEFT,
|
||||
}}
|
||||
/>
|
||||
</ControlGroup>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
{/* ------------ Deposit account ------------ */}
|
||||
<FastField name={'deposit_account_id'}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'deposit_to'} />}
|
||||
className={classNames(
|
||||
'form-group--deposit_account_id',
|
||||
'form-group--select-list',
|
||||
CLASSES.FILL,
|
||||
)}
|
||||
inline={true}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'deposit_account_id'} />}
|
||||
>
|
||||
<AccountsSelectList
|
||||
accounts={accounts}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
onAccountSelected={(account) => {
|
||||
form.setFieldValue('deposit_account_id', account.id);
|
||||
}}
|
||||
defaultSelectText={<T id={'select_deposit_account'} />}
|
||||
selectedAccountId={value}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
{/* ------------ Reference No. ------------ */}
|
||||
<FastField name={'customer_name'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'reference'} />}
|
||||
inline={true}
|
||||
className={classNames('form-group--reference', CLASSES.FILL)}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="reference" />}
|
||||
>
|
||||
<InputGroup
|
||||
intent={inputIntent({ error, touched })}
|
||||
minimal={true}
|
||||
{...field}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withSettings(({ organizationSettings }) => ({
|
||||
baseCurrency: organizationSettings?.baseCurrency,
|
||||
})),
|
||||
withDialogActions,
|
||||
)(PaymentReceiveHeaderFields);
|
||||
@@ -0,0 +1,33 @@
|
||||
import moment from 'moment';
|
||||
import { transformToForm } from 'utils';
|
||||
|
||||
// Default payment receive entry.
|
||||
export const defaultPaymentReceiveEntry = {
|
||||
id: '',
|
||||
payment_amount: '',
|
||||
invoice_id: '',
|
||||
due_amount: '',
|
||||
};
|
||||
|
||||
// Form initial values.
|
||||
export const defaultPaymentReceive = {
|
||||
customer_id: '',
|
||||
deposit_account_id: '',
|
||||
payment_date: moment(new Date()).format('YYYY-MM-DD'),
|
||||
reference_no: '',
|
||||
payment_receive_no: '',
|
||||
description: '',
|
||||
full_amount: '',
|
||||
entries: [],
|
||||
};
|
||||
|
||||
export const transformToEditForm = (paymentReceive, paymentReceiveEntries) => {
|
||||
return {
|
||||
...transformToForm(paymentReceive, defaultPaymentReceive),
|
||||
entries: [
|
||||
...paymentReceiveEntries.map((paymentReceiveEntry) => ({
|
||||
...transformToForm(paymentReceiveEntry, defaultPaymentReceiveEntry),
|
||||
})),
|
||||
],
|
||||
};
|
||||
};
|
||||
@@ -54,10 +54,11 @@ export function useEditPaymentMade(props) {
|
||||
const client = useQueryClient();
|
||||
|
||||
return useMutation(
|
||||
(id, values) => ApiService.post(`purchases/bill_payments/${id}`, values),
|
||||
([id, values]) => ApiService.post(`purchases/bill_payments/${id}`, values),
|
||||
{
|
||||
onSuccess: () => {
|
||||
onSuccess: (res, [id, values]) => {
|
||||
client.invalidateQueries('PAYMENT_MADES');
|
||||
client.invalidateQueries(['PAYMENT_MADE', id]);
|
||||
},
|
||||
...props,
|
||||
},
|
||||
@@ -71,10 +72,11 @@ export function useDeletePaymentMade(props) {
|
||||
const client = useQueryClient();
|
||||
|
||||
return useMutation(
|
||||
(id, values) => ApiService.delete(`purchases/bill_payments/${id}`, values),
|
||||
(id) => ApiService.delete(`purchases/bill_payments/${id}`),
|
||||
{
|
||||
onSuccess: () => {
|
||||
onSuccess: (res, id) => {
|
||||
client.invalidateQueries('PAYMENT_MADES');
|
||||
client.invalidateQueries(['PAYMENT_MADE', id]);
|
||||
},
|
||||
...props,
|
||||
},
|
||||
@@ -88,7 +90,14 @@ export function usePaymentMade(id, props) {
|
||||
const states = useQuery(
|
||||
['PAYMENT_MADE', id],
|
||||
() => ApiService.get(`purchases/bill_payments/${id}`),
|
||||
props,
|
||||
{
|
||||
select: res => ({
|
||||
paymentMade: res.data.bill_payment,
|
||||
payableBills: res.data.payable_bills,
|
||||
paymentBills: res.data.payment_bills,
|
||||
}),
|
||||
...props,
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
@@ -58,7 +58,7 @@ export function useEditPaymentReceive(props) {
|
||||
const client = useQueryClient();
|
||||
|
||||
return useMutation(
|
||||
(id, values) => ApiService.post(`sales/payment_receives/${id}`, values),
|
||||
([id, values]) => ApiService.post(`sales/payment_receives/${id}`, values),
|
||||
{
|
||||
onSuccess: () => {
|
||||
client.invalidateQueries('PAYMENT_RECEIVES');
|
||||
@@ -92,7 +92,10 @@ export function usePaymentReceive(id, props) {
|
||||
const states = useQuery(
|
||||
['PAYMENT_RECEIVE', id],
|
||||
() => ApiService.get(`sales/payment_receives/${id}`),
|
||||
props,
|
||||
{
|
||||
select: (res) => res.data.payment_receive,
|
||||
...props
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
@@ -40,7 +40,7 @@ export default [
|
||||
hotkey: 'ctrl+shift+m',
|
||||
pageTitle: formatMessage({ id: 'new_journal' }),
|
||||
sidebarShrink: true,
|
||||
backLink: true
|
||||
backLink: true,
|
||||
},
|
||||
{
|
||||
path: `/manual-journals/:id/edit`,
|
||||
@@ -50,7 +50,7 @@ export default [
|
||||
breadcrumb: 'Edit',
|
||||
pageTitle: formatMessage({ id: 'edit_journal' }),
|
||||
sidebarShrink: true,
|
||||
backLink: true
|
||||
backLink: true,
|
||||
},
|
||||
{
|
||||
path: `/manual-journals`,
|
||||
@@ -74,7 +74,7 @@ export default [
|
||||
component: lazy(() => import('containers/Items/ItemFormPage')),
|
||||
breadcrumb: 'Edit Item',
|
||||
pageTitle: formatMessage({ id: 'edit_item' }),
|
||||
backLink: true
|
||||
backLink: true,
|
||||
},
|
||||
{
|
||||
path: `/items/new`,
|
||||
@@ -82,7 +82,7 @@ export default [
|
||||
breadcrumb: 'New Item',
|
||||
hotkey: 'ctrl+shift+w',
|
||||
pageTitle: formatMessage({ id: 'new_item' }),
|
||||
backLink: true
|
||||
backLink: true,
|
||||
},
|
||||
{
|
||||
path: `/items`,
|
||||
@@ -178,7 +178,7 @@ export default [
|
||||
hotkey: 'ctrl+shift+x',
|
||||
pageTitle: formatMessage({ id: 'new_expense' }),
|
||||
sidebarShrink: true,
|
||||
backLink: true
|
||||
backLink: true,
|
||||
},
|
||||
{
|
||||
path: `/expenses/:id/edit`,
|
||||
@@ -188,7 +188,7 @@ export default [
|
||||
breadcrumb: 'Edit',
|
||||
pageTitle: formatMessage({ id: 'edit_expense' }),
|
||||
sidebarShrink: true,
|
||||
backLink: true
|
||||
backLink: true,
|
||||
},
|
||||
{
|
||||
path: `/expenses`,
|
||||
@@ -208,7 +208,7 @@ export default [
|
||||
),
|
||||
breadcrumb: 'Edit Customer',
|
||||
pageTitle: formatMessage({ id: 'edit_customer' }),
|
||||
backLink: true
|
||||
backLink: true,
|
||||
},
|
||||
{
|
||||
path: `/customers/new`,
|
||||
@@ -218,7 +218,7 @@ export default [
|
||||
breadcrumb: 'New Customer',
|
||||
hotkey: 'ctrl+shift+c',
|
||||
pageTitle: formatMessage({ id: 'new_customer' }),
|
||||
backLink: true
|
||||
backLink: true,
|
||||
},
|
||||
{
|
||||
path: `/customers`,
|
||||
@@ -238,6 +238,7 @@ export default [
|
||||
),
|
||||
breadcrumb: 'Edit Vendor',
|
||||
pageTitle: formatMessage({ id: 'edit_vendor' }),
|
||||
backLink: true,
|
||||
},
|
||||
{
|
||||
path: `/vendors/new`,
|
||||
@@ -247,6 +248,7 @@ export default [
|
||||
breadcrumb: 'New Vendor',
|
||||
hotkey: 'ctrl+shift+v',
|
||||
pageTitle: formatMessage({ id: 'new_vendor' }),
|
||||
backLink: true,
|
||||
},
|
||||
{
|
||||
path: `/vendors`,
|
||||
@@ -261,7 +263,9 @@ export default [
|
||||
// Estimates
|
||||
{
|
||||
path: `/estimates/:id/edit`,
|
||||
component: lazy(() => import('containers/Sales/Estimates/EstimateForm/EstimateFormPage')),
|
||||
component: lazy(() =>
|
||||
import('containers/Sales/Estimates/EstimateForm/EstimateFormPage'),
|
||||
),
|
||||
breadcrumb: 'Edit',
|
||||
pageTitle: formatMessage({ id: 'edit_estimate' }),
|
||||
backLink: true,
|
||||
@@ -269,7 +273,9 @@ export default [
|
||||
},
|
||||
{
|
||||
path: `/estimates/new`,
|
||||
component: lazy(() => import('containers/Sales/Estimates/EstimateForm/EstimateFormPage')),
|
||||
component: lazy(() =>
|
||||
import('containers/Sales/Estimates/EstimateForm/EstimateFormPage'),
|
||||
),
|
||||
breadcrumb: 'New Estimate',
|
||||
hotkey: 'ctrl+shift+e',
|
||||
pageTitle: formatMessage({ id: 'new_estimate' }),
|
||||
@@ -283,7 +289,7 @@ export default [
|
||||
),
|
||||
breadcrumb: 'Estimates List',
|
||||
hotkey: 'shift+e',
|
||||
pageTitle: formatMessage({ id: 'estimates_list' })
|
||||
pageTitle: formatMessage({ id: 'estimates_list' }),
|
||||
},
|
||||
|
||||
// Invoices.
|
||||
@@ -295,7 +301,7 @@ export default [
|
||||
breadcrumb: 'Edit',
|
||||
pageTitle: formatMessage({ id: 'edit_invoice' }),
|
||||
sidebarShrink: true,
|
||||
backLink: true
|
||||
backLink: true,
|
||||
},
|
||||
{
|
||||
path: `/invoices/new`,
|
||||
@@ -306,7 +312,7 @@ export default [
|
||||
hotkey: 'ctrl+shift+i',
|
||||
pageTitle: formatMessage({ id: 'new_invoice' }),
|
||||
sidebarShrink: true,
|
||||
backLink: true
|
||||
backLink: true,
|
||||
},
|
||||
{
|
||||
path: `/invoices`,
|
||||
@@ -347,31 +353,39 @@ export default [
|
||||
),
|
||||
breadcrumb: 'Receipts List',
|
||||
hotkey: 'shift+r',
|
||||
pageTitle: formatMessage({ id: 'receipts_list' })
|
||||
pageTitle: formatMessage({ id: 'receipts_list' }),
|
||||
},
|
||||
|
||||
// Payment receives
|
||||
// {
|
||||
// path: `/payment-receives/:id/edit`,
|
||||
// component: LazyLoader({
|
||||
// loader: () =>
|
||||
// import('containers/Sales/PaymentReceive/PaymentReceiveFormPage'),
|
||||
// }),
|
||||
// breadcrumb: 'Edit',
|
||||
// },
|
||||
// {
|
||||
// path: `/payment-receives/new`,
|
||||
// component: LazyLoader({
|
||||
// loader: () =>
|
||||
// import('containers/Sales/PaymentReceive/PaymentReceiveFormPage'),
|
||||
// }),
|
||||
// breadcrumb: 'New Payment Receive',
|
||||
// },
|
||||
{
|
||||
path: `/payment-receives/:id/edit`,
|
||||
component: lazy(() =>
|
||||
import(
|
||||
'containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveFormPage'
|
||||
),
|
||||
),
|
||||
breadcrumb: 'Edit',
|
||||
pageTitle: formatMessage({ id: 'edit_payment_receive' }),
|
||||
backLink: true,
|
||||
sidebarShrink: true,
|
||||
},
|
||||
{
|
||||
path: `/payment-receives/new`,
|
||||
component: lazy(() =>
|
||||
import(
|
||||
'containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveFormPage'
|
||||
),
|
||||
),
|
||||
breadcrumb: 'New Payment Receive',
|
||||
pageTitle: formatMessage({ id: 'new_payment_receive' }),
|
||||
backLink: true,
|
||||
sidebarShrink: true,
|
||||
},
|
||||
{
|
||||
path: `/payment-receives`,
|
||||
component: lazy(() =>
|
||||
import(
|
||||
'containers/Sales/PaymentReceive/PaymentsLanding/PaymentReceivesList'
|
||||
'containers/Sales/PaymentReceives/PaymentsLanding/PaymentReceivesList'
|
||||
),
|
||||
),
|
||||
breadcrumb: 'Payment Receives List',
|
||||
@@ -386,7 +400,7 @@ export default [
|
||||
breadcrumb: 'Edit',
|
||||
pageTitle: formatMessage({ id: 'edit_bill' }),
|
||||
sidebarShrink: true,
|
||||
backLink: true
|
||||
backLink: true,
|
||||
},
|
||||
{
|
||||
path: `/bills/new`,
|
||||
@@ -397,7 +411,7 @@ export default [
|
||||
hotkey: 'ctrl+shift+b',
|
||||
pageTitle: formatMessage({ id: 'new_bill' }),
|
||||
sidebarShrink: true,
|
||||
backLink: true
|
||||
backLink: true,
|
||||
},
|
||||
{
|
||||
path: `/bills`,
|
||||
@@ -406,7 +420,7 @@ export default [
|
||||
),
|
||||
breadcrumb: 'Bills List',
|
||||
hotkey: 'shift+b',
|
||||
pageTitle: formatMessage({ id: 'bills_list' })
|
||||
pageTitle: formatMessage({ id: 'bills_list' }),
|
||||
},
|
||||
|
||||
// Subscription billing.
|
||||
@@ -416,22 +430,30 @@ export default [
|
||||
breadcrumb: 'New Billing',
|
||||
},
|
||||
// Payment modes.
|
||||
// {
|
||||
// path: `/payment-mades/:id/edit`,
|
||||
// component: LazyLoader({
|
||||
// loader: () =>
|
||||
// import('containers/Purchases/PaymentMades/PaymentMadeFormPage'),
|
||||
// }),
|
||||
// breadcrumb: 'Edit',
|
||||
// },
|
||||
// {
|
||||
// path: `/payment-mades/new`,
|
||||
// component: LazyLoader({
|
||||
// loader: () =>
|
||||
// import('containers/Purchases/PaymentMades/PaymentMadeFormPage'),
|
||||
// }),
|
||||
// breadcrumb: 'New Payment Made',
|
||||
// },
|
||||
{
|
||||
path: `/payment-mades/:id/edit`,
|
||||
component: lazy(() =>
|
||||
import(
|
||||
'containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFormPage'
|
||||
),
|
||||
),
|
||||
breadcrumb: 'Edit',
|
||||
pageTitle: formatMessage({ id: 'edit_payment_made' }),
|
||||
sidebarShrink: true,
|
||||
backLink: true,
|
||||
},
|
||||
{
|
||||
path: `/payment-mades/new`,
|
||||
component: lazy(() =>
|
||||
import(
|
||||
'containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFormPage'
|
||||
),
|
||||
),
|
||||
breadcrumb: 'New Payment Made',
|
||||
pageTitle: formatMessage({ id: 'edit_payment_made' }),
|
||||
sidebarShrink: true,
|
||||
backLink: true,
|
||||
},
|
||||
{
|
||||
path: `/payment-mades`,
|
||||
component: lazy(() =>
|
||||
@@ -440,6 +462,6 @@ export default [
|
||||
),
|
||||
),
|
||||
breadcrumb: 'Payment Made List',
|
||||
pageTitle: formatMessage({ id: 'payment_made_list' })
|
||||
pageTitle: formatMessage({ id: 'payment_made_list' }),
|
||||
},
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user