mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-18 13:50:31 +00:00
fix: logo style.
fix: page forms style. feat: auto-fill items entries from item details. fix: hiding dashboard copyright bar.
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import React from 'react';
|
||||
|
||||
import 'style/pages/ManualJournal/List.scss';
|
||||
|
||||
import { DashboardContentTable, DashboardPageContent } from 'components';
|
||||
|
||||
import { ManualJournalsListProvider } from './ManualJournalsListProvider';
|
||||
@@ -9,11 +11,8 @@ import ManualJournalsDataTable from './ManualJournalsDataTable';
|
||||
import ManualJournalsActionsBar from './ManualJournalActionsBar';
|
||||
|
||||
import withManualJournals from './withManualJournals';
|
||||
|
||||
import { transformTableStateToQuery, compose } from 'utils';
|
||||
|
||||
import 'style/pages/ManualJournal/List.scss';
|
||||
|
||||
/**
|
||||
* Manual journals table.
|
||||
*/
|
||||
|
||||
@@ -169,6 +169,7 @@ function MakeJournalEntriesForm({
|
||||
<MakeJournalFormFooter />
|
||||
<MakeJournalFormFloatingActions />
|
||||
|
||||
{/* --------- Dialogs --------- */}
|
||||
<MakeJournalFormDialogs />
|
||||
</Form>
|
||||
</Formik>
|
||||
|
||||
@@ -1,12 +1,27 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { useFormikContext } from 'formik';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import MakeJournalEntriesHeaderFields from "./MakeJournalEntriesHeaderFields";
|
||||
import { PageFormBigNumber } from 'components';
|
||||
import { safeSumBy } from 'utils';
|
||||
|
||||
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 (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
|
||||
<MakeJournalEntriesHeaderFields />
|
||||
|
||||
<PageFormBigNumber
|
||||
label={'Due Amount'}
|
||||
amount={total}
|
||||
currencyCode={'USD'}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import React from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
import 'style/pages/ManualJournal/MakeJournal.scss';
|
||||
|
||||
import MakeJournalEntriesForm from './MakeJournalEntriesForm';
|
||||
import { MakeJournalProvider } from './MakeJournalProvider';
|
||||
|
||||
import 'style/pages/ManualJournal/MakeJournal.scss';
|
||||
|
||||
/**
|
||||
* Make journal entries page.
|
||||
*/
|
||||
|
||||
@@ -9,9 +9,7 @@ import { updateDataReducer } from './utils';
|
||||
import { useMakeJournalFormContext } from './MakeJournalProvider';
|
||||
import { useJournalTableEntriesColumns } from './components';
|
||||
|
||||
import JournalDeleteEntriesAlert from 'containers/Alerts/ManualJournals/JournalDeleteEntriesAlert';
|
||||
import { compose } from 'redux';
|
||||
import { repeatValue } from 'utils';
|
||||
|
||||
/**
|
||||
* Make journal entries table component.
|
||||
@@ -32,13 +30,7 @@ function MakeJournalEntriesTable({
|
||||
|
||||
// Memorized data table columns.
|
||||
const columns = useJournalTableEntriesColumns();
|
||||
|
||||
// Handles click new line.
|
||||
const onClickNewRow = () => {
|
||||
const newRows = [...entries, defaultEntry];
|
||||
saveInvoke(onChange, newRows);
|
||||
};
|
||||
|
||||
|
||||
// Handles update datatable data.
|
||||
const handleUpdateData = (rowIndex, columnId, value) => {
|
||||
const newRows = updateDataReducer(entries, rowIndex, columnId, value);
|
||||
@@ -50,61 +42,28 @@ function MakeJournalEntriesTable({
|
||||
const newRows = removeRowsByIndex(entries, rowIndex);
|
||||
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 (
|
||||
<>
|
||||
<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
|
||||
small={true}
|
||||
className={'button--secondary button--clear-lines ml1'}
|
||||
onClick={handleClickClearAllLines}
|
||||
>
|
||||
<T id={'clear_all_lines'} />
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<JournalDeleteEntriesAlert
|
||||
name={'make-journal-delete-all-entries'}
|
||||
onConfirm={handleCofirmClearEntriesAlert}
|
||||
/>
|
||||
</>
|
||||
<DataTableEditable
|
||||
columns={columns}
|
||||
data={entries}
|
||||
sticky={true}
|
||||
totalRow={true}
|
||||
footer={true}
|
||||
payload={{
|
||||
accounts,
|
||||
errors: error,
|
||||
updateData: handleUpdateData,
|
||||
removeRow: handleRemoveRow,
|
||||
contacts: customers.map((customer) => ({
|
||||
...customer,
|
||||
contact_type: 'customer',
|
||||
})),
|
||||
autoFocus: ['account_id', 0],
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,39 +4,41 @@ import classNames from 'classnames';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import { FormGroup, TextArea } from '@blueprintjs/core';
|
||||
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 { inputIntent } from 'utils';
|
||||
|
||||
export default function MakeJournalFormFooter() {
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
||||
<Row>
|
||||
<Col md={8}>
|
||||
<FastField name={'description'}>
|
||||
{({ field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'description'} />}
|
||||
className={'form-group--description'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="description" />}
|
||||
fill={true}
|
||||
>
|
||||
<TextArea fill={true} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
<Postbox title={'Journal details'} defaultOpen={false}>
|
||||
<Row>
|
||||
<Col md={8}>
|
||||
<FastField name={'description'}>
|
||||
{({ field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'description'} />}
|
||||
className={'form-group--description'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="description" />}
|
||||
fill={true}
|
||||
>
|
||||
<TextArea fill={true} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
|
||||
<Col md={4}>
|
||||
<Dragzone
|
||||
initialFiles={[]}
|
||||
// onDrop={handleDropFiles}
|
||||
// onDeleteFile={handleDeleteFile}
|
||||
hint={'Attachments: Maxiumum size: 20MB'}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Col md={4}>
|
||||
<Dragzone
|
||||
initialFiles={[]}
|
||||
// onDrop={handleDropFiles}
|
||||
// onDeleteFile={handleDeleteFile}
|
||||
hint={'Attachments: Maxiumum size: 20MB'}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Postbox>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -29,7 +29,9 @@ function InvoiceNumberDialogContent({
|
||||
const { mutateAsync: saveSettings } = useSaveSettings();
|
||||
|
||||
const handleSubmitForm = (values, { setSubmitting }) => {
|
||||
const options = optionsMapToArray(values).map((option) => ({
|
||||
const { mode, ...autoModeValues } = values;
|
||||
|
||||
const options = optionsMapToArray(autoModeValues).map((option) => ({
|
||||
key: option.key,
|
||||
...option,
|
||||
group: 'sales_invoices',
|
||||
|
||||
@@ -14,7 +14,6 @@ function InvoiceNumberDialog({
|
||||
isOpen,
|
||||
onConfirm,
|
||||
}) {
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
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 { FormattedMessage as T } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
import { useItem } from 'hooks/query';
|
||||
|
||||
import ItemsEntriesDeleteAlert from 'containers/Alerts/ItemsEntries/ItemsEntriesDeleteAlert';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
@@ -17,7 +18,15 @@ import {
|
||||
removeRowsByIndex,
|
||||
compose,
|
||||
} 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.
|
||||
@@ -34,11 +43,64 @@ function ItemsEntriesTable({
|
||||
errors,
|
||||
onUpdateData,
|
||||
linesNumber,
|
||||
itemType, // sellable or purchasable
|
||||
}) {
|
||||
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.
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (entries && entries !== rows) {
|
||||
setRows(entries);
|
||||
}
|
||||
@@ -50,15 +112,19 @@ function ItemsEntriesTable({
|
||||
// Handles the editor data update.
|
||||
const handleUpdateData = useCallback(
|
||||
(rowIndex, columnId, value) => {
|
||||
if (columnId === 'item_id') {
|
||||
setRowItem({ rowIndex, columnId, itemId: value });
|
||||
}
|
||||
const newRows = compose(
|
||||
updateAutoAddNewLine(defaultEntry),
|
||||
updateItemsEntriesTotal,
|
||||
updateTableRow(rowIndex, columnId, value),
|
||||
)(entries);
|
||||
)(rows);
|
||||
|
||||
setRows(newRows);
|
||||
onUpdateData(newRows);
|
||||
},
|
||||
[entries, onUpdateData],
|
||||
[rows, defaultEntry, onUpdateData],
|
||||
);
|
||||
|
||||
// Handle table rows removing by index.
|
||||
@@ -80,9 +146,7 @@ function ItemsEntriesTable({
|
||||
openAlert('items-entries-clear-lines');
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle alert confirm of clear all lines.
|
||||
*/
|
||||
// Handle alert confirm of clear all lines.
|
||||
const handleClearLinesAlertConfirm = () => {
|
||||
const newRows = repeatValue(defaultEntry, linesNumber);
|
||||
setRows(newRows);
|
||||
@@ -94,8 +158,12 @@ function ItemsEntriesTable({
|
||||
<DataTableEditable
|
||||
className={classNames(CLASSES.DATATABLE_EDITOR_ITEMS_ENTRIES)}
|
||||
columns={columns}
|
||||
data={entries}
|
||||
data={rows}
|
||||
sticky={true}
|
||||
progressBarLoading={isItemFetching}
|
||||
cellsLoading={isItemFetching}
|
||||
cellsLoadingCoords={cellsLoading}
|
||||
footer={true}
|
||||
payload={{
|
||||
items,
|
||||
errors: errors || [],
|
||||
@@ -103,25 +171,7 @@ function ItemsEntriesTable({
|
||||
removeRow: handleRemoveRow,
|
||||
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
|
||||
name={'items-entries-clear-lines'}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
import { Tooltip, Button, Intent, Position } from '@blueprintjs/core';
|
||||
import { sumBy } from 'lodash';
|
||||
import { Hint, Icon } from 'components';
|
||||
import { formattedAmount } from 'utils';
|
||||
import { formattedAmount, safeSumBy } from 'utils';
|
||||
import {
|
||||
InputGroupCell,
|
||||
MoneyFieldCell,
|
||||
@@ -62,7 +61,7 @@ export function ActionsCellRenderer({
|
||||
* Quantity total footer cell.
|
||||
*/
|
||||
export function QuantityTotalFooterCell({ rows }) {
|
||||
const quantity = sumBy(rows, r => parseInt(r.original.quantity, 10));
|
||||
const quantity = safeSumBy(rows, 'original.quantity');
|
||||
return <span>{ quantity }</span>;
|
||||
}
|
||||
|
||||
@@ -70,7 +69,7 @@ export function QuantityTotalFooterCell({ rows }) {
|
||||
* Total footer cell.
|
||||
*/
|
||||
export function TotalFooterCell({ rows }) {
|
||||
const total = sumBy(rows, 'original.total');
|
||||
const total = safeSumBy(rows, 'original.total');
|
||||
return <span>{ formattedAmount(total, 'USD') }</span>;
|
||||
}
|
||||
|
||||
@@ -110,9 +109,8 @@ export function useEditableItemsEntriesColumns() {
|
||||
Cell: ItemsListCell,
|
||||
Footer: ItemFooterCell,
|
||||
disableSortBy: true,
|
||||
width: 180,
|
||||
// filterPurchasable: filterPurchasableItems,
|
||||
// filterSellable: filterSellableItems,
|
||||
width: 130,
|
||||
className: 'item',
|
||||
},
|
||||
{
|
||||
Header: formatMessage({ id: 'description' }),
|
||||
@@ -120,7 +118,7 @@ export function useEditableItemsEntriesColumns() {
|
||||
Cell: InputGroupCell,
|
||||
disableSortBy: true,
|
||||
className: 'description',
|
||||
width: 100,
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
Header: formatMessage({ id: 'quantity' }),
|
||||
@@ -128,7 +126,7 @@ export function useEditableItemsEntriesColumns() {
|
||||
Cell: NumericInputCell,
|
||||
Footer: QuantityTotalFooterCell,
|
||||
disableSortBy: true,
|
||||
width: 80,
|
||||
width: 70,
|
||||
className: 'quantity',
|
||||
},
|
||||
{
|
||||
@@ -136,7 +134,7 @@ export function useEditableItemsEntriesColumns() {
|
||||
accessor: 'rate',
|
||||
Cell: MoneyFieldCell,
|
||||
disableSortBy: true,
|
||||
width: 80,
|
||||
width: 70,
|
||||
className: 'rate',
|
||||
},
|
||||
{
|
||||
@@ -144,7 +142,7 @@ export function useEditableItemsEntriesColumns() {
|
||||
accessor: 'discount',
|
||||
Cell: PercentFieldCell,
|
||||
disableSortBy: true,
|
||||
width: 80,
|
||||
width: 60,
|
||||
className: 'discount',
|
||||
},
|
||||
{
|
||||
@@ -153,7 +151,7 @@ export function useEditableItemsEntriesColumns() {
|
||||
accessor: 'total',
|
||||
Cell: TotalCell,
|
||||
disableSortBy: true,
|
||||
width: 120,
|
||||
width: 100,
|
||||
className: 'total',
|
||||
},
|
||||
{
|
||||
|
||||
@@ -23,4 +23,9 @@ export function updateItemsEntriesTotal(rows) {
|
||||
...row,
|
||||
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
|
||||
const handleError = (error) => {
|
||||
transformErrors(error, { setErrors });
|
||||
const handleError = ({ response: { data: { errors } } }) => {
|
||||
transformErrors(errors, { setErrors });
|
||||
setSubmitting(false);
|
||||
};
|
||||
if (isNewMode) {
|
||||
|
||||
@@ -15,7 +15,7 @@ import { transformUpdatedRows, compose, saveInvoke, repeatValue } from 'utils';
|
||||
* Expenses form entries.
|
||||
*/
|
||||
function ExpenseFormEntriesTable({
|
||||
// #withAlertActions
|
||||
// #withAlertActions
|
||||
openAlert,
|
||||
|
||||
// #ownPorps
|
||||
@@ -57,65 +57,21 @@ function ExpenseFormEntriesTable({
|
||||
[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 (
|
||||
<>
|
||||
<DataTableEditable
|
||||
columns={columns}
|
||||
data={entries}
|
||||
sticky={true}
|
||||
payload={{
|
||||
accounts: accounts,
|
||||
errors: error,
|
||||
updateData: handleUpdateData,
|
||||
removeRow: handleRemoveRow,
|
||||
autoFocus: ['expense_account_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>
|
||||
</>
|
||||
}
|
||||
totalRow={true}
|
||||
/>
|
||||
<ExpenseDeleteEntriesAlert
|
||||
name={'expense-delete-entries'}
|
||||
onConfirm={handleConfirmClearEntriesAlert}
|
||||
/>
|
||||
</>
|
||||
<DataTableEditable
|
||||
columns={columns}
|
||||
data={entries}
|
||||
sticky={true}
|
||||
payload={{
|
||||
accounts: accounts,
|
||||
errors: error,
|
||||
updateData: handleUpdateData,
|
||||
removeRow: handleRemoveRow,
|
||||
autoFocus: ['expense_account_id', 0],
|
||||
}}
|
||||
footer={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertActions
|
||||
)(ExpenseFormEntriesTable);
|
||||
export default compose(withAlertActions)(ExpenseFormEntriesTable);
|
||||
|
||||
@@ -4,36 +4,38 @@ import { FormGroup, TextArea } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
import { inputIntent } from 'utils';
|
||||
import { Row, Dragzone, Col } from 'components';
|
||||
import { Row, Dragzone, Col, Postbox } from 'components';
|
||||
import { CLASSES } from 'common/classes';
|
||||
|
||||
export default function ExpenseFormFooter() {
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
||||
<Row>
|
||||
<Col md={8}>
|
||||
<FastField name={'description'}>
|
||||
{({ field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'description'} />}
|
||||
className={'form-group--description'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
>
|
||||
<TextArea growVertically={true} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
<Postbox title={'Expense details'} defaultOpen={false}>
|
||||
<Row>
|
||||
<Col md={8}>
|
||||
<FastField name={'description'}>
|
||||
{({ field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'description'} />}
|
||||
className={'form-group--description'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
>
|
||||
<TextArea growVertically={true} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
|
||||
<Col md={4}>
|
||||
<Dragzone
|
||||
initialFiles={[]}
|
||||
// onDrop={handleDropFiles}
|
||||
// onDeleteFile={handleDeleteFile}
|
||||
hint={'Attachments: Maxiumum size: 20MB'}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Col md={4}>
|
||||
<Dragzone
|
||||
initialFiles={[]}
|
||||
// onDrop={handleDropFiles}
|
||||
// onDeleteFile={handleDeleteFile}
|
||||
hint={'Attachments: Maxiumum size: 20MB'}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Postbox>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import React from 'react';
|
||||
import { InputGroup, FormGroup, Position, Classes } from '@blueprintjs/core';
|
||||
import { DateInput } from '@blueprintjs/datetime';
|
||||
import { FastField } from 'formik';
|
||||
import { FastField, ErrorMessage } from 'formik';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import {
|
||||
momentFormatter,
|
||||
@@ -10,16 +11,13 @@ import {
|
||||
inputIntent,
|
||||
handleDateChange,
|
||||
} from 'utils';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
CurrencySelectList,
|
||||
ContactSelecetList,
|
||||
ErrorMessage,
|
||||
AccountsSelectList,
|
||||
FieldRequiredHint,
|
||||
Hint,
|
||||
} from 'components';
|
||||
|
||||
import { ACCOUNT_PARENT_TYPE } from 'common/accountTypes';
|
||||
import { useExpenseFormContext } from './ExpenseFormPageProvider';
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
import 'style/pages/Expense/PageForm.scss';
|
||||
|
||||
import ExpenseForm from './ExpenseForm';
|
||||
import { ExpenseFormPageProvider } from './ExpenseFormPageProvider';
|
||||
|
||||
import 'style/pages/Expense/PageForm.scss';
|
||||
|
||||
/**
|
||||
* Expense page form.
|
||||
*/
|
||||
|
||||
@@ -39,6 +39,8 @@ const defaultInitialValues = {
|
||||
category_id: '',
|
||||
sellable: 1,
|
||||
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 { useFormik } from 'formik';
|
||||
import { Row, Col, ErrorMessage } from 'components';
|
||||
import { Formik, Form } from 'formik';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import {
|
||||
Button,
|
||||
Classes,
|
||||
FormGroup,
|
||||
InputGroup,
|
||||
Intent,
|
||||
} from '@blueprintjs/core';
|
||||
import { Button, Classes } from '@blueprintjs/core';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import { saveInvoke } from 'utils';
|
||||
import ReferenceNumberFormContent from './ReferenceNumberFormContent';
|
||||
|
||||
/**
|
||||
* Reference number form.
|
||||
@@ -21,6 +17,7 @@ export default function ReferenceNumberForm({
|
||||
initialNumber,
|
||||
}) {
|
||||
const validationSchema = Yup.object().shape({
|
||||
// mode: Yup.string(),
|
||||
number_prefix: Yup.string(),
|
||||
next_number: Yup.number(),
|
||||
});
|
||||
@@ -33,85 +30,44 @@ export default function ReferenceNumberForm({
|
||||
[initialPrefix, initialNumber],
|
||||
);
|
||||
|
||||
const {
|
||||
errors,
|
||||
touched,
|
||||
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>
|
||||
const handleSubmit = (values) => {
|
||||
debugger;
|
||||
saveInvoke(onSubmit, values);
|
||||
};
|
||||
|
||||
{/* ------------- Next number ------------- */}
|
||||
<Col xs={6}>
|
||||
<FormGroup
|
||||
label={<T id={'next_number'} />}
|
||||
className={'form-group--'}
|
||||
intent={
|
||||
errors.next_number && touched.next_number && Intent.DANGER
|
||||
}
|
||||
helperText={
|
||||
<ErrorMessage name={'next_number'} {...{ errors, touched }} />
|
||||
}
|
||||
>
|
||||
<InputGroup
|
||||
intent={
|
||||
errors.next_number && touched.next_number && Intent.DANGER
|
||||
}
|
||||
{...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>
|
||||
return (
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
{({ isSubmitting }) => (
|
||||
<Form>
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
<p className="paragraph">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce
|
||||
tincidunt porta quam,
|
||||
</p>
|
||||
|
||||
<ReferenceNumberFormContent />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</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>
|
||||
</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 { FastField } from 'formik';
|
||||
import classNames from 'classnames';
|
||||
import { Row, Col } from 'components';
|
||||
import { Postbox, Row, Col } from 'components';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import Dragzone from 'components/Dragzone';
|
||||
import { inputIntent } from 'utils';
|
||||
@@ -12,30 +12,32 @@ import { inputIntent } from 'utils';
|
||||
export default function BillFormFooter() {
|
||||
return (
|
||||
<div class={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
||||
<Row>
|
||||
<Col md={8}>
|
||||
<FastField name={'note'}>
|
||||
{({ field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'note'} />}
|
||||
className={'form-group--note'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
>
|
||||
<TextArea growVertically={true} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
<Postbox title={'Bill details'} defaultOpen={false}>
|
||||
<Row>
|
||||
<Col md={8}>
|
||||
<FastField name={'note'}>
|
||||
{({ field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'note'} />}
|
||||
className={'form-group--note'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
>
|
||||
<TextArea growVertically={true} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
|
||||
<Col md={4}>
|
||||
<Dragzone
|
||||
initialFiles={[]}
|
||||
// onDrop={onDropFiles}
|
||||
// onDeleteFile={onDropFiles}
|
||||
hint={'Attachments: Maxiumum size: 20MB'}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Col md={4}>
|
||||
<Dragzone
|
||||
initialFiles={[]}
|
||||
// onDrop={onDropFiles}
|
||||
// onDeleteFile={onDropFiles}
|
||||
hint={'Attachments: Maxiumum size: 20MB'}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Postbox>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ export const defaultBillEntry = {
|
||||
index: 0,
|
||||
item_id: '',
|
||||
rate: '',
|
||||
discount: 0,
|
||||
quantity: 1,
|
||||
discount: '',
|
||||
quantity: '',
|
||||
description: '',
|
||||
};
|
||||
|
||||
|
||||
@@ -20,23 +20,23 @@ function PaymentMadeEntriesTable({
|
||||
entries,
|
||||
|
||||
// #withAlertsActions
|
||||
openAlert
|
||||
openAlert,
|
||||
}) {
|
||||
const {
|
||||
paymentVendorId,
|
||||
isDueBillsFetching,
|
||||
} = usePaymentMadeFormContext();
|
||||
const { paymentVendorId, isDueBillsFetching } = usePaymentMadeFormContext();
|
||||
|
||||
const columns = usePaymentMadeEntriesTableColumns();
|
||||
|
||||
// Handle update data.
|
||||
const handleUpdateData = useCallback((rowIndex, columnId, value) => {
|
||||
const newRows = compose(
|
||||
updateTableRow(rowIndex, columnId, value),
|
||||
)(entries);
|
||||
|
||||
onUpdateData(newRows);
|
||||
}, [onUpdateData, entries]);
|
||||
// Handle update data.
|
||||
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
|
||||
// selecting vendor id.
|
||||
@@ -44,15 +44,6 @@ function PaymentMadeEntriesTable({
|
||||
? '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.';
|
||||
|
||||
// Handle clear all lines action.
|
||||
const handleClearAllLines = () => {
|
||||
const fullAmount = safeSumBy(entries, 'payment_amount');
|
||||
|
||||
if (fullAmount > 0) {
|
||||
openAlert('clear-all-lines-payment-made');
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<CloudLoadingIndicator isLoading={isDueBillsFetching}>
|
||||
<DataTableEditable
|
||||
@@ -66,21 +57,10 @@ function PaymentMadeEntriesTable({
|
||||
updateData: handleUpdateData,
|
||||
}}
|
||||
noResults={noResultsMessage}
|
||||
actions={
|
||||
<Button
|
||||
small={true}
|
||||
className={'button--secondary button--clear-lines'}
|
||||
onClick={handleClearAllLines}
|
||||
>
|
||||
<T id={'clear_all_lines'} />
|
||||
</Button>
|
||||
}
|
||||
totalRow={true}
|
||||
footer={true}
|
||||
/>
|
||||
</CloudLoadingIndicator>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertActions
|
||||
)(PaymentMadeEntriesTable);
|
||||
export default compose(withAlertActions)(PaymentMadeEntriesTable);
|
||||
|
||||
@@ -3,7 +3,7 @@ import classNames from 'classnames';
|
||||
import { FormGroup, TextArea } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import { FastField } from 'formik';
|
||||
import { Row, Col } from 'components';
|
||||
import { Postbox, Row, Col } from 'components';
|
||||
import { CLASSES } from 'common/classes';
|
||||
|
||||
/**
|
||||
@@ -12,21 +12,23 @@ import { CLASSES } from 'common/classes';
|
||||
export default function PaymentMadeFooter() {
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
||||
<Row>
|
||||
<Col md={8}>
|
||||
{/* --------- Statement --------- */}
|
||||
<FastField name={'customer_name'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'statement'} />}
|
||||
className={'form-group--statement'}
|
||||
>
|
||||
<TextArea growVertically={true} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
</Row>
|
||||
<Postbox title={'Payment made details'} defaultOpen={false}>
|
||||
<Row>
|
||||
<Col md={8}>
|
||||
{/* --------- Statement --------- */}
|
||||
<FastField name={'customer_name'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'statement'} />}
|
||||
className={'form-group--statement'}
|
||||
>
|
||||
<TextArea growVertically={true} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
</Row>
|
||||
</Postbox>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,55 +4,57 @@ import { FormattedMessage as T } from 'react-intl';
|
||||
import { FastField } from 'formik';
|
||||
import classNames from 'classnames';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import { Row, Col } from 'components';
|
||||
import { Row, Col, Postbox } from 'components';
|
||||
import Dragzone from 'components/Dragzone';
|
||||
|
||||
import { inputIntent } from 'utils';
|
||||
|
||||
/**
|
||||
* Estimate form footer.
|
||||
*/
|
||||
*/
|
||||
export default function EstiamteFormFooter({}) {
|
||||
return (
|
||||
<div class={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
||||
<Row>
|
||||
<Col md={8}>
|
||||
{/* --------- Customer Note --------- */}
|
||||
<FastField name={'note'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'customer_note'} />}
|
||||
className={'form-group--customer_note'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
>
|
||||
<TextArea growVertically={true} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
<Postbox title={'Estimate details'} defaultOpen={false}>
|
||||
<Row>
|
||||
<Col md={8}>
|
||||
{/* --------- Customer Note --------- */}
|
||||
<FastField name={'note'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'customer_note'} />}
|
||||
className={'form-group--customer_note'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
>
|
||||
<TextArea growVertically={true} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
{/* --------- Terms and conditions --------- */}
|
||||
<FastField name={'terms_conditions'}>
|
||||
{({ field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'terms_conditions'} />}
|
||||
className={'form-group--terms_conditions'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
>
|
||||
<TextArea growVertically={true} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
{/* --------- Terms and conditions --------- */}
|
||||
<FastField name={'terms_conditions'}>
|
||||
{({ field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'terms_conditions'} />}
|
||||
className={'form-group--terms_conditions'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
>
|
||||
<TextArea growVertically={true} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
|
||||
<Col md={4}>
|
||||
<Dragzone
|
||||
initialFiles={[]}
|
||||
// onDrop={handleDropFiles}
|
||||
// onDeleteFile={handleDeleteFile}
|
||||
hint={'Attachments: Maxiumum size: 20MB'}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Col md={4}>
|
||||
<Dragzone
|
||||
initialFiles={[]}
|
||||
// onDrop={handleDropFiles}
|
||||
// onDeleteFile={handleDeleteFile}
|
||||
hint={'Attachments: Maxiumum size: 20MB'}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Postbox>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ export const defaultEstimateEntry = {
|
||||
index: 0,
|
||||
item_id: '',
|
||||
rate: '',
|
||||
discount: 0,
|
||||
quantity: 1,
|
||||
discount: '',
|
||||
quantity: '',
|
||||
description: '',
|
||||
};
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ export const statusAccessor = (row) => (
|
||||
*/
|
||||
export function ActionsMenu({
|
||||
row: { original },
|
||||
payload: { onEdit, onDeliver, onReject, onApprove, onDelete ,onDrawer },
|
||||
payload: { onEdit, onDeliver, onReject, onApprove, onDelete, onDrawer },
|
||||
}) {
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
@@ -101,9 +101,10 @@ export function ActionsMenu({
|
||||
</Choose.When>
|
||||
</Choose>
|
||||
<MenuItem
|
||||
text={formatMessage({ id: 'estimate_paper' })}
|
||||
onClick={() => onDrawer()}
|
||||
/>
|
||||
icon={<Icon icon={'receipt-24'} iconSize={16} />}
|
||||
text={formatMessage({ id: 'estimate_paper' })}
|
||||
onClick={() => onDrawer()}
|
||||
/>
|
||||
<MenuItem
|
||||
text={formatMessage({ id: 'delete_estimate' })}
|
||||
intent={Intent.DANGER}
|
||||
|
||||
@@ -123,7 +123,11 @@ function InvoiceForm({
|
||||
};
|
||||
|
||||
// Handle the request error.
|
||||
const onError = ({ response: { data: { errors } } }) => {
|
||||
const onError = ({
|
||||
response: {
|
||||
data: { errors },
|
||||
},
|
||||
}) => {
|
||||
if (errors) {
|
||||
handleErrors(errors, { setErrors });
|
||||
}
|
||||
@@ -146,6 +150,7 @@ function InvoiceForm({
|
||||
)}
|
||||
>
|
||||
<Formik
|
||||
enableReinitialize={true}
|
||||
validationSchema={
|
||||
isNewMode ? CreateInvoiceFormSchema : EditInvoiceFormSchema
|
||||
}
|
||||
@@ -154,10 +159,7 @@ function InvoiceForm({
|
||||
>
|
||||
<Form>
|
||||
<InvoiceFormHeader />
|
||||
|
||||
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
|
||||
<InvoiceItemsEntriesEditorField />
|
||||
</div>
|
||||
<InvoiceItemsEntriesEditorField />
|
||||
<InvoiceFormFooter />
|
||||
<InvoiceFloatingActions />
|
||||
<InvoiceFormDialogs />
|
||||
|
||||
@@ -11,6 +11,9 @@ export default function InvoiceFormDialogs() {
|
||||
|
||||
// Update the form once the invoice number form submit confirm.
|
||||
const handleInvoiceNumberFormConfirm = (values) => {
|
||||
debugger;
|
||||
console.log(values, 'XX');
|
||||
|
||||
setFieldValue(
|
||||
'invoice_no',
|
||||
transactionNumber(values.number_prefix, values.next_number),
|
||||
|
||||
@@ -4,7 +4,7 @@ import classNames from 'classnames';
|
||||
import { FormGroup, TextArea } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import { Row, Col } from 'components';
|
||||
import { Row, Col, Postbox } from 'components';
|
||||
import Dragzone from 'components/Dragzone';
|
||||
|
||||
import { inputIntent } from 'utils';
|
||||
@@ -12,44 +12,46 @@ import { inputIntent } from 'utils';
|
||||
export default function InvoiceFormFooter() {
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
||||
<Row>
|
||||
<Col md={8}>
|
||||
{/* --------- Invoice message --------- */}
|
||||
<FastField name={'invoice_message'}>
|
||||
{({ field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'invoice_message'} />}
|
||||
className={'form-group--invoice_message'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
>
|
||||
<TextArea growVertically={true} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
<Postbox title={'Invoice details'} defaultOpen={false}>
|
||||
<Row>
|
||||
<Col md={8}>
|
||||
{/* --------- Invoice message --------- */}
|
||||
<FastField name={'invoice_message'}>
|
||||
{({ field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'invoice_message'} />}
|
||||
className={'form-group--invoice_message'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
>
|
||||
<TextArea growVertically={true} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
{/* --------- Terms and conditions --------- */}
|
||||
<FastField name={'terms_conditions'}>
|
||||
{({ field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'terms_conditions'} />}
|
||||
className={'form-group--terms_conditions'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
>
|
||||
<TextArea growVertically={true} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
{/* --------- Terms and conditions --------- */}
|
||||
<FastField name={'terms_conditions'}>
|
||||
{({ field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'terms_conditions'} />}
|
||||
className={'form-group--terms_conditions'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
>
|
||||
<TextArea growVertically={true} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
|
||||
<Col md={4}>
|
||||
<Dragzone
|
||||
initialFiles={[]}
|
||||
// onDrop={handleDropFiles}
|
||||
// onDeleteFile={handleDeleteFile}
|
||||
hint={'Attachments: Maxiumum size: 20MB'}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Col md={4}>
|
||||
<Dragzone
|
||||
initialFiles={[]}
|
||||
// onDrop={handleDropFiles}
|
||||
// onDeleteFile={handleDeleteFile}
|
||||
hint={'Attachments: Maxiumum size: 20MB'}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Postbox>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ function InvoiceFormHeaderFields({
|
||||
<FormGroup
|
||||
label={<T id={'customer_name'} />}
|
||||
inline={true}
|
||||
className={classNames('form-group--customer-name', CLASSES.FILL)}
|
||||
className={classNames('form-group--customer-name', 'form-group--select-list', CLASSES.FILL)}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'customer_id'} />}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
import InvoiceForm from './InvoiceForm';
|
||||
|
||||
import 'style/pages/SaleInvoice/PageForm.scss';
|
||||
|
||||
import InvoiceForm from './InvoiceForm';
|
||||
import { InvoiceFormProvider } from './InvoiceFormProvider';
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import React from 'react';
|
||||
import React, { useCallback } from 'react';
|
||||
import { FastField } from 'formik';
|
||||
import classNames from 'classnames';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import ItemsEntriesTable from 'containers/Entries/ItemsEntriesTable';
|
||||
import { useInvoiceFormContext } from './InvoiceFormProvider';
|
||||
|
||||
@@ -8,20 +10,22 @@ import { useInvoiceFormContext } from './InvoiceFormProvider';
|
||||
*/
|
||||
export default function InvoiceItemsEntriesEditorField() {
|
||||
const { items } = useInvoiceFormContext();
|
||||
|
||||
|
||||
return (
|
||||
<FastField name={'entries'}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<ItemsEntriesTable
|
||||
entries={value}
|
||||
onUpdateData={(entries) => {
|
||||
form.setFieldValue('entries', entries);
|
||||
}}
|
||||
items={items}
|
||||
errors={error}
|
||||
linesNumber={4}
|
||||
/>
|
||||
)}
|
||||
</FastField>
|
||||
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
|
||||
<FastField name={'entries'}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<ItemsEntriesTable
|
||||
entries={value}
|
||||
onUpdateData={(entries) => {
|
||||
form.setFieldValue('entries', entries);
|
||||
}}
|
||||
items={items}
|
||||
errors={error}
|
||||
linesNumber={4}
|
||||
/>
|
||||
)}
|
||||
</FastField>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ export const defaultInvoiceEntry = {
|
||||
index: 0,
|
||||
item_id: '',
|
||||
rate: '',
|
||||
discount: 0,
|
||||
quantity: 1,
|
||||
discount: '',
|
||||
quantity: '',
|
||||
description: '',
|
||||
total: 0,
|
||||
};
|
||||
|
||||
@@ -118,6 +118,7 @@ export function ActionsMenu({
|
||||
/>
|
||||
</If>
|
||||
<MenuItem
|
||||
icon={<Icon icon={'receipt-24'} iconSize={16} />}
|
||||
text={formatMessage({ id: 'invoice_paper' })}
|
||||
onClick={() => onDrawer()}
|
||||
/>
|
||||
|
||||
@@ -165,7 +165,7 @@ function PaymentReceiveForm({
|
||||
<PaymentReceiveFormFooter />
|
||||
<PaymentReceiveFloatingActions />
|
||||
|
||||
{/* Alerts & Dialogs */}
|
||||
{/* ------- Alerts & Dialogs ------- */}
|
||||
<PaymentReceiveFormAlerts />
|
||||
<PaymentReceiveFormDialogs />
|
||||
</PaymentReceiveInnerProvider>
|
||||
|
||||
@@ -3,7 +3,7 @@ import classNames from 'classnames';
|
||||
import { FormGroup, TextArea } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import { FastField } from 'formik';
|
||||
import { Row, Col } from 'components';
|
||||
import { Row, Col, Postbox } from 'components';
|
||||
import { CLASSES } from 'common/classes';
|
||||
|
||||
/**
|
||||
@@ -12,21 +12,23 @@ import { CLASSES } from 'common/classes';
|
||||
export default function PaymentReceiveFormFooter({ getFieldProps }) {
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
||||
<Row>
|
||||
<Col md={8}>
|
||||
{/* --------- Statement --------- */}
|
||||
<FastField name={'statement'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'statement'} />}
|
||||
className={'form-group--statement'}
|
||||
>
|
||||
<TextArea growVertically={true} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
</Row>
|
||||
<Postbox title={'Payment receive details'} defaultOpen={false}>
|
||||
<Row>
|
||||
<Col md={8}>
|
||||
{/* --------- Statement --------- */}
|
||||
<FastField name={'statement'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'statement'} />}
|
||||
className={'form-group--statement'}
|
||||
>
|
||||
<TextArea growVertically={true} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
</Row>
|
||||
</Postbox>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -45,15 +45,6 @@ function PaymentReceiveItemsTable({
|
||||
onUpdateData(newRows);
|
||||
}, [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 (
|
||||
<CloudLoadingIndicator isLoading={isDueInvoicesFetching}>
|
||||
<DataTableEditable
|
||||
@@ -67,16 +58,7 @@ function PaymentReceiveItemsTable({
|
||||
updateData: handleUpdateData,
|
||||
}}
|
||||
noResults={noResultsMessage}
|
||||
actions={
|
||||
<Button
|
||||
small={true}
|
||||
className={'button--secondary button--clear-lines'}
|
||||
onClick={handleClickClearAllLines}
|
||||
>
|
||||
<T id={'clear_all_lines'} />
|
||||
</Button>
|
||||
}
|
||||
totalRow={true}
|
||||
footer={true}
|
||||
/>
|
||||
</CloudLoadingIndicator>
|
||||
);
|
||||
|
||||
@@ -88,12 +88,13 @@ export const usePaymentReceiveEntriesColumns = () => {
|
||||
disableSortBy: true,
|
||||
disableResizing: true,
|
||||
width: 250,
|
||||
className: 'date'
|
||||
},
|
||||
{
|
||||
Header: formatMessage({ id: 'invocie_number' }),
|
||||
accessor: InvNumberCellAccessor,
|
||||
disableSortBy: true,
|
||||
className: '',
|
||||
className: 'invoice_number',
|
||||
},
|
||||
{
|
||||
Header: formatMessage({ id: 'invoice_amount' }),
|
||||
@@ -102,7 +103,7 @@ export const usePaymentReceiveEntriesColumns = () => {
|
||||
Cell: MoneyTableCell,
|
||||
disableSortBy: true,
|
||||
width: 100,
|
||||
className: '',
|
||||
className: 'invoice_amount',
|
||||
},
|
||||
{
|
||||
Header: formatMessage({ id: 'amount_due' }),
|
||||
@@ -111,7 +112,7 @@ export const usePaymentReceiveEntriesColumns = () => {
|
||||
Cell: MoneyTableCell,
|
||||
disableSortBy: true,
|
||||
width: 150,
|
||||
className: '',
|
||||
className: 'amount_due',
|
||||
},
|
||||
{
|
||||
Header: formatMessage({ id: 'payment_amount' }),
|
||||
@@ -120,7 +121,7 @@ export const usePaymentReceiveEntriesColumns = () => {
|
||||
Footer: PaymentAmountFooterCell,
|
||||
disableSortBy: true,
|
||||
width: 150,
|
||||
className: '',
|
||||
className: 'payment_amount',
|
||||
},
|
||||
],
|
||||
[formatMessage],
|
||||
|
||||
@@ -35,6 +35,7 @@ export function ActionsMenu({
|
||||
onClick={safeCallback(onEdit, paymentReceive)}
|
||||
/>
|
||||
<MenuItem
|
||||
icon={<Icon icon={'receipt-24'} iconSize={16} />}
|
||||
text={formatMessage({ id: 'payment_receive_paper' })}
|
||||
onClick={() => onDrawer()}
|
||||
/>
|
||||
|
||||
@@ -10,11 +10,9 @@ import { CLASSES } from 'common/classes';
|
||||
import { ERROR } from 'common/errors';
|
||||
import {
|
||||
EditReceiptFormSchema,
|
||||
CreateReceiptFormSchema,
|
||||
CreateReceiptFormSchema,
|
||||
} from './ReceiptForm.schema';
|
||||
|
||||
import 'style/pages/SaleReceipt/PageForm.scss';
|
||||
|
||||
import { useReceiptFormContext } from './ReceiptFormProvider';
|
||||
|
||||
import ReceiptFromHeader from './ReceiptFormHeader';
|
||||
|
||||
@@ -3,51 +3,53 @@ import { FormGroup, TextArea } from '@blueprintjs/core';
|
||||
import { FastField } from 'formik';
|
||||
import classNames from 'classnames';
|
||||
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 { inputIntent } from 'utils';
|
||||
|
||||
export default function ReceiptFormFooter({}) {
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
||||
<Row>
|
||||
<Col md={8}>
|
||||
{/* --------- Receipt message --------- */}
|
||||
<FastField name={'receipt_message'}>
|
||||
{({ field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'receipt_message'} />}
|
||||
className={'form-group--receipt_message'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
>
|
||||
<TextArea growVertically={true} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
<Postbox title={'Invoice details'} defaultOpen={false}>
|
||||
<Row>
|
||||
<Col md={8}>
|
||||
{/* --------- Receipt message --------- */}
|
||||
<FastField name={'receipt_message'}>
|
||||
{({ field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'receipt_message'} />}
|
||||
className={'form-group--receipt_message'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
>
|
||||
<TextArea growVertically={true} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
{/* --------- Statement--------- */}
|
||||
<FastField name={'statement'}>
|
||||
{({ field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'statement'} />}
|
||||
className={'form-group--statement'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
>
|
||||
<TextArea growVertically={true} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
{/* --------- Statement--------- */}
|
||||
<FastField name={'statement'}>
|
||||
{({ field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'statement'} />}
|
||||
className={'form-group--statement'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
>
|
||||
<TextArea growVertically={true} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
|
||||
<Col md={4}>
|
||||
<Dragzone
|
||||
initialFiles={[]}
|
||||
// onDrop={handleDropFiles}
|
||||
// onDeleteFile={handleDeleteFile}
|
||||
hint={'Attachments: Maxiumum size: 20MB'}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Col md={4}>
|
||||
<Dragzone
|
||||
initialFiles={[]}
|
||||
// onDrop={handleDropFiles}
|
||||
// onDeleteFile={handleDeleteFile}
|
||||
hint={'Attachments: Maxiumum size: 20MB'}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Postbox>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import React from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
import 'style/pages/SaleReceipt/PageForm.scss';
|
||||
|
||||
import ReceiptFrom from './ReceiptForm';
|
||||
import { ReceiptFormProvider } from './ReceiptFormProvider';
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ export const defaultReceiptEntry = {
|
||||
index: 0,
|
||||
item_id: '',
|
||||
rate: '',
|
||||
discount: 0,
|
||||
discount: '',
|
||||
quantity: '',
|
||||
description: '',
|
||||
};
|
||||
|
||||
@@ -15,7 +15,7 @@ import { Choose, Money, Icon, If } from 'components';
|
||||
import moment from 'moment';
|
||||
|
||||
export function ActionsMenu({
|
||||
payload: { onEdit, onDelete, onClose ,onDrawer },
|
||||
payload: { onEdit, onDelete, onClose, onDrawer },
|
||||
row: { original: receipt },
|
||||
}) {
|
||||
const { formatMessage } = useIntl();
|
||||
@@ -39,9 +39,10 @@ export function ActionsMenu({
|
||||
/>
|
||||
</If>
|
||||
<MenuItem
|
||||
text={formatMessage({ id: 'receipt_paper' })}
|
||||
onClick={() => onDrawer()}
|
||||
/>
|
||||
icon={<Icon icon={'receipt-24'} iconSize={16} />}
|
||||
text={formatMessage({ id: 'receipt_paper' })}
|
||||
onClick={() => onDrawer()}
|
||||
/>
|
||||
<MenuItem
|
||||
text={formatMessage({ id: 'delete_receipt' })}
|
||||
intent={Intent.DANGER}
|
||||
|
||||
Reference in New Issue
Block a user