浏览代码

rename old files

master
kelvinsuen 22 小时前
父节点
当前提交
ded3d5ae22
共有 7 个文件被更改,包括 1081 次插入84 次删除
  1. +2
    -2
      src/components/PoDetail/EscalationTab.tsx
  2. +2
    -2
      src/components/PoDetail/PoInputGrid.tsx
  3. +2
    -2
      src/components/PoDetail/PoQcStockInModal.tsx
  4. +0
    -1
      src/components/PoDetail/QcComponent.tsx
  5. +610
    -0
      src/components/PoDetail/QcStockInModal.tsx
  6. +156
    -77
      src/components/PoDetail/StockInForm.tsx
  7. +309
    -0
      src/components/PoDetail/StockInFormOld.tsx

+ 2
- 2
src/components/PoDetail/EscalationTab.tsx 查看文件

@@ -1,4 +1,4 @@
import StockInForm from "./StockInForm";
import StockInFormOld from "./StockInFormOld";
import EscalationLog from "./EscalationLog";
import EscalationComponent from "./EscalationComponent";
import React from "react";
@@ -14,7 +14,7 @@ interface Props {

const EscalationTab:React.FC<Props> = ({itemDetail, disabled}) => {
return <>
<StockInForm itemDetail={itemDetail} disabled={disabled}/>
<StockInFormOld itemDetail={itemDetail} disabled={disabled}/>
<EscalationLog/>
<EscalationComponent/>
</>


+ 2
- 2
src/components/PoDetail/PoInputGrid.tsx 查看文件

@@ -60,7 +60,7 @@ import PoQcStockInModal from "./PoQcStockInModal";
import DoDisturbIcon from "@mui/icons-material/DoDisturb";
import { useSession } from "next-auth/react";
// import { SessionWithTokens } from "src/config/authConfig";
import PoQcStockInModalVer2 from "./QcStockInModalVer2";
import QcStockInModal from "./QcStockInModal";
import { decimalFormatter, integerFormatter } from "@/app/utils/formatUtil";
import { PrinterCombo } from "@/app/api/settings/printer";
import { EscalationResult } from "@/app/api/escalation";
@@ -939,7 +939,7 @@ const closeNewModal = useCallback(() => {
/>
{modalInfo !== undefined && (
<>
<PoQcStockInModalVer2
<QcStockInModal
// setRows={setRows}
setEntries={setEntries}
setStockInLine={setStockInLine}


+ 2
- 2
src/components/PoDetail/PoQcStockInModal.tsx 查看文件

@@ -27,7 +27,7 @@ import { PurchaseOrderLine, StockInLine } from "@/app/api/po";
import { useSearchParams } from "next/navigation";
import { StockInLineRow } from "./PoInputGrid";
import EscalationForm from "./EscalationForm";
import StockInForm from "./StockInForm";
import StockInFormOld from "./StockInFormOld";
import PutAwayForm from "./PutAwayForm";
import {
INPUT_DATE_FORMAT,
@@ -431,7 +431,7 @@ const PoQcStockInModal: React.FC<Props> = ({
/>
)}
{itemDetail !== undefined && type === "stockIn" && (
<StockInForm
<StockInFormOld
itemDetail={itemDetail}
disabled={!renderSubmitButton}
/>


+ 0
- 1
src/components/PoDetail/QcComponent.tsx 查看文件

@@ -48,7 +48,6 @@ import { NEXT_PUBLIC_API_URL } from "@/config/api";
import axiosInstance from "@/app/(main)/axios/axiosInstance";
import EscalationComponent from "./EscalationComponent";
import QcDataGrid from "./QCDatagrid";
import StockInFormVer2 from "./StockInFormVer2";
import { dummyEscalationHistory, dummyQCData } from "./dummyQcTemplate";
import { ModalFormInput } from "@/app/api/po/actions";
import { escape, min } from "lodash";


+ 610
- 0
src/components/PoDetail/QcStockInModal.tsx 查看文件

@@ -0,0 +1,610 @@
"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 QcComponent from "./QcComponent";
import { dummyPutAwayLine, dummyQCData } from "./dummyQcTemplate";
import PutAwayForm from "./PutAwayForm";
import { GridRowModes, GridRowSelectionModel, useGridApiRef } from "@mui/x-data-grid";
import {submitDialogWithWarning} from "../Swal/CustomAlerts";
import { INPUT_DATE_FORMAT, arrayToDateString, 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";
import { EscalationCombo } from "@/app/api/user";
import { truncateSync } from "fs";


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<ModalProps, "children"> {
// setRows: Dispatch<SetStateAction<PurchaseOrderLine[]>>;
setEntries?: Dispatch<SetStateAction<StockInLineRow[]>>;
setStockInLine?: Dispatch<SetStateAction<StockInLine[]>>;
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<Props> = ({
// 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: 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,
}
)
},[itemDetail])

const [qcItems, setQcItems] = useState(dummyQCData)
const formProps = useForm<ModalFormInput>({
defaultValues: {
...defaultNewValue,
},
});
const closeHandler = useCallback<NonNullable<ModalProps["onClose"]>>(
() => {
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(() => {
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<SubmitHandler<ModalFormInput>>(
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<SubmitErrorHandler<ModalFormInput>>(
async (data, event) => {
console.log("Error", data);
}, []
);

// QC submission handler
const onSubmitQc = useCallback<SubmitHandler<ModalFormInput>>(
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;
let acceptQty = Number(data.acceptQty);
const qcResults = data.qcResult?.filter((qc) => qc.escalationLogId === undefined) || []; // Remove old QC data
// 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 (data.qcDecision == 2) {
acceptQty = 0;
} else {
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? arrayToDateString(data.dnDate, "input") : dayjsToInputDateString(dayjs()),
productionDate : arrayToDateString(data.productionDate, "input"),
expiryDate : arrayToDateString(data.expiryDate, "input"),
receiptDate : arrayToDateString(data.receiptDate, "input"),
qcAccept: qcAccept? qcAccept : false,
acceptQty: acceptQty? acceptQty : 0,
// qcResult: itemDetail.status != "escalated" ? qcResults.map(item => ({
qcResult: qcResults.map(item => ({
// id: item.id,
qcItemId: item.qcItemId,
// 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
if (data.escalationLog?.handlerId == undefined) { alert("請選擇上報負責同事!"); return; }
else if (data.escalationLog?.handlerId < 1) { alert("上報負責同事資料有誤"); return; }
const escalationLog = {
type : "qc",
status : "pending", // TODO: update with supervisor decision
reason : data.escalationLog?.reason,
recordDate : dayjsToInputDateString(dayjs()),
handlerId : data.escalationLog?.handlerId,
}
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<SubmitHandler<ModalFormInput>>(
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<GridRowModesModel>({})
const [pafRowSelectionModel, setPafRowSelectionModel] = useState<GridRowSelectionModel>([])
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<SubmitHandler<ModalFormInput>>(
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? arrayToDateString(data.dnDate, "input") : dayjsToInputDateString(dayjs()),
productionDate : arrayToDateString(data.productionDate, "input"),
expiryDate : arrayToDateString(data.expiryDate, "input"),
receiptDate : arrayToDateString(data.receiptDate, "input"),

// 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;
}
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)
if ((formProps.watch("putAwayLines") ?? []).filter((line) => /[^0-9]/.test(String(line.printQty))).length > 0) { //TODO Improve
alert("列印數量不正確!");
return;
}
// console.log(pafRowSelectionModel)
const printList = formProps.watch("putAwayLines")?.filter((line) => ((pafRowSelectionModel ?? []).some((model) => model === line.id))) ?? []
const printQty = printList.reduce((acc, cur) => acc + cur.printQty, 0)
// console.log(printQty)
const data: PrintQrCodeForSilRequest = {
stockInLineId: itemDetail.id,
printerId: selectedPrinter.id,
printQty: printQty
}
const response = await printQrCodeForSil(data);
if (response) {
console.log(response)
}
} finally {
setIsPrinting(() => false)
}
}, [itemDetail.id, pafRowSelectionModel]);
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 (
<>
<FormProvider {...formProps}>
<Modal open={open} onClose={closeHandler}>
<Box
sx={{
...style,
padding: 2,
maxHeight: "90vh",
overflowY: "auto",
marginLeft: 3,
marginRight: 3,
}}
>
{openPutaway ? (
<Box
component="form"
onSubmit={formProps.handleSubmit(onSubmitPutaway)}
>
<PutAwayForm
itemDetail={itemDetail}
warehouse={warehouse!}
disabled={viewOnly}
setRowModesModel={setPafRowModesModel}
setRowSelectionModel={setPafRowSelectionModel}
/>
<Stack direction="row" justifyContent="flex-end" gap={1}>
<Autocomplete
disableClearable
options={printerCombo}
defaultValue={selectedPrinter}
onChange={(event, value) => {
setSelectedPrinter(value)
}}
renderInput={(params) => (
<TextField
{...params}
variant="outlined"
label={t("Printer")}
sx={{ width: 300}}
/>
)}
/>
<Button
id="printButton"
type="button"
variant="contained"
color="primary"
sx={{ mt: 1 }}
onClick={handlePrint}
disabled={isPrinting || printerCombo.length <= 0 || pafSubmitDisable}
>
{isPrinting ? t("Printing") : t("print")}
</Button>
<Button
id="putawaySubmit"
type="submit"
variant="contained"
color="primary"
sx={{ mt: 1 }}
onClick={formProps.handleSubmit(onSubmitPutaway)}
disabled={pafSubmitDisable}
>
{t("confirm putaway")}
</Button>
</Stack>
</Box>
) : (
<>
<Grid
container
justifyContent="flex-start"
alignItems="flex-start"
>
<Grid item xs={12}>
<Typography variant="h6" display="block" marginBlockEnd={1}>
{t("qc processing")}
</Typography>
</Grid>
<Grid item xs={12}>
<StockInForm itemDetail={itemDetail} disabled={viewOnly} />
</Grid>
</Grid>
{/* <Stack direction="row" justifyContent="flex-end" gap={1}>
<Button
id="stockInSubmit"
type="button"
variant="contained"
color="primary"
onClick={formProps.handleSubmit(onSubmitStockIn)}
>
{t("submitStockIn")}
</Button>
</Stack> */}
<Grid
container
justifyContent="flex-start"
alignItems="flex-start"
>
<QcComponent
// qc={qc!}
itemDetail={itemDetail}
disabled={viewOnly}
// qcItems={qcItems}
// setQcItems={setQcItems}
/>
</Grid>
<Stack direction="row" justifyContent="flex-end" gap={1}>
{!viewOnly && (<Button
id="qcSubmit"
type="button"
variant="contained"
color="primary"
sx={{ mt: 1 }}
onClick={formProps.handleSubmit(onSubmitQc, onSubmitErrorQc)}
>
{t("confirm qc result")}
</Button>)}
</Stack>
</>
)}
</Box>
</Modal>
</FormProvider>
</>
);
};
export default PoQcStockInModalVer2;

+ 156
- 77
src/components/PoDetail/StockInForm.tsx 查看文件

@@ -44,6 +44,7 @@ interface Props {
itemDetail: StockInLine;
// qc: QcItemWithChecks[];
disabled: boolean;
putawayMode?: boolean;
}
type EntryError =
| {
@@ -57,6 +58,7 @@ const StockInForm: React.FC<Props> = ({
// qc,
itemDetail,
disabled,
putawayMode = false,
}) => {
const {
t,
@@ -75,36 +77,51 @@ const StockInForm: React.FC<Props> = ({
setError,
clearErrors,
} = useFormContext<StockInInput>();
console.log(itemDetail);
// console.log(itemDetail);
useEffect(() => {
console.log("triggered");
// receiptDate default tdy
setValue("receiptDate", dayjs().add(0, "month").format(INPUT_DATE_FORMAT));
setValue("status", "received");
// console.log("triggered");
// // receiptDate default tdy
// setValue("receiptDate", dayjs().add(0, "month").format(INPUT_DATE_FORMAT));
// setValue("status", "received");
}, [setValue]);

useEffect(() => {
console.log(errors);
}, [errors]);
const productionDate = watch("productionDate");
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(productionDate);
console.log(expiryDate);
// console.log(uom);
// console.log(productionDate);
// console.log(expiryDate);
if (expiryDate) clearErrors();
if (productionDate) clearErrors();
}, [productionDate, expiryDate, clearErrors]);

useEffect(() => {
console.log("%c StockInForm itemDetail update: ", "color: brown", itemDetail);
}, [itemDetail]);

return (
<Grid container justifyContent="flex-start" alignItems="flex-start">
<Grid item xs={12}>
{/* <Grid item xs={12}>
<Typography variant="h6" display="block" marginBlockEnd={1}>
{t("Stock In Detail")}
</Typography>
</Grid>
</Grid> */}
<Grid
container
justifyContent="flex-start"
@@ -126,89 +143,91 @@ const StockInForm: React.FC<Props> = ({
</Grid>
<Grid item xs={6}>
<TextField
label={t("invoiceNo")}
label={t("itemName")}
fullWidth
{...register("invoiceNo", {
{...register("itemName", {
// required: "productLotNo required!",
})}
disabled={disabled}
disabled={true}
// error={Boolean(errors.productLotNo)}
// helperText={errors.productLotNo?.message}
/>
</Grid>
<Grid item xs={4}>
<Grid item xs={6}>
<TextField
label={t("productLotNo")}
label={t("PO No.")}
fullWidth
{...register("productLotNo", {
{...register("poCode", {
// required: "productLotNo required!",
})}
disabled={disabled}
error={Boolean(errors.productLotNo)}
helperText={errors.productLotNo?.message}
/>
</Grid>
<Grid item xs={4}>
<Controller
control={control}
name="receiptDate"
rules={{ required: true }}
render={({ field }) => {
return (
<LocalizationProvider
dateAdapter={AdapterDayjs}
adapterLocale={`${language}-hk`}
>
<DatePicker
{...field}
sx={{ width: "100%" }}
label={t("receiptDate")}
value={dayjs(watch("receiptDate"))}
disabled={disabled}
onChange={(date) => {
if (!date) return;
// setValue("receiptDate", date.format(INPUT_DATE_FORMAT));
field.onChange(date);
}}
inputRef={field.ref}
slotProps={{
textField: {
// required: true,
error: Boolean(errors.receiptDate?.message),
helperText: errors.receiptDate?.message,
},
}}
/>
</LocalizationProvider>
);
}}
disabled={true}
// error={Boolean(errors.productLotNo)}
// helperText={errors.productLotNo?.message}
/>
</Grid>
<Grid item xs={4}>
{putawayMode || (
<>
<Grid item xs={6}>
<Controller
control={control}
name="receiptDate"
rules={{ required: true }}
render={({ field }) => {
return (
<LocalizationProvider
dateAdapter={AdapterDayjs}
adapterLocale={`${language}-hk`}
>
<DatePicker
{...field}
sx={{ width: "100%" }}
label={t("receiptDate")}
value={dayjs(watch("receiptDate"))}
disabled={true}
onChange={(date) => {
if (!date) return;
// setValue("receiptDate", date.format(INPUT_DATE_FORMAT));
field.onChange(date);
}}
inputRef={field.ref}
slotProps={{
textField: {
// required: true,
error: Boolean(errors.receiptDate?.message),
helperText: errors.receiptDate?.message,
},
}}
/>
</LocalizationProvider>
);
}}
/>
</Grid>
<Grid item xs={6}>
<TextField
label={t("Supplier")}
fullWidth
{...register("supplier", {
// required: "productLotNo required!",
})}
disabled={true}
/>
</Grid>
</>
)}
<Grid item xs={6}>
<TextField
label={t("acceptedQty")}
label={t("productLotNo")}
fullWidth
{...register("acceptedQty", {
required: "acceptedQty required!",
{...register("productLotNo", {
// required: "productLotNo required!",
})}
disabled={disabled}
error={Boolean(errors.acceptedQty)}
helperText={errors.acceptedQty?.message}
error={Boolean(errors.productLotNo)}
helperText={errors.productLotNo?.message}
/>
</Grid>
{/* <Grid item xs={4}>
<TextField
label={t("acceptedWeight")}
fullWidth
// {...register("acceptedWeight", {
// required: "acceptedWeight required!",
// })}
disabled={disabled}
error={Boolean(errors.acceptedWeight)}
helperText={errors.acceptedWeight?.message}
/>
</Grid> */}
<Grid item xs={4}>
<Grid item xs={6}>
<Controller
control={control}
name="productionDate"
@@ -247,7 +266,19 @@ const StockInForm: React.FC<Props> = ({
}}
/>
</Grid>
<Grid item xs={4}>
{putawayMode || (
<Grid item xs={6}>
<TextField
label={t("qty")}
fullWidth
{...register("qty", {
required: "qty required!",
})}
disabled={true}
/>
</Grid>
)}
<Grid item xs={6}>
<Controller
control={control}
name="expiryDate"
@@ -265,7 +296,6 @@ const StockInForm: React.FC<Props> = ({
value={expiryDate ? dayjs(expiryDate) : undefined}
disabled={disabled}
onChange={(date) => {
console.log(date);
if (!date) return;
console.log(date.format(INPUT_DATE_FORMAT));
setValue("expiryDate", date.format(INPUT_DATE_FORMAT));
@@ -285,6 +315,55 @@ const StockInForm: React.FC<Props> = ({
}}
/>
</Grid>
{putawayMode || (
<Grid item xs={6}>
<TextField
label={t("receivedQty")}
fullWidth
{...register("receivedQty", {
required: "receivedQty required!",
})}
disabled={true}
/>
</Grid>
)}
<Grid item xs={6}>
<TextField
label={t("uom")}
fullWidth
{...register("uom.code", {
required: "uom required!",
})}
// value={uom?.code}
disabled={true}
/>
</Grid>
<Grid item xs={6}>
<TextField
label={t("acceptedQty")}
fullWidth
disabled={true}
{...register("acceptedQty", {
required: "acceptedQty required!",
})}
// disabled={true}
// disabled={disabled}
// error={Boolean(errors.acceptedQty)}
// helperText={errors.acceptedQty?.message}
/>
</Grid>
{/* <Grid item xs={4}>
<TextField
label={t("acceptedWeight")}
fullWidth
// {...register("acceptedWeight", {
// required: "acceptedWeight required!",
// })}
disabled={disabled}
error={Boolean(errors.acceptedWeight)}
helperText={errors.acceptedWeight?.message}
/>
</Grid> */}
</Grid>
<Grid
container


+ 309
- 0
src/components/PoDetail/StockInFormOld.tsx 查看文件

@@ -0,0 +1,309 @@
"use client";

import {
PurchaseQcResult,
PurchaseQCInput,
StockInInput,
} from "@/app/api/po/actions";
import {
Box,
Card,
CardContent,
Grid,
Stack,
TextField,
Tooltip,
Typography,
} from "@mui/material";
import { Controller, useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next";
import StyledDataGrid from "../StyledDataGrid";
import { useCallback, useEffect, useMemo } from "react";
import {
GridColDef,
GridRowIdGetter,
GridRowModel,
useGridApiContext,
GridRenderCellParams,
GridRenderEditCellParams,
useGridApiRef,
} from "@mui/x-data-grid";
import InputDataGrid from "../InputDataGrid";
import { TableRow } from "../InputDataGrid/InputDataGrid";
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 { DatePicker, LocalizationProvider } from "@mui/x-date-pickers";
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
import { INPUT_DATE_FORMAT } from "@/app/utils/formatUtil";
import dayjs from "dayjs";
// change PurchaseQcResult to stock in entry props
interface Props {
itemDetail: StockInLine;
// qc: QcItemWithChecks[];
disabled: boolean;
}
type EntryError =
| {
[field in keyof StockInInput]?: string;
}
| undefined;

// type PoQcRow = TableRow<Partial<PurchaseQcResult>, EntryError>;

const StockInFormOld: React.FC<Props> = ({
// qc,
itemDetail,
disabled,
}) => {
const {
t,
i18n: { language },
} = useTranslation("purchaseOrder");
const apiRef = useGridApiRef();
const {
register,
formState: { errors, defaultValues, touchedFields },
watch,
control,
setValue,
getValues,
reset,
resetField,
setError,
clearErrors,
} = useFormContext<StockInInput>();
console.log(itemDetail);

useEffect(() => {
console.log("triggered");
// receiptDate default tdy
setValue("receiptDate", dayjs().add(0, "month").format(INPUT_DATE_FORMAT));
setValue("status", "received");
}, [setValue]);

useEffect(() => {
console.log(errors);
}, [errors]);

const productionDate = watch("productionDate");
const expiryDate = watch("expiryDate");

useEffect(() => {
console.log(productionDate);
console.log(expiryDate);
if (expiryDate) clearErrors();
if (productionDate) clearErrors();
}, [productionDate, expiryDate, clearErrors]);

return (
<Grid container justifyContent="flex-start" alignItems="flex-start">
<Grid item xs={12}>
<Typography variant="h6" display="block" marginBlockEnd={1}>
{t("Stock In Detail")}
</Typography>
</Grid>
<Grid
container
justifyContent="flex-start"
alignItems="flex-start"
spacing={2}
sx={{ mt: 0.5 }}
>
<Grid item xs={6}>
<TextField
label={t("dnNo")}
fullWidth
{...register("dnNo", {
// required: "productLotNo required!",
})}
disabled={disabled}
// error={Boolean(errors.productLotNo)}
// helperText={errors.productLotNo?.message}
/>
</Grid>
<Grid item xs={6}>
<TextField
label={t("invoiceNo")}
fullWidth
{...register("invoiceNo", {
// required: "productLotNo required!",
})}
disabled={disabled}
// error={Boolean(errors.productLotNo)}
// helperText={errors.productLotNo?.message}
/>
</Grid>
<Grid item xs={4}>
<TextField
label={t("productLotNo")}
fullWidth
{...register("productLotNo", {
// required: "productLotNo required!",
})}
disabled={disabled}
error={Boolean(errors.productLotNo)}
helperText={errors.productLotNo?.message}
/>
</Grid>
<Grid item xs={4}>
<Controller
control={control}
name="receiptDate"
rules={{ required: true }}
render={({ field }) => {
return (
<LocalizationProvider
dateAdapter={AdapterDayjs}
adapterLocale={`${language}-hk`}
>
<DatePicker
{...field}
sx={{ width: "100%" }}
label={t("receiptDate")}
value={dayjs(watch("receiptDate"))}
disabled={disabled}
onChange={(date) => {
if (!date) return;
// setValue("receiptDate", date.format(INPUT_DATE_FORMAT));
field.onChange(date);
}}
inputRef={field.ref}
slotProps={{
textField: {
// required: true,
error: Boolean(errors.receiptDate?.message),
helperText: errors.receiptDate?.message,
},
}}
/>
</LocalizationProvider>
);
}}
/>
</Grid>
<Grid item xs={4}>
<TextField
label={t("acceptedQty")}
fullWidth
{...register("acceptedQty", {
required: "acceptedQty required!",
})}
disabled={disabled}
error={Boolean(errors.acceptedQty)}
helperText={errors.acceptedQty?.message}
/>
</Grid>
{/* <Grid item xs={4}>
<TextField
label={t("acceptedWeight")}
fullWidth
// {...register("acceptedWeight", {
// required: "acceptedWeight required!",
// })}
disabled={disabled}
error={Boolean(errors.acceptedWeight)}
helperText={errors.acceptedWeight?.message}
/>
</Grid> */}
<Grid item xs={4}>
<Controller
control={control}
name="productionDate"
// rules={{ required: !Boolean(expiryDate) }}
render={({ field }) => {
return (
<LocalizationProvider
dateAdapter={AdapterDayjs}
adapterLocale={`${language}-hk`}
>
<DatePicker
{...field}
sx={{ width: "100%" }}
label={t("productionDate")}
value={productionDate ? dayjs(productionDate) : undefined}
disabled={disabled}
onChange={(date) => {
if (!date) return;
setValue(
"productionDate",
date.format(INPUT_DATE_FORMAT),
);
// field.onChange(date);
}}
inputRef={field.ref}
slotProps={{
textField: {
// required: true,
error: Boolean(errors.productionDate?.message),
helperText: errors.productionDate?.message,
},
}}
/>
</LocalizationProvider>
);
}}
/>
</Grid>
<Grid item xs={4}>
<Controller
control={control}
name="expiryDate"
// rules={{ required: !Boolean(productionDate) }}
render={({ field }) => {
return (
<LocalizationProvider
dateAdapter={AdapterDayjs}
adapterLocale={`${language}-hk`}
>
<DatePicker
{...field}
sx={{ width: "100%" }}
label={t("expiryDate")}
value={expiryDate ? dayjs(expiryDate) : undefined}
disabled={disabled}
onChange={(date) => {
console.log(date);
if (!date) return;
console.log(date.format(INPUT_DATE_FORMAT));
setValue("expiryDate", date.format(INPUT_DATE_FORMAT));
// field.onChange(date);
}}
inputRef={field.ref}
slotProps={{
textField: {
// required: true,
error: Boolean(errors.expiryDate?.message),
helperText: errors.expiryDate?.message,
},
}}
/>
</LocalizationProvider>
);
}}
/>
</Grid>
</Grid>
<Grid
container
justifyContent="flex-start"
alignItems="flex-start"
spacing={2}
sx={{ mt: 0.5 }}
>
{/* <Grid item xs={12}>
<InputDataGrid<PurchaseQCInput, PurchaseQcResult, EntryError>
apiRef={apiRef}
checkboxSelection={false}
_formKey={"qcCheck"}
columns={columns}
validateRow={validationTest}
/>
</Grid> */}
</Grid>
</Grid>
);
};
export default StockInFormOld;

正在加载...
取消
保存