diff --git a/src/containers/Dialogs/CreditNoteNumberDialog/CreditNoteNumberDialogContent.js b/src/containers/Dialogs/CreditNoteNumberDialog/CreditNoteNumberDialogContent.js new file mode 100644 index 000000000..ff1d56f16 --- /dev/null +++ b/src/containers/Dialogs/CreditNoteNumberDialog/CreditNoteNumberDialogContent.js @@ -0,0 +1,102 @@ +import React from 'react'; +import intl from 'react-intl-universal'; +import { useSaveSettings } from 'hooks/query'; + +import { CreditNoteNumberDialogProvider } from './CreditNoteNumberDialogProvider'; +import ReferenceNumberForm from 'containers/JournalNumber/ReferenceNumberForm'; + +import withDialogActions from 'containers/Dialog/withDialogActions'; +import withSettings from 'containers/Settings/withSettings'; +import withSettingsActions from 'containers/Settings/withSettingsActions'; +import { compose } from 'utils'; +import { + transformFormToSettings, + transformSettingsToForm, +} from 'containers/JournalNumber/utils'; + +/** + * credit note number dialog content + */ +function CreditNoteNumberDialogContent({ + // #ownProps + initialValues, + onConfirm, + + // #withSettings + nextNumber, + numberPrefix, + autoIncrement, + + // #withDialogActions + closeDialog, +}) { + const { mutateAsync: saveSettings } = useSaveSettings(); + const [referenceFormValues, setReferenceFormValues] = React.useState(null); + + // Handle the submit form. + const handleSubmitForm = (values, { setSubmitting }) => { + // Handle the form success. + const handleSuccess = () => { + setSubmitting(false); + closeDialog('credit-number-form'); + onConfirm(values); + }; + // Handle the form errors. + const handleErrors = () => { + setSubmitting(false); + }; + if (values.incrementMode === 'manual-transaction') { + handleSuccess(); + return; + } + // Transformes the form values to settings to save it. + const options = transformFormToSettings(values, 'credit_note'); + + // Save the settings. + saveSettings({ options }).then(handleSuccess).catch(handleErrors); + }; + + // Handle the dialog close. + const handleClose = () => { + closeDialog('credit-number-form'); + }; + // Handle form change. + const handleChange = (values) => { + setReferenceFormValues(values); + }; + + // Description. + const description = + referenceFormValues?.incrementMode === 'auto' + ? intl.get('credit_note.auto_increment.auto') + : intl.get('credit_note.auto_increment.manually'); + + return ( + + + + ); +} + +export default compose( + withDialogActions, + withSettingsActions, + withSettings(({ creditNoteSettings }) => ({ + autoIncrement: creditNoteSettings?.autoIncrement, + nextNumber: creditNoteSettings?.nextNumber, + numberPrefix: creditNoteSettings?.numberPrefix, + })), +)(CreditNoteNumberDialogContent); diff --git a/src/containers/Dialogs/CreditNoteNumberDialog/CreditNoteNumberDialogProvider.js b/src/containers/Dialogs/CreditNoteNumberDialog/CreditNoteNumberDialogProvider.js new file mode 100644 index 000000000..94daf467f --- /dev/null +++ b/src/containers/Dialogs/CreditNoteNumberDialog/CreditNoteNumberDialogProvider.js @@ -0,0 +1,28 @@ +import React from 'react'; +import { DialogContent } from 'components'; +import { useSettingsCreditNotes } from 'hooks/query'; + +const CreditNoteNumberDialogContext = React.createContext(); + +/** + *Credit Note number dialog provider + */ +function CreditNoteNumberDialogProvider({ query, ...props }) { + const { isLoading: isSettingsLoading } = useSettingsCreditNotes(); + + // Provider payload. + const provider = { + isSettingsLoading, + }; + + return ( + + + + ); +} + +const useCreditNoteNumberDialogContext = () => + React.useContext(CreditNoteNumberDialogContext); + +export { CreditNoteNumberDialogProvider, useCreditNoteNumberDialogContext }; diff --git a/src/containers/Dialogs/CreditNoteNumberDialog/index.js b/src/containers/Dialogs/CreditNoteNumberDialog/index.js new file mode 100644 index 000000000..c1786c324 --- /dev/null +++ b/src/containers/Dialogs/CreditNoteNumberDialog/index.js @@ -0,0 +1,40 @@ +import React from 'react'; +import { Dialog, DialogSuspense, FormattedMessage as T } from 'components'; +import withDialogRedux from 'components/DialogReduxConnect'; +import { compose, saveInvoke } from 'utils'; + +const CreditNoteNumberDialogContent = React.lazy(() => + import('./CreditNoteNumberDialogContent'), +); + +/** + * Credit note number dialog. + */ +function CreditNoteNumberDialog({ + dialogName, + payload: { initialFormValues }, + isOpen, + onConfirm, +}) { + const handleConfirm = (values) => { + saveInvoke(onConfirm, values); + }; + + return ( + } + name={dialogName} + autoFocus={true} + canEscapeKeyClose={true} + isOpen={isOpen} + > + + + + + ); +} +export default compose(withDialogRedux())(CreditNoteNumberDialog); diff --git a/src/containers/Sales/CreditNotes/CreditNoteForm/CreditNoteForm.js b/src/containers/Sales/CreditNotes/CreditNoteForm/CreditNoteForm.js index 9fe97db87..b75e6130d 100644 --- a/src/containers/Sales/CreditNotes/CreditNoteForm/CreditNoteForm.js +++ b/src/containers/Sales/CreditNotes/CreditNoteForm/CreditNoteForm.js @@ -15,6 +15,7 @@ import CreditNoteFormHeader from './CreditNoteFormHeader'; import CreditNoteItemsEntriesEditorField from './CreditNoteItemsEntriesEditorField'; import CreditNoteFormFooter from './CreditNoteFormFooter'; import CreditNoteFloatingActions from './CreditNoteFloatingActions'; +import CreditNoteFormDialogs from './CreditNoteFormDialogs'; import { AppToaster } from 'components'; @@ -41,6 +42,9 @@ import withCurrentOrganization from 'containers/Organization/withCurrentOrganiza */ function CreditNoteForm({ // #withSettings + creditAutoIncrement, + creditNumberPrefix, + creditNextNumber, // #withCurrentOrganization organization: { base_currency }, @@ -56,6 +60,9 @@ function CreditNoteForm({ editCreditNoteMutate, } = useCreditNoteFormContext(); + // Credit number. + const creditNumber = transactionNumber(creditNumberPrefix, creditNextNumber); + // Initial values. const initialValues = React.useMemo( () => ({ @@ -63,6 +70,9 @@ function CreditNoteForm({ ? { ...transformToEditForm(creditNote), currency_code: base_currency } : { ...defaultCreditNote, + ...(creditAutoIncrement && { + credit_note_number: creditNumber, + }), entries: orderingLinesIndexes(defaultCreditNote.entries), }), }), @@ -144,10 +154,17 @@ function CreditNoteForm({ + ); } - -export default compose(withCurrentOrganization())(CreditNoteForm); +export default compose( + withSettings(({ creditNoteSettings }) => ({ + creditAutoIncrement: creditNoteSettings?.autoIncrement, + creditNextNumber: creditNoteSettings?.nextNumber, + creditNumberPrefix: creditNoteSettings?.numberPrefix, + })), + withCurrentOrganization(), +)(CreditNoteForm); diff --git a/src/containers/Sales/CreditNotes/CreditNoteForm/CreditNoteFormDialogs.js b/src/containers/Sales/CreditNotes/CreditNoteForm/CreditNoteFormDialogs.js new file mode 100644 index 000000000..bfb2d99c7 --- /dev/null +++ b/src/containers/Sales/CreditNotes/CreditNoteForm/CreditNoteFormDialogs.js @@ -0,0 +1,24 @@ +import React from 'react'; +import CreditNoteNumberDialog from '../../../Dialogs/CreditNoteNumberDialog'; +import { useFormikContext } from 'formik'; + +/** + * Credit note form dialogs. + */ +export default function CreditNoteFormDialogs() { + // Update the form once the credit number form submit confirm. + const handleCreditNumberFormConfirm = ({ incrementNumber, manually }) => { + setFieldValue('credit_note_number', incrementNumber || ''); + setFieldValue('credit_note_no_manually', manually); + }; + + const { setFieldValue } = useFormikContext(); + return ( + + + + ); +} diff --git a/src/containers/Sales/CreditNotes/CreditNoteForm/CreditNoteFormHeaderFields.js b/src/containers/Sales/CreditNotes/CreditNoteForm/CreditNoteFormHeaderFields.js index d17d28e8c..00ac441d2 100644 --- a/src/containers/Sales/CreditNotes/CreditNoteForm/CreditNoteFormHeaderFields.js +++ b/src/containers/Sales/CreditNotes/CreditNoteForm/CreditNoteFormHeaderFields.js @@ -1,5 +1,10 @@ import React from 'react'; -import { FormGroup, InputGroup, Position } from '@blueprintjs/core'; +import { + FormGroup, + InputGroup, + Position, + ControlGroup, +} from '@blueprintjs/core'; import { DateInput } from '@blueprintjs/datetime'; import { FastField, Field, ErrorMessage } from 'formik'; import { CLASSES } from 'common/classes'; @@ -7,10 +12,14 @@ import classNames from 'classnames'; import { ContactSelecetList, FieldRequiredHint, + InputPrependButton, Icon, FormattedMessage as T, } from 'components'; -import { customerNameFieldShouldUpdate } from './utils'; +import { + customerNameFieldShouldUpdate, + useObserveCreditNoSettings, +} from './utils'; import { useCreditNoteFormContext } from './CreditNoteFormProvider'; import withSettings from 'containers/Settings/withSettings'; @@ -27,10 +36,40 @@ import { /** * Credit note form header fields. */ -function CreditNoteFormHeaderFields() { - +function CreditNoteFormHeaderFields({ + // #withDialogActions + openDialog, + + // #withSettings + creditAutoIncrement, + creditNumberPrefix, + creditNextNumber, +}) { // Credit note form context. const { customers } = useCreditNoteFormContext(); + + // Handle credit number changing. + const handleCreditNumberChange = () => { + openDialog('credit-number-form'); + }; + + // Handle credit no. field blur. + const handleCreditNoBlur = (form, field) => (event) => { + const newValue = event.target.value; + + if (field.value !== newValue && creditAutoIncrement) { + openDialog('credit-number-form', { + initialFormValues: { + manualTransactionNo: newValue, + incrementMode: 'manual-transaction', + }, + }); + } + }; + + // Syncs credit number settings with form. + useObserveCreditNoSettings(creditNumberPrefix, creditNextNumber); + return (
{/* ----------- Customer name ----------- */} @@ -96,11 +135,34 @@ function CreditNoteFormHeaderFields() { label={} labelInfo={} inline={true} - className={classNames('form-group--credit_note_number', CLASSES.FILL)} + className={classNames( + 'form-group--credit_note_number', + CLASSES.FILL, + )} intent={inputIntent({ error, touched })} helperText={} > - + + + , + }} + tooltip={true} + tooltipProps={{ + content: ( + + ), + position: Position.BOTTOM_LEFT, + }} + /> + )} @@ -121,5 +183,11 @@ function CreditNoteFormHeaderFields() {
); } - -export default CreditNoteFormHeaderFields; +export default compose( + withDialogActions, + withSettings(({ creditNoteSettings }) => ({ + creditAutoIncrement: creditNoteSettings?.autoIncrement, + creditNextNumber: creditNoteSettings?.nextNumber, + creditNumberPrefix: creditNoteSettings?.numberPrefix, + })), +)(CreditNoteFormHeaderFields); diff --git a/src/containers/Sales/CreditNotes/CreditNoteForm/CreditNoteFormProvider.js b/src/containers/Sales/CreditNotes/CreditNoteForm/CreditNoteFormProvider.js index 0c33c8868..210875d51 100644 --- a/src/containers/Sales/CreditNotes/CreditNoteForm/CreditNoteFormProvider.js +++ b/src/containers/Sales/CreditNotes/CreditNoteForm/CreditNoteFormProvider.js @@ -10,6 +10,7 @@ import { useEditCreditNote, useItems, useCustomers, + useSettingsCreditNotes, } from 'hooks/query'; const CreditNoteFormContext = React.createContext(); @@ -40,6 +41,9 @@ function CreditNoteFormProvider({ creditNoteId, ...props }) { }, ); + // Handle fetching settings. + useSettingsCreditNotes(); + // Create and edit credit note mutations. const { mutateAsync: createCreditNoteMutate } = useCreateCreditNote(); const { mutateAsync: editCreditNoteMutate } = useEditCreditNote(); diff --git a/src/containers/Sales/CreditNotes/CreditNoteForm/utils.js b/src/containers/Sales/CreditNotes/CreditNoteForm/utils.js index d2992cf9d..807029d9f 100644 --- a/src/containers/Sales/CreditNotes/CreditNoteForm/utils.js +++ b/src/containers/Sales/CreditNotes/CreditNoteForm/utils.js @@ -6,8 +6,10 @@ import { defaultFastFieldShouldUpdate, transformToForm, repeatValue, + transactionNumber, orderingLinesIndexes, } from 'utils'; +import { useFormikContext } from 'formik'; import { updateItemsEntriesTotal, @@ -115,3 +117,15 @@ export const entriesFieldShouldUpdate = (newProps, oldProps) => { defaultFastFieldShouldUpdate(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]); +}; diff --git a/src/containers/Sales/CreditNotes/CreditNotesLanding/CreditNotesActionsBar.js b/src/containers/Sales/CreditNotes/CreditNotesLanding/CreditNotesActionsBar.js index be363cfc4..cb1b864c6 100644 --- a/src/containers/Sales/CreditNotes/CreditNotesLanding/CreditNotesActionsBar.js +++ b/src/containers/Sales/CreditNotes/CreditNotesLanding/CreditNotesActionsBar.js @@ -65,7 +65,7 @@ function CreditNotesActionsBar({ // Handle table row size change. const handleTableRowSizeChange = (size) => { - addSetting('creditNotes', 'tableSize', size); + addSetting('creditNote', 'tableSize', size); }; return ( diff --git a/src/hooks/query/creditNote.js b/src/hooks/query/creditNote.js index e107005fd..3095f53c7 100644 --- a/src/hooks/query/creditNote.js +++ b/src/hooks/query/creditNote.js @@ -21,6 +21,9 @@ const commonInvalidateQueries = (queryClient) => { queryClient.invalidateQueries(t.ACCOUNTS); queryClient.invalidateQueries(t.ACCOUNT); + // Invalidate settings. + queryClient.invalidateQueries([t.SETTING, t.SETTING_CREDIT_NOTES]); + // Invalidate financial reports. queryClient.invalidateQueries(t.FINANCIAL_REPORT); }; @@ -95,8 +98,6 @@ const transformInvoices = (res) => ({ * Retrieve credit notes list with pagination meta. */ export function useCreditNotes(query, props) { - - return useRequestQuery( [t.CREDIT_NOTES, query], { method: 'get', url: 'sales/credit_notes', params: query },