fix: the auto-increment of transactions.

This commit is contained in:
a.bouhuolia
2023-05-19 00:29:35 +02:00
parent 425d0293cc
commit d369f0bb17
37 changed files with 383 additions and 221 deletions

View File

@@ -46,4 +46,5 @@ export enum DialogsName {
EstimateExpenseForm = 'estimate-expense-form', EstimateExpenseForm = 'estimate-expense-form',
ProjectInvoicingForm = 'project-invoicing-form', ProjectInvoicingForm = 'project-invoicing-form',
ProjectBillableEntriesForm = 'project-billable-entries', ProjectBillableEntriesForm = 'project-billable-entries',
InvoiceNumberSettings = 'InvoiceNumberSettings'
} }

View File

@@ -4,7 +4,7 @@ import { Formik, Form } from 'formik';
import { Intent } from '@blueprintjs/core'; import { Intent } from '@blueprintjs/core';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import * as R from 'ramda'; import * as R from 'ramda';
import { defaultTo, isEmpty, omit } from 'lodash'; import { isEmpty, omit } from 'lodash';
import classNames from 'classnames'; import classNames from 'classnames';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
@@ -31,6 +31,7 @@ import {
transformToEditForm, transformToEditForm,
defaultManualJournal, defaultManualJournal,
} from './utils'; } from './utils';
import { JournalSyncIncrementSettingsToForm } from './components';
/** /**
* Journal entries form. * Journal entries form.
@@ -40,6 +41,7 @@ function MakeJournalEntriesForm({
journalNextNumber, journalNextNumber,
journalNumberPrefix, journalNumberPrefix,
journalAutoIncrement, journalAutoIncrement,
// #withCurrentOrganization // #withCurrentOrganization
organization: { base_currency }, organization: { base_currency },
}) { }) {
@@ -179,6 +181,9 @@ function MakeJournalEntriesForm({
{/* --------- Dialogs --------- */} {/* --------- Dialogs --------- */}
<MakeJournalFormDialogs /> <MakeJournalFormDialogs />
{/* --------- Effects --------- */}
<JournalSyncIncrementSettingsToForm />
</Form> </Form>
</Formik> </Formik>
</div> </div>

View File

@@ -8,6 +8,19 @@ import { PageFormBigNumber, FormattedMessage as T } from '@/components';
import MakeJournalEntriesHeaderFields from './MakeJournalEntriesHeaderFields'; import MakeJournalEntriesHeaderFields from './MakeJournalEntriesHeaderFields';
export default function MakeJournalEntriesHeader() { export default function MakeJournalEntriesHeader() {
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
<MakeJournalEntriesHeaderFields />
<MakeJournalHeaderBigNumber />
</div>
);
}
/**
* Big total number of make journal header.
* @returns {React.ReactNode}
*/
function MakeJournalHeaderBigNumber() {
const { const {
values: { entries, currency_code }, values: { entries, currency_code },
} = useFormikContext(); } = useFormikContext();
@@ -17,14 +30,10 @@ export default function MakeJournalEntriesHeader() {
const total = Math.max(totalCredit, totalDebit); const total = Math.max(totalCredit, totalDebit);
return ( return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
<MakeJournalEntriesHeaderFields />
<PageFormBigNumber <PageFormBigNumber
label={<T id={'amount'} />} label={<T id={'amount'} />}
amount={total} amount={total}
currencyCode={currency_code} currencyCode={currency_code}
/> />
</div>
); );
} }

View File

@@ -25,30 +25,24 @@ import {
Icon, Icon,
InputPrependButton, InputPrependButton,
CurrencySelectList, CurrencySelectList,
FormattedMessage as T FormattedMessage as T,
} from '@/components'; } from '@/components';
import { useMakeJournalFormContext } from './MakeJournalProvider'; import { useMakeJournalFormContext } from './MakeJournalProvider';
import { JournalExchangeRateInputField } from './components'; import { JournalExchangeRateInputField } from './components';
import { currenciesFieldShouldUpdate } from './utils';
import withSettings from '@/containers/Settings/withSettings'; import withSettings from '@/containers/Settings/withSettings';
import withDialogActions from '@/containers/Dialog/withDialogActions'; import withDialogActions from '@/containers/Dialog/withDialogActions';
import {
currenciesFieldShouldUpdate,
useObserveJournalNoSettings,
} from './utils';
/** /**
* Make journal entries header. * Make journal entries header.
*/ */
function MakeJournalEntriesHeader({ function MakeJournalEntriesHeader({
// #ownProps
onJournalNumberChanged,
// #withDialog // #withDialog
openDialog, openDialog,
// #withSettings // #withSettings
journalAutoIncrement, journalAutoIncrement,
journalNextNumber,
journalNumberPrefix,
}) { }) {
const { currencies } = useMakeJournalFormContext(); const { currencies } = useMakeJournalFormContext();
@@ -71,8 +65,6 @@ function MakeJournalEntriesHeader({
} }
}; };
useObserveJournalNoSettings(journalNumberPrefix, journalNextNumber);
return ( return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}> <div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/*------------ Posting date -----------*/} {/*------------ Posting date -----------*/}
@@ -224,7 +216,5 @@ export default compose(
withDialogActions, withDialogActions,
withSettings(({ manualJournalsSettings }) => ({ withSettings(({ manualJournalsSettings }) => ({
journalAutoIncrement: manualJournalsSettings?.autoIncrement, journalAutoIncrement: manualJournalsSettings?.autoIncrement,
journalNextNumber: manualJournalsSettings?.nextNumber,
journalNumberPrefix: manualJournalsSettings?.numberPrefix,
})), })),
)(MakeJournalEntriesHeader); )(MakeJournalEntriesHeader);

View File

@@ -16,11 +16,9 @@ export default function MakeJournalFormDialogs() {
}; };
return ( return (
<>
<JournalNumberDialog <JournalNumberDialog
dialogName={'journal-number-form'} dialogName={'journal-number-form'}
onConfirm={handleConfirm} onConfirm={handleConfirm}
/> />
</>
); );
} }

