From 28a23973c0e3331b79aecf58dfecc8c5c63400c7 Mon Sep 17 00:00:00 2001 From: kelvinsuen Date: Thu, 28 Aug 2025 14:01:02 +0800 Subject: [PATCH] update new escalation flow --- src/app/api/po/actions.ts | 9 +- src/app/api/po/index.ts | 7 + src/components/PoDetail/PoInputGrid.tsx | 5 +- src/components/PoDetail/PutAwayForm.tsx | 3 +- src/components/PoDetail/QcComponent.tsx | 354 +++++++++++------- .../PoDetail/QcStockInModalVer2.tsx | 86 +++-- src/components/PoDetail/StockInFormVer2.tsx | 15 +- src/i18n/zh/purchaseOrder.json | 9 +- 8 files changed, 293 insertions(+), 195 deletions(-) diff --git a/src/app/api/po/actions.ts b/src/app/api/po/actions.ts index ff24acf..2ad9de4 100644 --- a/src/app/api/po/actions.ts +++ b/src/app/api/po/actions.ts @@ -38,11 +38,10 @@ export interface StockInLineEntry { } export interface PurchaseQcResult{ - qcItemId: number; + id: number; qcPassed?: boolean; failQty?: number; remarks?: string; - } export interface StockInInput { status: string; @@ -66,9 +65,9 @@ export interface PurchaseQCInput { status: string; acceptQty: number; passingQty: number; - sampleRate: number; - sampleWeight: number; - totalWeight: number; + sampleRate?: number; + sampleWeight?: number; + totalWeight?: number; qcAccept: boolean; qcDecision?: number; qcResult: PurchaseQcResult[]; diff --git a/src/app/api/po/index.ts b/src/app/api/po/index.ts index 6e1c3a2..f7a312a 100644 --- a/src/app/api/po/index.ts +++ b/src/app/api/po/index.ts @@ -8,6 +8,13 @@ import { Uom } from "../settings/uom"; import { RecordsRes } from "../utils"; import { PutAwayLine } from "./actions"; +export enum StockInStatus { + PENDING = "pending", + RECEIVED = "received", + APPROVED = "escalated", + REJECTED = "rejected", + COMPLETED = "completed", +} export interface PoResult { id: number; code: string; diff --git a/src/components/PoDetail/PoInputGrid.tsx b/src/components/PoDetail/PoInputGrid.tsx index 80072c4..a2e6f3d 100644 --- a/src/components/PoDetail/PoInputGrid.tsx +++ b/src/components/PoDetail/PoInputGrid.tsx @@ -124,7 +124,7 @@ function PoInputGrid({ handleMailTemplateForStockInLine, printerCombo }: Props) { - console.log(itemDetail); + const { t } = useTranslation("purchaseOrder"); const apiRef = useGridApiRef(); const [rowModesModel, setRowModesModel] = useState({}); @@ -132,7 +132,7 @@ function PoInputGrid({ (row) => row.id as number, [], ); - console.log(stockInLine); + const [entries, setEntries] = useState(stockInLine || []); useEffect(() => { setEntries(stockInLine) @@ -448,6 +448,7 @@ const closeNewModal = useCallback(() => { btnSx = {label: t("escalation processing"), color:"warning.main"}; break;} case "rejected": + case "partially_completed": case "completed": btnSx = {label: t("view stockin"), color:"info.main"}; break; default: btnSx = {label: t("qc processing"), color:"success.main"}; } diff --git a/src/components/PoDetail/PutAwayForm.tsx b/src/components/PoDetail/PutAwayForm.tsx index dadedc8..c76452b 100644 --- a/src/components/PoDetail/PutAwayForm.tsx +++ b/src/components/PoDetail/PutAwayForm.tsx @@ -338,7 +338,8 @@ const PutAwayForm: React.FC = ({ itemDetail, warehouse, disabled, printer ); const addRowDefaultValue = useMemo(() => { - const defaultMaxQty = watch("acceptedQty") - watch("putAwayLines").reduce((acc, cur) => acc + cur.qty, 0) + 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 return {qty: defaultMaxQty, warehouseId: defaultWarehouseId, warehouse: defaultWarehouse, printQty: 1, _isNew: true } as Partial diff --git a/src/components/PoDetail/QcComponent.tsx b/src/components/PoDetail/QcComponent.tsx index 9780f02..ad482ec 100644 --- a/src/components/PoDetail/QcComponent.tsx +++ b/src/components/PoDetail/QcComponent.tsx @@ -19,7 +19,7 @@ import { Tooltip, Typography, } from "@mui/material"; -import { useFormContext, Controller } from "react-hook-form"; +import { useFormContext, Controller, FieldPath, useFieldArray } from "react-hook-form"; import { useTranslation } from "react-i18next"; import StyledDataGrid from "../StyledDataGrid"; import { Dispatch, SetStateAction, useCallback, useEffect, useMemo, useState } from "react"; @@ -59,8 +59,8 @@ interface Props { itemDetail: StockInLine & { qcResult?: PurchaseQcResult[] } & { escResult?: EscalationResult[] }; qc: QcItemWithChecks[]; disabled: boolean; - qcItems: QcData[] - setQcItems: Dispatch> + // qcItems: QcData[] + // setQcItems: Dispatch> } type EntryError = @@ -71,7 +71,7 @@ type EntryError = type QcRow = TableRow, EntryError>; // fetchQcItemCheck -const QcComponent: React.FC = ({ qc, itemDetail, disabled, qcItems, setQcItems }) => { +const QcComponent: React.FC = ({ qc, itemDetail, disabled = false }) => { const { t } = useTranslation("purchaseOrder"); const apiRef = useGridApiRef(); const { @@ -93,8 +93,9 @@ const QcComponent: React.FC = ({ qc, itemDetail, disabled, qcItems, setQc // const [qcResult, setQcResult] = useState(); const qcAccept = watch("qcAccept"); const qcDecision = watch("qcDecision"); //WIP - const qcResult = watch("qcResult"); - console.log(qcResult); + // const qcResult = useMemo(() => [...watch("qcResult")], [watch("qcResult")]); + const qcResult = [...watch("qcResult")]; + // const [qcAccept, setQcAccept] = useState(true); // const [qcItems, setQcItems] = useState(dummyQCData) @@ -119,32 +120,59 @@ const QcComponent: React.FC = ({ qc, itemDetail, disabled, qcItems, setQc [], ); + // W I P // + const validateFieldFail = (field : FieldPath, condition: boolean, message: string) : boolean => { + // console.log("Checking if " + message) + if (condition) { setError(field, { message: message}); return false; } + else { clearErrors(field); return true; } + } + //// validate form const accQty = watch("acceptQty"); const validateForm = useCallback(() => { if (qcDecision == 1) { - if (accQty > itemDetail.acceptedQty) { - setError("acceptQty", { - message: `${t("acceptQty must not greater than")} ${ - itemDetail.acceptedQty - }`, - type: "required", - }); + if (accQty > itemDetail.acceptedQty){ + setError("acceptQty", { message: `${t("acceptQty must not greater than")} ${ + itemDetail.acceptedQty}` }); } - if (accQty < 1) { - setError("acceptQty", { - message: t("minimal value is 1"), - type: "required", - }); + if (accQty < 1){ + setError("acceptQty", { message: t("minimal value is 1") }); } - if (isNaN(accQty)) { - setError("acceptQty", { - message: t("value must be a number"), - type: "required", - }); + if (isNaN(accQty)){ + setError("acceptQty", { message: t("value must be a number") }); } } - }, [accQty, qcDecision]); + },[setError, qcDecision, accQty, itemDetail]) + + useEffect(() => { // W I P // ----- + if (qcDecision == 1) { + if (validateFieldFail("acceptQty", accQty > itemDetail.acceptedQty, `${t("acceptQty must not greater than")} ${ + itemDetail.acceptedQty}`)) return; + + if (validateFieldFail("acceptQty", accQty < 1, t("minimal value is 1"))) return; + if (validateFieldFail("acceptQty", isNaN(accQty), t("value must be a number"))) return; + } + + 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) + ); + if (validateFieldFail("qcResult", failedItemsWithoutQty.length > 0, `${t("Failed items must have failed quantity")}`)) return; + + // Check if all QC items have results + const itemsWithoutResult = qcResultItems.filter(item => item.qcPassed === undefined); + if (validateFieldFail("qcDecision", (itemsWithoutResult.length > 0 && itemDetail.status != "escalated"), + `${t("QC items without result")}`)) return; + + if (validateFieldFail("qcDecision", (!qcResultItems.every((qc) => qc.qcPassed) && qcDecision == 1 && itemDetail.status != "escalated"), + "有不合格檢查項目,無法收貨!")) return; // TODO: Fix it please + // submitDialogWithWarning(() => postStockInLineWithQc(qcData), t, {title:"有不合格檢查項目,確認接受收貨?", + // confirmButtonText: t("confirm putaway"), html: ""}); + // return; + + // console.log("Validated without errors"); + }, [accQty, qcDecision, watch("qcResult")]); useEffect(() => { clearErrors(); @@ -188,39 +216,41 @@ const QcComponent: React.FC = ({ qc, itemDetail, disabled, qcItems, setQc return ; } - const qcColumns: GridColDef[] = [ + const qcColumns: GridColDef[] = useMemo(() => [ { field: "code", headerName: t("qcItem"), flex: 2, - renderCell: (params) => ( + renderCell: (params) => { + const index = params.api.getRowIndexRelativeToVisibleRows(params.id) + 1; + return ( - {`${params.api.getRowIndexRelativeToVisibleRows(params.id) + 1}. ${params.value}`}
+ {`${index}. ${params.value}`}
{params.row.name}
- ), + )}, }, { - field: 'qcPassed', + field: 'qcResult', headerName: t("qcResult"), flex: 1.5, renderCell: (params) => { - const currentValue = params.row; + const rowValue = params.row; const index = params.api.getRowIndexRelativeToVisibleRows(params.id); - // console.log(currentValue.row); + // console.log(rowValue.row); return ( { - const value = e.target.value; - setQcItems((prev) => - prev.map((r): QcData => (r.id === params.id ? { ...r, qcPassed: value === "true" } : r)) - ); - // setValue(`qcResult.${index}.qcPassed`, value == "true"); + const value = (e.target.value === "true"); + // setQcItems((prev) => + // prev.map((r): QcData => (r.id === params.id ? { ...r, qcPassed: value === "true" } : r)) + // ); + setValue(`qcResult.${index}.qcPassed`, value); }} name={`qcPassed-${params.id}`} > @@ -230,7 +260,7 @@ const QcComponent: React.FC = ({ qc, itemDetail, disabled, qcItems, setQc label="合格" disabled={disabled || itemDetail.status == "escalated"} sx={{ - color: currentValue.qcPassed === true ? "green" : "inherit", + color: rowValue.qcPassed === true ? "green" : "inherit", "& .Mui-checked": {color: "green"} }} /> @@ -240,7 +270,7 @@ const QcComponent: React.FC = ({ qc, itemDetail, disabled, qcItems, setQc label="不合格" disabled={disabled || itemDetail.status == "escalated"} sx={{ - color: currentValue.qcPassed === false ? "red" : "inherit", + color: rowValue.qcPassed === false ? "red" : "inherit", "& .Mui-checked": {color: "red"} }} /> @@ -254,58 +284,68 @@ const QcComponent: React.FC = ({ qc, itemDetail, disabled, qcItems, setQc headerName: t("failedQty"), flex: 1, // editable: true, - renderCell: (params) => ( - { - const v = e.target.value; - const next = v === '' ? undefined : Number(v); - if (Number.isNaN(next)) return; - setQcItems((prev) => - prev.map((r) => (r.id === params.id ? { ...r, failQty: next } : r)) - ); - // setValue(`failQty`,failQty); - }} - onClick={(e) => e.stopPropagation()} - onMouseDown={(e) => e.stopPropagation()} - onKeyDown={(e) => e.stopPropagation()} - inputProps={{ min: 0 }} - sx={{ width: '100%' }} - /> - ), + renderCell: (params) => { + const index = params.api.getRowIndexRelativeToVisibleRows(params.id); + return ( + { + const v = e.target.value; + const next = v === '' ? undefined : Number(v); + if (Number.isNaN(next)) return; + // setQcItems((prev) => + // prev.map((r) => (r.id === params.id ? { ...r, failQty: next } : r)) + // ); + setValue(`qcResult.${index}.failQty`, next); + }} + onClick={(e) => e.stopPropagation()} + onMouseDown={(e) => e.stopPropagation()} + onKeyDown={(e) => e.stopPropagation()} + inputProps={{ min: 0 }} + sx={{ width: '100%' }} + /> + ); + }, }, { field: "remarks", headerName: t("remarks"), flex: 2, - renderCell: (params) => ( - { - const remarks = e.target.value; - // const next = v === '' ? undefined : Number(v); - // if (Number.isNaN(next)) return; - setQcItems((prev) => - prev.map((r) => (r.id === params.id ? { ...r, remarks: remarks } : r)) - ); - }} - // {...register(`qcResult.${params.row.rowIndex}.remarks`, { - // required: "remarks required!", - // })} - onClick={(e) => e.stopPropagation()} - onMouseDown={(e) => e.stopPropagation()} - onKeyDown={(e) => e.stopPropagation()} - inputProps={{ min: 0 }} - sx={{ width: '100%' }} - /> - ), + renderCell: (params) => { + const index = params.api.getRowIndexRelativeToVisibleRows(params.id); + return ( + { + const value = e.target.value; + setValue(`qcResult.${index}.remarks`, value); + }} + // onChange={(e) => { + // const remarks = e.target.value; + // // const next = v === '' ? undefined : Number(v); + // // if (Number.isNaN(next)) return; + // // setQcItems((prev) => + // // prev.map((r) => (r.id === params.id ? { ...r, remarks: remarks } : r)) + // // ); + // }} + // {...register(`qcResult.${index}.remarks`, { + // required: "remarks required!", + // })} + onClick={(e) => e.stopPropagation()} + onMouseDown={(e) => e.stopPropagation()} + onKeyDown={(e) => e.stopPropagation()} + inputProps={{ min: 0 }} + sx={{ width: '100%' }} + /> + ); + }, }, - ] + ], []) // Set initial value for acceptQty useEffect(() => { @@ -316,12 +356,23 @@ const QcComponent: React.FC = ({ qc, itemDetail, disabled, qcItems, setQc } }, [itemDetail?.demandQty, itemDetail?.acceptedQty, setValue]); + useEffect(() => { + // console.log("Qc Result updated:", qcResult); + if (qcResult.length < 1) { // New QC + const mutableQcData = dummyQCData; + // const mutableQcData = JSON.parse(JSON.stringify(dummyQCData)); + // replace([mutableQcData]); + setValue("qcResult", mutableQcData); + // setValue("qcResult.0.qcPassed", false); + } + }, [qcResult, setValue]) + // const [openCollapse, setOpenCollapse] = useState(false) const [isCollapsed, setIsCollapsed] = useState(true); const onFailedOpenCollapse = useCallback((qcItems: PurchaseQcResult[]) => { const isFailed = qcItems.some((qc) => !qc.qcPassed) - console.log(isFailed) + // console.log(isFailed) if (isFailed) { setIsCollapsed(true) } else { @@ -336,30 +387,29 @@ const QcComponent: React.FC = ({ qc, itemDetail, disabled, qcItems, setQc useEffect(() => { - console.log("ItemDetail in QC:", itemDetail); - console.log("Qc Result in QC:", qcResult); + console.log("%c QC ItemDetail updated:", "color: gold", itemDetail); }, [itemDetail]); - const setQcDecision = (status : string | undefined) => { + const setDefaultQcDecision = (status : string | undefined) => { const param = status?.toLowerCase(); if (param !== undefined && param !== null) { - if (param == "completed") { + if (param == "completed" || param == "partially_completed") { return 1; } else if (param == "rejected") { return 2; } else if (param == "escalated") { - return 3; + return 1; // For new flow + // return 3; } else { return undefined; } } else { return undefined; } } - - useEffect(() => { - // onFailedOpenCollapse(qcItems) // This function is no longer needed - }, [qcItems]); + // useEffect(() => { + // // onFailedOpenCollapse(qcItems) + // }, [qcItems]); return ( <> @@ -405,9 +455,11 @@ const QcComponent: React.FC = ({ qc, itemDetail, disabled, qcItems, setQc /> */} 0 ? qcResult : qcItems} + rows={qcResult} + // rows={qcResult && qcResult.length > 0 ? qcResult : qcItems} // rows={disabled? qcResult:qcItems} autoHeight + sortModel={[]} /> @@ -443,57 +495,69 @@ const QcComponent: React.FC = ({ qc, itemDetail, disabled, qcItems, setQc name="qcDecision" // name="qcAccept" control={control} - defaultValue={setQcDecision(itemDetail?.status)} + defaultValue={setDefaultQcDecision(itemDetail?.status)} // defaultValue={true} render={({ field }) => ( - { - const value = e.target.value.toString();// === 'true'; - if (value != "1" && Boolean(errors.acceptQty)) { - // if (!value && Boolean(errors.acceptQty)) { - setValue("acceptQty", itemDetail.acceptedQty ?? 0); - } - field.onChange(value); - }} - > - } label="接受來貨" /> - - {itemDetail.status == "escalated" && ( - - )} - - {itemDetail.status == "pending" && (<> - } - sx={{"& .Mui-checked": {color: "red"}}} - label="不接受並退貨" /> - + <> + {/* + {errors.qcDecision?.message} + */} + { + const value = e.target.value.toString();// === 'true'; + if (value != "1" && Boolean(errors.acceptQty)) { + // if (!value && Boolean(errors.acceptQty)) { + setValue("acceptQty", itemDetail.acceptedQty ?? 0); + } + field.onChange(value); + }} + > + } label="接受來貨" /> - } - sx={{"& .Mui-checked": {color: "blue"}}} - label="上報品檢結果" /> - )} - + {(itemDetail.status == "escalated" || (disabled && accQty != itemDetail.acceptedQty && qcDecision == 1)) && ( //TODO Improve + + + + )} + + } + sx={{"& .Mui-checked": {color: "red"}}} + label= {itemDetail.status == "escalated" ? "全部拒絕並退貨" : "不接受並退貨"} /> + + {(itemDetail.status == "pending" || disabled) && (<> + } + sx={{"& .Mui-checked": {color: "blue"}}} + label="上報品檢結果" /> + )} + + )} /> diff --git a/src/components/PoDetail/QcStockInModalVer2.tsx b/src/components/PoDetail/QcStockInModalVer2.tsx index bcd133c..0e042af 100644 --- a/src/components/PoDetail/QcStockInModalVer2.tsx +++ b/src/components/PoDetail/QcStockInModalVer2.tsx @@ -30,7 +30,6 @@ import dayjs from "dayjs"; import { fetchPoQrcode } from "@/app/api/pdf/actions"; import { downloadFile } from "@/app/utils/commonUtil"; import { PrinterCombo } from "@/app/api/settings/printer"; -import { watch } from "fs"; import { EscalationResult } from "@/app/api/escalation"; import { SessionWithTokens } from "@/config/authConfig"; @@ -93,7 +92,7 @@ const PoQcStockInModalVer2: React.FC = ({ } = useTranslation("purchaseOrder"); // Select Printer - const [selectedPrinter, setSelectedPrinter] = useState(printerCombo[0]) + const [selectedPrinter, setSelectedPrinter] = useState(printerCombo[0]); const defaultNewValue = useMemo(() => { return ( @@ -104,7 +103,7 @@ const defaultNewValue = useMemo(() => { // putAwayLines: dummyPutAwayLine, // putAwayLines: itemDetail.putAwayLines.map((line) => (return {...line, printQty: 1})) ?? [], putAwayLines: itemDetail.putAwayLines?.map((line) => ({...line, printQty: 1, _isNew: false})) ?? [], - qcResult: (itemDetail.qcResult && itemDetail.qcResult?.length > 0) ? itemDetail.qcResult : [],//[...dummyQCData], + // qcResult: (itemDetail.qcResult && itemDetail.qcResult?.length > 0) ? itemDetail.qcResult : [],//[...dummyQCData], escResult: (itemDetail.escResult && itemDetail.escResult?.length > 0) ? itemDetail.escResult : [], receiptDate: itemDetail.receiptDate ?? dayjs().add(0, "month").format(INPUT_DATE_FORMAT), acceptQty: itemDetail.demandQty?? itemDetail.acceptedQty, @@ -135,27 +134,17 @@ const [qcItems, setQcItems] = useState(dummyQCData) } else return false; }; - - useEffect(() => { - formProps.reset({ - ...itemDetail, - dnDate: dayjsToInputDateString(dayjs()), - // putAwayLines: dummyPutAwayLine, - putAwayLines: itemDetail.putAwayLines?.map((line) => ({...line, printQty: 1, _isNew: false})) ?? [], - }) - setOpenPutaway(isPutaway); - - }, [open, itemDetail]) 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("ITEM", itemDetail); + console.log("Modal ItemDetail updated:", itemDetail); }, [itemDetail]); useEffect(() => { @@ -170,7 +159,7 @@ const [qcItems, setQcItems] = useState(dummyQCData) setQcItems(dummyQCData); setOpenPutaway(isPutaway); - }, [open, defaultNewValue]) + }, [open]) const [openPutaway, setOpenPutaway] = useState(false); const onOpenPutaway = useCallback(() => { @@ -216,12 +205,17 @@ const [qcItems, setQcItems] = useState(dummyQCData) async (data, event) => { console.log("QC Submission:", event!.nativeEvent); // TODO: Move validation into QC page - + + // if (errors.length > 0) { + // alert(`未完成品檢: ${errors.map((err) => err[1].message)}`); + // return; + // } + // Get QC data from the shared form context const qcAccept = data.qcDecision == 1; // const qcAccept = data.qcAccept; - const acceptQty = data.acceptQty as number; - const qcResults = qcItems; // qcItems; + const acceptQty = Number(data.acceptQty); + const qcResults = data.qcResult || dummyQCData; // qcItems; // const qcResults = data.qcResult as PurchaseQcResult[]; // qcItems; // const qcResults = viewOnly? data.qcResult as PurchaseQcResult[] : qcItems; @@ -239,9 +233,10 @@ const [qcItems, setQcItems] = useState(dummyQCData) } // Check if QC accept decision is made + if (data.qcDecision === undefined) { // if (qcAccept === undefined) { - // validationErrors.push("QC accept/reject decision is required"); - // } + validationErrors.push(t("QC decision is required")); + } // Check if accept quantity is valid if (acceptQty === undefined || acceptQty <= 0) { @@ -250,10 +245,12 @@ const [qcItems, setQcItems] = useState(dummyQCData) // Check if dates are input if (data.productionDate === undefined || data.productionDate == null) { - validationErrors.push("請輸入生產日期!"); + alert("請輸入生產日期!"); + return; } if (data.expiryDate === undefined || data.expiryDate == null) { - validationErrors.push("請輸入到期日!"); + alert("請輸入到期日!"); + return; } if (!qcResults.every((qc) => qc.qcPassed) && qcAccept && itemDetail.status != "escalated") { //TODO: fix it please! validationErrors.push("有不合格檢查項目,無法收貨!"); @@ -265,10 +262,10 @@ 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! - // validationErrors.push(`${t("QC items without result")}`); - // // validationErrors.push(`${t("QC items without result")}: ${itemsWithoutResult.map(item => item.code).join(', ')}`); - // } + if (itemsWithoutResult.length > 0 && itemDetail.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(', ')}`); + } if (validationErrors.length > 0) { console.error("QC Validation failed:", validationErrors); @@ -286,7 +283,7 @@ const [qcItems, setQcItems] = useState(dummyQCData) qcAccept: qcAccept? qcAccept : false, acceptQty: acceptQty? acceptQty : 0, qcResult: itemDetail.status != "escalated" ? qcResults.map(item => ({ - // id: item.id, + id: item.id, qcItemId: item.id, // code: item.code, // qcDescription: item.qcDescription, @@ -307,7 +304,7 @@ const [qcItems, setQcItems] = useState(dummyQCData) recordDate : dayjsToInputDateString(dayjs()), handlerId : Number(session?.id), } - console.log("ESCALATION RESULT", escalationLog); + console.log("Escalation Data for submission", escalationLog); await postStockInLine({...qcData, escalationLog}); } else { await postStockInLine(qcData); @@ -323,7 +320,7 @@ const [qcItems, setQcItems] = useState(dummyQCData) return ; }, - [onOpenPutaway, qcItems], + [onOpenPutaway, qcItems, formProps.formState.errors], ); const postStockInLine = useCallback(async (args: ModalFormInput) => { @@ -371,8 +368,10 @@ const [qcItems, setQcItems] = useState(dummyQCData) // binLocation: data.binLocation, // putawayQuantity: data.putawayQuantity, // putawayNotes: data.putawayNotes, - acceptQty: itemDetail.demandQty?? itemDetail.acceptedQty, - ...data, + acceptQty: Number(data.acceptQty?? (itemDetail.demandQty?? (itemDetail.acceptedQty))), //TODO improve + warehouseId: data.warehouseId, + status: data.status, //TODO Fix it! + // ...data, dnDate : data.dnDate? arrayToInputDateString(data.dnDate) : dayjsToInputDateString(dayjs()), productionDate : arrayToInputDateString(data.productionDate), @@ -386,6 +385,21 @@ const [qcItems, setQcItems] = useState(dummyQCData) } as ModalFormInput; console.log("Putaway Data:", putawayData); + console.log("DEBUG",data.putAwayLines); + if (data.putAwayLines!!.filter((line) => line._isNew !== false).length <= 0) { + alert("請新增上架資料!"); + return; + } + console.log(typeof data.putAwayLines!![0].qty + " = 'number'"); + console.log(typeof data.putAwayLines!![0].qty !== "number"); + if (data.putAwayLines!!.filter((line) => /[^0-9]/.test(String(line.qty))).length > 0) { //TODO Improve + alert("上架數量不正確!"); + return; + } + if (data.putAwayLines!!.reduce((acc, cur) => acc + Number(cur.qty), 0) > putawayData.acceptQty!!) { + alert(`上架數量不能大於 ${putawayData.acceptQty}!`); + return; + } // Handle putaway submission logic here const res = await postStockInLine(putawayData); console.log("result ", res); @@ -438,7 +452,7 @@ const [qcItems, setQcItems] = useState(dummyQCData) useEffect(() => { // maybe check if submitted before - console.log(qcItems) + console.log("Modal QC Items updated:", qcItems); // checkQcIsPassed(qcItems) }, [qcItems, checkQcIsPassed]) @@ -491,7 +505,7 @@ const [qcItems, setQcItems] = useState(dummyQCData) color="primary" sx={{ mt: 1 }} onClick={handlePrint} - disabled={isPrinting} + disabled={isPrinting || printerCombo.length <= 0} > {isPrinting ? t("Printing") : t("print")} @@ -543,8 +557,8 @@ const [qcItems, setQcItems] = useState(dummyQCData) qc={qc!} itemDetail={itemDetail} disabled={viewOnly} - qcItems={qcItems} - setQcItems={setQcItems} + // qcItems={qcItems} + // setQcItems={setQcItems} /> diff --git a/src/components/PoDetail/StockInFormVer2.tsx b/src/components/PoDetail/StockInFormVer2.tsx index e9d8d3e..336c27c 100644 --- a/src/components/PoDetail/StockInFormVer2.tsx +++ b/src/components/PoDetail/StockInFormVer2.tsx @@ -92,10 +92,19 @@ const StockInFormVer2: React.FC = ({ const expiryDate = watch("expiryDate"); const uom = watch("uom"); + //// TODO : Add Checking //// + // Check if dates are input + // if (data.productionDate === undefined || data.productionDate == null) { + // validationErrors.push("請輸入生產日期!"); + // } + // if (data.expiryDate === undefined || data.expiryDate == null) { + // validationErrors.push("請輸入到期日!"); + // } + useEffect(() => { - console.log(uom); - console.log(productionDate); - console.log(expiryDate); + // console.log(uom); + // console.log(productionDate); + // console.log(expiryDate); if (expiryDate) clearErrors(); if (productionDate) clearErrors(); }, [productionDate, expiryDate, clearErrors]); diff --git a/src/i18n/zh/purchaseOrder.json b/src/i18n/zh/purchaseOrder.json index 97edfb7..7117860 100644 --- a/src/i18n/zh/purchaseOrder.json +++ b/src/i18n/zh/purchaseOrder.json @@ -58,7 +58,7 @@ "putaway": "上架", "delete": "刪除", "Accept quantity must be greater than 0": "揀收數量不能少於1", - "QC items without result": "請完成品檢結果", + "QC items without result": "有未完成品檢項目", "Failed items must have failed quantity": "請輸入不合格數量", "qty cannot be greater than remaining qty": "數量不能大於剩餘數量", "acceptQty must not greater than": "揀收數量不能大於", @@ -70,8 +70,9 @@ "determine2": "上報2", "determine3": "上報3", "receiving": "收貨中", - "received": "已檢收", + "received": "待上架", "completed": "已上架", + "partially_completed": "已部分上架", "rejected": "已拒絕", "escalated": "已上報", "status": "狀態", @@ -135,5 +136,7 @@ "Found": "已找到", "escalation processing": "處理上報記錄", "Printer": "列印機", - "Printing": "列印中" + "Printing": "列印中", + "rejectQty": "拒絕數量", + "QC decision is required": "請決定品檢結果" }