# Conflicts:
#	client/src/style/App.scss
This commit is contained in:
a.bouhuolia
2021-01-17 12:20:30 +02:00
41 changed files with 908 additions and 397 deletions

View File

@@ -7,38 +7,42 @@ import {
MenuItem,
MenuDivider,
Position,
Tag,
} from '@blueprintjs/core';
import { FormattedMessage as T, useIntl } from 'react-intl';
import moment from 'moment';
import classNames from 'classnames';
import {
DataTable,
If,
Money,
Choose,
Icon,
LoadingIndicator,
} from 'components';
import { DataTable, Icon, LoadingIndicator } from 'components';
import { CLASSES } from 'common/classes';
import { useIsValuePassed } from 'hooks';
import withDialogActions from 'containers/Dialog/withDialogActions';
// withInventoryAdjustments
// withInventoryAdjustmentsActions
import withInventoryAdjustments from './withInventoryAdjustments';
import withInventoryAdjustmentActions from './withInventoryAdjustmentActions';
import { compose, saveInvoke } from 'utils';
import { withRouter } from 'react-router-dom';
function InventoryAdjustmentDataTable({
// withInventoryAdjustments
inventoryAdjustmentItems,
inventoryAdjustmentCurrentPage,
inventoryAdjustmentLoading,
inventoryAdjustmentsPagination,
// withInventoryAdjustmentsActions
addInventoryAdjustmentTableQueries,
// #ownProps
onDeleteInventoryAdjustment,
onSelectedRowsChange,
}) {
const { formatMessage } = useIntl();
const isLoadedBefore = useIsValuePassed(inventoryAdjustmentLoading, false);
const handleDeleteInventoryAdjustment = useCallback(
(_inventory) => {
saveInvoke(onDeleteInventoryAdjustment, _inventory);
(_adjustment) => () => {
saveInvoke(onDeleteInventoryAdjustment, _adjustment);
},
[onDeleteInventoryAdjustment],
);
@@ -53,9 +57,9 @@ function InventoryAdjustmentDataTable({
<MenuDivider />
<MenuItem
text={formatMessage({ id: 'delete_adjustment' })}
icon={<Icon icon="trash-16" iconSize={16} />}
intent={Intent.DANGER}
onClick={handleDeleteInventoryAdjustment(adjustment)}
icon={<Icon icon="trash-16" iconSize={16} />}
/>
</Menu>
),
@@ -79,41 +83,53 @@ function InventoryAdjustmentDataTable({
{
id: 'type',
Header: formatMessage({ id: 'type' }),
accessor: 'type',
accessor: (row) =>
row.type ? (
<Tag minimal={true} round={true} intent={Intent.NONE}>
{formatMessage({ id: row.type })}
</Tag>
) : (
''
),
className: 'type',
width: 100,
},
{
id: 'reason',
Header: formatMessage({ id: 'reason' }),
// accessor: (r) => (
// <Tooltip
// content={}
// position={Position.RIGHT_BOTTOM}
// >
// </Tooltip>
// ),
accessor: 'reason',
className: 'reason',
width: 115,
},
{
id: 'reference',
Header: formatMessage({ id: 'reference' }),
accessor: (row) => `#${row.reference}`,
className: 'reference',
id: 'reference_no',
Header: formatMessage({ id: 'reference_no' }),
accessor: 'reference_no',
className: 'reference_no',
width: 100,
},
{
id: 'status',
id: 'publish',
Header: formatMessage({ id: 'status' }),
accessor: 'status',
accessor: (r) => {
return r.is_published ? (
<Tag minimal={true}>
<T id={'published'} />
</Tag>
) : (
<Tag minimal={true} intent={Intent.WARNING}>
<T id={'draft'} />
</Tag>
);
},
width: 95,
className: 'status',
className: 'publish',
},
{
id: 'description',
Header: formatMessage({ id: 'description' }),
accessor: 'description',
disableSorting: true,
width: 85,
className: 'description',
@@ -144,6 +160,23 @@ function InventoryAdjustmentDataTable({
[actionMenuList, formatMessage],
);
const handleDataTableFetchData = useCallback(
({ pageSize, pageIndex, sortBy }) => {
addInventoryAdjustmentTableQueries({
...(sortBy.length > 0
? {
column_sort_by: sortBy[0].id,
sort_order: sortBy[0].desc ? 'desc' : 'asc',
}
: {}),
page_size: pageSize,
page: pageIndex + 1,
});
},
[addInventoryAdjustmentTableQueries],
);
const handleSelectedRowsChange = useCallback(
(selectedRows) => {
saveInvoke(
@@ -160,11 +193,23 @@ function InventoryAdjustmentDataTable({
return (
<div className={classNames(CLASSES.DASHBOARD_DATATABLE)}>
<LoadingIndicator
// loading={}
>
<DataTable noInitialFetch={true} columns={columns} data={[]} />
<LoadingIndicator loading={inventoryAdjustmentLoading && !isLoadedBefore}>
<DataTable
columns={columns}
data={inventoryAdjustmentItems}
onFetchData={handleDataTableFetchData}
manualSortBy={true}
selectionColumn={true}
noInitialFetch={true}
onSelectedRowsChange={handleSelectedRowsChange}
rowContextMenu={onRowContextMenu}
pagination={true}
autoResetSortBy={false}
autoResetPage={false}
pagesCount={inventoryAdjustmentsPagination.pagesCount}
initialPageSize={inventoryAdjustmentsPagination.pageSize}
initialPageIndex={inventoryAdjustmentsPagination.page - 1}
/>
</LoadingIndicator>
</div>
);
@@ -173,4 +218,18 @@ function InventoryAdjustmentDataTable({
export default compose(
withRouter,
withDialogActions,
withInventoryAdjustmentActions,
withInventoryAdjustments(
({
inventoryAdjustmentLoading,
inventoryAdjustmentItems,
inventoryAdjustmentCurrentPage,
inventoryAdjustmentsPagination,
}) => ({
inventoryAdjustmentLoading,
inventoryAdjustmentItems,
inventoryAdjustmentCurrentPage,
inventoryAdjustmentsPagination,
}),
),
)(InventoryAdjustmentDataTable);

View File

@@ -3,13 +3,15 @@ import { useQuery } from 'react-query';
import { Alert, Intent } from '@blueprintjs/core';
import { FormattedMessage as T, useIntl } from 'react-intl';
import AppToaster from 'components/AppToaster';
import DashboardPageContent from 'components/Dashboard/DashboardPageContent';
import DashboardInsider from 'components/Dashboard/DashboardInsider';
import InventoryAdjustmentDataTable from './InventoryAdjustmentDataTable';
import withInventoryAdjustmentActions from './withInventoryAdjustmentActions';
import withInventoryAdjustments from './withInventoryAdjustments';
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
//withInventoryAdjustmentsActions
import { compose } from 'utils';
import { Route, Switch } from 'react-router-dom';
@@ -19,7 +21,13 @@ import { Route, Switch } from 'react-router-dom';
function InventoryAdjustmentList({
// #withDashboardActions
changePageTitle,
// #withInventoryAdjustments
inventoryAdjustmentTableQuery,
// #withInventoryAdjustmentsActions
requestFetchInventoryAdjustmentTable,
requestDeleteInventoryAdjustment,
}) {
const { formatMessage } = useIntl();
const [selectedRows, setSelectedRows] = useState([]);
@@ -32,8 +40,8 @@ function InventoryAdjustmentList({
}, [changePageTitle, formatMessage]);
const fetchInventoryAdjustments = useQuery(
['inventory-adjustment-list'],
() => {},
['inventory-adjustment-list' ,inventoryAdjustmentTableQuery],
(key, query) => requestFetchInventoryAdjustmentTable({ ...query }),
);
// Handle selected rows change.
@@ -55,7 +63,25 @@ function InventoryAdjustmentList({
setDeleteInventoryAdjustment(false);
}, [setDeleteInventoryAdjustment]);
const handleConfirmInventoryAdjustmentDelete = useCallback(() => {}, []);
const handleConfirmInventoryAdjustmentDelete = useCallback(() => {
requestDeleteInventoryAdjustment(deleteInventoryAdjustment.id)
.then(() => {
setDeleteInventoryAdjustment(false);
AppToaster.show({
message: formatMessage({
id: 'the_adjustment_has_been_successfully_deleted',
}),
intent: Intent.SUCCESS,
});
})
.catch((errors) => {
setDeleteInventoryAdjustment(false);
});
}, [
deleteInventoryAdjustment,
requestDeleteInventoryAdjustment,
formatMessage,
]);
// Calculates the data table selected rows count.
const selectedRowsCount = useMemo(() => Object.values(selectedRows).length, [
@@ -63,7 +89,7 @@ function InventoryAdjustmentList({
]);
return (
<DashboardInsider>
<DashboardInsider name={'inventory_adjustments'}>
<DashboardPageContent>
<Switch>
<Route exact={true}>
@@ -73,9 +99,32 @@ function InventoryAdjustmentList({
/>
</Route>
</Switch>
<Alert
cancelButtonText={<T id={'cancel'} />}
confirmButtonText={<T id={'delete'} />}
icon={'trash'}
intent={Intent.DANGER}
isOpen={deleteInventoryAdjustment}
onCancel={handleCancelInventoryAdjustmentDelete}
onConfirm={handleConfirmInventoryAdjustmentDelete}
>
<p>
<T
id={
'once_delete_this_inventory_a_adjustment_you_will_able_to_restore_it'
}
/>
</p>
</Alert>
</DashboardPageContent>
</DashboardInsider>
);
}
export default compose(withDashboardActions)(InventoryAdjustmentList);
export default compose(
withDashboardActions,
withInventoryAdjustmentActions,
withInventoryAdjustments(({ inventoryAdjustmentTableQuery }) => ({
inventoryAdjustmentTableQuery,
})),
)(InventoryAdjustmentList);

View File

@@ -45,9 +45,6 @@ const defaultInitialValues = {
category_id: '',
sellable: 1,
purchasable: true,
opening_quantity: '',
opening_cost: '',
opening_date: moment(new Date()).format('YYYY-MM-DD'),
};
/**

View File

@@ -61,26 +61,6 @@ 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) => {

View File

@@ -65,77 +65,6 @@ function ItemFormInventorySection({ accountsList, baseCurrency }) {
</FormGroup>
)}
</FastField>
{/*------------- Opening quantity ------------- */}
<FastField name={'opening_quantity'}>
{({ 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} />
</FormGroup>
)}
</FastField>
{/*------------- Opening date ------------- */}
<FastField name={'opening_date'}>
{({ form, field: { value }, meta: { touched, error } }) => (
<FormGroup
label={<T id={'opening_date'} />}
labelInfo={<Hint />}
className={classNames(
'form-group--select-list',
'form-group--opening_date',
CLASSES.FILL,
)}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'opening_date'} />}
inline={true}
>
<DateInput
{...momentFormatter('YYYY/MM/DD')}
value={tansformDateValue(value)}
onChange={handleDateChange((value) => {
form.setFieldValue('opening_date', value);
})}
helperText={<ErrorMessage name={'opening_date'} />}
popoverProps={{ position: Position.BOTTOM, minimal: true }}
/>
</FormGroup>
)}
</FastField>
</Col>
{/*------------- Opening cost ------------- */}
<Col xs={6}>
<FastField name={'opening_cost'}>
{({ form, field: { value }, meta: { touched, error } }) => (
<FormGroup
label={<T id={'opening_average_cost'} />}
labelInfo={<Hint />}
className={'form-group--opening_cost'}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'opening_cost'} />}
inline={true}
>
<ControlGroup>
<InputPrependText text={baseCurrency} />
<MoneyInputGroup
value={value}
inputGroupProps={{ fill: true }}
onChange={(unformattedValue) => {
form.setFieldValue('opening_cost', unformattedValue);
}}
/>
</ControlGroup>
</FormGroup>
)}
</FastField>
</Col>
</Row>
</div>

View File

@@ -75,6 +75,17 @@ function ItemsDataTable({
[addItemsTableQueries],
);
const handleMakeAdjustment = useCallback(
(item) => () => {
openDialog('inventory-adjustment-form', {
action: 'make_adjustment',
item_id: item.id,
quantity_on_hand: item.quantity_on_hand,
});
},
[openDialog],
);
const handleEditItem = useCallback(
(item) => () => {
onEditItem && onEditItem(item);
@@ -89,10 +100,6 @@ function ItemsDataTable({
[onDeleteItem],
);
const handleMakeAdjustment = useCallback(() => {
openDialog('inventory-adjustment-form', {});
}, [openDialog]);
const actionMenuList = useCallback(
(item) => (
<Menu>
@@ -120,11 +127,11 @@ function ItemsDataTable({
onClick={() => onActivateItem(item)}
/>
</If>
<If condition={item.type === 'inventory'}>
<MenuItem
text={formatMessage({ id: 'make_adjustment' })}
onClick={handleMakeAdjustment}
onClick={handleMakeAdjustment(item)}
/>
</If>
<MenuItem

View File

@@ -98,6 +98,33 @@ function ItemsList({
setDeleteItem(false);
}, [setDeleteItem]);
const handleDeleteErrors = (errors) => {
if (
errors.find((error) => error.type === 'ITEM_HAS_ASSOCIATED_TRANSACTINS')
) {
AppToaster.show({
message: formatMessage({
id: 'the_item_has_associated_transactions',
}),
intent: Intent.DANGER,
});
}
if (
errors.find(
(error) => error.type === 'ITEM_HAS_ASSOCIATED_INVENTORY_ADJUSTMENT',
)
) {
AppToaster.show({
message: formatMessage({
id:
'you_could_not_delete_item_that_has_associated_inventory_adjustments_transacions',
}),
intent: Intent.DANGER,
});
}
};
// handle confirm delete item.
const handleConfirmDeleteItem = useCallback(() => {
requestDeleteItem(deleteItem.id)
@@ -112,19 +139,8 @@ function ItemsList({
setDeleteItem(false);
})
.catch(({ errors }) => {
if (
errors.find(
(error) => error.type === 'ITEM_HAS_ASSOCIATED_TRANSACTINS',
)
) {
AppToaster.show({
message: formatMessage({
id: 'the_item_has_associated_transactions',
}),
intent: Intent.DANGER,
});
}
setDeleteItem(false);
handleDeleteErrors(errors);
});
}, [requestDeleteItem, deleteItem, formatMessage]);

View File

@@ -7,7 +7,7 @@ import {
import t from 'store/types';
const mapDispatchToProps = (dispatch) => ({
requestSubmitInventoryAdjustment: (form) =>
requestSubmitInventoryAdjustment: ({ form }) =>
dispatch(submitInventoryAdjustment({ form })),
requestFetchInventoryAdjustmentTable: (query = {}) =>
dispatch(fetchInventoryAdjustmentsTable({ query: { ...query } })),

View File

@@ -1,4 +1,34 @@
import { connect } from 'react-redux';
import {
getInvoiceTableQueryFactory,
getInventoryAdjustmentCurrentPageFactory,
getInventoryAdjustmentPaginationMetaFactory,
} from 'store/inventoryAdjustments/inventoryAdjustment.selector';
export default (mapState) => {
const getInventoryAdjustmentItems = getInventoryAdjustmentCurrentPageFactory();
const getInventoryAdjustmentTableQuery = getInvoiceTableQueryFactory();
const getInventoryAdjustmentsPaginationMeta = getInventoryAdjustmentPaginationMetaFactory();
const mapStateToProps = (state, props) => {
const query = getInventoryAdjustmentTableQuery(state, props);
const mapped = {
inventoryAdjustmentCurrentPage: getInventoryAdjustmentItems(
state,
props,
query,
),
inventoryAdjustmentItems: Object.values(state.inventoryAdjustments.items),
inventoryAdjustmentTableQuery: query,
inventoryAdjustmentsPagination: getInventoryAdjustmentsPaginationMeta(
state,
props,
query,
),
inventoryAdjustmentLoading: state.inventoryAdjustments.loading,
};
return mapState ? mapState(mapped, state, props) : mapped;
};
return connect(mapStateToProps);
};