View File

@@ -1,9 +1,10 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React, { useEffect } from 'react';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import { Menu, MenuItem, Position, Button } from '@blueprintjs/core'; import { Menu, MenuItem, Position, Button } from '@blueprintjs/core';
import { Popover2 } from '@blueprintjs/popover2'; import { Popover2 } from '@blueprintjs/popover2';
import { useFormikContext } from 'formik'; import { useFormikContext } from 'formik';
import * as R from 'ramda';
import { import {
ExchangeRateInputGroup, ExchangeRateInputGroup,
@@ -24,6 +25,9 @@ import { CellType, Features, Align } from '@/constants';
import { useCurrentOrganization, useFeatureCan } from '@/hooks/state'; import { useCurrentOrganization, useFeatureCan } from '@/hooks/state';
import { useJournalIsForeign } from './utils'; import { useJournalIsForeign } from './utils';
import withSettings from '@/containers/Settings/withSettings';
import { transactionNumber } from '@/utils';
import { useUpdateEffect } from '@/hooks';
/** /**
* Contact header cell. * Contact header cell.
@@ -199,3 +203,34 @@ export function JournalExchangeRateInputField({ ...props }) {
/> />
); );
} }
/**
* Syncs journal auto-increment settings to form.
* @return {React.ReactNode}
*/
export const JournalSyncIncrementSettingsToForm = R.compose(
withSettings(({ manualJournalsSettings }) => ({
journalAutoIncrement: manualJournalsSettings?.autoIncrement,
journalNextNumber: manualJournalsSettings?.nextNumber,
journalNumberPrefix: manualJournalsSettings?.numberPrefix,
})),
)(({ journalAutoIncrement, journalNextNumber, journalNumberPrefix }) => {
const { setFieldValue } = useFormikContext();
useUpdateEffect(() => {
if (!journalAutoIncrement) return null;
const journalNo = transactionNumber(journalNumberPrefix, journalNextNumber);
setFieldValue('journal_number', journalNo);
}, [
setFieldValue,
journalNumberPrefix,
journalNextNumber,
journalAutoIncrement,
]);
return null;
});
JournalSyncIncrementSettingsToForm.displayName =
'JournalSyncIncrementSettingsToForm';

View File

@@ -174,15 +174,6 @@ export const transformErrors = (resErrors, { setErrors, errors }) => {
} }
}; };
export const useObserveJournalNoSettings = (prefix, nextNumber) => {
const { setFieldValue } = useFormikContext();
React.useEffect(() => {
const journalNo = transactionNumber(prefix, nextNumber);
setFieldValue('journal_number', journalNo);
}, [setFieldValue, prefix, nextNumber]);
};
/** /**
* Detarmines entries fast field should update. * Detarmines entries fast field should update.
*/ */

View File

