diff --git a/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFloatingActions.js b/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFloatingActions.js
index f03e6a45a..06943211f 100644
--- a/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFloatingActions.js
+++ b/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFloatingActions.js
@@ -15,7 +15,6 @@ import { useFormikContext } from 'formik';
import { usePaymentMadeFormContext } from './PaymentMadeFormProvider';
import { CLASSES } from 'common/classes';
-
import { Icon } from 'components';
/**
diff --git a/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFooter.js b/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFooter.js
index 515baea79..10d1a6328 100644
--- a/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFooter.js
+++ b/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFooter.js
@@ -9,7 +9,7 @@ import { CLASSES } from 'common/classes';
/**
* Payment made form footer.
*/
-export default function PaymentMadeFooter({ getFieldProps }) {
+export default function PaymentMadeFooter() {
return (
diff --git a/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeForm.js b/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeForm.js
index 682222fb0..3f5e11a85 100644
--- a/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeForm.js
+++ b/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeForm.js
@@ -11,6 +11,7 @@ import { AppToaster } from 'components';
import PaymentMadeHeader from './PaymentMadeFormHeader';
import PaymentMadeFloatingActions from './PaymentMadeFloatingActions';
import PaymentMadeFooter from './PaymentMadeFooter';
+import PaymentMadeItemsTable from './PaymentMadeItemsTable';
import withSettings from 'containers/Settings/withSettings';
import {
@@ -135,7 +136,7 @@ function PaymentMadeForm() {
diff --git a/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFormHeaderFields.js b/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFormHeaderFields.js
index cf07eb753..a64da7c51 100644
--- a/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFormHeaderFields.js
+++ b/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFormHeaderFields.js
@@ -34,7 +34,12 @@ import {
* Payment made form header fields.
*/
function PaymentMadeFormHeaderFields({ baseCurrency }) {
- const { vendors, accounts, isNewMode } = usePaymentMadeFormContext();
+ const {
+ vendors,
+ accounts,
+ isNewMode,
+ setPaymentVendorId,
+ } = usePaymentMadeFormContext();
const payableFullAmount = 0;
@@ -57,6 +62,7 @@ function PaymentMadeFormHeaderFields({ baseCurrency }) {
defaultSelectText={}
onContactSelected={(contact) => {
form.setFieldValue('vendor_id', contact.id);
+ setPaymentVendorId(contact.id);
}}
disabled={!isNewMode}
popoverFill={true}
diff --git a/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFormProvider.js b/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFormProvider.js
index 302600de3..c0af6177c 100644
--- a/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFormProvider.js
+++ b/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFormProvider.js
@@ -6,7 +6,8 @@ import {
usePaymentMade,
useSettings,
useCreatePaymentMade,
- useEditPaymentMade
+ useEditPaymentMade,
+ useDueBills,
} from 'hooks/query';
import { DashboardInsider } from 'components';
@@ -17,6 +18,9 @@ const PaymentMadeFormContext = createContext();
* Payment made form provider.
*/
function PaymentMadeFormProvider({ paymentMadeId, ...props }) {
+ const [submitPayload, setSubmitPayload] = React.useState({});
+ const [paymentVendorId, setPaymentVendorId] = React.useState(null);
+
// Handle fetch accounts data.
const { data: accounts, isFetching: isAccountsFetching } = useAccounts();
@@ -42,6 +46,13 @@ function PaymentMadeFormProvider({ paymentMadeId, ...props }) {
enabled: !!paymentMadeId,
});
+ // Retrieve the due bills of the given vendor.
+ const {
+ data: dueBills,
+ isLoading: isDueBillsLoading,
+ isFetching: isDueBillsFetching,
+ } = useDueBills(paymentVendorId, { enabled: !!paymentVendorId });
+
// Fetch payment made settings.
useSettings();
@@ -49,6 +60,8 @@ function PaymentMadeFormProvider({ paymentMadeId, ...props }) {
const { mutateAsync: createPaymentMadeMutate } = useCreatePaymentMade();
const { mutateAsync: editPaymentMadeMutate } = useEditPaymentMade();
+ const isNewMode = !paymentMadeId;
+
// Provider payload.
const provider = {
paymentMadeId,
@@ -58,16 +71,25 @@ function PaymentMadeFormProvider({ paymentMadeId, ...props }) {
paymentBills,
vendors,
items,
+ dueBills,
+ submitPayload,
+ paymentVendorId,
+ isNewMode,
isAccountsFetching,
isItemsFetching,
isItemsLoading,
isVendorsFetching,
isPaymentFetching,
isPaymentLoading,
+ isDueBillsLoading,
+ isDueBillsFetching,
createPaymentMadeMutate,
editPaymentMadeMutate,
+
+ setSubmitPayload,
+ setPaymentVendorId,
};
return (
diff --git a/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeItemsTable.js b/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeItemsTable.js
index 5c3b6c8b0..d9850583f 100644
--- a/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeItemsTable.js
+++ b/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeItemsTable.js
@@ -1,153 +1,70 @@
-import React, { useState, useEffect, useMemo, useCallback } from 'react';
-import { useQuery } from 'react-query';
-import { isEmpty } from 'lodash';
+import React, { useMemo, useCallback } from 'react';
import { CloudLoadingIndicator } from 'components';
-import PaymentMadeItemsTableEditor from './PaymentMadeItemsTableEditor';
+import { Button } from '@blueprintjs/core';
+import { FormattedMessage as T } from 'react-intl';
+import classNames from 'classnames';
-import withPaymentMadeActions from './withPaymentMadeActions';
-import withBillActions from '../Bill/withBillActions';
-import withBills from '../Bill/withBills';
+import { CLASSES } from 'common/classes';
+import { DataTableEditable } from 'components';
+import { usePaymentMadeEntriesTableColumns } from './components';
-import { compose } from 'utils';
+import { usePaymentMadeFormContext } from './PaymentMadeFormProvider';
/**
* Payment made items table.
*/
-function PaymentMadeItemsTable({
- // #ownProps
- paymentMadeId,
- vendorId,
- fullAmount,
- onUpdateData,
- paymentEntries = [], // { bill_id: number, payment_amount: number, id?: number }
- onClickClearAllLines,
- errors,
- onFetchEntriesSuccess,
+export default function PaymentMadeItemsTable() {
+ const {
+ paymentVendorId,
+ dueBills,
+ isDueBillsFetching,
+ isNewMode,
+ } = usePaymentMadeFormContext();
- // #withBillActions
- requestFetchDueBills,
-
- // #withBills
- vendorPayableBillsEntries,
-}) {
- const isNewMode = !paymentMadeId;
+ const columns = usePaymentMadeEntriesTableColumns();
// Detarmines takes vendor payable bills entries in create mode
// or payment made entries in edit mode.
- const computedTableEntries = useMemo(
- () =>
- !isEmpty(paymentEntries)
- ? paymentEntries
- : (vendorPayableBillsEntries || []),
- [vendorPayableBillsEntries, paymentEntries],
- );
- const [tableData, setTableData] = useState(computedTableEntries);
- const [localEntries, setLocalEntries] = useState(computedTableEntries);
-
- const [localAmount, setLocalAmount] = useState(fullAmount);
+ const computedTableEntries = useMemo(() => [], []);
// Triggers `onUpdateData` event that passes changed entries.
- const triggerUpdateData = useCallback(
- (entries) => {
- onUpdateData && onUpdateData(entries);
- },
- [onUpdateData],
- );
+ const triggerUpdateData = useCallback((entries) => {}, []);
- const triggerOnFetchBillsSuccess = useCallback(
- (bills) => {
- onFetchEntriesSuccess && onFetchEntriesSuccess(bills);
- },
- [onFetchEntriesSuccess],
- );
-
- useEffect(() => {
- if (computedTableEntries !== localEntries) {
- setTableData(computedTableEntries);
- setLocalEntries(computedTableEntries);
- }
- }, [computedTableEntries, localEntries]);
-
- // Handle mapping `fullAmount` prop to `localAmount` state.
- useEffect(() => {
- if (localAmount !== fullAmount) {
- let _fullAmount = fullAmount;
- const newTableData = tableData.map((data) => {
- const amount = Math.min(data.due_amount, _fullAmount);
- _fullAmount -= Math.max(amount, 0);
-
- return {
- ...data,
- payment_amount: amount,
- };
- });
- setTableData(newTableData);
- setLocalAmount(fullAmount);
- triggerUpdateData(newTableData);
- }
- }, [
- tableData,
- setTableData,
- setLocalAmount,
- triggerUpdateData,
- localAmount,
- fullAmount,
- ]);
-
- // Fetches vendor due bills.
- const fetchVendorDueBills = useQuery(
- ['vendor-due-bills', vendorId],
- (key, _vendorId) => requestFetchDueBills(_vendorId),
- { enabled: isNewMode && vendorId },
- );
-
- useEffect(() => {
- const enabled = isNewMode && vendorId;
-
- if (!fetchVendorDueBills.isFetching && enabled) {
- triggerOnFetchBillsSuccess(computedTableEntries);
- }
- }, [
- vendorId,
- isNewMode,
- fetchVendorDueBills.isFetching,
- computedTableEntries,
- triggerOnFetchBillsSuccess,
- ]);
+ const triggerOnFetchBillsSuccess = useCallback((bills) => {}, []);
// Handle update data.
- const handleUpdateData = useCallback(
- (rows) => {
- setTableData(rows);
- triggerUpdateData(rows);
- },
- [setTableData, triggerUpdateData],
- );
+ const handleUpdateData = useCallback((rows) => {}, []);
// Detarmines the right no results message before selecting vendor and aftering
// selecting vendor id.
- const noResultsMessage = vendorId
+ const noResultsMessage = paymentVendorId
? 'There is no payable bills for this vendor that can be applied for this payment'
: 'Please select a vendor to display all open bills for it.';
return (
-
-
+
+
+
+ }
+ totalRow={true}
/>
);
}
-
-export default compose(
- withPaymentMadeActions,
- withBillActions,
- withBills(({ paymentMadePayableBills, vendorPayableBillsEntries }) => ({
- paymentMadePayableBills,
- vendorPayableBillsEntries,
- })),
-)(PaymentMadeItemsTable);
diff --git a/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeItemsTableEditor.js b/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeItemsTableEditor.js
deleted file mode 100644
index b4a2cf22f..000000000
--- a/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeItemsTableEditor.js
+++ /dev/null
@@ -1,166 +0,0 @@
-import React, { useState, useEffect, useMemo, useCallback } from 'react';
-import { Button } from '@blueprintjs/core';
-import { FormattedMessage as T, useIntl } from 'react-intl';
-import moment from 'moment';
-import { sumBy } from 'lodash';
-import classNames from 'classnames';
-
-import { CLASSES } from 'common/classes';
-import { DataTableEditable, Money } from 'components';
-import { transformUpdatedRows } from 'utils';
-import {
- MoneyFieldCell,
- DivFieldCell,
- EmptyDiv,
-} from 'components/DataTableCells';
-
-/**
- * Cell renderer guard.
- */
-const CellRenderer = (content, type) => (props) => {
- if (props.data.length === props.row.index + 1) {
- return '';
- }
- return content(props);
-};
-
-const TotalCellRederer = (content, type) => (props) => {
- if (props.data.length === props.row.index + 1) {
- return ;
- }
- return content(props);
-};
-
-/**
- * Payment made items editor table.
- */
-export default function PaymentMadeItemsTableEditor({
- //#ownProps
- onClickClearAllLines,
- onUpdateData,
- data,
- errors,
- noResultsMessage,
-}) {
- const transformedData = useMemo(() => {
- const rows = [...data];
- const totalRow = {
- due_amount: sumBy(data, 'due_amount'),
- payment_amount: sumBy(data, 'payment_amount'),
- };
- if (rows.length > 0) {
- rows.push(totalRow);
- }
- return rows;
- }, [data]);
-
- const [localData, setLocalData] = useState(transformedData);
- const { formatMessage } = useIntl();
-
- useEffect(() => {
- if (localData !== transformedData) {
- setLocalData(transformedData);
- }
- }, [setLocalData, localData, transformedData]);
-
- const columns = useMemo(
- () => [
- {
- Header: '#',
- accessor: 'index',
- Cell: ({ row: { index } }) => {index + 1},
- width: 40,
- disableResizing: true,
- disableSortBy: true,
- },
- {
- Header: formatMessage({ id: 'Date' }),
- id: 'bill_date',
- accessor: (r) => moment(r.bill_date).format('YYYY MMM DD'),
- Cell: CellRenderer(EmptyDiv, 'bill_date'),
- disableSortBy: true,
- },
- {
- Header: formatMessage({ id: 'bill_number' }),
- accessor: (row) => `#${row?.bill_number || ''}`,
- Cell: CellRenderer(EmptyDiv, 'bill_number'),
- disableSortBy: true,
- className: 'bill_number',
- },
- {
- Header: formatMessage({ id: 'bill_amount' }),
- accessor: 'amount',
- Cell: CellRenderer(DivFieldCell, 'amount'),
- disableSortBy: true,
- className: '',
- },
- {
- Header: formatMessage({ id: 'amount_due' }),
- accessor: 'due_amount',
- Cell: TotalCellRederer(DivFieldCell, 'due_amount'),
- disableSortBy: true,
- className: '',
- },
- {
- Header: formatMessage({ id: 'payment_amount' }),
- accessor: 'payment_amount',
- Cell: TotalCellRederer(MoneyFieldCell, 'payment_amount'),
- disableSortBy: true,
- className: '',
- },
- ],
- [formatMessage],
- );
-
- // Handle click clear all lines button.
- const handleClickClearAllLines = () => {
- onClickClearAllLines && onClickClearAllLines();
- };
-
- const rowClassNames = useCallback(
- (row) => ({ 'row--total': localData.length === row.index + 1 }),
- [localData],
- );
-
- // Handle update data.
- const handleUpdateData = useCallback(
- (rowIndex, columnId, value) => {
- const newRows = transformUpdatedRows(
- localData,
- rowIndex,
- columnId,
- value,
- );
- newRows.splice(-1, 1); // removes the total row.
-
- setLocalData(newRows);
- onUpdateData && onUpdateData(newRows);
- },
- [localData, setLocalData, onUpdateData],
- );
-
- return (
-
-
-
- }
- totalRow={true}
- />
- );
-}
diff --git a/client/src/containers/Purchases/PaymentMades/PaymentForm/components.js b/client/src/containers/Purchases/PaymentMades/PaymentForm/components.js
new file mode 100644
index 000000000..9e6383341
--- /dev/null
+++ b/client/src/containers/Purchases/PaymentMades/PaymentForm/components.js
@@ -0,0 +1,95 @@
+import React from 'react';
+import { useIntl } from "react-intl";
+import moment from 'moment';
+import { Money } from 'components';
+import { safeSumBy, formattedAmount } from 'utils';
+
+function BillNumberAccessor(row) {
+ return `#${row?.bill_number || ''}`
+}
+
+function IndexTableCell({ row: { index } }) {
+ return ({index + 1});
+}
+
+function BillDateTableCell({ value }) {
+ return moment(value).format('YYYY MMM DD');
+}
+/**
+ * Balance footer cell.
+ */
+function AmountFooterCell({ rows }) {
+ const total = safeSumBy(rows, 'original.amount');
+ return { formattedAmount(total, 'USD') };
+}
+
+/**
+ * Due amount footer cell.
+ */
+function DueAmountFooterCell({ rows }) {
+ const totalDueAmount = safeSumBy(rows, 'original.due_amount');
+ return { formattedAmount(totalDueAmount, 'USD') };
+}
+
+/**
+ * Payment amount footer cell.
+ */
+function PaymentAmountFooterCell({ rows }) {
+ const totalPaymentAmount = safeSumBy(rows, 'original.payment_amount');
+ return { formattedAmount(totalPaymentAmount, 'USD') };
+}
+
+/**
+ * Payment made entries table columns
+ */
+export function usePaymentMadeEntriesTableColumns() {
+ const { formatMessage } = useIntl();
+
+ return React.useMemo(
+ () => [
+ {
+ Header: '#',
+ accessor: 'index',
+ Cell: IndexTableCell,
+ width: 40,
+ disableResizing: true,
+ disableSortBy: true,
+ },
+ {
+ Header: formatMessage({ id: 'Date' }),
+ id: 'bill_date',
+ accessor: 'bill_date',
+ Cell: BillDateTableCell,
+ disableSortBy: true,
+ },
+ {
+ Header: formatMessage({ id: 'bill_number' }),
+ accessor: BillNumberAccessor,
+ disableSortBy: true,
+ className: 'bill_number',
+ },
+ {
+ Header: formatMessage({ id: 'bill_amount' }),
+ accessor: 'amount',
+ Footer: AmountFooterCell,
+ disableSortBy: true,
+ className: '',
+ },
+ {
+ Header: formatMessage({ id: 'amount_due' }),
+ accessor: 'due_amount',
+ Footer: DueAmountFooterCell,
+ disableSortBy: true,
+ className: '',
+ },
+ {
+ Header: formatMessage({ id: 'payment_amount' }),
+ accessor: 'payment_amount',
+ Footer: PaymentAmountFooterCell,
+ disableSortBy: true,
+ className: '',
+ },
+ ],
+ [formatMessage],
+ )
+}
\ 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 029bf2af3..30cdb99dd 100644
--- a/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveForm.js
+++ b/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveForm.js
@@ -9,7 +9,7 @@ import { useHistory } from 'react-router-dom';
import { CLASSES } from 'common/classes';
import PaymentReceiveHeader from './PaymentReceiveFormHeader';
-// import PaymentReceiveItemsTable from './PaymentReceiveItemsTable';
+import PaymentReceiveItemsTable from './PaymentReceiveItemsTable';
import PaymentReceiveFloatingActions from './PaymentReceiveFloatingActions';
import PaymentReceiveFormFooter from './PaymentReceiveFormFooter';
@@ -167,48 +167,16 @@ function PaymentReceiveForm({
>
- {/* */}
);
diff --git a/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveFormProvider.js b/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveFormProvider.js
index a68299d11..a4c7cd8f3 100644
--- a/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveFormProvider.js
+++ b/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveFormProvider.js
@@ -6,7 +6,8 @@ import {
useAccounts,
useCustomers,
useCreatePaymentReceive,
- useEditPaymentReceive
+ useEditPaymentReceive,
+ useDueInvoices,
} from 'hooks/query';
// Payment receive form context.
@@ -16,14 +17,16 @@ const PaymentReceiveFormContext = createContext();
* Payment receive form provider.
*/
function PaymentReceiveFormProvider({ paymentReceiveId, ...props }) {
+ // Form state.
+ const [paymentCustomerId, setPaymentCustomerId] = React.useState(null);
+ const [submitPayload, setSubmitPayload] = React.useState({});
+
// Fetches payment recevie details.
const {
data: paymentReceive,
isLoading: isPaymentLoading,
isFetching: isPaymentFetching,
- } = usePaymentReceive(paymentReceiveId, {
- enabled: !!paymentReceiveId,
- });
+ } = usePaymentReceive(paymentReceiveId, { enabled: !!paymentReceiveId });
// Handle fetch accounts data.
const { data: accounts, isFetching: isAccountsFetching } = useAccounts();
@@ -37,7 +40,16 @@ function PaymentReceiveFormProvider({ paymentReceiveId, ...props }) {
isFetching: isCustomersFetching,
} = useCustomers();
- const [submitPayload, setSubmitPayload] = React.useState({});
+ // Fetches customer receivable invoices.
+ const {
+ data: dueInvoices,
+ isLoading: isDueInvoicesLoading,
+ isFetching: isDueInvoicesFetching,
+ } = useDueInvoices(paymentCustomerId, {
+ enabled: !!paymentCustomerId,
+ });
+
+ // Detarmines whether the new mode.
const isNewMode = !paymentReceiveId;
// Create and edit payment receive mutations.
@@ -49,27 +61,29 @@ function PaymentReceiveFormProvider({ paymentReceiveId, ...props }) {
paymentReceive,
accounts,
customers,
+ dueInvoices,
isPaymentLoading,
isPaymentFetching,
isAccountsFetching,
isCustomersFetching,
+ isDueInvoicesLoading,
+ isDueInvoicesFetching,
+ paymentCustomerId,
submitPayload,
- setSubmitPayload,
isNewMode,
+
+ setSubmitPayload,
+ setPaymentCustomerId,
editPaymentReceiveMutate,
- createPaymentReceiveMutate
+ createPaymentReceiveMutate,
};
return (
diff --git a/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveHeaderFields.js b/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveHeaderFields.js
index e1f6d5fab..fca90d310 100644
--- a/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveHeaderFields.js
+++ b/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveHeaderFields.js
@@ -37,7 +37,12 @@ import { compose } from 'utils';
*/
function PaymentReceiveHeaderFields({ baseCurrency }) {
// Payment receive form context.
- const { customers, accounts, isNewMode } = usePaymentReceiveFormContext();
+ const {
+ customers,
+ accounts,
+ isNewMode,
+ setPaymentCustomerId,
+ } = usePaymentReceiveFormContext();
// Formik form context.
const { values } = useFormikContext();
@@ -66,8 +71,9 @@ function PaymentReceiveHeaderFields({ baseCurrency }) {
contactsList={customers}
selectedContactId={value}
defaultSelectText={}
- onContactSelected={(value) => {
- form.setFieldValue('customer_id', value);
+ onContactSelected={(customer) => {
+ form.setFieldValue('customer_id', customer);
+ setPaymentCustomerId(customer.id);
}}
popoverFill={true}
disabled={!isNewMode}
diff --git a/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveItemsTable.js b/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveItemsTable.js
index 25ff66360..182fd64e8 100644
--- a/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveItemsTable.js
+++ b/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveItemsTable.js
@@ -1,137 +1,76 @@
-import React, { useState, useMemo, useEffect, useCallback } from 'react';
+import React, { useMemo, useCallback } from 'react';
+import { Button } from '@blueprintjs/core';
+import { FormattedMessage as T } from 'react-intl';
import { CloudLoadingIndicator } from 'components';
-import { useQuery } from 'react-query';
-import { omit } from 'lodash';
+import classNames from 'classnames';
-import { compose } from 'utils';
+import { CLASSES } from 'common/classes';
+import { usePaymentReceiveFormContext } from './PaymentReceiveFormProvider';
+import { DataTableEditable } from 'components';
+import { usePaymentReceiveEntriesColumns } from './components';
-import withInvoices from '../Invoice/withInvoices';
-import PaymentReceiveItemsTableEditor from './PaymentReceiveItemsTableEditor';
-import withInvoiceActions from 'containers/Sales/Invoice/withInvoiceActions';
+/**
+ * Payment receive items table.
+ */
+export default function PaymentReceiveItemsTable() {
+ // Payment receive form context.
+ const {
+ isNewMode,
+ isDueInvoicesFetching,
+ paymentCustomerId,
+ dueInvoices,
+ } = usePaymentReceiveFormContext();
-
-function PaymentReceiveItemsTable({
- // #ownProps
- paymentReceiveId,
- customerId,
- fullAmount,
- onUpdateData,
- paymentReceiveEntries = [],
- errors,
- onClickClearAllLines,
- onFetchEntriesSuccess,
-
- // #withInvoices
- customerInvoiceEntries,
-
- // #withPaymentReceiveActions
- requestFetchDueInvoices
-}) {
- const isNewMode = !paymentReceiveId;
+ // 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 ? (paymentReceiveEntries || []) : []),
- ...(isNewMode ? (customerInvoiceEntries || []) : []),
- ], [
- isNewMode,
- paymentReceiveEntries,
- customerInvoiceEntries,
- ]);
-
- const [tableData, setTableData] = useState(computedTableData);
- const [localEntries, setLocalEntries] = useState(computedTableData);
-
- const [localAmount, setLocalAmount] = useState(fullAmount);
-
- useEffect(() => {
- if (computedTableData !== localEntries) {
- setTableData(computedTableData);
- setLocalEntries(computedTableData);
- }
- }, [computedTableData, localEntries]);
-
- // Triggers `onUpdateData` prop event.
- const triggerUpdateData = useCallback((entries) => {
- onUpdateData && onUpdateData(entries);
- }, [onUpdateData]);
-
- useEffect(() => {
- if (localAmount !== fullAmount) {
- let _fullAmount = fullAmount;
-
- const newTableData = tableData.map((data) => {
- const amount = Math.min(data?.due_amount, _fullAmount);
- _fullAmount -= Math.max(amount, 0);
-
- return {
- ...data,
- payment_amount: amount,
- };
- });
- setTableData(newTableData);
- setLocalAmount(fullAmount);
- triggerUpdateData(newTableData);
- }
- }, [
- fullAmount,
- localAmount,
- tableData,
- triggerUpdateData,
- ]);
-
- // Fetches customer receivable invoices.
- const fetchCustomerDueInvoices = useQuery(
- ['customer-due-invoices', customerId],
- (key, _customerId) => requestFetchDueInvoices(_customerId),
- { enabled: isNewMode && customerId },
+ const computedTableData = useMemo(
+ () => [
+ ...(!isNewMode ? [] || [] : []),
+ ...(isNewMode ? dueInvoices || [] : []),
+ ],
+ [isNewMode, dueInvoices],
);
- // No results message.
- const noResultsMessage = (customerId) ?
- '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.';
- const triggerOnFetchInvoicesSuccess = useCallback((bills) => {
- onFetchEntriesSuccess && onFetchEntriesSuccess(bills);
- }, [onFetchEntriesSuccess])
+ // 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) => {
- triggerUpdateData(rows);
- setTableData(rows);
- }, [triggerUpdateData]);
+ const handleUpdateData = useCallback((rows) => {}, []);
- useEffect(() => {
- const enabled = isNewMode && customerId;
-
- if (!fetchCustomerDueInvoices.isFetching && enabled) {
- triggerOnFetchInvoicesSuccess(computedTableData);
- }
- }, [
- isNewMode,
- customerId,
- fetchCustomerDueInvoices.isFetching,
- computedTableData,
- triggerOnFetchInvoicesSuccess,
- ]);
+ // Handle click clear all lines button.
+ const handleClickClearAllLines = () => {
+
+ };
return (
-
-
+
+
+
+ }
+ totalRow={true}
/>
);
}
-
-export default compose(
- withInvoices(({ customerInvoiceEntries }) => ({
- customerInvoiceEntries,
- })),
- withInvoiceActions,
-)(PaymentReceiveItemsTable);
diff --git a/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveItemsTableEditor.js b/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveItemsTableEditor.js
deleted file mode 100644
index 579dd01e1..000000000
--- a/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveItemsTableEditor.js
+++ /dev/null
@@ -1,172 +0,0 @@
-import React, { useState, useEffect, useCallback, useMemo } from 'react';
-import { Button } from '@blueprintjs/core';
-import { FormattedMessage as T, useIntl } from 'react-intl';
-import moment from 'moment';
-import { sumBy } from 'lodash';
-import classNames from 'classnames';
-
-import { CLASSES } from 'common/classes';
-import { DataTableEditable, Money } from 'components';
-import { transformUpdatedRows } from 'utils';
-import {
- MoneyFieldCell,
- DivFieldCell,
- EmptyDiv,
-} from 'components/DataTableCells';
-
-/**
- * Cell renderer guard.
- */
-const CellRenderer = (content, type) => (props) => {
- if (props.data.length === props.row.index + 1) {
- return '';
- }
- return content(props);
-};
-
-const TotalCellRederer = (content, type) => (props) => {
- if (props.data.length === props.row.index + 1) {
- return ;
- }
- return content(props);
-};
-
-export default function PaymentReceiveItemsTableEditor({
- onClickClearAllLines,
- onUpdateData,
- data,
- errors,
- noResultsMessage,
-}) {
- const transformedData = useMemo(() => {
- const rows = [...data];
- const totalRow = {
- due_amount: sumBy(data, 'due_amount'),
- payment_amount: sumBy(data, 'payment_amount'),
- };
- if (rows.length > 0) {
- rows.push(totalRow);
- }
- return rows;
- }, [data]);
-
- const [localData, setLocalData] = useState(transformedData);
- const { formatMessage } = useIntl();
-
- useEffect(() => {
- if (localData !== transformedData) {
- setLocalData(transformedData);
- }
- }, [setLocalData, localData, transformedData]);
-
- const columns = useMemo(
- () => [
- {
- Header: '#',
- accessor: 'index',
- Cell: ({ row: { index } }) => {index + 1},
- width: 40,
- disableResizing: true,
- disableSortBy: true,
- },
- {
- Header: formatMessage({ id: 'Date' }),
- id: 'invoice_date',
- accessor: (r) => moment(r.invoice_date).format('YYYY MMM DD'),
- Cell: CellRenderer(EmptyDiv, 'invoice_date'),
- disableSortBy: true,
- disableResizing: true,
- width: 250,
- },
-
- {
- Header: formatMessage({ id: 'invocie_number' }),
- accessor: (row) => {
- const invNumber = row?.invoice_no || row?.id;
- return `#INV-${invNumber || ''}`;
- },
- Cell: CellRenderer(EmptyDiv, 'invoice_no'),
- disableSortBy: true,
- className: '',
- },
- {
- Header: formatMessage({ id: 'invoice_amount' }),
- accessor: 'balance',
- Cell: CellRenderer(DivFieldCell, 'balance'),
- disableSortBy: true,
- width: 100,
- className: '',
- },
- {
- Header: formatMessage({ id: 'amount_due' }),
- accessor: 'due_amount',
- Cell: TotalCellRederer(DivFieldCell, 'due_amount'),
- disableSortBy: true,
- width: 150,
- className: '',
- },
- {
- Header: formatMessage({ id: 'payment_amount' }),
- accessor: 'payment_amount',
- Cell: TotalCellRederer(MoneyFieldCell, 'payment_amount'),
- disableSortBy: true,
- width: 150,
- className: '',
- },
- ],
- [formatMessage],
- );
-
- // Handle click clear all lines button.
- const handleClickClearAllLines = () => {
- onClickClearAllLines && onClickClearAllLines();
- };
-
- const rowClassNames = useCallback(
- (row) => ({ 'row--total': localData.length === row.index + 1 }),
- [localData],
- );
-
- // Handle update data.
- const handleUpdateData = useCallback(
- (rowIndex, columnId, value) => {
- const newRows = transformUpdatedRows(
- localData,
- rowIndex,
- columnId,
- value,
- );
- if (newRows.length > 0) {
- newRows.splice(-1, 1);
- }
- setLocalData(newRows);
- onUpdateData && onUpdateData(newRows);
- },
- [localData, setLocalData, onUpdateData],
- );
-
- return (
-
-
-
- }
- totalRow={true}
- />
- );
-}
diff --git a/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/components.js b/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/components.js
new file mode 100644
index 000000000..e37d9d5d4
--- /dev/null
+++ b/client/src/containers/Sales/PaymentReceives/PaymentReceiveForm/components.js
@@ -0,0 +1,111 @@
+import React from 'react';
+import moment from 'moment';
+import { useIntl } from 'react-intl';
+import { safeSumBy, formattedAmount } from 'utils';
+
+/**
+ * Invoice date cell.
+ */
+function InvoiceDateCell({ value }) {
+ return { moment(value).format('YYYY MMM DD') }
+}
+
+/**
+ * Index table cell.
+ */
+function IndexCell({ row: { index } }) {
+ return ({index + 1});
+}
+
+/**
+ * Invoice number table cell accessor.
+ */
+function InvNumberCellAccessor(row) {
+ const invNumber = row?.invoice_no || row?.id;
+ return `#INV-${invNumber || ''}`;
+}
+
+/**
+ * Balance footer cell.
+ */
+function BalanceFooterCell({ rows }) {
+ const total = safeSumBy(rows, 'original.balance');
+ return { formattedAmount(total, 'USD') };
+}
+
+/**
+ * Due amount footer cell.
+ */
+function DueAmountFooterCell({ rows }) {
+ const totalDueAmount = safeSumBy(rows, 'original.due_amount');
+ return { formattedAmount(totalDueAmount, 'USD') };
+}
+
+/**
+ * Payment amount footer cell.
+ */
+function PaymentAmountFooterCell({ rows }) {
+ const totalPaymentAmount = safeSumBy(rows, 'original.payment_amount');
+ return { formattedAmount(totalPaymentAmount, 'USD') };
+}
+
+/**
+ * Retrieve payment receive form entries columns.
+ */
+export const usePaymentReceiveEntriesColumns = () => {
+ const { formatMessage } = useIntl();
+
+ return React.useMemo(
+ () => [
+ {
+ Header: '#',
+ accessor: 'index',
+ Cell: IndexCell,
+ width: 40,
+ disableResizing: true,
+ disableSortBy: true,
+ },
+ {
+ Header: formatMessage({ id: 'Date' }),
+ id: 'invoice_date',
+ accessor: 'invoice_date',
+ Cell: InvoiceDateCell,
+ disableSortBy: true,
+ disableResizing: true,
+ width: 250,
+ },
+ {
+ Header: formatMessage({ id: 'invocie_number' }),
+ accessor: InvNumberCellAccessor,
+ Cell: 'invoice_no',
+ disableSortBy: true,
+ className: '',
+ },
+ {
+ Header: formatMessage({ id: 'invoice_amount' }),
+ accessor: 'balance',
+ Footer: BalanceFooterCell,
+ disableSortBy: true,
+ width: 100,
+ className: '',
+ },
+ {
+ Header: formatMessage({ id: 'amount_due' }),
+ accessor: 'due_amount',
+ Footer: DueAmountFooterCell,
+ disableSortBy: true,
+ width: 150,
+ className: '',
+ },
+ {
+ Header: formatMessage({ id: 'payment_amount' }),
+ accessor: 'payment_amount',
+ Footer: PaymentAmountFooterCell,
+ disableSortBy: true,
+ width: 150,
+ className: '',
+ },
+ ],
+ [formatMessage],
+ )
+}
\ No newline at end of file
diff --git a/client/src/hooks/query/bills.js b/client/src/hooks/query/bills.js
index e0971cb26..3697eeb00 100644
--- a/client/src/hooks/query/bills.js
+++ b/client/src/hooks/query/bills.js
@@ -119,3 +119,26 @@ export function useOpenBill(props) {
},
);
}
+
+/**
+ * Retrieve the due bills of the given vendor id.
+ * @param {number} vendorId -
+ */
+export function useDueBills(vendorId, props) {
+ const states = useQuery(
+ ['BILLS_DUE', vendorId],
+ () =>
+ ApiService.get(`purchases/bills/due`, {
+ params: { vendor_id: vendorId },
+ }),
+ {
+ select: (res) => res.data.bills,
+ ...props,
+ },
+ );
+
+ return {
+ ...states,
+ data: defaultTo(states.data, []),
+ };
+}
diff --git a/client/src/hooks/query/invoices.js b/client/src/hooks/query/invoices.js
index ac206095d..75200b557 100644
--- a/client/src/hooks/query/invoices.js
+++ b/client/src/hooks/query/invoices.js
@@ -77,8 +77,8 @@ export function useInvoices(query, props) {
total: 0,
},
filterMeta: {},
- })
- }
+ }),
+ };
}
/**
@@ -87,27 +87,25 @@ export function useInvoices(query, props) {
export function useDeliverInvoice(props) {
const queryClient = useQueryClient();
- return useMutation(
- (id) => ApiService.post(`sales/invoices/${id}/deliver`),
- {
- onSuccess: (res, id) => {
- queryClient.invalidateQueries('SALE_INVOICES');
- queryClient.invalidateQueries(['SALE_INVOICE', id]);
- },
- ...props,
+ return useMutation((id) => ApiService.post(`sales/invoices/${id}/deliver`), {
+ onSuccess: (res, id) => {
+ queryClient.invalidateQueries('SALE_INVOICES');
+ queryClient.invalidateQueries(['SALE_INVOICE', id]);
},
- );
+ ...props,
+ });
}
/**
* Retrieve the sale invoice details.
*/
export function useInvoice(id, props) {
- const states = useQuery(['SALE_INVOICE', id], () =>
- ApiService.get(`sales/invoices/${id}`),
- {
+ const states = useQuery(
+ ['SALE_INVOICE', id],
+ () => ApiService.get(`sales/invoices/${id}`),
+ {
select: (res) => res.data.sale_invoice,
- ...props
+ ...props,
},
);
@@ -116,3 +114,26 @@ export function useInvoice(id, props) {
data: defaultTo(states.data, {}),
};
}
+
+/**
+ * Retrieve due invoices of the given customer id.
+ * @param {number} customerId - Customer id.
+ */
+export function useDueInvoices(customerId, props) {
+ const states = useQuery(
+ ['SALE_INVOICE_DUE', customerId],
+ () =>
+ ApiService.get(`sales/invoices/payable`, {
+ params: { customer_id: customerId },
+ }),
+ {
+ select: (res) => res.data.sales_invoices,
+ ...props,
+ },
+ );
+
+ return {
+ ...states,
+ data: defaultTo(states.data, []),
+ };
+}
diff --git a/client/src/routes/dashboard.js b/client/src/routes/dashboard.js
index 00e736b57..98528683b 100644
--- a/client/src/routes/dashboard.js
+++ b/client/src/routes/dashboard.js
@@ -450,7 +450,7 @@ export default [
),
),
breadcrumb: 'New Payment Made',
- pageTitle: formatMessage({ id: 'edit_payment_made' }),
+ pageTitle: formatMessage({ id: 'new_payment_made' }),
sidebarShrink: true,
backLink: true,
},