From bded62fa3a4877aa68587e137bb1d115a925d377 Mon Sep 17 00:00:00 2001 From: "CANCERYS\\kw093" Date: Mon, 4 May 2026 23:29:54 +0800 Subject: [PATCH] update jo matching --- .../Jodetail/JobPickExecutionsecondscan.tsx | 363 ++++++++++-------- 1 file changed, 204 insertions(+), 159 deletions(-) diff --git a/src/components/Jodetail/JobPickExecutionsecondscan.tsx b/src/components/Jodetail/JobPickExecutionsecondscan.tsx index 21163e1..167b23e 100644 --- a/src/components/Jodetail/JobPickExecutionsecondscan.tsx +++ b/src/components/Jodetail/JobPickExecutionsecondscan.tsx @@ -354,8 +354,34 @@ const JobPickExecution: React.FC = ({ filterArgs, onBack }) => { const [lastProcessedQr, setLastProcessedQr] = useState(''); const [isRefreshingData, setIsRefreshingData] = useState(false); const [currentPickOrderId, setCurrentPickOrderId] = useState(null); - + const getItemKey = useCallback((lot: any) => { + return `${lot.pickOrderId}-${lot.pickOrderLineId}-${lot.itemId}`; + }, []); // 添加:unassign 函数 + const itemMatchSummaryMap = useMemo(() => { + return combinedLotData.reduce< + Record + >((acc, lot) => { + const key = getItemKey(lot); + const matchStatus = String(lot.matchStatus || "").toLowerCase(); + const isCompleted = matchStatus === "completed"; + + // 來源優先:matchQty -> match_qty -> 0 + const qty = Number(lot.matchQty ?? lot.match_qty ?? 0); + + if (!acc[key]) { + acc[key] = { hasCompleted: false, completedMatchQty: 0 }; + } + + if (isCompleted) { + acc[key].hasCompleted = true; + // 同 item 若有 completed,取最大的 matchQty(避免被 0 覆蓋) + acc[key].completedMatchQty = Math.max(acc[key].completedMatchQty, qty); + } + + return acc; + }, {}); + }, [combinedLotData, getItemKey]); const handleUnassign = useCallback(async (pickOrderId: number | null) => { if (!pickOrderId || !currentUserId) { console.log("No pickOrderId or userId to unassign"); @@ -482,7 +508,7 @@ const JobPickExecution: React.FC = ({ filterArgs, onBack }) => { stockOutLineId: lot.stockOutLineId, stockOutLineStatus: lot.stockOutLineStatus, stockOutLineQty: lot.stockOutLineQty, - + matchQty: lot.matchQty ?? lot.match_qty ?? null, // Router info routerIndex: lot.routerIndex, matchStatus: lot.matchStatus, @@ -1015,7 +1041,14 @@ const JobPickExecution: React.FC = ({ filterArgs, onBack }) => { pageSize: newPageSize, }); }, []); - + const itemActualPickQtyMap = useMemo(() => { + return combinedLotData.reduce>((acc, lot) => { + const key = getItemKey(lot); // 一定要跟下方讀取同一套 + const qty = Number(lot.actualPickQty ?? 0); + acc[key] = (acc[key] ?? 0) + (Number.isFinite(qty) ? qty : 0); + return acc; + }, {}); + }, [combinedLotData, getItemKey]); const paginatedData = useMemo(() => { const sortedData = [...combinedLotData].sort((a, b) => { const aIndex = a.routerIndex || 0; @@ -1081,7 +1114,7 @@ const JobPickExecution: React.FC = ({ filterArgs, onBack }) => { return t("Please finish QR code scan and pick order."); } }, [t]); - + const [submitQtyFieldEnabledByLotKey, setSubmitQtyFieldEnabledByLotKey] =useState>({}); return ( = ({ filterArgs, onBack }) => { t("Confirm All") )} - {/* - - - {!isManualScanning ? ( - - ) : ( - - )} - - - - - - {qrScanError && !qrScanSuccess && ( - - {t("QR code does not match any item in current orders.")} - - )} - {qrScanSuccess && ( - - {t("QR code verified.")} - - )} - */} + @@ -1207,8 +1186,8 @@ const JobPickExecution: React.FC = ({ filterArgs, onBack }) => { {t("Item Code")} {t("Item Name")} {t("Lot No")} - {t("Lot Required Pick Qty")} - {/* {t("Scan Result")} */} + {t("Actual Pick Qty")} + {t("Scan Result")} {t("Submit Required Pick Qty")} @@ -1222,108 +1201,174 @@ const JobPickExecution: React.FC = ({ filterArgs, onBack }) => { ) : ( - paginatedData.map((lot, index) => ( - - - - {index + 1} - - - - - {lot.routerRoute || '-'} - - - {lot.handler || '-'} - {lot.itemCode} - {lot.itemName+'('+lot.uomDesc+')'} - - - - {lot.lotNo} - - - - - {(() => { - const requiredQty = lot.requiredQty || 0; - return requiredQty.toLocaleString()+'('+lot.uomShortDesc+')'; - })()} - + paginatedData.map((lot, index) => { + const itemKey = getItemKey(lot); + const itemSummary = itemMatchSummaryMap[itemKey] ?? { + hasCompleted: false, + completedMatchQty: 0, + }; + + const itemCompleted = itemSummary.hasCompleted; + const itemCompletedQty = itemSummary.completedMatchQty; + const isFirstRowOfItem = + index === 0 || getItemKey(paginatedData[index - 1]) !== itemKey; + const itemTotalActualPickQty = Number(itemActualPickQtyMap[itemKey] ?? 0); - - - - - - - - + {lot.lotNo} + + + + + + {`${itemTotalActualPickQty.toLocaleString()}(${lot.uomShortDesc || ""})`} + + + {(() => { + const matchStatus = String(lot.matchStatus || "").toLowerCase(); + const isRejected = lot.lotAvailability === "rejected"; + const isCompleted = matchStatus === "completed"; + + if (isRejected) { + return ; + } + if (isCompleted) { + return ; + } + return ; + })()} - - )) + + + {!isFirstRowOfItem ? ( + + - + + ) : ( + + handlePickQtyChange(itemKey, e.target.value)} + inputProps={{ min: 0, step: 1 }} + sx={{ + width: 96, + "& .MuiInputBase-input": { + fontSize: "0.75rem", + py: 0.5, + textAlign: "center", + }, + }} + /> + + + + + + )} + + + + ); + }) )}