@@ -14,6 +14,7 @@ import {
transformFormToSettings, transformFormToSettings,
transformSettingsToForm, transformSettingsToForm,
} from '@/containers/JournalNumber/utils'; } from '@/containers/JournalNumber/utils';
import { DialogsName } from '@/constants/dialogs';
/** /**
* invoice number dialog's content. * invoice number dialog's content.
@@ -39,7 +40,7 @@ function InvoiceNumberDialogContent({
// Handle the form success. // Handle the form success.
const handleSuccess = () => { const handleSuccess = () => {
setSubmitting(false); setSubmitting(false);
closeDialog('invoice-number-form'); closeDialog(DialogsName.InvoiceNumberSettings);
onConfirm(values); onConfirm(values);
}; };
// Handle the form errors. // Handle the form errors.
@@ -56,10 +57,9 @@ function InvoiceNumberDialogContent({
// Save the settings. // Save the settings.
saveSettings({ options }).then(handleSuccess).catch(handleErrors); saveSettings({ options }).then(handleSuccess).catch(handleErrors);
}; };
// Handle the dialog close. // Handle the dialog close.
const handleClose = () => { const handleClose = () => {
closeDialog('invoice-number-form'); closeDialog(DialogsName.InvoiceNumberSettings);
}; };
// Handle form change. // Handle form change.
const handleChange = (values) => { const handleChange = (values) => {
@@ -71,17 +71,19 @@ function InvoiceNumberDialogContent({
? intl.get('invoice.auto_increment.auto') ? intl.get('invoice.auto_increment.auto')
: intl.get('invoice.auto_increment.manually'); : intl.get('invoice.auto_increment.manually');
return ( const initialFormValues = {
<InvoiceNumberDialogProvider>
<ReferenceNumberForm
initialValues={{
...transformSettingsToForm({ ...transformSettingsToForm({
nextNumber, nextNumber,
numberPrefix, numberPrefix,
autoIncrement, autoIncrement,
}), }),
...initialValues, ...initialValues,
}} };
return (
<InvoiceNumberDialogProvider>
<ReferenceNumberForm
initialValues={initialFormValues}
description={description} description={description}
onSubmit={handleSubmitForm} onSubmit={handleSubmitForm}
onClose={handleClose} onClose={handleClose}

View File

@@ -16,10 +16,10 @@ import { saveInvoke } from '@/utils';
* Reference number form. * Reference number form.
*/ */
export default function ReferenceNumberForm({ export default function ReferenceNumberForm({
onSubmit,
onClose,
initialValues, initialValues,
description, description,
onSubmit,
onClose,
onChange, onChange,
}) { }) {
// Validation schema. // Validation schema.

View File

@@ -14,7 +14,7 @@ export const defaultInvoiceNoSettings = {
export const transformSettingsToForm = (settings) => ({ export const transformSettingsToForm = (settings) => ({
...settings, ...settings,
incrementMode: settings.autoIncrement === 'true' ? 'auto' : 'manual', incrementMode: settings.autoIncrement ? 'auto' : 'manual',
}); });
export const transformFormToSettings = (values, group) => { export const transformFormToSettings = (values, group) => {

View File

@@ -13,6 +13,15 @@ import BillFormHeaderFields from './BillFormHeaderFields';
* Fill form header. * Fill form header.
*/ */
function BillFormHeader() { function BillFormHeader() {
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
<BillFormHeaderFields />
<BillFormBigTotal />
</div>
);
}
function BillFormBigTotal() {
const { const {
values: { currency_code, entries }, values: { currency_code, entries },
} = useFormikContext(); } = useFormikContext();
@@ -21,14 +30,12 @@ function BillFormHeader() {
const totalDueAmount = useMemo(() => sumBy(entries, 'amount'), [entries]); const totalDueAmount = useMemo(() => sumBy(entries, 'amount'), [entries]);
return ( return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
<BillFormHeaderFields />
<PageFormBigNumber <PageFormBigNumber
label={intl.get('due_amount')} label={intl.get('due_amount')}
amount={totalDueAmount} amount={totalDueAmount}
currencyCode={currency_code} currencyCode={currency_code}
/> />
</div>
); );
} }
export default BillFormHeader; export default BillFormHeader;

View File

@@ -38,6 +38,7 @@ import {
import withSettings from '@/containers/Settings/withSettings'; import withSettings from '@/containers/Settings/withSettings';
import withCurrentOrganization from '@/containers/Organization/withCurrentOrganization'; import withCurrentOrganization from '@/containers/Organization/withCurrentOrganization';
import { CreditNoteSyncIncrementSettingsToForm } from './components';
/** /**
* Credit note form. * Credit note form.
@@ -162,6 +163,7 @@ function CreditNoteForm({
<CreditNoteFormFooter /> <CreditNoteFormFooter />
<CreditNoteFloatingActions /> <CreditNoteFloatingActions />
<CreditNoteFormDialogs /> <CreditNoteFormDialogs />
<CreditNoteSyncIncrementSettingsToForm />
</Form> </Form>
</Formik> </Formik>
</div> </div>

View File

@@ -13,6 +13,19 @@ import { PageFormBigNumber } from '@/components';
* Credit note header. * Credit note header.
*/ */
function CreditNoteFormHeader() { function CreditNoteFormHeader() {
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
<CreditNoteFormHeaderFields />
<CreditNoteFormBigNumber />
</div>
);
}
/**
* Big total number of credit note form header.
* @returns {React.ReactNode}
*/
function CreditNoteFormBigNumber() {
const { const {
values: { entries, currency_code }, values: { entries, currency_code },
} = useFormikContext(); } = useFormikContext();
@@ -21,14 +34,11 @@ function CreditNoteFormHeader() {
const totalAmount = React.useMemo(() => getEntriesTotal(entries), [entries]); const totalAmount = React.useMemo(() => getEntriesTotal(entries), [entries]);
return ( return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
<CreditNoteFormHeaderFields />
<PageFormBigNumber <PageFormBigNumber
label={intl.get('credit_note.label_amount_to_credit')} label={intl.get('credit_note.label_amount_to_credit')}
amount={totalAmount} amount={totalAmount}
currencyCode={currency_code} currencyCode={currency_code}
/> />
</div>
); );
} }

View File

@@ -21,7 +21,6 @@ import {
} from '@/components'; } from '@/components';
import { import {
customerNameFieldShouldUpdate, customerNameFieldShouldUpdate,
useObserveCreditNoSettings,
} from './utils'; } from './utils';
import { useCreditNoteFormContext } from './CreditNoteFormProvider'; import { useCreditNoteFormContext } from './CreditNoteFormProvider';
@@ -45,8 +44,6 @@ function CreditNoteFormHeaderFields({
// #withSettings // #withSettings
creditAutoIncrement, creditAutoIncrement,
creditNumberPrefix,
creditNextNumber,
}) { }) {
// Credit note form context. // Credit note form context.
const { customers } = useCreditNoteFormContext(); const { customers } = useCreditNoteFormContext();
@@ -70,9 +67,6 @@ function CreditNoteFormHeaderFields({
} }
}; };
// Syncs credit number settings with form.
useObserveCreditNoSettings(creditNumberPrefix, creditNextNumber);
return ( return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}> <div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ----------- Customer name ----------- */} {/* ----------- Customer name ----------- */}

View File

@@ -1,10 +1,12 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React, { useEffect } from 'react';
import { useFormikContext } from 'formik'; import { useFormikContext } from 'formik';
import * as R from 'ramda';
import { ExchangeRateInputGroup } from '@/components'; import { ExchangeRateInputGroup } from '@/components';
import { useCurrentOrganization } from '@/hooks/state'; import { useCurrentOrganization } from '@/hooks/state';
import { useCreditNoteIsForeignCustomer } from './utils'; import { useCreditNoteIsForeignCustomer } from './utils';
import withSettings from '@/containers/Settings/withSettings';
import { transactionNumber } from '@/utils';
/** /**
* credit exchange rate input field. * credit exchange rate input field.
@@ -28,3 +30,26 @@ import { useCreditNoteIsForeignCustomer } from './utils';
/> />
); );
} }
/**
* Syncs credit note auto-increment settings to form.
* @return {React.ReactNode}
*/
export const CreditNoteSyncIncrementSettingsToForm = R.compose(
withSettings(({ creditNoteSettings }) => ({
creditAutoIncrement: creditNoteSettings?.autoIncrement,
creditNextNumber: creditNoteSettings?.nextNumber,
creditNumberPrefix: creditNoteSettings?.numberPrefix,
})),
)(({ creditAutoIncrement, creditNextNumber, creditNumberPrefix }) => {
const { setFieldValue } = useFormikContext();
useEffect(() => {
if (!creditAutoIncrement) return;
const creditNo = transactionNumber(creditNumberPrefix, creditNextNumber);
setFieldValue('credit_note_number', creditNo);
}, [setFieldValue, creditNumberPrefix, creditNextNumber]);
return null;
});

