|
- "use client";
-
- import {
- Box,
- Button,
- Checkbox,
- Paper,
- Stack,
- Table,
- TableBody,
- TableCell,
- TableContainer,
- TableHead,
- TableRow,
- TextField,
- Typography,
- TablePagination,
- Modal,
- } from "@mui/material";
- import { useCallback, useMemo, useState, useEffect } from "react";
- import { useTranslation } from "react-i18next";
- import QrCodeIcon from '@mui/icons-material/QrCode';
- import { GetPickOrderLineInfo, recordPickExecutionIssue } from "@/app/api/pickOrder/actions";
- import { useQrCodeScannerContext } from '../QrCodeScannerProvider/QrCodeScannerProvider';
- import { updateInventoryLotLineStatus } from "@/app/api/inventory/actions";
- import { updateStockOutLineStatus } from "@/app/api/pickOrder/actions";
- import { fetchStockInLineInfo } from "@/app/api/po/actions"; // Add this import
- import PickExecutionForm from "./PickExecutionForm";
- interface LotPickData {
- id: number;
- lotId: number;
- lotNo: string;
- expiryDate: string;
- location: string;
- stockUnit: string;
- inQty: number;
- availableQty: number;
- requiredQty: number;
- actualPickQty: number;
- lotStatus: string;
- outQty: number;
- holdQty: number;
- totalPickedByAllPickOrders: number;
- lotAvailability: 'available' | 'insufficient_stock' | 'expired' | 'status_unavailable' | 'rejected'; // 添加 'rejected'
- stockOutLineId?: number;
- stockOutLineStatus?: string;
- stockOutLineQty?: number;
- }
-
- interface PickQtyData {
- [lineId: number]: {
- [lotId: number]: number;
- };
- }
-
- interface LotTableProps {
- lotData: LotPickData[];
- selectedRowId: number | null;
- selectedRow: (GetPickOrderLineInfo & { pickOrderCode: string; pickOrderId: number }) | null; // 添加 pickOrderId
- pickQtyData: PickQtyData;
- selectedLotRowId: string | null;
- selectedLotId: number | null;
- onLotSelection: (uniqueLotId: string, lotId: number) => void;
- onPickQtyChange: (lineId: number, lotId: number, value: number) => void;
- onSubmitPickQty: (lineId: number, lotId: number) => void;
- onCreateStockOutLine: (inventoryLotLineId: number) => void;
- onQcCheck: (line: GetPickOrderLineInfo, pickOrderCode: string) => void;
- onLotSelectForInput: (lot: LotPickData) => void;
- showInputBody: boolean;
- totalPickedByAllPickOrders: number;
- outQty: number;
- holdQty: number;
- setShowInputBody: (show: boolean) => void;
- selectedLotForInput: LotPickData | null;
- generateInputBody: () => any;
- onDataRefresh: () => Promise<void>;
- onLotDataRefresh: () => Promise<void>;
- }
-
- // QR Code Modal Component
- const QrCodeModal: React.FC<{
- open: boolean;
- onClose: () => void;
- lot: LotPickData | null;
- onQrCodeSubmit: (lotNo: string) => void;
- }> = ({ open, onClose, lot, onQrCodeSubmit }) => {
- const { t } = useTranslation("pickOrder");
- const { values: qrValues, isScanning, startScan, stopScan, resetScan } = useQrCodeScannerContext();
- const [manualInput, setManualInput] = useState<string>('');
- const [validationErrors, setValidationErrors] = useState<{[key: string]: string}>({});
- // Add state to track manual input submission
- const [manualInputSubmitted, setManualInputSubmitted] = useState<boolean>(false);
- const [manualInputError, setManualInputError] = useState<boolean>(false);
- const [isProcessingQr, setIsProcessingQr] = useState<boolean>(false);
- const [qrScanFailed, setQrScanFailed] = useState<boolean>(false);
- const [qrScanSuccess, setQrScanSuccess] = useState<boolean>(false);
-
- // Add state to track processed QR codes to prevent re-processing
- const [processedQrCodes, setProcessedQrCodes] = useState<Set<string>>(new Set());
-
- // Add state to store the scanned QR result
- const [scannedQrResult, setScannedQrResult] = useState<string>('');
-
- // Process scanned QR codes with new format
- useEffect(() => {
- if (qrValues.length > 0 && lot && !isProcessingQr && !qrScanSuccess) {
- const latestQr = qrValues[qrValues.length - 1];
-
- // Check if this QR code has already been processed
- if (processedQrCodes.has(latestQr)) {
- console.log("QR code already processed, skipping...");
- return;
- }
-
- // Add to processed set immediately to prevent re-processing
- setProcessedQrCodes(prev => new Set(prev).add(latestQr));
-
- try {
- // Parse QR code as JSON
- const qrData = JSON.parse(latestQr);
-
- // Check if it has the expected structure
- if (qrData.stockInLineId && qrData.itemId) {
- setIsProcessingQr(true);
- setQrScanFailed(false);
-
- // Fetch stock in line info to get lotNo
- fetchStockInLineInfo(qrData.stockInLineId)
- .then((stockInLineInfo) => {
- console.log("Stock in line info:", stockInLineInfo);
-
- // Store the scanned result for display
- setScannedQrResult(stockInLineInfo.lotNo || 'Unknown lot number');
-
- // Compare lotNo from API with expected lotNo
- if (stockInLineInfo.lotNo === lot.lotNo) {
- console.log(` QR Code verified for lot: ${lot.lotNo}`);
- setQrScanSuccess(true);
- onQrCodeSubmit(lot.lotNo);
- onClose();
- resetScan();
- } else {
- console.log(`❌ QR Code mismatch. Expected: ${lot.lotNo}, Got: ${stockInLineInfo.lotNo}`);
- setQrScanFailed(true);
- setManualInputError(true);
- setManualInputSubmitted(true);
- // DON'T stop scanning - allow new QR codes to be processed
- }
- })
- .catch((error) => {
- console.error("Error fetching stock in line info:", error);
- setScannedQrResult('Error fetching data');
- setQrScanFailed(true);
- setManualInputError(true);
- setManualInputSubmitted(true);
- // DON'T stop scanning - allow new QR codes to be processed
- })
- .finally(() => {
- setIsProcessingQr(false);
- });
- } else {
- // Fallback to old format (direct lotNo comparison)
- const qrContent = latestQr.replace(/[{}]/g, '');
-
- // Store the scanned result for display
- setScannedQrResult(qrContent);
-
- if (qrContent === lot.lotNo) {
- setQrScanSuccess(true);
- onQrCodeSubmit(lot.lotNo);
- onClose();
- resetScan();
- } else {
- setQrScanFailed(true);
- setManualInputError(true);
- setManualInputSubmitted(true);
- // DON'T stop scanning - allow new QR codes to be processed
- }
- }
- } catch (error) {
- // If JSON parsing fails, fallback to old format
- console.log("QR code is not JSON format, trying direct comparison");
- const qrContent = latestQr.replace(/[{}]/g, '');
-
- // Store the scanned result for display
- setScannedQrResult(qrContent);
-
- if (qrContent === lot.lotNo) {
- setQrScanSuccess(true);
- onQrCodeSubmit(lot.lotNo);
- onClose();
- resetScan();
- } else {
- setQrScanFailed(true);
- setManualInputError(true);
- setManualInputSubmitted(true);
- // DON'T stop scanning - allow new QR codes to be processed
- }
- }
- }
- }, [qrValues, lot, onQrCodeSubmit, onClose, resetScan, isProcessingQr, qrScanSuccess, processedQrCodes, stopScan]);
-
- // Clear states when modal opens or lot changes
- useEffect(() => {
- if (open) {
- setManualInput('');
- setManualInputSubmitted(false);
- setManualInputError(false);
- setIsProcessingQr(false);
- setQrScanFailed(false);
- setQrScanSuccess(false);
- setScannedQrResult(''); // Clear scanned result
- // Clear processed QR codes when modal opens
- setProcessedQrCodes(new Set());
- }
- }, [open]);
-
- useEffect(() => {
- if (lot) {
- setManualInput('');
- setManualInputSubmitted(false);
- setManualInputError(false);
- setIsProcessingQr(false);
- setQrScanFailed(false);
- setQrScanSuccess(false);
- setScannedQrResult(''); // Clear scanned result
- // Clear processed QR codes when lot changes
- setProcessedQrCodes(new Set());
- }
- }, [lot]);
-
- // Auto-submit manual input when it matches (but only if QR scan hasn't failed)
- useEffect(() => {
- if (manualInput.trim() === lot?.lotNo && manualInput.trim() !== '' && !qrScanFailed && !qrScanSuccess) {
- console.log('🔄 Auto-submitting manual input:', manualInput.trim());
-
- const timer = setTimeout(() => {
- setQrScanSuccess(true);
- onQrCodeSubmit(lot.lotNo);
- onClose();
- setManualInput('');
- setManualInputError(false);
- setManualInputSubmitted(false);
- }, 200);
-
- return () => clearTimeout(timer);
- }
- }, [manualInput, lot, onQrCodeSubmit, onClose, qrScanFailed, qrScanSuccess]);
-
- // Add the missing handleManualSubmit function
- const handleManualSubmit = () => {
- if (manualInput.trim() === lot?.lotNo) {
- setQrScanSuccess(true);
- onQrCodeSubmit(lot.lotNo);
- onClose();
- setManualInput('');
- } else {
- setQrScanFailed(true);
- setManualInputError(true);
- setManualInputSubmitted(true);
- }
- };
-
- // Add function to restart scanning after manual input error
- const handleRestartScan = () => {
- setQrScanFailed(false);
- setManualInputError(false);
- setManualInputSubmitted(false);
- setProcessedQrCodes(new Set()); // Clear processed QR codes
- startScan(); // Restart scanning
- };
-
- 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>
- {t("QR Code Scan for Lot")}: {lot?.lotNo}
- </Typography>
-
- {/* Show processing status */}
- {isProcessingQr && (
- <Box sx={{ mb: 2, p: 2, backgroundColor: '#e3f2fd', borderRadius: 1 }}>
- <Typography variant="body2" color="primary">
- {t("Processing QR code...")}
- </Typography>
- </Box>
- )}
-
-
- {/* Manual Input with Submit-Triggered Helper Text */}
- {false &&(
- <Box sx={{ mb: 2 }}>
- <Typography variant="body2" gutterBottom>
- <strong>{t("Manual Input")}:</strong>
- </Typography>
- <TextField
- fullWidth
- size="small"
- value={manualInput}
- onChange={(e) => {
- setManualInput(e.target.value);
- // Reset error states when user starts typing
- if (qrScanFailed || manualInputError) {
- setQrScanFailed(false);
- setManualInputError(false);
- setManualInputSubmitted(false);
- }
- }}
- sx={{ mb: 1 }}
- error={manualInputSubmitted && manualInputError}
- helperText={
- manualInputSubmitted && manualInputError
- ? `${t("The input is not the same as the expected lot number.")}`
- : ''
- }
- />
- <Button
- variant="contained"
- onClick={handleManualSubmit}
- disabled={!manualInput.trim()}
- size="small"
- color="primary"
- >
- {t("Submit")}
- </Button>
- </Box>
- )}
-
- {/* Show QR Scan Status */}
- {qrValues.length > 0 && (
- <Box sx={{
- mb: 2,
- p: 2,
- backgroundColor: qrScanFailed ? '#ffebee' : qrScanSuccess ? '#e8f5e8' : '#f5f5f5',
- borderRadius: 1
- }}>
- <Typography variant="body2" color={qrScanFailed ? 'error' : qrScanSuccess ? 'success' : 'text.secondary'}>
- <strong>{t("QR Scan Result:")}</strong> {scannedQrResult}
- </Typography>
-
- {qrScanSuccess && (
- <Typography variant="caption" color="success" display="block">
- {t("Verified successfully!")}
- </Typography>
- )}
- </Box>
- )}
-
- <Box sx={{ mt: 2, textAlign: 'right' }}>
- <Button onClick={onClose} variant="outlined">
- {t("Cancel")}
- </Button>
- </Box>
- </Box>
- </Modal>
- );
- };
-
-
- const LotTable: React.FC<LotTableProps> = ({
- lotData,
- selectedRowId,
- selectedRow,
- pickQtyData,
- selectedLotRowId,
- selectedLotId,
- onLotSelection,
- onPickQtyChange,
- onSubmitPickQty,
- onCreateStockOutLine,
- onQcCheck,
- onLotSelectForInput,
- showInputBody,
- setShowInputBody,
- selectedLotForInput,
- generateInputBody,
- onDataRefresh,
- onLotDataRefresh,
- }) => {
- const { t } = useTranslation("pickOrder");
- const calculateRemainingRequiredQty = useCallback((lot: LotPickData) => {
- const requiredQty = lot.requiredQty || 0;
- const stockOutLineQty = lot.stockOutLineQty || 0;
- return Math.max(0, requiredQty - stockOutLineQty);
- }, []);
- // 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<LotPickData | null>(null);
- const [manualQrInput, setManualQrInput] = useState<string>('');
-
- // 分页控制器
- const [lotTablePagingController, setLotTablePagingController] = useState({
- pageNum: 0,
- pageSize: 10,
- });
-
- // 添加状态消息生成函数
- const getStatusMessage = useCallback((lot: LotPickData) => {
-
- switch (lot.stockOutLineStatus?.toLowerCase()) {
- case 'pending':
- return t("Please finish QR code scanand 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.");
- }
- }, []);
-
- const prepareLotTableData = useMemo(() => {
- return lotData.map((lot) => ({
- ...lot,
- id: lot.lotId,
- }));
- }, [lotData]);
-
- // 分页数据
- const paginatedLotTableData = useMemo(() => {
- const startIndex = lotTablePagingController.pageNum * lotTablePagingController.pageSize;
- const endIndex = startIndex + lotTablePagingController.pageSize;
- return prepareLotTableData.slice(startIndex, endIndex);
- }, [prepareLotTableData, lotTablePagingController]);
-
- // 分页处理函数
- const handleLotTablePageChange = useCallback((event: unknown, newPage: number) => {
- setLotTablePagingController(prev => ({
- ...prev,
- pageNum: newPage,
- }));
- }, []);
-
- const handleLotTablePageSizeChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
- const newPageSize = parseInt(event.target.value, 10);
- setLotTablePagingController({
- pageNum: 0,
- pageSize: newPageSize,
- });
- }, []);
- const calculateRemainingAvailableQty = useCallback((lot: LotPickData) => {
- if (!selectedRowId) return lot.availableQty;
- const lactualPickQty = lot.actualPickQty || 0;
- const actualPickQty = pickQtyData[selectedRowId]?.[lot.lotId] || 0;
-
- 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
- });
-
- console.log(" Stock out line updated to 'checked':", stockOutLineUpdate);
-
- // Close modal
- setQrModalOpen(false);
- setSelectedLotForQr(null);
- 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 is complete
- setTimeout(() => {
- onPickQtyChange(selectedRowId, lotId, requiredQty);
- console.log(` Auto-set pick quantity to ${requiredQty} for lot ${lotNo}`);
- }, 500); // 500ms delay to ensure refresh is complete
- }
-
- // Show success message
- 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.");
- }
- } 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);
- const [selectedLotForExecutionForm, setSelectedLotForExecutionForm] = useState<LotPickData | null>(null);
-
- // 添加处理函数
- const handlePickExecutionForm = useCallback((lot: LotPickData) => {
- console.log("=== Pick Execution Form ===");
- console.log("Lot data:", lot);
-
- if (!lot) {
- console.warn("No lot data provided for pick execution form");
- return;
- }
-
- console.log("Opening pick execution form for lot:", lot.lotNo);
-
- setSelectedLotForExecutionForm(lot);
- setPickExecutionFormOpen(true);
-
- console.log("Pick execution form opened for lot ID:", lot.lotId);
- }, []);
-
- const handlePickExecutionFormSubmit = useCallback(async (data: any) => {
- try {
- console.log("Pick execution form submitted:", data);
-
- // 调用 API 提交数据
- const result = await recordPickExecutionIssue(data);
- console.log("Pick execution issue recorded:", result);
-
- if (result && result.code === "SUCCESS") {
- console.log(" Pick execution issue recorded successfully");
- } else {
- console.error("❌ Failed to record pick execution issue:", result);
- }
-
- setPickExecutionFormOpen(false);
- setSelectedLotForExecutionForm(null);
-
- // 刷新数据
- if (onDataRefresh) {
- await onDataRefresh();
- }
- if (onLotDataRefresh) {
- await onLotDataRefresh();
- }
- } catch (error) {
- console.error("Error submitting pick execution form:", error);
- }
- }, [onDataRefresh, onLotDataRefresh]);
-
- return (
- <>
- <TableContainer component={Paper}>
- <Table>
- <TableHead>
- <TableRow>
- <TableCell>{t("Selected")}</TableCell>
- <TableCell>{t("Lot#")}</TableCell>
- <TableCell>{t("Lot Expiry Date")}</TableCell>
- <TableCell>{t("Lot Location")}</TableCell>
- <TableCell>{t("Stock Unit")}</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("Available Lot")}</TableCell>*/}
- <TableCell align="right">{t("Remaining Available Qty")}</TableCell>
-
- {/*<TableCell align="center">{t("QR Code Scan")}</TableCell>*/}
- {/*}
- <TableCell align="center">{t("Reject")}</TableCell>
- */}
-
- <TableCell align="center">{t("Action")}</TableCell>
- </TableRow>
- </TableHead>
- <TableBody>
- {paginatedLotTableData.length === 0 ? (
- <TableRow>
- <TableCell colSpan={11} align="center">
- <Typography variant="body2" color="text.secondary">
- {t("No data available")}
- </Typography>
- </TableCell>
- </TableRow>
- ) : (
- paginatedLotTableData.map((lot, index) => (
- <TableRow
- key={lot.id}
- sx={{
- backgroundColor: lot.lotAvailability === 'rejected' ? 'grey.100' : 'inherit',
- opacity: lot.lotAvailability === 'rejected' ? 0.6 : 1,
- '& .MuiTableCell-root': {
- color: lot.lotAvailability === 'rejected' ? 'text.disabled' : 'inherit'
- }
- }}
- >
- <TableCell>
- <Checkbox
- checked={selectedLotRowId === `row_${index}`}
- onChange={() => 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"
- />
- </TableCell>
- <TableCell>
- <Box>
- <Typography
- sx={{
- color: lot.lotAvailability === 'rejected' ? 'text.disabled' : 'inherit',
- opacity: lot.lotAvailability === 'rejected' ? 0.6 : 1
- }}
- >
- {lot.lotNo}
- </Typography>
- {/*
- {lot.lotAvailability !== 'available' && (
- <Typography variant="caption" color="error" display="block">
- ({lot.lotAvailability === 'expired' ? 'Expired' :
- lot.lotAvailability === 'insufficient_stock' ? 'Insufficient' :
- lot.lotAvailability === 'rejected' ? 'Rejected' : // 添加 rejected 显示
- 'Unavailable'})
- </Typography>
- )} */}
- </Box>
- </TableCell>
- <TableCell>{lot.expiryDate}</TableCell>
- <TableCell>{lot.location}</TableCell>
- <TableCell>{lot.stockUnit}</TableCell>
- <TableCell align="right">{calculateRemainingRequiredQty(lot).toLocaleString()}</TableCell>
- <TableCell align="right">
- {(() => {
- const inQty = lot.inQty || 0;
- const outQty = lot.outQty || 0;
-
-
- const result = inQty - outQty;
- return result.toLocaleString();
- })()}
- </TableCell>
- <TableCell align="center">
- {/* Show QR Scan Button if not scanned, otherwise show TextField + Pick Form */}
- {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') ||
- selectedLotRowId !== `row_${index}`
- }
- sx={{
- fontSize: '0.7rem',
- py: 0.5,
- minHeight: '40px',
- whiteSpace: 'nowrap',
- minWidth: '80px',
- opacity: selectedLotRowId === `row_${index}` ? 1 : 0.5
- }}
- startIcon={<QrCodeIcon />}
- title={
- selectedLotRowId !== `row_${index}`
- ? "Please select this lot first to enable QR scanning"
- : "Click to scan QR code"
- }
- >
- {t("Scan")}
- </Button>
- ) : (
- <Stack
- direction="row"
- spacing={1}
- alignItems="center"
- justifyContent="center" // 添加水平居中
- sx={{
- width: '100%', // 确保占满整个单元格宽度
- minHeight: '40px' // 设置最小高度确保垂直居中
- }}
- >
- {/* 恢复 TextField 用于正常数量输入 */}
- <TextField
- type="number"
- size="small"
- value={pickQtyData[selectedRowId!]?.[lot.lotId] || ''}
- onChange={(e) => {
- if (selectedRowId) {
- const inputValue = parseFloat(e.target.value) || 0;
- const maxAllowed = Math.min(calculateRemainingAvailableQty(lot), calculateRemainingRequiredQty(lot));
-
- 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}`]}
- helperText={validationErrors[`lot_${lot.lotId}`]}
- 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 按钮用于问题情况 */}
- <Button
- variant="outlined"
- size="small"
- onClick={() => handlePickExecutionForm(lot)}
- disabled={
- (lot.lotAvailability === 'expired' ||
- lot.lotAvailability === 'status_unavailable' ||
- lot.lotAvailability === 'rejected') ||
- selectedLotRowId !== `row_${index}`
- }
- 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">{lot.availableQty.toLocaleString()}</TableCell>*/}
- <TableCell align="right">{calculateRemainingAvailableQty(lot).toLocaleString()}</TableCell>
-
- <TableCell align="center">
-
- <Stack direction="column" spacing={1} alignItems="center">
- <Button
- variant="contained"
- onClick={() => {
- if (selectedRowId) {
- onSubmitPickQty(selectedRowId, lot.lotId);
- }
-
- }}
- disabled={
- (lot.lotAvailability === 'expired' ||
- lot.lotAvailability === 'status_unavailable' ||
- lot.lotAvailability === 'rejected') || // 添加 rejected
- !pickQtyData[selectedRowId!]?.[lot.lotId] ||
- !lot.stockOutLineStatus ||
- !['pending','checked', 'partially_completed'].includes(lot.stockOutLineStatus.toLowerCase())
- }
- // Allow submission for available AND insufficient_stock lots
- sx={{
- fontSize: '0.75rem',
- py: 0.5,
- minHeight: '28px'
- }}
- >
- {t("Submit")}
- </Button>
- </Stack>
- </TableCell>
- </TableRow>
- ))
- )}
- </TableBody>
- </Table>
- </TableContainer>
-
- {/* Status Messages Display */}
- {paginatedLotTableData.length > 0 && (
- <Box sx={{ mt: 2, p: 2, backgroundColor: 'grey.50', borderRadius: 1 }}>
- {paginatedLotTableData.map((lot, index) => (
- <Box key={lot.id} sx={{ mb: 1 }}>
- <Typography variant="body2" color="text.secondary">
- <strong>{t("Lot")} {lot.lotNo}:</strong> {getStatusMessage(lot)}
- </Typography>
- </Box>
- ))}
- </Box>
- )}
-
-
-
- <TablePagination
- component="div"
- count={prepareLotTableData.length}
- page={lotTablePagingController.pageNum}
- rowsPerPage={lotTablePagingController.pageSize}
- onPageChange={handleLotTablePageChange}
- onRowsPerPageChange={handleLotTablePageSizeChange}
- rowsPerPageOptions={[10, 25, 50]}
- labelRowsPerPage={t("Rows per page")}
- labelDisplayedRows={({ from, to, count }) =>
- `${from}-${to} of ${count !== -1 ? count : `more than ${to}`}`
- }
- />
-
- {/* QR Code Modal */}
- <QrCodeModal
- open={qrModalOpen}
- onClose={() => {
- setQrModalOpen(false);
- setSelectedLotForQr(null);
- stopScan();
- resetScan();
- }}
- lot={selectedLotForQr}
- onQrCodeSubmit={handleQrCodeSubmit}
- />
-
- {/* Pick Execution Form Modal */}
- {pickExecutionFormOpen && selectedLotForExecutionForm && selectedRow && (
- <PickExecutionForm
- open={pickExecutionFormOpen}
- onClose={() => {
- setPickExecutionFormOpen(false);
- setSelectedLotForExecutionForm(null);
- }}
- onSubmit={handlePickExecutionFormSubmit}
- selectedLot={selectedLotForExecutionForm}
- selectedPickOrderLine={selectedRow}
- pickOrderId={selectedRow.pickOrderId}
- pickOrderCreateDate={new Date()}
- />
- )}
- </>
- );
- };
-
- export default LotTable;
|