diff --git a/client/package.json b/client/package.json
index b8345e974..e5d3a7e97 100644
--- a/client/package.json
+++ b/client/package.json
@@ -11,7 +11,6 @@
"@blueprintjs/timezone": "^3.6.2",
"@reduxjs/toolkit": "^1.2.5",
"@svgr/webpack": "4.3.3",
- "@syncfusion/ej2-react-grids": "^17.4.50",
"@tanem/react-nprogress": "^3.0.24",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.4.0",
@@ -57,7 +56,6 @@
"moment": "^2.24.0",
"node-sass": "^4.13.1",
"optimize-css-assets-webpack-plugin": "5.0.3",
- "p-progress": "^0.4.2",
"pnp-webpack-plugin": "1.6.0",
"postcss-flexbugs-fixes": "4.1.0",
"postcss-loader": "3.0.0",
@@ -72,8 +70,6 @@
"react-dom": "^16.12.0",
"react-dropzone": "^11.0.1",
"react-error-boundary": "^3.0.2",
- "react-grid-system": "^6.2.3",
- "react-hook-form": "^4.9.4",
"react-hotkeys-hook": "^3.0.3",
"react-intl": "^3.12.0",
"react-loadable": "^5.5.0",
@@ -98,12 +94,10 @@
"resolve-url-loader": "3.1.1",
"sass-loader": "8.0.2",
"semver": "6.3.0",
- "sortablejs": "^1.10.2",
"style-loader": "0.23.1",
"terser-webpack-plugin": "2.3.4",
"ts-pnp": "1.1.5",
"url-loader": "2.3.0",
- "use-named-routes": "^0.3.2",
"webpack": "4.41.5",
"webpack-dev-server": "3.10.2",
"webpack-manifest-plugin": "2.2.0",
diff --git a/client/src/components/ContactSelecetList.js b/client/src/components/ContactSelecetList.js
index ed10398c2..d19d4943c 100644
--- a/client/src/components/ContactSelecetList.js
+++ b/client/src/components/ContactSelecetList.js
@@ -14,6 +14,7 @@ export default function ContactSelecetList({
onContactSelected,
popoverFill = false,
disabled = false,
+ buttonProps
}) {
const contacts = useMemo(
() =>
@@ -94,6 +95,7 @@ export default function ContactSelecetList({
text={
selecetedContact ? selecetedContact.display_name : defaultSelectText
}
+ {...buttonProps}
/>
);
diff --git a/client/src/components/Sidebar/SidebarMenu.js b/client/src/components/Sidebar/SidebarMenu.js
index 5e5bf3f2c..31275978c 100644
--- a/client/src/components/Sidebar/SidebarMenu.js
+++ b/client/src/components/Sidebar/SidebarMenu.js
@@ -59,7 +59,7 @@ export default function SidebarMenu() {
disabled={item.disabled}
children={children}
dropdownType={item.dropdownType || 'collapse'}
- caretIconSize={15}
+ caretIconSize={16}
onClick={handleItemClick}
callapseActive={!!isActive}
itemClassName={classNames({
diff --git a/client/src/containers/Alerts/Invoices/InvoiceDeleteAlert.js b/client/src/containers/Alerts/Invoices/InvoiceDeleteAlert.js
index a93147048..e3a8e9969 100644
--- a/client/src/containers/Alerts/Invoices/InvoiceDeleteAlert.js
+++ b/client/src/containers/Alerts/Invoices/InvoiceDeleteAlert.js
@@ -47,7 +47,7 @@ function InvoiceDeleteAlert({
intent: Intent.SUCCESS,
});
})
- .catch((errors) => {
+ .catch(({ response: { data: { errors } } }) => {
handleDeleteErrors(errors);
})
.finally(() => {
diff --git a/client/src/containers/Alerts/PaymentMades/ChangingFullAmountAlert.js b/client/src/containers/Alerts/PaymentMades/ChangingFullAmountAlert.js
index 30f3c470f..94b86d689 100644
--- a/client/src/containers/Alerts/PaymentMades/ChangingFullAmountAlert.js
+++ b/client/src/containers/Alerts/PaymentMades/ChangingFullAmountAlert.js
@@ -1,15 +1,45 @@
import React from 'react';
-import { Alert } from '@blueprintjs/core';
+import { Intent, Alert } from '@blueprintjs/core';
+
+import withAlertActions from 'containers/Alert/withAlertActions';
+import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
+
+import { compose } from 'utils';
+import { saveInvoke } from '../../../utils';
+
+/**
+ * Changing full-amount alert in payment made form.
+ */
+function ChangingFullAmountAlert({
+ name,
+ onConfirm,
+
+ // #withAlertStoreConnect
+ isOpen,
+ payload: { },
+
+ // #withAlertActions
+ closeAlert,
+}) {
+ // Handle the alert cancel.
+ const handleCancel = () => {
+ closeAlert(name);
+ };
+
+ // Handle confirm delete manual journal.
+ const handleConfirm = (event) => {
+ closeAlert(name);
+ saveInvoke(onConfirm, event)
+ };
-function ChangingFullAmountAlert() {
return (
}
confirmButtonText={}
intent={Intent.DANGER}
- isOpen={amountChangeAlert}
- onCancel={handleCancelAmountChangeAlert}
- onConfirm={handleConfirmAmountChangeAlert}
+ isOpen={isOpen}
+ onCancel={handleCancel}
+ onConfirm={handleConfirm}
>
Changing full amount will change all credit and payment were applied, Is
@@ -18,3 +48,8 @@ function ChangingFullAmountAlert() {
);
}
+
+export default compose(
+ withAlertStoreConnect(),
+ withAlertActions,
+)(ChangingFullAmountAlert);
diff --git a/client/src/containers/Alerts/PaymentMades/ClearTransactionAlert.js b/client/src/containers/Alerts/PaymentMades/ClearTransactionAlert.js
index 535417f08..9adb0f22d 100644
--- a/client/src/containers/Alerts/PaymentMades/ClearTransactionAlert.js
+++ b/client/src/containers/Alerts/PaymentMades/ClearTransactionAlert.js
@@ -1,24 +1,54 @@
+import React from 'react';
+import { Intent, Alert } from '@blueprintjs/core';
+import { FormattedMessage as T } from 'react-intl';
+import withAlertActions from 'containers/Alert/withAlertActions';
+import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
+import { compose } from 'utils';
+/**
+ * Alert description.
+ */
+function ClearPaymentTransactionAlert({
+ name,
-export default function ClearTransactionAlert() {
+ // #withAlertStoreConnect
+ isOpen,
+ payload: { },
+ // #withAlertActions
+ closeAlert,
+}) {
+ // Handle the alert cancel.
+ const handleCancel = () => {
+ closeAlert(name);
+ };
+ // Handle confirm delete manual journal.
+ const handleConfirm = () => {
+
+ };
+ return (
+ }
+ confirmButtonText={}
+ icon="trash"
+ intent={Intent.DANGER}
+ isOpen={isOpen}
+ onCancel={handleCancel}
+ onConfirm={handleConfirm}
+ loading={false}
+ >
+
+
+
+
+ );
+}
- return (
- }
- confirmButtonText={}
- intent={Intent.WARNING}
- isOpen={clearFormAlert}
- onCancel={handleCancelClearFormAlert}
- onConfirm={handleConfirmCancelClearFormAlert}
- >
-
-
-
-
- )
-}
\ No newline at end of file
+export default compose(
+ withAlertStoreConnect(),
+ withAlertActions,
+)(ClearPaymentTransactionAlert);
\ No newline at end of file
diff --git a/client/src/containers/Alerts/PaymentMades/ClearningAllLinesAlert.js b/client/src/containers/Alerts/PaymentMades/ClearningAllLinesAlert.js
index c9ccc1a70..e9c3a670c 100644
--- a/client/src/containers/Alerts/PaymentMades/ClearningAllLinesAlert.js
+++ b/client/src/containers/Alerts/PaymentMades/ClearningAllLinesAlert.js
@@ -1,23 +1,54 @@
+import React from 'react';
+import { Intent, Alert } from '@blueprintjs/core';
+import { FormattedMessage as T } from 'react-intl';
+import withAlertActions from 'containers/Alert/withAlertActions';
+import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
+import { compose } from 'utils';
+/**
+ * Clearning all lines alert.
+ */
+function ClearAllLinesAlert({
+ name,
-function ClearningAllLinesAlert() {
+ // #withAlertStoreConnect
+ isOpen,
+ payload: {},
- return (
+ // #withAlertActions
+ closeAlert,
+}) {
- }
- confirmButtonText={}
- intent={Intent.DANGER}
- isOpen={clearLinesAlert}
- onCancel={handleCancelClearLines}
- onConfirm={handleConfirmClearLines}
- >
-
- Clearing the table lines will delete all credits and payments were
- applied. Is this okay?
-
-
- )
-}
\ No newline at end of file
+ // Handle the alert cancel.
+ const handleCancel = () => {
+ closeAlert(name);
+ };
+
+ // Handle confirm delete manual journal.
+ const handleConfirm = () => {};
+
+ return (
+ }
+ confirmButtonText={}
+ icon="trash"
+ intent={Intent.DANGER}
+ isOpen={isOpen}
+ onCancel={handleCancel}
+ onConfirm={handleConfirm}
+ loading={false}
+ >
+
+ Clearing the table lines will delete all credits and payments were
+ applied. Is this okay?
+
+
+ );
+}
+
+export default compose(
+ withAlertStoreConnect(),
+ withAlertActions,
+)(ClearAllLinesAlert);
diff --git a/client/src/containers/Alerts/PaymentReceives/ChangingFullAmountAlert.js b/client/src/containers/Alerts/PaymentReceives/ChangingFullAmountAlert.js
deleted file mode 100644
index 47f8676d4..000000000
--- a/client/src/containers/Alerts/PaymentReceives/ChangingFullAmountAlert.js
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-function ChangingFullAmountAlert() {
-
- return (
- }
- confirmButtonText={}
- intent={Intent.WARNING}
- isOpen={amountChangeAlert}
- onCancel={handleCancelAmountChangeAlert}
- onConfirm={handleConfirmAmountChangeAlert}
- >
-
-
-
-
- )
-}
\ No newline at end of file
diff --git a/client/src/containers/Alerts/PaymentReceives/ClearTransactionAlert.js b/client/src/containers/Alerts/PaymentReceives/ClearTransactionAlert.js
deleted file mode 100644
index a8873302f..000000000
--- a/client/src/containers/Alerts/PaymentReceives/ClearTransactionAlert.js
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-function ClearTransactionAlert() {
-
- return (
- }
- confirmButtonText={}
- intent={Intent.WARNING}
- isOpen={clearFormAlert}
- onCancel={handleCancelClearFormAlert}
- onConfirm={handleConfirmCancelClearFormAlert}
- >
-
-
-
-
- )
-}
\ No newline at end of file
diff --git a/client/src/containers/Alerts/PaymentReceives/ClearingAllLinesAlert.js b/client/src/containers/Alerts/PaymentReceives/ClearingAllLinesAlert.js
index 856a5aeb7..9143d98aa 100644
--- a/client/src/containers/Alerts/PaymentReceives/ClearingAllLinesAlert.js
+++ b/client/src/containers/Alerts/PaymentReceives/ClearingAllLinesAlert.js
@@ -1,20 +1,54 @@
+import React from 'react';
+import { Intent, Alert } from '@blueprintjs/core';
+import { FormattedMessage as T } from 'react-intl';
+import withAlertActions from 'containers/Alert/withAlertActions';
+import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
+import { saveInvoke, compose } from 'utils';
-function ClearingAllLinesAlert() {
+/**
+ * Clearning all lines alert.
+ */
+function ClearningAllLinesAlert({
+ name,
+ onConfirm,
- return (
- }
- confirmButtonText={}
- intent={Intent.WARNING}
- isOpen={clearLinesAlert}
- onCancel={handleCancelClearLines}
- onConfirm={handleConfirmClearLines}
- >
-
-
-
-
- )
-}
\ No newline at end of file
+ // #withAlertStoreConnect
+ isOpen,
+ payload: {},
+
+ // #withAlertActions
+ closeAlert,
+}) {
+ // Handle the alert cancel.
+ const handleCancel = () => {
+ closeAlert(name);
+ };
+
+ // Handle confirm delete manual journal.
+ const handleConfirm = (event) => {
+ closeAlert(name);
+ saveInvoke(onConfirm, event)
+ };
+
+ return (
+ }
+ confirmButtonText={}
+ intent={Intent.DANGER}
+ isOpen={isOpen}
+ onCancel={handleCancel}
+ onConfirm={handleConfirm}
+ >
+
+
+
+
+ );
+}
+
+export default compose(
+ withAlertStoreConnect(),
+ withAlertActions,
+)(ClearningAllLinesAlert);
diff --git a/client/src/containers/Items/ItemForm.schema.js b/client/src/containers/Items/ItemForm.schema.js
index 8eade519d..283934c4f 100644
--- a/client/src/containers/Items/ItemForm.schema.js
+++ b/client/src/containers/Items/ItemForm.schema.js
@@ -2,7 +2,6 @@ import * as Yup from 'yup';
import { defaultTo } from 'lodash';
import { formatMessage } from 'services/intl';
import { DATATYPES_LENGTH } from 'common/dataTypes';
-import { isBlank } from 'utils';
const Schema = Yup.object().shape({
active: Yup.boolean(),
@@ -20,6 +19,7 @@ const Schema = Yup.object().shape({
code: Yup.string().trim().min(0).max(DATATYPES_LENGTH.STRING),
cost_price: Yup.number()
.min(0)
+ .max(DATATYPES_LENGTH.DECIMAL_13_3)
.when(['purchasable'], {
is: true,
then: Yup.number()
@@ -29,6 +29,7 @@ const Schema = Yup.object().shape({
}),
sell_price: Yup.number()
.min(0)
+ .max(DATATYPES_LENGTH.DECIMAL_13_3)
.when(['sellable'], {
is: true,
then: Yup.number()
diff --git a/client/src/containers/Sales/Estimates/EstimatesLanding/EstimatesList.js b/client/src/containers/Sales/Estimates/EstimatesLanding/EstimatesList.js
index 4b3ac6e39..016216290 100644
--- a/client/src/containers/Sales/Estimates/EstimatesLanding/EstimatesList.js
+++ b/client/src/containers/Sales/Estimates/EstimatesLanding/EstimatesList.js
@@ -1,6 +1,8 @@
import React from 'react';
import { DashboardContentTable, DashboardPageContent } from 'components';
+import 'style/pages/SaleEstimate/List.scss';
+
import EstimatesActionsBar from './EstimatesActionsBar';
import EstimatesAlerts from '../EstimatesAlerts';
import EstimatesViewTabs from './EstimatesViewTabs';
diff --git a/client/src/containers/Sales/Estimates/EstimatesLanding/components.js b/client/src/containers/Sales/Estimates/EstimatesLanding/components.js
index 87f8c11ea..ff5bec06f 100644
--- a/client/src/containers/Sales/Estimates/EstimatesLanding/components.js
+++ b/client/src/containers/Sales/Estimates/EstimatesLanding/components.js
@@ -75,6 +75,7 @@ export function ActionsMenu({
}
text={formatMessage({ id: 'mark_as_rejected' })}
onClick={safeCallback(onReject, original)}
/>
diff --git a/client/src/containers/Sales/Invoices/InvoiceForm/InvoiceForm.js b/client/src/containers/Sales/Invoices/InvoiceForm/InvoiceForm.js
index 5d574c78f..1f4eaca01 100644
--- a/client/src/containers/Sales/Invoices/InvoiceForm/InvoiceForm.js
+++ b/client/src/containers/Sales/Invoices/InvoiceForm/InvoiceForm.js
@@ -122,7 +122,7 @@ function InvoiceForm({
};
// Handle the request error.
- const onError = (errors) => {
+ const onError = ({ response: { data: { errors } } }) => {
if (errors) {
handleErrors(errors, { setErrors });
}
diff --git a/client/src/containers/Sales/Invoices/InvoicesLanding/components.js b/client/src/containers/Sales/Invoices/InvoicesLanding/components.js
index 30465bebf..043b673f9 100644
--- a/client/src/containers/Sales/Invoices/InvoicesLanding/components.js
+++ b/client/src/containers/Sales/Invoices/InvoicesLanding/components.js
@@ -10,15 +10,19 @@ import {
Position,
Button
} from '@blueprintjs/core';
-import { Choose, If, Icon } from 'components';
import { FormattedMessage as T, useIntl } from 'react-intl';
import moment from 'moment';
+import { round } from 'lodash';
+import { Choose, If, Icon } from 'components';
import { Money, AppToaster } from 'components';
import { formatMessage } from 'services/intl';
import { safeCallback } from 'utils';
-const calculateStatus = (paymentAmount, balanceAmount) =>
- paymentAmount / balanceAmount;
+
+const calculateStatus = (paymentAmount, balanceAmount) => {
+ return round(paymentAmount / balanceAmount, 2);
+}
+
export const statusAccessor = (row) => {
return (
@@ -52,7 +56,7 @@ export const statusAccessor = (row) => {
@@ -110,6 +114,7 @@ export function ActionsMenu({
/>
}
text={formatMessage({ id: 'mark_as_delivered' })}
onClick={safeCallback(onDeliver, original)}
/>
@@ -157,13 +162,6 @@ export function useInvoicesTableColumns() {
width: 180,
className: 'customer_id',
},
- {
- id: 'invoice_no',
- Header: formatMessage({ id: 'invoice_no__' }),
- accessor: (row) => (row.invoice_no ? `#${row.invoice_no}` : null),
- width: 100,
- className: 'invoice_no',
- },
{
id: 'balance',
Header: formatMessage({ id: 'balance' }),
@@ -171,6 +169,13 @@ export function useInvoicesTableColumns() {
width: 110,
className: 'balance',
},
+ {
+ id: 'invoice_no',
+ Header: formatMessage({ id: 'invoice_no__' }),
+ accessor: (row) => (row.invoice_no ? `#${row.invoice_no}` : null),
+ width: 100,
+ className: 'invoice_no',
+ },
{
id: 'status',
Header: formatMessage({ id: 'status' }),
diff --git a/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveFloatingActions.js b/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveFloatingActions.js
index 24dcc0913..6cc28a56b 100644
--- a/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveFloatingActions.js
+++ b/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveFloatingActions.js
@@ -22,25 +22,23 @@ import { usePaymentReceiveFormContext } from './PaymentReceiveFormProvider';
* Payment receive floating actions bar.
*/
export default function PaymentReceiveFormFloatingActions() {
-
// Payment receive form context.
const { setSubmitPayload, isNewMode } = usePaymentReceiveFormContext();
// Formik form context.
- const { isSubmitting } = useFormikContext();
+ const { isSubmitting, submitForm } = useFormikContext();
// History context.
const history = useHistory();
// Handle submit button click.
const handleSubmitBtnClick = (event) => {
- setSubmitPayload({ redirect: true, });
+ setSubmitPayload({ redirect: true });
+ submitForm();
};
// Handle clear button click.
- const handleClearBtnClick = (event) => {
-
- };
+ const handleClearBtnClick = (event) => {};
// Handle cancel button click.
const handleCancelBtnClick = (event) => {
@@ -49,12 +47,14 @@ export default function PaymentReceiveFormFloatingActions() {
// Handle submit & new button click.
const handleSubmitAndNewClick = (event) => {
- setSubmitPayload({ redirect: false, resetForm: true, });
+ setSubmitPayload({ redirect: false, resetForm: true });
+ submitForm();
};
// Handle submit & continue editing button click.
const handleSubmitContinueEditingBtnClick = (event) => {
setSubmitPayload({ redirect: false, publish: true });
+ submitForm();
};
return (
@@ -63,6 +63,7 @@ export default function PaymentReceiveFormFloatingActions() {
);
-}
\ No newline at end of file
+}
diff --git a/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveForm.js b/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveForm.js
index 30cdb99dd..5a2341440 100644
--- a/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveForm.js
+++ b/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveForm.js
@@ -1,17 +1,18 @@
import React, { useMemo } from 'react';
import { Formik, Form } from 'formik';
-
import { useIntl } from 'react-intl';
-import { pick, sumBy, omit, isEmpty } from 'lodash';
+import { sumBy, pick, 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 PaymentReceiveFormBody from './PaymentReceiveFormBody';
import PaymentReceiveFloatingActions from './PaymentReceiveFloatingActions';
import PaymentReceiveFormFooter from './PaymentReceiveFormFooter';
+import PaymentReceiveFormAlerts from './PaymentReceiveFormAlerts';
+import { PaymentReceiveInnerProvider } from './PaymentReceiveInnerProvider';
import withSettings from 'containers/Settings/withSettings';
import {
@@ -24,7 +25,6 @@ import { compose } from 'utils';
import { usePaymentReceiveFormContext } from './PaymentReceiveFormProvider';
import {
defaultPaymentReceive,
- defaultPaymentReceiveEntry,
transformToEditForm,
} from './utils';
@@ -44,7 +44,8 @@ function PaymentReceiveForm({
// Payment receive form context.
const {
isNewMode,
- paymentReceive,
+ paymentReceiveEditPage,
+ paymentEntriesEditPage,
paymentReceiveId,
submitPayload,
editPaymentReceiveMutate,
@@ -56,24 +57,17 @@ function PaymentReceiveForm({
? `${paymentReceiveNumberPrefix}-${paymentReceiveNextNumber}`
: paymentReceiveNextNumber;
- // Form validation schema.
- const validationSchema = isNewMode
- ? CreatePaymentReceiveFormSchema
- : EditPaymentReceiveFormSchema;
-
// Form initial values.
const initialValues = useMemo(
() => ({
- ...(!isEmpty(paymentReceive)
- ? {
- ...transformToEditForm(paymentReceive, []),
- }
+ ...(!isEmpty(paymentReceiveEditPage)
+ ? transformToEditForm(paymentReceiveEditPage, paymentEntriesEditPage)
: {
- ...paymentReceive,
+ ...defaultPaymentReceive,
payment_receive_no: paymentReceiveNumber,
}),
}),
- [paymentReceive, paymentReceiveNumber],
+ [paymentReceiveEditPage, paymentReceiveNumber, paymentEntriesEditPage],
);
// Handle form submit.
@@ -87,7 +81,7 @@ function PaymentReceiveForm({
const entries = values.entries
.filter((entry) => entry.invoice_id && entry.payment_amount)
.map((entry) => ({
- ...omit(entry, ['due_amount']),
+ ...pick(entry, ['invoice_id', 'payment_amount']),
}));
// Calculates the total payment amount of entries.
@@ -125,7 +119,7 @@ function PaymentReceiveForm({
}
};
// Handle request response errors.
- const onError = (errors) => {
+ const onError = ({ response: { data: { errors } } }) => {
const getError = (errorType) => errors.find((e) => e.type === errorType);
if (getError('PAYMENT_RECEIVE_NO_EXISTS')) {
@@ -146,12 +140,6 @@ function PaymentReceiveForm({
}
};
- const transformDataTableToEntries = (dataTable) => {
- return dataTable.map((data) => ({
- ...pick(data, Object.keys(defaultPaymentReceiveEntry)),
- }));
- };
-
return (
);
}
diff --git a/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveFormAlerts.js b/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveFormAlerts.js
new file mode 100644
index 000000000..086b5d467
--- /dev/null
+++ b/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveFormAlerts.js
@@ -0,0 +1,26 @@
+import React from 'react';
+import { useFormikContext } from 'formik';
+import ClearingAllLinesAlert from 'containers/Alerts/PaymentReceives/ClearingAllLinesAlert';
+import { clearAllPaymentEntries } from './utils';
+
+/**
+ * Payment receive form alerts.
+ */
+export default function PaymentReceiveFormAlerts() {
+ const { values: { entries }, setFieldValue } = useFormikContext();
+
+ const handleClearingAllLines = () => {
+ const newEntries = clearAllPaymentEntries(entries);
+ setFieldValue('entries', newEntries);
+ setFieldValue('full_amount', '');
+ };
+
+ return (
+ <>
+
+ >
+ );
+}
diff --git a/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveFormBody.js b/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveFormBody.js
new file mode 100644
index 000000000..c64456d94
--- /dev/null
+++ b/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveFormBody.js
@@ -0,0 +1,25 @@
+import React from 'react';
+import { FastField } from 'formik';
+import PaymentReceiveItemsTable from './PaymentReceiveItemsTable';
+import classNames from 'classnames';
+import { CLASSES } from 'common/classes';
+
+/**
+ * Payment Receive form body.
+ */
+export default function PaymentReceiveFormBody() {
+ return (
+
+
+ {({ form, field: { value } }) => (
+ {
+ form.setFieldValue('entries', newEntries);
+ }}
+ />
+ )}
+
+
+ );
+}
diff --git a/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveFormHeader.js b/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveFormHeader.js
index 9f3a32591..ee2a95d4f 100644
--- a/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveFormHeader.js
+++ b/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveFormHeader.js
@@ -20,9 +20,9 @@ function PaymentReceiveFormHeader({
// Formik form context.
const { values } = useFormikContext();
- // Calculates the total receivable amount from due amount.
- const receivableFullAmount = useMemo(
- () => sumBy(values.entries, 'due_amount'),
+ // Calculates the total payment amount from due amount.
+ const paymentFullAmount = useMemo(
+ () => sumBy(values.entries, 'payment_amount'),
[values.entries],
);
@@ -35,7 +35,7 @@ function PaymentReceiveFormHeader({
Amount Received
-
+
diff --git a/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveFormProvider.js b/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveFormProvider.js
index a4c7cd8f3..ab609c975 100644
--- a/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveFormProvider.js
+++ b/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveFormProvider.js
@@ -2,12 +2,11 @@ import React, { createContext, useContext } from 'react';
import { DashboardInsider } from 'components';
import {
useSettings,
- usePaymentReceive,
+ usePaymentReceiveEditPage,
useAccounts,
useCustomers,
useCreatePaymentReceive,
useEditPaymentReceive,
- useDueInvoices,
} from 'hooks/query';
// Payment receive form context.
@@ -18,15 +17,19 @@ const PaymentReceiveFormContext = createContext();
*/
function PaymentReceiveFormProvider({ paymentReceiveId, ...props }) {
// Form state.
- const [paymentCustomerId, setPaymentCustomerId] = React.useState(null);
const [submitPayload, setSubmitPayload] = React.useState({});
// Fetches payment recevie details.
const {
- data: paymentReceive,
+ data: {
+ paymentReceive: paymentReceiveEditPage,
+ entries: paymentEntriesEditPage,
+ },
isLoading: isPaymentLoading,
isFetching: isPaymentFetching,
- } = usePaymentReceive(paymentReceiveId, { enabled: !!paymentReceiveId });
+ } = usePaymentReceiveEditPage(paymentReceiveId, {
+ enabled: !!paymentReceiveId,
+ });
// Handle fetch accounts data.
const { data: accounts, isFetching: isAccountsFetching } = useAccounts();
@@ -40,15 +43,6 @@ function PaymentReceiveFormProvider({ paymentReceiveId, ...props }) {
isFetching: isCustomersFetching,
} = useCustomers();
- // Fetches customer receivable invoices.
- const {
- data: dueInvoices,
- isLoading: isDueInvoicesLoading,
- isFetching: isDueInvoicesFetching,
- } = useDueInvoices(paymentCustomerId, {
- enabled: !!paymentCustomerId,
- });
-
// Detarmines whether the new mode.
const isNewMode = !paymentReceiveId;
@@ -58,24 +52,20 @@ function PaymentReceiveFormProvider({ paymentReceiveId, ...props }) {
// Provider payload.
const provider = {
- paymentReceive,
+ paymentReceiveId,
+ paymentReceiveEditPage,
+ paymentEntriesEditPage,
accounts,
customers,
- dueInvoices,
isPaymentLoading,
isPaymentFetching,
isAccountsFetching,
isCustomersFetching,
- isDueInvoicesLoading,
- isDueInvoicesFetching,
-
- paymentCustomerId,
- submitPayload,
isNewMode,
-
+
+ submitPayload,
setSubmitPayload,
- setPaymentCustomerId,
editPaymentReceiveMutate,
createPaymentReceiveMutate,
diff --git a/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveHeaderFields.js b/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveHeaderFields.js
index fca90d310..a055f72c5 100644
--- a/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveHeaderFields.js
+++ b/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveHeaderFields.js
@@ -4,19 +4,25 @@ import {
InputGroup,
Position,
ControlGroup,
+ Button,
} 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 { FastField, Field, useFormikContext, ErrorMessage } from 'formik';
+import { useAutofocus } from 'hooks';
import { CLASSES } from 'common/classes';
import classNames from 'classnames';
-import { momentFormatter, tansformDateValue, inputIntent } from 'utils';
+import {
+ compose,
+ safeSumBy,
+ momentFormatter,
+ tansformDateValue,
+ inputIntent,
+} from 'utils';
import {
AccountsSelectList,
ContactSelecetList,
- ErrorMessage,
FieldRequiredHint,
Icon,
InputPrependButton,
@@ -28,31 +34,44 @@ import {
import { usePaymentReceiveFormContext } from './PaymentReceiveFormProvider';
import withSettings from 'containers/Settings/withSettings';
-import withDialogActions from 'containers/Dialog/withDialogActions';
-import { compose } from 'utils';
+import { amountPaymentEntries, fullAmountPaymentEntries } from './utils';
+import { toSafeInteger } from 'lodash';
/**
* Payment receive header fields.
*/
function PaymentReceiveHeaderFields({ baseCurrency }) {
// Payment receive form context.
- const {
- customers,
- accounts,
- isNewMode,
- setPaymentCustomerId,
- } = usePaymentReceiveFormContext();
+ const { customers, accounts, isNewMode } = usePaymentReceiveFormContext();
// Formik form context.
- const { values } = useFormikContext();
+ const {
+ values: { entries },
+ setFieldValue,
+ } = useFormikContext();
- const fullAmountReceived = useMemo(
- () => sumBy(values.entries, 'payment_amount'),
- [values.entries],
- );
+ const customerFieldRef = useAutofocus();
- const handleReceiveFullAmountClick = () => {};
+ // Calculates the full-amount received.
+ const totalDueAmount = useMemo(() => safeSumBy(entries, 'due_amount'), [
+ entries,
+ ]);
+
+ // Handle receive full-amount link click.
+ const handleReceiveFullAmountClick = () => {
+ const newEntries = fullAmountPaymentEntries(entries);
+ const fullAmount = safeSumBy(newEntries, 'payment_amount');
+
+ setFieldValue('entries', newEntries);
+ setFieldValue('full_amount', fullAmount);
+ };
+
+ // Handles the full-amount field blur.
+ const onFullAmountBlur = (value) => {
+ const newEntries = amountPaymentEntries(toSafeInteger(value), entries);
+ setFieldValue('entries', newEntries);
+ };
return (
@@ -72,11 +91,14 @@ function PaymentReceiveHeaderFields({ baseCurrency }) {
selectedContactId={value}
defaultSelectText={
}
onContactSelected={(customer) => {
- form.setFieldValue('customer_id', customer);
- setPaymentCustomerId(customer.id);
+ form.setFieldValue('customer_id', customer.id);
+ form.setFieldValue('full_amount', '');
}}
popoverFill={true}
disabled={!isNewMode}
+ buttonProps={{
+ elementRef: (ref) => (customerFieldRef.current = ref),
+ }}
/>
)}
@@ -107,8 +129,12 @@ function PaymentReceiveHeaderFields({ baseCurrency }) {
{/* ------------ Full amount ------------ */}
-
- {({ form, field, meta: { error, touched } }) => (
+
+ {({
+ form: { setFieldValue },
+ field: { value, onChange },
+ meta: { error, touched },
+ }) => (
}
inline={true}
@@ -120,24 +146,26 @@ function PaymentReceiveHeaderFields({ baseCurrency }) {
{
+ setFieldValue('full_amount', value);
}}
+ onBlurValue={onFullAmountBlur}
/>
-
Receive full amount (
- )
-
+ )
+
)}
-
+
{/* ------------ Payment receive no. ------------ */}
@@ -200,7 +228,7 @@ function PaymentReceiveHeaderFields({ baseCurrency }) {
{/* ------------ Reference No. ------------ */}
-
+
{({ form, field, meta: { error, touched } }) => (
}
@@ -225,5 +253,4 @@ export default compose(
withSettings(({ organizationSettings }) => ({
baseCurrency: organizationSettings?.baseCurrency,
})),
- withDialogActions,
)(PaymentReceiveHeaderFields);
diff --git a/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveInnerProvider.js b/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveInnerProvider.js
new file mode 100644
index 000000000..c642911f6
--- /dev/null
+++ b/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveInnerProvider.js
@@ -0,0 +1,50 @@
+import React, { createContext, useContext, useEffect } from 'react';
+import { useFormikContext } from 'formik';
+import { useDueInvoices } from 'hooks/query';
+import { transformInvoicesNewPageEntries } from './utils';
+import { usePaymentReceiveFormContext } from './PaymentReceiveFormProvider';
+import { isEmpty } from 'lodash';
+
+const PaymentReceiveInnerContext = createContext();
+
+/**
+ * Payment receive inner form provider.
+ */
+function PaymentReceiveInnerProvider({ ...props }) {
+ const { isNewMode } = usePaymentReceiveFormContext();
+
+ // Formik context.
+ const {
+ values: { customer_id: customerId },
+ setFieldValue,
+ } = useFormikContext();
+
+ // Fetches customer receivable invoices.
+ const {
+ data: dueInvoices,
+ isLoading: isDueInvoicesLoading,
+ isFetching: isDueInvoicesFetching,
+ } = useDueInvoices(customerId, {
+ enabled: !!customerId && isNewMode,
+ });
+
+ useEffect(() => {
+ if (!isDueInvoicesFetching && !isEmpty(dueInvoices)) {
+ setFieldValue('entries', transformInvoicesNewPageEntries(dueInvoices));
+ }
+ }, [isDueInvoicesFetching, dueInvoices, setFieldValue]);
+
+ // Provider payload.
+ const provider = {
+ dueInvoices,
+ isDueInvoicesLoading,
+ isDueInvoicesFetching,
+ };
+
+ return ;
+}
+
+const usePaymentReceiveInnerContext = () =>
+ useContext(PaymentReceiveInnerContext);
+
+export { PaymentReceiveInnerProvider, usePaymentReceiveInnerContext };
diff --git a/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveItemsTable.js b/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveItemsTable.js
index 182fd64e8..29fca29ee 100644
--- a/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveItemsTable.js
+++ b/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveItemsTable.js
@@ -1,4 +1,4 @@
-import React, { useMemo, useCallback } from 'react';
+import React, { useCallback } from 'react';
import { Button } from '@blueprintjs/core';
import { FormattedMessage as T } from 'react-intl';
import { CloudLoadingIndicator } from 'components';
@@ -8,43 +8,50 @@ import { CLASSES } from 'common/classes';
import { usePaymentReceiveFormContext } from './PaymentReceiveFormProvider';
import { DataTableEditable } from 'components';
import { usePaymentReceiveEntriesColumns } from './components';
+import { compose, updateTableRow, safeSumBy } from 'utils';
+import withAlertActions from 'containers/Alert/withAlertActions';
+
/**
* Payment receive items table.
*/
-export default function PaymentReceiveItemsTable() {
+function PaymentReceiveItemsTable({
+ entries,
+ onUpdateData,
+
+ // #withDialogActions
+ openAlert
+}) {
// Payment receive form context.
const {
- isNewMode,
isDueInvoicesFetching,
paymentCustomerId,
- dueInvoices,
} = usePaymentReceiveFormContext();
// Payment receive entries form context.
const columns = usePaymentReceiveEntriesColumns();
- // Detarmines takes payment receive invoices entries from customer receivable
- // invoices or associated payment receive invoices.
- const computedTableData = useMemo(
- () => [
- ...(!isNewMode ? [] || [] : []),
- ...(isNewMode ? dueInvoices || [] : []),
- ],
- [isNewMode, dueInvoices],
- );
-
// No results message.
const noResultsMessage = paymentCustomerId
? 'There is no receivable invoices for this customer that can be applied for this payment'
: 'Please select a customer to display all open invoices for it.';
// Handle update data.
- const handleUpdateData = useCallback((rows) => {}, []);
+ const handleUpdateData = useCallback((rowIndex, columnId, value) => {
+ const newRows = compose(
+ updateTableRow(rowIndex, columnId, value),
+ )(entries);
+
+ onUpdateData(newRows);
+ }, [entries, onUpdateData]);
// Handle click clear all lines button.
const handleClickClearAllLines = () => {
-
+ const fullAmount = safeSumBy(entries, 'payment_amount');
+
+ if (fullAmount > 0) {
+ openAlert('clear-all-lines-payment-receive');
+ }
};
return (
@@ -53,7 +60,7 @@ export default function PaymentReceiveItemsTable() {
progressBarLoading={isDueInvoicesFetching}
className={classNames(CLASSES.DATATABLE_EDITOR_ITEMS_ENTRIES)}
columns={columns}
- data={[]}
+ data={entries}
spinnerProps={false}
payload={{
errors: [],
@@ -74,3 +81,5 @@ export default function PaymentReceiveItemsTable() {
);
}
+
+export default compose(withAlertActions)(PaymentReceiveItemsTable);
\ No newline at end of file
diff --git a/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/components.js b/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/components.js
index e37d9d5d4..300538acd 100644
--- a/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/components.js
+++ b/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/components.js
@@ -1,6 +1,8 @@
import React from 'react';
import moment from 'moment';
import { useIntl } from 'react-intl';
+import { Money } from 'components';
+import { MoneyFieldCell } from 'components/DataTableCells';
import { safeSumBy, formattedAmount } from 'utils';
/**
@@ -21,15 +23,14 @@ function IndexCell({ row: { index } }) {
* Invoice number table cell accessor.
*/
function InvNumberCellAccessor(row) {
- const invNumber = row?.invoice_no || row?.id;
- return `#INV-${invNumber || ''}`;
+ return row?.invoice_no ? `#${row?.invoice_no || ''}` : '-';
}
/**
* Balance footer cell.
*/
function BalanceFooterCell({ rows }) {
- const total = safeSumBy(rows, 'original.balance');
+ const total = safeSumBy(rows, 'original.amount');
return { formattedAmount(total, 'USD') };
}
@@ -49,6 +50,18 @@ function PaymentAmountFooterCell({ rows }) {
return { formattedAmount(totalPaymentAmount, 'USD') };
}
+
+/**
+ * Mobey table cell.
+ */
+function MoneyTableCell({ value }) {
+ return
+}
+
+function DateFooterCell() {
+ return 'Total';
+}
+
/**
* Retrieve payment receive form entries columns.
*/
@@ -64,12 +77,14 @@ export const usePaymentReceiveEntriesColumns = () => {
width: 40,
disableResizing: true,
disableSortBy: true,
+ className: 'index'
},
{
Header: formatMessage({ id: 'Date' }),
id: 'invoice_date',
accessor: 'invoice_date',
Cell: InvoiceDateCell,
+ Footer: DateFooterCell,
disableSortBy: true,
disableResizing: true,
width: 250,
@@ -77,14 +92,14 @@ export const usePaymentReceiveEntriesColumns = () => {
{
Header: formatMessage({ id: 'invocie_number' }),
accessor: InvNumberCellAccessor,
- Cell: 'invoice_no',
disableSortBy: true,
className: '',
},
{
Header: formatMessage({ id: 'invoice_amount' }),
- accessor: 'balance',
+ accessor: 'amount',
Footer: BalanceFooterCell,
+ Cell: MoneyTableCell,
disableSortBy: true,
width: 100,
className: '',
@@ -93,6 +108,7 @@ export const usePaymentReceiveEntriesColumns = () => {
Header: formatMessage({ id: 'amount_due' }),
accessor: 'due_amount',
Footer: DueAmountFooterCell,
+ Cell: MoneyTableCell,
disableSortBy: true,
width: 150,
className: '',
@@ -100,6 +116,7 @@ export const usePaymentReceiveEntriesColumns = () => {
{
Header: formatMessage({ id: 'payment_amount' }),
accessor: 'payment_amount',
+ Cell: MoneyFieldCell,
Footer: PaymentAmountFooterCell,
disableSortBy: true,
width: 150,
diff --git a/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/utils.js b/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/utils.js
index 6a8555501..6388f0a7b 100644
--- a/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/utils.js
+++ b/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/utils.js
@@ -1,12 +1,14 @@
import moment from 'moment';
-import { transformToForm } from 'utils';
+import { transformToForm, safeSumBy } from 'utils';
// Default payment receive entry.
export const defaultPaymentReceiveEntry = {
- id: '',
payment_amount: '',
invoice_id: '',
+ invoice_no: '',
due_amount: '',
+ date: '',
+ amount: '',
};
// Form initial values.
@@ -21,13 +23,61 @@ export const defaultPaymentReceive = {
entries: [],
};
-export const transformToEditForm = (paymentReceive, paymentReceiveEntries) => {
- return {
- ...transformToForm(paymentReceive, defaultPaymentReceive),
- entries: [
- ...paymentReceiveEntries.map((paymentReceiveEntry) => ({
- ...transformToForm(paymentReceiveEntry, defaultPaymentReceiveEntry),
- })),
- ],
- };
+/**
+ *
+ */
+export const transformToEditForm = (paymentReceive, paymentReceiveEntries) => ({
+ ...transformToForm(paymentReceive, defaultPaymentReceive),
+ full_amount: safeSumBy(paymentReceiveEntries, 'payment_amount'),
+ entries: [
+ ...paymentReceiveEntries.map((paymentReceiveEntry) => ({
+ ...transformToForm(paymentReceiveEntry, defaultPaymentReceiveEntry),
+ })),
+ ],
+});
+
+/**
+ * Transformes the given invoices to the new page receivable entries.
+ */
+export const transformInvoicesNewPageEntries = (invoices) => [
+ ...invoices.map((invoice, index) => ({
+ index: index + 1,
+ invoice_id: invoice.id,
+ entry_type: 'invoice',
+ due_amount: invoice.due_amount,
+ date: invoice.invoice_date,
+ amount: invoice.balance,
+ payment_amount: 0,
+ invoice_no: invoice.invoice_no,
+ total_payment_amount: invoice.payment_amount,
+ })),
+];
+
+export const transformEntriesToEditForm = (receivableEntries) => [
+ ...transformInvoicesNewPageEntries([...(receivableEntries || [])]),
+];
+
+export const clearAllPaymentEntries = (entries) => [
+ ...entries.map((entry) => ({ ...entry, payment_amount: 0 })),
+];
+
+export const amountPaymentEntries = (amount, entries) => {
+ let total = amount;
+
+ return entries.map((item) => {
+ const diff = Math.min(item.due_amount, total);
+ total -= Math.max(diff, 0);
+
+ return {
+ ...item,
+ payment_amount: diff,
+ };
+ });
};
+
+export const fullAmountPaymentEntries = (entries) => {
+ return entries.map((item) => ({
+ ...item,
+ payment_amount: item.due_amount,
+ }));
+}
\ No newline at end of file
diff --git a/client/src/containers/Sales/PaymentReceives/PaymentsLanding/PaymentReceiptsListProvider.js b/client/src/containers/Sales/PaymentReceives/PaymentsLanding/PaymentReceiptsListProvider.js
index aaff54857..51042b245 100644
--- a/client/src/containers/Sales/PaymentReceives/PaymentsLanding/PaymentReceiptsListProvider.js
+++ b/client/src/containers/Sales/PaymentReceives/PaymentsLanding/PaymentReceiptsListProvider.js
@@ -57,7 +57,7 @@ function PaymentReceivesListProvider({ query, ...props }) {
return (
diff --git a/client/src/containers/Sales/PaymentReceives/PaymentsLanding/PaymentReceivesList.js b/client/src/containers/Sales/PaymentReceives/PaymentsLanding/PaymentReceivesList.js
index 7c2483125..852dff346 100644
--- a/client/src/containers/Sales/PaymentReceives/PaymentsLanding/PaymentReceivesList.js
+++ b/client/src/containers/Sales/PaymentReceives/PaymentsLanding/PaymentReceivesList.js
@@ -1,5 +1,6 @@
-import React, { useEffect } from 'react';
-import { useIntl } from 'react-intl';
+import React from 'react';
+
+import 'style/pages/PaymentReceive/List.scss';
import { DashboardContentTable, DashboardPageContent } from 'components';
import PaymentReceiveActionsBar from './PaymentReceiveActionsBar';
@@ -8,7 +9,6 @@ import { PaymentReceivesListProvider } from './PaymentReceiptsListProvider';
import PaymentReceiveViewTabs from './PaymentReceiveViewTabs';
import PaymentReceivesTable from './PaymentReceivesTable';
-import withDashboardActions from 'containers/Dashboard/withDashboardActions';
import withPaymentReceives from './withPaymentReceives';
import { compose, transformTableStateToQuery } from 'utils';
@@ -17,19 +17,9 @@ import { compose, transformTableStateToQuery } from 'utils';
* Payment receives list.
*/
function PaymentReceiveList({
- // #withDashboardActions
- changePageTitle,
-
// #withPaymentReceives
paymentReceivesTableState,
}) {
- const { formatMessage } = useIntl();
-
- // Changes the dashboard page title once the page mount.
- useEffect(() => {
- changePageTitle(formatMessage({ id: 'payment_Receives_list' }));
- }, [changePageTitle, formatMessage]);
-
return (
({
paymentReceivesTableState,
})),
diff --git a/client/src/containers/Sales/PaymentReceives/PaymentsLanding/PaymentReceivesListProvider.js b/client/src/containers/Sales/PaymentReceives/PaymentsLanding/PaymentReceivesListProvider.js
index 2df4e069a..bb7fa2487 100644
--- a/client/src/containers/Sales/PaymentReceives/PaymentsLanding/PaymentReceivesListProvider.js
+++ b/client/src/containers/Sales/PaymentReceives/PaymentsLanding/PaymentReceivesListProvider.js
@@ -29,7 +29,7 @@ function PaymentReceivesListProvider({ query, ...props }) {
data: { paymentReceives, pagination, filterMeta },
isLoading: isPaymentReceivesLoading,
isFetching: isPaymentReceivesFetching,
- } = usePaymentReceives(query);
+ } = usePaymentReceives(query, { keepPreviousData: true });
// Provider payload.
const provider = {
diff --git a/client/src/containers/Sales/PaymentReceives/PaymentsLanding/PaymentReceivesTable.js b/client/src/containers/Sales/PaymentReceives/PaymentsLanding/PaymentReceivesTable.js
index 46ac7120c..6bf775718 100644
--- a/client/src/containers/Sales/PaymentReceives/PaymentsLanding/PaymentReceivesTable.js
+++ b/client/src/containers/Sales/PaymentReceives/PaymentsLanding/PaymentReceivesTable.js
@@ -1,10 +1,8 @@
import React, { useCallback } from 'react';
-import classNames from 'classnames';
-
-import { compose } from 'utils';
import { useHistory } from 'react-router-dom';
-import { CLASSES } from 'common/classes';
+import { compose } from 'utils';
+
import PaymentReceivesEmptyStatus from './PaymentReceivesEmptyStatus';
import { DataTable } from 'components';
import TableSkeletonRows from 'components/Datatable/TableSkeletonRows';
diff --git a/client/src/containers/Sales/PaymentReceives/PaymentsLanding/components.js b/client/src/containers/Sales/PaymentReceives/PaymentsLanding/components.js
index 3d2f18405..d97397766 100644
--- a/client/src/containers/Sales/PaymentReceives/PaymentsLanding/components.js
+++ b/client/src/containers/Sales/PaymentReceives/PaymentsLanding/components.js
@@ -91,9 +91,16 @@ export function usePaymentReceivesColumns() {
id: 'customer_id',
Header: formatMessage({ id: 'customer_name' }),
accessor: 'customer.display_name',
- width: 140,
+ width: 160,
className: 'customer_id',
},
+ {
+ id: 'amount',
+ Header: formatMessage({ id: 'amount' }),
+ accessor: AmountAccessor,
+ width: 120,
+ className: 'amount',
+ },
{
id: 'payment_receive_no',
Header: formatMessage({ id: 'payment_receive_no' }),
@@ -102,13 +109,6 @@ export function usePaymentReceivesColumns() {
width: 140,
className: 'payment_receive_no',
},
- {
- id: 'amount',
- Header: formatMessage({ id: 'amount' }),
- accessor: AmountAccessor,
- width: 140,
- className: 'amount',
- },
{
id: 'reference_no',
Header: formatMessage({ id: 'reference_no' }),
diff --git a/client/src/containers/Sales/Receipts/ReceiptsLanding/ReceiptsList.js b/client/src/containers/Sales/Receipts/ReceiptsLanding/ReceiptsList.js
index 9a63e3727..673c64268 100644
--- a/client/src/containers/Sales/Receipts/ReceiptsLanding/ReceiptsList.js
+++ b/client/src/containers/Sales/Receipts/ReceiptsLanding/ReceiptsList.js
@@ -1,6 +1,8 @@
import React from 'react';
import { DashboardContentTable, DashboardPageContent } from 'components';
+import 'style/pages/SaleReceipt/List.scss';
+
import ReceiptActionsBar from './ReceiptActionsBar';
import ReceiptViewTabs from './ReceiptViewTabs';
import ReceiptsAlerts from '../ReceiptsAlerts';
diff --git a/client/src/hooks/query/paymentReceives.js b/client/src/hooks/query/paymentReceives.js
index b6027971c..eb42d0c6f 100644
--- a/client/src/hooks/query/paymentReceives.js
+++ b/client/src/hooks/query/paymentReceives.js
@@ -1,7 +1,7 @@
import { useMutation, useQuery, useQueryClient } from 'react-query';
import { defaultTo } from 'lodash';
import ApiService from 'services/ApiService';
-import { transformPagination } from 'utils';
+import { transformPagination, saveInvoke } from 'utils';
/**
* Retrieve accounts list.
@@ -43,8 +43,13 @@ export function useCreatePaymentReceive(props) {
return useMutation(
(values) => ApiService.post('sales/payment_receives', values),
{
- onSuccess: () => {
+ onSuccess: (data, values) => {
client.invalidateQueries('PAYMENT_RECEIVES');
+ client.invalidateQueries('SALE_INVOICE_DUE');
+ client.invalidateQueries('SALE_INVOICES');
+ client.invalidateQueries('SALE_INVOICE');
+
+ saveInvoke(props?.onSuccess, data);
},
...props,
},
@@ -60,8 +65,13 @@ export function useEditPaymentReceive(props) {
return useMutation(
([id, values]) => ApiService.post(`sales/payment_receives/${id}`, values),
{
- onSuccess: () => {
+ onSuccess: (data) => {
client.invalidateQueries('PAYMENT_RECEIVES');
+ client.invalidateQueries('SALE_INVOICE_DUE');
+ client.invalidateQueries('SALE_INVOICES');
+ client.invalidateQueries('SALE_INVOICE');
+
+ saveInvoke(props?.onSuccess, data);
},
...props,
},
@@ -75,10 +85,15 @@ export function useDeletePaymentReceive(props) {
const client = useQueryClient();
return useMutation(
- (id, values) => ApiService.delete(`sales/payment_receives/${id}`, values),
+ (id) => ApiService.delete(`sales/payment_receives/${id}`),
{
- onSuccess: () => {
+ onSuccess: (data, [id]) => {
client.invalidateQueries('PAYMENT_RECEIVES');
+ client.invalidateQueries('SALE_INVOICE_DUE');
+ client.invalidateQueries('SALE_INVOICES');
+ client.invalidateQueries('SALE_INVOICE');
+
+ saveInvoke(props?.onSuccess, data);
},
...props,
},
@@ -87,19 +102,53 @@ export function useDeletePaymentReceive(props) {
/**
* Retrieve specific payment receive.
+ * @param {number} id - Payment receive.
*/
export function usePaymentReceive(id, props) {
const states = useQuery(
['PAYMENT_RECEIVE', id],
() => ApiService.get(`sales/payment_receives/${id}`),
{
- select: (res) => res.data.payment_receive,
+ select: (res) => ({
+ paymentReceive: res.data.payment_receive,
+ receivableEntries: res.data.receivable_entries,
+ }),
...props
},
);
return {
...states,
- data: defaultTo(states.data, {}),
+ data: defaultTo(states.data, {
+ paymentReceive: {},
+ receivableInvoices: {},
+ paymentInvoices: {}
+ }),
+ }
+}
+
+/**
+ * Retrieve information of payment receive in edit page.
+ * @param {number} id - Payment receive id.
+ */
+export function usePaymentReceiveEditPage(id, props) {
+ const states = useQuery(
+ ['PAYMENT_RECEIVE_EDIT_PAGE', id],
+ () => ApiService.get(`sales/payment_receives/${id}/edit-page`),
+ {
+ select: (res) => ({
+ paymentReceive: res.data.payment_receive,
+ entries: res.data.entries,
+ }),
+ ...props,
+ },
+ );
+
+ return {
+ ...states,
+ data: defaultTo(states.data, {
+ paymentReceive: {},
+ entries: [],
+ })
}
}
diff --git a/client/src/lang/en/index.js b/client/src/lang/en/index.js
index c58470627..21e19ce97 100644
--- a/client/src/lang/en/index.js
+++ b/client/src/lang/en/index.js
@@ -678,7 +678,7 @@ export default {
deposit_to: 'Deposit to',
edit_payment_receive: 'Edit Payment Receive',
delete_payment_receive: 'Delete Payment Receive',
- payment_Receives_list: 'Payment Receives List',
+ payment_receives_list: 'Payment Receives List',
payment_receive: 'Payment Receive',
new_payment_receive: 'New Payment Receive',
payment_receives: 'Payment Receives',
@@ -965,5 +965,7 @@ export default {
running_balance: 'Running balance',
payment_via_voucher: 'Payment via voucher',
voucher_number: 'Voucher number',
- voucher: 'Voucher'
+ voucher: 'Voucher',
+ payment_number_is_not_unique: 'Payment number is not unique.',
+ change_full_amount: 'Change full amount'
};
diff --git a/client/src/routes/dashboard.js b/client/src/routes/dashboard.js
index 98528683b..f2362089c 100644
--- a/client/src/routes/dashboard.js
+++ b/client/src/routes/dashboard.js
@@ -389,6 +389,7 @@ export default [
),
),
breadcrumb: 'Payment Receives List',
+ pageTitle: formatMessage({ id: 'payment_receives_list' }),
},
// Bills
diff --git a/client/src/static/json/icons.js b/client/src/static/json/icons.js
index 569aa06a3..f815f5389 100644
--- a/client/src/static/json/icons.js
+++ b/client/src/static/json/icons.js
@@ -386,4 +386,10 @@ export default {
],
viewBox: '0 0 20 20',
},
+ "send": {
+ path: [
+ 'M2.01 21L23 12 2.01 3 2 10l15 2-15 2z'
+ ],
+ viewBox: '0 0 24 24',
+ }
};
diff --git a/client/src/style/App.scss b/client/src/style/App.scss
index 75a6ca587..47dd9ff2e 100644
--- a/client/src/style/App.scss
+++ b/client/src/style/App.scss
@@ -95,3 +95,8 @@ body.hide-scrollbar .Pane2{
}
+
+
+.bp3-progress-bar.bp3-intent-primary .bp3-progress-meter{
+ background-color: #0066ff;
+}
\ No newline at end of file
diff --git a/client/src/style/pages/PaymentReceive/List.scss b/client/src/style/pages/PaymentReceive/List.scss
new file mode 100644
index 000000000..44e284fde
--- /dev/null
+++ b/client/src/style/pages/PaymentReceive/List.scss
@@ -0,0 +1,18 @@
+
+.dashboard__insider--payment-receives-list{
+
+ .bigcapital-datatable{
+
+ .tbody{
+
+ .td.amount {
+
+ .cell-inner{
+ > span{
+ font-weight: 600;
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/client/src/style/pages/PaymentReceive/PageForm.scss b/client/src/style/pages/PaymentReceive/PageForm.scss
index b555d4e6f..9dfd80667 100644
--- a/client/src/style/pages/PaymentReceive/PageForm.scss
+++ b/client/src/style/pages/PaymentReceive/PageForm.scss
@@ -20,10 +20,18 @@
&.bp3-inline{
max-width: 470px;
}
- a.receive-full-amount{
+ button.receive-full-amount{
+ width: auto;
+ padding: 0;
+ min-height: auto;
font-size: 12px;
- margin-top: 6px;
- display: inline-block;
+ margin-top: 4px;
+ background-color: transparent;
+ color: #0052cc;
+
+ &:hover{
+ text-decoration: underline;
+ }
}
}
}
diff --git a/client/src/style/pages/SaleEstimate/List.scss b/client/src/style/pages/SaleEstimate/List.scss
new file mode 100644
index 000000000..33688fad7
--- /dev/null
+++ b/client/src/style/pages/SaleEstimate/List.scss
@@ -0,0 +1,18 @@
+
+.dashboard__insider--sale_estimate{
+
+ .bigcapital-datatable{
+
+ .tbody{
+
+ .td.amount {
+
+ .cell-inner{
+ > span{
+ font-weight: 600;
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/client/src/style/pages/SaleInvoice/List.scss b/client/src/style/pages/SaleInvoice/List.scss
index 75cc9216b..59a3db16a 100644
--- a/client/src/style/pages/SaleInvoice/List.scss
+++ b/client/src/style/pages/SaleInvoice/List.scss
@@ -7,6 +7,14 @@
.tbody{
+ .balance.td{
+
+ .cell-inner{
+ > span{
+ font-weight: 600;
+ }
+ }
+ }
.status.td{
.status-accessor{
@@ -33,7 +41,7 @@
line-height: 1;
display: block;
margin-bottom: 8px;
- opacity: 0.65;
+ opacity: 0.7;
}
.fully-paid-icon{
width: 18px;
@@ -49,10 +57,11 @@
}
.bp3-progress-bar{
height: 4px;
+ max-width: 180px;
&,
.bp3-progress-meter{
- border-radius: 4px;
+ border-radius: 0;
}
}
}
diff --git a/client/src/style/pages/SaleReceipt/List.scss b/client/src/style/pages/SaleReceipt/List.scss
new file mode 100644
index 000000000..7aec30dd2
--- /dev/null
+++ b/client/src/style/pages/SaleReceipt/List.scss
@@ -0,0 +1,18 @@
+
+.dashboard__insider--sales_receipts{
+
+ .bigcapital-datatable{
+
+ .tbody{
+
+ .td.amount {
+
+ .cell-inner{
+ > span{
+ font-weight: 600;
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/server/src/api/controllers/Sales/PaymentReceives.ts b/server/src/api/controllers/Sales/PaymentReceives.ts
index 143c765d5..282a2888e 100644
--- a/server/src/api/controllers/Sales/PaymentReceives.ts
+++ b/server/src/api/controllers/Sales/PaymentReceives.ts
@@ -48,10 +48,10 @@ export default class PaymentReceivesController extends BaseController {
this.handleServiceErrors
);
router.get(
- '/:id',
+ '/:id/edit-page',
this.paymentReceiveValidation,
this.validationResult,
- asyncMiddleware(this.getPaymentReceive.bind(this)),
+ asyncMiddleware(this.getPaymentReceiveEditPage.bind(this)),
this.handleServiceErrors
);
router.get(
@@ -215,16 +215,15 @@ export default class PaymentReceivesController extends BaseController {
* @param {Request} req -
* @param {Response} res -
*/
- async getPaymentReceive(req: Request, res: Response, next: NextFunction) {
+ async getPaymentReceiveEditPage(req: Request, res: Response, next: NextFunction) {
const { tenantId, user } = req;
const { id: paymentReceiveId } = req.params;
try {
const {
paymentReceive,
- receivableInvoices,
- paymentReceiveInvoices,
- } = await this.paymentReceiveService.getPaymentReceive(
+ entries
+ } = await this.paymentReceiveService.getPaymentReceiveEditPage(
tenantId,
paymentReceiveId,
user
@@ -232,8 +231,7 @@ export default class PaymentReceivesController extends BaseController {
return res.status(200).send({
payment_receive: this.transfromToResponse({ ...paymentReceive }),
- receivable_invoices: this.transfromToResponse([...receivableInvoices]),
- payment_invoices: this.transfromToResponse([...paymentReceiveInvoices]),
+ entries: this.transfromToResponse([...entries]),
});
} catch (error) {
next(error);
diff --git a/server/src/interfaces/PaymentReceive.ts b/server/src/interfaces/PaymentReceive.ts
index 60c9c12d4..9c04496fa 100644
--- a/server/src/interfaces/PaymentReceive.ts
+++ b/server/src/interfaces/PaymentReceive.ts
@@ -1,5 +1,5 @@
-import { IDynamicListFilterDTO } from "./DynamicFilter";
+import { IDynamicListFilterDTO } from "./DynamicFilter";
export interface IPaymentReceive {
id?: number,
@@ -50,4 +50,20 @@ export interface IPaymentReceiveEntryDTO {
export interface IPaymentReceivesFilter extends IDynamicListFilterDTO {
stringifiedFilterRoles?: string,
-}
\ No newline at end of file
+}
+
+export interface IPaymentReceiveEditPageEntry {
+ invoiceId: number,
+ entryType: string,
+ invoiceNo: string,
+ dueAmount: number,
+ amount: number,
+ totalPaymentAmount: number,
+ paymentAmount: number,
+ date: Date|string,
+};
+
+export interface IPaymentReceiveEditPage {
+ paymentReceive: IPaymentReceive,
+ entries: IPaymentReceiveEditPageEntry[];
+};
\ No newline at end of file
diff --git a/server/src/services/Sales/PaymentsReceives.ts b/server/src/services/Sales/PaymentsReceives.ts
index bf000906d..4a0616e41 100644
--- a/server/src/services/Sales/PaymentsReceives.ts
+++ b/server/src/services/Sales/PaymentsReceives.ts
@@ -1,5 +1,4 @@
import { omit, sumBy, difference } from 'lodash';
-import moment from 'moment';
import { Service, Inject } from 'typedi';
import {
EventDispatcher,
@@ -19,6 +18,7 @@ import {
ISaleInvoice,
ISystemService,
ISystemUser,
+ IPaymentReceiveEditPageEntry,
} from 'interfaces';
import AccountsService from 'services/Accounts/AccountsService';
import JournalPoster from 'services/Accounting/JournalPoster';
@@ -466,46 +466,80 @@ export default class PaymentReceiveService {
});
}
+ /**
+ * Retrive edit page invoices entries from the given sale invoices models.
+ * @param {ISaleInvoice[]} invoices - Invoices.
+ * @return {IPaymentReceiveEditPageEntry}
+ */
+ public invoicesToEditPageEntries(
+ invoice: ISaleInvoice
+ ): IPaymentReceiveEditPageEntry {
+ return {
+ entryType: 'invoice',
+ invoiceId: invoice.id,
+ dueAmount: invoice.dueAmount + invoice.paymentAmount,
+ amount: invoice.balance,
+ invoiceNo: invoice.invoiceNo,
+ totalPaymentAmount: invoice.paymentAmount,
+ paymentAmount: invoice.paymentAmount,
+ date: invoice.invoiceDate,
+ };
+ }
+
/**
* Retrieve the payment receive details of the given id.
* @param {number} tenantId - Tenant id.
* @param {Integer} paymentReceiveId - Payment receive id.
*/
- public async getPaymentReceive(
+ public async getPaymentReceiveEditPage(
tenantId: number,
paymentReceiveId: number,
- authorizedUser: ISystemService
): Promise<{
paymentReceive: IPaymentReceive;
- receivableInvoices: ISaleInvoice[];
- paymentReceiveInvoices: ISaleInvoice[];
+ entries: IPaymentReceiveEditPageEntry[];
}> {
const { PaymentReceive, SaleInvoice } = this.tenancy.models(tenantId);
+
+ // Retrieve payment receive.
const paymentReceive = await PaymentReceive.query()
.findById(paymentReceiveId)
- .withGraphFetched('entries.invoice')
- .withGraphFetched('customer')
- .withGraphFetched('depositAccount');
+ .withGraphFetched('entries.invoice');
+ // Throw not found the payment receive.
if (!paymentReceive) {
throw new ServiceError(ERRORS.PAYMENT_RECEIVE_NOT_EXISTS);
}
- const invoicesIds = paymentReceive.entries.map((entry) => entry.invoiceId);
- // Retrieves all receivable bills that associated to the payment receive transaction.
- const receivableInvoices = await SaleInvoice.query()
- .modify('dueInvoices')
- .where('customer_id', paymentReceive.customerId)
- .whereNotIn('id', invoicesIds)
- .orderBy('invoice_date', 'ASC');
+ // Mapping the entries invoices.
+ const entriesInvoicesIds = paymentReceive.entries.map(
+ (entry) => entry.invoiceId
+ );
- // Retrieve all payment receive associated invoices.
- const paymentReceiveInvoices = paymentReceive.entries.map((entry) => ({
- ...entry.invoice,
- dueAmount: entry.invoice.dueAmount + entry.paymentAmount,
+ const paymentEntries = paymentReceive.entries.map((entry) => ({
+ ...this.invoicesToEditPageEntries(entry.invoice),
+ paymentAmount: entry.paymentAmount,
}));
- return { paymentReceive, receivableInvoices, paymentReceiveInvoices };
+ // Retrieves all receivable bills that associated to the payment receive transaction.
+ const restReceivableInvoices = await SaleInvoice.query()
+ .modify('dueInvoices')
+ .where('customer_id', paymentReceive.customerId)
+ .whereNotIn('id', entriesInvoicesIds)
+ .orderBy('invoice_date', 'ASC');
+
+ const restReceivableEntries = restReceivableInvoices.map(
+ this.invoicesToEditPageEntries
+ );
+
+ const entries = [
+ ...paymentEntries,
+ ...restReceivableEntries,
+ ];
+
+ return {
+ paymentReceive: omit(paymentReceive, ['entries']),
+ entries,
+ };
}
/**
@@ -664,7 +698,7 @@ export default class PaymentReceiveService {
*/
async revertPaymentReceiveJournalEntries(
tenantId: number,
- paymentReceiveId: number,
+ paymentReceiveId: number
) {
const { accountRepository } = this.tenancy.repositories(tenantId);
diff --git a/server/src/services/Sales/SalesInvoices.ts b/server/src/services/Sales/SalesInvoices.ts
index 0bbcb0dcf..b8c6a4f72 100644
--- a/server/src/services/Sales/SalesInvoices.ts
+++ b/server/src/services/Sales/SalesInvoices.ts
@@ -583,6 +583,8 @@ export default class SaleInvoicesService {
query.where('customer_id', customerId);
}
});
+
+
return salesInvoices;
}
}