diff --git a/src/app/(main)/ps/page.tsx b/src/app/(main)/ps/page.tsx index 6c72bc6..81bb48e 100644 --- a/src/app/(main)/ps/page.tsx +++ b/src/app/(main)/ps/page.tsx @@ -210,8 +210,8 @@ export default function ProductionSchedulePage() { } }; - const fromDateDefault = dayjs().subtract(29, "day").format("YYYY-MM-DD"); - const toDateDefault = dayjs().format("YYYY-MM-DD"); + const fromDateDefault = dayjs().subtract(6, "day").format("YYYY-MM-DD"); + const toDateDefault = dayjs().add(1, "day").format("YYYY-MM-DD"); const fetchItemDailyOut = async (force: boolean = false) => { // Avoid starting a new fetch while an import is in progress, diff --git a/src/components/PoDetail/PoDetail.tsx b/src/components/PoDetail/PoDetail.tsx index a7fdb26..7ef386e 100644 --- a/src/components/PoDetail/PoDetail.tsx +++ b/src/components/PoDetail/PoDetail.tsx @@ -416,14 +416,17 @@ const PoDetail: React.FC = ({ po, warehouse, printerCombo }) => { }, []); useEffect(() => { - if (processedQty === row.qty) { + // `processedQty` comes from putAwayLines (stock unit). + // After the fix, `row.qty` is qtyM18 (M18 unit), so compare using stockUom demand. + const targetStockQty = Number(row.stockUom?.stockQty ?? row.qty ?? 0); + if (targetStockQty > 0 && processedQty >= targetStockQty) { setCurrStatus("completed".toUpperCase()); } else if (processedQty > 0) { setCurrStatus("receiving".toUpperCase()); } else { setCurrStatus("pending".toUpperCase()); } - }, [processedQty, row.qty]); + }, [processedQty, row.qty, row.stockUom?.stockQty]); const handleRowSelect = () => { // setSelectedRowId(row.id); diff --git a/src/components/PoDetail/PoInputGrid.tsx b/src/components/PoDetail/PoInputGrid.tsx index bccde84..f2bae05 100644 --- a/src/components/PoDetail/PoInputGrid.tsx +++ b/src/components/PoDetail/PoInputGrid.tsx @@ -153,7 +153,8 @@ function PoInputGrid({ const [btnIsLoading, setBtnIsLoading] = useState(false); const [currQty, setCurrQty] = useState(() => { const total = entries.reduce( - (acc, curr) => acc + (curr.acceptedQty || 0), + // remaining qty (M18 unit) + (acc, curr) => acc + (curr.purchaseAcceptedQty || 0), 0, ); return total; @@ -231,7 +232,8 @@ function PoInputGrid({ itemName: params.row.itemName, // purchaseOrderId: params.row.purchaseOrderId, purchaseOrderLineId: params.row.purchaseOrderLineId, - acceptedQty: params.row.acceptedQty, + // For PO-origin, backend expects M18 qty and converts it to stock qty. + acceptedQty: params.row.purchaseAcceptedQty ?? params.row.acceptedQty, }; const res = await createStockInLine(postData); console.log(res); @@ -516,7 +518,7 @@ function PoInputGrid({ // // flex: 0.6, // }, { - field: "acceptedQty", + field: "purchaseAcceptedQty", headerName: t("acceptedQty"), // flex: 0.5, width: 125, @@ -524,7 +526,7 @@ function PoInputGrid({ // editable: true, // replace with tooltip + content renderCell: (params) => { - const qty = params.row.purchaseAcceptedQty ?? params.row.acceptedQty ?? 0; + const qty = params.row.purchaseAcceptedQty ?? 0; return integerFormatter.format(qty); } }, @@ -818,7 +820,8 @@ function PoInputGrid({ purchaseOrderLineId: itemDetail.id, itemNo: itemDetail.itemNo, itemName: itemDetail.itemName, - acceptedQty: itemDetail.qty - currQty, // this bug + // User inputs qty in M18 unit; backend will convert to stock unit on create. + purchaseAcceptedQty: itemDetail.qty - currQty, uom: itemDetail.uom, status: "draft", }; @@ -840,8 +843,13 @@ function PoInputGrid({ const error: StockInLineEntryError = {}; console.log(newRow); console.log(currQty); - if (newRow.acceptedQty && newRow.acceptedQty > itemDetail.qty) { - error["acceptedQty"] = t("qty cannot be greater than remaining qty"); + if ( + newRow.purchaseAcceptedQty && + newRow.purchaseAcceptedQty > itemDetail.qty + ) { + error["purchaseAcceptedQty"] = t( + "qty cannot be greater than remaining qty", + ); } return Object.keys(error).length > 0 ? error : undefined; }, @@ -872,7 +880,7 @@ function PoInputGrid({ setEntries(newEntries); //update remaining qty const total = newEntries.reduce( - (acc, curr) => acc + (curr.acceptedQty || 0), + (acc, curr) => acc + (curr.purchaseAcceptedQty || 0), 0, ); setCurrQty(total); diff --git a/src/components/PoDetail/PutAwayForm.tsx b/src/components/PoDetail/PutAwayForm.tsx index 732b9db..0aa74b4 100644 --- a/src/components/PoDetail/PutAwayForm.tsx +++ b/src/components/PoDetail/PutAwayForm.tsx @@ -395,7 +395,7 @@ const PutAwayForm: React.FC = ({ itemDetail, warehouse=[], disabled, sugg @@ -403,7 +403,7 @@ const PutAwayForm: React.FC = ({ itemDetail, warehouse=[], disabled, sugg diff --git a/src/components/PoSearch/PoSearch.tsx b/src/components/PoSearch/PoSearch.tsx index ca95ebe..770b306 100644 --- a/src/components/PoSearch/PoSearch.tsx +++ b/src/components/PoSearch/PoSearch.tsx @@ -13,7 +13,7 @@ import { WarehouseResult } from "@/app/api/warehouse"; import NotificationIcon from "@mui/icons-material/NotificationImportant"; import { useSession } from "next-auth/react"; import { defaultPagingController } from "../SearchResults/SearchResults"; -import { fetchPoListClient, testing } from "@/app/api/po/actions"; +import { testing } from "@/app/api/po/actions"; import dayjs from "dayjs"; import { arrayToDateString, dayjsToDateString } from "@/app/utils/formatUtil"; import arraySupport from "dayjs/plugin/arraySupport"; @@ -289,7 +289,20 @@ const PoSearch: React.FC = ({ }; setAutoSyncStatus(null); - const res = await fetchPoListClient(params); + const cleanedQuery: Record = {}; + Object.entries(params).forEach(([k, v]) => { + if (v === undefined || v === null) return; + if (typeof v === "string" && (v as string).trim() === "") return; + cleanedQuery[k] = String(v); + }); + const baseListResp = await clientAuthFetch( + `${NEXT_PUBLIC_API_URL}/po/list?${new URLSearchParams(cleanedQuery).toString()}`, + { method: "GET" }, + ); + if (!baseListResp.ok) { + throw new Error(`PO list fetch failed: ${baseListResp.status}`); + } + const res = await baseListResp.json(); if (!res) return; if (res.records && res.records.length > 0) { @@ -340,14 +353,6 @@ const PoSearch: React.FC = ({ if (syncOk) { setAutoSyncStatus("成功找到PO"); - // Re-fetch /po/list directly from client to avoid cached server action results. - const cleanedQuery: Record = {}; - Object.entries(params).forEach(([k, v]) => { - if (v === undefined || v === null) return; - if (typeof v === "string" && v.trim() === "") return; - cleanedQuery[k] = String(v); - }); - const listResp = await clientAuthFetch( `${NEXT_PUBLIC_API_URL}/po/list?${new URLSearchParams( cleanedQuery,