diff --git a/src/app/api/stockTake/actions.ts b/src/app/api/stockTake/actions.ts index 73899c97..b4721c54 100644 --- a/src/app/api/stockTake/actions.ts +++ b/src/app/api/stockTake/actions.ts @@ -449,6 +449,18 @@ export const getSectionCreateItemOverlaps = async (sections: string[]) => { ); }; +/** Sections that currently have no creatable lots (would become empty headers). */ +export const getSectionsWithNoAvailableLots = async (sections: string[]) => { + const list = sections.map((s) => s.trim()).filter((s) => s.length > 0); + if (list.length === 0) return [] as string[]; + const params = new URLSearchParams(); + list.forEach((s) => params.append("sections", s)); + return serverFetchJson( + `${BASE_API_URL}/stockTakeRecord/sectionsWithNoAvailableLots?${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 36e66b81..63d45107 100644 --- a/src/components/StockTakeManagement/PickerCardList.tsx +++ b/src/components/StockTakeManagement/PickerCardList.tsx @@ -55,6 +55,7 @@ import { getBusyStockTakeSections, getAvailableLotsForStockTakeCreate, getSectionCreateItemOverlaps, + getSectionsWithNoAvailableLots, searchItemsForStockTakeCreate, type SectionCreateItemOverlap, type StockTakeCreateLotOption, @@ -362,6 +363,7 @@ const PickerCardList: React.FC = ({ const [createDialogLotsLoading, setCreateDialogLotsLoading] = useState(false); const [createDialogItemSearching, setCreateDialogItemSearching] = useState(false); const [sectionItemOverlaps, setSectionItemOverlaps] = useState([]); + const [sectionEmptySections, setSectionEmptySections] = useState([]); const [sectionOverlapsLoading, setSectionOverlapsLoading] = useState(false); const [sectionsLoading, setSectionsLoading] = useState(false); const [stockTakeSectionRows, setStockTakeSectionRows] = useState([]); @@ -1206,25 +1208,32 @@ const PickerCardList: React.FC = ({ useEffect(() => { if (!openConfirmDialog || createMode !== "section") { setSectionItemOverlaps([]); + setSectionEmptySections([]); setSectionOverlapsLoading(false); return; } const sections = createDialogSelectedSections.map((s) => s.trim()).filter((s) => s.length > 0); if (sections.length === 0) { setSectionItemOverlaps([]); + setSectionEmptySections([]); setSectionOverlapsLoading(false); return; } let cancelled = false; const timer = window.setTimeout(() => { setSectionOverlapsLoading(true); - getSectionCreateItemOverlaps(sections) - .then((rows) => { - if (!cancelled) setSectionItemOverlaps(Array.isArray(rows) ? rows : []); + Promise.all([getSectionCreateItemOverlaps(sections), getSectionsWithNoAvailableLots(sections)]) + .then(([rows, emptySections]) => { + if (cancelled) return; + setSectionItemOverlaps(Array.isArray(rows) ? rows : []); + setSectionEmptySections(Array.isArray(emptySections) ? emptySections : []); }) .catch((e) => { - console.error("Failed to load section create item overlaps:", e); - if (!cancelled) setSectionItemOverlaps([]); + console.error("Failed to load section create previews:", e); + if (!cancelled) { + setSectionItemOverlaps([]); + setSectionEmptySections([]); + } }) .finally(() => { if (!cancelled) setSectionOverlapsLoading(false); @@ -1339,6 +1348,7 @@ const PickerCardList: React.FC = ({ setCreateDialogLotOptions([]); setCreateDialogSelectedLotLineIds([]); setSectionItemOverlaps([]); + setSectionEmptySections([]); setSectionOverlapsLoading(false); onCreateDialogClose?.(); }; @@ -2053,6 +2063,28 @@ const PickerCardList: React.FC = ({ … ) : null} + {createMode === "section" && sectionEmptySections.length > 0 ? ( + + + {t("Section create empty lots title")} + + + {t("Section create empty lots hint")} + + + {sectionEmptySections.map((section) => ( + + {section} + + ))} + + + ) : null} {createMode === "section" && sectionItemOverlaps.length > 0 ? ( @@ -2547,6 +2579,28 @@ const PickerCardList: React.FC = ({ {" "} {createMode === "item" ? t("lots unit") : t("sections unit")} + {createMode === "section" && sectionEmptySections.length > 0 ? ( + + + {t("Section create empty lots title")} + + + {t("Section create empty lots hint")} + + + {sectionEmptySections.map((section) => ( + + {section} + + ))} + + + ) : null} {createMode === "section" && sectionItemOverlaps.length > 0 ? ( diff --git a/src/i18n/en/stockTake.json b/src/i18n/en/stockTake.json index 7345b61e..c9dac003 100644 --- a/src/i18n/en/stockTake.json +++ b/src/i18n/en/stockTake.json @@ -64,6 +64,8 @@ "Confirm create stock take": "Confirm create stock take", "Section create item overlap title": "These items are already in an open item stock-take round; this round will skip them", "Section create item overlap line": "Item {{item}} is in round #{{round}}{{roundName}}; this round will not include it (section {{section}})", + "Section create empty lots title": "These sections have no lots available to stock-take", + "Section create empty lots hint": "Creating them will add empty section headers (excluded from round progress). Uncheck them, or continue anyway.", "Control Time": "Time spent", "Create by item": "Item stock-take", "Create by section": "Location stock-take", diff --git a/src/i18n/zh/stockTake.json b/src/i18n/zh/stockTake.json index 6122aacb..a4cb92f9 100644 --- a/src/i18n/zh/stockTake.json +++ b/src/i18n/zh/stockTake.json @@ -185,6 +185,8 @@ "Confirm create stock take": "確認建立盤點", "Section create item overlap title": "以下貨品已在進行中的貨品盤點輪,本輪會略過(不會盤到)", "Section create item overlap line": "貨品 {{item}} 正由輪次 #{{round}}{{roundName}} 盤點中,本輪不會包含此貨品(區域 {{section}})", + "Section create empty lots title": "以下區域目前沒有可盤批號", + "Section create empty lots hint": "建立後會變成沒有明細的空區(不計入輪次進度)。可取消勾選後再建立,或仍繼續建立。", "Not filled": "(未填寫)", "Warehouse missing stock take section drawer hint": "以下倉庫位置尚未設定盤點區域(ST-xxx),無法納入盤點區域選擇。請點各筆「前往此倉庫設定」補上。", "Warehouse missing stock take section showing": "顯示前 {{shown}} 筆,共 {{count}} 筆",