From 4c4571d52ba1f061200d00cc70e4e9a56f6ec789 Mon Sep 17 00:00:00 2001 From: kelvinsuen Date: Tue, 9 Sep 2025 19:05:02 +0800 Subject: [PATCH] update putaway --- src/app/utils/formatUtil.ts | 5 +- src/components/PoDetail/PutAwayForm.tsx | 26 +++++- src/components/PoDetail/QcStockInModal.tsx | 1 - src/components/PoSearch/PoSearch.tsx | 4 +- src/components/PutAwayScan/PutAwayModal.tsx | 36 +++++-- .../PutAwayScan/PutAwayReviewGrid.tsx | 93 +++++++++++++++++++ src/components/PutAwayScan/PutAwayScan.tsx | 62 ++++++++----- src/components/PutAwayScan/index.ts | 13 ++- src/i18n/zh/purchaseOrder.json | 5 +- src/i18n/zh/putAway.json | 9 +- 10 files changed, 209 insertions(+), 45 deletions(-) create mode 100644 src/components/PutAwayScan/PutAwayReviewGrid.tsx diff --git a/src/app/utils/formatUtil.ts b/src/app/utils/formatUtil.ts index 4aada22..ca728de 100644 --- a/src/app/utils/formatUtil.ts +++ b/src/app/utils/formatUtil.ts @@ -49,7 +49,10 @@ export const arrayToDayjs = (arr: ConfigType | (number | undefined)[], showTime: tempArr = `${arr[0]?.toString().padStart(4, "0")}-${arr[1]?.toString().padStart(2, "0")}-${arr[2]?.toString().padStart(2, "0")}`; if (showTime) { // [year, month, day, hour, minute, second] - tempArr += ` ${arr[3]?.toString().padStart(2, "0")}:${arr[4]?.toString().padStart(2, "0")}:${arr[5]?.toString().padStart(2, "0")}`; + tempArr += ` ${ + arr[3]?.toString().padStart(2, "0")}:${ + arr[4]?.toString().padStart(2, "0")}:${ + (arr[5] ?? 0)?.toString().padStart(2, "0")}`; } } diff --git a/src/components/PoDetail/PutAwayForm.tsx b/src/components/PoDetail/PutAwayForm.tsx index be9510b..e02e064 100644 --- a/src/components/PoDetail/PutAwayForm.tsx +++ b/src/components/PoDetail/PutAwayForm.tsx @@ -39,6 +39,7 @@ import { GridEditInputCell } from "@mui/x-data-grid"; import { StockInLine } from "@/app/api/po"; import { WarehouseResult } from "@/app/api/warehouse"; import { + arrayToDateTimeString, OUTPUT_DATE_FORMAT, stockInLineStatusMap, } from "@/app/utils/formatUtil"; @@ -231,19 +232,33 @@ const PutAwayForm: React.FC = ({ itemDetail, warehouse, disabled, setRowM return `${params.api.getRowIndexRelativeToVisibleRows(params.id) + 1}.` }, }, + { + field: "putawayDate", + headerName: t("putawayDatetime"), + flex: 1, + editable: false, + renderCell(params) { + return `${(arrayToDateTimeString(params.value))}`; + }, + }, + { + field: "putawayUser", + headerName: t("putawayUser"), + flex: 1, + editable: false, + }, { field: "qty", - headerName: t("qty"), + headerName: t("putawayQty"), flex: 0.5, editable: false, - // renderCell(params) { - // return <>100 - // }, + headerAlign: "right", + align: "right", }, { field: "warehouse", headerName: t("warehouse"), - flex: 1, + flex: 2, editable: false, renderEditCell: (params) => { const index = params.api.getRowIndexRelativeToVisibleRows(params.row.id) @@ -275,6 +290,7 @@ const PutAwayForm: React.FC = ({ itemDetail, warehouse, disabled, setRowM // return <>{filteredWarehouse[0].name} // }, }, + // { // field: "printQty", // headerName: t("printQty"), diff --git a/src/components/PoDetail/QcStockInModal.tsx b/src/components/PoDetail/QcStockInModal.tsx index 4232f5d..74b4a43 100644 --- a/src/components/PoDetail/QcStockInModal.tsx +++ b/src/components/PoDetail/QcStockInModal.tsx @@ -163,7 +163,6 @@ const [qcItems, setQcItems] = useState(dummyQCData) setViewOnly(isViewOnly) } console.log("Modal ItemDetail updated:", itemDetail); - console.log("%c SHOW PUTAWAY? ", "color:lime", showPutaway); if (showPutaway) { setTabIndex(1); } else { setTabIndex(0); } }, [itemDetail]); diff --git a/src/components/PoSearch/PoSearch.tsx b/src/components/PoSearch/PoSearch.tsx index 6308731..7c64f1c 100644 --- a/src/components/PoSearch/PoSearch.tsx +++ b/src/components/PoSearch/PoSearch.tsx @@ -48,7 +48,7 @@ const PoSearch: React.FC = ({ const searchCriteria: Criterion[] = useMemo(() => { const searchCriteria: Criterion[] = [ { label: t("Supplier"), paramName: "supplier", type: "text" }, - { label: t("Po No."), paramName: "code", type: "text" }, + { label: t("PO No."), paramName: "code", type: "text" }, { label: t("Escalated"), paramName: "escalated", @@ -141,7 +141,7 @@ const PoSearch: React.FC = ({ }, { name: "code", - label: `${t("Po No.")} &\n${t("Supplier")}`, + label: `${t("PO No.")} &\n${t("Supplier")}`, renderCell: (params) => { return <>{params.code}
{params.supplier} }, diff --git a/src/components/PutAwayScan/PutAwayModal.tsx b/src/components/PutAwayScan/PutAwayModal.tsx index 8aae74b..978d5f0 100644 --- a/src/components/PutAwayScan/PutAwayModal.tsx +++ b/src/components/PutAwayScan/PutAwayModal.tsx @@ -35,6 +35,7 @@ import StockInForm from "../PoDetail/StockInForm"; import { arrayToDateString, INPUT_DATE_FORMAT } from "@/app/utils/formatUtil"; import { QrCodeScanner } from "../QrCodeScannerProvider/QrCodeScannerProvider"; import { msg } from "../Swal/CustomAlerts"; +import { PutAwayRecord } from "."; interface Props extends Omit { @@ -42,6 +43,7 @@ interface Props extends Omit { stockInLineId: number; warehouseId: number; scanner: QrCodeScanner; + addPutAwayHistory: (putAwayData: PutAwayRecord) => void; } const style = { position: "absolute", @@ -69,7 +71,7 @@ const scannerStyle = { width: { xs: "60%", sm: "60%", md: "60%" }, }; -const PutAwayModal: React.FC = ({ open, onClose, warehouse, stockInLineId, warehouseId, scanner }) => { +const PutAwayModal: React.FC = ({ open, onClose, warehouse, stockInLineId, warehouseId, scanner, addPutAwayHistory }) => { const { t } = useTranslation("putAway"); const [serverError, setServerError] = useState(""); const params = useSearchParams(); @@ -77,7 +79,7 @@ const PutAwayModal: React.FC = ({ open, onClose, warehouse, stockInLineId const [isOpenScanner, setIsOpenScanner] = useState(false); const [itemDetail, setItemDetail] = useState(); - const [putAwayQty, setPutAwayQty] = useState(0); + const [totalPutAwayQty, setTotalPutAwayQty] = useState(0); const [unavailableText, setUnavailableText] = useState( undefined, ); @@ -124,7 +126,7 @@ const PutAwayModal: React.FC = ({ open, onClose, warehouse, stockInLineId (...args) => { setVerified(false); setItemDetail(undefined); - setPutAwayQty(0); + setTotalPutAwayQty(0); onClose?.(...args); // reset(); }, @@ -177,7 +179,8 @@ const PutAwayModal: React.FC = ({ open, onClose, warehouse, stockInLineId formProps.reset({ ...defaultNewValue }) - setPutQty(itemDetail?.demandQty); + const total = itemDetail.putAwayLines?.reduce((sum, p) => sum + p.qty, 0) ?? 0; + setPutQty(itemDetail?.demandQty - total); console.log("%c Loaded data:", "color:lime", defaultNewValue); } else { switch (itemDetail.status) { @@ -201,8 +204,8 @@ const PutAwayModal: React.FC = ({ open, onClose, warehouse, stockInLineId const res = await fetchStockInLineInfo(stockInLineId); console.log("%c Fetched Stock In Line Info:", "color:gold", res); - const totalPutAwayQty = res.putAwayLines?.reduce((sum, p) => sum + p.qty, 0) ?? 0; - setPutAwayQty(totalPutAwayQty); + const total = res.putAwayLines?.reduce((sum, p) => sum + p.qty, 0) ?? 0; + setTotalPutAwayQty(total); setItemDetail(res); } catch (e) { console.log("%c Error when fetching Stock In Line: ", "color:red", e); @@ -224,9 +227,9 @@ const PutAwayModal: React.FC = ({ open, onClose, warehouse, stockInLineId if (!Number.isInteger(qty)) { setQtyError(t("value must be integer")); } - if (qty > itemDetail?.acceptedQty!! - putAwayQty) { + if (qty > itemDetail?.demandQty!! - totalPutAwayQty) { setQtyError(`${t("putQty must not greater than")} ${ - itemDetail?.acceptedQty!! - putAwayQty}` ); + itemDetail?.demandQty!! - totalPutAwayQty}` ); } else // if (qty > itemDetail?.acceptedQty!!) { // setQtyError(`${t("putQty must not greater than")} ${ @@ -290,11 +293,24 @@ const PutAwayModal: React.FC = ({ open, onClose, warehouse, stockInLineId // update entries console.log("%c Update Success:", "color:green", res); // add loading + + const putAwayData = { + itemName: itemDetail?.itemName, + itemCode: itemDetail?.itemNo, + poCode: itemDetail?.poCode, + lotNo: itemDetail?.lotNo, + warehouse: warehouse.find((w) => w.id == warehouseId)?.name, + putQty: putQty, + uom: itemDetail?.uom?.udfudesc, + } as PutAwayRecord; + + addPutAwayHistory(putAwayData); + msg("貨品上架成功!"); closeHandler({}, "backdropClick"); } - console.log(res); + // console.log(res); // if (res) } catch (e) { // server error @@ -393,7 +409,7 @@ const PutAwayModal: React.FC = ({ open, onClose, warehouse, stockInLineId lineHeight: "1.2", }, }} - defaultValue={itemDetail?.acceptedQty!! - putAwayQty} + defaultValue={itemDetail?.demandQty!! - totalPutAwayQty} // defaultValue={itemDetail.demandQty} onChange={(e) => { const value = e.target.value; diff --git a/src/components/PutAwayScan/PutAwayReviewGrid.tsx b/src/components/PutAwayScan/PutAwayReviewGrid.tsx new file mode 100644 index 0000000..950d07a --- /dev/null +++ b/src/components/PutAwayScan/PutAwayReviewGrid.tsx @@ -0,0 +1,93 @@ +"use client"; + +import { + Paper, + } from "@mui/material"; +import { useEffect, useMemo } from "react"; +import StyledDataGrid from "../StyledDataGrid"; +import { useTranslation } from "react-i18next"; +import { GridColDef } from "@mui/x-data-grid"; +import { PutAwayRecord } from "."; + +type Props = { + putAwayHistory : PutAwayRecord[]; +}; + +const PutAwayReviewGrid: React.FC = ({ putAwayHistory }) => { + const { t } = useTranslation("putAway"); + + const columns: GridColDef[] = useMemo(() => [ + { + field: "index", + headerName: t(""), + flex: 0.5, + renderCell: (params) => { + return ({params.id}.); + }, + disableColumnMenu: true, + }, + { + field: "poCode", + headerName: t("poCode"), + flex: 2, + disableColumnMenu: true, + }, + { + field: "itemCode", + headerName: t("itemCode"), + flex: 1, + disableColumnMenu: true, + }, + { + field: "itemName", + headerName: t("itemName"), + flex: 2, + disableColumnMenu: true, + }, + { + field: "putQty", + headerName: t("putawayQty"), + flex: 1, + headerAlign: 'right', + align: 'right', + disableColumnMenu: true, + }, + { + field: "uom", + headerName: t("uom"), + flex: 1.5, + disableColumnMenu: true, + }, + { + field: "warehouse", + headerName: t("warehouse"), + flex: 2, + disableColumnMenu: true, + }, + ], [] + ) + + return (<> + + + + ) +} + +export default PutAwayReviewGrid; \ No newline at end of file diff --git a/src/components/PutAwayScan/PutAwayScan.tsx b/src/components/PutAwayScan/PutAwayScan.tsx index e2a8937..7c53514 100644 --- a/src/components/PutAwayScan/PutAwayScan.tsx +++ b/src/components/PutAwayScan/PutAwayScan.tsx @@ -29,6 +29,8 @@ import { import { useSearchParams } from "next/navigation"; import { useQrCodeScannerContext } from "../QrCodeScannerProvider/QrCodeScannerProvider"; import PutAwayModal from "./PutAwayModal"; +import { PutAwayRecord } from "."; +import PutAwayReviewGrid from "./PutAwayReviewGrid"; type Props = { warehouse : WarehouseResult[]; @@ -43,6 +45,7 @@ const PutAwayScan: React.FC = ({ warehouse }) => { const [openPutAwayModal, setOpenPutAwayModal] = useState(false); const [scannedSilId, setScannedSilId] = useState(0); // TODO use QR code info const [scannedWareHouseId, setScannedWareHouseId] = useState(0); // TODO use QR code info + const [putAwayHistory, setPutAwayHistory] = useState([]); // QR Code Scanner const scanner = useQrCodeScannerContext(); @@ -115,6 +118,12 @@ const PutAwayScan: React.FC = ({ warehouse }) => { }; } + const addPutAwayHistory = (putAwayData: PutAwayRecord) => { + console.log("%c Added new data to Putaway history: ", "color:orange", putAwayData); + const newPutaway = { ...putAwayData, id: putAwayHistory.length + 1 }; + setPutAwayHistory([...putAwayHistory, newPutaway]); // Create a new array with the new row + // putAwayHistory.push(putAwayData); + }; useEffect(() => { if (scannedSilId > 0) { @@ -170,29 +179,38 @@ const PutAwayScan: React.FC = ({ warehouse }) => { } }, [scanner.values]); - return (<> - - - {scanDisplay == "pending" ? t("Pending scan") : t("Rescan")} - - - - - + + + {scanDisplay == "pending" ? t("Pending scan") : t("Rescan")} + + + + + {putAwayHistory.length > 0 && (<> + + {t("putAwayHistory")} + + - ) + )} + + ) } export default PutAwayScan; \ No newline at end of file diff --git a/src/components/PutAwayScan/index.ts b/src/components/PutAwayScan/index.ts index 8d2e4fb..5112fe8 100644 --- a/src/components/PutAwayScan/index.ts +++ b/src/components/PutAwayScan/index.ts @@ -1 +1,12 @@ -export { default } from "./PutAwayScanWrapper" \ No newline at end of file +export { default } from "./PutAwayScanWrapper" + +export interface PutAwayRecord { + id: number; + itemName: string; + itemCode?: string; + warehouse: string; + putQty: number; + lotNo?: string; + poCode?: string; + uom?: string; +}; \ No newline at end of file diff --git a/src/i18n/zh/purchaseOrder.json b/src/i18n/zh/purchaseOrder.json index 0b70b33..0ea83e3 100644 --- a/src/i18n/zh/purchaseOrder.json +++ b/src/i18n/zh/purchaseOrder.json @@ -25,7 +25,6 @@ "Complete PO": "完成採購訂單", "General": "一般", "Bind Storage": "綁定倉位", - "Po No.": "採購訂單編號", "PO No.": "採購訂單編號", "itemNo": "貨品編號", "itemName": "貨品名稱", @@ -149,5 +148,7 @@ "value must be integer": "請輸入整數", "dn and qc info": "來貨及品檢詳情", "Qc Decision": "品檢詳情", - "Print Qty": "列印數量" + "Print Qty": "列印數量", + "putawayDatetime": "上架時間", + "putawayUser": "上架同事" } diff --git a/src/i18n/zh/putAway.json b/src/i18n/zh/putAway.json index 3cbb98b..90eb045 100644 --- a/src/i18n/zh/putAway.json +++ b/src/i18n/zh/putAway.json @@ -9,9 +9,16 @@ "Please scan warehouse qr code": "請掃瞄倉庫二維碼", "scan loading": "載入中,請稍候…", "warehouse": "倉庫", + "putawayQty": "上架數量", "putQty": "是次上架數量", "minimal value is 1": "最小為1", "putQty must not greater than": "上架數量不得大於", "value must be integer": "必須是整數", - "value must be a number": "必須是數字" + "value must be a number": "必須是數字", + "putAwayHistory": "是次上架記錄", + "itemName": "貨品名稱", + "lotNo": "貨品批號", + "poCode": "採購訂單編號", + "itemCode": "貨品編號", + "uom": "單位" }