"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, Box, Button, Grid, Modal, ModalProps, Stack, TextField, Typography, } from "@mui/material"; import { Dispatch, SetStateAction, useCallback, useEffect, useMemo, useState } from "react"; import { FormProvider, SubmitErrorHandler, SubmitHandler, useForm } from "react-hook-form"; import { StockInLineRow } from "./PoInputGrid"; import { useTranslation } from "react-i18next"; import StockInForm from "./StockInForm"; import StockInFormVer2 from "./StockInFormVer2"; import QcComponent from "./QcComponent"; import { dummyPutAwayLine, dummyQCData } from "./dummyQcTemplate"; // import QcFormVer2 from "./QcFormVer2"; import PutAwayForm from "./PutAwayForm"; import { GridRowModes, useGridApiRef } from "@mui/x-data-grid"; import {submitDialogWithWarning} from "../Swal/CustomAlerts"; import { INPUT_DATE_FORMAT, arrayToDateString, arrayToInputDateString, dayjsToInputDateString } from "@/app/utils/formatUtil"; 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 { EscalationResult } from "@/app/api/escalation"; import { SessionWithTokens } from "@/config/authConfig"; import { GridRowModesModel } from "@mui/x-data-grid"; import { isEmpty } from "lodash"; const style = { position: "absolute", top: "50%", left: "50%", transform: "translate(-50%, -50%)", bgcolor: "background.paper", pt: 5, px: 5, pb: 10, display: "block", width: { xs: "90%", sm: "90%", md: "90%" }, // height: { xs: "60%", sm: "60%", md: "60%" }, }; interface CommonProps extends Omit { // setRows: Dispatch>; setEntries?: Dispatch>; setStockInLine?: Dispatch>; itemDetail: StockInLine & { qcResult?: PurchaseQcResult[] } & { escResult?: EscalationResult[] }; setItemDetail: Dispatch< SetStateAction< | (StockInLine & { warehouseId?: number; }) | 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[] }; } const PoQcStockInModalVer2: React.FC = ({ // type, // setRows, setEntries, setStockInLine, open, onClose, itemDetail, setItemDetail, session, qc, warehouse, handleMailTemplateForStockInLine, printerCombo }) => { const { t, i18n: { language }, } = useTranslation("purchaseOrder"); // Select Printer const [selectedPrinter, setSelectedPrinter] = useState(printerCombo[0]); const defaultNewValue = useMemo(() => { return ( { ...itemDetail, status: itemDetail.status ?? "pending", dnDate: arrayToInputDateString(itemDetail.dnDate)?? dayjsToInputDateString(dayjs()), // 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], 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, warehouseId: itemDetail.defaultWarehouseId ?? 1 } ) },[itemDetail]) const [qcItems, setQcItems] = useState(dummyQCData) const formProps = useForm({ defaultValues: { ...defaultNewValue, }, }); const closeHandler = useCallback>( () => { onClose?.(); // reset(); }, [onClose], ); const isPutaway = () => { if (itemDetail) { const status = itemDetail.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); }, [itemDetail]); useEffect(() => { const qcRes = itemDetail?.qcResult; // if (!qcRes || qcRes?.length <= 0) { // itemDetail.qcResult = dummyQCData; // } formProps.reset({ ...defaultNewValue }) setQcItems(dummyQCData); setOpenPutaway(isPutaway); }, [open]) const [openPutaway, setOpenPutaway] = useState(false); const onOpenPutaway = useCallback(() => { setOpenPutaway(true); }, []); const onClosePutaway = useCallback(() => { setOpenPutaway(false); }, []); // Stock In submission handler const onSubmitStockIn = useCallback>( async (data, event) => { console.log("Stock In Submission:", event!.nativeEvent); // Extract only stock-in related fields const stockInData = { // quantity: data.quantity, // receiptDate: data.receiptDate, // batchNumber: data.batchNumber, // expiryDate: data.expiryDate, // warehouseId: data.warehouseId, // location: data.location, // unitCost: data.unitCost, data: data, // Add other stock-in specific fields from your form }; console.log("Stock In Data:", stockInData); // Handle stock-in submission logic here // e.g., call API, update state, etc. }, [], ); // QC submission handler const onSubmitErrorQc = useCallback>( async (data, event) => { console.log("Error", data); }, [] ); // QC submission handler const onSubmitQc = useCallback>( 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 = Number(data.acceptQty); const qcResults = data.qcResult || dummyQCData; // qcItems; // const qcResults = data.qcResult as PurchaseQcResult[]; // qcItems; // const qcResults = viewOnly? data.qcResult as PurchaseQcResult[] : qcItems; // Validate QC data const validationErrors : string[] = []; // Check if failed items have failed quantity const failedItemsWithoutQty = qcResults.filter(item => item.qcPassed === false && (!item.failQty || item.failQty <= 0) ); if (failedItemsWithoutQty.length > 0) { validationErrors.push(`${t("Failed items must have failed quantity")}`); // validationErrors.push(`${t("Failed items must have failed quantity")}: ${failedItemsWithoutQty.map(item => item.code).join(', ')}`); } // Check if QC accept decision is made if (data.qcDecision === undefined) { // if (qcAccept === undefined) { validationErrors.push(t("QC decision is required")); } // Check if accept quantity is valid if (acceptQty === undefined || acceptQty <= 0) { validationErrors.push("Accept quantity must be greater than 0"); } // Check if dates are input if (data.productionDate === undefined || data.productionDate == null) { alert("請輸入生產日期!"); return; } if (data.expiryDate === undefined || data.expiryDate == null) { alert("請輸入到期日!"); return; } if (!qcResults.every((qc) => qc.qcPassed) && qcAccept && itemDetail.status != "escalated") { //TODO: fix it please! validationErrors.push("有不合格檢查項目,無法收貨!"); // submitDialogWithWarning(() => postStockInLineWithQc(qcData), t, {title:"有不合格檢查項目,確認接受收貨?", // confirmButtonText: t("confirm putaway"), html: ""}); // return; } // 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 (validationErrors.length > 0) { console.error("QC Validation failed:", validationErrors); alert(`未完成品檢: ${validationErrors}`); return; } const qcData = { dnNo : data.dnNo? data.dnNo : "DN00000", dnDate : data.dnDate? arrayToInputDateString(data.dnDate) : dayjsToInputDateString(dayjs()), productionDate : arrayToInputDateString(data.productionDate), expiryDate : arrayToInputDateString(data.expiryDate), receiptDate : arrayToInputDateString(data.receiptDate), qcAccept: qcAccept? qcAccept : false, acceptQty: acceptQty? acceptQty : 0, qcResult: itemDetail.status != "escalated" ? qcResults.map(item => ({ id: item.id, qcItemId: item.id, // code: item.code, // qcDescription: item.qcDescription, 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 || '' })) : [], }; // const qcData = data; console.log("QC Data for submission:", qcData); if (data.qcDecision == 3) { // Escalate const escalationLog = { type : "qc", status : "pending", // TODO: update with supervisor decision reason : data.escalationLog?.reason, recordDate : dayjsToInputDateString(dayjs()), handlerId : Number(session?.id), } console.log("Escalation Data for submission", escalationLog); await postStockInLine({...qcData, escalationLog}); } else { await postStockInLine(qcData); } if (qcData.qcAccept) { // submitDialogWithWarning(onOpenPutaway, t, {title:"Save success, confirm to proceed?", // confirmButtonText: t("confirm putaway"), html: ""}); onOpenPutaway(); } else { closeHandler({}, "backdropClick"); } return ; }, [onOpenPutaway, qcItems, formProps.formState.errors], ); const postStockInLine = useCallback(async (args: ModalFormInput) => { const submitData = { ...itemDetail, ...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. }, [], ); // Put away model const [pafRowModesModel, setPafRowModesModel] = 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 warehouseId: data.warehouseId, status: data.status, //TODO Fix it! // ...data, dnDate : data.dnDate? arrayToInputDateString(data.dnDate) : dayjsToInputDateString(dayjs()), productionDate : arrayToInputDateString(data.productionDate), expiryDate : arrayToInputDateString(data.expiryDate), receiptDate : arrayToInputDateString(data.receiptDate), // for putaway data inventoryLotLines: data.putAwayLines?.filter((line) => line._isNew !== false) // Add other putaway specific fields } 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); // Close modal after successful putaway closeHandler({}, "backdropClick"); }, [closeHandler], ); // Print handler const [isPrinting, setIsPrinting] = useState(false) const handlePrint = useCallback(async () => { // console.log("Print putaway documents"); console.log("%c data", "background: white; color: red", formProps.watch("putAwayLines")); // Handle print logic here // window.print(); // const postData = { stockInLineIds: [itemDetail.id]}; // const response = await fetchPoQrcode(postData); // if (response) { // downloadFile(new Uint8Array(response.blobValue), response.filename) // } try { setIsPrinting(() => true) const data: PrintQrCodeForSilRequest = { stockInLineId: itemDetail.id, printerId: selectedPrinter.id, printQty: formProps.watch("putAwayLines")?.reduce((acc, cur) => acc + cur.printQty, 0) } const response = await printQrCodeForSil(data); if (response) { console.log(response) } } finally { setIsPrinting(() => false) } }, [itemDetail.id]); const acceptQty = formProps.watch("acceptedQty") 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]) return ( <> {openPutaway ? ( { setSelectedPrinter(value) }} renderInput={(params) => ( )} /> ) : ( <> {t("qc processing")} {/* */} {!viewOnly && ()} )} ); }; export default PoQcStockInModalVer2;