diff --git a/client/src/common/numberFormatsOptions.js b/client/src/common/numberFormatsOptions.js new file mode 100644 index 000000000..d39845288 --- /dev/null +++ b/client/src/common/numberFormatsOptions.js @@ -0,0 +1,18 @@ +export const moneyFormat = [ + { id: 'total', text: 'Total rows' }, + { id: 'always', text: 'Always' }, + { id: 'none', text: 'None' }, +]; + +export const negativeFormat = [ + { id: 'parentheses', text: 'Parentheses ($1000)' }, + { id: 'mines', text: 'Minus -$1000' }, +]; + +export const decimalPlaces = [ + { text: '1 Decimals', label: '$0.1', id: 1 }, + { text: '2 Decimals', label: '$0.01', id: 2 }, + { text: '3 Decimals', label: '$0.001', id: 3 }, + { text: '4 Decimals', label: '$0.0001', id: 4 }, + { text: '5 Decimals', label: '$0.00001', id: 5 }, +]; diff --git a/client/src/components/AccountsTypesSelect.js b/client/src/components/AccountsTypesSelect.js index 1803f49f6..69782bab7 100644 --- a/client/src/components/AccountsTypesSelect.js +++ b/client/src/components/AccountsTypesSelect.js @@ -39,7 +39,7 @@ export default function AccountsTypesSelect({ items={items} selectedItemProp={'id'} selectedItem={selectedTypeId} - labelProp={'label'} + textProp={'label'} defaultText={defaultSelectText} onItemSelect={handleItemSelected} itemPredicate={filterAccountTypeItems} diff --git a/client/src/components/CategoriesSelectList.js b/client/src/components/CategoriesSelectList.js index 541c2d813..52bf07a14 100644 --- a/client/src/components/CategoriesSelectList.js +++ b/client/src/components/CategoriesSelectList.js @@ -44,7 +44,7 @@ export default function CategoriesSelectList({ items={categoriesList} selectedItemProp={'id'} selectedItem={selecetedCategoryId} - labelProp={'name'} + textProp={'name'} defaultText={defaultSelectText} onItemSelect={handleItemCategorySelected} itemPredicate={filterItemCategory} diff --git a/client/src/components/DisplayNameList.js b/client/src/components/DisplayNameList.js index eb0cb28b3..cb09cb4ac 100644 --- a/client/src/components/DisplayNameList.js +++ b/client/src/components/DisplayNameList.js @@ -34,7 +34,7 @@ export default function DisplayNameList({ ); diff --git a/client/src/components/ListSelect.js b/client/src/components/ListSelect.js index c0aedf686..ebbfb5479 100644 --- a/client/src/components/ListSelect.js +++ b/client/src/components/ListSelect.js @@ -10,6 +10,7 @@ export default function ListSelect({ defaultText, noResultsText = , isLoading = false, + textProp, labelProp, selectedItem, @@ -52,8 +53,9 @@ export default function ListSelect({ const itemRenderer = (item, { handleClick, modifiers, query }) => { return ( ); @@ -77,7 +79,7 @@ export default function ListSelect({ )} > + + + + + ); +} diff --git a/client/src/components/NumberFormats/NumberFormats.schema.js b/client/src/components/NumberFormats/NumberFormats.schema.js new file mode 100644 index 000000000..4b67fa97b --- /dev/null +++ b/client/src/components/NumberFormats/NumberFormats.schema.js @@ -0,0 +1,15 @@ +import * as Yup from 'yup'; +import { DATATYPES_LENGTH } from 'common/dataTypes'; +import { defaultTo } from 'lodash'; + + +const Schema = Yup.object().shape({ + format_money: Yup.string(), + show_zero: Yup.boolean(), + show_in_red: Yup.boolean(), + divide_on_1000: Yup.boolean(), + negative_format: Yup.string(), + precision: Yup.string(), +}); + +export const CreateNumberFormateSchema = Schema; diff --git a/client/src/components/NumberFormats/index.js b/client/src/components/NumberFormats/index.js new file mode 100644 index 000000000..1d1a5a87a --- /dev/null +++ b/client/src/components/NumberFormats/index.js @@ -0,0 +1,42 @@ +import React, { useState, useCallback, useMemo } from 'react'; +import { Classes } from '@blueprintjs/core'; +import { Formik } from 'formik'; +import classNames from 'classnames'; +import NumberFormatFields from './NumberFormatFields'; + +import { compose } from 'utils'; + +/** + * Number format form popover content. + */ +function NumberFormats() { + const initialValues = useMemo( + () => ({ + format_money: '', + show_zero: '', + show_in_red: '', + divide_on_1000: '', + negative_format: '', + precision: '', + }), + [], + ); + // Handle cancel button click. + const handleCancelClick = useCallback(() => {}, []); + + // Handle form submit. + const handleFormSubmit = (values, { setSubmitting }) => { + setSubmitting(true); + const form = { ...values }; + }; + + return ( +
+ + + +
+ ); +} + +export default NumberFormats; diff --git a/client/src/components/PaymentReceiveListField.js b/client/src/components/PaymentReceiveListField.js index 4c9f828ba..6b6b3a34d 100644 --- a/client/src/components/PaymentReceiveListField.js +++ b/client/src/components/PaymentReceiveListField.js @@ -29,7 +29,7 @@ function PaymentReceiveListField({ onItemSelect={onInvoiceSelect} selectedItem={`${selectedInvoiceId}`} selectedItemProp={'id'} - labelProp={'name'} + textProp={'name'} defaultText={defaultSelectText} /> ); diff --git a/client/src/components/SalutationList.js b/client/src/components/SalutationList.js index 9b7e1d660..96b881589 100644 --- a/client/src/components/SalutationList.js +++ b/client/src/components/SalutationList.js @@ -13,7 +13,7 @@ export default function SalutationList({ {/*------------ Quantity on hand -----------*/} - + {({ field, meta: { error, touched } }) => ( } intent={inputIntent({ error, touched })} - helperText={} + helperText={} > @@ -24,15 +25,24 @@ function DecrementAdjustmentFields() { {/*------------ Decrement -----------*/} - - {({ field, meta: { error, touched } }) => ( + + {({ + form: { values, setFieldValue }, + field, + meta: { error, touched }, + }) => ( } intent={inputIntent({ error, touched })} - helperText={} + helperText={} fill={true} > - + { + setFieldValue('new_quantity', decrementCalc(values, event)); + }} + /> )} @@ -40,13 +50,22 @@ function DecrementAdjustmentFields() { {/*------------ New quantity -----------*/} - {({ field, meta: { error, touched } }) => ( + {({ + form: { values, setFieldValue }, + field, + meta: { error, touched }, + }) => ( } intent={inputIntent({ error, touched })} helperText={} > - + { + setFieldValue('quantity', decrementCalc(values, event)); + }} + /> )} diff --git a/client/src/containers/Dialogs/InventoryAdjustmentFormDialog/IncrementAdjustmentFields.js b/client/src/containers/Dialogs/InventoryAdjustmentFormDialog/IncrementAdjustmentFields.js index 4a667e94d..286bea519 100644 --- a/client/src/containers/Dialogs/InventoryAdjustmentFormDialog/IncrementAdjustmentFields.js +++ b/client/src/containers/Dialogs/InventoryAdjustmentFormDialog/IncrementAdjustmentFields.js @@ -1,21 +1,22 @@ import React from 'react'; -import { FastField, ErrorMessage } from 'formik'; +import { FastField, ErrorMessage, useFormikContext } from 'formik'; import { FormGroup, InputGroup, Intent } from '@blueprintjs/core'; import { Row, Col, FieldRequiredHint } from 'components'; import { inputIntent } from 'utils'; import { FormattedMessage as T } from 'react-intl'; +import { decrementCalc, incrementCalc } from './utils'; function IncrementAdjustmentFields() { return ( {/*------------ Quantity on hand -----------*/} - + {({ field, meta: { error, touched } }) => ( } intent={inputIntent({ error, touched })} - helperText={} + helperText={} > @@ -24,15 +25,24 @@ function IncrementAdjustmentFields() { {/*------------ Increment -----------*/} - - {({ field, meta: { error, touched } }) => ( + + {({ + form: { values, setFieldValue }, + field, + meta: { error, touched }, + }) => ( } intent={inputIntent({ error, touched })} - helperText={} + helperText={} fill={true} > - + { + setFieldValue('new_quantity', incrementCalc(values, event)); + }} + /> )} @@ -54,13 +64,22 @@ function IncrementAdjustmentFields() { {/*------------ New quantity -----------*/} - {({ field, meta: { error, touched } }) => ( + {({ + form: { values, setFieldValue }, + field, + meta: { error, touched }, + }) => ( } intent={inputIntent({ error, touched })} helperText={} > - + { + setFieldValue('quantity', decrementCalc(values, event)); + }} + /> )} diff --git a/client/src/containers/Dialogs/InventoryAdjustmentFormDialog/InventoryAdjustmentFloatingActions.js b/client/src/containers/Dialogs/InventoryAdjustmentFormDialog/InventoryAdjustmentFloatingActions.js new file mode 100644 index 000000000..1665e8adf --- /dev/null +++ b/client/src/containers/Dialogs/InventoryAdjustmentFormDialog/InventoryAdjustmentFloatingActions.js @@ -0,0 +1,51 @@ +import React from 'react'; +import { Intent, Button, Classes } from '@blueprintjs/core'; +import { useFormikContext } from 'formik'; +import { FormattedMessage as T } from 'react-intl'; +import { saveInvoke } from 'utils'; + +export default function InventoryAdjustmentFloatingActions({ + onCloseClick, + onSubmitClick, +}) { + const { isSubmitting } = useFormikContext(); + + const handleSubmitDraftBtnClick = (event) => { + saveInvoke(onSubmitClick, event, { + publish: false, + }); + }; + const handleSubmitMakeAdjustmentBtnClick = (event) => { + saveInvoke(onSubmitClick, event, { + publish: true, + }); + }; + + return ( +
+
+ + + + +
+
+ ); +} diff --git a/client/src/containers/Dialogs/InventoryAdjustmentFormDialog/InventoryAdjustmentForm.schema.js b/client/src/containers/Dialogs/InventoryAdjustmentFormDialog/InventoryAdjustmentForm.schema.js index 7b0048f2d..25fe6c687 100644 --- a/client/src/containers/Dialogs/InventoryAdjustmentFormDialog/InventoryAdjustmentForm.schema.js +++ b/client/src/containers/Dialogs/InventoryAdjustmentFormDialog/InventoryAdjustmentForm.schema.js @@ -6,22 +6,25 @@ const Schema = Yup.object().shape({ date: Yup.date() .required() .label(formatMessage({ id: 'date' })), - type: Yup.number().required(), - adjustment_account_id: Yup.number().required(), + type: Yup.string().required(), + adjustment_account_id: Yup.string().required(), + item_id: Yup.number().required(), reason: Yup.string() .required() .label(formatMessage({ id: 'reason' })), - quantity: Yup.number().when(['type'], { - is: (type) => type, - then: Yup.number().required(), - }), + quantity_on_hand: Yup.number() + .min(0) + .required() + .label(formatMessage({ id: 'qty' })), + quantity: Yup.number().integer().max(Yup.ref('quantity_on_hand')).required(), cost: Yup.number().when(['type'], { is: (type) => type, - then: Yup.number().required(), + then: Yup.number(), }), reference_no: Yup.string(), - new_quantity: Yup.number(), + new_quantity: Yup.number().min(Yup.ref('quantity')).required(), description: Yup.string().min(3).max(DATATYPES_LENGTH.TEXT).nullable().trim(), + publish: Yup.boolean(), }); export const CreateInventoryAdjustmentFormSchema = Schema; diff --git a/client/src/containers/Dialogs/InventoryAdjustmentFormDialog/InventoryAdjustmentFormDialogContent.js b/client/src/containers/Dialogs/InventoryAdjustmentFormDialog/InventoryAdjustmentFormDialogContent.js index 4887fbc00..c66401cfb 100644 --- a/client/src/containers/Dialogs/InventoryAdjustmentFormDialog/InventoryAdjustmentFormDialogContent.js +++ b/client/src/containers/Dialogs/InventoryAdjustmentFormDialog/InventoryAdjustmentFormDialogContent.js @@ -1,33 +1,31 @@ -import React, { useCallback, useMemo } from 'react'; +import React, { useState, useCallback, useMemo } from 'react'; import { Intent } from '@blueprintjs/core'; -import { Formik } from 'formik'; +import { Formik, Form } from 'formik'; import { FormattedMessage as T, useIntl } from 'react-intl'; import { useQuery, queryCache } from 'react-query'; import moment from 'moment'; import { omit } from 'lodash'; -import { - AppToaster, - DialogContent, - Row, - Col, - ListSelect, - IF, -} from 'components'; +import { AppToaster, DialogContent } from 'components'; import { CreateInventoryAdjustmentFormSchema } from './InventoryAdjustmentForm.schema'; import InventoryAdjustmentFormDialogFields from './InventoryAdjustmentFormDialogFields'; - +import InventoryAdjustmentFloatingActions from './InventoryAdjustmentFloatingActions'; import withDialogActions from 'containers/Dialog/withDialogActions'; - +import withInventoryAdjustmentActions from 'containers/Items/withInventoryAdjustmentActions'; import { compose } from 'utils'; const defaultInitialValues = { date: moment(new Date()).format('YYYY-MM-DD'), type: 'decrement', adjustment_account_id: '', + item_id: '', reason: '', + cost: '', + quantity: '', reference_no: '', + quantity_on_hand: '', description: '', + publish: '', }; /** @@ -40,31 +38,38 @@ function InventoryAdjustmentFormDialogContent({ // #withAccountsActions requestFetchAccounts, + // #withInventoryAdjustmentActions + requestSubmitInventoryAdjustment, + // #ownProp + itemDetail, dialogName, - action, }) { const { formatMessage } = useIntl(); + const [submitPayload, setSubmitPayload] = useState({}); // Fetches accounts list. - const fetchAccountsList = useQuery('accounts-list', () => - requestFetchAccounts(), - ); + const fetchAccount = useQuery('accounts-list', () => requestFetchAccounts()); const initialValues = useMemo( () => ({ ...defaultInitialValues, + ...itemDetail, }), [], ); // Handles the form submit. const handleFormSubmit = (values, { setSubmitting, setErrors }) => { - const form = { ...values }; + const form = { + ...omit(values, ['quantity_on_hand', 'new_quantity', 'action']), + publish: submitPayload.publish, + }; const onSuccess = ({ response }) => { closeDialog(dialogName); queryCache.invalidateQueries('accounts-list'); + queryCache.invalidateQueries('items-table'); AppToaster.show({ message: formatMessage({ @@ -76,14 +81,21 @@ function InventoryAdjustmentFormDialogContent({ const onError = (error) => { setSubmitting(false); }; - //requestInventoryAdjustment + requestSubmitInventoryAdjustment({ form }).then(onSuccess).catch(onError); }; // Handles dialog close. - const handleClose = useCallback(() => { + const handleCloseClick = useCallback(() => { closeDialog(dialogName); }, [closeDialog, dialogName]); + const handleSubmitClick = useCallback( + (event, payload) => { + setSubmitPayload({ ...payload }); + }, + [setSubmitPayload], + ); + return ( - +
+ + +
); } -export default compose(withDialogActions)(InventoryAdjustmentFormDialogContent); +export default compose( + withInventoryAdjustmentActions, + withDialogActions, +)(InventoryAdjustmentFormDialogContent); diff --git a/client/src/containers/Dialogs/InventoryAdjustmentFormDialog/InventoryAdjustmentFormDialogFields.js b/client/src/containers/Dialogs/InventoryAdjustmentFormDialog/InventoryAdjustmentFormDialogFields.js index fca2d3b27..c950dc918 100644 --- a/client/src/containers/Dialogs/InventoryAdjustmentFormDialog/InventoryAdjustmentFormDialogFields.js +++ b/client/src/containers/Dialogs/InventoryAdjustmentFormDialog/InventoryAdjustmentFormDialogFields.js @@ -1,22 +1,14 @@ import React from 'react'; +import { FastField, ErrorMessage, useFormikContext } from 'formik'; import { - Form, - FastField, - ErrorMessage, - useFormikContext, - useField, -} from 'formik'; -import { - Button, Classes, FormGroup, InputGroup, - Intent, TextArea, Position, } from '@blueprintjs/core'; import classNames from 'classnames'; -import { FormattedMessage as T } from 'react-intl'; +import { FormattedMessage as T, useIntl } from 'react-intl'; import { DateInput } from '@blueprintjs/datetime'; import { ListSelect, Choose, If, FieldRequiredHint } from 'components'; import { @@ -30,166 +22,149 @@ import adjustmentType from 'common/adjustmentType'; import IncrementAdjustmentFields from './IncrementAdjustmentFields'; import DecrementAdjustmentFields from './DecrementAdjustmentFields'; import AccountsSuggestField from 'components/AccountsSuggestField'; - import withAccounts from 'containers/Accounts/withAccounts'; import { compose } from 'redux'; +import { decrementCalc, incrementCalc, dec } from './utils'; /** * Inventory adjustment form dialogs fields. */ function InventoryAdjustmentFormDialogFields({ - // #ownProps - onClose, - //# withAccount accountsList, }) { - const { values, isSubmitting } = useFormikContext(); + const { values } = useFormikContext(); + const { formatMessage } = useIntl(); return ( -
-
- {/*------------ Date -----------*/} - - {({ form, field: { value }, meta: { error, touched } }) => ( - } - labelInfo={} - intent={inputIntent({ error, touched })} - helperText={} - minimal={true} - className={classNames(CLASSES.FILL)} - > - { - form.setFieldValue('date', formattedDate); - })} - value={tansformDateValue(value)} - popoverProps={{ - position: Position.BOTTOM, - minimal: true, - }} - /> - - )} - - - {/*------------ Adjustment type -----------*/} - - {({ form, field: { value }, meta: { error, touched } }) => ( - } - labelInfo={} - helperText={} - intent={inputIntent({ error, touched })} - className={classNames(CLASSES.FILL)} - > - { - console.log(type.value, 'EE'); - form.setFieldValue('type', type.value); - }} - selectedItem={value} - selectedItemProp={'value'} - labelProp={'name'} - popoverProps={{ minimal: true }} - /> - - )} - - - - - - - - - - - {/*------------ Reason -----------*/} - - {({ form, field, meta: { error, touched } }) => ( - } - labelInfo={} - intent={inputIntent({ error, touched })} - helperText={} - > - - - )} - - {/*------------ Adjustment account -----------*/} - - {({ form, field, meta: { error, touched } }) => ( - } - labelInfo={} - intent={inputIntent({ error, touched })} - helperText={} - > - - form.setFieldValue('adjustment_account_id', item) - } - /> - - )} - - {/*------------ Reference -----------*/} - - {({ form, field, meta: { error, touched } }) => ( - } - className={classNames(CLASSES.FILL)} - intent={inputIntent({ error, touched })} - helperText={} - > - - - )} - - {/*------------ description -----------*/} - - {({ field, meta: { error, touched } }) => ( - } - className={'form-group--description'} - intent={inputIntent({ error, touched })} - helperText={} - > -