|
- "use client";
-
- import {
- Box,
- Button,
- Stack,
- TextField,
- Typography,
- Alert,
- CircularProgress,
- Table,
- TableBody,
- TableCell,
- TableContainer,
- TableHead,
- TableRow,
- Paper,
- Checkbox,
- TablePagination,
- Modal,
- Chip,
- } from "@mui/material";
- import dayjs from 'dayjs';
- import TestQrCodeProvider from '../QrCodeScannerProvider/TestQrCodeProvider';
- import { fetchLotDetail } from "@/app/api/inventory/actions";
- import { useCallback, useEffect, useState, useRef, useMemo } from "react";
- import { useTranslation } from "react-i18next";
- import { useRouter } from "next/navigation";
- import {
- fetchALLPickOrderLineLotDetails,
- updateStockOutLineStatus,
- createStockOutLine,
- updateStockOutLine,
- recordPickExecutionIssue,
- fetchFGPickOrders, // Add this import
- FGPickOrderResponse,
- stockReponse,
- PickExecutionIssueData,
- checkPickOrderCompletion,
- fetchAllPickOrderLotsHierarchical,
- PickOrderCompletionResponse,
- checkAndCompletePickOrderByConsoCode,
- updateSuggestedLotLineId,
- confirmLotSubstitution,
- fetchDoPickOrderDetail, // 必须添加
- DoPickOrderDetail, // 必须添加
- fetchFGPickOrdersByUserId
- } from "@/app/api/pickOrder/actions";
-
- import FGPickOrderInfoCard from "./FGPickOrderInfoCard";
- import LotConfirmationModal from "./LotConfirmationModal";
- //import { fetchItem } from "@/app/api/settings/item";
- import { updateInventoryLotLineStatus, analyzeQrCode } from "@/app/api/inventory/actions";
- import { fetchNameList, NameList } from "@/app/api/user/actions";
- import {
- FormProvider,
- useForm,
- } from "react-hook-form";
- import SearchBox, { Criterion } from "../SearchBox";
- import { CreateStockOutLine } from "@/app/api/pickOrder/actions";
- import { updateInventoryLotLineQuantities } from "@/app/api/inventory/actions";
- import QrCodeIcon from '@mui/icons-material/QrCode';
- import { useQrCodeScannerContext } from '../QrCodeScannerProvider/QrCodeScannerProvider';
- import { useSession } from "next-auth/react";
- import { SessionWithTokens } from "@/config/authConfig";
- import { fetchStockInLineInfo } from "@/app/api/po/actions";
- import GoodPickExecutionForm from "./GoodPickExecutionForm";
- import FGPickOrderCard from "./FGPickOrderCard";
- interface Props {
- filterArgs: Record<string, any>;
- }
-
- // QR Code Modal Component (from LotTable)
- const QrCodeModal: React.FC<{
- open: boolean;
- onClose: () => void;
- lot: any | null;
- onQrCodeSubmit: (lotNo: string) => void;
- combinedLotData: any[]; // Add this prop
- }> = ({ open, onClose, lot, onQrCodeSubmit, combinedLotData }) => {
- const { t } = useTranslation("pickOrder");
- const { values: qrValues, isScanning, startScan, stopScan, resetScan } = useQrCodeScannerContext();
- const [manualInput, setManualInput] = useState<string>('');
-
- 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);
-
- const [processedQrCodes, setProcessedQrCodes] = useState<Set<string>>(new Set());
- const [scannedQrResult, setScannedQrResult] = useState<string>('');
- const [fgPickOrder, setFgPickOrder] = useState<FGPickOrderResponse | null>(null);
- // Process scanned QR codes
- useEffect(() => {
- if (qrValues.length > 0 && lot && !isProcessingQr && !qrScanSuccess) {
- const latestQr = qrValues[qrValues.length - 1];
-
- if (processedQrCodes.has(latestQr)) {
- console.log("QR code already processed, skipping...");
- return;
- }
-
- setProcessedQrCodes(prev => new Set(prev).add(latestQr));
-
- try {
- const qrData = JSON.parse(latestQr);
-
- if (qrData.stockInLineId && qrData.itemId) {
- setIsProcessingQr(true);
- setQrScanFailed(false);
-
- fetchStockInLineInfo(qrData.stockInLineId)
- .then((stockInLineInfo) => {
- console.log("Stock in line info:", stockInLineInfo);
- setScannedQrResult(stockInLineInfo.lotNo || 'Unknown lot number');
-
- 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);
- }
- })
- .catch((error) => {
- console.error("Error fetching stock in line info:", error);
- setScannedQrResult('Error fetching data');
- setQrScanFailed(true);
- setManualInputError(true);
- setManualInputSubmitted(true);
- })
- .finally(() => {
- setIsProcessingQr(false);
- });
- } else {
- const qrContent = latestQr.replace(/[{}]/g, '');
- setScannedQrResult(qrContent);
-
- if (qrContent === lot.lotNo) {
- setQrScanSuccess(true);
- onQrCodeSubmit(lot.lotNo);
- onClose();
- resetScan();
- } else {
- setQrScanFailed(true);
- setManualInputError(true);
- setManualInputSubmitted(true);
- }
- }
- } catch (error) {
- console.log("QR code is not JSON format, trying direct comparison");
- const qrContent = latestQr.replace(/[{}]/g, '');
- setScannedQrResult(qrContent);
-
- if (qrContent === lot.lotNo) {
- setQrScanSuccess(true);
- onQrCodeSubmit(lot.lotNo);
- onClose();
- resetScan();
- } else {
- setQrScanFailed(true);
- setManualInputError(true);
- setManualInputSubmitted(true);
- }
- }
- }
- }, [qrValues, lot, onQrCodeSubmit, onClose, resetScan, isProcessingQr, qrScanSuccess, processedQrCodes]);
-
- // Clear states when modal opens
- useEffect(() => {
- if (open) {
- setManualInput('');
- setManualInputSubmitted(false);
- setManualInputError(false);
- setIsProcessingQr(false);
- setQrScanFailed(false);
- setQrScanSuccess(false);
- setScannedQrResult('');
- setProcessedQrCodes(new Set());
- }
- }, [open]);
-
- useEffect(() => {
- if (lot) {
- setManualInput('');
- setManualInputSubmitted(false);
- setManualInputError(false);
- setIsProcessingQr(false);
- setQrScanFailed(false);
- setQrScanSuccess(false);
- setScannedQrResult('');
- setProcessedQrCodes(new Set());
- }
- }, [lot]);
-
- // Auto-submit manual input when it matches
- 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]);
-
- const handleManualSubmit = () => {
- if (manualInput.trim() === lot?.lotNo) {
- setQrScanSuccess(true);
- onQrCodeSubmit(lot.lotNo);
- onClose();
- setManualInput('');
- } else {
- setQrScanFailed(true);
- setManualInputError(true);
- setManualInputSubmitted(true);
- }
- };
-
- 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>
-
- {isProcessingQr && (
- <Box sx={{ mb: 2, p: 2, backgroundColor: '#e3f2fd', borderRadius: 1 }}>
- <Typography variant="body2" color="primary">
- {t("Processing QR code...")}
- </Typography>
- </Box>
- )}
-
- <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);
- 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>
-
- {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 PickExecution: React.FC<Props> = ({ filterArgs }) => {
- const { t } = useTranslation("pickOrder");
- const router = useRouter();
- const { data: session } = useSession() as { data: SessionWithTokens | null };
- const [doPickOrderDetail, setDoPickOrderDetail] = useState<DoPickOrderDetail | null>(null);
- const [selectedPickOrderId, setSelectedPickOrderId] = useState<number | null>(null);
- const [pickOrderSwitching, setPickOrderSwitching] = useState(false);
- const currentUserId = session?.id ? parseInt(session.id) : undefined;
- const [allLotsCompleted, setAllLotsCompleted] = useState(false);
- const [combinedLotData, setCombinedLotData] = useState<any[]>([]);
- const [combinedDataLoading, setCombinedDataLoading] = useState(false);
- const [originalCombinedData, setOriginalCombinedData] = useState<any[]>([]);
-
- const { values: qrValues, isScanning, startScan, stopScan, resetScan } = useQrCodeScannerContext();
-
- const [qrScanInput, setQrScanInput] = useState<string>('');
- const [qrScanError, setQrScanError] = useState<boolean>(false);
- const [qrScanSuccess, setQrScanSuccess] = useState<boolean>(false);
-
- const [pickQtyData, setPickQtyData] = useState<Record<string, number>>({});
- const [searchQuery, setSearchQuery] = useState<Record<string, any>>({});
-
- const [paginationController, setPaginationController] = useState({
- pageNum: 0,
- pageSize: 10,
- });
-
- const [usernameList, setUsernameList] = useState<NameList[]>([]);
-
- const initializationRef = useRef(false);
- const autoAssignRef = useRef(false);
-
- const formProps = useForm();
- const errors = formProps.formState.errors;
-
- // Add QR modal states
- const [qrModalOpen, setQrModalOpen] = useState(false);
- const [selectedLotForQr, setSelectedLotForQr] = useState<any | null>(null);
- const [lotConfirmationOpen, setLotConfirmationOpen] = useState(false);
- const [expectedLotData, setExpectedLotData] = useState<any>(null);
- const [scannedLotData, setScannedLotData] = useState<any>(null);
- const [isConfirmingLot, setIsConfirmingLot] = useState(false);
- // Add GoodPickExecutionForm states
- const [pickExecutionFormOpen, setPickExecutionFormOpen] = useState(false);
- 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 [isSubmittingAll, setIsSubmittingAll] = useState<boolean>(false);
-
-
-
- // Handle QR code button click
- const handleQrCodeClick = (pickOrderId: number) => {
- console.log(`QR Code clicked for pick order ID: ${pickOrderId}`);
- // TODO: Implement QR code functionality
- };
-
- const handleLotMismatch = useCallback((expectedLot: any, scannedLot: any) => {
- console.log("Lot mismatch detected:", { expectedLot, scannedLot });
- setExpectedLotData(expectedLot);
- setScannedLotData(scannedLot);
- setLotConfirmationOpen(true);
- }, []);
- const checkAllLotsCompleted = useCallback((lotData: any[]) => {
- if (lotData.length === 0) {
- setAllLotsCompleted(false);
- return false;
- }
-
- // Filter out rejected lots
- const nonRejectedLots = lotData.filter(lot =>
- lot.lotAvailability !== 'rejected' &&
- lot.stockOutLineStatus !== 'rejected'
- );
-
- if (nonRejectedLots.length === 0) {
- setAllLotsCompleted(false);
- return false;
- }
-
- // Check if all non-rejected lots are completed
- const allCompleted = nonRejectedLots.every(lot =>
- lot.stockOutLineStatus === 'completed'
- );
-
- setAllLotsCompleted(allCompleted);
- return allCompleted;
- }, []);
- // 在 fetchAllCombinedLotData 函数中(约 446-684 行)
-
- const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdOverride?: number) => {
- setCombinedDataLoading(true);
- try {
- const userIdToUse = userId || currentUserId;
-
- console.log("🔍 fetchAllCombinedLotData called with userId:", userIdToUse);
-
- if (!userIdToUse) {
- console.warn("⚠️ No userId available, skipping API call");
- setCombinedLotData([]);
- setOriginalCombinedData([]);
- setAllLotsCompleted(false);
- return;
- }
-
- // 获取新结构的层级数据
- const hierarchicalData = await fetchAllPickOrderLotsHierarchical(userIdToUse);
- console.log(" Hierarchical data (new structure):", hierarchicalData);
-
- // 检查数据结构
- if (!hierarchicalData.fgInfo || !hierarchicalData.pickOrders || hierarchicalData.pickOrders.length === 0) {
- console.warn("⚠️ No FG info or pick orders found");
- setCombinedLotData([]);
- setOriginalCombinedData([]);
- setAllLotsCompleted(false);
- return;
- }
-
- // 使用合并后的 pick order 对象(现在只有一个对象,包含所有数据)
- const mergedPickOrder = hierarchicalData.pickOrders[0];
-
- // 设置 FG info 到 fgPickOrders(用于显示 FG 信息卡片)
- // 修改第 478-509 行的 fgOrder 构建逻辑:
-
- const fgOrder: FGPickOrderResponse = {
- doPickOrderId: hierarchicalData.fgInfo.doPickOrderId,
- ticketNo: hierarchicalData.fgInfo.ticketNo,
- storeId: hierarchicalData.fgInfo.storeId,
- shopCode: hierarchicalData.fgInfo.shopCode,
- shopName: hierarchicalData.fgInfo.shopName,
- truckLanceCode: hierarchicalData.fgInfo.truckLanceCode,
- DepartureTime: hierarchicalData.fgInfo.departureTime,
- shopAddress: "",
- pickOrderCode: mergedPickOrder.pickOrderCodes?.[0] || "",
- // 兼容字段
- pickOrderId: mergedPickOrder.pickOrderIds?.[0] || 0,
- pickOrderConsoCode: mergedPickOrder.consoCode || "",
- pickOrderTargetDate: mergedPickOrder.targetDate || "",
- pickOrderStatus: mergedPickOrder.status || "",
- deliveryOrderId: mergedPickOrder.doOrderIds?.[0] || 0,
- deliveryNo: mergedPickOrder.deliveryOrderCodes?.[0] || "",
- deliveryDate: "",
- shopId: 0,
- shopPoNo: "",
- numberOfCartons: mergedPickOrder.pickOrderLines?.length || 0,
- qrCodeData: hierarchicalData.fgInfo.doPickOrderId,
-
- // 新增:多个 pick orders 信息 - 保持数组格式,不要 join
- numberOfPickOrders: mergedPickOrder.pickOrderIds?.length || 0,
- pickOrderIds: mergedPickOrder.pickOrderIds || [],
- pickOrderCodes: Array.isArray(mergedPickOrder.pickOrderCodes)
- ? mergedPickOrder.pickOrderCodes
- : [], // 改:保持数组
- deliveryOrderIds: mergedPickOrder.doOrderIds || [],
- deliveryNos: Array.isArray(mergedPickOrder.deliveryOrderCodes)
- ? mergedPickOrder.deliveryOrderCodes
- : [], // 改:保持数组
- lineCountsPerPickOrder: Array.isArray(mergedPickOrder.lineCountsPerPickOrder)
- ? mergedPickOrder.lineCountsPerPickOrder
- : []
- };
-
- setFgPickOrders([fgOrder]);
- console.log("🔍 DEBUG fgOrder.lineCountsPerPickOrder:", fgOrder.lineCountsPerPickOrder);
- console.log("🔍 DEBUG fgOrder.pickOrderCodes:", fgOrder.pickOrderCodes);
- console.log("🔍 DEBUG fgOrder.deliveryNos:", fgOrder.deliveryNos);
- // ❌ 移除:不需要 doPickOrderDetail 和 switcher 逻辑
- // if (hierarchicalData.pickOrders.length > 1) { ... }
-
- // 直接使用合并后的 pickOrderLines
- console.log("🎯 Displaying merged pick order lines");
-
- // 将层级数据转换为平铺格式(用于表格显示)
- const flatLotData: any[] = [];
-
- mergedPickOrder.pickOrderLines.forEach((line: any) => {
- if (line.lots && line.lots.length > 0) {
- // 修复:先对 lots 按 lotId 去重并合并 requiredQty
- const lotMap = new Map<number, any>();
-
- line.lots.forEach((lot: any) => {
- const lotId = lot.id;
- if (lotMap.has(lotId)) {
- // 如果已存在,合并 requiredQty
- const existingLot = lotMap.get(lotId);
- existingLot.requiredQty = (existingLot.requiredQty || 0) + (lot.requiredQty || 0);
- // 保留其他字段(使用第一个遇到的 lot 的字段)
- } else {
- // 首次遇到,添加到 map
- lotMap.set(lotId, { ...lot });
- }
- });
-
- // 遍历去重后的 lots
- lotMap.forEach((lot: any) => {
- flatLotData.push({
- // 使用合并后的数据
- pickOrderConsoCode: mergedPickOrder.consoCode,
- pickOrderTargetDate: mergedPickOrder.targetDate,
- pickOrderStatus: mergedPickOrder.status,
- pickOrderId: line.pickOrderId || mergedPickOrder.pickOrderIds?.[0] || 0, // 使用第一个 pickOrderId
- pickOrderCode: mergedPickOrder.pickOrderCodes?.[0] || "",
- pickOrderLineId: line.id,
- pickOrderLineRequiredQty: line.requiredQty,
- pickOrderLineStatus: line.status,
-
- itemId: line.item.id,
- itemCode: line.item.code,
- itemName: line.item.name,
- uomDesc: line.item.uomDesc,
- uomShortDesc: line.item.uomShortDesc,
-
- lotId: lot.id,
- lotNo: lot.lotNo,
- expiryDate: lot.expiryDate,
- location: lot.location,
- stockUnit: lot.stockUnit,
- availableQty: lot.availableQty,
- requiredQty: lot.requiredQty, // 使用合并后的 requiredQty
- actualPickQty: lot.actualPickQty,
- inQty: lot.inQty,
- outQty: lot.outQty,
- holdQty: lot.holdQty,
- lotStatus: lot.lotStatus,
- lotAvailability: lot.lotAvailability,
- processingStatus: lot.processingStatus,
- suggestedPickLotId: lot.suggestedPickLotId,
- stockOutLineId: lot.stockOutLineId,
- stockOutLineStatus: lot.stockOutLineStatus,
- stockOutLineQty: lot.stockOutLineQty,
-
- routerId: lot.router?.id,
- routerIndex: lot.router?.index,
- routerRoute: lot.router?.route,
- routerArea: lot.router?.area,
- noLot: false,
- });
- });
- } else {
- // 没有 lots 的情况(null stock)- 从 stockouts 数组中获取 id
- const firstStockout = line.stockouts && line.stockouts.length > 0
- ? line.stockouts[0]
- : null;
-
- flatLotData.push({
- pickOrderConsoCode: mergedPickOrder.consoCodes?.[0] || "", // 修复:consoCodes 是数组
- pickOrderTargetDate: mergedPickOrder.targetDate,
- pickOrderStatus: mergedPickOrder.status,
- pickOrderId: line.pickOrderId || mergedPickOrder.pickOrderIds?.[0] || 0, // 使用第一个 pickOrderId
- pickOrderCode: mergedPickOrder.pickOrderCodes?.[0] || "",
- pickOrderLineId: line.id,
- pickOrderLineRequiredQty: line.requiredQty,
- pickOrderLineStatus: line.status,
-
- itemId: line.item.id,
- itemCode: line.item.code,
- itemName: line.item.name,
- uomDesc: line.item.uomDesc,
- uomShortDesc: line.item.uomShortDesc,
-
- // Null stock 字段 - 从 stockouts 数组中获取
- lotId: firstStockout?.lotId || null,
- lotNo: firstStockout?.lotNo || null,
- expiryDate: null,
- location: firstStockout?.location || null,
- stockUnit: line.item.uomDesc,
- availableQty: firstStockout?.availableQty || 0,
- requiredQty: line.requiredQty,
- actualPickQty: firstStockout?.qty || 0,
- inQty: 0,
- outQty: 0,
- holdQty: 0,
- lotStatus: 'unavailable',
- lotAvailability: 'insufficient_stock',
- processingStatus: firstStockout?.status || 'pending',
- suggestedPickLotId: null,
- stockOutLineId: firstStockout?.id || null, // 使用 stockouts 数组中的 id
- stockOutLineStatus: firstStockout?.status || null,
- stockOutLineQty: firstStockout?.qty || 0,
-
- routerId: null,
- routerIndex: 999999,
- routerRoute: null,
- routerArea: null,
- noLot: true,
- });
- }
- });
-
- console.log(" Transformed flat lot data:", flatLotData);
- console.log("🔍 Total items (including null stock):", flatLotData.length);
-
- setCombinedLotData(flatLotData);
- setOriginalCombinedData(flatLotData);
- checkAllLotsCompleted(flatLotData);
-
- } catch (error) {
- console.error("❌ Error fetching combined lot data:", error);
- setCombinedLotData([]);
- setOriginalCombinedData([]);
- setAllLotsCompleted(false);
- } finally {
- setCombinedDataLoading(false);
- }
- }, [currentUserId, checkAllLotsCompleted]); // ❌ 移除 selectedPickOrderId 依赖
- // Add effect to check completion when lot data changes
- useEffect(() => {
- if (combinedLotData.length > 0) {
- checkAllLotsCompleted(combinedLotData);
- }
- }, [combinedLotData, checkAllLotsCompleted]);
-
- // Add function to expose completion status to parent
- const getCompletionStatus = useCallback(() => {
- return allLotsCompleted;
- }, [allLotsCompleted]);
-
- // Expose completion status to parent component
- useEffect(() => {
- // Dispatch custom event with completion status
- const event = new CustomEvent('pickOrderCompletionStatus', {
- detail: {
- allLotsCompleted,
- tabIndex: 1 // 明确指定这是来自标签页 1 的事件
- }
- });
- window.dispatchEvent(event);
- }, [allLotsCompleted]);
- const handleLotConfirmation = useCallback(async () => {
- if (!expectedLotData || !scannedLotData || !selectedLotForQr) return;
- setIsConfirmingLot(true);
- try {
- let newLotLineId = scannedLotData?.inventoryLotLineId;
- if (!newLotLineId && scannedLotData?.stockInLineId) {
- const ld = await fetchLotDetail(scannedLotData.stockInLineId);
- newLotLineId = ld.inventoryLotLineId;
- }
- if (!newLotLineId) {
- console.error("No inventory lot line id for scanned lot");
- return;
- }
-
- await confirmLotSubstitution({
- pickOrderLineId: selectedLotForQr.pickOrderLineId,
- stockOutLineId: selectedLotForQr.stockOutLineId,
- originalSuggestedPickLotId: selectedLotForQr.suggestedPickLotId,
- newInventoryLotLineId: newLotLineId
- });
-
- setQrScanError(false);
- setQrScanSuccess(false);
- setQrScanInput('');
- //setIsManualScanning(false);
- //stopScan();
- //resetScan();
- setProcessedQrCodes(new Set());
- setLastProcessedQr('');
- setQrModalOpen(false);
- setPickExecutionFormOpen(false);
- if(selectedLotForQr?.stockOutLineId){
- const stockOutLineUpdate = await updateStockOutLineStatus({
- id: selectedLotForQr.stockOutLineId,
- status: 'checked',
- qty: 0
- });
- }
- setLotConfirmationOpen(false);
- setExpectedLotData(null);
- setScannedLotData(null);
- setSelectedLotForQr(null);
- await fetchAllCombinedLotData();
- } catch (error) {
- console.error("Error confirming lot substitution:", error);
- } finally {
- setIsConfirmingLot(false);
- }
- }, [expectedLotData, scannedLotData, selectedLotForQr, fetchAllCombinedLotData]);
- const handleQrCodeSubmit = useCallback(async (lotNo: string) => {
- console.log(` Processing QR Code for lot: ${lotNo}`);
-
- // 检查 lotNo 是否为 null 或 undefined(包括字符串 "null")
- if (!lotNo || lotNo === 'null' || lotNo.trim() === '') {
- console.error("❌ Invalid lotNo: null, undefined, or empty");
- return;
- }
-
- // Use current data without refreshing to avoid infinite loop
- const currentLotData = combinedLotData;
- console.log(` Available lots:`, currentLotData.map(lot => lot.lotNo));
-
- // 修复:在比较前确保 lotNo 不为 null
- const lotNoLower = lotNo.toLowerCase();
- const matchingLots = currentLotData.filter(lot => {
- if (!lot.lotNo) return false; // 跳过 null lotNo
- return lot.lotNo === lotNo || lot.lotNo.toLowerCase() === lotNoLower;
- });
-
- if (matchingLots.length === 0) {
- console.error(`❌ Lot not found: ${lotNo}`);
- setQrScanError(true);
- setQrScanSuccess(false);
- const availableLotNos = currentLotData.map(lot => lot.lotNo).join(', ');
- console.log(`❌ QR Code "${lotNo}" does not match any expected lots. Available lots: ${availableLotNos}`);
- return;
- }
-
- console.log(` Found ${matchingLots.length} matching lots:`, matchingLots);
- setQrScanError(false);
-
- try {
- let successCount = 0;
- let errorCount = 0;
-
- for (const matchingLot of matchingLots) {
- console.log(`🔄 Processing pick order line ${matchingLot.pickOrderLineId} for lot ${lotNo}`);
-
- if (matchingLot.stockOutLineId) {
- const stockOutLineUpdate = await updateStockOutLineStatus({
- id: matchingLot.stockOutLineId,
- status: 'checked',
- qty: 0
- });
- console.log(`Update stock out line result for line ${matchingLot.pickOrderLineId}:`, stockOutLineUpdate);
-
- // Treat multiple backend shapes as success (type-safe via any)
- const r: any = stockOutLineUpdate as any;
- const updateOk =
- r?.code === 'SUCCESS' ||
- typeof r?.id === 'number' ||
- r?.type === 'checked' ||
- r?.status === 'checked' ||
- typeof r?.entity?.id === 'number' ||
- r?.entity?.status === 'checked';
-
- if (updateOk) {
- successCount++;
- } else {
- errorCount++;
- }
- } else {
- const createStockOutLineData = {
- consoCode: matchingLot.pickOrderConsoCode,
- pickOrderLineId: matchingLot.pickOrderLineId,
- inventoryLotLineId: matchingLot.lotId,
- qty: 0
- };
-
- const createResult = await createStockOutLine(createStockOutLineData);
- console.log(`Create stock out line result for line ${matchingLot.pickOrderLineId}:`, createResult);
-
- if (createResult && createResult.code === "SUCCESS") {
- // Immediately set status to checked for new line
- let newSolId: number | undefined;
- const anyRes: any = createResult as any;
- if (typeof anyRes?.id === 'number') {
- newSolId = anyRes.id;
- } else if (anyRes?.entity) {
- newSolId = Array.isArray(anyRes.entity) ? anyRes.entity[0]?.id : anyRes.entity?.id;
- }
-
- if (newSolId) {
- const setChecked = await updateStockOutLineStatus({
- id: newSolId,
- status: 'checked',
- qty: 0
- });
- if (setChecked && setChecked.code === "SUCCESS") {
- successCount++;
- } else {
- errorCount++;
- }
- } else {
- console.warn("Created stock out line but no ID returned; cannot set to checked");
- errorCount++;
- }
- } else {
- errorCount++;
- }
- }
- }
-
- // FIXED: Set refresh flag before refreshing data
- setIsRefreshingData(true);
- console.log("🔄 Refreshing data after QR code processing...");
- await fetchAllCombinedLotData();
-
- if (successCount > 0) {
- console.log(` QR Code processing completed: ${successCount} updated/created`);
- setQrScanSuccess(true);
- setQrScanError(false);
- setQrScanInput(''); // Clear input after successful processing
- //setIsManualScanning(false);
- // stopScan();
- // resetScan();
- // Clear success state after a delay
-
- //setTimeout(() => {
- //setQrScanSuccess(false);
- //}, 2000);
- } else {
- console.error(`❌ QR Code processing failed: ${errorCount} errors`);
- setQrScanError(true);
- setQrScanSuccess(false);
-
- // Clear error state after a delay
- // setTimeout(() => {
- // setQrScanError(false);
- //}, 3000);
- }
- } catch (error) {
- console.error("❌ Error processing QR code:", error);
- setQrScanError(true);
- 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]);
- const processOutsideQrCode = useCallback(async (latestQr: string) => {
- // 1) Parse JSON safely
- let qrData: any = null;
- try {
- qrData = JSON.parse(latestQr);
- } catch {
- console.log("QR content is not JSON; skipping lotNo direct submit to avoid false matches.");
- setQrScanError(true);
- setQrScanSuccess(false);
- return;
- }
-
- try {
- // Only use the new API when we have JSON with stockInLineId + itemId
- if (!(qrData?.stockInLineId && qrData?.itemId)) {
- console.log("QR JSON missing required fields (itemId, stockInLineId).");
- setQrScanError(true);
- setQrScanSuccess(false);
- return;
- }
-
- // Call new analyze-qr-code API
- const analysis = await analyzeQrCode({
- itemId: qrData.itemId,
- stockInLineId: qrData.stockInLineId
- });
-
- if (!analysis) {
- console.error("analyzeQrCode returned no data");
- setQrScanError(true);
- setQrScanSuccess(false);
- return;
- }
-
- const {
- itemId: analyzedItemId,
- itemCode: analyzedItemCode,
- itemName: analyzedItemName,
- scanned,
- } = analysis || {};
-
- // 1) Find all lots for the same item from current expected list
- const sameItemLotsInExpected = combinedLotData.filter(l =>
- (l.itemId && analyzedItemId && l.itemId === analyzedItemId) ||
- (l.itemCode && analyzedItemCode && l.itemCode === analyzedItemCode)
- );
-
- if (!sameItemLotsInExpected || sameItemLotsInExpected.length === 0) {
- // Case 3: No item code match
- console.error("No item match in expected lots for scanned code");
- setQrScanError(true);
- setQrScanSuccess(false);
- return;
- }
-
- // FIXED: Find the ACTIVE suggested lot (not rejected lots)
- const activeSuggestedLots = sameItemLotsInExpected.filter(lot =>
- lot.lotAvailability !== 'rejected' &&
- lot.stockOutLineStatus !== 'rejected' &&
- lot.processingStatus !== 'rejected'
- );
-
- if (activeSuggestedLots.length === 0) {
- console.error("No active suggested lots found for this item");
- setQrScanError(true);
- setQrScanSuccess(false);
- return;
- }
-
- // 2) Check if scanned lot is exactly in active suggested lots
- const exactLotMatch = activeSuggestedLots.find(l =>
- (scanned?.inventoryLotLineId && l.lotId === scanned.inventoryLotLineId) ||
- (scanned?.lotNo && l.lotNo === scanned.lotNo)
- );
-
- if (exactLotMatch && scanned?.lotNo) {
- // Case 1: Normal case - item matches AND lot matches -> proceed
- console.log(`Exact lot match found for ${scanned.lotNo}, submitting QR`);
- handleQrCodeSubmit(scanned.lotNo);
- return;
- }
-
- // Case 2: Item matches but lot number differs -> open confirmation modal
- // FIXED: Use the first ACTIVE suggested lot, not just any lot
- const expectedLot = activeSuggestedLots[0];
- if (!expectedLot) {
- console.error("Could not determine expected lot for confirmation");
- setQrScanError(true);
- setQrScanSuccess(false);
- return;
- }
-
- // Check if the expected lot is already the scanned lot (after substitution)
- if (expectedLot.lotNo === scanned?.lotNo) {
- console.log(`Lot already substituted, proceeding with ${scanned.lotNo}`);
- handleQrCodeSubmit(scanned.lotNo);
- return;
- }
-
- console.log(`🔍 Lot mismatch: Expected ${expectedLot.lotNo}, Scanned ${scanned?.lotNo}`);
- setSelectedLotForQr(expectedLot);
- handleLotMismatch(
- {
- lotNo: expectedLot.lotNo,
- itemCode: analyzedItemCode || expectedLot.itemCode,
- itemName: analyzedItemName || expectedLot.itemName
- },
- {
- lotNo: scanned?.lotNo || '',
- itemCode: analyzedItemCode || expectedLot.itemCode,
- itemName: analyzedItemName || expectedLot.itemName,
- inventoryLotLineId: scanned?.inventoryLotLineId,
- stockInLineId: qrData.stockInLineId
- }
- );
- } catch (error) {
- console.error("Error during analyzeQrCode flow:", error);
- setQrScanError(true);
- setQrScanSuccess(false);
- return;
- }
- }, [combinedLotData, handleQrCodeSubmit, handleLotMismatch]);
- // Update the outside QR scanning effect to use enhanced processing
- // Update the outside QR scanning effect to use enhanced processing
- useEffect(() => {
- if (!isManualScanning || qrValues.length === 0 || combinedLotData.length === 0 || isRefreshingData) {
- return;
- }
-
- const latestQr = qrValues[qrValues.length - 1];
-
- if (processedQrCodes.has(latestQr) || lastProcessedQr === latestQr) {
- console.log("QR code already processed, skipping...");
- return;
- }
-
- if (latestQr && latestQr !== lastProcessedQr) {
- console.log(`🔍 Processing new QR code with enhanced validation: ${latestQr}`);
- setLastProcessedQr(latestQr);
- setProcessedQrCodes(prev => new Set(prev).add(latestQr));
-
- processOutsideQrCode(latestQr);
- }
- }, [qrValues, isManualScanning, processedQrCodes, lastProcessedQr, isRefreshingData, processOutsideQrCode, combinedLotData]);
- // Only fetch existing data when session is ready, no auto-assignment
- useEffect(() => {
- if (session && currentUserId && !initializationRef.current) {
- console.log(" Session loaded, initializing pick order...");
- initializationRef.current = true;
-
- // Only fetch existing data, no auto-assignment
- fetchAllCombinedLotData();
- }
- }, [session, currentUserId, fetchAllCombinedLotData]);
-
- // Add event listener for manual assignment
- useEffect(() => {
- const handlePickOrderAssigned = () => {
- console.log("🔄 Pick order assigned event received, refreshing data...");
- fetchAllCombinedLotData();
- };
-
- window.addEventListener('pickOrderAssigned', handlePickOrderAssigned);
-
- return () => {
- window.removeEventListener('pickOrderAssigned', handlePickOrderAssigned);
- };
- }, [fetchAllCombinedLotData]);
-
-
-
- const handleManualInputSubmit = useCallback(() => {
- if (qrScanInput.trim() !== '') {
- handleQrCodeSubmit(qrScanInput.trim());
- }
- }, [qrScanInput, handleQrCodeSubmit]);
-
- // Handle QR code submission from modal (internal scanning)
- const handleQrCodeSubmitFromModal = useCallback(async (lotNo: string) => {
- if (selectedLotForQr && selectedLotForQr.lotNo === lotNo) {
- console.log(` QR Code verified for lot: ${lotNo}`);
-
- const requiredQty = selectedLotForQr.requiredQty;
- const lotId = selectedLotForQr.lotId;
-
- // Create stock out line
-
-
- try {
- const stockOutLineUpdate = await updateStockOutLineStatus({
- id: selectedLotForQr.stockOutLineId,
- status: 'checked',
- qty: selectedLotForQr.stockOutLineQty || 0
- });
- console.log("Stock out line updated successfully!");
- setQrScanSuccess(true);
- setQrScanError(false);
-
- // Close modal
- setQrModalOpen(false);
- setSelectedLotForQr(null);
-
- // Set pick quantity
- const lotKey = `${selectedLotForQr.pickOrderLineId}-${lotId}`;
- setTimeout(() => {
- setPickQtyData(prev => ({
- ...prev,
- [lotKey]: requiredQty
- }));
- console.log(` Auto-set pick quantity to ${requiredQty} for lot ${lotNo}`);
- }, 500);
-
- // Refresh data
- await fetchAllCombinedLotData();
- } catch (error) {
- console.error("Error creating stock out line:", error);
- }
- }
- }, [selectedLotForQr, fetchAllCombinedLotData]);
-
-
- const handlePickQtyChange = useCallback((lotKey: string, value: number | string) => {
- if (value === '' || value === null || value === undefined) {
- setPickQtyData(prev => ({
- ...prev,
- [lotKey]: 0
- }));
- return;
- }
-
- const numericValue = typeof value === 'string' ? parseFloat(value) : value;
-
- if (isNaN(numericValue)) {
- setPickQtyData(prev => ({
- ...prev,
- [lotKey]: 0
- }));
- return;
- }
-
- setPickQtyData(prev => ({
- ...prev,
- [lotKey]: numericValue
- }));
- }, []);
-
- const [autoAssignStatus, setAutoAssignStatus] = useState<'idle' | 'checking' | 'assigned' | 'no_orders'>('idle');
- const [autoAssignMessage, setAutoAssignMessage] = useState<string>('');
- const [completionStatus, setCompletionStatus] = useState<PickOrderCompletionResponse | null>(null);
-
- const checkAndAutoAssignNext = useCallback(async () => {
- if (!currentUserId) return;
-
- try {
- const completionResponse = await checkPickOrderCompletion(currentUserId);
-
- if (completionResponse.code === "SUCCESS" && completionResponse.entity?.hasCompletedOrders) {
- console.log("Found completed pick orders, auto-assigning next...");
- // 移除前端的自动分配逻辑,因为后端已经处理了
- // await handleAutoAssignAndRelease(); // 删除这个函数
- }
- } catch (error) {
- console.error("Error checking pick order completion:", error);
- }
- }, [currentUserId]);
-
- // Handle submit pick quantity
- const handleSubmitPickQty = useCallback(async (lot: any) => {
- const lotKey = `${lot.pickOrderLineId}-${lot.lotId}`;
- const newQty = pickQtyData[lotKey] || 0;
-
- if (!lot.stockOutLineId) {
- console.error("No stock out line found for this lot");
- return;
- }
-
- try {
- // FIXED: Calculate cumulative quantity correctly
- const currentActualPickQty = lot.actualPickQty || 0;
- const cumulativeQty = currentActualPickQty + newQty;
-
- // FIXED: Determine status based on cumulative quantity vs required quantity
- let newStatus = 'partially_completed';
-
- if (cumulativeQty >= lot.requiredQty) {
- newStatus = 'completed';
- } else if (cumulativeQty > 0) {
- newStatus = 'partially_completed';
- } else {
- newStatus = 'checked'; // QR scanned but no quantity submitted yet
- }
-
- console.log(`=== PICK QUANTITY SUBMISSION DEBUG ===`);
- console.log(`Lot: ${lot.lotNo}`);
- console.log(`Required Qty: ${lot.requiredQty}`);
- console.log(`Current Actual Pick Qty: ${currentActualPickQty}`);
- console.log(`New Submitted Qty: ${newQty}`);
- console.log(`Cumulative Qty: ${cumulativeQty}`);
- console.log(`New Status: ${newStatus}`);
- console.log(`=====================================`);
-
- await updateStockOutLineStatus({
- id: lot.stockOutLineId,
- status: newStatus,
- qty: cumulativeQty // Use cumulative quantity
- });
-
- if (newQty > 0) {
- await updateInventoryLotLineQuantities({
- inventoryLotLineId: lot.lotId,
- qty: newQty,
- status: 'available',
- operation: 'pick'
- });
- }
-
- // Check if pick order is completed when lot status becomes 'completed'
- if (newStatus === 'completed' && lot.pickOrderConsoCode) {
- console.log(` Lot ${lot.lotNo} completed, checking if pick order ${lot.pickOrderConsoCode} is complete...`);
-
- try {
- const completionResponse = await checkAndCompletePickOrderByConsoCode(lot.pickOrderConsoCode);
- console.log(` Pick order completion check result:`, completionResponse);
-
- if (completionResponse.code === "SUCCESS") {
- console.log(`�� Pick order ${lot.pickOrderConsoCode} completed successfully!`);
- } else if (completionResponse.message === "not completed") {
- console.log(`⏳ Pick order not completed yet, more lines remaining`);
- } else {
- console.error(`❌ Error checking completion: ${completionResponse.message}`);
- }
- } catch (error) {
- console.error("Error checking pick order completion:", error);
- }
- }
-
- await fetchAllCombinedLotData();
- console.log("Pick quantity submitted successfully!");
-
- setTimeout(() => {
- checkAndAutoAssignNext();
- }, 1000);
-
- } catch (error) {
- console.error("Error submitting pick quantity:", error);
- }
- }, [pickQtyData, fetchAllCombinedLotData, checkAndAutoAssignNext]);
-
- // Handle reject lot
- const handleRejectLot = useCallback(async (lot: any) => {
- if (!lot.stockOutLineId) {
- console.error("No stock out line found for this lot");
- return;
- }
-
- try {
- await updateStockOutLineStatus({
- id: lot.stockOutLineId,
- status: 'rejected',
- qty: 0
- });
-
- await fetchAllCombinedLotData();
- console.log("Lot rejected successfully!");
-
- setTimeout(() => {
- checkAndAutoAssignNext();
- }, 1000);
-
- } catch (error) {
- console.error("Error rejecting lot:", error);
- }
- }, [fetchAllCombinedLotData, checkAndAutoAssignNext]);
-
- // Handle pick execution form
- const handlePickExecutionForm = useCallback((lot: any) => {
- 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);
- const issueData = {
- ...data,
- type: "Do", // Delivery Order Record 类型
- pickerName: session?.user?.name || '',
- };
-
- const result = await recordPickExecutionIssue(issueData);
- 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);
- setQrScanError(false);
- setQrScanSuccess(false);
- setQrScanInput('');
- setIsManualScanning(false);
- stopScan();
- resetScan();
- setProcessedQrCodes(new Set());
- setLastProcessedQr('');
- await fetchAllCombinedLotData();
- } catch (error) {
- console.error("Error submitting pick execution form:", error);
- }
- }, [fetchAllCombinedLotData]);
-
- // Calculate remaining required quantity
- const calculateRemainingRequiredQty = useCallback((lot: any) => {
- const requiredQty = lot.requiredQty || 0;
- const stockOutLineQty = lot.stockOutLineQty || 0;
- return Math.max(0, requiredQty - stockOutLineQty);
- }, []);
-
- // Search criteria
- const searchCriteria: Criterion<any>[] = [
- {
- label: t("Pick Order Code"),
- paramName: "pickOrderCode",
- type: "text",
- },
- {
- label: t("Item Code"),
- paramName: "itemCode",
- type: "text",
- },
- {
- label: t("Item Name"),
- paramName: "itemName",
- type: "text",
- },
- {
- label: t("Lot No"),
- paramName: "lotNo",
- type: "text",
- },
- ];
-
- const handleSearch = useCallback((query: Record<string, any>) => {
- setSearchQuery({ ...query });
- console.log("Search query:", query);
-
- if (!originalCombinedData) return;
-
- const filtered = originalCombinedData.filter((lot: any) => {
- const pickOrderCodeMatch = !query.pickOrderCode ||
- lot.pickOrderCode?.toLowerCase().includes((query.pickOrderCode || "").toLowerCase());
-
- const itemCodeMatch = !query.itemCode ||
- lot.itemCode?.toLowerCase().includes((query.itemCode || "").toLowerCase());
-
- const itemNameMatch = !query.itemName ||
- lot.itemName?.toLowerCase().includes((query.itemName || "").toLowerCase());
-
- const lotNoMatch = !query.lotNo ||
- lot.lotNo?.toLowerCase().includes((query.lotNo || "").toLowerCase());
-
- return pickOrderCodeMatch && itemCodeMatch && itemNameMatch && lotNoMatch;
- });
-
- setCombinedLotData(filtered);
- console.log("Filtered lots count:", filtered.length);
- }, [originalCombinedData]);
-
- const handleReset = useCallback(() => {
- setSearchQuery({});
- if (originalCombinedData) {
- setCombinedLotData(originalCombinedData);
- }
- }, [originalCombinedData]);
-
- const handlePageChange = useCallback((event: unknown, newPage: number) => {
- setPaginationController(prev => ({
- ...prev,
- pageNum: newPage,
- }));
- }, []);
-
- const handlePageSizeChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
- const newPageSize = parseInt(event.target.value, 10);
- setPaginationController({
- pageNum: 0,
- pageSize: newPageSize,
- });
- }, []);
-
- // Pagination data with sorting by routerIndex
- // Remove the sorting logic and just do pagination
- const paginatedData = useMemo(() => {
- const startIndex = paginationController.pageNum * paginationController.pageSize;
- const endIndex = startIndex + paginationController.pageSize;
- return combinedLotData.slice(startIndex, endIndex); // No sorting needed
- }, [combinedLotData, paginationController]);
- const allItemsReady = useMemo(() => {
- if (combinedLotData.length === 0) return false;
-
- return combinedLotData.every((lot: any) => {
- const status = lot.stockOutLineStatus?.toLowerCase();
- const isRejected =
- status === 'rejected' || lot.lotAvailability === 'rejected';
- const isCompleted =
- status === 'completed' || status === 'partially_completed' || status === 'partially_complete';
- const isChecked = status === 'checked';
-
- // 无库存(noLot)行:只要状态不是 pending/rejected 即视为已处理
- if (lot.noLot === true) {
- return isChecked || isCompleted || isRejected;
- }
-
- // 正常 lot:必须已扫描/提交或者被拒收
- return isChecked || isCompleted || isRejected;
- });
- }, [combinedLotData]);
- const handleSubmitPickQtyWithQty = useCallback(async (lot: any, submitQty: number) => {
- if (!lot.stockOutLineId) {
- console.error("No stock out line found for this lot");
- return;
- }
-
- try {
- // FIXED: Calculate cumulative quantity correctly
- const currentActualPickQty = lot.actualPickQty || 0;
- const cumulativeQty = currentActualPickQty + submitQty;
-
- // FIXED: Determine status based on cumulative quantity vs required quantity
- let newStatus = 'partially_completed';
-
- if (cumulativeQty >= lot.requiredQty) {
- newStatus = 'completed';
- } else if (cumulativeQty > 0) {
- newStatus = 'partially_completed';
- } else {
- newStatus = 'checked'; // QR scanned but no quantity submitted yet
- }
-
- console.log(`=== PICK QUANTITY SUBMISSION DEBUG ===`);
- console.log(`Lot: ${lot.lotNo}`);
- console.log(`Required Qty: ${lot.requiredQty}`);
- console.log(`Current Actual Pick Qty: ${currentActualPickQty}`);
- console.log(`New Submitted Qty: ${submitQty}`);
- console.log(`Cumulative Qty: ${cumulativeQty}`);
- console.log(`New Status: ${newStatus}`);
- console.log(`=====================================`);
-
- await updateStockOutLineStatus({
- id: lot.stockOutLineId,
- status: newStatus,
- qty: cumulativeQty // Use cumulative quantity
- });
-
- if (submitQty > 0) {
- await updateInventoryLotLineQuantities({
- inventoryLotLineId: lot.lotId,
- qty: submitQty,
- status: 'available',
- operation: 'pick'
- });
- }
-
- // Check if pick order is completed when lot status becomes 'completed'
- if (newStatus === 'completed' && lot.pickOrderConsoCode) {
- console.log(` Lot ${lot.lotNo} completed, checking if pick order ${lot.pickOrderConsoCode} is complete...`);
-
- try {
- const completionResponse = await checkAndCompletePickOrderByConsoCode(lot.pickOrderConsoCode);
- console.log(` Pick order completion check result:`, completionResponse);
-
- if (completionResponse.code === "SUCCESS") {
- console.log(`�� Pick order ${lot.pickOrderConsoCode} completed successfully!`);
- } else if (completionResponse.message === "not completed") {
- console.log(`⏳ Pick order not completed yet, more lines remaining`);
- } else {
- console.error(`❌ Error checking completion: ${completionResponse.message}`);
- }
- } catch (error) {
- console.error("Error checking pick order completion:", error);
- }
- }
-
- await fetchAllCombinedLotData();
- console.log("Pick quantity submitted successfully!");
-
- setTimeout(() => {
- checkAndAutoAssignNext();
- }, 1000);
-
- } catch (error) {
- console.error("Error submitting pick quantity:", error);
- }
- }, [fetchAllCombinedLotData, checkAndAutoAssignNext]);
-
-
- // Add these functions after line 395
- const handleStartScan = useCallback(() => {
- console.log(" Starting manual QR scan...");
- setIsManualScanning(true);
- setProcessedQrCodes(new Set());
- setLastProcessedQr('');
- setQrScanError(false);
- setQrScanSuccess(false);
- startScan();
- }, [startScan]);
- const handlePickOrderSwitch = useCallback(async (pickOrderId: number) => {
- if (pickOrderSwitching) return;
-
- setPickOrderSwitching(true);
- try {
- console.log("🔍 Switching to pick order:", pickOrderId);
- setSelectedPickOrderId(pickOrderId);
-
- // 强制刷新数据,确保显示正确的 pick order 数据
- await fetchAllCombinedLotData(currentUserId, pickOrderId);
- } catch (error) {
- console.error("Error switching pick order:", error);
- } finally {
- setPickOrderSwitching(false);
- }
- }, [pickOrderSwitching, currentUserId, fetchAllCombinedLotData]);
-
- const handleStopScan = useCallback(() => {
- console.log("⏹️ Stopping manual QR scan...");
- setIsManualScanning(false);
- setQrScanError(false);
- setQrScanSuccess(false);
- stopScan();
- resetScan();
- }, [stopScan, resetScan]);
- // ... existing code around line 1469 ...
- const handlelotnull = useCallback(async (lot: any) => {
- // 优先使用 stockouts 中的 id,如果没有则使用 stockOutLineId
- const stockOutLineId = lot.stockOutLineId;
-
- if (!stockOutLineId) {
- console.error("❌ No stockOutLineId found for lot:", lot);
- return;
- }
-
- try {
- // Step 1: Update stock out line status
- await updateStockOutLineStatus({
- id: stockOutLineId,
- status: 'completed',
- qty: 0
- });
-
- // Step 2: Create pick execution issue for no-lot case
- // Get pick order ID from fgPickOrders or use 0 if not available
- const pickOrderId = lot.pickOrderId || fgPickOrders[0]?.pickOrderId || 0;
- const pickOrderCode = lot.pickOrderCode || fgPickOrders[0]?.pickOrderCode || lot.pickOrderConsoCode || '';
-
- const issueData: PickExecutionIssueData = {
- type: "Do", // Delivery Order type
- pickOrderId: pickOrderId,
- pickOrderCode: pickOrderCode,
- pickOrderCreateDate: dayjs().format('YYYY-MM-DD'), // Use dayjs format
- pickExecutionDate: dayjs().format('YYYY-MM-DD'),
- pickOrderLineId: lot.pickOrderLineId,
- itemId: lot.itemId,
- itemCode: lot.itemCode || '',
- itemDescription: lot.itemName || '',
- lotId: null, // No lot available
- lotNo: null, // No lot number
- storeLocation: lot.location || '',
- requiredQty: lot.requiredQty || lot.pickOrderLineRequiredQty || 0,
- actualPickQty: 0, // No items picked (no lot available)
- missQty: lot.requiredQty || lot.pickOrderLineRequiredQty || 0, // All quantity is missing
- badItemQty: 0,
- issueRemark: `No lot available for this item. Handled via handlelotnull.`,
- pickerName: session?.user?.name || '',
-
- };
-
- const result = await recordPickExecutionIssue(issueData);
- console.log(" Pick execution issue created for no-lot item:", result);
-
- if (result && result.code === "SUCCESS") {
- console.log(" No-lot item handled and issue recorded successfully");
- } else {
- console.error("❌ Failed to record pick execution issue:", result);
- }
-
- // Step 3: Refresh data
- await fetchAllCombinedLotData();
- } catch (error) {
- console.error("❌ Error in handlelotnull:", error);
- }
- }, [fetchAllCombinedLotData, session, currentUserId, fgPickOrders]);
- // ... existing code ...
- const handleSubmitAllScanned = useCallback(async () => {
- const scannedLots = combinedLotData.filter(lot => {
- // 如果是 noLot 情况,检查状态是否为 pending 或 partially_complete
- if (lot.noLot === true) {
- return lot.stockOutLineStatus === 'checked' ||
- lot.stockOutLineStatus === 'pending' ||
- lot.stockOutLineStatus === 'partially_completed' ||
- lot.stockOutLineStatus === 'PARTIALLY_COMPLETE';
- }
- // 正常情况:只包含 checked 状态
- return lot.stockOutLineStatus === 'checked';
- });
-
- if (scannedLots.length === 0) {
- console.log("No scanned items to submit");
- return;
- }
-
- setIsSubmittingAll(true);
- console.log(`📦 Submitting ${scannedLots.length} scanned items in parallel...`);
-
- try {
- // Submit all items in parallel using Promise.all
- const submitPromises = scannedLots.map(async (lot) => {
- // 检查是否是 noLot 情况
- if (lot.noLot === true) {
- // 使用 handlelotnull 处理无 lot 的情况
- console.log(`Submitting no-lot item: ${lot.itemName || lot.itemCode}`);
- await updateStockOutLineStatus({
- id: lot.stockOutLineId,
- status: 'completed',
- qty: 0
- });
- console.log(` No-lot item completed: ${lot.itemName || lot.itemCode}`);
- const pickOrderId = lot.pickOrderId || fgPickOrders[0]?.pickOrderId || 0;
- const pickOrderCode = lot.pickOrderCode || fgPickOrders[0]?.pickOrderCode || lot.pickOrderConsoCode || '';
- const issueData: PickExecutionIssueData = {
- type: "Do", // Delivery Order type
- pickOrderId: pickOrderId,
- pickOrderCode: pickOrderCode,
- pickOrderCreateDate: dayjs().format('YYYY-MM-DD'), // Use dayjs format
- pickExecutionDate: dayjs().format('YYYY-MM-DD'),
- pickOrderLineId: lot.pickOrderLineId,
- itemId: lot.itemId,
- itemCode: lot.itemCode || '',
- itemDescription: lot.itemName || '',
- lotId: null, // No lot available
- lotNo: null, // No lot number
- storeLocation: lot.location || '',
- requiredQty: lot.requiredQty || lot.pickOrderLineRequiredQty || 0,
- actualPickQty: 0, // No items picked (no lot available)
- missQty: lot.requiredQty || lot.pickOrderLineRequiredQty || 0,
- badItemQty: 0,
- issueRemark: `No lot available for this item. Handled via handlelotnull.`,
- pickerName: session?.user?.name || '',
-
- };
- const result = await recordPickExecutionIssue(issueData);
- return { success: true, lotNo: lot.lotNo || 'No Lot', isNoLot: true };
- }
-
- // 正常情况:有 lot 的处理逻辑
- const submitQty = lot.requiredQty || lot.pickOrderLineRequiredQty;
- const currentActualPickQty = lot.actualPickQty || 0;
- const cumulativeQty = currentActualPickQty + submitQty;
-
- let newStatus = 'partially_completed';
- if (cumulativeQty >= lot.requiredQty) {
- newStatus = 'completed';
- }
-
- console.log(`Submitting lot ${lot.lotNo}: qty=${cumulativeQty}, status=${newStatus}`);
-
- // Update stock out line
- await updateStockOutLineStatus({
- id: lot.stockOutLineId,
- status: newStatus,
- qty: cumulativeQty
- });
-
- // Update inventory
- if (submitQty > 0 && lot.lotId) {
- await updateInventoryLotLineQuantities({
- inventoryLotLineId: lot.lotId,
- qty: submitQty,
- status: 'available',
- operation: 'pick'
- });
- }
-
- // Check if pick order is completed
- if (newStatus === 'completed' && lot.pickOrderConsoCode) {
- await checkAndCompletePickOrderByConsoCode(lot.pickOrderConsoCode);
- }
-
- return { success: true, lotNo: lot.lotNo };
- });
-
- // Wait for all submissions to complete
- const results = await Promise.all(submitPromises);
- const successCount = results.filter(r => r.success).length;
-
- console.log(` Batch submit completed: ${successCount}/${scannedLots.length} items submitted`);
-
- // Refresh data once after all submissions
- await fetchAllCombinedLotData();
-
- if (successCount > 0) {
- setQrScanSuccess(true);
- setTimeout(() => {
- setQrScanSuccess(false);
- checkAndAutoAssignNext();
- }, 2000);
- }
-
- } catch (error) {
- console.error("Error submitting all scanned items:", error);
- setQrScanError(true);
- } finally {
- setIsSubmittingAll(false);
- }
- }, [combinedLotData, fetchAllCombinedLotData, checkAndAutoAssignNext, handlelotnull]);
-
- // Calculate scanned items count
- // Calculate scanned items count (should match handleSubmitAllScanned filter logic)
- const scannedItemsCount = useMemo(() => {
- const filtered = combinedLotData.filter(lot => {
- // 如果是 noLot 情况,只要状态不是 completed 或 rejected,就包含
- if (lot.noLot === true) {
- const status = lot.stockOutLineStatus?.toLowerCase();
- const include = status !== 'completed' && status !== 'rejected';
- if (include) {
- console.log(`📊 Including noLot item: ${lot.itemName || lot.itemCode}, status: ${lot.stockOutLineStatus}`);
- }
- return include;
- }
- // 正常情况:只包含 checked 状态
- return lot.stockOutLineStatus === 'checked';
- });
-
- // 添加调试日志
- const noLotCount = filtered.filter(l => l.noLot === true).length;
- const normalCount = filtered.filter(l => l.noLot !== true).length;
- console.log(`📊 scannedItemsCount calculation: total=${filtered.length}, noLot=${noLotCount}, normal=${normalCount}`);
- console.log(`📊 All items breakdown:`, {
- total: combinedLotData.length,
- noLot: combinedLotData.filter(l => l.noLot === true).length,
- normal: combinedLotData.filter(l => l.noLot !== true).length
- });
-
- return filtered.length;
- }, [combinedLotData]);
-
- // ADD THIS: Auto-stop scan when no data available
- useEffect(() => {
- if (isManualScanning && combinedLotData.length === 0) {
- console.log("⏹️ No data available, auto-stopping QR scan...");
- handleStopScan();
- }
- }, [combinedLotData.length, isManualScanning, handleStopScan]);
-
- // Cleanup effect
- useEffect(() => {
- return () => {
- // Cleanup when component unmounts (e.g., when switching tabs)
- if (isManualScanning) {
- console.log("🧹 Pick execution component unmounting, stopping QR scanner...");
- stopScan();
- resetScan();
- }
- };
- }, [isManualScanning, 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 (
- <TestQrCodeProvider
- lotData={combinedLotData}
- onScanLot={handleQrCodeSubmit}
- filterActive={(lot) => (
- lot.lotAvailability !== 'rejected' &&
- lot.stockOutLineStatus !== 'rejected' &&
- lot.stockOutLineStatus !== 'completed'
- )}
- >
- <FormProvider {...formProps}>
- <Stack spacing={2}>
- {/* DO Header */}
-
-
-
-
- {/* 保留:Combined Lot Table - 包含所有 QR 扫描功能 */}
- <Box>
- <Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>
- <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>
- )}
-
- {/* 保留:Submit All Scanned Button */}
- <Button
- variant="contained"
- color="success"
- onClick={handleSubmitAllScanned}
- disabled={
- // scannedItemsCount === 0
- !allItemsReady
- || isSubmittingAll}
- sx={{ minWidth: '160px' }}
- >
- {isSubmittingAll ? (
- <>
- <CircularProgress size={16} sx={{ mr: 1, color: 'white' }} />
- {t("Submitting...")}
- </>
- ) : (
- `${t("Submit All Scanned")} (${scannedItemsCount})`
- )}
- </Button>
- </Box>
- </Box>
-
-
- {fgPickOrders.length > 0 && (
- <Paper sx={{ p: 2, mb: 2 }}>
- <Stack spacing={2}>
- {/* 基本信息 */}
- <Stack direction="row" spacing={4} useFlexGap flexWrap="wrap">
- <Typography variant="subtitle1">
- <strong>{t("Shop Name")}:</strong> {fgPickOrders[0].shopName || '-'}
- </Typography>
- <Typography variant="subtitle1">
- <strong>{t("Store ID")}:</strong> {fgPickOrders[0].storeId || '-'}
- </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>
-
- {/* 改进:三个字段显示在一起,使用表格式布局 */}
- {/* 改进:三个字段合并显示 */}
- {/* 改进:表格式显示每个 pick order */}
- <Box sx={{
- p: 2,
- backgroundColor: '#f5f5f5',
- borderRadius: 1
- }}>
- <Typography variant="subtitle2" sx={{ mb: 1, fontWeight: 'bold' }}>
- {t("Pick Orders Details")}:
- </Typography>
-
- {(() => {
- const pickOrderCodes = fgPickOrders[0].pickOrderCodes as string[] | string | undefined;
- const deliveryNos = fgPickOrders[0].deliveryNos as string[] | string | undefined;
- const lineCounts = fgPickOrders[0].lineCountsPerPickOrder;
-
- const pickOrderCodesArray = Array.isArray(pickOrderCodes)
- ? pickOrderCodes
- : (typeof pickOrderCodes === 'string' ? pickOrderCodes.split(', ') : []);
-
- const deliveryNosArray = Array.isArray(deliveryNos)
- ? deliveryNos
- : (typeof deliveryNos === 'string' ? deliveryNos.split(', ') : []);
-
- const lineCountsArray = Array.isArray(lineCounts) ? lineCounts : [];
-
- const maxLength = Math.max(
- pickOrderCodesArray.length,
- deliveryNosArray.length,
- lineCountsArray.length
- );
-
- if (maxLength === 0) {
- return <Typography variant="body2" color="text.secondary">-</Typography>;
- }
-
- // 使用与外部基本信息相同的样式
- return Array.from({ length: maxLength }, (_, idx) => (
- <Stack
- key={idx}
- direction="row"
- spacing={4}
- useFlexGap
- flexWrap="wrap"
- sx={{ mb: idx < maxLength - 1 ? 1 : 0 }} // 除了最后一行,都添加底部间距
- >
- <Typography variant="subtitle1">
- <strong>{t("Delivery Order")}:</strong> {deliveryNosArray[idx] || '-'}
- </Typography>
- <Typography variant="subtitle1">
- <strong>{t("Pick Order")}:</strong> {pickOrderCodesArray[idx] || '-'}
- </Typography>
- <Typography variant="subtitle1">
- <strong>{t("Finsihed good items")}:</strong> {lineCountsArray[idx] || '-'}<strong>{t("kinds")}</strong>
- </Typography>
- </Stack>
- ));
- })()}
- </Box>
- </Stack>
- </Paper>
- )}
-
-
- <TableContainer component={Paper}>
- <Table>
- <TableHead>
- <TableRow>
- <TableCell>{t("Index")}</TableCell>
- <TableCell>{t("Route")}</TableCell>
- <TableCell>{t("Item Code")}</TableCell>
- <TableCell>{t("Item Name")}</TableCell>
- <TableCell>{t("Lot#")}</TableCell>
- <TableCell align="right">{t("Lot Required Pick Qty")}</TableCell>
- <TableCell align="center">{t("Scan Result")}</TableCell>
- <TableCell align="center">{t("Submit Required Pick Qty")}</TableCell>
- </TableRow>
- </TableHead>
- <TableBody>
- {paginatedData.length === 0 ? (
- <TableRow>
- <TableCell colSpan={11} align="center">
- <Typography variant="body2" color="text.secondary">
- {t("No data available")}
- </Typography>
- </TableCell>
- </TableRow>
- ) : (
- // 在第 1797-1938 行之间,将整个 map 函数修改为:
- paginatedData.map((lot, index) => {
- // 检查是否是 issue lot
- const isIssueLot = lot.stockOutLineStatus === 'rejected' || !lot.lotNo;
-
- return (
- <TableRow
- key={`${lot.pickOrderLineId}-${lot.lotId || 'null'}`}
- sx={{
- //backgroundColor: isIssueLot ? '#fff3e0' : 'inherit',
- // opacity: isIssueLot ? 0.6 : 1,
- '& .MuiTableCell-root': {
- //color: isIssueLot ? 'warning.main' : 'inherit'
- }
- }}
- >
- <TableCell>
- <Typography variant="body2" fontWeight="bold">
- {index + 1}
- </Typography>
- </TableCell>
- <TableCell>
- <Typography variant="body2">
- {lot.routerRoute || '-'}
- </Typography>
- </TableCell>
- <TableCell>{lot.itemCode}</TableCell>
- <TableCell>{lot.itemName + '(' + lot.stockUnit + ')'}</TableCell>
- <TableCell>
- <Box>
- <Typography
- sx={{
- // color: isIssueLot ? 'warning.main' : lot.lotAvailability === 'rejected' ? 'text.disabled' : 'inherit',
- }}
- >
- {lot.lotNo ||
- t('⚠️ No Stock Available')}
- </Typography>
- </Box>
- </TableCell>
- <TableCell align="right">
- {(() => {
- const requiredQty = lot.requiredQty || 0;
- return requiredQty.toLocaleString() + '(' + lot.uomShortDesc + ')';
- })()}
- </TableCell>
-
- <TableCell align="center">
- {(() => {
- const status = lot.stockOutLineStatus?.toLowerCase();
- const isRejected = status === 'rejected' || lot.lotAvailability === 'rejected';
- const isNoLot = !lot.lotNo;
-
- // rejected lot:显示红色勾选(已扫描但被拒绝)
- if (isRejected && !isNoLot) {
- return (
- <Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
- <Checkbox
- checked={true}
- disabled={true}
- readOnly={true}
- size="large"
- sx={{
- color: 'error.main',
- '&.Mui-checked': { color: 'error.main' },
- transform: 'scale(1.3)',
- }}
- />
- </Box>
- );
- }
-
- // 正常 lot:已扫描(checked/partially_completed/completed)
- if (!isNoLot && status !== 'pending' && status !== 'rejected') {
- return (
- <Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
- <Checkbox
- checked={true}
- disabled={true}
- readOnly={true}
- size="large"
- sx={{
- color: 'success.main',
- '&.Mui-checked': { color: 'success.main' },
- transform: 'scale(1.3)',
- }}
- />
- </Box>
- );
- }
-
- // noLot 且已完成/部分完成:显示红色勾选
- if (isNoLot && (status === 'partially_completed' || status === 'completed')) {
- return (
- <Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
- <Checkbox
- checked={true}
- disabled={true}
- readOnly={true}
- size="large"
- sx={{
- color: 'error.main',
- '&.Mui-checked': { color: 'error.main' },
- transform: 'scale(1.3)',
- }}
- />
- </Box>
- );
- }
-
- return null;
- })()}
- </TableCell>
- <TableCell align="center">
- <Box sx={{ display: 'flex', justifyContent: 'center' }}>
- {(() => {
- const status = lot.stockOutLineStatus?.toLowerCase();
- const isRejected = status === 'rejected' || lot.lotAvailability === 'rejected';
- const isNoLot = !lot.lotNo;
-
- // rejected lot:不显示任何按钮
- if (isRejected && !isNoLot) {
- return null;
- }
-
- // noLot 情况:只显示 Issue 按钮
- if (isNoLot) {
- return (
- <Button
- variant="outlined"
- size="small"
- onClick={() => handlelotnull(lot)}
- disabled={status === 'completed'}
- sx={{
- fontSize: '0.7rem',
- py: 0.5,
- minHeight: '28px',
- minWidth: '60px',
- borderColor: 'warning.main',
- color: 'warning.main'
- }}
- >
- {t("Issue")}
- </Button>
- );
- }
-
- // 正常 lot:显示 Submit 和 Issue 按钮
- return (
- <Stack direction="row" spacing={1} alignItems="center">
- <Button
- variant="contained"
- onClick={() => {
- const lotKey = `${lot.pickOrderLineId}-${lot.lotId}`;
- const submitQty = lot.requiredQty || lot.pickOrderLineRequiredQty;
- handlePickQtyChange(lotKey, submitQty);
- handleSubmitPickQtyWithQty(lot, submitQty);
- }}
- disabled={
- lot.lotAvailability === 'expired' ||
- lot.lotAvailability === 'status_unavailable' ||
- lot.lotAvailability === 'rejected' ||
- lot.stockOutLineStatus === 'completed' ||
- lot.stockOutLineStatus === 'pending'
- }
- 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' ||
- lot.stockOutLineStatus === 'pending'
- }
- 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>
-
- <TablePagination
- component="div"
- count={combinedLotData.length}
- page={paginationController.pageNum}
- rowsPerPage={paginationController.pageSize}
- onPageChange={handlePageChange}
- onRowsPerPageChange={handlePageSizeChange}
- rowsPerPageOptions={[10, 25, 50]}
- labelRowsPerPage={t("Rows per page")}
- labelDisplayedRows={({ from, to, count }) =>
- `${from}-${to} of ${count !== -1 ? count : `more than ${to}`}`
- }
- />
- </Box>
- </Stack>
-
- {/* 保留:QR Code Modal */}
- <QrCodeModal
- open={qrModalOpen}
- onClose={() => {
- setQrModalOpen(false);
- setSelectedLotForQr(null);
- stopScan();
- resetScan();
- }}
- lot={selectedLotForQr}
- combinedLotData={combinedLotData}
- onQrCodeSubmit={handleQrCodeSubmitFromModal}
- />
-
- {/* 保留:Lot Confirmation Modal */}
- {lotConfirmationOpen && expectedLotData && scannedLotData && (
- <LotConfirmationModal
- open={lotConfirmationOpen}
- onClose={() => {
- setLotConfirmationOpen(false);
- setExpectedLotData(null);
- setScannedLotData(null);
- }}
- onConfirm={handleLotConfirmation}
- expectedLot={expectedLotData}
- scannedLot={scannedLotData}
- isLoading={isConfirmingLot}
- />
- )}
-
- {/* 保留:Good Pick Execution Form Modal */}
- {pickExecutionFormOpen && selectedLotForExecutionForm && (
- <GoodPickExecutionForm
- open={pickExecutionFormOpen}
- onClose={() => {
- setPickExecutionFormOpen(false);
- setSelectedLotForExecutionForm(null);
- }}
- onSubmit={handlePickExecutionFormSubmit}
- selectedLot={selectedLotForExecutionForm}
- selectedPickOrderLine={{
- id: selectedLotForExecutionForm.pickOrderLineId,
- itemId: selectedLotForExecutionForm.itemId,
- itemCode: selectedLotForExecutionForm.itemCode,
- itemName: selectedLotForExecutionForm.itemName,
- pickOrderCode: selectedLotForExecutionForm.pickOrderCode,
- availableQty: selectedLotForExecutionForm.availableQty || 0,
- requiredQty: selectedLotForExecutionForm.requiredQty || 0,
- // uomCode: selectedLotForExecutionForm.uomCode || '',
- uomDesc: selectedLotForExecutionForm.uomDesc || '',
- pickedQty: selectedLotForExecutionForm.actualPickQty || 0,
- uomShortDesc: selectedLotForExecutionForm.uomShortDesc || '',
- suggestedList: []
- }}
- pickOrderId={selectedLotForExecutionForm.pickOrderId}
- pickOrderCreateDate={new Date()}
- />
- )}
- </FormProvider>
-
-
-
-
- </TestQrCodeProvider>
- );
- };
-
- export default PickExecution;
|