feat(warehouseTransfer): add warehouseTransfer.

This commit is contained in:
elforjani13
2022-01-23 14:07:23 +02:00
parent a958c6088a
commit 13da985864
43 changed files with 1787 additions and 10 deletions

View File

@@ -0,0 +1,38 @@
import React from 'react';
import { FastField } from 'formik';
import classNames from 'classnames';
import { CLASSES } from 'common/classes';
import { useWarehouseTransferFormContext } from './WarehouseTransferFormProvider';
import { entriesFieldShouldUpdate } from './utils';
import WarehouseTransferFormEntriesTable from './WarehouseTransferFormEntriesTable';
/**
* Warehouse transafer editor field.
*/
export default function WarehouseTransferEditorField() {
const { items } = useWarehouseTransferFormContext();
return (
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
<FastField
name={'entries'}
items={items}
// shouldUpdate={entriesFieldShouldUpdate}
>
{({
form: { values, setFieldValue },
field: { value },
meta: { error, touched },
}) => (
<WarehouseTransferFormEntriesTable
entries={value}
onUpdateData={(entries) => {
setFieldValue('entries', entries);
}}
items={items}
errors={error}
/>
)}
</FastField>
</div>
);
}

View File

@@ -0,0 +1,89 @@
import React from 'react';
import intl from 'react-intl-universal';
import { Formik, Form } from 'formik';
import { Intent } from '@blueprintjs/core';
import { useHistory } from 'react-router-dom';
import { CLASSES } from 'common/classes';
import classNames from 'classnames';
import {
CreateWarehouseFormSchema,
EditWarehouseFormSchema,
} from './WarehouseTransferForm.schema';
import WarehouseTransferFormHeader from './WarehouseTransferFormHeader';
import WarehouseTransferEditorField from './WarehouseTransferEditorField';
import WarehouseTransferFormFooter from './WarehouseTransferFormFooter';
import WarehouseTransferFormDialog from './WarehouseTransferFormDialog';
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
import { compose, orderingLinesIndexes, transactionNumber } from 'utils';
import { defaultWareTransferEntry, defaultWarehouseTransfer } from './utils';
function WarehouseTransferForm({
// #withSettings
warehouseTransferNextNumber,
warehouseTransferNumberPrefix,
warehouseTransferIncrementMode,
}) {
const history = useHistory();
// WarehouseTransfer number.
const warehouseTransferNumber = transactionNumber(
warehouseTransferNumberPrefix,
warehouseTransferNextNumber,
);
// Form initial values.
const initialValues = React.useMemo(
() => ({
...defaultWarehouseTransfer,
}),
[],
);
// Handles form submit.
const handleSubmit = (values, { setSubmitting, setErrors, resetForm }) => {
// Transformes the values of the form to request.
const form = {};
// Handle the request success.
const onSuccess = () => {};
// Handle the request error.
const onError = ({
response: {
data: { errors },
},
}) => {
setSubmitting(false);
};
};
return (
<div
className={classNames(
CLASSES.PAGE_FORM,
CLASSES.PAGE_FORM_STRIP_STYLE,
CLASSES.PAGE_FORM_WAREHOUSE_TRANSFER,
)}
>
<Formik
validationSchema={
true ? CreateWarehouseFormSchema : EditWarehouseFormSchema
}
initialValues={initialValues}
onSubmit={handleSubmit}
>
<Form>
<WarehouseTransferFormHeader />
<WarehouseTransferEditorField />
<WarehouseTransferFormFooter />
<WarehouseTransferFormDialog />
</Form>
</Formik>
</div>
);
}
export default compose(withDashboardActions)(WarehouseTransferForm);

View File

@@ -0,0 +1,28 @@
import * as Yup from 'yup';
import intl from 'react-intl-universal';
import { DATATYPES_LENGTH } from 'common/dataTypes';
const Schema = Yup.object().shape({
date: Yup.date().required().label(intl.get('date')),
transfer_number: Yup.string()
.max(DATATYPES_LENGTH.STRING)
.label(intl.get('transfer_number')),
from_warehouse: Yup.number().required().label(intl.get('from_warehouse')),
to_warehouse: Yup.number().required().label(intl.get('from_warehouse')),
reason: Yup.string()
.trim()
.min(1)
.max(DATATYPES_LENGTH.STRING)
.label(intl.get('reason')),
entries: Yup.array().of(
Yup.object().shape({
item_id: Yup.number().nullable(),
source_warehouse: Yup.number().nullable(),
destination_warehouse: Yup.number().nullable(),
quantity: Yup.number().nullable().max(DATATYPES_LENGTH.INT_10),
}),
),
});
export const CreateWarehouseFormSchema = Schema;
export const EditWarehouseFormSchema = Schema;