View File

@@ -130,18 +130,6 @@ export const entriesFieldShouldUpdate = (newProps, oldProps) => {
); );
}; };
/**
* Syncs invoice no. settings with form.
*/
export const useObserveCreditNoSettings = (prefix, nextNumber) => {
const { setFieldValue } = useFormikContext();
React.useEffect(() => {
const creditNo = transactionNumber(prefix, nextNumber);
setFieldValue('credit_note_number', creditNo);
}, [setFieldValue, prefix, nextNumber]);
};
export const useSetPrimaryBranchToForm = () => { export const useSetPrimaryBranchToForm = () => {
const { setFieldValue } = useFormikContext(); const { setFieldValue } = useFormikContext();
const { branches, isBranchesSuccess } = useCreditNoteFormContext(); const { branches, isBranchesSuccess } = useCreditNoteFormContext();

View File

@@ -32,6 +32,7 @@ import {
transfromsFormValuesToRequest, transfromsFormValuesToRequest,
handleErrors, handleErrors,
} from './utils'; } from './utils';
import { EstimateIncrementSyncSettingsToForm } from './components';
/** /**
* Estimate form. * Estimate form.
@@ -160,8 +161,8 @@ function EstimateForm({
<EstimateItemsEntriesField /> <EstimateItemsEntriesField />
<EstimateFormFooter /> <EstimateFormFooter />
<EstimateFloatingActions /> <EstimateFloatingActions />
<EstimateFormDialogs /> <EstimateFormDialogs />
<EstimateIncrementSyncSettingsToForm />
</Form> </Form>
</Formik> </Formik>
</div> </div>

View File

@@ -12,6 +12,19 @@ import { PageFormBigNumber } from '@/components';
// Estimate form top header. // Estimate form top header.
function EstimateFormHeader() { function EstimateFormHeader() {
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
<EstimateFormHeaderFields />
<EstimateFormBigTotal />
</div>
);
}
/**
* Big total of estimate form header.
* @returns {React.ReactNode}
*/
function EstimateFormBigTotal() {
const { const {
values: { entries, currency_code }, values: { entries, currency_code },
} = useFormikContext(); } = useFormikContext();
@@ -20,15 +33,11 @@ function EstimateFormHeader() {
const totalDueAmount = useMemo(() => getEntriesTotal(entries), [entries]); const totalDueAmount = useMemo(() => getEntriesTotal(entries), [entries]);
return ( return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
<EstimateFormHeaderFields />
<PageFormBigNumber <PageFormBigNumber
label={intl.get('amount')} label={intl.get('amount')}
amount={totalDueAmount} amount={totalDueAmount}
currencyCode={currency_code} currencyCode={currency_code}
/> />
</div>
); );
} }

View File

@@ -39,7 +39,6 @@ import {
EstimateProjectSelectButton, EstimateProjectSelectButton,
} from './components'; } from './components';
import { useObserveEstimateNoSettings } from './utils';
import { useEstimateFormContext } from './EstimateFormProvider'; import { useEstimateFormContext } from './EstimateFormProvider';
/** /**
@@ -71,8 +70,7 @@ function EstimateFormHeader({
}); });
} }
}; };
// Syncs estimate number settings with the form.
useObserveEstimateNoSettings(estimateNumberPrefix, estimateNextNumber);
return ( return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}> <div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>

View File

@@ -1,12 +1,14 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React, { useEffect } from 'react';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import { Button } from '@blueprintjs/core'; import { Button } from '@blueprintjs/core';
import * as R from 'ramda';
import { useFormikContext } from 'formik'; import { useFormikContext } from 'formik';
import { ExchangeRateInputGroup } from '@/components'; import { ExchangeRateInputGroup } from '@/components';
import { useCurrentOrganization } from '@/hooks/state'; import { useCurrentOrganization } from '@/hooks/state';
import { useEstimateIsForeignCustomer } from './utils'; import { useEstimateIsForeignCustomer } from './utils';
import withSettings from '@/containers/Settings/withSettings';
import { transactionNumber } from '@/utils';
/** /**
* Estimate exchange rate input field. * Estimate exchange rate input field.
@@ -38,3 +40,29 @@ import { useEstimateIsForeignCustomer } from './utils';
export function EstimateProjectSelectButton({ label }) { export function EstimateProjectSelectButton({ label }) {
return <Button text={label ?? intl.get('select_project')} />; return <Button text={label ?? intl.get('select_project')} />;
} }
/**
* Syncs the estimate auto-increment settings to estimate form.
* @returns {React.ReactNode}
*/
export const EstimateIncrementSyncSettingsToForm = R.compose(
withSettings(({ estimatesSettings }) => ({
estimateNextNumber: estimatesSettings?.nextNumber,
estimateNumberPrefix: estimatesSettings?.numberPrefix,
estimateAutoIncrement: estimatesSettings?.autoIncrement,
})),
)(({ estimateNextNumber, estimateNumberPrefix, estimateAutoIncrement }) => {
const { setFieldValue } = useFormikContext();
useEffect(() => {
if (!estimateAutoIncrement) return null;
const estimateNo = transactionNumber(
estimateNumberPrefix,
estimateNextNumber,
);
setFieldValue('estimate_number', estimateNo);
}, [setFieldValue, estimateNumberPrefix, estimateNextNumber]);
return null;
});

View File

@@ -74,18 +74,6 @@ export const transformToEditForm = (estimate) => {
}; };
}; };
/**
* Syncs estimate number of the settings with the context form.
*/
export const useObserveEstimateNoSettings = (prefix, nextNumber) => {
const { setFieldValue } = useFormikContext();
React.useEffect(() => {
const estimateNo = transactionNumber(prefix, nextNumber);
setFieldValue('estimate_number', estimateNo);
}, [setFieldValue, prefix, nextNumber]);
};
/** /**
* Detarmines customers fast field when update. * Detarmines customers fast field when update.
*/ */

View File

