Merge remote-tracking branch 'origin/feature/editItem'

This commit is contained in:
Ahmed Bouhuolia
2020-05-24 00:28:12 +02:00
12 changed files with 285 additions and 60 deletions

View File

@@ -1,4 +1,4 @@
import React, { useState, useMemo, useCallback } from 'react'; import React, { useState, useMemo, useCallback,useEffect } from 'react';
import * as Yup from 'yup'; import * as Yup from 'yup';
import { useFormik } from 'formik'; import { useFormik } from 'formik';
import { import {
@@ -15,35 +15,60 @@ import { Row, Col } from 'react-grid-system';
import { FormattedMessage as T, useIntl } from 'react-intl'; import { FormattedMessage as T, useIntl } from 'react-intl';
import { Select } from '@blueprintjs/select'; import { Select } from '@blueprintjs/select';
import { queryCache } from 'react-query'; import { queryCache } from 'react-query';
import {useParams ,useHistory} from 'react-router-dom';
import AppToaster from 'components/AppToaster'; import AppToaster from 'components/AppToaster';
import AccountsConnect from 'connectors/Accounts.connector';
import ItemsConnect from 'connectors/Items.connect';
import { compose } from 'utils'; import { compose } from 'utils';
import ErrorMessage from 'components/ErrorMessage'; import ErrorMessage from 'components/ErrorMessage';
import classNames from 'classnames'; import classNames from 'classnames';
import Icon from 'components/Icon'; import Icon from 'components/Icon';
import ItemCategoryConnect from 'connectors/ItemsCategory.connect'; import withItemsActions from 'containers/Items/withItemsActions';
import withItemCategories from 'containers/Items/withItemCategories'
import withAccounts from 'containers/Accounts/withAccounts';
import withMediaActions from 'containers/Media/withMediaActions';
import MoneyInputGroup from 'components/MoneyInputGroup'; import MoneyInputGroup from 'components/MoneyInputGroup';
import { useHistory } from 'react-router-dom';
import Dragzone from 'components/Dragzone'; import Dragzone from 'components/Dragzone';
import MediaConnect from 'connectors/Media.connect';
import useMedia from 'hooks/useMedia'; import useMedia from 'hooks/useMedia';
import withItems from './withItems';
import withItemDetail from 'containers/Items/withItemDetail'
import { pick } from 'lodash';
import withDashboardActions from 'containers/Dashboard/withDashboard';
import withAccountDetail from 'containers/Accounts/withAccountDetail';
const ItemForm = ({ const ItemForm = ({
// #withItemActions
requestSubmitItem, requestSubmitItem,
requestEditItem,
accounts, accounts,
categories, itemDetail,
onFormSubmit,
onCancelForm,
// #withDashboard
changePageTitle,
// #withItemCategories
categoriesList,
// #withMediaActions
requestSubmitMedia, requestSubmitMedia,
requestDeleteMedia, requestDeleteMedia,
}) => { }) => {
const [selectedAccounts, setSelectedAccounts] = useState({}); const [selectedAccounts, setSelectedAccounts] = useState({});
const [payload, setPayload] = useState({});
const history = useHistory(); const history = useHistory();
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
const {id} =useParams();
const { const {
files, files,
setFiles, setFiles,
@@ -81,7 +106,7 @@ const ItemForm = ({
stock: Yup.string() || Yup.boolean(), stock: Yup.string() || Yup.boolean(),
}); });
const initialValues = useMemo( const defaultInitialValues = useMemo(
() => ({ () => ({
active: true, active: true,
name: '', name: '',
@@ -97,6 +122,24 @@ const ItemForm = ({
}), }),
[] []
); );
const initialValues = useMemo(() => ({
...(itemDetail) ? {
...pick(itemDetail, Object.keys(defaultInitialValues)),
} : {
...defaultInitialValues,
}
}), [itemDetail, defaultInitialValues]);
const saveInvokeSubmit = useCallback((payload) => {
onFormSubmit && onFormSubmit(payload)
}, [onFormSubmit]);
useEffect(() => {
itemDetail && itemDetail.id ?
changePageTitle(formatMessage({id:'edit_item_details'})) :
changePageTitle(formatMessage({id:'new_item'}));
}, [changePageTitle,itemDetail]);
const { const {
getFieldProps, getFieldProps,
@@ -112,11 +155,33 @@ const ItemForm = ({
initialValues: { initialValues: {
...initialValues, ...initialValues,
}, },
onSubmit: (values, { setSubmitting }) => { onSubmit: (values, { setSubmitting,resetForm,setErrors }) => {
const saveItem = (mediaIds) => { const saveItem = (mediaIds) => {
const formValues = { ...values, media_ids: mediaIds }; const formValues = { ...values, media_ids: mediaIds };
if(itemDetail && itemDetail.id ){
return requestSubmitItem(formValues).then((response) => { requestEditItem(itemDetail.id,formValues)
.then((response)=>{
AppToaster.show({
message:formatMessage({
id:'the_item_has_been_successfully_edited',
},{
number:itemDetail.id
}),
intent:Intent.SUCCESS
});
setSubmitting(false);
saveInvokeSubmit({action:'update',...payload})
history.push('/items');
resetForm();
}).catch((errors)=>{
setSubmitting(false)
});
}else{
requestSubmitItem(formValues).then((response) => {
AppToaster.show({ AppToaster.show({
message: formatMessage({ message: formatMessage({
id: 'service_has_been_successful_created', id: 'service_has_been_successful_created',
@@ -127,9 +192,12 @@ const ItemForm = ({
intent: Intent.SUCCESS, intent: Intent.SUCCESS,
}); });
queryCache.removeQueries(['items-table']); queryCache.removeQueries(['items-table']);
history.push('/dashboard/items'); history.push('/items');
}); });
}; };
}
Promise.all([saveMedia(), deleteMedia()]).then( Promise.all([saveMedia(), deleteMedia()]).then(
([savedMediaResponses]) => { ([savedMediaResponses]) => {
@@ -152,6 +220,7 @@ const ItemForm = ({
[] []
); );
// Filter Account Items // Filter Account Items
const filterAccounts = (query, account, _index, exactMatch) => { const filterAccounts = (query, account, _index, exactMatch) => {
const normalizedTitle = account.name.toLowerCase(); const normalizedTitle = account.name.toLowerCase();
@@ -163,6 +232,7 @@ const ItemForm = ({
} }
}; };
const onItemAccountSelect = useCallback( const onItemAccountSelect = useCallback(
(filedName) => { (filedName) => {
return (account) => { return (account) => {
@@ -183,6 +253,7 @@ const ItemForm = ({
[] []
); );
const getSelectedAccountLabel = useCallback( const getSelectedAccountLabel = useCallback(
(fieldName, defaultLabel) => { (fieldName, defaultLabel) => {
return typeof selectedAccounts[fieldName] !== 'undefined' return typeof selectedAccounts[fieldName] !== 'undefined'
@@ -199,10 +270,18 @@ const ItemForm = ({
setFieldValue(fieldKey, value); setFieldValue(fieldKey, value);
}; };
const initialAttachmentFiles = useMemo(() => {
return [];
}, []);
const initialAttachmentFiles =useMemo(()=>{
return itemDetail && itemDetail.media
? itemDetail.media.map((attach)=>({
preview:attach.attachment_file,
upload:true,
metadata:{...attach}
})):[];
},[itemDetail])
const handleDropFiles = useCallback((_files) => { const handleDropFiles = useCallback((_files) => {
setFiles(_files.filter((file) => file.uploaded === false)); setFiles(_files.filter((file) => file.uploaded === false));
}, []); }, []);
@@ -297,7 +376,7 @@ const ItemForm = ({
)} )}
> >
<Select <Select
items={categories} items={categoriesList}
itemRenderer={categoryItem} itemRenderer={categoryItem}
itemPredicate={filterAccounts} itemPredicate={filterAccounts}
popoverProps={{ minimal: true }} popoverProps={{ minimal: true }}
@@ -532,7 +611,8 @@ const ItemForm = ({
<div class='form__floating-footer'> <div class='form__floating-footer'>
<Button intent={Intent.PRIMARY} disabled={isSubmitting} type='submit'> <Button intent={Intent.PRIMARY} disabled={isSubmitting} type='submit'>
<T id={'save'}/>
{ itemDetail && itemDetail.id ? <T id={'edit'}/> : <T id={'save'}/> }
</Button> </Button>
<Button className={'ml1'} disabled={isSubmitting}> <Button className={'ml1'} disabled={isSubmitting}>
@@ -549,8 +629,15 @@ const ItemForm = ({
}; };
export default compose( export default compose(
AccountsConnect, withAccounts(({accounts})=>({
ItemsConnect, accounts,
ItemCategoryConnect, })),
MediaConnect withAccountDetail,
withItemsActions,
withItemDetail,
withItemCategories(({ categoriesList }) => ({
categoriesList,
})),
withDashboardActions,
withMediaActions,
)(ItemForm); )(ItemForm);

View File

@@ -1,5 +1,5 @@
import React, { useEffect } from 'react'; import React, { useEffect,useCallback } from 'react';
import { useParams } from 'react-router-dom'; import { useParams,useHistory } from 'react-router-dom';
import { useQuery } from 'react-query'; import { useQuery } from 'react-query';
import ItemForm from 'containers/Items/ItemForm'; import ItemForm from 'containers/Items/ItemForm';
@@ -11,6 +11,7 @@ import withItemCategoriesActions from 'containers/Items/withItemCategoriesAction
import { compose } from 'utils'; import { compose } from 'utils';
import { FormattedMessage as T, useIntl } from 'react-intl'; import { FormattedMessage as T, useIntl } from 'react-intl';
import withItemsActions from './withItemsActions';
const ItemFormContainer = ({ const ItemFormContainer = ({
@@ -20,16 +21,15 @@ const ItemFormContainer = ({
// #withAccountsActions // #withAccountsActions
requestFetchAccounts, requestFetchAccounts,
// #withItemsActions
requestFetchItems,
// #withItemCategoriesActions // #withItemCategoriesActions
requestFetchItemCategories, requestFetchItemCategories,
}) => { }) => {
const { id } = useParams(); const { id } = useParams();
const {formatMessage} =useIntl() const {formatMessage} =useIntl()
useEffect(() => { const history = useHistory();
id ?
changePageTitle(formatMessage({id:'edit_item_details'})) :
changePageTitle(formatMessage({id:'new_item'}));
}, [id, changePageTitle]);
const fetchAccounts = useQuery('accounts-list', const fetchAccounts = useQuery('accounts-list',
(key) => requestFetchAccounts()); (key) => requestFetchAccounts());
@@ -37,11 +37,30 @@ const ItemFormContainer = ({
const fetchCategories = useQuery('item-categories-list', const fetchCategories = useQuery('item-categories-list',
(key) => requestFetchItemCategories()); (key) => requestFetchItemCategories());
const fetchItemDetail = useQuery(
id && ['item-detail-list', id],
(key) => requestFetchItems());
const handleFormSubmit =useCallback((payload)=>{
payload.redirect && history.push('/items/new');
},[history])
const handleCancel =useCallback(()=>{
history.push('/items/new');
},[])
return ( return (
<DashboardInsider <DashboardInsider
loading={fetchAccounts.isFetching || fetchCategories.isFetching} loading={fetchItemDetail.isFetching || fetchAccounts.isFetching || fetchCategories.isFetching }
name={'item-form'}> name={'item-form'}>
<ItemForm /> <ItemForm
itemId={id}
onFormSubmit={handleFormSubmit}
onCancelForm={handleCancel}
/>
</DashboardInsider> </DashboardInsider>
); );
}; };
@@ -50,4 +69,5 @@ export default compose(
withDashboard, withDashboard,
withAccountsActions, withAccountsActions,
withItemCategoriesActions, withItemCategoriesActions,
withItemsActions
)(ItemFormContainer); )(ItemFormContainer);

View File

@@ -17,7 +17,7 @@ import { compose } from 'utils';
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar'; import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
import Icon from 'components/Icon'; import Icon from 'components/Icon';
import FilterDropdown from 'components/FilterDropdown'; import FilterDropdown from 'components/FilterDropdown';
import DialogConnect from 'connectors/Dialog.connector'; import withDialog from 'connectors/Dialog.connector';
import withResourceDetail from 'containers/Resources/withResourceDetails'; import withResourceDetail from 'containers/Resources/withResourceDetails';
import withItems from 'containers/Items/withItems'; import withItems from 'containers/Items/withItems';
import { If } from 'components'; import { If } from 'components';
@@ -34,6 +34,7 @@ const ItemsActionsBar = ({
onFilterChanged, onFilterChanged,
selectedRows = [], selectedRows = [],
onBulkDelete,
}) => { }) => {
const { path } = useRouteMatch(); const { path } = useRouteMatch();
const history = useHistory(); const history = useHistory();
@@ -43,9 +44,11 @@ const ItemsActionsBar = ({
<MenuItem href={`${path}/${view.id}/custom_view`} text={view.name} /> <MenuItem href={`${path}/${view.id}/custom_view`} text={view.name} />
)); ));
const onClickNewItem = () => {
const onClickNewItem = useCallback(() => {
history.push('/items/new'); history.push('/items/new');
}; }, [history]);
const hasSelectedRows = useMemo(() => selectedRows.length > 0, [ const hasSelectedRows = useMemo(() => selectedRows.length > 0, [
selectedRows, selectedRows,
]); ]);
@@ -62,6 +65,11 @@ const ItemsActionsBar = ({
openDialog('item-form', {}); openDialog('item-form', {});
}, [openDialog]); }, [openDialog]);
const handleBulkDelete = useCallback(() => {
onBulkDelete && onBulkDelete(selectedRows.map(r => r.id));
}, [onBulkDelete, selectedRows]);
return ( return (
<DashboardActionsBar> <DashboardActionsBar>
<NavbarGroup> <NavbarGroup>
@@ -116,9 +124,10 @@ const ItemsActionsBar = ({
<If condition={hasSelectedRows}> <If condition={hasSelectedRows}>
<Button <Button
className={Classes.MINIMAL} className={Classes.MINIMAL}
icon={<Icon icon='trash' iconSize={15} />}
text={<T id={'delete'}/>}
intent={Intent.DANGER} intent={Intent.DANGER}
icon={<Icon icon='trash' />} onClick={handleBulkDelete}
text={<T id={'delete'} />}
/> />
</If> </If>
@@ -138,7 +147,7 @@ const ItemsActionsBar = ({
}; };
export default compose( export default compose(
DialogConnect, withDialog,
withItems(({ itemsViews }) => ({ withItems(({ itemsViews }) => ({
itemsViews, itemsViews,
})), })),

View File

@@ -8,7 +8,6 @@ import {
Position, Position,
} from '@blueprintjs/core' } from '@blueprintjs/core'
import { FormattedMessage as T, useIntl } from 'react-intl'; import { FormattedMessage as T, useIntl } from 'react-intl';
import {compose} from 'utils'; import {compose} from 'utils';
import DataTable from 'components/DataTable'; import DataTable from 'components/DataTable';
import Icon from 'components/Icon'; import Icon from 'components/Icon';
@@ -17,7 +16,6 @@ import Money from 'components/Money';
import withItems from 'containers/Items/withItems'; import withItems from 'containers/Items/withItems';
import LoadingIndicator from 'components/LoadingIndicator'; import LoadingIndicator from 'components/LoadingIndicator';
const ItemsDataTable = ({ const ItemsDataTable = ({
loading, loading,
@@ -32,6 +30,7 @@ const ItemsDataTable = ({
onSelectedRowsChange, onSelectedRowsChange,
}) => { }) => {
const {formatMessage} = useIntl(); const {formatMessage} = useIntl();
const [initialMount, setInitialMount] = useState(false); const [initialMount, setInitialMount] = useState(false);
@@ -41,7 +40,14 @@ const ItemsDataTable = ({
} }
}, [itemsTableLoading, setInitialMount]); }, [itemsTableLoading, setInitialMount]);
const handleEditItem = (item) => () => { onEditItem(item); }; const handleEditItem = useCallback(
(item) => () => {
onEditItem && onEditItem(item);
},
[onEditItem]
);
const handleDeleteItem = (item) => () => { onDeleteItem(item); }; const handleDeleteItem = (item) => () => { onDeleteItem(item); };
const actionMenuList = useCallback((item) => const actionMenuList = useCallback((item) =>
@@ -53,6 +59,7 @@ const ItemsDataTable = ({
</Menu>), [handleEditItem, handleDeleteItem]); </Menu>), [handleEditItem, handleDeleteItem]);
const columns = useMemo(() => [ const columns = useMemo(() => [
{ {
Header: formatMessage({ id:'item_name' }), Header: formatMessage({ id:'item_name' }),
accessor: 'name', accessor: 'name',
@@ -130,12 +137,16 @@ const ItemsDataTable = ({
onFetchData={handleFetchData} onFetchData={handleFetchData}
loading={itemsTableLoading && !initialMount} loading={itemsTableLoading && !initialMount}
noInitialFetch={true} noInitialFetch={true}
expandable={true}
treeGraph={true}
spinnerProps={{size: 30}}
onSelectedRowsChange={handleSelectedRowsChange} /> onSelectedRowsChange={handleSelectedRowsChange} />
</LoadingIndicator> </LoadingIndicator>
); );
}; };
export default compose( export default compose(
withItems(({ itemsCurrentPage, itemsTableLoading }) => ({ withItems(({ itemsCurrentPage, itemsTableLoading }) => ({
itemsCurrentPage, itemsCurrentPage,
itemsTableLoading, itemsTableLoading,

View File

@@ -1,7 +1,8 @@
import React, { useEffect, useCallback, useState } from 'react'; import React, { useEffect, useCallback, useState,useMemo } from 'react';
import { import {
Route, Route,
Switch, Switch,
useHistory
} from 'react-router-dom'; } from 'react-router-dom';
import { import {
Intent, Intent,
@@ -42,13 +43,17 @@ function ItemsList({
requestDeleteItem, requestDeleteItem,
requestFetchItems, requestFetchItems,
addItemsTableQueries, addItemsTableQueries,
requestDeleteBulkItems,
}) { }) {
const [deleteItem, setDeleteItem] = useState(false); const [deleteItem, setDeleteItem] = useState(false);
const [selectedRows, setSelectedRows] = useState([]); const [selectedRows, setSelectedRows] = useState([]);
const [tableLoading, setTableLoading] = useState(false); const [tableLoading, setTableLoading] = useState(false);
const [bulkDelete, setBulkDelete] = useState(false);
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
const history = useHistory();
useEffect(() => { useEffect(() => {
changePageTitle(formatMessage({id:'items_list'})); changePageTitle(formatMessage({id:'items_list'}));
}, [changePageTitle]); }, [changePageTitle]);
@@ -68,7 +73,14 @@ function ItemsList({
setDeleteItem(item); setDeleteItem(item);
}, [setDeleteItem]); }, [setDeleteItem]);
const handleEditItem = () => {}; const handleEditItem = useCallback(
(item) => {
history.push(`/items/${item.id}/edit`);
},
[history]
);
// Handle cancel delete the item. // Handle cancel delete the item.
const handleCancelDeleteItem = useCallback(() => { const handleCancelDeleteItem = useCallback(() => {
@@ -121,14 +133,45 @@ function ItemsList({
setSelectedRows(accounts); setSelectedRows(accounts);
}, [setSelectedRows]); }, [setSelectedRows]);
// Calculates the data table selected rows count.
const selectedRowsCount = useMemo(() => Object.values(selectedRows).length, [selectedRows]);
// Handle items bulk delete button click.,
const handleBulkDelete = useCallback((itemsIds) => {
setBulkDelete(itemsIds);
}, [setBulkDelete]);
// Handle confirm items bulk delete.
const handleConfirmBulkDelete = useCallback(() => {
requestDeleteBulkItems(bulkDelete).then(() => {
setBulkDelete(false);
AppToaster.show({
message: formatMessage({ id: 'the_items_has_been_successfully_deleted' }),
intent: Intent.SUCCESS,
});
}).catch((errors) => {
setBulkDelete(false);
});
}, [requestDeleteBulkItems, bulkDelete]);
// Handle cancel accounts bulk delete.
const handleCancelBulkDelete = useCallback(() => {
setBulkDelete(false);
}, []);
return ( return (
<DashboardInsider <DashboardInsider
isLoading={fetchHook.isFetching} isLoading={fetchHook.isFetching}
name={'items-list'}> name={'items-list'}>
<ItemsActionsBar <ItemsActionsBar
selectedRows={selectedRows}
onFilterChanged={handleFilterChanged} onFilterChanged={handleFilterChanged}
selectedRows={selectedRows} /> onBulkDelete={handleBulkDelete}/>
<DashboardPageContent> <DashboardPageContent>
<Switch> <Switch>
@@ -161,6 +204,20 @@ function ItemsList({
id={'once_delete_this_item_you_will_able_to_restore_it'} /> id={'once_delete_this_item_you_will_able_to_restore_it'} />
</p> </p>
</Alert> </Alert>
<Alert
cancelButtonText={<T id={'cancel'}/>}
confirmButtonText={`${formatMessage({id:'delete'})} (${selectedRowsCount})`}
icon="trash"
intent={Intent.DANGER}
isOpen={bulkDelete}
onCancel={handleCancelBulkDelete}
onConfirm={handleConfirmBulkDelete}
>
<p>
<T id={'once_delete_these_items_you_will_not_able_restore_them'} />
</p>
</Alert>
</Route> </Route>
</Switch> </Switch>
</DashboardPageContent> </DashboardPageContent>

View File

@@ -0,0 +1,9 @@
import { connect } from 'react-redux';
import t from 'store/types';
import { getItemById } from 'store/items/items.reducer';
const mapStateToProps = (state, props) => ({
itemDetail: getItemById(state, props.itemId),
});
export default connect(mapStateToProps);

View File

@@ -3,13 +3,17 @@ import {
fetchItems, fetchItems,
deleteItem, deleteItem,
submitItem, submitItem,
editItem,
deleteBulkItems
} from 'store/items/items.actions'; } from 'store/items/items.actions';
import t from 'store/types'; import t from 'store/types';
export const mapDispatchToProps = (dispatch) => ({ export const mapDispatchToProps = (dispatch) => ({
requestFetchItems: (query) => dispatch(fetchItems({ query })), requestFetchItems: (query) => dispatch(fetchItems({ query })),
requestDeleteItem: (id) => dispatch(deleteItem({ id })), requestDeleteItem: (id) => dispatch(deleteItem({ id })),
requestDeleteBulkItems:(ids)=>dispatch(deleteBulkItems({ids})),
requestSubmitItem: (form) => dispatch(submitItem({ form })), requestSubmitItem: (form) => dispatch(submitItem({ form })),
requestEditItem:(id,form) => dispatch(editItem({id,form})),
addBulkActionItem: (id) => dispatch({ addBulkActionItem: (id) => dispatch({
type: t.ITEM_BULK_ACTION_ADD, itemId: id type: t.ITEM_BULK_ACTION_ADD, itemId: id
}), }),

View File

@@ -322,7 +322,10 @@ export default {
organization_industry_:'Organization industry', organization_industry_:'Organization industry',
base_currency_:'Base currency', base_currency_:'Base currency',
date_format_:'Date format', date_format_:'Date format',
view_name_:'View name' category_name_:'Category name',
view_name_:'View name',
the_items_has_been_successfully_deleted: 'The items have been successfully deleted.',
once_delete_these_items_you_will_not_able_restore_them: 'Once you delete these items, you won\'t be able to retrieve them later. Are you sure you want to delete them?',
}; };

View File

@@ -82,6 +82,13 @@ export default [
}), }),
breadcrumb: 'Categories', breadcrumb: 'Categories',
}, },
{
path: `/items/:id/edit`,
component: LazyLoader({
loader: () => import('containers/Items/ItemFormPage'),
}),
breadcrumb: 'Edit Item',
},
{ {
path: `/items/new`, path: `/items/new`,
component: LazyLoader({ component: LazyLoader({

View File

@@ -76,3 +76,19 @@ export const deleteItem = ({ id }) => {
}).catch((error) => { reject(error); }); }).catch((error) => { reject(error); });
}); });
}; };
export const deleteBulkItems = ({ ids }) => {
return dispatch => new Promise((resolve, reject) => {
ApiService.delete(`items`, { params: { ids }}).then((response) => {
dispatch({
type: t.ITEMS_BULK_DELETE,
payload: { ids }
});
resolve(response);
}).catch((error) => {
reject(error);
});
});
};

View File

@@ -12,4 +12,6 @@ export default {
ITEMS_TABLE_LOADING: 'ITEMS_TABLE_LOADING', ITEMS_TABLE_LOADING: 'ITEMS_TABLE_LOADING',
ITEMS_SET_CURRENT_VIEW: 'ITEMS_SET_CURRENT_VIEW', ITEMS_SET_CURRENT_VIEW: 'ITEMS_SET_CURRENT_VIEW',
ITEMS_BULK_DELETE:'ITEMS_BULK_DELETE'
}; };

View File

@@ -203,7 +203,7 @@ export default {
check('sell_price').exists().isNumeric(), check('sell_price').exists().isNumeric(),
check('cost_account_id').exists().isInt(), check('cost_account_id').exists().isInt(),
check('sell_account_id').exists().isInt(), check('sell_account_id').exists().isInt(),
check('category_id').optional().isInt(), check('category_id').optional({ nullable: true }).isInt().toInt(),
check('note').optional(), check('note').optional(),
check('attachment').optional(), check('attachment').optional(),
check('') check('')