# Conflicts:
#	client/src/lang/en/index.js
This commit is contained in:
a.bouhuolia
2020-12-30 20:41:39 +02:00
7 changed files with 113 additions and 45 deletions

View File

@@ -6,6 +6,7 @@ import { useHistory } from 'react-router-dom';
import { useIntl } from 'react-intl';
import classNames from 'classnames';
import { defaultTo } from 'lodash';
import moment from 'moment';
import { CLASSES } from 'common/classes';
import AppToaster from 'components/AppToaster';
@@ -42,6 +43,9 @@ const defaultInitialValues = {
category_id: '',
sellable: 1,
purchasable: true,
opening_quantity: '',
opening_cost: '',
opening_date: moment(new Date()).format('YYYY-MM-DD'),
};
/**

View File

@@ -2,6 +2,7 @@ import * as Yup from 'yup';
import { defaultTo } from 'lodash';
import { formatMessage } from 'services/intl';
import { DATATYPES_LENGTH } from 'common/dataTypes';
import { isBlank } from 'utils';
const Schema = Yup.object().shape({
active: Yup.boolean(),
@@ -17,20 +18,24 @@ const Schema = Yup.object().shape({
.max(DATATYPES_LENGTH.STRING)
.label(formatMessage({ id: 'item_type_' })),
code: Yup.string().trim().min(0).max(DATATYPES_LENGTH.STRING),
cost_price: Yup.number().min(0).when(['purchasable'], {
is: true,
then: Yup.number()
.required()
.label(formatMessage({ id: 'cost_price_' })),
otherwise: Yup.number().nullable(true),
}),
sell_price: Yup.number().min(0).when(['sellable'], {
is: true,
then: Yup.number()
.required()
.label(formatMessage({ id: 'sell_price_' })),
otherwise: Yup.number().nullable(true),
}),
cost_price: Yup.number()
.min(0)
.when(['purchasable'], {
is: true,
then: Yup.number()
.required()
.label(formatMessage({ id: 'cost_price_' })),
otherwise: Yup.number().nullable(true),
}),
sell_price: Yup.number()
.min(0)
.when(['sellable'], {
is: true,
then: Yup.number()
.required()
.label(formatMessage({ id: 'sell_price_' })),
otherwise: Yup.number().nullable(true),
}),
cost_account_id: Yup.number()
.when(['purchasable'], {
is: true,
@@ -56,9 +61,28 @@ const Schema = Yup.object().shape({
stock: Yup.string() || Yup.boolean(),
sellable: 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) => {
return {
...item,
@@ -66,7 +90,7 @@ export const transformItemFormData = (item, defaultValue) => {
purchasable: !!defaultTo(item?.purchasable, defaultValue.purchasable),
active: !!defaultTo(item?.active, defaultValue.active),
};
}
};
export const CreateItemFormSchema = Schema;
export const EditItemFormSchema = Schema;

View File

@@ -57,7 +57,6 @@ function ItemFormBody({ accountsList, baseCurrency }) {
<InputPrependText text={baseCurrency} />
<MoneyInputGroup
value={value}
prefix={'$'}
inputGroupProps={{ fill: true }}
disabled={!form.values.sellable}
onChange={(unformattedValue) => {
@@ -132,7 +131,6 @@ function ItemFormBody({ accountsList, baseCurrency }) {
<InputPrependText text={baseCurrency} />
<MoneyInputGroup
value={value}
prefix={'$'}
inputGroupProps={{ medium: true }}
disabled={!form.values.purchasable}
onChange={(unformattedValue) => {

View File

@@ -1,18 +1,37 @@
import React from 'react';
import { FastField, ErrorMessage } from 'formik';
import { FormGroup, InputGroup, Position } from '@blueprintjs/core';
import {
FormGroup,
InputGroup,
ControlGroup,
Position,
} from '@blueprintjs/core';
import { DateInput } from '@blueprintjs/datetime';
import { AccountsSelectList, Col, Row, Hint } from 'components';
import {
AccountsSelectList,
MoneyInputGroup,
InputPrependText,
Col,
Row,
Hint,
} from 'components';
import { CLASSES } from 'common/classes';
import { FormattedMessage as T } from 'react-intl';
import classNames from 'classnames';
import withAccounts from 'containers/Accounts/withAccounts';
import { compose, tansformDateValue, momentFormatter, inputIntent } from 'utils';
import withSettings from 'containers/Settings/withSettings';
import {
compose,
tansformDateValue,
momentFormatter,
inputIntent,
handleDateChange,
} from 'utils';
/**
* Item form inventory sections.
*/
function ItemFormInventorySection({ accountsList }) {
function ItemFormInventorySection({ accountsList, baseCurrency }) {
return (
<div class="page-form__section page-form__section--inventory">
<h3>
@@ -47,23 +66,23 @@ function ItemFormInventorySection({ accountsList }) {
)}
</FastField>
{/*------------- Opening quantity ------------- */}
<FastField name={'opening_quantity'}>
{({ field, field: { value }, meta: { touched, error } }) => (
{({ 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}
/>
<InputGroup medium={true} {...field} />
</FormGroup>
)}
</FastField>
{/*------------- Opening date ------------- */}
<FastField name={'opening_date'}>
{({ form, field: { value }, meta: { touched, error } }) => (
<FormGroup
@@ -75,14 +94,16 @@ function ItemFormInventorySection({ accountsList }) {
CLASSES.FILL,
)}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'opening_date'} />}
inline={true}
>
<DateInput
{...momentFormatter('YYYY/MM/DD')}
value={tansformDateValue(value)}
onChange={(value) => {
onChange={handleDateChange((value) => {
form.setFieldValue('opening_date', value);
}}
})}
helperText={<ErrorMessage name={'opening_date'} />}
popoverProps={{ position: Position.BOTTOM, minimal: true }}
/>
</FormGroup>
@@ -90,20 +111,28 @@ function ItemFormInventorySection({ accountsList }) {
</FastField>
</Col>
{/*------------- Opening cost ------------- */}
<Col xs={6}>
<FastField name={'opening_average_rate'}>
{({ field, field: { value }, meta: { touched, error } }) => (
<FastField name={'opening_cost'}>
{({ form, field: { value }, meta: { touched, error } }) => (
<FormGroup
label={'Opening average rate'}
label={<T id={'opening_average_cost'} />}
labelInfo={<Hint />}
className={'form-group--opening_average_rate'}
className={'form-group--opening_cost'}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'opening_cost'} />}
inline={true}
>
<InputGroup
medium={true}
{...field}
/>
<ControlGroup>
<InputPrependText text={baseCurrency} />
<MoneyInputGroup
value={value}
inputGroupProps={{ fill: true }}
onChange={(unformattedValue) => {
form.setFieldValue('opening_cost', unformattedValue);
}}
/>
</ControlGroup>
</FormGroup>
)}
</FastField>
@@ -117,4 +146,7 @@ export default compose(
withAccounts(({ accountsList }) => ({
accountsList,
})),
withSettings(({ organizationSettings }) => ({
baseCurrency: organizationSettings?.baseCurrency,
})),
)(ItemFormInventorySection);

View File

@@ -124,6 +124,14 @@ function ItemsDataTable({
],
);
const quantityonHandCell = ({ value: quantity }) => {
return quantity <= 0 ? (
<span className={'quantity_on_hand'}>{quantity}</span>
) : (
<span>{quantity}</span>
);
};
const handleRowContextMenu = useCallback(
(cell) => {
return actionMenuList(cell.row.original);
@@ -178,7 +186,7 @@ function ItemsDataTable({
{
Header: formatMessage({ id: 'cost_price' }),
accessor: (row) =>
!isBlank(row.sell_price) ? (
!isBlank(row.cost_price) ? (
<Money amount={row.cost_price} currency={'USD'} />
) : (
''
@@ -189,13 +197,7 @@ function ItemsDataTable({
{
Header: formatMessage({ id: 'quantity_on_hand' }),
accessor: 'quantity_on_hand',
className: 'quantity_on_hand',
width: 140,
},
{
Header: formatMessage({ id: 'average_rate' }),
accessor: 'average_cost_rate',
className: 'average_cost_rate',
Cell: quantityonHandCell,
width: 140,
},
{