|
|
|
@@ -350,6 +350,11 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => { |
|
|
|
const [selectedLotForExecutionForm, setSelectedLotForExecutionForm] = useState<any | null>(null); |
|
|
|
const [fgPickOrders, setFgPickOrders] = useState<FGPickOrderResponse[]>([]); |
|
|
|
const [fgPickOrdersLoading, setFgPickOrdersLoading] = useState(false); |
|
|
|
// ✅ Add these missing state variables after line 352 |
|
|
|
const [isManualScanning, setIsManualScanning] = useState<boolean>(false); |
|
|
|
const [processedQrCodes, setProcessedQrCodes] = useState<Set<string>>(new Set()); |
|
|
|
const [lastProcessedQr, setLastProcessedQr] = useState<string>(''); |
|
|
|
const [isRefreshingData, setIsRefreshingData] = useState<boolean>(false); |
|
|
|
const fetchFgPickOrdersData = useCallback(async () => { |
|
|
|
if (!currentUserId) return; |
|
|
|
|
|
|
|
@@ -394,13 +399,6 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => { |
|
|
|
// TODO: Implement QR code functionality |
|
|
|
}; |
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
startScan(); |
|
|
|
return () => { |
|
|
|
stopScan(); |
|
|
|
resetScan(); |
|
|
|
}; |
|
|
|
}, [startScan, stopScan, resetScan]); |
|
|
|
|
|
|
|
const fetchAllCombinedLotData = useCallback(async (userId?: number) => { |
|
|
|
setCombinedDataLoading(true); |
|
|
|
@@ -455,14 +453,12 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => { |
|
|
|
}; |
|
|
|
}, [fetchAllCombinedLotData]); |
|
|
|
|
|
|
|
// ✅ Handle QR code submission for matched lot (external scanning) |
|
|
|
// ✅ Handle QR code submission for matched lot (external scanning) |
|
|
|
const handleQrCodeSubmit = useCallback(async (lotNo: string) => { |
|
|
|
const handleQrCodeSubmit = useCallback(async (lotNo: string) => { |
|
|
|
console.log(`✅ Processing QR Code for lot: ${lotNo}`); |
|
|
|
|
|
|
|
// ✅ Use current data without refreshing to avoid infinite loop |
|
|
|
const currentLotData = combinedLotData; |
|
|
|
console.log(`🔍 Available lots:`, currentLotData.map(lot => lot.lotNo)); |
|
|
|
console.log(`�� Available lots:`, currentLotData.map(lot => lot.lotNo)); |
|
|
|
|
|
|
|
const matchingLots = currentLotData.filter(lot => |
|
|
|
lot.lotNo === lotNo || |
|
|
|
@@ -488,35 +484,50 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => { |
|
|
|
console.log(`🔄 Processing pick order line ${matchingLot.pickOrderLineId} for lot ${lotNo}`); |
|
|
|
|
|
|
|
if (matchingLot.stockOutLineId) { |
|
|
|
console.log(`✅ Stock out line already exists for line ${matchingLot.pickOrderLineId}`); |
|
|
|
existsCount++; |
|
|
|
} else { |
|
|
|
// ✅ FIXED: Use matchingLot.stockOutLineId instead of selectedLotForQr.stockOutLineId |
|
|
|
const stockOutLineUpdate = await updateStockOutLineStatus({ |
|
|
|
id: selectedLotForQr.stockOutLineId, |
|
|
|
id: matchingLot.stockOutLineId, // ✅ Use the correct ID |
|
|
|
status: 'checked', |
|
|
|
qty: selectedLotForQr.stockOutLineQty || 0 |
|
|
|
qty: matchingLot.stockOutLineQty || matchingLot.requiredQty || 0 |
|
|
|
}); |
|
|
|
console.log(`Create stock out line result for line ${matchingLot.pickOrderLineId}:`,stockOutLineUpdate); |
|
|
|
console.log(`Update stock out line result for line ${matchingLot.pickOrderLineId}:`, stockOutLineUpdate); |
|
|
|
|
|
|
|
if (stockOutLineUpdate && stockOutLineUpdate.code === "EXISTS") { |
|
|
|
console.log(`✅ Stock out line already exists for line ${matchingLot.pickOrderLineId}`); |
|
|
|
existsCount++; |
|
|
|
} else if (stockOutLineUpdate && stockOutLineUpdate.code === "SUCCESS") { |
|
|
|
if (stockOutLineUpdate && stockOutLineUpdate.code === "SUCCESS") { |
|
|
|
console.log(`✅ Stock out line updated successfully for line ${matchingLot.pickOrderLineId}`); |
|
|
|
successCount++; |
|
|
|
} else { |
|
|
|
console.error(`❌ Failed to update stock out line for line ${matchingLot.pickOrderLineId}:`, stockOutLineUpdate); |
|
|
|
errorCount++; |
|
|
|
} |
|
|
|
} else { |
|
|
|
// ✅ If no stock out line exists, create one |
|
|
|
const createStockOutLineData = { |
|
|
|
consoCode: matchingLot.pickOrderConsoCode, |
|
|
|
pickOrderLineId: matchingLot.pickOrderLineId, |
|
|
|
inventoryLotLineId: matchingLot.lotId, |
|
|
|
qty: matchingLot.requiredQty || 0 |
|
|
|
}; |
|
|
|
|
|
|
|
const createResult = await createStockOutLine(createStockOutLineData); |
|
|
|
console.log(`Create stock out line result for line ${matchingLot.pickOrderLineId}:`, createResult); |
|
|
|
|
|
|
|
if (createResult && createResult.code === "SUCCESS") { |
|
|
|
console.log(`✅ Stock out line created successfully for line ${matchingLot.pickOrderLineId}`); |
|
|
|
successCount++; |
|
|
|
} else { |
|
|
|
console.error(`❌ Failed to create stock out line for line ${matchingLot.pickOrderLineId}:`, stockOutLineUpdate); |
|
|
|
console.error(`❌ Failed to create stock out line for line ${matchingLot.pickOrderLineId}:`, createResult); |
|
|
|
errorCount++; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// ✅ Always refresh data after processing (success or failure) |
|
|
|
// ✅ FIXED: Set refresh flag before refreshing data |
|
|
|
setIsRefreshingData(true); |
|
|
|
console.log("🔄 Refreshing data after QR code processing..."); |
|
|
|
await fetchAllCombinedLotData(); |
|
|
|
|
|
|
|
if (successCount > 0 || existsCount > 0) { |
|
|
|
console.log(`✅ QR Code processing completed: ${successCount} created, ${existsCount} already existed`); |
|
|
|
if (successCount > 0) { |
|
|
|
console.log(`✅ QR Code processing completed: ${successCount} updated/created`); |
|
|
|
setQrScanSuccess(true); |
|
|
|
setQrScanInput(''); // Clear input after successful processing |
|
|
|
|
|
|
|
@@ -540,12 +551,18 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => { |
|
|
|
setQrScanSuccess(false); |
|
|
|
|
|
|
|
// ✅ Still refresh data even on error |
|
|
|
setIsRefreshingData(true); |
|
|
|
await fetchAllCombinedLotData(); |
|
|
|
|
|
|
|
// ✅ Clear error state after a delay |
|
|
|
setTimeout(() => { |
|
|
|
setQrScanError(false); |
|
|
|
}, 3000); |
|
|
|
} finally { |
|
|
|
// ✅ Clear refresh flag after a short delay |
|
|
|
setTimeout(() => { |
|
|
|
setIsRefreshingData(false); |
|
|
|
}, 1000); |
|
|
|
} |
|
|
|
}, [combinedLotData, fetchAllCombinedLotData]); |
|
|
|
|
|
|
|
@@ -598,15 +615,28 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => { |
|
|
|
|
|
|
|
// ✅ Outside QR scanning - process QR codes from outside the page automatically |
|
|
|
useEffect(() => { |
|
|
|
if (qrValues.length > 0 && combinedLotData.length > 0) { |
|
|
|
const latestQr = qrValues[qrValues.length - 1]; |
|
|
|
// ✅ Don't process QR codes when refreshing data or if not manually scanning |
|
|
|
if (!isManualScanning || qrValues.length === 0 || combinedLotData.length === 0 || isRefreshingData) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
const latestQr = qrValues[qrValues.length - 1]; |
|
|
|
|
|
|
|
// ✅ Prevent processing the same QR code multiple times |
|
|
|
if (processedQrCodes.has(latestQr) || lastProcessedQr === latestQr) { |
|
|
|
console.log(" QR code already processed, skipping..."); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (latestQr && latestQr !== lastProcessedQr) { |
|
|
|
console.log(` Processing new QR code: ${latestQr}`); |
|
|
|
setLastProcessedQr(latestQr); |
|
|
|
setProcessedQrCodes(prev => new Set(prev).add(latestQr)); |
|
|
|
|
|
|
|
// Extract lot number from QR code |
|
|
|
let lotNo = ''; |
|
|
|
try { |
|
|
|
const qrData = JSON.parse(latestQr); |
|
|
|
if (qrData.stockInLineId && qrData.itemId) { |
|
|
|
// For JSON QR codes, we need to fetch the lot number |
|
|
|
fetchStockInLineInfo(qrData.stockInLineId) |
|
|
|
.then((stockInLineInfo) => { |
|
|
|
console.log("Outside QR scan - Stock in line info:", stockInLineInfo); |
|
|
|
@@ -619,20 +649,18 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => { |
|
|
|
.catch((error) => { |
|
|
|
console.error("Outside QR scan - Error fetching stock in line info:", error); |
|
|
|
}); |
|
|
|
return; // Exit early for JSON QR codes |
|
|
|
return; |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
// Not JSON format, treat as direct lot number |
|
|
|
lotNo = latestQr.replace(/[{}]/g, ''); |
|
|
|
} |
|
|
|
|
|
|
|
// For direct lot number QR codes |
|
|
|
if (lotNo) { |
|
|
|
console.log(`Outside QR scan detected (direct): ${lotNo}`); |
|
|
|
handleQrCodeSubmit(lotNo); |
|
|
|
} |
|
|
|
} |
|
|
|
}, [qrValues, combinedLotData, handleQrCodeSubmit]); |
|
|
|
}, [qrValues, isManualScanning, processedQrCodes, lastProcessedQr, isRefreshingData, handleQrCodeSubmit]); |
|
|
|
|
|
|
|
|
|
|
|
const handlePickQtyChange = useCallback((lotKey: string, value: number | string) => { |
|
|
|
@@ -910,39 +938,45 @@ const paginatedData = useMemo(() => { |
|
|
|
return combinedLotData.slice(startIndex, endIndex); // ✅ No sorting needed |
|
|
|
}, [combinedLotData, paginationController]); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ✅ Add these functions after line 395 |
|
|
|
const handleStartScan = useCallback(() => { |
|
|
|
console.log(" Starting manual QR scan..."); |
|
|
|
setIsManualScanning(true); |
|
|
|
setProcessedQrCodes(new Set()); |
|
|
|
setLastProcessedQr(''); |
|
|
|
startScan(); |
|
|
|
}, [startScan]); |
|
|
|
|
|
|
|
const handleStopScan = useCallback(() => { |
|
|
|
console.log("⏹️ Stopping manual QR scan..."); |
|
|
|
setIsManualScanning(false); |
|
|
|
stopScan(); |
|
|
|
resetScan(); |
|
|
|
}, [stopScan, resetScan]); |
|
|
|
const getStatusMessage = useCallback((lot: any) => { |
|
|
|
switch (lot.stockOutLineStatus?.toLowerCase()) { |
|
|
|
case 'pending': |
|
|
|
return t("Please finish QR code scan and pick order."); |
|
|
|
case 'checked': |
|
|
|
return t("Please submit the pick order."); |
|
|
|
case 'partially_completed': |
|
|
|
return t("Partial quantity submitted. Please submit more or complete the order."); |
|
|
|
case 'completed': |
|
|
|
return t("Pick order completed successfully!"); |
|
|
|
case 'rejected': |
|
|
|
return t("Lot has been rejected and marked as unavailable."); |
|
|
|
case 'unavailable': |
|
|
|
return t("This order is insufficient, please pick another lot."); |
|
|
|
default: |
|
|
|
return t("Please finish QR code scan and pick order."); |
|
|
|
} |
|
|
|
}, [t]); |
|
|
|
return ( |
|
|
|
<FormProvider {...formProps}> |
|
|
|
|
|
|
|
{/* Search Box */} |
|
|
|
{/* |
|
|
|
<Box> |
|
|
|
|
|
|
|
{fgPickOrdersLoading ? ( |
|
|
|
<Box sx={{ display: 'flex', justifyContent: 'center', p: 3 }}> |
|
|
|
<CircularProgress /> |
|
|
|
</Box> |
|
|
|
) : ( |
|
|
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}> |
|
|
|
{fgPickOrders.length === 0 ? ( |
|
|
|
<Box sx={{ p: 3, textAlign: 'center' }}> |
|
|
|
<Typography variant="body2" color="text.secondary"> |
|
|
|
{t("No FG pick orders found")} |
|
|
|
</Typography> |
|
|
|
</Box> |
|
|
|
) : ( |
|
|
|
fgPickOrders.map((fgOrder) => ( |
|
|
|
<FGPickOrderCard |
|
|
|
key={fgOrder.pickOrderId} |
|
|
|
fgOrder={fgOrder} |
|
|
|
onQrCodeClick={handleQrCodeClick} |
|
|
|
/> |
|
|
|
)) |
|
|
|
)} |
|
|
|
</Box> |
|
|
|
)} |
|
|
|
</Box> |
|
|
|
*/} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<Stack spacing={2}> |
|
|
|
{/* DO Header */} |
|
|
|
@@ -960,9 +994,13 @@ const paginatedData = useMemo(() => { |
|
|
|
<Typography variant="subtitle1"> |
|
|
|
<strong>{t("Pick Order Code")}:</strong>{fgPickOrders[0].pickOrderCode || '-'} |
|
|
|
</Typography> |
|
|
|
<Typography variant="subtitle1"> |
|
|
|
<strong>{t("Ticket No.")}:</strong> {fgPickOrders[0].ticketNo || '-'} |
|
|
|
</Typography> |
|
|
|
<Typography variant="subtitle1"> |
|
|
|
<strong>{t("Departure Time")}:</strong> {fgPickOrders[0].DepartureTime || '-'} |
|
|
|
</Typography> |
|
|
|
|
|
|
|
</Stack> |
|
|
|
</Paper> |
|
|
|
) |
|
|
|
@@ -975,8 +1013,39 @@ const paginatedData = useMemo(() => { |
|
|
|
<Typography variant="h6" gutterBottom sx={{ mb: 0 }}> |
|
|
|
{t("All Pick Order Lots")} |
|
|
|
</Typography> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<Box sx={{ display: 'flex', gap: 2, alignItems: 'center' }}> |
|
|
|
{!isManualScanning ? ( |
|
|
|
<Button |
|
|
|
variant="contained" |
|
|
|
startIcon={<QrCodeIcon />} |
|
|
|
onClick={handleStartScan} |
|
|
|
color="primary" |
|
|
|
sx={{ minWidth: '120px' }} |
|
|
|
> |
|
|
|
{t("Start QR Scan")} |
|
|
|
</Button> |
|
|
|
) : ( |
|
|
|
<Button |
|
|
|
variant="outlined" |
|
|
|
startIcon={<QrCodeIcon />} |
|
|
|
onClick={handleStopScan} |
|
|
|
color="secondary" |
|
|
|
sx={{ minWidth: '120px' }} |
|
|
|
> |
|
|
|
{t("Stop QR Scan")} |
|
|
|
</Button> |
|
|
|
)} |
|
|
|
|
|
|
|
{isManualScanning && ( |
|
|
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}> |
|
|
|
<CircularProgress size={16} /> |
|
|
|
<Typography variant="caption" color="primary"> |
|
|
|
{t("Scanning...")} |
|
|
|
</Typography> |
|
|
|
</Box> |
|
|
|
)} |
|
|
|
</Box> |
|
|
|
</Box> |
|
|
|
|
|
|
|
<TableContainer component={Paper}> |
|
|
|
@@ -991,10 +1060,11 @@ const paginatedData = useMemo(() => { |
|
|
|
{/* <TableCell>{t("Lot Location")}</TableCell> */} |
|
|
|
<TableCell align="right">{t("Lot Required Pick Qty")}</TableCell> |
|
|
|
{/* <TableCell align="right">{t("Original Available Qty")}</TableCell> */} |
|
|
|
<TableCell align="center">{t("Lot Actual Pick Qty")}</TableCell> |
|
|
|
<TableCell align="right">{t("Scan Result")}</TableCell> |
|
|
|
<TableCell align="center">{t("Submit Required Pick Qty")}</TableCell> |
|
|
|
{/* <TableCell align="right">{t("Remaining Available Qty")}</TableCell> */} |
|
|
|
<TableCell align="right">{t("Finish Scan?")}</TableCell> |
|
|
|
<TableCell align="center">{t("Action")}</TableCell> |
|
|
|
|
|
|
|
{/* <TableCell align="center">{t("Action")}</TableCell> */} |
|
|
|
</TableRow> |
|
|
|
</TableHead> |
|
|
|
<TableBody> |
|
|
|
@@ -1028,7 +1098,7 @@ const paginatedData = useMemo(() => { |
|
|
|
{lot.routerRoute || '-'} |
|
|
|
</Typography> |
|
|
|
</TableCell> |
|
|
|
<TableCell>{lot.itemName}</TableCell> |
|
|
|
<TableCell>{lot.itemName+'('+lot.stockUnit+')'}</TableCell> |
|
|
|
<TableCell> |
|
|
|
<Box> |
|
|
|
<Typography |
|
|
|
@@ -1049,143 +1119,97 @@ const paginatedData = useMemo(() => { |
|
|
|
const inQty = lot.inQty || 0; |
|
|
|
const outQty = lot.outQty || 0; |
|
|
|
const result = inQty - outQty; |
|
|
|
return result.toLocaleString()+'('+lot.stockUnit+')'; |
|
|
|
})()} |
|
|
|
</TableCell> |
|
|
|
<TableCell align="center"> |
|
|
|
{/* ✅ QR Scan Button if not scanned, otherwise show TextField + Issue button */} |
|
|
|
{lot.stockOutLineStatus?.toLowerCase() === 'pending' ? ( |
|
|
|
<Button |
|
|
|
variant="outlined" |
|
|
|
size="small" |
|
|
|
onClick={() => { |
|
|
|
setSelectedLotForQr(lot); |
|
|
|
setQrModalOpen(true); |
|
|
|
resetScan(); |
|
|
|
}} |
|
|
|
disabled={ |
|
|
|
(lot.lotAvailability === 'expired' || |
|
|
|
lot.lotAvailability === 'status_unavailable' || |
|
|
|
lot.lotAvailability === 'rejected') |
|
|
|
} |
|
|
|
sx={{ |
|
|
|
fontSize: '0.7rem', |
|
|
|
py: 0.5, |
|
|
|
minHeight: '40px', |
|
|
|
whiteSpace: 'nowrap', |
|
|
|
minWidth: '80px', |
|
|
|
}} |
|
|
|
startIcon={<QrCodeIcon />} |
|
|
|
title="Click to scan QR code" |
|
|
|
> |
|
|
|
{t("Scan")} |
|
|
|
</Button> |
|
|
|
) : ( |
|
|
|
|
|
|
|
<Stack direction="row" spacing={1} alignItems="center"> |
|
|
|
<TextField |
|
|
|
type="number" |
|
|
|
size="small" |
|
|
|
value={pickQtyData[`${lot.pickOrderLineId}-${lot.lotId}`] || ''} |
|
|
|
onChange={(e) => { |
|
|
|
const lotKey = `${lot.pickOrderLineId}-${lot.lotId}`; |
|
|
|
handlePickQtyChange(lotKey, parseFloat(e.target.value) || 0); |
|
|
|
}} |
|
|
|
disabled={ |
|
|
|
(lot.lotAvailability === 'expired' || |
|
|
|
lot.lotAvailability === 'status_unavailable' || |
|
|
|
lot.lotAvailability === 'rejected') || |
|
|
|
lot.stockOutLineStatus === 'completed' |
|
|
|
} |
|
|
|
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" |
|
|
|
/> |
|
|
|
|
|
|
|
<Button |
|
|
|
variant="outlined" |
|
|
|
size="small" |
|
|
|
onClick={() => handlePickExecutionForm(lot)} |
|
|
|
sx={{ |
|
|
|
fontSize: '0.7rem', |
|
|
|
py: 0.5, |
|
|
|
minHeight: '28px', |
|
|
|
minWidth: '60px', |
|
|
|
borderColor: 'warning.main', |
|
|
|
color: 'warning.main' |
|
|
|
}} |
|
|
|
title="Report missing or bad items" |
|
|
|
> |
|
|
|
{t("Issue")} |
|
|
|
</Button> |
|
|
|
</Stack> |
|
|
|
)} |
|
|
|
</TableCell> |
|
|
|
{/* <TableCell align="right"> |
|
|
|
{(() => { |
|
|
|
const inQty = lot.inQty || 0; |
|
|
|
const outQty = lot.outQty || 0; |
|
|
|
const result = inQty - outQty; |
|
|
|
return result.toLocaleString(); |
|
|
|
return result.toLocaleString()+'('+lot.uomShortDesc+')'; |
|
|
|
})()} |
|
|
|
</TableCell> */} |
|
|
|
<TableCell align="center"> |
|
|
|
<Checkbox |
|
|
|
checked={lot.stockOutLineStatus?.toLowerCase() !== 'pending'} |
|
|
|
disabled={true} |
|
|
|
readOnly={true} |
|
|
|
sx={{ |
|
|
|
color: lot.stockOutLineStatus?.toLowerCase() !== 'pending' ? 'success.main' : 'grey.400', |
|
|
|
'&.Mui-checked': { |
|
|
|
color: 'success.main', |
|
|
|
}, |
|
|
|
}} |
|
|
|
/> |
|
|
|
</TableCell> |
|
|
|
<TableCell align="center"> |
|
|
|
<Stack direction="column" spacing={1} alignItems="center"> |
|
|
|
<Button |
|
|
|
variant="contained" |
|
|
|
onClick={() => { |
|
|
|
handleSubmitPickQty(lot); |
|
|
|
}} |
|
|
|
disabled={ |
|
|
|
(lot.lotAvailability === 'expired' || |
|
|
|
lot.lotAvailability === 'status_unavailable' || |
|
|
|
lot.lotAvailability === 'rejected') || |
|
|
|
!pickQtyData[`${lot.pickOrderLineId}-${lot.lotId}`] || |
|
|
|
!lot.stockOutLineStatus || |
|
|
|
!['pending','checked', 'partially_completed'].includes(lot.stockOutLineStatus.toLowerCase()) |
|
|
|
} |
|
|
|
sx={{ |
|
|
|
fontSize: '0.75rem', |
|
|
|
py: 0.5, |
|
|
|
minHeight: '28px' |
|
|
|
}} |
|
|
|
> |
|
|
|
{t("Submit")} |
|
|
|
</Button> |
|
|
|
</Stack> |
|
|
|
</TableCell> |
|
|
|
</TableRow> |
|
|
|
{lot.stockOutLineStatus?.toLowerCase() !== 'pending' ? ( |
|
|
|
<Checkbox |
|
|
|
checked={lot.stockOutLineStatus?.toLowerCase() !== 'pending'} |
|
|
|
disabled={true} |
|
|
|
readOnly={true} |
|
|
|
sx={{ |
|
|
|
color: lot.stockOutLineStatus?.toLowerCase() !== 'pending' ? 'success.main' : 'grey.400', |
|
|
|
'&.Mui-checked': { |
|
|
|
color: 'success.main', |
|
|
|
}, |
|
|
|
}} |
|
|
|
/> |
|
|
|
) : null} |
|
|
|
</TableCell> |
|
|
|
<TableCell align="center"> |
|
|
|
<Box sx={{ display: 'flex', justifyContent: 'center' }}> |
|
|
|
<Stack direction="row" spacing={1} alignItems="center"> |
|
|
|
<Button |
|
|
|
variant="contained" |
|
|
|
onClick={() => { |
|
|
|
// Submit with default lot required pick qty |
|
|
|
const lotKey = `${lot.pickOrderLineId}-${lot.lotId}`; |
|
|
|
handlePickQtyChange(lotKey, lot.requiredQty || lot.pickOrderLineRequiredQty); |
|
|
|
handleSubmitPickQty(lot); |
|
|
|
}} |
|
|
|
disabled={ |
|
|
|
(lot.lotAvailability === 'expired' || |
|
|
|
lot.lotAvailability === 'status_unavailable' || |
|
|
|
lot.lotAvailability === 'rejected') || |
|
|
|
lot.stockOutLineStatus === 'completed' || |
|
|
|
lot.stockOutLineStatus === 'pending' // ✅ Disable when QR scan not passed |
|
|
|
} |
|
|
|
sx={{ |
|
|
|
fontSize: '0.75rem', |
|
|
|
py: 0.5, |
|
|
|
minHeight: '28px', |
|
|
|
minWidth: '70px' |
|
|
|
}} |
|
|
|
> |
|
|
|
{t("Submit")} |
|
|
|
</Button> |
|
|
|
|
|
|
|
<Button |
|
|
|
variant="outlined" |
|
|
|
size="small" |
|
|
|
onClick={() => handlePickExecutionForm(lot)} |
|
|
|
disabled={ |
|
|
|
(lot.lotAvailability === 'expired' || |
|
|
|
lot.lotAvailability === 'status_unavailable' || |
|
|
|
lot.lotAvailability === 'rejected') || |
|
|
|
lot.stockOutLineStatus === 'completed' || // ✅ Disable when finished |
|
|
|
lot.stockOutLineStatus === 'pending' // ✅ Disable when QR scan not passed |
|
|
|
} |
|
|
|
sx={{ |
|
|
|
fontSize: '0.7rem', |
|
|
|
py: 0.5, |
|
|
|
minHeight: '28px', |
|
|
|
minWidth: '60px', |
|
|
|
borderColor: 'warning.main', |
|
|
|
color: 'warning.main' |
|
|
|
}} |
|
|
|
title="Report missing or bad items" |
|
|
|
> |
|
|
|
{t("Issue")} |
|
|
|
</Button> |
|
|
|
</Stack> |
|
|
|
</Box> |
|
|
|
</TableCell> |
|
|
|
|
|
|
|
</TableRow> |
|
|
|
)) |
|
|
|
)} |
|
|
|
</TableBody> |
|
|
|
</Table> |
|
|
|
</TableContainer> |
|
|
|
|
|
|
|
{/* ✅ Status Messages Display - Move here, outside the table */} |
|
|
|
{paginatedData.length > 0 && ( |
|
|
|
<Box sx={{ mt: 2, p: 2, backgroundColor: 'grey.50', borderRadius: 1 }}> |
|
|
|
{paginatedData.map((lot, index) => ( |
|
|
|
<Box key={`${lot.pickOrderLineId}-${lot.lotId}`} sx={{ mb: 1 }}> |
|
|
|
<Typography variant="body2" color="text.secondary"> |
|
|
|
<strong>{t("Lot")} {lot.lotNo}:</strong> {getStatusMessage(lot)} |
|
|
|
</Typography> |
|
|
|
</Box> |
|
|
|
))} |
|
|
|
</Box> |
|
|
|
)} |
|
|
|
<TablePagination |
|
|
|
component="div" |
|
|
|
count={combinedLotData.length} |
|
|
|
|