View File

@@ -0,0 +1,23 @@
import React from 'react';
import WarehouseTransferNumberDialog from '../../Dialogs/WarehouseTransferNumberDialog';
import { useFormikContext } from 'formik';
/**
* Warehouse transfer form dialog.
*/
export default function WarehouseTransferFormDialog() {
// Update the form once the credit number form submit confirm.
const handleWarehouseNumberFormConfirm = ({ incrementNumber, manually }) => {
setFieldValue('transfer_no', incrementNumber || '');
setFieldValue('transfer_no_manually', manually);
};
const { setFieldValue } = useFormikContext();
return (
<React.Fragment>
<WarehouseTransferNumberDialog
dialogName={'warehouse-transfer-no-form'}
onConfirm={handleWarehouseNumberFormConfirm}
/>
</React.Fragment>
);
}

View File

@@ -0,0 +1,42 @@
import React from 'react';
import { useWarehouseTransferTableColumns } from '../utils';
import { DataTableEditable } from 'components';
import { compose, saveInvoke, updateTableCell } from 'utils';
/**
* Warehouse transfer form entries table.
*/
export default function WarehouseTransferFormEntriesTable({
// #ownProps
items,
entries,
onUpdateData,
errors,
}) {
// Retrieve the warehouse transfer table columns.
const columns = useWarehouseTransferTableColumns();
// Handle update data.
const handleUpdateData = React.useCallback(
(rowIndex, columnId, value) => {
const newRows = compose(updateTableCell(rowIndex, columnId, value))(
entries,
);
onUpdateData(newRows);
},
[onUpdateData, entries],
);
return (
<DataTableEditable
columns={columns}
data={entries}
payload={{
items,
errors: errors || [],
updateData: handleUpdateData,
}}
/>
);
}

View File

@@ -0,0 +1,42 @@
import React from 'react';
import classNames from 'classnames';
import { FormGroup, TextArea } from '@blueprintjs/core';
import { FormattedMessage as T } from 'components';
import { FastField, ErrorMessage } from 'formik';
import { Row, Col, Postbox } from 'components';
import { CLASSES } from 'common/classes';
import { inputIntent } from 'utils';
export default function WarehouseTransferFormFooter() {
return (
<div className={classNames(CLASSES.PAGE_FORM_FOOTER)}>
<Postbox
title={<T id={'warehouse_transfer.label.warehouse_transfer_detail'} />}
defaultOpen={false}
>
<Row>
<Col md={8}>
{/*------------ reason -----------*/}
<FastField name={'reason'}>
{({ field, meta: { error, touched } }) => (
<FormGroup
label={<T id={'reason'} />}
className={'form-group--reason'}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'reason'} />}
>
<TextArea
growVertically={true}
large={true}
intent={inputIntent({ error, touched })}
{...field}
/>
</FormGroup>
)}
</FastField>
</Col>
</Row>
</Postbox>
</div>
);
}

View File

@@ -0,0 +1,20 @@
import React from 'react';
import classNames from 'classnames';
import { useFormikContext } from 'formik';
import intl from 'react-intl-universal';
import { CLASSES } from 'common/classes';
import WarehouseTransferFormHeaderFields from './WarehouseTransferFormHeaderFields';
/**
* Warehose transfer form header section.
*/
function WarehouseTransferFormHeader() {
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
<WarehouseTransferFormHeaderFields />
</div>
);
}
export default WarehouseTransferFormHeader;

View File

