import React from 'react'; import moment from 'moment'; import intl from 'react-intl-universal'; import { Intent } from '@blueprintjs/core'; import { keyBy, omit } from 'lodash'; import { useFormikContext } from 'formik'; import * as R from 'ramda'; import { useWatch } from 'hooks/utils'; import { useWarehouseTransferFormContext } from './WarehouseTransferFormProvider'; import { AppToaster } from 'components'; import { orderingLinesIndexes, updateAutoAddNewLine, updateTableCell, } from 'utils'; import { compose, transformToForm, repeatValue, transactionNumber, defaultFastFieldShouldUpdate, updateTableRow, updateMinEntriesLines, updateRemoveLineByIndex, } from 'utils'; import { updateItemsEntriesTotal, ensureEntriesHaveEmptyLine, } from 'containers/Entries/utils'; export const MIN_LINES_NUMBER = 1; // Default warehouse transfer entry. export const defaultWarehouseTransferEntry = { index: 0, item_id: '', source_warehouse: '', destination_warehouse: '', description: '', quantity: '', }; // Default warehouse transfer entry. export const defaultWarehouseTransfer = { date: moment(new Date()).format('YYYY-MM-DD'), transaction_number: '', from_warehouse_id: '', to_warehouse_id: '', reason: '', transfer_initiated: '', transfer_delivered: '', entries: [...repeatValue(defaultWarehouseTransferEntry, MIN_LINES_NUMBER)], }; export const ITEMS_FILTER_ROLES_QUERY = JSON.stringify([ { fieldKey: 'type', comparator: 'is', value: 'inventory', index: 1 }, ]); /** * Transform warehouse transfer to initial values in edit mode. */ export function transformToEditForm(warehouse) { const initialEntries = [ ...warehouse.entries.map((warehouse) => ({ ...transformToForm(warehouse, defaultWarehouseTransferEntry), })), ...repeatValue( defaultWarehouseTransferEntry, Math.max(MIN_LINES_NUMBER - warehouse.entries.length, 0), ), ]; const entries = compose( ensureEntriesHaveEmptyLine(defaultWarehouseTransferEntry), updateItemsEntriesTotal, )(initialEntries); return { ...transformToForm(warehouse, defaultWarehouseTransfer), entries, }; } /** * Syncs transfer no. settings with form. */ export const useObserveTransferNoSettings = (prefix, nextNumber) => { const { setFieldValue } = useFormikContext(); React.useEffect(() => { const transferNo = transactionNumber(prefix, nextNumber); setFieldValue('transaction_number', transferNo); }, [setFieldValue, prefix, nextNumber]); }; /** * Detarmines warehouse entries field when should update. */ export const entriesFieldShouldUpdate = (newProps, oldProps) => { return ( 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) ); }; /** * Transformes the form values to request body values. */ export function transformValueToRequest(values) { const entries = values.entries.filter( (item) => item.item_id && item.quantity, ); return { ...values, entries: entries.map((entry) => ({ ...omit(entry, [ 'warehouses', 'destination_warehouse', 'source_warehouse', 'cost', ]), })), }; } /** * Transformes the response errors types. */ export const transformErrors = (errors, { setErrors }) => { if ( errors.some(({ type }) => type === 'WAREHOUSES_TRANSFER_SHOULD_NOT_BE_SAME') ) { AppToaster.show({ message: intl.get( 'warehouse_transfer.error.could_not_transfer_item_from_source_to_destination', ), intent: Intent.DANGER, }); } }; /** * 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(MIN_LINES_NUMBER, defaultEntry), // Remove the line by the given index. updateRemoveLineByIndex(rowIndex), )(rows); }); /** * Watches the inventory items cost and sets the cost to form entries. */ export function useWatchItemsCostSetCostEntries() { const { isItemsCostSuccess, inventoryItemsCost } = useWarehouseTransferFormContext(); const { setFieldValue, values: { entries }, } = useFormikContext(); // Transformes items cost map by item id. const itemsCostByItemId = React.useMemo( () => keyBy(inventoryItemsCost, 'item_id'), [inventoryItemsCost], ); // Observes the inventory items cost and set form entries with cost. useWatch(() => { if (!isItemsCostSuccess) return; const newEntries = entries.map((entry) => { const costEntry = itemsCostByItemId[entry.item_id]; return entry.item_id ? { ...entry, cost: costEntry?.average, } : entry; }); setFieldValue('entries', newEntries); }, inventoryItemsCost); }