From 181140738d143b084808db14e88410b0b8b46d97 Mon Sep 17 00:00:00 2001 From: "Tommy\\2Fi-Staff" Date: Tue, 14 Apr 2026 22:54:47 +0800 Subject: [PATCH] lotlabelprintmodal update --- .../GoodPickExecutiondetail.tsx | 32 +++++++--- .../InventorySearch/LotLabelPrintModal.tsx | 58 ++++++++++++++++--- 2 files changed, 74 insertions(+), 16 deletions(-) diff --git a/src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx b/src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx index 80baa7b..8a3a7e9 100644 --- a/src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx +++ b/src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx @@ -77,7 +77,6 @@ import QrCodeIcon from "@mui/icons-material/QrCode"; import { useQrCodeScannerContext } from "../QrCodeScannerProvider/QrCodeScannerProvider"; import { useSession } from "next-auth/react"; import { SessionWithTokens } from "@/config/authConfig"; -import { AUTH } from "@/authorities"; import { fetchStockInLineInfo } from "@/app/api/po/actions"; import GoodPickExecutionForm from "./GoodPickExecutionForm"; import FGPickOrderCard from "./FGPickOrderCard"; @@ -687,8 +686,6 @@ const PickExecution: React.FC = ({ const { t } = useTranslation("pickOrder"); const router = useRouter(); const { data: session } = useSession() as { data: SessionWithTokens | null }; - const abilities = session?.abilities ?? session?.user?.abilities ?? []; - const isAdmin = abilities.some((a) => String(a).trim() === AUTH.ADMIN); const [doPickOrderDetail, setDoPickOrderDetail] = useState(null); const [selectedPickOrderId, setSelectedPickOrderId] = useState( @@ -783,6 +780,25 @@ const PickExecution: React.FC = ({ useState(null); const [fgPickOrders, setFgPickOrders] = useState([]); const [fgPickOrdersLoading, setFgPickOrdersLoading] = useState(false); + const lotFloorPrefixFilter = useMemo(() => { + const storeId = String(fgPickOrders?.[0]?.storeId ?? "") + .trim() + .toUpperCase() + .replace(/\s/g, ""); + // e.g. "2/F" -> "2F-", "4/F" -> "4F-" + const floorKey = storeId.replace(/\//g, ""); + return floorKey ? `${floorKey}-` : ""; + }, [fgPickOrders]); + const defaultLabelPrinterName = useMemo(() => { + const storeId = String(fgPickOrders?.[0]?.storeId ?? "") + .trim() + .toUpperCase() + .replace(/\s/g, ""); + const floorKey = storeId.replace(/\//g, ""); + if (floorKey === "2F") return "Label機 2F A+B"; + if (floorKey === "4F") return "Label機 4F 乾貨 C, D"; + return undefined; + }, [fgPickOrders]); // Add these missing state variables after line 352 const [isManualScanning, setIsManualScanning] = useState(false); // Track processed QR codes by itemId+stockInLineId combination for better lot confirmation handling @@ -1690,7 +1706,7 @@ const PickExecution: React.FC = ({ return; } - if (switchedToUnavailable && isAdmin) { + if (switchedToUnavailable) { const itemId = Number(sel?.itemId ?? exp?.itemId); const stockInLineId = Number(newStockInLineId); if (Number.isFinite(itemId) && Number.isFinite(stockInLineId)) { @@ -1745,7 +1761,6 @@ const PickExecution: React.FC = ({ resetScan, clearLotConfirmationState, t, - isAdmin, ], ); @@ -2138,7 +2153,7 @@ const PickExecution: React.FC = ({ const byLotId = new Map(); const byLotNo = new Map(); const byStockInLineId = new Map(); - + // Cache active lots separately to avoid filtering on every scan const activeLotsByItemId = new Map(); const rejectedStatuses = new Set(["rejected"]); @@ -5055,9 +5070,12 @@ const PickExecution: React.FC = ({ setLotLabelPrintReminderText(null); }} initialPayload={lotLabelPrintInitialPayload} - defaultPrinterName="Label機 2F A+B" + defaultPrinterName={defaultLabelPrinterName} hideScanSection reminderText={lotLabelPrintReminderText ?? undefined} + statusTitleText="此批號的已用完/已過期" + warehouseCodePrefixFilter={lotFloorPrefixFilter} + hideTriggeredLot /> diff --git a/src/components/InventorySearch/LotLabelPrintModal.tsx b/src/components/InventorySearch/LotLabelPrintModal.tsx index 20f5026..4bdf59e 100644 --- a/src/components/InventorySearch/LotLabelPrintModal.tsx +++ b/src/components/InventorySearch/LotLabelPrintModal.tsx @@ -75,6 +75,12 @@ export interface LotLabelPrintModalProps { hideScanSection?: boolean; /** 額外提醒(顯示在最上方) */ reminderText?: string; + /** 額外標題(顯示在最上方,reminderText 之下) */ + statusTitleText?: string; + /** 只顯示特定倉位前綴(例如 "2F-") */ + warehouseCodePrefixFilter?: string; + /** 不顯示觸發視窗的批號(analysis.scanned) */ + hideTriggeredLot?: boolean; } function safeParseScanPayload(raw: string): ScanPayload | null { @@ -115,6 +121,9 @@ const LotLabelPrintModal: React.FC = ({ defaultPrinterName, hideScanSection, reminderText, + statusTitleText, + warehouseCodePrefixFilter, + hideTriggeredLot, }) => { const scanInputRef = useRef(null); const [scanInput, setScanInput] = useState(""); @@ -126,6 +135,7 @@ const LotLabelPrintModal: React.FC = ({ const [analysisLoading, setAnalysisLoading] = useState(false); const [analysis, setAnalysis] = useState(null); + const [lastPayload, setLastPayload] = useState(null); const [printQty, setPrintQty] = useState(1); const [printingLotLineId, setPrintingLotLineId] = useState( @@ -234,6 +244,7 @@ const LotLabelPrintModal: React.FC = ({ return; } + setLastPayload(payload); setScanError(null); setAnalysisLoading(true); try { @@ -279,6 +290,19 @@ const LotLabelPrintModal: React.FC = ({ await analyzePayload(payload); }, [scanInput, analyzePayload]); + const handleRefreshLots = useCallback(async () => { + const payload = lastPayload ?? safeParseScanPayload(scanInput.trim()); + if (!payload) { + setSnackbar({ + open: true, + message: "請先掃碼或查詢一次,才可刷新批號清單。", + severity: "info", + }); + return; + } + await analyzePayload(payload); + }, [analyzePayload, lastPayload, scanInput]); + useEffect(() => { if (!open) return; if (!initialPayload) return; @@ -310,14 +334,22 @@ const LotLabelPrintModal: React.FC = ({ : null; const merged = [ - ...(scannedLot ? [scannedLot] : []), + ...(!hideTriggeredLot && scannedLot ? [scannedLot] : []), ...list .filter((x) => x.inventoryLotLineId !== scannedLotLineId) .map((x) => ({ ...x, _scanned: false as const })), ]; return merged; - }, [analysis]); + }, [analysis, hideTriggeredLot]); + + const filteredLots = useMemo(() => { + const prefix = String(warehouseCodePrefixFilter ?? "").trim(); + if (!prefix) return availableLots; + return availableLots.filter((lot) => + String(lot.warehouseCode ?? "").startsWith(prefix), + ); + }, [availableLots, warehouseCodePrefixFilter]); const selectedPrinter = useMemo(() => { if (selectedPrinterId === "") return null; @@ -390,6 +422,14 @@ const LotLabelPrintModal: React.FC = ({ 批號標籤列印 + {statusTitleText ? ( + + {statusTitleText} + + ) : null} {reminderText ? ( {reminderText} ) : null} @@ -485,13 +525,13 @@ const LotLabelPrintModal: React.FC = ({ @@ -512,13 +552,13 @@ const LotLabelPrintModal: React.FC = ({ 品號:{analysis.itemCode} {analysis.itemName} - {availableLots.length === 0 ? ( + {filteredLots.length === 0 ? ( - 找不到可用批號(availableQty > 0)。 + 找不到該樓層有可用批號(availableQty > 0)。 ) : ( - {availableLots.map((lot) => { + {filteredLots.map((lot) => { const isPrinting = printingLotLineId === lot.inventoryLotLineId; const loc = String(lot.warehouseCode ?? "").trim();