mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-23 16:19:49 +00:00
feat: auto-complete warehouse transfer row.
This commit is contained in:
@@ -3,11 +3,12 @@ import { FastField } from 'formik';
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { CLASSES } from 'common/classes';
|
import { CLASSES } from 'common/classes';
|
||||||
import { useWarehouseTransferFormContext } from './WarehouseTransferFormProvider';
|
import { useWarehouseTransferFormContext } from './WarehouseTransferFormProvider';
|
||||||
|
import WarehouseTransferFormEntriesTable from './WarehouseTransferFormEntriesTable';
|
||||||
import {
|
import {
|
||||||
entriesFieldShouldUpdate,
|
entriesFieldShouldUpdate,
|
||||||
defaultWarehouseTransferEntry,
|
defaultWarehouseTransferEntry,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
import WarehouseTransferFormEntriesTable from './WarehouseTransferFormEntriesTable';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Warehouse transafer editor field.
|
* Warehouse transafer editor field.
|
||||||
@@ -35,6 +36,8 @@ export default function WarehouseTransferEditorField() {
|
|||||||
items={items}
|
items={items}
|
||||||
defaultEntry={defaultWarehouseTransferEntry}
|
defaultEntry={defaultWarehouseTransferEntry}
|
||||||
errors={error}
|
errors={error}
|
||||||
|
sourceWarehouseId={values.from_warehouse_id}
|
||||||
|
distentionWarehouseId={value.to_warehouse_id}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</FastField>
|
</FastField>
|
||||||
|
|||||||
@@ -68,9 +68,7 @@ function WarehouseTransferForm({
|
|||||||
const handleSubmit = (values, { setSubmitting, setErrors, resetForm }) => {
|
const handleSubmit = (values, { setSubmitting, setErrors, resetForm }) => {
|
||||||
setSubmitting(true);
|
setSubmitting(true);
|
||||||
// Transformes the values of the form to request.
|
// Transformes the values of the form to request.
|
||||||
const form = {
|
const form = transformValueToRequest(values);
|
||||||
...transformValueToRequest(values),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Handle the request success.
|
// Handle the request success.
|
||||||
const onSuccess = () => {
|
const onSuccess = () => {
|
||||||
|
|||||||
@@ -17,8 +17,6 @@ const Schema = Yup.object().shape({
|
|||||||
entries: Yup.array().of(
|
entries: Yup.array().of(
|
||||||
Yup.object().shape({
|
Yup.object().shape({
|
||||||
item_id: Yup.number().nullable(),
|
item_id: Yup.number().nullable(),
|
||||||
// source_warehouse: Yup.number().nullable(),
|
|
||||||
// destination_warehouse: Yup.number().nullable(),
|
|
||||||
description: Yup.string().nullable().max(DATATYPES_LENGTH.TEXT),
|
description: Yup.string().nullable().max(DATATYPES_LENGTH.TEXT),
|
||||||
quantity: Yup.number().nullable().max(DATATYPES_LENGTH.INT_10),
|
quantity: Yup.number().nullable().max(DATATYPES_LENGTH.INT_10),
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -1,16 +1,14 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { useWarehouseTransferTableColumns } from '../utils';
|
|
||||||
import { DataTableEditable } from 'components';
|
import { DataTableEditable } from 'components';
|
||||||
import {
|
|
||||||
saveInvoke,
|
import { useWarehouseTransferTableColumns } from '../utils';
|
||||||
compose,
|
import { useFetchItemWarehouseQuantity } from './hooks';
|
||||||
updateTableCell,
|
import { useDeepCompareEffect } from 'hooks/utils';
|
||||||
updateMinEntriesLines,
|
|
||||||
updateAutoAddNewLine,
|
import { saveInvoke } from 'utils';
|
||||||
updateRemoveLineByIndex,
|
import { mutateTableCell, mutateTableRow, deleteTableRow } from './utils';
|
||||||
orderingLinesIndexes,
|
|
||||||
} from 'utils';
|
|
||||||
/**
|
/**
|
||||||
* Warehouse transfer form entries table.
|
* Warehouse transfer form entries table.
|
||||||
*/
|
*/
|
||||||
@@ -21,34 +19,63 @@ export default function WarehouseTransferFormEntriesTable({
|
|||||||
defaultEntry,
|
defaultEntry,
|
||||||
onUpdateData,
|
onUpdateData,
|
||||||
errors,
|
errors,
|
||||||
|
|
||||||
|
destinationWarehouseId,
|
||||||
|
sourceWarehouseId,
|
||||||
}) {
|
}) {
|
||||||
|
// Fetch the table row.
|
||||||
|
const { newRowMeta, setTableRow, resetTableRow, cellsLoading } =
|
||||||
|
useFetchItemWarehouseQuantity();
|
||||||
|
|
||||||
// Retrieve the warehouse transfer table columns.
|
// Retrieve the warehouse transfer table columns.
|
||||||
const columns = useWarehouseTransferTableColumns();
|
const columns = useWarehouseTransferTableColumns();
|
||||||
|
|
||||||
|
// Observes the new row meta to call `onUpdateData` callback.
|
||||||
|
useDeepCompareEffect(() => {
|
||||||
|
if (newRowMeta) {
|
||||||
|
const newRow = {
|
||||||
|
item_id: newRowMeta.itemId,
|
||||||
|
warehouses: newRowMeta.warehouses,
|
||||||
|
description: '',
|
||||||
|
quantity: 0,
|
||||||
|
};
|
||||||
|
const newRows = mutateTableRow(newRowMeta.rowIndex, newRow, entries);
|
||||||
|
|
||||||
|
saveInvoke(onUpdateData, newRows);
|
||||||
|
resetTableRow();
|
||||||
|
}
|
||||||
|
}, [newRowMeta]);
|
||||||
|
|
||||||
// Handle update data.
|
// Handle update data.
|
||||||
const handleUpdateData = React.useCallback(
|
const handleUpdateData = React.useCallback(
|
||||||
(rowIndex, columnId, value) => {
|
(rowIndex, columnId, itemId) => {
|
||||||
const newRows = compose(
|
if (columnId === 'item_id') {
|
||||||
// Update auto-adding new line.
|
setTableRow({
|
||||||
updateAutoAddNewLine(defaultEntry, ['item_id']),
|
rowIndex,
|
||||||
// Update the row value of the given row index and column id.
|
columnId,
|
||||||
updateTableCell(rowIndex, columnId, value),
|
itemId,
|
||||||
)(entries);
|
sourceWarehouseId,
|
||||||
|
destinationWarehouseId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const editCell = mutateTableCell(rowIndex, columnId, defaultEntry);
|
||||||
|
const newRows = editCell(itemId, entries);
|
||||||
|
|
||||||
saveInvoke(onUpdateData, newRows);
|
saveInvoke(onUpdateData, newRows);
|
||||||
},
|
},
|
||||||
[entries, defaultEntry, onUpdateData],
|
[
|
||||||
|
entries,
|
||||||
|
defaultEntry,
|
||||||
|
onUpdateData,
|
||||||
|
destinationWarehouseId,
|
||||||
|
sourceWarehouseId,
|
||||||
|
setTableRow,
|
||||||
|
],
|
||||||
);
|
);
|
||||||
// Handles click remove datatable row.
|
// Handles click remove datatable row.
|
||||||
const handleRemoveRow = React.useCallback(
|
const handleRemoveRow = React.useCallback(
|
||||||
(rowIndex) => {
|
(rowIndex) => {
|
||||||
const newRows = compose(
|
const newRows = deleteTableRow(rowIndex, defaultEntry, entries);
|
||||||
// Ensure minimum lines count.
|
|
||||||
updateMinEntriesLines(4, defaultEntry),
|
|
||||||
// Remove the line by the given index.
|
|
||||||
updateRemoveLineByIndex(rowIndex),
|
|
||||||
)(entries);
|
|
||||||
|
|
||||||
saveInvoke(onUpdateData, newRows);
|
saveInvoke(onUpdateData, newRows);
|
||||||
},
|
},
|
||||||
[entries, defaultEntry, onUpdateData],
|
[entries, defaultEntry, onUpdateData],
|
||||||
@@ -58,12 +85,17 @@ export default function WarehouseTransferFormEntriesTable({
|
|||||||
<DataTableEditable
|
<DataTableEditable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={entries}
|
data={entries}
|
||||||
|
cellsLoading={!!cellsLoading}
|
||||||
|
cellsLoadingCoords={cellsLoading}
|
||||||
payload={{
|
payload={{
|
||||||
items,
|
items,
|
||||||
errors: errors || [],
|
errors: errors || [],
|
||||||
updateData: handleUpdateData,
|
updateData: handleUpdateData,
|
||||||
removeRow: handleRemoveRow,
|
removeRow: handleRemoveRow,
|
||||||
autoFocus: ['item_id', 0],
|
autoFocus: ['item_id', 0],
|
||||||
|
|
||||||
|
sourceWarehouseId,
|
||||||
|
destinationWarehouseId,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
// @ts-nocheck
|
||||||
|
import React from 'react';
|
||||||
|
import { useItem } from 'hooks/query';
|
||||||
|
|
||||||
|
interface IItemMeta {
|
||||||
|
rowIndex: number;
|
||||||
|
columnId: string;
|
||||||
|
itemId: number;
|
||||||
|
|
||||||
|
sourceWarehouseId: number;
|
||||||
|
distentionWarehouseId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
type CellLoading = any;
|
||||||
|
|
||||||
|
interface IWarehouseMeta {
|
||||||
|
warehouseId: number;
|
||||||
|
warehouseQuantity: number;
|
||||||
|
warehouseQuantityFormatted: string;
|
||||||
|
}
|
||||||
|
interface IRow {
|
||||||
|
rowIndex: number;
|
||||||
|
columnId: number;
|
||||||
|
itemId: number;
|
||||||
|
|
||||||
|
warehouses: IWarehouseMeta[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the item warehouse quantity.
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const useFetchItemWarehouseQuantity = () => {
|
||||||
|
// Holds the table row meta of the given row index.
|
||||||
|
const [tableRow, setTableRow] = React.useState<IItemMeta | null>(null);
|
||||||
|
|
||||||
|
// Table cells loading coords.
|
||||||
|
const [cellsLoading, setCellsLoading] = React.useState<CellLoading | null>(
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
// Fetches the item warehouse locations.
|
||||||
|
const {
|
||||||
|
data: item,
|
||||||
|
isLoading: isItemLoading,
|
||||||
|
isSuccess: isItemSuccess,
|
||||||
|
} = useItem(tableRow?.itemId, {
|
||||||
|
enabled: !!(tableRow && tableRow.itemId),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Effects with row cells loading state.
|
||||||
|
React.useEffect(() => {
|
||||||
|
setCellsLoading(null);
|
||||||
|
|
||||||
|
if (isItemLoading && tableRow) {
|
||||||
|
setCellsLoading([
|
||||||
|
[tableRow.rowIndex, 'quantity'],
|
||||||
|
[tableRow.rowIndex, 'source_warehouse'],
|
||||||
|
[tableRow.rowIndex, 'destination_warehouse'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}, [isItemLoading, tableRow]);
|
||||||
|
|
||||||
|
// New table row meta.
|
||||||
|
const newRowMeta = React.useMemo(() => {
|
||||||
|
return isItemSuccess
|
||||||
|
? {
|
||||||
|
...tableRow,
|
||||||
|
warehouses: [],
|
||||||
|
}
|
||||||
|
: null;
|
||||||
|
}, [isItemSuccess, tableRow]);
|
||||||
|
|
||||||
|
// Reset the table row.
|
||||||
|
const resetTableRow = React.useCallback(() => {
|
||||||
|
setTableRow(null);
|
||||||
|
setCellsLoading(null);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return {
|
||||||
|
setTableRow,
|
||||||
|
resetTableRow,
|
||||||
|
|
||||||
|
cellsLoading,
|
||||||
|
newRowMeta,
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -2,16 +2,25 @@ import React from 'react';
|
|||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import { Intent } from '@blueprintjs/core';
|
import { Intent } from '@blueprintjs/core';
|
||||||
import { useFormikContext } from 'formik';
|
|
||||||
import { AppToaster } from 'components';
|
|
||||||
import { omit } from 'lodash';
|
import { omit } from 'lodash';
|
||||||
|
import { useFormikContext } from 'formik';
|
||||||
|
import * as R from 'ramda';
|
||||||
|
|
||||||
|
import { AppToaster } from 'components';
|
||||||
|
import {
|
||||||
|
orderingLinesIndexes,
|
||||||
|
updateAutoAddNewLine,
|
||||||
|
updateTableCell,
|
||||||
|
} from 'utils';
|
||||||
import {
|
import {
|
||||||
compose,
|
compose,
|
||||||
transformToForm,
|
transformToForm,
|
||||||
repeatValue,
|
repeatValue,
|
||||||
transactionNumber,
|
transactionNumber,
|
||||||
defaultFastFieldShouldUpdate,
|
defaultFastFieldShouldUpdate,
|
||||||
|
updateTableRow,
|
||||||
|
updateMinEntriesLines,
|
||||||
|
updateRemoveLineByIndex,
|
||||||
} from 'utils';
|
} from 'utils';
|
||||||
|
|
||||||
// import { defaultFastFieldShouldUpdate } from 'utils';
|
// import { defaultFastFieldShouldUpdate } from 'utils';
|
||||||
@@ -88,6 +97,10 @@ export const useObserveTransferNoSettings = (prefix, nextNumber) => {
|
|||||||
export const entriesFieldShouldUpdate = (newProps, oldProps) => {
|
export const entriesFieldShouldUpdate = (newProps, oldProps) => {
|
||||||
return (
|
return (
|
||||||
newProps.items !== oldProps.items ||
|
newProps.items !== oldProps.items ||
|
||||||
|
newProps.formik.values.from_warehouse_id !==
|
||||||
|
oldProps.formik.values.from_warehouse_id ||
|
||||||
|
newProps.formik.values.to_warehouse_id !==
|
||||||
|
oldProps.formik.values.to_warehouse_id ||
|
||||||
defaultFastFieldShouldUpdate(newProps, oldProps)
|
defaultFastFieldShouldUpdate(newProps, oldProps)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -102,7 +115,7 @@ export function transformValueToRequest(values) {
|
|||||||
return {
|
return {
|
||||||
...values,
|
...values,
|
||||||
entries: entries.map((entry) => ({
|
entries: entries.map((entry) => ({
|
||||||
...omit(entry, ['destination_warehouse', 'source_warehouse']),
|
...omit(entry, ['warehouses']),
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -122,3 +135,42 @@ export const transformErrors = (errors, { setErrors }) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mutates table cell.
|
||||||
|
* @param {*} rowIndex
|
||||||
|
* @param {*} columnId
|
||||||
|
* @param {*} defaultEntry
|
||||||
|
* @param {*} value
|
||||||
|
* @param {*} entries
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const mutateTableCell = R.curry(
|
||||||
|
(rowIndex, columnId, defaultEntry, value, entries) => {
|
||||||
|
return compose(
|
||||||
|
// Update auto-adding new line.
|
||||||
|
updateAutoAddNewLine(defaultEntry, ['item_id']),
|
||||||
|
// Update the row value of the given row index and column id.
|
||||||
|
updateTableCell(rowIndex, columnId, value),
|
||||||
|
)(entries);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compose table rows when insert a new row to table rows.
|
||||||
|
*/
|
||||||
|
export const mutateTableRow = R.curry((rowIndex, newRow, rows) => {
|
||||||
|
return compose(orderingLinesIndexes, updateTableRow(rowIndex, newRow))(rows);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the table row from the given rows.
|
||||||
|
*/
|
||||||
|
export const deleteTableRow = R.curry((rowIndex, defaultEntry, rows) => {
|
||||||
|
return compose(
|
||||||
|
// Ensure minimum lines count.
|
||||||
|
updateMinEntriesLines(4, defaultEntry),
|
||||||
|
// Remove the line by the given index.
|
||||||
|
updateRemoveLineByIndex(rowIndex),
|
||||||
|
)(rows);
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
|
import { find, get } from 'lodash';
|
||||||
import { Tooltip, Button, Intent, Position } from '@blueprintjs/core';
|
import { Tooltip, Button, Intent, Position } from '@blueprintjs/core';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -43,6 +44,26 @@ export function ActionsCellRenderer({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function SourceWarehouseAccessorCell({ value, row: { original }, payload }) {
|
||||||
|
const warehouse = find(
|
||||||
|
original.warehouses,
|
||||||
|
(w) => w.warehouseId === payload.sourceWarehouseId,
|
||||||
|
);
|
||||||
|
return get(warehouse, 'warehouseQuantityFormatted', '0');
|
||||||
|
}
|
||||||
|
|
||||||
|
function DistentionWarehouseAccessorCell({
|
||||||
|
value,
|
||||||
|
row: { original },
|
||||||
|
payload,
|
||||||
|
}) {
|
||||||
|
const warehouse = find(
|
||||||
|
original.warehouses,
|
||||||
|
(w) => w.warehouseId === payload.distentionWarehouseId,
|
||||||
|
);
|
||||||
|
return get(warehouse, 'warehouseQuantityFormatted', '0');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves warehouse transfer table columns.
|
* Retrieves warehouse transfer table columns.
|
||||||
* @returns
|
* @returns
|
||||||
@@ -82,6 +103,7 @@ export const useWarehouseTransferTableColumns = () => {
|
|||||||
Header: 'Source Warehouse',
|
Header: 'Source Warehouse',
|
||||||
accessor: 'source_warehouse',
|
accessor: 'source_warehouse',
|
||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
|
Cell: SourceWarehouseAccessorCell,
|
||||||
align: 'right',
|
align: 'right',
|
||||||
width: 120,
|
width: 120,
|
||||||
},
|
},
|
||||||
@@ -89,6 +111,7 @@ export const useWarehouseTransferTableColumns = () => {
|
|||||||
id: 'destination_warehouse',
|
id: 'destination_warehouse',
|
||||||
Header: 'Destination Warehouse',
|
Header: 'Destination Warehouse',
|
||||||
accessor: 'destination_warehouse',
|
accessor: 'destination_warehouse',
|
||||||
|
Cell: DistentionWarehouseAccessorCell,
|
||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
align: 'right',
|
align: 'right',
|
||||||
width: 120,
|
width: 120,
|
||||||
|
|||||||
Reference in New Issue
Block a user