From 971a4b1c56449d2dee4bb559f7981f0485322966 Mon Sep 17 00:00:00 2001 From: olewandowski1 Date: Fri, 21 Aug 2026 11:41:50 +0200 Subject: [PATCH] OBLS-837 Fix discrete picking QA findings Index product codes so search by code works, return the picker to the screen the session started from instead of the batch pick type screen, reset the stack on completion so the finished task is no longer reachable with the back arrow, and show a single loading indicator per state. Consume the order level pick progress from the pick task API so a partially picked order reads In progress with the lines left to pick. --- .../Picking/DiscretePickingListScreen.tsx | 23 ++++-- .../Picking/DiscretePickingOrderCard.tsx | 51 ++++++------ src/screens/Picking/PickingContext.tsx | 19 +++++ .../Picking/PickingPickLocationScreen.tsx | 16 +++- .../PickingPickOutboundContainerScreen.tsx | 10 ++- .../Picking/PickingPickQuantityScreen.tsx | 11 ++- .../PickingPickStagingLocationScreen.tsx | 10 +-- src/screens/Picking/discretePickingLib.ts | 49 ++++++++--- src/screens/Picking/discretePickingStyles.ts | 3 +- src/screens/Picking/lib.ts | 82 +++++++++++-------- src/types/picking.ts | 19 ++++- 11 files changed, 196 insertions(+), 97 deletions(-) diff --git a/src/screens/Picking/DiscretePickingListScreen.tsx b/src/screens/Picking/DiscretePickingListScreen.tsx index 8d037117..5a3b02be 100644 --- a/src/screens/Picking/DiscretePickingListScreen.tsx +++ b/src/screens/Picking/DiscretePickingListScreen.tsx @@ -38,6 +38,7 @@ export default function DiscretePickingListScreen() { const [isRefreshing, setIsRefreshing] = useState(false); const [isPullRefreshing, setIsPullRefreshing] = useState(false); const [hasLoaded, setHasLoaded] = useState(false); + const [isStartingOrder, setIsStartingOrder] = useState(false); const fetchOrders = useCallback( (excludeAssignedRequisitionsParam: boolean, fromPull = false) => { @@ -61,6 +62,7 @@ export default function DiscretePickingListScreen() { useFocusEffect( useCallback(() => { + setIsStartingOrder(false); fetchOrders(excludeAssignedRequisitions); }, [fetchOrders, excludeAssignedRequisitions]) ); @@ -75,14 +77,19 @@ export default function DiscretePickingListScreen() { ); const handleOrderPress = (order: DiscretePickingOrder) => { + // startOrderSession shows a full-screen loader, so suppress the search bar spinner. + setIsStartingOrder(true); startOrderSession(order.requisitionId).then((success) => { if (success) { navigate('PickingPickLocation'); + return; } + setIsStartingOrder(false); }); }; - const isLoadingList = !hasLoaded || isPullRefreshing; + // Skeleton covers the first load only, later refreshes keep the list on screen. + const isLoadingList = !hasLoaded; const chips: { value: QueueTypeFilter; label: string; count: number }[] = [ { value: ALL_QUEUE_TYPES, label: 'All', count: sortedOrders.length }, @@ -97,7 +104,7 @@ export default function DiscretePickingListScreen() { placeholder="Search by order, customer, or product" resetSearch={() => setSearchTerm('')} accessibilityLabel="Search open orders" - loading={hasLoaded && isRefreshing} + loading={hasLoaded && isRefreshing && !isPullRefreshing && !isStartingOrder} onSearchTermSubmit={setSearchTerm} /> @@ -134,7 +141,7 @@ export default function DiscretePickingListScreen() { {!isLoadingList && ( setExcludeAssignedRequisitions(!value)} /> @@ -149,14 +156,16 @@ export default function DiscretePickingListScreen() { data={visibleOrders} keyExtractor={(order) => order.requisitionId} renderItem={({ item }) => ( - + )} keyboardShouldPersistTaps="handled" keyboardDismissMode="on-drag" contentContainerStyle={styles.listContent} - // The skeleton above owns the pull-to-refresh loading state, so the spinner - // only needs to retract once the gesture hands off to it. - refreshing={false} + refreshing={isPullRefreshing} ListEmptyComponent={ = DELIVERY_TYPES.reduce + + {inProgress ? 'In progress' : 'Ready'} + + + ); +} + type Props = { order: DiscretePickingOrder; + showAssignee?: boolean; onPress: (order: DiscretePickingOrder) => void; }; -export default function DiscretePickingOrderCard({ order, onPress }: Props) { +export default function DiscretePickingOrderCard({ order, showAssignee = false, onPress }: Props) { const deliveryTypeLabel = order.deliveryTypeCode ? DELIVERY_TYPE_LABELS[order.deliveryTypeCode] ?? order.deliveryTypeCode : null; const lineCountLabel = order.taskCount === 1 ? 'Line' : 'Lines'; + const lineCountValue = + order.openTaskCount < order.taskCount ? `${order.openTaskCount} / ${order.taskCount} Left` : `${order.taskCount}`; const assigneeName = order.assignee ? `${order.assignee.firstName} ${order.assignee.lastName}`.trim() : null; return ( @@ -33,25 +56,7 @@ export default function DiscretePickingOrderCard({ order, onPress }: Props) { {order.requisitionNumber ?? HYPHEN} - - - {order.inProgress ? 'In progress' : 'Ready'} - - + @@ -73,13 +78,13 @@ export default function DiscretePickingOrderCard({ order, onPress }: Props) { ) : null} - {lineCountLabel}: {order.taskCount} + {lineCountLabel}: {lineCountValue} - {assigneeName ? ( + {showAssignee && assigneeName ? ( - Assigned to: {assigneeName} + Assigned To: {assigneeName} ) : null} diff --git a/src/screens/Picking/PickingContext.tsx b/src/screens/Picking/PickingContext.tsx index f58e507d..83dafdce 100644 --- a/src/screens/Picking/PickingContext.tsx +++ b/src/screens/Picking/PickingContext.tsx @@ -14,6 +14,14 @@ import { } from '../../redux/actions/picking'; import { DeliveryType, PickTask } from '../../types/picking'; +// Which screen the session was started from, used to send the picker back there when it ends. +export type PickingEntryPoint = 'BATCH' | 'DISCRETE'; + +const HOME_ROUTE_BY_ENTRY_POINT: Record = { + BATCH: 'PickingPickType', + DISCRETE: 'DiscretePickingList' +}; + type PickingContextType = { /** The list of all tasks for this session */ tasks: PickTask[]; @@ -27,6 +35,10 @@ type PickingContextType = { currentTask: PickTask | undefined; /** Total number of tasks in the session */ allTasksCount: number; + /** The screen this session was started from */ + entryPoint: PickingEntryPoint; + /** Route to return to when the session ends, derived from the entry point */ + homeRoute: string; /** Starts a new picking session, returns whether it was successful */ startSession: (deliveryType: DeliveryType, ordersCount: number) => Promise; /** Starts a picking session for a single order (discrete picking), returns whether it was successful */ @@ -65,11 +77,14 @@ const PickingContext = React.createContext(undef export function PickingProvider({ children }: { children: React.ReactNode }) { const [tasks, setTasks] = React.useState([]); const [currentTaskIndex, setCurrentTaskIndex] = React.useState(0); + const [entryPoint, setEntryPoint] = React.useState('BATCH'); const dispatch = useDispatch(); + const homeRoute = HOME_ROUTE_BY_ENTRY_POINT[entryPoint]; const allTasksCount = tasks.length; const currentTask = allTasksCount > 0 && currentTaskIndex < allTasksCount ? tasks[currentTaskIndex] : undefined; const startSession = async (deliveryType: DeliveryType, ordersCount: number): Promise => { + setEntryPoint('BATCH'); return new Promise((resolve) => { dispatch( getPickTasksAction({ deliveryTypeCode: deliveryType.code, ordersCount }, ({ response, errorMessage }) => { @@ -101,6 +116,7 @@ export function PickingProvider({ children }: { children: React.ReactNode }) { }; const startOrderSession = async (requisitionId: string): Promise => { + setEntryPoint('DISCRETE'); return new Promise((resolve) => { dispatch( getPickTasksByRequisitionAction(requisitionId, (res) => { @@ -210,6 +226,7 @@ export function PickingProvider({ children }: { children: React.ReactNode }) { onPress: () => resetToRoutes([ { name: 'Drawer', params: { screen: 'Dashboard' } }, + { name: homeRoute }, { name: 'PickingPickStagingLocation' } ]) } @@ -288,6 +305,8 @@ export function PickingProvider({ children }: { children: React.ReactNode }) { setCurrentTaskIndex, currentTask, allTasksCount, + entryPoint, + homeRoute, startSession, startOrderSession, pickCurrentTask, diff --git a/src/screens/Picking/PickingPickLocationScreen.tsx b/src/screens/Picking/PickingPickLocationScreen.tsx index 7d7582a0..1f771fa0 100644 --- a/src/screens/Picking/PickingPickLocationScreen.tsx +++ b/src/screens/Picking/PickingPickLocationScreen.tsx @@ -8,7 +8,7 @@ import { ScannerInput } from '../../components/ScannerInput'; import { SearchButton } from '../../components/SearchButton'; import { useSearchButton } from '../../components/SearchButton/useSearchButton'; import { EMPTY_STRING, HYPHEN } from '../../constants'; -import { navigate } from '../../NavigationService'; +import { navigate, resetToRoutes } from '../../NavigationService'; import { RootState } from '../../redux/reducers'; import { parseFromISODateToLocaleString } from '../../utils/utils'; import { CustomerDetails } from './CustomerDetails'; @@ -17,8 +17,15 @@ import { ReallocateModal } from './ReallocateModal'; import styles from './styles'; export default function PickingPickLocationScreen() { - const { currentTask, currentTaskIndex, allTasksCount, startPickTask, revalidateCurrentTask, resetSession } = - usePickingContext(); + const { + currentTask, + currentTaskIndex, + allTasksCount, + startPickTask, + revalidateCurrentTask, + resetSession, + homeRoute + } = usePickingContext(); const [pickLocationBarcode, setPickLocationBarcode] = React.useState(EMPTY_STRING); const [isReallocateModalOpen, setIsReallocateModalOpen] = React.useState(false); const { allowReallocationDuringPicking } = useSelector((state: RootState) => state.settingsReducer); @@ -150,7 +157,8 @@ export default function PickingPickLocationScreen() { onAllocated={() => { setIsReallocateModalOpen(false); resetSession(); - navigate('PickingPickType'); + // Reset so the finished task screens are not left behind the back arrow. + resetToRoutes([{ name: 'Drawer', params: { screen: 'Dashboard' } }, { name: homeRoute }]); }} /> )} diff --git a/src/screens/Picking/PickingPickOutboundContainerScreen.tsx b/src/screens/Picking/PickingPickOutboundContainerScreen.tsx index 47a9ebd0..27dbc6ad 100644 --- a/src/screens/Picking/PickingPickOutboundContainerScreen.tsx +++ b/src/screens/Picking/PickingPickOutboundContainerScreen.tsx @@ -30,7 +30,8 @@ export default function PickingPickOutboundContainerScreen() { allTasksCount, revalidateCurrentTask, goToNextTask, - revalidateTasksForRequisition + revalidateTasksForRequisition, + homeRoute } = usePickingContext(); const { params } = useRoute(); const parsedQuantityPicked = params?.quantityPicked ? Number(params.quantityPicked) : undefined; @@ -72,13 +73,14 @@ export default function PickingPickOutboundContainerScreen() { const omitStagingLocationStep = currentTask.quantityPicked + parsedQuantityPicked < currentTask.quantityRequired; - revalidateTaskAndProceed( + revalidateTaskAndProceed({ revalidateCurrentTask, currentTaskIndex, allTasksCount, goToNextTask, + homeRoute, omitStagingLocationStep - ); + }); } }, params?.reasonCode?.name @@ -93,7 +95,7 @@ export default function PickingPickOutboundContainerScreen() { return; } - revalidateTaskAndProceed(revalidateCurrentTask, currentTaskIndex, allTasksCount, goToNextTask); + revalidateTaskAndProceed({ revalidateCurrentTask, currentTaskIndex, allTasksCount, goToNextTask, homeRoute }); }); setOutboundContainerId(EMPTY_STRING); diff --git a/src/screens/Picking/PickingPickQuantityScreen.tsx b/src/screens/Picking/PickingPickQuantityScreen.tsx index be49010f..723ba768 100644 --- a/src/screens/Picking/PickingPickQuantityScreen.tsx +++ b/src/screens/Picking/PickingPickQuantityScreen.tsx @@ -19,7 +19,8 @@ import { usePickingContext } from './PickingContext'; import styles from './styles'; export default function PickingPickQuantityScreen() { - const { tasks, currentTask, currentTaskIndex, allTasksCount, shortPickTask, goToNextTask } = usePickingContext(); + const { tasks, currentTask, currentTaskIndex, allTasksCount, shortPickTask, goToNextTask, homeRoute } = + usePickingContext(); const dispatch = useDispatch(); const isFocused = useIsFocused(); @@ -105,7 +106,13 @@ export default function PickingPickQuantityScreen() { index !== currentTaskIndex && task.quantityPicked < task.quantityRequired && !task.reasonCode ); // Skip revalidation: task is closed server-side, and GET /pick-tasks/:id 404s if the requisition is canceled. - proceedToNextOrComplete(currentTaskIndex, allTasksCount, goToNextTask, omitStagingLocationStep); + proceedToNextOrComplete({ + currentTaskIndex, + allTasksCount, + goToNextTask, + homeRoute, + omitStagingLocationStep + }); }, reasonCode?.name ); diff --git a/src/screens/Picking/PickingPickStagingLocationScreen.tsx b/src/screens/Picking/PickingPickStagingLocationScreen.tsx index 86fd8820..ff790fa0 100644 --- a/src/screens/Picking/PickingPickStagingLocationScreen.tsx +++ b/src/screens/Picking/PickingPickStagingLocationScreen.tsx @@ -18,7 +18,7 @@ import styles from './styles'; const SKIP_STAGING_LOCATION_VALIDATION = true; export default function PickingPickStagingLocationScreen() { - const { tasks, dropCurrentTask, dropCurrentTaskAtStagingLocation, resetSession, setCurrentTaskIndex } = + const { tasks, dropCurrentTask, dropCurrentTaskAtStagingLocation, resetSession, setCurrentTaskIndex, homeRoute } = usePickingContext(); const [stagingLocationNumber, setStagingLocationNumber] = React.useState(EMPTY_STRING); const [currentUniqueIndex, setCurrentUniqueIndex] = React.useState(0); @@ -37,9 +37,9 @@ export default function PickingPickStagingLocationScreen() { if (!currentTask) { // No tasks left at all, return to home Alert.alert('Staging', 'No more tasks available for staging drop.'); - resetToRoutes([{ name: 'Drawer', params: { screen: 'Dashboard' } }, { name: 'PickingPickType' }]); + resetToRoutes([{ name: 'Drawer', params: { screen: 'Dashboard' } }, { name: homeRoute }]); } - }, [currentTask, tasks.length, setCurrentTaskIndex, uniqueTasks.length, tasks]); + }, [currentTask, tasks.length, setCurrentTaskIndex, uniqueTasks.length, tasks, homeRoute]); // Requires the scanned location to match the one suggested by the task. Used when SKIP_STAGING_LOCATION_VALIDATION is false. function handleScan(locationId: string) { @@ -78,7 +78,7 @@ export default function PickingPickStagingLocationScreen() { text: 'OK', onPress: () => { resetSession(); - resetToRoutes([{ name: 'Drawer', params: { screen: 'Dashboard' } }, { name: 'PickingPickType' }]); + resetToRoutes([{ name: 'Drawer', params: { screen: 'Dashboard' } }, { name: homeRoute }]); } } ]); @@ -117,7 +117,7 @@ export default function PickingPickStagingLocationScreen() { text: 'OK', onPress: () => { resetSession(); - resetToRoutes([{ name: 'Drawer', params: { screen: 'Dashboard' } }, { name: 'PickingPickType' }]); + resetToRoutes([{ name: 'Drawer', params: { screen: 'Dashboard' } }, { name: homeRoute }]); } } ]); diff --git a/src/screens/Picking/discretePickingLib.ts b/src/screens/Picking/discretePickingLib.ts index 70816d6c..82f5b4af 100644 --- a/src/screens/Picking/discretePickingLib.ts +++ b/src/screens/Picking/discretePickingLib.ts @@ -1,4 +1,10 @@ -import { DeliveryTypeCode, DiscretePickingOrder, PickTask, PickTaskStatus } from '../../types/picking'; +import { + DeliveryTypeCode, + DiscretePickingOrder, + OrderPickStatusCode, + PickTask, + PickTaskStatus +} from '../../types/picking'; import { DELIVERY_TYPES } from './constants'; export const ALL_QUEUE_TYPES = 'ALL' as const; @@ -22,7 +28,17 @@ function deliveryTypePriority(code?: DeliveryTypeCode): number { */ export function groupTasksIntoOrders(tasks: PickTask[]): DiscretePickingOrder[] { const ordersById = new Map(); - const productNamesById = new Map>(); + const searchTokensById = new Map>(); + const returnedTaskCountById = new Map(); + + const collectSearchTokens = (requisitionId: string, task: PickTask) => { + const tokens = searchTokensById.get(requisitionId); + [task.product?.name, task.product?.productCode].forEach((token) => { + if (token) { + tokens?.add(token); + } + }); + }; tasks.forEach((task) => { const requisitionId = task.requisitionId; @@ -30,8 +46,6 @@ export function groupTasksIntoOrders(tasks: PickTask[]): DiscretePickingOrder[] return; } - const productName = task.product?.name; - if (!ordersById.has(requisitionId)) { ordersById.set(requisitionId, { requisitionId, @@ -41,26 +55,35 @@ export function groupTasksIntoOrders(tasks: PickTask[]): DiscretePickingOrder[] deliveryTypeCode: task.deliveryTypeCode, assignee: task.assignee, priority: task.priority, - taskCount: 0, + // Prefer the order wide counts, since this response only carries the open lines. + taskCount: task.orderTotalTaskCount ?? 0, + openTaskCount: task.orderOpenTaskCount ?? 0, inProgress: false, searchIndex: '' }); - productNamesById.set(requisitionId, new Set()); + searchTokensById.set(requisitionId, new Set()); + returnedTaskCountById.set(requisitionId, 0); } const order = ordersById.get(requisitionId) as DiscretePickingOrder; - order.taskCount += 1; - if (task.status === PickTaskStatus.PICKING) { - order.inProgress = true; + returnedTaskCountById.set(requisitionId, (returnedTaskCountById.get(requisitionId) ?? 0) + 1); + // Tasks are only assigned once picking starts, so take the first assignee found. + if (!order.assignee && task.assignee) { + order.assignee = task.assignee; } - if (productName) { - productNamesById.get(requisitionId)?.add(productName); + if (task.status === PickTaskStatus.PICKING || task.orderPickStatusCode === OrderPickStatusCode.PARTIALLY_PICKED) { + order.inProgress = true; } + collectSearchTokens(requisitionId, task); }); return Array.from(ordersById.values()).map((order) => { - const productNames = Array.from(productNamesById.get(order.requisitionId) ?? []); - order.searchIndex = [order.requisitionNumber, order.destination, ...productNames] + const returnedTaskCount = returnedTaskCountById.get(order.requisitionId) ?? 0; + order.taskCount = order.taskCount || returnedTaskCount; + order.openTaskCount = order.openTaskCount || returnedTaskCount; + + const searchTokens = Array.from(searchTokensById.get(order.requisitionId) ?? []); + order.searchIndex = [order.requisitionNumber, order.destination, ...searchTokens] .filter(Boolean) .join(' ') .toLowerCase(); diff --git a/src/screens/Picking/discretePickingStyles.ts b/src/screens/Picking/discretePickingStyles.ts index 1f6c0ea7..67f97725 100644 --- a/src/screens/Picking/discretePickingStyles.ts +++ b/src/screens/Picking/discretePickingStyles.ts @@ -46,7 +46,8 @@ export default StyleSheet.create({ overflow: 'hidden' }, showAssignedToggle: { - paddingHorizontal: Theme.spacing.medium + paddingHorizontal: Theme.spacing.medium, + marginTop: -Theme.spacing.small }, filterChipSkeleton: { height: 32, diff --git a/src/screens/Picking/lib.ts b/src/screens/Picking/lib.ts index 02751cc6..1892a6d5 100644 --- a/src/screens/Picking/lib.ts +++ b/src/screens/Picking/lib.ts @@ -2,12 +2,39 @@ import { Alert } from 'react-native'; import { navigate, resetToRoutes } from '../../NavigationService'; import { PickTask } from '../../types/picking'; -export function proceedToNextOrComplete( - currentTaskIndex: number, - allTasksCount: number, - goToNextTask: () => void, - omitStagingLocationStep?: boolean -) { +type PickingFlowNavigation = { + currentTaskIndex: number; + allTasksCount: number; + goToNextTask: () => void; + /** Screen the session started from, reset to when it ends so finished screens are unreachable */ + homeRoute: string; + omitStagingLocationStep?: boolean; +}; + +function returnHome(homeRoute: string) { + resetToRoutes([{ name: 'Drawer', params: { screen: 'Dashboard' } }, { name: homeRoute }]); +} + +function alertShortPickWithoutReasonCode(homeRoute: string) { + Alert.alert( + 'Short Pick Without Reason Code', + 'You have completed all picks with a short pick without a reason code. This task will remain available to pick.', + [ + { + text: 'OK', + onPress: () => returnHome(homeRoute) + } + ] + ); +} + +export function proceedToNextOrComplete({ + currentTaskIndex, + allTasksCount, + goToNextTask, + homeRoute, + omitStagingLocationStep +}: PickingFlowNavigation) { if (currentTaskIndex + 1 < allTasksCount) { goToNextTask(); navigate('PickingPickLocation'); @@ -15,16 +42,7 @@ export function proceedToNextOrComplete( } if (omitStagingLocationStep) { - Alert.alert( - 'Short Pick Without Reason Code', - 'You have completed all picks with a short pick without a reason code. This task will remain available to pick. You will be redirected to the Pick Type screen.', - [ - { - text: 'OK', - onPress: () => navigate('PickingPickType') - } - ] - ); + alertShortPickWithoutReasonCode(homeRoute); return; } @@ -34,20 +52,23 @@ export function proceedToNextOrComplete( onPress: () => resetToRoutes([ { name: 'Drawer', params: { screen: 'Dashboard' } }, - { name: 'PickingPickType' }, + { name: homeRoute }, { name: 'PickingPickStagingLocation' } ]) } ]); } -export function revalidateTaskAndProceed( - revalidateCurrentTask: (callback: (revalidatedTask: PickTask | undefined) => void) => void, - currentTaskIndex: number, - allTasksCount: number, - goToNextTask: () => void, - omitStagingLocationStep?: boolean -) { +export function revalidateTaskAndProceed({ + revalidateCurrentTask, + currentTaskIndex, + allTasksCount, + goToNextTask, + homeRoute, + omitStagingLocationStep +}: PickingFlowNavigation & { + revalidateCurrentTask: (callback: (revalidatedTask: PickTask | undefined) => void) => void; +}) { revalidateCurrentTask((revalidatedTask) => { if (!revalidatedTask) { Alert.alert('Error', 'Failed to revalidate the current pick task after picking.'); @@ -57,19 +78,10 @@ export function revalidateTaskAndProceed( const isLastTask = currentTaskIndex + 1 >= allTasksCount; if (isLastTask && omitStagingLocationStep) { - Alert.alert( - 'Short Pick Without Reason Code', - 'You have completed all picks with a short pick without a reason code. This task will remain available to pick. You will be redirected to the Pick Type screen.', - [ - { - text: 'OK', - onPress: () => navigate('PickingPickType') - } - ] - ); + alertShortPickWithoutReasonCode(homeRoute); return; } - proceedToNextOrComplete(currentTaskIndex, allTasksCount, goToNextTask); + proceedToNextOrComplete({ currentTaskIndex, allTasksCount, goToNextTask, homeRoute }); }); } diff --git a/src/types/picking.ts b/src/types/picking.ts index 1abfd9f8..269cbb70 100644 --- a/src/types/picking.ts +++ b/src/types/picking.ts @@ -19,6 +19,12 @@ export enum DeliveryTypeCode { DEFAULT = 'DEFAULT' } +export enum OrderPickStatusCode { + NOT_PICKED = 'NOT_PICKED', + PARTIALLY_PICKED = 'PARTIALLY_PICKED', + PICKED = 'PICKED' +} + export enum PickTaskStatus { PENDING = 'PENDING', PICKING = 'PICKING', @@ -71,6 +77,11 @@ export type PickTask = { reasonCode?: string | null; status: PickTaskStatus; + /** Progress of the whole order, counting lines this status filtered response leaves out */ + orderTotalTaskCount?: number | null; + orderOpenTaskCount?: number | null; + orderPickStatusCode?: OrderPickStatusCode | null; + dateRequested?: string | null; dateAssigned?: string | null; dateStarted?: string | null; @@ -97,11 +108,13 @@ export type DiscretePickingOrder = { assignee?: Person | null; /** requisition.priority (lower = higher priority) */ priority?: number; - /** number of open pick tasks (line items) in this order */ + /** total number of pick tasks (line items) in this order */ taskCount: number; - /** true when at least one task is already being picked */ + /** number of pick tasks (line items) still left to pick */ + openTaskCount: number; + /** true when at least one line of this order has already been started or picked */ inProgress: boolean; - /** lowercased blob of order number, destination and product names for real-time search */ + /** lowercased blob of order number, destination, product names and product codes for search */ searchIndex: string; };