diff --git a/src/app/api/stockIssue/actions.ts b/src/app/api/stockIssue/actions.ts index 5b30c36c..495e9e91 100644 --- a/src/app/api/stockIssue/actions.ts +++ b/src/app/api/stockIssue/actions.ts @@ -18,8 +18,10 @@ export interface ExpiryItemResult { expiryDate: string | null; remainingQty: number; uomDesc?: string | null; - /** True when expiryDate is today or earlier. */ + /** True when expiryDate is today or earlier (and not blocked by open stock take for today-expiry). */ canHandle?: boolean; + /** When canHandle is false: FUTURE | STOCK_TAKE_OPEN */ + canHandleBlockKey?: string | null; } export interface ExpiryItemFilter { diff --git a/src/components/StockIssue/ExpiryHandleTab.tsx b/src/components/StockIssue/ExpiryHandleTab.tsx index fbb12ea2..a2bf28af 100644 --- a/src/components/StockIssue/ExpiryHandleTab.tsx +++ b/src/components/StockIssue/ExpiryHandleTab.tsx @@ -114,6 +114,13 @@ function canHandleExpiryItem(item: ExpiryItemResult): boolean { return d != null && !d.isAfter(dayjs(), "day"); } +function cannotHandleTooltipKey(item: ExpiryItemResult): string { + if (item.canHandleBlockKey === "STOCK_TAKE_OPEN") { + return "Expires today but in open stock take; cannot dispose"; + } + return "Not yet due; cannot dispose until the expiry date"; +} + /** FP-MTMS Version Checklist | Functions Ref. No. 74 | v1.0.3 | 2026-09-08 */ const ExpiryHandleTab: React.FC = () => { const BATCH_CHUNK_SIZE = 20; @@ -285,7 +292,7 @@ const ExpiryHandleTab: React.FC = () => { ); if (canHandle) return button; return ( - + {button} ); diff --git a/src/components/StockTakeManagement/ApproverStockTakeAll.tsx b/src/components/StockTakeManagement/ApproverStockTakeAll.tsx index ec60c86f..99286147 100644 --- a/src/components/StockTakeManagement/ApproverStockTakeAll.tsx +++ b/src/components/StockTakeManagement/ApproverStockTakeAll.tsx @@ -62,6 +62,7 @@ import { stockTakeHiddenOnHand, stockTakeQtyGapWarnText } from "./stockTakeQtyGa import { useStockTakeQtyGapWarnPercent } from "./useStockTakeQtyGapWarnPercent"; import { useSession } from "next-auth/react"; import { SessionWithTokens } from "@/config/authConfig"; +import { AUTH } from "@/authorities"; import dayjs from "dayjs"; import { OUTPUT_DATE_FORMAT, roundDownPercent } from "@/app/utils/formatUtil"; import { DataGrid, GridColDef, GridRenderCellParams } from '@mui/x-data-grid'; @@ -373,6 +374,9 @@ const ApproverStockTakeAll: React.FC = ({ const { t } = useTranslation(["stockTake", "common"]); const qtyGapWarnPercent = useStockTakeQtyGapWarnPercent(); const { data: session } = useSession() as { data: SessionWithTokens | null }; + const isAdmin = (session?.abilities ?? session?.user?.abilities ?? []).some( + (ability) => String(ability).trim() === AUTH.ADMIN, + ); const [inventoryLotDetails, setInventoryLotDetails] = useState([]); const [loadingDetails, setLoadingDetails] = useState(false); @@ -1575,7 +1579,7 @@ const ApproverStockTakeAll: React.FC = ({ size="small" variant="contained" onClick={() => handleSaveApproverStockTake(detail)} - disabled={saving} + disabled={!isAdmin || saving} > {t("Save")} @@ -1586,7 +1590,7 @@ const ApproverStockTakeAll: React.FC = ({ variant="outlined" color="warning" onClick={() => handleUpdateStatusToNotMatch(detail)} - disabled={updatingStatus||hasSecond} + disabled={!isAdmin || updatingStatus || hasSecond} > {t("Not Match")} @@ -1618,6 +1622,7 @@ const ApproverStockTakeAll: React.FC = ({ approverBadQty, saving, updatingStatus, + isAdmin, blockNonIntegerKeys, sanitizeIntegerInput, handleSaveApproverStockTake, @@ -1632,7 +1637,7 @@ const ApproverStockTakeAll: React.FC = ({ variant="contained" color="primary" onClick={handleOpenBatchSaveConfirm} - disabled={batchSaving} + disabled={!isAdmin || batchSaving} > {t("Batch Save All")} diff --git a/src/components/StockTakeManagement/PickerCardList.tsx b/src/components/StockTakeManagement/PickerCardList.tsx index 9f379f32..caba73f4 100644 --- a/src/components/StockTakeManagement/PickerCardList.tsx +++ b/src/components/StockTakeManagement/PickerCardList.tsx @@ -223,6 +223,8 @@ interface PickerCardListProps { locationPick: PickerLocationPick; onLocationPickChange: (next: PickerLocationPick) => void; stockTakeRoundId?: number | null; + /** 父層遞增:從明細返回時重抓區域卡片狀態 */ + refreshNonce?: number; hideSectionList?: boolean; autoOpenCreate?: boolean; onCreateDialogClose?: () => void; @@ -312,6 +314,7 @@ const PickerCardList: React.FC = ({ locationPick, onLocationPickChange, stockTakeRoundId = null, + refreshNonce = 0, hideSectionList = false, autoOpenCreate = false, onCreateDialogClose, @@ -496,7 +499,7 @@ const PickerCardList: React.FC = ({ return () => { cancelled = true; }; - }, [hideSectionList, listRefreshNonce, stockTakeRoundId]); + }, [hideSectionList, listRefreshNonce, refreshNonce, stockTakeRoundId]); const roundSectionCodes = useMemo( () => @@ -705,7 +708,7 @@ const PickerCardList: React.FC = ({ return () => { cancelled = true; }; - }, [listRefreshNonce]); + }, [listRefreshNonce, refreshNonce]); //const startIdx = page * PER_PAGE; //const paged = stockTakeSessions.slice(startIdx, startIdx + PER_PAGE); @@ -762,11 +765,21 @@ const PickerCardList: React.FC = ({ }; }, [openConfirmDialog]); - const handleGoWarehouseSettings = useCallback(() => { - setMissingSectionWarnDrawerOpen(false); - setOpenConfirmDialog(false); - router.push("/settings/warehouse"); - }, [router]); + const handleGoWarehouseSettingsForIssue = useCallback( + (row: MissingStockTakeSectionIssueItem) => { + const params = new URLSearchParams(); + if (row.storeId?.trim()) params.set("store_id", row.storeId.trim()); + if (row.warehouse?.trim()) params.set("warehouse", row.warehouse.trim()); + if (row.area?.trim()) params.set("area", row.area.trim()); + if (row.slot?.trim()) params.set("slot", row.slot.trim()); + if (row.code?.trim()) params.set("code", row.code.trim()); + setMissingSectionWarnDrawerOpen(false); + setOpenConfirmDialog(false); + const qs = params.toString(); + router.push(qs ? `/settings/warehouse?${qs}` : "/settings/warehouse"); + }, + [router], + ); const createStockTakeSummarySections = useMemo(() => { return [...createDialogSelectedSections].sort((a, b) => a.localeCompare(b)); @@ -2485,6 +2498,14 @@ const PickerCardList: React.FC = ({ {row.order} ) : null} + ))} @@ -2503,14 +2524,6 @@ const PickerCardList: React.FC = ({ - diff --git a/src/components/StockTakeManagement/StockTakeRoundCardList.tsx b/src/components/StockTakeManagement/StockTakeRoundCardList.tsx index 1ecbfe63..69409d63 100644 --- a/src/components/StockTakeManagement/StockTakeRoundCardList.tsx +++ b/src/components/StockTakeManagement/StockTakeRoundCardList.tsx @@ -16,7 +16,6 @@ import { DialogContent, DialogTitle, Grid, - LinearProgress, Stack, Typography, } from "@mui/material"; @@ -55,11 +54,6 @@ export function roundCardToSession(card: StockTakeRoundCard): AllPickedStockTake }; } -function progressPercent(done: number, total: number): number { - if (total <= 0) return 0; - return Math.min(100, Math.round((done / total) * 100)); -} - interface StockTakeRoundCardListProps { isAdmin: boolean; onEnterRound: (card: StockTakeRoundCard, rounds: StockTakeRoundCard[]) => void; @@ -277,35 +271,38 @@ const StockTakeRoundCardList: React.FC = ({ {t("Main floor")}: {card.mainFloor?.trim() || "-"} - - - + + + {isItemRound ? t("Stock taken lots") : t("Stock taken")} - - {taken}/{total} + + {taken} / {total} - - - + + {isItemRound ? t("Approved lots") : t("Approved progress")} - - {approved}/{total} + + {approved} / {total} - - + diff --git a/src/components/StockTakeManagement/StockTakeTab.tsx b/src/components/StockTakeManagement/StockTakeTab.tsx index 6bae49c4..0d376831 100644 --- a/src/components/StockTakeManagement/StockTakeTab.tsx +++ b/src/components/StockTakeManagement/StockTakeTab.tsx @@ -74,6 +74,7 @@ const StockTakeTab: React.FC = () => { const [approverLoading, setApproverLoading] = useState(false); const [pickerListPage, setPickerListPage] = useState(0); const [pickerListPageSize] = useState(6); + const [pickerListRefreshNonce, setPickerListRefreshNonce] = useState(0); const [pickerSearchFilters, setPickerSearchFilters] = useState(DEFAULT_PICKER_CARD_LIST_FILTERS); const [pickerAppliedFilters, setPickerAppliedFilters] = useState(DEFAULT_PICKER_CARD_LIST_FILTERS); const [pickerLocationPick, setPickerLocationPick] = useState(EMPTY_PICKER_LOCATION); @@ -110,6 +111,8 @@ const StockTakeTab: React.FC = () => { const handleBackToList = useCallback(() => { setSelectedSession(null); setViewMode("details"); + // 明細/重新盤點可能已改單頭狀態;列表是 display:none 未卸載,需重抓 + setPickerListRefreshNonce((n) => n + 1); }, []); const handleSnackbar = useCallback((message: string, severity: "success" | "error" | "warning") => { @@ -385,6 +388,7 @@ const StockTakeTab: React.FC = () => { locationPick={pickerLocationPick} onLocationPickChange={setPickerLocationPick} stockTakeRoundId={roundId} + refreshNonce={pickerListRefreshNonce} onCardClick={(session) => { setViewScope("picker"); handleCardClick(session); diff --git a/src/components/WarehouseHandle/WarehouseHandle.tsx b/src/components/WarehouseHandle/WarehouseHandle.tsx index fbac9d93..026bd010 100644 --- a/src/components/WarehouseHandle/WarehouseHandle.tsx +++ b/src/components/WarehouseHandle/WarehouseHandle.tsx @@ -1,11 +1,11 @@ "use client"; -import { useCallback, useMemo, useState } from "react"; +import { useCallback, useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import SearchResults, { Column } from "../SearchResults/SearchResults"; import DeleteIcon from "@mui/icons-material/Delete"; import EditIcon from "@mui/icons-material/Edit"; -import { useRouter } from "next/navigation"; +import { useRouter, useSearchParams } from "next/navigation"; import { deleteDialog, successDialog } from "../Swal/CustomAlerts"; import { WarehouseResult } from "@/app/api/warehouse"; import { deleteWarehouse, editWarehouse } from "@/app/api/warehouse/actions"; @@ -31,8 +31,99 @@ interface Props { type SearchQuery = Partial>; type SearchParamNames = keyof SearchQuery; +type WarehouseSearchInputs = { + store_id: string; + warehouse: string; + area: string; + slot: string; + stockTakeSection: string; + stockTakeSectionDescription: string; +}; + +const EMPTY_WAREHOUSE_SEARCH: WarehouseSearchInputs = { + store_id: "", + warehouse: "", + area: "", + slot: "", + stockTakeSection: "", + stockTakeSectionDescription: "", +}; + +function parseWarehouseSearchFromParams(params: URLSearchParams): WarehouseSearchInputs { + const next: WarehouseSearchInputs = { + store_id: (params.get("store_id") || "").trim(), + warehouse: (params.get("warehouse") || "").trim(), + area: (params.get("area") || "").trim(), + slot: (params.get("slot") || "").trim(), + stockTakeSection: (params.get("stockTakeSection") || "").trim(), + stockTakeSectionDescription: (params.get("stockTakeSectionDescription") || "").trim(), + }; + const code = (params.get("code") || "").trim(); + if (code && !next.store_id && !next.warehouse && !next.area && !next.slot) { + const parts = code.split("-"); + if (parts.length >= 4) { + next.store_id = parts[0] || ""; + next.warehouse = parts[1] || ""; + next.area = parts[2] || ""; + next.slot = parts.slice(3).join("-") || ""; + } + } + return next; +} + +function filterWarehousesBySearch( + list: WarehouseResult[], + inputs: WarehouseSearchInputs, +): WarehouseResult[] { + const storeId = inputs.store_id?.trim() || ""; + const warehouse = inputs.warehouse?.trim() || ""; + const area = inputs.area?.trim() || ""; + const slot = inputs.slot?.trim() || ""; + const stockTakeSection = inputs.stockTakeSection?.trim() || ""; + const stockTakeSectionDescription = inputs.stockTakeSectionDescription?.trim() || ""; + if (!storeId && !warehouse && !area && !slot && !stockTakeSection && !stockTakeSectionDescription) { + return list; + } + return list.filter((warehouseItem) => { + if (stockTakeSection) { + const itemStockTakeSection = String(warehouseItem.stockTakeSection || "").toLowerCase(); + if (!itemStockTakeSection.includes(stockTakeSection.toLowerCase())) return false; + } + if (stockTakeSectionDescription) { + const itemStockTakeSectionDescription = String( + warehouseItem.stockTakeSectionDescription || "", + ).toLowerCase(); + if (!itemStockTakeSectionDescription.includes(stockTakeSectionDescription.toLowerCase())) { + return false; + } + } + if (storeId || warehouse || area || slot) { + if (!warehouseItem.code) return false; + const codeValue = String(warehouseItem.code).toLowerCase(); + const codeParts = codeValue.split("-"); + if (codeParts.length >= 4) { + const slotPart = codeParts.slice(3).join("-"); + return ( + (!storeId || codeParts[0].includes(storeId.toLowerCase())) && + (!warehouse || codeParts[1].includes(warehouse.toLowerCase())) && + (!area || codeParts[2].includes(area.toLowerCase())) && + (!slot || slotPart.includes(slot.toLowerCase())) + ); + } + return ( + (!storeId || codeValue.includes(storeId.toLowerCase())) && + (!warehouse || codeValue.includes(warehouse.toLowerCase())) && + (!area || codeValue.includes(area.toLowerCase())) && + (!slot || codeValue.includes(slot.toLowerCase())) + ); + } + return true; + }); +} + const WarehouseHandle: React.FC = ({ warehouses }) => { const { t } = useTranslation(["warehouse", "common"]); + const searchParams = useSearchParams(); const [filteredWarehouse, setFilteredWarehouse] = useState(warehouses); const [pagingController, setPagingController] = useState({ pageNum: 1, @@ -51,14 +142,22 @@ const WarehouseHandle: React.FC = ({ warehouses }) => { const [isSavingEdit, setIsSavingEdit] = useState(false); const [editError, setEditError] = useState(""); - const [searchInputs, setSearchInputs] = useState({ - store_id: "", - warehouse: "", - area: "", - slot: "", - stockTakeSection: "", - stockTakeSectionDescription: "", - }); + const [searchInputs, setSearchInputs] = useState(EMPTY_WAREHOUSE_SEARCH); + + useEffect(() => { + const fromUrl = parseWarehouseSearchFromParams(searchParams); + const hasFilter = + Boolean(fromUrl.store_id) || + Boolean(fromUrl.warehouse) || + Boolean(fromUrl.area) || + Boolean(fromUrl.slot) || + Boolean(fromUrl.stockTakeSection) || + Boolean(fromUrl.stockTakeSectionDescription); + if (!hasFilter) return; + setSearchInputs(fromUrl); + setFilteredWarehouse(filterWarehousesBySearch(warehouses, fromUrl)); + setPagingController((prev) => ({ pageNum: 1, pageSize: prev.pageSize })); + }, [searchParams, warehouses]); const onDeleteClick = useCallback((warehouse: WarehouseResult) => { deleteDialog(async () => { @@ -74,17 +173,13 @@ const WarehouseHandle: React.FC = ({ warehouses }) => { }, [t, router]); const handleReset = useCallback(() => { - setSearchInputs({ - store_id: "", - warehouse: "", - area: "", - slot: "", - stockTakeSection: "", - stockTakeSectionDescription: "", - }); + setSearchInputs(EMPTY_WAREHOUSE_SEARCH); setFilteredWarehouse(warehouses); setPagingController({ pageNum: 1, pageSize: pagingController.pageSize }); - }, [warehouses, pagingController.pageSize]); + if (searchParams.toString()) { + router.replace("/settings/warehouse"); + } + }, [warehouses, pagingController.pageSize, searchParams, router]); const onEditClick = useCallback((warehouse: WarehouseResult) => { setEditingWarehouse(warehouse); @@ -154,114 +249,11 @@ const WarehouseHandle: React.FC = ({ warehouses }) => { const handleSearch = useCallback(() => { setIsSearching(true); try { - let results: WarehouseResult[] = warehouses; - - const storeId = searchInputs.store_id?.trim() || ""; - const warehouse = searchInputs.warehouse?.trim() || ""; - const area = searchInputs.area?.trim() || ""; - const slot = searchInputs.slot?.trim() || ""; - const stockTakeSection = searchInputs.stockTakeSection?.trim() || ""; - const stockTakeSectionDescription = searchInputs.stockTakeSectionDescription?.trim() || ""; - if (storeId || warehouse || area || slot || stockTakeSection || stockTakeSectionDescription) { - results = warehouses.filter((warehouseItem) => { - if (stockTakeSection) { - const itemStockTakeSection = String(warehouseItem.stockTakeSection || "").toLowerCase(); - if (!itemStockTakeSection.includes(stockTakeSection.toLowerCase())) { - return false; - } - } - if (stockTakeSectionDescription) { - const itemStockTakeSectionDescription = String(warehouseItem.stockTakeSectionDescription || "").toLowerCase(); - if (!itemStockTakeSectionDescription.includes(stockTakeSectionDescription.toLowerCase())) { - return false; - } - } - if (storeId || warehouse || area || slot) { - if (!warehouseItem.code) { - return false; - } - - const codeValue = String(warehouseItem.code).toLowerCase(); - - const codeParts = codeValue.split("-"); - - if (codeParts.length >= 4) { - const codeStoreId = codeParts[0] || ""; - const codeWarehouse = codeParts[1] || ""; - const codeArea = codeParts[2] || ""; - const codeSlot = codeParts[3] || ""; - - const storeIdMatch = !storeId || codeStoreId.includes(storeId.toLowerCase()); - const warehouseMatch = !warehouse || codeWarehouse.includes(warehouse.toLowerCase()); - const areaMatch = !area || codeArea.includes(area.toLowerCase()); - const slotMatch = !slot || codeSlot.includes(slot.toLowerCase()); - - return storeIdMatch && warehouseMatch && areaMatch && slotMatch; - } - - const storeIdMatch = !storeId || codeValue.includes(storeId.toLowerCase()); - const warehouseMatch = !warehouse || codeValue.includes(warehouse.toLowerCase()); - const areaMatch = !area || codeValue.includes(area.toLowerCase()); - const slotMatch = !slot || codeValue.includes(slot.toLowerCase()); - - return storeIdMatch && warehouseMatch && areaMatch && slotMatch; - } - - return true; - }); - } else { - results = warehouses; - } - - setFilteredWarehouse(results); + setFilteredWarehouse(filterWarehousesBySearch(warehouses, searchInputs)); setPagingController({ pageNum: 1, pageSize: pagingController.pageSize }); } catch (error) { console.error("Error searching warehouses:", error); - const storeId = searchInputs.store_id?.trim().toLowerCase() || ""; - const warehouse = searchInputs.warehouse?.trim().toLowerCase() || ""; - const area = searchInputs.area?.trim().toLowerCase() || ""; - const slot = searchInputs.slot?.trim().toLowerCase() || ""; - const stockTakeSection = searchInputs.stockTakeSection?.trim().toLowerCase() || ""; - const stockTakeSectionDescription = searchInputs.stockTakeSectionDescription?.trim().toLowerCase() || ""; - setFilteredWarehouse( - warehouses.filter((warehouseItem) => { - if (stockTakeSection) { - const itemStockTakeSection = String(warehouseItem.stockTakeSection || "").toLowerCase(); - if (!itemStockTakeSection.includes(stockTakeSection)) { - return false; - } - } - if (stockTakeSectionDescription) { - const itemStockTakeSectionDescription = String(warehouseItem.stockTakeSectionDescription || "").toLowerCase(); - if (!itemStockTakeSectionDescription.includes(stockTakeSectionDescription)) { - return false; - } - } - if (storeId || warehouse || area || slot) { - if (!warehouseItem.code) { - return false; - } - - const codeValue = String(warehouseItem.code).toLowerCase(); - const codeParts = codeValue.split("-"); - - if (codeParts.length >= 4) { - const storeIdMatch = !storeId || codeParts[0].includes(storeId); - const warehouseMatch = !warehouse || codeParts[1].includes(warehouse); - const areaMatch = !area || codeParts[2].includes(area); - const slotMatch = !slot || codeParts[3].includes(slot); - return storeIdMatch && warehouseMatch && areaMatch && slotMatch; - } - - return (!storeId || codeValue.includes(storeId)) && - (!warehouse || codeValue.includes(warehouse)) && - (!area || codeValue.includes(area)) && - (!slot || codeValue.includes(slot)); - } - - return true; - }) - ); + setFilteredWarehouse(filterWarehousesBySearch(warehouses, searchInputs)); } finally { setIsSearching(false); } diff --git a/src/i18n/en/stockIssue.json b/src/i18n/en/stockIssue.json index 45590bce..caca3b47 100644 --- a/src/i18n/en/stockIssue.json +++ b/src/i18n/en/stockIssue.json @@ -39,6 +39,7 @@ "Failed to submit": "Failed to submit", "Failed to submit expiry item": "Failed to submit expiry item", "Not yet due; cannot dispose until the expiry date": "Not yet due; cannot dispose until the expiry date", + "Expires today but in open stock take; cannot dispose": "Expires today but this lot is in an open stock take; dispose after the round finishes or the lot leaves the round", "Handled Date": "Handled Date", "Handled Start Date": "Handled Start Date", "Handled End Date": "Handled End Date", diff --git a/src/i18n/en/stockTake.json b/src/i18n/en/stockTake.json index 02b1d9da..0910e7b8 100644 --- a/src/i18n/en/stockTake.json +++ b/src/i18n/en/stockTake.json @@ -181,9 +181,9 @@ "Warehouse": "Warehouse", "Warehouse code": "Warehouse code", "Warehouse Location": "Warehouse Location", - "Warehouse missing stock take section drawer hint": "These locations have no stock take section (ST-xxx) and cannot be included in stock take. Fix in warehouse settings.", + "Warehouse missing stock take section drawer hint": "These locations have no stock take section (ST-xxx) and cannot be included in stock take. Use “Go to this warehouse” on each row to open settings with that location searched.", "Warehouse missing stock take section empty": "No warehouses missing stock take section", - "Warehouse missing stock take section go settings": "Go to warehouse settings", + "Warehouse missing stock take section go settings": "Go to this warehouse", "Warehouse missing stock take section showing": "Showing {{shown}} of {{count}}", "Warehouse missing stock take section tooltip has": "{{count}} warehouse location(s) missing stock take section — click to view", "Warehouse missing stock take section tooltip none": "All warehouses have a stock take section", diff --git a/src/i18n/zh/stockIssue.json b/src/i18n/zh/stockIssue.json index f9bec61e..d6aba31e 100644 --- a/src/i18n/zh/stockIssue.json +++ b/src/i18n/zh/stockIssue.json @@ -39,6 +39,7 @@ "Failed to submit": "提交失敗", "Failed to submit expiry item": "提交過期品失敗", "Not yet due; cannot dispose until the expiry date": "尚未到期,到期日當日才可處置", + "Expires today but in open stock take; cannot dispose": "今日到期但盤點進行中,請待盤點結束或該批退出盤點後再處置", "Handled Date": "處理日期", "Handled Start Date": "處理日期(開始)", "Handled End Date": "處理日期(結束)", diff --git a/src/i18n/zh/stockTake.json b/src/i18n/zh/stockTake.json index 068f0379..07bf0cfe 100644 --- a/src/i18n/zh/stockTake.json +++ b/src/i18n/zh/stockTake.json @@ -162,10 +162,10 @@ "No sections match search": "沒有符合搜索條件的區域", "Confirm create stock take": "確認建立盤點", "Not filled": "(未填寫)", - "Warehouse missing stock take section drawer hint": "以下倉庫位置尚未設定盤點區域(ST-xxx),無法納入盤點區域選擇。請至倉庫設定補上。", + "Warehouse missing stock take section drawer hint": "以下倉庫位置尚未設定盤點區域(ST-xxx),無法納入盤點區域選擇。請點各筆「前往此倉庫設定」補上。", "Warehouse missing stock take section showing": "顯示前 {{shown}} 筆,共 {{count}} 筆", "Warehouse missing stock take section empty": "沒有未設定盤點區域的倉庫", - "Warehouse missing stock take section go settings": "前往倉庫設定", + "Warehouse missing stock take section go settings": "前往此倉庫設定", "Available QTY cannot be negative": "可用數量不能為負數", "Stock take record saved successfully": "盤點記錄保存成功", "Batch save completed: {{success}} success, {{errors}} errors": "批量保存完成:{{success}} 成功,{{errors}} 失敗",