From b0fa2ac4d5e2ac6da0551ef69f338cd2d4867749 Mon Sep 17 00:00:00 2001 From: kelvinsuen Date: Thu, 25 Sep 2025 12:55:29 +0800 Subject: [PATCH] update stock in modal & po --- src/app/api/po/actions.ts | 23 +- src/app/api/po/index.ts | 4 +- src/app/api/qc/index.ts | 8 + src/app/api/stockIn/actions.ts | 243 +++++++++ src/app/api/stockIn/index.ts | 168 ++++++ src/app/utils/formatUtil.ts | 15 +- .../PoDetail/EscalationComponent.tsx | 2 +- src/components/PoDetail/EscalationForm.tsx | 4 +- src/components/PoDetail/PoDetail.tsx | 27 +- src/components/PoDetail/PoDetailWrapper.tsx | 6 +- src/components/PoDetail/PoInputGrid.tsx | 148 ++---- src/components/PoDetail/PutAwayForm.tsx | 124 ++--- src/components/PoDetail/QcComponent.tsx | 288 +++++----- src/components/PoDetail/QcStockInModal.tsx | 495 +++++++++--------- src/components/PoDetail/StockInForm.tsx | 65 ++- src/components/PoSearch/PoSearch.tsx | 7 +- src/components/PutAwayScan/PutAwayModal.tsx | 11 +- src/components/PutAwayScan/PutAwayScan.tsx | 8 +- src/components/SearchBox/SearchBox.tsx | 21 +- src/i18n/zh/purchaseOrder.json | 6 +- 20 files changed, 1005 insertions(+), 668 deletions(-) create mode 100644 src/app/api/stockIn/actions.ts create mode 100644 src/app/api/stockIn/index.ts diff --git a/src/app/api/po/actions.ts b/src/app/api/po/actions.ts index 6455eff..cdbfbc4 100644 --- a/src/app/api/po/actions.ts +++ b/src/app/api/po/actions.ts @@ -4,13 +4,14 @@ import { BASE_API_URL } from "../../../config/api"; // import { ServerFetchError, serverFetchJson, serverFetchWithNoContent } from "@/app/utils/fetchUtil"; import { revalidateTag } from "next/cache"; import { cache } from "react"; -import { PoResult, StockInLine } from "."; +import { PoResult } from "."; //import { serverFetchJson } from "@/app/utils/fetchUtil"; import { serverFetchJson, serverFetchWithNoContent } from "../../utils/fetchUtil"; import { QcItemResult } from "../settings/qcItem"; import { RecordsRes } from "../utils"; import { Uom } from "../settings/uom"; import { convertObjToURLSearchParams } from "@/app/utils/commonUtil"; +import { StockInLine } from "../stockIn"; // import { BASE_API_URL } from "@/config/api"; export interface PostStockInLineResponse { @@ -24,11 +25,12 @@ export interface PostStockInLineResponse { // entity: StockInLine | StockInLine[] } +// DEPRECIATED export interface StockInLineEntry { id?: number; itemId: number; - purchaseOrderId: number; - purchaseOrderLineId: number; + purchaseOrderId?: number; + purchaseOrderLineId?: number; acceptedQty: number; status?: string; expiryDate?: string; @@ -38,6 +40,7 @@ export interface StockInLineEntry { dnNo?: string; } +// DEPRECIATED export interface PurchaseQcResult{ id?: number; qcItemId: number; @@ -46,6 +49,7 @@ export interface PurchaseQcResult{ remarks?: string; escalationLogId?: number; } +// DEPRECIATED export interface StockInInput { status: string; poCode: string; @@ -53,6 +57,7 @@ export interface StockInInput { dnNo?: string; dnDate?: string; itemName: string; + lotNo?: string; invoiceNo?: string; receiptDate: string; supplier: string; @@ -64,6 +69,7 @@ export interface StockInInput { expiryDate: string; uom: Uom; } +// DEPRECIATED export interface PurchaseQCInput { status: string; acceptQty: number; @@ -75,6 +81,7 @@ export interface PurchaseQCInput { qcDecision?: number; qcResult: PurchaseQcResult[]; } +// DEPRECIATED export interface EscalationInput { status: string; remarks?: string; @@ -84,6 +91,8 @@ export interface EscalationInput { acceptedQty?: number; // this is the qty to be escalated // escalationQty: number } + +// DEPRECIATED export interface PutAwayLine { id?: number qty: number @@ -92,6 +101,8 @@ export interface PutAwayLine { printQty: number; _isNew?: boolean; } + +// DEPRECIATED export interface PutAwayInput { status: string; acceptedQty: number; @@ -99,12 +110,14 @@ export interface PutAwayInput { putAwayLines: PutAwayLine[] } +// DEPRECIATED export type ModalFormInput = Partial< PurchaseQCInput & StockInInput & PutAwayInput > & { escalationLog? : Partial }; +// DEPRECIATED export interface PrintQrCodeForSilRequest { stockInLineId: number; printerId: number; @@ -117,6 +130,7 @@ export const testFetch = cache(async (id: number) => { }); }); +// DEPRECIATED export const fetchStockInLineInfo = cache(async (stockInLineId: number) => { return serverFetchJson( `${BASE_API_URL}/stockInLine/${stockInLineId}`, @@ -126,6 +140,7 @@ export const fetchStockInLineInfo = cache(async (stockInLineId: number) => { ); }); +// DEPRECIATED export const createStockInLine = async (data: StockInLineEntry) => { const stockInLine = await serverFetchJson< PostStockInLineResponse @@ -138,6 +153,7 @@ export const createStockInLine = async (data: StockInLineEntry) => { return stockInLine; }; +// DEPRECIATED export const updateStockInLine = async ( data: StockInLineEntry & ModalFormInput, ) => { @@ -228,6 +244,7 @@ export const testing = cache(async (queryParams?: Record) => { } }); +// DEPRECIATED export const printQrCodeForSil = cache(async(data: PrintQrCodeForSilRequest) => { const params = convertObjToURLSearchParams(data) return serverFetchWithNoContent(`${BASE_API_URL}/stockInLine/printQrCode?${params}`, diff --git a/src/app/api/po/index.ts b/src/app/api/po/index.ts index f7a312a..b09ca3e 100644 --- a/src/app/api/po/index.ts +++ b/src/app/api/po/index.ts @@ -7,6 +7,7 @@ import { BASE_API_URL } from "../../../config/api"; import { Uom } from "../settings/uom"; import { RecordsRes } from "../utils"; import { PutAwayLine } from "./actions"; +import { StockInLine } from "../stockIn"; export enum StockInStatus { PENDING = "pending", @@ -60,7 +61,8 @@ export interface StockUomForPoLine { purchaseRatioD: number; } -export interface StockInLine { +// DEPRECIATED +export interface StockInLine_old { id: number; stockInId?: number; purchaseOrderId?: number; diff --git a/src/app/api/qc/index.ts b/src/app/api/qc/index.ts index 95ffefd..d178c2a 100644 --- a/src/app/api/qc/index.ts +++ b/src/app/api/qc/index.ts @@ -15,6 +15,14 @@ export interface QcItemWithChecks { description: string | undefined; } +export interface QcResult{ + id?: number; + qcItemId: number; + qcPassed?: boolean; + failQty?: number; + remarks?: string; + escalationLogId?: number; +} export interface QcData { id?: number, qcItemId: number, diff --git a/src/app/api/stockIn/actions.ts b/src/app/api/stockIn/actions.ts new file mode 100644 index 0000000..61a26da --- /dev/null +++ b/src/app/api/stockIn/actions.ts @@ -0,0 +1,243 @@ +"use server"; +// import { BASE_API_URL } from "@/config/api"; +import { BASE_API_URL } from "../../../config/api"; +// import { ServerFetchError, serverFetchJson, serverFetchWithNoContent } from "@/app/utils/fetchUtil"; +import { revalidateTag } from "next/cache"; +import { cache } from "react"; +import { PoResult, StockInLine } from "."; +//import { serverFetchJson } from "@/app/utils/fetchUtil"; +import { serverFetchJson, serverFetchWithNoContent } from "../../utils/fetchUtil"; +import { QcItemResult } from "../settings/qcItem"; +import { RecordsRes } from "../utils"; +import { Uom } from "../settings/uom"; +import { convertObjToURLSearchParams } from "@/app/utils/commonUtil"; +// import { BASE_API_URL } from "@/config/api"; + +export interface PostStockInLineResponse { + id: number | null; + name: string; + code: string; + type?: string; + message: string | null; + errorPosition: string | keyof T; + entity: T | T[]; + // entity: StockInLine | StockInLine[] +} + +export interface StockInLineEntry { + id?: number; + itemId: number; + acceptedQty: number; + purchaseOrderId?: number; + purchaseOrderLineId?: number; + status?: string; + expiryDate?: string; + productLotNo?: string; + receiptDate?: string; + dnDate?: string; + dnNo?: string; +} + +export interface PurchaseQcResult{ + id?: number; + qcItemId: number; + qcPassed?: boolean; + failQty?: number; + remarks?: string; + escalationLogId?: number; +} +export interface StockInInput { + status: string; + poCode: string; + productLotNo?: string; + dnNo?: string; + dnDate?: string; + itemName: string; + lotNo?: string; + invoiceNo?: string; + receiptDate: string; + supplier: string; + acceptedQty: number; + qty: number; + receivedQty: number; + acceptedWeight?: number; + productionDate?: string; + expiryDate: string; + uom: Uom; +} +export interface PurchaseQCInput { + status: string; + acceptQty: number; + passingQty: number; + sampleRate?: number; + sampleWeight?: number; + totalWeight?: number; + qcAccept: boolean; + qcDecision?: number; + qcResult: PurchaseQcResult[]; +} +export interface EscalationInput { + status: string; + remarks?: string; + reason?: string; + handlerId: number; + productLotNo?: string; + acceptedQty?: number; // this is the qty to be escalated + // escalationQty: number +} +export interface PutAwayLine { + id?: number + qty: number + warehouseId: number; + warehouse: string; + printQty: number; + _isNew?: boolean; +} +export interface PutAwayInput { + status: string; + acceptedQty: number; + warehouseId: number; + putAwayLines: PutAwayLine[] +} + +export type ModalFormInput = Partial< + PurchaseQCInput & StockInInput & PutAwayInput +> & { + escalationLog? : Partial +}; + +export interface PrintQrCodeForSilRequest { + stockInLineId: number; + printerId: number; + printQty?: number; +} + +export const testFetch = cache(async (id: number) => { + return serverFetchJson(`${BASE_API_URL}/po/detail/${id}`, { + next: { tags: ["po"] }, + }); +}); + +export const fetchStockInLineInfo = cache(async (stockInLineId: number) => { + return serverFetchJson( + `${BASE_API_URL}/stockInLine/${stockInLineId}`, + { + next: { tags: ["stockInLine"] }, + }, + ); +}); + +export const createStockInLine = async (data: StockInLineEntry) => { + const stockInLine = await serverFetchJson< + PostStockInLineResponse + >(`${BASE_API_URL}/stockInLine/create`, { + method: "POST", + body: JSON.stringify(data), + headers: { "Content-Type": "application/json" }, + }); + // revalidateTag("po"); + return stockInLine; +}; + +export const updateStockInLine = async ( + data: StockInLineEntry & ModalFormInput, +) => { + const stockInLine = await serverFetchJson< + PostStockInLineResponse + >(`${BASE_API_URL}/stockInLine/update`, { + method: "POST", + body: JSON.stringify(data), + headers: { "Content-Type": "application/json" }, + }); + // revalidateTag("po"); + return stockInLine; +}; + +export const startPo = async (poId: number) => { + const po = await serverFetchJson>( + `${BASE_API_URL}/po/start/${poId}`, + { + method: "POST", + body: JSON.stringify({ poId }), + headers: { "Content-Type": "application/json" }, + }, + ); + revalidateTag("po"); + return po; +}; + +export const checkPolAndCompletePo = async (poId: number) => { + const po = await serverFetchJson>( + `${BASE_API_URL}/po/check/${poId}`, + { + method: "POST", + body: JSON.stringify({ poId }), + headers: { "Content-Type": "application/json" }, + }, + ); + revalidateTag("po"); + return po; +}; + +export const fetchPoInClient = cache(async (id: number) => { + return serverFetchJson(`${BASE_API_URL}/po/detail/${id}`, { + next: { tags: ["po"] }, + }); +}); + +export const fetchPoListClient = cache( + async (queryParams?: Record) => { + if (queryParams) { + const queryString = new URLSearchParams(queryParams).toString(); + return serverFetchJson>( + `${BASE_API_URL}/po/list?${queryString}`, + { + method: "GET", + next: { tags: ["po"] }, + }, + ); + } else { + return serverFetchJson>( + `${BASE_API_URL}/po/list`, + { + method: "GET", + next: { tags: ["po"] }, + }, + ); + } + }, +); + +export const testing = cache(async (queryParams?: Record) => { + if (queryParams) { + const queryString = new URLSearchParams(queryParams).toString(); + return serverFetchJson>( + `${BASE_API_URL}/po/testing?${queryString}`, + { + method: "GET", + next: { tags: ["po"] }, + }, + ); + } else { + return serverFetchJson>( + `${BASE_API_URL}/po/testing`, + { + method: "GET", + next: { tags: ["po"] }, + }, + ); + } +}); + +export const printQrCodeForSil = cache(async(data: PrintQrCodeForSilRequest) => { + const params = convertObjToURLSearchParams(data) + return serverFetchWithNoContent(`${BASE_API_URL}/stockInLine/printQrCode?${params}`, + { + method: "GET", + headers: { "Content-Type": "application/json" }, + next: { + tags: ["printQrCodeForSil"], + }, + }, + ) +}) diff --git a/src/app/api/stockIn/index.ts b/src/app/api/stockIn/index.ts new file mode 100644 index 0000000..9ef5cec --- /dev/null +++ b/src/app/api/stockIn/index.ts @@ -0,0 +1,168 @@ +import { cache } from "react"; +import "server-only"; +// import { serverFetchJson } from "@/app/utils/fetchUtil"; +// import { BASE_API_URL } from "@/config/api"; +import { serverFetchJson } from "../../utils/fetchUtil"; +import { BASE_API_URL } from "../../../config/api"; +import { Uom } from "../settings/uom"; +import { RecordsRes } from "../utils"; +import { QcResult } from "../qc"; +import { EscalationResult } from "../escalation"; + +export enum StockInStatus { + PENDING = "pending", + RECEIVED = "received", + APPROVED = "escalated", + REJECTED = "rejected", + COMPLETED = "completed", + PARTIALLY_COMPLETED = "partially_completed", +} + +export interface StockInLineInput { + id?: number; + itemId?: number; + acceptedQty?: number; + receivedQty?: number; + status?: string; + expiryDate?: string; + productLotNo?: string; + receiptDate?: string; + dnDate?: number[]; + dnNo?: string; +} + +export interface StockInInput { + stockInId?: number; + poCode?: string; + productLotNo?: string; + dnNo?: string; + dnDate?: string; + itemName?: string; + lotNo?: string; + invoiceNo?: string; + receiptDate: string; + supplier?: string; + acceptedQty: number; + qty: number; + receivedQty: number; + acceptedWeight?: number; + productionDate?: string; + expiryDate: string; + uom?: Uom; +} + +export interface PoResult { + id: number; + code: string; + orderDate: string; + supplier: string; + estimatedArrivalDate: string; + completedDate: string; + itemDetail?: string; + itemCode?: string; + itemName?: string; + itemQty?: string; + itemSumAcceptedQty?: string; + itemUom?: string; + escalated: boolean; + status: string; + pol?: PurchaseOrderLine[]; +} + +export interface PurchaseOrderLine { + id: number; + purchaseOrderId: number; + itemId: number; + itemNo: string; + itemName: string; + qty: number; + processed: number; + receivedQty: number; + uom: Uom; + stockUom: StockUomForPoLine; + price: number; + status: string; + stockInLine: StockInLine[]; +} + +export interface StockUomForPoLine { + id: number; + stockUomCode: string; + stockUomDesc: string; + stockQty: number; + stockRatioN: number; + stockRatioD: number; + purchaseRatioN: number; + purchaseRatioD: number; +} + +export interface StockInLine { + id: number; + stockInId?: number; + purchaseOrderId?: number; + purchaseOrderLineId: number; + itemId: number; + itemNo: string; + itemName: string; + itemType: string; + demandQty: number; + acceptedQty: number; + qty?: number; + receivedQty?: number; + processed?: number; + price?: number; + priceUnit?: string; + shelfLife?: number; + receiptDate?: string; + productionDate?: string; + productLotNo?: string; + expiryDate?: string; + status: string; + supplier?: string; + lotNo?: string; + poCode?: string; + uom?: Uom; + defaultWarehouseId: number; // id for now + dnNo?: string; + dnDate?: number[]; + stockQty?: number; + handlerId?: number; + putAwayLines?: PutAwayLine[]; + qcResult?: QcResult[]; + escResult?: EscalationResult[]; +} + +export interface EscalationInput { + status: string; + remarks?: string; + reason?: string; + handlerId: number; + productLotNo?: string; + acceptedQty?: number; // this is the qty to be escalated + // escalationQty: number +} +export interface PutAwayLine { + id?: number + qty: number + warehouseId: number; + warehouse: string; + printQty: number; + _isNew?: boolean; +} +export interface PutAwayInput { + status: string; + acceptedQty: number; + warehouseId: number; + putAwayLines: PutAwayLine[] +} +export interface QcInput { + acceptQty: number; + qcAccept: boolean; + qcDecision?: number; + qcResult: QcResult[]; +} +export type ModalFormInput = Partial< + QcInput & StockInInput & PutAwayInput +> & { + escalationLog? : Partial +}; \ No newline at end of file diff --git a/src/app/utils/formatUtil.ts b/src/app/utils/formatUtil.ts index ca728de..a077da2 100644 --- a/src/app/utils/formatUtil.ts +++ b/src/app/utils/formatUtil.ts @@ -86,11 +86,11 @@ export const dayjsToDateString = (date: Dayjs) => { return date.format(OUTPUT_DATE_FORMAT); }; -export const dayjsToInputDateString = (date: Dayjs) => { +export const dayjsToInputDateString = (date: Dayjs) => { // TODO fix remove the time format (need global check) return date.format(INPUT_DATE_FORMAT + "T" + INPUT_TIME_FORMAT); }; -export const dayjsToInputDatetimeString = (date: Dayjs) => { +export const dayjsToInputDateStringFIX = (date: Dayjs) => { // TODO fix it after the above one is fixed return date.format(INPUT_DATE_FORMAT); }; @@ -98,6 +98,17 @@ export const dayjsToInputDateTimeString = (date: Dayjs) => { return date.format(`${INPUT_DATE_FORMAT}T${OUTPUT_TIME_FORMAT}`); }; +export const dayjsToArray = (date: Dayjs) => { + return [ + date.year(), + date.month() + 1, // Months are 0-based in Day.js, so add 1 + date.date(), + date.hour(), // (24-hour format) + date.minute(), + date.second(), + ]; +}; + export const outputDateStringToInputDateString = (date: string) => { return dayjsToInputDateString(dateStringToDayjs(date)) } diff --git a/src/components/PoDetail/EscalationComponent.tsx b/src/components/PoDetail/EscalationComponent.tsx index 30e215c..f9cfab0 100644 --- a/src/components/PoDetail/EscalationComponent.tsx +++ b/src/components/PoDetail/EscalationComponent.tsx @@ -22,7 +22,7 @@ import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import ExpandLessIcon from '@mui/icons-material/ExpandLess'; import { useTranslation } from 'react-i18next'; import { Controller, useFormContext } from 'react-hook-form'; -import { EscalationInput, ModalFormInput } from '@/app/api/po/actions'; +import { EscalationInput, ModalFormInput } from '@/app/api/stockIn/actions'; import { EscalationCombo } from "@/app/api/user"; import { fetchEscalationCombo } from "@/app/api/user/actions"; import { FireExtinguisher } from '@mui/icons-material'; diff --git a/src/components/PoDetail/EscalationForm.tsx b/src/components/PoDetail/EscalationForm.tsx index a8b9cd3..25eae0e 100644 --- a/src/components/PoDetail/EscalationForm.tsx +++ b/src/components/PoDetail/EscalationForm.tsx @@ -1,6 +1,6 @@ "use client"; -import { StockInLineEntry, EscalationInput } from "@/app/api/po/actions"; +import { StockInLineEntry, EscalationInput } from "@/app/api/stockIn/actions"; import { Box, Card, @@ -30,7 +30,7 @@ import TwoLineCell from "./TwoLineCell"; import QcSelect from "./QcSelect"; import { QcItemWithChecks } from "@/app/api/qc"; import { GridEditInputCell } from "@mui/x-data-grid"; -import { StockInLine } from "@/app/api/po"; +import { StockInLine } from "@/app/api/stockIn"; import { stockInLineStatusMap } from "@/app/utils/formatUtil"; interface Props { diff --git a/src/components/PoDetail/PoDetail.tsx b/src/components/PoDetail/PoDetail.tsx index 6836a78..867f04d 100644 --- a/src/components/PoDetail/PoDetail.tsx +++ b/src/components/PoDetail/PoDetail.tsx @@ -4,7 +4,6 @@ import { fetchPoWithStockInLines, PoResult, PurchaseOrderLine, - StockInLine, } from "@/app/api/po"; import { Box, @@ -44,11 +43,11 @@ import { checkPolAndCompletePo, fetchPoInClient, fetchPoListClient, - fetchStockInLineInfo, - PurchaseQcResult, startPo, - createStockInLine } from "@/app/api/po/actions"; +import { + createStockInLine +} from "@/app/api/stockIn/actions"; import { useCallback, useContext, @@ -59,7 +58,7 @@ import { import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp"; import PoInputGrid from "./PoInputGrid"; -import { QcItemWithChecks } from "@/app/api/qc"; +// import { QcItemWithChecks } from "@/app/api/qc"; import { useRouter, useSearchParams, usePathname } from "next/navigation"; import { WarehouseResult } from "@/app/api/warehouse"; import { calculateWeight, dateStringToDayjs, dayjsToDateString, OUTPUT_DATE_FORMAT, outputDateStringToInputDateString, returnWeightUnit } from "@/app/utils/formatUtil"; @@ -81,12 +80,13 @@ import LoadingComponent from "../General/LoadingComponent"; import { getMailTemplatePdfForStockInLine } from "@/app/api/mailTemplate/actions"; import { PrinterCombo } from "@/app/api/settings/printer"; import { EscalationCombo } from "@/app/api/user"; +import { StockInLine } from "@/app/api/stockIn"; //import { useRouter } from "next/navigation"; type Props = { po: PoResult; - qc: QcItemWithChecks[]; + // qc: QcItemWithChecks[]; warehouse: WarehouseResult[]; printerCombo: PrinterCombo[]; }; @@ -191,7 +191,7 @@ interface PolInputResult { dnQty: number, } -const PoDetail: React.FC = ({ po, qc, warehouse, printerCombo }) => { +const PoDetail: React.FC = ({ po, warehouse, printerCombo }) => { const cameras = useContext(CameraContext); // console.log(cameras); const { t } = useTranslation("purchaseOrder"); @@ -236,7 +236,7 @@ const PoDetail: React.FC = ({ po, qc, warehouse, printerCombo }) => { const dnFormProps = useForm({ defaultValues: { dnNo: '', - dnDate: dayjsToDateString(dayjs()) + receiptDate: dayjsToDateString(dayjs()) } }) const fetchPoList = useCallback(async () => { @@ -263,7 +263,6 @@ const PoDetail: React.FC = ({ po, qc, warehouse, printerCombo }) => { const fetchPoDetail = useCallback(async (poId: string) => { try { const result = await fetchPoInClient(parseInt(poId)); - console.log(result) if (result) { console.log("%c Fetched PO:", "color:orange", result); setPurchaseOrder(result); @@ -420,11 +419,11 @@ const PoDetail: React.FC = ({ po, qc, warehouse, printerCombo }) => { const postData = { dnNo: dnFormProps.watch("dnNo"), - dnDate: outputDateStringToInputDateString(dnFormProps.watch("dnDate")), + receiptDate: outputDateStringToInputDateString(dnFormProps.watch("receiptDate")), itemId: row.itemId, itemNo: row.itemNo, itemName: row.itemName, - purchaseOrderId: row.purchaseOrderId, + // purchaseOrderId: row.purchaseOrderId, purchaseOrderLineId: row.id, acceptedQty: acceptedQty, productLotNo: polInputList[rowIndex].lotNo || '', @@ -765,11 +764,11 @@ const PoDetail: React.FC = ({ po, qc, warehouse, printerCombo }) => { > ( { @@ -854,7 +853,7 @@ const PoDetail: React.FC = ({ po, qc, warehouse, printerCombo }) => { & SubComponents = async ({ id }) => { const [ poWithStockInLine, - warehouse, - qc, + warehouse, printerCombo, ] = await Promise.all([ fetchPoWithStockInLines(id), fetchWarehouseList(), - fetchQcItemCheck(), fetchPrinterCombo(), ]); // const poWithStockInLine = await fetchPoWithStockInLines(id) console.log("%c pol:", "color:green", poWithStockInLine); - return ; + return ; }; PoDetailWrapper.Loading = PoDetailLoading; diff --git a/src/components/PoDetail/PoInputGrid.tsx b/src/components/PoDetail/PoInputGrid.tsx index 9e38727..200ee94 100644 --- a/src/components/PoDetail/PoInputGrid.tsx +++ b/src/components/PoDetail/PoInputGrid.tsx @@ -31,10 +31,11 @@ import DeleteIcon from "@mui/icons-material/Delete"; import CancelIcon from "@mui/icons-material/Cancel"; import FactCheckIcon from "@mui/icons-material/FactCheck"; import ShoppingCartIcon from "@mui/icons-material/ShoppingCart"; -import { QcItemWithChecks } from "src/app/api/qc"; +// import { QcItemWithChecks } from "src/app/api/qc"; import PlayArrowIcon from "@mui/icons-material/PlayArrow"; -import { PurchaseOrderLine, StockInLine } from "@/app/api/po"; -import { createStockInLine, PurchaseQcResult } from "@/app/api/po/actions"; +import { PurchaseOrderLine } from "@/app/api/po"; +import { StockInLine } from "@/app/api/stockIn"; +import { createStockInLine, PurchaseQcResult } from "@/app/api/stockIn/actions"; import { usePathname, useRouter, useSearchParams } from "next/navigation"; import { returnWeightUnit, @@ -73,7 +74,7 @@ interface ResultWithId { } interface Props { - qc: QcItemWithChecks[]; + // qc: QcItemWithChecks[]; setRows: Dispatch>; setStockInLine: Dispatch>; setProcessedQty: Dispatch>; @@ -114,7 +115,7 @@ class ProcessRowUpdateError extends Error { } function PoInputGrid({ - qc, + // qc, setRows, setStockInLine, setProcessedQty, @@ -136,7 +137,7 @@ function PoInputGrid({ const [entries, setEntries] = useState(stockInLine || []); useEffect(() => { - setEntries(stockInLine) + setEntries(stockInLine); }, [stockInLine]) const [modalInfo, setModalInfo] = useState< StockInLine & { qcResult?: PurchaseQcResult[] } & { escalationResult?: EscalationResult[] } @@ -215,7 +216,7 @@ function PoInputGrid({ setRejectOpen(true); }, []); - const handleStart = useCallback( + const handleStart = useCallback( // NOTE: Seems unused!!!!!!!! (id: GridRowId, params: any) => () => { setBtnIsLoading(true); setRowModesModel((prev) => ({ @@ -229,7 +230,7 @@ function PoInputGrid({ itemId: params.row.itemId, itemNo: params.row.itemNo, itemName: params.row.itemName, - purchaseOrderId: params.row.purchaseOrderId, + // purchaseOrderId: params.row.purchaseOrderId, purchaseOrderLineId: params.row.purchaseOrderLineId, acceptedQty: params.row.acceptedQty, }; @@ -263,7 +264,6 @@ function PoInputGrid({ [id]: { mode: GridRowModes.View }, })); const qcResult = await fetchQcDefaultValue(id); - const escResult = await fetchEscalationLogsByStockInLines([Number(id)]); // console.log(params.row); console.log("Fetched QC Result:", qcResult); @@ -291,9 +291,9 @@ const closeNewModal = useCallback(() => { newParams.delete("stockInLineId"); // Remove the parameter router.replace(`${pathname}?${newParams.toString()}`); fetchPoDetail(itemDetail.purchaseOrderId.toString()); - setTimeout(() => { - setNewOpen(false); // Close the modal first - }, 300); // Add a delay to avoid immediate re-trigger of useEffect + setNewOpen(false); // Close the modal first + // setTimeout(() => { + // }, 300); // Add a delay to avoid immediate re-trigger of useEffect }, [searchParams, pathname, router]); // Open modal @@ -304,31 +304,27 @@ const closeNewModal = useCallback(() => { // Button handler to update the URL and open the modal const handleNewQC = useCallback( (id: GridRowId, params: any) => async() => { - // console.log(id) // setBtnIsLoading(true); setRowModesModel((prev) => ({ ...prev, [id]: { mode: GridRowModes.View }, })); - - const qcResult = await fetchQcDefaultValue(id); - const escResult = await fetchEscalationLogsByStockInLines([Number(id)]); - + + // const qcResult = await fetchQcDefaultValue(id); + // const escResult = await fetchEscalationLogsByStockInLines([Number(id)]); setModalInfo(() => ({ ...params.row, - qcResult: qcResult, - escResult: escResult, + // qcResult: qcResult, + // escResult: escResult, receivedQty: itemDetail.receivedQty, })); - - setTimeout(() => { - const newParams = new URLSearchParams(searchParams.toString()); - newParams.set("stockInLineId", id.toString()); // Ensure `set` to avoid duplicates - router.replace(`${pathname}?${newParams.toString()}`); - // console.log("hello") - openNewModal() - // setBtnIsLoading(false); - }, 200); + + const newParams = new URLSearchParams(searchParams.toString()); + newParams.set("stockInLineId", id.toString()); // Ensure `set` to avoid duplicates + router.replace(`${pathname}?${newParams.toString()}`); + openNewModal() + // setTimeout(() => { + // }, 200); }, [fetchQcDefaultValue, openNewModal, pathname, router, searchParams] ); @@ -337,7 +333,6 @@ const closeNewModal = useCallback(() => { const [firstCheckForSil, setFirstCheckForSil] = useState(false) useEffect(() => { if (stockInLineId && itemDetail && !firstCheckForSil) { - // console.log("heeloo") // console.log(stockInLineId) // console.log(apiRef.current.getRow(stockInLineId)) setFirstCheckForSil(true) @@ -492,8 +487,8 @@ const closeNewModal = useCallback(() => { // flex: 0.4, }, { - field: "dnDate", - headerName: t("dnDate"), + field: "receiptDate", + headerName: t("receiptDate"), width: 125, renderCell: (params) => { // console.log(params.row) @@ -952,100 +947,19 @@ const closeNewModal = useCallback(() => { footer: { child: footer }, }} /> - {modalInfo !== undefined && ( + {/* {modalInfo !== undefined && ( */} <> - ) - } - {/* {modalInfo !== undefined && ( - <> - - - )} - {modalInfo !== undefined && ( - <> - - - )} - {modalInfo !== undefined && ( - <> - - - )} - {modalInfo !== undefined && ( - <> - - - )} - {modalInfo !== undefined && ( - <> - - - )} */} + {/* ) + } */} ); } diff --git a/src/components/PoDetail/PutAwayForm.tsx b/src/components/PoDetail/PutAwayForm.tsx index 9706376..89ee121 100644 --- a/src/components/PoDetail/PutAwayForm.tsx +++ b/src/components/PoDetail/PutAwayForm.tsx @@ -1,6 +1,6 @@ "use client"; -import { PurchaseQcResult, PutAwayInput, PutAwayLine } from "@/app/api/po/actions"; +import { PurchaseQcResult, PutAwayInput, PutAwayLine } from "@/app/api/stockIn/actions"; import { Autocomplete, Box, @@ -36,7 +36,7 @@ import TwoLineCell from "./TwoLineCell"; import QcSelect from "./QcSelect"; import { QcItemWithChecks } from "@/app/api/qc"; import { GridEditInputCell } from "@mui/x-data-grid"; -import { StockInLine } from "@/app/api/po"; +import { StockInLine } from "@/app/api/stockIn"; import { WarehouseResult } from "@/app/api/warehouse"; import { arrayToDateTimeString, @@ -58,7 +58,7 @@ dayjs.extend(arraySupport); interface Props { itemDetail: StockInLine; - warehouse: WarehouseResult[]; + warehouse?: WarehouseResult[]; disabled: boolean; // qc: QcItemWithChecks[]; setRowModesModel: Dispatch>; @@ -84,7 +84,7 @@ const style = { width: "auto", }; -const PutAwayForm: React.FC = ({ itemDetail, warehouse, disabled, setRowModesModel, setRowSelectionModel }) => { +const PutAwayForm: React.FC = ({ itemDetail, warehouse=[], disabled, setRowModesModel, setRowSelectionModel }) => { const { t } = useTranslation("purchaseOrder"); const apiRef = useGridApiRef(); const { @@ -100,7 +100,7 @@ const PutAwayForm: React.FC = ({ itemDetail, warehouse, disabled, setRowM clearErrors, } = useFormContext(); // const [recordQty, setRecordQty] = useState(0); - const [warehouseId, setWarehouseId] = useState(itemDetail.defaultWarehouseId); + const [warehouseId, setWarehouseId] = useState(itemDetail.defaultWarehouseId ?? 1); const filteredWarehouse = useMemo(() => { // do filtering here if any return warehouse; @@ -113,11 +113,11 @@ const PutAwayForm: React.FC = ({ itemDetail, warehouse, disabled, setRowM }; const options = useMemo(() => { return [ - // { - // value: 0, // think think sin - // label: t("Select warehouse"), - // group: "default", - // }, + { + value: 1, + label: t("W001 - 憶兆 3樓A倉"), + group: "default", + }, ...filteredWarehouse.map((w) => ({ value: w.id, label: `${w.code} - ${w.name}`, @@ -175,32 +175,10 @@ const PutAwayForm: React.FC = ({ itemDetail, warehouse, disabled, setRowM // validateForm(); // }, [validateForm]); - const qrContent = useMemo( - () => ({ - stockInLineId: itemDetail.id, - itemId: itemDetail.itemId, - lotNo: itemDetail.lotNo, - // warehouseId: 2 // for testing - // expiryDate: itemDetail.expiryDate, - // productionDate: itemDetail.productionDate, - // supplier: itemDetail.supplier, - // poCode: itemDetail.poCode, - }), - [itemDetail], - ); - const [isOpenScanner, setOpenScanner] = useState(false); - - const closeHandler = useCallback>( - (...args) => { - setOpenScanner(false); - }, - [], - ); - useEffect(() => { setValue("status", "received"); // setValue("status", "completed"); - setValue("warehouseId", options[0].value); //TODO: save all warehouse entry? + // setValue("warehouseId", options[0].value); //TODO: save all warehouse entry? }, []); useEffect(() => { @@ -342,7 +320,7 @@ const PutAwayForm: React.FC = ({ itemDetail, warehouse, disabled, setRowM const defaultMaxQty = Number(itemDetail.demandQty?? itemDetail.acceptedQty)//watch("acceptedQty") - watch("putAwayLines").reduce((acc, cur) => acc + cur.qty, 0) const defaultWarehouseId = itemDetail.defaultWarehouseId ?? 1 - const defaultWarehouse = options.find((o) => o.value === defaultWarehouseId)?.label + const defaultWarehouse = "W001 - 憶兆 3樓A倉"//options.find((o) => o.value === defaultWarehouseId)?.label return {qty: defaultMaxQty, warehouseId: defaultWarehouseId, warehouse: defaultWarehouse, printQty: 1, _isNew: true } as Partial }, []) @@ -360,27 +338,27 @@ const PutAwayForm: React.FC = ({ itemDetail, warehouse, disabled, setRowM spacing={2} sx={{ mt: 0.5 }} > - + {/* - - + */} + {/* - + */} @@ -394,44 +372,52 @@ const PutAwayForm: React.FC = ({ itemDetail, warehouse, disabled, setRowM + + + - + - + {/* - + */} = ({ itemDetail, warehouse, disabled, setRowM {/* */} @@ -548,24 +534,6 @@ const PutAwayForm: React.FC = ({ itemDetail, warehouse, disabled, setRowM /> - {/* - - */} - - - - - {t("Please scan warehouse qr code.")} - - {/* */} - - ); }; diff --git a/src/components/PoDetail/QcComponent.tsx b/src/components/PoDetail/QcComponent.tsx index 83cf68a..4802529 100644 --- a/src/components/PoDetail/QcComponent.tsx +++ b/src/components/PoDetail/QcComponent.tsx @@ -1,6 +1,6 @@ "use client"; -import { PurchaseQcResult, PurchaseQCInput } from "@/app/api/po/actions"; +import { PurchaseQcResult, PurchaseQCInput } from "@/app/api/stockIn/actions"; import { Box, Card, @@ -39,7 +39,7 @@ import { TableRow } from "../InputDataGrid/InputDataGrid"; import TwoLineCell from "./TwoLineCell"; import QcSelect from "./QcSelect"; import { GridEditInputCell } from "@mui/x-data-grid"; -import { StockInLine } from "@/app/api/po"; +import { ModalFormInput, StockInLine } from "@/app/api/stockIn"; import { stockInLineStatusMap } from "@/app/utils/formatUtil"; import { fetchQcItemCheck, fetchQcResult } from "@/app/api/qc/actions"; import { QcItemWithChecks, QcData } from "@/app/api/qc"; @@ -49,16 +49,17 @@ import axiosInstance from "@/app/(main)/axios/axiosInstance"; import EscalationComponent from "./EscalationComponent"; import QcDataGrid from "./QCDatagrid"; import { dummyEscalationHistory, dummyQCData } from "./dummyQcTemplate"; -import { ModalFormInput } from "@/app/api/po/actions"; import { escape, min } from "lodash"; import { PanoramaSharp } from "@mui/icons-material"; import EscalationLogTable from "../DashboardPage/escalation/EscalationLogTable"; import { EscalationResult } from "@/app/api/escalation"; import { EscalationCombo } from "@/app/api/user"; import CollapsibleCard from "../CollapsibleCard/CollapsibleCard"; +import LoadingComponent from "../General/LoadingComponent"; interface Props { - itemDetail: StockInLine & { qcResult?: PurchaseQcResult[] } & { escResult?: EscalationResult[] }; + itemDetail: StockInLine; + // itemDetail: StockInLine & { qcResult?: PurchaseQcResult[] } & { escResult?: EscalationResult[] }; // qc: QcItemWithChecks[]; disabled: boolean; // qcItems: QcData[] @@ -97,7 +98,7 @@ const QcComponent: React.FC = ({ itemDetail, disabled = false }) => { // const qcResult = useMemo(() => [...watch("qcResult")], [watch("qcResult")]); const qcRecord = useMemo(() => { // Need testing - const value = watch('qcResult'); console.log("%c QC update!", "color:green", value); + const value = watch('qcResult'); //console.log("%c QC update!", "color:green", value); return Array.isArray(value) ? [...value] : []; }, [watch('qcResult')]); const [qcHistory, setQcHistory] = useState([]); @@ -168,7 +169,7 @@ const QcComponent: React.FC = ({ itemDetail, disabled = false }) => { if (validateFieldFail("acceptQty", isNaN(accQty), t("value must be a number"))) return; } - const qcResultItems = qcResult; console.log("Validating:", qcResultItems); + const qcResultItems = qcResult; //console.log("Validating:", qcResultItems); // Check if failed items have failed quantity const failedItemsWithoutQty = qcResultItems.filter(item => item.qcPassed === false && (!item.failQty || item.failQty <= 0) @@ -194,21 +195,6 @@ const QcComponent: React.FC = ({ itemDetail, disabled = false }) => { validateForm(); }, [clearErrors, validateForm]); - const columns = useMemo( - () => [ - { - field: "escalation", - headerName: t("escalation"), - flex: 1, - }, - { - field: "supervisor", - headerName: t("supervisor"), - flex: 1, - }, - ], - [], - ); /// validate datagrid const validation = useCallback( (newRow: GridRowModel): EntryError => { @@ -220,16 +206,16 @@ const QcComponent: React.FC = ({ itemDetail, disabled = false }) => { ); function BooleanEditCell(params: GridRenderEditCellParams) { - const apiRef = useGridApiContext(); - const { id, field, value } = params; + const apiRef = useGridApiContext(); + const { id, field, value } = params; - const handleChange = (e: React.ChangeEvent) => { - apiRef.current.setEditCellValue({ id, field, value: e.target.checked }); - apiRef.current.stopCellEditMode({ id, field }); // commit immediately - }; + const handleChange = (e: React.ChangeEvent) => { + apiRef.current.setEditCellValue({ id, field, value: e.target.checked }); + apiRef.current.stopCellEditMode({ id, field }); // commit immediately + }; - return ; -} + return ; + } const qcDisabled = (row : PurchaseQcResult) => { return disabled || isExist(row.escalationLogId); @@ -396,12 +382,12 @@ const QcComponent: React.FC = ({ itemDetail, disabled = false }) => { }, [itemDetail?.demandQty, itemDetail?.acceptedQty, setValue]); useEffect(() => { - console.log("%c Qc Record updated:", "color:red", qcRecord); + console.log("%c Qc Record updated:", "color:green", qcRecord); if (qcRecord.length < 1) { // New QC const fetchedQcData = dummyQCData; //TODO fetch from DB setValue("qcResult", fetchedQcData); } else { - if (itemDetail.status == "escalated") { // Copy the previous QC data for editing + if (itemDetail?.status == "escalated") { // Copy the previous QC data for editing if (qcRecord.find((qc) => !isExist(qc.escalationLogId)) === undefined) { const copiedQcData = qcRecord.map(qc => ({ ...qc, escalationLogId: undefined })); const mutableQcData = [...qcRecord, ...copiedQcData]; @@ -442,7 +428,7 @@ const QcComponent: React.FC = ({ itemDetail, disabled = false }) => { useEffect(() => { - console.log("%c QC ItemDetail updated:", "color: gold", itemDetail); + // console.log("%c QC ItemDetail updated:", "color: gold", itemDetail); }, [itemDetail]); @@ -482,6 +468,7 @@ const QcComponent: React.FC = ({ itemDetail, disabled = false }) => { return ( <> + {itemDetail ? ( = ({ itemDetail, disabled = false }) => { )} - - {t("Qc Decision")} - - - ( - <> - {/* - {errors.qcDecision?.message} - */} - { - const value = e.target.value.toString();// === 'true'; - - const input = document.getElementById('accQty') as HTMLInputElement; //TODO improve - console.log("%c AccQty Error", "color:pink", errors.acceptQty); - if (input) { // Selected Reject in new flow with Error - if (value == "1") { // Selected Accept - input.value = Number(accQty).toString(); - } else { - if (Boolean(errors.acceptQty)) { - setValue("acceptQty", 0); - } - input.value = '0'; - } - } - // setValue("acceptQty", itemDetail.acceptedQty ?? 0); - // clearErrors("acceptQty"); - // } - field.onChange(value); - }} - > - } label="接受來貨" /> - - {(itemDetail.status == "escalated"|| (disabled && accQty != itemDetail.acceptedQty && qcDecision == 1)) && ( //TODO Improve - - { - const value = e.target.value; - const input = document.getElementById('accQty') as HTMLInputElement; - input.value = Number(value).toString() - setValue(`acceptQty`, Number(value)); - }} - onInput={(e: React.ChangeEvent) => { - const input = e.target.value; - - const numReg = /^[0-9]+$/ - let r = ''; - if (!numReg.test(input)) { - const result = input.replace(/\D/g, ""); - r = (result === '' ? result : Number(result)).toString(); + + + {t("Qc Decision")} + + + ( + <> + {/* + {errors.qcDecision?.message} + */} + { + const value = e.target.value.toString();// === 'true'; + + const input = document.getElementById('accQty') as HTMLInputElement; //TODO improve + // console.log("%c AccQty Error", "color:red", errors.acceptQty); + if (input) { // Selected Reject in new flow with Error + if (value == "1") { // Selected Accept + input.value = Number(accQty).toString(); } else { - r = Number(input).toString() + if (Boolean(errors.acceptQty)) { + setValue("acceptQty", 0); + } + input.value = '0'; } - e.target.value = r; - }} - inputProps={{ min: 1, max:itemDetail.acceptedQty }} - // onChange={(e) => { - // const inputValue = e.target.value; - // if (inputValue === '' || /^[0-9]*$/.test(inputValue)) { - // setValue("acceptQty", Number(inputValue === '' ? null : parseInt(inputValue, 10))); - // } - // }} - // {...register("acceptQty", { - // required: "acceptQty required!", - // })} - error={Boolean(errors.acceptQty)} - helperText={errors.acceptQty?.message} - /> - - )} - - } - sx={{"& .Mui-checked": {color: "red"}}} - label= {itemDetail.status == "escalated" ? "全部拒絕並退貨" : "不接受並退貨"} /> + // setValue("acceptQty", itemDetail.acceptedQty ?? 0); + // clearErrors("acceptQty"); + // } + field.onChange(value); + }} + > + } label="接受來貨" /> - {(itemDetail.status == "pending" || disabled) && (<> - } - sx={{"& .Mui-checked": {color: "blue"}}} - label="上報品檢結果" /> - )} - - - )} - /> - + {(itemDetail.status == "escalated"|| (disabled && accQty != itemDetail.acceptedQty && qcDecision == 1)) && ( //TODO Improve + + { + const value = e.target.value; + const input = document.getElementById('accQty') as HTMLInputElement; + input.value = Number(value).toString() + setValue(`acceptQty`, Number(value)); + }} + onInput={(e: React.ChangeEvent) => { + const input = e.target.value; + + const numReg = /^[0-9]+$/ + let r = ''; + if (!numReg.test(input)) { + const result = input.replace(/\D/g, ""); + r = (result === '' ? result : Number(result)).toString(); + } else { + r = Number(input).toString() + } + e.target.value = r; + }} + inputProps={{ min: 1, max:itemDetail.acceptedQty }} + // onChange={(e) => { + // const inputValue = e.target.value; + // if (inputValue === '' || /^[0-9]*$/.test(inputValue)) { + // setValue("acceptQty", Number(inputValue === '' ? null : parseInt(inputValue, 10))); + // } + // }} + // {...register("acceptQty", { + // required: "acceptQty required!", + // })} + error={Boolean(errors.acceptQty)} + helperText={errors.acceptQty?.message} + /> + + )} + + } + sx={{"& .Mui-checked": {color: "red"}}} + label= {itemDetail.status == "escalated" ? "全部拒絕並退貨" : "不接受並退貨"} /> + + {(itemDetail.status == "pending" || disabled) && (<> + } + sx={{"& .Mui-checked": {color: "blue"}}} + label="上報品檢結果" /> + )} + + + )} + /> + + {qcDecision == 3 && ( // {!qcAccept && ( @@ -721,6 +710,7 @@ const QcComponent: React.FC = ({ itemDetail, disabled = false }) => { /> } */} + ) : } ); diff --git a/src/components/PoDetail/QcStockInModal.tsx b/src/components/PoDetail/QcStockInModal.tsx index 33455aa..696c864 100644 --- a/src/components/PoDetail/QcStockInModal.tsx +++ b/src/components/PoDetail/QcStockInModal.tsx @@ -1,6 +1,4 @@ "use client"; -import { StockInLine } from "@/app/api/po"; -import { ModalFormInput, PurchaseQcResult, StockInLineEntry, updateStockInLine, PurchaseQCInput, printQrCodeForSil, PrintQrCodeForSilRequest } from "@/app/api/po/actions"; import { QcItemWithChecks, QcData } from "@/app/api/qc"; import { Autocomplete, @@ -38,6 +36,12 @@ import { GridRowModesModel } from "@mui/x-data-grid"; import { isEmpty } from "lodash"; import { EscalationCombo } from "@/app/api/user"; import { truncateSync } from "fs"; +import { ModalFormInput, StockInLineInput, StockInLine } from "@/app/api/stockIn"; +import { PurchaseQcResult, StockInLineEntry, updateStockInLine, printQrCodeForSil, PrintQrCodeForSilRequest } from "@/app/api/stockIn/actions"; +import { fetchStockInLineInfo } from "@/app/api/stockIn/actions"; +import { fetchQcResult } from "@/app/api/qc/actions"; +import { fetchEscalationLogsByStockInLines } from "@/app/api/escalation/actions"; +import LoadingComponent from "../General/LoadingComponent"; const style = { @@ -54,42 +58,23 @@ const style = { height: { xs: "90%", sm: "90%", md: "90%" }, }; interface CommonProps extends Omit { - // setRows: Dispatch>; - setEntries?: Dispatch>; - setStockInLine?: Dispatch>; - itemDetail: StockInLine & { qcResult?: PurchaseQcResult[] } & { escResult?: EscalationResult[] }; - setItemDetail: Dispatch< - SetStateAction< - | (StockInLine & { - warehouseId?: number; - }) - | undefined - > - >; + // itemDetail: StockInLine & { qcResult?: PurchaseQcResult[] } & { escResult?: EscalationResult[] } | undefined; + inputDetail: StockInLineInput | undefined; session: SessionWithTokens | null; - qc?: QcItemWithChecks[]; warehouse?: any[]; - // type: "qc" | "stockIn" | "escalation" | "putaway" | "reject"; - handleMailTemplateForStockInLine: (stockInLineId: number) => void; printerCombo: PrinterCombo[]; onClose: () => void; } interface Props extends CommonProps { - itemDetail: StockInLine & { qcResult?: PurchaseQcResult[] } & { escResult?: EscalationResult[] }; + // itemDetail: StockInLine & { qcResult?: PurchaseQcResult[] } & { escResult?: EscalationResult[] }; } const PoQcStockInModalVer2: React.FC = ({ - // type, - // setRows, - setEntries, - setStockInLine, open, onClose, - itemDetail, - setItemDetail, + // itemDetail, + inputDetail, session, - qc, warehouse, - handleMailTemplateForStockInLine, printerCombo, }) => { const { @@ -97,6 +82,10 @@ const PoQcStockInModalVer2: React.FC = ({ i18n: { language }, } = useTranslation("purchaseOrder"); + const [stockInLineInfo, setStockInLineInfo] = useState(); + const [isLoading, setIsLoading] = useState(false); + // const [viewOnly, setViewOnly] = useState(false); + // Select Printer const [selectedPrinter, setSelectedPrinter] = useState(printerCombo[0]); const [printQty, setPrintQty] = useState(1); @@ -109,28 +98,108 @@ const PoQcStockInModalVer2: React.FC = ({ [], ); -const defaultNewValue = useMemo(() => { - return ( - { - ...itemDetail, - status: itemDetail.status ?? "pending", - dnDate: arrayToDateString(itemDetail.dnDate, "input")?? dayjsToInputDateString(dayjs()), - // putAwayLines: dummyPutAwayLine, - // putAwayLines: itemDetail.putAwayLines.map((line) => (return {...line, printQty: 1})) ?? [], - putAwayLines: itemDetail.putAwayLines?.map((line) => ({...line, printQty: 1, _isNew: false, _disableDelete: true})) ?? [], - // qcResult: (itemDetail.qcResult && itemDetail.qcResult?.length > 0) ? itemDetail.qcResult : [],//[...dummyQCData], - escResult: (itemDetail.escResult && itemDetail.escResult?.length > 0) ? itemDetail.escResult : [], - productionDate: itemDetail.productionDate ? arrayToDateString(itemDetail.productionDate, "input") : undefined, - expiryDate: itemDetail.expiryDate ? arrayToDateString(itemDetail.expiryDate, "input") : undefined, - receiptDate: itemDetail.receiptDate ? arrayToDateString(itemDetail.receiptDate, "input") - : dayjs().add(0, "month").format(INPUT_DATE_FORMAT), - acceptQty: itemDetail.demandQty?? itemDetail.acceptedQty, - warehouseId: itemDetail.defaultWarehouseId ?? 1, + const fetchStockInLineData = useCallback( + async (stockInLineId: number) => { + try { + const res = await fetchStockInLineInfo(stockInLineId); + if (res) { + console.log("%c Fetched Stock In Line: ", "color:orange", res); + setStockInLineInfo({...inputDetail, ...res}); + fetchQcResultData(stockInLineId); + + } else throw("Result is undefined"); + } catch (e) { + console.log("%c Error when fetching Stock In Line: ", "color:red", e); + } + },[fetchStockInLineInfo, inputDetail] + ); + + const fetchQcResultData = useCallback( // TODO: put this inside QC Component + async (stockInLineId: number) => { + try { + const res = await fetchQcResult(stockInLineId); + if (res.length > 0) { + console.log("%c Fetched Qc Result: ", "color:orange", res); + setStockInLineInfo((prev) => ({...prev, qcResult: res} as StockInLine)); + formProps.setValue("qcResult", res); + + fetchEscalationLogData(stockInLineId); + + } else {setStockInLineInfo((prev) => ({...prev, qcResult: []} as StockInLine));} + // } else throw("Result is undefined"); + } catch (e) { + console.log("%c Error when fetching Qc Result: ", "color:red", e); + } + },[fetchQcResult] + ); + + const fetchEscalationLogData = useCallback( + async (stockInLineId: number) => { + try { + const res = await fetchEscalationLogsByStockInLines([stockInLineId]); + if (res.length > 0) { + console.log("%c Fetched Escalation Log: ", "color:orange", res[0]); + setStockInLineInfo((prev) => ({...prev, escResult: res} as StockInLine)); + // formProps.setValue("escalationLog", res[0]); + + } else throw("Result is undefined"); + } catch (e) { + console.log("%c Error when fetching EscalationLog: ", "color:red", e); + } + },[fetchEscalationLogsByStockInLines] + ); + + // Fetch info if id is input + useEffect(() => { + setIsLoading(true); + if (inputDetail && open) { + console.log("%c Opened Modal with input:", "color:yellow", inputDetail); + if (inputDetail.id) { + const id = inputDetail.id; + fetchStockInLineData(id); + } } - ) -},[itemDetail]) + }, [open]); -const [qcItems, setQcItems] = useState(dummyQCData) + // Make sure stock in line info is fetched + useEffect(() => { + if (stockInLineInfo) { + if (stockInLineInfo.id) { + if (isLoading) { + formProps.reset({ + ...defaultNewValue + }); + console.log("%c Modal loaded successfully", "color:lime"); + setIsLoading(false); + } + } + } + + }, [stockInLineInfo]); + + const defaultNewValue = useMemo(() => { + const d = stockInLineInfo; + if (d !== undefined) { + // console.log("%c sil info", "color:yellow", d ) + return ( + { + ...d, + // status: d.status ?? "pending", + productionDate: d.productionDate ? arrayToDateString(d.productionDate, "input") : undefined, + expiryDate: d.expiryDate ? arrayToDateString(d.expiryDate, "input") : undefined, + receiptDate: d.receiptDate ? arrayToDateString(d.receiptDate, "input") + : dayjs().add(0, "month").format(INPUT_DATE_FORMAT), + acceptQty: d.demandQty?? d.acceptedQty, + // escResult: (d.escResult && d.escResult?.length > 0) ? d.escResult : [], + // qcResult: (d.qcResult && d.qcResult?.length > 0) ? d.qcResult : [],//[...dummyQCData], + warehouseId: d.defaultWarehouseId ?? 1, + putAwayLines: d.putAwayLines?.map((line) => ({...line, printQty: 1, _isNew: false, _disableDelete: true})) ?? [], + } as ModalFormInput + ) + } return undefined + }, [stockInLineInfo]) + +// const [qcItems, setQcItems] = useState(dummyQCData) const formProps = useForm({ defaultValues: { ...defaultNewValue, @@ -139,41 +208,45 @@ const [qcItems, setQcItems] = useState(dummyQCData) const closeHandler = useCallback>( () => { + setStockInLineInfo(undefined); + formProps.reset({}); onClose?.(); - // reset(); }, [onClose], ); const isPutaway = () => { - if (itemDetail) { - const status = itemDetail.status; + if (stockInLineInfo) { + const status = stockInLineInfo.status; return status == "received"; } else return false; }; - - const [viewOnly, setViewOnly] = useState(false); - useEffect(() => { - if (itemDetail && itemDetail.status) { - const isViewOnly = itemDetail.status.toLowerCase() == "completed" - || itemDetail.status.toLowerCase() == "partially_completed" // TODO update DB - || itemDetail.status.toLowerCase() == "rejected" - || (itemDetail.status.toLowerCase() == "escalated" && session?.id != itemDetail.handlerId) - setViewOnly(isViewOnly) - } - console.log("Modal ItemDetail updated:", itemDetail); - if (showPutaway) { setTabIndex(1); } else { setTabIndex(0); } - }, [itemDetail]); - useEffect(() => { - formProps.reset({ - ...defaultNewValue - }) - - setQcItems(dummyQCData); - // setOpenPutaway(isPutaway); - }, [open]) + // Get show putaway + const showPutaway = useMemo(() => { + if (stockInLineInfo) { + const status = stockInLineInfo.status; + return status !== "pending" && status !== "escalated" && status !== "rejected"; + } + return false; + }, [stockInLineInfo]); + + // Get is view only + const viewOnly = useMemo(() => { + if (stockInLineInfo) { + if (stockInLineInfo.status) { + const status = stockInLineInfo.status; + const isViewOnly = status.toLowerCase() == "completed" + || status.toLowerCase() == "partially_completed" // TODO update DB + || status.toLowerCase() == "rejected" + || (status.toLowerCase() == "escalated" && session?.id != stockInLineInfo.handlerId) + if (showPutaway) { setTabIndex(1); } else { setTabIndex(0); } + return isViewOnly; + } + } + return true; + }, [stockInLineInfo]) const [openPutaway, setOpenPutaway] = useState(false); const onOpenPutaway = useCallback(() => { @@ -269,7 +342,7 @@ const [qcItems, setQcItems] = useState(dummyQCData) alert("請輸入到期日!"); return; } - if (!qcResults.every((qc) => qc.qcPassed) && qcAccept && itemDetail.status != "escalated") { //TODO: fix it please! + if (!qcResults.every((qc) => qc.qcPassed) && qcAccept && stockInLineInfo?.status != "escalated") { //TODO: fix it please! validationErrors.push("有不合格檢查項目,無法收貨!"); // submitDialogWithWarning(() => postStockInLineWithQc(qcData), t, {title:"有不合格檢查項目,確認接受收貨?", // confirmButtonText: t("confirm putaway"), html: ""}); @@ -279,7 +352,7 @@ const [qcItems, setQcItems] = useState(dummyQCData) // Check if all QC items have results const itemsWithoutResult = qcResults.filter(item => item.qcPassed === undefined); - if (itemsWithoutResult.length > 0 && itemDetail.status != "escalated") { //TODO: fix it please! + if (itemsWithoutResult.length > 0 && stockInLineInfo?.status != "escalated") { //TODO: fix it please! validationErrors.push(`${t("QC items without result")}`); // validationErrors.push(`${t("QC items without result")}: ${itemsWithoutResult.map(item => item.code).join(', ')}`); } @@ -292,7 +365,7 @@ const [qcItems, setQcItems] = useState(dummyQCData) const qcData = { dnNo : data.dnNo? data.dnNo : "DN00000", - dnDate : data.dnDate? arrayToDateString(data.dnDate, "input") : dayjsToInputDateString(dayjs()), + // dnDate : data.dnDate? arrayToDateString(data.dnDate, "input") : dayjsToInputDateString(dayjs()), productionDate : arrayToDateString(data.productionDate, "input"), expiryDate : arrayToDateString(data.expiryDate, "input"), receiptDate : arrayToDateString(data.receiptDate, "input"), @@ -345,69 +418,36 @@ const [qcItems, setQcItems] = useState(dummyQCData) return ; }, - [onOpenPutaway, qcItems, formProps.formState.errors], + [onOpenPutaway, formProps.formState.errors], ); const postStockInLine = useCallback(async (args: ModalFormInput) => { const submitData = { - ...itemDetail, ...args + ...stockInLineInfo, ...args } as StockInLineEntry & ModalFormInput; console.log("Submitting", submitData); const res = await updateStockInLine(submitData); return res; - },[itemDetail]) - - // Email supplier handler - const onSubmitEmailSupplier = useCallback>( - async (data, event) => { - console.log("Email Supplier Submission:", event!.nativeEvent); - // Extract only email supplier related fields - const emailData = { - // supplierEmail: data.supplierEmail, - // issueDescription: data.issueDescription, - // qcComments: data.qcComments, - // defectNotes: data.defectNotes, - // attachments: data.attachments, - // escalationReason: data.escalationReason, - data: data, - - // Add other email-specific fields - }; - console.log("Email Supplier Data:", emailData); - // Handle email supplier logic here - // e.g., send email to supplier, log escalation, etc. - }, - [], - ); + }, [stockInLineInfo]) // Put away model const [pafRowModesModel, setPafRowModesModel] = useState({}) const [pafRowSelectionModel, setPafRowSelectionModel] = useState([]) const pafSubmitDisable = useMemo(() => { - // console.log("%c mode: ", "background:#90EE90; color:red", Object.entries(pafRowModesModel)) - // console.log("%c mode: ", "background:pink; color:#87CEEB", Object.entries(pafRowModesModel)) return Object.entries(pafRowModesModel).length > 0 || Object.entries(pafRowModesModel).some(([key, value], index) => value.mode === GridRowModes.Edit) }, [pafRowModesModel]) // Putaway submission handler const onSubmitPutaway = useCallback>( async (data, event) => { - // console.log("Putaway Submission:", event!.nativeEvent); - // console.log(data.putAwayLines) - // console.log(data.putAwayLines?.filter((line) => line._isNew !== false)) // Extract only putaway related fields const putawayData = { - // putawayLine: data.putawayLine, - // putawayLocation: data.putawayLocation, - // binLocation: data.binLocation, - // putawayQuantity: data.putawayQuantity, - // putawayNotes: data.putawayNotes, - acceptQty: Number(data.acceptQty?? (itemDetail.demandQty?? (itemDetail.acceptedQty))), //TODO improve + acceptQty: Number(data.acceptQty?? (stockInLineInfo?.demandQty?? (stockInLineInfo?.acceptedQty))), //TODO improve warehouseId: data.warehouseId, status: data.status, //TODO Fix it! // ...data, - dnDate : data.dnDate? arrayToDateString(data.dnDate, "input") : dayjsToInputDateString(dayjs()), + // dnDate : data.dnDate? arrayToDateString(data.dnDate, "input") : dayjsToInputDateString(dayjs()), productionDate : arrayToDateString(data.productionDate, "input"), expiryDate : arrayToDateString(data.expiryDate, "input"), receiptDate : arrayToDateString(data.receiptDate, "input"), @@ -464,7 +504,7 @@ const [qcItems, setQcItems] = useState(dummyQCData) // const printQty = printList.reduce((acc, cur) => acc + cur.printQty, 0) // console.log(printQty) const data: PrintQrCodeForSilRequest = { - stockInLineId: itemDetail.id, + stockInLineId: stockInLineInfo?.id ?? 0, printerId: selectedPrinter.id, printQty: printQty } @@ -475,32 +515,22 @@ const [qcItems, setQcItems] = useState(dummyQCData) } finally { setIsPrinting(() => false) } - }, [itemDetail.id, pafRowSelectionModel, printQty, selectedPrinter]); + // }, [pafRowSelectionModel, printQty, selectedPrinter]); + }, [stockInLineInfo?.id, pafRowSelectionModel, printQty, selectedPrinter]); const acceptQty = formProps.watch("acceptedQty") - const showPutaway = useMemo(() => { - const status = itemDetail.status; - return status !== "pending" && status !== "escalated" && status !== "rejected"; - }, [itemDetail]); - - const checkQcIsPassed = useCallback((qcItems: PurchaseQcResult[]) => { - const isPassed = qcItems.every((qc) => qc.qcPassed); - console.log(isPassed) - if (isPassed) { - formProps.setValue("passingQty", acceptQty) - } else { - formProps.setValue("passingQty", 0) - } - return isPassed - }, [acceptQty, formProps]) - - // useEffect(() => { - // // maybe check if submitted before - // console.log("Modal QC Items updated:", qcItems); - // // checkQcIsPassed(qcItems) - // }, [qcItems, checkQcIsPassed]) - + // const checkQcIsPassed = useCallback((qcItems: PurchaseQcResult[]) => { + // const isPassed = qcItems.every((qc) => qc.qcPassed); + // console.log(isPassed) + // if (isPassed) { + // formProps.setValue("passingQty", acceptQty) + // } else { + // formProps.setValue("passingQty", 0) + // } + // return isPassed + // }, [acceptQty, formProps]) + return ( <> @@ -514,8 +544,11 @@ const [qcItems, setQcItems] = useState(dummyQCData) marginLeft: 3, marginRight: 3, // overflow: "hidden", + display: 'flex', + flexDirection: 'column', }} > + {(!isLoading && stockInLineInfo) ? (<> - {tabIndex === 0 && <> - - - {t("Delivery Detail")} - - - - - - - - - {(!viewOnly && !showPutaway) && ()} - - } + {tabIndex === 0 && + + + + {t("Delivery Detail")} + + + + {stockInLineInfo.qcResult ? + : + } + + {(!viewOnly && !showPutaway) && ()} + + + } {tabIndex === 1 && - - - - - - {/* */} - - - { - setSelectedPrinter(value) - }} - renderInput={(params) => ( - - )} - /> - { - event.target.value = event.target.value.replace(/[^0-9]/g, '') - - setPrintQty(Number(event.target.value)) - }} - sx={{ width: 300}} - /> - - {/* */} - - - + + } + + {tabIndex == 1 && ( + + { + setSelectedPrinter(value) + }} + renderInput={(params) => ( + + )} + /> + { + event.target.value = event.target.value.replace(/[^0-9]/g, '') + + setPrintQty(Number(event.target.value)) + }} + sx={{ width: 300}} + /> + + + )} + ) : } diff --git a/src/components/PoDetail/StockInForm.tsx b/src/components/PoDetail/StockInForm.tsx index 9373d74..0dad7e6 100644 --- a/src/components/PoDetail/StockInForm.tsx +++ b/src/components/PoDetail/StockInForm.tsx @@ -4,7 +4,7 @@ import { PurchaseQcResult, PurchaseQCInput, StockInInput, -} from "@/app/api/po/actions"; +} from "@/app/api/stockIn/actions"; import { Box, Card, @@ -34,7 +34,7 @@ import TwoLineCell from "./TwoLineCell"; import QcSelect from "./QcSelect"; import { QcItemWithChecks } from "@/app/api/qc"; import { GridEditInputCell } from "@mui/x-data-grid"; -import { StockInLine } from "@/app/api/po"; +import { StockInLine } from "@/app/api/stockIn"; import { DatePicker, LocalizationProvider } from "@mui/x-date-pickers"; import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; import { INPUT_DATE_FORMAT } from "@/app/utils/formatUtil"; @@ -249,6 +249,19 @@ const StockInForm: React.FC = ({ )} + {putawayMode ? ( + ) : ( + = ({ disabled={disabled} error={Boolean(errors.productLotNo)} helperText={errors.productLotNo?.message} - /> + />)} {putawayMode || (<> @@ -350,8 +363,20 @@ const StockInForm: React.FC = ({ }} /> - {putawayMode || ( - + + {putawayMode ? ( + + ) : ( = ({ sx={textfieldSx} disabled={true} /> - - )} + )} + = ({ disabled={true} /> - {putawayMode ? (<> - - - - + + {putawayMode ? ( = ({ // error={Boolean(errors.acceptedQty)} // helperText={errors.acceptedQty?.message} /> - - ) : ( - + ) : ( = ({ // error={Boolean(errors.acceptedQty)} // helperText={errors.acceptedQty?.message} /> - - ) - } + )} + {/* = ({ const [selectedPoIds, setSelectedPoIds] = useState([]); const [selectAll, setSelectAll] = useState(false); const [filteredPo, setFilteredPo] = useState(po); - const [filterArgs, setFilterArgs] = useState>({}); + const [filterArgs, setFilterArgs] = useState>({estimatedArrivalDate : dayjsToInputDateStringFIX(dayjs())}); const { t } = useTranslation("purchaseOrder"); const router = useRouter(); const [pagingController, setPagingController] = useState( @@ -66,7 +66,8 @@ const PoSearch: React.FC = ({ { label: t(`completed`), value: `completed` }, ], }, - { label: t("ETA"), label2: t("ETA To"), paramName: "estimatedArrivalDate", type: "dateRange" }, + { label: t("ETA"), label2: t("ETA To"), paramName: "estimatedArrivalDate", type: "dateRange", + preFilledValue: dayjsToInputDateStringFIX(dayjs()) }, ]; return searchCriteria; diff --git a/src/components/PutAwayScan/PutAwayModal.tsx b/src/components/PutAwayScan/PutAwayModal.tsx index 978d5f0..4c9268a 100644 --- a/src/components/PutAwayScan/PutAwayModal.tsx +++ b/src/components/PutAwayScan/PutAwayModal.tsx @@ -19,11 +19,10 @@ import ReactQrCodeScanner, { import { FormProvider, SubmitHandler, useForm } from "react-hook-form"; import { fetchStockInLineInfo, - ModalFormInput, StockInLineEntry, updateStockInLine, -} from "@/app/api/po/actions"; -import { StockInLine } from "@/app/api/po"; +} from "@/app/api/stockIn/actions"; +import { ModalFormInput, StockInLine } from "@/app/api/stockIn"; import { WarehouseResult } from "@/app/api/warehouse"; // import { QrCodeInfo } from "@/app/api/qrcde"; import { Check, QrCode, ErrorOutline, CheckCircle } from "@mui/icons-material"; @@ -94,7 +93,7 @@ const PutAwayModal: React.FC = ({ open, onClose, warehouse, stockInLineId { ...itemDetail, // status: itemDetail.status ?? "pending", - dnDate: arrayToDateString(itemDetail?.dnDate, "input")?? undefined, + // dnDate: arrayToDateString(itemDetail?.dnDate, "input")?? undefined, // // putAwayLines: dummyPutAwayLine, // // putAwayLines: itemDetail.putAwayLines.map((line) => (return {...line, printQty: 1})) ?? [], // putAwayLines: itemDetail.putAwayLines?.map((line) => ({...line, printQty: 1, _isNew: false})) ?? [], @@ -105,7 +104,7 @@ const PutAwayModal: React.FC = ({ open, onClose, warehouse, stockInLineId receiptDate: itemDetail?.receiptDate ? arrayToDateString(itemDetail?.receiptDate, "input") : undefined, // acceptQty: itemDetail.demandQty?? itemDetail.acceptedQty, defaultWarehouseId: itemDetail?.defaultWarehouseId ?? 1, - } + } as ModalFormInput ) }, [itemDetail]) @@ -218,7 +217,7 @@ const PutAwayModal: React.FC = ({ open, onClose, warehouse, stockInLineId useEffect(() => { if (stockInLineId) { fetchStockInLine(stockInLineId); } - }, [stockInLineId]); + }, [stockInLineId]); const validateQty = useCallback((qty : number = putQty) => { // if (isNaN(putQty) || putQty === undefined || putQty === null || typeof(putQty) != "number") { diff --git a/src/components/PutAwayScan/PutAwayScan.tsx b/src/components/PutAwayScan/PutAwayScan.tsx index a4d21b5..4fda256 100644 --- a/src/components/PutAwayScan/PutAwayScan.tsx +++ b/src/components/PutAwayScan/PutAwayScan.tsx @@ -15,13 +15,7 @@ import { ScannerConfig, } from "../ReactQrCodeScanner/ReactQrCodeScanner"; import { FormProvider, SubmitHandler, useForm } from "react-hook-form"; - import { - fetchStockInLineInfo, - ModalFormInput, - StockInLineEntry, - updateStockInLine, - } from "@/app/api/po/actions"; - import { StockInLine } from "@/app/api/po"; + import { StockInLine } from "@/app/api/stockIn"; import { WarehouseResult } from "@/app/api/warehouse"; import { QrCodeInfo } from "@/app/api/qrcode"; import { Check, QrCodeScanner, Warehouse } from "@mui/icons-material"; diff --git a/src/components/SearchBox/SearchBox.tsx b/src/components/SearchBox/SearchBox.tsx index df1f533..a6a1ff1 100644 --- a/src/components/SearchBox/SearchBox.tsx +++ b/src/components/SearchBox/SearchBox.tsx @@ -36,6 +36,8 @@ interface BaseCriterion { paramName: T; paramName2?: T; // options?: T[] | string[]; + defaultValue?: string; + preFilledValue?: string; filterObj?: T; handleSelectionChange?: (selectedOptions: T[]) => void; } @@ -136,18 +138,31 @@ function SearchBox({ if (c.type === "dateRange") { tempCriteria = { ...tempCriteria, - [c.paramName]: "", + [c.paramName]: c.defaultValue ?? "", [`${c.paramName}To`]: "", }; } - return tempCriteria; }, {} as Record, ), [criteria], ); - const [inputs, setInputs] = useState(defaultInputs); + const preFilledInputs = useMemo(() => { + const preFilledCriteria = criteria.reduce>( + (acc, c) => { + if (c.preFilledValue !== undefined) { + return { + ...acc, + [c.paramName]: c.preFilledValue, + }; + } else return acc; + }, + {} as Record,); + return {...defaultInputs, ...preFilledCriteria} + }, [defaultInputs]) + + const [inputs, setInputs] = useState(preFilledInputs); const [isReset, setIsReset] = useState(false); const makeInputChangeHandler = useCallback( diff --git a/src/i18n/zh/purchaseOrder.json b/src/i18n/zh/purchaseOrder.json index 687f43b..2fbe0b7 100644 --- a/src/i18n/zh/purchaseOrder.json +++ b/src/i18n/zh/purchaseOrder.json @@ -44,7 +44,7 @@ "price": "訂單貨值", "processedQty": "已上架數量", "expiryDate": "到期日", - "acceptedQty": "是次來貨數量", + "acceptedQty": "本批收貨數量", "putawayQty": "上架數量", "acceptQty": "揀收數量", "printQty": "列印數量", @@ -91,7 +91,7 @@ "to be processed": "待處理", "supervisor": "管理層", "Stock In Detail": "入庫詳情", - "productLotNo": "貨品批號", + "productLotNo": "來貨批號", "receiptDate": "收貨日期", "acceptedWeight": "接受重量", "productionDate": "生產日期", @@ -100,7 +100,7 @@ "Select warehouse": "選擇倉庫", "Putaway Detail": "上架詳情", "Delivery Detail": "來貨詳情", - "LotNo": "批號", + "stockLotNo": "入倉批號", "Po Code": "採購訂單編號", "No Warehouse": "沒有倉庫", "Please scan warehouse qr code.": "請掃描倉庫 QR 碼。",