@@ -32,6 +32,7 @@ import {
transformErrors, transformErrors,
transformValueToRequest, transformValueToRequest,
} from './utils'; } from './utils';
import { InvoiceNoSyncSettingsToForm } from './components';
/** /**
* Invoice form. * Invoice form.
@@ -64,8 +65,7 @@ function InvoiceForm({
invoiceNextNumber, invoiceNextNumber,
); );
// Form initial values. // Form initial values.
const initialValues = useMemo( const initialValues = {
() => ({
...(!isEmpty(invoice) ...(!isEmpty(invoice)
? { ...transformToEditForm(invoice) } ? { ...transformToEditForm(invoice) }
: { : {
@@ -77,10 +77,7 @@ function InvoiceForm({
currency_code: base_currency, currency_code: base_currency,
...newInvoice, ...newInvoice,
}), }),
}), };
[invoice, newInvoice, invoiceNumber, invoiceIncrementMode, base_currency],
);
// Handles form submit. // Handles form submit.
const handleSubmit = (values, { setSubmitting, setErrors, resetForm }) => { const handleSubmit = (values, { setSubmitting, setErrors, resetForm }) => {
setSubmitting(true); setSubmitting(true);
@@ -105,7 +102,6 @@ function InvoiceForm({
delivered: submitPayload.deliver, delivered: submitPayload.deliver,
from_estimate_id: estimateId, from_estimate_id: estimateId,
}; };
// Handle the request success. // Handle the request success.
const onSuccess = () => { const onSuccess = () => {
AppToaster.show({ AppToaster.show({
@@ -173,6 +169,7 @@ function InvoiceForm({
<InvoiceFormFooter /> <InvoiceFormFooter />
<InvoiceFloatingActions /> <InvoiceFloatingActions />
<InvoiceFormDialogs /> <InvoiceFormDialogs />
<InvoiceNoSyncSettingsToForm />
</Form> </Form>
</Formik> </Formik>
</div> </div>

View File

@@ -2,6 +2,7 @@
import React from 'react'; import React from 'react';
import InvoiceNumberDialog from '@/containers/Dialogs/InvoiceNumberDialog'; import InvoiceNumberDialog from '@/containers/Dialogs/InvoiceNumberDialog';
import { useFormikContext } from 'formik'; import { useFormikContext } from 'formik';
import { DialogsName } from '@/constants/dialogs';
/** /**
* Invoice form dialogs. * Invoice form dialogs.
@@ -16,11 +17,9 @@ export default function InvoiceFormDialogs() {
}; };
return ( return (
<>
<InvoiceNumberDialog <InvoiceNumberDialog
dialogName={'invoice-number-form'} dialogName={DialogsName.InvoiceNumberSettings}
onConfirm={handleInvoiceNumberFormConfirm} onConfirm={handleInvoiceNumberFormConfirm}
/> />
</>
); );
} }

View File

@@ -28,7 +28,6 @@ import { momentFormatter, compose, tansformDateValue } from '@/utils';
import { CLASSES } from '@/constants/classes'; import { CLASSES } from '@/constants/classes';
import { inputIntent, handleDateChange } from '@/utils'; import { inputIntent, handleDateChange } from '@/utils';
import { import {
useObserveInvoiceNoSettings,
customerNameFieldShouldUpdate, customerNameFieldShouldUpdate,
} from './utils'; } from './utils';
@@ -42,6 +41,7 @@ import {
ProjectBillableEntriesLink, ProjectBillableEntriesLink,
} from '@/containers/Projects/components'; } from '@/containers/Projects/components';
import { Features } from '@/constants'; import { Features } from '@/constants';
import { DialogsName } from '@/constants/dialogs';
import withSettings from '@/containers/Settings/withSettings'; import withSettings from '@/containers/Settings/withSettings';
import withDialogActions from '@/containers/Dialog/withDialogActions'; import withDialogActions from '@/containers/Dialog/withDialogActions';
@@ -55,8 +55,6 @@ function InvoiceFormHeaderFields({
// #withSettings // #withSettings
invoiceAutoIncrement, invoiceAutoIncrement,
invoiceNumberPrefix,
invoiceNextNumber,
}) { }) {
// Invoice form context. // Invoice form context.
const { customers, projects } = useInvoiceFormContext(); const { customers, projects } = useInvoiceFormContext();
@@ -66,14 +64,14 @@ function InvoiceFormHeaderFields({
// Handle invoice number changing. // Handle invoice number changing.
const handleInvoiceNumberChange = () => { const handleInvoiceNumberChange = () => {
openDialog('invoice-number-form'); openDialog(DialogsName.InvoiceNumberSettings);
}; };
// Handle invoice no. field blur. // Handle invoice no. field blur.
const handleInvoiceNoBlur = (form, field) => (event) => { const handleInvoiceNoBlur = (form, field) => (event) => {
const newValue = event.target.value; const newValue = event.target.value;
if (field.value !== newValue && invoiceAutoIncrement) { if (field.value !== newValue && invoiceAutoIncrement) {
openDialog('invoice-number-form', { openDialog(DialogsName.InvoiceNumberSettings, {
initialFormValues: { initialFormValues: {
manualTransactionNo: newValue, manualTransactionNo: newValue,
incrementMode: 'manual-transaction', incrementMode: 'manual-transaction',
@@ -81,8 +79,6 @@ function InvoiceFormHeaderFields({
}); });
} }
}; };
// Syncs invoice number settings with form.
useObserveInvoiceNoSettings(invoiceNumberPrefix, invoiceNextNumber);
return ( return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}> <div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
@@ -268,8 +264,6 @@ export default compose(
withDialogActions, withDialogActions,
withSettings(({ invoiceSettings }) => ({ withSettings(({ invoiceSettings }) => ({
invoiceAutoIncrement: invoiceSettings?.autoIncrement, invoiceAutoIncrement: invoiceSettings?.autoIncrement,
invoiceNextNumber: invoiceSettings?.nextNumber,
invoiceNumberPrefix: invoiceSettings?.numberPrefix,
})), })),
)(InvoiceFormHeaderFields); )(InvoiceFormHeaderFields);

View File

@@ -1,11 +1,15 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React from 'react';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import * as R from 'ramda';
import { Button } from '@blueprintjs/core'; import { Button } from '@blueprintjs/core';
import { useFormikContext } from 'formik'; import { useFormikContext } from 'formik';
import { ExchangeRateInputGroup } from '@/components'; import { ExchangeRateInputGroup } from '@/components';
import { useCurrentOrganization } from '@/hooks/state'; import { useCurrentOrganization } from '@/hooks/state';
import { useInvoiceIsForeignCustomer } from './utils'; import { useInvoiceIsForeignCustomer } from './utils';
import withSettings from '@/containers/Settings/withSettings';
import { useUpdateEffect } from '@/hooks';
import { transactionNumber } from '@/utils';
/** /**
* Invoice exchange rate input field. * Invoice exchange rate input field.
@@ -37,3 +41,26 @@ export function InvoiceExchangeRateInputField({ ...props }) {
export function InvoiceProjectSelectButton({ label }) { export function InvoiceProjectSelectButton({ label }) {
return <Button text={label ?? intl.get('select_project')} />; return <Button text={label ?? intl.get('select_project')} />;
} }
/**
* Syncs invoice auto-increment settings to invoice form once update.
*/
export const InvoiceNoSyncSettingsToForm = R.compose(
withSettings(({ invoiceSettings }) => ({
invoiceAutoIncrement: invoiceSettings?.autoIncrement,
invoiceNextNumber: invoiceSettings?.nextNumber,
invoiceNumberPrefix: invoiceSettings?.numberPrefix,
})),
)(({ invoiceAutoIncrement, invoiceNextNumber, invoiceNumberPrefix }) => {
const { setFieldValue } = useFormikContext();
useUpdateEffect(() => {
// Do not update if the invoice auto-increment is disabled.
if (!invoiceAutoIncrement) return null;
const invoiceNo = transactionNumber(invoiceNumberPrefix, invoiceNextNumber);
setFieldValue('invoice_no', invoiceNo);
}, [setFieldValue, invoiceNumberPrefix, invoiceNextNumber]);
return null;
});

