mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-22 15:50:32 +00:00
Merge branch 'master' of https://github.com/abouolia/Bigcapital
This commit is contained in:
@@ -51,6 +51,6 @@ function Dashboard({
|
|||||||
<DrawersContainer />
|
<DrawersContainer />
|
||||||
</DashboardLoadingIndicator>
|
</DashboardLoadingIndicator>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default compose(withSettingsActions)(Dashboard);
|
export default compose(withSettingsActions)(Dashboard);
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import React, { useEffect, Suspense } from 'react';
|
import React from 'react';
|
||||||
import { Route, Switch } from 'react-router-dom';
|
import { Route, Switch } from 'react-router-dom';
|
||||||
import routes from 'routes/dashboard';
|
import routes from 'routes/dashboard';
|
||||||
|
|
||||||
import DashboardPage from './DashboardPage';
|
import DashboardPage from './DashboardPage';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -18,6 +17,7 @@ export default function DashboardContentRoute() {
|
|||||||
path={`${route.path}`}
|
path={`${route.path}`}
|
||||||
>
|
>
|
||||||
<DashboardPage
|
<DashboardPage
|
||||||
|
name={route.name}
|
||||||
Component={route.component}
|
Component={route.component}
|
||||||
pageTitle={route.pageTitle}
|
pageTitle={route.pageTitle}
|
||||||
backLink={route.backLink}
|
backLink={route.backLink}
|
||||||
|
|||||||
@@ -6,14 +6,14 @@ import { For } from 'components';
|
|||||||
function FooterLinkItem({ title, link }) {
|
function FooterLinkItem({ title, link }) {
|
||||||
return (
|
return (
|
||||||
<div class="">
|
<div class="">
|
||||||
<a href={link} target="_blank">
|
<a href={link} target="_blank" rel="noopener noreferrer">
|
||||||
{title}
|
{title}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function DashboardFooter({}) {
|
export default function DashboardFooter() {
|
||||||
return (
|
return (
|
||||||
<div class="dashboard__footer">
|
<div class="dashboard__footer">
|
||||||
<div class="footer-links">
|
<div class="footer-links">
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ function DashboardPage({
|
|||||||
backLink,
|
backLink,
|
||||||
sidebarShrink,
|
sidebarShrink,
|
||||||
Component,
|
Component,
|
||||||
|
name,
|
||||||
|
|
||||||
// #withDashboardActions
|
// #withDashboardActions
|
||||||
changePageTitle,
|
changePageTitle,
|
||||||
@@ -44,6 +45,15 @@ function DashboardPage({
|
|||||||
};
|
};
|
||||||
}, [resetSidebarPreviousExpand, sidebarShrink, setSidebarShrink]);
|
}, [resetSidebarPreviousExpand, sidebarShrink, setSidebarShrink]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const className = `page-${name}`;
|
||||||
|
name && document.body.classList.add(className);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
name && document.body.classList.remove(className);
|
||||||
|
};
|
||||||
|
}, [name]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={CLASSES.DASHBOARD_PAGE}>
|
<div className={CLASSES.DASHBOARD_PAGE}>
|
||||||
<Suspense fallback={''}>
|
<Suspense fallback={''}>
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ export default function ItemsListCell({
|
|||||||
purchasable={filterPurchasable}
|
purchasable={filterPurchasable}
|
||||||
inputProps={{
|
inputProps={{
|
||||||
inputRef: (ref) => (fieldRef.current = ref),
|
inputRef: (ref) => (fieldRef.current = ref),
|
||||||
|
placeholder: 'Enter an item...'
|
||||||
}}
|
}}
|
||||||
openOnKeyDown={true}
|
openOnKeyDown={true}
|
||||||
blurOnSelectClose={false}
|
blurOnSelectClose={false}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ const PercentFieldCell = ({
|
|||||||
return (
|
return (
|
||||||
<FormGroup intent={error ? Intent.DANGER : null}>
|
<FormGroup intent={error ? Intent.DANGER : null}>
|
||||||
<MoneyInputGroup
|
<MoneyInputGroup
|
||||||
|
prefix={'%'}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
onBlurValue={handleBlurChange}
|
onBlurValue={handleBlurChange}
|
||||||
|
|||||||
@@ -1,22 +1,50 @@
|
|||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { If } from 'components';
|
import { If } from 'components';
|
||||||
import { ConditionalWrapper } from 'utils';
|
import { Skeleton } from 'components';
|
||||||
import TableContext from './TableContext';
|
import TableContext from './TableContext';
|
||||||
|
import { isCellLoading } from './utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tabl cell.
|
* Table cell.
|
||||||
*/
|
*/
|
||||||
export default function TableCell({
|
export default function TableCell({
|
||||||
cell,
|
cell,
|
||||||
row: { depth, getToggleRowExpandedProps, isExpanded },
|
row: { index: rowIndex, depth, getToggleRowExpandedProps, isExpanded },
|
||||||
index,
|
index,
|
||||||
}) {
|
}) {
|
||||||
const {
|
const {
|
||||||
props: { expandToggleColumn, expandColumnSpace, expandable },
|
props: {
|
||||||
|
expandToggleColumn,
|
||||||
|
expandColumnSpace,
|
||||||
|
expandable,
|
||||||
|
cellsLoading,
|
||||||
|
cellsLoadingCoords,
|
||||||
|
},
|
||||||
} = useContext(TableContext);
|
} = useContext(TableContext);
|
||||||
|
|
||||||
const isExpandColumn = expandToggleColumn === index;
|
const isExpandColumn = expandToggleColumn === index;
|
||||||
|
const { skeletonWidthMax = 100, skeletonWidthMin = 40 } = {};
|
||||||
|
|
||||||
|
// Detarmines whether the current cell is loading.
|
||||||
|
const cellLoading = isCellLoading(
|
||||||
|
cellsLoading,
|
||||||
|
cellsLoadingCoords,
|
||||||
|
rowIndex,
|
||||||
|
cell.column.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (cellLoading) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
{...cell.getCellProps({
|
||||||
|
className: classNames(cell.column.className, 'td'),
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Skeleton minWidth={skeletonWidthMin} maxWidth={skeletonWidthMax} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -27,9 +55,12 @@ export default function TableCell({
|
|||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={classNames({
|
className={classNames(
|
||||||
'text-overview': cell.column.textOverview,
|
{
|
||||||
}, 'cell-inner')}
|
'text-overview': cell.column.textOverview,
|
||||||
|
},
|
||||||
|
'cell-inner',
|
||||||
|
)}
|
||||||
style={{
|
style={{
|
||||||
'padding-left':
|
'padding-left':
|
||||||
isExpandColumn && expandable
|
isExpandColumn && expandable
|
||||||
|
|||||||
@@ -6,9 +6,13 @@ import TableContext from './TableContext';
|
|||||||
*/
|
*/
|
||||||
export default function TableFooter() {
|
export default function TableFooter() {
|
||||||
const {
|
const {
|
||||||
|
props: { footer },
|
||||||
table: { footerGroups },
|
table: { footerGroups },
|
||||||
} = useContext(TableContext);
|
} = useContext(TableContext);
|
||||||
|
|
||||||
|
// Can't contiunue if the footer is disabled.
|
||||||
|
if (!footer) { return null; }
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="tfooter">
|
<div class="tfooter">
|
||||||
{footerGroups.map((group) => (
|
{footerGroups.map((group) => (
|
||||||
|
|||||||
10
client/src/components/Datatable/utils.js
Normal file
10
client/src/components/Datatable/utils.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
export const isCellLoading = (loading, cellsCoords, rowIndex, columnId) => {
|
||||||
|
if (!loading) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !cellsCoords
|
||||||
|
? true
|
||||||
|
: cellsCoords.some(
|
||||||
|
(cellCoord) => cellCoord[0] === rowIndex && cellCoord[1] === columnId,
|
||||||
|
);
|
||||||
|
};
|
||||||
41
client/src/components/Postbox.js
Normal file
41
client/src/components/Postbox.js
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { Collapse } from '@blueprintjs/core';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Postbox.
|
||||||
|
*/
|
||||||
|
export default function Postbox({
|
||||||
|
defaultOpen = true,
|
||||||
|
toggable = true,
|
||||||
|
title,
|
||||||
|
children,
|
||||||
|
}) {
|
||||||
|
const [isOpen, setIsOpen] = useState(defaultOpen);
|
||||||
|
|
||||||
|
// Handle the title click.
|
||||||
|
const handleTitleClick = () => {
|
||||||
|
if (toggable) {
|
||||||
|
setIsOpen(!isOpen);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
class={classNames('postbox', {
|
||||||
|
'is-toggable': toggable,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<div class="postbox__header" onClick={handleTitleClick}>
|
||||||
|
<h5 class="postbox__title">{title}</h5>
|
||||||
|
|
||||||
|
<span class="postbox__toggle-indicator"></span>
|
||||||
|
</div>
|
||||||
|
<div class="postbox__content">
|
||||||
|
<Collapse isOpen={isOpen}>
|
||||||
|
<div class="postbox__content-inner">{children}</div>
|
||||||
|
</Collapse>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -51,6 +51,7 @@ import DashboardPageContent from './Dashboard/DashboardPageContent';
|
|||||||
import DashboardInsider from './Dashboard/DashboardInsider';
|
import DashboardInsider from './Dashboard/DashboardInsider';
|
||||||
import Drawer from './Drawer/Drawer';
|
import Drawer from './Drawer/Drawer';
|
||||||
import DrawerSuspense from './Drawer/DrawerSuspense';
|
import DrawerSuspense from './Drawer/DrawerSuspense';
|
||||||
|
import Postbox from './Postbox';
|
||||||
|
|
||||||
const Hint = FieldHint;
|
const Hint = FieldHint;
|
||||||
|
|
||||||
@@ -109,4 +110,5 @@ export {
|
|||||||
DashboardInsider,
|
DashboardInsider,
|
||||||
Drawer,
|
Drawer,
|
||||||
DrawerSuspense,
|
DrawerSuspense,
|
||||||
|
Postbox
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ export default [
|
|||||||
{
|
{
|
||||||
text: <T id={'homepage'} />,
|
text: <T id={'homepage'} />,
|
||||||
disabled: false,
|
disabled: false,
|
||||||
href: '/homepage',
|
href: '/',
|
||||||
|
matchExact: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: 'Sales & inventory',
|
text: 'Sales & inventory',
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
import 'style/pages/ManualJournal/List.scss';
|
||||||
|
|
||||||
import { DashboardContentTable, DashboardPageContent } from 'components';
|
import { DashboardContentTable, DashboardPageContent } from 'components';
|
||||||
|
|
||||||
import { ManualJournalsListProvider } from './ManualJournalsListProvider';
|
import { ManualJournalsListProvider } from './ManualJournalsListProvider';
|
||||||
@@ -9,11 +11,8 @@ import ManualJournalsDataTable from './ManualJournalsDataTable';
|
|||||||
import ManualJournalsActionsBar from './ManualJournalActionsBar';
|
import ManualJournalsActionsBar from './ManualJournalActionsBar';
|
||||||
|
|
||||||
import withManualJournals from './withManualJournals';
|
import withManualJournals from './withManualJournals';
|
||||||
|
|
||||||
import { transformTableStateToQuery, compose } from 'utils';
|
import { transformTableStateToQuery, compose } from 'utils';
|
||||||
|
|
||||||
import 'style/pages/ManualJournal/List.scss';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manual journals table.
|
* Manual journals table.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -169,6 +169,7 @@ function MakeJournalEntriesForm({
|
|||||||
<MakeJournalFormFooter />
|
<MakeJournalFormFooter />
|
||||||
<MakeJournalFormFloatingActions />
|
<MakeJournalFormFloatingActions />
|
||||||
|
|
||||||
|
{/* --------- Dialogs --------- */}
|
||||||
<MakeJournalFormDialogs />
|
<MakeJournalFormDialogs />
|
||||||
</Form>
|
</Form>
|
||||||
</Formik>
|
</Formik>
|
||||||
|
|||||||
@@ -1,12 +1,27 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import { useFormikContext } from 'formik';
|
||||||
import { CLASSES } from 'common/classes';
|
import { CLASSES } from 'common/classes';
|
||||||
import MakeJournalEntriesHeaderFields from "./MakeJournalEntriesHeaderFields";
|
import MakeJournalEntriesHeaderFields from "./MakeJournalEntriesHeaderFields";
|
||||||
|
import { PageFormBigNumber } from 'components';
|
||||||
|
import { safeSumBy } from 'utils';
|
||||||
|
|
||||||
export default function MakeJournalEntriesHeader() {
|
export default function MakeJournalEntriesHeader() {
|
||||||
|
const { values: { entries } } = useFormikContext();
|
||||||
|
const totalCredit = safeSumBy(entries, 'credit');
|
||||||
|
const totalDebit = safeSumBy(entries, 'debit');
|
||||||
|
|
||||||
|
const total = Math.max(totalCredit, totalDebit);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
|
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
|
||||||
<MakeJournalEntriesHeaderFields />
|
<MakeJournalEntriesHeaderFields />
|
||||||
|
|
||||||
|
<PageFormBigNumber
|
||||||
|
label={'Due Amount'}
|
||||||
|
amount={total}
|
||||||
|
currencyCode={'USD'}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
|
import 'style/pages/ManualJournal/MakeJournal.scss';
|
||||||
|
|
||||||
import MakeJournalEntriesForm from './MakeJournalEntriesForm';
|
import MakeJournalEntriesForm from './MakeJournalEntriesForm';
|
||||||
import { MakeJournalProvider } from './MakeJournalProvider';
|
import { MakeJournalProvider } from './MakeJournalProvider';
|
||||||
|
|
||||||
import 'style/pages/ManualJournal/MakeJournal.scss';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make journal entries page.
|
* Make journal entries page.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -9,9 +9,7 @@ import { updateDataReducer } from './utils';
|
|||||||
import { useMakeJournalFormContext } from './MakeJournalProvider';
|
import { useMakeJournalFormContext } from './MakeJournalProvider';
|
||||||
import { useJournalTableEntriesColumns } from './components';
|
import { useJournalTableEntriesColumns } from './components';
|
||||||
|
|
||||||
import JournalDeleteEntriesAlert from 'containers/Alerts/ManualJournals/JournalDeleteEntriesAlert';
|
|
||||||
import { compose } from 'redux';
|
import { compose } from 'redux';
|
||||||
import { repeatValue } from 'utils';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make journal entries table component.
|
* Make journal entries table component.
|
||||||
@@ -32,13 +30,7 @@ function MakeJournalEntriesTable({
|
|||||||
|
|
||||||
// Memorized data table columns.
|
// Memorized data table columns.
|
||||||
const columns = useJournalTableEntriesColumns();
|
const columns = useJournalTableEntriesColumns();
|
||||||
|
|
||||||
// Handles click new line.
|
|
||||||
const onClickNewRow = () => {
|
|
||||||
const newRows = [...entries, defaultEntry];
|
|
||||||
saveInvoke(onChange, newRows);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Handles update datatable data.
|
// Handles update datatable data.
|
||||||
const handleUpdateData = (rowIndex, columnId, value) => {
|
const handleUpdateData = (rowIndex, columnId, value) => {
|
||||||
const newRows = updateDataReducer(entries, rowIndex, columnId, value);
|
const newRows = updateDataReducer(entries, rowIndex, columnId, value);
|
||||||
@@ -50,61 +42,28 @@ function MakeJournalEntriesTable({
|
|||||||
const newRows = removeRowsByIndex(entries, rowIndex);
|
const newRows = removeRowsByIndex(entries, rowIndex);
|
||||||
saveInvoke(onChange, newRows);
|
saveInvoke(onChange, newRows);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle clear all lines action.
|
|
||||||
const handleClickClearAllLines = () => {
|
|
||||||
openAlert('make-journal-delete-all-entries');
|
|
||||||
};
|
|
||||||
|
|
||||||
// Handle clear all lines alaert confirm.
|
|
||||||
const handleCofirmClearEntriesAlert = () => {
|
|
||||||
const newRows = repeatValue(defaultEntry, initialLinesNumber);
|
|
||||||
saveInvoke(onChange, newRows);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
<DataTableEditable
|
|
||||||
columns={columns}
|
|
||||||
data={entries}
|
|
||||||
sticky={true}
|
|
||||||
totalRow={true}
|
|
||||||
payload={{
|
|
||||||
accounts,
|
|
||||||
errors: error,
|
|
||||||
updateData: handleUpdateData,
|
|
||||||
removeRow: handleRemoveRow,
|
|
||||||
contacts: customers.map((customer) => ({
|
|
||||||
...customer,
|
|
||||||
contact_type: 'customer',
|
|
||||||
})),
|
|
||||||
autoFocus: ['account_id', 0],
|
|
||||||
}}
|
|
||||||
actions={
|
|
||||||
<>
|
|
||||||
<Button
|
|
||||||
small={true}
|
|
||||||
className={'button--secondary button--new-line'}
|
|
||||||
onClick={onClickNewRow}
|
|
||||||
>
|
|
||||||
<T id={'new_lines'} />
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
<DataTableEditable
|
||||||
small={true}
|
columns={columns}
|
||||||
className={'button--secondary button--clear-lines ml1'}
|
data={entries}
|
||||||
onClick={handleClickClearAllLines}
|
sticky={true}
|
||||||
>
|
totalRow={true}
|
||||||
<T id={'clear_all_lines'} />
|
footer={true}
|
||||||
</Button>
|
payload={{
|
||||||
</>
|
accounts,
|
||||||
}
|
errors: error,
|
||||||
/>
|
updateData: handleUpdateData,
|
||||||
<JournalDeleteEntriesAlert
|
removeRow: handleRemoveRow,
|
||||||
name={'make-journal-delete-all-entries'}
|
contacts: customers.map((customer) => ({
|
||||||
onConfirm={handleCofirmClearEntriesAlert}
|
...customer,
|
||||||
/>
|
contact_type: 'customer',
|
||||||
</>
|
})),
|
||||||
|
autoFocus: ['account_id', 0],
|
||||||
|
}}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,39 +4,41 @@ import classNames from 'classnames';
|
|||||||
import { CLASSES } from 'common/classes';
|
import { CLASSES } from 'common/classes';
|
||||||
import { FormGroup, TextArea } from '@blueprintjs/core';
|
import { FormGroup, TextArea } from '@blueprintjs/core';
|
||||||
import { FormattedMessage as T } from 'react-intl';
|
import { FormattedMessage as T } from 'react-intl';
|
||||||
import { ErrorMessage, Row, Col } from 'components';
|
import { Postbox, ErrorMessage, Row, Col } from 'components';
|
||||||
import Dragzone from 'components/Dragzone';
|
import Dragzone from 'components/Dragzone';
|
||||||
import { inputIntent } from 'utils';
|
import { inputIntent } from 'utils';
|
||||||
|
|
||||||
export default function MakeJournalFormFooter() {
|
export default function MakeJournalFormFooter() {
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
<div className={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
||||||
<Row>
|
<Postbox title={'Journal details'} defaultOpen={false}>
|
||||||
<Col md={8}>
|
<Row>
|
||||||
<FastField name={'description'}>
|
<Col md={8}>
|
||||||
{({ field, meta: { error, touched } }) => (
|
<FastField name={'description'}>
|
||||||
<FormGroup
|
{({ field, meta: { error, touched } }) => (
|
||||||
label={<T id={'description'} />}
|
<FormGroup
|
||||||
className={'form-group--description'}
|
label={<T id={'description'} />}
|
||||||
intent={inputIntent({ error, touched })}
|
className={'form-group--description'}
|
||||||
helperText={<ErrorMessage name="description" />}
|
intent={inputIntent({ error, touched })}
|
||||||
fill={true}
|
helperText={<ErrorMessage name="description" />}
|
||||||
>
|
fill={true}
|
||||||
<TextArea fill={true} {...field} />
|
>
|
||||||
</FormGroup>
|
<TextArea fill={true} {...field} />
|
||||||
)}
|
</FormGroup>
|
||||||
</FastField>
|
)}
|
||||||
</Col>
|
</FastField>
|
||||||
|
</Col>
|
||||||
|
|
||||||
<Col md={4}>
|
<Col md={4}>
|
||||||
<Dragzone
|
<Dragzone
|
||||||
initialFiles={[]}
|
initialFiles={[]}
|
||||||
// onDrop={handleDropFiles}
|
// onDrop={handleDropFiles}
|
||||||
// onDeleteFile={handleDeleteFile}
|
// onDeleteFile={handleDeleteFile}
|
||||||
hint={'Attachments: Maxiumum size: 20MB'}
|
hint={'Attachments: Maxiumum size: 20MB'}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
</Postbox>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,9 @@ function InvoiceNumberDialogContent({
|
|||||||
const { mutateAsync: saveSettings } = useSaveSettings();
|
const { mutateAsync: saveSettings } = useSaveSettings();
|
||||||
|
|
||||||
const handleSubmitForm = (values, { setSubmitting }) => {
|
const handleSubmitForm = (values, { setSubmitting }) => {
|
||||||
const options = optionsMapToArray(values).map((option) => ({
|
const { mode, ...autoModeValues } = values;
|
||||||
|
|
||||||
|
const options = optionsMapToArray(autoModeValues).map((option) => ({
|
||||||
key: option.key,
|
key: option.key,
|
||||||
...option,
|
...option,
|
||||||
group: 'sales_invoices',
|
group: 'sales_invoices',
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ function InvoiceNumberDialog({
|
|||||||
isOpen,
|
isOpen,
|
||||||
onConfirm,
|
onConfirm,
|
||||||
}) {
|
}) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
title={<T id={'invoice_number_settings'} />}
|
title={<T id={'invoice_number_settings'} />}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import React, { useCallback } from 'react';
|
import React, { useEffect, useCallback } from 'react';
|
||||||
import { Button } from '@blueprintjs/core';
|
import { Button } from '@blueprintjs/core';
|
||||||
import { FormattedMessage as T } from 'react-intl';
|
import { FormattedMessage as T } from 'react-intl';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import { useItem } from 'hooks/query';
|
||||||
|
|
||||||
import ItemsEntriesDeleteAlert from 'containers/Alerts/ItemsEntries/ItemsEntriesDeleteAlert';
|
import ItemsEntriesDeleteAlert from 'containers/Alerts/ItemsEntries/ItemsEntriesDeleteAlert';
|
||||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||||
@@ -17,7 +18,15 @@ import {
|
|||||||
removeRowsByIndex,
|
removeRowsByIndex,
|
||||||
compose,
|
compose,
|
||||||
} from 'utils';
|
} from 'utils';
|
||||||
import { updateItemsEntriesTotal } from './utils';
|
import { updateItemsEntriesTotal, ITEM_TYPE } from './utils';
|
||||||
|
import { last } from 'lodash';
|
||||||
|
|
||||||
|
const updateAutoAddNewLine = (defaultEntry) => (entries) => {
|
||||||
|
const newEntries = [...entries];
|
||||||
|
const lastEntry = last(newEntries);
|
||||||
|
|
||||||
|
return (lastEntry.item_id) ? [...entries, defaultEntry] : [...entries];
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Items entries table.
|
* Items entries table.
|
||||||
@@ -34,11 +43,64 @@ function ItemsEntriesTable({
|
|||||||
errors,
|
errors,
|
||||||
onUpdateData,
|
onUpdateData,
|
||||||
linesNumber,
|
linesNumber,
|
||||||
|
itemType, // sellable or purchasable
|
||||||
}) {
|
}) {
|
||||||
const [rows, setRows] = React.useState(initialEntries);
|
const [rows, setRows] = React.useState(initialEntries);
|
||||||
|
const [rowItem, setRowItem] = React.useState(null);
|
||||||
|
const [cellsLoading, setCellsLoading] = React.useState(null);
|
||||||
|
|
||||||
|
// Fetches the item details.
|
||||||
|
const { data: item, isFetching: isItemFetching } = useItem(
|
||||||
|
rowItem && rowItem.itemId,
|
||||||
|
{
|
||||||
|
enabled: !!rowItem,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Once the item start loading give the table cells loading state.
|
||||||
|
useEffect(() => {
|
||||||
|
if (rowItem && isItemFetching) {
|
||||||
|
setCellsLoading([
|
||||||
|
[rowItem.rowIndex, 'rate'],
|
||||||
|
[rowItem.rowIndex, 'description'],
|
||||||
|
[rowItem.rowIndex, 'quantity'],
|
||||||
|
[rowItem.rowIndex, 'discount'],
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
setCellsLoading(null);
|
||||||
|
}
|
||||||
|
}, [isItemFetching, setCellsLoading, rowItem]);
|
||||||
|
|
||||||
|
// Once the item selected and fetched set the initial details to the table.
|
||||||
|
useEffect(() => {
|
||||||
|
if (item && rowItem) {
|
||||||
|
const { rowIndex } = rowItem;
|
||||||
|
const price =
|
||||||
|
itemType === ITEM_TYPE.PURCHASABLE
|
||||||
|
? item.purchase_price
|
||||||
|
: item.sell_price;
|
||||||
|
|
||||||
|
const description =
|
||||||
|
itemType === ITEM_TYPE.PURCHASABLE
|
||||||
|
? item.purchase_description
|
||||||
|
: item.sell_description;
|
||||||
|
|
||||||
|
// Update the rate, description and quantity data of the row.
|
||||||
|
const newRows = compose(
|
||||||
|
updateItemsEntriesTotal,
|
||||||
|
updateTableRow(rowIndex, 'rate', price),
|
||||||
|
updateTableRow(rowIndex, 'description', description),
|
||||||
|
updateTableRow(rowIndex, 'quantity', 1),
|
||||||
|
)(rows);
|
||||||
|
|
||||||
|
setRows(newRows);
|
||||||
|
setRowItem(null);
|
||||||
|
saveInvoke(onUpdateData, newRows);
|
||||||
|
}
|
||||||
|
}, [item, rowItem, rows, itemType, onUpdateData]);
|
||||||
|
|
||||||
// Allows to observes `entries` to make table rows outside controlled.
|
// Allows to observes `entries` to make table rows outside controlled.
|
||||||
React.useEffect(() => {
|
useEffect(() => {
|
||||||
if (entries && entries !== rows) {
|
if (entries && entries !== rows) {
|
||||||
setRows(entries);
|
setRows(entries);
|
||||||
}
|
}
|
||||||
@@ -50,15 +112,19 @@ function ItemsEntriesTable({
|
|||||||
// Handles the editor data update.
|
// Handles the editor data update.
|
||||||
const handleUpdateData = useCallback(
|
const handleUpdateData = useCallback(
|
||||||
(rowIndex, columnId, value) => {
|
(rowIndex, columnId, value) => {
|
||||||
|
if (columnId === 'item_id') {
|
||||||
|
setRowItem({ rowIndex, columnId, itemId: value });
|
||||||
|
}
|
||||||
const newRows = compose(
|
const newRows = compose(
|
||||||
|
updateAutoAddNewLine(defaultEntry),
|
||||||
updateItemsEntriesTotal,
|
updateItemsEntriesTotal,
|
||||||
updateTableRow(rowIndex, columnId, value),
|
updateTableRow(rowIndex, columnId, value),
|
||||||
)(entries);
|
)(rows);
|
||||||
|
|
||||||
setRows(newRows);
|
setRows(newRows);
|
||||||
onUpdateData(newRows);
|
onUpdateData(newRows);
|
||||||
},
|
},
|
||||||
[entries, onUpdateData],
|
[rows, defaultEntry, onUpdateData],
|
||||||
);
|
);
|
||||||
|
|
||||||
// Handle table rows removing by index.
|
// Handle table rows removing by index.
|
||||||
@@ -80,9 +146,7 @@ function ItemsEntriesTable({
|
|||||||
openAlert('items-entries-clear-lines');
|
openAlert('items-entries-clear-lines');
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
// Handle alert confirm of clear all lines.
|
||||||
* Handle alert confirm of clear all lines.
|
|
||||||
*/
|
|
||||||
const handleClearLinesAlertConfirm = () => {
|
const handleClearLinesAlertConfirm = () => {
|
||||||
const newRows = repeatValue(defaultEntry, linesNumber);
|
const newRows = repeatValue(defaultEntry, linesNumber);
|
||||||
setRows(newRows);
|
setRows(newRows);
|
||||||
@@ -94,8 +158,12 @@ function ItemsEntriesTable({
|
|||||||
<DataTableEditable
|
<DataTableEditable
|
||||||
className={classNames(CLASSES.DATATABLE_EDITOR_ITEMS_ENTRIES)}
|
className={classNames(CLASSES.DATATABLE_EDITOR_ITEMS_ENTRIES)}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={entries}
|
data={rows}
|
||||||
sticky={true}
|
sticky={true}
|
||||||
|
progressBarLoading={isItemFetching}
|
||||||
|
cellsLoading={isItemFetching}
|
||||||
|
cellsLoadingCoords={cellsLoading}
|
||||||
|
footer={true}
|
||||||
payload={{
|
payload={{
|
||||||
items,
|
items,
|
||||||
errors: errors || [],
|
errors: errors || [],
|
||||||
@@ -103,25 +171,7 @@ function ItemsEntriesTable({
|
|||||||
removeRow: handleRemoveRow,
|
removeRow: handleRemoveRow,
|
||||||
autoFocus: ['item_id', 0],
|
autoFocus: ['item_id', 0],
|
||||||
}}
|
}}
|
||||||
actions={
|
|
||||||
<>
|
|
||||||
<Button
|
|
||||||
small={true}
|
|
||||||
className={'button--secondary button--new-line'}
|
|
||||||
onClick={onClickNewRow}
|
|
||||||
>
|
|
||||||
<T id={'new_lines'} />
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
small={true}
|
|
||||||
className={'button--secondary button--clear-lines ml1'}
|
|
||||||
onClick={handleClickClearAllLines}
|
|
||||||
>
|
|
||||||
<T id={'clear_all_lines'} />
|
|
||||||
</Button>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
<ItemsEntriesDeleteAlert
|
<ItemsEntriesDeleteAlert
|
||||||
name={'items-entries-clear-lines'}
|
name={'items-entries-clear-lines'}
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||||
import { Tooltip, Button, Intent, Position } from '@blueprintjs/core';
|
import { Tooltip, Button, Intent, Position } from '@blueprintjs/core';
|
||||||
import { sumBy } from 'lodash';
|
|
||||||
import { Hint, Icon } from 'components';
|
import { Hint, Icon } from 'components';
|
||||||
import { formattedAmount } from 'utils';
|
import { formattedAmount, safeSumBy } from 'utils';
|
||||||
import {
|
import {
|
||||||
InputGroupCell,
|
InputGroupCell,
|
||||||
MoneyFieldCell,
|
MoneyFieldCell,
|
||||||
@@ -62,7 +61,7 @@ export function ActionsCellRenderer({
|
|||||||
* Quantity total footer cell.
|
* Quantity total footer cell.
|
||||||
*/
|
*/
|
||||||
export function QuantityTotalFooterCell({ rows }) {
|
export function QuantityTotalFooterCell({ rows }) {
|
||||||
const quantity = sumBy(rows, r => parseInt(r.original.quantity, 10));
|
const quantity = safeSumBy(rows, 'original.quantity');
|
||||||
return <span>{ quantity }</span>;
|
return <span>{ quantity }</span>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +69,7 @@ export function QuantityTotalFooterCell({ rows }) {
|
|||||||
* Total footer cell.
|
* Total footer cell.
|
||||||
*/
|
*/
|
||||||
export function TotalFooterCell({ rows }) {
|
export function TotalFooterCell({ rows }) {
|
||||||
const total = sumBy(rows, 'original.total');
|
const total = safeSumBy(rows, 'original.total');
|
||||||
return <span>{ formattedAmount(total, 'USD') }</span>;
|
return <span>{ formattedAmount(total, 'USD') }</span>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,9 +109,8 @@ export function useEditableItemsEntriesColumns() {
|
|||||||
Cell: ItemsListCell,
|
Cell: ItemsListCell,
|
||||||
Footer: ItemFooterCell,
|
Footer: ItemFooterCell,
|
||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
width: 180,
|
width: 130,
|
||||||
// filterPurchasable: filterPurchasableItems,
|
className: 'item',
|
||||||
// filterSellable: filterSellableItems,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: formatMessage({ id: 'description' }),
|
Header: formatMessage({ id: 'description' }),
|
||||||
@@ -120,7 +118,7 @@ export function useEditableItemsEntriesColumns() {
|
|||||||
Cell: InputGroupCell,
|
Cell: InputGroupCell,
|
||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
className: 'description',
|
className: 'description',
|
||||||
width: 100,
|
width: 120,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: formatMessage({ id: 'quantity' }),
|
Header: formatMessage({ id: 'quantity' }),
|
||||||
@@ -128,7 +126,7 @@ export function useEditableItemsEntriesColumns() {
|
|||||||
Cell: NumericInputCell,
|
Cell: NumericInputCell,
|
||||||
Footer: QuantityTotalFooterCell,
|
Footer: QuantityTotalFooterCell,
|
||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
width: 80,
|
width: 70,
|
||||||
className: 'quantity',
|
className: 'quantity',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -136,7 +134,7 @@ export function useEditableItemsEntriesColumns() {
|
|||||||
accessor: 'rate',
|
accessor: 'rate',
|
||||||
Cell: MoneyFieldCell,
|
Cell: MoneyFieldCell,
|
||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
width: 80,
|
width: 70,
|
||||||
className: 'rate',
|
className: 'rate',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -144,7 +142,7 @@ export function useEditableItemsEntriesColumns() {
|
|||||||
accessor: 'discount',
|
accessor: 'discount',
|
||||||
Cell: PercentFieldCell,
|
Cell: PercentFieldCell,
|
||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
width: 80,
|
width: 60,
|
||||||
className: 'discount',
|
className: 'discount',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -153,7 +151,7 @@ export function useEditableItemsEntriesColumns() {
|
|||||||
accessor: 'total',
|
accessor: 'total',
|
||||||
Cell: TotalCell,
|
Cell: TotalCell,
|
||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
width: 120,
|
width: 100,
|
||||||
className: 'total',
|
className: 'total',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -23,4 +23,9 @@ export function updateItemsEntriesTotal(rows) {
|
|||||||
...row,
|
...row,
|
||||||
total: calcItemEntryTotal(row.discount, row.quantity, row.rate)
|
total: calcItemEntryTotal(row.discount, row.quantity, row.rate)
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const ITEM_TYPE = {
|
||||||
|
SELLABLE: 'SELLABLE',
|
||||||
|
PURCHASABLE: 'PURCHASABLE',
|
||||||
|
};
|
||||||
|
|||||||
@@ -118,8 +118,8 @@ function ExpenseForm({
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Handle request error
|
// Handle request error
|
||||||
const handleError = (error) => {
|
const handleError = ({ response: { data: { errors } } }) => {
|
||||||
transformErrors(error, { setErrors });
|
transformErrors(errors, { setErrors });
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
};
|
};
|
||||||
if (isNewMode) {
|
if (isNewMode) {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { transformUpdatedRows, compose, saveInvoke, repeatValue } from 'utils';
|
|||||||
* Expenses form entries.
|
* Expenses form entries.
|
||||||
*/
|
*/
|
||||||
function ExpenseFormEntriesTable({
|
function ExpenseFormEntriesTable({
|
||||||
// #withAlertActions
|
// #withAlertActions
|
||||||
openAlert,
|
openAlert,
|
||||||
|
|
||||||
// #ownPorps
|
// #ownPorps
|
||||||
@@ -57,65 +57,21 @@ function ExpenseFormEntriesTable({
|
|||||||
[entries, onChange],
|
[entries, onChange],
|
||||||
);
|
);
|
||||||
|
|
||||||
// Invoke when click on add new line button.
|
|
||||||
const onClickNewRow = () => {
|
|
||||||
const newRows = [...entries, defaultEntry];
|
|
||||||
saveInvoke(onChange, newRows);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Invoke when click on clear all lines button.
|
|
||||||
const handleClickClearAllLines = () => {
|
|
||||||
openAlert('expense-delete-entries');
|
|
||||||
};
|
|
||||||
|
|
||||||
// handle confirm clear all entries alert.
|
|
||||||
const handleConfirmClearEntriesAlert = () => {
|
|
||||||
const newRows = repeatValue(defaultEntry, 3);
|
|
||||||
saveInvoke(onChange, newRows);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<DataTableEditable
|
||||||
<DataTableEditable
|
columns={columns}
|
||||||
columns={columns}
|
data={entries}
|
||||||
data={entries}
|
sticky={true}
|
||||||
sticky={true}
|
payload={{
|
||||||
payload={{
|
accounts: accounts,
|
||||||
accounts: accounts,
|
errors: error,
|
||||||
errors: error,
|
updateData: handleUpdateData,
|
||||||
updateData: handleUpdateData,
|
removeRow: handleRemoveRow,
|
||||||
removeRow: handleRemoveRow,
|
autoFocus: ['expense_account_id', 0],
|
||||||
autoFocus: ['expense_account_id', 0],
|
}}
|
||||||
}}
|
footer={true}
|
||||||
actions={
|
/>
|
||||||
<>
|
|
||||||
<Button
|
|
||||||
small={true}
|
|
||||||
className={'button--secondary button--new-line'}
|
|
||||||
onClick={onClickNewRow}
|
|
||||||
>
|
|
||||||
<T id={'new_lines'} />
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
small={true}
|
|
||||||
className={'button--secondary button--clear-lines ml1'}
|
|
||||||
onClick={handleClickClearAllLines}
|
|
||||||
>
|
|
||||||
<T id={'clear_all_lines'} />
|
|
||||||
</Button>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
totalRow={true}
|
|
||||||
/>
|
|
||||||
<ExpenseDeleteEntriesAlert
|
|
||||||
name={'expense-delete-entries'}
|
|
||||||
onConfirm={handleConfirmClearEntriesAlert}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default compose(
|
export default compose(withAlertActions)(ExpenseFormEntriesTable);
|
||||||
withAlertActions
|
|
||||||
)(ExpenseFormEntriesTable);
|
|
||||||
|
|||||||
@@ -4,36 +4,38 @@ import { FormGroup, TextArea } from '@blueprintjs/core';
|
|||||||
import { FormattedMessage as T } from 'react-intl';
|
import { FormattedMessage as T } from 'react-intl';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { inputIntent } from 'utils';
|
import { inputIntent } from 'utils';
|
||||||
import { Row, Dragzone, Col } from 'components';
|
import { Row, Dragzone, Col, Postbox } from 'components';
|
||||||
import { CLASSES } from 'common/classes';
|
import { CLASSES } from 'common/classes';
|
||||||
|
|
||||||
export default function ExpenseFormFooter() {
|
export default function ExpenseFormFooter() {
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
<div className={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
||||||
<Row>
|
<Postbox title={'Expense details'} defaultOpen={false}>
|
||||||
<Col md={8}>
|
<Row>
|
||||||
<FastField name={'description'}>
|
<Col md={8}>
|
||||||
{({ field, meta: { error, touched } }) => (
|
<FastField name={'description'}>
|
||||||
<FormGroup
|
{({ field, meta: { error, touched } }) => (
|
||||||
label={<T id={'description'} />}
|
<FormGroup
|
||||||
className={'form-group--description'}
|
label={<T id={'description'} />}
|
||||||
intent={inputIntent({ error, touched })}
|
className={'form-group--description'}
|
||||||
>
|
intent={inputIntent({ error, touched })}
|
||||||
<TextArea growVertically={true} {...field} />
|
>
|
||||||
</FormGroup>
|
<TextArea growVertically={true} {...field} />
|
||||||
)}
|
</FormGroup>
|
||||||
</FastField>
|
)}
|
||||||
</Col>
|
</FastField>
|
||||||
|
</Col>
|
||||||
|
|
||||||
<Col md={4}>
|
<Col md={4}>
|
||||||
<Dragzone
|
<Dragzone
|
||||||
initialFiles={[]}
|
initialFiles={[]}
|
||||||
// onDrop={handleDropFiles}
|
// onDrop={handleDropFiles}
|
||||||
// onDeleteFile={handleDeleteFile}
|
// onDeleteFile={handleDeleteFile}
|
||||||
hint={'Attachments: Maxiumum size: 20MB'}
|
hint={'Attachments: Maxiumum size: 20MB'}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
</Postbox>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { InputGroup, FormGroup, Position, Classes } from '@blueprintjs/core';
|
import { InputGroup, FormGroup, Position, Classes } from '@blueprintjs/core';
|
||||||
import { DateInput } from '@blueprintjs/datetime';
|
import { DateInput } from '@blueprintjs/datetime';
|
||||||
import { FastField } from 'formik';
|
import { FastField, ErrorMessage } from 'formik';
|
||||||
import { FormattedMessage as T } from 'react-intl';
|
import { FormattedMessage as T } from 'react-intl';
|
||||||
|
import classNames from 'classnames';
|
||||||
import { CLASSES } from 'common/classes';
|
import { CLASSES } from 'common/classes';
|
||||||
import {
|
import {
|
||||||
momentFormatter,
|
momentFormatter,
|
||||||
@@ -10,16 +11,13 @@ import {
|
|||||||
inputIntent,
|
inputIntent,
|
||||||
handleDateChange,
|
handleDateChange,
|
||||||
} from 'utils';
|
} from 'utils';
|
||||||
import classNames from 'classnames';
|
|
||||||
import {
|
import {
|
||||||
CurrencySelectList,
|
CurrencySelectList,
|
||||||
ContactSelecetList,
|
ContactSelecetList,
|
||||||
ErrorMessage,
|
|
||||||
AccountsSelectList,
|
AccountsSelectList,
|
||||||
FieldRequiredHint,
|
FieldRequiredHint,
|
||||||
Hint,
|
Hint,
|
||||||
} from 'components';
|
} from 'components';
|
||||||
|
|
||||||
import { ACCOUNT_PARENT_TYPE } from 'common/accountTypes';
|
import { ACCOUNT_PARENT_TYPE } from 'common/accountTypes';
|
||||||
import { useExpenseFormContext } from './ExpenseFormPageProvider';
|
import { useExpenseFormContext } from './ExpenseFormPageProvider';
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
|
import 'style/pages/Expense/PageForm.scss';
|
||||||
|
|
||||||
import ExpenseForm from './ExpenseForm';
|
import ExpenseForm from './ExpenseForm';
|
||||||
import { ExpenseFormPageProvider } from './ExpenseFormPageProvider';
|
import { ExpenseFormPageProvider } from './ExpenseFormPageProvider';
|
||||||
|
|
||||||
import 'style/pages/Expense/PageForm.scss';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expense page form.
|
* Expense page form.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ const defaultInitialValues = {
|
|||||||
category_id: '',
|
category_id: '',
|
||||||
sellable: 1,
|
sellable: 1,
|
||||||
purchasable: true,
|
purchasable: true,
|
||||||
|
sell_description: '',
|
||||||
|
purchase_description: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,15 +1,11 @@
|
|||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
import { useFormik } from 'formik';
|
import { Formik, Form } from 'formik';
|
||||||
import { Row, Col, ErrorMessage } from 'components';
|
|
||||||
import { FormattedMessage as T } from 'react-intl';
|
import { FormattedMessage as T } from 'react-intl';
|
||||||
import {
|
import { Button, Classes } from '@blueprintjs/core';
|
||||||
Button,
|
import { Intent } from '@blueprintjs/core';
|
||||||
Classes,
|
import { saveInvoke } from 'utils';
|
||||||
FormGroup,
|
import ReferenceNumberFormContent from './ReferenceNumberFormContent';
|
||||||
InputGroup,
|
|
||||||
Intent,
|
|
||||||
} from '@blueprintjs/core';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reference number form.
|
* Reference number form.
|
||||||
@@ -21,6 +17,7 @@ export default function ReferenceNumberForm({
|
|||||||
initialNumber,
|
initialNumber,
|
||||||
}) {
|
}) {
|
||||||
const validationSchema = Yup.object().shape({
|
const validationSchema = Yup.object().shape({
|
||||||
|
// mode: Yup.string(),
|
||||||
number_prefix: Yup.string(),
|
number_prefix: Yup.string(),
|
||||||
next_number: Yup.number(),
|
next_number: Yup.number(),
|
||||||
});
|
});
|
||||||
@@ -33,85 +30,44 @@ export default function ReferenceNumberForm({
|
|||||||
[initialPrefix, initialNumber],
|
[initialPrefix, initialNumber],
|
||||||
);
|
);
|
||||||
|
|
||||||
const {
|
const handleSubmit = (values) => {
|
||||||
errors,
|
debugger;
|
||||||
touched,
|
saveInvoke(onSubmit, values);
|
||||||
handleSubmit,
|
};
|
||||||
isSubmitting,
|
|
||||||
getFieldProps,
|
|
||||||
} = useFormik({
|
|
||||||
enableReinitialize: true,
|
|
||||||
initialValues: {
|
|
||||||
...initialValues,
|
|
||||||
},
|
|
||||||
validationSchema,
|
|
||||||
onSubmit: (values, { setSubmitting, setErrors }) => {
|
|
||||||
onSubmit(values, { setSubmitting, setErrors });
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<form onSubmit={handleSubmit}>
|
|
||||||
<div className={Classes.DIALOG_BODY}>
|
|
||||||
<p className="paragraph">
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce
|
|
||||||
tincidunt porta quam,
|
|
||||||
</p>
|
|
||||||
<Row>
|
|
||||||
{/* ------------- Prefix ------------- */}
|
|
||||||
<Col xs={6}>
|
|
||||||
<FormGroup
|
|
||||||
label={<T id={'prefix'} />}
|
|
||||||
className={'form-group--'}
|
|
||||||
intent={errors.number_prefix && touched.number_prefix && Intent.DANGER}
|
|
||||||
helperText={
|
|
||||||
<ErrorMessage name={'prefix'} {...{ errors, touched }} />
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<InputGroup
|
|
||||||
intent={errors.number_prefix && touched.number_prefix && Intent.DANGER}
|
|
||||||
{...getFieldProps('number_prefix')}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
</Col>
|
|
||||||
|
|
||||||
{/* ------------- Next number ------------- */}
|
return (
|
||||||
<Col xs={6}>
|
<Formik
|
||||||
<FormGroup
|
initialValues={initialValues}
|
||||||
label={<T id={'next_number'} />}
|
validationSchema={validationSchema}
|
||||||
className={'form-group--'}
|
onSubmit={handleSubmit}
|
||||||
intent={
|
>
|
||||||
errors.next_number && touched.next_number && Intent.DANGER
|
{({ isSubmitting }) => (
|
||||||
}
|
<Form>
|
||||||
helperText={
|
<div className={Classes.DIALOG_BODY}>
|
||||||
<ErrorMessage name={'next_number'} {...{ errors, touched }} />
|
<p className="paragraph">
|
||||||
}
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce
|
||||||
>
|
tincidunt porta quam,
|
||||||
<InputGroup
|
</p>
|
||||||
intent={
|
|
||||||
errors.next_number && touched.next_number && Intent.DANGER
|
<ReferenceNumberFormContent />
|
||||||
}
|
|
||||||
{...getFieldProps('next_number')}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
</div>
|
|
||||||
<div className={Classes.DIALOG_FOOTER}>
|
|
||||||
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
|
||||||
<Button onClick={onClose}>
|
|
||||||
<T id={'cancel'} />
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
intent={Intent.PRIMARY}
|
|
||||||
type="submit"
|
|
||||||
disabled={isSubmitting}
|
|
||||||
>
|
|
||||||
<T id={'submit'} />
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</form>
|
<div className={Classes.DIALOG_FOOTER}>
|
||||||
</div>
|
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
||||||
|
<Button onClick={onClose}>
|
||||||
|
<T id={'cancel'} />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
intent={Intent.PRIMARY}
|
||||||
|
type="submit"
|
||||||
|
disabled={isSubmitting}
|
||||||
|
>
|
||||||
|
<T id={'submit'} />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
)}
|
||||||
|
</Formik>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { FastField } from 'formik';
|
||||||
|
import { FormattedMessage as T } from 'react-intl';
|
||||||
|
import { FormGroup, InputGroup, Radio } from '@blueprintjs/core';
|
||||||
|
import { Row, Col, ErrorMessage } from 'components';
|
||||||
|
import { inputIntent } from 'utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference number form content.
|
||||||
|
*/
|
||||||
|
export default function ReferenceNumberFormContent() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<FastField name={'mode'}>
|
||||||
|
{({ form, field, meta: { error, touched } }) => (
|
||||||
|
<Radio
|
||||||
|
label="Auto-incrementing invoice number."
|
||||||
|
value="auto-increment"
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</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
|
||||||
|
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
|
||||||
|
intent={inputIntent({ error, touched })}
|
||||||
|
{...field}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
)}
|
||||||
|
</FastField>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
<FastField name={'mode'}>
|
||||||
|
{({ form, field, meta: { error, touched } }) => (
|
||||||
|
<Radio label="Manual entring for this transaction." value="manual" {...field} />
|
||||||
|
)}
|
||||||
|
</FastField>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ import { FormGroup, TextArea } from '@blueprintjs/core';
|
|||||||
import { FormattedMessage as T } from 'react-intl';
|
import { FormattedMessage as T } from 'react-intl';
|
||||||
import { FastField } from 'formik';
|
import { FastField } from 'formik';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { Row, Col } from 'components';
|
import { Postbox, Row, Col } from 'components';
|
||||||
import { CLASSES } from 'common/classes';
|
import { CLASSES } from 'common/classes';
|
||||||
import Dragzone from 'components/Dragzone';
|
import Dragzone from 'components/Dragzone';
|
||||||
import { inputIntent } from 'utils';
|
import { inputIntent } from 'utils';
|
||||||
@@ -12,30 +12,32 @@ import { inputIntent } from 'utils';
|
|||||||
export default function BillFormFooter() {
|
export default function BillFormFooter() {
|
||||||
return (
|
return (
|
||||||
<div class={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
<div class={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
||||||
<Row>
|
<Postbox title={'Bill details'} defaultOpen={false}>
|
||||||
<Col md={8}>
|
<Row>
|
||||||
<FastField name={'note'}>
|
<Col md={8}>
|
||||||
{({ field, meta: { error, touched } }) => (
|
<FastField name={'note'}>
|
||||||
<FormGroup
|
{({ field, meta: { error, touched } }) => (
|
||||||
label={<T id={'note'} />}
|
<FormGroup
|
||||||
className={'form-group--note'}
|
label={<T id={'note'} />}
|
||||||
intent={inputIntent({ error, touched })}
|
className={'form-group--note'}
|
||||||
>
|
intent={inputIntent({ error, touched })}
|
||||||
<TextArea growVertically={true} {...field} />
|
>
|
||||||
</FormGroup>
|
<TextArea growVertically={true} {...field} />
|
||||||
)}
|
</FormGroup>
|
||||||
</FastField>
|
)}
|
||||||
</Col>
|
</FastField>
|
||||||
|
</Col>
|
||||||
|
|
||||||
<Col md={4}>
|
<Col md={4}>
|
||||||
<Dragzone
|
<Dragzone
|
||||||
initialFiles={[]}
|
initialFiles={[]}
|
||||||
// onDrop={onDropFiles}
|
// onDrop={onDropFiles}
|
||||||
// onDeleteFile={onDropFiles}
|
// onDeleteFile={onDropFiles}
|
||||||
hint={'Attachments: Maxiumum size: 20MB'}
|
hint={'Attachments: Maxiumum size: 20MB'}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
</Postbox>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ export const defaultBillEntry = {
|
|||||||
index: 0,
|
index: 0,
|
||||||
item_id: '',
|
item_id: '',
|
||||||
rate: '',
|
rate: '',
|
||||||
discount: 0,
|
discount: '',
|
||||||
quantity: 1,
|
quantity: '',
|
||||||
description: '',
|
description: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -20,23 +20,23 @@ function PaymentMadeEntriesTable({
|
|||||||
entries,
|
entries,
|
||||||
|
|
||||||
// #withAlertsActions
|
// #withAlertsActions
|
||||||
openAlert
|
openAlert,
|
||||||
}) {
|
}) {
|
||||||
const {
|
const { paymentVendorId, isDueBillsFetching } = usePaymentMadeFormContext();
|
||||||
paymentVendorId,
|
|
||||||
isDueBillsFetching,
|
|
||||||
} = usePaymentMadeFormContext();
|
|
||||||
|
|
||||||
const columns = usePaymentMadeEntriesTableColumns();
|
const columns = usePaymentMadeEntriesTableColumns();
|
||||||
|
|
||||||
// Handle update data.
|
|
||||||
const handleUpdateData = useCallback((rowIndex, columnId, value) => {
|
|
||||||
const newRows = compose(
|
|
||||||
updateTableRow(rowIndex, columnId, value),
|
|
||||||
)(entries);
|
|
||||||
|
|
||||||
onUpdateData(newRows);
|
// Handle update data.
|
||||||
}, [onUpdateData, entries]);
|
const handleUpdateData = useCallback(
|
||||||
|
(rowIndex, columnId, value) => {
|
||||||
|
const newRows = compose(updateTableRow(rowIndex, columnId, value))(
|
||||||
|
entries,
|
||||||
|
);
|
||||||
|
|
||||||
|
onUpdateData(newRows);
|
||||||
|
},
|
||||||
|
[onUpdateData, entries],
|
||||||
|
);
|
||||||
|
|
||||||
// Detarmines the right no results message before selecting vendor and aftering
|
// Detarmines the right no results message before selecting vendor and aftering
|
||||||
// selecting vendor id.
|
// selecting vendor id.
|
||||||
@@ -44,15 +44,6 @@ function PaymentMadeEntriesTable({
|
|||||||
? 'There is no payable bills for this vendor that can be applied for this payment'
|
? 'There is no payable bills for this vendor that can be applied for this payment'
|
||||||
: 'Please select a vendor to display all open bills for it.';
|
: 'Please select a vendor to display all open bills for it.';
|
||||||
|
|
||||||
// Handle clear all lines action.
|
|
||||||
const handleClearAllLines = () => {
|
|
||||||
const fullAmount = safeSumBy(entries, 'payment_amount');
|
|
||||||
|
|
||||||
if (fullAmount > 0) {
|
|
||||||
openAlert('clear-all-lines-payment-made');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CloudLoadingIndicator isLoading={isDueBillsFetching}>
|
<CloudLoadingIndicator isLoading={isDueBillsFetching}>
|
||||||
<DataTableEditable
|
<DataTableEditable
|
||||||
@@ -66,21 +57,10 @@ function PaymentMadeEntriesTable({
|
|||||||
updateData: handleUpdateData,
|
updateData: handleUpdateData,
|
||||||
}}
|
}}
|
||||||
noResults={noResultsMessage}
|
noResults={noResultsMessage}
|
||||||
actions={
|
footer={true}
|
||||||
<Button
|
|
||||||
small={true}
|
|
||||||
className={'button--secondary button--clear-lines'}
|
|
||||||
onClick={handleClearAllLines}
|
|
||||||
>
|
|
||||||
<T id={'clear_all_lines'} />
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
totalRow={true}
|
|
||||||
/>
|
/>
|
||||||
</CloudLoadingIndicator>
|
</CloudLoadingIndicator>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default compose(
|
export default compose(withAlertActions)(PaymentMadeEntriesTable);
|
||||||
withAlertActions
|
|
||||||
)(PaymentMadeEntriesTable);
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import classNames from 'classnames';
|
|||||||
import { FormGroup, TextArea } from '@blueprintjs/core';
|
import { FormGroup, TextArea } from '@blueprintjs/core';
|
||||||
import { FormattedMessage as T } from 'react-intl';
|
import { FormattedMessage as T } from 'react-intl';
|
||||||
import { FastField } from 'formik';
|
import { FastField } from 'formik';
|
||||||
import { Row, Col } from 'components';
|
import { Postbox, Row, Col } from 'components';
|
||||||
import { CLASSES } from 'common/classes';
|
import { CLASSES } from 'common/classes';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -12,21 +12,23 @@ import { CLASSES } from 'common/classes';
|
|||||||
export default function PaymentMadeFooter() {
|
export default function PaymentMadeFooter() {
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
<div className={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
||||||
<Row>
|
<Postbox title={'Payment made details'} defaultOpen={false}>
|
||||||
<Col md={8}>
|
<Row>
|
||||||
{/* --------- Statement --------- */}
|
<Col md={8}>
|
||||||
<FastField name={'customer_name'}>
|
{/* --------- Statement --------- */}
|
||||||
{({ form, field, meta: { error, touched } }) => (
|
<FastField name={'customer_name'}>
|
||||||
<FormGroup
|
{({ form, field, meta: { error, touched } }) => (
|
||||||
label={<T id={'statement'} />}
|
<FormGroup
|
||||||
className={'form-group--statement'}
|
label={<T id={'statement'} />}
|
||||||
>
|
className={'form-group--statement'}
|
||||||
<TextArea growVertically={true} {...field} />
|
>
|
||||||
</FormGroup>
|
<TextArea growVertically={true} {...field} />
|
||||||
)}
|
</FormGroup>
|
||||||
</FastField>
|
)}
|
||||||
</Col>
|
</FastField>
|
||||||
</Row>
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Postbox>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,55 +4,57 @@ import { FormattedMessage as T } from 'react-intl';
|
|||||||
import { FastField } from 'formik';
|
import { FastField } from 'formik';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { CLASSES } from 'common/classes';
|
import { CLASSES } from 'common/classes';
|
||||||
import { Row, Col } from 'components';
|
import { Row, Col, Postbox } from 'components';
|
||||||
import Dragzone from 'components/Dragzone';
|
import Dragzone from 'components/Dragzone';
|
||||||
|
|
||||||
import { inputIntent } from 'utils';
|
import { inputIntent } from 'utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Estimate form footer.
|
* Estimate form footer.
|
||||||
*/
|
*/
|
||||||
export default function EstiamteFormFooter({}) {
|
export default function EstiamteFormFooter({}) {
|
||||||
return (
|
return (
|
||||||
<div class={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
<div class={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
||||||
<Row>
|
<Postbox title={'Estimate details'} defaultOpen={false}>
|
||||||
<Col md={8}>
|
<Row>
|
||||||
{/* --------- Customer Note --------- */}
|
<Col md={8}>
|
||||||
<FastField name={'note'}>
|
{/* --------- Customer Note --------- */}
|
||||||
{({ form, field, meta: { error, touched } }) => (
|
<FastField name={'note'}>
|
||||||
<FormGroup
|
{({ form, field, meta: { error, touched } }) => (
|
||||||
label={<T id={'customer_note'} />}
|
<FormGroup
|
||||||
className={'form-group--customer_note'}
|
label={<T id={'customer_note'} />}
|
||||||
intent={inputIntent({ error, touched })}
|
className={'form-group--customer_note'}
|
||||||
>
|
intent={inputIntent({ error, touched })}
|
||||||
<TextArea growVertically={true} {...field} />
|
>
|
||||||
</FormGroup>
|
<TextArea growVertically={true} {...field} />
|
||||||
)}
|
</FormGroup>
|
||||||
</FastField>
|
)}
|
||||||
|
</FastField>
|
||||||
|
|
||||||
{/* --------- Terms and conditions --------- */}
|
{/* --------- Terms and conditions --------- */}
|
||||||
<FastField name={'terms_conditions'}>
|
<FastField name={'terms_conditions'}>
|
||||||
{({ field, meta: { error, touched } }) => (
|
{({ field, meta: { error, touched } }) => (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'terms_conditions'} />}
|
label={<T id={'terms_conditions'} />}
|
||||||
className={'form-group--terms_conditions'}
|
className={'form-group--terms_conditions'}
|
||||||
intent={inputIntent({ error, touched })}
|
intent={inputIntent({ error, touched })}
|
||||||
>
|
>
|
||||||
<TextArea growVertically={true} {...field} />
|
<TextArea growVertically={true} {...field} />
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
)}
|
)}
|
||||||
</FastField>
|
</FastField>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
<Col md={4}>
|
<Col md={4}>
|
||||||
<Dragzone
|
<Dragzone
|
||||||
initialFiles={[]}
|
initialFiles={[]}
|
||||||
// onDrop={handleDropFiles}
|
// onDrop={handleDropFiles}
|
||||||
// onDeleteFile={handleDeleteFile}
|
// onDeleteFile={handleDeleteFile}
|
||||||
hint={'Attachments: Maxiumum size: 20MB'}
|
hint={'Attachments: Maxiumum size: 20MB'}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
</Postbox>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ export const defaultEstimateEntry = {
|
|||||||
index: 0,
|
index: 0,
|
||||||
item_id: '',
|
item_id: '',
|
||||||
rate: '',
|
rate: '',
|
||||||
discount: 0,
|
discount: '',
|
||||||
quantity: 1,
|
quantity: '',
|
||||||
description: '',
|
description: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ export const statusAccessor = (row) => (
|
|||||||
*/
|
*/
|
||||||
export function ActionsMenu({
|
export function ActionsMenu({
|
||||||
row: { original },
|
row: { original },
|
||||||
payload: { onEdit, onDeliver, onReject, onApprove, onDelete ,onDrawer },
|
payload: { onEdit, onDeliver, onReject, onApprove, onDelete, onDrawer },
|
||||||
}) {
|
}) {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
|
|
||||||
@@ -101,9 +101,10 @@ export function ActionsMenu({
|
|||||||
</Choose.When>
|
</Choose.When>
|
||||||
</Choose>
|
</Choose>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
text={formatMessage({ id: 'estimate_paper' })}
|
icon={<Icon icon={'receipt-24'} iconSize={16} />}
|
||||||
onClick={() => onDrawer()}
|
text={formatMessage({ id: 'estimate_paper' })}
|
||||||
/>
|
onClick={() => onDrawer()}
|
||||||
|
/>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
text={formatMessage({ id: 'delete_estimate' })}
|
text={formatMessage({ id: 'delete_estimate' })}
|
||||||
intent={Intent.DANGER}
|
intent={Intent.DANGER}
|
||||||
|
|||||||
@@ -123,7 +123,11 @@ function InvoiceForm({
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Handle the request error.
|
// Handle the request error.
|
||||||
const onError = ({ response: { data: { errors } } }) => {
|
const onError = ({
|
||||||
|
response: {
|
||||||
|
data: { errors },
|
||||||
|
},
|
||||||
|
}) => {
|
||||||
if (errors) {
|
if (errors) {
|
||||||
handleErrors(errors, { setErrors });
|
handleErrors(errors, { setErrors });
|
||||||
}
|
}
|
||||||
@@ -146,6 +150,7 @@ function InvoiceForm({
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Formik
|
<Formik
|
||||||
|
enableReinitialize={true}
|
||||||
validationSchema={
|
validationSchema={
|
||||||
isNewMode ? CreateInvoiceFormSchema : EditInvoiceFormSchema
|
isNewMode ? CreateInvoiceFormSchema : EditInvoiceFormSchema
|
||||||
}
|
}
|
||||||
@@ -154,10 +159,7 @@ function InvoiceForm({
|
|||||||
>
|
>
|
||||||
<Form>
|
<Form>
|
||||||
<InvoiceFormHeader />
|
<InvoiceFormHeader />
|
||||||
|
<InvoiceItemsEntriesEditorField />
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
|
|
||||||
<InvoiceItemsEntriesEditorField />
|
|
||||||
</div>
|
|
||||||
<InvoiceFormFooter />
|
<InvoiceFormFooter />
|
||||||
<InvoiceFloatingActions />
|
<InvoiceFloatingActions />
|
||||||
<InvoiceFormDialogs />
|
<InvoiceFormDialogs />
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ export default function InvoiceFormDialogs() {
|
|||||||
|
|
||||||
// Update the form once the invoice number form submit confirm.
|
// Update the form once the invoice number form submit confirm.
|
||||||
const handleInvoiceNumberFormConfirm = (values) => {
|
const handleInvoiceNumberFormConfirm = (values) => {
|
||||||
|
debugger;
|
||||||
|
console.log(values, 'XX');
|
||||||
|
|
||||||
setFieldValue(
|
setFieldValue(
|
||||||
'invoice_no',
|
'invoice_no',
|
||||||
transactionNumber(values.number_prefix, values.next_number),
|
transactionNumber(values.number_prefix, values.next_number),
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import classNames from 'classnames';
|
|||||||
import { FormGroup, TextArea } from '@blueprintjs/core';
|
import { FormGroup, TextArea } from '@blueprintjs/core';
|
||||||
import { FormattedMessage as T } from 'react-intl';
|
import { FormattedMessage as T } from 'react-intl';
|
||||||
import { CLASSES } from 'common/classes';
|
import { CLASSES } from 'common/classes';
|
||||||
import { Row, Col } from 'components';
|
import { Row, Col, Postbox } from 'components';
|
||||||
import Dragzone from 'components/Dragzone';
|
import Dragzone from 'components/Dragzone';
|
||||||
|
|
||||||
import { inputIntent } from 'utils';
|
import { inputIntent } from 'utils';
|
||||||
@@ -12,44 +12,46 @@ import { inputIntent } from 'utils';
|
|||||||
export default function InvoiceFormFooter() {
|
export default function InvoiceFormFooter() {
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
<div className={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
||||||
<Row>
|
<Postbox title={'Invoice details'} defaultOpen={false}>
|
||||||
<Col md={8}>
|
<Row>
|
||||||
{/* --------- Invoice message --------- */}
|
<Col md={8}>
|
||||||
<FastField name={'invoice_message'}>
|
{/* --------- Invoice message --------- */}
|
||||||
{({ field, meta: { error, touched } }) => (
|
<FastField name={'invoice_message'}>
|
||||||
<FormGroup
|
{({ field, meta: { error, touched } }) => (
|
||||||
label={<T id={'invoice_message'} />}
|
<FormGroup
|
||||||
className={'form-group--invoice_message'}
|
label={<T id={'invoice_message'} />}
|
||||||
intent={inputIntent({ error, touched })}
|
className={'form-group--invoice_message'}
|
||||||
>
|
intent={inputIntent({ error, touched })}
|
||||||
<TextArea growVertically={true} {...field} />
|
>
|
||||||
</FormGroup>
|
<TextArea growVertically={true} {...field} />
|
||||||
)}
|
</FormGroup>
|
||||||
</FastField>
|
)}
|
||||||
|
</FastField>
|
||||||
|
|
||||||
{/* --------- Terms and conditions --------- */}
|
{/* --------- Terms and conditions --------- */}
|
||||||
<FastField name={'terms_conditions'}>
|
<FastField name={'terms_conditions'}>
|
||||||
{({ field, meta: { error, touched } }) => (
|
{({ field, meta: { error, touched } }) => (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'terms_conditions'} />}
|
label={<T id={'terms_conditions'} />}
|
||||||
className={'form-group--terms_conditions'}
|
className={'form-group--terms_conditions'}
|
||||||
intent={inputIntent({ error, touched })}
|
intent={inputIntent({ error, touched })}
|
||||||
>
|
>
|
||||||
<TextArea growVertically={true} {...field} />
|
<TextArea growVertically={true} {...field} />
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
)}
|
)}
|
||||||
</FastField>
|
</FastField>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
<Col md={4}>
|
<Col md={4}>
|
||||||
<Dragzone
|
<Dragzone
|
||||||
initialFiles={[]}
|
initialFiles={[]}
|
||||||
// onDrop={handleDropFiles}
|
// onDrop={handleDropFiles}
|
||||||
// onDeleteFile={handleDeleteFile}
|
// onDeleteFile={handleDeleteFile}
|
||||||
hint={'Attachments: Maxiumum size: 20MB'}
|
hint={'Attachments: Maxiumum size: 20MB'}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
</Postbox>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ function InvoiceFormHeaderFields({
|
|||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'customer_name'} />}
|
label={<T id={'customer_name'} />}
|
||||||
inline={true}
|
inline={true}
|
||||||
className={classNames('form-group--customer-name', CLASSES.FILL)}
|
className={classNames('form-group--customer-name', 'form-group--select-list', CLASSES.FILL)}
|
||||||
labelInfo={<FieldRequiredHint />}
|
labelInfo={<FieldRequiredHint />}
|
||||||
intent={inputIntent({ error, touched })}
|
intent={inputIntent({ error, touched })}
|
||||||
helperText={<ErrorMessage name={'customer_id'} />}
|
helperText={<ErrorMessage name={'customer_id'} />}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
import InvoiceForm from './InvoiceForm';
|
|
||||||
|
|
||||||
import 'style/pages/SaleInvoice/PageForm.scss';
|
import 'style/pages/SaleInvoice/PageForm.scss';
|
||||||
|
|
||||||
|
import InvoiceForm from './InvoiceForm';
|
||||||
import { InvoiceFormProvider } from './InvoiceFormProvider';
|
import { InvoiceFormProvider } from './InvoiceFormProvider';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import React from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { FastField } from 'formik';
|
import { FastField } from 'formik';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import { CLASSES } from 'common/classes';
|
||||||
import ItemsEntriesTable from 'containers/Entries/ItemsEntriesTable';
|
import ItemsEntriesTable from 'containers/Entries/ItemsEntriesTable';
|
||||||
import { useInvoiceFormContext } from './InvoiceFormProvider';
|
import { useInvoiceFormContext } from './InvoiceFormProvider';
|
||||||
|
|
||||||
@@ -8,20 +10,22 @@ import { useInvoiceFormContext } from './InvoiceFormProvider';
|
|||||||
*/
|
*/
|
||||||
export default function InvoiceItemsEntriesEditorField() {
|
export default function InvoiceItemsEntriesEditorField() {
|
||||||
const { items } = useInvoiceFormContext();
|
const { items } = useInvoiceFormContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FastField name={'entries'}>
|
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
|
||||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
<FastField name={'entries'}>
|
||||||
<ItemsEntriesTable
|
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||||
entries={value}
|
<ItemsEntriesTable
|
||||||
onUpdateData={(entries) => {
|
entries={value}
|
||||||
form.setFieldValue('entries', entries);
|
onUpdateData={(entries) => {
|
||||||
}}
|
form.setFieldValue('entries', entries);
|
||||||
items={items}
|
}}
|
||||||
errors={error}
|
items={items}
|
||||||
linesNumber={4}
|
errors={error}
|
||||||
/>
|
linesNumber={4}
|
||||||
)}
|
/>
|
||||||
</FastField>
|
)}
|
||||||
|
</FastField>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ export const defaultInvoiceEntry = {
|
|||||||
index: 0,
|
index: 0,
|
||||||
item_id: '',
|
item_id: '',
|
||||||
rate: '',
|
rate: '',
|
||||||
discount: 0,
|
discount: '',
|
||||||
quantity: 1,
|
quantity: '',
|
||||||
description: '',
|
description: '',
|
||||||
total: 0,
|
total: 0,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ export function ActionsMenu({
|
|||||||
/>
|
/>
|
||||||
</If>
|
</If>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
|
icon={<Icon icon={'receipt-24'} iconSize={16} />}
|
||||||
text={formatMessage({ id: 'invoice_paper' })}
|
text={formatMessage({ id: 'invoice_paper' })}
|
||||||
onClick={() => onDrawer()}
|
onClick={() => onDrawer()}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ function PaymentReceiveForm({
|
|||||||
<PaymentReceiveFormFooter />
|
<PaymentReceiveFormFooter />
|
||||||
<PaymentReceiveFloatingActions />
|
<PaymentReceiveFloatingActions />
|
||||||
|
|
||||||
{/* Alerts & Dialogs */}
|
{/* ------- Alerts & Dialogs ------- */}
|
||||||
<PaymentReceiveFormAlerts />
|
<PaymentReceiveFormAlerts />
|
||||||
<PaymentReceiveFormDialogs />
|
<PaymentReceiveFormDialogs />
|
||||||
</PaymentReceiveInnerProvider>
|
</PaymentReceiveInnerProvider>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import classNames from 'classnames';
|
|||||||
import { FormGroup, TextArea } from '@blueprintjs/core';
|
import { FormGroup, TextArea } from '@blueprintjs/core';
|
||||||
import { FormattedMessage as T } from 'react-intl';
|
import { FormattedMessage as T } from 'react-intl';
|
||||||
import { FastField } from 'formik';
|
import { FastField } from 'formik';
|
||||||
import { Row, Col } from 'components';
|
import { Row, Col, Postbox } from 'components';
|
||||||
import { CLASSES } from 'common/classes';
|
import { CLASSES } from 'common/classes';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -12,21 +12,23 @@ import { CLASSES } from 'common/classes';
|
|||||||
export default function PaymentReceiveFormFooter({ getFieldProps }) {
|
export default function PaymentReceiveFormFooter({ getFieldProps }) {
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
<div className={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
||||||
<Row>
|
<Postbox title={'Payment receive details'} defaultOpen={false}>
|
||||||
<Col md={8}>
|
<Row>
|
||||||
{/* --------- Statement --------- */}
|
<Col md={8}>
|
||||||
<FastField name={'statement'}>
|
{/* --------- Statement --------- */}
|
||||||
{({ form, field, meta: { error, touched } }) => (
|
<FastField name={'statement'}>
|
||||||
<FormGroup
|
{({ form, field, meta: { error, touched } }) => (
|
||||||
label={<T id={'statement'} />}
|
<FormGroup
|
||||||
className={'form-group--statement'}
|
label={<T id={'statement'} />}
|
||||||
>
|
className={'form-group--statement'}
|
||||||
<TextArea growVertically={true} {...field} />
|
>
|
||||||
</FormGroup>
|
<TextArea growVertically={true} {...field} />
|
||||||
)}
|
</FormGroup>
|
||||||
</FastField>
|
)}
|
||||||
</Col>
|
</FastField>
|
||||||
</Row>
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Postbox>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,15 +45,6 @@ function PaymentReceiveItemsTable({
|
|||||||
onUpdateData(newRows);
|
onUpdateData(newRows);
|
||||||
}, [entries, onUpdateData]);
|
}, [entries, onUpdateData]);
|
||||||
|
|
||||||
// Handle click clear all lines button.
|
|
||||||
const handleClickClearAllLines = () => {
|
|
||||||
const fullAmount = safeSumBy(entries, 'payment_amount');
|
|
||||||
|
|
||||||
if (fullAmount > 0) {
|
|
||||||
openAlert('clear-all-lines-payment-receive');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CloudLoadingIndicator isLoading={isDueInvoicesFetching}>
|
<CloudLoadingIndicator isLoading={isDueInvoicesFetching}>
|
||||||
<DataTableEditable
|
<DataTableEditable
|
||||||
@@ -67,16 +58,7 @@ function PaymentReceiveItemsTable({
|
|||||||
updateData: handleUpdateData,
|
updateData: handleUpdateData,
|
||||||
}}
|
}}
|
||||||
noResults={noResultsMessage}
|
noResults={noResultsMessage}
|
||||||
actions={
|
footer={true}
|
||||||
<Button
|
|
||||||
small={true}
|
|
||||||
className={'button--secondary button--clear-lines'}
|
|
||||||
onClick={handleClickClearAllLines}
|
|
||||||
>
|
|
||||||
<T id={'clear_all_lines'} />
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
totalRow={true}
|
|
||||||
/>
|
/>
|
||||||
</CloudLoadingIndicator>
|
</CloudLoadingIndicator>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -88,12 +88,13 @@ export const usePaymentReceiveEntriesColumns = () => {
|
|||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
disableResizing: true,
|
disableResizing: true,
|
||||||
width: 250,
|
width: 250,
|
||||||
|
className: 'date'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: formatMessage({ id: 'invocie_number' }),
|
Header: formatMessage({ id: 'invocie_number' }),
|
||||||
accessor: InvNumberCellAccessor,
|
accessor: InvNumberCellAccessor,
|
||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
className: '',
|
className: 'invoice_number',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: formatMessage({ id: 'invoice_amount' }),
|
Header: formatMessage({ id: 'invoice_amount' }),
|
||||||
@@ -102,7 +103,7 @@ export const usePaymentReceiveEntriesColumns = () => {
|
|||||||
Cell: MoneyTableCell,
|
Cell: MoneyTableCell,
|
||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
width: 100,
|
width: 100,
|
||||||
className: '',
|
className: 'invoice_amount',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: formatMessage({ id: 'amount_due' }),
|
Header: formatMessage({ id: 'amount_due' }),
|
||||||
@@ -111,7 +112,7 @@ export const usePaymentReceiveEntriesColumns = () => {
|
|||||||
Cell: MoneyTableCell,
|
Cell: MoneyTableCell,
|
||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
width: 150,
|
width: 150,
|
||||||
className: '',
|
className: 'amount_due',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: formatMessage({ id: 'payment_amount' }),
|
Header: formatMessage({ id: 'payment_amount' }),
|
||||||
@@ -120,7 +121,7 @@ export const usePaymentReceiveEntriesColumns = () => {
|
|||||||
Footer: PaymentAmountFooterCell,
|
Footer: PaymentAmountFooterCell,
|
||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
width: 150,
|
width: 150,
|
||||||
className: '',
|
className: 'payment_amount',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[formatMessage],
|
[formatMessage],
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ export function ActionsMenu({
|
|||||||
onClick={safeCallback(onEdit, paymentReceive)}
|
onClick={safeCallback(onEdit, paymentReceive)}
|
||||||
/>
|
/>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
|
icon={<Icon icon={'receipt-24'} iconSize={16} />}
|
||||||
text={formatMessage({ id: 'payment_receive_paper' })}
|
text={formatMessage({ id: 'payment_receive_paper' })}
|
||||||
onClick={() => onDrawer()}
|
onClick={() => onDrawer()}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -10,11 +10,9 @@ import { CLASSES } from 'common/classes';
|
|||||||
import { ERROR } from 'common/errors';
|
import { ERROR } from 'common/errors';
|
||||||
import {
|
import {
|
||||||
EditReceiptFormSchema,
|
EditReceiptFormSchema,
|
||||||
CreateReceiptFormSchema,
|
CreateReceiptFormSchema,
|
||||||
} from './ReceiptForm.schema';
|
} from './ReceiptForm.schema';
|
||||||
|
|
||||||
import 'style/pages/SaleReceipt/PageForm.scss';
|
|
||||||
|
|
||||||
import { useReceiptFormContext } from './ReceiptFormProvider';
|
import { useReceiptFormContext } from './ReceiptFormProvider';
|
||||||
|
|
||||||
import ReceiptFromHeader from './ReceiptFormHeader';
|
import ReceiptFromHeader from './ReceiptFormHeader';
|
||||||
|
|||||||
@@ -3,51 +3,53 @@ import { FormGroup, TextArea } from '@blueprintjs/core';
|
|||||||
import { FastField } from 'formik';
|
import { FastField } from 'formik';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { FormattedMessage as T } from 'react-intl';
|
import { FormattedMessage as T } from 'react-intl';
|
||||||
import { Dragzone, Row, Col } from 'components';
|
import { Dragzone, Postbox, Row, Col } from 'components';
|
||||||
import { CLASSES } from 'common/classes';
|
import { CLASSES } from 'common/classes';
|
||||||
import { inputIntent } from 'utils';
|
import { inputIntent } from 'utils';
|
||||||
|
|
||||||
export default function ReceiptFormFooter({}) {
|
export default function ReceiptFormFooter({}) {
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
<div className={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
||||||
<Row>
|
<Postbox title={'Invoice details'} defaultOpen={false}>
|
||||||
<Col md={8}>
|
<Row>
|
||||||
{/* --------- Receipt message --------- */}
|
<Col md={8}>
|
||||||
<FastField name={'receipt_message'}>
|
{/* --------- Receipt message --------- */}
|
||||||
{({ field, meta: { error, touched } }) => (
|
<FastField name={'receipt_message'}>
|
||||||
<FormGroup
|
{({ field, meta: { error, touched } }) => (
|
||||||
label={<T id={'receipt_message'} />}
|
<FormGroup
|
||||||
className={'form-group--receipt_message'}
|
label={<T id={'receipt_message'} />}
|
||||||
intent={inputIntent({ error, touched })}
|
className={'form-group--receipt_message'}
|
||||||
>
|
intent={inputIntent({ error, touched })}
|
||||||
<TextArea growVertically={true} {...field} />
|
>
|
||||||
</FormGroup>
|
<TextArea growVertically={true} {...field} />
|
||||||
)}
|
</FormGroup>
|
||||||
</FastField>
|
)}
|
||||||
|
</FastField>
|
||||||
|
|
||||||
{/* --------- Statement--------- */}
|
{/* --------- Statement--------- */}
|
||||||
<FastField name={'statement'}>
|
<FastField name={'statement'}>
|
||||||
{({ field, meta: { error, touched } }) => (
|
{({ field, meta: { error, touched } }) => (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'statement'} />}
|
label={<T id={'statement'} />}
|
||||||
className={'form-group--statement'}
|
className={'form-group--statement'}
|
||||||
intent={inputIntent({ error, touched })}
|
intent={inputIntent({ error, touched })}
|
||||||
>
|
>
|
||||||
<TextArea growVertically={true} {...field} />
|
<TextArea growVertically={true} {...field} />
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
)}
|
)}
|
||||||
</FastField>
|
</FastField>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
<Col md={4}>
|
<Col md={4}>
|
||||||
<Dragzone
|
<Dragzone
|
||||||
initialFiles={[]}
|
initialFiles={[]}
|
||||||
// onDrop={handleDropFiles}
|
// onDrop={handleDropFiles}
|
||||||
// onDeleteFile={handleDeleteFile}
|
// onDeleteFile={handleDeleteFile}
|
||||||
hint={'Attachments: Maxiumum size: 20MB'}
|
hint={'Attachments: Maxiumum size: 20MB'}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
</Postbox>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
|
import 'style/pages/SaleReceipt/PageForm.scss';
|
||||||
|
|
||||||
import ReceiptFrom from './ReceiptForm';
|
import ReceiptFrom from './ReceiptForm';
|
||||||
import { ReceiptFormProvider } from './ReceiptFormProvider';
|
import { ReceiptFormProvider } from './ReceiptFormProvider';
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export const defaultReceiptEntry = {
|
|||||||
index: 0,
|
index: 0,
|
||||||
item_id: '',
|
item_id: '',
|
||||||
rate: '',
|
rate: '',
|
||||||
discount: 0,
|
discount: '',
|
||||||
quantity: '',
|
quantity: '',
|
||||||
description: '',
|
description: '',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { Choose, Money, Icon, If } from 'components';
|
|||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
|
||||||
export function ActionsMenu({
|
export function ActionsMenu({
|
||||||
payload: { onEdit, onDelete, onClose ,onDrawer },
|
payload: { onEdit, onDelete, onClose, onDrawer },
|
||||||
row: { original: receipt },
|
row: { original: receipt },
|
||||||
}) {
|
}) {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
@@ -39,9 +39,10 @@ export function ActionsMenu({
|
|||||||
/>
|
/>
|
||||||
</If>
|
</If>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
text={formatMessage({ id: 'receipt_paper' })}
|
icon={<Icon icon={'receipt-24'} iconSize={16} />}
|
||||||
onClick={() => onDrawer()}
|
text={formatMessage({ id: 'receipt_paper' })}
|
||||||
/>
|
onClick={() => onDrawer()}
|
||||||
|
/>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
text={formatMessage({ id: 'delete_receipt' })}
|
text={formatMessage({ id: 'delete_receipt' })}
|
||||||
intent={Intent.DANGER}
|
intent={Intent.DANGER}
|
||||||
|
|||||||
@@ -13,9 +13,11 @@ export function useCreateInvoice(props) {
|
|||||||
return useMutation((values) => apiRequest.post('sales/invoices', values), {
|
return useMutation((values) => apiRequest.post('sales/invoices', values), {
|
||||||
onSuccess: (values) => {
|
onSuccess: (values) => {
|
||||||
queryClient.invalidateQueries('SALE_INVOICES');
|
queryClient.invalidateQueries('SALE_INVOICES');
|
||||||
queryClient.invalidateQueries(['SETTINGS', 'INVOICES']);
|
|
||||||
queryClient.invalidateQueries('CUSTOMERS');
|
queryClient.invalidateQueries('CUSTOMERS');
|
||||||
queryClient.invalidateQueries(['CUSTOMER', values.customer_id]);
|
queryClient.invalidateQueries(['CUSTOMER', values.customer_id]);
|
||||||
|
|
||||||
|
queryClient.invalidateQueries(['SETTINGS', 'INVOICES']);
|
||||||
},
|
},
|
||||||
...props,
|
...props,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
import { useQuery, useMutation, useQueryClient } from 'react-query';
|
import { useQuery, useMutation, useQueryClient } from 'react-query';
|
||||||
import { useDispatch } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
import useApiRequest from '../useRequest';
|
import useApiRequest from '../useRequest';
|
||||||
@@ -22,7 +23,7 @@ function useSettingsQuery(key, query, props) {
|
|||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const apiRequest = useApiRequest();
|
const apiRequest = useApiRequest();
|
||||||
|
|
||||||
return useQuery(
|
const state = useQuery(
|
||||||
key,
|
key,
|
||||||
() => apiRequest.get('settings', { params: query }),
|
() => apiRequest.get('settings', { params: query }),
|
||||||
{
|
{
|
||||||
@@ -33,12 +34,17 @@ function useSettingsQuery(key, query, props) {
|
|||||||
settings: [],
|
settings: [],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
onSuccess: (settings) => {
|
|
||||||
dispatch({ type: t.SETTING_SET, options: settings });
|
|
||||||
},
|
|
||||||
...props,
|
...props,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (typeof state.data !== 'undefined') {
|
||||||
|
dispatch({ type: t.SETTING_SET, options: state.data });
|
||||||
|
}
|
||||||
|
}, [state.data, dispatch]);
|
||||||
|
|
||||||
|
return state.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -4,13 +4,7 @@ import { formatMessage } from 'services/intl';
|
|||||||
// const BASE_URL = '/dashboard';
|
// const BASE_URL = '/dashboard';
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
// Homepage
|
|
||||||
{
|
|
||||||
path: `/homepage`,
|
|
||||||
component: lazy(() => import('containers/Homepage/Homepage')),
|
|
||||||
breadcrumb: 'Home',
|
|
||||||
pageTitle: 'Homepage',
|
|
||||||
},
|
|
||||||
// Accounts.
|
// Accounts.
|
||||||
{
|
{
|
||||||
path: `/accounts`,
|
path: `/accounts`,
|
||||||
@@ -73,6 +67,7 @@ export default [
|
|||||||
{
|
{
|
||||||
path: `/items/:id/edit`,
|
path: `/items/:id/edit`,
|
||||||
component: lazy(() => import('containers/Items/ItemFormPage')),
|
component: lazy(() => import('containers/Items/ItemFormPage')),
|
||||||
|
name: 'item-edit',
|
||||||
breadcrumb: 'Edit Item',
|
breadcrumb: 'Edit Item',
|
||||||
pageTitle: formatMessage({ id: 'edit_item' }),
|
pageTitle: formatMessage({ id: 'edit_item' }),
|
||||||
backLink: true,
|
backLink: true,
|
||||||
@@ -80,6 +75,7 @@ export default [
|
|||||||
{
|
{
|
||||||
path: `/items/new`,
|
path: `/items/new`,
|
||||||
component: lazy(() => import('containers/Items/ItemFormPage')),
|
component: lazy(() => import('containers/Items/ItemFormPage')),
|
||||||
|
name: 'item-new',
|
||||||
breadcrumb: 'New Item',
|
breadcrumb: 'New Item',
|
||||||
hotkey: 'ctrl+shift+w',
|
hotkey: 'ctrl+shift+w',
|
||||||
pageTitle: formatMessage({ id: 'new_item' }),
|
pageTitle: formatMessage({ id: 'new_item' }),
|
||||||
@@ -233,6 +229,7 @@ export default [
|
|||||||
component: lazy(() =>
|
component: lazy(() =>
|
||||||
import('containers/Customers/CustomerForm/CustomerFormPage'),
|
import('containers/Customers/CustomerForm/CustomerFormPage'),
|
||||||
),
|
),
|
||||||
|
name: 'customer-edit',
|
||||||
breadcrumb: 'Edit Customer',
|
breadcrumb: 'Edit Customer',
|
||||||
pageTitle: formatMessage({ id: 'edit_customer' }),
|
pageTitle: formatMessage({ id: 'edit_customer' }),
|
||||||
backLink: true,
|
backLink: true,
|
||||||
@@ -242,6 +239,7 @@ export default [
|
|||||||
component: lazy(() =>
|
component: lazy(() =>
|
||||||
import('containers/Customers/CustomerForm/CustomerFormPage'),
|
import('containers/Customers/CustomerForm/CustomerFormPage'),
|
||||||
),
|
),
|
||||||
|
name: 'customer-new',
|
||||||
breadcrumb: 'New Customer',
|
breadcrumb: 'New Customer',
|
||||||
hotkey: 'ctrl+shift+c',
|
hotkey: 'ctrl+shift+c',
|
||||||
pageTitle: formatMessage({ id: 'new_customer' }),
|
pageTitle: formatMessage({ id: 'new_customer' }),
|
||||||
@@ -263,6 +261,7 @@ export default [
|
|||||||
component: lazy(() =>
|
component: lazy(() =>
|
||||||
import('containers/Vendors/VendorForm/VendorFormPage'),
|
import('containers/Vendors/VendorForm/VendorFormPage'),
|
||||||
),
|
),
|
||||||
|
name: 'vendor-edit',
|
||||||
breadcrumb: 'Edit Vendor',
|
breadcrumb: 'Edit Vendor',
|
||||||
pageTitle: formatMessage({ id: 'edit_vendor' }),
|
pageTitle: formatMessage({ id: 'edit_vendor' }),
|
||||||
backLink: true,
|
backLink: true,
|
||||||
@@ -272,6 +271,7 @@ export default [
|
|||||||
component: lazy(() =>
|
component: lazy(() =>
|
||||||
import('containers/Vendors/VendorForm/VendorFormPage'),
|
import('containers/Vendors/VendorForm/VendorFormPage'),
|
||||||
),
|
),
|
||||||
|
name: 'vendor-new',
|
||||||
breadcrumb: 'New Vendor',
|
breadcrumb: 'New Vendor',
|
||||||
hotkey: 'ctrl+shift+v',
|
hotkey: 'ctrl+shift+v',
|
||||||
pageTitle: formatMessage({ id: 'new_vendor' }),
|
pageTitle: formatMessage({ id: 'new_vendor' }),
|
||||||
@@ -293,6 +293,7 @@ export default [
|
|||||||
component: lazy(() =>
|
component: lazy(() =>
|
||||||
import('containers/Sales/Estimates/EstimateForm/EstimateFormPage'),
|
import('containers/Sales/Estimates/EstimateForm/EstimateFormPage'),
|
||||||
),
|
),
|
||||||
|
name: 'estimate-edit',
|
||||||
breadcrumb: 'Edit',
|
breadcrumb: 'Edit',
|
||||||
pageTitle: formatMessage({ id: 'edit_estimate' }),
|
pageTitle: formatMessage({ id: 'edit_estimate' }),
|
||||||
backLink: true,
|
backLink: true,
|
||||||
@@ -303,6 +304,7 @@ export default [
|
|||||||
component: lazy(() =>
|
component: lazy(() =>
|
||||||
import('containers/Sales/Estimates/EstimateForm/EstimateFormPage'),
|
import('containers/Sales/Estimates/EstimateForm/EstimateFormPage'),
|
||||||
),
|
),
|
||||||
|
name: 'estimate-new',
|
||||||
breadcrumb: 'New Estimate',
|
breadcrumb: 'New Estimate',
|
||||||
hotkey: 'ctrl+shift+e',
|
hotkey: 'ctrl+shift+e',
|
||||||
pageTitle: formatMessage({ id: 'new_estimate' }),
|
pageTitle: formatMessage({ id: 'new_estimate' }),
|
||||||
@@ -314,6 +316,7 @@ export default [
|
|||||||
component: lazy(() =>
|
component: lazy(() =>
|
||||||
import('containers/Sales/Estimates/EstimatesLanding/EstimatesList'),
|
import('containers/Sales/Estimates/EstimatesLanding/EstimatesList'),
|
||||||
),
|
),
|
||||||
|
name: 'estimates-list',
|
||||||
breadcrumb: 'Estimates List',
|
breadcrumb: 'Estimates List',
|
||||||
hotkey: 'shift+e',
|
hotkey: 'shift+e',
|
||||||
pageTitle: formatMessage({ id: 'estimates_list' }),
|
pageTitle: formatMessage({ id: 'estimates_list' }),
|
||||||
@@ -325,6 +328,7 @@ export default [
|
|||||||
component: lazy(() =>
|
component: lazy(() =>
|
||||||
import('containers/Sales/Invoices/InvoiceForm/InvoiceFormPage'),
|
import('containers/Sales/Invoices/InvoiceForm/InvoiceFormPage'),
|
||||||
),
|
),
|
||||||
|
name: 'invoice-edit',
|
||||||
breadcrumb: 'Edit',
|
breadcrumb: 'Edit',
|
||||||
pageTitle: formatMessage({ id: 'edit_invoice' }),
|
pageTitle: formatMessage({ id: 'edit_invoice' }),
|
||||||
sidebarShrink: true,
|
sidebarShrink: true,
|
||||||
@@ -335,6 +339,7 @@ export default [
|
|||||||
component: lazy(() =>
|
component: lazy(() =>
|
||||||
import('containers/Sales/Invoices/InvoiceForm/InvoiceFormPage'),
|
import('containers/Sales/Invoices/InvoiceForm/InvoiceFormPage'),
|
||||||
),
|
),
|
||||||
|
name: 'invoice-new',
|
||||||
breadcrumb: 'New Invoice',
|
breadcrumb: 'New Invoice',
|
||||||
hotkey: 'ctrl+shift+i',
|
hotkey: 'ctrl+shift+i',
|
||||||
pageTitle: formatMessage({ id: 'new_invoice' }),
|
pageTitle: formatMessage({ id: 'new_invoice' }),
|
||||||
@@ -357,6 +362,7 @@ export default [
|
|||||||
component: lazy(() =>
|
component: lazy(() =>
|
||||||
import('containers/Sales/Receipts/ReceiptForm/ReceiptFormPage'),
|
import('containers/Sales/Receipts/ReceiptForm/ReceiptFormPage'),
|
||||||
),
|
),
|
||||||
|
name: 'receipt-edit',
|
||||||
breadcrumb: 'Edit',
|
breadcrumb: 'Edit',
|
||||||
pageTitle: formatMessage({ id: 'edit_receipt' }),
|
pageTitle: formatMessage({ id: 'edit_receipt' }),
|
||||||
backLink: true,
|
backLink: true,
|
||||||
@@ -367,6 +373,7 @@ export default [
|
|||||||
component: lazy(() =>
|
component: lazy(() =>
|
||||||
import('containers/Sales/Receipts/ReceiptForm/ReceiptFormPage'),
|
import('containers/Sales/Receipts/ReceiptForm/ReceiptFormPage'),
|
||||||
),
|
),
|
||||||
|
name: 'receipt-new',
|
||||||
breadcrumb: 'New Receipt',
|
breadcrumb: 'New Receipt',
|
||||||
hotkey: 'ctrl+shift+r',
|
hotkey: 'ctrl+shift+r',
|
||||||
pageTitle: formatMessage({ id: 'new_receipt' }),
|
pageTitle: formatMessage({ id: 'new_receipt' }),
|
||||||
@@ -391,6 +398,7 @@ export default [
|
|||||||
'containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveFormPage'
|
'containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveFormPage'
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
name: 'payment-receive-edit',
|
||||||
breadcrumb: 'Edit',
|
breadcrumb: 'Edit',
|
||||||
pageTitle: formatMessage({ id: 'edit_payment_receive' }),
|
pageTitle: formatMessage({ id: 'edit_payment_receive' }),
|
||||||
backLink: true,
|
backLink: true,
|
||||||
@@ -403,6 +411,7 @@ export default [
|
|||||||
'containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveFormPage'
|
'containers/Sales/PaymentReceives/PaymentReceiveForm/PaymentReceiveFormPage'
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
name: 'payment-receive-new',
|
||||||
breadcrumb: 'New Payment Receive',
|
breadcrumb: 'New Payment Receive',
|
||||||
pageTitle: formatMessage({ id: 'new_payment_receive' }),
|
pageTitle: formatMessage({ id: 'new_payment_receive' }),
|
||||||
backLink: true,
|
backLink: true,
|
||||||
@@ -425,6 +434,7 @@ export default [
|
|||||||
component: lazy(() =>
|
component: lazy(() =>
|
||||||
import('containers/Purchases/Bills/BillForm/BillFormPage'),
|
import('containers/Purchases/Bills/BillForm/BillFormPage'),
|
||||||
),
|
),
|
||||||
|
name: 'bill-edit',
|
||||||
breadcrumb: 'Edit',
|
breadcrumb: 'Edit',
|
||||||
pageTitle: formatMessage({ id: 'edit_bill' }),
|
pageTitle: formatMessage({ id: 'edit_bill' }),
|
||||||
sidebarShrink: true,
|
sidebarShrink: true,
|
||||||
@@ -435,6 +445,7 @@ export default [
|
|||||||
component: lazy(() =>
|
component: lazy(() =>
|
||||||
import('containers/Purchases/Bills/BillForm/BillFormPage'),
|
import('containers/Purchases/Bills/BillForm/BillFormPage'),
|
||||||
),
|
),
|
||||||
|
name: 'bill-new',
|
||||||
breadcrumb: 'New Bill',
|
breadcrumb: 'New Bill',
|
||||||
hotkey: 'ctrl+shift+b',
|
hotkey: 'ctrl+shift+b',
|
||||||
pageTitle: formatMessage({ id: 'new_bill' }),
|
pageTitle: formatMessage({ id: 'new_bill' }),
|
||||||
@@ -465,6 +476,7 @@ export default [
|
|||||||
'containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFormPage'
|
'containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFormPage'
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
name: 'payment-made-edit',
|
||||||
breadcrumb: 'Edit',
|
breadcrumb: 'Edit',
|
||||||
pageTitle: formatMessage({ id: 'edit_payment_made' }),
|
pageTitle: formatMessage({ id: 'edit_payment_made' }),
|
||||||
sidebarShrink: true,
|
sidebarShrink: true,
|
||||||
@@ -477,6 +489,7 @@ export default [
|
|||||||
'containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFormPage'
|
'containers/Purchases/PaymentMades/PaymentForm/PaymentMadeFormPage'
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
name: 'payment-made-new',
|
||||||
breadcrumb: 'New Payment Made',
|
breadcrumb: 'New Payment Made',
|
||||||
pageTitle: formatMessage({ id: 'new_payment_made' }),
|
pageTitle: formatMessage({ id: 'new_payment_made' }),
|
||||||
sidebarShrink: true,
|
sidebarShrink: true,
|
||||||
@@ -492,4 +505,11 @@ export default [
|
|||||||
breadcrumb: 'Payment Made List',
|
breadcrumb: 'Payment Made List',
|
||||||
pageTitle: formatMessage({ id: 'payment_made_list' }),
|
pageTitle: formatMessage({ id: 'payment_made_list' }),
|
||||||
},
|
},
|
||||||
|
// Homepage
|
||||||
|
{
|
||||||
|
path: `/`,
|
||||||
|
component: lazy(() => import('containers/Homepage/Homepage')),
|
||||||
|
breadcrumb: 'Home',
|
||||||
|
pageTitle: 'Homepage',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -396,4 +396,8 @@ export default {
|
|||||||
path: ['M9,5v2h6.59L4,18.59L5.41,20L17,8.41V15h2V5H9'],
|
path: ['M9,5v2h6.59L4,18.59L5.41,20L17,8.41V15h2V5H9'],
|
||||||
viewBox: '0 0 24 24',
|
viewBox: '0 0 24 24',
|
||||||
},
|
},
|
||||||
|
'receipt-24': {
|
||||||
|
path: ['M19.5 3.5L18 2l-1.5 1.5L15 2l-1.5 1.5L12 2l-1.5 1.5L9 2 7.5 3.5 6 2 4.5 3.5 3 2v20l1.5-1.5L6 22l1.5-1.5L9 22l1.5-1.5L12 22l1.5-1.5L15 22l1.5-1.5L18 22l1.5-1.5L21 22V2l-1.5 1.5zM19 19.09H5V4.91h14v14.18zM6 15h12v2H6zm0-4h12v2H6zm0-4h12v2H6z'],
|
||||||
|
viewBox: '0 0 24 24',
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
@import 'components/Toast';
|
@import 'components/Toast';
|
||||||
@import 'components/PageForm';
|
@import 'components/PageForm';
|
||||||
@import 'components/Tooltip';
|
@import 'components/Tooltip';
|
||||||
|
@import 'components/Postbox';
|
||||||
|
|
||||||
// Pages
|
// Pages
|
||||||
@import 'pages/view-form';
|
@import 'pages/view-form';
|
||||||
@@ -99,4 +100,4 @@ body.hide-scrollbar .Pane2{
|
|||||||
|
|
||||||
.bp3-progress-bar.bp3-intent-primary .bp3-progress-meter{
|
.bp3-progress-bar.bp3-intent-primary .bp3-progress-meter{
|
||||||
background-color: #0066ff;
|
background-color: #0066ff;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
|
|
||||||
.big-amount{
|
.big-amount{
|
||||||
|
text-align: right;
|
||||||
|
|
||||||
&__label{
|
&__label{
|
||||||
color: #5d6f90;
|
color: #5d6f90;
|
||||||
@@ -11,7 +12,7 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
font-weight: 400;
|
font-weight: 500;
|
||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
color: #343463;
|
color: #343463;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
.datatable-editor {
|
.datatable-editor {
|
||||||
|
|
||||||
.bp3-form-group {
|
.bp3-form-group {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
.table {
|
.table {
|
||||||
border: 1px solid #d2dce2;
|
border: 1px solid #d2dce2;
|
||||||
border-left: transparent;
|
border-left: transparent;
|
||||||
|
background-color: #FFF;
|
||||||
|
|
||||||
.th,
|
.th,
|
||||||
.td {
|
.td {
|
||||||
border-left: 1px solid #e2e2e2;
|
border-left: 1px dashed #e2e2e2;
|
||||||
|
|
||||||
&.index {
|
&.index {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@@ -29,7 +31,8 @@
|
|||||||
background-color: #f0f2f8;
|
background-color: #f0f2f8;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #1c1940;
|
color: #415060;
|
||||||
|
border-bottom: 1px solid #d2dce2;
|
||||||
|
|
||||||
&.index > div {
|
&.index > div {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -39,13 +42,14 @@
|
|||||||
|
|
||||||
.tbody {
|
.tbody {
|
||||||
.tr .td {
|
.tr .td {
|
||||||
padding: 5px;
|
padding: 4px;
|
||||||
border-bottom: 0;
|
border-bottom: 0;
|
||||||
border-top: 1px dashed #aaa;
|
border-top: 1px solid #d8d8d8;
|
||||||
min-height: 42px;
|
min-height: 40px;
|
||||||
|
|
||||||
&.index {
|
&.index {
|
||||||
background-color: #f0f2f8;
|
background-color: #f0f2f8;
|
||||||
|
color: #718294;
|
||||||
|
|
||||||
> span {
|
> span {
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
@@ -126,6 +130,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tfooter{
|
||||||
|
.td{
|
||||||
|
min-height: 38px;
|
||||||
|
}
|
||||||
|
}
|
||||||
.th {
|
.th {
|
||||||
color: #444;
|
color: #444;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
@@ -142,6 +152,23 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.tbody,
|
||||||
|
.thead,
|
||||||
|
.tfooter{
|
||||||
|
|
||||||
|
// .total,
|
||||||
|
.quantity,
|
||||||
|
.rate,
|
||||||
|
.discount{
|
||||||
|
|
||||||
|
&,
|
||||||
|
input{
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.table {
|
.table {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
padding: 14px 18px;
|
padding: 14px 18px;
|
||||||
border-top: 1px solid rgba(0, 0, 0, 0.05);
|
border-top: 1px solid rgb(210, 221, 226);
|
||||||
box-shadow: 0px -1px 4px 0px rgba(0, 0, 0, 0.05);
|
box-shadow: 0px -1px 4px 0px rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
.bp3-button-group{
|
.bp3-button-group{
|
||||||
@@ -33,6 +33,14 @@
|
|||||||
&--strip {
|
&--strip {
|
||||||
#{$self}__header-fields {
|
#{$self}__header-fields {
|
||||||
width: 85%;
|
width: 85%;
|
||||||
|
|
||||||
|
.bp3-form-group{
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .bp3-form-group:last-of-type{
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#{$self}__body,
|
#{$self}__body,
|
||||||
#{$self}__footer {
|
#{$self}__footer {
|
||||||
@@ -40,8 +48,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#{$self}__header {
|
#{$self}__header {
|
||||||
background-color: #fbfbfb;
|
background-color: #FFF;
|
||||||
padding: 30px 20px 0;
|
padding: 25px 32px;
|
||||||
border-bottom: 1px solid #d2dce2;
|
border-bottom: 1px solid #d2dce2;
|
||||||
|
|
||||||
.bp3-form-group.bp3-inline label.bp3-label {
|
.bp3-form-group.bp3-inline label.bp3-label {
|
||||||
@@ -50,15 +58,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#{$self}__body {
|
#{$self}__body {
|
||||||
padding-top: 15px;
|
padding: 18px 32px 0;
|
||||||
padding-left: 20px;
|
|
||||||
padding-right: 20px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#{$self}__footer {
|
#{$self}__footer {
|
||||||
margin: 25px 0 0 0;
|
margin: 20px 0 0 0;
|
||||||
padding-left: 20px;
|
padding-left: 32px;
|
||||||
padding-right: 20px;
|
padding-right: 32px;
|
||||||
|
|
||||||
label.bp3-label{
|
label.bp3-label{
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
|||||||
48
client/src/style/components/Postbox.scss
Normal file
48
client/src/style/components/Postbox.scss
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
.postbox {
|
||||||
|
border: 1px solid #d2dce2;
|
||||||
|
background: #FFF;
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
border-bottom: 1px solid #d2dde2;
|
||||||
|
height: 38px;
|
||||||
|
padding-left: 18px;
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
vertical-align: middle;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-toggable .postbox__header {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__toggle-indicator {
|
||||||
|
margin-left: auto;
|
||||||
|
width: 40px;
|
||||||
|
text-align: center;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: "";
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-left: 4px solid transparent;
|
||||||
|
border-right: 4px solid transparent;
|
||||||
|
border-bottom: 6px solid #8ca0b3;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {}
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
|
||||||
|
&-inner {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
}
|
}
|
||||||
.path-1,
|
.path-1,
|
||||||
.path-13 {
|
.path-13 {
|
||||||
fill: #4f5861;
|
fill: #2d95fd;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ label.bp3-label {
|
|||||||
background: #e9ecef;
|
background: #e9ecef;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bp3-form-group.bp3-intent-danger & {
|
.bp3-form-group.bp3-intent-danger > & {
|
||||||
box-shadow: 0 0 0 transparent;
|
box-shadow: 0 0 0 transparent;
|
||||||
border-color: #db3737;
|
border-color: #db3737;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,17 @@
|
|||||||
.dashboard__insider--bill-form{
|
|
||||||
background-color: #FFF;
|
body.page-bill-new,
|
||||||
|
body.page-bill-edit{
|
||||||
|
|
||||||
|
.dashboard__footer{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dashboard__insider--bill-form{
|
||||||
|
padding-bottom: 64px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.page-form--bill{
|
.page-form--bill{
|
||||||
$self: '.page-form';
|
$self: '.page-form';
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,18 @@
|
|||||||
@import '../../Base.scss';
|
@import '../../Base.scss';
|
||||||
|
|
||||||
|
|
||||||
|
body.page-customer-new,
|
||||||
|
body.page-customer-edit{
|
||||||
|
|
||||||
|
.dashboard__footer{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard__insider--customer-form{
|
||||||
|
padding-bottom: 64px;
|
||||||
|
}
|
||||||
|
|
||||||
.page-form--customer {
|
.page-form--customer {
|
||||||
$self: '.page-form';
|
$self: '.page-form';
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ $dashboard-views-bar-height: 45px;
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-bottom: 1px solid #dddee3;
|
border-bottom: 1px solid #c7d5db;
|
||||||
|
|
||||||
&-right,
|
&-right,
|
||||||
&-left {
|
&-left {
|
||||||
@@ -137,7 +137,11 @@ $dashboard-views-bar-height: 45px;
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__actions-bar {
|
&__actions-bar {
|
||||||
border-bottom: 2px solid #eaeaea;
|
border-bottom: 2px solid #e1e2e8;
|
||||||
|
|
||||||
|
.bp3-navbar-divider{
|
||||||
|
border-left-color: rgb(199, 214, 219);
|
||||||
|
}
|
||||||
|
|
||||||
.#{$ns}-navbar {
|
.#{$ns}-navbar {
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
@@ -493,3 +497,5 @@ $dashboard-views-bar-height: 45px;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
.dashboard__insider--expense-form{
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
.dashboard__insider--expenses{
|
|
||||||
|
|
||||||
|
.dashboard__insider--expenses{
|
||||||
|
|
||||||
.bigcapital-datatable{
|
.bigcapital-datatable{
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,16 @@
|
|||||||
|
|
||||||
|
body.page-item-new,
|
||||||
|
body.page-item-edit{
|
||||||
|
|
||||||
|
.dashboard__footer{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard__insider--item-form{
|
||||||
|
padding-bottom: 64px;
|
||||||
|
}
|
||||||
|
|
||||||
.page-form--item {
|
.page-form--item {
|
||||||
$self: '.page-form';
|
$self: '.page-form';
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
border-bottom: 1px solid #e7e7e7;
|
border-bottom: 1px solid #e7e7e7;
|
||||||
|
|
||||||
> span {
|
> span {
|
||||||
font-weight: 500;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,12 @@
|
|||||||
$self: '.page-form';
|
$self: '.page-form';
|
||||||
|
|
||||||
#{$self}__header{
|
#{$self}__header{
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
&-fields {
|
||||||
|
flex: 1 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
.bp3-label{
|
.bp3-label{
|
||||||
min-width: 140px;
|
min-width: 140px;
|
||||||
}
|
}
|
||||||
@@ -28,10 +34,3 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dashboard__insider{
|
|
||||||
|
|
||||||
&--make-journal-page{
|
|
||||||
background: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,14 @@
|
|||||||
|
|
||||||
|
body.page-payment-made-new,
|
||||||
|
body.page-payment-made-edit{
|
||||||
|
|
||||||
|
.dashboard__footer{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.dashboard__insider--payment-made{
|
.dashboard__insider--payment-made{
|
||||||
background-color: #FFF;
|
padding-bottom: 64px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-form--payment-made {
|
.page-form--payment-made {
|
||||||
|
|||||||
@@ -1,26 +1,37 @@
|
|||||||
|
|
||||||
|
body.page-payment-receive-new,
|
||||||
|
body.page-payment-receive-edit{
|
||||||
|
|
||||||
|
.dashboard__footer{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.dashboard__insider--payment-receive-form{
|
.dashboard__insider--payment-receive-form{
|
||||||
background-color: #FFF;
|
padding-bottom: 64px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.page-form--payment-receive {
|
.page-form--payment-receive {
|
||||||
$self: '.page-form';
|
$self: '.page-form';
|
||||||
|
|
||||||
#{$self}__header{
|
#{$self}__header {
|
||||||
.bp3-label{
|
.bp3-label {
|
||||||
min-width: 160px;
|
min-width: 160px;
|
||||||
}
|
}
|
||||||
.bp3-form-content{
|
|
||||||
|
.bp3-form-content {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bp3-form-group{
|
.bp3-form-group {
|
||||||
margin-bottom: 18px;
|
margin-bottom: 18px;
|
||||||
|
|
||||||
&.bp3-inline{
|
&.bp3-inline {
|
||||||
max-width: 470px;
|
max-width: 470px;
|
||||||
}
|
}
|
||||||
button.receive-full-amount{
|
|
||||||
|
button.receive-full-amount {
|
||||||
width: auto;
|
width: auto;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
min-height: auto;
|
min-height: auto;
|
||||||
@@ -29,28 +40,28 @@
|
|||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
color: #0052cc;
|
color: #0052cc;
|
||||||
|
|
||||||
&:hover{
|
&:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#{$self}__primary-section{
|
#{$self}__primary-section {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding-bottom: 2px;
|
padding-bottom: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#{$self}__big-numbers{
|
#{$self}__big-numbers {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.datatable-editor{
|
.datatable-editor {
|
||||||
|
|
||||||
.table .tbody{
|
.table .tbody {
|
||||||
.tr.no-results{
|
.tr.no-results {
|
||||||
.td{
|
.td {
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
padding: 26px 0;
|
padding: 26px 0;
|
||||||
color: #5a5a77;
|
color: #5a5a77;
|
||||||
@@ -58,11 +69,30 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.table{
|
.table {
|
||||||
.tr{
|
|
||||||
|
|
||||||
|
.th,
|
||||||
|
.td {
|
||||||
|
|
||||||
|
&.invoice_amount,
|
||||||
|
&.amount_due,
|
||||||
|
&.payment_amount {
|
||||||
|
|
||||||
|
&,
|
||||||
|
input {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tr {
|
||||||
|
|
||||||
.td:first-of-type,
|
.td:first-of-type,
|
||||||
.th:first-of-type{
|
.th:first-of-type {
|
||||||
span, div{
|
|
||||||
|
span,
|
||||||
|
div {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
@@ -71,12 +101,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#{$self}__footer{
|
#{$self}__footer {
|
||||||
.form-group--statement{
|
.form-group--statement {
|
||||||
max-width: 500px;
|
max-width: 500px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
textarea{
|
textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 70px;
|
min-height: 70px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,14 @@
|
|||||||
|
|
||||||
|
body.page-estimate-new,
|
||||||
|
body.page-estimate-edit{
|
||||||
|
|
||||||
|
.dashboard__footer{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.dashboard__insider--estimate-form{
|
.dashboard__insider--estimate-form{
|
||||||
background-color: #FFF;
|
padding-bottom: 64px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-form--estimate {
|
.page-form--estimate {
|
||||||
|
|||||||
@@ -1,5 +1,14 @@
|
|||||||
.dashboard__insider--invoice-form {
|
|
||||||
background-color: #fff;
|
body.page-invoice-new,
|
||||||
|
body.page-invoice-edit{
|
||||||
|
|
||||||
|
.dashboard__footer{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard__insider--invoice-form{
|
||||||
|
padding-bottom: 64px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-form--invoice {
|
.page-form--invoice {
|
||||||
@@ -20,7 +29,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.bp3-form-group {
|
.bp3-form-group {
|
||||||
margin-bottom: 18px;
|
|
||||||
|
|
||||||
&.bp3-inline {
|
&.bp3-inline {
|
||||||
max-width: 440px;
|
max-width: 440px;
|
||||||
|
|||||||
@@ -1,5 +1,15 @@
|
|||||||
|
|
||||||
|
|
||||||
|
body.page-receipt-new,
|
||||||
|
body.page-receipt-edit{
|
||||||
|
|
||||||
|
.dashboard__footer{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.dashboard__insider--receipt-form{
|
.dashboard__insider--receipt-form{
|
||||||
background-color: #fff;
|
padding-bottom: 64px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-form--receipt{
|
.page-form--receipt{
|
||||||
|
|||||||
@@ -1,5 +1,18 @@
|
|||||||
@import '../../Base.scss';
|
@import '../../Base.scss';
|
||||||
|
|
||||||
|
body.page-vendor-new,
|
||||||
|
body.page-vendor-edit{
|
||||||
|
|
||||||
|
.dashboard__footer{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard__insider--vendor-form{
|
||||||
|
padding-bottom: 64px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.page-form--vendor {
|
.page-form--vendor {
|
||||||
$self: '.page-form';
|
$self: '.page-form';
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
|
|||||||
@@ -136,8 +136,8 @@ export default class BillsController extends BaseController {
|
|||||||
check('entries.*.item_id').exists().isNumeric().toInt(),
|
check('entries.*.item_id').exists().isNumeric().toInt(),
|
||||||
check('entries.*.rate').exists().isNumeric().toFloat(),
|
check('entries.*.rate').exists().isNumeric().toFloat(),
|
||||||
check('entries.*.quantity').exists().isNumeric().toFloat(),
|
check('entries.*.quantity').exists().isNumeric().toFloat(),
|
||||||
check('entries.*.discount').optional().isNumeric().toFloat(),
|
check('entries.*.discount').optional({ nullable: true }).isNumeric().toFloat(),
|
||||||
check('entries.*.description').optional().trim().escape(),
|
check('entries.*.description').optional({ nullable: true }).trim().escape(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -108,10 +108,10 @@ export default class SalesEstimatesController extends BaseController {
|
|||||||
check('entries').exists().isArray({ min: 1 }),
|
check('entries').exists().isArray({ min: 1 }),
|
||||||
check('entries.*.index').exists().isNumeric().toInt(),
|
check('entries.*.index').exists().isNumeric().toInt(),
|
||||||
check('entries.*.item_id').exists().isNumeric().toInt(),
|
check('entries.*.item_id').exists().isNumeric().toInt(),
|
||||||
check('entries.*.description').optional().trim().escape(),
|
|
||||||
check('entries.*.quantity').exists().isNumeric().toInt(),
|
check('entries.*.quantity').exists().isNumeric().toInt(),
|
||||||
check('entries.*.rate').exists().isNumeric().toFloat(),
|
check('entries.*.rate').exists().isNumeric().toFloat(),
|
||||||
check('entries.*.discount').optional().isNumeric().toFloat(),
|
check('entries.*.description').optional({ nullable: true }).trim().escape(),
|
||||||
|
check('entries.*.discount').optional({ nullable: true }).isNumeric().toFloat(),
|
||||||
|
|
||||||
check('note').optional().trim().escape(),
|
check('note').optional().trim().escape(),
|
||||||
check('terms_conditions').optional().trim().escape(),
|
check('terms_conditions').optional().trim().escape(),
|
||||||
|
|||||||
@@ -106,8 +106,8 @@ export default class SaleInvoicesController extends BaseController {
|
|||||||
check('entries.*.item_id').exists().isNumeric().toInt(),
|
check('entries.*.item_id').exists().isNumeric().toInt(),
|
||||||
check('entries.*.rate').exists().isNumeric().toFloat(),
|
check('entries.*.rate').exists().isNumeric().toFloat(),
|
||||||
check('entries.*.quantity').exists().isNumeric().toFloat(),
|
check('entries.*.quantity').exists().isNumeric().toFloat(),
|
||||||
check('entries.*.discount').optional().isNumeric().toFloat(),
|
check('entries.*.discount').optional({ nullable: true }).isNumeric().toFloat(),
|
||||||
check('entries.*.description').optional().trim().escape(),
|
check('entries.*.description').optional({ nullable: true }).trim().escape(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -88,14 +88,14 @@ export default class SalesReceiptsController extends BaseController{
|
|||||||
check('closed').default(false).isBoolean().toBoolean(),
|
check('closed').default(false).isBoolean().toBoolean(),
|
||||||
|
|
||||||
check('entries').exists().isArray({ min: 1 }),
|
check('entries').exists().isArray({ min: 1 }),
|
||||||
|
|
||||||
check('entries.*.id').optional({ nullable: true }).isNumeric().toInt(),
|
check('entries.*.id').optional({ nullable: true }).isNumeric().toInt(),
|
||||||
check('entries.*.index').exists().isNumeric().toInt(),
|
check('entries.*.index').exists().isNumeric().toInt(),
|
||||||
check('entries.*.item_id').exists().isNumeric().toInt(),
|
check('entries.*.item_id').exists().isNumeric().toInt(),
|
||||||
check('entries.*.description').optional().trim().escape(),
|
|
||||||
check('entries.*.quantity').exists().isNumeric().toInt(),
|
check('entries.*.quantity').exists().isNumeric().toInt(),
|
||||||
check('entries.*.rate').exists().isNumeric().toInt(),
|
check('entries.*.rate').exists().isNumeric().toInt(),
|
||||||
check('entries.*.discount').optional().isNumeric().toInt(),
|
check('entries.*.discount').optional({ nullable: true }).isNumeric().toInt(),
|
||||||
|
check('entries.*.description').optional({ nullable: true }).trim().escape(),
|
||||||
|
|
||||||
check('receipt_message').optional().trim().escape(),
|
check('receipt_message').optional().trim().escape(),
|
||||||
check('statement').optional().trim().escape(),
|
check('statement').optional().trim().escape(),
|
||||||
|
|||||||
Reference in New Issue
Block a user