diff --git a/src/app/(main)/production/page.tsx b/src/app/(main)/production/page.tsx
index 9cdec1a..2386750 100644
--- a/src/app/(main)/production/page.tsx
+++ b/src/app/(main)/production/page.tsx
@@ -1,5 +1,5 @@
import ProductionProcessPage from "../../../components/ProductionProcess/ProductionProcessPage";
-import { getServerI18n } from "../../../i18n";
+import { I18nProvider, getServerI18n } from "../../../i18n";
import Add from "@mui/icons-material/Add";
import Button from "@mui/material/Button";
@@ -15,7 +15,7 @@ export const metadata: Metadata = {
};
const production: React.FC = async () => {
- const { t } = await getServerI18n("claims");
+ const { t } = await getServerI18n("common");
const printerCombo = await fetchPrinterCombo();
return (
<>
@@ -38,7 +38,9 @@ const production: React.FC = async () => {
{t("Create Process")}
*/}
- {/* Use new component */}
+
+ {/* Use new component */}
+
>
);
};
diff --git a/src/app/api/jo/actions.ts b/src/app/api/jo/actions.ts
index 1070597..f3ba859 100644
--- a/src/app/api/jo/actions.ts
+++ b/src/app/api/jo/actions.ts
@@ -263,6 +263,7 @@ export interface AllJoborderProductProcessInfoResponse {
date: string;
bomId?: number;
itemName: string;
+ requiredQty: number;
jobOrderId: number;
stockInLineId: number;
jobOrderCode: string;
@@ -332,6 +333,7 @@ export interface JobOrderProcessLineDetailResponse {
operatorName: string;
handlerId: number;
seqNo: number;
+ durationInMinutes: number;
name: string;
description: string;
equipmentId: number;
@@ -360,7 +362,10 @@ export interface JobOrderLineInfo {
stockQty: number,
uom: string,
shortUom: string,
- availableStatus: string
+ availableStatus: string,
+ bomProcessId: number,
+ bomProcessSeqNo: number,
+
}
export interface ProductProcessLineInfoResponse {
id: number,
diff --git a/src/components/ProductionProcess/ProductionOutputForm.tsx b/src/components/ProductionProcess/ProductionOutputForm.tsx
new file mode 100644
index 0000000..1f1d536
--- /dev/null
+++ b/src/components/ProductionProcess/ProductionOutputForm.tsx
@@ -0,0 +1,210 @@
+"use client";
+import React from "react";
+import {
+ Box,
+ Button,
+ Paper,
+ Stack,
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableRow,
+ TextField,
+ Typography,
+} from "@mui/material";
+import CheckCircleIcon from "@mui/icons-material/CheckCircle";
+import { useTranslation } from "react-i18next";
+import { UpdateProductProcessLineQtyRequest } from "@/app/api/jo/actions";
+
+interface ProductionOutputFormProps {
+ outputData: (UpdateProductProcessLineQtyRequest & {
+ byproductName: string;
+ byproductQty: number;
+ byproductUom: string;
+ });
+ setOutputData: React.Dispatch<
+ React.SetStateAction<
+ UpdateProductProcessLineQtyRequest & {
+ byproductName: string;
+ byproductQty: number;
+ byproductUom: string;
+ }
+ >
+ >;
+ onSubmit: () => Promise | void;
+ onCancel: () => void;
+}
+
+const ProductionOutputForm: React.FC = ({
+ outputData,
+ setOutputData,
+ onSubmit,
+ onCancel,
+}) => {
+ const { t } = useTranslation();
+
+ return (
+
+
+
+
+ {t("Type")}
+ {t("Quantity")}
+ {t("Unit")}
+
+
+
+
+
+ {t("Output from Process")}
+
+
+
+ setOutputData({
+ ...outputData,
+ outputFromProcessQty: parseInt(e.target.value) || 0,
+ })
+ }
+ />
+
+
+
+ setOutputData({
+ ...outputData,
+ outputFromProcessUom: e.target.value,
+ })
+ }
+ />
+
+
+
+
+
+
+ {t("By-product")}
+
+
+
+
+ setOutputData({
+ ...outputData,
+ byproductQty: parseInt(e.target.value) || 0,
+ })
+ }
+ />
+
+
+
+ setOutputData({
+ ...outputData,
+ byproductUom: e.target.value,
+ })
+ }
+ />
+
+
+
+
+
+
+ {t("Defect")}
+
+
+
+
+ setOutputData({
+ ...outputData,
+ defectQty: parseInt(e.target.value) || 0,
+ })
+ }
+ />
+
+
+
+ setOutputData({
+ ...outputData,
+ defectUom: e.target.value,
+ })
+ }
+ />
+
+
+
+
+
+
+ {t("Scrap")}
+
+
+
+
+ setOutputData({
+ ...outputData,
+ scrapQty: parseInt(e.target.value) || 0,
+ })
+ }
+ />
+
+
+
+ setOutputData({
+ ...outputData,
+ scrapUom: e.target.value,
+ })
+ }
+ />
+
+
+
+
+
+
+
+ } onClick={onSubmit}>
+ {t("Complete Step")}
+
+
+
+ );
+};
+
+export default ProductionOutputForm;
\ No newline at end of file
diff --git a/src/components/ProductionProcess/ProductionOutputFormPage.tsx b/src/components/ProductionProcess/ProductionOutputFormPage.tsx
new file mode 100644
index 0000000..633942b
--- /dev/null
+++ b/src/components/ProductionProcess/ProductionOutputFormPage.tsx
@@ -0,0 +1,146 @@
+"use client";
+import React, { useEffect, useState } from "react";
+import {
+ Box,
+ Button,
+ Typography,
+} from "@mui/material";
+import { useTranslation } from "react-i18next";
+import {
+ fetchProductProcessLineDetail,
+ updateProductProcessLineQty,
+ UpdateProductProcessLineQtyRequest,
+ JobOrderProcessLineDetailResponse,
+} from "@/app/api/jo/actions";
+import ProductionOutputForm from "./ProductionOutputForm";
+
+interface ProductionOutputFormPageProps {
+ lineId: number | null;
+ onBack: () => void;
+}
+
+const ProductionOutputFormPage: React.FC = ({
+ lineId,
+ onBack,
+}) => {
+ const { t } = useTranslation();
+ const [lineDetail, setLineDetail] = useState(null);
+ const [outputData, setOutputData] = useState({
+ productProcessLineId: lineId ?? 0,
+ outputFromProcessQty: 0,
+ outputFromProcessUom: "",
+ defectQty: 0,
+ defectUom: "",
+ scrapQty: 0,
+ scrapUom: "",
+ byproductName: "",
+ byproductQty: 0,
+ byproductUom: "",
+ });
+
+ useEffect(() => {
+ if (!lineId) {
+ setLineDetail(null);
+ return;
+ }
+ fetchProductProcessLineDetail(lineId)
+ .then((detail) => {
+ setLineDetail(detail as any);
+ setOutputData((prev) => ({
+ ...prev,
+ productProcessLineId: detail.id,
+ outputFromProcessQty: (detail as any).outputFromProcessQty || 0,
+ outputFromProcessUom: (detail as any).outputFromProcessUom || "",
+ defectQty: detail.defectQty || 0,
+ defectUom: detail.defectUom || "",
+ scrapQty: detail.scrapQty || 0,
+ scrapUom: detail.scrapUom || "",
+ byproductName: detail.byproductName || "",
+ byproductQty: detail.byproductQty || 0,
+ byproductUom: detail.byproductUom || "",
+ }));
+ })
+ .catch((err) => {
+ console.error("Failed to load line detail", err);
+ setLineDetail(null);
+ });
+ }, [lineId]);
+
+ const handleSubmitOutput = async () => {
+ if (!lineDetail?.id) return;
+
+ try {
+ await updateProductProcessLineQty({
+ productProcessLineId: lineDetail.id || 0,
+ byproductName: outputData.byproductName,
+ byproductQty: outputData.byproductQty,
+ byproductUom: outputData.byproductUom,
+ outputFromProcessQty: outputData.outputFromProcessQty,
+ outputFromProcessUom: outputData.outputFromProcessUom,
+ defectQty: outputData.defectQty,
+ defectUom: outputData.defectUom,
+ scrapQty: outputData.scrapQty,
+ scrapUom: outputData.scrapUom,
+ });
+
+ console.log("Output data submitted successfully");
+
+ // 重新加载数据
+ const detail = await fetchProductProcessLineDetail(lineDetail.id);
+ setLineDetail(detail as any);
+ setOutputData((prev) => ({
+ ...prev,
+ productProcessLineId: detail.id,
+ outputFromProcessQty: (detail as any).outputFromProcessQty || 0,
+ outputFromProcessUom: (detail as any).outputFromProcessUom || "",
+ defectQty: detail.defectQty || 0,
+ defectUom: detail.defectUom || "",
+ scrapQty: detail.scrapQty || 0,
+ scrapUom: detail.scrapUom || "",
+ byproductName: detail.byproductName || "",
+ byproductQty: detail.byproductQty || 0,
+ byproductUom: detail.byproductUom || "",
+ }));
+
+ // 提交成功后返回
+ onBack();
+ } catch (error) {
+ console.error("Error submitting output:", error);
+ alert("Failed to submit output data. Please try again.");
+ }
+ };
+
+ return (
+
+
+
+
+
+
+
+ {t("Production Output Data Entry")}
+
+ {lineDetail && (
+
+ {t("Step")}: {lineDetail.name} (Seq: {lineDetail.seqNo})
+
+ )}
+
+
+
+
+ );
+};
+
+export default ProductionOutputFormPage;
\ No newline at end of file
diff --git a/src/components/ProductionProcess/ProductionProcessDetail.tsx b/src/components/ProductionProcess/ProductionProcessDetail.tsx
index 6e75c71..1cc4138 100644
--- a/src/components/ProductionProcess/ProductionProcessDetail.tsx
+++ b/src/components/ProductionProcess/ProductionProcessDetail.tsx
@@ -48,7 +48,7 @@ import {
} from "@/app/api/jo/actions";
import { fetchNameList, NameList } from "@/app/api/user/actions";
import ProductionProcessStepExecution from "./ProductionProcessStepExecution";
-
+import ProductionOutputFormPage from "./ProductionOutputFormPage";
interface ProductProcessDetailProps {
jobOrderId: number;
@@ -63,7 +63,7 @@ const ProductionProcessDetail: React.FC = ({
const { data: session } = useSession() as { data: SessionWithTokens | null };
const currentUserId = session?.id ? parseInt(session.id) : undefined;
const { values: qrValues, startScan, stopScan, resetScan } = useQrCodeScannerContext();
-
+ const [showOutputPage, setShowOutputPage] = useState(false);
// 基本信息
const [processData, setProcessData] = useState(null);
const [lines, setLines] = useState([]);
@@ -102,6 +102,7 @@ const ProductionProcessDetail: React.FC = ({
await fetchProcessDetail(); // 重新拉取最新的 process/lines
setIsExecutingLine(false);
setSelectedLineId(null);
+ setShowOutputPage(false);
};
// 获取 process 和 lines 数据
@@ -486,11 +487,12 @@ const ProductionProcessDetail: React.FC = ({
{t("Seq")}
{t("Step Name")}
{t("Description")}
+ {t("Equipment Type/Code")}
{t("Operator")}
- {t("Equipment Type")}
- {t("Duration")}
- {t("Prep Time")}
- {t("Post Prod Time")}
+
+ {t("Processing Time (mins)")}
+ {t("Setup Time (mins)")}
+ {t("Changeover Time (mins)")}
{t("Status")}
{t("Action")}
@@ -512,16 +514,30 @@ const ProductionProcessDetail: React.FC = ({
{line.name}
{line.description || "-"}
- {line.operatorName}
{equipmentName}
- {line.durationInMinutes} {t("Minutes")}
- {line.prepTimeInMinutes} {t("Minutes")}
- {line.postProdTimeInMinutes} {t("Minutes")}
+ {line.operatorName}
+
+ {line.durationInMinutes}
+ {line.prepTimeInMinutes}
+ {line.postProdTimeInMinutes}
{isCompleted ? (
-
+ {
+ setSelectedLineId(line.id);
+ setShowOutputPage(false); // 不显示输出页面
+ setIsExecutingLine(true);
+ await fetchProcessDetail();
+ }}
+ />
) : isInProgress ? (
-
+ {
+ setSelectedLineId(line.id);
+ setShowOutputPage(false); // 不显示输出页面
+ setIsExecutingLine(true);
+ await fetchProcessDetail();
+ }} />
) : isPending ? (
) : (
diff --git a/src/components/ProductionProcess/ProductionProcessJobOrderDetail.tsx b/src/components/ProductionProcess/ProductionProcessJobOrderDetail.tsx
index b4f2f48..23ad8d5 100644
--- a/src/components/ProductionProcess/ProductionProcessJobOrderDetail.tsx
+++ b/src/components/ProductionProcess/ProductionProcessJobOrderDetail.tsx
@@ -1,5 +1,5 @@
"use client";
-import React, { useCallback, useEffect, useState } from "react";
+import React, { useCallback, useEffect, useState, useMemo } from "react";
import {
Box,
Button,
@@ -99,7 +99,36 @@ const ProductionProcessJobOrderDetail: React.FC {
fetchData();
}, [fetchData]);
+// PickTable 组件内容
+const getStockAvailable = (line: JobOrderLine) => {
+ const inventory = inventoryData.find(inv =>
+ inv.itemCode === line.itemCode || inv.itemName === line.itemName
+ );
+ if (inventory) {
+ return inventory.availableQty || (inventory.onHandQty - inventory.onHoldQty - inventory.unavailableQty);
+ }
+ return line.stockQty || 0;
+};
+
+const isStockSufficient = (line: JobOrderLine) => {
+ const stockAvailable = getStockAvailable(line);
+ return stockAvailable >= line.reqQty;
+};
+const stockCounts = useMemo(() => {
+ const total = jobOrderLines.length;
+ const sufficient = jobOrderLines.filter(isStockSufficient).length;
+ return {
+ total,
+ sufficient,
+ insufficient: total - sufficient,
+ };
+}, [jobOrderLines, inventoryData]);
+const status = processData?.status?.toLowerCase?.() ?? "";
+const handleRelease = useCallback(() => {
+ // TODO: 替换为实际的 release 调用
+ console.log("Release clicked for jobOrderId:", jobOrderId);
+}, [jobOrderId]);
const handleTabChange = useCallback>(
(_e, newValue) => {
setTabIndex(newValue);
@@ -139,6 +168,7 @@ const ProductionProcessJobOrderDetail: React.FC (
@@ -153,21 +183,13 @@ const ProductionProcessJobOrderDetail: React.FC
-
+
-
-
-
@@ -175,17 +197,10 @@ const ProductionProcessJobOrderDetail: React.FC
-
-
-
+
-
-
-
-
-
-
+
);
- // PickTable 组件内容
- const getStockAvailable = (line: JobOrderLine) => {
- const inventory = inventoryData.find(inv =>
- inv.itemCode === line.itemCode || inv.itemName === line.itemName
- );
- if (inventory) {
- return inventory.availableQty || (inventory.onHandQty - inventory.onHoldQty - inventory.unavailableQty);
- }
- return line.stockQty || 0;
- };
-
- const isStockSufficient = (line: JobOrderLine) => {
- const stockAvailable = getStockAvailable(line);
- return stockAvailable >= line.reqQty;
- };
const productionProcessesLineRemarkTableColumns: GridColDef[] = [
{
field: "seqNo",
@@ -321,11 +306,28 @@ const ProductionProcessJobOrderDetail: React.FC) => {
return isStockSufficient(params.row)
@@ -342,14 +344,44 @@ const ProductionProcessJobOrderDetail: React.FC (
+
+
+
+
+ {t("Total lines: ")}{stockCounts.total}
+
+
+
+ {t("Lines with sufficient stock: ")}{stockCounts.sufficient}
+
+
+
+ {t("Lines with insufficient stock: ")}{stockCounts.insufficient}
+
+
+
+
+
+
+
'auto'}
+ getRowHeight={() => "auto"}
/>
);
@@ -381,7 +413,7 @@ const ProductionProcessJobOrderDetail: React.FC
-
+
diff --git a/src/components/ProductionProcess/ProductionProcessList.tsx b/src/components/ProductionProcess/ProductionProcessList.tsx
index 6c48394..a659e29 100644
--- a/src/components/ProductionProcess/ProductionProcessList.tsx
+++ b/src/components/ProductionProcess/ProductionProcessList.tsx
@@ -34,7 +34,7 @@ interface ProductProcessListProps {
const PER_PAGE = 6;
const ProductProcessList: React.FC = ({ onSelectProcess, printerCombo }) => {
- const { t } = useTranslation();
+ const { t } = useTranslation( ["common", "production","purchaseOrder"]);
const { data: session } = useSession() as { data: SessionWithTokens | null };
const sessionToken = session as SessionWithTokens | null;
const [loading, setLoading] = useState(false);
@@ -144,16 +144,22 @@ const ProductProcessList: React.FC = ({ onSelectProcess
- {process.productProcessCode}
+ {t("Job Order")}: {jobOrderCode}
-
- {t("Job Order")}: {jobOrderCode}
-
-
+
+
+ {t("Item Name")}: {process.itemName}
+
+
+ {t("Required Qty")}: {process.requiredQty}
+
+
+ {t("Production date")}: {process.date ? dayjs(process.date as any).format(OUTPUT_DATE_FORMAT) : "-"}
+
{statusLower !== "pending" && linesWithStatus.length > 0 && (
@@ -184,9 +190,7 @@ const ProductProcessList: React.FC = ({ onSelectProcess
)}
-
- {t("Lines")}: {totalCount}
-
+
diff --git a/src/components/ProductionProcess/ProductionProcessStepExecution.tsx b/src/components/ProductionProcess/ProductionProcessStepExecution.tsx
index 92b9a4b..a20958e 100644
--- a/src/components/ProductionProcess/ProductionProcessStepExecution.tsx
+++ b/src/components/ProductionProcess/ProductionProcessStepExecution.tsx
@@ -63,7 +63,7 @@ const ProductionProcessStepExecution: React.FC(null);
// 检查是否两个都已扫描
//const bothScanned = lineDetail?.operatorId && lineDetail?.equipmentId;
@@ -98,6 +98,27 @@ const ProductionProcessStepExecution: React.FC {
+ if (!lineDetail?.durationInMinutes || !lineDetail?.startTime) {
+ setRemainingTime(null);
+ return;
+ }
+ const start = new Date(lineDetail.startTime as any);
+ const end = new Date(start.getTime() + lineDetail.durationInMinutes * 60_000);
+ const update = () => {
+ const diff = end.getTime() - Date.now();
+ if (diff <= 0) {
+ setRemainingTime("00:00");
+ return;
+ }
+ const minutes = Math.floor(diff / 60000).toString().padStart(2, "0");
+ const seconds = Math.floor((diff % 60000) / 1000).toString().padStart(2, "0");
+ setRemainingTime(`${minutes}:${seconds}`);
+ };
+ update();
+ const timer = setInterval(update, 1000);
+ return () => clearInterval(timer);
+ }, [lineDetail?.durationInMinutes, lineDetail?.startTime]);
const handleSubmitOutput = async () => {
if (!lineDetail?.id) return;
@@ -255,7 +276,7 @@ const ProductionProcessStepExecution: React.FC
{/* By-product */}
- {lineDetail?.byproductQty && lineDetail.byproductQty > 0 && (
+
{t("By-product")}
@@ -272,10 +293,9 @@ const ProductionProcessStepExecution: React.FC{lineDetail.byproductUom || "-"}
- )}
{/* Defect */}
- {lineDetail?.defectQty && lineDetail.defectQty > 0 && (
+
{t("Defect")}
@@ -287,10 +307,8 @@ const ProductionProcessStepExecution: React.FC{lineDetail.defectUom || "-"}
- )}
{/* Scrap */}
- {lineDetail?.scrapQty && lineDetail.scrapQty > 0 && (
{t("Scrap")}
@@ -302,7 +320,6 @@ const ProductionProcessStepExecution: React.FC{lineDetail.scrapUom || "-"}
- )}
@@ -311,8 +328,9 @@ const ProductionProcessStepExecution: React.FC
{/* 如果未完成,显示原来的两个部分 */}
{/* 当前步骤信息 */}
+ {!showOutputTable && (
-
+
@@ -327,6 +345,7 @@ const ProductionProcessStepExecution: React.FC
{t("Equipment")}: {equipmentName}
+
-
+ )}
{/* ========== 产出输入表单 ========== */}
+ {showOutputTable && (
-
-
- {t("Production Output Data Entry")}
-
- setShowOutputTable(!showOutputTable)}
- >
- {showOutputTable ? t("Hide Table") : t("Show Table")}
-
-
+
- {showOutputTable && (
@@ -526,8 +542,8 @@ const ProductionProcessStepExecution: React.FC
- )}
-
+
+ )}
>
)}
diff --git a/src/i18n/zh/common.json b/src/i18n/zh/common.json
index f38e4c2..aa72170 100644
--- a/src/i18n/zh/common.json
+++ b/src/i18n/zh/common.json
@@ -16,7 +16,6 @@
"Add Record": "新增",
"Clean Record": "重置",
"Dashboard": "資訊展示面板",
- "Store Management": "原材料",
"Stock Take Management": "盤點管理",
"Store Management": "倉庫管理",
"Delivery": "送貨訂單",
@@ -27,7 +26,7 @@
"User Group": "用戶群組",
"Items": "物料",
"Demand Forecast Setting": "需求預測設定",
- "Equipment Type": "設備類型",
+ "Equipment Type/Code": "使用設備-編號",
"Equipment": "設備",
"Warehouse": "倉庫",
"Supplier": "供應商",
@@ -107,6 +106,93 @@
"Row per page": "每頁行數",
"No data available": "沒有資料",
"jodetail": "工單細節",
- "Sign out": "登出"
+ "Sign out": "登出",
+
+
+ "By-product": "副產品",
+ "Complete Step": "完成步驟",
+ "Defect": "缺陷",
+ "Output from Process": "流程輸出",
+ "Quantity": "數量",
+ "Scrap": "廢料",
+ "Unit": "單位",
+ "Back to List": "返回列表",
+ "Production Output Data Entry": "生產輸出數據輸入",
+ "Step": "步驟",
+ "Quality Check": "品質檢查",
+ "Action": "操作",
+ "Changeover Time (mins)": "生產後轉換時間(分鐘)",
+ "Completed": "完成",
+ "completed": "完成",
+ "Date": "日期",
+ "Failed to submit scan data. Please try again.": "掃碼數據提交失敗. 請重試.",
+ "In Progress": "進行中",
+ "Is Dark": "是否黑暗",
+ "Is Dense": "是否密集",
+ "Is Float": "是否浮動",
+ "Job Order Code": "工單編號",
+ "Operator": "操作員",
+ "Output Qty": "輸出數量",
+ "Pending": "待處理",
+ "pending": "待處理",
+
+ "Please scan equipment code (optional if not required)": "請掃描設備編號(可選)",
+ "Please scan operator code": "請掃描操作員編號",
+ "Please scan operator code first": "請先掃描操作員編號",
+ "Processing Time (mins)": "步驟時間(分鐘)",
+ "Production Process Information": "生產流程信息",
+ "Production Process Steps": "生產流程步驟",
+ "Scan Operator & Equipment": "掃描操作員和設備",
+ "Seq": "序號",
+ "Setup Time (mins)": "生產前預備時間(分鐘)",
+ "Start": "開始",
+ "Start QR Scan": "開始掃碼",
+ "Status": "狀態",
+ "in_progress": "進行中",
+ "In_Progress": "進行中",
+ "inProgress": "進行中",
+
+ "Step Name": "步驟名稱",
+ "Stop QR Scan": "停止掃碼",
+ "Submit & Start": "提交並開始",
+ "Total Steps": "總步驟數",
+ "Unknown": "",
+ "Validation failed. Please check operator and equipment.": "驗證失敗. 請檢查操作員和設備.",
+ "View": "查看",
+ "Back": "返回",
+ "BoM Material": "物料清單",
+ "Is Dark | Dense | Float": "是否黑暗 | 密集 | 浮動",
+ "Item Code": "物料編號",
+ "Item Name": "物料名稱",
+ "Job Order Info": "工單信息",
+ "Matching Stock": "匹配庫存",
+ "No data found": "沒有找到資料",
+ "Production Priority": "生產優先級",
+ "Production Process": "工藝流程",
+ "Production Process Line Remark": "工藝明細",
+ "Remark": "明細",
+ "Req. Qty": "需求數量",
+ "Seq No": "序號",
+ "Seq No Remark": "序號明細",
+ "Stock Available": "庫存可用",
+ "Stock Status": "庫存狀態",
+ "Target Production Date": "目標生產日期",
+ "id": "ID",
+ "Finished lines": "完成行",
+ "Invalid Stock In Line Id": "無效庫存行ID",
+ "Production date": "生產日期",
+ "Required Qty": "需求數量",
+ "Total processes": "總流程數",
+ "View Details": "查看詳情",
+ "view stockin": "查看入庫",
+ "Completed Step": "完成步驟",
+ "Continue": "繼續",
+ "Executing": "執行中",
+ "Order Complete": "訂單完成",
+ "Pause": "暫停",
+ "Production Output Data": "生產輸出數據",
+ "Step Information": "步驟信息",
+ "Stop": "停止",
+ "Putaway Detail": "上架詳情"
}