diff --git a/src/app/(main)/settings/importExcel/page.tsx b/src/app/(main)/settings/importExcel/page.tsx
new file mode 100644
index 0000000..e83b3b6
--- /dev/null
+++ b/src/app/(main)/settings/importExcel/page.tsx
@@ -0,0 +1,34 @@
+import { Metadata } from "next";
+import { getServerI18n } from "@/i18n";
+import Stack from "@mui/material/Stack";
+import Typography from "@mui/material/Typography";
+import { Suspense } from "react";
+import ExcelFileImport from "@/components/ExcelFileImport";
+
+export const metadata: Metadata = {
+ title: "Excel File Import",
+};
+
+const ImportExcel: React.FC = async () => {
+ const { t } = await getServerI18n("common");
+
+ return (
+ <>
+
+
+ {t("Excel File Import")}
+
+
+
+
+
+ >
+ )
+};
+
+export default ImportExcel;
diff --git a/src/app/api/jo/actions.ts b/src/app/api/jo/actions.ts
index 6c3d4fa..135f2d9 100644
--- a/src/app/api/jo/actions.ts
+++ b/src/app/api/jo/actions.ts
@@ -2,7 +2,7 @@
import { cache } from 'react';
import { Pageable, serverFetchJson } from "@/app/utils/fetchUtil";
-import { Machine, Operator } from ".";
+import { JoStatus, Machine, Operator } from ".";
import { BASE_API_URL } from "@/config/api";
import { revalidateTag } from "next/cache";
import { convertObjToURLSearchParams } from "@/app/utils/commonUtil";
@@ -35,7 +35,7 @@ export interface SearchJoResult {
name: string;
reqQty: number;
uom: string;
- status: string;
+ status: JoStatus;
}
export interface ReleaseJoRequest {
@@ -44,7 +44,7 @@ export interface ReleaseJoRequest {
export interface ReleaseJoResponse {
id: number;
- entity: { status: string }
+ entity: { status: JoStatus }
}
export interface IsOperatorExistResponse {
diff --git a/src/app/api/jo/index.ts b/src/app/api/jo/index.ts
index 85bd195..33a6802 100644
--- a/src/app/api/jo/index.ts
+++ b/src/app/api/jo/index.ts
@@ -4,6 +4,9 @@ import { serverFetchJson } from "@/app/utils/fetchUtil";
import { BASE_API_URL } from "@/config/api";
import { cache } from "react";
+export type JoStatus = "planning" | "pending" | "processing" | "packaging" | "storing" | "completed"
+export type JoBomMaterialStatus = "pending" | "completed"
+
export interface Operator {
id: number;
name: string;
@@ -24,7 +27,7 @@ export interface JoDetail {
reqQty: number;
uom: string;
pickLines: JoDetailPickLine[];
- status: string;
+ status: JoStatus;
planStart: number[];
planEnd: number[];
type: string;
@@ -34,10 +37,16 @@ export interface JoDetailPickLine {
id: number;
code: string;
name: string;
- lotNo: string;
+ pickedLotNo?: JoDetailPickedLotNo[];
reqQty: number;
uom: string;
- status: string;
+ status: JoBomMaterialStatus;
+}
+
+export interface JoDetailPickedLotNo {
+ lotNo: string;
+ qty: number;
+ isScanned: boolean;
}
export const fetchJoDetail = cache(async (id: number) => {
diff --git a/src/app/api/stockTake/actions.ts b/src/app/api/stockTake/actions.ts
new file mode 100644
index 0000000..6b91b20
--- /dev/null
+++ b/src/app/api/stockTake/actions.ts
@@ -0,0 +1,16 @@
+"use server";
+
+import { serverFetchString } from "@/app/utils/fetchUtil";
+import { BASE_API_URL } from "@/config/api";
+
+export const importStockTake = async (data: FormData) => {
+ const importStockTake = await serverFetchString(
+ `${BASE_API_URL}/stockTake/import`,
+ {
+ method: "POST",
+ body: data,
+ },
+ );
+ console.log(importStockTake)
+ return importStockTake;
+}
\ No newline at end of file
diff --git a/src/app/utils/fetchUtil.ts b/src/app/utils/fetchUtil.ts
index f9d82b5..d1484f8 100644
--- a/src/app/utils/fetchUtil.ts
+++ b/src/app/utils/fetchUtil.ts
@@ -91,6 +91,25 @@ export async function serverFetchJson(...args: FetchParams) {
}
}
+export async function serverFetchString(...args: FetchParams) {
+ const response = await serverFetch(...args);
+
+ if (response.ok) {
+ return response.text() as T;
+ } else {
+ switch (response.status) {
+ case 401:
+ signOutUser();
+ default:
+ console.error(await response.text());
+ throw new ServerFetchError(
+ "Something went wrong fetching data in server.",
+ response,
+ );
+ }
+ }
+}
+
export async function serverFetchBlob(...args: FetchParams) {
const response = await serverFetch(...args);
diff --git a/src/components/ExcelFileImport/ExcelFileImport.tsx b/src/components/ExcelFileImport/ExcelFileImport.tsx
new file mode 100644
index 0000000..800b841
--- /dev/null
+++ b/src/components/ExcelFileImport/ExcelFileImport.tsx
@@ -0,0 +1,88 @@
+"use client";
+
+import { FileUpload } from "@mui/icons-material";
+import { Button, Grid, Stack } from "@mui/material";
+import React, { ChangeEvent, useCallback, useMemo } from "react";
+import { useTranslation } from "react-i18next";
+import { errorDialogWithContent, submitDialog, successDialog, successDialogWithContent } from "../Swal/CustomAlerts";
+import { importStockTake } from "@/app/api/stockTake/actions";
+
+interface Props {
+}
+
+const ExcelFileImport: React.FC = async ({ }) => {
+
+ const { t } = useTranslation("common");
+
+ const buttonName: string[] = useMemo(() => {
+ return ["Import Stock Take"]
+ }, [])
+
+ const handleExcelFileImportClick = useCallback(async (event: ChangeEvent) => {
+
+ try {
+ if (event.target.files !== null) {
+ const file = event.target.files[0]
+ const formData = new FormData();
+ formData.append('multipartFileList', file);
+
+ if (file.name.endsWith(".xlsx") || file.name.endsWith(".csv")) {
+ let response: String = ""
+
+ console.log(event.target.id)
+ switch (event.target.id) {
+ case "Import Stock Take":
+ response = await importStockTake(formData)
+ break;
+ }
+
+ if (response.includes("Import Excel success")) {
+ successDialogWithContent(t("Import Success"), t(`${response}`), t)
+ } else {
+ errorDialogWithContent(t("Import Fail"), t(`${response}`), t)
+ }
+ }
+ }
+
+ } catch (err) {
+ console.log(err)
+ return false
+ }
+
+ return
+ }, [])
+
+ return (
+ <>
+
+ {
+ buttonName.map((name) => {
+ return (
+
+ }
+ component="label"
+ >
+ {
+ handleExcelFileImportClick(event)
+ }}
+ />
+ {t(name)}
+
+
+ )
+ })
+ }
+
+ >
+ );
+};
+
+export default ExcelFileImport;
\ No newline at end of file
diff --git a/src/components/ExcelFileImport/ExcelFileImportWrapper.tsx b/src/components/ExcelFileImport/ExcelFileImportWrapper.tsx
new file mode 100644
index 0000000..d55def2
--- /dev/null
+++ b/src/components/ExcelFileImport/ExcelFileImportWrapper.tsx
@@ -0,0 +1,10 @@
+import React from "react";
+import ExcelFileImport from "./ExcelFileImport";
+
+const ExcelFileImportWrapper: React.FC = async () => {
+
+ return ;
+};
+
+
+export default ExcelFileImportWrapper;
\ No newline at end of file
diff --git a/src/components/ExcelFileImport/index.ts b/src/components/ExcelFileImport/index.ts
new file mode 100644
index 0000000..542fe57
--- /dev/null
+++ b/src/components/ExcelFileImport/index.ts
@@ -0,0 +1 @@
+export { default } from './ExcelFileImportWrapper'
\ No newline at end of file
diff --git a/src/components/FinishedGoodSearch/FinishedGoodSearch.tsx b/src/components/FinishedGoodSearch/FinishedGoodSearch.tsx
index f20f418..0d9087e 100644
--- a/src/components/FinishedGoodSearch/FinishedGoodSearch.tsx
+++ b/src/components/FinishedGoodSearch/FinishedGoodSearch.tsx
@@ -279,38 +279,50 @@ const PickOrderSearch: React.FC = ({ pickOrders }) => {
overflow: 'auto' // Single scrollbar for the whole page
}}>
{/* Header section */}
-
-
-
-
-
- {t("Finished Good Order")}
-
-
-
-
-
-
-
+
+
+
+
+
+
+ {t("Finished Good Order")}
+
+
-
+
+
+ {/* First 4 buttons aligned left */}
+
+
+
+
+
+
-
+
+
+ {/* Last 2 buttons aligned right */}
+
+
+
+
+
+
+
+
+
{/* Tabs section - ✅ Move the click handler here */}
0) {
console.log(`✅ QR Code processing completed: ${successCount} updated/created`);
setQrScanSuccess(true);
+ setQrScanError(false);
setQrScanInput(''); // Clear input after successful processing
-
+ setIsManualScanning(false);
+ stopScan();
+ resetScan();
// ✅ Clear success state after a delay
//setTimeout(() => {
@@ -778,7 +791,9 @@ const [isConfirmingLot, setIsConfirmingLot] = useState(false);
qty: selectedLotForQr.stockOutLineQty || 0
});
console.log("Stock out line updated successfully!");
-
+ setQrScanSuccess(true);
+ setQrScanError(false);
+
// Close modal
setQrModalOpen(false);
setSelectedLotForQr(null);
@@ -1276,7 +1291,7 @@ const handleSubmitPickQtyWithQty = useCallback(async (lot: any, submitQty: numbe
- {qrScanError && (
+ {qrScanError && !qrScanSuccess && (
{t("QR code does not match any item in current orders.")}
@@ -1444,6 +1459,7 @@ const handleSubmitPickQtyWithQty = useCallback(async (lot: any, submitQty: numbe
{/* ✅ Status Messages Display - Move here, outside the table */}
+ {/*
{paginatedData.length > 0 && (
{paginatedData.map((lot, index) => (
@@ -1455,6 +1471,7 @@ const handleSubmitPickQtyWithQty = useCallback(async (lot: any, submitQty: numbe
))}
)}
+*/}
= ({
{t("If you proceed, the system will:")}
- - {t("Update the stock out line to use the scanned lot")}
- - {t("Put the original suggested lot on hold")}
- - {t("Update inventory lot line for the new lot")}
+ - {t("Update your suggested lot to the this scanned lot")}
diff --git a/src/components/JoSave/ActionButtons.tsx b/src/components/JoSave/ActionButtons.tsx
new file mode 100644
index 0000000..480576d
--- /dev/null
+++ b/src/components/JoSave/ActionButtons.tsx
@@ -0,0 +1,86 @@
+import { JoDetail } from "@/app/api/jo";
+import { Box, Button, Stack, Typography } from "@mui/material";
+import { useMemo } from "react";
+import { useFormContext } from "react-hook-form";
+import { useTranslation } from "react-i18next";
+import StartIcon from "@mui/icons-material/Start";
+import PlayCircleFilledWhiteIcon from '@mui/icons-material/PlayCircleFilledWhite';
+
+type Props = {
+ handleRelease: () => void;
+ handleStart: () => void;
+};
+
+interface ErrorEntry {
+ qtyErr: boolean;
+ scanErr: boolean;
+}
+
+const ActionButtons: React.FC = ({
+ handleRelease,
+ handleStart
+}) => {
+ const { t } = useTranslation("jo");
+
+ const { watch } = useFormContext();
+
+ const status = useMemo(() => {
+ return watch("status").toLowerCase()
+ }, [watch("status")])
+
+ const pickLines = useMemo(() => {
+ return watch("pickLines")
+ }, [watch("pickLines")])
+
+ // Check Error Handling (e.g. start jo)
+ const errors: ErrorEntry = useMemo(() => {
+ let qtyErr = false;
+ let scanErr = false;
+
+ pickLines.forEach((line) => {
+ if (!qtyErr) {
+ const pickedQty = line.pickedLotNo?.reduce((acc, cur) => acc + cur.qty, 0) ?? 0
+ qtyErr = pickedQty > 0 && pickedQty >= line.reqQty
+ }
+
+ if (!scanErr) {
+ scanErr = line.pickedLotNo?.some((lotNo) => Boolean(lotNo.isScanned) === false) ?? false // default false
+ }
+ })
+
+ return {
+ qtyErr: qtyErr,
+ scanErr: scanErr
+ }
+ }, [pickLines])
+
+ return (
+
+ {status === "planning" && (
+ }
+ onClick={handleRelease}
+ >
+ {t("Release")}
+
+ )}
+ {status === "pending" && (
+
+ }
+ onClick={handleStart}
+ disabled={errors.qtyErr || errors.scanErr}
+ >
+ {t("Start Job Order")}
+
+ {errors.scanErr && ({t("Please scan the item qr code.")})}
+ {errors.qtyErr && ({t("Please make sure the qty is enough.")})}
+
+ )}
+
+ )
+}
+
+export default ActionButtons;
\ No newline at end of file
diff --git a/src/components/JoSave/JoSave.tsx b/src/components/JoSave/JoSave.tsx
index fd86fcb..000768c 100644
--- a/src/components/JoSave/JoSave.tsx
+++ b/src/components/JoSave/JoSave.tsx
@@ -4,13 +4,16 @@ import { useRouter } from "next/navigation";
import { useTranslation } from "react-i18next";
import useUploadContext from "../UploadProvider/useUploadContext";
import { FormProvider, SubmitErrorHandler, SubmitHandler, useForm } from "react-hook-form";
-import { useCallback, useState } from "react";
+import React, { useCallback, useEffect, useLayoutEffect, useMemo, useState } from "react";
import { Button, Stack, Typography } from "@mui/material";
import StartIcon from "@mui/icons-material/Start";
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import { releaseJo } from "@/app/api/jo/actions";
import InfoCard from "./InfoCard";
import PickTable from "./PickTable";
+import ActionButtons from "./ActionButtons";
+import { useQrCodeScannerContext } from "../QrCodeScannerProvider/QrCodeScannerProvider";
+import { fetchStockInLineInfo } from "@/app/api/po/actions";
type Props = {
id?: number;
@@ -24,12 +27,92 @@ const JoSave: React.FC = ({
const { t } = useTranslation("jo")
const router = useRouter();
const { setIsUploading } = useUploadContext();
+ const scanner = useQrCodeScannerContext()
const [serverError, setServerError] = useState("");
+ const finalDefaultValues = useMemo(() => {
+ const values = {
+ ...defaultValues,
+ pickLines: defaultValues?.pickLines?.map(line => ({
+ ...line,
+ pickedLotNo: Array.isArray(line.pickedLotNo)
+ ? line.pickedLotNo.map(lot => ({
+ ...lot,
+ isScanned: false
+ }))
+ : line.pickedLotNo
+ }))
+ }
+
+ return values;
+ }, [defaultValues])
+
const formProps = useForm({
- defaultValues: defaultValues
+ defaultValues: finalDefaultValues
})
+ const pickLines = useMemo(() => {
+ return formProps.watch("pickLines")
+ }, [formProps.watch("pickLines")])
+
+ // --------------------------------------------- Qr Code Scan --------------------------------------------- //
+ const needScan = useMemo(() => {
+ return pickLines.some((line) => line.status === "pending")
+ }, [pickLines.some((line) => line.status === "pending")])
+
+ useLayoutEffect(() => {
+ if (needScan && !scanner.isScanning) {
+ scanner.startScan();
+ } else if (!needScan) {
+ scanner.stopScan();
+ }
+ }, [needScan])
+
+ const checkScannedId = useCallback(async (stockInLineId: number | undefined) => {
+ try {
+ setIsUploading(true)
+ if (stockInLineId) {
+ const response = await fetchStockInLineInfo(stockInLineId);
+ // const pickLines = formProps.watch("pickLines")
+ const pickedLotNoIndex = pickLines.findIndex((line) => line.pickedLotNo?.some((pln) => pln.lotNo === response?.lotNo))
+ if (pickedLotNoIndex >= 0) {
+ const updatedPickLines = [...pickLines]
+ const matchedLotNoIndex = updatedPickLines[pickedLotNoIndex].pickedLotNo?.findIndex((line) => line?.lotNo === response?.lotNo && Boolean(line?.isScanned) === false)
+ if (matchedLotNoIndex !== null && matchedLotNoIndex !== undefined && matchedLotNoIndex >= 0) {
+ const updatedPickedLotNo = [...(updatedPickLines[pickedLotNoIndex].pickedLotNo || [])]
+ updatedPickedLotNo[matchedLotNoIndex] = {
+ ...updatedPickedLotNo[matchedLotNoIndex],
+ isScanned: true,
+ }
+
+ updatedPickLines[pickedLotNoIndex] = {
+ ...updatedPickLines[pickedLotNoIndex],
+ pickedLotNo: updatedPickedLotNo,
+ };
+
+ formProps.setValue("pickLines", updatedPickLines, {
+ shouldValidate: true,
+ shouldDirty: true,
+ });
+ }
+ }
+ }
+ } finally {
+ scanner.resetScan()
+ setIsUploading(false)
+ }
+ }, [])
+ useEffect(() => {
+ const result = scanner.result
+ console.log(scanner.result)
+ if (result?.value) {
+ if (!isNaN(Number(result.value))) { checkScannedId(Number(result?.value)); }
+ } else if (result?.stockInLineId) {
+ checkScannedId(result?.stockInLineId)
+ }
+ }, [scanner.result])
+
+ // --------------------------------------------- Button Actions --------------------------------------------- //
const handleBack = useCallback(() => {
router.replace(`/jo`)
}, [])
@@ -38,12 +121,9 @@ const JoSave: React.FC = ({
try {
setIsUploading(true)
if (id) {
- console.log(id)
const response = await releaseJo({ id: id })
- console.log(response.entity.status)
if (response) {
formProps.setValue("status", response.entity.status)
- console.log(formProps.watch("status"))
}
}
@@ -56,6 +136,11 @@ const JoSave: React.FC = ({
}
}, [])
+ const handleStart = useCallback(async () => {
+ console.log("first")
+ }, [])
+
+ // --------------------------------------------- Form Submit --------------------------------------------- //
const onSubmit = useCallback>(async (data, event) => {
console.log(data)
}, [t])
@@ -76,18 +161,7 @@ const JoSave: React.FC = ({
{serverError}
)}
- {
- formProps.watch("status").toLowerCase() === "planning" && (
-
- }
- onClick={handleRelease}
- >
- {t("Release")}
-
-
- )}
+
diff --git a/src/components/JoSave/JoSaveWrapper.tsx b/src/components/JoSave/JoSaveWrapper.tsx
index edc7b4e..4be64e0 100644
--- a/src/components/JoSave/JoSaveWrapper.tsx
+++ b/src/components/JoSave/JoSaveWrapper.tsx
@@ -17,7 +17,7 @@ const JoSaveWrapper: React.FC & SubComponents = async ({
id,
}) => {
const jo = id ? await fetchJoDetail(id) : undefined
-
+
return
}
diff --git a/src/components/JoSave/PickTable.tsx b/src/components/JoSave/PickTable.tsx
index 7946492..064b946 100644
--- a/src/components/JoSave/PickTable.tsx
+++ b/src/components/JoSave/PickTable.tsx
@@ -1,11 +1,15 @@
-import { JoDetail } from "@/app/api/jo";
+import { JoDetail, JoDetailPickLine } from "@/app/api/jo";
import { decimalFormatter } from "@/app/utils/formatUtil";
-import { GridColDef } from "@mui/x-data-grid";
+import { GridColDef, GridRenderCellParams, GridValidRowModel } from "@mui/x-data-grid";
import { isEmpty, upperFirst } from "lodash";
-import { useMemo } from "react";
+import { useCallback, useMemo } from "react";
import { useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next";
import StyledDataGrid from "../StyledDataGrid/StyledDataGrid";
+import { Box, Grid, Icon, IconButton, Stack, Typography } from "@mui/material";
+import PendingOutlinedIcon from '@mui/icons-material/PendingOutlined';
+import CheckCircleOutlineOutlinedIcon from '@mui/icons-material/CheckCircleOutlineOutlined';
+import HelpOutlineOutlinedIcon from '@mui/icons-material/HelpOutlineOutlined';
type Props = {
@@ -19,33 +23,74 @@ const PickTable: React.FC = ({
watch
} = useFormContext()
+ const notPickedStatusColumn = useMemo(() => {
+ return ()
+ }, [])
+ const scanStatusColumn = useCallback((status: boolean) => {
+ return status ?
+
+ :
+ }, [])
+
const columns = useMemo(() => [
{
field: "code",
headerName: t("Code"),
- flex: 1,
+ flex: 0.6,
},
{
field: "name",
headerName: t("Name"),
flex: 1,
},
+ {
+ field: "scanStatus",
+ headerName: t("Scan Status"),
+ flex: 0.4,
+ align: "right",
+ headerAlign: "right",
+ renderCell: (params: GridRenderCellParams) => {
+ if (params.row.pickedLotNo === null || params.row.pickedLotNo === undefined) {
+ return notPickedStatusColumn
+ }
+ const scanStatus = params.row.pickedLotNo.map((pln) => Boolean(pln.isScanned))
+ return isEmpty(scanStatus) ? notPickedStatusColumn : {scanStatus.map((status) => scanStatusColumn(status))}
+ },
+ },
{
field: "lotNo",
headerName: t("Lot No."),
flex: 1,
- renderCell: (row) => {
- return isEmpty(row.value) ? "N/A" : row.value
+ renderCell: (params: GridRenderCellParams) => {
+ if (params.row.pickedLotNo === null || params.row.pickedLotNo === undefined) {
+ return t("Pending for pick")
+ }
+ const lotNos = params.row.pickedLotNo.map((pln) => pln.lotNo)
+ return isEmpty(lotNos) ? t("Pending for pick") : lotNos.map((lotNo) => (<>{lotNo}
>))
+ },
+ },
+ {
+ field: "pickedQty",
+ headerName: t("Picked Qty"),
+ flex: 0.7,
+ align: "right",
+ headerAlign: "right",
+ renderCell: (params: GridRenderCellParams) => {
+ if (params.row.pickedLotNo === null || params.row.pickedLotNo === undefined) {
+ return t("Pending for pick")
+ }
+ const qtys = params.row.pickedLotNo.map((pln) => pln.qty)
+ return isEmpty(qtys) ? t("Pending for pick") : qtys.map((qty) => <>{qty}
>)
},
},
{
field: "reqQty",
headerName: t("Req. Qty"),
- flex: 1,
+ flex: 0.7,
align: "right",
headerAlign: "right",
- renderCell: (row) => {
- return decimalFormatter.format(row.value)
+ renderCell: (params: GridRenderCellParams) => {
+ return decimalFormatter.format(params.value)
},
},
{
@@ -59,8 +104,15 @@ const PickTable: React.FC = ({
field: "status",
headerName: t("Status"),
flex: 1,
- renderCell: (row) => {
- return t(upperFirst(row.value))
+ align: "right",
+ headerAlign: "right",
+ renderCell: (params: GridRenderCellParams) => {
+ return (
+ <>
+ {params.row.pickedLotNo?.every((lotNo) => Boolean(lotNo.isScanned)) ? t("Scanned") : t(upperFirst(params.value))}
+ {scanStatusColumn(Boolean(params.row.pickedLotNo?.every((lotNo) => Boolean(lotNo.isScanned))))}
+ >
+ )
},
},
], [])
@@ -82,6 +134,7 @@ const PickTable: React.FC = ({
disableColumnMenu
rows={watch("pickLines")}
columns={columns}
+ getRowHeight={() => 'auto'}
/>
>
)
diff --git a/src/components/NavigationContent/NavigationContent.tsx b/src/components/NavigationContent/NavigationContent.tsx
index 9b1f6a6..e221bea 100644
--- a/src/components/NavigationContent/NavigationContent.tsx
+++ b/src/components/NavigationContent/NavigationContent.tsx
@@ -295,6 +295,11 @@ const NavigationContent: React.FC = () => {
label: "Import Testing",
path: "/settings/m18ImportTesting",
},
+ {
+ icon: ,
+ label: "Import Excel",
+ path: "/settings/importExcel",
+ },
],
},
];
diff --git a/src/i18n/zh/do.json b/src/i18n/zh/do.json
index e1eda74..7ee021d 100644
--- a/src/i18n/zh/do.json
+++ b/src/i18n/zh/do.json
@@ -41,7 +41,11 @@
"Delete": "刪除",
"Release": "放單",
"Back": "返回",
+<<<<<<< HEAD
"Batch Release": "批量放單",
"Batch release completed successfully.": "已完成批量放單"
+=======
+ "Edit Delivery Order Detail": "編輯交貨單詳情"
+>>>>>>> 99400f68e2291628aae3d93b1a901a41ff80c570
}
\ No newline at end of file
diff --git a/src/i18n/zh/jo.json b/src/i18n/zh/jo.json
index 6ba8158..0ed0760 100644
--- a/src/i18n/zh/jo.json
+++ b/src/i18n/zh/jo.json
@@ -1,19 +1,21 @@
{
- "Job Order": "工單",
- "Create Job Order": "創建工單",
- "Edit Job Order Detail": "編輯工單",
-
- "Details": "細節",
- "Code": "編號",
- "Name": "名稱",
- "Req. Qty": "需求數量",
- "UoM": "單位",
- "Status": "來貨狀態",
- "Lot No.": "批號",
- "Bom": "物料清單",
-
- "Release": "發佈",
-
- "Pending": "待提料",
- "Planning": "計劃中"
-}
\ No newline at end of file
+ "Job Order": "工單",
+ "Create Job Order": "創建工單",
+ "Edit Job Order Detail": "編輯工單",
+ "Details": "細節",
+ "Code": "編號",
+ "Name": "名稱",
+ "Picked Qty": "已提料數量",
+ "Req. Qty": "需求數量",
+ "UoM": "單位",
+ "Status": "來貨狀態",
+ "Lot No.": "批號",
+ "Bom": "物料清單",
+ "Release": "發佈",
+ "Pending": "待掃碼",
+ "Pending for pick": "待提料",
+ "Planning": "計劃中",
+ "Scanned": "已掃碼",
+ "Scan Status": "掃碼狀態",
+ "Start Job Order": "開始工單"
+}
diff --git a/src/i18n/zh/pickOrder.json b/src/i18n/zh/pickOrder.json
index ee124f9..a814f08 100644
--- a/src/i18n/zh/pickOrder.json
+++ b/src/i18n/zh/pickOrder.json
@@ -262,7 +262,20 @@
"Stop QR Scan":"停止QR掃描",
"Scanning...":"掃描中...",
"Print DN/Label":"列印送貨單/標籤",
- "Store ID":"店鋪編號",
- "QR code does not match any item in current orders.":"QR 碼不符合當前訂單中的任何貨品。"
+ "Store ID":"儲存編號",
+ "QR code does not match any item in current orders.":"QR 碼不符合當前訂單中的任何貨品。",
+ "Lot Number Mismatch":"批次號碼不符",
+ "The scanned item matches the expected item, but the lot number is different. Do you want to proceed with this different lot?":"掃描的貨品與預期的貨品相同,但批次號碼不同。您是否要繼續使用不同的批次?",
+ "Expected Lot:":"預期批次:",
+ "Scanned Lot:":"掃描批次:",
+ "Confirm":"確認",
+ "Update your suggested lot to the this scanned lot":"更新您的建議批次為此掃描的批次",
+ "Print Draft":"列印草稿",
+ "Print Pick Order and DN Label":"列印提料單和送貨單標貼",
+ "Print Pick Order":"列印提料單",
+ "Print DN Label":"列印送貨單標貼"
+
+
+
}
\ No newline at end of file