@@ -299,7 +410,8 @@ function ItemCategoryDialog({
}
const mapStateToProps = (state, props) => ({
- itemCategoryId: props?.dialogPayload?.id || null,
+ dialogName: 'item-category-form',
+ itemCategoryId: props?.payload?.id || null,
});
const withItemCategoryDialog = connect(mapStateToProps);
@@ -312,5 +424,9 @@ export default compose(
withItemCategories(({ categoriesList }) => ({
categoriesList,
})),
+ withAccounts(({ accountsList }) => ({
+ accountsList,
+ })),
withItemCategoriesActions,
+ withAccountsActions,
)(ItemCategoryDialog);
diff --git a/client/src/containers/ExchangeRates/ExchangeRateActionsBar.js b/client/src/containers/ExchangeRates/ExchangeRateActionsBar.js
index 2f5436f44..49e20c7d4 100644
--- a/client/src/containers/ExchangeRates/ExchangeRateActionsBar.js
+++ b/client/src/containers/ExchangeRates/ExchangeRateActionsBar.js
@@ -20,7 +20,7 @@ import FilterDropdown from 'components/FilterDropdown';
import withDialogActions from 'containers/Dialog/withDialogActions';
import withResourceDetail from 'containers/Resources/withResourceDetails';
-
+import withExchangeRatesActions from './withExchangeRatesActions';
import { compose } from 'utils';
/**
@@ -33,6 +33,9 @@ function ExchangeRateActionsBar({
// #withResourceDetail
resourceFields,
+ //#withExchangeRatesActions
+ addExchangeRatesTableQueries,
+
// #ownProps
selectedRows = [],
onDeleteExchangeRate,
@@ -46,14 +49,20 @@ function ExchangeRateActionsBar({
openDialog('exchangeRate-form', {});
};
- const filterDropdown = FilterDropdown({
- fields: resourceFields,
- onFilterChange: (filterConditions) => {
- setFilterCount(filterConditions.length || 0);
-
- onFilterChanged && onFilterChanged(filterConditions);
- },
- });
+ // const filterDropdown = FilterDropdown({
+ // initialCondition: {
+ // fieldKey: '',
+ // compatator: 'contains',
+ // value: '',
+ // },
+ // fields: resourceFields,
+ // onFilterChange: (filterConditions) => {
+ // addExchangeRatesTableQueries({
+ // filter_roles: filterConditions || '',
+ // });
+ // onFilterChanged && onFilterChanged(filterConditions);
+ // },
+ // });
const hasSelectedRows = useMemo(() => selectedRows.length > 0, [
selectedRows,
@@ -76,16 +85,18 @@ function ExchangeRateActionsBar({
) :
- (`${filterCount} ${formatMessage({ id: 'filters_applied' })}`)
+ filterCount <= 0 ? (
+
+ ) : (
+ `${filterCount} ${formatMessage({ id: 'filters_applied' })}`
+ )
}
icon={}
/>
@@ -117,7 +128,7 @@ function ExchangeRateActionsBar({
}
const mapStateToProps = (state, props) => ({
- resourceName: 'exchange_rates',
+ resourceName: '',
});
const withExchangeRateActionBar = connect(mapStateToProps);
@@ -128,4 +139,5 @@ export default compose(
withResourceDetail(({ resourceFields }) => ({
resourceFields,
})),
+ withExchangeRatesActions,
)(ExchangeRateActionsBar);
diff --git a/client/src/containers/ExchangeRates/ExchangeRateTable.js b/client/src/containers/ExchangeRates/ExchangeRateTable.js
index d52af7356..5badc2075 100644
--- a/client/src/containers/ExchangeRates/ExchangeRateTable.js
+++ b/client/src/containers/ExchangeRates/ExchangeRateTable.js
@@ -1,9 +1,16 @@
import React, { useCallback, useMemo, useState, useEffect } from 'react';
-import { Button, Popover, Menu, MenuItem, Position,Intent } from '@blueprintjs/core';
+import {
+ Button,
+ Popover,
+ Menu,
+ MenuItem,
+ Position,
+ Intent,
+} from '@blueprintjs/core';
import { FormattedMessage as T, useIntl } from 'react-intl';
+import moment from 'moment';
-import Icon from 'components/Icon';
-import DataTable from 'components/DataTable';
+import { DataTable, Money, Icon } from 'components';
import LoadingIndicator from 'components/LoadingIndicator';
import withDialogActions from 'containers/Dialog/withDialogActions';
@@ -30,10 +37,12 @@ function ExchangeRateTable({
const [initialMount, setInitialMount] = useState(false);
const { formatMessage } = useIntl();
- const handelEditExchangeRate = (exchange_rate) => () => {
- openDialog('exchangeRate-form', { action: 'edit', id: exchange_rate.id });
- onEditExchangeRate(exchange_rate.id);
- };
+ const handelEditExchangeRate = useCallback(
+ (exchange_rate) => () => {
+ openDialog('exchangeRate-form', { action: 'edit', id: exchange_rate.id });
+ },
+ [openDialog],
+ );
const handleDeleteExchangeRate = (exchange_rate) => () => {
onDeleteExchangeRate(exchange_rate);
@@ -50,49 +59,53 @@ function ExchangeRateTable({
text={}
intent={Intent.DANGER}
onClick={handleDeleteExchangeRate(ExchangeRate)}
+ icon={}
/>
),
[handelEditExchangeRate, handleDeleteExchangeRate],
);
- const columns = useMemo(() => [
- {
- id: 'date',
- Header: formatMessage({ id: 'date' }),
- // accessor: 'date',
- width: 150,
- },
- {
- id: 'currency_code',
- Header: formatMessage({ id: 'currency_code' }),
- accessor: 'currency_code',
- className: 'currency_code',
- width: 150,
- },
- {
- id: 'exchange_rate',
- Header: formatMessage({ id: 'exchange_rate' }),
- accessor: 'exchange_rate',
- className: 'exchange_rate',
- width: 150,
- },
- {
- id: 'actions',
- Header: '',
- Cell: ({ cell }) => (
-
- } />
-
- ),
- className: 'actions',
- width: 50,
- disableResizing: false,
- },
- ], [actionMenuList,formatMessage]);
+ const columns = useMemo(
+ () => [
+ {
+ id: 'date',
+ Header: formatMessage({ id: 'date' }),
+ accessor: (r) => moment(r.date).format('YYYY MMM DD'),
+ width: 150,
+ },
+ {
+ id: 'currency_code',
+ Header: formatMessage({ id: 'currency_code' }),
+ accessor: 'currency_code',
+ className: 'currency_code',
+ width: 150,
+ },
+ {
+ id: 'exchange_rate',
+ Header: formatMessage({ id: 'exchange_rate' }),
+ accessor: (r) => ,
+ className: 'exchange_rate',
+ width: 150,
+ },
+ {
+ id: 'actions',
+ Header: '',
+ Cell: ({ cell }) => (
+
+ } />
+
+ ),
+ className: 'actions',
+ width: 50,
+ disableResizing: false,
+ },
+ ],
+ [actionMenuList, formatMessage],
+ );
const selectionColumn = useMemo(
() => ({
@@ -120,18 +133,18 @@ function ExchangeRateTable({
return (
-
+
);
}
diff --git a/client/src/containers/ExchangeRates/withExchangeRateDetail.js b/client/src/containers/ExchangeRates/withExchangeRateDetail.js
new file mode 100644
index 000000000..ee914514a
--- /dev/null
+++ b/client/src/containers/ExchangeRates/withExchangeRateDetail.js
@@ -0,0 +1,8 @@
+import { connect } from 'react-redux';
+import { getExchangeRateById } from 'store/ExchangeRate/exchange.selector';
+
+const mapStateToProps = (state, props) => ({
+ exchangeRate: getExchangeRateById(state, props),
+});
+
+export default connect(mapStateToProps);
diff --git a/client/src/containers/Items/ItemCategoriesList.js b/client/src/containers/Items/ItemCategoriesList.js
index c7d229d1c..a7bf770ea 100644
--- a/client/src/containers/Items/ItemCategoriesList.js
+++ b/client/src/containers/Items/ItemCategoriesList.js
@@ -14,6 +14,7 @@ import ItemCategoriesDataTable from 'containers/Items/ItemCategoriesTable';
import ItemsCategoryActionsBar from 'containers/Items/ItemsCategoryActionsBar';
import withDialogActions from 'containers/Dialog/withDialogActions';
+import withResourceActions from 'containers/Resources/withResourcesActions';
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
import withItemCategoriesActions from 'containers/Items/withItemCategoriesActions';
import { compose } from 'utils';
@@ -25,10 +26,15 @@ const ItemCategoryList = ({
// #withDashboardActions
changePageTitle,
+ // #withViewsActions
+ requestFetchResourceViews,
+ requestFetchResourceFields,
+
// #withItemCategoriesActions
requestFetchItemCategories,
requestDeleteItemCategory,
requestDeleteBulkItemCategories,
+ addItemCategoriesTableQueries,
// #withDialog
openDialog,
@@ -48,9 +54,13 @@ const ItemCategoryList = ({
: changePageTitle(formatMessage({ id: 'category_list' }));
}, [id, changePageTitle, formatMessage]);
- const fetchCategories = useQuery(
- ['items-categories-list', filter],
- (key, query) => requestFetchItemCategories(query),
+ const fetchCategories = useQuery(['items-categories-list'], () =>
+ requestFetchItemCategories(),
+ );
+
+ const fetchResourceFields = useQuery(
+ ['resource-fields', 'items_categories'],
+ (key, resourceName) => requestFetchResourceFields(resourceName),
);
const handleFilterChanged = useCallback(() => {}, []);
@@ -63,17 +73,23 @@ const ItemCategoryList = ({
[setSelectedRows],
);
- // Handle fetch data of accounts datatable.
- const handleFetchData = useCallback(({ pageIndex, pageSize, sortBy }) => {
- setFilter({
- ...(sortBy.length > 0
- ? {
- column_sort_by: sortBy[0].id,
- sort_order: sortBy[0].desc ? 'desc' : 'asc',
- }
- : {}),
- });
- }, []);
+ const handleFetchData = useCallback(
+ ({ pageIndex, pageSize, sortBy }) => {
+ const page = pageIndex + 1;
+
+ addItemCategoriesTableQueries({
+ ...(sortBy.length > 0
+ ? {
+ column_sort_by: sortBy[0].id,
+ sort_order: sortBy[0].desc ? 'desc' : 'asc',
+ }
+ : {}),
+ page_size: pageSize,
+ page,
+ });
+ },
+ [addItemCategoriesTableQueries],
+ );
const handleDeleteCategory = (itemCategory) => {
setDeleteCategory(itemCategory);
@@ -139,7 +155,10 @@ const ItemCategoryList = ({
]);
return (
-
+
{
- onEditCategory(category);
+ (category) => () => {
+ openDialog('item-category-form', { action: 'edit', id: category.id });
},
- [onEditCategory],
+ [],
);
+
const handleDeleteCategory = useCallback(
(category) => {
onDeleteCategory(category);
@@ -46,13 +51,14 @@ const ItemsCategoryList = ({
),
@@ -77,7 +83,7 @@ const ItemsCategoryList = ({
{
id: 'count',
Header: formatMessage({ id: 'count' }),
- accessor: (r) => r.count || '',
+ accessor: 'count',
className: 'count',
width: 50,
},
@@ -154,4 +160,5 @@ export default compose(
withItemCategories(({ categoriesList }) => ({
categoriesList,
})),
+ withDialogActions,
)(ItemsCategoryList);
diff --git a/client/src/containers/Items/ItemForm.js b/client/src/containers/Items/ItemForm.js
index a3d26f832..ec9dd3576 100644
--- a/client/src/containers/Items/ItemForm.js
+++ b/client/src/containers/Items/ItemForm.js
@@ -1,6 +1,6 @@
-import React, { useState, useMemo, useCallback,useEffect } from 'react';
+import React, { useState, useMemo, useCallback, useEffect } from 'react';
import * as Yup from 'yup';
-import { useFormik } from 'formik';
+import { useFormik, Formik } from 'formik';
import {
FormGroup,
MenuItem,
@@ -23,29 +23,28 @@ import ErrorMessage from 'components/ErrorMessage';
import Icon from 'components/Icon';
import MoneyInputGroup from 'components/MoneyInputGroup';
import Dragzone from 'components/Dragzone';
-import { ListSelect } from 'components';
+import { ListSelect, AccountsSelectList, If } from 'components';
import withItemsActions from 'containers/Items/withItemsActions';
-import withItemCategories from 'containers/Items/withItemCategories'
+import withItemCategories from 'containers/Items/withItemCategories';
import withAccounts from 'containers/Accounts/withAccounts';
import withMediaActions from 'containers/Media/withMediaActions';
import useMedia from 'hooks/useMedia';
-import withItemDetail from 'containers/Items/withItemDetail'
+import withItemDetail from 'containers/Items/withItemDetail';
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
import withAccountDetail from 'containers/Accounts/withAccountDetail';
import { compose } from 'utils';
-
const ItemForm = ({
// #withItemActions
requestSubmitItem,
requestEditItem,
- accounts,
+ accountsList,
itemDetail,
onFormSubmit,
- onCancelForm,
+ onCancelForm,
// #withDashboardActions
changePageTitle,
@@ -55,10 +54,10 @@ const ItemForm = ({
// #withMediaActions
requestSubmitMedia,
- requestDeleteMedia,
+ requestDeleteMedia,
}) => {
const [payload, setPayload] = useState({});
-
+
const history = useHistory();
const { formatMessage } = useIntl();
const {
@@ -72,22 +71,34 @@ const ItemForm = ({
deleteCallback: requestDeleteMedia,
});
- const ItemTypeDisplay = useMemo(() => [
- { value: null, label: formatMessage({id:'select_item_type'}) },
- { value: 'service', label: formatMessage({id:'service'}) },
- { value: 'inventory', label: formatMessage({id:'inventory'}) },
- { value: 'non-inventory', label: formatMessage({id:'non_inventory'}) },
- ], [formatMessage]);
+ const ItemTypeDisplay = useMemo(
+ () => [
+ { value: null, label: formatMessage({ id: 'select_item_type' }) },
+ { value: 'service', label: formatMessage({ id: 'service' }) },
+ { value: 'inventory', label: formatMessage({ id: 'inventory' }) },
+ { value: 'non-inventory', label: formatMessage({ id: 'non_inventory' }) },
+ ],
+ [formatMessage],
+ );
const validationSchema = Yup.object().shape({
active: Yup.boolean(),
- name: Yup.string().required().label(formatMessage({id:'item_name_'})),
- type: Yup.string().trim().required().label(formatMessage({id:'item_type_'})),
+ name: Yup.string()
+ .required()
+ .label(formatMessage({ id: 'item_name_' })),
+ type: Yup.string()
+ .trim()
+ .required()
+ .label(formatMessage({ id: 'item_type_' })),
sku: Yup.string().trim(),
cost_price: Yup.number(),
sell_price: Yup.number(),
- cost_account_id: Yup.number().required().label(formatMessage({id:'cost_account_id'})),
- sell_account_id: Yup.number().required().label(formatMessage({id:'sell_account_id'})),
+ cost_account_id: Yup.number()
+ .required()
+ .label(formatMessage({ id: 'cost_account_id' })),
+ sell_account_id: Yup.number()
+ .required()
+ .label(formatMessage({ id: 'sell_account_id' })),
inventory_account_id: Yup.number().when('type', {
is: (value) => value === 'inventory',
then: Yup.number().required(),
@@ -95,6 +106,8 @@ const ItemForm = ({
}),
category_id: Yup.number().nullable(),
stock: Yup.string() || Yup.boolean(),
+ sellable: Yup.boolean().required(),
+ purchasable: Yup.boolean().required(),
});
const defaultInitialValues = useMemo(
@@ -110,27 +123,36 @@ const ItemForm = ({
inventory_account_id: null,
category_id: null,
note: '',
+ sellable: null,
+ purchasable: null,
}),
- []
+ [],
+ );
+ const initialValues = useMemo(
+ () => ({
+ ...(itemDetail
+ ? {
+ ...pick(itemDetail, Object.keys(defaultInitialValues)),
+ }
+ : {
+ ...defaultInitialValues,
+ }),
+ }),
+ [itemDetail, defaultInitialValues],
);
- const initialValues = useMemo(() => ({
- ...(itemDetail) ? {
- ...pick(itemDetail, Object.keys(defaultInitialValues)),
-
- } : {
- ...defaultInitialValues,
- }
- }), [itemDetail, defaultInitialValues]);
- const saveInvokeSubmit = useCallback((payload) => {
- onFormSubmit && onFormSubmit(payload)
- }, [onFormSubmit]);
+ 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,formatMessage]);
+ itemDetail && itemDetail.id
+ ? changePageTitle(formatMessage({ id: 'edit_item_details' }))
+ : changePageTitle(formatMessage({ id: 'new_item' }));
+ }, [changePageTitle, itemDetail, formatMessage]);
const {
getFieldProps,
@@ -146,55 +168,56 @@ const ItemForm = ({
initialValues: {
...initialValues,
},
- onSubmit: (values, { setSubmitting,resetForm,setErrors }) => {
-
+ onSubmit: (values, { setSubmitting, resetForm, setErrors }) => {
const saveItem = (mediaIds) => {
const formValues = { ...values, media_ids: mediaIds };
- if(itemDetail && itemDetail.id ){
-
- requestEditItem(itemDetail.id,formValues)
- .then((response)=>{
- AppToaster.show({
- message:formatMessage({
- id:'the_item_has_been_successfully_edited',
- },{
- number:itemDetail.id
- }),
- intent:Intent.SUCCESS
+ if (itemDetail && itemDetail.id) {
+ 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);
});
- setSubmitting(false);
- saveInvokeSubmit({action:'update',...payload})
- history.push('/items');
- resetForm();
- }).catch((errors)=>{
- setSubmitting(false)
- });
-
- }else{
-
+ } else {
requestSubmitItem(formValues).then((response) => {
AppToaster.show({
- message: formatMessage({
- id: 'service_has_been_successful_created',
- }, {
- name: values.name,
- service: formatMessage({ id: 'item' }),
- }),
+ message: formatMessage(
+ {
+ id: 'service_has_been_successful_created',
+ },
+ {
+ name: values.name,
+ service: formatMessage({ id: 'item' }),
+ },
+ ),
intent: Intent.SUCCESS,
});
queryCache.removeQueries(['items-table']);
history.push('/items');
});
- };
}
-
-
+ };
Promise.all([saveMedia(), deleteMedia()]).then(
([savedMediaResponses]) => {
const mediaIds = savedMediaResponses.map((res) => res.data.media.id);
return saveItem(mediaIds);
- }
+ },
);
},
});
@@ -208,53 +231,52 @@ const ItemForm = ({
onClick={handleClick}
/>
),
- []
+ [],
);
-
// Filter Account Items
- const filterAccounts = (query, account, _index, exactMatch) => {
- const normalizedTitle = account.name.toLowerCase();
+ const filterAccounts = (query, item, _index, exactMatch) => {
+ const normalizedTitle = item.name.toLowerCase();
const normalizedQuery = query.toLowerCase();
if (exactMatch) {
return normalizedTitle === normalizedQuery;
} else {
- return `${account.code} ${normalizedTitle}`.indexOf(normalizedQuery) >= 0;
+ return `${item.code} ${normalizedTitle}`.indexOf(normalizedQuery) >= 0;
}
};
- const onItemAccountSelect = useCallback((filedName) => {
- return (account) => {
- setFieldValue(filedName, account.id);
- };
- }, [setFieldValue]);
+ const onItemAccountSelect = useCallback(
+ (filedName) => {
+ return (account) => {
+ setFieldValue(filedName, account.id);
+ };
+ },
+ [setFieldValue],
+ );
const categoryItem = useCallback(
(item, { handleClick }) => (
-
+
),
- []
+ [],
);
- const requiredSpan = useMemo(() => *, []);
- const infoIcon = useMemo(() => , []);
+ const requiredSpan = useMemo(() => *, []);
+ const infoIcon = useMemo(() => , []);
const handleMoneyInputChange = (fieldKey) => (e, value) => {
setFieldValue(fieldKey, value);
};
-
- const initialAttachmentFiles =useMemo(()=>{
+ const initialAttachmentFiles = useMemo(() => {
return itemDetail && itemDetail.media
- ? itemDetail.media.map((attach)=>({
-
- preview:attach.attachment_file,
- upload:true,
- metadata:{...attach}
-
- })):[];
-
- },[itemDetail])
+ ? itemDetail.media.map((attach) => ({
+ preview: attach.attachment_file,
+ upload: true,
+ metadata: { ...attach },
+ }))
+ : [];
+ }, [itemDetail]);
const handleDropFiles = useCallback((_files) => {
setFiles(_files.filter((file) => file.uploaded === false));
}, []);
@@ -267,7 +289,7 @@ const ItemForm = ({
}
});
},
- [setDeletedFiles, deletedFiles,]
+ [setDeletedFiles, deletedFiles],
);
const handleCancelClickBtn = () => {
@@ -275,12 +297,11 @@ const ItemForm = ({
};
return (
-