diff --git a/src/components/StockTakeManagement/ApproverStockTakeAll.tsx b/src/components/StockTakeManagement/ApproverStockTakeAll.tsx index 944129d1..bd50558f 100644 --- a/src/components/StockTakeManagement/ApproverStockTakeAll.tsx +++ b/src/components/StockTakeManagement/ApproverStockTakeAll.tsx @@ -77,6 +77,8 @@ interface ApproverStockTakeAllProps { onSnackbar: (message: string, severity: "success" | "error" | "warning") => void; /** 審核待處理時,把批量保存按鈕掛到分頁列右側 */ batchButtonHost?: HTMLElement | null; + /** History / closed round — disable save / batch save */ + roundReadOnly?: boolean; } type QtySelectionType = "first" | "second" | "approver"; @@ -368,6 +370,7 @@ const ApproverStockTakeAll: React.FC = ({ onBack, onSnackbar, batchButtonHost = null, + roundReadOnly = false, }) => { const { t } = useTranslation(["stockTake", "common"]); const qtyGapWarnPercent = useStockTakeQtyGapWarnPercent(); @@ -375,6 +378,8 @@ const ApproverStockTakeAll: React.FC = ({ const isAdmin = (session?.abilities ?? session?.user?.abilities ?? []).some( (ability) => String(ability).trim() === AUTH.ADMIN, ); + const isRoundClosed = + roundReadOnly || selectedSession?.status?.toLowerCase() === "completed"; const [inventoryLotDetails, setInventoryLotDetails] = useState([]); const [loadingDetails, setLoadingDetails] = useState(false); @@ -896,7 +901,7 @@ const ApproverStockTakeAll: React.FC = ({ const handleSaveApproverStockTake = useCallback( async (detail: InventoryLotDetailResponse) => { - if (mode === "approved") return; + if (mode === "approved" || isRoundClosed) return; if (!selectedSession || !currentUserId) { return; } @@ -975,7 +980,7 @@ const ApproverStockTakeAll: React.FC = ({ const handleUpdateStatusToNotMatch = useCallback( async (detail: InventoryLotDetailResponse) => { - if (mode === "approved") return; + if (mode === "approved" || isRoundClosed) return; if (!detail.stockTakeRecordId) { onSnackbar(t("Stock take record ID is required"), "error"); return; @@ -1056,7 +1061,7 @@ const ApproverStockTakeAll: React.FC = ({ }, [sortedDetails]); const handleOpenBatchSaveConfirm = useCallback(() => { - if (mode === "approved") return; + if (mode === "approved" || isRoundClosed) return; if (!selectedSession || !currentUserId) return; if (inventoryLotDetails.length === 0) { onSnackbar(t("No rows loaded; set search criteria and search first"), "warning"); @@ -1079,7 +1084,7 @@ const ApproverStockTakeAll: React.FC = ({ const handleBatchSubmitAll = useCallback(async () => { if (batchSaveInFlightRef.current) return; - if (mode === "approved") return; + if (mode === "approved" || isRoundClosed) return; if (!selectedSession || !currentUserId) return; const records = buildBatchApproverSaveRequests( @@ -1169,7 +1174,30 @@ const ApproverStockTakeAll: React.FC = ({ if (!ms) return "-"; return dayjs(ms).format("YYYY-MM-DD HH:mm"); }; - const rows = sortedDetails; + /** Word-style: same location grouped under a heading (picker-like). */ + const locationGroups = useMemo(() => { + const groups: { key: string; label: string; items: InventoryLotDetailResponse[] }[] = []; + const indexByKey = new Map(); + for (const detail of sortedDetails) { + const area = detail.warehouseArea?.trim() || ""; + const slot = detail.warehouseSlot?.trim() || ""; + const code = detail.warehouseCode?.trim() || ""; + const key = code || `${area}|${slot}` || String(detail.id); + const label = + code || + (area || slot ? `${area}${slot}` : "") || + detail.stockTakeSection?.trim() || + "-"; + let idx = indexByKey.get(key); + if (idx == null) { + idx = groups.length; + indexByKey.set(key, idx); + groups.push({ key, label, items: [] }); + } + groups[idx].items.push(detail); + } + return groups; + }, [sortedDetails]); const columns = useMemo[]>(() => { const cols: GridColDef[] = []; @@ -1190,29 +1218,11 @@ const ApproverStockTakeAll: React.FC = ({ } cols.push( - { - field: "warehouseLocation", - headerName: t("Warehouse Location"), - minWidth: 220, - flex: 1, - sortable: false, - renderCell: (params) => ( - - - - {params.row.stockTakeSection || "-"}{" "} - {params.row.stockTakeSectionDescription || "-"} - - - {params.row.warehouseCode || "-"} - - ), - }, { field: "itemLot", headerName: t("Item-lotNo-ExpiryDate"), - minWidth: 160, - flex: 1, + minWidth: 220, + flex: 1.6, sortable: false, renderCell: (params) => ( @@ -1222,12 +1232,20 @@ const ApproverStockTakeAll: React.FC = ({ > {params.row.itemCode || "-"} {params.row.itemName || "-"} - {params.row.lotNo || "-"} - + + {params.row.lotNo || "-"} + + {params.row.expiryDate ? dayjs(params.row.expiryDate).format(OUTPUT_DATE_FORMAT) : "-"} - + ), }, @@ -1577,7 +1595,7 @@ const ApproverStockTakeAll: React.FC = ({ size="small" variant="contained" onClick={() => handleSaveApproverStockTake(detail)} - disabled={!isAdmin || saving} + disabled={!isAdmin || saving || isRoundClosed} > {t("Save")} @@ -1588,7 +1606,7 @@ const ApproverStockTakeAll: React.FC = ({ variant="outlined" color="warning" onClick={() => handleUpdateStatusToNotMatch(detail)} - disabled={!isAdmin || updatingStatus || hasSecond} + disabled={!isAdmin || updatingStatus || hasSecond || isRoundClosed} > {t("Not Match")} @@ -1626,6 +1644,7 @@ const ApproverStockTakeAll: React.FC = ({ handleSaveApproverStockTake, handleUpdateStatusToNotMatch, formatRecordEndTime, + isRoundClosed, ]); const effectivePageSize = pageSize === "all" ? Math.max(total || 0, 0) : (pageSize as number); @@ -1635,7 +1654,7 @@ const ApproverStockTakeAll: React.FC = ({ variant="contained" color="primary" onClick={handleOpenBatchSaveConfirm} - disabled={!isAdmin || batchSaving} + disabled={!isAdmin || batchSaving || isRoundClosed} > {t("Batch Save All")} @@ -2133,24 +2152,48 @@ const ApproverStockTakeAll: React.FC = ({ {varianceRangeBar} - - row.id} - loading={loadingDetails} - disableRowSelectionOnClick - paginationMode="client" // ← important: client-side - pageSizeOptions={[50, 100, 200, 500]} - getRowHeight={() => "auto"} - sx={{ - backgroundColor: "transparent", - "& .MuiDataGrid-columnHeaders": { backgroundColor: "#fff" }, - "& .MuiDataGrid-cell": { py: 1, alignItems: "flex-start" }, - "& .MuiDataGrid-row": { minHeight: 96 }, - }} - /> - + + {loadingDetails && sortedDetails.length === 0 ? ( + + + + ) : sortedDetails.length === 0 ? ( + + {t("No data")} + + ) : ( + locationGroups.map((group) => ( + + + {t("Warehouse Location")}: {group.label} + + + row.id} + loading={loadingDetails} + disableRowSelectionOnClick + hideFooter + getRowHeight={() => "auto"} + sx={{ + backgroundColor: "transparent", + "& .MuiDataGrid-columnHeaders": { backgroundColor: "#fff" }, + "& .MuiDataGrid-cell": { py: 1, alignItems: "flex-start" }, + "& .MuiDataGrid-row": { minHeight: 96 }, + }} + /> + + + )) + )} + = ({ /> - {createMode === "item" ? ( - - {t("Create by item hint")} - - ) : sortedFloorKeys.length === 0 && !sectionsLoading ? ( + {createMode === "item" ? null : sortedFloorKeys.length === 0 && !sectionsLoading ? ( {t("No stock take sections from warehouse")} @@ -2141,9 +2137,6 @@ const PickerCardList: React.FC = ({ - - {t("Create by item hint")} - {createDialogSelectedItems.map((it) => ( = ({ }} > - - {t("Create by item search hint")} - {createDialogItemSearching ? ( ) : createDialogItemHits.length === 0 ? ( - - {createDialogItemCode.trim() || - createDialogItemName.trim() || - (createDialogLotOrigin.trim() !== "" && - createDialogLotOrigin.trim().toLowerCase() !== "all") - ? t("No data") - : t("Create by item search hint")} - + createDialogItemCode.trim() || + createDialogItemName.trim() || + (createDialogLotOrigin.trim() !== "" && + createDialogLotOrigin.trim().toLowerCase() !== "all") ? ( + + {t("No data")} + + ) : null ) : ( {createDialogItemHits.map((it) => { @@ -2244,11 +2234,7 @@ const PickerCardList: React.FC = ({ - ) : createDialogSelectedItems.length === 0 ? ( - - {t("Create by item select item first")} - - ) : createDialogLotOptions.length === 0 ? ( + ) : createDialogSelectedItems.length === 0 ? null : createDialogLotOptions.length === 0 ? ( {t("No available lots for selected items")} @@ -2671,9 +2657,7 @@ const PickerCardList: React.FC = ({ {t("Warehouse missing stock take section warn title")} - - {t("Warehouse missing stock take section drawer hint")} - + {missingSectionCount > 0 ? ( {t("Warehouse missing stock take section showing", { diff --git a/src/components/StockTakeManagement/PickerReStockTake.tsx b/src/components/StockTakeManagement/PickerReStockTake.tsx index 00eb21db..000e255d 100644 --- a/src/components/StockTakeManagement/PickerReStockTake.tsx +++ b/src/components/StockTakeManagement/PickerReStockTake.tsx @@ -17,7 +17,7 @@ import { TextField, TablePagination, } from "@mui/material"; -import { useState, useCallback, useEffect, useRef } from "react"; +import { useState, useCallback, useEffect, useRef, useMemo } from "react"; import { useTranslation } from "react-i18next"; import { AllPickedStockTakeListReponse, @@ -49,12 +49,15 @@ interface PickerReStockTakeProps { selectedSession: AllPickedStockTakeListReponse; onBack: () => void; onSnackbar: (message: string, severity: "success" | "error" | "warning") => void; + /** History / closed round — disable all save actions */ + roundReadOnly?: boolean; } const PickerReStockTake: React.FC = ({ selectedSession, onBack, onSnackbar, + roundReadOnly = false, }) => { const { t } = useTranslation(["stockTake", "common"]); const qtyGapWarnPercent = useStockTakeQtyGapWarnPercent(); @@ -81,7 +84,8 @@ const PickerReStockTake: React.FC = ({ const currentUserId = session?.id ? parseInt(session.id) : undefined; const handleBatchTestAllRef = useRef<() => Promise>(); const batchInFlightRef = useRef(false); - const isSessionCompleted = selectedSession?.status?.toLowerCase() === "completed"; + const isSessionCompleted = + roundReadOnly || selectedSession?.status?.toLowerCase() === "completed"; const handleChangePage = useCallback((event: unknown, newPage: number) => { setPage(newPage); @@ -204,7 +208,7 @@ const PickerReStockTake: React.FC = ({ }); }; const handleSaveStockTake = useCallback(async (detail: InventoryLotDetailResponse) => { - if (!selectedSession || !currentUserId) { + if (!selectedSession || !currentUserId || isSessionCompleted) { return; } @@ -319,7 +323,7 @@ const PickerReStockTake: React.FC = ({ }, [selectedSession, recordInputs, t, currentUserId, onSnackbar, page, pageSize, loadDetails, qtyGapWarnPercent]); const isSubmitDisabled = useCallback((detail: InventoryLotDetailResponse): boolean => { - if (selectedSession?.status?.toLowerCase() === "completed") { + if (roundReadOnly || selectedSession?.status?.toLowerCase() === "completed") { return true; } const recordStatus = detail.stockTakeRecordStatus?.toLowerCase(); @@ -327,10 +331,10 @@ const PickerReStockTake: React.FC = ({ return true; } return false; - }, [selectedSession?.status]); + }, [roundReadOnly, selectedSession?.status]); const handleBatchTestAutoFill = useCallback(async () => { - if (!selectedSession || !currentUserId || batchInFlightRef.current) { + if (!selectedSession || !currentUserId || batchInFlightRef.current || isSessionCompleted) { return; } @@ -368,7 +372,7 @@ const PickerReStockTake: React.FC = ({ }, [selectedSession, t, currentUserId, onSnackbar, page, pageSize, loadDetails]); const handleBatchSaveInputted = useCallback(async () => { - if (!selectedSession || !currentUserId || batchInFlightRef.current) return; + if (!selectedSession || !currentUserId || batchInFlightRef.current || isSessionCompleted) return; const built = buildPickerBatchSaveRequests( inventoryLotDetails, @@ -478,6 +482,31 @@ const PickerReStockTake: React.FC = ({ }; }, []); + const locationGroups = useMemo(() => { + const groups: { key: string; label: string; items: InventoryLotDetailResponse[] }[] = []; + const indexByKey = new Map(); + const itemMode = selectedSession.listMode === "item"; + for (const detail of inventoryLotDetails) { + const area = detail.warehouseArea?.trim() || ""; + const slot = detail.warehouseSlot?.trim() || ""; + const code = detail.warehouseCode?.trim() || ""; + const key = itemMode ? `${code}|${area}|${slot}` : `${area}|${slot}`; + const label = itemMode + ? [code, [area, slot].filter(Boolean).join(" ")].filter(Boolean).join(" · ") || "-" + : area || slot + ? `${area}${slot}` + : code || "-"; + let idx = indexByKey.get(key); + if (idx == null) { + idx = groups.length; + indexByKey.set(key, idx); + groups.push({ key, label, items: [] }); + } + groups[idx].items.push(detail); + } + return groups; + }, [inventoryLotDetails, selectedSession.listMode]); + const uniqueWarehouses = Array.from( new Set( inventoryLotDetails @@ -523,9 +552,6 @@ const PickerReStockTake: React.FC = ({ - - {selectedSession.listMode === "item" ? t("Warehouse code") : t("Warehouse Location")} - {t("Item-lotNo-ExpiryDate")} {t("UOM")} {t("Stock Take Qty(include Bad Qty)= Available Qty")} @@ -537,14 +563,30 @@ const PickerReStockTake: React.FC = ({ {inventoryLotDetails.length === 0 ? ( - + {t("No data")} ) : ( - inventoryLotDetails.map((detail) => { + locationGroups.flatMap((group) => [ + + + + {t("Warehouse Location")}: {group.label} + + + , + ...group.items.map((detail) => { const submitDisabled = isSubmitDisabled(detail); const isFirstSubmit = detail.firstStockTakeQty == null; const isSecondSubmit = @@ -554,23 +596,7 @@ const PickerReStockTake: React.FC = ({ return ( - - {selectedSession.listMode === "item" ? ( - - {detail.warehouseCode || "-"} - - {[detail.warehouseArea, detail.warehouseSlot].filter((v) => v?.trim()).join(" ") || "-"} - - - ) : ( - <> - {detail.warehouseArea || "-"} - {detail.warehouseSlot || "-"} - - )} - = ({ > {detail.itemCode || "-"} {detail.itemName || "-"} - {detail.lotNo || "-"} - {detail.expiryDate ? dayjs(detail.expiryDate).format(OUTPUT_DATE_FORMAT) : "-"} + + {detail.lotNo || "-"} + + + {detail.expiryDate ? dayjs(detail.expiryDate).format(OUTPUT_DATE_FORMAT) : "-"} + {detail.uom || "-"} @@ -810,7 +846,8 @@ const PickerReStockTake: React.FC = ({ ); - }) + }), + ]) )}
diff --git a/src/components/StockTakeManagement/PickerStockTake.tsx b/src/components/StockTakeManagement/PickerStockTake.tsx index 06300ec4..19a85397 100644 --- a/src/components/StockTakeManagement/PickerStockTake.tsx +++ b/src/components/StockTakeManagement/PickerStockTake.tsx @@ -22,7 +22,7 @@ import { InputLabel, } from "@mui/material"; import { SelectChangeEvent } from "@mui/material/Select"; -import { useState, useCallback, useEffect, useRef } from "react"; +import { useState, useCallback, useEffect, useRef, useMemo } from "react"; import { useTranslation } from "react-i18next"; import { AllPickedStockTakeListReponse, @@ -67,12 +67,15 @@ interface PickerStockTakeProps { selectedSession: AllPickedStockTakeListReponse; onBack: () => void; onSnackbar: (message: string, severity: "success" | "error" | "warning") => void; + /** History / closed round — disable all save actions */ + roundReadOnly?: boolean; } const PickerStockTake: React.FC = ({ selectedSession, onBack, onSnackbar, + roundReadOnly = false, }) => { const { t } = useTranslation(["stockTake", "common"]); const qtyGapWarnPercent = useStockTakeQtyGapWarnPercent(); @@ -107,7 +110,8 @@ const PickerStockTake: React.FC = ({ const handleBatchTestAllRef = useRef<() => Promise>(); const batchInFlightRef = useRef(false); const saveInFlightRef = useRef(false); - const isSessionCompleted = selectedSession?.status?.toLowerCase() === "completed"; + const isSessionCompleted = + roundReadOnly || selectedSession?.status?.toLowerCase() === "completed"; const handleChangePage = useCallback((event: unknown, newPage: number) => { setPage(newPage); }, []); @@ -215,6 +219,7 @@ const PickerStockTake: React.FC = ({ }; const canReviseFirstCount = useCallback((detail: InventoryLotDetailResponse): boolean => { + if (roundReadOnly) return false; const sessionStatus = selectedSession?.status?.toLowerCase() ?? ""; if (sessionStatus === "completed") { return false; @@ -236,11 +241,11 @@ const PickerStockTake: React.FC = ({ return false; } return true; - }, [selectedSession?.status]); + }, [roundReadOnly, selectedSession?.status]); const handleSaveStockTake = useCallback( async (detail: InventoryLotDetailResponse) => { - if (!selectedSession || !currentUserId || saveInFlightRef.current) { + if (!selectedSession || !currentUserId || saveInFlightRef.current || isSessionCompleted) { return; } @@ -361,6 +366,7 @@ const PickerStockTake: React.FC = ({ remark, t, currentUserId, + isSessionCompleted, onSnackbar, qtyGapWarnPercent, canReviseFirstCount, @@ -369,7 +375,7 @@ const PickerStockTake: React.FC = ({ /** 测试快捷键:按账面自动建记录(非用户输入批量) */ const handleBatchTestAutoFill = useCallback(async () => { - if (!selectedSession || !currentUserId || batchInFlightRef.current) { + if (!selectedSession || !currentUserId || batchInFlightRef.current || isSessionCompleted) { return; } @@ -502,7 +508,7 @@ const PickerStockTake: React.FC = ({ }; const isSubmitDisabled = useCallback((detail: InventoryLotDetailResponse): boolean => { - if (selectedSession?.status?.toLowerCase() === "completed") { + if (roundReadOnly || selectedSession?.status?.toLowerCase() === "completed") { return true; } const recordStatus = detail.stockTakeRecordStatus?.toLowerCase(); @@ -510,12 +516,12 @@ const PickerStockTake: React.FC = ({ return true; } return false; - }, [selectedSession?.status]); + }, [roundReadOnly, selectedSession?.status]); editableQtyIdsRef.current = inventoryLotDetails .filter((detail) => !isSubmitDisabled(detail) && (detail.firstStockTakeQty == null || detail.secondStockTakeQty == null)) .map((detail) => detail.id); const handleBatchSaveInputted = useCallback(async () => { - if (!selectedSession || !currentUserId || batchInFlightRef.current) return; + if (!selectedSession || !currentUserId || batchInFlightRef.current || isSessionCompleted) return; const built = buildPickerBatchSaveRequests( inventoryLotDetails, @@ -564,6 +570,7 @@ const PickerStockTake: React.FC = ({ }, [ selectedSession, currentUserId, + isSessionCompleted, inventoryLotDetails, recordInputs, isSubmitDisabled, @@ -573,6 +580,31 @@ const PickerStockTake: React.FC = ({ pageSize, loadDetails, ]); + const locationGroups = useMemo(() => { + const groups: { key: string; label: string; items: InventoryLotDetailResponse[] }[] = []; + const indexByKey = new Map(); + const itemMode = selectedSession.listMode === "item"; + for (const detail of inventoryLotDetails) { + const area = detail.warehouseArea?.trim() || ""; + const slot = detail.warehouseSlot?.trim() || ""; + const code = detail.warehouseCode?.trim() || ""; + const key = itemMode ? `${code}|${area}|${slot}` : `${area}|${slot}`; + const label = itemMode + ? [code, [area, slot].filter(Boolean).join(" ")].filter(Boolean).join(" · ") || "-" + : area || slot + ? `${area}${slot}` + : code || "-"; + let idx = indexByKey.get(key); + if (idx == null) { + idx = groups.length; + indexByKey.set(key, idx); + groups.push({ key, label, items: [] }); + } + groups[idx].items.push(detail); + } + return groups; + }, [inventoryLotDetails, selectedSession.listMode]); + const uniqueWarehouses = Array.from( new Set( inventoryLotDetails @@ -651,9 +683,6 @@ const PickerStockTake: React.FC = ({ - - {selectedSession.listMode === "item" ? t("Warehouse code") : t("Warehouse Location")} - {t("Item-lotNo-ExpiryDate")} {t("UOM")} {t("Stock Take Qty(include Bad Qty)= Available Qty")} @@ -666,18 +695,34 @@ const PickerStockTake: React.FC = ({ {inventoryLotDetails.length === 0 ? ( - + {t("No data")} ) : ( - inventoryLotDetails.map((detail) => { + locationGroups.flatMap((group) => [ + + + + {t("Warehouse Location")}: {group.label} + + + , + ...group.items.map((detail) => { const submitDisabled = isSubmitDisabled(detail); const revisingFirst = canReviseFirstCount(detail); const editingFirst = revisingFirst && !!editingFirstIds[detail.id]; - const sessionOpen = selectedSession?.status?.toLowerCase() !== "completed"; + const sessionOpen = !isSessionCompleted; const changeUsedUp = sessionOpen && detail.stockTakeRecordStatus?.toLowerCase() === "pass" && @@ -710,24 +755,8 @@ const PickerStockTake: React.FC = ({ ...(canEditQty ? { cursor: "pointer" } : {}), }} > - - {selectedSession.listMode === "item" ? ( - - {detail.warehouseCode || "-"} - - {[detail.warehouseArea, detail.warehouseSlot].filter((v) => v?.trim()).join(" ") || "-"} - - - ) : ( - <> - {detail.warehouseArea || "-"} - {detail.warehouseSlot || "-"} - - )} - = ({ > {detail.itemCode || "-"} {detail.itemName || "-"} - {detail.lotNo || "-"} - + + {detail.lotNo || "-"} + + {detail.expiryDate ? dayjs(detail.expiryDate).format(OUTPUT_DATE_FORMAT) : "-"} - + {detail.uom || "-"} @@ -1040,7 +1077,8 @@ const PickerStockTake: React.FC = ({ ); - }) + }), + ]) )}
diff --git a/src/components/StockTakeManagement/StockTakeRoundCardList.tsx b/src/components/StockTakeManagement/StockTakeRoundCardList.tsx index 592a36c1..fb100fda 100644 --- a/src/components/StockTakeManagement/StockTakeRoundCardList.tsx +++ b/src/components/StockTakeManagement/StockTakeRoundCardList.tsx @@ -348,9 +348,18 @@ const StockTakeRoundCardList: React.FC = ({ {roundLabel(card)} - - {floor ? `${modeLabel} · ${floor}` : modeLabel} - + + + {floor ? ( + + {floor} + + ) : null} +
{ const showingPickerDetail = Boolean(selectedSession) && viewScope === "picker" && tabValue === 0; const roundId = selectedRound?.stockTakeRoundId ?? 0; + /** History / closed round: disable picker & approver save actions */ + const roundReadOnly = Boolean(selectedRound && !selectedRound.open); const roundLabel = (card: StockTakeRoundCard) => { const name = card.stockTakeRoundName?.trim(); const title = name || `${t("Stock Take Round")} #${card.stockTakeRoundId}`; @@ -228,12 +230,14 @@ const StockTakeTab: React.FC = () => { selectedSession={selectedSession} onBack={handleBackToList} onSnackbar={handleSnackbar} + roundReadOnly={roundReadOnly} /> ) : ( ) ) : null} @@ -413,6 +417,7 @@ const StockTakeTab: React.FC = () => { mode="pending" onSnackbar={handleSnackbar} batchButtonHost={tabValue === 1 ? batchButtonHost : null} + roundReadOnly={roundReadOnly} /> ) : ( @@ -426,6 +431,7 @@ const StockTakeTab: React.FC = () => { selectedSession={approverSession} mode="approved" onSnackbar={handleSnackbar} + roundReadOnly={roundReadOnly} /> ) : !approverLoading ? ( diff --git a/src/i18n/en/stockTake.json b/src/i18n/en/stockTake.json index d31d6ac8..7345b61e 100644 --- a/src/i18n/en/stockTake.json +++ b/src/i18n/en/stockTake.json @@ -65,8 +65,8 @@ "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}})", "Control Time": "Time spent", - "Create by item": "By item", - "Create by section": "By section", + "Create by item": "Item stock-take", + "Create by section": "Location stock-take", "Cannot create while both section and item rounds are open": "Cannot create a new round while both section and item rounds are open", "Lot Origin": "Lot origin", "Create by item hint": "Only items with available lots are listed. Pick origin (PP / PF / Other) to load the list, optionally narrow by code/name, then tick lots on the right.", diff --git a/src/i18n/zh/stockTake.json b/src/i18n/zh/stockTake.json index d13a4097..6122aacb 100644 --- a/src/i18n/zh/stockTake.json +++ b/src/i18n/zh/stockTake.json @@ -146,9 +146,9 @@ "Select area first": "請先選擇倉位", "Slot": "位置", "Start Stock Take Date": "盤點日期", - "Create by item": "依貨品", - "Create by section": "依區域", - "Cannot create while both section and item rounds are open": "目前同時有開著的依區域與依貨品盤點輪,無法再建立新輪", + "Create by item": "貨品盤點", + "Create by section": "區域盤點", + "Cannot create while both section and item rounds are open": "目前同時有開著的區域盤點與貨品盤點輪,無法再建立新輪", "Lot Origin": "來源", "Create by item hint": "只列出有可用庫存的貨品。選來源(PP/PF/其他)即可列出;也可再輸入編號/名稱縮小。先勾選貨品,再於右側勾選批號/位置。", "Create by item search hint": "可只選來源列出;或再輸入編號/名稱縮小。兩個關鍵字都填時要同時符合",