From 1a03c86508688077f9bf48f57dea63ee0270b148 Mon Sep 17 00:00:00 2001 From: elforjani3 Date: Mon, 26 Oct 2020 16:41:51 +0200 Subject: [PATCH 1/2] fix: make sale invoices resource-able. --- server/src/models/Account.js | 1 - server/src/models/PaymentReceive.js | 4 ++++ server/src/models/SaleInvoice.js | 17 +++++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/server/src/models/Account.js b/server/src/models/Account.js index fe1ee777d..4bba025bc 100644 --- a/server/src/models/Account.js +++ b/server/src/models/Account.js @@ -8,7 +8,6 @@ import { } from 'lib/ViewRolesBuilder'; import { flatToNestedArray } from 'utils'; import DependencyGraph from 'lib/DependencyGraph'; -import TenantManagerSubscriber from 'subscribers/tenantManager'; export default class Account extends TenantModel { /** diff --git a/server/src/models/PaymentReceive.js b/server/src/models/PaymentReceive.js index ddf65f54e..9effadf09 100644 --- a/server/src/models/PaymentReceive.js +++ b/server/src/models/PaymentReceive.js @@ -16,6 +16,10 @@ export default class PaymentReceive extends TenantModel { return ['created_at', 'updated_at']; } + static get resourceable() { + return true; + } + /** * Relationship mapping. */ diff --git a/server/src/models/SaleInvoice.js b/server/src/models/SaleInvoice.js index d1a4c89a7..3b1262445 100644 --- a/server/src/models/SaleInvoice.js +++ b/server/src/models/SaleInvoice.js @@ -24,6 +24,10 @@ export default class SaleInvoice extends TenantModel { return ['created_at', 'updated_at']; } + static get resourceable() { + return true; + } + /** * Model modifiers. */ @@ -123,4 +127,17 @@ export default class SaleInvoice extends TenantModel { .where('id', invoiceId) [changeMethod]('payment_amount', Math.abs(amount)); } + + /** + * Model defined fields. + */ + static get fields() { + return { + created_at: { + label: 'Created at', + column: 'created_at', + columnType: 'date', + }, + }; + } } From cbe6934b58d92dbb6f165c96bba9f2dcc57221c6 Mon Sep 17 00:00:00 2001 From: elforjani3 Date: Mon, 26 Oct 2020 17:45:21 +0200 Subject: [PATCH 2/2] Fix : Receipt & Invoices --- client/src/components/DialogsContainer.js | 5 +- .../InvoiceNumberDialogContent.js | 74 +++++++++++++++++++ .../Dialogs/InvoiceNumberDialog/index.js | 27 +++++++ .../ReceiptNumberDialogContent.js | 73 ++++++++++++++++++ .../Dialogs/ReceiptNumberDialog/index.js | 27 +++++++ .../containers/Sales/Invoice/InvoiceForm.js | 34 +++++++-- .../Sales/Invoice/InvoiceFormHeader.js | 29 +++++++- .../containers/Sales/Invoice/InvoiceList.js | 18 ++--- .../Sales/Invoice/InvoicesDataTable.js | 7 +- .../Sales/Receipt/ReceiptActionsBar.js | 2 +- .../containers/Sales/Receipt/ReceiptForm.js | 53 +++++++++---- .../Sales/Receipt/ReceiptFormHeader.js | 48 +++++++++--- .../containers/Sales/Receipt/ReceiptList.js | 22 +++--- .../Sales/Receipt/ReceiptViewTabs.js | 2 +- .../src/containers/Sales/Receipt/Receipts.js | 11 ++- .../Sales/Receipt/ReceiptsDataTable.js | 23 ++++-- ...ReceipActions.js => withReceiptActions.js} | 0 .../src/containers/Settings/withSettings.js | 2 + client/src/lang/en/index.js | 13 ++-- client/src/store/Invoice/invoices.actions.js | 8 +- client/src/store/Invoice/invoices.reducer.js | 2 +- client/src/store/receipt/receipt.actions.js | 20 ++--- client/src/store/receipt/receipt.reducer.js | 4 +- server/src/data/options.js | 20 +++++ 24 files changed, 430 insertions(+), 94 deletions(-) create mode 100644 client/src/containers/Dialogs/InvoiceNumberDialog/InvoiceNumberDialogContent.js create mode 100644 client/src/containers/Dialogs/InvoiceNumberDialog/index.js create mode 100644 client/src/containers/Dialogs/ReceiptNumberDialog/ReceiptNumberDialogContent.js create mode 100644 client/src/containers/Dialogs/ReceiptNumberDialog/index.js rename client/src/containers/Sales/Receipt/{withReceipActions.js => withReceiptActions.js} (100%) diff --git a/client/src/components/DialogsContainer.js b/client/src/components/DialogsContainer.js index 20aa85eda..23ce3cde6 100644 --- a/client/src/components/DialogsContainer.js +++ b/client/src/components/DialogsContainer.js @@ -11,7 +11,8 @@ import JournalNumberDialog from 'containers/Dialogs/JournalNumberDialog'; import BillNumberDialog from 'containers/Dialogs/BillNumberDialog'; import PaymentNumberDialog from 'containers/Dialogs/PaymentNumberDialog'; import EstimateNumberDialog from 'containers/Dialogs/EstimateNumberDialog'; - +import ReceiptNumberDialog from 'containers/Dialogs/ReceiptNumberDialog'; +import InvoiceNumberDialog from 'containers/Dialogs/InvoiceNumberDialog'; export default function DialogsContainer() { return (
@@ -20,6 +21,8 @@ export default function DialogsContainer() { + +
); } diff --git a/client/src/containers/Dialogs/InvoiceNumberDialog/InvoiceNumberDialogContent.js b/client/src/containers/Dialogs/InvoiceNumberDialog/InvoiceNumberDialogContent.js new file mode 100644 index 000000000..5282a0226 --- /dev/null +++ b/client/src/containers/Dialogs/InvoiceNumberDialog/InvoiceNumberDialogContent.js @@ -0,0 +1,74 @@ +import React from 'react'; +import { DialogContent } from 'components'; +import { useQuery, queryCache } from 'react-query'; + +import ReferenceNumberForm from 'containers/JournalNumber/ReferenceNumberForm'; + +import withDialogActions from 'containers/Dialog/withDialogActions'; +import withSettingsActions from 'containers/Settings/withSettingsActions'; +import withSettings from 'containers/Settings/withSettings'; + +import { compose, optionsMapToArray } from 'utils'; + +/** + * invoice number dialog's content. + */ + +function InvoiceNumberDialogContent({ + // #withSettings + nextNumber, + numberPrefix, + + // #withSettingsActions + requestFetchOptions, + requestSubmitOptions, + + // #withDialogActions + closeDialog, +}) { + const fetchSettings = useQuery(['settings'], () => requestFetchOptions({})); + + const handleSubmitForm = (values, { setSubmitting }) => { + const options = optionsMapToArray(values).map((option) => { + return { key: option.key, ...option, group: 'sales_invoices' }; + }); + + requestSubmitOptions({ options }) + .then(() => { + setSubmitting(false); + closeDialog('invoice-number-form'); + + setTimeout(() => { + queryCache.invalidateQueries('settings'); + }, 250); + }) + .catch(() => { + setSubmitting(false); + }); + }; + + const handleClose = () => { + closeDialog('invoice-number-form'); + }; + + return ( + + + + ); +} + +export default compose( + withDialogActions, + withSettingsActions, + withSettings(({ invoiceSettings }) => ({ + nextNumber: invoiceSettings?.next_number, + numberPrefix: invoiceSettings?.number_prefix, + })), + +) (InvoiceNumberDialogContent); diff --git a/client/src/containers/Dialogs/InvoiceNumberDialog/index.js b/client/src/containers/Dialogs/InvoiceNumberDialog/index.js new file mode 100644 index 000000000..78a44194a --- /dev/null +++ b/client/src/containers/Dialogs/InvoiceNumberDialog/index.js @@ -0,0 +1,27 @@ +import React, { lazy } from 'react'; +import { FormattedMessage as T } from 'react-intl'; +import { Dialog, DialogSuspense } from 'components'; +import withDialogRedux from 'components/DialogReduxConnect'; +import { compose } from 'utils'; + +const InvoiceNumberDialogContent = lazy(() => + import('./InvoiceNumberDialogContent'), +); + +function InvoiceNumberDialog({ dialogName, payload = { id: null }, isOpen }) { + return ( + } + name={dialogName} + autoFocus={true} + canEscapeKeyClose={true} + isOpen={isOpen} + > + + + + + ); +} + +export default compose(withDialogRedux())(InvoiceNumberDialog); diff --git a/client/src/containers/Dialogs/ReceiptNumberDialog/ReceiptNumberDialogContent.js b/client/src/containers/Dialogs/ReceiptNumberDialog/ReceiptNumberDialogContent.js new file mode 100644 index 000000000..701056e49 --- /dev/null +++ b/client/src/containers/Dialogs/ReceiptNumberDialog/ReceiptNumberDialogContent.js @@ -0,0 +1,73 @@ +import React from 'react'; +import { DialogContent } from 'components'; +import { useQuery, queryCache } from 'react-query'; + +import ReferenceNumberForm from 'containers/JournalNumber/ReferenceNumberForm'; + +import withDialogActions from 'containers/Dialog/withDialogActions'; +import withSettingsActions from 'containers/Settings/withSettingsActions'; +import withSettings from 'containers/Settings/withSettings'; + +import { compose, optionsMapToArray } from 'utils'; + +/** + * Receipt number dialog's content. + */ + +function ReceiptNumberDialogContent({ + // #withSettings + nextNumber, + numberPrefix, + + // #withSettingsActions + requestFetchOptions, + requestSubmitOptions, + + // #withDialogActions + closeDialog, +}) { + const fetchSettings = useQuery(['settings'], () => requestFetchOptions({})); + + const handleSubmitForm = (values, { setSubmitting }) => { + const options = optionsMapToArray(values).map((option) => { + return { key: option.key, ...option, group: 'sales_receipts' }; + }); + + requestSubmitOptions({ options }) + .then(() => { + setSubmitting(false); + closeDialog('receipt-number-form'); + + setTimeout(() => { + queryCache.invalidateQueries('settings'); + }, 250); + }) + .catch(() => { + setSubmitting(false); + }); + }; + + const handleClose = () => { + closeDialog('receipt-number-form'); + }; + + return ( + + + + ); +} + +export default compose( + withDialogActions, + withSettingsActions, + withSettings(({ receiptSettings }) => ({ + nextNumber: receiptSettings?.next_number, + numberPrefix: receiptSettings?.number_prefix, + })), +)(ReceiptNumberDialogContent); diff --git a/client/src/containers/Dialogs/ReceiptNumberDialog/index.js b/client/src/containers/Dialogs/ReceiptNumberDialog/index.js new file mode 100644 index 000000000..f4a3cf96b --- /dev/null +++ b/client/src/containers/Dialogs/ReceiptNumberDialog/index.js @@ -0,0 +1,27 @@ +import React, { lazy } from 'react'; +import { FormattedMessage as T } from 'react-intl'; +import { Dialog, DialogSuspense } from 'components'; +import withDialogRedux from 'components/DialogReduxConnect'; +import { compose } from 'utils'; + +const ReceiptNumberDialogContent = lazy(() => + import('./ReceiptNumberDialogContent'), +); + +function ReceiptNumberDialog({ dialogName, paylaod = { id: null }, isOpen }) { + return ( + } + autoFocus={true} + canEscapeKeyClose={true} + isOpen={isOpen} + > + + + + + ); +} + +export default compose(withDialogRedux())(ReceiptNumberDialog); diff --git a/client/src/containers/Sales/Invoice/InvoiceForm.js b/client/src/containers/Sales/Invoice/InvoiceForm.js index 42a07c0d9..f7955d252 100644 --- a/client/src/containers/Sales/Invoice/InvoiceForm.js +++ b/client/src/containers/Sales/Invoice/InvoiceForm.js @@ -21,6 +21,7 @@ import withInvoiceActions from './withInvoiceActions'; import withInvoiceDetail from './withInvoiceDetail'; import withDashboardActions from 'containers/Dashboard/withDashboardActions'; import withMediaActions from 'containers/Media/withMediaActions'; +import withSettings from 'containers/Settings/withSettings'; import { AppToaster } from 'components'; import Dragzone from 'components/Dragzone'; @@ -41,7 +42,10 @@ function InvoiceForm({ //#withDashboard changePageTitle, - changePageSubtitle, + + // #withSettings + invoiceNextNumber, + invoiceNumberPrefix, //#withInvoiceDetail invoice, @@ -91,7 +95,7 @@ function InvoiceForm({ due_date: Yup.date() .required() .label(formatMessage({ id: 'due_date_' })), - invoice_no: Yup.number() + invoice_no: Yup.string() .required() .label(formatMessage({ id: 'invoice_no_' })), reference_no: Yup.string().min(1).max(255), @@ -142,13 +146,17 @@ function InvoiceForm({ [], ); + const invoiceNumber = invoiceNumberPrefix + ? `${invoiceNumberPrefix}-${invoiceNextNumber}` + : invoiceNextNumber; + const defaultInitialValues = useMemo( () => ({ customer_id: '', invoice_date: moment(new Date()).format('YYYY-MM-DD'), due_date: moment(new Date()).format('YYYY-MM-DD'), status: 'SEND', - invoice_no: '', + invoice_no: invoiceNumber, reference_no: '', invoice_message: '', terms_conditions: '', @@ -198,7 +206,6 @@ function InvoiceForm({ }, [invoice]); const formik = useFormik({ - enableReinitialize: true, validationSchema, initialValues: { ...initialValues, @@ -219,9 +226,12 @@ function InvoiceForm({ requestEditInvoice(invoice.id, requestForm) .then((response) => { AppToaster.show({ - message: formatMessage({ - id: 'the_invoice_has_been_successfully_edited', - }), + message: formatMessage( + { + id: 'the_invoice_has_been_successfully_edited', + }, + { number: values.invoice_no }, + ), intent: Intent.SUCCESS, }); setSubmitting(false); @@ -251,6 +261,11 @@ function InvoiceForm({ } }, }); + + useEffect(() => { + formik.setFieldValue('invoice_no', invoiceNumber); + }, [invoiceNumber]); + const handleSubmitClick = useCallback( (payload) => { setPayload(payload); @@ -349,4 +364,9 @@ export default compose( withDashboardActions, withMediaActions, withInvoiceDetail(), + + withSettings(({ invoiceSettings }) => ({ + invoiceNextNumber: invoiceSettings?.next_number, + invoiceNumberPrefix: invoiceSettings?.number_prefix, + })), )(InvoiceForm); diff --git a/client/src/containers/Sales/Invoice/InvoiceFormHeader.js b/client/src/containers/Sales/Invoice/InvoiceFormHeader.js index bd5f85418..1e1a63fcd 100644 --- a/client/src/containers/Sales/Invoice/InvoiceFormHeader.js +++ b/client/src/containers/Sales/Invoice/InvoiceFormHeader.js @@ -13,15 +13,24 @@ import { Row, Col } from 'react-grid-system'; import moment from 'moment'; import { momentFormatter, compose, tansformDateValue } from 'utils'; import classNames from 'classnames'; -import { ListSelect, ErrorMessage, FieldRequiredHint, Hint } from 'components'; +import { + ListSelect, + ErrorMessage, + FieldRequiredHint, + Icon, + InputPrependButton, +} from 'components'; import withCustomers from 'containers/Customers/withCustomers'; +import withDialogActions from 'containers/Dialog/withDialogActions'; function InvoiceFormHeader({ formik: { errors, touched, setFieldValue, getFieldProps, values }, //#withCustomers customers, + //#withDialogActions + openDialog, }) { const handleDateChange = useCallback( (date_filed) => (date) => { @@ -67,6 +76,10 @@ function InvoiceFormHeader({ [setFieldValue], ); + const handleInvoiceNumberChange = useCallback(() => { + openDialog('invoice-number-form', {}); + }, [openDialog]); + return (
@@ -149,6 +162,19 @@ function InvoiceFormHeader({ , + }} + tooltip={true} + tooltipProps={{ + content: 'Setting your auto-generated invoice number', + position: Position.BOTTOM_LEFT, + }} + /> + } {...getFieldProps('invoice_no')} /> @@ -174,4 +200,5 @@ export default compose( withCustomers(({ customers }) => ({ customers, })), + withDialogActions, )(InvoiceFormHeader); diff --git a/client/src/containers/Sales/Invoice/InvoiceList.js b/client/src/containers/Sales/Invoice/InvoiceList.js index 12ba1b74d..da94dff50 100644 --- a/client/src/containers/Sales/Invoice/InvoiceList.js +++ b/client/src/containers/Sales/Invoice/InvoiceList.js @@ -46,15 +46,15 @@ function InvoiceList({ changePageTitle(formatMessage({ id: 'invoice_list' })); }, [changePageTitle, formatMessage]); - const fetchResourceViews = useQuery( - ['resource-views', 'sales_invoices'], - (key, resourceName) => requestFetchResourceViews(resourceName), - ); + // const fetchResourceViews = useQuery( + // ['resource-views', 'sales_invoices'], + // (key, resourceName) => requestFetchResourceViews(resourceName), + // ); - const fetchResourceFields = useQuery( - ['resource-fields', 'sales_invoices'], - (key, resourceName) => requestFetchResourceFields(resourceName), - ); + // const fetchResourceFields = useQuery( + // ['resource-fields', 'sales_invoices'], + // (key, resourceName) => requestFetchResourceFields(resourceName), + // ); const fetchInvoices = useQuery(['invoices-table', invoicesTableQuery], () => requestFetchInvoiceTable(), @@ -124,7 +124,7 @@ function InvoiceList({ ); return ( ( - + } + text={formatMessage({ id: 'view_details' })} + /> } text={formatMessage({ id: 'edit_invoice' })} onClick={handleEditInvoice(invoice)} /> diff --git a/client/src/containers/Sales/Receipt/ReceiptActionsBar.js b/client/src/containers/Sales/Receipt/ReceiptActionsBar.js index 107ae0988..12912af5c 100644 --- a/client/src/containers/Sales/Receipt/ReceiptActionsBar.js +++ b/client/src/containers/Sales/Receipt/ReceiptActionsBar.js @@ -24,7 +24,7 @@ import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar'; import withResourceDetail from 'containers/Resources/withResourceDetails'; import withDialogActions from 'containers/Dialog/withDialogActions'; -import withReceiptActions from './withReceipActions'; +import withReceiptActions from './withReceiptActions'; import withReceipts from './withReceipts'; import { compose } from 'utils'; diff --git a/client/src/containers/Sales/Receipt/ReceiptForm.js b/client/src/containers/Sales/Receipt/ReceiptForm.js index a5bc4b059..19e839e81 100644 --- a/client/src/containers/Sales/Receipt/ReceiptForm.js +++ b/client/src/containers/Sales/Receipt/ReceiptForm.js @@ -18,10 +18,11 @@ import ReceiptFromHeader from './ReceiptFormHeader'; import EstimatesItemsTable from 'containers/Sales/Estimate/EntriesItemsTable'; import ReceiptFormFooter from './ReceiptFormFooter'; -import withReceipActions from './withReceipActions'; +import withReceiptActions from './withReceiptActions'; import withReceiptDetail from './withReceiptDetail'; import withDashboardActions from 'containers/Dashboard/withDashboardActions'; import withMediaActions from 'containers/Media/withMediaActions'; +import withSettings from 'containers/Settings/withSettings'; import { AppToaster } from 'components'; import Dragzone from 'components/Dragzone'; @@ -45,7 +46,10 @@ function ReceiptForm({ //#withDashboard changePageTitle, - changePageSubtitle, + + // #withSettings + receiptNextNumber, + receiptNumberPrefix, //#own Props receiptId, @@ -90,9 +94,9 @@ function ReceiptForm({ receipt_date: Yup.date() .required() .label(formatMessage({ id: 'receipt_date_' })), - // receipt_no: Yup.number() - // .required() - // .label(formatMessage({ id: 'receipt_no_' })), + receipt_number: Yup.string() + .required() + .label(formatMessage({ id: 'receipt_no_' })), deposit_account_id: Yup.number() .required() .label(formatMessage({ id: 'deposit_account_' })), @@ -102,7 +106,7 @@ function ReceiptForm({ .min(1) .max(1024) .label(formatMessage({ id: 'receipt_message_' })), - email_send_to: Yup.string().email().nullable(), + send_to_email: Yup.string().email().nullable(), statement: Yup.string() .trim() .min(1) @@ -143,12 +147,17 @@ function ReceiptForm({ [], ); + const receiptNumber = receiptNumberPrefix + ? `${receiptNumberPrefix}-${receiptNextNumber}` + : receiptNextNumber; + const defaultInitialValues = useMemo( () => ({ customer_id: '', deposit_account_id: '', + receipt_number: receiptNumber, receipt_date: moment(new Date()).format('YYYY-MM-DD'), - email_send_to: '', + send_to_email: '', reference_no: '', receipt_message: '', statement: '', @@ -198,7 +207,6 @@ function ReceiptForm({ }, [receipt]); const formik = useFormik({ - enableReinitialize: true, validationSchema, initialValues: { ...initialValues, @@ -217,9 +225,12 @@ function ReceiptForm({ if (receipt && receipt.id) { requestEditReceipt(receipt.id, requestForm).then(() => { AppToaster.show({ - message: formatMessage({ - id: 'the_receipt_has_been_successfully_edited', - }), + message: formatMessage( + { + id: 'the_receipt_has_been_successfully_edited', + }, + { number: values.receipt_number }, + ), intent: Intent.SUCCESS, }); setSubmitting(false); @@ -230,9 +241,12 @@ function ReceiptForm({ requestSubmitReceipt(requestForm) .then((response) => { AppToaster.show({ - message: formatMessage({ - id: 'the_receipt_has_been_successfully_created', - }), + message: formatMessage( + { + id: 'the_receipt_has_been_successfully_created', + }, + { number: values.receipt_number }, + ), intent: Intent.SUCCESS, }); setSubmitting(false); @@ -245,7 +259,6 @@ function ReceiptForm({ } }, }); - console.log(formik.errors, 'ERROR'); const handleDeleteFile = useCallback( (_deletedFiles) => { @@ -287,6 +300,10 @@ function ReceiptForm({ ); }; + useEffect(() => { + formik.setFieldValue('receipt_number', receiptNumber); + }, [receiptNumber]); + return (
@@ -342,8 +359,12 @@ function ReceiptForm({ } export default compose( - withReceipActions, + withReceiptActions, withDashboardActions, withMediaActions, withReceiptDetail(), + withSettings(({ receiptSettings }) => ({ + receiptNextNumber: receiptSettings?.next_number, + receiptNumberPrefix: receiptSettings?.number_prefix, + })), )(ReceiptForm); diff --git a/client/src/containers/Sales/Receipt/ReceiptFormHeader.js b/client/src/containers/Sales/Receipt/ReceiptFormHeader.js index 94aaa50d2..9b4182d47 100644 --- a/client/src/containers/Sales/Receipt/ReceiptFormHeader.js +++ b/client/src/containers/Sales/Receipt/ReceiptFormHeader.js @@ -18,11 +18,13 @@ import { ListSelect, ErrorMessage, FieldRequiredHint, - Hint, + Icon, + InputPrependButton, } from 'components'; import withCustomers from 'containers/Customers/withCustomers'; import withAccounts from 'containers/Accounts/withAccounts'; +import withDialogActions from 'containers/Dialog/withDialogActions'; function ReceiptFormHeader({ formik: { errors, touched, setFieldValue, getFieldProps, values }, @@ -31,6 +33,8 @@ function ReceiptFormHeader({ customers, //#withAccouts accountsList, + //#withDialogActions + openDialog, }) { const handleDateChange = useCallback( (date) => { @@ -82,6 +86,10 @@ function ReceiptFormHeader({ [accountsList], ); + const handleReceiptNumberChange = useCallback(() => { + openDialog('receipt-number-form', {}); + }, [openDialog]); + return (
@@ -162,20 +170,39 @@ function ReceiptFormHeader({
{/* receipt_no */} - {/* } inline={true} - className={('form-group--receipt_no', Classes.FILL)} + className={('form-group--receipt_number', Classes.FILL)} labelInfo={} - intent={errors.receipt_no && touched.receipt_no && Intent.DANGER} - helperText={} + intent={ + errors.receipt_number && touched.receipt_number && Intent.DANGER + } + helperText={ + + } > , + }} + tooltip={true} + tooltipProps={{ + content: 'Setting your auto-generated receipt number', + position: Position.BOTTOM_LEFT, + }} + /> + } + {...getFieldProps('receipt_number')} /> - */} + {/*- Reference -*/} } inline={true} className={classNames('form-group--send_to_email', Classes.FILL)} - intent={errors.email_send_to && touched.email_send_to && Intent.DANGER} + intent={errors.send_to_email && touched.send_to_email && Intent.DANGER} helperText={} > ({ accountsList, })), + withDialogActions, )(ReceiptFormHeader); diff --git a/client/src/containers/Sales/Receipt/ReceiptList.js b/client/src/containers/Sales/Receipt/ReceiptList.js index 6df1324aa..1d3a6ea85 100644 --- a/client/src/containers/Sales/Receipt/ReceiptList.js +++ b/client/src/containers/Sales/Receipt/ReceiptList.js @@ -15,7 +15,7 @@ import ReceiptViewTabs from './ReceiptViewTabs'; import withDashboardActions from 'containers/Dashboard/withDashboardActions'; import withResourceActions from 'containers/Resources/withResourcesActions'; import withReceipts from './withReceipts'; -import withReceipActions from './withReceipActions'; +import withReceiptActions from './withReceiptActions'; import withViewsActions from 'containers/Views/withViewsActions'; import { compose } from 'utils'; @@ -46,15 +46,15 @@ function ReceiptList({ requestFetchReceiptsTable(), ); - const fetchResourceViews = useQuery( - ['resource-views', 'sales_receipts'], - (key, resourceName) => requestFetchResourceViews(resourceName), - ); + // const fetchResourceViews = useQuery( + // ['resource-views', 'sales_receipts'], + // (key, resourceName) => requestFetchResourceViews(resourceName), + // ); - const fetchResourceFields = useQuery( - ['resource-fields', 'sales_receipts'], - (key, resourceName) => requestFetchResourceFields(resourceName), - ); + // const fetchResourceFields = useQuery( + // ['resource-fields', 'sales_receipts'], + // (key, resourceName) => requestFetchResourceFields(resourceName), + // ); useEffect(() => { changePageTitle(formatMessage({ id: 'receipt_list' })); @@ -140,7 +140,7 @@ function ReceiptList({ return ( ({ diff --git a/client/src/containers/Sales/Receipt/ReceiptViewTabs.js b/client/src/containers/Sales/Receipt/ReceiptViewTabs.js index 40b8ab424..b817b96df 100644 --- a/client/src/containers/Sales/Receipt/ReceiptViewTabs.js +++ b/client/src/containers/Sales/Receipt/ReceiptViewTabs.js @@ -9,7 +9,7 @@ import { DashboardViewsTabs } from 'components'; import { useUpdateEffect } from 'hooks'; import withReceipts from './withReceipts'; -import withReceiptActions from './withReceipActions'; +import withReceiptActions from './withReceiptActions'; import withDashboardActions from 'containers/Dashboard/withDashboardActions'; import withViewDetails from 'containers/Views/withViewDetails'; diff --git a/client/src/containers/Sales/Receipt/Receipts.js b/client/src/containers/Sales/Receipt/Receipts.js index aa54386a0..70fbb3439 100644 --- a/client/src/containers/Sales/Receipt/Receipts.js +++ b/client/src/containers/Sales/Receipt/Receipts.js @@ -8,7 +8,8 @@ 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 withReceipActions from './withReceipActions'; +import withReceiptActions from './withReceiptActions'; +import withSettingsActions from 'containers/Settings/withSettingsActions'; import { compose } from 'utils'; @@ -24,6 +25,9 @@ function Receipts({ //#withReceiptsActions requestFetchReceipt, + + // #withSettingsActions + requestFetchOptions, }) { const history = useHistory(); const { id } = useParams(); @@ -44,6 +48,8 @@ function Receipts({ // Handle fetch Items data table or list const fetchItems = useQuery('items-table', () => requestFetchItems({})); + const fetchSettings = useQuery(['settings'], () => requestFetchOptions({})); + const handleFormSubmit = useCallback( (payload) => { payload.redirect && history.push('/receipts'); @@ -75,8 +81,9 @@ function Receipts({ } export default compose( - withReceipActions, + withReceiptActions, withCustomersActions, withItemsActions, withAccountsActions, + withSettingsActions, )(Receipts); diff --git a/client/src/containers/Sales/Receipt/ReceiptsDataTable.js b/client/src/containers/Sales/Receipt/ReceiptsDataTable.js index d1ec2939b..45ad5c793 100644 --- a/client/src/containers/Sales/Receipt/ReceiptsDataTable.js +++ b/client/src/containers/Sales/Receipt/ReceiptsDataTable.js @@ -24,7 +24,7 @@ import withDashboardActions from 'containers/Dashboard/withDashboardActions'; import withViewDetails from 'containers/Views/withViewDetails'; import withReceipts from './withReceipts'; -import withReceipActions from './withReceipActions'; +import withReceiptActions from './withReceiptActions'; import withCurrentView from 'containers/Views/withCurrentView'; function ReceiptsDataTable({ @@ -94,9 +94,13 @@ function ReceiptsDataTable({ const actionMenuList = useCallback( (estimate) => ( - + } + text={formatMessage({ id: 'view_details' })} + /> } text={formatMessage({ id: 'edit_receipt' })} onClick={handleEditReceipt(estimate)} /> @@ -134,6 +138,13 @@ function ReceiptsDataTable({ width: 140, className: 'customer_id', }, + { + id: 'receipt_number', + Header: formatMessage({ id: 'receipt_number' }), + accessor: (row) => `#${row.receipt_number}`, + width: 140, + className: 'receipt_number', + }, { id: 'deposit_account_id', Header: formatMessage({ id: 'deposit_account' }), @@ -142,11 +153,11 @@ function ReceiptsDataTable({ className: 'deposit_account', }, { - id: 'email_send_to', + id: 'send_to_email', Header: formatMessage({ id: 'email' }), - accessor: 'email_send_to', + accessor: 'send_to_email', width: 140, - className: 'email_send_to', + className: 'send_to_email', }, { id: 'amount', @@ -229,7 +240,7 @@ export default compose( withCurrentView, withDialogActions, withDashboardActions, - withReceipActions, + withReceiptActions, withReceipts( ({ receiptsCurrentPage, diff --git a/client/src/containers/Sales/Receipt/withReceipActions.js b/client/src/containers/Sales/Receipt/withReceiptActions.js similarity index 100% rename from client/src/containers/Sales/Receipt/withReceipActions.js rename to client/src/containers/Sales/Receipt/withReceiptActions.js diff --git a/client/src/containers/Settings/withSettings.js b/client/src/containers/Settings/withSettings.js index 763817cd1..06bdd6292 100644 --- a/client/src/containers/Settings/withSettings.js +++ b/client/src/containers/Settings/withSettings.js @@ -8,6 +8,8 @@ export default (mapState) => { billsettings: state.settings.data.bills, billPaymentSettings: state.settings.data.bill_payments, estimatesSettings: state.settings.data.sales_estimates, + receiptSettings: state.settings.data.sales_receipts, + invoiceSettings: state.settings.data.sales_invoices, }; return mapState ? mapState(mapped, state, props) : mapped; }; diff --git a/client/src/lang/en/index.js b/client/src/lang/en/index.js index 7f06bb25e..1ba279218 100644 --- a/client/src/lang/en/index.js +++ b/client/src/lang/en/index.js @@ -645,9 +645,9 @@ export default { select_deposit_account: 'Select Deposit Account', once_delete_this_receipt_you_will_able_to_restore_it: `Once you delete this receipt, you won\'t be able to restore it later. Are you sure you want to delete this receipt?`, the_receipt_has_been_successfully_created: - 'The recepit has been successfully created.', + 'The recepit #{number} has been successfully created.', the_receipt_has_been_successfully_edited: - 'The receipt has been successfully edited.', + 'The receipt #{number} has been successfully edited.', the_receipt_has_been_successfully_deleted: 'The receipt has been successfully deleted.', bill_list: 'Bill List', @@ -776,8 +776,11 @@ export default { bigger_or_equals: 'Bigger or equals', prefix: 'Prefix', next_number: 'Next Number', - journal_number_settings: 'Journal number Settings', - bill_number_settings: 'Bill number Settings', - payment_number_settings: 'Payment number Settings', + journal_number_settings: 'Journal Number Settings', + bill_number_settings: 'Bill Number Settings', + payment_number_settings: 'Payment Number Settings', Estimate_number_settings: 'Estimate Number Settings', + receipt_number_settings: 'Receipt Number Settings', + invoice_number_settings: 'Invoice Number Settings', + receipt_number: 'Receipt Number', }; diff --git a/client/src/store/Invoice/invoices.actions.js b/client/src/store/Invoice/invoices.actions.js index 019eb568c..92787ad8c 100644 --- a/client/src/store/Invoice/invoices.actions.js +++ b/client/src/store/Invoice/invoices.actions.js @@ -67,8 +67,8 @@ export const fetchInvoicesTable = ({ query } = {}) => { dispatch({ type: t.INVOICES_PAGE_SET, payload: { - sales_invoices: response.data.sales_invoices.results, - pagination: response.data.sales_invoices.pagination, + sales_invoices: response.data.sales_invoices, + pagination: response.data.pagination, customViewId: response.data.customViewId || -1, }, }); @@ -76,13 +76,13 @@ export const fetchInvoicesTable = ({ query } = {}) => { dispatch({ type: t.INVOICES_ITEMS_SET, payload: { - sales_invoices: response.data.sales_invoices.results, + sales_invoices: response.data.sales_invoices, }, }); dispatch({ type: t.INVOICES_PAGINATION_SET, payload: { - pagination: response.data.sales_invoices.pagination, + pagination: response.data.pagination, customViewId: response.data.customViewId || -1, }, }); diff --git a/client/src/store/Invoice/invoices.reducer.js b/client/src/store/Invoice/invoices.reducer.js index df86dce66..098f78db6 100644 --- a/client/src/store/Invoice/invoices.reducer.js +++ b/client/src/store/Invoice/invoices.reducer.js @@ -80,7 +80,7 @@ const reducer = createReducer(initialState, { const { pagination, customViewId } = action.payload; const mapped = { - pageSize: parseInt(pagination.pageSize, 10), + pageSize: parseInt(pagination.page_size, 10), page: parseInt(pagination.page, 10), total: parseInt(pagination.total, 10), }; diff --git a/client/src/store/receipt/receipt.actions.js b/client/src/store/receipt/receipt.actions.js index 71e0f8202..61f712d32 100644 --- a/client/src/store/receipt/receipt.actions.js +++ b/client/src/store/receipt/receipt.actions.js @@ -6,17 +6,11 @@ export const submitReceipt = ({ form }) => { new Promise((resolve, reject) => { ApiService.post('sales/receipts', 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); }); }); @@ -60,12 +54,10 @@ export const fetchReceipt = ({ id }) => { new Promise((resovle, reject) => { ApiService.get(`sales/receipts/${id}`) .then((response) => { + const { receipt } = response.data; dispatch({ type: t.RECEIPT_SET, - payload: { - id, - receipt: response.data.receipt, - }, + payload: { id, receipt }, }); resovle(response); }) @@ -94,21 +86,21 @@ export const fetchReceiptsTable = ({ query = {} }) => { dispatch({ type: t.RECEIPTS_PAGE_SET, payload: { - sales_receipts: response.data.sales_receipts.results, - pagination: response.data.sales_receipts.pagination, + sales_receipts: response.data.sale_receipts, + pagination: response.data.pagination, customViewId: response.data.customViewId || -1, }, }); dispatch({ type: t.RECEIPTS_ITEMS_SET, payload: { - sales_receipts: response.data.sales_receipts.results, + sales_receipts: response.data.sale_receipts, }, }); dispatch({ type: t.RECEIPTS_PAGINATION_SET, payload: { - pagination: response.data.sales_receipts.pagination, + pagination: response.data.pagination, customViewId: response.data.customViewId || -1, }, }); diff --git a/client/src/store/receipt/receipt.reducer.js b/client/src/store/receipt/receipt.reducer.js index 346859bdc..d463afcd0 100644 --- a/client/src/store/receipt/receipt.reducer.js +++ b/client/src/store/receipt/receipt.reducer.js @@ -60,7 +60,6 @@ const reducer = createReducer(initialState, { const viewId = customViewId || -1; const view = state.views[viewId] || {}; - state.views[viewId] = { ...view, pages: { @@ -76,7 +75,7 @@ const reducer = createReducer(initialState, { const { pagination, customViewId } = action.payload; const mapped = { - pageSize: parseInt(pagination.pageSize, 10), + pageSize: parseInt(pagination.page_size, 10), page: parseInt(pagination.page, 10), total: parseInt(pagination.total, 10), }; @@ -85,7 +84,6 @@ const reducer = createReducer(initialState, { pagesCount: Math.ceil(mapped.total / mapped.pageSize), pageIndex: Math.max(mapped.page - 1, 0), }; - state.views = { ...state.views, [customViewId]: { diff --git a/server/src/data/options.js b/server/src/data/options.js index 2d91ee112..575bf910e 100644 --- a/server/src/data/options.js +++ b/server/src/data/options.js @@ -83,4 +83,24 @@ export default { type: "string", }, ], + sales_receipts: [ + { + key: "next_number", + type: "number", + }, + { + key: "number_prefix", + type: "string", + }, + ], + sales_invoices: [ + { + key: "next_number", + type: "number", + }, + { + key: "number_prefix", + type: "string", + }, + ], };