| @@ -164,8 +164,8 @@ const QrCodeModal: React.FC<{ | |||||
| console.log(` QR Code verified for lot: ${lot.lotNo}`); | console.log(` QR Code verified for lot: ${lot.lotNo}`); | ||||
| setQrScanSuccess(true); | setQrScanSuccess(true); | ||||
| onQrCodeSubmit(lot.lotNo); | onQrCodeSubmit(lot.lotNo); | ||||
| onClose(); | |||||
| resetScan(); | |||||
| // onClose(); | |||||
| //resetScan(); | |||||
| } else { | } else { | ||||
| console.log(` QR Code mismatch. Expected: ${lot.lotNo}, Got: ${stockInLineInfo.lotNo}`); | console.log(` QR Code mismatch. Expected: ${lot.lotNo}, Got: ${stockInLineInfo.lotNo}`); | ||||
| setQrScanFailed(true); | setQrScanFailed(true); | ||||
| @@ -1959,7 +1959,7 @@ const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdO | |||||
| return; | return; | ||||
| } | } | ||||
| const checkTime = performance.now() - checkProcessedStartTime; | const checkTime = performance.now() - checkProcessedStartTime; | ||||
| console.log(`⏱️ [QR PROCESS] Not processed check time: ${checkTime.toFixed(2)}ms`); | |||||
| console.log(` [QR PROCESS] Not processed check time: ${checkTime.toFixed(2)}ms`); | |||||
| // Handle special shortcut | // Handle special shortcut | ||||
| if (latestQr === "{2fic}") { | if (latestQr === "{2fic}") { | ||||
| @@ -2718,14 +2718,13 @@ const handleSubmitAllScanned = useCallback(async () => { | |||||
| // ✅ noLot 情况:允许 checked / pending / partially_completed / PARTIALLY_COMPLETE | // ✅ noLot 情况:允许 checked / pending / partially_completed / PARTIALLY_COMPLETE | ||||
| if (lot.noLot === true) { | if (lot.noLot === true) { | ||||
| return status === 'checked' || | return status === 'checked' || | ||||
| status === 'pending' || | |||||
| status === 'partially_completed' || | status === 'partially_completed' || | ||||
| status === 'PARTIALLY_COMPLETE'; | status === 'PARTIALLY_COMPLETE'; | ||||
| } | } | ||||
| // ✅ 正常 lot:也放宽为允许 checked / pending / partially_completed / PARTIALLY_COMPLETE | // ✅ 正常 lot:也放宽为允许 checked / pending / partially_completed / PARTIALLY_COMPLETE | ||||
| // 这样即使用户先改数(状态变为 pending / partially_completed),仍然可以批量提交 | // 这样即使用户先改数(状态变为 pending / partially_completed),仍然可以批量提交 | ||||
| return status === 'checked' || | return status === 'checked' || | ||||
| status === 'pending' || | |||||
| status === 'partially_completed' || | status === 'partially_completed' || | ||||
| status === 'PARTIALLY_COMPLETE'; | status === 'PARTIALLY_COMPLETE'; | ||||
| }); | }); | ||||
| @@ -2741,24 +2740,36 @@ const handleSubmitAllScanned = useCallback(async () => { | |||||
| try { | try { | ||||
| // 转换为 batchSubmitList 所需的格式(与后端 QrPickBatchSubmitRequest 匹配) | // 转换为 batchSubmitList 所需的格式(与后端 QrPickBatchSubmitRequest 匹配) | ||||
| const lines: batchSubmitListLineRequest[] = scannedLots.map((lot) => { | const lines: batchSubmitListLineRequest[] = scannedLots.map((lot) => { | ||||
| const submitQty = lot.requiredQty || lot.pickOrderLineRequiredQty || 0; | |||||
| const currentActualPickQty = lot.actualPickQty || 0; | |||||
| const cumulativeQty = currentActualPickQty + submitQty; | |||||
| let newStatus = 'partially_completed'; | |||||
| if (cumulativeQty >= (lot.requiredQty || 0)) { | |||||
| newStatus = 'completed'; | |||||
| // 1. 需求数量:优先用 lot.requiredQty,没有就用 pickOrderLineRequiredQty | |||||
| const requiredQty = | |||||
| Number(lot.requiredQty || lot.pickOrderLineRequiredQty || 0); | |||||
| // 2. 当前已经拣到的数量(数据库里对应的是 stock_out_line.qty) | |||||
| const currentActualPickQty = Number(lot.actualPickQty || 0); | |||||
| // 3. 还需要拣多少:不能为负数 | |||||
| const remainingQty = Math.max(0, requiredQty - currentActualPickQty); | |||||
| // 4. 本次批量提交后的目标累计值 = 当前 + 剩余 | |||||
| const cumulativeQty = currentActualPickQty + remainingQty; | |||||
| // 5. 根据“目标累计值是否达到需求”决定状态 | |||||
| let newStatus = "partially_completed"; | |||||
| if (requiredQty > 0 && cumulativeQty >= requiredQty) { | |||||
| newStatus = "completed"; | |||||
| } | } | ||||
| return { | return { | ||||
| stockOutLineId: Number(lot.stockOutLineId) || 0, | stockOutLineId: Number(lot.stockOutLineId) || 0, | ||||
| pickOrderLineId: Number(lot.pickOrderLineId), | pickOrderLineId: Number(lot.pickOrderLineId), | ||||
| // ⚠️ 这里按你现在的写法是用 lot.lotId,当心是否真的是 inventoryLotLineId | |||||
| inventoryLotLineId: lot.lotId ? Number(lot.lotId) : null, | inventoryLotLineId: lot.lotId ? Number(lot.lotId) : null, | ||||
| requiredQty: Number(lot.requiredQty || lot.pickOrderLineRequiredQty || 0), | |||||
| actualPickQty: Number(cumulativeQty), | |||||
| requiredQty, | |||||
| // 传“目标累计值”,后端会用它减去当前数据库里的 qty 得到增量 | |||||
| actualPickQty: cumulativeQty, | |||||
| stockOutLineStatus: newStatus, | stockOutLineStatus: newStatus, | ||||
| pickOrderConsoCode: String(lot.pickOrderConsoCode || ''), | |||||
| noLot: Boolean(lot.noLot === true) | |||||
| pickOrderConsoCode: String(lot.pickOrderConsoCode || ""), | |||||
| noLot: Boolean(lot.noLot === true), | |||||
| }; | }; | ||||
| }); | }); | ||||
| @@ -2822,12 +2833,12 @@ const handleSubmitAllScanned = useCallback(async () => { | |||||
| // ✅ 与 handleSubmitAllScanned 完全保持一致 | // ✅ 与 handleSubmitAllScanned 完全保持一致 | ||||
| if (lot.noLot === true) { | if (lot.noLot === true) { | ||||
| return status === 'checked' || | return status === 'checked' || | ||||
| status === 'pending' || | |||||
| status === 'partially_completed' || | status === 'partially_completed' || | ||||
| status === 'PARTIALLY_COMPLETE'; | status === 'PARTIALLY_COMPLETE'; | ||||
| } | } | ||||
| return status === 'checked' || | return status === 'checked' || | ||||
| status === 'pending' || | |||||
| status === 'partially_completed' || | status === 'partially_completed' || | ||||
| status === 'PARTIALLY_COMPLETE'; | status === 'PARTIALLY_COMPLETE'; | ||||
| }); | }); | ||||
| @@ -2844,7 +2855,7 @@ const handleSubmitAllScanned = useCallback(async () => { | |||||
| return filtered.length; | return filtered.length; | ||||
| }, [combinedLotData]); | }, [combinedLotData]); | ||||
| /* | |||||
| // ADD THIS: Auto-stop scan when no data available | // ADD THIS: Auto-stop scan when no data available | ||||
| useEffect(() => { | useEffect(() => { | ||||
| if (isManualScanning && combinedLotData.length === 0) { | if (isManualScanning && combinedLotData.length === 0) { | ||||
| @@ -2852,6 +2863,7 @@ const handleSubmitAllScanned = useCallback(async () => { | |||||
| handleStopScan(); | handleStopScan(); | ||||
| } | } | ||||
| }, [combinedLotData.length, isManualScanning, handleStopScan]); | }, [combinedLotData.length, isManualScanning, handleStopScan]); | ||||
| */ | |||||
| // Cleanup effect | // Cleanup effect | ||||
| useEffect(() => { | useEffect(() => { | ||||