mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 21:00:31 +00:00
feat : inventory adjustments.
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
import React from 'react';
|
||||
import { FastField, ErrorMessage } from 'formik';
|
||||
import { FormGroup, InputGroup } from '@blueprintjs/core';
|
||||
import { Row, Col, FieldRequiredHint } from 'components';
|
||||
import { inputIntent } from 'utils';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
|
||||
function DecrementAdjustmentFields() {
|
||||
return (
|
||||
<Row>
|
||||
{/*------------ Quantity on hand -----------*/}
|
||||
<Col sm={3}>
|
||||
<FastField name={'quantity'}>
|
||||
{({ field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'qty_on_hand'} />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="quantity" />}
|
||||
>
|
||||
<InputGroup disabled={true} medium={'true'} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
{/*------------ Decrement -----------*/}
|
||||
<Col sm={2}>
|
||||
<FastField name={'decrement'}>
|
||||
{({ field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'decrement'} />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="decrement" />}
|
||||
fill={true}
|
||||
>
|
||||
<InputGroup medium={'true'} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
{/*------------ New quantity -----------*/}
|
||||
<Col sm={4}>
|
||||
<FastField name={'new_quantity'}>
|
||||
{({ field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'new_quantity'} />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="new_quantity" />}
|
||||
>
|
||||
<InputGroup medium={'true'} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
||||
export default DecrementAdjustmentFields;
|
||||
@@ -0,0 +1,72 @@
|
||||
import React from 'react';
|
||||
import { FastField, ErrorMessage } 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';
|
||||
|
||||
function IncrementAdjustmentFields() {
|
||||
return (
|
||||
<Row>
|
||||
{/*------------ Quantity on hand -----------*/}
|
||||
<Col sm={3}>
|
||||
<FastField name={'quantity'}>
|
||||
{({ field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'qty_on_hand'} />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="quantity" />}
|
||||
>
|
||||
<InputGroup disabled={true} medium={'true'} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
{/*------------ Increment -----------*/}
|
||||
<Col sm={2}>
|
||||
<FastField name={'increment'}>
|
||||
{({ field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'increment'} />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="increment" />}
|
||||
fill={true}
|
||||
>
|
||||
<InputGroup medium={'true'} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
{/*------------ Cost -----------*/}
|
||||
<Col sm={2}>
|
||||
<FastField name={'cost'}>
|
||||
{({ field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'cost'} />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="cost" />}
|
||||
>
|
||||
<InputGroup medium={'true'} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
{/*------------ New quantity -----------*/}
|
||||
<Col sm={4}>
|
||||
<FastField name={'new_quantity'}>
|
||||
{({ field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'new_quantity'} />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="new_quantity" />}
|
||||
>
|
||||
<InputGroup medium={'true'} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
||||
export default IncrementAdjustmentFields;
|
||||
@@ -0,0 +1,27 @@
|
||||
import * as Yup from 'yup';
|
||||
import { formatMessage } from 'services/intl';
|
||||
import { DATATYPES_LENGTH } from 'common/dataTypes';
|
||||
|
||||
const Schema = Yup.object().shape({
|
||||
date: Yup.date()
|
||||
.required()
|
||||
.label(formatMessage({ id: 'date' })),
|
||||
type: Yup.number().required(),
|
||||
adjustment_account_id: Yup.number().required(),
|
||||
reason: Yup.string()
|
||||
.required()
|
||||
.label(formatMessage({ id: 'reason' })),
|
||||
quantity: Yup.number().when(['type'], {
|
||||
is: (type) => type,
|
||||
then: Yup.number().required(),
|
||||
}),
|
||||
cost: Yup.number().when(['type'], {
|
||||
is: (type) => type,
|
||||
then: Yup.number().required(),
|
||||
}),
|
||||
reference_no: Yup.string(),
|
||||
new_quantity: Yup.number(),
|
||||
description: Yup.string().min(3).max(DATATYPES_LENGTH.TEXT).nullable().trim(),
|
||||
});
|
||||
|
||||
export const CreateInventoryAdjustmentFormSchema = Schema;
|
||||
@@ -0,0 +1,103 @@
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import { Formik } 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 { CreateInventoryAdjustmentFormSchema } from './InventoryAdjustmentForm.schema';
|
||||
import InventoryAdjustmentFormDialogFields from './InventoryAdjustmentFormDialogFields';
|
||||
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
const defaultInitialValues = {
|
||||
date: moment(new Date()).format('YYYY-MM-DD'),
|
||||
type: 'decrement',
|
||||
adjustment_account_id: '',
|
||||
reason: '',
|
||||
reference_no: '',
|
||||
description: '',
|
||||
};
|
||||
|
||||
/**
|
||||
* Inventory adjustment form dialog content.
|
||||
*/
|
||||
function InventoryAdjustmentFormDialogContent({
|
||||
// #withDialogActions
|
||||
closeDialog,
|
||||
|
||||
// #withAccountsActions
|
||||
requestFetchAccounts,
|
||||
|
||||
// #ownProp
|
||||
dialogName,
|
||||
action,
|
||||
}) {
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
// Fetches accounts list.
|
||||
const fetchAccountsList = useQuery('accounts-list', () =>
|
||||
requestFetchAccounts(),
|
||||
);
|
||||
|
||||
const initialValues = useMemo(
|
||||
() => ({
|
||||
...defaultInitialValues,
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
// Handles the form submit.
|
||||
const handleFormSubmit = (values, { setSubmitting, setErrors }) => {
|
||||
const form = { ...values };
|
||||
|
||||
const onSuccess = ({ response }) => {
|
||||
closeDialog(dialogName);
|
||||
queryCache.invalidateQueries('accounts-list');
|
||||
|
||||
AppToaster.show({
|
||||
message: formatMessage({
|
||||
id: 'the_make_adjustment_has_been_successfully_created',
|
||||
}),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
};
|
||||
const onError = (error) => {
|
||||
setSubmitting(false);
|
||||
};
|
||||
//requestInventoryAdjustment
|
||||
};
|
||||
|
||||
// Handles dialog close.
|
||||
const handleClose = useCallback(() => {
|
||||
closeDialog(dialogName);
|
||||
}, [closeDialog, dialogName]);
|
||||
|
||||
return (
|
||||
<DialogContent>
|
||||
<Formik
|
||||
validationSchema={CreateInventoryAdjustmentFormSchema}
|
||||
initialValues={initialValues}
|
||||
onSubmit={handleFormSubmit}
|
||||
>
|
||||
<InventoryAdjustmentFormDialogFields
|
||||
dialogName={dialogName}
|
||||
onClose={handleClose}
|
||||
/>
|
||||
</Formik>
|
||||
</DialogContent>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withDialogActions)(InventoryAdjustmentFormDialogContent);
|
||||
@@ -0,0 +1,200 @@
|
||||
import React from 'react';
|
||||
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 { DateInput } from '@blueprintjs/datetime';
|
||||
import { ListSelect, Choose, If, FieldRequiredHint } from 'components';
|
||||
import {
|
||||
inputIntent,
|
||||
momentFormatter,
|
||||
tansformDateValue,
|
||||
handleDateChange,
|
||||
} from 'utils';
|
||||
import { CLASSES } from 'common/classes';
|
||||
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';
|
||||
|
||||
/**
|
||||
* Inventory adjustment form dialogs fields.
|
||||
*/
|
||||
function InventoryAdjustmentFormDialogFields({
|
||||
// #ownProps
|
||||
onClose,
|
||||
|
||||
//# withAccount
|
||||
accountsList,
|
||||
}) {
|
||||
const { values, isSubmitting } = useFormikContext();
|
||||
|
||||
return (
|
||||
<Form>
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
{/*------------ Date -----------*/}
|
||||
<FastField name={'date'}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'date'} />}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="date" />}
|
||||
minimal={true}
|
||||
className={classNames(CLASSES.FILL)}
|
||||
>
|
||||
<DateInput
|
||||
{...momentFormatter('YYYY/MM/DD')}
|
||||
onChange={handleDateChange((formattedDate) => {
|
||||
form.setFieldValue('date', formattedDate);
|
||||
})}
|
||||
value={tansformDateValue(value)}
|
||||
popoverProps={{
|
||||
position: Position.BOTTOM,
|
||||
minimal: true,
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
|
||||
{/*------------ Adjustment type -----------*/}
|
||||
<FastField name={'type'}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'adjustment_type'} />}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
helperText={<ErrorMessage name="type" />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
className={classNames(CLASSES.FILL)}
|
||||
>
|
||||
<ListSelect
|
||||
items={adjustmentType}
|
||||
onItemSelect={(type) => {
|
||||
console.log(type.value, 'EE');
|
||||
form.setFieldValue('type', type.value);
|
||||
}}
|
||||
selectedItem={value}
|
||||
selectedItemProp={'value'}
|
||||
labelProp={'name'}
|
||||
popoverProps={{ minimal: true }}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
<Choose>
|
||||
<Choose.When condition={values.type === 'decrement'}>
|
||||
<DecrementAdjustmentFields />
|
||||
</Choose.When>
|
||||
<Choose.When condition={values.type === 'increment'}>
|
||||
<IncrementAdjustmentFields />
|
||||
</Choose.When>
|
||||
</Choose>
|
||||
|
||||
{/*------------ Reason -----------*/}
|
||||
<FastField name={'reason'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'reason'} />}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="reason" />}
|
||||
>
|
||||
<InputGroup fill={true} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
{/*------------ Adjustment account -----------*/}
|
||||
<FastField name={'adjustment_account_id'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'adjustment_account'} />}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="reason" />}
|
||||
>
|
||||
<AccountsSuggestField
|
||||
accounts={accountsList}
|
||||
onAccountSelected={(item) =>
|
||||
form.setFieldValue('adjustment_account_id', item)
|
||||
}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
{/*------------ Reference -----------*/}
|
||||
<FastField name={'reference_no'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'reference_no'} />}
|
||||
className={classNames(CLASSES.FILL)}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="reference_no" />}
|
||||
>
|
||||
<InputGroup {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
{/*------------ description -----------*/}
|
||||
<FastField name={'description'}>
|
||||
{({ field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'description'} />}
|
||||
className={'form-group--description'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'description'} />}
|
||||
>
|
||||
<TextArea growVertically={true} large={true} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</div>
|
||||
<div className={Classes.DIALOG_FOOTER}>
|
||||
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
||||
<Button onClick={onClose} style={{ minWidth: '75px' }}>
|
||||
<T id={'close'} />
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
disabled={isSubmitting}
|
||||
style={{ minWidth: '75px' }}
|
||||
type="submit"
|
||||
>
|
||||
{<T id={'save_as_draft'} />}
|
||||
</Button>
|
||||
<Button
|
||||
intent={Intent.PRIMARY}
|
||||
disabled={isSubmitting}
|
||||
style={{ minWidth: '75px' }}
|
||||
type="submit"
|
||||
>
|
||||
{<T id={'make_adjustment'} />}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAccounts(({ accountsList }) => ({
|
||||
accountsList,
|
||||
})),
|
||||
)(InventoryAdjustmentFormDialogFields);
|
||||
@@ -0,0 +1,37 @@
|
||||
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 'redux';
|
||||
|
||||
const InventoryAdjustmentFormDialogContent = lazy(() =>
|
||||
import('./InventoryAdjustmentFormDialogContent'),
|
||||
);
|
||||
|
||||
/**
|
||||
* Inventory adjustments form dialog.
|
||||
*/
|
||||
function InventoryAdjustmentFormDialog({
|
||||
dialogName,
|
||||
payload = { action: '', id: null },
|
||||
isOpen,
|
||||
}) {
|
||||
return (
|
||||
<Dialog
|
||||
name={dialogName}
|
||||
title={<T id={'make_adjustment'} />}
|
||||
isOpen={isOpen}
|
||||
canEscapeJeyClose={true}
|
||||
autoFocus={true}
|
||||
>
|
||||
<DialogSuspense>
|
||||
<InventoryAdjustmentFormDialogContent
|
||||
dialogName={dialogName}
|
||||
action={payload.action}
|
||||
/>
|
||||
</DialogSuspense>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withDialogRedux())(InventoryAdjustmentFormDialog);
|
||||
Reference in New Issue
Block a user