diff --git a/src/app/(main)/layout.tsx b/src/app/(main)/layout.tsx index dc236bc..4144526 100644 --- a/src/app/(main)/layout.tsx +++ b/src/app/(main)/layout.tsx @@ -37,7 +37,7 @@ export default async function MainLayout({ return ( - + {/* */} <> @@ -62,7 +62,7 @@ export default async function MainLayout({ - + {/* */} ); diff --git a/src/app/api/dashboard/actions.ts b/src/app/api/dashboard/actions.ts index da6d72d..bd0d240 100644 --- a/src/app/api/dashboard/actions.ts +++ b/src/app/api/dashboard/actions.ts @@ -30,9 +30,6 @@ export interface StockInLineEntry { acceptedQty: number; status?: string; expiryDate?: string; - productLotNo?: string; - receiptDate?: string; - dnDate?: string; } export interface PurchaseQcResult { diff --git a/src/app/api/po/actions.ts b/src/app/api/po/actions.ts index 92f1b7f..a013ab4 100644 --- a/src/app/api/po/actions.ts +++ b/src/app/api/po/actions.ts @@ -31,11 +31,14 @@ export interface StockInLineEntry { acceptedQty: number; status?: string; expiryDate?: string; + productLotNo?: string; + receiptDate?: string; + dnDate?: string; } export interface PurchaseQcResult{ qcItemId: number; - isPassed: boolean; + qcPassed: boolean; failQty: number; remarks?: string; @@ -111,7 +114,7 @@ export const fetchStockInLineInfo = cache(async (stockInLineId: number) => { export const createStockInLine = async (data: StockInLineEntry) => { const stockInLine = await serverFetchJson< - PostStockInLineResponse + PostStockInLineResponse >(`${BASE_API_URL}/stockInLine/create`, { method: "POST", body: JSON.stringify(data), @@ -125,7 +128,7 @@ export const updateStockInLine = async ( data: StockInLineEntry & ModalFormInput, ) => { const stockInLine = await serverFetchJson< - PostStockInLineResponse + PostStockInLineResponse >(`${BASE_API_URL}/stockInLine/update`, { method: "POST", body: JSON.stringify(data), diff --git a/src/app/api/po/index.ts b/src/app/api/po/index.ts index 55678b1..8cff8fc 100644 --- a/src/app/api/po/index.ts +++ b/src/app/api/po/index.ts @@ -54,7 +54,7 @@ export interface StockUomForPoLine { export interface StockInLine { id: number; - stockInId: number; + stockInId?: number; purchaseOrderId?: number; purchaseOrderLineId: number; itemId: number; @@ -63,23 +63,24 @@ export interface StockInLine { itemType: string; demandQty: number; acceptedQty: number; - qty: number; - processed: number; - price: number; - priceUnit: string; + qty?: 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; + supplier?: string; + lotNo?: string; + poCode?: string; + uom?: Uom; defaultWarehouseId: number; // id for now - dnNo: string; - dnDate: number[]; - stockQty: number; + dnNo?: string; + dnDate?: number[]; + stockQty?: number; } export const fetchPoList = cache(async (queryParams?: Record) => { diff --git a/src/app/api/qc/index.ts b/src/app/api/qc/index.ts index 6c65043..a99d457 100644 --- a/src/app/api/qc/index.ts +++ b/src/app/api/qc/index.ts @@ -20,7 +20,7 @@ export interface QcData { code: string, name: string, qcDescription: string, - isPassed: boolean | undefined + qcPassed: boolean | undefined failQty: number | undefined remarks: string | undefined } diff --git a/src/components/PickOrderSearch/QcFormVer2.tsx b/src/components/PickOrderSearch/QcFormVer2.tsx index 9c615a2..ebea29d 100644 --- a/src/components/PickOrderSearch/QcFormVer2.tsx +++ b/src/components/PickOrderSearch/QcFormVer2.tsx @@ -49,7 +49,7 @@ import EscalationComponent from "./EscalationComponent"; import QcDataGrid from "./QCDatagrid"; import StockInFormVer2 from "./StockInFormVer2"; import { dummyEscalationHistory, dummyQCData, QcData } from "./dummyQcTemplate"; -import { ModalFormInput } from "@/app/api/dashboard/actions"; +import { ModalFormInput } from "@/app/api/po/actions"; import { escape } from "lodash"; interface Props { diff --git a/src/components/PoDetail/PoDetail.tsx b/src/components/PoDetail/PoDetail.tsx index 6b39623..1715d8b 100644 --- a/src/components/PoDetail/PoDetail.tsx +++ b/src/components/PoDetail/PoDetail.tsx @@ -43,9 +43,11 @@ import { import { checkPolAndCompletePo, fetchPoInClient, + fetchPoListClient, fetchStockInLineInfo, PurchaseQcResult, startPo, + createStockInLine } from "@/app/api/po/actions"; import { useCallback, @@ -69,9 +71,7 @@ import DoneIcon from "@mui/icons-material/Done"; import { getCustomWidth } from "@/app/utils/commonUtil"; import PoInfoCard from "./PoInfoCard"; import { decimalFormatter, integerFormatter } from "@/app/utils/formatUtil"; -import { fetchPoListClient } from "@/app/api/po/actions"; import { List, ListItem, ListItemButton, ListItemText, Divider } from "@mui/material"; -import { createStockInLine } from "@/app/api/dashboard/actions"; import { Controller, FormProvider, useForm } from "react-hook-form"; import dayjs, { Dayjs } from "dayjs"; import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; @@ -81,7 +81,6 @@ import LoadingComponent from "../General/LoadingComponent"; //import { useRouter } from "next/navigation"; - type Props = { po: PoResult; qc: QcItemWithChecks[]; @@ -397,6 +396,9 @@ const PoDetail: React.FC = ({ po, qc, warehouse }) => { setTimeout(async () => { // post stock in line const oldId = row.id; + const acceptedQty = Number(polInputList[rowIndex].dnQty); + if (isNaN(acceptedQty) || acceptedQty <= 0) { alert("來貨數量必須大於0!"); return; } // Temp check, need update + const postData = { dnNo: dnFormProps.watch("dnNo"), dnDate: outputDateStringToInputDateString(dnFormProps.watch("dnDate")), @@ -405,7 +407,7 @@ const PoDetail: React.FC = ({ po, qc, warehouse }) => { itemName: row.itemName, purchaseOrderId: row.purchaseOrderId, purchaseOrderLineId: row.id, - acceptedQty: polInputList[rowIndex].dnQty || 0, + acceptedQty: acceptedQty, productLotNo: polInputList[rowIndex].lotNo || '', // acceptedQty: secondReceiveQty || 0, // acceptedQty: row.acceptedQty, @@ -535,7 +537,7 @@ const PoDetail: React.FC = ({ po, qc, warehouse }) => { handleStart() } > - 提交 + {t("submit")} diff --git a/src/components/PoDetail/QcFormVer2.tsx b/src/components/PoDetail/QcFormVer2.tsx index 09e2ee0..37667a9 100644 --- a/src/components/PoDetail/QcFormVer2.tsx +++ b/src/components/PoDetail/QcFormVer2.tsx @@ -49,7 +49,7 @@ import EscalationComponent from "./EscalationComponent"; import QcDataGrid from "./QCDatagrid"; import StockInFormVer2 from "./StockInFormVer2"; import { dummyEscalationHistory, dummyQCData } from "./dummyQcTemplate"; -import { ModalFormInput } from "@/app/api/dashboard/actions"; +import { ModalFormInput } from "@/app/api/po/actions"; import { escape } from "lodash"; import { PanoramaSharp } from "@mui/icons-material"; @@ -197,7 +197,7 @@ const QcFormVer2: React.FC = ({ qc, itemDetail, disabled, qcItems, setQcI ), }, { - field: 'qcResult', + field: 'qcPassed', headerName: t("qcResult"), flex: 1.5, renderCell: (params) => { @@ -208,14 +208,15 @@ const QcFormVer2: React.FC = ({ qc, itemDetail, disabled, qcItems, setQcI { const value = e.target.value; setQcItems((prev) => - prev.map((r): QcData => (r.id === params.id ? { ...r, isPassed: value === "true" } : r)) + prev.map((r): QcData => (r.id === params.id ? { ...r, qcPassed: value === "true" } : r)) ); }} - name={`isPassed-${params.id}`} + name={`qcPassed-${params.id}`} > = ({ qc, itemDetail, disabled, qcItems, setQcI label="合格" disabled={disabled} sx={{ - color: currentValue.isPassed === true ? "green" : "inherit", + color: currentValue.qcPassed === true ? "green" : "inherit", "& .Mui-checked": {color: "green"} }} /> @@ -233,7 +234,7 @@ const QcFormVer2: React.FC = ({ qc, itemDetail, disabled, qcItems, setQcI label="不合格" disabled={disabled} sx={{ - color: currentValue.isPassed === false ? "red" : "inherit", + color: currentValue.qcPassed === false ? "red" : "inherit", "& .Mui-checked": {color: "red"} }} /> @@ -251,8 +252,8 @@ const QcFormVer2: React.FC = ({ qc, itemDetail, disabled, qcItems, setQcI { const v = e.target.value; const next = v === '' ? undefined : Number(v); @@ -313,7 +314,7 @@ const QcFormVer2: React.FC = ({ qc, itemDetail, disabled, qcItems, setQcI const [isCollapsed, setIsCollapsed] = useState(true); const onFailedOpenCollapse = useCallback((qcItems: PurchaseQcResult[]) => { - const isFailed = qcItems.some((qc) => !qc.isPassed) + const isFailed = qcItems.some((qc) => !qc.qcPassed) console.log(isFailed) if (isFailed) { setIsCollapsed(true) @@ -439,7 +440,11 @@ const QcFormVer2: React.FC = ({ qc, itemDetail, disabled, qcItems, setQcI } sx={{"& .Mui-checked": {color: "red"}}} - label="不接受及上報" /> + label="不接受" /> + } + sx={{"& .Mui-checked": {color: "blue"}}} + label="上報品檢結果" /> )} /> diff --git a/src/components/PoDetail/QcStockInModalVer2.tsx b/src/components/PoDetail/QcStockInModalVer2.tsx index 879f4cf..79997c9 100644 --- a/src/components/PoDetail/QcStockInModalVer2.tsx +++ b/src/components/PoDetail/QcStockInModalVer2.tsx @@ -1,6 +1,6 @@ "use client"; import { StockInLine } from "@/app/api/po"; -import { ModalFormInput, PurchaseQcResult, StockInLineEntry, updateStockInLine } from "@/app/api/po/actions"; +import { ModalFormInput, PurchaseQcResult, StockInLineEntry, updateStockInLine, PurchaseQCInput } from "@/app/api/po/actions"; import { QcItemWithChecks, QcData } from "@/app/api/qc"; import { Box, @@ -22,7 +22,6 @@ import PutawayForm from "./PutawayForm"; import { dummyPutawayLine, dummyQCData } from "./dummyQcTemplate"; import { useGridApiRef } from "@mui/x-data-grid"; import {submitDialogWithWarning} from "../Swal/CustomAlerts"; -import { PurchaseQCInput, PutawayInput } from "@/app/api/dashboard/actions"; import { arrayToDateString, arrayToInputDateString, dayjsToInputDateString } from "@/app/utils/formatUtil"; import dayjs from "dayjs"; @@ -114,10 +113,11 @@ const [qcItems, setQcItems] = useState(dummyQCData) }, [open]) - const [isCompleted, setIsCompleted] = useState(false); + const [viewOnly, setViewOnly] = useState(false); useEffect(() => { - setIsCompleted(itemDetail.status.toLowerCase() == "completed") + const isViewOnly = itemDetail.status.toLowerCase() == "completed" || itemDetail.status.toLowerCase() == "rejected" + setViewOnly(isViewOnly) }, [itemDetail]); const [openPutaway, setOpenPutaway] = useState(false); @@ -162,12 +162,12 @@ const [qcItems, setQcItems] = useState(dummyQCData) const qcAccept = data.qcAccept; const acceptQty = data.acceptQty as number; const qcResults = qcItems; - // const qcResults = isCompleted? data.qcResult as PurchaseQcResult[] : qcItems; + // const qcResults = viewOnly? data.qcResult as PurchaseQcResult[] : qcItems; // Validate QC data const validationErrors : string[] = []; // Check if all QC items have results - const itemsWithoutResult = qcResults.filter(item => item.isPassed === undefined); + const itemsWithoutResult = qcResults.filter(item => item.qcPassed === undefined); if (itemsWithoutResult.length > 0) { validationErrors.push(`${t("QC items without result")}`); // validationErrors.push(`${t("QC items without result")}: ${itemsWithoutResult.map(item => item.code).join(', ')}`); @@ -175,7 +175,7 @@ const [qcItems, setQcItems] = useState(dummyQCData) // Check if failed items have failed quantity const failedItemsWithoutQty = qcResults.filter(item => - item.isPassed === false && (!item.failQty || item.failQty <= 0) + item.qcPassed === false && (!item.failQty || item.failQty <= 0) ); if (failedItemsWithoutQty.length > 0) { validationErrors.push(`${t("Failed items must have failed quantity")}`); @@ -194,10 +194,16 @@ const [qcItems, setQcItems] = useState(dummyQCData) // Check if dates are input if (data.productionDate === undefined || data.productionDate == null) { - validationErrors.push("Production Date cannot be null!"); + validationErrors.push("請輸入生產日期!"); } if (data.expiryDate === undefined || data.expiryDate == null) { - validationErrors.push("Expiry Date cannot be null!"); + validationErrors.push("請輸入到期日!"); + } + if (!qcResults.every((qc) => qc.qcPassed) && qcAccept) { + validationErrors.push("有不合格檢查項目,無法收貨!"); + // submitDialogWithWarning(() => postStockInLineWithQc(qcData), t, {title:"有不合格檢查項目,確認接受收貨?", + // confirmButtonText: t("confirm putaway"), html: ""}); + // return; } if (validationErrors.length > 0) { @@ -219,8 +225,8 @@ const [qcItems, setQcItems] = useState(dummyQCData) qcItemId: item.id, // code: item.code, // qcDescription: item.qcDescription, - isPassed: item.isPassed? item.isPassed : false, - failQty: (item.failQty && !item.isPassed) ? item.failQty : 0, + qcPassed: item.qcPassed? item.qcPassed : false, + failQty: (item.failQty && !item.qcPassed) ? item.failQty : 0, // failedQty: (typeof item.failedQty === "number" && !item.isPassed) ? item.failedQty : 0, remarks: item.remarks || '' })) @@ -228,42 +234,21 @@ const [qcItems, setQcItems] = useState(dummyQCData) // const qcData = data; console.log("QC Data for submission:", qcData); + + await postStockInLine(qcData); - if (!qcData.qcResult.every((qc) => qc.isPassed) && qcData.qcAccept) { - submitDialogWithWarning(() => postStockInLineWithQc(qcData), t, {title:"有不合格檢查項目,確認接受收貨?", - confirmButtonText: t("confirm putaway"), html: ""}); - return; + if (qcData.qcAccept) { + // submitDialogWithWarning(onOpenPutaway, t, {title:"Save success, confirm to proceed?", + // confirmButtonText: t("confirm putaway"), html: ""}); + onOpenPutaway(); + } else { + closeHandler({}, "backdropClick"); } - await postStockInLineWithQc(qcData); - // return; + return ; + }, [onOpenPutaway, qcItems], ); - - const postStockInLineWithQc = useCallback(async (qcData: PurchaseQCInput) => { - const args = { - ...qcData - // id: itemDetail.id, - // purchaseOrderId: itemDetail.purchaseOrderId, - // purchaseOrderLineId: itemDetail.purchaseOrderLineId, - // itemId: itemDetail.itemId, - // ...data, - // productionDate: productionDate, - // expiryDate: expiryDate, - // receiptDate: receiptDate, - } as ModalFormInput; - - await postStockInLine(args); - - if (qcData.qcAccept) { - // submitDialogWithWarning(onOpenPutaway, t, {title:"Save success, confirm to proceed?", - // confirmButtonText: t("confirm putaway"), html: ""}); - onOpenPutaway(); - } else { - closeHandler({}, "backdropClick"); - } - return ; - },[onOpenPutaway,closeHandler]); const postStockInLine = useCallback(async (args: ModalFormInput) => { const submitData = { @@ -340,7 +325,7 @@ const [qcItems, setQcItems] = useState(dummyQCData) const acceptQty = formProps.watch("acceptedQty") const checkQcIsPassed = useCallback((qcItems: PurchaseQcResult[]) => { - const isPassed = qcItems.every((qc) => qc.isPassed); + const isPassed = qcItems.every((qc) => qc.qcPassed); console.log(isPassed) if (isPassed) { formProps.setValue("passingQty", acceptQty) @@ -378,7 +363,7 @@ const [qcItems, setQcItems] = useState(dummyQCData)