From e4b2e48dc8b22eb27c552ad2d8d57a05b9e00978 Mon Sep 17 00:00:00 2001 From: "CANCERYS\\kw093" Date: Tue, 27 Oct 2026 14:38:25 +0800 Subject: [PATCH] if have open item round, select new round by location if have overlap lcoation will tell those item will skip --- src/app/api/stockTake/actions.ts | 21 ++++ .../StockTakeManagement/PickerCardList.tsx | 99 +++++++++++++++++++ src/i18n/en/stockTake.json | 2 + src/i18n/zh/stockTake.json | 2 + 4 files changed, 124 insertions(+) diff --git a/src/app/api/stockTake/actions.ts b/src/app/api/stockTake/actions.ts index 793d3454..73899c97 100644 --- a/src/app/api/stockTake/actions.ts +++ b/src/app/api/stockTake/actions.ts @@ -428,6 +428,27 @@ export type StockTakeCreateItemOption = { }; /** 來源:All / PP / PF / other(與庫存批次現況報告一致) */ +/** 建立區域輪:與 open 貨品輪重疊、本輪會略過的貨品 */ +export type SectionCreateItemOverlap = { + itemId: number; + itemCode: string | null; + itemName: string | null; + stockTakeSection: string | null; + stockTakeRoundId: number; + stockTakeRoundName: string | null; +}; + +export const getSectionCreateItemOverlaps = async (sections: string[]) => { + const list = sections.map((s) => s.trim()).filter((s) => s.length > 0); + if (list.length === 0) return [] as SectionCreateItemOverlap[]; + const params = new URLSearchParams(); + list.forEach((s) => params.append("sections", s)); + return serverFetchJson( + `${BASE_API_URL}/stockTakeRecord/sectionCreateItemOverlaps?${params.toString()}`, + { method: "GET" }, + ); +}; + export const getAvailableLotsForStockTakeCreate = async ( itemIds: number[], lotOrigin?: string | null, diff --git a/src/components/StockTakeManagement/PickerCardList.tsx b/src/components/StockTakeManagement/PickerCardList.tsx index cf916d15..ca5fc4df 100644 --- a/src/components/StockTakeManagement/PickerCardList.tsx +++ b/src/components/StockTakeManagement/PickerCardList.tsx @@ -35,6 +35,7 @@ import { Accordion, AccordionSummary, AccordionDetails, + Alert, } from "@mui/material"; import SearchIcon from "@mui/icons-material/Search"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; @@ -53,7 +54,9 @@ import { getLatestStockTakeRoundMeta, getBusyStockTakeSections, getAvailableLotsForStockTakeCreate, + getSectionCreateItemOverlaps, searchItemsForStockTakeCreate, + type SectionCreateItemOverlap, type StockTakeCreateLotOption, } from "@/app/api/stockTake/actions"; import { fetchStockTakeSections } from "@/app/api/warehouse/actions"; @@ -357,6 +360,8 @@ const PickerCardList: React.FC = ({ const [createDialogSelectedLotLineIds, setCreateDialogSelectedLotLineIds] = useState([]); const [createDialogLotsLoading, setCreateDialogLotsLoading] = useState(false); const [createDialogItemSearching, setCreateDialogItemSearching] = useState(false); + const [sectionItemOverlaps, setSectionItemOverlaps] = useState([]); + const [sectionOverlapsLoading, setSectionOverlapsLoading] = useState(false); const [sectionsLoading, setSectionsLoading] = useState(false); const [stockTakeSectionRows, setStockTakeSectionRows] = useState([]); const createStockTakeInFlightRef = useRef(false); @@ -1197,6 +1202,39 @@ const PickerCardList: React.FC = ({ }; }, [openConfirmDialog, createMode, createDialogSelectedItems, createDialogLotOrigin]); + useEffect(() => { + if (!openConfirmDialog || createMode !== "section") { + setSectionItemOverlaps([]); + setSectionOverlapsLoading(false); + return; + } + const sections = createDialogSelectedSections.map((s) => s.trim()).filter((s) => s.length > 0); + if (sections.length === 0) { + setSectionItemOverlaps([]); + setSectionOverlapsLoading(false); + return; + } + let cancelled = false; + const timer = window.setTimeout(() => { + setSectionOverlapsLoading(true); + getSectionCreateItemOverlaps(sections) + .then((rows) => { + if (!cancelled) setSectionItemOverlaps(Array.isArray(rows) ? rows : []); + }) + .catch((e) => { + console.error("Failed to load section create item overlaps:", e); + if (!cancelled) setSectionItemOverlaps([]); + }) + .finally(() => { + if (!cancelled) setSectionOverlapsLoading(false); + }); + }, 250); + return () => { + cancelled = true; + window.clearTimeout(timer); + }; + }, [openConfirmDialog, createMode, createDialogSelectedSections]); + useEffect(() => { setSectionsLoading(true); fetchStockTakeSections() @@ -1318,8 +1356,26 @@ const PickerCardList: React.FC = ({ setCreateDialogSelectedItems([]); setCreateDialogLotOptions([]); setCreateDialogSelectedLotLineIds([]); + setSectionItemOverlaps([]); + setSectionOverlapsLoading(false); onCreateDialogClose?.(); }; + + const formatSectionItemOverlapLine = useCallback( + (row: SectionCreateItemOverlap) => { + const itemLabel = [row.itemCode, row.itemName].filter(Boolean).join(" ").trim() || `#${row.itemId}`; + const roundName = row.stockTakeRoundName?.trim() + ? `「${row.stockTakeRoundName.trim()}」` + : ""; + return t("Section create item overlap line", { + item: itemLabel, + round: row.stockTakeRoundId, + roundName, + section: row.stockTakeSection || "-", + }); + }, + [t], + ); return ( {!hideSectionList && ( @@ -2010,6 +2066,30 @@ const PickerCardList: React.FC = ({ {" "} {createMode === "item" ? t("lots unit") : t("sections unit")} + {createMode === "section" && sectionOverlapsLoading ? ( + + … + + ) : null} + {createMode === "section" && sectionItemOverlaps.length > 0 ? ( + + + {t("Section create item overlap title")} + + + {sectionItemOverlaps.map((row) => ( + + {formatSectionItemOverlapLine(row)} + + ))} + + + ) : null} {/* 右側:區域卡片網格 */} @@ -2495,6 +2575,25 @@ const PickerCardList: React.FC = ({ {" "} {createMode === "item" ? t("lots unit") : t("sections unit")} + {createMode === "section" && sectionItemOverlaps.length > 0 ? ( + + + {t("Section create item overlap title")} + + + {sectionItemOverlaps.map((row) => ( + + {formatSectionItemOverlapLine(row)} + + ))} + + + ) : null}