@@ -0,0 +1,212 @@
import React from 'react';
import {
FormGroup,
InputGroup,
TextArea,
Position,
ControlGroup,
} from '@blueprintjs/core';
import { DateInput } from '@blueprintjs/datetime';
import { FastField, Field, ErrorMessage } from 'formik';
import { FormattedMessage as T } from 'components';
import { momentFormatter, compose, tansformDateValue } from 'utils';
import classNames from 'classnames';
import { CLASSES } from 'common/classes';
import {
AccountsSelectList,
FieldRequiredHint,
Icon,
InputPrependButton,
} from 'components';
import { inputIntent, handleDateChange } from 'utils';
import { useWarehouseTransferFormContext } from './WarehouseTransferFormProvider';
import { useObserveTransferNoSettings } from './utils';
import withSettings from 'containers/Settings/withSettings';
import withDialogActions from 'containers/Dialog/withDialogActions';
/**
* Warehouse transfer form header fields.
*/
function WarehouseTransferFormHeaderFields({
// #withDialogActions
openDialog,
// #withSettings
warehouseTransferAutoIncrement,
warehouseTransferNextNumber,
warehouseTransferNumberPrefix,
}) {
const { accounts } = useWarehouseTransferFormContext();
// Handle warehouse transfer number changing.
const handleTransferNumberChange = () => {
openDialog('warehouse-transfer-no-form');
};
// Handle transfer no. field blur.
const handleTransferNoBlur = (form, field) => (event) => {
const newValue = event.target.value;
if (field.value !== newValue && warehouseTransferAutoIncrement) {
openDialog('warehouse-transfer-no-form', {
initialFormValues: {
manualTransactionNo: newValue,
incrementMode: 'manual-transaction',
},
});
}
};
// Syncs transfer number settings with form.
useObserveTransferNoSettings(
warehouseTransferNumberPrefix,
warehouseTransferNextNumber,
);
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ----------- Date ----------- */}
<FastField name={'date'}>
{({ form, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'date'} />}
inline={true}
labelInfo={<FieldRequiredHint />}
className={classNames('form-group--date', CLASSES.FILL)}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="date" />}
>
<DateInput
{...momentFormatter('YYYY/MM/DD')}
value={tansformDateValue(value)}
onChange={handleDateChange((formattedDate) => {
form.setFieldValue('date', formattedDate);
})}
popoverProps={{ position: Position.BOTTOM_LEFT, minimal: true }}
inputProps={{
leftIcon: <Icon icon={'date-range'} />,
}}
/>
</FormGroup>
)}
</FastField>
{/* ----------- Transfer number ----------- */}
<Field name={'transfer_number'}>
{({ form, field, meta: { error, touched } }) => (
<FormGroup
label={<T id={'warehouse_transfer.label.transfer_no'} />}
labelInfo={<FieldRequiredHint />}
inline={true}
className={classNames('form-group--transfer-no', CLASSES.FILL)}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="transfer_number" />}
>
<ControlGroup fill={true}>
<InputGroup
minimal={true}
value={field.value}
asyncControl={true}
onBlur={handleTransferNoBlur(form, field)}
/>
<InputPrependButton
buttonProps={{
onClick: handleTransferNumberChange,
icon: <Icon icon={'settings-18'} />,
}}
tooltip={true}
tooltipProps={{
content: (
<T
id={
'warehouse_transfer.setting_your_auto_generated_transfer_no'
}
/>
),
position: Position.BOTTOM_LEFT,
}}
/>
</ControlGroup>
</FormGroup>
)}
</Field>
{/* ----------- Form Warehouse ----------- */}
<FastField name={'form_warehouse'} accounts={accounts}>
{({ form, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'warehouse_transfer.label.form_warehouse'} />}
className={classNames(
'form-group--warehouse-transfer',
CLASSES.FILL,
)}
inline={true}
labelInfo={<FieldRequiredHint />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'form_warehouse'} />}
>
<AccountsSelectList
accounts={accounts}
onAccountSelected={(account) => {
form.setFieldValue('form_warehouse', account.id);
}}
defaultSelectText={'Select Warehouse Transfer'}
selectedAccountId={value}
popoverFill={true}
allowCreate={true}
/>
</FormGroup>
)}
</FastField>
{/* ----------- To Warehouse ----------- */}
<FastField name={'to_warehouse'} accounts={accounts}>
{({ form, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'warehouse_transfer.label.to_warehouse'} />}
className={classNames(
'form-group--warehouse-transfer',
CLASSES.FILL,
)}
inline={true}
labelInfo={<FieldRequiredHint />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'to_warehouse'} />}
>
<AccountsSelectList
accounts={accounts}
onAccountSelected={(account) => {
form.setFieldValue('to_warehouse', account.id);
}}
defaultSelectText={'Select Warehouse Transfer'}
selectedAccountId={value}
popoverFill={true}
allowCreate={true}
/>
</FormGroup>
)}
</FastField>
{/*------------ reason -----------*/}
<FastField name={'reason'}>
{({ field, meta: { error, touched } }) => (
<FormGroup
label={<T id={'reason'} />}
className={'form-group--reason'}
intent={inputIntent({ error, touched })}
inline={true}
helperText={<ErrorMessage name={'reason'} />}
>
<InputGroup minimal={true} {...field} />
</FormGroup>
)}
</FastField>
</div>
);
}
export default compose(
withDialogActions,
withSettings(({ warehouseTransferSettings }) => ({
warehouseTransferAutoIncrement: warehouseTransferSettings?.autoIncrement,
warehouseTransferNextNumber: warehouseTransferSettings?.nextNumber,
warehouseTransferNumberPrefix: warehouseTransferSettings?.numberPrefix,
})),
)(WarehouseTransferFormHeaderFields);