View File

@@ -108,18 +108,6 @@ export const transformErrors = (errors, { setErrors }) => {
} }
}; };
/**
* Syncs invoice no. settings with form.
*/
export const useObserveInvoiceNoSettings = (prefix, nextNumber) => {
const { setFieldValue } = useFormikContext();
React.useEffect(() => {
const invoiceNo = transactionNumber(prefix, nextNumber);
setFieldValue('invoice_no', invoiceNo);
}, [setFieldValue, prefix, nextNumber]);
};
/** /**
* Detarmines customer name field when should update. * Detarmines customer name field when should update.
*/ */

View File

@@ -36,6 +36,7 @@ import {
transformFormToRequest, transformFormToRequest,
transformErrors, transformErrors,
} from './utils'; } from './utils';
import { PaymentReceiveSyncIncrementSettingsToForm } from './components';
/** /**
* Payment Receive form. * Payment Receive form.
@@ -176,6 +177,8 @@ function PaymentReceiveForm({
<PaymentReceiveFormFooter /> <PaymentReceiveFormFooter />
<PaymentReceiveFloatingActions /> <PaymentReceiveFloatingActions />
<PaymentReceiveSyncIncrementSettingsToForm />
{/* ------- Alerts & Dialogs ------- */} {/* ------- Alerts & Dialogs ------- */}
<PaymentReceiveFormAlerts /> <PaymentReceiveFormAlerts />
<PaymentReceiveFormDialogs /> <PaymentReceiveFormDialogs />

View File

@@ -13,6 +13,21 @@ import PaymentReceiveHeaderFields from './PaymentReceiveHeaderFields';
* Payment receive form header. * Payment receive form header.
*/ */
function PaymentReceiveFormHeader() { function PaymentReceiveFormHeader() {
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
<div className={classNames(CLASSES.PAGE_FORM_HEADER_PRIMARY)}>
<PaymentReceiveHeaderFields />
<PaymentReceiveFormBigTotal />
</div>
</div>
);
}
/**
* Big total amount of payment receive form.
* @returns {React.ReactNode}
*/
function PaymentReceiveFormBigTotal() {
// Formik form context. // Formik form context.
const { const {
values: { currency_code, entries }, values: { currency_code, entries },
@@ -25,10 +40,6 @@ function PaymentReceiveFormHeader() {
); );
return ( return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
<div className={classNames(CLASSES.PAGE_FORM_HEADER_PRIMARY)}>
<PaymentReceiveHeaderFields />
<div className={classNames(CLASSES.PAGE_FORM_HEADER_BIG_NUMBERS)}> <div className={classNames(CLASSES.PAGE_FORM_HEADER_BIG_NUMBERS)}>
<div class="big-amount"> <div class="big-amount">
<span class="big-amount__label"> <span class="big-amount__label">
@@ -39,8 +50,6 @@ function PaymentReceiveFormHeader() {
</h1> </h1>
</div> </div>
</div> </div>
</div>
</div>
); );
} }

View File

