| @@ -1210,12 +1210,15 @@ export const fetchMaterialPickStatus = cache(async (): Promise<MaterialPickStatu | |||
| ); | |||
| }) | |||
| export interface ProcessStatusInfo { | |||
| processName?: string | null; | |||
| equipmentDescription?: string | null; | |||
| equipmentDetailName?: string | null; | |||
| startTime?: string | null; | |||
| endTime?: string | null; | |||
| equipmentCode?: string | null; | |||
| isRequired: boolean; | |||
| } | |||
| export interface JobProcessStatusResponse { | |||
| jobOrderId: number; | |||
| jobOrderCode: string; | |||
| @@ -1229,15 +1232,17 @@ export interface JobProcessStatusResponse { | |||
| processes: ProcessStatusInfo[]; | |||
| } | |||
| // 添加API调用函数 | |||
| export const fetchJobProcessStatus = cache(async () => { | |||
| return serverFetchJson<JobProcessStatusResponse[]>( | |||
| `${BASE_API_URL}/product-process/Demo/JobProcessStatus`, | |||
| { | |||
| method: "GET", | |||
| next: { tags: ["jobProcessStatus"] }, | |||
| } | |||
| ); | |||
| export const fetchJobProcessStatus = cache(async (date?: string) => { | |||
| const params = new URLSearchParams(); | |||
| if (date) params.set("date", date); // yyyy-MM-dd | |||
| const qs = params.toString(); | |||
| const url = `${BASE_API_URL}/product-process/Demo/JobProcessStatus${qs ? `?${qs}` : ""}`; | |||
| return serverFetchJson<JobProcessStatusResponse[]>(url, { | |||
| method: "GET", | |||
| next: { tags: ["jobProcessStatus"] }, | |||
| }); | |||
| }); | |||
| export const deleteProductProcessLine = async (lineId: number) => { | |||
| return serverFetchJson<any>( | |||
| @@ -12,7 +12,7 @@ import { RecordsRes } from "../utils"; | |||
| import { Uom } from "../settings/uom"; | |||
| import { convertObjToURLSearchParams } from "@/app/utils/commonUtil"; | |||
| // import { BASE_API_URL } from "@/config/api"; | |||
| import { Result } from "../settings/item"; | |||
| export interface PostStockInLineResponse<T> { | |||
| id: number | null; | |||
| name: string; | |||
| @@ -242,3 +242,9 @@ export const printQrCodeForSil = cache(async(data: PrintQrCodeForSilRequest) => | |||
| }, | |||
| ) | |||
| }) | |||
| // 添加服务器端 action 用于从客户端组件获取 item 信息 | |||
| export const fetchItemForPutAway = cache(async (id: number): Promise<Result> => { | |||
| return serverFetchJson<Result>(`${BASE_API_URL}/items/details/${id}`, { | |||
| next: { tags: ["items"] }, | |||
| }); | |||
| }); | |||
| @@ -28,10 +28,10 @@ import { fetchStockInLineInfo } from "@/app/api/po/actions"; // Add this import | |||
| import PickExecutionForm from "./PickExecutionForm"; | |||
| interface LotPickData { | |||
| id: number; | |||
| lotId: number; | |||
| lotNo: string; | |||
| lotId: number ; | |||
| lotNo: string ; | |||
| expiryDate: string; | |||
| location: string; | |||
| location: string| null; | |||
| stockUnit: string; | |||
| inQty: number; | |||
| availableQty: number; | |||
| @@ -45,6 +45,7 @@ interface LotPickData { | |||
| stockOutLineId?: number; | |||
| stockOutLineStatus?: string; | |||
| stockOutLineQty?: number; | |||
| noLot?: boolean; | |||
| } | |||
| interface PickQtyData { | |||
| @@ -334,7 +334,7 @@ const PickExecution: React.FC<Props> = ({ filterArgs }) => { | |||
| const handlePickQtyChange = useCallback((lineId: number, lotId: number, value: number | string) => { | |||
| console.log("Changing pick qty:", { lineId, lotId, value }); | |||
| const numericValue = typeof value === 'string' ? (value === '' ? 0 : parseInt(value, 10)) : value; | |||
| const numericValue = typeof value === 'string' ? (value === '' ? 0 : parseFloat(value) || 0) : value; | |||
| setPickQtyData(prev => { | |||
| const newData = { | |||
| @@ -74,7 +74,7 @@ const SearchResultsTable: React.FC<SearchResultsTableProps> = ({ | |||
| const handleQtyChange = useCallback((itemId: number, value: string) => { | |||
| // Only allow numbers | |||
| if (value === "" || /^\d+$/.test(value)) { | |||
| if (value === "" || /^\d*\.?\d+$/.test(value)) { | |||
| const numValue = value === "" ? null : Number(value); | |||
| onQtyChange(itemId, numValue); | |||
| } | |||
| @@ -20,7 +20,7 @@ import { useTranslation } from 'react-i18next'; | |||
| import dayjs from 'dayjs'; | |||
| import { fetchJobProcessStatus, JobProcessStatusResponse } from '@/app/api/jo/actions'; | |||
| import { arrayToDayjs } from '@/app/utils/formatUtil'; | |||
| import { FormControl, Select, MenuItem } from "@mui/material"; | |||
| const REFRESH_INTERVAL = 10 * 60 * 1000; // 10 minutes | |||
| const JobProcessStatus: React.FC = () => { | |||
| @@ -29,6 +29,7 @@ const JobProcessStatus: React.FC = () => { | |||
| const [loading, setLoading] = useState<boolean>(true); | |||
| const refreshCountRef = useRef<number>(0); | |||
| const [currentTime, setCurrentTime] = useState(dayjs()); | |||
| const [selectedDate, setSelectedDate] = useState(dayjs().format("YYYY-MM-DD")); | |||
| // Update current time every second for countdown | |||
| useEffect(() => { | |||
| @@ -41,21 +42,8 @@ const JobProcessStatus: React.FC = () => { | |||
| const loadData = useCallback(async () => { | |||
| setLoading(true); | |||
| try { | |||
| const result = await fetchJobProcessStatus(); | |||
| // On second refresh, filter out completed jobs | |||
| if (refreshCountRef.current >= 1) { | |||
| const filtered = result.filter(item => { | |||
| // Check if all required processes are completed | |||
| const allCompleted = item.processes | |||
| .filter(p => p.isRequired) | |||
| .every(p => p.endTime != null); | |||
| return !allCompleted; | |||
| }); | |||
| setData(filtered); | |||
| } else { | |||
| setData(result); | |||
| } | |||
| const result = await fetchJobProcessStatus(selectedDate); | |||
| setData(result); | |||
| refreshCountRef.current += 1; | |||
| } catch (error) { | |||
| console.error('Error fetching job process status:', error); | |||
| @@ -63,7 +51,7 @@ const JobProcessStatus: React.FC = () => { | |||
| } finally { | |||
| setLoading(false); | |||
| } | |||
| }, []); | |||
| }, [selectedDate]); | |||
| useEffect(() => { | |||
| loadData(); | |||
| @@ -183,12 +171,22 @@ const JobProcessStatus: React.FC = () => { | |||
| return ( | |||
| <Card sx={{ mb: 2 }}> | |||
| <CardContent> | |||
| <Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 3 }}> | |||
| <Typography variant="h5" sx={{ fontWeight: 600 }}> | |||
| {t("Job Process Status", )} | |||
| </Typography> | |||
| </Box> | |||
| <Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 3 }}> | |||
| <Typography variant="h5" sx={{ fontWeight: 600 }}> | |||
| {t("Job Process Status")} | |||
| </Typography> | |||
| <FormControl size="small" sx={{ minWidth: 160 }}> | |||
| <Select | |||
| value={selectedDate} | |||
| onChange={(e) => setSelectedDate(e.target.value)} | |||
| > | |||
| <MenuItem value={dayjs().format("YYYY-MM-DD")}>今天</MenuItem> | |||
| <MenuItem value={dayjs().subtract(1, "day").format("YYYY-MM-DD")}>昨天</MenuItem> | |||
| <MenuItem value={dayjs().subtract(2, "day").format("YYYY-MM-DD")}>前天</MenuItem> | |||
| </Select> | |||
| </FormControl> | |||
| </Box> | |||
| <Box sx={{ mt: 2 }}> | |||
| {loading ? ( | |||
| @@ -285,12 +283,16 @@ const JobProcessStatus: React.FC = () => { | |||
| </TableCell> | |||
| ); | |||
| } | |||
| const label = [ | |||
| process.processName, | |||
| process.equipmentDescription, | |||
| process.equipmentDetailName ? `-${process.equipmentDetailName}` : "", | |||
| ].filter(Boolean).join(" "); | |||
| // 如果工序是必需的,显示三行(Start、Finish、Wait Time) | |||
| return ( | |||
| <TableCell key={index} align="center"> | |||
| <Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.5 }}> | |||
| <Typography variant="body2">{process.equipmentCode || '-'}</Typography> | |||
| <Typography variant="body2">{label || "-"}</Typography> | |||
| <Typography variant="body2"> | |||
| {formatTime(process.startTime)} | |||
| </Typography> | |||
| @@ -467,17 +467,19 @@ const handleRelease = useCallback(async ( jobOrderId: number) => { | |||
| align: "left", | |||
| headerAlign: "left", | |||
| type: "number", | |||
| sortable: false, // ✅ 禁用排序 | |||
| }, | |||
| { | |||
| field: "itemCode", | |||
| headerName: t("Item Code"), | |||
| headerName: t("Material Code"), | |||
| flex: 0.6, | |||
| sortable: false, // ✅ 禁用排序 | |||
| }, | |||
| { | |||
| field: "itemName", | |||
| headerName: t("Item Name"), | |||
| flex: 1, | |||
| sortable: false, // ✅ 禁用排序 | |||
| renderCell: (params: GridRenderCellParams<JobOrderLineInfo>) => { | |||
| return `${params.value} (${params.row.reqUom})`; | |||
| }, | |||
| @@ -488,21 +490,35 @@ const handleRelease = useCallback(async ( jobOrderId: number) => { | |||
| flex: 0.7, | |||
| align: "right", | |||
| headerAlign: "right", | |||
| renderCell: (params: GridRenderCellParams<JobOrderLineInfo>) => { | |||
| const qty = showBaseQty ? params.row.baseReqQty : params.value; | |||
| const uom = showBaseQty ? params.row.reqBaseUom : params.row.reqUom; | |||
| sortable: false, // ✅ 禁用排序 | |||
| // ✅ 将切换功能移到 header | |||
| renderHeader: () => { | |||
| const qty = showBaseQty ? t("Base") : t("Req"); | |||
| const uom = showBaseQty ? t("Base UOM") : t(" "); | |||
| return ( | |||
| <Box | |||
| onClick={toggleBaseQty} | |||
| sx={{ | |||
| cursor: "pointer", | |||
| userSelect: "none", | |||
| width: "100%", | |||
| textAlign: "right", | |||
| "&:hover": { | |||
| textDecoration: "underline", | |||
| }, | |||
| }} | |||
| > | |||
| {t("Bom Req. Qty")} ({uom}) | |||
| </Box> | |||
| ); | |||
| }, | |||
| // ✅ 移除 cell 中的 onClick,只显示值 | |||
| renderCell: (params: GridRenderCellParams<JobOrderLineInfo>) => { | |||
| const qty = showBaseQty ? params.row.baseReqQty : params.value; | |||
| const uom = showBaseQty ? params.row.reqBaseUom : params.row.reqUom; | |||
| return ( | |||
| <Box sx={{ textAlign: "right" }}> | |||
| {decimalFormatter.format(qty || 0)} ({uom || ""}) | |||
| </Box> | |||
| ); | |||
| @@ -514,27 +530,39 @@ const handleRelease = useCallback(async ( jobOrderId: number) => { | |||
| flex: 0.7, | |||
| align: "right", | |||
| headerAlign: "right", | |||
| renderCell: (params: GridRenderCellParams<JobOrderLineInfo>) => { | |||
| const qty = showBaseQty ? params.row.baseReqQty : params.value; | |||
| const uom = showBaseQty ? params.row.reqBaseUom : params.row.stockUom; | |||
| sortable: false, // ✅ 禁用排序 | |||
| // ✅ 将切换功能移到 header | |||
| renderHeader: () => { | |||
| const uom = showBaseQty ? t("Base UOM") : t("Stock UOM"); | |||
| return ( | |||
| <Box | |||
| onClick={toggleBaseQty} | |||
| sx={{ | |||
| cursor: "pointer", | |||
| userSelect: "none", | |||
| width: "100%", | |||
| textAlign: "right", | |||
| "&:hover": { | |||
| textDecoration: "underline", | |||
| }, | |||
| }} | |||
| > | |||
| {t("Stock Req. Qty")} ({uom}) | |||
| </Box> | |||
| ); | |||
| }, | |||
| // ✅ 移除 cell 中的 onClick | |||
| renderCell: (params: GridRenderCellParams<JobOrderLineInfo>) => { | |||
| const qty = showBaseQty ? params.row.baseReqQty : params.value; | |||
| const uom = showBaseQty ? params.row.reqBaseUom : params.row.stockUom; | |||
| return ( | |||
| <Box sx={{ textAlign: "right" }}> | |||
| {decimalFormatter.format(qty || 0)} ({uom || ""}) | |||
| </Box> | |||
| ); | |||
| }, | |||
| }, | |||
| { | |||
| field: "stockAvailable", | |||
| headerName: t("Stock Available"), | |||
| @@ -542,22 +570,35 @@ const handleRelease = useCallback(async ( jobOrderId: number) => { | |||
| align: "right", | |||
| headerAlign: "right", | |||
| type: "number", | |||
| renderCell: (params: GridRenderCellParams<JobOrderLineInfo>) => { | |||
| const stockAvailable = getStockAvailable(params.row); | |||
| const qty = showBaseQty ? params.row.baseStockQty : (stockAvailable || 0); | |||
| const uom = showBaseQty ? params.row.stockBaseUom : params.row.stockUom; | |||
| sortable: false, // ✅ 禁用排序 | |||
| // ✅ 将切换功能移到 header | |||
| renderHeader: () => { | |||
| const uom = showBaseQty ? t("Base UOM") : t("Stock UOM"); | |||
| return ( | |||
| <Box | |||
| onClick={toggleBaseQty} | |||
| sx={{ | |||
| cursor: "pointer", | |||
| userSelect: "none", | |||
| width: "100%", | |||
| textAlign: "right", | |||
| "&:hover": { | |||
| textDecoration: "underline", | |||
| }, | |||
| }} | |||
| > | |||
| {t("Stock Available")} ({uom}) | |||
| </Box> | |||
| ); | |||
| }, | |||
| // ✅ 移除 cell 中的 onClick | |||
| renderCell: (params: GridRenderCellParams<JobOrderLineInfo>) => { | |||
| const stockAvailable = getStockAvailable(params.row); | |||
| const qty = showBaseQty ? params.row.baseStockQty : (stockAvailable || 0); | |||
| const uom = showBaseQty ? params.row.stockBaseUom : params.row.stockUom; | |||
| return ( | |||
| <Box sx={{ textAlign: "right" }}> | |||
| {decimalFormatter.format(qty || 0)} ({uom || ""}) | |||
| </Box> | |||
| ); | |||
| @@ -570,17 +611,8 @@ const handleRelease = useCallback(async ( jobOrderId: number) => { | |||
| align: "right", | |||
| headerAlign: "right", | |||
| type: "number", | |||
| sortable: false, // ✅ 禁用排序 | |||
| }, | |||
| /* | |||
| { | |||
| field: "seqNoRemark", | |||
| headerName: t("Seq No Remark"), | |||
| flex: 1, | |||
| align: "left", | |||
| headerAlign: "left", | |||
| type: "string", | |||
| }, | |||
| */ | |||
| { | |||
| field: "stockStatus", | |||
| headerName: t("Stock Status"), | |||
| @@ -588,8 +620,8 @@ const handleRelease = useCallback(async ( jobOrderId: number) => { | |||
| align: "center", | |||
| headerAlign: "center", | |||
| type: "boolean", | |||
| sortable: false, // ✅ 禁用排序 | |||
| renderCell: (params: GridRenderCellParams<JobOrderLineInfo>) => { | |||
| return isStockSufficient(params.row) | |||
| ? <CheckCircleOutlineOutlinedIcon fontSize={"large"} color="success" /> | |||
| : <DoDisturbAltRoundedIcon fontSize={"large"} color="error" />; | |||
| @@ -18,6 +18,8 @@ import { | |||
| Card, | |||
| CardContent, | |||
| Grid, | |||
| Select, | |||
| MenuItem, | |||
| } from "@mui/material"; | |||
| import { Alert } from "@mui/material"; | |||
| @@ -106,7 +108,9 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro | |||
| if (!lineDetail) return false; | |||
| return lineDetail.name === "包裝"; | |||
| }, [lineDetail]) | |||
| const uomList = [ | |||
| "個", "件", "箱", "片", "塊" | |||
| ]; | |||
| // ✅ 添加:刷新 line detail 的函数 | |||
| const handleRefreshLineDetail = useCallback(async () => { | |||
| if (lineId) { | |||
| @@ -179,7 +183,7 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro | |||
| }); | |||
| if (!lineDetail?.durationInMinutes || !lineDetail?.startTime) { | |||
| console.log("❌ Line duration or start time is not valid", { | |||
| console.log(" Line duration or start time is not valid", { | |||
| durationInMinutes: lineDetail?.durationInMinutes, | |||
| startTime: lineDetail?.startTime, | |||
| equipmentId: lineDetail?.equipmentId, | |||
| @@ -527,11 +531,11 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro | |||
| setLastPauseTime(null); | |||
| }) | |||
| .catch(err => { | |||
| console.error("❌ Failed to load line detail after resume", err); | |||
| console.error(" Failed to load line detail after resume", err); | |||
| }); | |||
| } | |||
| } catch (error) { | |||
| console.error("❌ Error resuming:", error); | |||
| console.error(" Error resuming:", error); | |||
| alert(t("Failed to resume. Please try again.")); | |||
| } | |||
| }; | |||
| @@ -791,7 +795,7 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro | |||
| /> | |||
| </TableCell> | |||
| <TableCell> | |||
| <TextField | |||
| <Select | |||
| fullWidth | |||
| size="small" | |||
| value={outputData.outputFromProcessUom} | |||
| @@ -799,7 +803,17 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro | |||
| ...outputData, | |||
| outputFromProcessUom: e.target.value | |||
| })} | |||
| /> | |||
| displayEmpty | |||
| > | |||
| <MenuItem value=""> | |||
| <em>{t("Select Unit")}</em> | |||
| </MenuItem> | |||
| {uomList.map((uom) => ( | |||
| <MenuItem key={uom} value={uom}> | |||
| {uom} | |||
| </MenuItem> | |||
| ))} | |||
| </Select> | |||
| </TableCell> | |||
| <TableCell> | |||
| <Typography fontSize={15} align="center"> <strong>{t("Description")}</strong></Typography> | |||
| @@ -823,7 +837,7 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro | |||
| /> | |||
| </TableCell> | |||
| <TableCell> | |||
| <TextField | |||
| <Select | |||
| fullWidth | |||
| size="small" | |||
| value={outputData.defectUom} | |||
| @@ -831,7 +845,17 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro | |||
| ...outputData, | |||
| defectUom: e.target.value | |||
| })} | |||
| /> | |||
| displayEmpty | |||
| > | |||
| <MenuItem value=""> | |||
| <em>{t("Select Unit")}</em> | |||
| </MenuItem> | |||
| {uomList.map((uom) => ( | |||
| <MenuItem key={uom} value={uom}> | |||
| {uom} | |||
| </MenuItem> | |||
| ))} | |||
| </Select> | |||
| </TableCell> | |||
| <TableCell> | |||
| <TextField | |||
| @@ -861,7 +885,7 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro | |||
| /> | |||
| </TableCell> | |||
| <TableCell> | |||
| <TextField | |||
| <Select | |||
| fullWidth | |||
| size="small" | |||
| value={outputData.defect2Uom} | |||
| @@ -869,7 +893,17 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro | |||
| ...outputData, | |||
| defect2Uom: e.target.value | |||
| })} | |||
| /> | |||
| displayEmpty | |||
| > | |||
| <MenuItem value=""> | |||
| <em>{t("Select Unit")}</em> | |||
| </MenuItem> | |||
| {uomList.map((uom) => ( | |||
| <MenuItem key={uom} value={uom}> | |||
| {uom} | |||
| </MenuItem> | |||
| ))} | |||
| </Select> | |||
| </TableCell> | |||
| <TableCell> | |||
| <TextField | |||
| @@ -899,7 +933,7 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro | |||
| /> | |||
| </TableCell> | |||
| <TableCell> | |||
| <TextField | |||
| <Select | |||
| fullWidth | |||
| size="small" | |||
| value={outputData.defect3Uom} | |||
| @@ -907,7 +941,17 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro | |||
| ...outputData, | |||
| defect3Uom: e.target.value | |||
| })} | |||
| /> | |||
| displayEmpty | |||
| > | |||
| <MenuItem value=""> | |||
| <em>{t("Select Unit")}</em> | |||
| </MenuItem> | |||
| {uomList.map((uom) => ( | |||
| <MenuItem key={uom} value={uom}> | |||
| {uom} | |||
| </MenuItem> | |||
| ))} | |||
| </Select> | |||
| </TableCell> | |||
| <TableCell> | |||
| <TextField | |||
| @@ -937,7 +981,7 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro | |||
| /> | |||
| </TableCell> | |||
| <TableCell> | |||
| <TextField | |||
| <Select | |||
| fullWidth | |||
| size="small" | |||
| value={outputData.scrapUom} | |||
| @@ -945,7 +989,17 @@ const ProductionProcessStepExecution: React.FC<ProductionProcessStepExecutionPro | |||
| ...outputData, | |||
| scrapUom: e.target.value | |||
| })} | |||
| /> | |||
| displayEmpty | |||
| > | |||
| <MenuItem value=""> | |||
| <em>{t("Select Unit")}</em> | |||
| </MenuItem> | |||
| {uomList.map((uom) => ( | |||
| <MenuItem key={uom} value={uom}> | |||
| {uom} | |||
| </MenuItem> | |||
| ))} | |||
| </Select> | |||
| </TableCell> | |||
| </TableRow> | |||
| </TableBody> | |||
| @@ -207,7 +207,13 @@ | |||
| "Job Order Match": "工單對料", | |||
| "All Pick Order Lots": "所有提料單批號", | |||
| "Row per page": "每頁行數", | |||
| "Select Unit": "選擇單位", | |||
| "No data available": "沒有資料", | |||
| "Bom Req. Qty": "需求數(BOM單位)", | |||
| "Material Name": "材料清單", | |||
| "Material Code": "材料清單", | |||
| "Base UOM": "基本單位", | |||
| "Stock UOM": "庫存單位", | |||
| "jodetail": "工單細節", | |||
| "Sign out": "登出", | |||
| "By-product": "副產品", | |||
| @@ -25,6 +25,8 @@ | |||
| "Overall Time Remaining": "總剩餘時間", | |||
| "User not found with staffNo:": "用戶不存在", | |||
| "Time Remaining": "剩餘時間", | |||
| "Base UOM": "基本單位", | |||
| "Stock UOM": "庫存單位", | |||
| "Over Time": "超時", | |||
| "Staff No:": "員工編號:", | |||
| "Timer Paused": "計時器已暫停", | |||
| @@ -107,6 +109,8 @@ | |||
| "Lines with sufficient stock: ": "可提料項目數量: ", | |||
| "Lines with insufficient stock: ": "未能提料項目數量: ", | |||
| "Item Name": "成品/半成品", | |||
| "Material Code": "材料編號", | |||
| "Select Unit": "選擇單位", | |||
| "Job Order Pickexcution": "工單提料", | |||
| "Pick Order Detail": "提料單細節", | |||
| "Finished Job Order Record": "已完成工單記錄", | |||