# Conflicts:
#	client/src/style/App.scss
This commit is contained in:
a.bouhuolia
2021-01-17 12:20:30 +02:00
41 changed files with 908 additions and 397 deletions

View File

@@ -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 },
];

View File

@@ -39,7 +39,7 @@ export default function AccountsTypesSelect({
items={items} items={items}
selectedItemProp={'id'} selectedItemProp={'id'}
selectedItem={selectedTypeId} selectedItem={selectedTypeId}
labelProp={'label'} textProp={'label'}
defaultText={defaultSelectText} defaultText={defaultSelectText}
onItemSelect={handleItemSelected} onItemSelect={handleItemSelected}
itemPredicate={filterAccountTypeItems} itemPredicate={filterAccountTypeItems}

View File

@@ -44,7 +44,7 @@ export default function CategoriesSelectList({
items={categoriesList} items={categoriesList}
selectedItemProp={'id'} selectedItemProp={'id'}
selectedItem={selecetedCategoryId} selectedItem={selecetedCategoryId}
labelProp={'name'} textProp={'name'}
defaultText={defaultSelectText} defaultText={defaultSelectText}
onItemSelect={handleItemCategorySelected} onItemSelect={handleItemCategorySelected}
itemPredicate={filterItemCategory} itemPredicate={filterItemCategory}

View File

@@ -34,7 +34,7 @@ export default function DisplayNameList({
<ListSelect <ListSelect
items={formatOptions} items={formatOptions}
selectedItemProp={'label'} selectedItemProp={'label'}
labelProp={'label'} textProp={'label'}
defaultText={'Select display name as'} defaultText={'Select display name as'}
filterable={false} filterable={false}
{ ...restProps } { ...restProps }

View File

@@ -158,7 +158,7 @@ function DynamicFilterValueField({
selectedItem={value} selectedItem={value}
selectedItemProp={optionsKey} selectedItemProp={optionsKey}
defaultText={`Select an option`} defaultText={`Select an option`}
labelProp={optionsLabel} textProp={optionsLabel}
buttonProps={{ buttonProps={{
onClick: handleBtnClick onClick: handleBtnClick
}} }}

View File

@@ -75,7 +75,7 @@ function ItemsListField({
onItemSelect={onItemSelect} onItemSelect={onItemSelect}
selectedItem={`${selectedItemId}`} selectedItem={`${selectedItemId}`}
selectedItemProp={'id'} selectedItemProp={'id'}
labelProp={'name'} textProp={'name'}
defaultText={selectedItem ? selectedItem.name : defautlSelectText} defaultText={selectedItem ? selectedItem.name : defautlSelectText}
/> />
); );

View File

@@ -10,6 +10,7 @@ export default function ListSelect({
defaultText, defaultText,
noResultsText = <T id="no_results" />, noResultsText = <T id="no_results" />,
isLoading = false, isLoading = false,
textProp,
labelProp, labelProp,
selectedItem, selectedItem,
@@ -52,8 +53,9 @@ export default function ListSelect({
const itemRenderer = (item, { handleClick, modifiers, query }) => { const itemRenderer = (item, { handleClick, modifiers, query }) => {
return ( return (
<MenuItem <MenuItem
text={item[labelProp]} text={item[textProp]}
key={item[selectedItemProp]} key={item[selectedItemProp]}
label={item[labelProp]}
onClick={handleClick} onClick={handleClick}
/> />
); );
@@ -77,7 +79,7 @@ export default function ListSelect({
)} )}
> >
<Button <Button
text={currentItem ? currentItem[labelProp] : defaultText} text={currentItem ? currentItem[textProp] : defaultText}
loading={isLoading} loading={isLoading}
disabled={disabled} disabled={disabled}
{...buttonProps} {...buttonProps}

View File

@@ -0,0 +1,169 @@
import React from 'react';
import { Form, FastField, ErrorMessage, useFormikContext } from 'formik';
import {
Button,
Classes,
FormGroup,
InputGroup,
Intent,
Checkbox,
} from '@blueprintjs/core';
import { CLASSES } from 'common/classes';
import { Row, Col, ListSelect } from 'components';
import { FormattedMessage as T, useIntl } from 'react-intl';
import { inputIntent } from 'utils';
import {
moneyFormat,
negativeFormat,
decimalPlaces,
} from 'common/numberFormatsOptions';
import classNames from 'classnames';
/**
* Number Formats Fields.
*/
export default function NumberFormatFields({
// #ownProps
onCancelClick,
}) {
const { isSubmitting } = useFormikContext();
return (
<Form>
<div className={'number-format__content'}>
{/*------------ Money formats -----------*/}
<FastField name={'format_money'}>
{({ form, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'money_format'} />}
helperText={<ErrorMessage name="format_money" />}
intent={inputIntent({ error, touched })}
className={classNames(CLASSES.FILL)}
>
<ListSelect
items={moneyFormat}
onItemSelect={(format) => {
form.setFieldValue('format_money', format.name);
}}
filterable={false}
selectedItem={value}
selectedItemProp={'id'}
textProp={'text'}
popoverProps={{ minimal: true, captureDismiss: true }}
/>
</FormGroup>
)}
</FastField>
{/*------------ Negative formats -----------*/}
<FastField name={'negative_format'}>
{({ form, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'negative_format'} />}
helperText={<ErrorMessage name="negative_format" />}
intent={inputIntent({ error, touched })}
className={classNames(CLASSES.FILL)}
>
<ListSelect
items={negativeFormat}
onItemSelect={(format) => {
form.setFieldValue('negative_format', format.name);
}}
filterable={false}
selectedItem={value}
selectedItemProp={'id'}
textProp={'text'}
popoverProps={{ minimal: true, captureDismiss: true }}
/>
</FormGroup>
)}
</FastField>
{/*------------ Decimal places -----------*/}
<FastField name={'precision'}>
{({ form, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'decimal_places'} />}
helperText={<ErrorMessage name="format_money" />}
intent={inputIntent({ error, touched })}
className={classNames(CLASSES.FILL)}
>
<ListSelect
items={decimalPlaces}
onItemSelect={(format) => {
form.setFieldValue('precision', format.key);
}}
filterable={false}
selectedItem={value}
selectedItemProp={'id'}
labelProp={'label'}
textProp={'text'}
popoverProps={{ minimal: true, captureDismiss: true }}
/>
</FormGroup>
)}
</FastField>
{/*------------ show zero -----------*/}
<FastField name={'show_zero'} type={'checkbox'}>
{({ field }) => (
<FormGroup inline={true}>
<Checkbox
inline={true}
label={<T id={'show_zero'} />}
name={'show_zero'}
{...field}
/>
</FormGroup>
)}
</FastField>
{/*------------ show negative in red-----------*/}
<FastField name={'show_in_red'} type={'checkbox'}>
{({ field }) => (
<FormGroup inline={true}>
<Checkbox
inline={true}
label={<T id={'show_negative_in_red'} />}
name={'show_in_red'}
{...field}
/>
</FormGroup>
)}
</FastField>
{/*------------ Divide on 1000 -----------*/}
<FastField name={'divide_on_1000'} type={'checkbox'}>
{({ field }) => (
<FormGroup inline={true}>
<Checkbox
inline={true}
label={<T id={'divide_on_1000'} />}
name={'divide_on_1000'}
{...field}
/>
</FormGroup>
)}
</FastField>
</div>
<div
className={classNames('number-format__footer', Classes.POPOVER_DISMISS)}
>
<Button
className={'mr1'}
onClick={onCancelClick}
small={true}
style={{ minWidth: '75px' }}
>
<T id={'cancel'} />
</Button>
<Button
intent={Intent.PRIMARY}
disabled={isSubmitting}
small={true}
style={{ minWidth: '75px' }}
type="submit"
>
<T id={'run'} />
</Button>
</div>
</Form>
);
}

View File

@@ -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;

View File

@@ -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 (
<div className={'number-format'}>
<Formik initialValues={initialValues} onSubmit={handleFormSubmit}>
<NumberFormatFields onCancelClick={handleCancelClick} />
</Formik>
</div>
);
}
export default NumberFormats;

View File

@@ -29,7 +29,7 @@ function PaymentReceiveListField({
onItemSelect={onInvoiceSelect} onItemSelect={onInvoiceSelect}
selectedItem={`${selectedInvoiceId}`} selectedItem={`${selectedInvoiceId}`}
selectedItemProp={'id'} selectedItemProp={'id'}
labelProp={'name'} textProp={'name'}
defaultText={defaultSelectText} defaultText={defaultSelectText}
/> />
); );

View File

@@ -13,7 +13,7 @@ export default function SalutationList({
<ListSelect <ListSelect
items={items} items={items}
selectedItemProp={'key'} selectedItemProp={'key'}
labelProp={'label'} textProp={'label'}
defaultText={'Salutation'} defaultText={'Salutation'}
filterable={false} filterable={false}
{...restProps} {...restProps}

View File

@@ -1,21 +1,22 @@
import React from 'react'; import React, { useRef } from 'react';
import { FastField, ErrorMessage } from 'formik'; import { FastField, ErrorMessage, useFormikContext } from 'formik';
import { FormGroup, InputGroup } from '@blueprintjs/core'; import { FormGroup, InputGroup, Intent } from '@blueprintjs/core';
import { Row, Col, FieldRequiredHint } from 'components';
import { inputIntent } from 'utils'; import { inputIntent } from 'utils';
import { Row, Col, If, FieldRequiredHint } from 'components';
import { FormattedMessage as T } from 'react-intl'; import { FormattedMessage as T } from 'react-intl';
import { decrementCalc, dec } from './utils';
function DecrementAdjustmentFields() { function DecrementAdjustmentFields() {
return ( return (
<Row> <Row>
{/*------------ Quantity on hand -----------*/} {/*------------ Quantity on hand -----------*/}
<Col sm={3}> <Col sm={3}>
<FastField name={'quantity'}> <FastField name={'quantity_on_hand'}>
{({ field, meta: { error, touched } }) => ( {({ field, meta: { error, touched } }) => (
<FormGroup <FormGroup
label={<T id={'qty_on_hand'} />} label={<T id={'qty_on_hand'} />}
intent={inputIntent({ error, touched })} intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="quantity" />} helperText={<ErrorMessage name="quantity_on_hand" />}
> >
<InputGroup disabled={true} medium={'true'} {...field} /> <InputGroup disabled={true} medium={'true'} {...field} />
</FormGroup> </FormGroup>
@@ -24,15 +25,24 @@ function DecrementAdjustmentFields() {
</Col> </Col>
{/*------------ Decrement -----------*/} {/*------------ Decrement -----------*/}
<Col sm={2}> <Col sm={2}>
<FastField name={'decrement'}> <FastField name={'quantity'}>
{({ field, meta: { error, touched } }) => ( {({
form: { values, setFieldValue },
field,
meta: { error, touched },
}) => (
<FormGroup <FormGroup
label={<T id={'decrement'} />} label={<T id={'decrement'} />}
intent={inputIntent({ error, touched })} intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="decrement" />} helperText={<ErrorMessage name="quantity" />}
fill={true} fill={true}
> >
<InputGroup medium={'true'} {...field} /> <InputGroup
{...field}
onBlur={(event) => {
setFieldValue('new_quantity', decrementCalc(values, event));
}}
/>
</FormGroup> </FormGroup>
)} )}
</FastField> </FastField>
@@ -40,13 +50,22 @@ function DecrementAdjustmentFields() {
{/*------------ New quantity -----------*/} {/*------------ New quantity -----------*/}
<Col sm={4}> <Col sm={4}>
<FastField name={'new_quantity'}> <FastField name={'new_quantity'}>
{({ field, meta: { error, touched } }) => ( {({
form: { values, setFieldValue },
field,
meta: { error, touched },
}) => (
<FormGroup <FormGroup
label={<T id={'new_quantity'} />} label={<T id={'new_quantity'} />}
intent={inputIntent({ error, touched })} intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="new_quantity" />} helperText={<ErrorMessage name="new_quantity" />}
> >
<InputGroup medium={'true'} {...field} /> <InputGroup
{...field}
onBlur={(event) => {
setFieldValue('quantity', decrementCalc(values, event));
}}
/>
</FormGroup> </FormGroup>
)} )}
</FastField> </FastField>

View File

@@ -1,21 +1,22 @@
import React from 'react'; import React from 'react';
import { FastField, ErrorMessage } from 'formik'; import { FastField, ErrorMessage, useFormikContext } from 'formik';
import { FormGroup, InputGroup, Intent } from '@blueprintjs/core'; import { FormGroup, InputGroup, Intent } from '@blueprintjs/core';
import { Row, Col, FieldRequiredHint } from 'components'; import { Row, Col, FieldRequiredHint } from 'components';
import { inputIntent } from 'utils'; import { inputIntent } from 'utils';
import { FormattedMessage as T } from 'react-intl'; import { FormattedMessage as T } from 'react-intl';
import { decrementCalc, incrementCalc } from './utils';
function IncrementAdjustmentFields() { function IncrementAdjustmentFields() {
return ( return (
<Row> <Row>
{/*------------ Quantity on hand -----------*/} {/*------------ Quantity on hand -----------*/}
<Col sm={3}> <Col sm={3}>
<FastField name={'quantity'}> <FastField name={'quantity_on_hand'}>
{({ field, meta: { error, touched } }) => ( {({ field, meta: { error, touched } }) => (
<FormGroup <FormGroup
label={<T id={'qty_on_hand'} />} label={<T id={'qty_on_hand'} />}
intent={inputIntent({ error, touched })} intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="quantity" />} helperText={<ErrorMessage name="quantity_on_hand" />}
> >
<InputGroup disabled={true} medium={'true'} {...field} /> <InputGroup disabled={true} medium={'true'} {...field} />
</FormGroup> </FormGroup>
@@ -24,15 +25,24 @@ function IncrementAdjustmentFields() {
</Col> </Col>
{/*------------ Increment -----------*/} {/*------------ Increment -----------*/}
<Col sm={2}> <Col sm={2}>
<FastField name={'increment'}> <FastField name={'quantity'}>
{({ field, meta: { error, touched } }) => ( {({
form: { values, setFieldValue },
field,
meta: { error, touched },
}) => (
<FormGroup <FormGroup
label={<T id={'increment'} />} label={<T id={'increment'} />}
intent={inputIntent({ error, touched })} intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="increment" />} helperText={<ErrorMessage name="quantity" />}
fill={true} fill={true}
> >
<InputGroup medium={'true'} {...field} /> <InputGroup
{...field}
onBlur={(event) => {
setFieldValue('new_quantity', incrementCalc(values, event));
}}
/>
</FormGroup> </FormGroup>
)} )}
</FastField> </FastField>
@@ -54,13 +64,22 @@ function IncrementAdjustmentFields() {
{/*------------ New quantity -----------*/} {/*------------ New quantity -----------*/}
<Col sm={4}> <Col sm={4}>
<FastField name={'new_quantity'}> <FastField name={'new_quantity'}>
{({ field, meta: { error, touched } }) => ( {({
form: { values, setFieldValue },
field,
meta: { error, touched },
}) => (
<FormGroup <FormGroup
label={<T id={'new_quantity'} />} label={<T id={'new_quantity'} />}
intent={inputIntent({ error, touched })} intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="new_quantity" />} helperText={<ErrorMessage name="new_quantity" />}
> >
<InputGroup medium={'true'} {...field} /> <InputGroup
{...field}
onBlur={(event) => {
setFieldValue('quantity', decrementCalc(values, event));
}}
/>
</FormGroup> </FormGroup>
)} )}
</FastField> </FastField>

View File

@@ -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 (
<div className={Classes.DIALOG_FOOTER}>
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
<Button onClick={onCloseClick} style={{ minWidth: '75px' }}>
<T id={'close'} />
</Button>
<Button
disabled={isSubmitting}
style={{ minWidth: '75px' }}
type="submit"
onClick={handleSubmitDraftBtnClick}
>
{<T id={'save_as_draft'} />}
</Button>
<Button
intent={Intent.PRIMARY}
disabled={isSubmitting}
style={{ minWidth: '75px' }}
type="submit"
onClick={handleSubmitMakeAdjustmentBtnClick}
>
{<T id={'make_adjustment'} />}
</Button>
</div>
</div>
);
}

View File

@@ -6,22 +6,25 @@ const Schema = Yup.object().shape({
date: Yup.date() date: Yup.date()
.required() .required()
.label(formatMessage({ id: 'date' })), .label(formatMessage({ id: 'date' })),
type: Yup.number().required(), type: Yup.string().required(),
adjustment_account_id: Yup.number().required(), adjustment_account_id: Yup.string().required(),
item_id: Yup.number().required(),
reason: Yup.string() reason: Yup.string()
.required() .required()
.label(formatMessage({ id: 'reason' })), .label(formatMessage({ id: 'reason' })),
quantity: Yup.number().when(['type'], { quantity_on_hand: Yup.number()
is: (type) => type, .min(0)
then: Yup.number().required(), .required()
}), .label(formatMessage({ id: 'qty' })),
quantity: Yup.number().integer().max(Yup.ref('quantity_on_hand')).required(),
cost: Yup.number().when(['type'], { cost: Yup.number().when(['type'], {
is: (type) => type, is: (type) => type,
then: Yup.number().required(), then: Yup.number(),
}), }),
reference_no: Yup.string(), 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(), description: Yup.string().min(3).max(DATATYPES_LENGTH.TEXT).nullable().trim(),
publish: Yup.boolean(),
}); });
export const CreateInventoryAdjustmentFormSchema = Schema; export const CreateInventoryAdjustmentFormSchema = Schema;

View File

@@ -1,33 +1,31 @@
import React, { useCallback, useMemo } from 'react'; import React, { useState, useCallback, useMemo } from 'react';
import { Intent } from '@blueprintjs/core'; import { Intent } from '@blueprintjs/core';
import { Formik } from 'formik'; import { Formik, Form } from 'formik';
import { FormattedMessage as T, useIntl } from 'react-intl'; import { FormattedMessage as T, useIntl } from 'react-intl';
import { useQuery, queryCache } from 'react-query'; import { useQuery, queryCache } from 'react-query';
import moment from 'moment'; import moment from 'moment';
import { omit } from 'lodash'; import { omit } from 'lodash';
import { import { AppToaster, DialogContent } from 'components';
AppToaster,
DialogContent,
Row,
Col,
ListSelect,
IF,
} from 'components';
import { CreateInventoryAdjustmentFormSchema } from './InventoryAdjustmentForm.schema'; import { CreateInventoryAdjustmentFormSchema } from './InventoryAdjustmentForm.schema';
import InventoryAdjustmentFormDialogFields from './InventoryAdjustmentFormDialogFields'; import InventoryAdjustmentFormDialogFields from './InventoryAdjustmentFormDialogFields';
import InventoryAdjustmentFloatingActions from './InventoryAdjustmentFloatingActions';
import withDialogActions from 'containers/Dialog/withDialogActions'; import withDialogActions from 'containers/Dialog/withDialogActions';
import withInventoryAdjustmentActions from 'containers/Items/withInventoryAdjustmentActions';
import { compose } from 'utils'; import { compose } from 'utils';
const defaultInitialValues = { const defaultInitialValues = {
date: moment(new Date()).format('YYYY-MM-DD'), date: moment(new Date()).format('YYYY-MM-DD'),
type: 'decrement', type: 'decrement',
adjustment_account_id: '', adjustment_account_id: '',
item_id: '',
reason: '', reason: '',
cost: '',
quantity: '',
reference_no: '', reference_no: '',
quantity_on_hand: '',
description: '', description: '',
publish: '',
}; };
/** /**
@@ -40,31 +38,38 @@ function InventoryAdjustmentFormDialogContent({
// #withAccountsActions // #withAccountsActions
requestFetchAccounts, requestFetchAccounts,
// #withInventoryAdjustmentActions
requestSubmitInventoryAdjustment,
// #ownProp // #ownProp
itemDetail,
dialogName, dialogName,
action,
}) { }) {
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
const [submitPayload, setSubmitPayload] = useState({});
// Fetches accounts list. // Fetches accounts list.
const fetchAccountsList = useQuery('accounts-list', () => const fetchAccount = useQuery('accounts-list', () => requestFetchAccounts());
requestFetchAccounts(),
);
const initialValues = useMemo( const initialValues = useMemo(
() => ({ () => ({
...defaultInitialValues, ...defaultInitialValues,
...itemDetail,
}), }),
[], [],
); );
// Handles the form submit. // Handles the form submit.
const handleFormSubmit = (values, { setSubmitting, setErrors }) => { const handleFormSubmit = (values, { setSubmitting, setErrors }) => {
const form = { ...values }; const form = {
...omit(values, ['quantity_on_hand', 'new_quantity', 'action']),
publish: submitPayload.publish,
};
const onSuccess = ({ response }) => { const onSuccess = ({ response }) => {
closeDialog(dialogName); closeDialog(dialogName);
queryCache.invalidateQueries('accounts-list'); queryCache.invalidateQueries('accounts-list');
queryCache.invalidateQueries('items-table');
AppToaster.show({ AppToaster.show({
message: formatMessage({ message: formatMessage({
@@ -76,14 +81,21 @@ function InventoryAdjustmentFormDialogContent({
const onError = (error) => { const onError = (error) => {
setSubmitting(false); setSubmitting(false);
}; };
//requestInventoryAdjustment requestSubmitInventoryAdjustment({ form }).then(onSuccess).catch(onError);
}; };
// Handles dialog close. // Handles dialog close.
const handleClose = useCallback(() => { const handleCloseClick = useCallback(() => {
closeDialog(dialogName); closeDialog(dialogName);
}, [closeDialog, dialogName]); }, [closeDialog, dialogName]);
const handleSubmitClick = useCallback(
(event, payload) => {
setSubmitPayload({ ...payload });
},
[setSubmitPayload],
);
return ( return (
<DialogContent> <DialogContent>
<Formik <Formik
@@ -91,13 +103,19 @@ function InventoryAdjustmentFormDialogContent({
initialValues={initialValues} initialValues={initialValues}
onSubmit={handleFormSubmit} onSubmit={handleFormSubmit}
> >
<InventoryAdjustmentFormDialogFields <Form>
dialogName={dialogName} <InventoryAdjustmentFormDialogFields dialogName={dialogName} />
onClose={handleClose} <InventoryAdjustmentFloatingActions
onSubmitClick={handleSubmitClick}
onCloseClick={handleCloseClick}
/> />
</Form>
</Formik> </Formik>
</DialogContent> </DialogContent>
); );
} }
export default compose(withDialogActions)(InventoryAdjustmentFormDialogContent); export default compose(
withInventoryAdjustmentActions,
withDialogActions,
)(InventoryAdjustmentFormDialogContent);

View File

@@ -1,22 +1,14 @@
import React from 'react'; import React from 'react';
import { FastField, ErrorMessage, useFormikContext } from 'formik';
import { import {
Form,
FastField,
ErrorMessage,
useFormikContext,
useField,
} from 'formik';
import {
Button,
Classes, Classes,
FormGroup, FormGroup,
InputGroup, InputGroup,
Intent,
TextArea, TextArea,
Position, Position,
} from '@blueprintjs/core'; } from '@blueprintjs/core';
import classNames from 'classnames'; 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 { DateInput } from '@blueprintjs/datetime';
import { ListSelect, Choose, If, FieldRequiredHint } from 'components'; import { ListSelect, Choose, If, FieldRequiredHint } from 'components';
import { import {
@@ -30,24 +22,21 @@ import adjustmentType from 'common/adjustmentType';
import IncrementAdjustmentFields from './IncrementAdjustmentFields'; import IncrementAdjustmentFields from './IncrementAdjustmentFields';
import DecrementAdjustmentFields from './DecrementAdjustmentFields'; import DecrementAdjustmentFields from './DecrementAdjustmentFields';
import AccountsSuggestField from 'components/AccountsSuggestField'; import AccountsSuggestField from 'components/AccountsSuggestField';
import withAccounts from 'containers/Accounts/withAccounts'; import withAccounts from 'containers/Accounts/withAccounts';
import { compose } from 'redux'; import { compose } from 'redux';
import { decrementCalc, incrementCalc, dec } from './utils';
/** /**
* Inventory adjustment form dialogs fields. * Inventory adjustment form dialogs fields.
*/ */
function InventoryAdjustmentFormDialogFields({ function InventoryAdjustmentFormDialogFields({
// #ownProps
onClose,
//# withAccount //# withAccount
accountsList, accountsList,
}) { }) {
const { values, isSubmitting } = useFormikContext(); const { values } = useFormikContext();
const { formatMessage } = useIntl();
return ( return (
<Form>
<div className={Classes.DIALOG_BODY}> <div className={Classes.DIALOG_BODY}>
{/*------------ Date -----------*/} {/*------------ Date -----------*/}
<FastField name={'date'}> <FastField name={'date'}>
@@ -88,12 +77,18 @@ function InventoryAdjustmentFormDialogFields({
<ListSelect <ListSelect
items={adjustmentType} items={adjustmentType}
onItemSelect={(type) => { onItemSelect={(type) => {
console.log(type.value, 'EE');
form.setFieldValue('type', type.value); form.setFieldValue('type', type.value);
type?.value == 'increment'
? form.setFieldValue('new_quantity', incrementCalc(values))
: form.setFieldValue(
'new_quantity',
values.quantity_on_hand - values.quantity,
);
}} }}
filterable={false}
selectedItem={value} selectedItem={value}
selectedItemProp={'value'} selectedItemProp={'value'}
labelProp={'name'} textProp={'name'}
popoverProps={{ minimal: true }} popoverProps={{ minimal: true }}
/> />
</FormGroup> </FormGroup>
@@ -107,7 +102,6 @@ function InventoryAdjustmentFormDialogFields({
<IncrementAdjustmentFields /> <IncrementAdjustmentFields />
</Choose.When> </Choose.When>
</Choose> </Choose>
{/*------------ Reason -----------*/} {/*------------ Reason -----------*/}
<FastField name={'reason'}> <FastField name={'reason'}>
{({ form, field, meta: { error, touched } }) => ( {({ form, field, meta: { error, touched } }) => (
@@ -133,8 +127,13 @@ function InventoryAdjustmentFormDialogFields({
<AccountsSuggestField <AccountsSuggestField
accounts={accountsList} accounts={accountsList}
onAccountSelected={(item) => onAccountSelected={(item) =>
form.setFieldValue('adjustment_account_id', item) form.setFieldValue('adjustment_account_id', item.id)
} }
inputProps={{
placeholder: formatMessage({
id: 'select_adjustment_account',
}),
}}
/> />
</FormGroup> </FormGroup>
)} )}
@@ -166,30 +165,6 @@ function InventoryAdjustmentFormDialogFields({
)} )}
</FastField> </FastField>
</div> </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>
); );
} }

View File

@@ -27,7 +27,7 @@ function InventoryAdjustmentFormDialog({
<DialogSuspense> <DialogSuspense>
<InventoryAdjustmentFormDialogContent <InventoryAdjustmentFormDialogContent
dialogName={dialogName} dialogName={dialogName}
action={payload.action} itemDetail={payload}
/> />
</DialogSuspense> </DialogSuspense>
</Dialog> </Dialog>

View File

@@ -0,0 +1,11 @@
export const decrementCalc = ({ quantity_on_hand, type }, e) => {
if (type == 'decrement') {
return parseInt(quantity_on_hand, 10) - parseInt(e.currentTarget.value, 10);
} else {
return e.currentTarget.value - quantity_on_hand;
}
};
export const incrementCalc = ({ quantity_on_hand, quantity }, e) => {
return parseInt(quantity_on_hand, 10) + parseInt(quantity, 10);
};

View File

@@ -1,4 +1,4 @@
import React from 'react'; import React, { useState } from 'react';
import { import {
NavbarGroup, NavbarGroup,
Button, Button,
@@ -13,6 +13,7 @@ import classNames from 'classnames';
import Icon from 'components/Icon'; import Icon from 'components/Icon';
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar'; import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
import NumberFormats from 'components/NumberFormats';
import { compose } from 'utils'; import { compose } from 'utils';
import withBalanceSheetDetail from './withBalanceSheetDetail'; import withBalanceSheetDetail from './withBalanceSheetDetail';
@@ -72,6 +73,18 @@ function BalanceSheetActionsBar({
icon={<Icon icon="filter-16" iconSize={16} />} icon={<Icon icon="filter-16" iconSize={16} />}
/> />
</Popover> </Popover>
<Popover
content={<NumberFormats />}
minimal={true}
interactionKind={PopoverInteractionKind.CLICK}
position={Position.BOTTOM_LEFT}
>
<Button
className={classNames(Classes.MINIMAL, 'button--filter')}
text={<T id={'format'} />}
icon={<Icon icon="filter-16" iconSize={16} />}
/>
</Popover>
<NavbarDivider /> <NavbarDivider />

View File

@@ -54,7 +54,7 @@ export default function FinancialAccountsFilter({ ...restProps }) {
filterable={false} filterable={false}
selectedItem={value} selectedItem={value}
selectedItemProp={'key'} selectedItemProp={'key'}
labelProp={'name'} textProp={'name'}
onItemSelect={(item) => { onItemSelect={(item) => {
setFieldValue('accountsFilter', item.key); setFieldValue('accountsFilter', item.key);
}} }}

View File

@@ -27,7 +27,7 @@ export default function SelectsListColumnsBy(props) {
filterable={false} filterable={false}
selectedItem={value} selectedItem={value}
selectedItemProp={'key'} selectedItemProp={'key'}
labelProp={'name'} textProp={'name'}
onItemSelect={(item) => { onItemSelect={(item) => {
form.setFieldValue('displayColumnsType', item.key); form.setFieldValue('displayColumnsType', item.key);
}} }}

View File

@@ -7,38 +7,42 @@ import {
MenuItem, MenuItem,
MenuDivider, MenuDivider,
Position, Position,
Tag,
} from '@blueprintjs/core'; } from '@blueprintjs/core';
import { FormattedMessage as T, useIntl } from 'react-intl'; import { FormattedMessage as T, useIntl } from 'react-intl';
import moment from 'moment'; import moment from 'moment';
import classNames from 'classnames'; import classNames from 'classnames';
import { import { DataTable, Icon, LoadingIndicator } from 'components';
DataTable,
If,
Money,
Choose,
Icon,
LoadingIndicator,
} from 'components';
import { CLASSES } from 'common/classes'; import { CLASSES } from 'common/classes';
import { useIsValuePassed } from 'hooks'; import { useIsValuePassed } from 'hooks';
import withDialogActions from 'containers/Dialog/withDialogActions'; import withDialogActions from 'containers/Dialog/withDialogActions';
// withInventoryAdjustments import withInventoryAdjustments from './withInventoryAdjustments';
// withInventoryAdjustmentsActions import withInventoryAdjustmentActions from './withInventoryAdjustmentActions';
import { compose, saveInvoke } from 'utils'; import { compose, saveInvoke } from 'utils';
import { withRouter } from 'react-router-dom'; import { withRouter } from 'react-router-dom';
function InventoryAdjustmentDataTable({ function InventoryAdjustmentDataTable({
// withInventoryAdjustments
inventoryAdjustmentItems,
inventoryAdjustmentCurrentPage,
inventoryAdjustmentLoading,
inventoryAdjustmentsPagination,
// withInventoryAdjustmentsActions
addInventoryAdjustmentTableQueries,
// #ownProps // #ownProps
onDeleteInventoryAdjustment, onDeleteInventoryAdjustment,
onSelectedRowsChange, onSelectedRowsChange,
}) { }) {
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
const isLoadedBefore = useIsValuePassed(inventoryAdjustmentLoading, false);
const handleDeleteInventoryAdjustment = useCallback( const handleDeleteInventoryAdjustment = useCallback(
(_inventory) => { (_adjustment) => () => {
saveInvoke(onDeleteInventoryAdjustment, _inventory); saveInvoke(onDeleteInventoryAdjustment, _adjustment);
}, },
[onDeleteInventoryAdjustment], [onDeleteInventoryAdjustment],
); );
@@ -53,9 +57,9 @@ function InventoryAdjustmentDataTable({
<MenuDivider /> <MenuDivider />
<MenuItem <MenuItem
text={formatMessage({ id: 'delete_adjustment' })} text={formatMessage({ id: 'delete_adjustment' })}
icon={<Icon icon="trash-16" iconSize={16} />}
intent={Intent.DANGER} intent={Intent.DANGER}
onClick={handleDeleteInventoryAdjustment(adjustment)} onClick={handleDeleteInventoryAdjustment(adjustment)}
icon={<Icon icon="trash-16" iconSize={16} />}
/> />
</Menu> </Menu>
), ),
@@ -79,41 +83,53 @@ function InventoryAdjustmentDataTable({
{ {
id: 'type', id: 'type',
Header: formatMessage({ id: 'type' }), Header: formatMessage({ id: 'type' }),
accessor: 'type', accessor: (row) =>
row.type ? (
<Tag minimal={true} round={true} intent={Intent.NONE}>
{formatMessage({ id: row.type })}
</Tag>
) : (
''
),
className: 'type', className: 'type',
width: 100, width: 100,
}, },
{ {
id: 'reason', id: 'reason',
Header: formatMessage({ id: 'reason' }), Header: formatMessage({ id: 'reason' }),
// accessor: (r) => ( accessor: 'reason',
// <Tooltip
// content={}
// position={Position.RIGHT_BOTTOM}
// >
// </Tooltip>
// ),
className: 'reason', className: 'reason',
width: 115, width: 115,
}, },
{ {
id: 'reference', id: 'reference_no',
Header: formatMessage({ id: 'reference' }), Header: formatMessage({ id: 'reference_no' }),
accessor: (row) => `#${row.reference}`, accessor: 'reference_no',
className: 'reference', className: 'reference_no',
width: 100, width: 100,
}, },
{ {
id: 'status', id: 'publish',
Header: formatMessage({ id: 'status' }), Header: formatMessage({ id: 'status' }),
accessor: 'status', accessor: (r) => {
return r.is_published ? (
<Tag minimal={true}>
<T id={'published'} />
</Tag>
) : (
<Tag minimal={true} intent={Intent.WARNING}>
<T id={'draft'} />
</Tag>
);
},
width: 95, width: 95,
className: 'status', className: 'publish',
}, },
{ {
id: 'description', id: 'description',
Header: formatMessage({ id: 'description' }), Header: formatMessage({ id: 'description' }),
accessor: 'description',
disableSorting: true, disableSorting: true,
width: 85, width: 85,
className: 'description', className: 'description',
@@ -144,6 +160,23 @@ function InventoryAdjustmentDataTable({
[actionMenuList, formatMessage], [actionMenuList, formatMessage],
); );
const handleDataTableFetchData = useCallback(
({ pageSize, pageIndex, sortBy }) => {
addInventoryAdjustmentTableQueries({
...(sortBy.length > 0
? {
column_sort_by: sortBy[0].id,
sort_order: sortBy[0].desc ? 'desc' : 'asc',
}
: {}),
page_size: pageSize,
page: pageIndex + 1,
});
},
[addInventoryAdjustmentTableQueries],
);
const handleSelectedRowsChange = useCallback( const handleSelectedRowsChange = useCallback(
(selectedRows) => { (selectedRows) => {
saveInvoke( saveInvoke(
@@ -160,11 +193,23 @@ function InventoryAdjustmentDataTable({
return ( return (
<div className={classNames(CLASSES.DASHBOARD_DATATABLE)}> <div className={classNames(CLASSES.DASHBOARD_DATATABLE)}>
<LoadingIndicator <LoadingIndicator loading={inventoryAdjustmentLoading && !isLoadedBefore}>
<DataTable
// loading={} columns={columns}
> data={inventoryAdjustmentItems}
<DataTable noInitialFetch={true} columns={columns} data={[]} /> onFetchData={handleDataTableFetchData}
manualSortBy={true}
selectionColumn={true}
noInitialFetch={true}
onSelectedRowsChange={handleSelectedRowsChange}
rowContextMenu={onRowContextMenu}
pagination={true}
autoResetSortBy={false}
autoResetPage={false}
pagesCount={inventoryAdjustmentsPagination.pagesCount}
initialPageSize={inventoryAdjustmentsPagination.pageSize}
initialPageIndex={inventoryAdjustmentsPagination.page - 1}
/>
</LoadingIndicator> </LoadingIndicator>
</div> </div>
); );
@@ -173,4 +218,18 @@ function InventoryAdjustmentDataTable({
export default compose( export default compose(
withRouter, withRouter,
withDialogActions, withDialogActions,
withInventoryAdjustmentActions,
withInventoryAdjustments(
({
inventoryAdjustmentLoading,
inventoryAdjustmentItems,
inventoryAdjustmentCurrentPage,
inventoryAdjustmentsPagination,
}) => ({
inventoryAdjustmentLoading,
inventoryAdjustmentItems,
inventoryAdjustmentCurrentPage,
inventoryAdjustmentsPagination,
}),
),
)(InventoryAdjustmentDataTable); )(InventoryAdjustmentDataTable);

View File

@@ -3,13 +3,15 @@ import { useQuery } from 'react-query';
import { Alert, Intent } from '@blueprintjs/core'; import { Alert, Intent } from '@blueprintjs/core';
import { FormattedMessage as T, useIntl } from 'react-intl'; import { FormattedMessage as T, useIntl } from 'react-intl';
import AppToaster from 'components/AppToaster';
import DashboardPageContent from 'components/Dashboard/DashboardPageContent'; import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
import DashboardInsider from 'components/Dashboard/DashboardInsider'; import DashboardInsider from 'components/Dashboard/DashboardInsider';
import InventoryAdjustmentDataTable from './InventoryAdjustmentDataTable'; import InventoryAdjustmentDataTable from './InventoryAdjustmentDataTable';
import withInventoryAdjustmentActions from './withInventoryAdjustmentActions';
import withInventoryAdjustments from './withInventoryAdjustments';
import withDashboardActions from 'containers/Dashboard/withDashboardActions'; import withDashboardActions from 'containers/Dashboard/withDashboardActions';
//withInventoryAdjustmentsActions
import { compose } from 'utils'; import { compose } from 'utils';
import { Route, Switch } from 'react-router-dom'; import { Route, Switch } from 'react-router-dom';
@@ -19,7 +21,13 @@ import { Route, Switch } from 'react-router-dom';
function InventoryAdjustmentList({ function InventoryAdjustmentList({
// #withDashboardActions // #withDashboardActions
changePageTitle, changePageTitle,
// #withInventoryAdjustments
inventoryAdjustmentTableQuery,
// #withInventoryAdjustmentsActions // #withInventoryAdjustmentsActions
requestFetchInventoryAdjustmentTable,
requestDeleteInventoryAdjustment,
}) { }) {
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
const [selectedRows, setSelectedRows] = useState([]); const [selectedRows, setSelectedRows] = useState([]);
@@ -32,8 +40,8 @@ function InventoryAdjustmentList({
}, [changePageTitle, formatMessage]); }, [changePageTitle, formatMessage]);
const fetchInventoryAdjustments = useQuery( const fetchInventoryAdjustments = useQuery(
['inventory-adjustment-list'], ['inventory-adjustment-list' ,inventoryAdjustmentTableQuery],
() => {}, (key, query) => requestFetchInventoryAdjustmentTable({ ...query }),
); );
// Handle selected rows change. // Handle selected rows change.
@@ -55,7 +63,25 @@ function InventoryAdjustmentList({
setDeleteInventoryAdjustment(false); setDeleteInventoryAdjustment(false);
}, [setDeleteInventoryAdjustment]); }, [setDeleteInventoryAdjustment]);
const handleConfirmInventoryAdjustmentDelete = useCallback(() => {}, []); const handleConfirmInventoryAdjustmentDelete = useCallback(() => {
requestDeleteInventoryAdjustment(deleteInventoryAdjustment.id)
.then(() => {
setDeleteInventoryAdjustment(false);
AppToaster.show({
message: formatMessage({
id: 'the_adjustment_has_been_successfully_deleted',
}),
intent: Intent.SUCCESS,
});
})
.catch((errors) => {
setDeleteInventoryAdjustment(false);
});
}, [
deleteInventoryAdjustment,
requestDeleteInventoryAdjustment,
formatMessage,
]);
// Calculates the data table selected rows count. // Calculates the data table selected rows count.
const selectedRowsCount = useMemo(() => Object.values(selectedRows).length, [ const selectedRowsCount = useMemo(() => Object.values(selectedRows).length, [
@@ -63,7 +89,7 @@ function InventoryAdjustmentList({
]); ]);
return ( return (
<DashboardInsider> <DashboardInsider name={'inventory_adjustments'}>
<DashboardPageContent> <DashboardPageContent>
<Switch> <Switch>
<Route exact={true}> <Route exact={true}>
@@ -73,9 +99,32 @@ function InventoryAdjustmentList({
/> />
</Route> </Route>
</Switch> </Switch>
<Alert
cancelButtonText={<T id={'cancel'} />}
confirmButtonText={<T id={'delete'} />}
icon={'trash'}
intent={Intent.DANGER}
isOpen={deleteInventoryAdjustment}
onCancel={handleCancelInventoryAdjustmentDelete}
onConfirm={handleConfirmInventoryAdjustmentDelete}
>
<p>
<T
id={
'once_delete_this_inventory_a_adjustment_you_will_able_to_restore_it'
}
/>
</p>
</Alert>
</DashboardPageContent> </DashboardPageContent>
</DashboardInsider> </DashboardInsider>
); );
} }
export default compose(withDashboardActions)(InventoryAdjustmentList); export default compose(
withDashboardActions,
withInventoryAdjustmentActions,
withInventoryAdjustments(({ inventoryAdjustmentTableQuery }) => ({
inventoryAdjustmentTableQuery,
})),
)(InventoryAdjustmentList);

View File

@@ -45,9 +45,6 @@ const defaultInitialValues = {
category_id: '', category_id: '',
sellable: 1, sellable: 1,
purchasable: true, purchasable: true,
opening_quantity: '',
opening_cost: '',
opening_date: moment(new Date()).format('YYYY-MM-DD'),
}; };
/** /**

View File

@@ -61,26 +61,6 @@ const Schema = Yup.object().shape({
stock: Yup.string() || Yup.boolean(), stock: Yup.string() || Yup.boolean(),
sellable: Yup.boolean().required(), sellable: Yup.boolean().required(),
purchasable: Yup.boolean().required(), purchasable: Yup.boolean().required(),
opening_cost: Yup.number().when(['opening_quantity'], {
is: (value) => value,
then: Yup.number()
.min(0)
.required()
.label(formatMessage({ id: 'opening_cost_' })),
otherwise: Yup.number().nullable(),
}),
opening_quantity: Yup.number()
.integer()
.min(1)
.nullable()
.label(formatMessage({ id: 'opening_quantity_' })),
opening_date: Yup.date().when(['opening_quantity', 'opening_cost'], {
is: (quantity, cost) => !isBlank(quantity) && !isBlank(cost),
then: Yup.date()
.required()
.label(formatMessage({ id: 'opening_date_' })),
otherwise: Yup.date().nullable(),
}),
}); });
export const transformItemFormData = (item, defaultValue) => { export const transformItemFormData = (item, defaultValue) => {

View File

@@ -65,77 +65,6 @@ function ItemFormInventorySection({ accountsList, baseCurrency }) {
</FormGroup> </FormGroup>
)} )}
</FastField> </FastField>
{/*------------- Opening quantity ------------- */}
<FastField name={'opening_quantity'}>
{({ field, meta: { touched, error } }) => (
<FormGroup
label={<T id={'opening_quantity'} />}
labelInfo={<Hint />}
className={'form-group--opening_quantity'}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'opening_quantity'} />}
inline={true}
>
<InputGroup medium={true} {...field} />
</FormGroup>
)}
</FastField>
{/*------------- Opening date ------------- */}
<FastField name={'opening_date'}>
{({ form, field: { value }, meta: { touched, error } }) => (
<FormGroup
label={<T id={'opening_date'} />}
labelInfo={<Hint />}
className={classNames(
'form-group--select-list',
'form-group--opening_date',
CLASSES.FILL,
)}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'opening_date'} />}
inline={true}
>
<DateInput
{...momentFormatter('YYYY/MM/DD')}
value={tansformDateValue(value)}
onChange={handleDateChange((value) => {
form.setFieldValue('opening_date', value);
})}
helperText={<ErrorMessage name={'opening_date'} />}
popoverProps={{ position: Position.BOTTOM, minimal: true }}
/>
</FormGroup>
)}
</FastField>
</Col>
{/*------------- Opening cost ------------- */}
<Col xs={6}>
<FastField name={'opening_cost'}>
{({ form, field: { value }, meta: { touched, error } }) => (
<FormGroup
label={<T id={'opening_average_cost'} />}
labelInfo={<Hint />}
className={'form-group--opening_cost'}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'opening_cost'} />}
inline={true}
>
<ControlGroup>
<InputPrependText text={baseCurrency} />
<MoneyInputGroup
value={value}
inputGroupProps={{ fill: true }}
onChange={(unformattedValue) => {
form.setFieldValue('opening_cost', unformattedValue);
}}
/>
</ControlGroup>
</FormGroup>
)}
</FastField>
</Col> </Col>
</Row> </Row>
</div> </div>

View File

@@ -75,6 +75,17 @@ function ItemsDataTable({
[addItemsTableQueries], [addItemsTableQueries],
); );
const handleMakeAdjustment = useCallback(
(item) => () => {
openDialog('inventory-adjustment-form', {
action: 'make_adjustment',
item_id: item.id,
quantity_on_hand: item.quantity_on_hand,
});
},
[openDialog],
);
const handleEditItem = useCallback( const handleEditItem = useCallback(
(item) => () => { (item) => () => {
onEditItem && onEditItem(item); onEditItem && onEditItem(item);
@@ -89,10 +100,6 @@ function ItemsDataTable({
[onDeleteItem], [onDeleteItem],
); );
const handleMakeAdjustment = useCallback(() => {
openDialog('inventory-adjustment-form', {});
}, [openDialog]);
const actionMenuList = useCallback( const actionMenuList = useCallback(
(item) => ( (item) => (
<Menu> <Menu>
@@ -124,7 +131,7 @@ function ItemsDataTable({
<If condition={item.type === 'inventory'}> <If condition={item.type === 'inventory'}>
<MenuItem <MenuItem
text={formatMessage({ id: 'make_adjustment' })} text={formatMessage({ id: 'make_adjustment' })}
onClick={handleMakeAdjustment} onClick={handleMakeAdjustment(item)}
/> />
</If> </If>
<MenuItem <MenuItem

View File

@@ -98,6 +98,33 @@ function ItemsList({
setDeleteItem(false); setDeleteItem(false);
}, [setDeleteItem]); }, [setDeleteItem]);
const handleDeleteErrors = (errors) => {
if (
errors.find((error) => error.type === 'ITEM_HAS_ASSOCIATED_TRANSACTINS')
) {
AppToaster.show({
message: formatMessage({
id: 'the_item_has_associated_transactions',
}),
intent: Intent.DANGER,
});
}
if (
errors.find(
(error) => error.type === 'ITEM_HAS_ASSOCIATED_INVENTORY_ADJUSTMENT',
)
) {
AppToaster.show({
message: formatMessage({
id:
'you_could_not_delete_item_that_has_associated_inventory_adjustments_transacions',
}),
intent: Intent.DANGER,
});
}
};
// handle confirm delete item. // handle confirm delete item.
const handleConfirmDeleteItem = useCallback(() => { const handleConfirmDeleteItem = useCallback(() => {
requestDeleteItem(deleteItem.id) requestDeleteItem(deleteItem.id)
@@ -112,19 +139,8 @@ function ItemsList({
setDeleteItem(false); setDeleteItem(false);
}) })
.catch(({ errors }) => { .catch(({ errors }) => {
if (
errors.find(
(error) => error.type === 'ITEM_HAS_ASSOCIATED_TRANSACTINS',
)
) {
AppToaster.show({
message: formatMessage({
id: 'the_item_has_associated_transactions',
}),
intent: Intent.DANGER,
});
}
setDeleteItem(false); setDeleteItem(false);
handleDeleteErrors(errors);
}); });
}, [requestDeleteItem, deleteItem, formatMessage]); }, [requestDeleteItem, deleteItem, formatMessage]);

View File

@@ -7,7 +7,7 @@ import {
import t from 'store/types'; import t from 'store/types';
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
requestSubmitInventoryAdjustment: (form) => requestSubmitInventoryAdjustment: ({ form }) =>
dispatch(submitInventoryAdjustment({ form })), dispatch(submitInventoryAdjustment({ form })),
requestFetchInventoryAdjustmentTable: (query = {}) => requestFetchInventoryAdjustmentTable: (query = {}) =>
dispatch(fetchInventoryAdjustmentsTable({ query: { ...query } })), dispatch(fetchInventoryAdjustmentsTable({ query: { ...query } })),

View File

@@ -1,4 +1,34 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import {
getInvoiceTableQueryFactory,
getInventoryAdjustmentCurrentPageFactory,
getInventoryAdjustmentPaginationMetaFactory,
} from 'store/inventoryAdjustments/inventoryAdjustment.selector';
export default (mapState) => {
const getInventoryAdjustmentItems = getInventoryAdjustmentCurrentPageFactory();
const getInventoryAdjustmentTableQuery = getInvoiceTableQueryFactory();
const getInventoryAdjustmentsPaginationMeta = getInventoryAdjustmentPaginationMetaFactory();
const mapStateToProps = (state, props) => {
const query = getInventoryAdjustmentTableQuery(state, props);
const mapped = {
inventoryAdjustmentCurrentPage: getInventoryAdjustmentItems(
state,
props,
query,
),
inventoryAdjustmentItems: Object.values(state.inventoryAdjustments.items),
inventoryAdjustmentTableQuery: query,
inventoryAdjustmentsPagination: getInventoryAdjustmentsPaginationMeta(
state,
props,
query,
),
inventoryAdjustmentLoading: state.inventoryAdjustments.loading,
};
return mapState ? mapState(mapped, state, props) : mapped;
};
return connect(mapStateToProps);
};

View File

@@ -105,7 +105,7 @@ export default function PreferencesGeneralForm({}) {
selectedItem={value} selectedItem={value}
selectedItemProp={'value'} selectedItemProp={'value'}
defaultText={<T id={'select_business_location'} />} defaultText={<T id={'select_business_location'} />}
labelProp={'name'} textProp={'name'}
popoverProps={{ minimal: true }} popoverProps={{ minimal: true }}
/> />
</FormGroup> </FormGroup>
@@ -130,7 +130,7 @@ export default function PreferencesGeneralForm({}) {
selectedItem={value} selectedItem={value}
selectedItemProp={'code'} selectedItemProp={'code'}
defaultText={<T id={'select_base_currency'} />} defaultText={<T id={'select_base_currency'} />}
labelProp={'label'} textProp={'label'}
popoverProps={{ minimal: true }} popoverProps={{ minimal: true }}
/> />
</FormGroup> </FormGroup>
@@ -150,7 +150,7 @@ export default function PreferencesGeneralForm({}) {
<ListSelect <ListSelect
items={fiscalYearOptions} items={fiscalYearOptions}
selectedItemProp={'value'} selectedItemProp={'value'}
labelProp={'name'} textProp={'name'}
defaultText={<T id={'select_fiscal_year'} />} defaultText={<T id={'select_fiscal_year'} />}
selectedItem={value} selectedItem={value}
onItemSelect={(item) => {}} onItemSelect={(item) => {}}
@@ -173,7 +173,7 @@ export default function PreferencesGeneralForm({}) {
<ListSelect <ListSelect
items={languages} items={languages}
selectedItemProp={'value'} selectedItemProp={'value'}
labelProp={'name'} textProp={'name'}
defaultText={<T id={'select_language'} />} defaultText={<T id={'select_language'} />}
selectedItem={value} selectedItem={value}
onItemSelect={(item) => {}} onItemSelect={(item) => {}}
@@ -227,7 +227,7 @@ export default function PreferencesGeneralForm({}) {
selectedItem={value} selectedItem={value}
selectedItemProp={'value'} selectedItemProp={'value'}
defaultText={<T id={'select_date_format'} />} defaultText={<T id={'select_date_format'} />}
labelProp={'name'} textProp={'name'}
popoverProps={{ minimal: true }} popoverProps={{ minimal: true }}
/> />
</FormGroup> </FormGroup>

View File

@@ -324,7 +324,7 @@ function SetupOrganizationForm({
selectedItem={values.base_currency} selectedItem={values.base_currency}
selectedItemProp={'value'} selectedItemProp={'value'}
defaultText={<T id={'select_base_currency'} />} defaultText={<T id={'select_base_currency'} />}
labelProp={'name'} textProp={'name'}
/> />
</FormGroup> </FormGroup>
</Col> </Col>
@@ -354,7 +354,7 @@ function SetupOrganizationForm({
selectedItem={values.language} selectedItem={values.language}
selectedItemProp={'value'} selectedItemProp={'value'}
defaultText={<T id={'select_language'} />} defaultText={<T id={'select_language'} />}
labelProp={'name'} textProp={'name'}
/> />
</FormGroup> </FormGroup>
</Col> </Col>
@@ -383,7 +383,7 @@ function SetupOrganizationForm({
selectedItem={values.fiscal_year} selectedItem={values.fiscal_year}
selectedItemProp={'value'} selectedItemProp={'value'}
defaultText={<T id={'select_fiscal_year'} />} defaultText={<T id={'select_fiscal_year'} />}
labelProp={'name'} textProp={'name'}
/> />
</FormGroup> </FormGroup>

View File

@@ -950,9 +950,24 @@ export default {
cost: 'Cost', cost: 'Cost',
qty_on_hand: 'Qty on hand', qty_on_hand: 'Qty on hand',
adjustment_account: 'Adjustment account', adjustment_account: 'Adjustment account',
inventory_adjustment_list:'Inventory Adjustment List', inventory_adjustment_list: 'Inventory Adjustment List',
delete_adjustment:'Delete Adjustment', delete_adjustment: 'Delete Adjustment',
the_make_adjustment_has_been_successfully_created: the_make_adjustment_has_been_successfully_created:
'The make adjustment has been successfully created.', 'The make adjustment has been successfully created.',
the_adjustment_has_been_successfully_deleted:
'The adjustment has been successfully deleted.',
once_delete_this_inventory_a_adjustment_you_will_able_to_restore_it: `Once you delete this inventory a adjustment, you won\'t be able to restore it later. Are you sure you want to delete this invoice?`,
select_adjustment_account: 'Select Adjustment account',
qty: 'Quantity on hand',
money_format: 'Money format',
show_zero: 'Show zero',
show_negative_in_red: 'Show negative in red',
divide_on_1000: 'Divide on 1000',
negative_format: 'Negative format',
decimal_places: 'Decimal places',
run: 'Run',
you_could_not_delete_item_that_has_associated_inventory_adjustments_transacions:
'You could not delete item that has associated inventory adjustments transactions',
format: 'Format',
current: 'Current', current: 'Current',
}; };

View File

@@ -4,10 +4,11 @@ import t from 'store/types';
export const submitInventoryAdjustment = ({ form }) => { export const submitInventoryAdjustment = ({ form }) => {
return (dispatch) => return (dispatch) =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
ApiService.post('inventory_adjustments', form).then((response) => { ApiService.post('inventory_adjustments/quick', form)
.then((response) => {
resolve(response); resolve(response);
}), })
caches((error) => { .catch((error) => {
const { response } = error; const { response } = error;
const { data } = response; const { data } = response;
@@ -51,7 +52,7 @@ export const fetchInventoryAdjustmentsTable = ({ query } = {}) => {
dispatch({ dispatch({
type: t.INVENTORY_ADJUSTMENTS_PAGE_SET, type: t.INVENTORY_ADJUSTMENTS_PAGE_SET,
payload: { payload: {
inventory_adjustments: response.data, inventory_adjustments: response.data.inventoy_adjustments,
pagination: response.data.pagination, pagination: response.data.pagination,
customViewId: customViewId:
response.data?.filter_meta?.view?.custom_view_id || -1, response.data?.filter_meta?.view?.custom_view_id || -1,
@@ -60,7 +61,7 @@ export const fetchInventoryAdjustmentsTable = ({ query } = {}) => {
dispatch({ dispatch({
type: t.INVENTORY_ADJUSTMENT_ITEMS_SET, type: t.INVENTORY_ADJUSTMENT_ITEMS_SET,
payload: { payload: {
inventory_adjustment: response.data, inventory_adjustment: response.data.inventoy_adjustments,
}, },
}); });
dispatch({ dispatch({

View File

@@ -14,9 +14,6 @@ const initialState = {
page_size: 12, page_size: 12,
page: 1, page: 1,
}, },
paginationMeta: {
total: 0,
},
}; };
export default createReducer(initialState, { export default createReducer(initialState, {
@@ -37,7 +34,6 @@ export default createReducer(initialState, {
const viewId = customViewId || -1; const viewId = customViewId || -1;
const view = state.views[viewId] || {}; const view = state.views[viewId] || {};
state.views[viewId] = { state.views[viewId] = {
...view, ...view,
pages: { pages: {
@@ -49,7 +45,6 @@ export default createReducer(initialState, {
}; };
}, },
//useless
[t.INVENTORY_ADJUSTMENT_ITEMS_SET]: (state, action) => { [t.INVENTORY_ADJUSTMENT_ITEMS_SET]: (state, action) => {
const { inventory_adjustment } = action.payload; const { inventory_adjustment } = action.payload;
const _inventory_adjustment = {}; const _inventory_adjustment = {};

View File

@@ -5,3 +5,55 @@ import {
defaultPaginationMeta, defaultPaginationMeta,
} from 'store/selectors'; } from 'store/selectors';
const inventoryAdjustmentTableQuery = (state) =>
state.inventoryAdjustments.tableQuery;
const inventoryAdjustmentsPaginationSelector = (state, props) => {
const viewId = state.inventoryAdjustments.currentViewId;
return state.inventoryAdjustments.views?.[viewId];
};
const inventoryAdjustmentItemsSelector = (state) =>
state.inventoryAdjustments.items;
const inventoryAdjustmentCurrentPageSelector = (state, props) => {
const currentViewId = state.inventoryAdjustments.currentViewId;
const currentView = state.inventoryAdjustments.views?.[currentViewId];
const currentPageId = currentView?.paginationMeta?.page;
return currentView?.pages?.[currentPageId];
};
const getinventoryAdjustmentCurrentViewIdSelector = (state) =>
state.inventoryAdjustments.currentViewId;
export const getInvoiceTableQueryFactory = () =>
createSelector(
paginationLocationQuery,
inventoryAdjustmentTableQuery,
(locationQuery, tableQuery) => {
return {
...locationQuery,
...tableQuery,
};
},
);
export const getInventoryAdjustmentCurrentPageFactory = () =>
createSelector(
inventoryAdjustmentCurrentPageSelector,
inventoryAdjustmentItemsSelector,
(currentPage, items) => {
return typeof currentPage === 'object'
? pickItemsFromIds(items, currentPage.ids) || []
: [];
},
);
export const getInventoryAdjustmentPaginationMetaFactory = () =>
createSelector(inventoryAdjustmentsPaginationSelector, (Page) => {
return {
...defaultPaginationMeta(),
...(Page?.paginationMeta || {}),
};
});

View File

@@ -8,7 +8,7 @@ export default {
INVENTORY_ADJUSTMENTS_TABLE_QUERIES_ADD: INVENTORY_ADJUSTMENTS_TABLE_QUERIES_ADD:
'INVENTORY_ADJUSTMENTS_TABLE_QUERIES_ADD', 'INVENTORY_ADJUSTMENTS/TABLE_QUERIES_ADD',
INVENTORY_ADJUSTMENTS_SET_CURRENT_VIEW: INVENTORY_ADJUSTMENTS_SET_CURRENT_VIEW:
'INVENTORY_ADJUSTMENTS_SET_CURRENT_VIEW', 'INVENTORY_ADJUSTMENTS_SET_CURRENT_VIEW',
}; };

View File

@@ -30,6 +30,8 @@
@import 'pages/view-form'; @import 'pages/view-form';
@import 'pages/register-organizaton'; @import 'pages/register-organizaton';
@import 'pages/number-format.scss';
// Views // Views
@import 'views/filter-dropdown'; @import 'views/filter-dropdown';

View File

@@ -0,0 +1,24 @@
.number-format {
width: 400px;
padding: 12px;
// width: 300px;
// padding: 10px;
&__content {
.bp3-form-group {
margin-bottom: 5px;
// margin-bottom: 0px;
.bp3-form-content {
.bp3-label {
margin-bottom: 3px;
}
.bp3-control {
margin: 5px 0px 0px;
}
}
}
}
&__footer {
display: flex;
justify-content: flex-end;
}
}