mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-19 14:20:31 +00:00
refactoring: payment made form.
This commit is contained in:
@@ -4,7 +4,7 @@ import { Intent } from '@blueprintjs/core';
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { sumBy, isEmpty, omit } from 'lodash';
|
import { isEmpty, omit } from 'lodash';
|
||||||
import { CLASSES } from 'common/classes';
|
import { CLASSES } from 'common/classes';
|
||||||
|
|
||||||
import { EditBillFormSchema, CreateBillFormSchema } from './BillForm.schema';
|
import { EditBillFormSchema, CreateBillFormSchema } from './BillForm.schema';
|
||||||
@@ -68,7 +68,7 @@ export default function BillForm() {
|
|||||||
const entries = values.entries.filter(
|
const entries = values.entries.filter(
|
||||||
(item) => item.item_id && item.quantity,
|
(item) => item.item_id && item.quantity,
|
||||||
);
|
);
|
||||||
const totalQuantity = safeSumBy(entries, (entry) => entry.quantity);
|
const totalQuantity = safeSumBy(entries, 'quantity');
|
||||||
|
|
||||||
if (totalQuantity === 0) {
|
if (totalQuantity === 0) {
|
||||||
AppToaster.show({
|
AppToaster.show({
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { DashboardContentTable, DashboardPageContent } from 'components';
|
import { DashboardContentTable, DashboardPageContent } from 'components';
|
||||||
|
|
||||||
|
import 'style/pages/Bills/List.scss';
|
||||||
|
|
||||||
import { BillsListProvider } from './BillsListProvider';
|
import { BillsListProvider } from './BillsListProvider';
|
||||||
|
|
||||||
import BillsActionsBar from './BillsActionsBar';
|
import BillsActionsBar from './BillsActionsBar';
|
||||||
|
|||||||
@@ -9,31 +9,34 @@ import { DataTableEditable } from 'components';
|
|||||||
import { usePaymentMadeEntriesTableColumns } from './components';
|
import { usePaymentMadeEntriesTableColumns } from './components';
|
||||||
|
|
||||||
import { usePaymentMadeFormContext } from './PaymentMadeFormProvider';
|
import { usePaymentMadeFormContext } from './PaymentMadeFormProvider';
|
||||||
|
import { compose, updateTableRow, safeSumBy } from 'utils';
|
||||||
|
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Payment made items table.
|
* Payment made items table.
|
||||||
*/
|
*/
|
||||||
export default function PaymentMadeItemsTable() {
|
function PaymentMadeEntriesTable({
|
||||||
|
onUpdateData,
|
||||||
|
entries,
|
||||||
|
|
||||||
|
// #withAlertsActions
|
||||||
|
openAlert
|
||||||
|
}) {
|
||||||
const {
|
const {
|
||||||
paymentVendorId,
|
paymentVendorId,
|
||||||
dueBills,
|
|
||||||
isDueBillsFetching,
|
isDueBillsFetching,
|
||||||
isNewMode,
|
|
||||||
} = usePaymentMadeFormContext();
|
} = usePaymentMadeFormContext();
|
||||||
|
|
||||||
const columns = usePaymentMadeEntriesTableColumns();
|
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.
|
// 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
|
// Detarmines the right no results message before selecting vendor and aftering
|
||||||
// selecting vendor id.
|
// 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'
|
? '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.';
|
: '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 (
|
return (
|
||||||
<CloudLoadingIndicator isLoading={isDueBillsFetching}>
|
<CloudLoadingIndicator isLoading={isDueBillsFetching}>
|
||||||
<DataTableEditable
|
<DataTableEditable
|
||||||
progressBarLoading={isDueBillsFetching}
|
progressBarLoading={isDueBillsFetching}
|
||||||
className={classNames(CLASSES.DATATABLE_EDITOR_ITEMS_ENTRIES)}
|
className={classNames(CLASSES.DATATABLE_EDITOR_ITEMS_ENTRIES)}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={[]}
|
data={entries}
|
||||||
spinnerProps={false}
|
spinnerProps={false}
|
||||||
payload={{
|
payload={{
|
||||||
errors: [],
|
errors: [],
|
||||||
@@ -58,7 +70,7 @@ export default function PaymentMadeItemsTable() {
|
|||||||
<Button
|
<Button
|
||||||
small={true}
|
small={true}
|
||||||
className={'button--secondary button--clear-lines'}
|
className={'button--secondary button--clear-lines'}
|
||||||
// onClick={handleClickClearAllLines}
|
onClick={handleClearAllLines}
|
||||||
>
|
>
|
||||||
<T id={'clear_all_lines'} />
|
<T id={'clear_all_lines'} />
|
||||||
</Button>
|
</Button>
|
||||||
@@ -68,3 +80,7 @@ export default function PaymentMadeItemsTable() {
|
|||||||
</CloudLoadingIndicator>
|
</CloudLoadingIndicator>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default compose(
|
||||||
|
withAlertActions
|
||||||
|
)(PaymentMadeEntriesTable);
|
||||||
@@ -2,7 +2,7 @@ import React, { useMemo } from 'react';
|
|||||||
import { Formik, Form } from 'formik';
|
import { Formik, Form } from 'formik';
|
||||||
import { Intent } from '@blueprintjs/core';
|
import { Intent } from '@blueprintjs/core';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import { sumBy, omit } from 'lodash';
|
import { sumBy, pick } from 'lodash';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
|
|
||||||
@@ -11,7 +11,8 @@ import { AppToaster } from 'components';
|
|||||||
import PaymentMadeHeader from './PaymentMadeFormHeader';
|
import PaymentMadeHeader from './PaymentMadeFormHeader';
|
||||||
import PaymentMadeFloatingActions from './PaymentMadeFloatingActions';
|
import PaymentMadeFloatingActions from './PaymentMadeFloatingActions';
|
||||||
import PaymentMadeFooter from './PaymentMadeFooter';
|
import PaymentMadeFooter from './PaymentMadeFooter';
|
||||||
import PaymentMadeItemsTable from './PaymentMadeItemsTable';
|
import PaymentMadeFormBody from './PaymentMadeFormBody';
|
||||||
|
import { PaymentMadeInnerProvider } from './PaymentMadeInnerProvider';
|
||||||
|
|
||||||
import withSettings from 'containers/Settings/withSettings';
|
import withSettings from 'containers/Settings/withSettings';
|
||||||
import {
|
import {
|
||||||
@@ -32,7 +33,9 @@ function PaymentMadeForm() {
|
|||||||
// Payment made form context.
|
// Payment made form context.
|
||||||
const {
|
const {
|
||||||
isNewMode,
|
isNewMode,
|
||||||
paymentMade,
|
paymentMadeId,
|
||||||
|
paymentMadeEditPage,
|
||||||
|
paymentEntriesEditPage,
|
||||||
submitPayload,
|
submitPayload,
|
||||||
createPaymentMadeMutate,
|
createPaymentMadeMutate,
|
||||||
editPaymentMadeMutate,
|
editPaymentMadeMutate,
|
||||||
@@ -43,14 +46,14 @@ function PaymentMadeForm() {
|
|||||||
() => ({
|
() => ({
|
||||||
...(!isNewMode
|
...(!isNewMode
|
||||||
? {
|
? {
|
||||||
...transformToEditForm(paymentMade, []),
|
...transformToEditForm(paymentMadeEditPage, paymentEntriesEditPage),
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
...defaultPaymentMade,
|
...defaultPaymentMade,
|
||||||
entries: orderingLinesIndexes(defaultPaymentMade.entries),
|
entries: orderingLinesIndexes(defaultPaymentMade.entries),
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
[isNewMode, paymentMade],
|
[isNewMode, paymentMadeEditPage, paymentEntriesEditPage],
|
||||||
);
|
);
|
||||||
|
|
||||||
// Handle the form submit.
|
// Handle the form submit.
|
||||||
@@ -62,9 +65,9 @@ function PaymentMadeForm() {
|
|||||||
|
|
||||||
// Filters entries that have no `bill_id` or `payment_amount`.
|
// Filters entries that have no `bill_id` or `payment_amount`.
|
||||||
const entries = values.entries
|
const entries = values.entries
|
||||||
.filter((item) => !item.bill_id || item.payment_amount)
|
.filter((item) => item.bill_id && item.payment_amount)
|
||||||
.map((entry) => ({
|
.map((entry) => ({
|
||||||
...omit(entry, ['due_amount']),
|
...pick(entry, ['payment_amount', 'bill_id']),
|
||||||
}));
|
}));
|
||||||
// Total payment amount of entries.
|
// Total payment amount of entries.
|
||||||
const totalPaymentAmount = sumBy(entries, 'payment_amount');
|
const totalPaymentAmount = sumBy(entries, 'payment_amount');
|
||||||
@@ -96,7 +99,11 @@ function PaymentMadeForm() {
|
|||||||
submitPayload.resetForm && resetForm();
|
submitPayload.resetForm && resetForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
const onError = ({ response: { error: { data: errors } } }) => {
|
const onError = ({
|
||||||
|
response: {
|
||||||
|
error: { data: errors },
|
||||||
|
},
|
||||||
|
}) => {
|
||||||
const getError = (errorType) => errors.find((e) => e.type === errorType);
|
const getError = (errorType) => errors.find((e) => e.type === errorType);
|
||||||
|
|
||||||
if (getError(ERRORS.PAYMENT_NUMBER_NOT_UNIQUE)) {
|
if (getError(ERRORS.PAYMENT_NUMBER_NOT_UNIQUE)) {
|
||||||
@@ -109,7 +116,7 @@ function PaymentMadeForm() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (!isNewMode) {
|
if (!isNewMode) {
|
||||||
editPaymentMadeMutate([paymentMade.id, form])
|
editPaymentMadeMutate([paymentMadeId, form])
|
||||||
.then(onSaved)
|
.then(onSaved)
|
||||||
.catch(onError);
|
.catch(onError);
|
||||||
} else {
|
} else {
|
||||||
@@ -133,13 +140,12 @@ function PaymentMadeForm() {
|
|||||||
onSubmit={handleSubmitForm}
|
onSubmit={handleSubmitForm}
|
||||||
>
|
>
|
||||||
<Form>
|
<Form>
|
||||||
<PaymentMadeHeader />
|
<PaymentMadeInnerProvider>
|
||||||
|
<PaymentMadeHeader />
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
|
<PaymentMadeFormBody />
|
||||||
<PaymentMadeItemsTable />
|
<PaymentMadeFooter />
|
||||||
</div>
|
<PaymentMadeFloatingActions />
|
||||||
<PaymentMadeFooter />
|
</PaymentMadeInnerProvider>
|
||||||
<PaymentMadeFloatingActions />
|
|
||||||
</Form>
|
</Form>
|
||||||
</Formik>
|
</Formik>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
|
||||||
|
<FastField name={'entries'}>
|
||||||
|
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||||
|
<PaymentMadeEntriesTable
|
||||||
|
entries={value}
|
||||||
|
onUpdateData={(newEntries) => {
|
||||||
|
form.setFieldValue('entries', newEntries);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</FastField>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -18,10 +18,10 @@ function PaymentMadeFormHeader({
|
|||||||
baseCurrency,
|
baseCurrency,
|
||||||
}) {
|
}) {
|
||||||
// Formik form context.
|
// Formik form context.
|
||||||
const { values } = useFormikContext();
|
const { values: { entries } } = useFormikContext();
|
||||||
|
|
||||||
// Calculate the payment amount of the entries.
|
// Calculate the payment amount of the entries.
|
||||||
const amountPaid = useMemo(() => sumBy(values, 'payment_amount'), [values]);
|
const amountPaid = useMemo(() => sumBy(entries, 'payment_amount'), [entries]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
|
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
|
||||||
|
|||||||
@@ -1,25 +1,27 @@
|
|||||||
import React from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import {
|
import {
|
||||||
FormGroup,
|
FormGroup,
|
||||||
InputGroup,
|
InputGroup,
|
||||||
Position,
|
Position,
|
||||||
Classes,
|
Classes,
|
||||||
ControlGroup,
|
ControlGroup,
|
||||||
|
Button
|
||||||
} from '@blueprintjs/core';
|
} from '@blueprintjs/core';
|
||||||
import { DateInput } from '@blueprintjs/datetime';
|
import { DateInput } from '@blueprintjs/datetime';
|
||||||
import { FastField } from 'formik';
|
import { FastField, Field, useFormikContext, ErrorMessage } from 'formik';
|
||||||
import { FormattedMessage as T } from 'react-intl';
|
import { FormattedMessage as T } from 'react-intl';
|
||||||
|
import { toSafeInteger } from 'lodash';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { CLASSES } from 'common/classes';
|
import { CLASSES } from 'common/classes';
|
||||||
import {
|
import {
|
||||||
AccountsSelectList,
|
AccountsSelectList,
|
||||||
ContactSelecetList,
|
ContactSelecetList,
|
||||||
ErrorMessage,
|
|
||||||
FieldRequiredHint,
|
FieldRequiredHint,
|
||||||
InputPrependText,
|
InputPrependText,
|
||||||
Money,
|
Money,
|
||||||
Hint,
|
Hint,
|
||||||
Icon,
|
Icon,
|
||||||
|
MoneyInputGroup
|
||||||
} from 'components';
|
} from 'components';
|
||||||
import withSettings from 'containers/Settings/withSettings';
|
import withSettings from 'containers/Settings/withSettings';
|
||||||
import { usePaymentMadeFormContext } from './PaymentMadeFormProvider';
|
import { usePaymentMadeFormContext } from './PaymentMadeFormProvider';
|
||||||
@@ -28,12 +30,19 @@ import {
|
|||||||
tansformDateValue,
|
tansformDateValue,
|
||||||
inputIntent,
|
inputIntent,
|
||||||
compose,
|
compose,
|
||||||
|
safeSumBy,
|
||||||
|
fullAmountPaymentEntries,
|
||||||
|
amountPaymentEntries,
|
||||||
} from 'utils';
|
} from 'utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Payment made form header fields.
|
* Payment made form header fields.
|
||||||
*/
|
*/
|
||||||
function PaymentMadeFormHeaderFields({ baseCurrency }) {
|
function PaymentMadeFormHeaderFields({ baseCurrency }) {
|
||||||
|
// Formik form context.
|
||||||
|
const { values: { entries }, setFieldValue } = useFormikContext();
|
||||||
|
|
||||||
|
// Payment made form context.
|
||||||
const {
|
const {
|
||||||
vendors,
|
vendors,
|
||||||
accounts,
|
accounts,
|
||||||
@@ -41,7 +50,23 @@ function PaymentMadeFormHeaderFields({ baseCurrency }) {
|
|||||||
setPaymentVendorId,
|
setPaymentVendorId,
|
||||||
} = usePaymentMadeFormContext();
|
} = usePaymentMadeFormContext();
|
||||||
|
|
||||||
const payableFullAmount = 0;
|
// 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');
|
||||||
|
|
||||||
|
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 (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
|
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
|
||||||
@@ -96,8 +121,8 @@ function PaymentMadeFormHeaderFields({ baseCurrency }) {
|
|||||||
</FastField>
|
</FastField>
|
||||||
|
|
||||||
{/* ------------ Full amount ------------ */}
|
{/* ------------ Full amount ------------ */}
|
||||||
<FastField name={'full_amount'}>
|
<Field name={'full_amount'}>
|
||||||
{({ form, field, meta: { error, touched } }) => (
|
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'full_amount'} />}
|
label={<T id={'full_amount'} />}
|
||||||
inline={true}
|
inline={true}
|
||||||
@@ -108,24 +133,27 @@ function PaymentMadeFormHeaderFields({ baseCurrency }) {
|
|||||||
>
|
>
|
||||||
<ControlGroup>
|
<ControlGroup>
|
||||||
<InputPrependText text={baseCurrency} />
|
<InputPrependText text={baseCurrency} />
|
||||||
<InputGroup
|
<MoneyInputGroup
|
||||||
intent={inputIntent({ error, touched })}
|
value={value}
|
||||||
minimal={true}
|
onChange={(value) => {
|
||||||
{...field}
|
setFieldValue('full_amount', value);
|
||||||
|
}}
|
||||||
|
onBlurValue={onFullAmountBlur}
|
||||||
/>
|
/>
|
||||||
</ControlGroup>
|
</ControlGroup>
|
||||||
|
|
||||||
<a
|
<Button
|
||||||
// onClick={handleReceiveFullAmountClick}
|
onClick={handleReceiveFullAmountClick}
|
||||||
href="#"
|
|
||||||
className={'receive-full-amount'}
|
className={'receive-full-amount'}
|
||||||
|
small={true}
|
||||||
|
minimal={true}
|
||||||
>
|
>
|
||||||
Receive full amount (
|
Receive full amount (
|
||||||
<Money amount={payableFullAmount} currency={baseCurrency} />)
|
<Money amount={payableFullAmount} currency={baseCurrency} />)
|
||||||
</a>
|
</Button>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
)}
|
)}
|
||||||
</FastField>
|
</Field>
|
||||||
|
|
||||||
{/* ------------ Payment number ------------ */}
|
{/* ------------ Payment number ------------ */}
|
||||||
<FastField name={'payment_number'}>
|
<FastField name={'payment_number'}>
|
||||||
|
|||||||
@@ -3,11 +3,10 @@ import {
|
|||||||
useAccounts,
|
useAccounts,
|
||||||
useVendors,
|
useVendors,
|
||||||
useItems,
|
useItems,
|
||||||
usePaymentMade,
|
usePaymentMadeEditPage,
|
||||||
useSettings,
|
useSettings,
|
||||||
useCreatePaymentMade,
|
useCreatePaymentMade,
|
||||||
useEditPaymentMade,
|
useEditPaymentMade,
|
||||||
useDueBills,
|
|
||||||
} from 'hooks/query';
|
} from 'hooks/query';
|
||||||
import { DashboardInsider } from 'components';
|
import { DashboardInsider } from 'components';
|
||||||
|
|
||||||
@@ -39,20 +38,13 @@ function PaymentMadeFormProvider({ paymentMadeId, ...props }) {
|
|||||||
|
|
||||||
// Handle fetch specific payment made details.
|
// Handle fetch specific payment made details.
|
||||||
const {
|
const {
|
||||||
data: { paymentMade, payableBills, paymentBills },
|
data: { paymentMade: paymentMadeEditPage, entries: paymentEntriesEditPage },
|
||||||
isFetching: isPaymentFetching,
|
isFetching: isPaymentFetching,
|
||||||
isLoading: isPaymentLoading,
|
isLoading: isPaymentLoading,
|
||||||
} = usePaymentMade(paymentMadeId, {
|
} = usePaymentMadeEditPage(paymentMadeId, {
|
||||||
enabled: !!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.
|
// Fetch payment made settings.
|
||||||
useSettings();
|
useSettings();
|
||||||
|
|
||||||
@@ -66,12 +58,10 @@ function PaymentMadeFormProvider({ paymentMadeId, ...props }) {
|
|||||||
const provider = {
|
const provider = {
|
||||||
paymentMadeId,
|
paymentMadeId,
|
||||||
accounts,
|
accounts,
|
||||||
paymentMade,
|
paymentEntriesEditPage,
|
||||||
payableBills,
|
paymentMadeEditPage,
|
||||||
paymentBills,
|
|
||||||
vendors,
|
vendors,
|
||||||
items,
|
items,
|
||||||
dueBills,
|
|
||||||
submitPayload,
|
submitPayload,
|
||||||
paymentVendorId,
|
paymentVendorId,
|
||||||
|
|
||||||
@@ -82,8 +72,6 @@ function PaymentMadeFormProvider({ paymentMadeId, ...props }) {
|
|||||||
isVendorsFetching,
|
isVendorsFetching,
|
||||||
isPaymentFetching,
|
isPaymentFetching,
|
||||||
isPaymentLoading,
|
isPaymentLoading,
|
||||||
isDueBillsLoading,
|
|
||||||
isDueBillsFetching,
|
|
||||||
|
|
||||||
createPaymentMadeMutate,
|
createPaymentMadeMutate,
|
||||||
editPaymentMadeMutate,
|
editPaymentMadeMutate,
|
||||||
|
|||||||
@@ -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 <PaymentMadeInnerContext.Provider value={provider} {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const usePaymentMadeInnerContext = () => useContext(PaymentMadeInnerContext);
|
||||||
|
|
||||||
|
export { PaymentMadeInnerProvider, usePaymentMadeInnerContext };
|
||||||
@@ -3,16 +3,17 @@ import { useIntl } from "react-intl";
|
|||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { Money } from 'components';
|
import { Money } from 'components';
|
||||||
import { safeSumBy, formattedAmount } from 'utils';
|
import { safeSumBy, formattedAmount } from 'utils';
|
||||||
|
import { MoneyFieldCell } from 'components/DataTableCells';
|
||||||
|
|
||||||
function BillNumberAccessor(row) {
|
function BillNumberAccessor(row) {
|
||||||
return `#${row?.bill_number || ''}`
|
return row?.bill_no ? row?.bill_no : '-';
|
||||||
}
|
}
|
||||||
|
|
||||||
function IndexTableCell({ row: { index } }) {
|
function IndexTableCell({ row: { index } }) {
|
||||||
return (<span>{index + 1}</span>);
|
return (<span>{index + 1}</span>);
|
||||||
}
|
}
|
||||||
|
|
||||||
function BillDateTableCell({ value }) {
|
function BillDateCell({ value }) {
|
||||||
return moment(value).format('YYYY MMM DD');
|
return moment(value).format('YYYY MMM DD');
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@@ -39,6 +40,14 @@ function PaymentAmountFooterCell({ rows }) {
|
|||||||
return <span>{ formattedAmount(totalPaymentAmount, 'USD') }</span>;
|
return <span>{ formattedAmount(totalPaymentAmount, 'USD') }</span>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mobey table cell.
|
||||||
|
*/
|
||||||
|
function MoneyTableCell({ value }) {
|
||||||
|
return <Money amount={value} currency={"USD"} />
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Payment made entries table columns
|
* Payment made entries table columns
|
||||||
*/
|
*/
|
||||||
@@ -54,13 +63,15 @@ export function usePaymentMadeEntriesTableColumns() {
|
|||||||
width: 40,
|
width: 40,
|
||||||
disableResizing: true,
|
disableResizing: true,
|
||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
|
className: 'index'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: formatMessage({ id: 'Date' }),
|
Header: formatMessage({ id: 'Date' }),
|
||||||
id: 'bill_date',
|
id: 'bill_date',
|
||||||
accessor: 'bill_date',
|
accessor: 'bill_date',
|
||||||
Cell: BillDateTableCell,
|
Cell: BillDateCell,
|
||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
|
width: 250,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: formatMessage({ id: 'bill_number' }),
|
Header: formatMessage({ id: 'bill_number' }),
|
||||||
@@ -71,6 +82,7 @@ export function usePaymentMadeEntriesTableColumns() {
|
|||||||
{
|
{
|
||||||
Header: formatMessage({ id: 'bill_amount' }),
|
Header: formatMessage({ id: 'bill_amount' }),
|
||||||
accessor: 'amount',
|
accessor: 'amount',
|
||||||
|
Cell: MoneyTableCell,
|
||||||
Footer: AmountFooterCell,
|
Footer: AmountFooterCell,
|
||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
className: '',
|
className: '',
|
||||||
@@ -78,6 +90,7 @@ export function usePaymentMadeEntriesTableColumns() {
|
|||||||
{
|
{
|
||||||
Header: formatMessage({ id: 'amount_due' }),
|
Header: formatMessage({ id: 'amount_due' }),
|
||||||
accessor: 'due_amount',
|
accessor: 'due_amount',
|
||||||
|
Cell: MoneyTableCell,
|
||||||
Footer: DueAmountFooterCell,
|
Footer: DueAmountFooterCell,
|
||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
className: '',
|
className: '',
|
||||||
@@ -85,6 +98,7 @@ export function usePaymentMadeEntriesTableColumns() {
|
|||||||
{
|
{
|
||||||
Header: formatMessage({ id: 'payment_amount' }),
|
Header: formatMessage({ id: 'payment_amount' }),
|
||||||
accessor: 'payment_amount',
|
accessor: 'payment_amount',
|
||||||
|
Cell: MoneyFieldCell,
|
||||||
Footer: PaymentAmountFooterCell,
|
Footer: PaymentAmountFooterCell,
|
||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
className: '',
|
className: '',
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { sumBy } from 'lodash';
|
import { safeSumBy, transformToForm } from 'utils';
|
||||||
import { transformToForm } from 'utils';
|
|
||||||
|
|
||||||
export const ERRORS = {
|
export const ERRORS = {
|
||||||
PAYMENT_NUMBER_NOT_UNIQUE: 'PAYMENT.NUMBER.NOT.UNIQUE',
|
PAYMENT_NUMBER_NOT_UNIQUE: 'PAYMENT.NUMBER.NOT.UNIQUE',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Default payment made entry values.
|
// Default payment made entry values.
|
||||||
export const defaultPaymentMadeEntry = {
|
export const defaultPaymentMadeEntry = {
|
||||||
bill_id: '',
|
bill_id: '',
|
||||||
@@ -30,7 +28,7 @@ export const defaultPaymentMade = {
|
|||||||
export const transformToEditForm = (paymentMade, paymentMadeEntries) => {
|
export const transformToEditForm = (paymentMade, paymentMadeEntries) => {
|
||||||
return {
|
return {
|
||||||
...transformToForm(paymentMade, defaultPaymentMade),
|
...transformToForm(paymentMade, defaultPaymentMade),
|
||||||
full_amount: sumBy(paymentMade.entries, 'payment_amount'),
|
full_amount: safeSumBy(paymentMadeEntries, 'payment_amount'),
|
||||||
entries: [
|
entries: [
|
||||||
...paymentMadeEntries.map((paymentMadeEntry) => ({
|
...paymentMadeEntries.map((paymentMadeEntry) => ({
|
||||||
...transformToForm(paymentMadeEntry, defaultPaymentMadeEntry),
|
...transformToForm(paymentMadeEntry, defaultPaymentMadeEntry),
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
import 'style/pages/PaymentMade/List.scss';
|
||||||
|
|
||||||
import { DashboardContentTable, DashboardPageContent } from 'components';
|
import { DashboardContentTable, DashboardPageContent } from 'components';
|
||||||
import PaymentMadeActionsBar from './PaymentMadeActionsBar';
|
import PaymentMadeActionsBar from './PaymentMadeActionsBar';
|
||||||
import PaymentMadesAlerts from '../PaymentMadesAlerts';
|
import PaymentMadesAlerts from '../PaymentMadesAlerts';
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export function usePaymentMades(query, props) {
|
|||||||
data: defaultTo(states.data, {
|
data: defaultTo(states.data, {
|
||||||
paymentMades: [],
|
paymentMades: [],
|
||||||
pagination: {},
|
pagination: {},
|
||||||
filterMeta: {}
|
filterMeta: {},
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -42,8 +42,9 @@ export function useCreatePaymentMade(props) {
|
|||||||
return useMutation(
|
return useMutation(
|
||||||
(values) => apiRequest.post('purchases/bill_payments', values),
|
(values) => apiRequest.post('purchases/bill_payments', values),
|
||||||
{
|
{
|
||||||
onSuccess: () => {
|
onSuccess: (res, values) => {
|
||||||
client.invalidateQueries('PAYMENT_MADES');
|
client.invalidateQueries('PAYMENT_MADES');
|
||||||
|
client.invalidateQueries(['PAYMENT_MADE_NEW_PAGE_ENTRIES', values.vendor_id]);
|
||||||
},
|
},
|
||||||
...props,
|
...props,
|
||||||
},
|
},
|
||||||
@@ -63,6 +64,8 @@ export function useEditPaymentMade(props) {
|
|||||||
onSuccess: (res, [id, values]) => {
|
onSuccess: (res, [id, values]) => {
|
||||||
client.invalidateQueries('PAYMENT_MADES');
|
client.invalidateQueries('PAYMENT_MADES');
|
||||||
client.invalidateQueries(['PAYMENT_MADE', id]);
|
client.invalidateQueries(['PAYMENT_MADE', id]);
|
||||||
|
|
||||||
|
client.invalidateQueries(['PAYMENT_MADE_NEW_PAGE_ENTRIES', values.vendor_id]);
|
||||||
},
|
},
|
||||||
...props,
|
...props,
|
||||||
},
|
},
|
||||||
@@ -82,6 +85,7 @@ export function useDeletePaymentMade(props) {
|
|||||||
onSuccess: (res, id) => {
|
onSuccess: (res, id) => {
|
||||||
client.invalidateQueries('PAYMENT_MADES');
|
client.invalidateQueries('PAYMENT_MADES');
|
||||||
client.invalidateQueries(['PAYMENT_MADE', id]);
|
client.invalidateQueries(['PAYMENT_MADE', id]);
|
||||||
|
|
||||||
},
|
},
|
||||||
...props,
|
...props,
|
||||||
},
|
},
|
||||||
@@ -91,17 +95,16 @@ export function useDeletePaymentMade(props) {
|
|||||||
/**
|
/**
|
||||||
* Retrieve specific payment made.
|
* Retrieve specific payment made.
|
||||||
*/
|
*/
|
||||||
export function usePaymentMade(id, props) {
|
export function usePaymentMadeEditPage(id, props) {
|
||||||
const apiRequest = useApiRequest();
|
const apiRequest = useApiRequest();
|
||||||
|
|
||||||
const states = useQuery(
|
const states = useQuery(
|
||||||
['PAYMENT_MADE', id],
|
['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,
|
paymentMade: res.data.bill_payment,
|
||||||
payableBills: res.data.payable_bills,
|
entries: res.data.entries,
|
||||||
paymentBills: res.data.payment_bills,
|
|
||||||
}),
|
}),
|
||||||
...props,
|
...props,
|
||||||
},
|
},
|
||||||
@@ -112,3 +115,23 @@ export function usePaymentMade(id, props) {
|
|||||||
data: defaultTo(states.data, {}),
|
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,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export default function useApiRequest() {
|
|||||||
const locale = 'en';
|
const locale = 'en';
|
||||||
|
|
||||||
if (token) {
|
if (token) {
|
||||||
request.headers.common['x-access-token'] = token;
|
request.headers.common['X-Access-Token'] = token;
|
||||||
}
|
}
|
||||||
if (organizationId) {
|
if (organizationId) {
|
||||||
request.headers.common['organization-id'] = organizationId;
|
request.headers.common['organization-id'] = organizationId;
|
||||||
|
|||||||
18
client/src/style/pages/Bills/List.scss
Normal file
18
client/src/style/pages/Bills/List.scss
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
.dashboard__insider--bills{
|
||||||
|
|
||||||
|
.bigcapital-datatable{
|
||||||
|
|
||||||
|
.tbody{
|
||||||
|
|
||||||
|
.td.amount {
|
||||||
|
|
||||||
|
.cell-inner{
|
||||||
|
> span{
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
client/src/style/pages/PaymentMade/List.scss
Normal file
18
client/src/style/pages/PaymentMade/List.scss
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
.dashboard__insider--payment-mades-list{
|
||||||
|
|
||||||
|
.bigcapital-datatable{
|
||||||
|
|
||||||
|
.tbody{
|
||||||
|
|
||||||
|
.td.amount {
|
||||||
|
|
||||||
|
.cell-inner{
|
||||||
|
> span{
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,10 +19,18 @@
|
|||||||
&.bp3-inline{
|
&.bp3-inline{
|
||||||
max-width: 470px;
|
max-width: 470px;
|
||||||
}
|
}
|
||||||
a.receive-full-amount{
|
button.receive-full-amount{
|
||||||
|
width: auto;
|
||||||
|
padding: 0;
|
||||||
|
min-height: auto;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
margin-top: 6px;
|
margin-top: 4px;
|
||||||
display: inline-block;
|
background-color: transparent;
|
||||||
|
color: #0052cc;
|
||||||
|
|
||||||
|
&:hover{
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -577,3 +577,27 @@ export function safeSumBy(entries, getter) {
|
|||||||
.sum()
|
.sum()
|
||||||
.value();
|
.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -17,14 +17,32 @@ export default class FinancialStatementsService {
|
|||||||
router() {
|
router() {
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
router.use('/balance_sheet', Container.get(BalanceSheetController).router());
|
router.use(
|
||||||
router.use('/profit_loss_sheet', Container.get(ProfitLossController).router());
|
'/balance_sheet',
|
||||||
router.use('/general_ledger', Container.get(GeneralLedgerController).router());
|
Container.get(BalanceSheetController).router()
|
||||||
router.use('/trial_balance_sheet', Container.get(TrialBalanceSheetController).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('/journal', Container.get(JournalSheetController).router());
|
||||||
router.use('/receivable_aging_summary', Container.get(ARAgingSummary).router());
|
router.use(
|
||||||
router.use('/payable_aging_summary', Container.get(APAgingSummary).router());
|
'/receivable_aging_summary',
|
||||||
|
Container.get(ARAgingSummary).router()
|
||||||
|
);
|
||||||
|
router.use(
|
||||||
|
'/payable_aging_summary',
|
||||||
|
Container.get(APAgingSummary).router()
|
||||||
|
);
|
||||||
|
|
||||||
return router;
|
return router;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import BillPaymentsService from 'services/Purchases/BillPayments';
|
|||||||
import DynamicListingService from 'services/DynamicListing/DynamicListService';
|
import DynamicListingService from 'services/DynamicListing/DynamicListService';
|
||||||
import AccountsService from 'services/Accounts/AccountsService';
|
import AccountsService from 'services/Accounts/AccountsService';
|
||||||
import ResourceController from '../Resources';
|
import ResourceController from '../Resources';
|
||||||
|
import { Request } from 'express-validator/src/base';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bills payments controller.
|
* Bills payments controller.
|
||||||
@@ -30,6 +31,20 @@ export default class BillsPayments extends BaseController {
|
|||||||
router() {
|
router() {
|
||||||
const router = 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(
|
router.post(
|
||||||
'/',
|
'/',
|
||||||
[...this.billPaymentSchemaValidation],
|
[...this.billPaymentSchemaValidation],
|
||||||
@@ -76,6 +91,7 @@ export default class BillsPayments extends BaseController {
|
|||||||
this.handleServiceError,
|
this.handleServiceError,
|
||||||
this.dynamicListService.handlerErrorsToResponse
|
this.dynamicListService.handlerErrorsToResponse
|
||||||
);
|
);
|
||||||
|
|
||||||
return router;
|
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.
|
* Creates a bill payment.
|
||||||
* @async
|
* @async
|
||||||
|
|||||||
@@ -54,6 +54,15 @@ export default class PaymentReceivesController extends BaseController {
|
|||||||
asyncMiddleware(this.getPaymentReceiveEditPage.bind(this)),
|
asyncMiddleware(this.getPaymentReceiveEditPage.bind(this)),
|
||||||
this.handleServiceErrors
|
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(
|
router.get(
|
||||||
'/',
|
'/',
|
||||||
this.validatePaymentReceiveList,
|
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.
|
* Handles service errors.
|
||||||
* @param error
|
* @param error
|
||||||
|
|||||||
@@ -33,3 +33,14 @@ export interface IBillPaymentDTO {
|
|||||||
reference: string,
|
reference: string,
|
||||||
entries: IBillPaymentEntryDTO[],
|
entries: IBillPaymentEntryDTO[],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface IBillReceivePageEntry {
|
||||||
|
billId: number,
|
||||||
|
entryType: string,
|
||||||
|
billNo: string,
|
||||||
|
dueAmount: number,
|
||||||
|
amount: number,
|
||||||
|
totalPaymentAmount: number,
|
||||||
|
paymentAmount: number,
|
||||||
|
date: Date|string,
|
||||||
|
};
|
||||||
@@ -52,7 +52,7 @@ export interface IPaymentReceivesFilter extends IDynamicListFilterDTO {
|
|||||||
stringifiedFilterRoles?: string,
|
stringifiedFilterRoles?: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IPaymentReceiveEditPageEntry {
|
export interface IPaymentReceivePageEntry {
|
||||||
invoiceId: number,
|
invoiceId: number,
|
||||||
entryType: string,
|
entryType: string,
|
||||||
invoiceNo: string,
|
invoiceNo: string,
|
||||||
@@ -65,5 +65,5 @@ export interface IPaymentReceiveEditPageEntry {
|
|||||||
|
|
||||||
export interface IPaymentReceiveEditPage {
|
export interface IPaymentReceiveEditPage {
|
||||||
paymentReceive: IPaymentReceive,
|
paymentReceive: IPaymentReceive,
|
||||||
entries: IPaymentReceiveEditPageEntry[];
|
entries: IPaymentReceivePageEntry[];
|
||||||
};
|
};
|
||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
IPaginationMeta,
|
IPaginationMeta,
|
||||||
IFilterMeta,
|
IFilterMeta,
|
||||||
IBillPaymentEntry,
|
IBillPaymentEntry,
|
||||||
|
IBillReceivePageEntry,
|
||||||
} from 'interfaces';
|
} from 'interfaces';
|
||||||
import AccountsService from 'services/Accounts/AccountsService';
|
import AccountsService from 'services/Accounts/AccountsService';
|
||||||
import JournalPoster from 'services/Accounting/JournalPoster';
|
import JournalPoster from 'services/Accounting/JournalPoster';
|
||||||
@@ -26,6 +27,7 @@ import DynamicListingService from 'services/DynamicListing/DynamicListService';
|
|||||||
import { entriesAmountDiff, formatDateFields } from 'utils';
|
import { entriesAmountDiff, formatDateFields } from 'utils';
|
||||||
import { ServiceError } from 'exceptions';
|
import { ServiceError } from 'exceptions';
|
||||||
import { ACCOUNT_PARENT_TYPE } from 'data/AccountTypes';
|
import { ACCOUNT_PARENT_TYPE } from 'data/AccountTypes';
|
||||||
|
import PayableAgingSummaryService from 'services/FinancialStatements/AgingSummary/APAgingSummaryService';
|
||||||
|
|
||||||
const ERRORS = {
|
const ERRORS = {
|
||||||
BILL_VENDOR_NOT_FOUND: 'VENDOR_NOT_FOUND',
|
BILL_VENDOR_NOT_FOUND: 'VENDOR_NOT_FOUND',
|
||||||
@@ -598,48 +600,43 @@ export default class BillPaymentsService {
|
|||||||
* @param {number} billPaymentId - The bill payment id.
|
* @param {number} billPaymentId - The bill payment id.
|
||||||
* @return {object}
|
* @return {object}
|
||||||
*/
|
*/
|
||||||
public async getBillPayment(
|
public async getBillPaymentEditPage(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
billPaymentId: number
|
billPaymentId: number
|
||||||
): Promise<{
|
): Promise<{
|
||||||
billPayment: IBillPayment;
|
billPayment: Omit<IBillPayment, "entries">;
|
||||||
payableBills: IBill[];
|
entries: IBillReceivePageEntry[];
|
||||||
paymentMadeBills: IBill[];
|
|
||||||
}> {
|
}> {
|
||||||
const { BillPayment, Bill } = this.tenancy.models(tenantId);
|
const { BillPayment, Bill } = this.tenancy.models(tenantId);
|
||||||
const billPayment = await BillPayment.query()
|
const billPayment = await BillPayment.query()
|
||||||
.findById(billPaymentId)
|
.findById(billPaymentId)
|
||||||
.withGraphFetched('entries.bill')
|
.withGraphFetched('entries.bill');
|
||||||
.withGraphFetched('vendor')
|
|
||||||
.withGraphFetched('paymentAccount');
|
|
||||||
|
|
||||||
|
// Throw not found the bill payment.
|
||||||
if (!billPayment) {
|
if (!billPayment) {
|
||||||
throw new ServiceError(ERRORS.PAYMENT_MADE_NOT_FOUND);
|
throw new ServiceError(ERRORS.PAYMENT_MADE_NOT_FOUND);
|
||||||
}
|
}
|
||||||
const billsIds = billPayment.entries.map((entry) => entry.billId);
|
const paymentEntries = billPayment.entries.map((entry) => ({
|
||||||
|
...this.mapBillToPageEntry(entry.bill),
|
||||||
// Retrieve all payable bills that assocaited to the payment made transaction.
|
paymentAmount: entry.paymentAmount,
|
||||||
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 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 {
|
return {
|
||||||
billPayment: {
|
billPayment: omit(billPayment, ['entries']),
|
||||||
...billPayment,
|
entries
|
||||||
entries: billPayment.entries.map((entry) => ({
|
|
||||||
...omit(entry, ['bill']),
|
|
||||||
})),
|
|
||||||
},
|
|
||||||
payableBills,
|
|
||||||
paymentMadeBills,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -678,4 +675,55 @@ export default class BillPaymentsService {
|
|||||||
);
|
);
|
||||||
await Promise.all(opers);
|
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<IBillReceivePageEntry[]> {
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import {
|
|||||||
ISaleInvoice,
|
ISaleInvoice,
|
||||||
ISystemService,
|
ISystemService,
|
||||||
ISystemUser,
|
ISystemUser,
|
||||||
IPaymentReceiveEditPageEntry,
|
IPaymentReceivePageEntry,
|
||||||
} from 'interfaces';
|
} from 'interfaces';
|
||||||
import AccountsService from 'services/Accounts/AccountsService';
|
import AccountsService from 'services/Accounts/AccountsService';
|
||||||
import JournalPoster from 'services/Accounting/JournalPoster';
|
import JournalPoster from 'services/Accounting/JournalPoster';
|
||||||
@@ -471,7 +471,7 @@ export default class PaymentReceiveService {
|
|||||||
* @param {ISaleInvoice[]} invoices - Invoices.
|
* @param {ISaleInvoice[]} invoices - Invoices.
|
||||||
* @return {IPaymentReceiveEditPageEntry}
|
* @return {IPaymentReceiveEditPageEntry}
|
||||||
*/
|
*/
|
||||||
public invoicesToEditPageEntries(
|
public invoiceToPageEntry(
|
||||||
invoice: ISaleInvoice
|
invoice: ISaleInvoice
|
||||||
): IPaymentReceiveEditPageEntry {
|
): IPaymentReceiveEditPageEntry {
|
||||||
return {
|
return {
|
||||||
@@ -494,9 +494,10 @@ export default class PaymentReceiveService {
|
|||||||
public async getPaymentReceiveEditPage(
|
public async getPaymentReceiveEditPage(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
paymentReceiveId: number,
|
paymentReceiveId: number,
|
||||||
|
authorizedUser: ISystemUser
|
||||||
): Promise<{
|
): Promise<{
|
||||||
paymentReceive: IPaymentReceive;
|
paymentReceive: Omit<IPaymentReceive, "entries">;
|
||||||
entries: IPaymentReceiveEditPageEntry[];
|
entries: IPaymentReceivePageEntry[];
|
||||||
}> {
|
}> {
|
||||||
const { PaymentReceive, SaleInvoice } = this.tenancy.models(tenantId);
|
const { PaymentReceive, SaleInvoice } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
@@ -509,14 +510,8 @@ export default class PaymentReceiveService {
|
|||||||
if (!paymentReceive) {
|
if (!paymentReceive) {
|
||||||
throw new ServiceError(ERRORS.PAYMENT_RECEIVE_NOT_EXISTS);
|
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) => ({
|
const paymentEntries = paymentReceive.entries.map((entry) => ({
|
||||||
...this.invoicesToEditPageEntries(entry.invoice),
|
...this.invoiceToPageEntry(entry.invoice),
|
||||||
paymentAmount: entry.paymentAmount,
|
paymentAmount: entry.paymentAmount,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -524,17 +519,16 @@ export default class PaymentReceiveService {
|
|||||||
const restReceivableInvoices = await SaleInvoice.query()
|
const restReceivableInvoices = await SaleInvoice.query()
|
||||||
.modify('dueInvoices')
|
.modify('dueInvoices')
|
||||||
.where('customer_id', paymentReceive.customerId)
|
.where('customer_id', paymentReceive.customerId)
|
||||||
.whereNotIn('id', entriesInvoicesIds)
|
.whereNotIn(
|
||||||
|
'id',
|
||||||
|
paymentReceive.entries.map((entry) => entry.invoiceId)
|
||||||
|
)
|
||||||
.orderBy('invoice_date', 'ASC');
|
.orderBy('invoice_date', 'ASC');
|
||||||
|
|
||||||
const restReceivableEntries = restReceivableInvoices.map(
|
const restReceivableEntries = restReceivableInvoices.map(
|
||||||
this.invoicesToEditPageEntries
|
this.invoiceToPageEntry
|
||||||
);
|
);
|
||||||
|
const entries = [...paymentEntries, ...restReceivableEntries];
|
||||||
const entries = [
|
|
||||||
...paymentEntries,
|
|
||||||
...restReceivableEntries,
|
|
||||||
];
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
paymentReceive: omit(paymentReceive, ['entries']),
|
paymentReceive: omit(paymentReceive, ['entries']),
|
||||||
@@ -616,6 +610,7 @@ export default class PaymentReceiveService {
|
|||||||
paymentReceiveId: number
|
paymentReceiveId: number
|
||||||
) {
|
) {
|
||||||
const { PaymentReceive } = this.tenancy.models(tenantId);
|
const { PaymentReceive } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
return PaymentReceive.query()
|
return PaymentReceive.query()
|
||||||
.where('id', paymentReceiveId)
|
.where('id', paymentReceiveId)
|
||||||
.withGraphFetched('invoices')
|
.withGraphFetched('invoices')
|
||||||
@@ -739,7 +734,6 @@ export default class PaymentReceiveService {
|
|||||||
if (diffEntry.paymentAmount === 0) {
|
if (diffEntry.paymentAmount === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const oper = SaleInvoice.changePaymentAmount(
|
const oper = SaleInvoice.changePaymentAmount(
|
||||||
diffEntry.invoiceId,
|
diffEntry.invoiceId,
|
||||||
diffEntry.paymentAmount
|
diffEntry.paymentAmount
|
||||||
@@ -748,4 +742,22 @@ export default class PaymentReceiveService {
|
|||||||
});
|
});
|
||||||
await Promise.all([...opers]);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user