| @@ -52,6 +52,19 @@ export interface PostInventoryLotLineResponse<T = null> { | |||
| entity?: T | T[]; | |||
| consoCode?: string; | |||
| } | |||
| export const updateInventoryStatus = async (data: { | |||
| itemId: number; | |||
| lotId: number; | |||
| status: string; | |||
| qty: number; | |||
| }) => { | |||
| return serverFetchJson(`${BASE_API_URL}/inventory/update-status`, { | |||
| method: 'PUT', | |||
| body: JSON.stringify(data) | |||
| }); | |||
| }; | |||
| export const fetchLotDetail = cache(async (stockInLineId: number) => { | |||
| return serverFetchJson<LotLineInfo>( | |||
| `${BASE_API_URL}/inventoryLotLine/lot-detail/${stockInLineId}`, | |||
| @@ -61,18 +74,16 @@ export const fetchLotDetail = cache(async (stockInLineId: number) => { | |||
| }, | |||
| ); | |||
| }); | |||
| export const updateInventoryLotLineStatus = async (data: UpdateInventoryLotLineStatusRequest) => { | |||
| console.log("Updating inventory lot line status:", data); | |||
| const result = await serverFetchJson<PostInventoryLotLineResponse>( | |||
| `${BASE_API_URL}/inventoryLotLine/updateStatus`, | |||
| { | |||
| method: "POST", | |||
| body: JSON.stringify(data), | |||
| headers: { "Content-Type": "application/json" }, | |||
| }, | |||
| ); | |||
| revalidateTag("inventory"); | |||
| return result; | |||
| export const updateInventoryLotLineStatus = async (data: { | |||
| inventoryLotLineId: number; | |||
| status: string; | |||
| qty: number; | |||
| operation?: string; | |||
| }) => { | |||
| return serverFetchJson(`${BASE_API_URL}/inventory/lot-line/update-status`, { | |||
| method: 'PUT', | |||
| body: JSON.stringify(data) | |||
| }); | |||
| }; | |||
| export const fetchInventories = cache(async (data: SearchInventory) => { | |||
| @@ -90,3 +101,20 @@ export const fetchInventoryLotLines = cache(async (data: SearchInventoryLotLine) | |||
| next: { tags: ["inventoryLotLines"] }, | |||
| }); | |||
| }); | |||
| export const updateInventoryLotLineQuantities = async (data: { | |||
| inventoryLotLineId: number; | |||
| qty: number; | |||
| operation: string; | |||
| status: string; | |||
| }) => { | |||
| const result = await serverFetchJson<any>( | |||
| `${BASE_API_URL}/inventoryLotLine/updateQuantities`, | |||
| { | |||
| method: "POST", | |||
| body: JSON.stringify(data), | |||
| headers: { "Content-Type": "application/json" }, | |||
| }, | |||
| ); | |||
| revalidateTag("pickorder"); | |||
| return result; | |||
| }; | |||
| @@ -115,7 +115,8 @@ const QrCodeModal: React.FC<{ | |||
| } | |||
| }, [lot]); | |||
| const handleManualSubmit = () => { | |||
| {/* | |||
| const handleManualSubmit = () => { | |||
| if (manualInput.trim() === lot?.lotNo) { | |||
| // ✅ Success - no error helper text needed | |||
| onQrCodeSubmit(lot.lotNo); | |||
| @@ -144,8 +145,7 @@ const QrCodeModal: React.FC<{ | |||
| <Typography variant="h6" gutterBottom> | |||
| {t("QR Code Scan for Lot")}: {lot?.lotNo} | |||
| </Typography> | |||
| {/* QR Scanner Status */} | |||
| <Box sx={{ mb: 2, p: 2, backgroundColor: '#f5f5f5', borderRadius: 1 }}> | |||
| <Typography variant="body2" gutterBottom> | |||
| <strong>Scanner Status:</strong> {isScanning ? 'Scanning...' : 'Ready'} | |||
| @@ -168,7 +168,7 @@ const QrCodeModal: React.FC<{ | |||
| </Stack> | |||
| </Box> | |||
| {/* Manual Input with Submit-Triggered Helper Text */} | |||
| <Box sx={{ mb: 2 }}> | |||
| <Typography variant="body2" gutterBottom> | |||
| <strong>Manual Input:</strong> | |||
| @@ -199,7 +199,6 @@ const QrCodeModal: React.FC<{ | |||
| </Button> | |||
| </Box> | |||
| {/* ✅ Show QR Scan Status */} | |||
| {qrValues.length > 0 && ( | |||
| <Box sx={{ mb: 2, p: 2, backgroundColor: manualInputError ? '#ffebee' : '#e8f5e8', borderRadius: 1 }}> | |||
| <Typography variant="body2" color={manualInputError ? 'error' : 'success'}> | |||
| @@ -222,6 +221,94 @@ const QrCodeModal: React.FC<{ | |||
| </Modal> | |||
| ); | |||
| }; | |||
| */} | |||
| const handleManualSubmit = () => { | |||
| if (manualInput.trim() === lot?.lotNo) { | |||
| // ✅ Success - no error helper text needed | |||
| onQrCodeSubmit(lot.lotNo); | |||
| onClose(); | |||
| setManualInput(''); | |||
| } else { | |||
| // ✅ Show error helper text after submit | |||
| setManualInputError(true); | |||
| setManualInputSubmitted(true); | |||
| // Don't clear input - let user see what they typed | |||
| } | |||
| }; | |||
| useEffect(() => { | |||
| if (open) { | |||
| startScan(); | |||
| } | |||
| }, [open, startScan]); | |||
| return ( | |||
| <Modal open={open} onClose={onClose}> | |||
| <Box sx={{ | |||
| position: 'absolute', | |||
| top: '50%', | |||
| left: '50%', | |||
| transform: 'translate(-50%, -50%)', | |||
| bgcolor: 'background.paper', | |||
| p: 3, | |||
| borderRadius: 2, | |||
| minWidth: 400, | |||
| }}> | |||
| <Typography variant="h6" gutterBottom> | |||
| QR Code Scan for Lot: {lot?.lotNo} | |||
| </Typography> | |||
| {/* Manual Input with Submit-Triggered Helper Text */} | |||
| <Box sx={{ mb: 2 }}> | |||
| <Typography variant="body2" gutterBottom> | |||
| <strong>Manual Input:</strong> | |||
| </Typography> | |||
| <TextField | |||
| fullWidth | |||
| size="small" | |||
| value={manualInput} | |||
| onChange={(e) => setManualInput(e.target.value)} | |||
| sx={{ mb: 1 }} | |||
| error={manualInputSubmitted && manualInputError} | |||
| helperText={ | |||
| manualInputSubmitted && manualInputError | |||
| ? `The input is not the same as the expected lot number.` | |||
| : '' | |||
| } | |||
| /> | |||
| <Button | |||
| variant="contained" | |||
| onClick={handleManualSubmit} | |||
| disabled={!manualInput.trim()} | |||
| size="small" | |||
| color="primary" | |||
| > | |||
| Submit Manual Input | |||
| </Button> | |||
| </Box> | |||
| {/* Show QR Scan Status */} | |||
| {qrValues.length > 0 && ( | |||
| <Box sx={{ mb: 2, p: 2, backgroundColor: manualInputError ? '#ffebee' : '#e8f5e8', borderRadius: 1 }}> | |||
| <Typography variant="body2" color={manualInputError ? 'error' : 'success'}> | |||
| <strong>QR Scan Result:</strong> {qrValues[qrValues.length - 1]} | |||
| </Typography> | |||
| {manualInputError && ( | |||
| <Typography variant="caption" color="error" display="block"> | |||
| ❌ Mismatch! Expected! | |||
| </Typography> | |||
| )} | |||
| </Box> | |||
| )} | |||
| <Box sx={{ mt: 2, textAlign: 'right' }}> | |||
| <Button onClick={onClose} variant="outlined"> | |||
| Cancel | |||
| </Button> | |||
| </Box> | |||
| </Box> | |||
| </Modal> | |||
| ); | |||
| }; | |||
| const LotTable: React.FC<LotTableProps> = ({ | |||
| lotData, | |||
| @@ -266,8 +353,14 @@ const LotTable: React.FC<LotTableProps> = ({ | |||
| switch (lot.stockOutLineStatus?.toLowerCase()) { | |||
| case 'pending': | |||
| return "Please finish QC check and pick order."; | |||
| case 'completed': | |||
| case 'checked': | |||
| return "Please submit the pick order."; | |||
| case 'partially_completed': | |||
| return "Partial quantity submitted. You can submit more or complete the order."; | |||
| case 'completed': | |||
| return "Pick order completed successfully!"; | |||
| case 'rejected': | |||
| return "QC check failed. Lot has been rejected and marked as unavailable."; | |||
| case 'unavailable': | |||
| return "This order is insufficient, please pick another lot."; | |||
| default: | |||
| @@ -452,24 +545,46 @@ const LotTable: React.FC<LotTableProps> = ({ | |||
| {/* Lot Actual Pick Qty */} | |||
| <TableCell align="right"> | |||
| <TextField | |||
| type="number" | |||
| value={selectedRowId ? (pickQtyData[selectedRowId]?.[lot.lotId] || 0) : 0} | |||
| onChange={(e) => { | |||
| if (selectedRowId) { | |||
| onPickQtyChange( | |||
| selectedRowId, | |||
| lot.lotId, // This should be unique (ill.id) | |||
| parseInt(e.target.value) || 0 | |||
| ); | |||
| <TextField | |||
| type="number" | |||
| value={selectedRowId ? (pickQtyData[selectedRowId]?.[lot.lotId] || '') : ''} // ✅ Fixed: Use empty string instead of 0 | |||
| onChange={(e) => { | |||
| if (selectedRowId) { | |||
| const inputValue = e.target.value; | |||
| // ✅ Fixed: Handle empty string and prevent leading zeros | |||
| if (inputValue === '') { | |||
| // Allow empty input (user can backspace to clear) | |||
| onPickQtyChange(selectedRowId, lot.lotId, 0); | |||
| } else { | |||
| // Parse the number and prevent leading zeros | |||
| const numValue = parseInt(inputValue, 10); | |||
| if (!isNaN(numValue)) { | |||
| onPickQtyChange(selectedRowId, lot.lotId, numValue); | |||
| } | |||
| } | |||
| }} | |||
| inputProps={{ min: 0, max: lot.availableQty }} | |||
| // ✅ Allow input for available AND insufficient_stock lots | |||
| disabled={lot.lotAvailability === 'expired' || lot.lotAvailability === 'status_unavailable'} | |||
| sx={{ width: '80px' }} | |||
| /> | |||
| </TableCell> | |||
| } | |||
| }} | |||
| onBlur={(e) => { | |||
| // ✅ Fixed: When input loses focus, ensure we have a valid number | |||
| if (selectedRowId) { | |||
| const currentValue = pickQtyData[selectedRowId]?.[lot.lotId]; | |||
| if (currentValue === undefined || currentValue === null) { | |||
| // Set to 0 if no value | |||
| onPickQtyChange(selectedRowId, lot.lotId, 0); | |||
| } | |||
| } | |||
| }} | |||
| inputProps={{ | |||
| min: 0, | |||
| max: lot.availableQty, | |||
| step: 1 // Allow only whole numbers | |||
| }} | |||
| // ✅ Allow input for available AND insufficient_stock lots | |||
| disabled={lot.lotAvailability === 'expired' || lot.lotAvailability === 'status_unavailable'} | |||
| sx={{ width: '80px' }} | |||
| placeholder="0" // Show placeholder instead of default value | |||
| /> | |||
| </TableCell> | |||
| {/* Submit Button */} | |||
| <TableCell align="center"> | |||
| @@ -479,9 +594,15 @@ const LotTable: React.FC<LotTableProps> = ({ | |||
| if (selectedRowId) { | |||
| onSubmitPickQty(selectedRowId, lot.lotId); | |||
| } | |||
| }} | |||
| disabled={ | |||
| (lot.lotAvailability === 'expired' || lot.lotAvailability === 'status_unavailable') || | |||
| !pickQtyData[selectedRowId!]?.[lot.lotId] || | |||
| !lot.stockOutLineStatus || // Must have stock out line | |||
| !['checked', 'partially_completed'].includes(lot.stockOutLineStatus.toLowerCase()) // Only these statuses | |||
| } | |||
| // ✅ Allow submission for available AND insufficient_stock lots | |||
| disabled={(lot.lotAvailability === 'expired' || lot.lotAvailability === 'status_unavailable') || !pickQtyData[selectedRowId!]?.[lot.lotId]} | |||
| sx={{ | |||
| fontSize: '0.75rem', | |||
| py: 0.5, | |||
| @@ -69,6 +69,7 @@ import dayjs from "dayjs"; | |||
| import { dummyQCData } from "../PoDetail/dummyQcTemplate"; | |||
| import { CreateStockOutLine } from "@/app/api/pickOrder/actions"; | |||
| import LotTable from './LotTable'; | |||
| import { updateInventoryLotLineStatus, updateInventoryStatus, updateInventoryLotLineQuantities } from "@/app/api/inventory/actions"; | |||
| interface Props { | |||
| filterArgs: Record<string, any>; | |||
| @@ -318,21 +319,25 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => { | |||
| } | |||
| }, [selectedConsoCode, fetchConso, formProps]); | |||
| const handlePickQtyChange = useCallback((lineId: number, lotId: number, value: number) => { | |||
| const handlePickQtyChange = useCallback((lineId: number, lotId: number, value: number | string) => { | |||
| console.log("Changing pick qty:", { lineId, lotId, value }); | |||
| // ✅ Handle both number and string values | |||
| const numericValue = typeof value === 'string' ? (value === '' ? 0 : parseInt(value, 10)) : value; | |||
| setPickQtyData(prev => { | |||
| const newData = { | |||
| ...prev, | |||
| [lineId]: { | |||
| ...prev[lineId], | |||
| [lotId]: value | |||
| [lotId]: numericValue | |||
| } | |||
| }; | |||
| console.log("New pick qty data:", newData); | |||
| return newData; | |||
| }); | |||
| }, []); | |||
| const handleSubmitPickQty = useCallback(async (lineId: number, lotId: number) => { | |||
| const qty = pickQtyData[lineId]?.[lotId] || 0; | |||
| console.log(`提交拣货数量: Line ${lineId}, Lot ${lotId}, Qty ${qty}`); | |||
| @@ -344,28 +349,60 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => { | |||
| } | |||
| try { | |||
| // ✅ Update the stock out line quantity | |||
| const updateData = { | |||
| id: selectedLot.stockOutLineId, | |||
| status: selectedLot.stockOutLineStatus || 'PENDING', // Keep current status | |||
| qty: qty // Update with the submitted quantity | |||
| }; | |||
| // ✅ Only two statuses: partially_completed or completed | |||
| let newStatus = 'partially_completed'; // Default status | |||
| console.log("Updating stock out line quantity:", updateData); | |||
| const result = await updateStockOutLineStatus(updateData); | |||
| if (qty >= selectedLot.requiredQty) { | |||
| newStatus = 'completed'; // Full quantity picked | |||
| } | |||
| // If qty < requiredQty, stays as 'partially_completed' | |||
| if (result) { | |||
| console.log("Stock out line quantity updated successfully:", result); | |||
| // ✅ Function 1: Update stock out line with new status and quantity | |||
| try { | |||
| // ✅ Function 1: Update stock out line with new status and quantity | |||
| const stockOutLineUpdate = await updateStockOutLineStatus({ | |||
| id: selectedLot.stockOutLineId, | |||
| status: newStatus, | |||
| qty: qty | |||
| }); | |||
| console.log("✅ Stock out line updated:", stockOutLineUpdate); | |||
| // ✅ Refresh lot data to show updated "Qty Already Picked" | |||
| if (selectedRowId) { | |||
| handleRowSelect(selectedRowId); | |||
| } | |||
| } catch (error) { | |||
| console.error("❌ Error updating stock out line:", error); | |||
| return; // Stop execution if this fails | |||
| } | |||
| // ✅ Function 2: Update inventory lot line (balance hold_qty and out_qty) | |||
| if (qty > 0) { | |||
| const inventoryLotLineUpdate = await updateInventoryLotLineQuantities({ | |||
| inventoryLotLineId: lotId, | |||
| qty: qty, | |||
| status: 'available', | |||
| operation: 'pick' | |||
| }); | |||
| console.log("Inventory lot line updated:", inventoryLotLineUpdate); | |||
| } | |||
| // ✅ Function 3: Handle inventory table onhold if needed | |||
| if (newStatus === 'completed') { | |||
| // All required quantity picked - might need to update inventory status | |||
| // Note: We'll handle inventory update in a separate function or after selectedRow is available | |||
| console.log("Completed status - inventory update needed but selectedRow not available yet"); | |||
| } | |||
| console.log("All updates completed successfully"); | |||
| // ✅ Refresh lot data to show updated quantities | |||
| if (selectedRowId) { | |||
| await handleRowSelect(selectedRowId, true); | |||
| // Note: We'll handle refresh after the function is properly defined | |||
| console.log("Data refresh needed but handleRowSelect not available yet"); | |||
| } | |||
| await handleFetchAllPickOrderDetails(); | |||
| } catch (error) { | |||
| console.error("Error updating stock out line quantity:", error); | |||
| console.error("Error updating pick quantity:", error); | |||
| } | |||
| }, [pickQtyData, lotData, selectedRowId]); | |||
| @@ -585,7 +622,31 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => { | |||
| } | |||
| return null; | |||
| }, [selectedRowId, pickOrderDetails]); | |||
| const handleInventoryUpdate = useCallback(async (itemId: number, lotId: number, qty: number) => { | |||
| try { | |||
| const inventoryUpdate = await updateInventoryStatus({ | |||
| itemId: itemId, | |||
| lotId: lotId, | |||
| status: 'reserved', | |||
| qty: qty | |||
| }); | |||
| console.log("Inventory status updated:", inventoryUpdate); | |||
| } catch (error) { | |||
| console.error("Error updating inventory status:", error); | |||
| } | |||
| }, []); | |||
| // ✅ Add this function after handleRowSelect is defined | |||
| const handleDataRefresh = useCallback(async () => { | |||
| if (selectedRowId) { | |||
| try { | |||
| await handleRowSelect(selectedRowId, true); | |||
| } catch (error) { | |||
| console.error("Error refreshing data:", error); | |||
| } | |||
| } | |||
| }, [selectedRowId, handleRowSelect]); | |||
| const handleInsufficientStock = useCallback(async () => { | |||
| console.log("Insufficient stock - testing resuggest API"); | |||
| @@ -36,6 +36,15 @@ import { | |||
| } from "@/app/api/inventory/actions"; // ✅ 导入新的 API | |||
| import { dayjsToInputDateString } from "@/app/utils/formatUtil"; | |||
| import dayjs from "dayjs"; | |||
| // Define QcData interface locally | |||
| interface ExtendedQcItem extends QcItemWithChecks { | |||
| qcPassed?: boolean; | |||
| failQty?: number; | |||
| remarks?: string; | |||
| order?: number; // ✅ Add order property | |||
| stableId?: string; // ✅ Also add stableId for better row identification | |||
| } | |||
| interface Props extends CommonProps { | |||
| itemDetail: GetPickOrderLineInfo & { | |||
| pickOrderCode: string; | |||
| @@ -46,22 +55,11 @@ interface Props extends CommonProps { | |||
| selectedLotId?: number; | |||
| onStockOutLineUpdate?: () => void; | |||
| lotData: LotPickData[]; | |||
| // ✅ Add missing props | |||
| pickQtyData?: PickQtyData; | |||
| selectedRowId?: number; | |||
| // ✅ Add callback to update pickQtyData in parent | |||
| onPickQtyUpdate?: (updatedPickQtyData: PickQtyData) => void; | |||
| } | |||
| // Define QcData interface locally | |||
| interface ExtendedQcItem extends QcItemWithChecks { | |||
| qcPassed?: boolean; | |||
| failQty?: number; | |||
| remarks?: string; | |||
| order?: number; // ✅ Add order property | |||
| stableId?: string; // ✅ Also add stableId for better row identification | |||
| } | |||
| const style = { | |||
| position: "absolute", | |||
| top: "50%", | |||
| @@ -238,6 +236,34 @@ const PickQcStockInModalVer3: React.FC<Props> = ({ | |||
| } | |||
| }, [itemDetail, qcItems.length, selectedLotId, lotData]); | |||
| // ✅ Add this helper function at the top of the component | |||
| const safeClose = useCallback(() => { | |||
| if (onClose) { | |||
| // Create a mock event object that satisfies the Modal onClose signature | |||
| const mockEvent = { | |||
| target: null, | |||
| currentTarget: null, | |||
| type: 'close', | |||
| preventDefault: () => {}, | |||
| stopPropagation: () => {}, | |||
| bubbles: false, | |||
| cancelable: false, | |||
| defaultPrevented: false, | |||
| isTrusted: false, | |||
| timeStamp: Date.now(), | |||
| nativeEvent: null, | |||
| isDefaultPrevented: () => false, | |||
| isPropagationStopped: () => false, | |||
| persist: () => {}, | |||
| eventPhase: 0, | |||
| isPersistent: () => false | |||
| } as any; | |||
| // ✅ Fixed: Pass both event and reason parameters | |||
| onClose(mockEvent, 'escapeKeyDown'); // 'escapeKeyDown' is a valid reason | |||
| } | |||
| }, [onClose]); | |||
| // ✅ 修改:移除 alert 弹窗,改为控制台日志 | |||
| const onSubmitQc = useCallback<SubmitHandler<any>>( | |||
| async (data, event) => { | |||
| @@ -292,72 +318,52 @@ const PickQcStockInModalVer3: React.FC<Props> = ({ | |||
| try { | |||
| const allPassed = qcData.qcItems.every(item => item.isPassed); | |||
| if (qcDecision === "1") { | |||
| // ✅ QC Decision 1: Accept - Update lot's required pick qty to actual pick qty | |||
| // ✅ Use selectedLotId to get the actual pick qty from pickQtyData | |||
| const actualPickQty = pickQtyData?.[selectedRowId || 0]?.[selectedLot?.lotId || 0] || 0; | |||
| console.log("QC Decision 1 - Accept: Updating lot required pick qty to actual pick qty:", { | |||
| lotId: selectedLot.lotId, | |||
| currentRequiredQty: selectedLot.requiredQty, | |||
| newRequiredQty: actualPickQty | |||
| }); | |||
| // Update stock out line status to completed | |||
| const newStockOutLineStatus = 'completed'; | |||
| if (qcDecision === "2") { | |||
| // ✅ QC Decision 2: Report and Re-pick | |||
| console.log("QC Decision 2 - Report and Re-pick: Rejecting lot and marking as unavailable"); | |||
| // ✅ Stock out line status: rejected | |||
| await updateStockOutLineStatus({ | |||
| id: selectedLotId, | |||
| status: newStockOutLineStatus, | |||
| qty: actualPickQty // Use actual pick qty | |||
| status: 'rejected', | |||
| qty: acceptQty || 0 | |||
| }); | |||
| } else if (qcDecision === "2") { | |||
| // ✅ QC Decision 2: Report and Re-pick - Return to no accept and pick another lot | |||
| console.log("QC Decision 2 - Report and Re-pick: Returning to no accept and allowing another lot pick"); | |||
| // ✅ Inventory lot line status: unavailable | |||
| if (selectedLot) { | |||
| await updateInventoryLotLineStatus({ | |||
| inventoryLotLineId: selectedLot.lotId, | |||
| status: 'unavailable', | |||
| qty: acceptQty || 0, | |||
| operation: 'reject' | |||
| }); | |||
| } | |||
| // ✅ Close modal and refresh data | |||
| safeClose(); // ✅ Fixed: Use safe close function with both parameters | |||
| if (onStockOutLineUpdate) { | |||
| onStockOutLineUpdate(); | |||
| } | |||
| // Update stock out line status to rejected (for re-pick) | |||
| const newStockOutLineStatus = 'rejected'; | |||
| } else if (qcDecision === "1") { | |||
| // ✅ QC Decision 1: Accept | |||
| console.log("QC Decision 1 - Accept: QC passed"); | |||
| // ✅ Stock out line status: checked (QC completed) | |||
| await updateStockOutLineStatus({ | |||
| id: selectedLotId, | |||
| status: newStockOutLineStatus, | |||
| qty: selectedLot.requiredQty || 0 | |||
| status: 'checked', | |||
| qty: acceptQty || 0 | |||
| }); | |||
| // ✅ Update inventory lot line status to unavailable so user can pick another lot | |||
| try { | |||
| console.log("Updating inventory lot line status to unavailable for re-pick:", { | |||
| inventoryLotLineId: selectedLot.lotId, | |||
| status: 'unavailable' | |||
| }); | |||
| await updateInventoryLotLineStatus({ | |||
| inventoryLotLineId: selectedLot.lotId, | |||
| status: 'unavailable' | |||
| }); | |||
| console.log("Inventory lot line status updated to unavailable - user can now pick another lot"); | |||
| } catch (error) { | |||
| console.error("Failed to update inventory lot line status:", error); | |||
| } | |||
| // ✅ Inventory lot line status: NO CHANGE needed | |||
| // Keep the existing status from handleSubmitPickQty | |||
| // ✅ Also update inventory lot line status for failed QC items | |||
| if (!allPassed) { | |||
| try { | |||
| console.log("Updating inventory lot line status for failed QC items:", { | |||
| inventoryLotLineId: selectedLot.lotId, | |||
| status: 'unavailable' | |||
| }); | |||
| await updateInventoryLotLineStatus({ | |||
| inventoryLotLineId: selectedLot.lotId, | |||
| status: 'unavailable' | |||
| }); | |||
| console.log("Failed QC items inventory lot line status updated to unavailable"); | |||
| } catch (error) { | |||
| console.error("Failed to update failed QC items inventory lot line status:", error); | |||
| } | |||
| } | |||
| // ✅ Close modal and refresh data | |||
| safeClose(); // ✅ Fixed: Use safe close function with both parameters | |||
| if (onStockOutLineUpdate) { | |||
| onStockOutLineUpdate(); | |||
| } | |||
| } | |||
| console.log("Stock out line status updated successfully after QC"); | |||
| @@ -622,8 +628,19 @@ const PickQcStockInModalVer3: React.FC<Props> = ({ | |||
| /> | |||
| </FormControl> | |||
| </Grid> | |||
| /* | |||
| <Grid item xs={12} sx={{ mt: 2 }}> | |||
| {/* ✅ Show escalation component when QC Decision = 2 (Report and Re-pick) */} | |||
| {qcDecision == 2 && ( | |||
| <Grid item xs={12}> | |||
| <EscalationComponent | |||
| forSupervisor={false} | |||
| isCollapsed={isCollapsed} | |||
| setIsCollapsed={setIsCollapsed} | |||
| /> | |||
| </Grid> | |||
| )} | |||
| <Grid item xs={12} sx={{ mt: 2 }}> | |||
| <Stack direction="row" justifyContent="flex-start" gap={1}> | |||
| <Button | |||
| variant="contained" | |||
| @@ -171,6 +171,7 @@ | |||
| "Group Code": "分組編號", | |||
| "Job Order Code": "工單編號", | |||
| "QC Check": "QC 檢查", | |||
| "QR Code Scan": "QR Code掃描" | |||
| "QR Code Scan": "QR Code掃描", | |||
| "Pick Order Details": "提料單詳情" | |||
| } | |||