View File

@@ -0,0 +1,16 @@
import React from 'react';
import '../../../style/pages/WarehouseTransfers/PageForm.scss'
import WarehouseTransferForm from './WarehouseTransferForm';
import { WarehouseTransferFormProvider } from './WarehouseTransferFormProvider';
/**
* WarehouseTransfer form page.
*/
export default function WarehouseTransferFormPage() {
return (
<WarehouseTransferFormProvider warehouseTransferId={null}>
<WarehouseTransferForm />
</WarehouseTransferFormProvider>
);
}

View File

@@ -0,0 +1,42 @@
import React from 'react';
import DashboardInsider from 'components/Dashboard/DashboardInsider';
import { useItems, useAccounts } from 'hooks/query';
import { ITEMS_FILTER_ROLES_QUERY } from './utils.js';
const WarehouseFormContext = React.createContext();
/**
* Warehouse transfer form provider.
*/
function WarehouseTransferFormProvider({ warehouseTransferId, ...props }) {
// Handle fetch Items data table or list
const {
data: { items },
isFetching: isItemsFetching,
isLoading: isItemsLoading,
} = useItems({
page_size: 10000,
stringified_filter_roles: ITEMS_FILTER_ROLES_QUERY,
});
// Fetch accounts list.
const { data: accounts, isLoading: isAccountsLoading } = useAccounts();
// Provider payload.
const provider = {
items,
accounts,
};
return (
<DashboardInsider
loading={isItemsLoading || isAccountsLoading}
name={'warehouseTransfer-form'}
>
<WarehouseFormContext.Provider value={provider} {...props} />
</DashboardInsider>
);
}
const useWarehouseTransferFormContext = () =>
React.useContext(WarehouseFormContext);
export { WarehouseTransferFormProvider, useWarehouseTransferFormContext };

View File

@@ -0,0 +1,55 @@
import React from 'react';
import intl from 'react-intl-universal';
import clsx from 'classnames';
import { useFormikContext } from 'formik';
import moment from 'moment';
import { transactionNumber, repeatValue } from 'utils';
export const defaultWareTransferEntry = {
index: 0,
item_id: '',
source_warehouse: '100',
destination_warehouse: '0',
quantity: '',
};
export const MIN_LINES_NUMBER = 4;
export const defaultWarehouseTransfer = {
date: moment(new Date()).format('YYYY-MM-DD'),
transfer_number: '',
from_warehouse: '',
to_warehouse: '',
reason: '',
entries: [...repeatValue(defaultWareTransferEntry, MIN_LINES_NUMBER)],
};
export const ITEMS_FILTER_ROLES_QUERY = JSON.stringify([
{
index: 1,
fieldKey: 'sellable',
value: true,
condition: '&&',
comparator: 'equals',
},
{
index: 2,
fieldKey: 'active',
value: true,
condition: '&&',
comparator: 'equals',
},
]);
/**
* Syncs transfer no. settings with form.
*/
export const useObserveTransferNoSettings = (prefix, nextNumber) => {
const { setFieldValue } = useFormikContext();
React.useEffect(() => {
const transferNo = transactionNumber(prefix, nextNumber);
setFieldValue('transfer_no', transferNo);
}, [setFieldValue, prefix, nextNumber]);
};