mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-15 20:30:33 +00:00
feat: journal number setting dialog.
This commit is contained in:
@@ -20,6 +20,8 @@ import withJournalsActions from 'containers/Accounting/withJournalsActions';
|
||||
import withManualJournalDetail from 'containers/Accounting/withManualJournalDetail';
|
||||
import withAccountsActions from 'containers/Accounts/withAccountsActions';
|
||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
import withSettings from 'containers/Settings/withSettings';
|
||||
import withManualJournals from './withManualJournals';
|
||||
|
||||
import AppToaster from 'components/AppToaster';
|
||||
import Dragzone from 'components/Dragzone';
|
||||
@@ -27,6 +29,7 @@ import withMediaActions from 'containers/Media/withMediaActions';
|
||||
|
||||
import useMedia from 'hooks/useMedia';
|
||||
import { compose, repeatValue } from 'utils';
|
||||
import withManualJournalsActions from './withManualJournalsActions';
|
||||
|
||||
const ERROR = {
|
||||
JOURNAL_NUMBER_ALREADY_EXISTS: 'JOURNAL.NUMBER.ALREADY.EXISTS',
|
||||
@@ -54,6 +57,15 @@ function MakeJournalEntriesForm({
|
||||
changePageTitle,
|
||||
changePageSubtitle,
|
||||
|
||||
// #withSettings
|
||||
journalNextNumber,
|
||||
journalNumberPrefix,
|
||||
|
||||
// #withManualJournals
|
||||
nextJournalNumberChanged,
|
||||
|
||||
setJournalNumberChanged,
|
||||
|
||||
manualJournalId,
|
||||
manualJournal,
|
||||
onFormSubmit,
|
||||
@@ -149,16 +161,19 @@ function MakeJournalEntriesForm({
|
||||
[],
|
||||
);
|
||||
|
||||
const journalNumber = (journalNumberPrefix) ?
|
||||
`${journalNumberPrefix}-${journalNextNumber}` : journalNextNumber;
|
||||
|
||||
const defaultInitialValues = useMemo(
|
||||
() => ({
|
||||
journal_number: '',
|
||||
journal_number: journalNumber,
|
||||
journal_type: 'Journal',
|
||||
date: moment(new Date()).format('YYYY-MM-DD'),
|
||||
description: '',
|
||||
reference: '',
|
||||
entries: [...repeatValue(defaultEntry, 4)],
|
||||
}),
|
||||
[defaultEntry],
|
||||
[defaultEntry, journalNumber],
|
||||
);
|
||||
|
||||
const initialValues = useMemo(
|
||||
@@ -255,7 +270,6 @@ function MakeJournalEntriesForm({
|
||||
};
|
||||
|
||||
const formik = useFormik({
|
||||
enableReinitialize: true,
|
||||
validationSchema,
|
||||
initialValues: {
|
||||
...initialValues,
|
||||
@@ -354,6 +368,11 @@ function MakeJournalEntriesForm({
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
formik.setFieldValue('journal_number', journalNumber);
|
||||
setJournalNumberChanged(false);
|
||||
}, [nextJournalNumberChanged, journalNumber]);
|
||||
|
||||
const handleSubmitClick = useCallback(
|
||||
(payload) => {
|
||||
setPayload(payload);
|
||||
@@ -430,7 +449,15 @@ function MakeJournalEntriesForm({
|
||||
export default compose(
|
||||
withJournalsActions,
|
||||
withManualJournalDetail,
|
||||
withManualJournals(({ nextJournalNumberChanged }) => ({
|
||||
nextJournalNumberChanged,
|
||||
})),
|
||||
withAccountsActions,
|
||||
withDashboardActions,
|
||||
withMediaActions,
|
||||
withSettings(({ manualJournalsSettings }) => ({
|
||||
journalNextNumber: manualJournalsSettings.next_number,
|
||||
journalNumberPrefix: manualJournalsSettings.number_prefix
|
||||
})),
|
||||
withManualJournalsActions,
|
||||
)(MakeJournalEntriesForm);
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
Intent,
|
||||
Position,
|
||||
Classes,
|
||||
Button,
|
||||
} from '@blueprintjs/core';
|
||||
import { DateInput } from '@blueprintjs/datetime';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
@@ -20,6 +19,7 @@ import {
|
||||
FieldHint,
|
||||
FieldRequiredHint,
|
||||
Icon,
|
||||
InputPrependButton
|
||||
} from 'components';
|
||||
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
@@ -40,7 +40,7 @@ function MakeJournalEntriesHeader({
|
||||
[setFieldValue],
|
||||
);
|
||||
const handleJournalNumberChange = useCallback(() => {
|
||||
openDialog('journalNumber-form', {});
|
||||
openDialog('journal-number-form', {});
|
||||
}, [openDialog]);
|
||||
|
||||
return (
|
||||
@@ -63,25 +63,29 @@ function MakeJournalEntriesHeader({
|
||||
<ErrorMessage name="journal_number" {...{ errors, touched }} />
|
||||
}
|
||||
fill={true}
|
||||
|
||||
>
|
||||
<InputGroup
|
||||
intent={
|
||||
errors.journal_number && touched.journal_number && Intent.DANGER
|
||||
}
|
||||
fill={true}
|
||||
rightElement={<InputPrependButton
|
||||
buttonProps={{
|
||||
onClick: handleJournalNumberChange,
|
||||
icon: (<Icon icon={'settings-18'} />)
|
||||
}}
|
||||
tooltip={true}
|
||||
tooltipProps={{
|
||||
content: 'Setting your auto-generated journal number',
|
||||
position: Position.BOTTOM_LEFT,
|
||||
}}
|
||||
/>}
|
||||
{...getFieldProps('journal_number')}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
<Col width={50}>
|
||||
{/* <FormGroup> */}
|
||||
<Button
|
||||
className={classNames('btn', Classes.SMALL)}
|
||||
icon={<Icon icon="setting" />}
|
||||
onClick={handleJournalNumberChange}
|
||||
/>
|
||||
{/* </FormGroup> */}
|
||||
</Col>
|
||||
|
||||
<Col width={220}>
|
||||
<FormGroup
|
||||
label={<T id={'date'} />}
|
||||
@@ -173,4 +177,6 @@ function MakeJournalEntriesHeader({
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withDialogActions)(MakeJournalEntriesHeader);
|
||||
export default compose(
|
||||
withDialogActions,
|
||||
)(MakeJournalEntriesHeader);
|
||||
|
||||
@@ -8,6 +8,7 @@ import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||
import withCustomersActions from 'containers/Customers/withCustomersActions';
|
||||
import withAccountsActions from 'containers/Accounts/withAccountsActions';
|
||||
import withManualJournalsActions from 'containers/Accounting/withManualJournalsActions';
|
||||
import withSettingsActions from 'containers/Settings/withSettingsActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
@@ -20,6 +21,9 @@ function MakeJournalEntriesPage({
|
||||
|
||||
// #withManualJournalActions
|
||||
requestFetchManualJournal,
|
||||
|
||||
// #withSettingsActions
|
||||
requestFetchOptions,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
const { id } = useParams();
|
||||
@@ -32,6 +36,8 @@ function MakeJournalEntriesPage({
|
||||
requestFetchCustomers(),
|
||||
);
|
||||
|
||||
const fetchSettings = useQuery(['settings'], () => requestFetchOptions({}));
|
||||
|
||||
const fetchJournal = useQuery(
|
||||
['manual-journal', id],
|
||||
(key, journalId) => requestFetchManualJournal(journalId),
|
||||
@@ -71,4 +77,5 @@ export default compose(
|
||||
withAccountsActions,
|
||||
withCustomersActions,
|
||||
withManualJournalsActions,
|
||||
withSettingsActions
|
||||
)(MakeJournalEntriesPage);
|
||||
|
||||
@@ -19,6 +19,8 @@ export default (mapState) => {
|
||||
|
||||
manualJournalsPagination: getManualJournalsPagination(state, props, query),
|
||||
manualJournalsLoading: state.manualJournals.loading,
|
||||
|
||||
nextJournalNumberChanged: state.manualJournals.nextJournalNumberChanged,
|
||||
};
|
||||
return mapState ? mapState(mapped, state, props) : mapped;
|
||||
};
|
||||
|
||||
@@ -24,6 +24,10 @@ const mapActionsToProps = (dispatch) => ({
|
||||
type: t.MANUAL_JOURNALS_TABLE_QUERIES_ADD,
|
||||
queries,
|
||||
}),
|
||||
setJournalNumberChanged: (isChanged) => dispatch({
|
||||
type: t.MANUAL_JOURNAL_NUMBER_CHANGED,
|
||||
payload: { isChanged },
|
||||
}),
|
||||
});
|
||||
|
||||
export default connect(null, mapActionsToProps);
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
import React, { useEffect, useCallback, useMemo } from 'react';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
import { useQuery, queryCache } from 'react-query';
|
||||
import { Dialog } from 'components';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
import withDialogRedux from 'components/DialogReduxConnect';
|
||||
import withSettingsActions from 'containers/Settings/withSettingsActions';
|
||||
import withSettings from 'containers/Settings/withSettings';
|
||||
import ReferenceNumberForm from 'containers/JournalNumber/ReferenceNumberForm';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
import { compose, optionsMapToArray } from 'utils';
|
||||
|
||||
function JournalNumberDailog({
|
||||
dialogName,
|
||||
payload,
|
||||
isOpen,
|
||||
|
||||
// #withSettingsActions
|
||||
requestSubmitOptions,
|
||||
|
||||
// #withDialogActions
|
||||
closeDialog,
|
||||
}) {
|
||||
// Handles dialog close.
|
||||
const handleClose = useCallback(() => {
|
||||
closeDialog(dialogName);
|
||||
}, [closeDialog, dialogName]);
|
||||
|
||||
const handleSubmitForm = useCallback(() => {});
|
||||
|
||||
// Handle dialog on closed.
|
||||
// const onDialogClosed = useCallback(() => {
|
||||
// resetForm();
|
||||
// }, [resetForm]);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
name={dialogName}
|
||||
// isLoading={}
|
||||
title={<T id={'journal_number_settings'} />}
|
||||
autoFocus={true}
|
||||
canEscapeKeyClose={true}
|
||||
isOpen={isOpen}
|
||||
onClose={handleClose}
|
||||
>
|
||||
<ReferenceNumberForm
|
||||
onSubmit={handleSubmitForm}
|
||||
initialNumber={'1000'}
|
||||
initialPrefix={'A'}
|
||||
onClose={handleClose}
|
||||
groupName={'manual_journals'}
|
||||
/>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, props) => ({
|
||||
dialogName: 'journalNumber-form',
|
||||
journalNumberId: props?.payload?.id || null,
|
||||
});
|
||||
|
||||
const withJournalNumberDailog = connect(mapStateToProps);
|
||||
|
||||
export default compose(
|
||||
withDialogRedux(null, 'journalNumber-form'),
|
||||
withJournalNumberDailog,
|
||||
withDialogActions,
|
||||
withSettingsActions,
|
||||
)(JournalNumberDailog);
|
||||
@@ -0,0 +1,76 @@
|
||||
import React from 'react';
|
||||
import { DialogContent } from 'components';
|
||||
import { useQuery, queryCache } from 'react-query';
|
||||
|
||||
import ReferenceNumberForm from 'containers/JournalNumber/ReferenceNumberForm';
|
||||
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
import withSettingsActions from 'containers/Settings/withSettingsActions';
|
||||
import withSettings from 'containers/Settings/withSettings';
|
||||
import withManualJournalsActions from 'containers/Accounting/withManualJournalsActions';
|
||||
|
||||
import { compose, optionsMapToArray } from 'utils';
|
||||
|
||||
/**
|
||||
* Journal number dialog's content.
|
||||
*/
|
||||
function JournalNumberDialogContent({
|
||||
// #withSettings
|
||||
nextNumber,
|
||||
numberPrefix,
|
||||
|
||||
// #withSettingsActions
|
||||
requestFetchOptions,
|
||||
requestSubmitOptions,
|
||||
|
||||
// #withDialogActions
|
||||
closeDialog,
|
||||
|
||||
// #withManualJournalsActions
|
||||
setJournalNumberChanged,
|
||||
}) {
|
||||
const fetchSettings = useQuery(['settings'], () => requestFetchOptions({}));
|
||||
|
||||
const handleSubmitForm = (values, { setSubmitting }) => {
|
||||
const options = optionsMapToArray(values).map((option) => {
|
||||
return { key: option.key, ...option, group: 'manual_journals' };
|
||||
});
|
||||
|
||||
requestSubmitOptions({ options }).then(() => {
|
||||
setSubmitting(false);
|
||||
closeDialog('journal-number-form');
|
||||
setJournalNumberChanged(true);
|
||||
|
||||
setTimeout(() => {
|
||||
queryCache.invalidateQueries('settings');
|
||||
}, 250);
|
||||
}).catch(() => {
|
||||
setSubmitting(false);
|
||||
});
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
closeDialog('journal-number-form');
|
||||
};
|
||||
|
||||
return (
|
||||
<DialogContent isLoading={fetchSettings.isFetching}>
|
||||
<ReferenceNumberForm
|
||||
initialNumber={nextNumber}
|
||||
initialPrefix={numberPrefix}
|
||||
onSubmit={handleSubmitForm}
|
||||
onClose={handleClose}
|
||||
/>
|
||||
</DialogContent>
|
||||
)
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withDialogActions,
|
||||
withSettingsActions,
|
||||
withSettings(({ manualJournalsSettings }) => ({
|
||||
nextNumber: manualJournalsSettings?.next_number,
|
||||
numberPrefix: manualJournalsSettings?.number_prefix,
|
||||
})),
|
||||
withManualJournalsActions,
|
||||
)(JournalNumberDialogContent);
|
||||
35
client/src/containers/Dialogs/JournalNumberDialog/index.js
Normal file
35
client/src/containers/Dialogs/JournalNumberDialog/index.js
Normal file
@@ -0,0 +1,35 @@
|
||||
import React, { lazy } from 'react';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import { Dialog, DialogSuspense } from 'components';
|
||||
import withDialogRedux from 'components/DialogReduxConnect';
|
||||
import { compose } from 'utils';
|
||||
|
||||
const JournalNumberDialogContent = lazy(() => import('./JournalNumberDialogContent'));
|
||||
|
||||
function JournalNumberDialog({
|
||||
dialogName,
|
||||
payload = { id: null },
|
||||
isOpen,
|
||||
}) {
|
||||
return (
|
||||
<Dialog
|
||||
name={dialogName}
|
||||
title={<T id={'journal_number_settings'} />}
|
||||
autoFocus={true}
|
||||
canEscapeKeyClose={true}
|
||||
isOpen={isOpen}
|
||||
className={'dialog--journal-number-settings'}
|
||||
>
|
||||
<DialogSuspense>
|
||||
<JournalNumberDialogContent
|
||||
journalNumberId={payload.id}
|
||||
/>
|
||||
</DialogSuspense>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
export default compose(
|
||||
withDialogRedux(),
|
||||
)(JournalNumberDialog);
|
||||
@@ -1,51 +1,43 @@
|
||||
import React, { useMemo, useCallback } from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import * as Yup from 'yup';
|
||||
import { useFormik } from 'formik';
|
||||
import { Row, Col } from 'react-grid-system';
|
||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||
import { ErrorMessage, AppToaster } from 'components';
|
||||
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import { ErrorMessage } from 'components';
|
||||
import {
|
||||
Button,
|
||||
Classes,
|
||||
FormGroup,
|
||||
InputGroup,
|
||||
Intent,
|
||||
Position,
|
||||
} from '@blueprintjs/core';
|
||||
import { compose, optionsMapToArray } from 'utils';
|
||||
import withSettingsActions from 'containers/Settings/withSettingsActions';
|
||||
import withSettings from 'containers/Settings/withSettings';
|
||||
|
||||
function ReferenceNumberForm({
|
||||
import { compose } from 'utils';
|
||||
|
||||
|
||||
export default function ReferenceNumberForm({
|
||||
onSubmit,
|
||||
onClose,
|
||||
initialPrefix,
|
||||
initialNumber,
|
||||
groupName,
|
||||
requestSubmitOptions,
|
||||
}) {
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
const validationSchema = Yup.object().shape({
|
||||
prefix: Yup.string(),
|
||||
number_prefix: Yup.string(),
|
||||
next_number: Yup.number(),
|
||||
});
|
||||
|
||||
const initialValues = useMemo(
|
||||
() => ({
|
||||
prefix: initialPrefix || '',
|
||||
number_prefix: initialPrefix || '',
|
||||
next_number: initialNumber || '',
|
||||
}),
|
||||
[],
|
||||
[initialPrefix, initialNumber],
|
||||
);
|
||||
|
||||
const {
|
||||
errors,
|
||||
values,
|
||||
touched,
|
||||
setFieldValue,
|
||||
resetForm,
|
||||
handleSubmit,
|
||||
isSubmitting,
|
||||
getFieldProps,
|
||||
@@ -56,33 +48,9 @@ function ReferenceNumberForm({
|
||||
},
|
||||
validationSchema,
|
||||
onSubmit: (values, { setSubmitting, setErrors }) => {
|
||||
const options = optionsMapToArray(values).map((option) => {
|
||||
return { key: option.key, ...option, group: groupName };
|
||||
});
|
||||
|
||||
onSubmit(
|
||||
requestSubmitOptions({ options })
|
||||
.then(() => {
|
||||
setSubmitting(false);
|
||||
})
|
||||
|
||||
.catch((erros) => {
|
||||
setSubmitting(false);
|
||||
}),
|
||||
);
|
||||
onSubmit(values, { setSubmitting, setErrors });
|
||||
},
|
||||
});
|
||||
|
||||
// Handles dialog close.
|
||||
// const handleClose = useCallback(() => {
|
||||
// closeDialog(dialogName);
|
||||
// }, [closeDialog, dialogName]);
|
||||
|
||||
// Handle dialog on closed.
|
||||
const onClosed = useCallback(() => {
|
||||
resetForm();
|
||||
}, [resetForm]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<form onSubmit={handleSubmit}>
|
||||
@@ -97,14 +65,14 @@ function ReferenceNumberForm({
|
||||
<FormGroup
|
||||
label={<T id={'prefix'} />}
|
||||
className={'form-group--'}
|
||||
intent={errors.prefix && touched.prefix && Intent.DANGER}
|
||||
intent={errors.number_prefix && touched.number_prefix && Intent.DANGER}
|
||||
helperText={
|
||||
<ErrorMessage name={'prefix'} {...{ errors, touched }} />
|
||||
}
|
||||
>
|
||||
<InputGroup
|
||||
intent={errors.prefix && touched.prefix && Intent.DANGER}
|
||||
{...getFieldProps('prefix')}
|
||||
intent={errors.number_prefix && touched.number_prefix && Intent.DANGER}
|
||||
{...getFieldProps('number_prefix')}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Col>
|
||||
@@ -147,6 +115,4 @@ function ReferenceNumberForm({
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withSettingsActions)(ReferenceNumberForm);
|
||||
}
|
||||
Reference in New Issue
Block a user