| @@ -601,77 +601,64 @@ const JobPickExecution: React.FC<Props> = ({ filterArgs, onBack }) => { | |||
| }, [currentPickOrderId, currentUserId, handleUnassign, onBack]); | |||
| const handleSubmitAllScanned = useCallback(async () => { | |||
| const scannedLots = combinedLotData.filter(lot => | |||
| lot.matchStatus === 'scanned'|| | |||
| lot.stockOutLineStatus === 'completed' | |||
| // A) 先算每个 item 的 actual pick 总量(仅给 Confirm All 用) | |||
| const itemTotalActualPickQtyMap = combinedLotData.reduce<Record<string, number>>((acc, lot) => { | |||
| const itemKey = `${lot.pickOrderId}-${lot.pickOrderLineId}-${lot.itemId}`; // 或 getItemKey(lot) | |||
| const qty = Number(lot.actualPickQty ?? 0); | |||
| acc[itemKey] = (acc[itemKey] ?? 0) + qty; | |||
| return acc; | |||
| }, {}); | |||
| // B) 只选 pending(不要 completed) | |||
| const pendingLots = combinedLotData.filter( | |||
| (lot) => String(lot.matchStatus || "").toLowerCase() === "pending" | |||
| ); | |||
| if (scannedLots.length === 0) { | |||
| console.log("No scanned items to submit"); | |||
| // C) 同一个 item 只提交一次(避免重复) | |||
| const uniqueLots = Array.from( | |||
| new Map( | |||
| pendingLots.map((lot) => [ | |||
| `${lot.pickOrderId}-${lot.pickOrderLineId}-${lot.itemId}`, | |||
| lot, | |||
| ]) | |||
| ).values() | |||
| ); | |||
| if (uniqueLots.length === 0) { | |||
| console.log("No pending items to submit"); | |||
| return; | |||
| } | |||
| setIsSubmittingAll(true); | |||
| console.log(`📦 Submitting ${scannedLots.length} scanned items in parallel...`); | |||
| try { | |||
| const submitPromises = scannedLots.map(async (lot) => { | |||
| const submitQty = lot.requiredQty || lot.pickOrderLineRequiredQty; | |||
| console.log(`Submitting item ${lot.itemCode}: qty=${submitQty}`); | |||
| const result = await submitSecondScanQuantity( | |||
| lot.pickOrderId, | |||
| lot.itemId, | |||
| { | |||
| qty: submitQty, | |||
| userId: currentUserId !!, | |||
| isMissing: false, | |||
| isBad: false, | |||
| reason: undefined | |||
| } | |||
| ); | |||
| const submitPromises = uniqueLots.map(async (lot) => { | |||
| const itemKey = `${lot.pickOrderId}-${lot.pickOrderLineId}-${lot.itemId}`; | |||
| const submitQty = Number(itemTotalActualPickQtyMap[itemKey] ?? 0); // Confirm All 用 actual total | |||
| if (submitQty <= 0) { | |||
| return { success: false, itemCode: lot.itemCode }; | |||
| } | |||
| const result = await submitSecondScanQuantity(lot.pickOrderId, lot.itemId, { | |||
| qty: submitQty, | |||
| userId: currentUserId!!, | |||
| isMissing: false, | |||
| isBad: false, | |||
| reason: undefined, | |||
| }); | |||
| return { success: result.code === "SUCCESS", itemCode: lot.itemCode }; | |||
| }); | |||
| const results = await Promise.all(submitPromises); | |||
| const successCount = results.filter(r => r.success).length; | |||
| console.log(` Batch submit completed: ${successCount}/${scannedLots.length} items submitted`); | |||
| const successCount = results.filter((r) => r.success).length; | |||
| console.log(`Batch submit completed: ${successCount}/${uniqueLots.length}`); | |||
| await fetchJobOrderData(); | |||
| if (successCount > 0) { | |||
| setQrScanSuccess(true); | |||
| setTimeout(() => { | |||
| setQrScanSuccess(false); | |||
| // 添加:提交成功后返回到列表 | |||
| if (onBack) { | |||
| onBack(); | |||
| } | |||
| }, 2000); | |||
| } | |||
| } catch (error: any) { | |||
| console.error("Error submitting all scanned items:", error); | |||
| const isAuthError = error?.status === 401 || | |||
| error?.status === 403 || | |||
| error?.message?.toLowerCase().includes('unauthorized') || | |||
| error?.message?.toLowerCase().includes('token'); | |||
| if (isAuthError) { | |||
| console.log("🔒 Authentication error in submit, unassigning pick order"); | |||
| await handleUnassign(currentPickOrderId); | |||
| } | |||
| setQrScanError(true); | |||
| } finally { | |||
| setIsSubmittingAll(false); | |||
| } | |||
| }, [combinedLotData, fetchJobOrderData, currentPickOrderId, handleUnassign, onBack]); | |||
| }, [combinedLotData, currentUserId, fetchJobOrderData]); | |||
| const scannedItemsCount = useMemo(() => { | |||
| return combinedLotData.filter(lot => lot.matchStatus === 'scanned').length; | |||
| }, [combinedLotData]); | |||