Register filters, inventory category, Excel download lock, and no-data dialog. Includes extra inbound-trace (rep-004) location/PP-PF filters already staged. Co-authored-by: Cursor <[email protected]>fix負數倉
| @@ -25,6 +25,7 @@ const REPORT_ICON_MAP: Record<string, SvgIconComponent> = { | |||
| "rep-011": Inventory2OutlinedIcon, | |||
| "rep-007": MonetizationOnOutlinedIcon, | |||
| "rep-012": LayersOutlinedIcon, | |||
| "rep-021": Inventory2OutlinedIcon, | |||
| "rep-010": SearchOutlinedIcon, | |||
| "rep-004": LocalShippingOutlinedIcon, | |||
| "rep-014": LocalShippingOutlinedIcon, | |||
| @@ -170,6 +171,7 @@ function CategoryColumn({ | |||
| ); | |||
| } | |||
| /** FP-MTMS Version Checklist | Functions Ref. No. 69 | v1.0.0 | 2026-08-31 */ | |||
| export default function ReportSelectionDashboard({ | |||
| selectedReportId, | |||
| onSelectReport, | |||
| @@ -1,6 +1,6 @@ | |||
| "use client"; | |||
| import React, { useState, useMemo, useEffect } from 'react'; | |||
| import React, { useState, useMemo, useEffect, useRef } from 'react'; | |||
| import { useSession } from "next-auth/react"; | |||
| import { SessionWithTokens } from "@/config/authConfig"; | |||
| import { AUTH } from "@/authorities"; | |||
| @@ -18,8 +18,13 @@ import { | |||
| Autocomplete, | |||
| Checkbox, | |||
| FormControlLabel, | |||
| Dialog, | |||
| DialogTitle, | |||
| DialogContent, | |||
| DialogActions, | |||
| } from '@mui/material'; | |||
| import DownloadIcon from '@mui/icons-material/Download'; | |||
| import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined'; | |||
| import { REPORTS } from '@/config/reportConfig'; | |||
| import { NEXT_PUBLIC_API_URL } from '@/config/api'; | |||
| import { clientAuthFetch } from '@/app/utils/clientAuthFetch'; | |||
| @@ -46,6 +51,7 @@ interface ItemCodeWithName { | |||
| /** FP-MTMS Version Checklist | Functions Ref. No. 39 | v1.0.0 | 2026-08-03 */ | |||
| /** FP-MTMS Version Checklist | Functions Ref. No. 60 | v1.0.1 | 2026-08-11 */ | |||
| /** FP-MTMS Version Checklist | Functions Ref. No. 69 | v1.0.0 | 2026-08-31 */ | |||
| export default function ReportPage() { | |||
| const { data: session } = useSession() as { data: SessionWithTokens | null }; | |||
| const { t, reportTitle, fieldLabel, optionLabel } = useReportLabels(); | |||
| @@ -55,8 +61,10 @@ export default function ReportPage() { | |||
| const [selectedReportId, setSelectedReportId] = useState<string>(''); | |||
| const [criteria, setCriteria] = useState<Record<string, string>>({}); | |||
| const [loading, setLoading] = useState(false); | |||
| const excelInFlightRef = useRef(false); | |||
| const [dynamicOptions, setDynamicOptions] = useState<Record<string, { label: string; value: string }[]>>({}); | |||
| const [showConfirmDialog, setShowConfirmDialog] = useState(false); | |||
| const [showNoDataDialog, setShowNoDataDialog] = useState(false); | |||
| // Find the configuration for the currently selected report | |||
| const rep012RoundIds = useMemo(() => { | |||
| @@ -76,13 +84,27 @@ export default function ReportPage() { | |||
| const handleSelectReport = (reportId: string) => { | |||
| if (reportId === selectedReportId) return; | |||
| setSelectedReportId(reportId); | |||
| // rep-010: default QC item scope to all (show label, not empty select) | |||
| setCriteria(reportId === 'rep-010' ? { qcType: 'all', qcItemScope: 'all' } : {}); | |||
| if (reportId === 'rep-010') { | |||
| setCriteria({ qcType: 'all', qcItemScope: 'all' }); | |||
| } else if (reportId === 'rep-004') { | |||
| setCriteria({ storeId: 'All', poPrefix: 'All' }); | |||
| } else if (reportId === 'rep-021') { | |||
| setCriteria({ storeId: 'All', stockTakeSectionDescription: 'All', lotOrigin: 'All' }); | |||
| } else { | |||
| setCriteria({}); | |||
| } | |||
| }; | |||
| const handleFieldChange = (name: string, value: string | string[]) => { | |||
| const stringValue = Array.isArray(value) ? value.join(',') : value; | |||
| setCriteria((prev) => ({ ...prev, [name]: stringValue })); | |||
| setCriteria((prev) => { | |||
| const next = { ...prev, [name]: stringValue }; | |||
| if (currentReport?.id === 'rep-021' && name === 'warehouse') { | |||
| const m = stringValue.trim().match(/^w(\d)/i); | |||
| if (m) next.storeId = `${m[1]}F`; | |||
| } | |||
| return next; | |||
| }); | |||
| // If this is stockCategory and there's a field that depends on it, fetch dynamic options | |||
| if (name === 'stockCategory' && currentReport) { | |||
| @@ -139,8 +161,27 @@ export default function ReportPage() { | |||
| if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); | |||
| const data = await response.json(); | |||
| const options = Array.isArray(data) | |||
| ? data.map((item: any) => ({ label: item.label || item.name || item.code || String(item), value: item.value || item.code || String(item) })) | |||
| const options = Array.isArray(data) | |||
| ? field.name === 'stockTakeSectionDescription' | |||
| ? (() => { | |||
| const seen = new Set<string>(); | |||
| const mapped: { label: string; value: string }[] = [{ label: '全部', value: 'All' }]; | |||
| data.forEach((item: { stockTakeSectionDescription?: string; stockTakeSection?: string }) => { | |||
| const desc = (item.stockTakeSectionDescription || '').trim(); | |||
| if (!desc || seen.has(desc)) return; | |||
| seen.add(desc); | |||
| const section = (item.stockTakeSection || '').trim(); | |||
| mapped.push({ | |||
| label: section ? `${desc} (${section})` : desc, | |||
| value: desc, | |||
| }); | |||
| }); | |||
| return mapped; | |||
| })() | |||
| : data.map((item: any) => ({ | |||
| label: item.label || item.name || item.code || String(item), | |||
| value: item.value || item.code || String(item), | |||
| })) | |||
| : []; | |||
| setDynamicOptions((prev) => ({ ...prev, [field.name]: options })); | |||
| @@ -303,6 +344,8 @@ export default function ReportPage() { | |||
| const executeExcelReport = async () => { | |||
| if (!currentReport) return; | |||
| if (excelInFlightRef.current) return; | |||
| excelInFlightRef.current = true; | |||
| setLoading(true); | |||
| try { | |||
| if (currentReport.id === 'rep-014') { | |||
| @@ -342,6 +385,10 @@ export default function ReportPage() { | |||
| }); | |||
| if (response.status === 401 || response.status === 403) return; | |||
| if (response.status === 204) { | |||
| setShowNoDataDialog(true); | |||
| return; | |||
| } | |||
| if (!response.ok) { | |||
| const errorText = await response.text(); | |||
| console.error("Response error:", errorText); | |||
| @@ -378,6 +425,7 @@ export default function ReportPage() { | |||
| alert(t('generateError')); | |||
| } finally { | |||
| setLoading(false); | |||
| excelInFlightRef.current = false; | |||
| } | |||
| }; | |||
| @@ -386,7 +434,7 @@ export default function ReportPage() { | |||
| setLoading(true); | |||
| try { | |||
| const queryParams = | |||
| let queryParams = | |||
| currentReport.id === 'rep-012' | |||
| ? buildRep012QueryString() | |||
| : currentReport.id === 'rep-010' | |||
| @@ -439,6 +487,7 @@ export default function ReportPage() { | |||
| }; | |||
| return ( | |||
| <> | |||
| <Box sx={{ p: 4, maxWidth: 1280, margin: '0 auto' }}> | |||
| <Typography variant="h4" gutterBottom fontWeight="bold"> | |||
| {t('title')} | |||
| @@ -460,12 +509,13 @@ export default function ReportPage() { | |||
| <Grid container spacing={3}> | |||
| {currentReport.fields.map((field) => { | |||
| const translatedLabel = fieldLabel(currentReport.id, field); | |||
| const options = field.dynamicOptions | |||
| ? (dynamicOptions[field.name] || []) | |||
| : (field.options || []).map((opt) => ({ | |||
| ...opt, | |||
| label: optionLabel(currentReport.id, field.name, opt), | |||
| })); | |||
| const rawOptions = field.dynamicOptions | |||
| ? (dynamicOptions[field.name] || field.options || []) | |||
| : (field.options || []); | |||
| const options = rawOptions.map((opt) => ({ | |||
| ...opt, | |||
| label: optionLabel(currentReport.id, field.name, opt), | |||
| })); | |||
| const currentValue = criteria[field.name] || ''; | |||
| const valueForSelect = field.multiple | |||
| ? (currentValue ? currentValue.split(',').map(v => v.trim()).filter(v => v) : []) | |||
| @@ -602,7 +652,7 @@ export default function ReportPage() { | |||
| label={translatedLabel} | |||
| type={field.type} | |||
| placeholder={field.placeholder} | |||
| disabled={disabledByCheckedCheckbox || disabledRep012Status} | |||
| disabled={disabledByCheckedCheckbox || disabledRep012Status || !!field.disabled} | |||
| InputLabelProps={field.type === 'date' ? { shrink: true } : {}} | |||
| inputProps={ | |||
| field.type === 'date' && field.minDate === 'today' | |||
| @@ -778,5 +828,49 @@ export default function ReportPage() { | |||
| </Card> | |||
| )} | |||
| </Box> | |||
| <Dialog | |||
| open={showNoDataDialog} | |||
| onClose={() => setShowNoDataDialog(false)} | |||
| maxWidth="sm" | |||
| fullWidth | |||
| PaperProps={{ | |||
| sx: { | |||
| borderRadius: 3, | |||
| px: 1, | |||
| }, | |||
| }} | |||
| > | |||
| <DialogTitle sx={{ display: 'flex', alignItems: 'center', gap: 1.5, pt: 3, px: 4, pb: 1.5 }}> | |||
| <Box | |||
| sx={{ | |||
| width: 36, | |||
| height: 36, | |||
| borderRadius: '50%', | |||
| bgcolor: 'warning.light', | |||
| color: 'warning.dark', | |||
| display: 'flex', | |||
| alignItems: 'center', | |||
| justifyContent: 'center', | |||
| flexShrink: 0, | |||
| }} | |||
| > | |||
| <InfoOutlinedIcon fontSize="small" /> | |||
| </Box> | |||
| <Typography component="span" variant="h6" fontWeight="bold"> | |||
| {t('noDataFoundTitle')} | |||
| </Typography> | |||
| </DialogTitle> | |||
| <DialogContent sx={{ px: 4, pt: 0.5 }}> | |||
| <Typography color="text.secondary" sx={{ pl: 6.5 }}> | |||
| {t('noDataFoundHint')} | |||
| </Typography> | |||
| </DialogContent> | |||
| <DialogActions sx={{ justifyContent: 'center', px: 4, pb: 3, pt: 2 }}> | |||
| <Button variant="contained" onClick={() => setShowNoDataDialog(false)} autoFocus sx={{ minWidth: 96 }}> | |||
| {t('ok')} | |||
| </Button> | |||
| </DialogActions> | |||
| </Dialog> | |||
| </> | |||
| ); | |||
| } | |||
| @@ -9,7 +9,7 @@ export interface ReportCategoryConfig { | |||
| reportIds: string[]; | |||
| } | |||
| /** Display order and grouping for the report management dashboard. */ | |||
| /** FP-MTMS Version Checklist | Functions Ref. No. 69 | v1.0.0 | 2026-08-31 */ | |||
| export const REPORT_CATEGORIES: ReportCategoryConfig[] = [ | |||
| { | |||
| id: "inventory", | |||
| @@ -17,7 +17,7 @@ export const REPORT_CATEGORIES: ReportCategoryConfig[] = [ | |||
| headerBg: "#b8e0b8", | |||
| bodyBg: "#eef8ee", | |||
| accent: "#2e7d32", | |||
| reportIds: ["rep-011", "rep-007", "rep-012", "rep-010"], | |||
| reportIds: ["rep-011", "rep-007", "rep-012", "rep-021", "rep-010"], | |||
| }, | |||
| { | |||
| id: "inbound-outbound", | |||
| @@ -19,6 +19,8 @@ export interface ReportField { | |||
| disablesFieldsWhenChecked?: string[]; | |||
| /** For date fields: restrict picker so value cannot be before today */ | |||
| minDate?: 'today'; | |||
| /** Disable the input (e.g. date locked to today) */ | |||
| disabled?: boolean; | |||
| } | |||
| export type ReportResponseType = 'pdf' | 'excel'; | |||
| @@ -32,6 +34,7 @@ export interface ReportDefinition { | |||
| fields: ReportField[]; | |||
| } | |||
| /** FP-MTMS Version Checklist | Functions Ref. No. 69 | v1.0.0 | 2026-08-31 */ | |||
| export const REPORTS: ReportDefinition[] = [ | |||
| //{ | |||
| // id: "rep-001", | |||
| @@ -81,10 +84,37 @@ export const REPORTS: ReportDefinition[] = [ | |||
| title: "入倉追蹤報告", | |||
| apiEndpoint: `${NEXT_PUBLIC_API_URL}/report/print-stock-in-traceability`, | |||
| fields: [ | |||
| { label: "入倉日期:由 Last In Date Start", name: "lastInDateStart", type: "date", required: false }, | |||
| { label: "入倉日期:至 Last In Date End", name: "lastInDateEnd", type: "date", required: false }, | |||
| { label: "貨品編號 Item Code", name: "itemCode", type: "text", required: false}, | |||
| { | |||
| label: "樓層 Store ID", | |||
| name: "storeId", | |||
| type: "select", | |||
| required: false, | |||
| options: [ | |||
| { label: "全部", value: "All" }, | |||
| { label: "1F", value: "1F" }, | |||
| { label: "2F", value: "2F" }, | |||
| { label: "3F", value: "3F" }, | |||
| { label: "4F", value: "4F" }, | |||
| ], | |||
| }, | |||
| { label: "倉庫 Warehouse", name: "warehouse", type: "text", required: false, placeholder: "e.g. W201" }, | |||
| { label: "區域 Area", name: "area", type: "text", required: false, placeholder: "e.g. #A" }, | |||
| { label: "儲位 Slot", name: "slot", type: "text", required: false, placeholder: "e.g. 01" }, | |||
| { label: "批號 Lot No", name: "lotNo", type: "text", required: false, placeholder: "e.g. LT-202608" }, | |||
| { | |||
| label: "PP/PF 分類", | |||
| name: "poPrefix", | |||
| type: "select", | |||
| required: false, | |||
| options: [ | |||
| { label: "全部", value: "All" }, | |||
| { label: "PP", value: "PP" }, | |||
| { label: "PF", value: "PF" }, | |||
| ], | |||
| }, | |||
| ] | |||
| }, | |||
| { | |||
| @@ -169,6 +199,80 @@ export const REPORTS: ReportDefinition[] = [ | |||
| }, | |||
| ] | |||
| }, | |||
| /* Hidden for now: 庫存批次結餘報告 (rep-019) | |||
| { | |||
| id: "rep-019", | |||
| title: "庫存批次結餘報告", | |||
| apiEndpoint: `${NEXT_PUBLIC_API_URL}/report/print-stock-lot-balance`, | |||
| responseType: "excel", | |||
| fields: [ | |||
| { label: "庫存日期 Stock Date(僅今天)", name: "stockDate", type: "date", required: true, disabled: true }, | |||
| { label: "貨品編號 Item Code", name: "itemCode", type: "text", required: false }, | |||
| { label: "倉位 Warehouse Code", name: "warehouseCode", type: "text", required: false, placeholder: "e.g. W200 or 2F-W200-#A-00" }, | |||
| { | |||
| label: "樓層 Store ID", | |||
| name: "storeId", | |||
| type: "select", | |||
| required: false, | |||
| options: [ | |||
| { label: "全部", value: "All" }, | |||
| { label: "1F", value: "1F" }, | |||
| { label: "2F", value: "2F" }, | |||
| { label: "3F", value: "3F" }, | |||
| { label: "4F", value: "4F" }, | |||
| ], | |||
| }, | |||
| { label: "批號 Lot No", name: "lotNo", type: "text", required: false, placeholder: "e.g. LT-202608" }, | |||
| ], | |||
| }, | |||
| */ | |||
| { | |||
| id: "rep-021", | |||
| title: "庫存批次現況報告", | |||
| apiEndpoint: `${NEXT_PUBLIC_API_URL}/report/print-stock-lot-onhand`, | |||
| responseType: "excel", | |||
| fields: [ | |||
| { label: "貨品編號 Item Code", name: "itemCode", type: "text", required: false }, | |||
| { | |||
| label: "樓層 Store ID", | |||
| name: "storeId", | |||
| type: "select", | |||
| required: false, | |||
| options: [ | |||
| { label: "全部", value: "All" }, | |||
| { label: "1F", value: "1F" }, | |||
| { label: "2F", value: "2F" }, | |||
| { label: "3F", value: "3F" }, | |||
| { label: "4F", value: "4F" }, | |||
| ], | |||
| }, | |||
| { label: "倉庫 Warehouse", name: "warehouse", type: "text", required: false, placeholder: "e.g. W201" }, | |||
| { label: "區域 Area", name: "area", type: "text", required: false, placeholder: "e.g. #A" }, | |||
| { label: "儲位 Slot", name: "slot", type: "text", required: false, placeholder: "e.g. 02" }, | |||
| { | |||
| label: "盤點區域說明 Stock Take Section", | |||
| name: "stockTakeSectionDescription", | |||
| type: "select", | |||
| required: false, | |||
| dynamicOptions: true, | |||
| dynamicOptionsEndpoint: `${NEXT_PUBLIC_API_URL}/warehouse/stockTakeSections`, | |||
| options: [{ label: "全部", value: "All" }], | |||
| }, | |||
| { label: "批號 Lot No", name: "lotNo", type: "text", required: false, placeholder: "e.g. LT-202608" }, | |||
| { | |||
| label: "來源 Lot Origin", | |||
| name: "lotOrigin", | |||
| type: "select", | |||
| required: false, | |||
| options: [ | |||
| { label: "全部", value: "All" }, | |||
| { label: "PP", value: "PP" }, | |||
| { label: "PF", value: "PF" }, | |||
| { label: "其他", value: "other" }, | |||
| ], | |||
| }, | |||
| ], | |||
| }, | |||
| { id: "rep-011", | |||
| title: "庫存明細報告", | |||
| apiEndpoint: `${NEXT_PUBLIC_API_URL}/report/print-stock-ledger`, | |||
| @@ -178,6 +282,19 @@ export const REPORTS: ReportDefinition[] = [ | |||
| { label: "貨品編號 Item Code", name: "itemCode", type: "text", required: false}, | |||
| ] | |||
| }, | |||
| /* Hidden for now: 庫存流水帳報告 (rep-020) | |||
| { | |||
| id: "rep-020", | |||
| title: "庫存流水帳報告", | |||
| apiEndpoint: `${NEXT_PUBLIC_API_URL}/report/print-stock-lot-ledger`, | |||
| responseType: "excel", | |||
| fields: [ | |||
| { label: "期間起 Period From(永遠該月1日)", name: "lastInDateStart", type: "date", required: true }, | |||
| { label: "期間迄 Period To", name: "lastInDateEnd", type: "date", required: true }, | |||
| { label: "貨品編號 Item Code", name: "itemCode", type: "text", required: false }, | |||
| ], | |||
| }, | |||
| */ | |||
| /* | |||
| { | |||
| id: "rep-007", | |||
| @@ -236,6 +353,7 @@ export const REPORTS: ReportDefinition[] = [ | |||
| ] | |||
| }, | |||
| { id: "rep-010", | |||
| /** FP-MTMS Version Checklist | Functions Ref. No. 64 | v1.0.0 | 2026-08-11 */ | |||
| title: "庫存品質檢測報告", | |||
| @@ -11,6 +11,10 @@ | |||
| "generatingExcel": "Generating Excel...", | |||
| "generatingReport": "Generating report...", | |||
| "generateError": "An error occurred while generating the report. Please try again.", | |||
| "noDataFound": "No data found", | |||
| "noDataFoundTitle": "No Data Found / 查無資料", | |||
| "noDataFoundHint": "No inventory records match your search. Please try adjusting the filters.", | |||
| "ok": "OK", | |||
| "missingRequired": "Missing required fields:\n- {{fields}}", | |||
| "dateNotBeforeToday": "Date cannot be before today:\n- {{fields}}", | |||
| "selectOrEnterItemCode": "Select or enter item code", | |||
| @@ -43,7 +47,23 @@ | |||
| "fields": { | |||
| "lastInDateStart": "Last In Date Start", | |||
| "lastInDateEnd": "Last In Date End", | |||
| "itemCode": "Item Code" | |||
| "itemCode": "Item Code", | |||
| "storeId": "Floor", | |||
| "warehouse": "Warehouse", | |||
| "area": "Area", | |||
| "slot": "Slot", | |||
| "lotNo": "Lot No", | |||
| "poPrefix": "PP/PF" | |||
| }, | |||
| "options": { | |||
| "storeId": { | |||
| "All": "All" | |||
| }, | |||
| "poPrefix": { | |||
| "All": "All", | |||
| "PP": "PP", | |||
| "PF": "PF" | |||
| } | |||
| } | |||
| }, | |||
| "rep-008": { | |||
| @@ -213,6 +233,33 @@ | |||
| "All": "All" | |||
| } | |||
| } | |||
| }, | |||
| "rep-021": { | |||
| "title": "Stock Lot On-hand Report", | |||
| "fields": { | |||
| "itemCode": "Item Code", | |||
| "storeId": "Floor", | |||
| "warehouse": "Warehouse", | |||
| "area": "Area", | |||
| "slot": "Slot", | |||
| "stockTakeSectionDescription": "Stock Take Section", | |||
| "lotNo": "Lot No", | |||
| "lotOrigin": "Lot Origin" | |||
| }, | |||
| "options": { | |||
| "storeId": { | |||
| "All": "All" | |||
| }, | |||
| "stockTakeSectionDescription": { | |||
| "All": "All" | |||
| }, | |||
| "lotOrigin": { | |||
| "All": "All", | |||
| "PP": "PP", | |||
| "PF": "PF", | |||
| "other": "Other" | |||
| } | |||
| } | |||
| } | |||
| }, | |||
| "excel": { | |||
| @@ -11,6 +11,10 @@ | |||
| "generatingExcel": "生成 Excel...", | |||
| "generatingReport": "生成報告...", | |||
| "generateError": "產生報告時發生錯誤,請再試一次。", | |||
| "noDataFound": "查無資料", | |||
| "noDataFoundTitle": "查無資料", | |||
| "noDataFoundHint": "找不到符合您搜尋條件的庫存資料,請嘗試調整篩選條件。", | |||
| "ok": "確定", | |||
| "missingRequired": "缺少必填條件:\n- {{fields}}", | |||
| "dateNotBeforeToday": "日期不可早於今天:\n- {{fields}}", | |||
| "selectOrEnterItemCode": "選擇或輸入物料編號", | |||
| @@ -43,7 +47,23 @@ | |||
| "fields": { | |||
| "lastInDateStart": "入倉日期:由 Last In Date Start", | |||
| "lastInDateEnd": "入倉日期:至 Last In Date End", | |||
| "itemCode": "貨品編號 Item Code" | |||
| "itemCode": "貨品編號 Item Code", | |||
| "storeId": "樓層 Store ID", | |||
| "warehouse": "倉庫 Warehouse", | |||
| "area": "區域 Area", | |||
| "slot": "儲位 Slot", | |||
| "lotNo": "批號 Lot No", | |||
| "poPrefix": "PP/PF 分類" | |||
| }, | |||
| "options": { | |||
| "storeId": { | |||
| "All": "全部" | |||
| }, | |||
| "poPrefix": { | |||
| "All": "全部", | |||
| "PP": "PP", | |||
| "PF": "PF" | |||
| } | |||
| } | |||
| }, | |||
| "rep-008": { | |||
| @@ -213,6 +233,33 @@ | |||
| "All": "全部" | |||
| } | |||
| } | |||
| }, | |||
| "rep-021": { | |||
| "title": "庫存批次現況報告", | |||
| "fields": { | |||
| "itemCode": "貨品編號 Item Code", | |||
| "storeId": "樓層 Store ID", | |||
| "warehouse": "倉庫 Warehouse", | |||
| "area": "區域 Area", | |||
| "slot": "儲位 Slot", | |||
| "stockTakeSectionDescription": "盤點區域說明 Stock Take Section", | |||
| "lotNo": "批號 Lot No", | |||
| "lotOrigin": "來源 Lot Origin" | |||
| }, | |||
| "options": { | |||
| "storeId": { | |||
| "All": "全部" | |||
| }, | |||
| "stockTakeSectionDescription": { | |||
| "All": "全部" | |||
| }, | |||
| "lotOrigin": { | |||
| "All": "全部", | |||
| "PP": "PP", | |||
| "PF": "PF", | |||
| "other": "其他" | |||
| } | |||
| } | |||
| } | |||
| }, | |||
| "excel": { | |||