feat(design): fix issues in sidebar design.

feat(sales): reference number auto-increment optimizations.
fix(payments): payment receive/made statement.
This commit is contained in:
a.bouhuolia
2021-03-11 14:29:38 +02:00
parent 59f8010122
commit 30cd6c8a61
62 changed files with 921 additions and 378 deletions

View File

@@ -13,4 +13,5 @@ export const ERROR = {
// Bills // Bills
BILL_NUMBER_EXISTS: 'BILL.NUMBER.EXISTS', BILL_NUMBER_EXISTS: 'BILL.NUMBER.EXISTS',
SALE_INVOICE_NO_IS_REQUIRED: 'SALE_INVOICE_NO_IS_REQUIRED'
}; };

View File

@@ -1,8 +1,8 @@
export default [ export default [
{ path: 'invoices/new', name: 'Sale invoice', label: 'Ctrl+Shift+I' }, { path: 'invoices/new', name: 'Sale invoice' },
{ path: 'bill//new', name: 'Purchase invoice',label:'Ctrl+Shift+B' }, { path: 'bill//new', name: 'Purchase invoice' },
{ path: 'make-journal-entry', name: 'Manual journal', label: 'Ctrl+Shift+M' }, { path: 'make-journal-entry', name: 'Manual journal' },
{ path: 'expenses/new', name: 'Expense', label: 'Ctrl+Shift+X' }, { path: 'expenses/new', name: 'Expense' },
{ path: 'customers/new', name: 'Customer', label: 'Ctrl+Shift+C' }, { path: 'customers/new', name: 'Customer' },
{ path: 'vendors/new', name: 'Vendor', label: 'Ctrl+Shift+V' }, { path: 'vendors/new', name: 'Vendor' },
]; ];

View File

