mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-21 15:20:34 +00:00
Merge branch 'bills' into sales
This commit is contained in:
@@ -0,0 +1,35 @@
|
|||||||
|
import React, { useCallback } from 'react';
|
||||||
|
import PaymentReceiveListField from 'components/PaymentReceiveListField';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import { FormGroup, Classes, Intent } from '@blueprintjs/core';
|
||||||
|
|
||||||
|
function PaymentReceiveListFieldCell({
|
||||||
|
column: { id },
|
||||||
|
row: { index },
|
||||||
|
cell: { value: initialValue },
|
||||||
|
payload: { invoices, updateData, errors },
|
||||||
|
}) {
|
||||||
|
const handleInvoicesSelected = useCallback(
|
||||||
|
(_item) => {
|
||||||
|
updateData(index, id, _item.id);
|
||||||
|
},
|
||||||
|
[updateData, index, id],
|
||||||
|
);
|
||||||
|
|
||||||
|
const error = errors?.[index]?.[id];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormGroup
|
||||||
|
intent={error ? Intent.DANGER : null}
|
||||||
|
className={classNames('form-group--selcet-list', Classes.FILL)}
|
||||||
|
>
|
||||||
|
<PaymentReceiveListField
|
||||||
|
invoices={invoices}
|
||||||
|
onInvoiceSelected={handleInvoicesSelected}
|
||||||
|
selectedInvoiceId={initialValue}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PaymentReceiveListFieldCell;
|
||||||
38
client/src/components/PaymentReceiveListField.js
Normal file
38
client/src/components/PaymentReceiveListField.js
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import React, { useCallback } from 'react';
|
||||||
|
import { MenuItem } from '@blueprintjs/core';
|
||||||
|
import ListSelect from 'components/ListSelect';
|
||||||
|
import { FormattedMessage as T } from 'react-intl';
|
||||||
|
|
||||||
|
function PaymentReceiveListField({
|
||||||
|
invoices,
|
||||||
|
selectedInvoiceId,
|
||||||
|
onInvoiceSelected,
|
||||||
|
defaultSelectText = <T id={'select_invoice'} />,
|
||||||
|
}) {
|
||||||
|
const onInvoiceSelect = useCallback((_invoice) => {
|
||||||
|
onInvoiceSelected && onInvoiceSelected(_invoice);
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleInvoiceRenderer = useCallback(
|
||||||
|
(item, { handleClick }) => (
|
||||||
|
<MenuItem id={item.id} name={item.name} onClick={handleClick} />
|
||||||
|
),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ListSelect
|
||||||
|
item={invoices}
|
||||||
|
noResults={<MenuItem disabled={true} text="No results." />}
|
||||||
|
itemRenderer={handleInvoiceRenderer}
|
||||||
|
popoverProps={{ minimal: true }}
|
||||||
|
onItemSelect={onInvoiceSelect}
|
||||||
|
selectedItem={`${selectedInvoiceId}`}
|
||||||
|
selectedItemProp={'id'}
|
||||||
|
labelProp={'name'}
|
||||||
|
defaultText={defaultSelectText}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PaymentReceiveListField;
|
||||||
@@ -46,6 +46,13 @@ export default [
|
|||||||
text: <T id={'invocies'} />,
|
text: <T id={'invocies'} />,
|
||||||
href: '/invoices/new',
|
href: '/invoices/new',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
text: <T id={'payment_receives'} />,
|
||||||
|
href: '/payment-receive/new',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
divider: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
text: <T id={'receipts'} />,
|
text: <T id={'receipts'} />,
|
||||||
href: '/receipts/new',
|
href: '/receipts/new',
|
||||||
@@ -56,10 +63,11 @@ export default [
|
|||||||
text: <T id={'purchases'} />,
|
text: <T id={'purchases'} />,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
icon: 'cut',
|
text: <T id={'bills'} />,
|
||||||
text: 'cut',
|
href: '/bill/new',
|
||||||
label: '⌘C',
|
},
|
||||||
disabled: false,
|
{
|
||||||
|
text: <T id={'payment_mades'} />,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
299
client/src/containers/Purchases/BillForm.js
Normal file
299
client/src/containers/Purchases/BillForm.js
Normal file
@@ -0,0 +1,299 @@
|
|||||||
|
import React, {
|
||||||
|
useMemo,
|
||||||
|
useState,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useRef,
|
||||||
|
} from 'react';
|
||||||
|
import * as Yup from 'yup';
|
||||||
|
import { useFormik } from 'formik';
|
||||||
|
import moment from 'moment';
|
||||||
|
import { Intent, FormGroup, TextArea } from '@blueprintjs/core';
|
||||||
|
|
||||||
|
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||||
|
import { pick, omit } from 'lodash';
|
||||||
|
|
||||||
|
import BillFormHeader from './BillFormHeader';
|
||||||
|
import EstimatesItemsTable from 'containers/Sales/Estimate/EntriesItemsTable';
|
||||||
|
import BillFormFooter from './BillFormFooter';
|
||||||
|
|
||||||
|
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||||
|
import withMediaActions from 'containers/Media/withMediaActions';
|
||||||
|
import withBillActions from './withBillActions';
|
||||||
|
|
||||||
|
import { AppToaster } from 'components';
|
||||||
|
import Dragzone from 'components/Dragzone';
|
||||||
|
import useMedia from 'hooks/useMedia';
|
||||||
|
|
||||||
|
import { compose, repeatValue } from 'utils';
|
||||||
|
|
||||||
|
const MIN_LINES_NUMBER = 4;
|
||||||
|
|
||||||
|
function BillForm({
|
||||||
|
//#WithMedia
|
||||||
|
requestSubmitMedia,
|
||||||
|
requestDeleteMedia,
|
||||||
|
|
||||||
|
//#withBillActions
|
||||||
|
requestSubmitBill,
|
||||||
|
|
||||||
|
//#withDashboard
|
||||||
|
changePageTitle,
|
||||||
|
changePageSubtitle,
|
||||||
|
|
||||||
|
//#withBillDetail
|
||||||
|
bill,
|
||||||
|
|
||||||
|
//#Own Props
|
||||||
|
onFormSubmit,
|
||||||
|
onCancelForm,
|
||||||
|
}) {
|
||||||
|
const { formatMessage } = useIntl();
|
||||||
|
const [payload, setPayload] = useState({});
|
||||||
|
|
||||||
|
const {
|
||||||
|
setFiles,
|
||||||
|
saveMedia,
|
||||||
|
deletedFiles,
|
||||||
|
setDeletedFiles,
|
||||||
|
deleteMedia,
|
||||||
|
} = useMedia({
|
||||||
|
saveCallback: requestSubmitMedia,
|
||||||
|
deleteCallback: requestDeleteMedia,
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleDropFiles = useCallback((_files) => {
|
||||||
|
setFiles(_files.filter((file) => file.uploaded === false));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const savedMediaIds = useRef([]);
|
||||||
|
const clearSavedMediaIds = () => {
|
||||||
|
savedMediaIds.current = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (bill && bill.id) {
|
||||||
|
changePageTitle(formatMessage({ id: 'edit_bill' }));
|
||||||
|
} else {
|
||||||
|
changePageTitle(formatMessage({ id: 'new_bill' }));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const validationSchema = Yup.object().shape({
|
||||||
|
vendor_id: Yup.number()
|
||||||
|
.required()
|
||||||
|
.label(formatMessage({ id: 'vendor_name_' })),
|
||||||
|
bill_date: Yup.date()
|
||||||
|
.required()
|
||||||
|
.label(formatMessage({ id: 'bill_date_' })),
|
||||||
|
due_date: Yup.date()
|
||||||
|
.required()
|
||||||
|
.label(formatMessage({ id: 'due_date_' })),
|
||||||
|
bill_number: Yup.number()
|
||||||
|
.required()
|
||||||
|
.label(formatMessage({ id: 'bill_number_' })),
|
||||||
|
reference_no: Yup.string().min(1).max(255),
|
||||||
|
status: Yup.string().required(),
|
||||||
|
note: Yup.string()
|
||||||
|
.trim()
|
||||||
|
.min(1)
|
||||||
|
.max(1024)
|
||||||
|
.label(formatMessage({ id: 'note' })),
|
||||||
|
|
||||||
|
entries: Yup.array().of(
|
||||||
|
Yup.object().shape({
|
||||||
|
quantity: Yup.number().nullable(),
|
||||||
|
rate: Yup.number().nullable(),
|
||||||
|
item_id: Yup.number()
|
||||||
|
.nullable()
|
||||||
|
.when(['quantity', 'rate'], {
|
||||||
|
is: (quantity, rate) => quantity || rate,
|
||||||
|
then: Yup.number().required(),
|
||||||
|
}),
|
||||||
|
total: Yup.number().nullable(),
|
||||||
|
discount: Yup.number().nullable(),
|
||||||
|
description: Yup.string().nullable(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
const saveBillSubmit = useCallback(
|
||||||
|
(payload) => {
|
||||||
|
onFormSubmit && onFormSubmit(payload);
|
||||||
|
},
|
||||||
|
[onFormSubmit],
|
||||||
|
);
|
||||||
|
|
||||||
|
const defaultBill = useMemo(() => ({
|
||||||
|
index: 0,
|
||||||
|
item_id: null,
|
||||||
|
rate: null,
|
||||||
|
discount: null,
|
||||||
|
quantity: null,
|
||||||
|
description: '',
|
||||||
|
status: '',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const defaultInitialValues = useMemo(
|
||||||
|
() => ({
|
||||||
|
accept: '',
|
||||||
|
vendor_name: '',
|
||||||
|
bill_number: '',
|
||||||
|
bill_date: moment(new Date()).format('YYYY-MM-DD'),
|
||||||
|
due_date: moment(new Date()).format('YYYY-MM-DD'),
|
||||||
|
reference_no: '',
|
||||||
|
note: '',
|
||||||
|
entries: [...repeatValue(defaultBill, MIN_LINES_NUMBER)],
|
||||||
|
}),
|
||||||
|
[defaultBill],
|
||||||
|
);
|
||||||
|
|
||||||
|
const orderingIndex = (_invoice) => {
|
||||||
|
return _invoice.map((item, index) => ({
|
||||||
|
...item,
|
||||||
|
index: index + 1,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const initialValues = useMemo(
|
||||||
|
() => ({
|
||||||
|
...defaultInitialValues,
|
||||||
|
entries: orderingIndex(defaultInitialValues.entries),
|
||||||
|
}),
|
||||||
|
[defaultInitialValues],
|
||||||
|
);
|
||||||
|
|
||||||
|
const initialAttachmentFiles = useMemo(() => {
|
||||||
|
return bill && bill.media
|
||||||
|
? bill.media.map((attach) => ({
|
||||||
|
preview: attach.attachment_file,
|
||||||
|
uploaded: true,
|
||||||
|
metadata: { ...attach },
|
||||||
|
}))
|
||||||
|
: [];
|
||||||
|
}, [bill]);
|
||||||
|
|
||||||
|
const formik = useFormik({
|
||||||
|
enableReinitialize: true,
|
||||||
|
validationSchema,
|
||||||
|
initialValues: {
|
||||||
|
...initialValues,
|
||||||
|
},
|
||||||
|
onSubmit: async (values, { setSubmitting, setErrors, resetForm }) => {
|
||||||
|
setSubmitting(true);
|
||||||
|
const entries = values.entries.map((item) => omit(item, ['total']));
|
||||||
|
|
||||||
|
const form = {
|
||||||
|
...values,
|
||||||
|
entries,
|
||||||
|
};
|
||||||
|
const saveBill = (mediaIds) =>
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
const requestForm = { ...form, media_ids: mediaIds };
|
||||||
|
|
||||||
|
requestSubmitBill(requestForm)
|
||||||
|
.then((response) => {
|
||||||
|
AppToaster.show({
|
||||||
|
message: formatMessage(
|
||||||
|
{ id: 'the_bill_has_been_successfully_created' },
|
||||||
|
{ number: values.bill_number },
|
||||||
|
),
|
||||||
|
intent: Intent.SUCCESS,
|
||||||
|
});
|
||||||
|
setSubmitting(false);
|
||||||
|
resetForm();
|
||||||
|
saveBillSubmit({ action: 'new', ...payload });
|
||||||
|
clearSavedMediaIds();
|
||||||
|
})
|
||||||
|
.catch((errors) => {
|
||||||
|
setSubmitting(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Promise.all([saveMedia(), deleteMedia()])
|
||||||
|
.then(([savedMediaResponses]) => {
|
||||||
|
const mediaIds = savedMediaResponses.map((res) => res.data.media.id);
|
||||||
|
savedMediaIds.current = mediaIds;
|
||||||
|
return savedMediaResponses;
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
return saveBill(savedMediaIds.current);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleSubmitClick = useCallback(
|
||||||
|
(payload) => {
|
||||||
|
setPayload(payload);
|
||||||
|
formik.submitForm();
|
||||||
|
},
|
||||||
|
[setPayload, formik],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleCancelClick = useCallback(
|
||||||
|
(payload) => {
|
||||||
|
onCancelForm && onCancelForm(payload);
|
||||||
|
},
|
||||||
|
[onCancelForm],
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(formik.errors, 'Bill');
|
||||||
|
const handleDeleteFile = useCallback(
|
||||||
|
(_deletedFiles) => {
|
||||||
|
_deletedFiles.forEach((deletedFile) => {
|
||||||
|
if (deletedFile.uploaded && deletedFile.metadata.id) {
|
||||||
|
setDeletedFiles([...deletedFiles, deletedFile.metadata.id]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[setDeletedFiles, deletedFiles],
|
||||||
|
);
|
||||||
|
|
||||||
|
const onClickCleanAllLines = () => {
|
||||||
|
formik.setFieldValue(
|
||||||
|
'entries',
|
||||||
|
orderingIndex([...repeatValue(defaultBill, MIN_LINES_NUMBER)]),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClickAddNewRow = () => {
|
||||||
|
formik.setFieldValue(
|
||||||
|
'entries',
|
||||||
|
orderingIndex([...formik.values.entries, defaultBill]),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={'bill-form'}>
|
||||||
|
<form onSubmit={formik.handleSubmit}>
|
||||||
|
<BillFormHeader formik={formik} />
|
||||||
|
<EstimatesItemsTable
|
||||||
|
formik={formik}
|
||||||
|
entries={formik.values.entries}
|
||||||
|
onClickAddNewRow={onClickAddNewRow}
|
||||||
|
onClickClearAllLines={onClickCleanAllLines}
|
||||||
|
/>
|
||||||
|
<FormGroup label={<T id={'note'} />} className={'form-group--'}>
|
||||||
|
<TextArea growVertically={true} {...formik.getFieldProps('note')} />
|
||||||
|
</FormGroup>
|
||||||
|
<Dragzone
|
||||||
|
initialFiles={initialAttachmentFiles}
|
||||||
|
onDrop={handleDropFiles}
|
||||||
|
onDeleteFile={handleDeleteFile}
|
||||||
|
hint={'Attachments: Maxiumum size: 20MB'}
|
||||||
|
/>
|
||||||
|
<BillFormFooter
|
||||||
|
formik={formik}
|
||||||
|
onSubmit={handleSubmitClick}
|
||||||
|
onCancelClick={handleCancelClick}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default compose(
|
||||||
|
withBillActions,
|
||||||
|
withDashboardActions,
|
||||||
|
withMediaActions,
|
||||||
|
)(BillForm);
|
||||||
41
client/src/containers/Purchases/BillFormFooter.js
Normal file
41
client/src/containers/Purchases/BillFormFooter.js
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Intent, Button } from '@blueprintjs/core';
|
||||||
|
import { FormattedMessage as T } from 'react-intl';
|
||||||
|
|
||||||
|
export default function BillFormFooter({
|
||||||
|
formik: { isSubmitting },
|
||||||
|
onSubmitClick,
|
||||||
|
onCancelClick,
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Button disabled={isSubmitting} intent={Intent.PRIMARY} type="submit">
|
||||||
|
<T id={'save_send'} />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
disabled={isSubmitting}
|
||||||
|
intent={Intent.PRIMARY}
|
||||||
|
className={'ml1'}
|
||||||
|
name={'save'}
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
<T id={'save'} />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button className={'ml1'} disabled={isSubmitting}>
|
||||||
|
<T id={'clear'} />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
className={'ml1'}
|
||||||
|
type="submit"
|
||||||
|
onClick={() => {
|
||||||
|
onCancelClick && onCancelClick();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<T id={'close'} />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
185
client/src/containers/Purchases/BillFormHeader.js
Normal file
185
client/src/containers/Purchases/BillFormHeader.js
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
import React, { useMemo, useCallback, useState } from 'react';
|
||||||
|
import {
|
||||||
|
FormGroup,
|
||||||
|
InputGroup,
|
||||||
|
Intent,
|
||||||
|
Position,
|
||||||
|
MenuItem,
|
||||||
|
Classes,
|
||||||
|
} from '@blueprintjs/core';
|
||||||
|
import { DateInput } from '@blueprintjs/datetime';
|
||||||
|
import { FormattedMessage as T } from 'react-intl';
|
||||||
|
import { Row, Col } from 'react-grid-system';
|
||||||
|
import moment from 'moment';
|
||||||
|
import { momentFormatter, compose, tansformDateValue } from 'utils';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import {
|
||||||
|
AccountsSelectList,
|
||||||
|
ListSelect,
|
||||||
|
ErrorMessage,
|
||||||
|
FieldRequiredHint,
|
||||||
|
Hint,
|
||||||
|
} from 'components';
|
||||||
|
|
||||||
|
import withCustomers from 'containers/Customers/withCustomers';
|
||||||
|
import withAccounts from 'containers/Accounts/withAccounts';
|
||||||
|
|
||||||
|
function BillFormHeader({
|
||||||
|
formik: { errors, touched, setFieldValue, getFieldProps, values },
|
||||||
|
|
||||||
|
//#withCustomers
|
||||||
|
customers,
|
||||||
|
//#withAccouts
|
||||||
|
accountsList,
|
||||||
|
}) {
|
||||||
|
const handleDateChange = useCallback(
|
||||||
|
(date_filed) => (date) => {
|
||||||
|
const formatted = moment(date).format('YYYY-MM-DD');
|
||||||
|
setFieldValue(date_filed, formatted);
|
||||||
|
},
|
||||||
|
[setFieldValue],
|
||||||
|
);
|
||||||
|
|
||||||
|
const onChangeSelected = useCallback(
|
||||||
|
(filedName) => {
|
||||||
|
return (item) => {
|
||||||
|
setFieldValue(filedName, item.id);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[setFieldValue],
|
||||||
|
);
|
||||||
|
|
||||||
|
const vendorNameRenderer = useCallback(
|
||||||
|
(accept, { handleClick }) => (
|
||||||
|
<MenuItem
|
||||||
|
key={accept.id}
|
||||||
|
text={accept.display_name}
|
||||||
|
onClick={handleClick}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Filter vendor name
|
||||||
|
const filterVendorAccount = (query, vendor, _index, exactMatch) => {
|
||||||
|
const normalizedTitle = vendor.display_name.toLowerCase();
|
||||||
|
const normalizedQuery = query.toLowerCase();
|
||||||
|
if (exactMatch) {
|
||||||
|
return normalizedTitle === normalizedQuery;
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
`${vendor.display_name} ${normalizedTitle}`.indexOf(normalizedQuery) >=
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
{/* vendor account name */}
|
||||||
|
<FormGroup
|
||||||
|
label={<T id={'vendor_name'} />}
|
||||||
|
inline={true}
|
||||||
|
className={classNames('form-group--select-list', Classes.FILL)}
|
||||||
|
labelInfo={<FieldRequiredHint />}
|
||||||
|
intent={errors.vendor_id && touched.vendor_id && Intent.DANGER}
|
||||||
|
helperText={
|
||||||
|
<ErrorMessage name={'vendor_id'} {...{ errors, touched }} />
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ListSelect
|
||||||
|
items={customers}
|
||||||
|
noResults={<MenuItem disabled={true} text="No results." />}
|
||||||
|
itemRenderer={vendorNameRenderer}
|
||||||
|
itemPredicate={filterVendorAccount}
|
||||||
|
popoverProps={{ minimal: true }}
|
||||||
|
onItemSelect={onChangeSelected('vendor_id')}
|
||||||
|
selectedItem={values.vendor_id}
|
||||||
|
selectedItemProp={'id'}
|
||||||
|
defaultText={<T id={'select_vendor_account'} />}
|
||||||
|
labelProp={'display_name'}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
<Row>
|
||||||
|
<Col>
|
||||||
|
<FormGroup
|
||||||
|
label={<T id={'bill_date'} />}
|
||||||
|
inline={true}
|
||||||
|
labelInfo={<FieldRequiredHint />}
|
||||||
|
className={classNames('form-group--select-list', Classes.FILL)}
|
||||||
|
intent={errors.bill_date && touched.bill_date && Intent.DANGER}
|
||||||
|
helperText={
|
||||||
|
<ErrorMessage name="bill_date" {...{ errors, touched }} />
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<DateInput
|
||||||
|
{...momentFormatter('YYYY/MM/DD')}
|
||||||
|
value={tansformDateValue(values.bill_date)}
|
||||||
|
onChange={handleDateChange('bill_date')}
|
||||||
|
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</Col>
|
||||||
|
<Col>
|
||||||
|
<FormGroup
|
||||||
|
label={<T id={'due_date'} />}
|
||||||
|
inline={true}
|
||||||
|
className={classNames('form-group--select-list', Classes.FILL)}
|
||||||
|
intent={errors.due_date && touched.due_date && Intent.DANGER}
|
||||||
|
helperText={
|
||||||
|
<ErrorMessage name="due_date" {...{ errors, touched }} />
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<DateInput
|
||||||
|
{...momentFormatter('YYYY/MM/DD')}
|
||||||
|
value={tansformDateValue(values.due_date)}
|
||||||
|
onChange={handleDateChange('due_date')}
|
||||||
|
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
{/* bill number */}
|
||||||
|
<FormGroup
|
||||||
|
label={<T id={'bill_number'} />}
|
||||||
|
inline={true}
|
||||||
|
className={('form-group--estimate', Classes.FILL)}
|
||||||
|
labelInfo={<FieldRequiredHint />}
|
||||||
|
intent={errors.bill_number && touched.bill_number && Intent.DANGER}
|
||||||
|
helperText={
|
||||||
|
<ErrorMessage name="bill_number" {...{ errors, touched }} />
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<InputGroup
|
||||||
|
intent={errors.bill_number && touched.bill_number && Intent.DANGER}
|
||||||
|
minimal={true}
|
||||||
|
{...getFieldProps('bill_number')}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</div>
|
||||||
|
<FormGroup
|
||||||
|
label={<T id={'reference'} />}
|
||||||
|
inline={true}
|
||||||
|
className={classNames('form-group--reference', Classes.FILL)}
|
||||||
|
intent={errors.reference_no && touched.reference_no && Intent.DANGER}
|
||||||
|
helperText={<ErrorMessage name="reference" {...{ errors, touched }} />}
|
||||||
|
>
|
||||||
|
<InputGroup
|
||||||
|
intent={errors.reference_no && touched.reference_no && Intent.DANGER}
|
||||||
|
minimal={true}
|
||||||
|
{...getFieldProps('reference_no')}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default compose(
|
||||||
|
withCustomers(({ customers }) => ({
|
||||||
|
customers,
|
||||||
|
})),
|
||||||
|
withAccounts(({ accountsList }) => ({
|
||||||
|
accountsList,
|
||||||
|
})),
|
||||||
|
)(BillFormHeader);
|
||||||
62
client/src/containers/Purchases/Bills.js
Normal file
62
client/src/containers/Purchases/Bills.js
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import React, { useCallback } from 'react';
|
||||||
|
import { useParams, useHistory } from 'react-router-dom';
|
||||||
|
import { useQuery } from 'react-query';
|
||||||
|
|
||||||
|
import BillForm from './BillForm';
|
||||||
|
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||||
|
|
||||||
|
import withCustomersActions from 'containers/Customers/withCustomersActions';
|
||||||
|
import withAccountsActions from 'containers/Accounts/withAccountsActions';
|
||||||
|
import withItemsActions from 'containers/Items/withItemsActions';
|
||||||
|
|
||||||
|
import { compose } from 'utils';
|
||||||
|
|
||||||
|
function Bills({
|
||||||
|
//#withwithAccountsActions
|
||||||
|
requestFetchAccounts,
|
||||||
|
|
||||||
|
//#withCustomersActions
|
||||||
|
requestFetchCustomers,
|
||||||
|
|
||||||
|
//#withItemsActions
|
||||||
|
requestFetchItems,
|
||||||
|
}) {
|
||||||
|
const history = useHistory();
|
||||||
|
|
||||||
|
// Handle fetch accounts
|
||||||
|
const fetchAccounts = useQuery('accounts-list', (key) =>
|
||||||
|
requestFetchAccounts(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Handle fetch customers data table
|
||||||
|
const fetchCustomers = useQuery('customers-table', () =>
|
||||||
|
requestFetchCustomers({}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Handle fetch Items data table or list
|
||||||
|
const fetchItems = useQuery('items-table', () => requestFetchItems({}));
|
||||||
|
|
||||||
|
const handleFormSubmit = useCallback((payload) => {}, [history]);
|
||||||
|
|
||||||
|
const handleCancel = useCallback(() => {
|
||||||
|
history.goBack();
|
||||||
|
}, [history]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DashboardInsider
|
||||||
|
loading={
|
||||||
|
fetchCustomers.isFetching ||
|
||||||
|
fetchItems.isFetching ||
|
||||||
|
fetchAccounts.isFetching
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<BillForm onSubmit={handleFormSubmit} onCancel={handleCancel} />
|
||||||
|
</DashboardInsider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default compose(
|
||||||
|
withCustomersActions,
|
||||||
|
withItemsActions,
|
||||||
|
withAccountsActions,
|
||||||
|
)(Bills);
|
||||||
32
client/src/containers/Purchases/withBillActions.js
Normal file
32
client/src/containers/Purchases/withBillActions.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { connect } from 'react-redux';
|
||||||
|
import {
|
||||||
|
submitBill,
|
||||||
|
deleteBill,
|
||||||
|
editBill,
|
||||||
|
fetchBillsTable,
|
||||||
|
fetchBill,
|
||||||
|
} from 'store/Bills/bills.actions';
|
||||||
|
import t from 'store/types';
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
requestSubmitBill: (form) => dispatch(submitBill({ form })),
|
||||||
|
requestFetchBill: (id) => dispatch(fetchBill({ id })),
|
||||||
|
requestEditBill: (id, form) => dispatch(editBill({ id, form })),
|
||||||
|
requestDeleteBill: (id) => dispatch(deleteBill({ id })),
|
||||||
|
requestFetchBillsTable: (query = {}) =>
|
||||||
|
dispatch(fetchBillsTable({ query: { ...query } })),
|
||||||
|
|
||||||
|
changeBillView: (id) =>
|
||||||
|
dispatch({
|
||||||
|
type: t.BILL_SET_CURRENT_VIEW,
|
||||||
|
currentViewId: parseInt(id, 10),
|
||||||
|
}),
|
||||||
|
|
||||||
|
addBillsTableQueries: (queries) =>
|
||||||
|
dispatch({
|
||||||
|
type: t.BILLS_TABLE_QUERIES_ADD,
|
||||||
|
queries,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(null, mapDispatchToProps);
|
||||||
260
client/src/containers/Sales/PaymentReceive/PaymentReceiveForm.js
Normal file
260
client/src/containers/Sales/PaymentReceive/PaymentReceiveForm.js
Normal file
@@ -0,0 +1,260 @@
|
|||||||
|
import React, { useMemo, useCallback, useEffect, useState,useRef } from 'react';
|
||||||
|
|
||||||
|
import * as Yup from 'yup';
|
||||||
|
import { useFormik } from 'formik';
|
||||||
|
import moment from 'moment';
|
||||||
|
import { Intent, FormGroup, TextArea } from '@blueprintjs/core';
|
||||||
|
|
||||||
|
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||||
|
|
||||||
|
import PaymentReceiveHeader from './PaymentReceiveFormHeader';
|
||||||
|
// PaymentReceiptItemsTable
|
||||||
|
import PaymentReceiveFooter from './PaymentReceiveFormFooter';
|
||||||
|
|
||||||
|
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||||
|
import withMediaActions from 'containers/Media/withMediaActions';
|
||||||
|
import withPaymentReceivesActions from './withPaymentReceivesActions'
|
||||||
|
|
||||||
|
import { AppToaster } from 'components';
|
||||||
|
import Dragzone from 'components/Dragzone';
|
||||||
|
import useMedia from 'hooks/useMedia';
|
||||||
|
|
||||||
|
import { compose, repeatValue } from 'utils';
|
||||||
|
|
||||||
|
const MIN_LINES_NUMBER = 4;
|
||||||
|
|
||||||
|
function PaymentReceiveForm({
|
||||||
|
//#withMedia
|
||||||
|
requestSubmitMedia,
|
||||||
|
requestDeleteMedia,
|
||||||
|
|
||||||
|
//#WithPaymentReceiveActions
|
||||||
|
requestSubmitPaymentReceive,
|
||||||
|
|
||||||
|
|
||||||
|
//#withDashboard
|
||||||
|
changePageTitle,
|
||||||
|
changePageSubtitle,
|
||||||
|
|
||||||
|
//#withPaymentReceiveDetail
|
||||||
|
|
||||||
|
//#OWn Props
|
||||||
|
payment_receive,
|
||||||
|
onFormSubmit,
|
||||||
|
onCancelForm,
|
||||||
|
}) {
|
||||||
|
const { formatMessage } = useIntl();
|
||||||
|
const [payload, setPayload] = useState({});
|
||||||
|
|
||||||
|
const {
|
||||||
|
setFiles,
|
||||||
|
saveMedia,
|
||||||
|
deletedFiles,
|
||||||
|
setDeletedFiles,
|
||||||
|
deleteMedia,
|
||||||
|
} = useMedia({
|
||||||
|
saveCallback: requestSubmitMedia,
|
||||||
|
deleteCallback: requestDeleteMedia,
|
||||||
|
});
|
||||||
|
|
||||||
|
const savedMediaIds = useRef([]);
|
||||||
|
const clearSavedMediaIds = () => {
|
||||||
|
savedMediaIds.current = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (payment_receive && payment_receive.id) {
|
||||||
|
changePageTitle(formatMessage({ id: 'edit_payment_receive' }));
|
||||||
|
} else {
|
||||||
|
changePageTitle(formatMessage({ id: 'new_payment_receive' }));
|
||||||
|
}
|
||||||
|
}, [changePageTitle, payment_receive, formatMessage]);
|
||||||
|
|
||||||
|
const validationSchema = Yup.object().shape({
|
||||||
|
customer_id: Yup.string()
|
||||||
|
.label(formatMessage({ id: 'customer_name_' }))
|
||||||
|
.required(),
|
||||||
|
deposit_account_id: Yup.number()
|
||||||
|
.required()
|
||||||
|
.label(formatMessage({ id: 'deposit_account_' })),
|
||||||
|
payment_date: Yup.date()
|
||||||
|
.required()
|
||||||
|
.label(formatMessage({ id: 'payment_date_' })),
|
||||||
|
payment_receive_no: Yup.number()
|
||||||
|
.required()
|
||||||
|
.label(formatMessage({ id: 'payment_receive_no_' })),
|
||||||
|
reference_no: Yup.string().min(1).max(255),
|
||||||
|
statement: Yup.string()
|
||||||
|
.trim()
|
||||||
|
.min(1)
|
||||||
|
.max(1024)
|
||||||
|
.label(formatMessage({ id: 'statement' })),
|
||||||
|
|
||||||
|
entries: Yup.array().of(
|
||||||
|
Yup.object().shape({
|
||||||
|
payment_amount: Yup.number().nullable(),
|
||||||
|
item_id: Yup.number()
|
||||||
|
.nullable()
|
||||||
|
.when(['payment_amount'], {
|
||||||
|
is: (payment_amount) => payment_amount,
|
||||||
|
then: Yup.number().required(),
|
||||||
|
}),
|
||||||
|
description: Yup.string().nullable(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleDropFiles = useCallback((_files) => {
|
||||||
|
setFiles(_files.filter((file) => file.uploaded === false));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const savePaymentReceiveSubmit = useCallback((payload) => {
|
||||||
|
onFormSubmit && onFormSubmit(payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
const defaultPaymentReceive = useMemo(
|
||||||
|
() => ({
|
||||||
|
item_id: null,
|
||||||
|
payment_amount: null,
|
||||||
|
description: null,
|
||||||
|
}),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
const defaultInitialValues = useMemo(
|
||||||
|
() => ({
|
||||||
|
customer_id: '',
|
||||||
|
deposit_account_id: '',
|
||||||
|
payment_date: moment(new Date()).format('YYYY-MM-DD'),
|
||||||
|
reference_no: '',
|
||||||
|
statement: '',
|
||||||
|
entries: [...repeatValue(defaultPaymentReceive, MIN_LINES_NUMBER)],
|
||||||
|
}),
|
||||||
|
[defaultPaymentReceive],
|
||||||
|
);
|
||||||
|
|
||||||
|
const initialValues = useMemo(
|
||||||
|
() => ({
|
||||||
|
...defaultInitialValues,
|
||||||
|
entries: defaultInitialValues.entries,
|
||||||
|
}),
|
||||||
|
[defaultPaymentReceive, defaultInitialValues, payment_receive],
|
||||||
|
);
|
||||||
|
|
||||||
|
const initialAttachmentFiles = useMemo(() => {
|
||||||
|
return payment_receive && payment_receive.media
|
||||||
|
? payment_receive.media.map((attach) => ({
|
||||||
|
preview: attach.attachment_file,
|
||||||
|
uploaded: true,
|
||||||
|
metadata: { ...attach },
|
||||||
|
}))
|
||||||
|
: [];
|
||||||
|
}, [payment_receive]);
|
||||||
|
|
||||||
|
const formik = useFormik({
|
||||||
|
enableReinitialize: true,
|
||||||
|
validationSchema,
|
||||||
|
initialValues: {
|
||||||
|
...initialValues,
|
||||||
|
},
|
||||||
|
onSubmit: async (values, { setSubmitting, setErrors, resetForm }) => {
|
||||||
|
const form = {
|
||||||
|
...values,
|
||||||
|
};
|
||||||
|
const savePaymentReceive = (mediaIds) =>
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
const requestForm = { ...form, media_ids: mediaIds };
|
||||||
|
|
||||||
|
requestSubmitPaymentReceive(requestForm)
|
||||||
|
.the((response) => {
|
||||||
|
AppToaster.show({
|
||||||
|
message: formatMessage({
|
||||||
|
id: 'the_payment_receive_has_been_successfully_created',
|
||||||
|
}),
|
||||||
|
intent: Intent.SUCCESS,
|
||||||
|
});
|
||||||
|
setSubmitting(false);
|
||||||
|
clearSavedMediaIds();
|
||||||
|
savePaymentReceiveSubmit({ action: 'new', ...payload });
|
||||||
|
})
|
||||||
|
.catch((errors) => {
|
||||||
|
setSubmitting(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
Promise.all([saveMedia(), deleteMedia()])
|
||||||
|
.then(([savedMediaResponses]) => {
|
||||||
|
const mediaIds = savedMediaResponses.map((res) => res.data.media.id);
|
||||||
|
savedMediaIds.current = mediaIds;
|
||||||
|
return savedMediaResponses;
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
return savePaymentReceive(savedMediaIds.current);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleDeleteFile = useCallback(
|
||||||
|
(_deletedFiles) => {
|
||||||
|
_deletedFiles.forEach((deletedFile) => {
|
||||||
|
if (deletedFile.upload && deletedFile.metadata.id) {
|
||||||
|
setDeletedFiles([...deletedFiles, deletedFile.metadata.id]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[setDeletedFiles, deletedFiles],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleSubmitClick = useCallback(
|
||||||
|
(payload) => {
|
||||||
|
setPayload(payload);
|
||||||
|
formik.submitForm();
|
||||||
|
},
|
||||||
|
[setPayload, formik],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleCancelClick = useCallback(
|
||||||
|
(payload) => {
|
||||||
|
onCancelForm && onCancelForm(payload);
|
||||||
|
},
|
||||||
|
[onCancelForm],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleClearClick = () => {
|
||||||
|
formik.resetForm();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={'payment_receive_form'}>
|
||||||
|
<form onSubmit={formik.handleSubmit}>
|
||||||
|
<PaymentReceiveHeader formik={formik} />
|
||||||
|
<FormGroup
|
||||||
|
label={<T id={'statement'} />}
|
||||||
|
className={'form-group--statement'}
|
||||||
|
>
|
||||||
|
<TextArea
|
||||||
|
growVertically={true}
|
||||||
|
{...formik.getFieldProps('statement')}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
<Dragzone
|
||||||
|
initialFiles={initialAttachmentFiles}
|
||||||
|
onDrop={handleDropFiles}
|
||||||
|
onDeleteFile={handleDeleteFile}
|
||||||
|
hint={'Attachments: Maxiumum size: 20MB'}
|
||||||
|
/>
|
||||||
|
<PaymentReceiveFooter
|
||||||
|
formik={formik}
|
||||||
|
onSubmit={handleSubmitClick}
|
||||||
|
onCancel={handleCancelClick}
|
||||||
|
onClearClick={handleClearClick}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default compose(
|
||||||
|
withPaymentReceivesActions,
|
||||||
|
withDashboardActions,
|
||||||
|
withMediaActions,
|
||||||
|
)(PaymentReceiveForm);
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Intent, Button } from '@blueprintjs/core';
|
||||||
|
import { FormattedMessage as T } from 'react-intl';
|
||||||
|
|
||||||
|
export default function PaymentReceiveFormFooter({
|
||||||
|
formik: { isSubmitting, resetForm },
|
||||||
|
onSubmitClick,
|
||||||
|
onCancelClick,
|
||||||
|
onClearClick,
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className={'estimate-form__floating-footer'}>
|
||||||
|
<Button disabled={isSubmitting} intent={Intent.PRIMARY} type="submit">
|
||||||
|
<T id={'save_send'} />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
disabled={isSubmitting}
|
||||||
|
intent={Intent.PRIMARY}
|
||||||
|
className={'ml1'}
|
||||||
|
name={'save'}
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
<T id={'save'} />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
className={'ml1'}
|
||||||
|
disabled={isSubmitting}
|
||||||
|
onClick={() => onClearClick && onClearClick()}
|
||||||
|
>
|
||||||
|
<T id={'clear'} />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
className={'ml1'}
|
||||||
|
type="submit"
|
||||||
|
onClick={() => {
|
||||||
|
onCancelClick && onCancelClick();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<T id={'close'} />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,204 @@
|
|||||||
|
import React, { useMemo, useCallback, useState } from 'react';
|
||||||
|
import {
|
||||||
|
FormGroup,
|
||||||
|
InputGroup,
|
||||||
|
Intent,
|
||||||
|
Position,
|
||||||
|
MenuItem,
|
||||||
|
Classes,
|
||||||
|
} from '@blueprintjs/core';
|
||||||
|
|
||||||
|
import { DateInput } from '@blueprintjs/datetime';
|
||||||
|
import { FormattedMessage as T } from 'react-intl';
|
||||||
|
import moment from 'moment';
|
||||||
|
import { momentFormatter, compose, tansformDateValue } from 'utils';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import {
|
||||||
|
AccountsSelectList,
|
||||||
|
ListSelect,
|
||||||
|
ErrorMessage,
|
||||||
|
FieldRequiredHint,
|
||||||
|
} from 'components';
|
||||||
|
|
||||||
|
import withCustomers from 'containers/Customers/withCustomers';
|
||||||
|
import withAccounts from 'containers/Accounts/withAccounts';
|
||||||
|
|
||||||
|
function PaymentReceiveFormHeader({
|
||||||
|
formik: { errors, touched, setFieldValue, getFieldProps, values },
|
||||||
|
|
||||||
|
//#withCustomers
|
||||||
|
customers,
|
||||||
|
//#withAccouts
|
||||||
|
accountsList,
|
||||||
|
}) {
|
||||||
|
const handleDateChange = useCallback(
|
||||||
|
(date_filed) => (date) => {
|
||||||
|
const formatted = moment(date).format('YYYY-MM-DD');
|
||||||
|
setFieldValue(date_filed, formatted);
|
||||||
|
},
|
||||||
|
[setFieldValue],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleCusomterRenderer = useCallback(
|
||||||
|
(custom, { handleClick }) => (
|
||||||
|
<MenuItem
|
||||||
|
key={custom.id}
|
||||||
|
text={custom.display_name}
|
||||||
|
onClick={handleClick}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleFilterCustomer = (query, customer, index, exactMatch) => {
|
||||||
|
const normalizedTitle = customer.display_name.toLowerCase();
|
||||||
|
const normalizedQuery = query.toLowerCase();
|
||||||
|
if (exactMatch) {
|
||||||
|
return normalizedTitle === normalizedQuery;
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
`${customer.display_name} ${normalizedTitle}`.indexOf(
|
||||||
|
normalizedQuery,
|
||||||
|
) >= 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onChangeSelect = useCallback(
|
||||||
|
(filedName) => {
|
||||||
|
return (item) => {
|
||||||
|
setFieldValue(filedName, item.id);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[setFieldValue],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Filter deposit accounts.
|
||||||
|
const depositAccounts = useMemo(
|
||||||
|
() => accountsList.filter((a) => a?.type?.key === 'current_asset'),
|
||||||
|
[accountsList],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
{/* Customer name */}
|
||||||
|
<FormGroup
|
||||||
|
label={<T id={'customer_name'} />}
|
||||||
|
inline={true}
|
||||||
|
className={classNames('form-group--select-list', Classes.FILL)}
|
||||||
|
labelInfo={<FieldRequiredHint />}
|
||||||
|
intent={errors.customer_id && touched.customer_id && Intent.DANGER}
|
||||||
|
helperText={
|
||||||
|
<ErrorMessage name={'customer_id'} {...{ errors, touched }} />
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ListSelect
|
||||||
|
items={customers}
|
||||||
|
noResults={<MenuItem disabled={true} text="No results." />}
|
||||||
|
itemRenderer={handleCusomterRenderer}
|
||||||
|
itemPredicate={handleFilterCustomer}
|
||||||
|
popoverProps={{ minimal: true }}
|
||||||
|
onItemSelect={onChangeSelect('customer_id')}
|
||||||
|
selectedItem={values.customer_id}
|
||||||
|
selectedItemProp={'id'}
|
||||||
|
defaultText={<T id={'select_customer_account'} />}
|
||||||
|
labelProp={'display_name'}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
label={<T id={'deposit_account'} />}
|
||||||
|
className={classNames(
|
||||||
|
'form-group--deposit_account_id',
|
||||||
|
'form-group--select-list',
|
||||||
|
Classes.FILL,
|
||||||
|
)}
|
||||||
|
inline={true}
|
||||||
|
labelInfo={<FieldRequiredHint />}
|
||||||
|
intent={
|
||||||
|
errors.deposit_account_id &&
|
||||||
|
touched.deposit_account_id &&
|
||||||
|
Intent.DANGER
|
||||||
|
}
|
||||||
|
helperText={
|
||||||
|
<ErrorMessage
|
||||||
|
name={'deposit_account_id'}
|
||||||
|
{...{ errors, touched }}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<AccountsSelectList
|
||||||
|
accounts={depositAccounts}
|
||||||
|
onAccountSelected={onChangeSelect('deposit_account_id')}
|
||||||
|
defaultSelectText={<T id={'select_deposit_account'} />}
|
||||||
|
selectedAccountId={values.deposit_account_id}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup
|
||||||
|
label={<T id={'payment_date'} />}
|
||||||
|
inline={true}
|
||||||
|
className={classNames('form-group--select-list', Classes.FILL)}
|
||||||
|
intent={errors.payment_date && touched.payment_date && Intent.DANGER}
|
||||||
|
helperText={
|
||||||
|
<ErrorMessage name="payment_date" {...{ errors, touched }} />
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<DateInput
|
||||||
|
{...momentFormatter('YYYY/MM/DD')}
|
||||||
|
value={tansformDateValue(values.payment_date)}
|
||||||
|
onChange={handleDateChange}
|
||||||
|
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</div>
|
||||||
|
{/* payment receive no */}
|
||||||
|
<FormGroup
|
||||||
|
label={<T id={'payment_receive_no'} />}
|
||||||
|
inline={true}
|
||||||
|
className={('form-group--payment_receive_no', Classes.FILL)}
|
||||||
|
labelInfo={<FieldRequiredHint />}
|
||||||
|
intent={
|
||||||
|
errors.payment_receive_no &&
|
||||||
|
touched.payment_receive_no &&
|
||||||
|
Intent.DANGER
|
||||||
|
}
|
||||||
|
helperText={
|
||||||
|
<ErrorMessage name="payment_receive_no" {...{ errors, touched }} />
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<InputGroup
|
||||||
|
intent={
|
||||||
|
errors.payment_receive_no &&
|
||||||
|
touched.payment_receive_no &&
|
||||||
|
Intent.DANGER
|
||||||
|
}
|
||||||
|
minimal={true}
|
||||||
|
{...getFieldProps('payment_receive_no')}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
{/* reference_no */}
|
||||||
|
<FormGroup
|
||||||
|
label={<T id={'reference'} />}
|
||||||
|
inline={true}
|
||||||
|
className={classNames('form-group--reference', Classes.FILL)}
|
||||||
|
intent={errors.reference_no && touched.reference_no && Intent.DANGER}
|
||||||
|
helperText={<ErrorMessage name="reference" {...{ errors, touched }} />}
|
||||||
|
>
|
||||||
|
<InputGroup
|
||||||
|
intent={errors.reference_no && touched.reference_no && Intent.DANGER}
|
||||||
|
minimal={true}
|
||||||
|
{...getFieldProps('reference_no')}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default compose(
|
||||||
|
withCustomers(({ customers }) => ({
|
||||||
|
customers,
|
||||||
|
})),
|
||||||
|
withAccounts(({ accountsList }) => ({
|
||||||
|
accountsList,
|
||||||
|
})),
|
||||||
|
)(PaymentReceiveFormHeader);
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import React, { useState, useMemo, useEffect, useCallback } from 'react';
|
||||||
|
import { Button, Intent, Position, Tooltip } from '@blueprintjs/core';
|
||||||
|
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||||
|
import DataTable from 'components/DataTable';
|
||||||
|
import Icon from 'components/Icon';
|
||||||
|
|
||||||
|
import { compose, formattedAmount, transformUpdatedRows } from 'utils';
|
||||||
|
import {
|
||||||
|
InputGroupCell,
|
||||||
|
MoneyFieldCell,
|
||||||
|
EstimatesListFieldCell,
|
||||||
|
DivFieldCell,
|
||||||
|
} from 'components/DataTableCells';
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
import React, { useCallback } from 'react';
|
||||||
|
import { useParams, useHistory } from 'react-router-dom';
|
||||||
|
import { useQuery } from 'react-query';
|
||||||
|
|
||||||
|
import PaymentReceiveForm from './PaymentReceiveForm';
|
||||||
|
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||||
|
|
||||||
|
import withCustomersActions from 'containers/Customers/withCustomersActions';
|
||||||
|
import withAccountsActions from 'containers/Accounts/withAccountsActions';
|
||||||
|
import withItemsActions from 'containers/Items/withItemsActions';
|
||||||
|
//#withInvoiceActions
|
||||||
|
|
||||||
|
import { compose } from 'utils';
|
||||||
|
|
||||||
|
function PaymentReceives({
|
||||||
|
//#withwithAccountsActions
|
||||||
|
requestFetchAccounts,
|
||||||
|
|
||||||
|
//#withCustomersActions
|
||||||
|
requestFetchCustomers,
|
||||||
|
|
||||||
|
//#withItemsActions
|
||||||
|
requestFetchItems,
|
||||||
|
|
||||||
|
//#withInvoiceActions
|
||||||
|
}) {
|
||||||
|
const history = useHistory();
|
||||||
|
|
||||||
|
// Handle fetch accounts data
|
||||||
|
const fetchAccounts = useQuery('accounts-list', (key) =>
|
||||||
|
requestFetchAccounts(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Handle fetch Items data table or list
|
||||||
|
const fetchItems = useQuery('items-table', () => requestFetchItems({}));
|
||||||
|
|
||||||
|
// Handle fetch customers data table or list
|
||||||
|
const fetchCustomers = useQuery('customers-table', () =>
|
||||||
|
requestFetchCustomers({}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleFormSubmit = useCallback((payload) => {}, [history]);
|
||||||
|
|
||||||
|
const handleCancel = useCallback(() => {
|
||||||
|
history.goBack();
|
||||||
|
}, [history]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DashboardInsider
|
||||||
|
loading={
|
||||||
|
fetchCustomers.isFetching ||
|
||||||
|
fetchItems.isFetching ||
|
||||||
|
fetchAccounts.isFetching
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<PaymentReceiveForm
|
||||||
|
onFormSubmit={handleFormSubmit}
|
||||||
|
onCancelForm={handleCancel}
|
||||||
|
/>
|
||||||
|
</DashboardInsider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default compose(
|
||||||
|
withCustomersActions,
|
||||||
|
withItemsActions,
|
||||||
|
withAccountsActions,
|
||||||
|
// withInvoiceActions
|
||||||
|
)(PaymentReceives);
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import { connect } from 'react-redux';
|
||||||
|
import {
|
||||||
|
submitPaymentReceive,
|
||||||
|
editPaymentReceive,
|
||||||
|
deletePaymentReceive,
|
||||||
|
fetchPaymentReceive,
|
||||||
|
fetchPaymentReceivesTable,
|
||||||
|
} from 'store/PaymentReceive/paymentReceive.actions';
|
||||||
|
import t from 'store/types';
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
requestSubmitPaymentReceive: (form) =>
|
||||||
|
dispatch(submitPaymentReceive({ form })),
|
||||||
|
requestFetchPaymentReceive: (id) => dispatch(fetchPaymentReceive({ id })),
|
||||||
|
requestEditPaymentReceive: (id, form) =>
|
||||||
|
dispatch(editPaymentReceive({ id, form })),
|
||||||
|
requestDeletePaymentReceive: (id) => dispatch(deletePaymentReceive({ id })),
|
||||||
|
requestFetchPaymentReceiveTable: (query = {}) =>
|
||||||
|
dispatch(fetchPaymentReceivesTable({ query: { ...query } })),
|
||||||
|
|
||||||
|
changePaymentReceiveView: (id) =>
|
||||||
|
dispatch({
|
||||||
|
type: t.PAYMENT_RECEIVE_SET_CURRENT_VIEW,
|
||||||
|
currentViewId: parseInt(id, 10),
|
||||||
|
}),
|
||||||
|
|
||||||
|
addPaymentReceivesTableQueries: (queries) =>
|
||||||
|
dispatch({
|
||||||
|
type: t.PAYMENT_RECEIVE_TABLE_QUERIES_ADD,
|
||||||
|
queries,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(null, mapDispatchToProps);
|
||||||
@@ -633,4 +633,33 @@ export default {
|
|||||||
'The receipt has been successfully edited.',
|
'The receipt has been successfully edited.',
|
||||||
the_receipt_has_been_successfully_deleted:
|
the_receipt_has_been_successfully_deleted:
|
||||||
'The receipt has been successfully deleted.',
|
'The receipt has been successfully deleted.',
|
||||||
|
|
||||||
|
bills: 'Bills',
|
||||||
|
accept: 'Accept',
|
||||||
|
vendor_name: 'Vendor Name',
|
||||||
|
select_vendor_account: 'Select Vendor Account',
|
||||||
|
select_accept_account: 'Select Accept Account',
|
||||||
|
bill_date: 'Bill Date',
|
||||||
|
due_date: 'Due Date',
|
||||||
|
bill_number: 'Bill Number',
|
||||||
|
edit_bill: 'Edit Bill',
|
||||||
|
new_bill: 'New Bill',
|
||||||
|
bill_date_: 'Bill date',
|
||||||
|
bill_number_: 'Bill number',
|
||||||
|
vendor_name_: 'Vendor name',
|
||||||
|
|
||||||
|
the_bill_has_been_successfully_created:
|
||||||
|
'The bill has been successfully created.',
|
||||||
|
|
||||||
|
edit_payment_receive: 'Edit Payment Receive',
|
||||||
|
new_payment_receive: 'New Payment Receive',
|
||||||
|
payment_receives: 'Payment Receives',
|
||||||
|
payment_receive_no: 'Payment Receive #',
|
||||||
|
payment_receive_no_: 'Payment receive no',
|
||||||
|
the_payment_receive_has_been_successfully_created:
|
||||||
|
'The payment receive has been successfully created.',
|
||||||
|
|
||||||
|
select_invoice:'Select Invoice',
|
||||||
|
|
||||||
|
payment_mades: 'Payment Mades',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -275,4 +275,42 @@ export default [
|
|||||||
// }),
|
// }),
|
||||||
// breadcrumb: 'New Receipt',
|
// breadcrumb: 'New Receipt',
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// Payment Receives
|
||||||
|
|
||||||
|
{
|
||||||
|
path: `/payment-receive/:id/edit`,
|
||||||
|
component: LazyLoader({
|
||||||
|
loader: () => import('containers/Sales/PaymentReceive/PaymentReceives'),
|
||||||
|
}),
|
||||||
|
breadcrumb: 'Edit',
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: `/payment-receive/new`,
|
||||||
|
component: LazyLoader({
|
||||||
|
loader: () => import('containers/Sales/PaymentReceive/PaymentReceives'),
|
||||||
|
}),
|
||||||
|
breadcrumb: 'New Payment Receive',
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//Bills
|
||||||
|
{
|
||||||
|
path: `/bill/:id/edit`,
|
||||||
|
component: LazyLoader({
|
||||||
|
loader: () => import('containers/Purchases/Bills'),
|
||||||
|
}),
|
||||||
|
breadcrumb: 'Edit',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: `/bill/new`,
|
||||||
|
component: LazyLoader({
|
||||||
|
loader: () => import('containers/Purchases/Bills'),
|
||||||
|
}),
|
||||||
|
breadcrumb: 'New Bill',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
136
client/src/store/Bills/bills.actions.js
Normal file
136
client/src/store/Bills/bills.actions.js
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
import ApiService from 'services/ApiService';
|
||||||
|
import t from 'store/types';
|
||||||
|
|
||||||
|
export const fetchBillsTable = ({ query = {} }) => {
|
||||||
|
return (dispatch, getState) =>
|
||||||
|
new Promise((resolve, rejcet) => {
|
||||||
|
const pageQuery = getState().bill.tableQuery;
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: t.BILLS_TABLE_LOADING,
|
||||||
|
payload: {
|
||||||
|
loading: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
ApiService.get('bills', {
|
||||||
|
params: { ...pageQuery, ...query },
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
dispatch({
|
||||||
|
type: t.BILLS_PAGE_SET,
|
||||||
|
payload: {
|
||||||
|
bills: response.data.bills.results,
|
||||||
|
pagination: response.data.bills.pagination,
|
||||||
|
customViewId: response.data.customViewId || -1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
dispatch({
|
||||||
|
type: t.BILLS_ITEMS_SET,
|
||||||
|
payload: {
|
||||||
|
bills: response.data.bills.results,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
dispatch({
|
||||||
|
type: t.BILLS_PAGINATION_SET,
|
||||||
|
payload: {
|
||||||
|
pagination: response.data.bills.pagination,
|
||||||
|
customViewId: response.data.customViewId || -1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
dispatch({
|
||||||
|
type: t.BILLS_TABLE_LOADING,
|
||||||
|
payload: {
|
||||||
|
loading: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
rejcet(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteBill = ({ id }) => {
|
||||||
|
return (dispatch) =>
|
||||||
|
new Promise((resovle, reject) => {
|
||||||
|
ApiService.delete(`bills/${id}`)
|
||||||
|
.then((response) => {
|
||||||
|
dispatch({ type: t.BILL_DELETE });
|
||||||
|
resovle(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(error.response.data.errors || []);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const submitBill = ({ form }) => {
|
||||||
|
return (dispatch) =>
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
dispatch({
|
||||||
|
type: t.SET_DASHBOARD_REQUEST_LOADING,
|
||||||
|
});
|
||||||
|
ApiService.post('bills', form)
|
||||||
|
.then((response) => {
|
||||||
|
dispatch({
|
||||||
|
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
||||||
|
});
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
const { response } = error;
|
||||||
|
const { data } = response;
|
||||||
|
dispatch({
|
||||||
|
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
||||||
|
});
|
||||||
|
reject(data?.errors);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fetchBill = ({ id }) => {
|
||||||
|
return (dispatch) =>
|
||||||
|
new Promise((resovle, reject) => {
|
||||||
|
ApiService.get(`bills/${id}`)
|
||||||
|
.then((response) => {
|
||||||
|
dispatch({
|
||||||
|
type: t.BILL_SET,
|
||||||
|
payload: {
|
||||||
|
id,
|
||||||
|
bill: response.data.bill,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
resovle(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
const { response } = error;
|
||||||
|
const { data } = response;
|
||||||
|
reject(data?.errors);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const editBill = (id, form) => {
|
||||||
|
return (dispatch) =>
|
||||||
|
new Promise((resolve, rejcet) => {
|
||||||
|
dispatch({
|
||||||
|
type: t.SET_DASHBOARD_REQUEST_LOADING,
|
||||||
|
});
|
||||||
|
ApiService.post(`bills/${id}`, form)
|
||||||
|
.then((response) => {
|
||||||
|
dispatch({
|
||||||
|
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
||||||
|
});
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
const { response } = error;
|
||||||
|
const { data } = response;
|
||||||
|
dispatch({
|
||||||
|
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
||||||
|
});
|
||||||
|
rejcet(data?.errors);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
0
client/src/store/Bills/bills.reducer.js
Normal file
0
client/src/store/Bills/bills.reducer.js
Normal file
6
client/src/store/Bills/bills.selectors.js
Normal file
6
client/src/store/Bills/bills.selectors.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
|
|
||||||
|
const billByIdSelector = (state, props) => state.bills.items[props.billId];
|
||||||
|
|
||||||
|
export const getBillById = () =>
|
||||||
|
createSelector(billByIdSelector, (_bill) => _bill);
|
||||||
12
client/src/store/Bills/bills.type.js
Normal file
12
client/src/store/Bills/bills.type.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
export default {
|
||||||
|
BILL_DELETE: 'BILL_DELETE',
|
||||||
|
BILLS_BULK_DELETE: 'BILLS_BULK_DELETE',
|
||||||
|
BILLS_LIST_SET: 'BILLS_LIST_SET',
|
||||||
|
BILL_SET: 'BILL_SET',
|
||||||
|
BILLS_SET_CURRENT_VIEW: 'BILLS_SET_CURRENT_VIEW',
|
||||||
|
BILLS_TABLE_QUERIES_ADD: 'BILLS_TABLE_QUERIES_ADD',
|
||||||
|
BILLS_TABLE_LOADING: 'BILLS_TABLE_LOADING',
|
||||||
|
BILLS_PAGINATION_SET: 'BILLS_PAGINATION_SET',
|
||||||
|
BILLS_PAGE_SET: 'BILLS_PAGE_SET',
|
||||||
|
BILLS_ITEMS_SET: 'BILLS_ITEMS_SET',
|
||||||
|
};
|
||||||
136
client/src/store/PaymentReceive/paymentReceive.actions.js
Normal file
136
client/src/store/PaymentReceive/paymentReceive.actions.js
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
import ApiService from 'services/ApiService';
|
||||||
|
import t from 'store/types';
|
||||||
|
|
||||||
|
export const submitPaymentReceive = ({ form }) => {
|
||||||
|
return (dispatch) =>
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
dispatch({
|
||||||
|
type: t.SET_DASHBOARD_REQUEST_LOADING,
|
||||||
|
});
|
||||||
|
ApiService.post('sales/payment_receives', form)
|
||||||
|
.then((response) => {
|
||||||
|
dispatch({
|
||||||
|
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
||||||
|
});
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
const { response } = error;
|
||||||
|
const { data } = response;
|
||||||
|
dispatch({
|
||||||
|
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
||||||
|
});
|
||||||
|
reject(data?.errors);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const editPaymentReceive = (id, form) => {
|
||||||
|
return (dispatch) =>
|
||||||
|
new Promise((resolve, rejcet) => {
|
||||||
|
dispatch({
|
||||||
|
type: t.SET_DASHBOARD_REQUEST_LOADING,
|
||||||
|
});
|
||||||
|
ApiService.post(`sales/payment_receives/${id}`, form)
|
||||||
|
.then((response) => {
|
||||||
|
dispatch({
|
||||||
|
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
||||||
|
});
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
const { response } = error;
|
||||||
|
const { data } = response;
|
||||||
|
dispatch({
|
||||||
|
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
||||||
|
});
|
||||||
|
rejcet(data?.errors);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deletePaymentReceive = ({ id }) => {
|
||||||
|
return (dispatch) =>
|
||||||
|
new Promise((resovle, reject) => {
|
||||||
|
ApiService.delete(`payment_receives/${id}`)
|
||||||
|
.then((response) => {
|
||||||
|
dispatch({ type: t.PAYMENT_RECEIVE_DELETE });
|
||||||
|
resovle(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(error.response.data.errors || []);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fetchPaymentReceive = ({ id }) => {
|
||||||
|
return (dispatch) =>
|
||||||
|
new Promise((resovle, reject) => {
|
||||||
|
ApiService.get(`payment_receives/${id}`)
|
||||||
|
.then((response) => {
|
||||||
|
dispatch({
|
||||||
|
type: t.PAYMENT_RECEIVE_SET,
|
||||||
|
payload: {
|
||||||
|
id,
|
||||||
|
payment_receive: response.data.payment_receive,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
resovle(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
const { response } = error;
|
||||||
|
const { data } = response;
|
||||||
|
reject(data?.errors);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fetchPaymentReceivesTable = ({ query = {} }) => {
|
||||||
|
return (dispatch, getState) =>
|
||||||
|
new Promise((resolve, rejcet) => {
|
||||||
|
const pageQuery = getState().payment_receive.tableQuery;
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: t.PAYMENT_RECEIVES_TABLE_LOADING,
|
||||||
|
payload: {
|
||||||
|
loading: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
ApiService.get('payment_receives', {
|
||||||
|
params: { ...pageQuery, ...query },
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
dispatch({
|
||||||
|
type: t.RECEIPTS_PAGE_SET,
|
||||||
|
payload: {
|
||||||
|
payment_receives: response.data.payment_receives.results,
|
||||||
|
pagination: response.data.payment_receives.pagination,
|
||||||
|
customViewId: response.data.customViewId || -1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
dispatch({
|
||||||
|
type: t.PAYMENT_RECEIVES_ITEMS_SET,
|
||||||
|
payload: {
|
||||||
|
payment_receives: response.data.payment_receives.results,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
dispatch({
|
||||||
|
type: t.PAYMENT_RECEIVES_PAGINATION_SET,
|
||||||
|
payload: {
|
||||||
|
pagination: response.data.payment_receives.pagination,
|
||||||
|
customViewId: response.data.customViewId || -1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
dispatch({
|
||||||
|
type: t.PAYMENT_RECEIVES_TABLE_LOADING,
|
||||||
|
payload: {
|
||||||
|
loading: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
rejcet(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
11
client/src/store/PaymentReceive/paymentReceive.type.js
Normal file
11
client/src/store/PaymentReceive/paymentReceive.type.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
export default {
|
||||||
|
PAYMENT_RECEIVE_LIST_SET: 'PAYMENT_RECEIVE_LIST_SET',
|
||||||
|
PAYMENT_RECEIVE_SET: 'PAYMENT_RECEIVE_SET',
|
||||||
|
PAYMENT_RECEIVE_DELETE: 'PAYMENT_RECEIVE_DELETE',
|
||||||
|
PAYMENT_RECEIVE_SET_CURRENT_VIEW: 'PAYMENT_RECEIVE_SET_CURRENT_VIEW',
|
||||||
|
PAYMENT_RECEIVE_TABLE_QUERIES_ADD: 'PAYMENT_RECEIVE_TABLE_QUERIES_ADD',
|
||||||
|
PAYMENT_RECEIVES_TABLE_LOADING: 'PAYMENT_RECEIVES_TABLE_LOADING',
|
||||||
|
PAYMENT_RECEIVES_PAGE_SET: 'PAYMENT_RECEIVES_PAGE_SET',
|
||||||
|
PAYMENT_RECEIVES_ITEMS_SET: 'PAYMENT_RECEIVES_ITEMS_SET',
|
||||||
|
PAYMENT_RECEIVES_PAGINATION_SET: 'PAYMENT_RECEIVES_PAGINATION_SET',
|
||||||
|
};
|
||||||
@@ -20,6 +20,8 @@ import customer from './customers/customers.type';
|
|||||||
import estimates from './Estimate/estimates.types';
|
import estimates from './Estimate/estimates.types';
|
||||||
import invoices from './Invoice/invoices.types';
|
import invoices from './Invoice/invoices.types';
|
||||||
import receipts from './receipt/receipt.type';
|
import receipts from './receipt/receipt.type';
|
||||||
|
import bills from './Bills/bills.type';
|
||||||
|
import paymentReceives from './PaymentReceive/paymentReceive.type';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
...authentication,
|
...authentication,
|
||||||
@@ -43,5 +45,7 @@ export default {
|
|||||||
...customer,
|
...customer,
|
||||||
...estimates,
|
...estimates,
|
||||||
...invoices,
|
...invoices,
|
||||||
...receipts
|
...receipts,
|
||||||
|
...bills,
|
||||||
|
...paymentReceives,
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user