Skip to content

Commit

Permalink
Merge pull request #387 from folio-org/UIOR-218-ui-validation-is-asyn…
Browse files Browse the repository at this point in the history
…c-with-backend

UIOR-218 UI validation is unsynced with back-end for location quantities
  • Loading branch information
AndreiKandratsenka authored May 3, 2019
2 parents 92f0ebb + 16fce3b commit 7637280
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 37 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@import '@folio/stripes-components/lib/variables';

.feedbackError {
color: var(--error);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';
import { get } from 'lodash';
import { FormattedMessage } from 'react-intl';
import { RepeatableField } from '@folio/stripes-components';
import css from './RepeatableFieldWithErrorMessage.css';

export const RepeatableFieldWithErrorMessage = (props) => { // eslint-disable-line import/prefer-default-export
const error = get(props, 'meta.error');

return (
<React.Fragment>
{error && (
<div className={css.feedbackError}>
<FormattedMessage id={`ui-orders.location.${error}`} />
</div>
)}
<RepeatableField {...props} />
</React.Fragment>
);
};
2 changes: 1 addition & 1 deletion src/components/LayerCollection/LayerPOLine.js
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ class LayerPOLine extends Component {
physical: {
createInventory: createInventorySetting.physical,
},
locations: [{}],
locations: [],
};

if (vendor) {
Expand Down
47 changes: 11 additions & 36 deletions src/components/POLine/Location/LocationForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,55 +5,30 @@ import {
} from 'redux-form';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import { get } from 'lodash';

import {
Col,
RepeatableField,
Select,
TextField,
} from '@folio/stripes/components';

import getLocationsForSelect from '../../Utils/getLocationsForSelect';
import { required } from '../../Utils/Validate';

const getTotalLocationsQuantities = (locations, propName) => {
const reducer = (accumulator, d) => accumulator + (d[propName] || 0);

return locations.reduce(reducer, 0);
};

const validateNotNegative = (value) => (value < 0
? <FormattedMessage id="ui-orders.validation.quantity.canNotBeLess0" />
: undefined);

const validateQuantityPhysical = (value, { cost, locations = [] }) => {
const allLocationsQuantity = getTotalLocationsQuantities(locations, 'quantityPhysical');
const overallLineQuantity = get(cost, 'quantityPhysical', 0);

return allLocationsQuantity === overallLineQuantity
? undefined
: <FormattedMessage id="ui-orders.location.quantityPhysical.notMatch" />;
};

const validateQuantityElectronic = (value, { cost, locations = [] }) => {
const allLocationsQuantity = getTotalLocationsQuantities(locations, 'quantityElectronic');
const overallLineQuantity = get(cost, 'quantityElectronic', 0);

return allLocationsQuantity === overallLineQuantity
? undefined
: <FormattedMessage id="ui-orders.location.quantityElectronic.notMatch" />;
};

const parseQuantity = (value) => {
return value ? Number(value) : 0;
};
import { RepeatableFieldWithErrorMessage } from '../../../common/RepeatableFieldWithErrorMessage/RepeatableFieldWithErrorMessage';
import {
isLocationsRequired,
parseQuantity,
validateLocation,
validateNotNegative, validateQuantityElectronic,
validateQuantityPhysical,
} from './validate';

const LocationForm = ({ parentResources }) => (
<FieldArray
addLabel={<FormattedMessage id="ui-orders.location.button.addLocation" />}
component={RepeatableField}
component={RepeatableFieldWithErrorMessage}
name="locations"
validate={isLocationsRequired}
renderField={(field) => (
<React.Fragment>
<Col xs={6}>
Expand All @@ -65,7 +40,7 @@ const LocationForm = ({ parentResources }) => (
name={`${field}.locationId`}
placeholder=" "
required
validate={required}
validate={[required, validateLocation]}
/>
</Col>
<Col xs={3}>
Expand Down
105 changes: 105 additions & 0 deletions src/components/POLine/Location/validate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import React from 'react';
import { FormattedMessage } from 'react-intl';
import { get } from 'lodash';
import {
ERESOURCES,
INVENTORY_RECORDS_TYPE,
OTHER,
PHRESOURCES,
} from '../const';

export const REQUIRED_LOCATION_QUANTITY_MAP_TO_INVENTORY = {
[INVENTORY_RECORDS_TYPE.all]: true,
[INVENTORY_RECORDS_TYPE.instance]: false,
[INVENTORY_RECORDS_TYPE.instanceAndHolding]: true,
[INVENTORY_RECORDS_TYPE.none]: false,
};

export const isLocationPhysicalQuantityRequired = (orderFormat, inventory) => {
return [...PHRESOURCES, OTHER].includes(orderFormat)
? REQUIRED_LOCATION_QUANTITY_MAP_TO_INVENTORY[inventory]
: false;
};

export const isLocationEresourceQuantityRequired = (orderFormat, inventory) => {
return ERESOURCES.includes(orderFormat)
? REQUIRED_LOCATION_QUANTITY_MAP_TO_INVENTORY[inventory]
: false;
};

const LOCATION_MUST_BE_SPECIFIED = 'required';

const getTotalLocationsQuantities = (locations, propName) => {
const reducer = (accumulator, d) => accumulator + (d[propName] || 0);

return locations.reduce(reducer, 0);
};

export const validateNotNegative = value => {
return value < 0
? <FormattedMessage id="ui-orders.validation.quantity.canNotBeLess0" />
: undefined;
};

export const validateQuantityPhysical = (value, { cost, locations = [], physical, orderFormat }) => {
const allLocationsQuantity = getTotalLocationsQuantities(locations, 'quantityPhysical');
const overallLineQuantity = Number(get(cost, 'quantityPhysical', 0));
const inventoryType = get(physical, 'createInventory', '');
const isQuantityRequired = isLocationPhysicalQuantityRequired(orderFormat, inventoryType);

if (isQuantityRequired) {
return allLocationsQuantity !== overallLineQuantity
? <FormattedMessage id="ui-orders.location.quantityPhysical.notMatch" />
: undefined;
} else {
return value && value !== 0 && allLocationsQuantity !== overallLineQuantity
? <FormattedMessage id="ui-orders.location.quantityPhysical.notMatchOrEmpty" />
: undefined;
}
};

export const validateQuantityElectronic = (value, { cost, locations = [], eresource, orderFormat }) => {
const allLocationsQuantity = getTotalLocationsQuantities(locations, 'quantityElectronic');
const overallLineQuantity = Number(get(cost, 'quantityElectronic', 0));
const inventoryType = get(eresource, 'createInventory', '');
const isQuantityRequired = isLocationEresourceQuantityRequired(orderFormat, inventoryType);

if (isQuantityRequired) {
return allLocationsQuantity !== overallLineQuantity
? <FormattedMessage id="ui-orders.location.quantityElectronic.notMatch" />
: undefined;
} else {
return value && value !== 0 && allLocationsQuantity !== overallLineQuantity
? <FormattedMessage id="ui-orders.location.quantityElectronic.notMatchOrEmpty" />
: undefined;
}
};

export const parseQuantity = (value) => {
return value
? Number(value)
: 0;
};

export const isLocationsRequired = (values, valuesAll) => {
const orderFormat = get(valuesAll, 'orderFormat');
const physicalInventory = get(valuesAll, 'physical.createInventory', '');
const eresourceInventory = get(valuesAll, 'eresource.createInventory', '');
const isPhysicalQuantityRequired = isLocationPhysicalQuantityRequired(orderFormat, physicalInventory);
const isElectronicQuantityRequired = isLocationEresourceQuantityRequired(orderFormat, eresourceInventory);
const isLocationRequired = (isPhysicalQuantityRequired || isElectronicQuantityRequired) && !values.length;

return isLocationRequired
? LOCATION_MUST_BE_SPECIFIED
: undefined;
};

export const validateLocation = (value, { locations }) => {
const { quantityPhysical, quantityElectronic } = locations.find(({ locationId }) => locationId === value);
const isQuantitiesNeed =
(!quantityPhysical || quantityPhysical === 0) && (!quantityElectronic || quantityElectronic === 0);

return isQuantitiesNeed
? <FormattedMessage id="ui-orders.location.emptyQuantities" />
: undefined;
};
4 changes: 4 additions & 0 deletions translations/ui-orders/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -434,8 +434,12 @@
"location.button.addLocation": "Add Location",
"location.nameCode": "Name (Code)",
"location.quantityElectronic.notMatch": "PO Line electronic quantity and Locations electronic quantity do not match",
"location.quantityElectronic.notMatchOrEmpty": "Locations electronic quantity should be empty or match with PO Line electronic quantity",
"location.required": "At least one location must be entered",
"location.emptyQuantities": "At least one quantity should be specified for location",
"location.quantityElectronic": "Quantity Electronic",
"location.quantityPhysical.notMatch": "PO Line physical quantity and Locations physical quantity do not match",
"location.quantityPhysical.notMatchOrEmpty": "Locations physical quantity should be empty or match with PO Line physical quantity",
"location.quantityPhysical": "Quantity Physical",
"meta.title": "Orders",
"order_format.container": "Container",
Expand Down

0 comments on commit 7637280

Please sign in to comment.