@@ -51,7 +51,6 @@ import withSettings from '@/containers/Settings/withSettings';
import withCurrentOrganization from '@/containers/Organization/withCurrentOrganization'; import withCurrentOrganization from '@/containers/Organization/withCurrentOrganization';
import { import {
useObservePaymentNoSettings,
amountPaymentEntries, amountPaymentEntries,
fullAmountPaymentEntries, fullAmountPaymentEntries,
customersFieldShouldUpdate, customersFieldShouldUpdate,
@@ -71,8 +70,6 @@ function PaymentReceiveHeaderFields({
// #withSettings // #withSettings
paymentReceiveAutoIncrement, paymentReceiveAutoIncrement,
paymentReceiveNumberPrefix,
paymentReceiveNextNumber,
}) { }) {
// Payment receive form context. // Payment receive form context.
const { customers, accounts, projects, isNewMode } = const { customers, accounts, projects, isNewMode } =
@@ -123,12 +120,6 @@ function PaymentReceiveHeaderFields({
} }
}; };
// Syncs payment receive number from settings to the form.
useObservePaymentNoSettings(
paymentReceiveNumberPrefix,
paymentReceiveNextNumber,
);
return ( return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}> <div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ------------- Customer name ------------- */} {/* ------------- Customer name ------------- */}
@@ -351,8 +342,6 @@ function PaymentReceiveHeaderFields({
export default compose( export default compose(
withSettings(({ paymentReceiveSettings }) => ({ withSettings(({ paymentReceiveSettings }) => ({
paymentReceiveNextNumber: paymentReceiveSettings?.nextNumber,
paymentReceiveNumberPrefix: paymentReceiveSettings?.numberPrefix,
paymentReceiveAutoIncrement: paymentReceiveSettings?.autoIncrement, paymentReceiveAutoIncrement: paymentReceiveSettings?.autoIncrement,
})), })),
withDialogActions, withDialogActions,

View File

@@ -1,14 +1,17 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React, { useEffect } from 'react';
import moment from 'moment'; import moment from 'moment';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import { Button } from '@blueprintjs/core'; import { Button } from '@blueprintjs/core';
import { useFormikContext } from 'formik'; import { useFormikContext } from 'formik';
import * as R from 'ramda';
import { Money, ExchangeRateInputGroup, MoneyFieldCell } from '@/components'; import { Money, ExchangeRateInputGroup, MoneyFieldCell } from '@/components';
import { useCurrentOrganization } from '@/hooks/state'; import { useCurrentOrganization } from '@/hooks/state';
import { useEstimateIsForeignCustomer } from './utils'; import { useEstimateIsForeignCustomer } from './utils';
import { transactionNumber } from '@/utils';
import withSettings from '@/containers/Settings/withSettings';
/** /**
* Invoice date cell. * Invoice date cell.
@@ -112,3 +115,39 @@ export function PaymentReceiveExchangeRateInputField({ ...props }) {
export function PaymentReceiveProjectSelectButton({ label }) { export function PaymentReceiveProjectSelectButton({ label }) {
return <Button text={label ?? intl.get('select_project')} />; return <Button text={label ?? intl.get('select_project')} />;
} }
/**
* Syncs the auto-increment settings to payment receive form.
* @returns {React.ReactNode}
*/
export const PaymentReceiveSyncIncrementSettingsToForm = R.compose(
withSettings(({ paymentReceiveSettings }) => ({
paymentReceiveNextNumber: paymentReceiveSettings?.nextNumber,
paymentReceiveNumberPrefix: paymentReceiveSettings?.numberPrefix,
paymentReceiveAutoIncrement: paymentReceiveSettings?.autoIncrement,
})),
)(
({
paymentReceiveNextNumber,
paymentReceiveNumberPrefix,
paymentReceiveAutoIncrement,
}) => {
const { setFieldValue } = useFormikContext();
useEffect(() => {
if (!paymentReceiveAutoIncrement) return;
const paymentReceiveNo = transactionNumber(
paymentReceiveNumberPrefix,
paymentReceiveNextNumber,
);
setFieldValue('payment_receive_no', paymentReceiveNo);
}, [
setFieldValue,
paymentReceiveNumberPrefix,
paymentReceiveNextNumber,
paymentReceiveAutoIncrement,
]);
return null;
},
);

View File

@@ -34,6 +34,7 @@ import {
handleErrors, handleErrors,
transformFormValuesToRequest, transformFormValuesToRequest,
} from './utils'; } from './utils';
import { ReceiptSyncIncrementSettingsToForm } from './components';
/** /**
* Receipt form. * Receipt form.
@@ -164,8 +165,8 @@ function ReceiptForm({
<ReceiptItemsEntriesEditor /> <ReceiptItemsEntriesEditor />
<ReceiptFormFooter /> <ReceiptFormFooter />
<ReceiptFormFloatingActions /> <ReceiptFormFloatingActions />
<ReceiptFormDialogs /> <ReceiptFormDialogs />
<ReceiptSyncIncrementSettingsToForm />
</Form> </Form>
</Formik> </Formik>
</div> </div>

View File

@@ -17,6 +17,21 @@ function ReceiptFormHeader({
// #ownProps // #ownProps
onReceiptNumberChanged, onReceiptNumberChanged,
}) { }) {
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
<ReceiptFormHeaderFields
onReceiptNumberChanged={onReceiptNumberChanged}
/>
<ReceiptFormHeaderBigTotal />
</div>
);
}
/**
* The big total amount of receipt form.
* @returns {React.ReactNode}
*/
function ReceiptFormHeaderBigTotal() {
const { const {
values: { currency_code, entries }, values: { currency_code, entries },
} = useFormikContext(); } = useFormikContext();
@@ -25,16 +40,11 @@ function ReceiptFormHeader({
const totalDueAmount = useMemo(() => getEntriesTotal(entries), [entries]); const totalDueAmount = useMemo(() => getEntriesTotal(entries), [entries]);
return ( return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
<ReceiptFormHeaderFields
onReceiptNumberChanged={onReceiptNumberChanged}
/>
<PageFormBigNumber <PageFormBigNumber
label={intl.get('due_amount')} label={intl.get('due_amount')}
amount={totalDueAmount} amount={totalDueAmount}
currencyCode={currency_code} currencyCode={currency_code}
/> />
</div>
); );
} }

View File

@@ -39,7 +39,6 @@ import { useReceiptFormContext } from './ReceiptFormProvider';
import { import {
accountsFieldShouldUpdate, accountsFieldShouldUpdate,
customersFieldShouldUpdate, customersFieldShouldUpdate,
useObserveReceiptNoSettings,
} from './utils'; } from './utils';
import { import {
ReceiptExchangeRateInputField, ReceiptExchangeRateInputField,
@@ -56,8 +55,6 @@ function ReceiptFormHeader({
// #withSettings // #withSettings
receiptAutoIncrement, receiptAutoIncrement,
receiptNextNumber,
receiptNumberPrefix,
}) { }) {
const { accounts, customers, projects } = useReceiptFormContext(); const { accounts, customers, projects } = useReceiptFormContext();
@@ -78,9 +75,6 @@ function ReceiptFormHeader({
} }
}; };
// Synsc receipt number settings with the form.
useObserveReceiptNoSettings(receiptNumberPrefix, receiptNextNumber);
return ( return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}> <div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ----------- Customer name ----------- */} {/* ----------- Customer name ----------- */}
@@ -255,8 +249,6 @@ export default compose(
withDialogActions, withDialogActions,
withSettings(({ receiptSettings }) => ({ withSettings(({ receiptSettings }) => ({
receiptAutoIncrement: receiptSettings?.autoIncrement, receiptAutoIncrement: receiptSettings?.autoIncrement,
receiptNextNumber: receiptSettings?.nextNumber,
receiptNumberPrefix: receiptSettings?.numberPrefix,
})), })),
)(ReceiptFormHeader); )(ReceiptFormHeader);

View File

@@ -3,9 +3,14 @@ import React from 'react';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import { Button } from '@blueprintjs/core'; import { Button } from '@blueprintjs/core';
import { useFormikContext } from 'formik'; import { useFormikContext } from 'formik';
import * as R from 'ramda';
import { ExchangeRateInputGroup } from '@/components'; import { ExchangeRateInputGroup } from '@/components';
import { useCurrentOrganization } from '@/hooks/state'; import { useCurrentOrganization } from '@/hooks/state';
import { useReceiptIsForeignCustomer } from './utils'; import { useReceiptIsForeignCustomer } from './utils';
import { useUpdateEffect } from '@/hooks';
import withSettings from '@/containers/Settings/withSettings';
import { transactionNumber } from '@/utils';
/** /**
* Receipt exchange rate input field. * Receipt exchange rate input field.
@@ -37,3 +42,31 @@ export function ReceiptExchangeRateInputField({ ...props }) {
export function ReceiptProjectSelectButton({ label }) { export function ReceiptProjectSelectButton({ label }) {
return <Button text={label ?? intl.get('select_project')} />; return <Button text={label ?? intl.get('select_project')} />;
} }
/**
* Syncs receipt auto-increment settings to form.
* @return {React.ReactNode}
*/
export const ReceiptSyncIncrementSettingsToForm = R.compose(
withSettings(({ receiptSettings }) => ({
receiptAutoIncrement: receiptSettings?.autoIncrement,
receiptNextNumber: receiptSettings?.nextNumber,
receiptNumberPrefix: receiptSettings?.numberPrefix,
})),
)(({ receiptAutoIncrement, receiptNextNumber, receiptNumberPrefix }) => {
const { setFieldValue } = useFormikContext();
useUpdateEffect(() => {
if (!receiptAutoIncrement) return;
const receiptNo = transactionNumber(receiptNumberPrefix, receiptNextNumber);
setFieldValue('receipt_number', receiptNo);
}, [
setFieldValue,
receiptNumberPrefix,
receiptAutoIncrement,
receiptNextNumber,
]);
return null;
});

View File

@@ -77,15 +77,6 @@ export const transformToEditForm = (receipt) => {
}; };
}; };
export const useObserveReceiptNoSettings = (prefix, nextNumber) => {
const { setFieldValue } = useFormikContext();
React.useEffect(() => {
const receiptNo = transactionNumber(prefix, nextNumber);
setFieldValue('receipt_number', receiptNo);
}, [setFieldValue, prefix, nextNumber]);
};
/** /**
* Detarmines entries fast field should update. * Detarmines entries fast field should update.
*/ */

