mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-21 15:20:34 +00:00
feat: auto-increment sale invoice, receipt, estimate and payment receive.
feat: style sales and purchases forms.
This commit is contained in:
@@ -1,16 +1,20 @@
|
|||||||
import { Classes } from '@blueprintjs/core';
|
import { Classes } from '@blueprintjs/core';
|
||||||
|
|
||||||
const CLASSES = {
|
const CLASSES = {
|
||||||
DATATABLE_EDITOR: 'DATATABLE_EDITOR',
|
DATATABLE_EDITOR: 'datatable-editor',
|
||||||
|
DATATABLE_EDITOR_ACTIONS: 'datatable-editor__actions',
|
||||||
|
DATATABLE_EDITOR_ITEMS_ENTRIES: 'items-entries-table',
|
||||||
|
|
||||||
PAGE_FORM: 'page-form',
|
PAGE_FORM: 'page-form',
|
||||||
PAGE_FORM_HEADER: 'page-form__header',
|
PAGE_FORM_HEADER: 'page-form__header',
|
||||||
|
PAGE_FORM_HEADER_PRIMARY: 'page-form__primary-section',
|
||||||
PAGE_FORM_FOOTER: 'page-form__footer',
|
PAGE_FORM_FOOTER: 'page-form__footer',
|
||||||
PAGE_FORM_FLOATING_ACTIONS: 'page-form__floating-action',
|
PAGE_FORM_FLOATING_ACTIONS: 'page-form__floating-action',
|
||||||
|
|
||||||
PAGE_FORM_BILL: 'page-form--bill',
|
PAGE_FORM_BILL: 'page-form--bill',
|
||||||
PAGE_FORM_ESTIMATE: 'page-form--estimate',
|
PAGE_FORM_ESTIMATE: 'page-form--estimate',
|
||||||
|
PAGE_FORM_INVOICE: 'page-form--invoice',
|
||||||
|
PAGE_FORM_RECEIPT: 'page-form--receipt',
|
||||||
...Classes,
|
...Classes,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -247,7 +247,7 @@ function MakeJournalEntriesTable({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="make-journal-entries__table">
|
<div class="make-journal-entries__table datatable-editor">
|
||||||
<DataTable
|
<DataTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={tableRows}
|
data={tableRows}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React, { useState, useMemo, useEffect, useCallback } from 'react';
|
|||||||
import { omit } from 'lodash';
|
import { omit } from 'lodash';
|
||||||
import { Button, Intent, Position, Tooltip } from '@blueprintjs/core';
|
import { Button, Intent, Position, Tooltip } from '@blueprintjs/core';
|
||||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import { CLASSES } from 'common/classes';
|
import { CLASSES } from 'common/classes';
|
||||||
import { Hint, Icon } from 'components';
|
import { Hint, Icon } from 'components';
|
||||||
@@ -229,7 +230,10 @@ function EstimateTable({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'estimate-form__table datatable-editor'}>
|
<div className={classNames(
|
||||||
|
CLASSES.DATATABLE_EDITOR,
|
||||||
|
CLASSES.DATATABLE_EDITOR_ITEMS_ENTRIES,
|
||||||
|
)}>
|
||||||
<DataTable
|
<DataTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={rows}
|
data={rows}
|
||||||
@@ -240,9 +244,8 @@ function EstimateTable({
|
|||||||
updateData: handleUpdateData,
|
updateData: handleUpdateData,
|
||||||
removeRow: handleRemoveRow,
|
removeRow: handleRemoveRow,
|
||||||
}}
|
}}
|
||||||
className={CLASSES.DATATABLE_EDITOR}
|
|
||||||
/>
|
/>
|
||||||
<div className={'datatable-editor__actions mt1'}>
|
<div className={classNames(CLASSES.DATATABLE_EDITOR_ACTIONS, 'mt1')}>
|
||||||
<Button
|
<Button
|
||||||
small={true}
|
small={true}
|
||||||
className={'button--secondary button--new-line'}
|
className={'button--secondary button--new-line'}
|
||||||
|
|||||||
@@ -317,7 +317,7 @@ const EstimateForm = ({
|
|||||||
|
|
||||||
<div class={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
<div class={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
||||||
<Row>
|
<Row>
|
||||||
<Col md={7}>
|
<Col md={8}>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'customer_note'} />}
|
label={<T id={'customer_note'} />}
|
||||||
className={'form-group--customer_note'}
|
className={'form-group--customer_note'}
|
||||||
@@ -327,6 +327,7 @@ const EstimateForm = ({
|
|||||||
{...formik.getFieldProps('note')}
|
{...formik.getFieldProps('note')}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'terms_conditions'} />}
|
label={<T id={'terms_conditions'} />}
|
||||||
className={'form-group--terms_conditions'}
|
className={'form-group--terms_conditions'}
|
||||||
@@ -338,7 +339,7 @@ const EstimateForm = ({
|
|||||||
</FormGroup>
|
</FormGroup>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
<Col md={5}>
|
<Col md={4}>
|
||||||
<Dragzone
|
<Dragzone
|
||||||
initialFiles={initialAttachmentFiles}
|
initialFiles={initialAttachmentFiles}
|
||||||
onDrop={handleDropFiles}
|
onDrop={handleDropFiles}
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ function EstimateFormHeader({
|
|||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
</Col>
|
</Col>
|
||||||
<Col md={4}>
|
<Col md={4} className={'col--expiration-date'}>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'expiration_date'} />}
|
label={<T id={'expiration_date'} />}
|
||||||
inline={true}
|
inline={true}
|
||||||
|
|||||||
@@ -8,10 +8,11 @@ import React, {
|
|||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
import { useFormik } from 'formik';
|
import { useFormik } from 'formik';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { Intent, FormGroup, TextArea, Button } from '@blueprintjs/core';
|
import { Intent, FormGroup, TextArea } from '@blueprintjs/core';
|
||||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||||
import { pick } from 'lodash';
|
import { pick } from 'lodash';
|
||||||
import { Row, Col } from 'react-grid-system';
|
import classNames from 'classnames';
|
||||||
|
import { CLASSES } from 'common/classes';
|
||||||
|
|
||||||
import InvoiceFormHeader from './InvoiceFormHeader';
|
import InvoiceFormHeader from './InvoiceFormHeader';
|
||||||
import EstimatesItemsTable from 'containers/Sales/Estimate/EntriesItemsTable';
|
import EstimatesItemsTable from 'containers/Sales/Estimate/EntriesItemsTable';
|
||||||
@@ -23,7 +24,7 @@ import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
|||||||
import withMediaActions from 'containers/Media/withMediaActions';
|
import withMediaActions from 'containers/Media/withMediaActions';
|
||||||
import withSettings from 'containers/Settings/withSettings';
|
import withSettings from 'containers/Settings/withSettings';
|
||||||
|
|
||||||
import { AppToaster } from 'components';
|
import { AppToaster, Col, Row } from 'components';
|
||||||
import Dragzone from 'components/Dragzone';
|
import Dragzone from 'components/Dragzone';
|
||||||
import useMedia from 'hooks/useMedia';
|
import useMedia from 'hooks/useMedia';
|
||||||
|
|
||||||
@@ -307,7 +308,7 @@ function InvoiceForm({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'invoice-form'}>
|
<div className={classNames(CLASSES.PAGE_FORM, CLASSES.PAGE_FORM_INVOICE)}>
|
||||||
<form onSubmit={formik.handleSubmit}>
|
<form onSubmit={formik.handleSubmit}>
|
||||||
<InvoiceFormHeader formik={formik} />
|
<InvoiceFormHeader formik={formik} />
|
||||||
<EstimatesItemsTable
|
<EstimatesItemsTable
|
||||||
@@ -316,37 +317,43 @@ function InvoiceForm({
|
|||||||
onClickClearAllLines={handleClearAllLines}
|
onClickClearAllLines={handleClearAllLines}
|
||||||
formik={formik}
|
formik={formik}
|
||||||
/>
|
/>
|
||||||
<Row>
|
|
||||||
<Col>
|
|
||||||
<FormGroup
|
|
||||||
label={<T id={'invoice_message'} />}
|
|
||||||
className={'form-group--customer_note'}
|
|
||||||
>
|
|
||||||
<TextArea
|
|
||||||
growVertically={true}
|
|
||||||
{...formik.getFieldProps('invoice_message')}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
<FormGroup
|
|
||||||
label={<T id={'terms_conditions'} />}
|
|
||||||
className={'form-group--terms_conditions'}
|
|
||||||
>
|
|
||||||
<TextArea
|
|
||||||
growVertically={true}
|
|
||||||
{...formik.getFieldProps('terms_conditions')}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
</Col>
|
|
||||||
|
|
||||||
<Col>
|
<div className={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
||||||
<Dragzone
|
<Row>
|
||||||
initialFiles={initialAttachmentFiles}
|
<Col md={8}>
|
||||||
onDrop={handleDropFiles}
|
{/* --------- Invoice message --------- */}
|
||||||
onDeleteFile={handleDeleteFile}
|
<FormGroup
|
||||||
hint={'Attachments: Maxiumum size: 20MB'}
|
label={<T id={'invoice_message'} />}
|
||||||
/>
|
className={'form-group--invoice_message'}
|
||||||
</Col>
|
>
|
||||||
</Row>
|
<TextArea
|
||||||
|
growVertically={true}
|
||||||
|
{...formik.getFieldProps('invoice_message')}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
|
{/* --------- Terms and conditions --------- */}
|
||||||
|
<FormGroup
|
||||||
|
label={<T id={'terms_conditions'} />}
|
||||||
|
className={'form-group--terms_conditions'}
|
||||||
|
>
|
||||||
|
<TextArea
|
||||||
|
growVertically={true}
|
||||||
|
{...formik.getFieldProps('terms_conditions')}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</Col>
|
||||||
|
|
||||||
|
<Col md={4}>
|
||||||
|
<Dragzone
|
||||||
|
initialFiles={initialAttachmentFiles}
|
||||||
|
onDrop={handleDropFiles}
|
||||||
|
onDeleteFile={handleDeleteFile}
|
||||||
|
hint={'Attachments: Maxiumum size: 20MB'}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<InvoiceFormFooter
|
<InvoiceFormFooter
|
||||||
|
|||||||
@@ -5,20 +5,21 @@ import {
|
|||||||
Intent,
|
Intent,
|
||||||
Position,
|
Position,
|
||||||
MenuItem,
|
MenuItem,
|
||||||
Classes,
|
|
||||||
} 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 { Row, Col } from 'react-grid-system';
|
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { momentFormatter, compose, tansformDateValue } from 'utils';
|
import { momentFormatter, compose, tansformDateValue } from 'utils';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import { CLASSES } from 'common/classes';
|
||||||
import {
|
import {
|
||||||
ListSelect,
|
ListSelect,
|
||||||
ErrorMessage,
|
ErrorMessage,
|
||||||
FieldRequiredHint,
|
FieldRequiredHint,
|
||||||
Icon,
|
Icon,
|
||||||
InputPrependButton,
|
InputPrependButton,
|
||||||
|
Row,
|
||||||
|
Col,
|
||||||
} from 'components';
|
} from 'components';
|
||||||
|
|
||||||
import withCustomers from 'containers/Customers/withCustomers';
|
import withCustomers from 'containers/Customers/withCustomers';
|
||||||
@@ -81,13 +82,17 @@ function InvoiceFormHeader({
|
|||||||
}, [openDialog]);
|
}, [openDialog]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="page-form page-form--invoice">
|
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
|
||||||
<div className={'page-form__primary-section'}>
|
<div className={classNames(CLASSES.PAGE_FORM_HEADER_PRIMARY)}>
|
||||||
{/* customer name */}
|
{/* ----------- Customer name ----------- */}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'customer_name'} />}
|
label={<T id={'customer_name'} />}
|
||||||
inline={true}
|
inline={true}
|
||||||
className={classNames('form-group--select-list', Classes.FILL)}
|
className={classNames(
|
||||||
|
'form-group--select-list',
|
||||||
|
'form-group--customer-name',
|
||||||
|
CLASSES.FILL,
|
||||||
|
)}
|
||||||
labelInfo={<FieldRequiredHint />}
|
labelInfo={<FieldRequiredHint />}
|
||||||
intent={errors.customer_id && touched.customer_id && Intent.DANGER}
|
intent={errors.customer_id && touched.customer_id && Intent.DANGER}
|
||||||
helperText={
|
helperText={
|
||||||
@@ -107,79 +112,109 @@ function InvoiceFormHeader({
|
|||||||
labelProp={'display_name'}
|
labelProp={'display_name'}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
</div>
|
|
||||||
|
|
||||||
<Row>
|
<Row>
|
||||||
<Col>
|
<Col md={7} className={'col--invoice-date'}>
|
||||||
<FormGroup
|
{/* ----------- Invoice date ----------- */}
|
||||||
label={<T id={'invoice_date'} />}
|
<FormGroup
|
||||||
inline={true}
|
label={<T id={'invoice_date'} />}
|
||||||
labelInfo={<FieldRequiredHint />}
|
inline={true}
|
||||||
className={classNames('form-group--select-list', Classes.FILL)}
|
labelInfo={<FieldRequiredHint />}
|
||||||
intent={
|
className={classNames(
|
||||||
errors.invoice_date && touched.invoice_date && Intent.DANGER
|
'form-group--select-list',
|
||||||
}
|
'form-group--invoice-date',
|
||||||
helperText={
|
CLASSES.FILL
|
||||||
<ErrorMessage name="invoice_date" {...{ errors, touched }} />
|
)}
|
||||||
}
|
intent={
|
||||||
>
|
errors.invoice_date && touched.invoice_date && Intent.DANGER
|
||||||
<DateInput
|
}
|
||||||
{...momentFormatter('YYYY/MM/DD')}
|
helperText={
|
||||||
value={tansformDateValue(values.invoice_date)}
|
<ErrorMessage name="invoice_date" {...{ errors, touched }} />
|
||||||
onChange={handleDateChange('invoice_date')}
|
}
|
||||||
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
>
|
||||||
/>
|
<DateInput
|
||||||
</FormGroup>
|
{...momentFormatter('YYYY/MM/DD')}
|
||||||
</Col>
|
value={tansformDateValue(values.invoice_date)}
|
||||||
|
onChange={handleDateChange('invoice_date')}
|
||||||
|
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</Col>
|
||||||
|
|
||||||
<Col>
|
<Col md={5} className={'col--due-date'}>
|
||||||
<FormGroup
|
{/* ----------- Due date ----------- */}
|
||||||
label={<T id={'due_date'} />}
|
<FormGroup
|
||||||
inline={true}
|
label={<T id={'due_date'} />}
|
||||||
className={classNames('form-group--select-list', Classes.FILL)}
|
inline={true}
|
||||||
intent={errors.due_date && touched.due_date && Intent.DANGER}
|
className={classNames(
|
||||||
helperText={
|
'form-group--select-list',
|
||||||
<ErrorMessage name="due_date" {...{ errors, touched }} />
|
'form-group--due-date',
|
||||||
}
|
CLASSES.FILL
|
||||||
>
|
)}
|
||||||
<DateInput
|
intent={errors.due_date && touched.due_date && Intent.DANGER}
|
||||||
{...momentFormatter('YYYY/MM/DD')}
|
helperText={
|
||||||
value={tansformDateValue(values.due_date)}
|
<ErrorMessage name="due_date" {...{ errors, touched }} />
|
||||||
onChange={handleDateChange('due_date')}
|
}
|
||||||
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
>
|
||||||
/>
|
<DateInput
|
||||||
</FormGroup>
|
{...momentFormatter('YYYY/MM/DD')}
|
||||||
</Col>
|
value={tansformDateValue(values.due_date)}
|
||||||
</Row>
|
onChange={handleDateChange('due_date')}
|
||||||
{/* invoice */}
|
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
||||||
<FormGroup
|
/>
|
||||||
label={<T id={'invoice_no'} />}
|
</FormGroup>
|
||||||
inline={true}
|
</Col>
|
||||||
className={('form-group--estimate', Classes.FILL)}
|
</Row>
|
||||||
labelInfo={<FieldRequiredHint />}
|
{/* ----------- Invoice number ----------- */}
|
||||||
intent={errors.invoice_no && touched.invoice_no && Intent.DANGER}
|
<FormGroup
|
||||||
helperText={<ErrorMessage name="invoice_no" {...{ errors, touched }} />}
|
label={<T id={'invoice_no'} />}
|
||||||
>
|
inline={true}
|
||||||
<InputGroup
|
className={classNames('form-group--invoice-no', CLASSES.FILL)}
|
||||||
|
labelInfo={<FieldRequiredHint />}
|
||||||
intent={errors.invoice_no && touched.invoice_no && Intent.DANGER}
|
intent={errors.invoice_no && touched.invoice_no && Intent.DANGER}
|
||||||
minimal={true}
|
helperText={
|
||||||
{...getFieldProps('invoice_no')}
|
<ErrorMessage name="invoice_no" {...{ errors, touched }} />
|
||||||
/>
|
}
|
||||||
</FormGroup>
|
rightElement={
|
||||||
|
<InputPrependButton
|
||||||
|
buttonProps={{
|
||||||
|
onClick: handleInvoiceNumberChange,
|
||||||
|
icon: <Icon icon={'settings-18'} />,
|
||||||
|
}}
|
||||||
|
tooltip={true}
|
||||||
|
tooltipProps={{
|
||||||
|
content: 'Setting your auto-generated invoice number',
|
||||||
|
position: Position.BOTTOM_LEFT,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<InputGroup
|
||||||
|
intent={errors.invoice_no && touched.invoice_no && Intent.DANGER}
|
||||||
|
minimal={true}
|
||||||
|
{...getFieldProps('invoice_no')}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
<FormGroup
|
{/* ----------- Reference ----------- */}
|
||||||
label={<T id={'reference'} />}
|
<FormGroup
|
||||||
inline={true}
|
label={<T id={'reference'} />}
|
||||||
className={classNames('form-group--reference', Classes.FILL)}
|
inline={true}
|
||||||
intent={errors.reference_no && touched.reference_no && Intent.DANGER}
|
className={classNames('form-group--reference', CLASSES.FILL)}
|
||||||
helperText={<ErrorMessage name="reference" {...{ errors, touched }} />}
|
|
||||||
>
|
|
||||||
<InputGroup
|
|
||||||
intent={errors.reference_no && touched.reference_no && Intent.DANGER}
|
intent={errors.reference_no && touched.reference_no && Intent.DANGER}
|
||||||
minimal={true}
|
helperText={
|
||||||
{...getFieldProps('reference_no')}
|
<ErrorMessage name="reference" {...{ errors, touched }} />
|
||||||
/>
|
}
|
||||||
</FormGroup>
|
>
|
||||||
|
<InputGroup
|
||||||
|
intent={
|
||||||
|
errors.reference_no && touched.reference_no && Intent.DANGER
|
||||||
|
}
|
||||||
|
minimal={true}
|
||||||
|
{...getFieldProps('reference_no')}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ import moment from 'moment';
|
|||||||
import { Intent, FormGroup, TextArea } from '@blueprintjs/core';
|
import { Intent, FormGroup, TextArea } from '@blueprintjs/core';
|
||||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||||
import { pick } from 'lodash';
|
import { pick } from 'lodash';
|
||||||
import { Row, Col } from 'react-grid-system';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import { CLASSES } from 'common/classes';
|
||||||
|
|
||||||
import ReceiptFromHeader from './ReceiptFormHeader';
|
import ReceiptFromHeader from './ReceiptFormHeader';
|
||||||
import EstimatesItemsTable from 'containers/Sales/Estimate/EntriesItemsTable';
|
import EstimatesItemsTable from 'containers/Sales/Estimate/EntriesItemsTable';
|
||||||
@@ -24,7 +26,7 @@ import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
|||||||
import withMediaActions from 'containers/Media/withMediaActions';
|
import withMediaActions from 'containers/Media/withMediaActions';
|
||||||
import withSettings from 'containers/Settings/withSettings';
|
import withSettings from 'containers/Settings/withSettings';
|
||||||
|
|
||||||
import { AppToaster } from 'components';
|
import { AppToaster, Row, Col } from 'components';
|
||||||
import Dragzone from 'components/Dragzone';
|
import Dragzone from 'components/Dragzone';
|
||||||
import useMedia from 'hooks/useMedia';
|
import useMedia from 'hooks/useMedia';
|
||||||
|
|
||||||
@@ -305,7 +307,7 @@ function ReceiptForm({
|
|||||||
}, [receiptNumber]);
|
}, [receiptNumber]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'receipt-form'}>
|
<div className={classNames(CLASSES.PAGE_FORM_RECEIPT, CLASSES.PAGE_FORM)}>
|
||||||
<form onSubmit={formik.handleSubmit}>
|
<form onSubmit={formik.handleSubmit}>
|
||||||
<ReceiptFromHeader formik={formik} />
|
<ReceiptFromHeader formik={formik} />
|
||||||
|
|
||||||
@@ -316,37 +318,40 @@ function ReceiptForm({
|
|||||||
formik={formik}
|
formik={formik}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Row>
|
<div className={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
||||||
<Col>
|
<Row>
|
||||||
<FormGroup
|
<Col md={8}>
|
||||||
label={<T id={'receipt_message'} />}
|
<FormGroup
|
||||||
className={'form-group--'}
|
label={<T id={'receipt_message'} />}
|
||||||
>
|
className={'form-group--receipt_message'}
|
||||||
<TextArea
|
>
|
||||||
growVertically={true}
|
<TextArea
|
||||||
{...formik.getFieldProps('receipt_message')}
|
growVertically={true}
|
||||||
/>
|
{...formik.getFieldProps('receipt_message')}
|
||||||
</FormGroup>
|
/>
|
||||||
<FormGroup
|
</FormGroup>
|
||||||
label={<T id={'statement'} />}
|
|
||||||
className={'form-group--statement'}
|
|
||||||
>
|
|
||||||
<TextArea
|
|
||||||
growVertically={true}
|
|
||||||
{...formik.getFieldProps('statement')}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
</Col>
|
|
||||||
|
|
||||||
<Col>
|
<FormGroup
|
||||||
<Dragzone
|
label={<T id={'statement'} />}
|
||||||
initialFiles={initialAttachmentFiles}
|
className={'form-group--statement'}
|
||||||
onDrop={handleDropFiles}
|
>
|
||||||
onDeleteFile={handleDeleteFile}
|
<TextArea
|
||||||
hint={'Attachments: Maxiumum size: 20MB'}
|
growVertically={true}
|
||||||
/>
|
{...formik.getFieldProps('statement')}
|
||||||
</Col>
|
/>
|
||||||
</Row>
|
</FormGroup>
|
||||||
|
</Col>
|
||||||
|
|
||||||
|
<Col md={4}>
|
||||||
|
<Dragzone
|
||||||
|
initialFiles={initialAttachmentFiles}
|
||||||
|
onDrop={handleDropFiles}
|
||||||
|
onDeleteFile={handleDeleteFile}
|
||||||
|
hint={'Attachments: Maxiumum size: 20MB'}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<ReceiptFormFooter
|
<ReceiptFormFooter
|
||||||
formik={formik}
|
formik={formik}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import {
|
|||||||
Intent,
|
Intent,
|
||||||
Position,
|
Position,
|
||||||
MenuItem,
|
MenuItem,
|
||||||
Classes,
|
|
||||||
} from '@blueprintjs/core';
|
} from '@blueprintjs/core';
|
||||||
|
|
||||||
import { DateInput } from '@blueprintjs/datetime';
|
import { DateInput } from '@blueprintjs/datetime';
|
||||||
@@ -13,6 +12,7 @@ import { FormattedMessage as T } from 'react-intl';
|
|||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { momentFormatter, compose, tansformDateValue } from 'utils';
|
import { momentFormatter, compose, tansformDateValue } from 'utils';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import { CLASSES } from 'common/classes';
|
||||||
import {
|
import {
|
||||||
AccountsSelectList,
|
AccountsSelectList,
|
||||||
ListSelect,
|
ListSelect,
|
||||||
@@ -91,15 +91,15 @@ function ReceiptFormHeader({
|
|||||||
}, [openDialog]);
|
}, [openDialog]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="page-form receipt-form">
|
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
|
||||||
<div class="page-form__primary-section">
|
<div className={classNames(CLASSES.PAGE_FORM_HEADER_PRIMARY)}>
|
||||||
{/*- Customer name -*/}
|
{/*- Customer name -*/}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'customer_name'} />}
|
label={<T id={'customer_name'} />}
|
||||||
inline={true}
|
inline={true}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'form-group--select-list',
|
'form-group--select-list',
|
||||||
Classes.FILL,
|
CLASSES.FILL,
|
||||||
'form-group--customer',
|
'form-group--customer',
|
||||||
)}
|
)}
|
||||||
labelInfo={<FieldRequiredHint />}
|
labelInfo={<FieldRequiredHint />}
|
||||||
@@ -128,7 +128,7 @@ function ReceiptFormHeader({
|
|||||||
className={classNames(
|
className={classNames(
|
||||||
'form-group--deposit-account',
|
'form-group--deposit-account',
|
||||||
'form-group--select-list',
|
'form-group--select-list',
|
||||||
Classes.FILL,
|
CLASSES.FILL,
|
||||||
)}
|
)}
|
||||||
inline={true}
|
inline={true}
|
||||||
labelInfo={<FieldRequiredHint />}
|
labelInfo={<FieldRequiredHint />}
|
||||||
@@ -155,7 +155,7 @@ function ReceiptFormHeader({
|
|||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'receipt_date'} />}
|
label={<T id={'receipt_date'} />}
|
||||||
inline={true}
|
inline={true}
|
||||||
className={classNames('form-group--select-list', Classes.FILL)}
|
className={classNames('form-group--select-list', CLASSES.FILL)}
|
||||||
intent={errors.receipt_date && touched.receipt_date && Intent.DANGER}
|
intent={errors.receipt_date && touched.receipt_date && Intent.DANGER}
|
||||||
helperText={
|
helperText={
|
||||||
<ErrorMessage name="receipt_date" {...{ errors, touched }} />
|
<ErrorMessage name="receipt_date" {...{ errors, touched }} />
|
||||||
@@ -168,73 +168,61 @@ function ReceiptFormHeader({
|
|||||||
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
</div>
|
|
||||||
{/* receipt_no */}
|
{/* receipt_no */}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'receipt'} />}
|
label={<T id={'receipt'} />}
|
||||||
inline={true}
|
inline={true}
|
||||||
className={('form-group--receipt_number', Classes.FILL)}
|
className={('form-group--receipt_number', CLASSES.FILL)}
|
||||||
labelInfo={<FieldRequiredHint />}
|
labelInfo={<FieldRequiredHint />}
|
||||||
intent={
|
|
||||||
errors.receipt_number && touched.receipt_number && Intent.DANGER
|
|
||||||
}
|
|
||||||
helperText={
|
|
||||||
<ErrorMessage name="receipt_number" {...{ errors, touched }} />
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<InputGroup
|
|
||||||
intent={
|
intent={
|
||||||
errors.receipt_number && touched.receipt_number && Intent.DANGER
|
errors.receipt_number && touched.receipt_number && Intent.DANGER
|
||||||
}
|
}
|
||||||
minimal={true}
|
helperText={
|
||||||
rightElement={
|
<ErrorMessage name="receipt_number" {...{ errors, touched }} />
|
||||||
<InputPrependButton
|
|
||||||
buttonProps={{
|
|
||||||
onClick: handleReceiptNumberChange,
|
|
||||||
icon: <Icon icon={'settings-18'} />,
|
|
||||||
}}
|
|
||||||
tooltip={true}
|
|
||||||
tooltipProps={{
|
|
||||||
content: 'Setting your auto-generated receipt number',
|
|
||||||
position: Position.BOTTOM_LEFT,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
}
|
||||||
{...getFieldProps('receipt_number')}
|
>
|
||||||
/>
|
<InputGroup
|
||||||
</FormGroup>
|
intent={
|
||||||
|
errors.receipt_number && touched.receipt_number && Intent.DANGER
|
||||||
|
}
|
||||||
|
minimal={true}
|
||||||
|
rightElement={
|
||||||
|
<InputPrependButton
|
||||||
|
buttonProps={{
|
||||||
|
onClick: handleReceiptNumberChange,
|
||||||
|
icon: <Icon icon={'settings-18'} />,
|
||||||
|
}}
|
||||||
|
tooltip={true}
|
||||||
|
tooltipProps={{
|
||||||
|
content: 'Setting your auto-generated receipt number',
|
||||||
|
position: Position.BOTTOM_LEFT,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
{...getFieldProps('receipt_number')}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
|
||||||
{/*- Reference -*/}
|
{/*- Reference -*/}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'reference'} />}
|
label={<T id={'reference'} />}
|
||||||
inline={true}
|
inline={true}
|
||||||
className={classNames('form-group--reference', Classes.FILL)}
|
className={classNames('form-group--reference', CLASSES.FILL)}
|
||||||
intent={errors.reference_no && touched.reference_no && Intent.DANGER}
|
|
||||||
helperText={<ErrorMessage name="reference" {...{ errors, touched }} />}
|
|
||||||
>
|
|
||||||
<InputGroup
|
|
||||||
intent={errors.reference_no && touched.reference_no && Intent.DANGER}
|
intent={errors.reference_no && touched.reference_no && Intent.DANGER}
|
||||||
minimal={true}
|
helperText={
|
||||||
{...getFieldProps('reference_no')}
|
<ErrorMessage name="reference" {...{ errors, touched }} />
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
|
|
||||||
{/*- Send to email -*/}
|
|
||||||
<FormGroup
|
|
||||||
label={<T id={'send_to_email'} />}
|
|
||||||
inline={true}
|
|
||||||
className={classNames('form-group--send_to_email', Classes.FILL)}
|
|
||||||
intent={errors.send_to_email && touched.send_to_email && Intent.DANGER}
|
|
||||||
helperText={<ErrorMessage name="reference" {...{ errors, touched }} />}
|
|
||||||
>
|
|
||||||
<InputGroup
|
|
||||||
intent={
|
|
||||||
errors.send_to_email && touched.send_to_email && Intent.DANGER
|
|
||||||
}
|
}
|
||||||
minimal={true}
|
>
|
||||||
{...getFieldProps('send_to_email')}
|
<InputGroup
|
||||||
/>
|
intent={
|
||||||
</FormGroup>
|
errors.reference_no && touched.reference_no && Intent.DANGER
|
||||||
|
}
|
||||||
|
minimal={true}
|
||||||
|
{...getFieldProps('reference_no')}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,11 +68,13 @@ $pt-font-family: Noto Sans, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto,
|
|||||||
@import 'pages/register-organizaton';
|
@import 'pages/register-organizaton';
|
||||||
@import 'pages/bills';
|
@import 'pages/bills';
|
||||||
@import 'pages/estimates';
|
@import 'pages/estimates';
|
||||||
|
@import 'pages/invoice-form';
|
||||||
|
@import 'pages/receipt-form';
|
||||||
|
|
||||||
// Views
|
// Views
|
||||||
@import 'views/filter-dropdown';
|
@import 'views/filter-dropdown';
|
||||||
@import 'views/sidebar';
|
@import 'views/sidebar';
|
||||||
@import 'pages/estimate';
|
// @import 'pages/estimate';
|
||||||
|
|
||||||
.App {
|
.App {
|
||||||
min-width: 960px;
|
min-width: 960px;
|
||||||
@@ -215,11 +217,152 @@ body.authentication {
|
|||||||
}
|
}
|
||||||
&__footer{
|
&__footer{
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
|
margin: 15px 0 0 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.datatable-editor{
|
.datatable-editor{
|
||||||
|
|
||||||
|
padding: 15px 15px 0;
|
||||||
|
|
||||||
|
.bp3-form-group {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
.table {
|
||||||
|
border: 1px dotted rgb(195, 195, 195);
|
||||||
|
border-bottom: transparent;
|
||||||
|
border-left: transparent;
|
||||||
|
|
||||||
|
.th,
|
||||||
|
.td {
|
||||||
|
border-left: 1px dotted rgb(195, 195, 195);
|
||||||
|
|
||||||
|
&.index {
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
span {
|
||||||
|
width: 100%;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.thead {
|
||||||
|
.tr .th {
|
||||||
|
padding: 10px 10px;
|
||||||
|
background-color: #f2f5fa;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #1e1c3e;
|
||||||
|
|
||||||
|
&.index > div {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tbody {
|
||||||
|
.tr .td {
|
||||||
|
padding: 7px;
|
||||||
|
border-bottom: 1px dotted rgb(195, 195, 195);
|
||||||
|
min-height: 46px;
|
||||||
|
|
||||||
|
&.index {
|
||||||
|
background-color: #f2f5fa;
|
||||||
|
|
||||||
|
> span {
|
||||||
|
margin-top: auto;
|
||||||
|
margin-bottom: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.tr {
|
||||||
|
.bp3-form-group:not(.bp3-intent-danger) .bp3-input,
|
||||||
|
.form-group--select-list .bp3-button {
|
||||||
|
border-color: #e5e5e5;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group--select-list {
|
||||||
|
&.bp3-intent-danger {
|
||||||
|
.bp3-button:not(.bp3-minimal) {
|
||||||
|
border-color: #db3737;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-of-type {
|
||||||
|
.td {
|
||||||
|
border-bottom: transparent;
|
||||||
|
|
||||||
|
.bp3-button,
|
||||||
|
.bp3-input-group {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.td.actions {
|
||||||
|
.bp3-button {
|
||||||
|
background-color: transparent;
|
||||||
|
color: #e66d6d;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #c23030;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.row--total {
|
||||||
|
.account.td,
|
||||||
|
.debit.td,
|
||||||
|
.credit.td {
|
||||||
|
> span {
|
||||||
|
padding-top: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.debit.td,
|
||||||
|
.credit.td {
|
||||||
|
> span {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #444;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.td {
|
||||||
|
&.note {
|
||||||
|
.bp3-form-group {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.th {
|
||||||
|
color: #444;
|
||||||
|
font-weight: 600;
|
||||||
|
border-bottom: 1px dotted #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.td {
|
||||||
|
border-bottom: 1px dotted #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions.td {
|
||||||
|
.bp3-button {
|
||||||
|
background: transparent;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.table{
|
.table{
|
||||||
.tbody{
|
.tbody{
|
||||||
.tr .td.actions .bp3-button{
|
.tr .td.actions .bp3-button{
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
.bp3-form-group{
|
.bp3-form-group{
|
||||||
&.bp3-inline{
|
&.bp3-inline{
|
||||||
max-width: 450px;
|
max-width: 420px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.form-group{
|
&.form-group{
|
||||||
@@ -45,12 +45,9 @@
|
|||||||
.row--bill-date{
|
.row--bill-date{
|
||||||
|
|
||||||
.col:first-of-type{
|
.col:first-of-type{
|
||||||
max-width: 465px;
|
max-width: 435px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.col--estimate-date{
|
|
||||||
max-width: 530px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#{$self}__footer{
|
#{$self}__footer{
|
||||||
|
|||||||
@@ -3,5 +3,57 @@
|
|||||||
|
|
||||||
#{$self}__header{
|
#{$self}__header{
|
||||||
|
|
||||||
|
.bp3-form-group{
|
||||||
|
margin-bottom: 18px;
|
||||||
|
}
|
||||||
|
.bp3-label{
|
||||||
|
min-width: 140px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bp3-form-content{
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bp3-form-group{
|
||||||
|
&.bp3-inline{
|
||||||
|
max-width: 420px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bp3-form-group{
|
||||||
|
|
||||||
|
&.form-group{
|
||||||
|
|
||||||
|
&--customer{
|
||||||
|
max-width: 650px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--expiration-date{
|
||||||
|
max-width: 340px;
|
||||||
|
|
||||||
|
.bp3-label{
|
||||||
|
min-width: 120px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.col--estimate-date{
|
||||||
|
max-width: 435px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#{$self}__footer{
|
||||||
|
.form-group--customer_note,
|
||||||
|
.form-group--terms_conditions{
|
||||||
|
max-width: 450px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
textarea{
|
||||||
|
width: 100%;
|
||||||
|
min-height: 50px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
52
client/src/style/pages/invoice-form.scss
Normal file
52
client/src/style/pages/invoice-form.scss
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
|
||||||
|
|
||||||
|
.page-form--invoice{
|
||||||
|
$self: '.page-form';
|
||||||
|
|
||||||
|
#{$self}__header{
|
||||||
|
.bp3-label{
|
||||||
|
min-width: 140px;
|
||||||
|
}
|
||||||
|
.bp3-form-content{
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bp3-form-group{
|
||||||
|
margin-bottom: 18px;
|
||||||
|
|
||||||
|
&.bp3-inline{
|
||||||
|
max-width: 420px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.form-group{
|
||||||
|
&--customer-name{
|
||||||
|
max-width: 600px
|
||||||
|
}
|
||||||
|
&--due-date{
|
||||||
|
max-width: 300px;
|
||||||
|
|
||||||
|
.bp3-label{
|
||||||
|
min-width: 95px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.col--invoice-date{
|
||||||
|
max-width: 435px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#{$self}__footer{
|
||||||
|
.form-group--invoice_message,
|
||||||
|
.form-group--terms_conditions{
|
||||||
|
max-width: 450px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
textarea{
|
||||||
|
width: 100%;
|
||||||
|
min-height: 60px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,145 +17,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__table {
|
&__table {
|
||||||
padding: 15px 15px 0;
|
|
||||||
|
|
||||||
.bp3-form-group {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
.table {
|
|
||||||
border: 1px dotted rgb(195, 195, 195);
|
|
||||||
border-bottom: transparent;
|
|
||||||
border-left: transparent;
|
|
||||||
|
|
||||||
.th,
|
|
||||||
.td {
|
|
||||||
border-left: 1px dotted rgb(195, 195, 195);
|
|
||||||
|
|
||||||
&.index {
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
span {
|
|
||||||
width: 100%;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.thead {
|
|
||||||
.tr .th {
|
|
||||||
padding: 10px 10px;
|
|
||||||
background-color: #f2f5fa;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: #1e1c3e;
|
|
||||||
|
|
||||||
&.index > div {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tbody {
|
|
||||||
.tr .td {
|
|
||||||
padding: 7px;
|
|
||||||
border-bottom: 1px dotted rgb(195, 195, 195);
|
|
||||||
min-height: 46px;
|
|
||||||
|
|
||||||
&.index {
|
|
||||||
background-color: #f2f5fa;
|
|
||||||
|
|
||||||
> span {
|
|
||||||
margin-top: auto;
|
|
||||||
margin-bottom: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.tr {
|
|
||||||
.bp3-form-group:not(.bp3-intent-danger) .bp3-input,
|
|
||||||
.form-group--select-list .bp3-button {
|
|
||||||
border-color: #e5e5e5;
|
|
||||||
border-radius: 3px;
|
|
||||||
padding-left: 8px;
|
|
||||||
padding-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group--select-list {
|
|
||||||
&.bp3-intent-danger {
|
|
||||||
.bp3-button:not(.bp3-minimal) {
|
|
||||||
border-color: #db3737;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:last-of-type {
|
|
||||||
.td {
|
|
||||||
border-bottom: transparent;
|
|
||||||
|
|
||||||
.bp3-button,
|
|
||||||
.bp3-input-group {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.td.actions {
|
|
||||||
.bp3-button {
|
|
||||||
background-color: transparent;
|
|
||||||
color: #e66d6d;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: #c23030;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg {
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.row--total {
|
|
||||||
.account.td,
|
|
||||||
.debit.td,
|
|
||||||
.credit.td {
|
|
||||||
> span {
|
|
||||||
padding-top: 2px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.debit.td,
|
|
||||||
.credit.td {
|
|
||||||
> span {
|
|
||||||
font-weight: 600;
|
|
||||||
color: #444;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.td {
|
|
||||||
&.note {
|
|
||||||
.bp3-form-group {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.th {
|
|
||||||
color: #444;
|
|
||||||
font-weight: 600;
|
|
||||||
border-bottom: 1px dotted #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.td {
|
|
||||||
border-bottom: 1px dotted #999;
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions.td {
|
|
||||||
.bp3-button {
|
|
||||||
background: transparent;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.bp3-button {
|
.bp3-button {
|
||||||
|
|||||||
41
client/src/style/pages/receipt-form.scss
Normal file
41
client/src/style/pages/receipt-form.scss
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
|
||||||
|
|
||||||
|
.page-form--receipt{
|
||||||
|
$self: '.page-form';
|
||||||
|
|
||||||
|
#{$self}__header{
|
||||||
|
.bp3-label{
|
||||||
|
min-width: 140px;
|
||||||
|
}
|
||||||
|
.bp3-form-content{
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bp3-form-group{
|
||||||
|
margin-bottom: 18px;
|
||||||
|
|
||||||
|
&.bp3-inline{
|
||||||
|
max-width: 450px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.form-group{
|
||||||
|
&--customer{
|
||||||
|
max-width: 600px
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#{$self}__footer{
|
||||||
|
.form-group--receipt_message,
|
||||||
|
.form-group--statement{
|
||||||
|
max-width: 450px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
textarea{
|
||||||
|
width: 100%;
|
||||||
|
min-height: 50px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -103,4 +103,14 @@ export default {
|
|||||||
type: "string",
|
type: "string",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
payment_receives: [
|
||||||
|
{
|
||||||
|
key: 'next_number',
|
||||||
|
type: 'number',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'number_prefix',
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
]
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,3 +10,5 @@ import 'subscribers/customers';
|
|||||||
import 'subscribers/vendors';
|
import 'subscribers/vendors';
|
||||||
import 'subscribers/paymentMades';
|
import 'subscribers/paymentMades';
|
||||||
import 'subscribers/paymentReceives';
|
import 'subscribers/paymentReceives';
|
||||||
|
import 'subscribers/saleEstimates';
|
||||||
|
import 'subscribers/saleReceipts';
|
||||||
@@ -400,6 +400,7 @@ export default class SaleInvoicesService extends SalesInvoicesCost {
|
|||||||
salesInvoicesFilter.page - 1,
|
salesInvoicesFilter.page - 1,
|
||||||
salesInvoicesFilter.pageSize,
|
salesInvoicesFilter.pageSize,
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
salesInvoices: results,
|
salesInvoices: results,
|
||||||
pagination,
|
pagination,
|
||||||
|
|||||||
30
server/src/services/Settings/SettingsService.ts
Normal file
30
server/src/services/Settings/SettingsService.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { Service, Inject } from "typedi";
|
||||||
|
import TenancyService from 'services/Tenancy/TenancyService';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export default class SettingsService {
|
||||||
|
@Inject()
|
||||||
|
tenancy: TenancyService;
|
||||||
|
|
||||||
|
@Inject('logger')
|
||||||
|
logger: any;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increment next number based on the given find query.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {any} findQuery
|
||||||
|
*/
|
||||||
|
async incrementNextNumber(tenantId: number, findQuery: any): Promise<void> {
|
||||||
|
const settings = this.tenancy.settings(tenantId);
|
||||||
|
|
||||||
|
this.logger.info('[settings] increment the next number.', { tenantId, findQuery });
|
||||||
|
const currentNumber = settings.find(findQuery);
|
||||||
|
|
||||||
|
if (currentNumber) {
|
||||||
|
const nextNumber = parseInt(currentNumber.value, 10) + 1;
|
||||||
|
settings.set(findQuery, nextNumber);
|
||||||
|
|
||||||
|
await settings.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -103,4 +103,10 @@ export default class HasTenancyService {
|
|||||||
return tenantCacheLoader(tenantId);
|
return tenantCacheLoader(tenantId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
settings(tenantId: number) {
|
||||||
|
return this.singletonService(tenantId, 'settings', () => {
|
||||||
|
throw new Error('Settings is not injected yet.');
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,7 @@ import { EventSubscriber, On } from 'event-dispatch';
|
|||||||
import events from 'subscribers/events';
|
import events from 'subscribers/events';
|
||||||
import TenancyService from 'services/Tenancy/TenancyService';
|
import TenancyService from 'services/Tenancy/TenancyService';
|
||||||
import PaymentReceiveService from 'services/Sales/PaymentsReceives';
|
import PaymentReceiveService from 'services/Sales/PaymentsReceives';
|
||||||
|
import SettingsService from 'services/Settings/SettingsService';
|
||||||
|
|
||||||
@EventSubscriber()
|
@EventSubscriber()
|
||||||
export default class PaymentReceivesSubscriber {
|
export default class PaymentReceivesSubscriber {
|
||||||
@@ -10,10 +11,14 @@ export default class PaymentReceivesSubscriber {
|
|||||||
logger: any;
|
logger: any;
|
||||||
paymentReceivesService: PaymentReceiveService;
|
paymentReceivesService: PaymentReceiveService;
|
||||||
|
|
||||||
|
settingsService: SettingsService;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.tenancy = Container.get(TenancyService);
|
this.tenancy = Container.get(TenancyService);
|
||||||
this.logger = Container.get('logger');
|
this.logger = Container.get('logger');
|
||||||
this.paymentReceivesService = Container.get(PaymentReceiveService);
|
this.paymentReceivesService = Container.get(PaymentReceiveService);
|
||||||
|
|
||||||
|
this.settingsService = Container.get(SettingsService);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -84,4 +89,15 @@ export default class PaymentReceivesSubscriber {
|
|||||||
oldPaymentReceive.customerId,
|
oldPaymentReceive.customerId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles increment next number of payment receive once be created.
|
||||||
|
*/
|
||||||
|
@On(events.paymentReceive.onCreated)
|
||||||
|
public async handlePaymentNextNumberIncrement({ tenantId, paymentReceiveId }) {
|
||||||
|
await this.settingsService.incrementNextNumber(tenantId, {
|
||||||
|
key: 'next_number',
|
||||||
|
group: 'payment_receives',
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
29
server/src/subscribers/saleEstimates.ts
Normal file
29
server/src/subscribers/saleEstimates.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { Container } from 'typedi';
|
||||||
|
import { On, EventSubscriber } from "event-dispatch";
|
||||||
|
import events from 'subscribers/events';
|
||||||
|
import TenancyService from 'services/Tenancy/TenancyService';
|
||||||
|
import SettingsService from 'services/Settings/SettingsService';
|
||||||
|
|
||||||
|
@EventSubscriber()
|
||||||
|
export default class SaleEstimateSubscriber {
|
||||||
|
logger: any;
|
||||||
|
tenancy: TenancyService;
|
||||||
|
settingsService: SettingsService;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.logger = Container.get('logger');
|
||||||
|
this.tenancy = Container.get(TenancyService);
|
||||||
|
this.settingsService = Container.get(SettingsService);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle sale estimate increment next number once be created.
|
||||||
|
*/
|
||||||
|
@On(events.saleEstimate.onCreated)
|
||||||
|
public async handleEstimateNextNumberIncrement({ tenantId, saleEstimateId }) {
|
||||||
|
await this.settingsService.incrementNextNumber(tenantId, {
|
||||||
|
key: 'next_number',
|
||||||
|
group: 'sales_estimates',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,15 +2,18 @@ import { Container } from 'typedi';
|
|||||||
import { On, EventSubscriber } from "event-dispatch";
|
import { On, EventSubscriber } from "event-dispatch";
|
||||||
import events from 'subscribers/events';
|
import events from 'subscribers/events';
|
||||||
import TenancyService from 'services/Tenancy/TenancyService';
|
import TenancyService from 'services/Tenancy/TenancyService';
|
||||||
|
import SettingsService from 'services/Settings/SettingsService';
|
||||||
|
|
||||||
@EventSubscriber()
|
@EventSubscriber()
|
||||||
export default class SaleInvoiceSubscriber {
|
export default class SaleInvoiceSubscriber {
|
||||||
logger: any;
|
logger: any;
|
||||||
tenancy: TenancyService;
|
tenancy: TenancyService;
|
||||||
|
settingsService: SettingsService;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.logger = Container.get('logger');
|
this.logger = Container.get('logger');
|
||||||
this.tenancy = Container.get(TenancyService);
|
this.tenancy = Container.get(TenancyService);
|
||||||
|
this.settingsService = Container.get(SettingsService);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,9 +51,20 @@ export default class SaleInvoiceSubscriber {
|
|||||||
const { customerRepository } = this.tenancy.repositories(tenantId);
|
const { customerRepository } = this.tenancy.repositories(tenantId);
|
||||||
|
|
||||||
this.logger.info('[sale_invoice] trying to decrement customer balance.', { tenantId });
|
this.logger.info('[sale_invoice] trying to decrement customer balance.', { tenantId });
|
||||||
await customerRepository.changeBalance(
|
await customerRepository.changeBalance(
|
||||||
oldSaleInvoice.customerId,
|
oldSaleInvoice.customerId,
|
||||||
oldSaleInvoice.balance * -1,
|
oldSaleInvoice.balance * -1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles sale invoice next number increment once invoice created.
|
||||||
|
*/
|
||||||
|
@On(events.saleInvoice.onCreated)
|
||||||
|
public async handleInvoiceNextNumberIncrement({ tenantId, saleInvoiceId, saleInvoice }) {
|
||||||
|
await this.settingsService.incrementNextNumber(tenantId, {
|
||||||
|
key: 'next_number',
|
||||||
|
group: 'sales_invoices'
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
29
server/src/subscribers/saleReceipts.ts
Normal file
29
server/src/subscribers/saleReceipts.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { Container } from 'typedi';
|
||||||
|
import { On, EventSubscriber } from "event-dispatch";
|
||||||
|
import events from 'subscribers/events';
|
||||||
|
import TenancyService from 'services/Tenancy/TenancyService';
|
||||||
|
import SettingsService from 'services/Settings/SettingsService';
|
||||||
|
|
||||||
|
@EventSubscriber()
|
||||||
|
export default class SaleReceiptSubscriber {
|
||||||
|
logger: any;
|
||||||
|
tenancy: TenancyService;
|
||||||
|
settingsService: SettingsService;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.logger = Container.get('logger');
|
||||||
|
this.tenancy = Container.get(TenancyService);
|
||||||
|
this.settingsService = Container.get(SettingsService);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle sale receipt increment next number once be created.
|
||||||
|
*/
|
||||||
|
@On(events.saleReceipt.onCreated)
|
||||||
|
public async handleReceiptNextNumberIncrement({ tenantId, saleReceiptId }) {
|
||||||
|
await this.settingsService.incrementNextNumber(tenantId, {
|
||||||
|
key: 'next_number',
|
||||||
|
group: 'sales_receipts',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user