From b7755acd2e505f62466e84d5610513f04c897915 Mon Sep 17 00:00:00 2001 From: "CANCERYS\\kw093" Date: Wed, 17 Sep 2025 11:51:53 +0800 Subject: [PATCH] update --- src/components/PickOrderSearch/LotTable.tsx | 334 +++++++++--------- .../PickOrderSearch/PickExecutionForm.tsx | 54 ++- src/i18n/zh/pickOrder.json | 15 +- 3 files changed, 210 insertions(+), 193 deletions(-) diff --git a/src/components/PickOrderSearch/LotTable.tsx b/src/components/PickOrderSearch/LotTable.tsx index cd41138..60bd9c3 100644 --- a/src/components/PickOrderSearch/LotTable.tsx +++ b/src/components/PickOrderSearch/LotTable.tsx @@ -87,7 +87,7 @@ const QrCodeModal: React.FC<{ const { t } = useTranslation("pickOrder"); const { values: qrValues, isScanning, startScan, stopScan, resetScan } = useQrCodeScannerContext(); const [manualInput, setManualInput] = useState(''); - + const [validationErrors, setValidationErrors] = useState<{[key: string]: string}>({}); // ✅ Add state to track manual input submission const [manualInputSubmitted, setManualInputSubmitted] = useState(false); const [manualInputError, setManualInputError] = useState(false); @@ -260,7 +260,7 @@ const QrCodeModal: React.FC<{ setManualInputSubmitted(true); } }; - + // ✅ Add function to restart scanning after manual input error const handleRestartScan = () => { setQrScanFailed(false); @@ -397,7 +397,7 @@ const LotTable: React.FC = ({ }, []); // ✅ Add QR scanner context const { values: qrValues, isScanning, startScan, stopScan, resetScan } = useQrCodeScannerContext(); - + const [validationErrors, setValidationErrors] = useState<{[key: string]: string}>({}); // ✅ Add state for QR input modal const [qrModalOpen, setQrModalOpen] = useState(false); const [selectedLotForQr, setSelectedLotForQr] = useState(null); @@ -411,13 +411,10 @@ const LotTable: React.FC = ({ // ✅ 添加状态消息生成函数 const getStatusMessage = useCallback((lot: LotPickData) => { - if (!lot.stockOutLineId) { - return t("Please finish QR code scanand pick order."); - } - + switch (lot.stockOutLineStatus?.toLowerCase()) { case 'pending': - return t("Please finish pick order."); + return t("Please finish QR code scanand pick order."); case 'checked': return t("Please submit the pick order."); case 'partially_completed': @@ -466,30 +463,58 @@ const LotTable: React.FC = ({ if (!selectedRowId) return lot.availableQty; const lactualPickQty = lot.actualPickQty || 0; const actualPickQty = pickQtyData[selectedRowId]?.[lot.lotId] || 0; - const remainingQty = lot.inQty - lot.outQty; + + const remainingQty = lot.inQty - lot.outQty-actualPickQty; // Ensure it doesn't go below 0 return Math.max(0, remainingQty); }, [selectedRowId, pickQtyData]); + const validatePickQty = useCallback((lot: LotPickData, inputValue: number) => { + const maxAllowed = Math.min(calculateRemainingAvailableQty(lot), calculateRemainingRequiredQty(lot)); + + if (inputValue > maxAllowed) { + return `${t('Input quantity cannot exceed')} ${maxAllowed}`; + } + + if (inputValue < 0) { + return t('Quantity cannot be negative'); + } + + return null; +}, [calculateRemainingAvailableQty, calculateRemainingRequiredQty, t]); + // ✅ Handle QR code submission const handleQrCodeSubmit = useCallback(async (lotNo: string) => { if (selectedLotForQr && selectedLotForQr.lotNo === lotNo) { console.log(`✅ QR Code verified for lot: ${lotNo}`); - + if (!selectedLotForQr.stockOutLineId) { + console.error("No stock out line ID found for this lot"); + alert("No stock out line found for this lot. Please contact administrator."); + return; + } // ✅ Store the required quantity before creating stock out line const requiredQty = selectedLotForQr.requiredQty; const lotId = selectedLotForQr.lotId; + + try { + // ✅ Update stock out line status to 'checked' (QR scan completed) + const stockOutLineUpdate = await updateStockOutLineStatus({ + id: selectedLotForQr.stockOutLineId, + status: 'checked', + qty: selectedLotForQr.stockOutLineQty || 0 + }); - // ✅ Create stock out line and wait for it to complete - await onCreateStockOutLine(selectedLotForQr.lotId); + console.log("✅ Stock out line updated to 'checked':", stockOutLineUpdate); // ✅ Close modal setQrModalOpen(false); setSelectedLotForQr(null); - - // ✅ Set pick quantity AFTER stock out line creation and refresh is complete + if (onLotDataRefresh) { + await onLotDataRefresh(); + } + // ✅ Set pick quantity AFTER stock out line update is complete if (selectedRowId) { - // Add a small delay to ensure the data refresh from onCreateStockOutLine is complete + // Add a small delay to ensure the data refresh is complete setTimeout(() => { onPickQtyChange(selectedRowId, lotId, requiredQty); console.log(`✅ Auto-set pick quantity to ${requiredQty} for lot ${lotNo}`); @@ -497,9 +522,18 @@ const LotTable: React.FC = ({ } // ✅ Show success message - console.log("Stock out line created successfully!"); + console.log("Stock out line updated successfully!"); + + } catch (error) { + console.error("❌ Error updating stock out line status:", error); + alert("Failed to update lot status. Please try again."); } - }, [selectedLotForQr, onCreateStockOutLine, selectedRowId, onPickQtyChange]); + } else { + // ✅ Handle case where lot numbers don't match + console.error("QR scan mismatch:", { scanned: lotNo, expected: selectedLotForQr?.lotNo }); + alert(`QR scan mismatch! Expected: ${selectedLotForQr?.lotNo}, Scanned: ${lotNo}`); + } +}, [selectedLotForQr, selectedRowId, onPickQtyChange]); // ✅ 添加 PickExecutionForm 相关的状态 const [pickExecutionFormOpen, setPickExecutionFormOpen] = useState(false); @@ -599,17 +633,17 @@ const LotTable: React.FC = ({ }} > - onLotSelection(`row_${index}`, lot.lotId)} - // ✅ 禁用 rejected、expired 和 status_unavailable 的批次 - disabled={lot.lotAvailability === 'expired' || - lot.lotAvailability === 'status_unavailable' || - lot.lotAvailability === 'rejected'} // ✅ 添加 rejected - value={`row_${index}`} - name="lot-selection" - /> - + onLotSelection(`row_${index}`, lot.lotId)} + // ✅ 禁用 rejected、expired 和 status_unavailable 的批次 + disabled={lot.lotAvailability === 'expired' || + lot.lotAvailability === 'status_unavailable' || + lot.lotAvailability === 'rejected'} // ✅ 添加 rejected + value={`row_${index}`} + name="lot-selection" + /> + = ({ })()} - {/* Show QR Scan Button if not scanned, otherwise show TextField + Pick Form */} - {!lot.stockOutLineId ? ( - - ) : ( - // ✅ 当有 stockOutLineId 时,显示 TextField + Pick Form 按钮 - - {/* ✅ 恢复 TextField 用于正常数量输入 */} - { - if (selectedRowId) { - onPickQtyChange(selectedRowId, lot.lotId, parseFloat(e.target.value) || 0); - } - }} - disabled={ - (lot.lotAvailability === 'expired' || - lot.lotAvailability === 'status_unavailable' || - lot.lotAvailability === 'rejected') || - selectedLotRowId !== `row_${index}` || - lot.stockOutLineStatus === 'completed' // ✅ 完成时禁用输入 - } - inputProps={{ - min: 0, - max: calculateRemainingRequiredQty(lot), - step: 0.01 - }} - sx={{ - width: '80px', - '& .MuiInputBase-input': { - fontSize: '0.75rem', - textAlign: 'center', - padding: '8px 4px' - } - }} - placeholder="0" - /> + {/* Show QR Scan Button if not scanned, otherwise show TextField + Pick Form */} + {lot.stockOutLineStatus?.toLowerCase() === 'pending' ? ( + + ) : ( + // ✅ 当有 stockOutLineId 时,显示 TextField + Pick Form 按钮 + + {/* ✅ 恢复 TextField 用于正常数量输入 */} + { + if (selectedRowId) { + const inputValue = parseFloat(e.target.value) || 0; + const maxAllowed = Math.min(calculateRemainingAvailableQty(lot), calculateRemainingRequiredQty(lot)); + {/* + // ✅ Validate input + if (inputValue > maxAllowed) { + // Set validation error for this lot + setValidationErrors(prev => ({ ...prev, [`lot_${lot.lotId}`]: `${t('Input quantity cannot exceed')} ${maxAllowed}` })); + return; + } else { + // Clear validation error if valid + setValidationErrors(prev => { + const newErrors = { ...prev }; + delete newErrors[`lot_${lot.lotId}`]; + return newErrors; + }); + */} - {/* ✅ 添加 Pick Form 按钮用于问题情况 */} - - - )} - + + onPickQtyChange(selectedRowId, lot.lotId, inputValue); + } + }} + disabled={ + (lot.lotAvailability === 'expired' || + lot.lotAvailability === 'status_unavailable' || + lot.lotAvailability === 'rejected') || + selectedLotRowId !== `row_${index}` || + lot.stockOutLineStatus === 'completed' + } + error={!!validationErrors[`lot_${lot.lotId}`]} // ✅ Show red border when error + helperText={validationErrors[`lot_${lot.lotId}`]} // ✅ Show red error text below + inputProps={{ + min: 0, + max: calculateRemainingRequiredQty(lot), + step: 0.01 + }} + sx={{ + width: '60px', + height: '28px', + '& .MuiInputBase-input': { + fontSize: '0.7rem', + textAlign: 'center', + padding: '6px 8px' + } + }} + placeholder="0" +/> + + {/* ✅ 添加 Pick Form 按钮用于问题情况 */} + + + )} + {/*{lot.availableQty.toLocaleString()}*/} {calculateRemainingAvailableQty(lot).toLocaleString()} - {/* - - - - */} - {/*} - - - - */} +