From 30bdc0fefa1f0c668ade23474f1e45e679a1c752 Mon Sep 17 00:00:00 2001 From: "cyril.tsui" Date: Tue, 23 Sep 2025 18:43:28 +0800 Subject: [PATCH 1/2] [Stock Take & Job Order] Import stock take excel & update jo --- src/app/(main)/settings/importExcel/page.tsx | 34 ++++++ src/app/api/jo/actions.ts | 6 +- src/app/api/jo/index.ts | 15 ++- src/app/api/stockTake/actions.ts | 16 +++ src/app/utils/fetchUtil.ts | 19 +++ .../ExcelFileImport/ExcelFileImport.tsx | 88 ++++++++++++++ .../ExcelFileImportWrapper.tsx | 10 ++ src/components/ExcelFileImport/index.ts | 1 + src/components/JoSave/ActionButtons.tsx | 86 ++++++++++++++ src/components/JoSave/JoSave.tsx | 108 +++++++++++++++--- src/components/JoSave/JoSaveWrapper.tsx | 2 +- src/components/JoSave/PickTable.tsx | 75 ++++++++++-- .../NavigationContent/NavigationContent.tsx | 5 + src/i18n/zh/jo.json | 38 +++--- 14 files changed, 450 insertions(+), 53 deletions(-) create mode 100644 src/app/(main)/settings/importExcel/page.tsx create mode 100644 src/app/api/stockTake/actions.ts create mode 100644 src/components/ExcelFileImport/ExcelFileImport.tsx create mode 100644 src/components/ExcelFileImport/ExcelFileImportWrapper.tsx create mode 100644 src/components/ExcelFileImport/index.ts create mode 100644 src/components/JoSave/ActionButtons.tsx 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 ( + + + + ) + }) + } + + + ); +}; + +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/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" && ( + + )} + {status === "pending" && ( + + + {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" && ( - - - - )} + 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/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": "開始工單" +} From 99400f68e2291628aae3d93b1a901a41ff80c570 Mon Sep 17 00:00:00 2001 From: "CANCERYS\\kw093" Date: Wed, 24 Sep 2025 19:48:17 +0800 Subject: [PATCH 2/2] update --- .../FinishedGoodSearch/FinishedGoodSearch.tsx | 72 +++++++++++-------- .../GoodPickExecutiondetail.tsx | 27 +++++-- .../LotConfirmationModal.tsx | 4 +- src/i18n/zh/do.json | 4 +- src/i18n/zh/pickOrder.json | 17 ++++- 5 files changed, 82 insertions(+), 42 deletions(-) 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/i18n/zh/do.json b/src/i18n/zh/do.json index 2720aab..7873dae 100644 --- a/src/i18n/zh/do.json +++ b/src/i18n/zh/do.json @@ -40,6 +40,6 @@ "Edit": "編輯", "Delete": "刪除", "Release": "放單", - "Back": "返回" - + "Back": "返回", + "Edit Delivery Order Detail": "編輯交貨單詳情" } \ No newline at end of file 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