View File

@@ -21,6 +21,9 @@ const commonInvalidateQueries = (client) => {
client.invalidateQueries(t.ACCOUNTS); client.invalidateQueries(t.ACCOUNTS);
client.invalidateQueries(t.ACCOUNT); client.invalidateQueries(t.ACCOUNT);
// Invalidate settings.
client.invalidateQueries([t.SETTING, t.SETTING_MANUAL_JOURNALS]);
// Invalidate financial reports. // Invalidate financial reports.
client.invalidateQueries(t.FINANCIAL_REPORT); client.invalidateQueries(t.FINANCIAL_REPORT);

View File

@@ -4,6 +4,7 @@ import { useRequestQuery } from '../useQueryRequest';
import useApiRequest from '../useRequest'; import useApiRequest from '../useRequest';
import { useSetSettings } from '@/hooks/state'; import { useSetSettings } from '@/hooks/state';
import t from './types'; import t from './types';
import { useEffect } from 'react';
/** /**
* Saves the settings. * Saves the settings.
@@ -23,18 +24,23 @@ export function useSaveSettings(props) {
function useSettingsQuery(key, query, props) { function useSettingsQuery(key, query, props) {
const setSettings = useSetSettings(); const setSettings = useSetSettings();
return useRequestQuery( const settingsQuery = useRequestQuery(
key, key,
{ method: 'get', url: 'settings', params: query }, { method: 'get', url: 'settings', params: query },
{ {
select: (res) => res.data.settings, select: (res) => res.data.settings,
defaultData: [], defaultData: [],
onSuccess: (settings) => {
setSettings(settings);
},
...props, ...props,
}, },
); );
useEffect(() => {
// Sync to Redux state if the reqeust success and is not fetching.
if (!settingsQuery.isFetching && settingsQuery.isSuccess) {
setSettings(settingsQuery.data);
}
}, [settingsQuery.isFetching, settingsQuery.isSuccess, settingsQuery.data]);
return settingsQuery;
} }
/** /**