diff --git a/client/src/containers/Purchases/Bills/BillForm/BillForm.js b/client/src/containers/Purchases/Bills/BillForm/BillForm.js
index 33228c3ba..184a19053 100644
--- a/client/src/containers/Purchases/Bills/BillForm/BillForm.js
+++ b/client/src/containers/Purchases/Bills/BillForm/BillForm.js
@@ -4,7 +4,7 @@ import { Intent } from '@blueprintjs/core';
import classNames from 'classnames';
import { useIntl } from 'react-intl';
import { useHistory } from 'react-router-dom';
-import { sumBy, isEmpty, omit } from 'lodash';
+import { isEmpty, omit } from 'lodash';
import { CLASSES } from 'common/classes';
import { EditBillFormSchema, CreateBillFormSchema } from './BillForm.schema';
@@ -68,7 +68,7 @@ export default function BillForm() {
const entries = values.entries.filter(
(item) => item.item_id && item.quantity,
);
- const totalQuantity = safeSumBy(entries, (entry) => entry.quantity);
+ const totalQuantity = safeSumBy(entries, 'quantity');
if (totalQuantity === 0) {
AppToaster.show({
diff --git a/client/src/containers/Purchases/Bills/BillsLanding/BillsList.js b/client/src/containers/Purchases/Bills/BillsLanding/BillsList.js
index 3aad2bd11..e1723e8c4 100644
--- a/client/src/containers/Purchases/Bills/BillsLanding/BillsList.js
+++ b/client/src/containers/Purchases/Bills/BillsLanding/BillsList.js
@@ -1,6 +1,8 @@
import React from 'react';
import { DashboardContentTable, DashboardPageContent } from 'components';
+import 'style/pages/Bills/List.scss';
+
import { BillsListProvider } from './BillsListProvider';
import BillsActionsBar from './BillsActionsBar';
diff --git a/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeItemsTable.js b/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeEntriesTable.js
similarity index 67%
rename from client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeItemsTable.js
rename to client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeEntriesTable.js
index d9850583f..1f6c4a546 100644
--- a/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeItemsTable.js
+++ b/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeEntriesTable.js
@@ -9,31 +9,34 @@ import { DataTableEditable } from 'components';
import { usePaymentMadeEntriesTableColumns } from './components';
import { usePaymentMadeFormContext } from './PaymentMadeFormProvider';
+import { compose, updateTableRow, safeSumBy } from 'utils';
+import withAlertActions from 'containers/Alert/withAlertActions';
/**
* Payment made items table.
*/
-export default function PaymentMadeItemsTable() {
+function PaymentMadeEntriesTable({
+ onUpdateData,
+ entries,
+
+ // #withAlertsActions
+ openAlert
+}) {
const {
paymentVendorId,
- dueBills,
isDueBillsFetching,
- isNewMode,
} = usePaymentMadeFormContext();
const columns = usePaymentMadeEntriesTableColumns();
-
- // Detarmines takes vendor payable bills entries in create mode
- // or payment made entries in edit mode.
- const computedTableEntries = useMemo(() => [], []);
-
- // Triggers `onUpdateData` event that passes changed entries.
- const triggerUpdateData = useCallback((entries) => {}, []);
-
- const triggerOnFetchBillsSuccess = useCallback((bills) => {}, []);
-
+
// Handle update data.
- const handleUpdateData = useCallback((rows) => {}, []);
+ const handleUpdateData = useCallback((rowIndex, columnId, value) => {
+ const newRows = compose(
+ updateTableRow(rowIndex, columnId, value),
+ )(entries);
+
+ onUpdateData(newRows);
+ }, [onUpdateData, entries]);
// Detarmines the right no results message before selecting vendor and aftering
// selecting vendor id.
@@ -41,13 +44,22 @@ export default function PaymentMadeItemsTable() {
? '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.';
+ // Handle clear all lines action.
+ const handleClearAllLines = () => {
+ const fullAmount = safeSumBy(entries, 'payment_amount');
+
+ if (fullAmount > 0) {
+ openAlert('clear-all-lines-payment-made');
+ }
+ }
+
return (
@@ -68,3 +80,7 @@ export default function PaymentMadeItemsTable() {
);
}
+
+export default compose(
+ withAlertActions
+)(PaymentMadeEntriesTable);
\ No newline at end of file
diff --git a/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeForm.js b/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeForm.js
index 3f5e11a85..13fafe03b 100644
--- a/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeForm.js
+++ b/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeForm.js
@@ -2,7 +2,7 @@ import React, { useMemo } from 'react';
import { Formik, Form } from 'formik';
import { Intent } from '@blueprintjs/core';
import { useIntl } from 'react-intl';
-import { sumBy, omit } from 'lodash';
+import { sumBy, pick } from 'lodash';
import classNames from 'classnames';
import { useHistory } from 'react-router-dom';
@@ -11,7 +11,8 @@ import { AppToaster } from 'components';
import PaymentMadeHeader from './PaymentMadeFormHeader';
import PaymentMadeFloatingActions from './PaymentMadeFloatingActions';
import PaymentMadeFooter from './PaymentMadeFooter';
-import PaymentMadeItemsTable from './PaymentMadeItemsTable';
+import PaymentMadeFormBody from './PaymentMadeFormBody';
+import { PaymentMadeInnerProvider } from './PaymentMadeInnerProvider';
import withSettings from 'containers/Settings/withSettings';
import {
@@ -32,7 +33,9 @@ function PaymentMadeForm() {
// Payment made form context.
const {
isNewMode,
- paymentMade,
+ paymentMadeId,
+ paymentMadeEditPage,
+ paymentEntriesEditPage,
submitPayload,
createPaymentMadeMutate,
editPaymentMadeMutate,
@@ -43,14 +46,14 @@ function PaymentMadeForm() {
() => ({
...(!isNewMode
? {
- ...transformToEditForm(paymentMade, []),
+ ...transformToEditForm(paymentMadeEditPage, paymentEntriesEditPage),
}
: {
...defaultPaymentMade,
entries: orderingLinesIndexes(defaultPaymentMade.entries),
}),
}),
- [isNewMode, paymentMade],
+ [isNewMode, paymentMadeEditPage, paymentEntriesEditPage],
);
// Handle the form submit.
@@ -62,9 +65,9 @@ function PaymentMadeForm() {
// Filters entries that have no `bill_id` or `payment_amount`.
const entries = values.entries
- .filter((item) => !item.bill_id || item.payment_amount)
+ .filter((item) => item.bill_id && item.payment_amount)
.map((entry) => ({
- ...omit(entry, ['due_amount']),
+ ...pick(entry, ['payment_amount', 'bill_id']),
}));
// Total payment amount of entries.
const totalPaymentAmount = sumBy(entries, 'payment_amount');
@@ -96,7 +99,11 @@ function PaymentMadeForm() {
submitPayload.resetForm && resetForm();
};
- const onError = ({ response: { error: { data: errors } } }) => {
+ const onError = ({
+ response: {
+ error: { data: errors },
+ },
+ }) => {
const getError = (errorType) => errors.find((e) => e.type === errorType);
if (getError(ERRORS.PAYMENT_NUMBER_NOT_UNIQUE)) {
@@ -109,7 +116,7 @@ function PaymentMadeForm() {
};
if (!isNewMode) {
- editPaymentMadeMutate([paymentMade.id, form])
+ editPaymentMadeMutate([paymentMadeId, form])
.then(onSaved)
.catch(onError);
} else {
@@ -133,13 +140,12 @@ function PaymentMadeForm() {
onSubmit={handleSubmitForm}
>
diff --git a/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFormBody.js b/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFormBody.js
new file mode 100644
index 000000000..334303194
--- /dev/null
+++ b/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFormBody.js
@@ -0,0 +1,23 @@
+import React from 'react';
+import { FastField } from 'formik';
+import classNames from 'classnames';
+import { CLASSES } from 'common/classes';
+import PaymentMadeEntriesTable from './PaymentMadeEntriesTable';
+
+export default function PaymentMadeFormBody() {
+ return (
+
+
+ {({ form, field: { value }, meta: { error, touched } }) => (
+ {
+ form.setFieldValue('entries', newEntries);
+ }}
+ />
+ )}
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFormHeader.js b/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFormHeader.js
index c2f98c3af..528524842 100644
--- a/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFormHeader.js
+++ b/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFormHeader.js
@@ -18,10 +18,10 @@ function PaymentMadeFormHeader({
baseCurrency,
}) {
// Formik form context.
- const { values } = useFormikContext();
+ const { values: { entries } } = useFormikContext();
// Calculate the payment amount of the entries.
- const amountPaid = useMemo(() => sumBy(values, 'payment_amount'), [values]);
+ const amountPaid = useMemo(() => sumBy(entries, 'payment_amount'), [entries]);
return (
diff --git a/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFormHeaderFields.js b/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFormHeaderFields.js
index a64da7c51..886912dae 100644
--- a/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFormHeaderFields.js
+++ b/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFormHeaderFields.js
@@ -1,25 +1,27 @@
-import React from 'react';
+import React, { useMemo } from 'react';
import {
FormGroup,
InputGroup,
Position,
Classes,
ControlGroup,
+ Button
} from '@blueprintjs/core';
import { DateInput } from '@blueprintjs/datetime';
-import { FastField } from 'formik';
+import { FastField, Field, useFormikContext, ErrorMessage } from 'formik';
import { FormattedMessage as T } from 'react-intl';
+import { toSafeInteger } from 'lodash';
import classNames from 'classnames';
import { CLASSES } from 'common/classes';
import {
AccountsSelectList,
ContactSelecetList,
- ErrorMessage,
FieldRequiredHint,
InputPrependText,
Money,
Hint,
Icon,
+ MoneyInputGroup
} from 'components';
import withSettings from 'containers/Settings/withSettings';
import { usePaymentMadeFormContext } from './PaymentMadeFormProvider';
@@ -28,20 +30,43 @@ import {
tansformDateValue,
inputIntent,
compose,
+ safeSumBy,
+ fullAmountPaymentEntries,
+ amountPaymentEntries,
} from 'utils';
/**
* Payment made form header fields.
*/
function PaymentMadeFormHeaderFields({ baseCurrency }) {
+ // Formik form context.
+ const { values: { entries }, setFieldValue } = useFormikContext();
+
+ // Payment made form context.
const {
vendors,
accounts,
isNewMode,
setPaymentVendorId,
} = usePaymentMadeFormContext();
+
+ // Sumation of payable full-amount.
+ const payableFullAmount = useMemo(() => safeSumBy(entries, 'due_amount'), [entries]);
+
+ // Handle receive full-amount click.
+ const handleReceiveFullAmountClick = () => {
+ const newEntries = fullAmountPaymentEntries(entries);
+ const fullAmount = safeSumBy(newEntries, 'payment_amount');
- const payableFullAmount = 0;
+ 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 (
@@ -96,8 +121,8 @@ function PaymentMadeFormHeaderFields({ baseCurrency }) {
{/* ------------ Full amount ------------ */}
-
- {({ form, field, meta: { error, touched } }) => (
+
+ {({ form, field: { value }, meta: { error, touched } }) => (
}
inline={true}
@@ -108,24 +133,27 @@ function PaymentMadeFormHeaderFields({ baseCurrency }) {
>
- {
+ setFieldValue('full_amount', value);
+ }}
+ onBlurValue={onFullAmountBlur}
/>
-
Receive full amount (
)
-
+
)}
-
+
{/* ------------ Payment number ------------ */}
diff --git a/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFormProvider.js b/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFormProvider.js
index c0af6177c..d1fd7a37e 100644
--- a/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFormProvider.js
+++ b/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFormProvider.js
@@ -3,11 +3,10 @@ import {
useAccounts,
useVendors,
useItems,
- usePaymentMade,
+ usePaymentMadeEditPage,
useSettings,
useCreatePaymentMade,
useEditPaymentMade,
- useDueBills,
} from 'hooks/query';
import { DashboardInsider } from 'components';
@@ -39,20 +38,13 @@ function PaymentMadeFormProvider({ paymentMadeId, ...props }) {
// Handle fetch specific payment made details.
const {
- data: { paymentMade, payableBills, paymentBills },
+ data: { paymentMade: paymentMadeEditPage, entries: paymentEntriesEditPage },
isFetching: isPaymentFetching,
isLoading: isPaymentLoading,
- } = usePaymentMade(paymentMadeId, {
+ } = usePaymentMadeEditPage(paymentMadeId, {
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();
@@ -66,12 +58,10 @@ function PaymentMadeFormProvider({ paymentMadeId, ...props }) {
const provider = {
paymentMadeId,
accounts,
- paymentMade,
- payableBills,
- paymentBills,
+ paymentEntriesEditPage,
+ paymentMadeEditPage,
vendors,
items,
- dueBills,
submitPayload,
paymentVendorId,
@@ -82,8 +72,6 @@ function PaymentMadeFormProvider({ paymentMadeId, ...props }) {
isVendorsFetching,
isPaymentFetching,
isPaymentLoading,
- isDueBillsLoading,
- isDueBillsFetching,
createPaymentMadeMutate,
editPaymentMadeMutate,
diff --git a/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeInnerProvider.js b/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeInnerProvider.js
new file mode 100644
index 000000000..9da5fe240
--- /dev/null
+++ b/client/src/containers/Purchases/PaymentMades/PaymentForm/PaymentMadeInnerProvider.js
@@ -0,0 +1,47 @@
+import { useFormikContext } from 'formik';
+import { isEmpty } from 'lodash';
+import React, { createContext, useContext, useEffect } from 'react';
+import { usePaymentMadeNewPageEntries } from 'hooks/query';
+import { usePaymentMadeFormContext } from './PaymentMadeFormProvider';
+
+const PaymentMadeInnerContext = createContext();
+
+/**
+ * Payment made inner form provider.
+ */
+function PaymentMadeInnerProvider({ ...props }) {
+ const { isNewMode } = usePaymentMadeFormContext();
+
+ // Formik context.
+ const {
+ values: { vendor_id: vendorId },
+ setFieldValue,
+ } = useFormikContext();
+
+ const {
+ data: newPageEntries,
+ isLoading: isNewEntriesLoading,
+ isFetching: isNewEntriesFetching,
+ } = usePaymentMadeNewPageEntries(vendorId, {
+ enabled: !!vendorId && isNewMode,
+ });
+
+ useEffect(() => {
+ if (!isNewEntriesFetching && !isEmpty(newPageEntries)) {
+ setFieldValue('entries', newPageEntries)
+ }
+ }, [isNewEntriesFetching, newPageEntries, setFieldValue]);
+
+ // Provider payload.
+ const provider = {
+ newPageEntries,
+ isNewEntriesLoading,
+ isNewEntriesFetching
+ };
+
+ return ;
+}
+
+const usePaymentMadeInnerContext = () => useContext(PaymentMadeInnerContext);
+
+export { PaymentMadeInnerProvider, usePaymentMadeInnerContext };
diff --git a/client/src/containers/Purchases/PaymentMades/PaymentForm/components.js b/client/src/containers/Purchases/PaymentMades/PaymentForm/components.js
index 9e6383341..9b95d879c 100644
--- a/client/src/containers/Purchases/PaymentMades/PaymentForm/components.js
+++ b/client/src/containers/Purchases/PaymentMades/PaymentForm/components.js
@@ -3,16 +3,17 @@ import { useIntl } from "react-intl";
import moment from 'moment';
import { Money } from 'components';
import { safeSumBy, formattedAmount } from 'utils';
+import { MoneyFieldCell } from 'components/DataTableCells';
function BillNumberAccessor(row) {
- return `#${row?.bill_number || ''}`
+ return row?.bill_no ? row?.bill_no : '-';
}
function IndexTableCell({ row: { index } }) {
return ({index + 1});
}
-function BillDateTableCell({ value }) {
+function BillDateCell({ value }) {
return moment(value).format('YYYY MMM DD');
}
/**
@@ -39,6 +40,14 @@ function PaymentAmountFooterCell({ rows }) {
return { formattedAmount(totalPaymentAmount, 'USD') };
}
+/**
+ * Mobey table cell.
+ */
+function MoneyTableCell({ value }) {
+ return
+}
+
+
/**
* Payment made entries table columns
*/
@@ -54,13 +63,15 @@ export function usePaymentMadeEntriesTableColumns() {
width: 40,
disableResizing: true,
disableSortBy: true,
+ className: 'index'
},
{
Header: formatMessage({ id: 'Date' }),
id: 'bill_date',
accessor: 'bill_date',
- Cell: BillDateTableCell,
+ Cell: BillDateCell,
disableSortBy: true,
+ width: 250,
},
{
Header: formatMessage({ id: 'bill_number' }),
@@ -71,6 +82,7 @@ export function usePaymentMadeEntriesTableColumns() {
{
Header: formatMessage({ id: 'bill_amount' }),
accessor: 'amount',
+ Cell: MoneyTableCell,
Footer: AmountFooterCell,
disableSortBy: true,
className: '',
@@ -78,6 +90,7 @@ export function usePaymentMadeEntriesTableColumns() {
{
Header: formatMessage({ id: 'amount_due' }),
accessor: 'due_amount',
+ Cell: MoneyTableCell,
Footer: DueAmountFooterCell,
disableSortBy: true,
className: '',
@@ -85,6 +98,7 @@ export function usePaymentMadeEntriesTableColumns() {
{
Header: formatMessage({ id: 'payment_amount' }),
accessor: 'payment_amount',
+ Cell: MoneyFieldCell,
Footer: PaymentAmountFooterCell,
disableSortBy: true,
className: '',
diff --git a/client/src/containers/Purchases/PaymentMades/PaymentForm/utils.js b/client/src/containers/Purchases/PaymentMades/PaymentForm/utils.js
index 244e4ae10..858d4d282 100644
--- a/client/src/containers/Purchases/PaymentMades/PaymentForm/utils.js
+++ b/client/src/containers/Purchases/PaymentMades/PaymentForm/utils.js
@@ -1,12 +1,10 @@
import moment from 'moment';
-import { sumBy } from 'lodash';
-import { transformToForm } from 'utils';
+import { safeSumBy, transformToForm } from 'utils';
export const ERRORS = {
-PAYMENT_NUMBER_NOT_UNIQUE: 'PAYMENT.NUMBER.NOT.UNIQUE',
+ PAYMENT_NUMBER_NOT_UNIQUE: 'PAYMENT.NUMBER.NOT.UNIQUE',
};
-
// Default payment made entry values.
export const defaultPaymentMadeEntry = {
bill_id: '',
@@ -30,7 +28,7 @@ export const defaultPaymentMade = {
export const transformToEditForm = (paymentMade, paymentMadeEntries) => {
return {
...transformToForm(paymentMade, defaultPaymentMade),
- full_amount: sumBy(paymentMade.entries, 'payment_amount'),
+ full_amount: safeSumBy(paymentMadeEntries, 'payment_amount'),
entries: [
...paymentMadeEntries.map((paymentMadeEntry) => ({
...transformToForm(paymentMadeEntry, defaultPaymentMadeEntry),
diff --git a/client/src/containers/Purchases/PaymentMades/PaymentsLanding/PaymentMadeList.js b/client/src/containers/Purchases/PaymentMades/PaymentsLanding/PaymentMadeList.js
index e6e597d2f..fa5ee8b18 100644
--- a/client/src/containers/Purchases/PaymentMades/PaymentsLanding/PaymentMadeList.js
+++ b/client/src/containers/Purchases/PaymentMades/PaymentsLanding/PaymentMadeList.js
@@ -1,4 +1,7 @@
import React from 'react';
+
+import 'style/pages/PaymentMade/List.scss';
+
import { DashboardContentTable, DashboardPageContent } from 'components';
import PaymentMadeActionsBar from './PaymentMadeActionsBar';
import PaymentMadesAlerts from '../PaymentMadesAlerts';
diff --git a/client/src/hooks/query/paymentMades.js b/client/src/hooks/query/paymentMades.js
index 994c7ed4b..00789db87 100644
--- a/client/src/hooks/query/paymentMades.js
+++ b/client/src/hooks/query/paymentMades.js
@@ -27,7 +27,7 @@ export function usePaymentMades(query, props) {
data: defaultTo(states.data, {
paymentMades: [],
pagination: {},
- filterMeta: {}
+ filterMeta: {},
}),
};
}
@@ -42,8 +42,9 @@ export function useCreatePaymentMade(props) {
return useMutation(
(values) => apiRequest.post('purchases/bill_payments', values),
{
- onSuccess: () => {
+ onSuccess: (res, values) => {
client.invalidateQueries('PAYMENT_MADES');
+ client.invalidateQueries(['PAYMENT_MADE_NEW_PAGE_ENTRIES', values.vendor_id]);
},
...props,
},
@@ -63,6 +64,8 @@ export function useEditPaymentMade(props) {
onSuccess: (res, [id, values]) => {
client.invalidateQueries('PAYMENT_MADES');
client.invalidateQueries(['PAYMENT_MADE', id]);
+
+ client.invalidateQueries(['PAYMENT_MADE_NEW_PAGE_ENTRIES', values.vendor_id]);
},
...props,
},
@@ -82,6 +85,7 @@ export function useDeletePaymentMade(props) {
onSuccess: (res, id) => {
client.invalidateQueries('PAYMENT_MADES');
client.invalidateQueries(['PAYMENT_MADE', id]);
+
},
...props,
},
@@ -91,17 +95,16 @@ export function useDeletePaymentMade(props) {
/**
* Retrieve specific payment made.
*/
-export function usePaymentMade(id, props) {
+export function usePaymentMadeEditPage(id, props) {
const apiRequest = useApiRequest();
const states = useQuery(
['PAYMENT_MADE', id],
- () => apiRequest.get(`purchases/bill_payments/${id}`),
+ () => apiRequest.get(`purchases/bill_payments/${id}/edit-page`),
{
- select: res => ({
+ select: (res) => ({
paymentMade: res.data.bill_payment,
- payableBills: res.data.payable_bills,
- paymentBills: res.data.payment_bills,
+ entries: res.data.entries,
}),
...props,
},
@@ -112,3 +115,23 @@ export function usePaymentMade(id, props) {
data: defaultTo(states.data, {}),
};
}
+
+/**
+ * Retreive payment made new page entries.
+ * @param {number} vendorId -
+ */
+export function usePaymentMadeNewPageEntries(vendorId, props) {
+ const apiRequest = useApiRequest();
+
+ return useQuery(
+ ['PAYMENT_MADE_NEW_PAGE_ENTRIES', vendorId],
+ () =>
+ apiRequest.get(`purchases/bill_payments/new-page/entries`, {
+ params: { vendor_id: vendorId },
+ }),
+ {
+ select: (res) => res.data.entries,
+ ...props,
+ },
+ );
+}
diff --git a/client/src/hooks/useRequest.js b/client/src/hooks/useRequest.js
index cc53c4436..f3cc834c6 100644
--- a/client/src/hooks/useRequest.js
+++ b/client/src/hooks/useRequest.js
@@ -26,7 +26,7 @@ export default function useApiRequest() {
const locale = 'en';
if (token) {
- request.headers.common['x-access-token'] = token;
+ request.headers.common['X-Access-Token'] = token;
}
if (organizationId) {
request.headers.common['organization-id'] = organizationId;
diff --git a/client/src/style/pages/Bills/List.scss b/client/src/style/pages/Bills/List.scss
new file mode 100644
index 000000000..f624d5afe
--- /dev/null
+++ b/client/src/style/pages/Bills/List.scss
@@ -0,0 +1,18 @@
+
+.dashboard__insider--bills{
+
+ .bigcapital-datatable{
+
+ .tbody{
+
+ .td.amount {
+
+ .cell-inner{
+ > span{
+ font-weight: 600;
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/client/src/style/pages/PaymentMade/List.scss b/client/src/style/pages/PaymentMade/List.scss
new file mode 100644
index 000000000..249dfd8d5
--- /dev/null
+++ b/client/src/style/pages/PaymentMade/List.scss
@@ -0,0 +1,18 @@
+
+.dashboard__insider--payment-mades-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/PaymentMade/PageForm.scss b/client/src/style/pages/PaymentMade/PageForm.scss
index 0ab552952..164c2efeb 100644
--- a/client/src/style/pages/PaymentMade/PageForm.scss
+++ b/client/src/style/pages/PaymentMade/PageForm.scss
@@ -19,10 +19,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/utils.js b/client/src/utils.js
index 7a595ba06..197a515d1 100644
--- a/client/src/utils.js
+++ b/client/src/utils.js
@@ -576,4 +576,28 @@ export function safeSumBy(entries, getter) {
.map(row => toSafeNumber(_.get(row, getter)))
.sum()
.value();
-}
\ No newline at end of file
+}
+
+
+
+export const fullAmountPaymentEntries = (entries) => {
+ return entries.map((item) => ({
+ ...item,
+ payment_amount: item.due_amount,
+ }));
+}
+
+
+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,
+ };
+ });
+};
\ No newline at end of file
diff --git a/server/src/api/controllers/FinancialStatements.ts b/server/src/api/controllers/FinancialStatements.ts
index eddad6615..43a0eb9b1 100644
--- a/server/src/api/controllers/FinancialStatements.ts
+++ b/server/src/api/controllers/FinancialStatements.ts
@@ -17,14 +17,32 @@ export default class FinancialStatementsService {
router() {
const router = Router();
- router.use('/balance_sheet', Container.get(BalanceSheetController).router());
- router.use('/profit_loss_sheet', Container.get(ProfitLossController).router());
- router.use('/general_ledger', Container.get(GeneralLedgerController).router());
- router.use('/trial_balance_sheet', Container.get(TrialBalanceSheetController).router());
+ router.use(
+ '/balance_sheet',
+ Container.get(BalanceSheetController).router()
+ );
+ router.use(
+ '/profit_loss_sheet',
+ Container.get(ProfitLossController).router()
+ );
+ router.use(
+ '/general_ledger',
+ Container.get(GeneralLedgerController).router()
+ );
+ router.use(
+ '/trial_balance_sheet',
+ Container.get(TrialBalanceSheetController).router()
+ );
router.use('/journal', Container.get(JournalSheetController).router());
- router.use('/receivable_aging_summary', Container.get(ARAgingSummary).router());
- router.use('/payable_aging_summary', Container.get(APAgingSummary).router());
+ router.use(
+ '/receivable_aging_summary',
+ Container.get(ARAgingSummary).router()
+ );
+ router.use(
+ '/payable_aging_summary',
+ Container.get(APAgingSummary).router()
+ );
return router;
}
-};
+}
diff --git a/server/src/api/controllers/Purchases/BillsPayments.ts b/server/src/api/controllers/Purchases/BillsPayments.ts
index dddddf8d2..16b5d34fe 100644
--- a/server/src/api/controllers/Purchases/BillsPayments.ts
+++ b/server/src/api/controllers/Purchases/BillsPayments.ts
@@ -8,6 +8,7 @@ import BillPaymentsService from 'services/Purchases/BillPayments';
import DynamicListingService from 'services/DynamicListing/DynamicListService';
import AccountsService from 'services/Accounts/AccountsService';
import ResourceController from '../Resources';
+import { Request } from 'express-validator/src/base';
/**
* Bills payments controller.
@@ -30,6 +31,20 @@ export default class BillsPayments extends BaseController {
router() {
const router = Router();
+ router.get(
+ '/new-page/entries',
+ [query('vendor_id').exists()],
+ this.validationResult,
+ asyncMiddleware(this.getBillPaymentNewPageEntries.bind(this)),
+ this.handleServiceError
+ );
+ router.get(
+ '/:id/edit-page',
+ this.specificBillPaymentValidateSchema,
+ this.validationResult,
+ asyncMiddleware(this.getBillPaymentEditPage.bind(this)),
+ this.handleServiceError
+ );
router.post(
'/',
[...this.billPaymentSchemaValidation],
@@ -76,6 +91,7 @@ export default class BillsPayments extends BaseController {
this.handleServiceError,
this.dynamicListService.handlerErrorsToResponse
);
+
return router;
}
@@ -118,6 +134,53 @@ export default class BillsPayments extends BaseController {
];
}
+ /**
+ * Retrieve bill payment new page entries.
+ * @param {Request} req -
+ * @param {Response} res -
+ */
+ async getBillPaymentNewPageEntries(req: Request, res: Response) {
+ const { tenantId } = req;
+ const { vendorId } = this.matchedQueryData(req);
+
+ try {
+ const entries = await this.billPaymentService.getNewPageEntries(
+ tenantId,
+ vendorId
+ );
+ return res.status(200).send({
+ entries: this.transfromToResponse(entries),
+ });
+ } catch (error) {}
+ }
+
+ /**
+ * Retrieve the bill payment edit page details.
+ * @param {Request} req
+ * @param {Response} res
+ */
+ async getBillPaymentEditPage(req: Request, res: Response, next: NextFunction) {
+ const { tenantId } = req;
+ const { id: paymentReceiveId } = req.params;
+
+ try {
+ const {
+ billPayment,
+ entries,
+ } = await this.billPaymentService.getBillPaymentEditPage(
+ tenantId,
+ paymentReceiveId,
+ );
+
+ return res.status(200).send({
+ bill_payment: this.transfromToResponse(billPayment),
+ entries: this.transfromToResponse(entries),
+ });
+ } catch (error) {
+ next(error);
+ }
+ }
+
/**
* Creates a bill payment.
* @async
diff --git a/server/src/api/controllers/Sales/PaymentReceives.ts b/server/src/api/controllers/Sales/PaymentReceives.ts
index 282a2888e..8f9b1849a 100644
--- a/server/src/api/controllers/Sales/PaymentReceives.ts
+++ b/server/src/api/controllers/Sales/PaymentReceives.ts
@@ -54,6 +54,15 @@ export default class PaymentReceivesController extends BaseController {
asyncMiddleware(this.getPaymentReceiveEditPage.bind(this)),
this.handleServiceErrors
);
+ router.get(
+ '/new-page/entries',
+ [
+ query('customer_id').exists().isNumeric().toInt(),
+ ],
+ this.validationResult,
+ asyncMiddleware(this.getPaymentReceiveNewPageEntries.bind(this)),
+ this.getPaymentReceiveNewPageEntries.bind(this)
+ );
router.get(
'/',
this.validatePaymentReceiveList,
@@ -303,6 +312,26 @@ export default class PaymentReceivesController extends BaseController {
}
}
+ /**
+ * Retrieve payment receive new page receivable entries.
+ * @param {Request} req - Request.
+ * @param {Response} res - Response.
+ */
+ async getPaymentReceiveNewPageEntries(req, res) {
+ const { tenantId } = req;
+ const { customerId } = this.matchedQueryData(req);
+
+ try {
+ const entries = await this.paymentReceiveService.getNewPageEntries(
+ tenantId,
+ customerId
+ );
+ return res.status(200).send({
+ entries: this.transfromToResponse(entries),
+ });
+ } catch (error) {}
+ }
+
/**
* Handles service errors.
* @param error
diff --git a/server/src/interfaces/BillPayment.ts b/server/src/interfaces/BillPayment.ts
index 4c31923f8..0de2cbd7e 100644
--- a/server/src/interfaces/BillPayment.ts
+++ b/server/src/interfaces/BillPayment.ts
@@ -32,4 +32,15 @@ export interface IBillPaymentDTO {
description: string,
reference: string,
entries: IBillPaymentEntryDTO[],
+};
+
+export interface IBillReceivePageEntry {
+ billId: number,
+ entryType: string,
+ billNo: string,
+ dueAmount: number,
+ amount: number,
+ totalPaymentAmount: number,
+ paymentAmount: number,
+ date: Date|string,
};
\ No newline at end of file
diff --git a/server/src/interfaces/PaymentReceive.ts b/server/src/interfaces/PaymentReceive.ts
index 9c04496fa..a545b8a01 100644
--- a/server/src/interfaces/PaymentReceive.ts
+++ b/server/src/interfaces/PaymentReceive.ts
@@ -52,7 +52,7 @@ export interface IPaymentReceivesFilter extends IDynamicListFilterDTO {
stringifiedFilterRoles?: string,
}
-export interface IPaymentReceiveEditPageEntry {
+export interface IPaymentReceivePageEntry {
invoiceId: number,
entryType: string,
invoiceNo: string,
@@ -65,5 +65,5 @@ export interface IPaymentReceiveEditPageEntry {
export interface IPaymentReceiveEditPage {
paymentReceive: IPaymentReceive,
- entries: IPaymentReceiveEditPageEntry[];
+ entries: IPaymentReceivePageEntry[];
};
\ No newline at end of file
diff --git a/server/src/services/Purchases/BillPayments.ts b/server/src/services/Purchases/BillPayments.ts
index c66aa678a..b126b3144 100644
--- a/server/src/services/Purchases/BillPayments.ts
+++ b/server/src/services/Purchases/BillPayments.ts
@@ -15,6 +15,7 @@ import {
IPaginationMeta,
IFilterMeta,
IBillPaymentEntry,
+ IBillReceivePageEntry,
} from 'interfaces';
import AccountsService from 'services/Accounts/AccountsService';
import JournalPoster from 'services/Accounting/JournalPoster';
@@ -26,6 +27,7 @@ import DynamicListingService from 'services/DynamicListing/DynamicListService';
import { entriesAmountDiff, formatDateFields } from 'utils';
import { ServiceError } from 'exceptions';
import { ACCOUNT_PARENT_TYPE } from 'data/AccountTypes';
+import PayableAgingSummaryService from 'services/FinancialStatements/AgingSummary/APAgingSummaryService';
const ERRORS = {
BILL_VENDOR_NOT_FOUND: 'VENDOR_NOT_FOUND',
@@ -598,48 +600,43 @@ export default class BillPaymentsService {
* @param {number} billPaymentId - The bill payment id.
* @return {object}
*/
- public async getBillPayment(
+ public async getBillPaymentEditPage(
tenantId: number,
billPaymentId: number
): Promise<{
- billPayment: IBillPayment;
- payableBills: IBill[];
- paymentMadeBills: IBill[];
+ billPayment: Omit;
+ entries: IBillReceivePageEntry[];
}> {
const { BillPayment, Bill } = this.tenancy.models(tenantId);
const billPayment = await BillPayment.query()
.findById(billPaymentId)
- .withGraphFetched('entries.bill')
- .withGraphFetched('vendor')
- .withGraphFetched('paymentAccount');
+ .withGraphFetched('entries.bill');
+ // Throw not found the bill payment.
if (!billPayment) {
throw new ServiceError(ERRORS.PAYMENT_MADE_NOT_FOUND);
}
- const billsIds = billPayment.entries.map((entry) => entry.billId);
-
- // Retrieve all payable bills that assocaited to the payment made transaction.
- const payableBills = await Bill.query()
- .modify('dueBills')
- .whereNotIn('id', billsIds)
- .where('vendor_id', billPayment.vendorId)
- .orderBy('bill_date', 'ASC');
-
- // Retrieve all payment made assocaited bills.
- const paymentMadeBills = billPayment.entries.map((entry) => ({
- ...entry.bill,
- dueAmount: entry.bill.dueAmount + entry.paymentAmount,
+ const paymentEntries = billPayment.entries.map((entry) => ({
+ ...this.mapBillToPageEntry(entry.bill),
+ paymentAmount: entry.paymentAmount,
}));
+ const resPayableBills = await Bill.query()
+ .modify('dueBills')
+ .where('vendor_id', billPayment.vendorId)
+ .whereNotIn(
+ 'id',
+ billPayment.entries.map((e) => e.billId),
+ )
+ .orderBy('bill_date', 'ASC');
+
+ // Mapping the payable bills to entries.
+ const restPayableEntries = resPayableBills.map(this.mapBillToPageEntry);
+ const entries = [...paymentEntries, ...restPayableEntries];
+
return {
- billPayment: {
- ...billPayment,
- entries: billPayment.entries.map((entry) => ({
- ...omit(entry, ['bill']),
- })),
- },
- payableBills,
- paymentMadeBills,
+ billPayment: omit(billPayment, ['entries']),
+ entries
};
}
@@ -678,4 +675,55 @@ export default class BillPaymentsService {
);
await Promise.all(opers);
}
+
+ /**
+ * Retrive edit page invoices entries from the given sale invoices models.
+ * @param {ISaleInvoice[]} invoices - Invoices.
+ * @return {IPaymentReceiveEditPageEntry}
+ */
+ public mapBillToPageEntry(bill: IBill): IBillReceivePageEntry {
+ return {
+ entryType: 'invoice',
+ billId: bill.id,
+ dueAmount: bill.dueAmount + bill.paymentAmount,
+ amount: bill.amount,
+ billNo: bill.billNumber,
+ totalPaymentAmount: bill.paymentAmount,
+ paymentAmount: bill.paymentAmount,
+ date: bill.billDate,
+ };
+ }
+
+ public mapBillToNewPageEntry(bill: IBill): IBillReceivePageEntry {
+ return {
+ entryType: 'invoice',
+ billId: bill.id,
+ dueAmount: bill.dueAmount,
+ amount: bill.amount,
+ billNo: bill.billNumber,
+ date: bill.billDate,
+ totalPaymentAmount: bill.paymentAmount,
+ paymentAmount: 0,
+ }
+ }
+
+ /**
+ * Retrieve the payable entries of the new page once vendor be selected.
+ * @param {number} tenantId
+ * @param {number} vendorId
+ */
+ async getNewPageEntries(
+ tenantId: number,
+ vendorId: number,
+ ): Promise {
+ const { Bill } = this.tenancy.models(tenantId);
+
+ // Retrieve all payable bills that assocaited to the payment made transaction.
+ const payableBills = await Bill.query()
+ .modify('dueBills')
+ .where('vendor_id', vendorId)
+ .orderBy('bill_date', 'ASC');
+
+ return payableBills.map(this.mapBillToNewPageEntry);
+ }
}
diff --git a/server/src/services/Sales/PaymentsReceives.ts b/server/src/services/Sales/PaymentsReceives.ts
index 4a0616e41..99be07bd3 100644
--- a/server/src/services/Sales/PaymentsReceives.ts
+++ b/server/src/services/Sales/PaymentsReceives.ts
@@ -18,7 +18,7 @@ import {
ISaleInvoice,
ISystemService,
ISystemUser,
- IPaymentReceiveEditPageEntry,
+ IPaymentReceivePageEntry,
} from 'interfaces';
import AccountsService from 'services/Accounts/AccountsService';
import JournalPoster from 'services/Accounting/JournalPoster';
@@ -471,7 +471,7 @@ export default class PaymentReceiveService {
* @param {ISaleInvoice[]} invoices - Invoices.
* @return {IPaymentReceiveEditPageEntry}
*/
- public invoicesToEditPageEntries(
+ public invoiceToPageEntry(
invoice: ISaleInvoice
): IPaymentReceiveEditPageEntry {
return {
@@ -485,7 +485,7 @@ export default class PaymentReceiveService {
date: invoice.invoiceDate,
};
}
-
+
/**
* Retrieve the payment receive details of the given id.
* @param {number} tenantId - Tenant id.
@@ -494,9 +494,10 @@ export default class PaymentReceiveService {
public async getPaymentReceiveEditPage(
tenantId: number,
paymentReceiveId: number,
+ authorizedUser: ISystemUser
): Promise<{
- paymentReceive: IPaymentReceive;
- entries: IPaymentReceiveEditPageEntry[];
+ paymentReceive: Omit;
+ entries: IPaymentReceivePageEntry[];
}> {
const { PaymentReceive, SaleInvoice } = this.tenancy.models(tenantId);
@@ -509,14 +510,8 @@ export default class PaymentReceiveService {
if (!paymentReceive) {
throw new ServiceError(ERRORS.PAYMENT_RECEIVE_NOT_EXISTS);
}
-
- // Mapping the entries invoices.
- const entriesInvoicesIds = paymentReceive.entries.map(
- (entry) => entry.invoiceId
- );
-
const paymentEntries = paymentReceive.entries.map((entry) => ({
- ...this.invoicesToEditPageEntries(entry.invoice),
+ ...this.invoiceToPageEntry(entry.invoice),
paymentAmount: entry.paymentAmount,
}));
@@ -524,17 +519,16 @@ export default class PaymentReceiveService {
const restReceivableInvoices = await SaleInvoice.query()
.modify('dueInvoices')
.where('customer_id', paymentReceive.customerId)
- .whereNotIn('id', entriesInvoicesIds)
+ .whereNotIn(
+ 'id',
+ paymentReceive.entries.map((entry) => entry.invoiceId)
+ )
.orderBy('invoice_date', 'ASC');
const restReceivableEntries = restReceivableInvoices.map(
- this.invoicesToEditPageEntries
+ this.invoiceToPageEntry
);
-
- const entries = [
- ...paymentEntries,
- ...restReceivableEntries,
- ];
+ const entries = [...paymentEntries, ...restReceivableEntries];
return {
paymentReceive: omit(paymentReceive, ['entries']),
@@ -616,6 +610,7 @@ export default class PaymentReceiveService {
paymentReceiveId: number
) {
const { PaymentReceive } = this.tenancy.models(tenantId);
+
return PaymentReceive.query()
.where('id', paymentReceiveId)
.withGraphFetched('invoices')
@@ -739,7 +734,6 @@ export default class PaymentReceiveService {
if (diffEntry.paymentAmount === 0) {
return;
}
-
const oper = SaleInvoice.changePaymentAmount(
diffEntry.invoiceId,
diffEntry.paymentAmount
@@ -748,4 +742,22 @@ export default class PaymentReceiveService {
});
await Promise.all([...opers]);
}
+
+ /**
+ * Retrieve payment receive new page receivable entries.
+ * @param {number} tenantId - Tenant id.
+ * @param {number} vendorId - Vendor id.
+ * @return {IPaymentReceivePageEntry[]}
+ */
+ async getNewPageEntries(tenantId: number, customerId: number) {
+ const { SaleInvoice } = this.tenancy.models(tenantId);
+
+ // Retrieve due invoices.
+ const entries = await SaleInvoice.query()
+ .modify('dueInvoices')
+ .where('customer_id', customerId)
+ .orderBy('invoice_date', 'ASC');
+
+ return entries.map(this.invoiceToPageEntry);
+ }
}