Commit 022855e5 authored by Oleksii Savran's avatar Oleksii Savran Committed by Matija Obreza

Inventory maintenance policy

parent d3e29bb8
......@@ -369,6 +369,20 @@
"completedDate": "Completed Date",
"completedDateCode": "Completed Date Format"
},
"InventoryMaintenancePolicy": {
"curatorCooperator": "Curator",
"distributionCriticalQuantity": "Distribution critical quantity",
"distributionDefaultFormCode": "Distribution default form code",
"distributionDefaultQuantity": "Distribution default quantity",
"distributionUnitCode": "Distribution unit code",
"formTypeCode": "Form type code",
"isAutoDeducted": "Is auto deducted",
"maintenanceName": "Maintenance name",
"quantityOnHandUnitCode": "Quantity on hand unit code",
"regenerationCriticalQuantity": "Regeneration critical quantity",
"regenerationMethodCode": "Regeneration method code",
"webAvailabilityNote": "Web availability note"
},
"OrderRequest": {
"id": "Order Request ID",
"originalOrderRequest": "Original Order",
......
......@@ -6,6 +6,8 @@ import Cooperator from '@gringlobal/client/model/gringlobal/Cooperator';
* GRIN-Global CE API
*/
class InventoryMaintenancePolicy {
public static clazz: string = 'org.gringlobal.model.InventoryMaintenancePolicy';
public createdBy: Cooperator;
public createdDate: Date;
public modifiedBy: Cooperator;
......@@ -27,7 +29,13 @@ class InventoryMaintenancePolicy {
public regenerationMethodCode: string;
public webAvailabilityNote: string;
public static CodeValues = {
distributionDefaultFormCode: 'GERMPLASM_FORM',
distributionUnitCode: 'UNIT_OF_QUANTITY',
quantityOnHandUnitCode: 'UNIT_OF_QUANTITY',
regenerationMethodCode: 'REGENERATION_METHOD',
formTypeCode: 'GERMPLASM_FORM',
}
}
export default InventoryMaintenancePolicy;
......@@ -358,6 +358,20 @@
"completedDate": "Completed Date",
"completedDateCode": "Completed Date Format"
},
"InventoryMaintenancePolicy": {
"curatorCooperator": "Curator",
"distributionCriticalQuantity": "Distribution critical quantity",
"distributionDefaultFormCode": "Distribution default form code",
"distributionDefaultQuantity": "Distribution default quantity",
"distributionUnitCode": "Distribution unit code",
"formTypeCode": "Form type code",
"isAutoDeducted": "Is auto deducted",
"maintenanceName": "Maintenance name",
"quantityOnHandUnitCode": "Quantity on hand unit code",
"regenerationCriticalQuantity": "Regeneration critical quantity",
"regenerationMethodCode": "Regeneration method code",
"webAvailabilityNote": "Web availability note"
},
"OrderRequest": {
"id": "Order Request ID",
"originalOrderRequest": "Original Order",
......
......@@ -302,7 +302,7 @@ class InventoryService {
* @param data Request body
* @param xhrConfig additional xhr config
*/
public update_3 = (data: InventoryMaintenancePolicy, xhrConfig?: AxiosRequestConfig): Promise<InventoryMaintenancePolicy> => {
public updateInventoryMaintenancePolicy = (data: InventoryMaintenancePolicy, xhrConfig?: AxiosRequestConfig): Promise<InventoryMaintenancePolicy> => {
const apiUrl = URL_UPDATE_3;
// console.log(`Fetching from ${apiUrl}`);
......
......@@ -11,6 +11,7 @@
"inventory": "Inventory",
"inventorygroup": "Groups",
"inventoryAction": "Inventory actions",
"inventoryPolicy": "Inventory maintenance policy",
"taxonomyGenus": "Genera",
"taxonomySpecies": "Species",
"request": "Requests"
......@@ -301,6 +302,21 @@
"method": "Acquisition method"
}
},
"inventorypolicy": {
"admin": {
"p": {
"browse": {
"title": "List of inventory maintenance policy"
},
"details": {
"title": "Inventory maintenance policy"
},
"edit": {
"title": "New inventory maintenance policy"
}
}
}
},
"kpi": {
"admin": {
"c": {
......
......@@ -66,7 +66,7 @@ class CodeValueField extends React.Component<ICPlProps & WithStyles & WithTransl
fullWidth
variant="filled"
{ ...input }
error={ meta && !!meta.error }
error={ meta && meta.touched && !!meta.error }
>
{ options ? Object.keys(options).map((key, i) => (
<MenuItem key={ `option-${key}-${i}` } value={ key }>
......
......@@ -6,6 +6,7 @@ import { FilteredPage } from '@gringlobal/client/model/page';
import { ListItemText } from '@material-ui/core';
import { CooperatorService } from '@gringlobal/client/service';
import Autocomplete from '@gringlobal/client/ui/common/form/Autocomplete';
import SysLang from '@gringlobal/client/model/gringlobal/SysLang';
interface ICoopAutocomplete {
label: string;
......@@ -33,7 +34,18 @@ class CooperatorAutocomplete extends React.Component<ICoopAutocomplete & FieldRe
);
};
private mapOptions = (page: FilteredPage<Cooperator>): Cooperator[] => page.content;
private mapOptions = (page: FilteredPage<Cooperator>): Cooperator[] => {
page.content.forEach((coop, i) => {
if (coop.ownedBy) {
const coopId = coop.ownedBy.id;
(coop.ownedBy as any) = coopId; // fixes cycle references
}
const sysLangId = coop.sysLang;
coop.sysLang = new SysLang();
(coop.sysLang as any).id = sysLangId;
});
return page.content;
};
private getHelperText = (cooperator: Cooperator): string => cooperator &&
(cooperator.lastName ? cooperator.organization || '' : '')
......
......@@ -17,6 +17,7 @@ import { siteAdminSagas } from 'site/action/admin';
import { repositoryAdminSagas } from 'repository/action/admin';
import { cropPublicSagas } from 'crop/action/public';
import { cropAdminSagas } from 'crop/action/admin';
import { inventoryPolicyAdminSagas } from 'inventorypolicy/action/admin';
import { AxiosRequestConfig } from 'axios';
......@@ -38,6 +39,7 @@ export default function*() {
...repositoryAdminSagas,
...userAdminSagas,
...cropAdminSagas,
...inventoryPolicyAdminSagas,
// if action has method
takeEvery((action) => action.type === 'API' && !! action.method, appendAxiosConfig),
......
......@@ -17,6 +17,7 @@ import site from 'site/reducer';
import kpi from 'kpi/reducer';
import repository from 'repository/reducer';
import crop from 'crop/reducer';
import inventoryPolicy from 'inventorypolicy/reducer';
const rootReducer = (history?) => (combineReducers({
// express reducers
......@@ -30,6 +31,7 @@ const rootReducer = (history?) => (combineReducers({
taxonomy,
inventory,
inventorygroup,
inventoryPolicy,
request,
user,
site,
......
import { put, call, take, takeEvery } from 'redux-saga/effects';
// Constants
import {
RECEIVE_INVENTORY_POLICIES,
RECEIVE_INVENTORY_POLICY,
REMOVE_INVENTORY_POLICY,
SAGA_REMOVE_INVENTORY_POLICY,
SAGA_RECEIVE_INVENTORY_POLICIES,
SAGA_CREATE_INVENTORY_POLICY,
SAGA_RECEIVE_INVENTORY_POLICY,
SAGA_EDIT_INVENTORY_POLICY,
} from 'inventorypolicy/constants';
// Model
import InventoryMaintenancePolicy from '@gringlobal/client/model/gringlobal/InventoryMaintenancePolicy';
import InventoryMaintenancePolicyFilter from '@gringlobal/client/model/gringlobal/InventoryMaintenancePolicyFilter';
import { IPageRequest, FilteredPage, Page } from '@gringlobal/client/model/page';
// Service
import { InventoryService } from '@gringlobal/client/service';
import { dereferenceReferences3 } from '@gringlobal/client/utilities';
import { sagaNavigate } from '@gringlobal/client/action/navigation';
export const inventoryPolicyAdminSagas = [
takeEvery(SAGA_RECEIVE_INVENTORY_POLICIES, listInventoryPoliciesSaga),
takeEvery(SAGA_CREATE_INVENTORY_POLICY, createInventoryPolicySaga),
takeEvery(SAGA_EDIT_INVENTORY_POLICY, editInventoryPolicySaga),
takeEvery(SAGA_RECEIVE_INVENTORY_POLICY, getInventoryPolicySaga),
takeEvery(SAGA_REMOVE_INVENTORY_POLICY, removeInventoryPolicySaga),
];
export const listInventoryPoliciesAction = (filter: Partial<InventoryMaintenancePolicyFilter> = {}, pageR: IPageRequest = { page: 0, size: 100 }) => ({
type: SAGA_RECEIVE_INVENTORY_POLICIES,
payload: {
filter,
pageR,
},
});
export const loadMoreInventoryPoliciesAction = (page: FilteredPage<InventoryMaintenancePolicy>) => ({
type: SAGA_RECEIVE_INVENTORY_POLICIES,
payload: {
filter: page.filter || {},
pageR: Page.nextPage(page),
},
});
export const createInventoryPolicyAction = (inventoryPolicy: InventoryMaintenancePolicy) => ({
type: SAGA_CREATE_INVENTORY_POLICY,
payload: { inventoryPolicy },
});
export const editInventoryPolicyAction = (inventoryPolicy: InventoryMaintenancePolicy) => ({
type: SAGA_EDIT_INVENTORY_POLICY,
payload: { inventoryPolicy },
});
export const removeInventoryPolicyAction = (id: number) => ({
type: SAGA_REMOVE_INVENTORY_POLICY,
payload: { id },
});
export const getInventoryPolicyAction = (id: string | number) => ({
type: SAGA_RECEIVE_INVENTORY_POLICY,
payload: { id },
});
function* listInventoryPoliciesSaga(action) {
yield put({
type: 'API',
target: RECEIVE_INVENTORY_POLICIES,
method: InventoryService.listInventoryMaintenancePolicy,
params: [action.payload.filter, action.payload.pageR],
onSuccess: (inventoryPolicies: FilteredPage<InventoryMaintenancePolicy>) => {
dereferenceReferences3(inventoryPolicies.content, {
// _self: { id: [ '_self', 'ownedBy.site' ] },
coo: { id: [ 'ownedBy', 'curatorCooperator' ] },
});
return inventoryPolicies;
},
});
}
function* createInventoryPolicySaga(action) {
yield put({
type: 'API',
target: RECEIVE_INVENTORY_POLICY,
method: InventoryService.createInventoryMaintenancePolicy,
params: [action.payload.inventoryPolicy],
onSuccess: (inventoryPolicy: InventoryMaintenancePolicy) => {
return inventoryPolicy;
},
});
yield take(RECEIVE_INVENTORY_POLICY);
const received = yield take(RECEIVE_INVENTORY_POLICY);
if (received.payload.apiCall.data) {
console.log('navigate after creating');
yield call(sagaNavigate, `/admin/policy/${received.payload.apiCall.data.id}`);
}
}
function* editInventoryPolicySaga(action) {
yield put({
type: 'API',
target: RECEIVE_INVENTORY_POLICY,
method: InventoryService.updateInventoryMaintenancePolicy,
params: [action.payload.inventoryPolicy],
onSuccess: (inventoryPolicy: InventoryMaintenancePolicy) => {
return inventoryPolicy;
},
});
yield call(sagaNavigate, `/admin/policy/${action.payload.inventoryPolicy.id}`);
}
function* removeInventoryPolicySaga(action) {
yield put({
type: 'API',
target: REMOVE_INVENTORY_POLICY,
method: InventoryService.removeInventoryMaintenancePolicy,
params: [action.payload.id],
onSuccess: (inventoryPolicy: InventoryMaintenancePolicy) => {
return (function* () {
yield call(sagaNavigate, '/admin/policy');
return inventoryPolicy;
})();
},
});
}
function* getInventoryPolicySaga(action) {
yield put({
type: 'API',
target: RECEIVE_INVENTORY_POLICY,
method: InventoryService.getInventoryMaintenancePolicy,
params: [action.payload.id],
onSuccess: (inventoryPolicy: InventoryMaintenancePolicy) => {
dereferenceReferences3([inventoryPolicy], {
// _self: { id: [ '_self', 'ownedBy.site' ] },
coo: { id: [ 'ownedBy', 'curatorCooperator' ] },
});
return inventoryPolicy;
},
});
}
export const RECEIVE_INVENTORY_POLICIES = 'site/admin/RECEIVE_INVENTORY_POLICIES';
export const SAGA_RECEIVE_INVENTORY_POLICIES = 'saga/site/admin/RECEIVE_INVENTORY_POLICIES';
export const SAGA_CREATE_INVENTORY_POLICY = 'saga/site/admin/CREATE_INVENTORY_POLICY';
export const SAGA_EDIT_INVENTORY_POLICY = 'saga/site/admin/EDIT_INVENTORY_POLICY';
export const SAGA_RECEIVE_INVENTORY_POLICY = 'saga/site/admin/RECEIVE_INVENTORY_POLICY';
export const RECEIVE_INVENTORY_POLICY = 'site/admin/RECEIVE_INVENTORY_POLICY';
export const SAGA_REMOVE_INVENTORY_POLICY = 'saga/site/admin/REMOVE_INVENTORY_POLICY';
export const REMOVE_INVENTORY_POLICY = 'site/admin/REMOVE_INVENTORY_POLICY';
import update from 'immutability-helper';
// Constants
import { RECEIVE_INVENTORY_POLICIES, RECEIVE_INVENTORY_POLICY, REMOVE_INVENTORY_POLICY } from 'inventorypolicy/constants';
// Model
import { FilteredPage } from '@gringlobal/client/model/page';
import { ApiCall } from '@gringlobal/client/model/common';
import InventoryMaintenancePolicy from '@gringlobal/client/model/gringlobal/InventoryMaintenancePolicy';
const initialState: {
inventoryPolicy: ApiCall<InventoryMaintenancePolicy>,
inventoryPolicyList: ApiCall<FilteredPage<InventoryMaintenancePolicy>>,
} = {
inventoryPolicy: null,
inventoryPolicyList: null,
};
const inventoryPolicyAdminReducer = (state = initialState, action) => {
switch (action.type) {
case RECEIVE_INVENTORY_POLICIES: {
const { apiCall: { loading, error, timestamp, data } } = action.payload;
return update(state, {
inventoryPolicyList: {
$set: {
loading,
error,
timestamp,
data: FilteredPage.merge(state.inventoryPolicyList && state.inventoryPolicyList.data, data),
},
},
});
}
case RECEIVE_INVENTORY_POLICY: {
const { apiCall } = action.payload;
if (apiCall.data && state.inventoryPolicyList) {
const { data: inventoryPolicyList } = state.inventoryPolicyList;
const inventoryPolicy = apiCall.data;
const updatedIndex = inventoryPolicyList && inventoryPolicyList.content && inventoryPolicyList.content.findIndex((stateInventoryPolicy) => +stateInventoryPolicy.id === +inventoryPolicy.id);
if (updatedIndex !== undefined && updatedIndex !== -1) {
return update(state, {
inventoryPolicy: { $set: apiCall },
inventoryPolicyList: {
data: {
content: {
[updatedIndex]: { $set: inventoryPolicy },
},
},
},
});
} else {
return update(state, {
inventoryPolicy: { $set: apiCall },
inventoryPolicyList: {
data: {
content: {
$set: [...inventoryPolicyList.content, inventoryPolicy],
},
},
},
});
}
}
return update(state, {
inventoryPolicy: { $set: apiCall },
});
}
case REMOVE_INVENTORY_POLICY: {
console.log('reducer remove', action.payload);
const { apiCall: { data } } = action.payload;
if (data) {
if (state.inventoryPolicyList && state.inventoryPolicyList.data && state.inventoryPolicyList.data.content) {
const updatedInventoryPolicyList = state.inventoryPolicyList.data.content.filter((inventoryPolicy) => inventoryPolicy.id !== data.id);
return update(state, {
inventoryPolicy: { $set: null },
inventoryPolicyList: {
data: {
content: {
$set: updatedInventoryPolicyList,
},
totalElements: {
$apply: (total) => total - 1,
},
},
},
});
}
return update(state, {
inventoryPolicy: { $set: null },
});
}
return state;
}
default:
return state;
}
};
export default inventoryPolicyAdminReducer;
import { combineReducers } from 'redux';
import siteAdmin from './admin';
const rootReducer = combineReducers({
admin: siteAdmin,
});
export default rootReducer;
import Loadable from '@gringlobal/client/utilities/CustomReactLoadable';
// model
import IRoute from '@gringlobal/client/model/common/IRoute';
import { UserRole } from '@gringlobal/client/model/gringlobal/SysUser';
const adminRoutes: IRoute[] = [
{
auth: [ UserRole.ADMINISTRATOR ],
exact: true,
component: Loadable({
loader: () => import(/* webpackMode:"lazy", webpackChunkName: "policy" */ 'inventorypolicy/ui/admin/InventoryPolicyBrowsePage'),
}),
path: '/policy',
},
{
exact: true,
auth: [ UserRole.ADMINISTRATOR ],
component: Loadable({
loader: () => import(/* webpackMode:"lazy", webpackChunkName: "policy" */ 'inventorypolicy/ui/admin/InventoryPolicyEditPage'),
}),
path: '/policy/edit/:id(\\d+)',
},
{
exact: true,
auth: [ UserRole.ADMINISTRATOR ],
component: Loadable({
loader: () => import(/* webpackMode:"lazy", webpackChunkName: "policy" */ 'inventorypolicy/ui/admin/InventoryPolicyEditPage'),
}),
path: '/policy/edit/',
},
{
auth: [ UserRole.ADMINISTRATOR ],
component: Loadable({
loader: () => import(/* webpackMode:"lazy", webpackChunkName: "policy" */'inventorypolicy/ui/admin/InventoryPolicyDetailsPage'),
}),
path: '/policy/:id(\\d+)',
},
];
export { adminRoutes as inventoryPolicyAdminRotes };
{
"admin": {
"p": {
"browse": {
"title": "List of inventory maintenance policy"
},
"details": {
"title": "Inventory maintenance policy"
},
"edit": {
"title": "New inventory maintenance policy"
}
}
}
}
import * as React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators, compose } from 'redux';
import { WithTranslation, withTranslation } from 'react-i18next';
// Action
import { listInventoryPoliciesAction, loadMoreInventoryPoliciesAction } from 'inventorypolicy/action/admin';
// Model
import InventoryMaintenancePolicy from '@gringlobal/client/model/gringlobal/InventoryMaintenancePolicy';
import { FilteredPage, SortDirection } from '@gringlobal/client/model/page';
import InventoryMaintenancePolicyFilter from '@gringlobal/client/model/gringlobal/InventoryMaintenancePolicyFilter';
import Cooperator from '@gringlobal/client/model/gringlobal/Cooperator';
// UI
import ContentHeader from '@gringlobal/client/ui/common/heading/ContentHeader';
import Table, { TextAlign } from '@gringlobal/client/ui/common/table/Table';
import AddNewButton from '@gringlobal/client/ui/common/button/AddNewButton';
import withBrowsePageBase, { WithBrowsePageBase } from 'ui/common/withBrowsePageBase';
import { CooperatorOwnedTableConfiguration as TableConfiguration } from '@gringlobal/client/ui/common/table/TableConfiguration';
import { CooperatorLink, InventoryPolicyLink } from 'ui/common/Links';
import { CodeValueDisplay } from 'common/CodeValue';
import PageTitle from '@gringlobal/client/ui/common/PageTitle';
interface IBrowsePageProps extends React.ClassAttributes<any>, WithTranslation, WithBrowsePageBase {
onSortChange: (sortBy: string, dir: SortDirection) => void;
applyFilter: (filter: InventoryMaintenancePolicyFilter) => void;
loadMore: () => void;
data: FilteredPage<InventoryMaintenancePolicy>;
}
export const InventoryPolicyTableDefaultConfig = {
defaultColumns: [
'id',
'maintenanceName',
'curatorCooperator',
'distributionCriticalQuantity',
'distributionDefaultFormCode',
'distributionDefaultQuantity',
'distributionUnitCode',
'formTypeCode',
'isAutoDeducted',
'quantityOnHandUnitCode',
'regenerationCriticalQuantity',
'regenerationMethodCode',
'webAvailabilityNote',
'note',
],
defaultColumnSettings: {
id: { readonly: true, align: TextAlign.right },
},
columnsRenderers: {
maintenanceName: (maintenanceName: string, inventoryPolicy: InventoryMaintenancePolicy): JSX.Element => <InventoryPolicyLink inventoryPolicy={ inventoryPolicy } />,
curatorCooperator: (cooperator: Cooperator): JSX.Element => <CooperatorLink cooperator={ cooperator } />,
formTypeCode: (formTypeCode: string) => (
<CodeValueDisplay codeGroup={ InventoryMaintenancePolicy.CodeValues.formTypeCode } value={ formTypeCode } />
),
distributionDefaultFormCode: (distributionDefaultFormCode: string) => (
<CodeValueDisplay codeGroup={ InventoryMaintenancePolicy.CodeValues.distributionDefaultFormCode } value={ distributionDefaultFormCode } />
),
distributionUnitCode: (distributionUnitCode: string) => (
<CodeValueDisplay codeGroup={ InventoryMaintenancePolicy.CodeValues.distributionUnitCode } value={ distributionUnitCode } />
),
quantityOnHandUnitCode: (quantityOnHandUnitCode: string) => (
<CodeValueDisplay codeGroup={ InventoryMaintenancePolicy.CodeValues.quantityOnHandUnitCode } value={ quantityOnHandUnitCode } />
),
regenerationMethodCode: (regenerationMethodCode: string) => (
<CodeValueDisplay codeGroup={ InventoryMaintenancePolicy.CodeValues.regenerationMethodCode } value={ regenerationMethodCode } />
),
},
};
const InventoryPolicyTableConfig = new TableConfiguration(InventoryPolicyTableDefaultConfig);
class BrowsePage extends React.Component<IBrowsePageProps> {
protected static needs = [
({}) => listInventoryPoliciesAction(),
];
public constructor(props) {
super(props);
const { listAction } = this.props;
console.log('Constructor calling load data');
listAction();
}
public state = {
selected: [],
};
private rowToggled = (toggledRow: number, selectedRows: number[], rowData: InventoryMaintenancePolicy) => {
const { selected } = this.state;
console.log(`Row ${toggledRow} was toggled. Have ${selectedRows}`);
if (!selectedRows.some((row) => row === toggledRow)) {
return this.setState({ selected: selected.filter((id) => id !== rowData.id) });