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
BILL_NUMBER_EXISTS: 'BILL.NUMBER.EXISTS',
SALE_INVOICE_NO_IS_REQUIRED: 'SALE_INVOICE_NO_IS_REQUIRED'
};

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,12 +1,66 @@
import React from 'react';
import appMeta from 'config/app';
import { Button, Popover, Menu, Position } from '@blueprintjs/core';
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 (
<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">
<Icon icon={'bigcapital'} width={140} height={28} className="bigcapital--alt" />
<Icon
icon={'mini-bigcapital'}
width={140}
height={28}
className="bigcapital--alt"
/>
</div>
</div>
);
};
}
export default compose(
withSettings(({ organizationSettings }) => ({
organizationName: organizationSettings.name,
})),
)(SidebarHead);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,71 +1,103 @@
import React from 'react';
import { FastField } from 'formik';
import { FastField, useFormikContext } from 'formik';
import { FormattedMessage as T } from 'react-intl';
import { FormGroup, InputGroup, Radio } from '@blueprintjs/core';
import { Row, Col, ErrorMessage } from 'components';
import { If, Row, Col, ErrorMessage } from 'components';
import { inputIntent } from 'utils';
/**
* Reference number form content.
*/
export default function ReferenceNumberFormContent() {
const { values } = useFormikContext();
return (
<>
<FastField name={'mode'}>
{/* ------------- Auto increment mode ------------- */}
<FastField name={'incrementMode'}>
{({ form, field, meta: { error, touched } }) => (
<Radio
label="Auto-incrementing invoice number."
value="auto-increment"
{...field}
onChange={() => {
form.setFieldValue('incrementMode', 'auto');
}}
checked={field.value === 'auto'}
/>
)}
</FastField>
<Row>
{/* ------------- Prefix ------------- */}
<Col xs={6}>
<FastField name={'prefix'}>
{({ form, field, meta: { error, touched } }) => (
<FormGroup
label={<T id={'prefix'} />}
className={'form-group--'}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'prefix'} />}
>
<InputGroup
<If condition={values.incrementMode === 'auto'}>
<Row>
{/* ------------- Prefix ------------- */}
<Col xs={4}>
<FastField name={'numberPrefix'}>
{({ form, field, meta: { error, touched } }) => (
<FormGroup
label={<T id={'prefix'} />}
className={'form-group--'}
intent={inputIntent({ error, touched })}
{...field}
/>
</FormGroup>
)}
</FastField>
</Col>
helperText={<ErrorMessage name={'numberPrefix'} />}
>
<InputGroup
intent={inputIntent({ error, touched })}
{...field}
/>
</FormGroup>
)}
</FastField>
</Col>
{/* ------------- Next number ------------- */}
<Col xs={6}>
<FastField name={'next_number'}>
{({ form, field, meta: { error, touched } }) => (
<FormGroup
label={<T id={'next_number'} />}
className={'form-group--'}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'next_number'} />}
>
<InputGroup
{/* ------------- Next number ------------- */}
<Col xs={6}>
<FastField name={'nextNumber'}>
{({ form, field, meta: { error, touched } }) => (
<FormGroup
label={<T id={'next_number'} />}
className={'form-group--next-number'}
intent={inputIntent({ error, touched })}
{...field}
/>
</FormGroup>
)}
</FastField>
</Col>
</Row>
helperText={<ErrorMessage name={'nextNumber'} />}
>
<InputGroup
intent={inputIntent({ error, touched })}
{...field}
/>
</FormGroup>
)}
</FastField>
</Col>
</Row>
</If>
<FastField name={'mode'}>
{/* ------------- Manual increment mode ------------- */}
<FastField name={'incrementMode'}>
{({ 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>
{/* ------------- 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>
<Col md={8}>
{/* --------- Statement --------- */}
<FastField name={'customer_name'}>
<FastField name={'statement'}>
{({ form, field, meta: { error, touched } }) => (
<FormGroup
label={<T id={'statement'} />}

View File

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

View File

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

View File

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

View File

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

View File

@@ -8,7 +8,13 @@ import {
import { DateInput } from '@blueprintjs/datetime';
import { FormattedMessage as T } from 'react-intl';
import { FastField, ErrorMessage } from 'formik';
import { momentFormatter, compose, tansformDateValue } from 'utils';
import {
momentFormatter,
compose,
tansformDateValue,
inputIntent,
handleDateChange,
} from 'utils';
import classNames from 'classnames';
import { CLASSES } from 'common/classes';
import {
@@ -19,8 +25,9 @@ import {
} from 'components';
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';
/**
@@ -29,6 +36,11 @@ import { useEstimateFormContext } from './EstimateFormProvider';
function EstimateFormHeader({
// #withDialogActions
openDialog,
// #withSettings
estimateAutoIncrement,
estimateNumberPrefix,
estimateNextNumber,
}) {
const { customers } = useEstimateFormContext();
@@ -36,6 +48,22 @@ function EstimateFormHeader({
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 (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ----------- Customer name ----------- */}
@@ -131,7 +159,9 @@ function EstimateFormHeader({
<ControlGroup fill={true}>
<InputGroup
minimal={true}
{...field}
value={field.value}
asyncControl={true}
onBlur={handleEstimateNoBlur(form, field)}
/>
<InputPrependButton
buttonProps={{
@@ -169,4 +199,9 @@ function EstimateFormHeader({
export default compose(
withDialogActions,
withSettings(({ estimatesSettings }) => ({
estimateNextNumber: estimatesSettings?.nextNumber,
estimateNumberPrefix: estimatesSettings?.numberPrefix,
estimateAutoIncrement: estimatesSettings?.autoIncrement,
})),
)(EstimateFormHeader);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,10 +1,19 @@
import React from 'react';
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 { ERROR } from 'common/errors';
import { useFormikContext } from 'formik';
import { Intent } from '@blueprintjs/core';
import { orderingLinesIndexes } from 'utils';
import { formatMessage } from 'services/intl';
import { ERROR } from 'common/errors';
import { AppToaster } from 'components';
export const MIN_LINES_NUMBER = 4;
@@ -27,6 +36,7 @@ export const defaultInvoice = {
due_date: moment().format('YYYY-MM-DD'),
delivered: '',
invoice_no: '',
invoice_no_manually: false,
reference_no: '',
invoice_message: '',
terms_conditions: '',
@@ -72,4 +82,24 @@ export const transformErrors = (errors, { setErrors }) => {
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',
Header: formatMessage({ id: 'invoice_no__' }),
accessor: (row) => (row.invoice_no ? `#${row.invoice_no}` : null),
accessor: 'invoice_no',
width: 100,
className: 'invoice_no',
},

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,7 @@
import React from 'react';
import { useFormikContext } from 'formik';
import moment from 'moment';
import { transformToForm, safeSumBy } from 'utils';
import { transactionNumber, transformToForm, safeSumBy } from 'utils';
// Default payment receive entry.
export const defaultPaymentReceiveEntry = {
@@ -18,7 +20,7 @@ export const defaultPaymentReceive = {
payment_date: moment(new Date()).format('YYYY-MM-DD'),
reference_no: '',
payment_receive_no: '',
description: '',
statement: '',
full_amount: '',
entries: [],
};
@@ -81,4 +83,16 @@ export const fullAmountPaymentEntries = (entries) => {
...item,
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 {
EditReceiptFormSchema,
CreateReceiptFormSchema,
CreateReceiptFormSchema,
} from './ReceiptForm.schema';
import { useReceiptFormContext } from './ReceiptFormProvider';
@@ -25,13 +25,8 @@ import withDashboardActions from 'containers/Dashboard/withDashboardActions';
import withSettings from 'containers/Settings/withSettings';
import { AppToaster } from 'components';
import {
compose,
orderingLinesIndexes,
transactionNumber,
} from 'utils';
import { transformToEditForm, defaultReceipt } from './utils'
import { compose, orderingLinesIndexes, transactionNumber } from 'utils';
import { transformToEditForm, defaultReceipt } from './utils';
/**
* Receipt form.
@@ -40,6 +35,7 @@ function ReceiptForm({
// #withSettings
receiptNextNumber,
receiptNumberPrefix,
receiptAutoIncrement,
preferredDepositAccount,
}) {
const { formatMessage } = useIntl();
@@ -47,16 +43,15 @@ function ReceiptForm({
// Receipt form context.
const {
receiptId,
receipt,
editReceiptMutate,
createReceiptMutate,
submitPayload,
isNewMode
isNewMode,
} = useReceiptFormContext();
// The next receipt number.
const receiptNumber = transactionNumber(
const nextReceiptNumber = transactionNumber(
receiptNumberPrefix,
receiptNextNumber,
);
@@ -67,12 +62,14 @@ function ReceiptForm({
? transformToEditForm(receipt)
: {
...defaultReceipt,
receipt_number: receiptNumber,
...(receiptAutoIncrement && {
receipt_number: nextReceiptNumber,
}),
deposit_account_id: parseInt(preferredDepositAccount),
entries: orderingLinesIndexes(defaultReceipt.entries),
}),
}),
[receipt, preferredDepositAccount, receiptNumber],
[receipt, preferredDepositAccount, nextReceiptNumber, receiptAutoIncrement],
);
// Transform response error to fields.
@@ -107,9 +104,12 @@ function ReceiptForm({
return;
}
const form = {
...values,
...omit(values, ['receipt_number_manually', 'receipt_number']),
...(values.receipt_number_manually) && ({
receipt_number: values.receipt_number,
}),
closed: submitPayload.status,
entries: entries.map((entry) => ({ ...omit(entry, ['total']), })),
entries: entries.map((entry) => ({ ...omit(entry, ['total']) })),
};
// Handle the request success.
const onSuccess = (response) => {
@@ -135,13 +135,16 @@ function ReceiptForm({
};
// Handle the request error.
const onError = ({response:{data:{errors}}}) => {
if(errors){
const onError = ({
response: {
data: { errors },
},
}) => {
if (errors) {
handleErrors(errors, { setErrors });
}
setSubmitting(false);
};
if (!isNewMode) {
editReceiptMutate([receipt.id, form]).then(onSuccess).catch(onError);
} else {
@@ -182,6 +185,7 @@ export default compose(
withSettings(({ receiptSettings }) => ({
receiptNextNumber: receiptSettings?.nextNumber,
receiptNumberPrefix: receiptSettings?.numberPrefix,
receiptAutoIncrement: receiptSettings?.autoIncrement,
preferredDepositAccount: receiptSettings?.preferredDepositAccount,
})),
)(ReceiptForm);

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,7 @@
import React from 'react';
import { useFormikContext } from 'formik';
import moment from 'moment';
import { repeatValue, transformToForm } from 'utils';
import { transactionNumber, repeatValue, transformToForm } from 'utils';
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',
Header: formatMessage({ id: 'receipt_number' }),
accessor: (row) =>
row.receipt_number ? `#${row.receipt_number}` : null,
accessor: 'receipt_number',
width: 140,
className: 'receipt_number',
},
@@ -138,7 +137,7 @@ export function useReceiptsTableColumns() {
Header: formatMessage({ id: 'status' }),
accessor: StatusAccessor,
width: 140,
className: 'amount',
className: 'status',
},
{
id: 'reference_no',

View File

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

View File

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

View File

@@ -159,6 +159,13 @@ export default {
],
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: {
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',
@@ -412,4 +419,10 @@ export default {
],
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{
background-color: #0066ff;
}
.bp3-overlay-backdrop{
background-color: rgba(0,10,30, .7);
}
}

View File

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

View File

@@ -8,12 +8,15 @@
z-index: $sidebar-zindex;
.ScrollbarsCustom-Track {
&.ScrollbarsCustom-TrackY,
&.ScrollbarsCustom-TrackX {
background: rgba(0, 0, 0, 0);
}
}
.ScrollbarsCustom-Thumb {
&.ScrollbarsCustom-ThumbX,
&.ScrollbarsCustom-ThumbY {
background: rgba(0, 0, 0, 0);
@@ -22,27 +25,69 @@
&:hover {
.ScrollbarsCustom-Thumb {
&.ScrollbarsCustom-ThumbX,
&.ScrollbarsCustom-ThumbY {
background: rgba(255, 255, 255, 0.25);
}
}
}
&__head {
padding: 18px 20px;
padding: 20px 20px;
&-logo {
position: relative;
top: 2px;
position: absolute;
top: 16px;
left: 12px;
opacity: 0;
visibility: hidden;
svg {
svg{
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;
}
}
&__scroll-wrapper {
height: 100%;
}
@@ -66,20 +111,24 @@
background: $sidebar-submenu-item-bg-color;
color: $sidebar-menu-item-color-active;
}
&:focus,
&:active {
background: #01143e;
}
> .#{$ns}-icon {
>.#{$ns}-icon {
color: #767b9b;
margin-right: 16px;
margin-top: 0;
}
> .#{$ns}-icon-caret-right {
>.#{$ns}-icon-caret-right {
margin-right: -4px;
margin-top: 3px;
color: rgba(255, 255, 255, 0.25);
}
&-labeler {
display: block;
color: $sidebar-menu-label-color;
@@ -113,11 +162,13 @@
background: transparent;
color: $sidebar-submenu-item-hover-color;
}
&.bp3-active {
font-weight: 500;
}
}
}
.#{$ns}-popover {
padding: 0;
@@ -126,14 +177,17 @@
}
}
}
.#{$ns}-popover-target.#{$ns}-popover-open .#{$ns}-menu-item {
color: $sidebar-menu-item-color;
}
.#{$ns}-menu-divider {
border-top-color: rgba(255, 255, 255, 0.1);
color: #6b708c;
margin: 4px 0;
}
.#{$ns}-menu-spacer {
margin: 4px 0;
height: 1px;
@@ -145,20 +199,20 @@
white-space: nowrap;
width: 50px;
.sidebar__head{
.sidebar__head-logo{
transform: translate(-8px, 0);
.sidebar__head {
.sidebar__head-logo {
transition: opacity 0.3s ease-in-out;
transition-delay: 0.15s;
opacity: 1;
visibility: visible;
}
}
// Hide text of bigcapital logo.
.sidebar__head-logo {
.bp3-icon-bigcapital {
path {
transition: opacity 0.3s ease-in-out;
}
path:not(.path-1):not(.path-2) {
opacity: 0;
}
// Hide text of bigcapital logo.
&-logo {
}
&-organization{
opacity: 0;
transition: opacity 0.3s ease-in-out;
}
}
@@ -175,15 +229,14 @@
&:hover {
min-width: 220px;
.sidebar__head-logo {
transform: translate(0px, 0);
.bp3-icon-bigcapital {
path:not(.path-1):not(.path-2) {
opacity: 1;
}
}
.sidebar__head-logo {
opacity: 0;
transition-delay: 0s;
}
.sidebar__head-organization{
opacity: 1;
}
.sidebar__menu {
opacity: 1;
}
@@ -205,6 +258,7 @@
.bp3-icon {
color: rgba(255, 255, 255, 0.4);
}
&,
&:hover {
min-height: auto;
@@ -221,9 +275,34 @@
}
}
}
.bp3-icon {
margin: 0;
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 {
.bp3-dialog-body {
.bp3-form-group.bp3-inline {
margin: 18px 0px;
> .paragraph {
margin-bottom: 1rem;
}
.bp3-form-group {
margin-bottom: 1.4rem;
.bp3-label {
min-width: 100px;
font-size: 13px;
}
}
.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;
+ .partial-paid{
margin-top: 5px;
margin-top: 2px;
}
}
.overdue-status{
@@ -40,7 +40,7 @@
font-size: 12px;
line-height: 1;
display: block;
margin-bottom: 8px;
margin-bottom: 6px;
opacity: 0.7;
}
.fully-paid-icon{

View File

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

View File

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

View File

@@ -472,7 +472,7 @@ export function transactionNumber(prefix, number) {
if (number) {
codes.push(number);
}
return codes.join('-');
return codes.join('');
}
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_number').optional({ nullable: true }).trim().escape(),
check('payment_date').exists(),
check('description').optional().trim().escape(),
check('statement').optional().trim().escape(),
check('reference').optional().trim().escape(),
check('entries').exists().isArray({ min: 1 }),

View File

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

View File

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

View File

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

View File

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

View File

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