refactoring: account form.

refactoring: expense form.
refactoring: manual journal form.
refactoring: invoice form.
This commit is contained in:
a.bouhuolia
2021-02-15 12:03:47 +02:00
parent 692f3b333a
commit 760c38b54b
124 changed files with 2694 additions and 2967 deletions

View File

@@ -3,12 +3,12 @@ import { FastField } from 'formik';
import classNames from 'classnames';
import { CLASSES } from 'common/classes';
import MakeJournalEntriesTable from './MakeJournalEntriesTable';
import { orderingLinesIndexes, repeatValue } from 'utils';
import { defaultManualJournal, MIN_LINES_NUMBER } from './utils';
export default function MakeJournalEntriesField({
defaultRow,
linesNumber = 4,
}) {
/**
* Make journal entries field.
*/
export default function MakeJournalEntriesField() {
return (
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
<FastField name={'entries'}>
@@ -18,16 +18,9 @@ export default function MakeJournalEntriesField({
form.setFieldValue('entries', entries);
}}
entries={value}
defaultEntry={defaultManualJournal}
initialLinesNumber={MIN_LINES_NUMBER}
error={error}
onClickAddNewRow={() => {
form.setFieldValue('entries', [...value, defaultRow]);
}}
onClickClearAllLines={() => {
form.setFieldValue(
'entries',
orderingLinesIndexes([...repeatValue(defaultRow, linesNumber)])
);
}}
/>
)}
</FastField>

View File

@@ -10,79 +10,70 @@ import {
MenuItem,
} from '@blueprintjs/core';
import { FormattedMessage as T } from 'react-intl';
import { useFormikContext } from 'formik';
import { CLASSES } from 'common/classes';
import classNames from 'classnames';
import { saveInvoke } from 'utils';
import { If, Icon } from 'components';
import { useMakeJournalFormContext } from './MakeJournalProvider';
import { useHistory } from 'react-router-dom';
/**
* Make Journal floating actions bar.
*/
export default function MakeJournalEntriesFooter({
isSubmitting,
onSubmitClick,
onCancelClick,
manualJournalId,
onSubmitForm,
onResetForm,
manualJournalPublished,
}) {
export default function MakeJournalEntriesFooter() {
const history = useHistory();
// Formik context.
const { isSubmitting, submitForm } = useFormikContext();
// Make journal form context.
const {
manualJournalId,
setSubmitPayload,
manualJournalPublished = false,
} = useMakeJournalFormContext();
// Handle `submit & publish` button click.
const handleSubmitPublishBtnClick = (event) => {
saveInvoke(onSubmitClick, event, {
redirect: true,
publish: true,
});
setSubmitPayload({ redirect: true, publish: true });
submitForm();
};
// Handle `submit, publish & new` button click.
const handleSubmitPublishAndNewBtnClick = (event) => {
onSubmitForm();
saveInvoke(onSubmitClick, event, {
redirect: false,
publish: true,
resetForm: true,
});
setSubmitPayload({ redirect: false, publish: true, resetForm: true });
submitForm();
};
// Handle `submit, publish & continue editing` button click.
const handleSubmitPublishContinueEditingBtnClick = (event) => {
onSubmitForm();
saveInvoke(onSubmitClick, event, {
redirect: false,
publish: true,
});
setSubmitPayload({ redirect: false, publish: true });
submitForm();
};
// Handle `submit as draft` button click.
const handleSubmitDraftBtnClick = (event) => {
saveInvoke(onSubmitClick, event, {
redirect: true,
publish: false,
});
setSubmitPayload({ redirect: true, publish: false });
};
// Handle `submit as draft & new` button click.
const handleSubmitDraftAndNewBtnClick = (event) => {
onSubmitForm();
saveInvoke(onSubmitClick, event, {
redirect: false,
publish: false,
resetForm: true,
});
setSubmitPayload({ redirect: false, publish: false, resetForm: true });
submitForm();
};
// Handles submit as draft & continue editing button click.
const handleSubmitDraftContinueEditingBtnClick = (event) => {
onSubmitForm();
saveInvoke(onSubmitClick, event, {
redirect: false,
publish: false,
});
setSubmitPayload({ redirect: false, publish: false });
submitForm();
};
// Handle cancel button action click.
const handleCancelBtnClick = (event) => {
saveInvoke(onCancelClick, event);
history.goBack();
};
const handleClearBtnClick = (event) => {
// saveInvoke(onClearClick, event);
onResetForm();
};
const handleClearBtnClick = (event) => {};
return (
<div className={classNames(CLASSES.PAGE_FORM_FLOATING_ACTIONS)}>

View File

@@ -1,9 +1,8 @@
import React, { useMemo, useState, useEffect, useCallback } from 'react';
import React, { useMemo } from 'react';
import { Formik, Form } from 'formik';
import moment from 'moment';
import { Intent } from '@blueprintjs/core';
import { useIntl } from 'react-intl';
import { pick, defaultTo } from 'lodash';
import { defaultTo, isEmpty } from 'lodash';
import classNames from 'classnames';
import { useHistory } from 'react-router-dom';
@@ -15,56 +14,30 @@ import {
import MakeJournalEntriesHeader from './MakeJournalEntriesHeader';
import MakeJournalFormFloatingActions from './MakeJournalFormFloatingActions';
import MakeJournalEntriesField from './MakeJournalEntriesField';
import MakeJournalNumberWatcher from './MakeJournalNumberWatcher';
import MakeJournalFormFooter from './MakeJournalFormFooter';
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
import withSettings from 'containers/Settings/withSettings';
import AppToaster from 'components/AppToaster';
import withMediaActions from 'containers/Media/withMediaActions';
import { compose, orderingLinesIndexes, transactionNumber } from 'utils';
import {
compose,
repeatValue,
orderingLinesIndexes,
defaultToTransform,
transactionNumber,
} from 'utils';
import { transformErrors } from './utils';
transformErrors,
transformToEditForm,
defaultManualJournal,
} from './utils';
import { useMakeJournalFormContext } from './MakeJournalProvider';
const defaultEntry = {
index: 0,
account_id: '',
credit: '',
debit: '',
contact_id: '',
note: '',
};
const defaultInitialValues = {
journal_number: '',
journal_type: 'Journal',
date: moment(new Date()).format('YYYY-MM-DD'),
description: '',
reference: '',
currency_code: '',
publish: '',
entries: [...repeatValue(defaultEntry, 4)],
};
/**
* Journal entries form.
*/
function MakeJournalEntriesForm({
// #withDashboard
changePageTitle,
changePageSubtitle,
// #withSettings
journalNextNumber,
journalNumberPrefix,
baseCurrency,
}) {
// Journal form context.
const {
createJournalMutate,
editJournalMutate,
@@ -81,58 +54,23 @@ function MakeJournalEntriesForm({
journalNumberPrefix,
journalNextNumber,
);
// Changes the page title based on the form in new and edit mode.
useEffect(() => {
const transactionNumber = manualJournal
? manualJournal.journal_number
: journalNumber;
if (isNewMode) {
changePageTitle(formatMessage({ id: 'new_journal' }));
} else {
changePageTitle(formatMessage({ id: 'edit_journal' }));
}
changePageSubtitle(
defaultToTransform(transactionNumber, `No. ${transactionNumber}`, ''),
);
}, [
changePageTitle,
changePageSubtitle,
journalNumber,
manualJournal,
formatMessage,
isNewMode,
]);
// Form initial values.
const initialValues = useMemo(
() => ({
...(manualJournal
...(!isEmpty(manualJournal)
? {
...pick(manualJournal, Object.keys(defaultInitialValues)),
entries: manualJournal.entries.map((entry) => ({
...pick(entry, Object.keys(defaultEntry)),
})),
...transformToEditForm(manualJournal),
}
: {
...defaultInitialValues,
...defaultManualJournal,
journal_number: defaultTo(journalNumber, ''),
currency_code: defaultTo(baseCurrency, ''),
entries: orderingLinesIndexes(defaultInitialValues.entries),
entries: orderingLinesIndexes(defaultManualJournal.entries),
}),
}),
[manualJournal, baseCurrency, journalNumber],
);
// Handle journal number field change.
const handleJournalNumberChanged = useCallback(
(journalNumber) => {
changePageSubtitle(
defaultToTransform(journalNumber, `No. ${journalNumber}`, ''),
);
},
[changePageSubtitle],
);
// Handle the form submiting.
const handleSubmit = (values, { setErrors, setSubmitting, resetForm }) => {
setSubmitting(true);
@@ -170,8 +108,12 @@ function MakeJournalEntriesForm({
const form = { ...values, publish: submitPayload.publish, entries };
// Handle the request error.
const handleError = (error) => {
transformErrors(error, { setErrors });
const handleError = ({
response: {
data: { errors },
},
}) => {
transformErrors(errors, { setErrors });
setSubmitting(false);
};
@@ -201,7 +143,7 @@ function MakeJournalEntriesForm({
if (isNewMode) {
createJournalMutate(form).then(handleSuccess).catch(handleError);
} else {
editJournalMutate(manualJournal.id, form)
editJournalMutate([manualJournal.id, form])
.then(handleSuccess)
.catch(handleError);
}
@@ -221,11 +163,8 @@ function MakeJournalEntriesForm({
onSubmit={handleSubmit}
>
<Form>
<MakeJournalEntriesHeader
onJournalNumberChanged={handleJournalNumberChanged}
/>
<MakeJournalNumberWatcher journalNumber={journalNumber} />
<MakeJournalEntriesField defaultRow={defaultEntry} />
<MakeJournalEntriesHeader />
<MakeJournalEntriesField />
<MakeJournalFormFooter />
<MakeJournalFormFloatingActions />
</Form>
@@ -235,7 +174,6 @@ function MakeJournalEntriesForm({
}
export default compose(
withDashboardActions,
withMediaActions,
withSettings(({ manualJournalsSettings, organizationSettings }) => ({
journalNextNumber: parseInt(manualJournalsSettings?.nextNumber, 10),

View File

@@ -1,62 +1,20 @@
import React, { useCallback, useEffect } from 'react';
import { useParams, useHistory } from 'react-router-dom';
import React from 'react';
import { useParams } from 'react-router-dom';
import MakeJournalEntriesForm from './MakeJournalEntriesForm';
import { MakeJournalProvider } from './MakeJournalProvider';
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
import { compose } from 'utils';
import 'style/pages/ManualJournal/MakeJournal.scss';
/**
* Make journal entries page.
*/
function MakeJournalEntriesPage({
// #withDashboardActions
setSidebarShrink,
resetSidebarPreviousExpand,
setDashboardBackLink,
}) {
const history = useHistory();
export default function MakeJournalEntriesPage() {
const { id: journalId } = useParams();
useEffect(() => {
// Shrink the sidebar by foce.
setSidebarShrink();
// Show the back link on dashboard topbar.
setDashboardBackLink('/manual-journals');
return () => {
// Reset the sidebar to the previous status.
resetSidebarPreviousExpand();
// Hide the back link on dashboard topbar.
setDashboardBackLink(false);
};
}, [resetSidebarPreviousExpand, setDashboardBackLink, setSidebarShrink]);
const handleFormSubmit = useCallback(
(payload) => {
payload.redirect && history.push('/manual-journals');
},
[history],
);
const handleCancel = useCallback(() => {
history.goBack();
}, [history]);
return (
<MakeJournalProvider journalId={journalId}>
<MakeJournalEntriesForm
onFormSubmit={handleFormSubmit}
onCancelForm={handleCancel}
/>
<MakeJournalEntriesForm />
</MakeJournalProvider>
);
}
export default compose(
withDashboardActions,
)(MakeJournalEntriesPage);

View File

@@ -1,205 +1,111 @@
import React, { useState, useMemo, useEffect, useCallback } from 'react';
import React from 'react';
import { Button } from '@blueprintjs/core';
import { FormattedMessage as T, useIntl } from 'react-intl';
import { omit } from 'lodash';
import { saveInvoke } from 'utils';
import {
AccountsListFieldCell,
MoneyFieldCell,
InputGroupCell,
ContactsListFieldCell,
} from 'components/DataTableCells';
import {
ContactHeaderCell,
ActionsCellRenderer,
TotalAccountCellRenderer,
TotalCreditDebitCellRenderer,
NoteCellRenderer,
} from './components';
import { FormattedMessage as T } from 'react-intl';
import { saveInvoke, removeRowsByIndex } from 'utils';
import { DataTableEditable } from 'components';
import withAlertActions from 'containers/Alert/withAlertActions';
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.
*/
export default function MakeJournalEntriesTable({
function MakeJournalEntriesTable({
// #withAlertsActions
openAlert,
// #ownPorps
onClickRemoveRow,
onClickAddNewRow,
onClickClearAllLines,
onChange,
entries,
defaultEntry,
error,
initialLinesNumber = 4,
minLinesNumber = 4,
}) {
const [rows, setRows] = useState([]);
const { formatMessage } = useIntl();
const { accounts, customers } = useMakeJournalFormContext();
useEffect(() => {
setRows([...entries.map((e) => ({ ...e, rowType: 'editor' }))]);
}, [entries, setRows]);
// Final table rows editor rows and total and final blank row.
const tableRows = useMemo(() => [...rows, { rowType: 'total' }], [rows]);
// Memorized data table columns.
const columns = useMemo(
() => [
{
Header: '#',
accessor: 'index',
Cell: ({ row: { index } }) => <span>{index + 1}</span>,
className: 'index',
width: 40,
disableResizing: true,
disableSortBy: true,
sticky: 'left',
},
{
Header: formatMessage({ id: 'account' }),
id: 'account_id',
accessor: 'account_id',
Cell: TotalAccountCellRenderer(AccountsListFieldCell),
className: 'account',
disableSortBy: true,
width: 140,
},
{
Header: formatMessage({ id: 'credit_currency' }, { currency: 'USD' }),
accessor: 'credit',
Cell: TotalCreditDebitCellRenderer(MoneyFieldCell, 'credit'),
className: 'credit',
disableSortBy: true,
width: 100,
},
{
Header: formatMessage({ id: 'debit_currency' }, { currency: 'USD' }),
accessor: 'debit',
Cell: TotalCreditDebitCellRenderer(MoneyFieldCell, 'debit'),
className: 'debit',
disableSortBy: true,
width: 100,
},
{
Header: ContactHeaderCell,
id: 'contact_id',
accessor: 'contact_id',
Cell: NoteCellRenderer(ContactsListFieldCell),
className: 'contact',
disableSortBy: true,
width: 120,
},
{
Header: formatMessage({ id: 'note' }),
accessor: 'note',
Cell: NoteCellRenderer(InputGroupCell),
disableSortBy: true,
className: 'note',
width: 200,
},
{
Header: '',
accessor: 'action',
Cell: ActionsCellRenderer,
className: 'actions',
disableSortBy: true,
disableResizing: true,
width: 45,
},
],
[formatMessage],
);
const columns = useJournalTableEntriesColumns();
// Handles click new line.
const onClickNewRow = () => {
saveInvoke(onClickAddNewRow);
const newRows = [...entries, defaultEntry];
saveInvoke(onChange, newRows);
};
// Handles update datatable data.
const handleUpdateData = (rowIndex, columnId, value) => {
const newRows = updateDataReducer(rows, rowIndex, columnId, value);
saveInvoke(
onChange,
newRows
.filter((row) => row.rowType === 'editor')
.map((row) => ({
...omit(row, ['rowType']),
})),
);
const newRows = updateDataReducer(entries, rowIndex, columnId, value);
saveInvoke(onChange, newRows);
};
// Handle remove datatable row.
const handleRemoveRow = (rowIndex) => {
// Can't continue if there is just one row line or less.
if (rows.length <= 2) {
return;
}
const removeIndex = parseInt(rowIndex, 10);
const newRows = rows.filter((row, index) => index !== removeIndex);
saveInvoke(
onChange,
newRows
.filter((row) => row.rowType === 'editor')
.map((row) => ({ ...omit(row, ['rowType']) })),
);
saveInvoke(onClickRemoveRow, removeIndex);
const newRows = removeRowsByIndex(entries, rowIndex);
saveInvoke(onChange, newRows);
};
// Rows class names callback.
const rowClassNames = useCallback(
(row) => ({
'row--total': rows.length === row.index + 2,
}),
[rows],
);
// Handle clear all lines action.
const handleClickClearAllLines = () => {
saveInvoke(onClickClearAllLines);
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={tableRows}
rowClassNames={rowClassNames}
sticky={true}
totalRow={true}
payload={{
accounts,
errors: error,
updateData: handleUpdateData,
removeRow: handleRemoveRow,
contacts: [
...customers.map((customer) => ({
<>
<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>
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>
</>
}
/>
<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}
/>
</>
);
}
export default compose(withAlertActions)(MakeJournalEntriesTable);

View File

@@ -80,8 +80,8 @@ export default function MakeJournalFloatingAction() {
<If condition={!manualJournal || !manualJournal?.is_published}>
<ButtonGroup>
<Button
disabled={isSubmitting}
loading={isSubmitting}
disabled={isSubmitting}
intent={Intent.PRIMARY}
onClick={handleSubmitPublishBtnClick}
text={<T id={'save_publish'} />}
@@ -146,6 +146,7 @@ export default function MakeJournalFloatingAction() {
<If condition={manualJournal && manualJournal?.is_published}>
<ButtonGroup>
<Button
loading={isSubmitting}
disabled={isSubmitting}
intent={Intent.PRIMARY}
onClick={handleSubmitPublishBtnClick}

View File

@@ -1,54 +0,0 @@
import { useEffect } from 'react';
import { compose } from 'redux';
import { useFormikContext } from 'formik';
import withManualJournalsActions from './withManualJournalsActions';
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
import withManualJournals from './withManualJournals';
import { defaultToTransform } from 'utils';
/**
* Journal number chaلing watcher.
*/
function MakeJournalNumberChangingWatcher({
// #withDashboardActions
changePageSubtitle,
// #withManualJournals
journalNumberChanged,
// #withManualJournalsActions
setJournalNumberChanged,
// #ownProps
journalNumber,
}) {
const { setFieldValue } = useFormikContext();
// Observes journal number settings changes.
useEffect(() => {
if (journalNumberChanged) {
setFieldValue('journal_number', journalNumber);
changePageSubtitle(
defaultToTransform(journalNumber, `No. ${journalNumber}`, ''),
);
setJournalNumberChanged(false);
}
}, [
journalNumber,
journalNumberChanged,
setJournalNumberChanged,
setFieldValue,
changePageSubtitle,
]);
return null;
}
export default compose(
withManualJournals(({ journalNumberChanged }) => ({
journalNumberChanged,
})),
withManualJournalsActions,
withDashboardActions,
)(MakeJournalNumberChangingWatcher);

View File

@@ -42,6 +42,7 @@ function MakeJournalProvider({ journalId, ...props }) {
// Loading the journal settings.
const { isFetching: isSettingsLoading } = useSettings();
// Submit form payload.
const [submitPayload, setSubmitPayload] = useState({});
const provider = {

View File

@@ -1,7 +1,14 @@
import React from 'react';
import { Position } from '@blueprintjs/core';
import { FormattedMessage as T } from 'react-intl';
import { Money, Hint } from 'components';
import { Intent, Position, Button, Tooltip } from '@blueprintjs/core';
import { FormattedMessage as T, useIntl } from 'react-intl';
import { Icon, Money, Hint } from 'components';
import {
AccountsListFieldCell,
MoneyFieldCell,
InputGroupCell,
ContactsListFieldCell,
} from 'components/DataTableCells';
import { safeSumBy } from 'utils';
/**
* Contact header cell.
@@ -18,74 +25,138 @@ export function ContactHeaderCell() {
);
}
/**
* Total text cell renderer.
/**
* Account footer cell.
*/
export const TotalAccountCellRenderer = (chainedComponent) => (props) => {
if (props.data.length === props.row.index + 1) {
return <span>{'Total USD'}</span>;
}
return chainedComponent(props);
};
function AccountFooterCell() {
return <span>{'Total USD'}</span>;
}
/**
* Total credit/debit cell renderer.
* Total credit table footer cell.
*/
export const TotalCreditDebitCellRenderer = (chainedComponent, type) => (
props,
) => {
if (props.data.length === props.row.index + 1) {
const total = props.data.reduce((total, entry) => {
const amount = parseInt(entry[type], 10);
const computed = amount ? total + amount : total;
function TotalCreditFooterCell({ rows }) {
const credit = safeSumBy(rows, 'original.credit');
return computed;
}, 0);
return (
<span>
<Money amount={total} currency={'USD'} />
</span>
);
}
return chainedComponent(props);
};
export const NoteCellRenderer = (chainedComponent) => (props) => {
if (props.data.length === props.row.index + 1) {
return '';
}
return chainedComponent(props);
};
return (
<span>
<Money amount={credit} currency={'USD'} />
</span>
);
}
/**
* Total debit table footer cell.
*/
function TotalDebitFooterCell({ rows }) {
const debit = safeSumBy(rows, 'original.debit');
return (
<span>
<Money amount={debit} currency={'USD'} />
</span>
);
}
/**
* Actions cell renderer.
*/
export const ActionsCellRenderer = ({
row: { index },
column: { id },
cell: { value: initialValue },
data,
payload,
}) => {
if (data.length <= index + 1) {
return '';
}
const onClickRemoveRole = () => {
payload.removeRow(index);
};
return (
<Tooltip content={<T id={'remove_the_line'} />} position={Position.LEFT}>
<Button
icon={<Icon icon="times-circle" iconSize={14} />}
iconSize={14}
className="ml2"
minimal={true}
intent={Intent.DANGER}
onClick={onClickRemoveRole}
/>
</Tooltip>
);
row: { index },
column: { id },
cell: { value: initialValue },
data,
payload,
}) => {
const onClickRemoveRole = () => {
payload.removeRow(index);
};
return (
<Tooltip content={<T id={'remove_the_line'} />} position={Position.LEFT}>
<Button
icon={<Icon icon="times-circle" iconSize={14} />}
iconSize={14}
className="ml2"
minimal={true}
intent={Intent.DANGER}
onClick={onClickRemoveRole}
/>
</Tooltip>
);
};
/**
* Retrieve columns of make journal entries table.
*/
export const useJournalTableEntriesColumns = () => {
const { formatMessage } = useIntl();
return React.useMemo(
() => [
{
Header: '#',
accessor: 'index',
Cell: ({ row: { index } }) => <span>{index + 1}</span>,
className: 'index',
width: 40,
disableResizing: true,
disableSortBy: true,
sticky: 'left',
},
{
Header: formatMessage({ id: 'account' }),
id: 'account_id',
accessor: 'account_id',
Cell: AccountsListFieldCell,
Footer: AccountFooterCell,
className: 'account',
disableSortBy: true,
width: 160,
},
{
Header: formatMessage({ id: 'credit_currency' }, { currency: 'USD' }),
accessor: 'credit',
Cell: MoneyFieldCell,
Footer: TotalCreditFooterCell,
className: 'credit',
disableSortBy: true,
width: 100,
},
{
Header: formatMessage({ id: 'debit_currency' }, { currency: 'USD' }),
accessor: 'debit',
Cell: MoneyFieldCell,
Footer: TotalDebitFooterCell,
className: 'debit',
disableSortBy: true,
width: 100,
},
{
Header: ContactHeaderCell,
id: 'contact_id',
accessor: 'contact_id',
Cell: ContactsListFieldCell,
className: 'contact',
disableSortBy: true,
width: 120,
},
{
Header: formatMessage({ id: 'note' }),
accessor: 'note',
Cell: InputGroupCell,
disableSortBy: true,
className: 'note',
width: 200,
},
{
Header: '',
accessor: 'action',
Cell: ActionsCellRenderer,
className: 'actions',
disableSortBy: true,
disableResizing: true,
width: 45,
},
],
[formatMessage],
);
};

View File

@@ -1,8 +1,9 @@
import React from 'react';
import { Intent } from '@blueprintjs/core';
import { sumBy, setWith, toSafeInteger, get } from 'lodash';
import moment from 'moment';
import { transformUpdatedRows } from 'utils';
import { transformUpdatedRows, repeatValue, transformToForm } from 'utils';
import { AppToaster } from 'components';
import { formatMessage } from 'services/intl';
@@ -17,6 +18,44 @@ const ERROR = {
ENTRIES_SHOULD_ASSIGN_WITH_CONTACT: 'ENTRIES_SHOULD_ASSIGN_WITH_CONTACT',
};
export const MIN_LINES_NUMBER = 4;
export const defaultEntry = {
index: 0,
account_id: '',
credit: '',
debit: '',
contact_id: '',
note: '',
};
export const defaultManualJournal = {
journal_number: '',
journal_type: 'Journal',
date: moment(new Date()).format('YYYY-MM-DD'),
description: '',
reference: '',
currency_code: '',
publish: '',
entries: [...repeatValue(defaultEntry, 4)],
};
// Transform to edit form.
export function transformToEditForm(manualJournal) {
return {
...transformToForm(manualJournal, defaultManualJournal),
entries: [
...manualJournal.entries.map((entry) => ({
...transformToForm(entry, defaultManualJournal.entries[0]),
})),
...repeatValue(
defaultEntry,
Math.max(MIN_LINES_NUMBER - manualJournal.entries.length, 0),
),
],
};
}
/**
* Entries adjustment.
*/
@@ -30,6 +69,9 @@ function adjustmentEntries(entries) {
};
}
/**
*
*/
export const updateDataReducer = (rows, rowIndex, columnId, value) => {
let newRows = transformUpdatedRows(rows, rowIndex, columnId, value);
@@ -59,7 +101,9 @@ export const updateDataReducer = (rows, rowIndex, columnId, value) => {
return newRows;
};
// Transform API errors in toasts messages.
/**
* Transform API errors in toasts messages.
*/
export const transformErrors = (resErrors, { setErrors, errors }) => {
const getError = (errorType) => resErrors.find((e) => e.type === errorType);
const toastMessages = [];