@@ -14,7 +14,7 @@ function PageFade(props) {
} }
export default function AuthenticationWrapper({ ...rest }) { export default function AuthenticationWrapper({ ...rest }) {
const to = { pathname: '/homepage' }; const to = { pathname: '/' };
const location = useLocation(); const location = useLocation();
const isAuthenticated = useIsAuthenticated(); const isAuthenticated = useIsAuthenticated();
const locationKey = location.pathname; const locationKey = location.pathname;

View File

@@ -24,6 +24,9 @@ import withSettings from 'containers/Settings/withSettings';
import QuickNewDropdown from 'containers/QuickNewDropdown/QuickNewDropdown'; import QuickNewDropdown from 'containers/QuickNewDropdown/QuickNewDropdown';
import { compose } from 'utils'; import { compose } from 'utils';
/**
* Dashboard topbar.
*/
function DashboardTopbar({ function DashboardTopbar({
// #withDashboard // #withDashboard
pageTitle, pageTitle,
@@ -116,11 +119,7 @@ function DashboardTopbar({
<div class="dashboard__breadcrumbs"> <div class="dashboard__breadcrumbs">
<DashboardBreadcrumbs /> <DashboardBreadcrumbs />
</div> </div>
{/* <div class="dashboard__organization-name">
{ organizationName }
</div> */}
<DashboardBackLink /> <DashboardBackLink />
</div> </div>

View File

@@ -9,7 +9,7 @@ export default function Sidebar() {
return ( return (
<SidebarContainer> <SidebarContainer>
<SidebarHead /> <SidebarHead />
<div className="sidebar__menu"> <div className="sidebar__menu">
<SidebarMenu /> <SidebarMenu />
</div> </div>

View File

@@ -1,12 +1,66 @@
import React from 'react'; import React from 'react';
import appMeta from 'config/app'; import { Button, Popover, Menu, Position } from '@blueprintjs/core';
import Icon from 'components/Icon'; import Icon from 'components/Icon';
export default function() { import { useAuthUser } from 'hooks/state';
import withSettings from 'containers/Settings/withSettings';
import { compose, firstLettersArgs } from 'utils';
// Popover modifiers.
const POPOVER_MODIFIERS = {
offset: { offset: '20, 8' },
};
/**
* Sideabr head.
*/
function SidebarHead({
// #withSettings
organizationName,
}) {
const user = useAuthUser();
return ( return (
<div className="sidebar__head"> <div className="sidebar__head">
<div className="sidebar__head-organization">
<Popover
modifiers={POPOVER_MODIFIERS}
content={
<Menu className={'menu--dashboard-organization'}>
<div class="org-item">
<div class="org-item__logo">
{firstLettersArgs(...organizationName.split(' '))}{' '}
</div>
<div class="org-item__name">{organizationName}</div>
</div>
</Menu>
}
position={Position.BOTTOM}
minimal={true}
>
<Button
className="title"
rightIcon={<Icon icon={'caret-down-16'} size={16} />}
>
{organizationName}
</Button>
</Popover>
<span class="subtitle">{user.full_name}</span>
</div>
<div className="sidebar__head-logo"> <div className="sidebar__head-logo">
<Icon icon={'bigcapital'} width={140} height={28} className="bigcapital--alt" /> <Icon
icon={'mini-bigcapital'}
width={140}
height={28}
className="bigcapital--alt"
/>
</div> </div>
</div> </div>
); );
}; }
export default compose(
withSettings(({ organizationSettings }) => ({
organizationName: organizationSettings.name,
})),
)(SidebarHead);

View File

@@ -30,7 +30,7 @@ function ItemDeleteAlert({
closeAlert, closeAlert,
// #withItemsActions // #withItemsActions
addItemsTableQueries, setItemsTableState,
}) { }) {
const { mutateAsync: deleteItem, isLoading } = useDeleteItem(); const { mutateAsync: deleteItem, isLoading } = useDeleteItem();
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
@@ -51,7 +51,7 @@ function ItemDeleteAlert({
intent: Intent.SUCCESS, intent: Intent.SUCCESS,
}); });
// Reset to page number one. // Reset to page number one.
addItemsTableQueries({ page: 1 }); setItemsTableState({ page: 1 });
}) })
.catch( .catch(
({ ({

View File

@@ -54,7 +54,7 @@ function ContactDuplicateForm({
{({ isSubmitting }) => ( {({ isSubmitting }) => (
<Form> <Form>
<div className={Classes.DIALOG_BODY}> <div className={Classes.DIALOG_BODY}>
<p> <p class="paragraph">
<T id={'are_you_sure_want_to_duplicate'} /> <T id={'are_you_sure_want_to_duplicate'} />
</p> </p>
@@ -66,7 +66,6 @@ function ContactDuplicateForm({
labelInfo={<FieldRequiredHint />} labelInfo={<FieldRequiredHint />}
intent={inputIntent({ error, touched })} intent={inputIntent({ error, touched })}
className={'form-group--select-list'} className={'form-group--select-list'}
inline={true}
helperText={<ErrorMessage name="contact_type" />} helperText={<ErrorMessage name="contact_type" />}
> >
<ListSelect <ListSelect

View File

@@ -6,41 +6,52 @@ import ReferenceNumberForm from 'containers/JournalNumber/ReferenceNumberForm';
import withDialogActions from 'containers/Dialog/withDialogActions'; import withDialogActions from 'containers/Dialog/withDialogActions';
import withSettings from 'containers/Settings/withSettings'; import withSettings from 'containers/Settings/withSettings';
import { compose, optionsMapToArray, saveInvoke } from 'utils'; import { compose, saveInvoke } from 'utils';
import {
transformFormToSettings,
transformSettingsToForm,
} from 'containers/JournalNumber/utils';
/** /**
* Estimate number dialog's content. * Estimate number dialog's content.
*/ */
function EstimateNumberDialogContent({ function EstimateNumberDialogContent({
// #withSettings // #withSettings
nextNumber, nextNumber,
numberPrefix, numberPrefix,
autoIncrement,
// #withDialogActions // #withDialogActions
closeDialog, closeDialog,
// #ownProps // #ownProps
initialValues,
onConfirm, onConfirm,
}) { }) {
// Fetches the estimates settings.
const { isLoading: isSettingsLoading } = useSettingsEstimates(); const { isLoading: isSettingsLoading } = useSettingsEstimates();
// Mutates the settings.
const { mutateAsync: saveSettingsMutate } = useSaveSettings(); const { mutateAsync: saveSettingsMutate } = useSaveSettings();
// Handle the submit form.
const handleSubmitForm = (values, { setSubmitting }) => { const handleSubmitForm = (values, { setSubmitting }) => {
const options = optionsMapToArray(values).map((option) => ({ // Transformes the form values to settings to save it.
key: option.key, const options = transformFormToSettings(values, 'sales_estimates');
...option,
group: 'sales_estimates', const handleSuccess = () => {
})); setSubmitting(false);
saveSettingsMutate({ options }) closeDialog('estimate-number-form');
.then(() => { saveInvoke(onConfirm, values);
setSubmitting(false); };
closeDialog('estimate-number-form'); const handleErrors = () => {
saveInvoke(onConfirm, values); setSubmitting(false);
}) };
.catch(() => { if (values.incrementMode === 'manual-transaction') {
setSubmitting(false); handleSuccess();
}); return;
}
saveSettingsMutate({ options }).then(handleSuccess).catch(handleErrors);
}; };
const handleClose = useCallback(() => { const handleClose = useCallback(() => {
@@ -50,8 +61,14 @@ function EstimateNumberDialogContent({
return ( return (
<DialogContent isLoading={isSettingsLoading}> <DialogContent isLoading={isSettingsLoading}>
<ReferenceNumberForm <ReferenceNumberForm
initialNumber={nextNumber} initialValues={{
initialPrefix={numberPrefix} ...transformSettingsToForm({
nextNumber,
numberPrefix,
autoIncrement,
}),
...initialValues,
}}
onSubmit={handleSubmitForm} onSubmit={handleSubmitForm}
onClose={handleClose} onClose={handleClose}
/> />
@@ -64,5 +81,6 @@ export default compose(
withSettings(({ estimatesSettings }) => ({ withSettings(({ estimatesSettings }) => ({
nextNumber: estimatesSettings?.nextNumber, nextNumber: estimatesSettings?.nextNumber,
numberPrefix: estimatesSettings?.numberPrefix, numberPrefix: estimatesSettings?.numberPrefix,
autoIncrement: estimatesSettings?.autoIncrement,
})), })),
)(EstimateNumberDialogContent); )(EstimateNumberDialogContent);

View File

@@ -8,12 +8,19 @@ const EstimateNumberDialogContent = lazy(() =>
import('./EstimateNumberDialogContent'), import('./EstimateNumberDialogContent'),
); );
/**
* Estimate number dialog.
*/
function EstimateNumberDialog({ function EstimateNumberDialog({
dialogName, dialogName,
paylaod = { id: null }, payload: { initialFormValues },
isOpen, isOpen,
onConfirm onConfirm
}) { }) {
const handleConfirm = (values) => {
saveInvoke(onConfirm, values);
};
return ( return (
<Dialog <Dialog
name={dialogName} name={dialogName}
@@ -25,8 +32,8 @@ function EstimateNumberDialog({
> >
<DialogSuspense> <DialogSuspense>
<EstimateNumberDialogContent <EstimateNumberDialogContent
estimateNumberId={paylaod.id} initialValues={{ ...initialFormValues }}
onConfirm={(values) => saveInvoke(onConfirm, values)}/> onConfirm={handleConfirm}/>
</DialogSuspense> </DialogSuspense>
</Dialog> </Dialog>
); );

View File

@@ -1,4 +1,4 @@
import React, { useCallback } from 'react'; import React from 'react';
import { useSaveSettings } from 'hooks/query'; import { useSaveSettings } from 'hooks/query';
import { InvoiceNumberDialogProvider } from './InvoiceNumberDialogProvider'; import { InvoiceNumberDialogProvider } from './InvoiceNumberDialogProvider';
@@ -7,57 +7,69 @@ import ReferenceNumberForm from 'containers/JournalNumber/ReferenceNumberForm';
import withDialogActions from 'containers/Dialog/withDialogActions'; import withDialogActions from 'containers/Dialog/withDialogActions';
import withSettings from 'containers/Settings/withSettings'; import withSettings from 'containers/Settings/withSettings';
import withSettingsActions from 'containers/Settings/withSettingsActions'; import withSettingsActions from 'containers/Settings/withSettingsActions';
// import withInvoicesActions from 'containers/Sales/Invoice/withInvoiceActions'; import { compose } from 'utils';
import {
import { compose, optionsMapToArray } from 'utils'; transformFormToSettings,
transformSettingsToForm,
} from 'containers/JournalNumber/utils';
/** /**
* invoice number dialog's content. * invoice number dialog's content.
*/ */
function InvoiceNumberDialogContent({ function InvoiceNumberDialogContent({
// #ownProps // #ownProps
initialValues,
onConfirm, onConfirm,
// #withSettings // #withSettings
nextNumber, nextNumber,
numberPrefix, numberPrefix,
autoIncrement,
// #withDialogActions // #withDialogActions
closeDialog, closeDialog,
}) { }) {
const { mutateAsync: saveSettings } = useSaveSettings(); const { mutateAsync: saveSettings } = useSaveSettings();
// Handle the submit form.
const handleSubmitForm = (values, { setSubmitting }) => { const handleSubmitForm = (values, { setSubmitting }) => {
const { mode, ...autoModeValues } = values; // Handle the form success.
const handleSuccess = () => {
setSubmitting(false);
closeDialog('invoice-number-form');
onConfirm(values);
};
// Handle the form errors.
const handleErrors = () => {
setSubmitting(false);
};
if (values.incrementMode === 'manual-transaction') {
handleSuccess();
return;
}
// Transformes the form values to settings to save it.
const options = transformFormToSettings(values, 'sales_invoices');
const options = optionsMapToArray(autoModeValues).map((option) => ({ // Save the goddamn settings.
key: option.key, saveSettings({ options }).then(handleSuccess).catch(handleErrors);
...option,
group: 'sales_invoices',
}));
saveSettings({ options })
.then(() => {
setSubmitting(false);
closeDialog('invoice-number-form');
onConfirm(values);
})
.catch(() => {
setSubmitting(false);
});
}; };
// Handle the dialog close. // Handle the dialog close.
const handleClose = useCallback(() => { const handleClose = () => {
closeDialog('invoice-number-form'); closeDialog('invoice-number-form');
}, [closeDialog]); };
return ( return (
<InvoiceNumberDialogProvider> <InvoiceNumberDialogProvider>
<ReferenceNumberForm <ReferenceNumberForm
initialNumber={nextNumber} initialValues={{
initialPrefix={numberPrefix} ...transformSettingsToForm({
nextNumber,
numberPrefix,
autoIncrement,
}),
...initialValues,
}}
onSubmit={handleSubmitForm} onSubmit={handleSubmitForm}
onClose={handleClose} onClose={handleClose}
/> />
@@ -71,5 +83,6 @@ export default compose(
withSettings(({ invoiceSettings }) => ({ withSettings(({ invoiceSettings }) => ({
nextNumber: invoiceSettings?.nextNumber, nextNumber: invoiceSettings?.nextNumber,
numberPrefix: invoiceSettings?.numberPrefix, numberPrefix: invoiceSettings?.numberPrefix,
autoIncrement: invoiceSettings?.autoIncrement,
})), })),
)(InvoiceNumberDialogContent); )(InvoiceNumberDialogContent);

View File

@@ -8,24 +8,32 @@ const InvoiceNumberDialogContent = lazy(() =>
import('./InvoiceNumberDialogContent'), import('./InvoiceNumberDialogContent'),
); );
/**
* Invoice number dialog.
*/
function InvoiceNumberDialog({ function InvoiceNumberDialog({
dialogName, dialogName,
payload = { id: null }, payload: { initialFormValues },
isOpen, isOpen,
onConfirm, onConfirm,
}) { }) {
const handleConfirm = (values) => {
saveInvoke(onConfirm, values);
};
return ( return (
<Dialog <Dialog
title={<T id={'invoice_number_settings'} />} title={<T id={'invoice_number_settings'} />}
name={dialogName} name={dialogName}
autoFocus={true} autoFocus={true}
canEscapeKeyClose={true} canEscapeKeyClose={true}
isOpen={isOpen} isOpen={isOpen}
> >
<DialogSuspense> <DialogSuspense>
<InvoiceNumberDialogContent <InvoiceNumberDialogContent
InvoiceNumberId={payload.id} initialValues={{ ...initialFormValues }}
onConfirm={(values) => saveInvoke(onConfirm, values)} /> onConfirm={handleConfirm}
/>
</DialogSuspense> </DialogSuspense>
</Dialog> </Dialog>
); );

View File

@@ -8,43 +8,50 @@ import withDialogActions from 'containers/Dialog/withDialogActions';
import withSettingsActions from 'containers/Settings/withSettingsActions'; import withSettingsActions from 'containers/Settings/withSettingsActions';
import withSettings from 'containers/Settings/withSettings'; import withSettings from 'containers/Settings/withSettings';
import { saveInvoke, compose, optionsMapToArray } from 'utils'; import { saveInvoke, compose } from 'utils';
import {
transformFormToSettings,
transformSettingsToForm,
} from 'containers/JournalNumber/utils';
/** /**
* payment receive number dialog's content. * Payment receive number dialog's content.
*/ */
function PaymentNumberDialogContent({ function PaymentNumberDialogContent({
// #withSettings // #withSettings
nextNumber, nextNumber,
numberPrefix, numberPrefix,
autoIncrement,
// #withDialogActions // #withDialogActions
closeDialog, closeDialog,
// #ownProps // #ownProps
onConfirm, onConfirm,
initialValues
}) { }) {
const { isLoading: isSettingsLoading } = useSettingsPaymentReceives(); const { isLoading: isSettingsLoading } = useSettingsPaymentReceives();
const { mutateAsync: saveSettingsMutate } = useSaveSettings(); const { mutateAsync: saveSettingsMutate } = useSaveSettings();
// Handle submit form.
const handleSubmitForm = (values, { setSubmitting }) => { const handleSubmitForm = (values, { setSubmitting }) => {
const options = optionsMapToArray(values).map((option) => ({ // Transformes the form values to settings to save it.
key: option.key, const options = transformFormToSettings(values, 'payment_receives');
...option,
group: 'payment_receives',
}));
saveSettingsMutate({ options }) const handleSuccess = () => {
.then(() => { setSubmitting(false);
setSubmitting(false); closeDialog('payment-receive-number-form');
closeDialog('payment-receive-number-form');
saveInvoke(onConfirm, values); saveInvoke(onConfirm, values);
}) };
.catch(() => { const handleErrors = () => {
setSubmitting(false); setSubmitting(false);
}); };
if (values.incrementMode === 'manual-transaction') {
handleSuccess();
return;
}
saveSettingsMutate({ options }).then(handleSuccess).catch(handleErrors);
}; };
const handleClose = useCallback(() => { const handleClose = useCallback(() => {
@@ -54,8 +61,14 @@ function PaymentNumberDialogContent({
return ( return (
<DialogContent isLoading={isSettingsLoading}> <DialogContent isLoading={isSettingsLoading}>
<ReferenceNumberForm <ReferenceNumberForm
initialNumber={nextNumber} initialValues={{
initialPrefix={numberPrefix} ...transformSettingsToForm({
nextNumber,
numberPrefix,
autoIncrement,
}),
...initialValues,
}}
onSubmit={handleSubmitForm} onSubmit={handleSubmitForm}
onClose={handleClose} onClose={handleClose}
/> />
@@ -69,5 +82,6 @@ export default compose(
withSettings(({ paymentReceiveSettings }) => ({ withSettings(({ paymentReceiveSettings }) => ({
nextNumber: paymentReceiveSettings?.nextNumber, nextNumber: paymentReceiveSettings?.nextNumber,
numberPrefix: paymentReceiveSettings?.numberPrefix, numberPrefix: paymentReceiveSettings?.numberPrefix,
autoIncrement: paymentReceiveSettings?.autoIncrement,
})), })),
)(PaymentNumberDialogContent); )(PaymentNumberDialogContent);

View File

@@ -4,7 +4,7 @@ import { Dialog, DialogSuspense } from 'components';
import withDialogRedux from 'components/DialogReduxConnect'; import withDialogRedux from 'components/DialogReduxConnect';
import { saveInvoke, compose } from 'utils'; import { saveInvoke, compose } from 'utils';
const PaymentReceiveNumbereDialogConetnet = lazy(() => const PaymentReceiveNumbereDialogContent = lazy(() =>
import('./PaymentReceiveNumberDialogContent'), import('./PaymentReceiveNumberDialogContent'),
); );
@@ -13,7 +13,7 @@ const PaymentReceiveNumbereDialogConetnet = lazy(() =>
*/ */
function PaymentReceiveNumberDialog({ function PaymentReceiveNumberDialog({
dialogName, dialogName,
payload = { id: null }, payload: { initialFormValues },
isOpen, isOpen,
onConfirm onConfirm
}) { }) {
@@ -26,8 +26,8 @@ function PaymentReceiveNumberDialog({
isOpen={isOpen} isOpen={isOpen}
> >
<DialogSuspense> <DialogSuspense>
<PaymentReceiveNumbereDialogConetnet <PaymentReceiveNumbereDialogContent
paymentReceiveNumberId={payload.id} initialValues={initialFormValues}
onConfirm={(values) => saveInvoke(onConfirm, values)} onConfirm={(values) => saveInvoke(onConfirm, values)}
/> />
</DialogSuspense> </DialogSuspense>

View File

@@ -7,20 +7,25 @@ import ReferenceNumberForm from 'containers/JournalNumber/ReferenceNumberForm';
import withDialogActions from 'containers/Dialog/withDialogActions'; import withDialogActions from 'containers/Dialog/withDialogActions';
import withSettings from 'containers/Settings/withSettings'; import withSettings from 'containers/Settings/withSettings';
import { compose, optionsMapToArray, saveInvoke } from 'utils'; import { compose, saveInvoke } from 'utils';
import {
transformFormToSettings,
transformSettingsToForm,
} from 'containers/JournalNumber/utils';
/** /**
* Receipt number dialog's content. * Receipt number dialog's content.
*/ */
function ReceiptNumberDialogContent({ function ReceiptNumberDialogContent({
// #ownProps // #ownProps
receiptId, receiptId,
onConfirm, onConfirm,
initialValues,
// #withSettings // #withSettings
nextNumber, nextNumber,
numberPrefix, numberPrefix,
autoIncrement,
// #withDialogActions // #withDialogActions
closeDialog, closeDialog,
@@ -28,22 +33,24 @@ function ReceiptNumberDialogContent({
const { isLoading: isSettingsLoading } = useSettingsReceipts(); const { isLoading: isSettingsLoading } = useSettingsReceipts();
const { mutateAsync: saveSettingsMutate } = useSaveSettings(); const { mutateAsync: saveSettingsMutate } = useSaveSettings();
// Handle the form submit.
const handleSubmitForm = (values, { setSubmitting }) => { const handleSubmitForm = (values, { setSubmitting }) => {
const options = optionsMapToArray(values).map((option) => ({ const handleSuccess = () => {
key: option.key, setSubmitting(false);
...option, closeDialog('receipt-number-form');
group: 'sales_receipts', saveInvoke(onConfirm, values);
})); };
const handleErrors = () => {
setSubmitting(false);
};
if (values.incrementMode === 'manual-transaction') {
handleSuccess();
return;
}
// Transformes the form values to settings to save it.
const options = transformFormToSettings(values, 'sales_receipts');
saveSettingsMutate({ options }) saveSettingsMutate({ options }).then(handleSuccess).catch(handleErrors);
.then(() => {
setSubmitting(false);
closeDialog('receipt-number-form');
saveInvoke(onConfirm, values)
})
.catch(() => {
setSubmitting(false);
});
}; };
const handleClose = useCallback(() => { const handleClose = useCallback(() => {
@@ -53,8 +60,14 @@ function ReceiptNumberDialogContent({
return ( return (
<DialogContent isLoading={isSettingsLoading}> <DialogContent isLoading={isSettingsLoading}>
<ReferenceNumberForm <ReferenceNumberForm
initialNumber={nextNumber} initialValues={{
initialPrefix={numberPrefix} ...transformSettingsToForm({
nextNumber,
numberPrefix,
autoIncrement,
}),
...initialValues,
}}
onSubmit={handleSubmitForm} onSubmit={handleSubmitForm}
onClose={handleClose} onClose={handleClose}
/> />
@@ -67,5 +80,6 @@ export default compose(
withSettings(({ receiptSettings }) => ({ withSettings(({ receiptSettings }) => ({
nextNumber: receiptSettings?.nextNumber, nextNumber: receiptSettings?.nextNumber,
numberPrefix: receiptSettings?.numberPrefix, numberPrefix: receiptSettings?.numberPrefix,
autoIncrement: receiptSettings?.autoIncrement,
})), })),
)(ReceiptNumberDialogContent); )(ReceiptNumberDialogContent);

View File

@@ -13,7 +13,7 @@ const ReceiptNumberDialogContent = lazy(() =>
*/ */
function ReceiptNumberDialog({ function ReceiptNumberDialog({
dialogName, dialogName,
paylaod = { id: null }, payload: { initialFormValues = {} },
isOpen, isOpen,
onConfirm, onConfirm,
}) { }) {
@@ -31,7 +31,7 @@ function ReceiptNumberDialog({
> >
<DialogSuspense> <DialogSuspense>
<ReceiptNumberDialogContent <ReceiptNumberDialogContent
receiptId={paylaod.id} initialValues={{ ...initialFormValues }}
onConfirm={handleConfirm} onConfirm={handleConfirm}
/> />
</DialogSuspense> </DialogSuspense>

View File

@@ -4,8 +4,12 @@ import { Formik, Form } from 'formik';
import { FormattedMessage as T } from 'react-intl'; import { FormattedMessage as T } from 'react-intl';
import { Button, Classes } from '@blueprintjs/core'; import { Button, Classes } from '@blueprintjs/core';
import { Intent } from '@blueprintjs/core'; import { Intent } from '@blueprintjs/core';
import { saveInvoke } from 'utils';
import 'style/pages/ReferenceNumber/ReferenceNumber.scss';
import ReferenceNumberFormContent from './ReferenceNumberFormContent'; import ReferenceNumberFormContent from './ReferenceNumberFormContent';
import { transformValuesToForm } from './utils';
import { saveInvoke } from 'utils';
/** /**
* Reference number form. * Reference number form.
@@ -13,42 +17,45 @@ import ReferenceNumberFormContent from './ReferenceNumberFormContent';
export default function ReferenceNumberForm({ export default function ReferenceNumberForm({
onSubmit, onSubmit,
onClose, onClose,
initialPrefix, initialValues,
initialNumber,
}) { }) {
// Validation schema.
const validationSchema = Yup.object().shape({ const validationSchema = Yup.object().shape({
// mode: Yup.string(), incrementMode: Yup.string(),
number_prefix: Yup.string(), numberPrefix: Yup.string(),
next_number: Yup.number(), nextNumber: Yup.number(),
manualTransactionNo: Yup.string(),
}); });
// Initial values.
const initialValues = useMemo( const formInitialValues = useMemo(
() => ({ () => ({
number_prefix: initialPrefix || '', ...initialValues,
next_number: initialNumber || '', incrementMode:
initialValues.incrementMode === 'auto' &&
initialValues.manualTransactionNo
? 'manual-transaction'
: initialValues.incrementMode,
}), }),
[initialPrefix, initialNumber], [initialValues],
); );
// Handle the form submit.
const handleSubmit = (values) => { const handleSubmit = (values, methods) => {
debugger; const parsed = transformValuesToForm(values);
saveInvoke(onSubmit, values); saveInvoke(onSubmit, { ...parsed, ...values }, methods);
}; };
return ( return (
<Formik <Formik
initialValues={initialValues} initialValues={formInitialValues}
validationSchema={validationSchema} validationSchema={validationSchema}
onSubmit={handleSubmit} onSubmit={handleSubmit}
> >
{({ isSubmitting }) => ( {({ isSubmitting }) => (
<Form> <Form className={'reference-number-form'}>
<div className={Classes.DIALOG_BODY}> <div className={Classes.DIALOG_BODY}>
<p className="paragraph"> <p className="paragraph">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce Your invoice numbers are set on auto-increment mod. Are you sure changing this setting?
tincidunt porta quam,
</p> </p>
<ReferenceNumberFormContent /> <ReferenceNumberFormContent />
</div> </div>

View File

@@ -1,71 +1,103 @@
import React from 'react'; import React from 'react';
import { FastField } from 'formik'; import { FastField, useFormikContext } from 'formik';
import { FormattedMessage as T } from 'react-intl'; import { FormattedMessage as T } from 'react-intl';
import { FormGroup, InputGroup, Radio } from '@blueprintjs/core'; import { FormGroup, InputGroup, Radio } from '@blueprintjs/core';
import { Row, Col, ErrorMessage } from 'components'; import { If, Row, Col, ErrorMessage } from 'components';
import { inputIntent } from 'utils'; import { inputIntent } from 'utils';
/** /**
* Reference number form content. * Reference number form content.
*/ */
export default function ReferenceNumberFormContent() { export default function ReferenceNumberFormContent() {
const { values } = useFormikContext();
return ( return (
<> <>
<FastField name={'mode'}> {/* ------------- Auto increment mode ------------- */}
<FastField name={'incrementMode'}>
{({ form, field, meta: { error, touched } }) => ( {({ form, field, meta: { error, touched } }) => (
<Radio <Radio
label="Auto-incrementing invoice number." label="Auto-incrementing invoice number."
value="auto-increment" value="auto-increment"
{...field} onChange={() => {
form.setFieldValue('incrementMode', 'auto');
}}
checked={field.value === 'auto'}
/> />
)} )}
</FastField> </FastField>
<Row> <If condition={values.incrementMode === 'auto'}>
{/* ------------- Prefix ------------- */} <Row>
<Col xs={6}> {/* ------------- Prefix ------------- */}
<FastField name={'prefix'}> <Col xs={4}>
{({ form, field, meta: { error, touched } }) => ( <FastField name={'numberPrefix'}>
<FormGroup {({ form, field, meta: { error, touched } }) => (
label={<T id={'prefix'} />} <FormGroup
className={'form-group--'} label={<T id={'prefix'} />}
intent={inputIntent({ error, touched })} className={'form-group--'}
helperText={<ErrorMessage name={'prefix'} />}
>
<InputGroup
intent={inputIntent({ error, touched })} intent={inputIntent({ error, touched })}
{...field} helperText={<ErrorMessage name={'numberPrefix'} />}
/> >
</FormGroup> <InputGroup
)} intent={inputIntent({ error, touched })}
</FastField> {...field}
</Col> />
</FormGroup>
)}
</FastField>
</Col>
{/* ------------- Next number ------------- */} {/* ------------- Next number ------------- */}
<Col xs={6}> <Col xs={6}>
<FastField name={'next_number'}> <FastField name={'nextNumber'}>
{({ form, field, meta: { error, touched } }) => ( {({ form, field, meta: { error, touched } }) => (
<FormGroup <FormGroup
label={<T id={'next_number'} />} label={<T id={'next_number'} />}
className={'form-group--'} className={'form-group--next-number'}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'next_number'} />}
>
<InputGroup
intent={inputIntent({ error, touched })} intent={inputIntent({ error, touched })}
{...field} helperText={<ErrorMessage name={'nextNumber'} />}
/> >
</FormGroup> <InputGroup
)} intent={inputIntent({ error, touched })}
</FastField> {...field}
</Col> />
</Row> </FormGroup>
)}
</FastField>
</Col>
</Row>
</If>
<FastField name={'mode'}> {/* ------------- Manual increment mode ------------- */}
<FastField name={'incrementMode'}>
{({ form, field, meta: { error, touched } }) => ( {({ form, field, meta: { error, touched } }) => (
<Radio label="Manual entring for this transaction." value="manual" {...field} /> <Radio
label="I will enter them manully each time."
value="manual"
onChange={() => {
form.setFieldValue('incrementMode', 'manual');
}}
checked={field.value === 'manual'}
/>
)} )}
</FastField> </FastField>
{/* ------------- Transaction manual increment mode ------------- */}
<If condition={values.manualTransactionNo}>
<FastField name={'incrementMode'}>
{({ form, field, meta: { error, touched } }) => (
<Radio
label="Manual entring for this transaction."
value="manual"
onChange={() => {
form.setFieldValue('incrementMode', 'manual-transaction');
}}
checked={field.value === 'manual-transaction'}
/>
)}
</FastField>
</If>
</> </>
); );
} }

View File

@@ -0,0 +1,36 @@
import {
transformToForm,
optionsMapToArray,
transfromToSnakeCase,
transactionNumber,
} from 'utils';
export const defaultInvoiceNoSettings = {
nextNumber: '',
numberPrefix: '',
autoIncrement: '',
};
export const transformSettingsToForm = (settings) => ({
...settings,
incrementMode: settings.autoIncrement === 'true' ? 'auto' : 'manual',
});
export const transformFormToSettings = (values, group) => {
const options = transfromToSnakeCase({
...transformToForm(values, defaultInvoiceNoSettings),
autoIncrement: values.incrementMode === 'auto',
});
return optionsMapToArray(options).map((option) => ({ ...option, group }));
};
export const transformValuesToForm = (values) => {
const incrementNumber =
values.incrementMode === 'auto'
? transactionNumber(values.numberPrefix, values.nextNumber)
: values.manualTransactionNo;
const manually = values.incrementMode === 'auto' ? false : true;
return { incrementNumber, manually };
};

View File

@@ -16,7 +16,7 @@ export default function PaymentMadeFooter() {
<Row> <Row>
<Col md={8}> <Col md={8}>
{/* --------- Statement --------- */} {/* --------- Statement --------- */}
<FastField name={'customer_name'}> <FastField name={'statement'}>
{({ form, field, meta: { error, touched } }) => ( {({ form, field, meta: { error, touched } }) => (
<FormGroup <FormGroup
label={<T id={'statement'} />} label={<T id={'statement'} />}

View File

@@ -21,7 +21,7 @@ export const defaultPaymentMade = {
payment_date: moment(new Date()).format('YYYY-MM-DD'), payment_date: moment(new Date()).format('YYYY-MM-DD'),
reference: '', reference: '',
payment_number: '', payment_number: '',
description: '', statement: '',
entries: [], entries: [],
}; };

View File

@@ -1,10 +1,11 @@
import React from 'react'; import React from 'react';
import ListSelect from 'components/ListSelect'; import { Button, MenuItem } from '@blueprintjs/core';
import { FormattedMessage as T } from 'react-intl'; import { FormattedMessage as T } from 'react-intl';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import { Icon } from 'components'; import { Icon } from 'components';
import { Position } from '@blueprintjs/core'; import { Position } from '@blueprintjs/core';
import quickNewOptions from 'common/quickNewOptions'; import quickNewOptions from 'common/quickNewOptions';
import { Select } from '@blueprintjs/select';
/** /**
* Quick New Dropdown. * Quick New Dropdown.
@@ -12,26 +13,29 @@ import quickNewOptions from 'common/quickNewOptions';
function QuickNewDropdown() { function QuickNewDropdown() {
const history = useHistory(); const history = useHistory();
const handleClickQuickNew = ({path }) => { const handleClickQuickNew = ({ path }) => {
history.push(`/${path}`); history.push(`/${path}`);
}; };
const itemRenderer = (item, { handleClick, modifiers, query }) => (
<MenuItem text={item.name} label={item.label} onClick={handleClick} />
);
return ( return (
<ListSelect <Select
items={quickNewOptions} items={quickNewOptions}
itemRenderer={itemRenderer}
onItemSelect={(type) => handleClickQuickNew(type)} onItemSelect={(type) => handleClickQuickNew(type)}
textProp={'name'}
labelProp={'label'}
filterable={false}
popoverProps={{ minimal: false, position: Position.BOTTOM }} popoverProps={{ minimal: false, position: Position.BOTTOM }}
defaultText={'Select'} className={'form-group--quick-new-downDrop'}
buttonProps={{ filterable={false}
text: <T id={'quick_new'} />, >
icon: <Icon icon={'plus-24'} iconSize={20} />, <Button
minimal: true, text={<T id={'quick_new'} />}
}} icon={<Icon icon={'plus-24'} iconSize={20} />}
className={'form-group-quick-new-downDrop'} minimal={true}
/> />
</Select>
); );
} }

View File

@@ -22,7 +22,7 @@ import withSettings from 'containers/Settings/withSettings';
import { AppToaster } from 'components'; import { AppToaster } from 'components';
import { ERROR } from 'common/errors'; import { ERROR } from 'common/errors';
import { compose, orderingLinesIndexes } from 'utils'; import { compose, transactionNumber, orderingLinesIndexes } from 'utils';
import { useEstimateFormContext } from './EstimateFormProvider'; import { useEstimateFormContext } from './EstimateFormProvider';
import { transformToEditForm, defaultEstimate } from './utils'; import { transformToEditForm, defaultEstimate } from './utils';
@@ -33,6 +33,7 @@ function EstimateForm({
// #withSettings // #withSettings
estimateNextNumber, estimateNextNumber,
estimateNumberPrefix, estimateNumberPrefix,
estimateIncrementMode,
}) { }) {
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
const history = useHistory(); const history = useHistory();
@@ -44,24 +45,25 @@ function EstimateForm({
editEstimateMutate, editEstimateMutate,
} = useEstimateFormContext(); } = useEstimateFormContext();
const estimateNumber = estimateNumberPrefix const estimateNumber = transactionNumber(
? `${estimateNumberPrefix}-${estimateNextNumber}` estimateNumberPrefix,
: estimateNextNumber; estimateNextNumber,
);
// Initial values in create and edit mode. // Initial values in create and edit mode.
const initialValues = useMemo( const initialValues = useMemo(
() => ({ () => ({
...(!isEmpty(estimate) ...(!isEmpty(estimate)
? { ? { ...transformToEditForm(estimate) }
...transformToEditForm(estimate),
}
: { : {
...defaultEstimate, ...defaultEstimate,
estimate_number: estimateNumber, ...(estimateIncrementMode) && ({
estimate_number: estimateNumber,
}),
entries: orderingLinesIndexes(defaultEstimate.entries), entries: orderingLinesIndexes(defaultEstimate.entries),
}), }),
}), }),
[estimate, estimateNumber], [estimate, estimateNumber, estimateIncrementMode],
); );
// Transform response errors to fields. // Transform response errors to fields.
@@ -98,7 +100,10 @@ function EstimateForm({
return; return;
} }
const form = { const form = {
...values, ...omit(values, ['estimate_number_manually', 'estimate_number']),
...(values.estimate_number_manually) && ({
estimate_number: values.estimate_number,
}),
delivered: submitPayload.deliver, delivered: submitPayload.deliver,
entries: entries.map((entry) => ({ ...omit(entry, ['total']) })), entries: entries.map((entry) => ({ ...omit(entry, ['total']) })),
}; };
@@ -134,7 +139,6 @@ function EstimateForm({
} }
setSubmitting(false); setSubmitting(false);
}; };
if (!isNewMode) { if (!isNewMode) {
editEstimateMutate([estimate.id, form]).then(onSuccess).catch(onError); editEstimateMutate([estimate.id, form]).then(onSuccess).catch(onError);
} else { } else {
@@ -174,5 +178,6 @@ export default compose(
withSettings(({ estimatesSettings }) => ({ withSettings(({ estimatesSettings }) => ({
estimateNextNumber: estimatesSettings?.nextNumber, estimateNextNumber: estimatesSettings?.nextNumber,
estimateNumberPrefix: estimatesSettings?.numberPrefix, estimateNumberPrefix: estimatesSettings?.numberPrefix,
estimateIncrementMode: estimatesSettings?.autoIncrement,
})), })),
)(EstimateForm); )(EstimateForm);

View File

@@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import { useFormikContext } from 'formik'; import { useFormikContext } from 'formik';
import EstimateNumberDialog from 'containers/Dialogs/EstimateNumberDialog'; import EstimateNumberDialog from 'containers/Dialogs/EstimateNumberDialog';
import { transactionNumber } from 'utils';
/** /**
* Estimate form dialogs. * Estimate form dialogs.
@@ -10,11 +9,9 @@ export default function EstimateFormDialogs() {
const { setFieldValue } = useFormikContext(); const { setFieldValue } = useFormikContext();
// Update the form once the invoice number form submit confirm. // Update the form once the invoice number form submit confirm.
const handleEstimateNumberFormConfirm = (values) => { const handleEstimateNumberFormConfirm = ({ incrementNumber, manually }) => {
setFieldValue( setFieldValue('estimate_number', incrementNumber || '');
'estimate_number', setFieldValue('estimate_number_manually', manually);
transactionNumber(values.number_prefix, values.next_number),
);
}; };
return ( return (

View File

@@ -8,7 +8,13 @@ import {
import { DateInput } from '@blueprintjs/datetime'; import { DateInput } from '@blueprintjs/datetime';
import { FormattedMessage as T } from 'react-intl'; import { FormattedMessage as T } from 'react-intl';
import { FastField, ErrorMessage } from 'formik'; import { FastField, ErrorMessage } from 'formik';
import { momentFormatter, compose, tansformDateValue } from 'utils'; import {
momentFormatter,
compose,
tansformDateValue,
inputIntent,
handleDateChange,
} from 'utils';
import classNames from 'classnames'; import classNames from 'classnames';
import { CLASSES } from 'common/classes'; import { CLASSES } from 'common/classes';
import { import {
@@ -19,8 +25,9 @@ import {
} from 'components'; } from 'components';
import withDialogActions from 'containers/Dialog/withDialogActions'; import withDialogActions from 'containers/Dialog/withDialogActions';
import withSettings from 'containers/Settings/withSettings';
import { inputIntent, handleDateChange } from 'utils'; import { useObserveEstimateNoSettings } from './utils';
import { useEstimateFormContext } from './EstimateFormProvider'; import { useEstimateFormContext } from './EstimateFormProvider';
/** /**
@@ -29,6 +36,11 @@ import { useEstimateFormContext } from './EstimateFormProvider';
function EstimateFormHeader({ function EstimateFormHeader({
// #withDialogActions // #withDialogActions
openDialog, openDialog,
// #withSettings
estimateAutoIncrement,
estimateNumberPrefix,
estimateNextNumber,
}) { }) {
const { customers } = useEstimateFormContext(); const { customers } = useEstimateFormContext();
@@ -36,6 +48,22 @@ function EstimateFormHeader({
openDialog('estimate-number-form', {}); openDialog('estimate-number-form', {});
}; };
const handleEstimateNoBlur = (form, field) => (event) => {
const newValue = event.target.value;
if (field.value !== newValue && estimateAutoIncrement) {
openDialog('estimate-number-form', {
initialFormValues: {
manualTransactionNo: newValue,
incrementMode: 'manual-transaction',
},
});
}
};
// Syncs estimate number settings with the form.
useObserveEstimateNoSettings(estimateNumberPrefix, estimateNextNumber);
return ( return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}> <div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ----------- Customer name ----------- */} {/* ----------- Customer name ----------- */}
@@ -131,7 +159,9 @@ function EstimateFormHeader({
<ControlGroup fill={true}> <ControlGroup fill={true}>
<InputGroup <InputGroup
minimal={true} minimal={true}
{...field} value={field.value}
asyncControl={true}
onBlur={handleEstimateNoBlur(form, field)}
/> />
<InputPrependButton <InputPrependButton
buttonProps={{ buttonProps={{
@@ -169,4 +199,9 @@ function EstimateFormHeader({
export default compose( export default compose(
withDialogActions, withDialogActions,
withSettings(({ estimatesSettings }) => ({
estimateNextNumber: estimatesSettings?.nextNumber,
estimateNumberPrefix: estimatesSettings?.numberPrefix,
estimateAutoIncrement: estimatesSettings?.autoIncrement,
})),
)(EstimateFormHeader); )(EstimateFormHeader);

View File

@@ -4,7 +4,7 @@ import {
useEstimate, useEstimate,
useCustomers, useCustomers,
useItems, useItems,
useSettings, useSettingsEstimates,
useCreateEstimate, useCreateEstimate,
useEditEstimate useEditEstimate
} from 'hooks/query'; } from 'hooks/query';
@@ -32,7 +32,7 @@ function EstimateFormProvider({ estimateId, ...props }) {
} = useCustomers({ page_size: 10000 }); } = useCustomers({ page_size: 10000 });
// Handle fetch settings. // Handle fetch settings.
useSettings(); useSettingsEstimates();
// Form submit payload. // Form submit payload.
const [submitPayload, setSubmitPayload] = React.useState({}); const [submitPayload, setSubmitPayload] = React.useState({});

View File

@@ -1,5 +1,7 @@
import React from 'react';
import { useFormikContext } from 'formik';
import moment from 'moment'; import moment from 'moment';
import { repeatValue, transformToForm } from 'utils'; import { transactionNumber, repeatValue, transformToForm } from 'utils';
export const MIN_LINES_NUMBER = 4; export const MIN_LINES_NUMBER = 4;
@@ -35,4 +37,16 @@ export const transformToEditForm = (estimate) => ({
Math.max(MIN_LINES_NUMBER - estimate.entries.length, 0), Math.max(MIN_LINES_NUMBER - estimate.entries.length, 0),
), ),
], ],
}); });
/**
* Syncs estimate number of the settings with the context form.
*/
export const useObserveEstimateNoSettings = (prefix, nextNumber) => {
const { setFieldValue } = useFormikContext();
React.useEffect(() => {
const estimateNo = transactionNumber(prefix, nextNumber);
setFieldValue('estimate_number', estimateNo);
}, [setFieldValue, prefix, nextNumber]);
}

View File

@@ -33,6 +33,7 @@ function InvoiceForm({
// #withSettings // #withSettings
invoiceNextNumber, invoiceNextNumber,
invoiceNumberPrefix, invoiceNumberPrefix,
invoiceIncrementMode,
}) { }) {
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
const history = useHistory(); const history = useHistory();
@@ -53,7 +54,6 @@ function InvoiceForm({
invoiceNumberPrefix, invoiceNumberPrefix,
invoiceNextNumber, invoiceNextNumber,
); );
// Form initial values. // Form initial values.
const initialValues = useMemo( const initialValues = useMemo(
() => ({ () => ({
@@ -61,12 +61,14 @@ function InvoiceForm({
? transformToEditForm(invoice) ? transformToEditForm(invoice)
: { : {
...defaultInvoice, ...defaultInvoice,
invoice_no: invoiceNumber, ...(invoiceIncrementMode) && ({
invoice_no: invoiceNumber,
}),
entries: orderingLinesIndexes(defaultInvoice.entries), entries: orderingLinesIndexes(defaultInvoice.entries),
...newInvoice, ...newInvoice,
}), }),
}), }),
[invoice, newInvoice,invoiceNumber], [invoice, newInvoice, invoiceNumber, invoiceIncrementMode],
); );
// Handles form submit. // Handles form submit.
@@ -88,7 +90,10 @@ function InvoiceForm({
return; return;
} }
const form = { const form = {
...values, ...omit(values, ['invoice_no', 'invoice_no_manually']),
...(values.invoice_no_manually) && ({
invoice_no: values.invoice_no,
}),
delivered: submitPayload.deliver, delivered: submitPayload.deliver,
from_estimate_id: estimateId, from_estimate_id: estimateId,
entries: entries.map((entry) => ({ ...omit(entry, ['total']) })), entries: entries.map((entry) => ({ ...omit(entry, ['total']) })),
@@ -127,7 +132,6 @@ function InvoiceForm({
} }
setSubmitting(false); setSubmitting(false);
}; };
if (!isEmpty(invoice)) { if (!isEmpty(invoice)) {
editInvoiceMutate([invoice.id, form]).then(onSuccess).catch(onError); editInvoiceMutate([invoice.id, form]).then(onSuccess).catch(onError);
} else { } else {
@@ -144,7 +148,6 @@ function InvoiceForm({
)} )}
> >
<Formik <Formik
enableReinitialize={true}
validationSchema={ validationSchema={
isNewMode ? CreateInvoiceFormSchema : EditInvoiceFormSchema isNewMode ? CreateInvoiceFormSchema : EditInvoiceFormSchema
} }
@@ -169,5 +172,6 @@ export default compose(
withSettings(({ invoiceSettings }) => ({ withSettings(({ invoiceSettings }) => ({
invoiceNextNumber: invoiceSettings?.nextNumber, invoiceNextNumber: invoiceSettings?.nextNumber,
invoiceNumberPrefix: invoiceSettings?.numberPrefix, invoiceNumberPrefix: invoiceSettings?.numberPrefix,
invoiceIncrementMode: invoiceSettings?.incrementMode,
})), })),
)(InvoiceForm); )(InvoiceForm);

View File

@@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import InvoiceNumberDialog from 'containers/Dialogs/InvoiceNumberDialog'; import InvoiceNumberDialog from 'containers/Dialogs/InvoiceNumberDialog';
import { useFormikContext } from 'formik'; import { useFormikContext } from 'formik';
import { transactionNumber } from 'utils';
/** /**
* Invoice form dialogs. * Invoice form dialogs.
@@ -10,15 +9,11 @@ export default function InvoiceFormDialogs() {
const { setFieldValue } = useFormikContext(); const { setFieldValue } = useFormikContext();
// Update the form once the invoice number form submit confirm. // Update the form once the invoice number form submit confirm.
const handleInvoiceNumberFormConfirm = (values) => { const handleInvoiceNumberFormConfirm = ({ incrementNumber, manually }) => {
debugger; setFieldValue('invoice_no', incrementNumber || '');
console.log(values, 'XX'); setFieldValue('invoice_no_manually', manually);
setFieldValue(
'invoice_no',
transactionNumber(values.number_prefix, values.next_number),
);
}; };
return ( return (
<> <>
<InvoiceNumberDialog <InvoiceNumberDialog

View File

@@ -6,10 +6,11 @@ import {
ControlGroup, ControlGroup,
} from '@blueprintjs/core'; } from '@blueprintjs/core';
import { DateInput } from '@blueprintjs/datetime'; import { DateInput } from '@blueprintjs/datetime';
import { FastField, ErrorMessage } from 'formik'; import { FastField, Field, ErrorMessage } from 'formik';
import { FormattedMessage as T } from 'react-intl'; import { FormattedMessage as T } from 'react-intl';
import { momentFormatter, compose, tansformDateValue } from 'utils'; import { momentFormatter, compose, tansformDateValue } from 'utils';
import classNames from 'classnames'; import classNames from 'classnames';
import { useObserveInvoiceNoSettings } from './utils';
import { CLASSES } from 'common/classes'; import { CLASSES } from 'common/classes';
import { import {
ContactSelecetList, ContactSelecetList,
@@ -17,8 +18,8 @@ import {
Icon, Icon,
InputPrependButton, InputPrependButton,
} from 'components'; } from 'components';
import { useInvoiceFormContext } from './InvoiceFormProvider'; import { useInvoiceFormContext } from './InvoiceFormProvider';
import withSettings from 'containers/Settings/withSettings';
import withDialogActions from 'containers/Dialog/withDialogActions'; import withDialogActions from 'containers/Dialog/withDialogActions';
import { inputIntent, handleDateChange } from 'utils'; import { inputIntent, handleDateChange } from 'utils';
@@ -28,15 +29,40 @@ import { inputIntent, handleDateChange } from 'utils';
function InvoiceFormHeaderFields({ function InvoiceFormHeaderFields({
// #withDialogActions // #withDialogActions
openDialog, openDialog,
// #withSettings
invoiceAutoIncrement,
invoiceNumberPrefix,
invoiceNextNumber,
}) { }) {
// Invoice form context. // Invoice form context.
const { customers } = useInvoiceFormContext(); const { customers } = useInvoiceFormContext();
// Handle invoice number changing. // Handle invoice number changing.
const handleInvoiceNumberChange = () => { const handleInvoiceNumberChange = () => {
openDialog('invoice-number-form', {}); openDialog('invoice-number-form');
}; };
// Handle invoice no. field blur.
const handleInvoiceNoBlur = (form, field) => (event) => {
const newValue = event.target.value;
if (field.value !== newValue && invoiceAutoIncrement) {
openDialog('invoice-number-form', {
initialFormValues: {
manualTransactionNo: newValue,
incrementMode: 'manual-transaction',
},
});
}
};
// Syncs invoice number settings with form.
useObserveInvoiceNoSettings(
invoiceNumberPrefix,
invoiceNextNumber,
);
return ( return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}> <div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ----------- Customer name ----------- */} {/* ----------- Customer name ----------- */}
@@ -45,7 +71,11 @@ function InvoiceFormHeaderFields({
<FormGroup <FormGroup
label={<T id={'customer_name'} />} label={<T id={'customer_name'} />}
inline={true} inline={true}
className={classNames('form-group--customer-name', 'form-group--select-list', CLASSES.FILL)} className={classNames(
'form-group--customer-name',
'form-group--select-list',
CLASSES.FILL,
)}
labelInfo={<FieldRequiredHint />} labelInfo={<FieldRequiredHint />}
intent={inputIntent({ error, touched })} intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'customer_id'} />} helperText={<ErrorMessage name={'customer_id'} />}
@@ -115,7 +145,7 @@ function InvoiceFormHeaderFields({
</FastField> </FastField>
{/* ----------- Invoice number ----------- */} {/* ----------- Invoice number ----------- */}
<FastField name={'invoice_no'}> <Field name={'invoice_no'}>
{({ form, field, meta: { error, touched } }) => ( {({ form, field, meta: { error, touched } }) => (
<FormGroup <FormGroup
label={<T id={'invoice_no'} />} label={<T id={'invoice_no'} />}
@@ -127,7 +157,9 @@ function InvoiceFormHeaderFields({
<ControlGroup fill={true}> <ControlGroup fill={true}>
<InputGroup <InputGroup
minimal={true} minimal={true}
{...field} value={field.value}
asyncControl={true}
onBlur={handleInvoiceNoBlur(form, field)}
/> />
<InputPrependButton <InputPrependButton
buttonProps={{ buttonProps={{
@@ -143,7 +175,7 @@ function InvoiceFormHeaderFields({
</ControlGroup> </ControlGroup>
</FormGroup> </FormGroup>
)} )}
</FastField> </Field>
{/* ----------- Reference ----------- */} {/* ----------- Reference ----------- */}
<FastField name={'reference'}> <FastField name={'reference'}>
@@ -165,4 +197,9 @@ function InvoiceFormHeaderFields({
export default compose( export default compose(
withDialogActions, withDialogActions,
withSettings(({ invoiceSettings }) => ({
invoiceAutoIncrement: invoiceSettings?.autoIncrement,
invoiceNextNumber: invoiceSettings?.nextNumber,
invoiceNumberPrefix: invoiceSettings?.numberPrefix,
})),
)(InvoiceFormHeaderFields); )(InvoiceFormHeaderFields);

View File

@@ -26,6 +26,7 @@ function InvoiceFormProvider({ invoiceId, ...props }) {
enabled: !!invoiceId, enabled: !!invoiceId,
}); });
// Fetches the estimate by the given id.
const { const {
data: estimate, data: estimate,
isFetching: isEstimateFetching, isFetching: isEstimateFetching,

View File

@@ -1,10 +1,19 @@
import React from 'react';
import moment from 'moment'; import moment from 'moment';
import { compose, transformToForm, repeatValue } from 'utils'; import { isEmpty } from 'lodash';
import {
compose,
transformToForm,
repeatValue,
transactionNumber,
} from 'utils';
import { updateItemsEntriesTotal } from 'containers/Entries/utils'; import { updateItemsEntriesTotal } from 'containers/Entries/utils';
import { ERROR } from 'common/errors'; import { useFormikContext } from 'formik';
import { Intent } from '@blueprintjs/core'; import { Intent } from '@blueprintjs/core';
import { orderingLinesIndexes } from 'utils';
import { formatMessage } from 'services/intl'; import { formatMessage } from 'services/intl';
import { ERROR } from 'common/errors';
import { AppToaster } from 'components'; import { AppToaster } from 'components';
export const MIN_LINES_NUMBER = 4; export const MIN_LINES_NUMBER = 4;
@@ -27,6 +36,7 @@ export const defaultInvoice = {
due_date: moment().format('YYYY-MM-DD'), due_date: moment().format('YYYY-MM-DD'),
delivered: '', delivered: '',
invoice_no: '', invoice_no: '',
invoice_no_manually: false,
reference_no: '', reference_no: '',
invoice_message: '', invoice_message: '',
terms_conditions: '', terms_conditions: '',
@@ -72,4 +82,24 @@ export const transformErrors = (errors, { setErrors }) => {
intent: Intent.DANGER, intent: Intent.DANGER,
}); });
} }
if (
errors.some((error) => error.type === ERROR.SALE_INVOICE_NO_IS_REQUIRED)
) {
setErrors({
invoice_no:
'Invoice number is required, use auto-increment mode or enter manually.',
});
}
}; };
/**
* Syncs invoice no. settings with form.
*/
export const useObserveInvoiceNoSettings = (prefix, nextNumber) => {
const { setFieldValue } = useFormikContext();
React.useEffect(() => {
const invoiceNo = transactionNumber(prefix, nextNumber);
setFieldValue('invoice_no', invoiceNo);
}, [setFieldValue, prefix, nextNumber]);
};

View File

@@ -186,7 +186,7 @@ export function useInvoicesTableColumns() {
{ {
id: 'invoice_no', id: 'invoice_no',
Header: formatMessage({ id: 'invoice_no__' }), Header: formatMessage({ id: 'invoice_no__' }),
accessor: (row) => (row.invoice_no ? `#${row.invoice_no}` : null), accessor: 'invoice_no',
width: 100, width: 100,
className: 'invoice_no', className: 'invoice_no',
}, },

View File

@@ -1,7 +1,7 @@
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import { Formik, Form } from 'formik'; import { Formik, Form } from 'formik';
import { useIntl } from 'react-intl'; import { useIntl } from 'react-intl';
import { sumBy, pick, isEmpty } from 'lodash'; import { omit, sumBy, pick, isEmpty } from 'lodash';
import { Intent } from '@blueprintjs/core'; import { Intent } from '@blueprintjs/core';
import classNames from 'classnames'; import classNames from 'classnames';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
@@ -23,13 +23,10 @@ import {
CreatePaymentReceiveFormSchema, CreatePaymentReceiveFormSchema,
} from './PaymentReceiveForm.schema'; } from './PaymentReceiveForm.schema';
import { AppToaster } from 'components'; import { AppToaster } from 'components';
import { compose } from 'utils'; import { transactionNumber, compose } from 'utils';
import { usePaymentReceiveFormContext } from './PaymentReceiveFormProvider'; import { usePaymentReceiveFormContext } from './PaymentReceiveFormProvider';
import { import { defaultPaymentReceive, transformToEditForm } from './utils';
defaultPaymentReceive,
transformToEditForm,
} from './utils';
/** /**
* Payment Receive form. * Payment Receive form.
@@ -38,6 +35,7 @@ function PaymentReceiveForm({
// #withSettings // #withSettings
paymentReceiveNextNumber, paymentReceiveNextNumber,
paymentReceiveNumberPrefix, paymentReceiveNumberPrefix,
paymentReceiveAutoIncrement,
}) { }) {
const history = useHistory(); const history = useHistory();
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
@@ -54,10 +52,10 @@ function PaymentReceiveForm({
} = usePaymentReceiveFormContext(); } = usePaymentReceiveFormContext();
// Payment receive number. // Payment receive number.
const paymentReceiveNumber = paymentReceiveNumberPrefix const nextPaymentNumber = transactionNumber(
? `${paymentReceiveNumberPrefix}-${paymentReceiveNextNumber}` paymentReceiveNumberPrefix,
: paymentReceiveNextNumber; paymentReceiveNextNumber
);
// Form initial values. // Form initial values.
const initialValues = useMemo( const initialValues = useMemo(
() => ({ () => ({
@@ -65,10 +63,17 @@ function PaymentReceiveForm({
? transformToEditForm(paymentReceiveEditPage, paymentEntriesEditPage) ? transformToEditForm(paymentReceiveEditPage, paymentEntriesEditPage)
: { : {
...defaultPaymentReceive, ...defaultPaymentReceive,
payment_receive_no: paymentReceiveNumber, ...(paymentReceiveAutoIncrement && {
payment_receive_no: nextPaymentNumber,
}),
}), }),
}), }),
[paymentReceiveEditPage, paymentReceiveNumber, paymentEntriesEditPage], [
paymentReceiveEditPage,
nextPaymentNumber,
paymentEntriesEditPage,
paymentReceiveAutoIncrement,
],
); );
// Handle form submit. // Handle form submit.
@@ -98,7 +103,13 @@ function PaymentReceiveForm({
setSubmitting(false); setSubmitting(false);
return; return;
} }
const form = { ...values, entries }; const form = {
...omit(values, ['payment_receive_no_manually', 'payment_receive_no']),
...(values.payment_receive_no_manually) && ({
payment_receive_no: values.payment_receive_no,
}),
entries
};
// Handle request response success. // Handle request response success.
const onSaved = (response) => { const onSaved = (response) => {
@@ -120,7 +131,11 @@ function PaymentReceiveForm({
} }
}; };
// Handle request response errors. // Handle request response errors.
const onError = ({ response: { data: { errors } } }) => { const onError = ({
response: {
data: { errors },
},
}) => {
const getError = (errorType) => errors.find((e) => e.type === errorType); const getError = (errorType) => errors.find((e) => e.type === errorType);
if (getError('PAYMENT_RECEIVE_NO_EXISTS')) { if (getError('PAYMENT_RECEIVE_NO_EXISTS')) {
@@ -179,5 +194,6 @@ export default compose(
withSettings(({ paymentReceiveSettings }) => ({ withSettings(({ paymentReceiveSettings }) => ({
paymentReceiveNextNumber: paymentReceiveSettings?.nextNumber, paymentReceiveNextNumber: paymentReceiveSettings?.nextNumber,
paymentReceiveNumberPrefix: paymentReceiveSettings?.numberPrefix, paymentReceiveNumberPrefix: paymentReceiveSettings?.numberPrefix,
paymentReceiveAutoIncrement: paymentReceiveSettings?.autoIncrement,
})), })),
)(PaymentReceiveForm); )(PaymentReceiveForm);

View File

@@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import { useFormikContext } from 'formik'; import { useFormikContext } from 'formik';
import PaymentReceiveNumberDialog from 'containers/Dialogs/PaymentReceiveNumberDialog'; import PaymentReceiveNumberDialog from 'containers/Dialogs/PaymentReceiveNumberDialog';
import { transactionNumber } from 'utils';
/** /**
* Payment receive form dialogs. * Payment receive form dialogs.
@@ -9,11 +8,9 @@ import { transactionNumber } from 'utils';
export default function PaymentReceiveFormDialogs() { export default function PaymentReceiveFormDialogs() {
const { setFieldValue } = useFormikContext(); const { setFieldValue } = useFormikContext();
const handleUpdatePaymentNumber = (values) => { const handleUpdatePaymentNumber = ({ incrementNumber, manually }) => {
setFieldValue( setFieldValue('payment_receive_no', incrementNumber);
'payment_receive_no', setFieldValue('payment_receive_no_manually', manually)
transactionNumber(values.number_prefix, values.next_number),
);
}; };
return ( return (

View File

@@ -1,7 +1,7 @@
import React, { createContext, useContext } from 'react'; import React, { createContext, useContext } from 'react';
import { DashboardInsider } from 'components'; import { DashboardInsider } from 'components';
import { import {
useSettings, useSettingsPaymentReceives,
usePaymentReceiveEditPage, usePaymentReceiveEditPage,
useAccounts, useAccounts,
useCustomers, useCustomers,
@@ -34,7 +34,7 @@ function PaymentReceiveFormProvider({ paymentReceiveId, ...props }) {
const { data: accounts, isFetching: isAccountsFetching } = useAccounts(); const { data: accounts, isFetching: isAccountsFetching } = useAccounts();
// Fetch payment made settings. // Fetch payment made settings.
const fetchSettings = useSettings(); const fetchSettings = useSettingsPaymentReceives();
// Fetches customers list. // Fetches customers list.
const { const {

View File

@@ -36,7 +36,11 @@ import { usePaymentReceiveFormContext } from './PaymentReceiveFormProvider';
import withDialogActions from 'containers/Dialog/withDialogActions'; import withDialogActions from 'containers/Dialog/withDialogActions';
import withSettings from 'containers/Settings/withSettings'; import withSettings from 'containers/Settings/withSettings';
import { amountPaymentEntries, fullAmountPaymentEntries } from './utils'; import {
useObservePaymentNoSettings,
amountPaymentEntries,
fullAmountPaymentEntries,
} from './utils';
import { toSafeInteger } from 'lodash'; import { toSafeInteger } from 'lodash';
/** /**
@@ -47,6 +51,11 @@ function PaymentReceiveHeaderFields({
// #withDialogActions // #withDialogActions
openDialog, openDialog,
// #withSettings
paymentReceiveAutoIncrement,
paymentReceiveNumberPrefix,
paymentReceiveNextNumber,
}) { }) {
// Payment receive form context. // Payment receive form context.
const { customers, accounts, isNewMode } = usePaymentReceiveFormContext(); const { customers, accounts, isNewMode } = usePaymentReceiveFormContext();
@@ -63,7 +72,6 @@ function PaymentReceiveHeaderFields({
const totalDueAmount = useMemo(() => safeSumBy(entries, 'due_amount'), [ const totalDueAmount = useMemo(() => safeSumBy(entries, 'due_amount'), [
entries, entries,
]); ]);
// Handle receive full-amount link click. // Handle receive full-amount link click.
const handleReceiveFullAmountClick = () => { const handleReceiveFullAmountClick = () => {
const newEntries = fullAmountPaymentEntries(entries); const newEntries = fullAmountPaymentEntries(entries);
@@ -72,18 +80,36 @@ function PaymentReceiveHeaderFields({
setFieldValue('entries', newEntries); setFieldValue('entries', newEntries);
setFieldValue('full_amount', fullAmount); setFieldValue('full_amount', fullAmount);
}; };
// Handles the full-amount field blur. // Handles the full-amount field blur.
const onFullAmountBlur = (value) => { const onFullAmountBlur = (value) => {
const newEntries = amountPaymentEntries(toSafeInteger(value), entries); const newEntries = amountPaymentEntries(toSafeInteger(value), entries);
setFieldValue('entries', newEntries); setFieldValue('entries', newEntries);
}; };
// Handle click open payment receive number dialog. // Handle click open payment receive number dialog.
const handleClickOpenDialog = () => { const handleClickOpenDialog = () => {
openDialog('payment-receive-number-form'); openDialog('payment-receive-number-form');
}; };
// Handle payment number field blur.
const handlePaymentNoBlur = (form, field) => (event) => {
const newValue = event.target.value;
if (field.value !== newValue && paymentReceiveAutoIncrement) {
openDialog('payment-receive-number-form', {
initialFormValues: {
manualTransactionNo: newValue,
incrementMode: 'manual-transaction',
},
});
}
};
// Syncs payment receive number from settings to the form.
useObservePaymentNoSettings(
paymentReceiveNumberPrefix,
paymentReceiveNextNumber,
);
return ( return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}> <div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ------------- Customer name ------------- */} {/* ------------- Customer name ------------- */}
@@ -194,7 +220,9 @@ function PaymentReceiveHeaderFields({
<InputGroup <InputGroup
intent={inputIntent({ error, touched })} intent={inputIntent({ error, touched })}
minimal={true} minimal={true}
{...field} value={field.value}
asyncControl={true}
onBlur={handlePaymentNoBlur(form, field)}
/> />
<InputPrependButton <InputPrependButton
buttonProps={{ buttonProps={{
@@ -263,8 +291,11 @@ function PaymentReceiveHeaderFields({
} }
export default compose( export default compose(
withSettings(({ organizationSettings }) => ({ withSettings(({ organizationSettings, paymentReceiveSettings }) => ({
baseCurrency: organizationSettings?.baseCurrency, baseCurrency: organizationSettings?.baseCurrency,
paymentReceiveNextNumber: paymentReceiveSettings?.nextNumber,
paymentReceiveNumberPrefix: paymentReceiveSettings?.numberPrefix,
paymentReceiveAutoIncrement: paymentReceiveSettings?.autoIncrement,
})), })),
withDialogActions, withDialogActions,
)(PaymentReceiveHeaderFields); )(PaymentReceiveHeaderFields);

View File

@@ -1,5 +1,7 @@
import React from 'react';
import { useFormikContext } from 'formik';
import moment from 'moment'; import moment from 'moment';
import { transformToForm, safeSumBy } from 'utils'; import { transactionNumber, transformToForm, safeSumBy } from 'utils';
// Default payment receive entry. // Default payment receive entry.
export const defaultPaymentReceiveEntry = { export const defaultPaymentReceiveEntry = {
@@ -18,7 +20,7 @@ export const defaultPaymentReceive = {
payment_date: moment(new Date()).format('YYYY-MM-DD'), payment_date: moment(new Date()).format('YYYY-MM-DD'),
reference_no: '', reference_no: '',
payment_receive_no: '', payment_receive_no: '',
description: '', statement: '',
full_amount: '', full_amount: '',
entries: [], entries: [],
}; };
@@ -81,4 +83,16 @@ export const fullAmountPaymentEntries = (entries) => {
...item, ...item,
payment_amount: item.due_amount, payment_amount: item.due_amount,
})); }));
} }
/**
* Syncs payment receive number settings with form.
*/
export const useObservePaymentNoSettings = (prefix, nextNumber) => {
const { setFieldValue } = useFormikContext();
React.useEffect(() => {
const invoiceNo = transactionNumber(prefix, nextNumber);
setFieldValue('payment_receive_no', invoiceNo);
}, [setFieldValue, prefix, nextNumber]);
};

View File

@@ -10,7 +10,7 @@ import { CLASSES } from 'common/classes';
import { ERROR } from 'common/errors'; import { ERROR } from 'common/errors';
import { import {
EditReceiptFormSchema, EditReceiptFormSchema,
CreateReceiptFormSchema, CreateReceiptFormSchema,
} from './ReceiptForm.schema'; } from './ReceiptForm.schema';
import { useReceiptFormContext } from './ReceiptFormProvider'; import { useReceiptFormContext } from './ReceiptFormProvider';
@@ -25,13 +25,8 @@ import withDashboardActions from 'containers/Dashboard/withDashboardActions';
import withSettings from 'containers/Settings/withSettings'; import withSettings from 'containers/Settings/withSettings';
import { AppToaster } from 'components'; import { AppToaster } from 'components';
import { import { compose, orderingLinesIndexes, transactionNumber } from 'utils';
compose, import { transformToEditForm, defaultReceipt } from './utils';
orderingLinesIndexes,
transactionNumber,
} from 'utils';
import { transformToEditForm, defaultReceipt } from './utils'
/** /**
* Receipt form. * Receipt form.
@@ -40,6 +35,7 @@ function ReceiptForm({
// #withSettings // #withSettings
receiptNextNumber, receiptNextNumber,
receiptNumberPrefix, receiptNumberPrefix,
receiptAutoIncrement,
preferredDepositAccount, preferredDepositAccount,
}) { }) {
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
@@ -47,16 +43,15 @@ function ReceiptForm({
// Receipt form context. // Receipt form context.
const { const {
receiptId,
receipt, receipt,
editReceiptMutate, editReceiptMutate,
createReceiptMutate, createReceiptMutate,
submitPayload, submitPayload,
isNewMode isNewMode,
} = useReceiptFormContext(); } = useReceiptFormContext();
// The next receipt number. // The next receipt number.
const receiptNumber = transactionNumber( const nextReceiptNumber = transactionNumber(
receiptNumberPrefix, receiptNumberPrefix,
receiptNextNumber, receiptNextNumber,
); );
@@ -67,12 +62,14 @@ function ReceiptForm({
? transformToEditForm(receipt) ? transformToEditForm(receipt)
: { : {
...defaultReceipt, ...defaultReceipt,
receipt_number: receiptNumber, ...(receiptAutoIncrement && {
receipt_number: nextReceiptNumber,
}),
deposit_account_id: parseInt(preferredDepositAccount), deposit_account_id: parseInt(preferredDepositAccount),
entries: orderingLinesIndexes(defaultReceipt.entries), entries: orderingLinesIndexes(defaultReceipt.entries),
}), }),
}), }),
[receipt, preferredDepositAccount, receiptNumber], [receipt, preferredDepositAccount, nextReceiptNumber, receiptAutoIncrement],
); );
// Transform response error to fields. // Transform response error to fields.
@@ -107,9 +104,12 @@ function ReceiptForm({
return; return;
} }
const form = { const form = {
...values, ...omit(values, ['receipt_number_manually', 'receipt_number']),
...(values.receipt_number_manually) && ({
receipt_number: values.receipt_number,
}),
closed: submitPayload.status, closed: submitPayload.status,
entries: entries.map((entry) => ({ ...omit(entry, ['total']), })), entries: entries.map((entry) => ({ ...omit(entry, ['total']) })),
}; };
// Handle the request success. // Handle the request success.
const onSuccess = (response) => { const onSuccess = (response) => {
@@ -135,13 +135,16 @@ function ReceiptForm({
}; };
// Handle the request error. // Handle the request error.
const onError = ({response:{data:{errors}}}) => { const onError = ({
if(errors){ response: {
data: { errors },
},
}) => {
if (errors) {
handleErrors(errors, { setErrors }); handleErrors(errors, { setErrors });
} }
setSubmitting(false); setSubmitting(false);
}; };
if (!isNewMode) { if (!isNewMode) {
editReceiptMutate([receipt.id, form]).then(onSuccess).catch(onError); editReceiptMutate([receipt.id, form]).then(onSuccess).catch(onError);
} else { } else {
@@ -182,6 +185,7 @@ export default compose(
withSettings(({ receiptSettings }) => ({ withSettings(({ receiptSettings }) => ({
receiptNextNumber: receiptSettings?.nextNumber, receiptNextNumber: receiptSettings?.nextNumber,
receiptNumberPrefix: receiptSettings?.numberPrefix, receiptNumberPrefix: receiptSettings?.numberPrefix,
receiptAutoIncrement: receiptSettings?.autoIncrement,
preferredDepositAccount: receiptSettings?.preferredDepositAccount, preferredDepositAccount: receiptSettings?.preferredDepositAccount,
})), })),
)(ReceiptForm); )(ReceiptForm);

View File

@@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import { useFormikContext } from 'formik'; import { useFormikContext } from 'formik';
import ReceiptNumberDialog from 'containers/Dialogs/ReceiptNumberDialog'; import ReceiptNumberDialog from 'containers/Dialogs/ReceiptNumberDialog';
import { transactionNumber } from 'utils';
/** /**
* Receipt form dialogs. * Receipt form dialogs.
@@ -10,11 +9,9 @@ export default function ReceiptFormDialogs() {
const { setFieldValue } = useFormikContext(); const { setFieldValue } = useFormikContext();
// Update the form once the receipt number form submit confirm. // Update the form once the receipt number form submit confirm.
const handleReceiptNumberFormConfirm = (values) => { const handleReceiptNumberFormConfirm = ({ incrementNumber, manually }) => {
setFieldValue( setFieldValue('receipt_number', incrementNumber || '');
'receipt_number', setFieldValue('receipt_number_manually', manually);
transactionNumber(values.number_prefix, values.next_number),
);
}; };
return ( return (

View File

@@ -5,12 +5,10 @@ import {
Position, Position,
ControlGroup, ControlGroup,
} from '@blueprintjs/core'; } from '@blueprintjs/core';
import { DateInput } from '@blueprintjs/datetime'; import { DateInput } from '@blueprintjs/datetime';
import { FormattedMessage as T } from 'react-intl'; import { FormattedMessage as T } from 'react-intl';
import classNames from 'classnames'; import classNames from 'classnames';
import { FastField, ErrorMessage } from 'formik'; import { FastField, ErrorMessage } from 'formik';
import { CLASSES } from 'common/classes'; import { CLASSES } from 'common/classes';
import { import {
AccountsSelectList, AccountsSelectList,
@@ -19,17 +17,17 @@ import {
Icon, Icon,
InputPrependButton, InputPrependButton,
} from 'components'; } from 'components';
import withSettings from 'containers/Settings/withSettings';
import withDialogActions from 'containers/Dialog/withDialogActions'; import withDialogActions from 'containers/Dialog/withDialogActions';
import { import {
momentFormatter, momentFormatter,
compose, compose,
tansformDateValue, tansformDateValue,
saveInvoke,
handleDateChange, handleDateChange,
inputIntent, inputIntent,
} from 'utils'; } from 'utils';
import { useReceiptFormContext } from './ReceiptFormProvider'; import { useReceiptFormContext } from './ReceiptFormProvider';
import { useObserveReceiptNoSettings } from './utils';
/** /**
* Receipt form header fields. * Receipt form header fields.
@@ -40,6 +38,11 @@ function ReceiptFormHeader({
// #ownProps // #ownProps
onReceiptNumberChanged, onReceiptNumberChanged,
// #withSettings
receiptAutoIncrement,
receiptNextNumber,
receiptNumberPrefix,
}) { }) {
const { accounts, customers } = useReceiptFormContext(); const { accounts, customers } = useReceiptFormContext();
@@ -47,10 +50,25 @@ function ReceiptFormHeader({
openDialog('receipt-number-form', {}); openDialog('receipt-number-form', {});
}, [openDialog]); }, [openDialog]);
const handleReceiptNumberChanged = (event) => { const handleReceiptNoBlur = (form, field) => (event) => {
saveInvoke(onReceiptNumberChanged, event.currentTarget.value); const newValue = event.target.value;
if (field.value !== newValue && receiptAutoIncrement) {
openDialog('receipt-number-form', {
initialFormValues: {
manualTransactionNo: newValue,
incrementMode: 'manual-transaction',
},
});
}
}; };
// Synsc receipt number settings with the form.
useObserveReceiptNoSettings(
receiptNumberPrefix,
receiptNextNumber,
);
return ( return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}> <div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ----------- Customer name ----------- */} {/* ----------- Customer name ----------- */}
@@ -129,7 +147,7 @@ function ReceiptFormHeader({
{/* ----------- Receipt number ----------- */} {/* ----------- Receipt number ----------- */}
<FastField name={'receipt_number'}> <FastField name={'receipt_number'}>
{({ field, meta: { error, touched } }) => ( {({ form, field, meta: { error, touched } }) => (
<FormGroup <FormGroup
label={<T id={'receipt'} />} label={<T id={'receipt'} />}
inline={true} inline={true}
@@ -141,8 +159,9 @@ function ReceiptFormHeader({
<ControlGroup fill={true}> <ControlGroup fill={true}>
<InputGroup <InputGroup
minimal={true} minimal={true}
{...field} value={field.value}
onBlur={handleReceiptNumberChanged} asyncControl={true}
onBlur={handleReceiptNoBlur(form, field)}
/> />
<InputPrependButton <InputPrependButton
buttonProps={{ buttonProps={{
@@ -183,4 +202,9 @@ function ReceiptFormHeader({
export default compose( export default compose(
withDialogActions, withDialogActions,
withSettings(({ receiptSettings }) => ({
receiptAutoIncrement: receiptSettings?.autoIncrement,
receiptNextNumber: receiptSettings?.nextNumber,
receiptNumberPrefix: receiptSettings?.numberPrefix,
})),
)(ReceiptFormHeader); )(ReceiptFormHeader);

View File

@@ -3,7 +3,7 @@ import DashboardInsider from 'components/Dashboard/DashboardInsider';
import { import {
useReceipt, useReceipt,
useAccounts, useAccounts,
useSettings, useSettingsReceipts,
useCustomers, useCustomers,
useItems, useItems,
useCreateReceipt, useCreateReceipt,
@@ -39,7 +39,7 @@ function ReceiptFormProvider({ receiptId, ...props }) {
} = useItems({ page_size: 10000 }); } = useItems({ page_size: 10000 });
// Fetch receipt settings. // Fetch receipt settings.
const { isLoading: isSettingLoading } = useSettings(); const { isLoading: isSettingLoading } = useSettingsReceipts();
const { mutateAsync: createReceiptMutate } = useCreateReceipt(); const { mutateAsync: createReceiptMutate } = useCreateReceipt();
const { mutateAsync: editReceiptMutate } = useEditReceipt(); const { mutateAsync: editReceiptMutate } = useEditReceipt();

View File

@@ -1,5 +1,7 @@
import React from 'react';
import { useFormikContext } from 'formik';
import moment from 'moment'; import moment from 'moment';
import { repeatValue, transformToForm } from 'utils'; import { transactionNumber, repeatValue, transformToForm } from 'utils';
export const MIN_LINES_NUMBER = 4; export const MIN_LINES_NUMBER = 4;
@@ -39,3 +41,13 @@ export const transformToEditForm = (receipt) => ({
), ),
], ],
}); });
export const useObserveReceiptNoSettings = (prefix, nextNumber) => {
const { setFieldValue } = useFormikContext();
React.useEffect(() => {
const receiptNo = transactionNumber(prefix, nextNumber);
setFieldValue('receipt_number', receiptNo);
}, [setFieldValue, prefix, nextNumber]);
}

View File

@@ -114,8 +114,7 @@ export function useReceiptsTableColumns() {
{ {
id: 'receipt_number', id: 'receipt_number',
Header: formatMessage({ id: 'receipt_number' }), Header: formatMessage({ id: 'receipt_number' }),
accessor: (row) => accessor: 'receipt_number',
row.receipt_number ? `#${row.receipt_number}` : null,
width: 140, width: 140,
className: 'receipt_number', className: 'receipt_number',
}, },
@@ -138,7 +137,7 @@ export function useReceiptsTableColumns() {
Header: formatMessage({ id: 'status' }), Header: formatMessage({ id: 'status' }),
accessor: StatusAccessor, accessor: StatusAccessor,
width: 140, width: 140,
className: 'amount', className: 'status',
}, },
{ {
id: 'reference_no', id: 'reference_no',

View File

@@ -51,6 +51,7 @@ export function useCreatePaymentReceive(props) {
client.invalidateQueries('SALE_INVOICE_DUE'); client.invalidateQueries('SALE_INVOICE_DUE');
client.invalidateQueries('SALE_INVOICES'); client.invalidateQueries('SALE_INVOICES');
client.invalidateQueries('SALE_INVOICE'); client.invalidateQueries('SALE_INVOICE');
client.invalidateQueries(['SETTINGS', 'PAYMENT_RECEIVES']);
saveInvoke(props?.onSuccess, data); saveInvoke(props?.onSuccess, data);
}, },
@@ -74,6 +75,7 @@ export function useEditPaymentReceive(props) {
client.invalidateQueries('SALE_INVOICE_DUE'); client.invalidateQueries('SALE_INVOICE_DUE');
client.invalidateQueries('SALE_INVOICES'); client.invalidateQueries('SALE_INVOICES');
client.invalidateQueries('SALE_INVOICE'); client.invalidateQueries('SALE_INVOICE');
client.invalidateQueries(['SETTINGS', 'PAYMENT_RECEIVES']);
saveInvoke(props?.onSuccess, data); saveInvoke(props?.onSuccess, data);
}, },

View File

@@ -13,6 +13,7 @@ export function useCreateReceipt(props) {
return useMutation((values) => apiRequest.post('sales/receipts', values), { return useMutation((values) => apiRequest.post('sales/receipts', values), {
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries('SALE_RECEIPTS'); queryClient.invalidateQueries('SALE_RECEIPTS');
queryClient.invalidateQueries(['SETTINGS', 'RECEIPTS']);
}, },
...props, ...props,
}); });
@@ -30,6 +31,7 @@ export function useEditReceipt(props) {
{ {
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries('SALE_RECEIPTS'); queryClient.invalidateQueries('SALE_RECEIPTS');
queryClient.invalidateQueries(['SETTINGS', 'RECEIPTS']);
}, },
...props, ...props,
}, },

View File

@@ -159,6 +159,13 @@ export default {
], ],
viewBox: '0 0 309.09 42.89', viewBox: '0 0 309.09 42.89',
}, },
"mini-bigcapital": {
path: [
'M56,3.16,61.33,8.5,31.94,37.9l-5.35-5.35Z',
'M29.53,6.94l5.35,5.34L5.49,41.67.14,36.33l15.8-15.8Z',
],
viewBox: '0 0 309.09 42.89',
},
eye: { eye: {
path: [ path: [
'M288 144a110.94 110.94 0 0 0-31.24 5 55.4 55.4 0 0 1 7.24 27 56 56 0 0 1-56 56 55.4 55.4 0 0 1-27-7.24A111.71 111.71 0 1 0 288 144zm284.52 97.4C518.29 135.59 410.93 64 288 64S57.68 135.64 3.48 241.41a32.35 32.35 0 0 0 0 29.19C57.71 376.41 165.07 448 288 448s230.32-71.64 284.52-177.41a32.35 32.35 0 0 0 0-29.19zM288 400c-98.65 0-189.09-55-237.93-144C98.91 167 189.34 112 288 112s189.09 55 237.93 144C477.1 345 386.66 400 288 400z', 'M288 144a110.94 110.94 0 0 0-31.24 5 55.4 55.4 0 0 1 7.24 27 56 56 0 0 1-56 56 55.4 55.4 0 0 1-27-7.24A111.71 111.71 0 1 0 288 144zm284.52 97.4C518.29 135.59 410.93 64 288 64S57.68 135.64 3.48 241.41a32.35 32.35 0 0 0 0 29.19C57.71 376.41 165.07 448 288 448s230.32-71.64 284.52-177.41a32.35 32.35 0 0 0 0-29.19zM288 400c-98.65 0-189.09-55-237.93-144C98.91 167 189.34 112 288 112s189.09 55 237.93 144C477.1 345 386.66 400 288 400z',
@@ -412,4 +419,10 @@ export default {
], ],
viewBox: '0 0 16 16', viewBox: '0 0 16 16',
}, },
'caret-down-16': {
path: [
'M12 6.5c0-.28-.22-.5-.5-.5h-7a.495.495 0 00-.37.83l3.5 4c.09.1.22.17.37.17s.28-.07.37-.17l3.5-4c.08-.09.13-.2.13-.33z',
],
viewBox: '0 0 16 16',
}
}; };

View File

@@ -92,11 +92,11 @@ body.hide-scrollbar .Pane2{
} }
} }
.bp3-progress-bar.bp3-intent-primary .bp3-progress-meter{ .bp3-progress-bar.bp3-intent-primary .bp3-progress-meter{
background-color: #0066ff; background-color: #0066ff;
} }
.bp3-overlay-backdrop{ .bp3-overlay-backdrop{
background-color: rgba(0,10,30, .7); background-color: rgba(0,10,30, .7);
} }

View File

@@ -161,7 +161,8 @@
// .total, // .total,
.quantity, .quantity,
.rate, .rate,
.discount{ .discount,
.total{
&, &,
input{ input{
@@ -216,7 +217,6 @@
border-top-color: #e9e9ef; border-top-color: #e9e9ef;
border-top-style: solid; border-top-style: solid;
min-height: 40px; min-height: 40px;
font-weight: 500; font-weight: 500;

View File

@@ -8,12 +8,15 @@
z-index: $sidebar-zindex; z-index: $sidebar-zindex;
.ScrollbarsCustom-Track { .ScrollbarsCustom-Track {
&.ScrollbarsCustom-TrackY, &.ScrollbarsCustom-TrackY,
&.ScrollbarsCustom-TrackX { &.ScrollbarsCustom-TrackX {
background: rgba(0, 0, 0, 0); background: rgba(0, 0, 0, 0);
} }
} }
.ScrollbarsCustom-Thumb { .ScrollbarsCustom-Thumb {
&.ScrollbarsCustom-ThumbX, &.ScrollbarsCustom-ThumbX,
&.ScrollbarsCustom-ThumbY { &.ScrollbarsCustom-ThumbY {
background: rgba(0, 0, 0, 0); background: rgba(0, 0, 0, 0);
@@ -22,27 +25,69 @@
&:hover { &:hover {
.ScrollbarsCustom-Thumb { .ScrollbarsCustom-Thumb {
&.ScrollbarsCustom-ThumbX, &.ScrollbarsCustom-ThumbX,
&.ScrollbarsCustom-ThumbY { &.ScrollbarsCustom-ThumbY {
background: rgba(255, 255, 255, 0.25); background: rgba(255, 255, 255, 0.25);
} }
} }
} }
&__head { &__head {
padding: 18px 20px; padding: 20px 20px;
&-logo { &-logo {
position: relative; position: absolute;
top: 2px; top: 16px;
left: 12px;
opacity: 0;
visibility: hidden;
svg { svg{
opacity: $sidebar-logo-opacity; opacity: $sidebar-logo-opacity;
} }
} }
.sidebar__head-logo{
&-organization {
.title {
background: transparent;
padding: 0;
min-height: auto;
min-width: auto;
font-weight: 600;
outline: 0;
font-size: 15px;
&:not([class*="bp3-intent-"]):not(.bp3-minimal) {
color: rgba(255, 255, 255, 0.8);
}
&:hover,
&:focus,
&:active,
&.bp3-active {
background: transparent;
}
.bp3-button-text {
margin-right: 4px;
}
svg {
fill: rgba(255, 255, 255, 0.3);
}
}
.subtitle {
display: block;
font-size: 12px;
margin-top: 6px;
color: rgba(255, 255, 255, 0.6);
}
}
.sidebar__head-logo {
transition: transform 0.05s ease-in-out; transition: transform 0.05s ease-in-out;
} }
} }
&__scroll-wrapper { &__scroll-wrapper {
height: 100%; height: 100%;
} }
@@ -66,20 +111,24 @@
background: $sidebar-submenu-item-bg-color; background: $sidebar-submenu-item-bg-color;
color: $sidebar-menu-item-color-active; color: $sidebar-menu-item-color-active;
} }
&:focus, &:focus,
&:active { &:active {
background: #01143e; background: #01143e;
} }
> .#{$ns}-icon {
>.#{$ns}-icon {
color: #767b9b; color: #767b9b;
margin-right: 16px; margin-right: 16px;
margin-top: 0; margin-top: 0;
} }
> .#{$ns}-icon-caret-right {
>.#{$ns}-icon-caret-right {
margin-right: -4px; margin-right: -4px;
margin-top: 3px; margin-top: 3px;
color: rgba(255, 255, 255, 0.25); color: rgba(255, 255, 255, 0.25);
} }
&-labeler { &-labeler {
display: block; display: block;
color: $sidebar-menu-label-color; color: $sidebar-menu-label-color;
@@ -113,11 +162,13 @@
background: transparent; background: transparent;
color: $sidebar-submenu-item-hover-color; color: $sidebar-submenu-item-hover-color;
} }
&.bp3-active { &.bp3-active {
font-weight: 500; font-weight: 500;
} }
} }
} }
.#{$ns}-popover { .#{$ns}-popover {
padding: 0; padding: 0;
@@ -126,14 +177,17 @@
} }
} }
} }
.#{$ns}-popover-target.#{$ns}-popover-open .#{$ns}-menu-item { .#{$ns}-popover-target.#{$ns}-popover-open .#{$ns}-menu-item {
color: $sidebar-menu-item-color; color: $sidebar-menu-item-color;
} }
.#{$ns}-menu-divider { .#{$ns}-menu-divider {
border-top-color: rgba(255, 255, 255, 0.1); border-top-color: rgba(255, 255, 255, 0.1);
color: #6b708c; color: #6b708c;
margin: 4px 0; margin: 4px 0;
} }
.#{$ns}-menu-spacer { .#{$ns}-menu-spacer {
margin: 4px 0; margin: 4px 0;
height: 1px; height: 1px;
@@ -145,20 +199,20 @@
white-space: nowrap; white-space: nowrap;
width: 50px; width: 50px;
.sidebar__head{ .sidebar__head {
.sidebar__head-logo{ .sidebar__head-logo {
transform: translate(-8px, 0); transition: opacity 0.3s ease-in-out;
transition-delay: 0.15s;
opacity: 1;
visibility: visible;
} }
} // Hide text of bigcapital logo.
// Hide text of bigcapital logo. &-logo {
.sidebar__head-logo {
.bp3-icon-bigcapital { }
path { &-organization{
transition: opacity 0.3s ease-in-out; opacity: 0;
} transition: opacity 0.3s ease-in-out;
path:not(.path-1):not(.path-2) {
opacity: 0;
}
} }
} }
@@ -175,15 +229,14 @@
&:hover { &:hover {
min-width: 220px; min-width: 220px;
.sidebar__head-logo { .sidebar__head-logo {
transform: translate(0px, 0); opacity: 0;
transition-delay: 0s;
.bp3-icon-bigcapital {
path:not(.path-1):not(.path-2) {
opacity: 1;
}
}
} }
.sidebar__head-organization{
opacity: 1;
}
.sidebar__menu { .sidebar__menu {
opacity: 1; opacity: 1;
} }
@@ -205,6 +258,7 @@
.bp3-icon { .bp3-icon {
color: rgba(255, 255, 255, 0.4); color: rgba(255, 255, 255, 0.4);
} }
&, &,
&:hover { &:hover {
min-height: auto; min-height: auto;
@@ -221,9 +275,34 @@
} }
} }
} }
.bp3-icon { .bp3-icon {
margin: 0; margin: 0;
display: block; display: block;
} }
} }
} }
.menu--dashboard-organization{
padding: 10px;
.org-item {
display: flex;
align-items: center;
&__logo {
height: 40px;
width: 40px;
line-height: 40px;
border-radius: 3px;
background-color: #CB22E5;
text-align: center;
font-weight: 400;
font-size: 16px;
color: #fff;
}
&__name {
margin-left: 12px;
font-weight: 600;
}
}
}

View File

@@ -1,9 +1,13 @@
.dialog--contact-duplicate { .dialog--contact-duplicate {
.bp3-dialog-body { .bp3-dialog-body {
.bp3-form-group.bp3-inline { > .paragraph {
margin: 18px 0px; margin-bottom: 1rem;
}
.bp3-form-group {
margin-bottom: 1.4rem;
.bp3-label { .bp3-label {
min-width: 100px; font-size: 13px;
} }
} }
.bp3-button:not([class*='bp3-intent-']):not(.bp3-minimal) { .bp3-button:not([class*='bp3-intent-']):not(.bp3-minimal) {

View File

@@ -0,0 +1,19 @@
.reference-number-form {
.bp3-dialog-body {
.paragraph {
margin-bottom: 22px;
}
.bp3-control {
margin-bottom: 16px;
}
}
.bp3-dialog-footer {
.bp3-button[type='submit'] {
min-width: 80px;
}
}
}

View File

@@ -27,7 +27,7 @@
line-height: 1.4; line-height: 1.4;
+ .partial-paid{ + .partial-paid{
margin-top: 5px; margin-top: 2px;
} }
} }
.overdue-status{ .overdue-status{
@@ -40,7 +40,7 @@
font-size: 12px; font-size: 12px;
line-height: 1; line-height: 1;
display: block; display: block;
margin-bottom: 8px; margin-bottom: 6px;
opacity: 0.7; opacity: 0.7;
} }
.fully-paid-icon{ .fully-paid-icon{

View File

@@ -22,17 +22,16 @@ body.page-invoice-edit{
} }
.bp3-label { .bp3-label {
min-width: 140px; min-width: 150px;
} }
.bp3-form-content { .bp3-form-content {
width: 100%; width: 100%;
} }
.bp3-form-group { .bp3-form-group {
&.bp3-inline { &.bp3-inline {
max-width: 440px; max-width: 450px;
} }
} }
.col--invoice-date { .col--invoice-date {

View File

@@ -22,7 +22,7 @@ body.page-receipt-edit{
flex: 1 0 0; flex: 1 0 0;
} }
.bp3-label{ .bp3-label{
min-width: 140px; min-width: 150px;
} }
.bp3-form-content{ .bp3-form-content{
width: 100%; width: 100%;

View File

@@ -472,7 +472,7 @@ export function transactionNumber(prefix, number) {
if (number) { if (number) {
codes.push(number); codes.push(number);
} }
return codes.join('-'); return codes.join('');
} }
export function safeCallback(callback, ...args) { export function safeCallback(callback, ...args) {

View File

@@ -107,7 +107,7 @@ export default class BillsPayments extends BaseController {
check('payment_account_id').exists().isNumeric().toInt(), check('payment_account_id').exists().isNumeric().toInt(),
check('payment_number').optional({ nullable: true }).trim().escape(), check('payment_number').optional({ nullable: true }).trim().escape(),
check('payment_date').exists(), check('payment_date').exists(),
check('description').optional().trim().escape(), check('statement').optional().trim().escape(),
check('reference').optional().trim().escape(), check('reference').optional().trim().escape(),
check('entries').exists().isArray({ min: 1 }), check('entries').exists().isArray({ min: 1 }),

View File

@@ -52,16 +52,10 @@ export default {
key: "number_prefix", key: "number_prefix",
type: "string", type: "string",
}, },
],
bills: [
{ {
key: "next_number", key: "auto_increment",
type: "number", type: "boolean",
}, }
{
key: "number_prefix",
type: "string",
},
], ],
bill_payments: [ bill_payments: [
{ {
@@ -82,6 +76,10 @@ export default {
key: "number_prefix", key: "number_prefix",
type: "string", type: "string",
}, },
{
key: "auto_increment",
type: "boolean",
}
], ],
sales_receipts: [ sales_receipts: [
{ {
@@ -92,6 +90,10 @@ export default {
key: "number_prefix", key: "number_prefix",
type: "string", type: "string",
}, },
{
key: "auto_increment",
type: "boolean",
},
{ {
key: "preferred_deposit_account", key: "preferred_deposit_account",
type: "number", type: "number",
@@ -107,8 +109,8 @@ export default {
type: "string", type: "string",
}, },
{ {
key: 'increment_mode', key: "auto_increment",
type: 'string' type: "boolean",
} }
], ],
payment_receives: [ payment_receives: [
@@ -120,6 +122,10 @@ export default {
key: "number_prefix", key: "number_prefix",
type: "string", type: "string",
}, },
{
key: "auto_increment",
type: "boolean",
}
], ],
items: [ items: [
{ {

View File

@@ -10,7 +10,7 @@ exports.up = function(knex) {
table.string('reference_no').index(); table.string('reference_no').index();
table.integer('deposit_account_id').unsigned().references('id').inTable('accounts'); table.integer('deposit_account_id').unsigned().references('id').inTable('accounts');
table.string('payment_receive_no').nullable(); table.string('payment_receive_no').nullable();
table.text('description'); table.text('statement');
table.integer('user_id').unsigned().index(); table.integer('user_id').unsigned().index();
table.timestamps(); table.timestamps();
}); });

View File

@@ -11,7 +11,7 @@ exports.up = function(knex) {
table.string('payment_method'); table.string('payment_method');
table.string('reference'); table.string('reference');
table.integer('user_id').unsigned().index(); table.integer('user_id').unsigned().index();
table.text('description'); table.text('statement');
table.timestamps(); table.timestamps();
}); });
}; };

View File

@@ -17,6 +17,7 @@ export interface IBillPayment {
paymentDate: Date, paymentDate: Date,
userId: number, userId: number,
entries: IBillPaymentEntry[], entries: IBillPaymentEntry[],
statement: string,
} }
export interface IBillPaymentEntryDTO { export interface IBillPaymentEntryDTO {
@@ -29,7 +30,7 @@ export interface IBillPaymentDTO {
paymentAccountId: number, paymentAccountId: number,
paymentNumber?: string, paymentNumber?: string,
paymentDate: Date, paymentDate: Date,
description: string, statement: string,
reference: string, reference: string,
entries: IBillPaymentEntryDTO[], entries: IBillPaymentEntryDTO[],
}; };

View File

@@ -9,7 +9,7 @@ export interface IPaymentReceive {
referenceNo: string, referenceNo: string,
depositAccountId: number, depositAccountId: number,
paymentReceiveNo: string, paymentReceiveNo: string,
description: string, statement: string,
entries: IPaymentReceiveEntry[], entries: IPaymentReceiveEntry[],
userId: number, userId: number,
}; };
@@ -20,7 +20,7 @@ export interface IPaymentReceiveCreateDTO {
referenceNo: string, referenceNo: string,
depositAccountId: number, depositAccountId: number,
paymentReceiveNo?: string, paymentReceiveNo?: string,
description: string, statement: string,
entries: IPaymentReceiveEntryDTO[], entries: IPaymentReceiveEntryDTO[],
}; };
@@ -31,7 +31,7 @@ export interface IPaymentReceiveEditDTO {
referenceNo: string, referenceNo: string,
depositAccountId: number, depositAccountId: number,
paymentReceiveNo?: string, paymentReceiveNo?: string,
description: string, statement: string,
entries: IPaymentReceiveEntryDTO[], entries: IPaymentReceiveEntryDTO[],
}; };