From 475857092dcc92dd4293399b68fdd1d38e389944 Mon Sep 17 00:00:00 2001 From: "CANCERYS\\kw093" Date: Wed, 22 Jul 2026 21:45:46 +0800 Subject: [PATCH] =?UTF-8?q?WorkbenchGoodPickExecutionDetail=20UI=20?= =?UTF-8?q?=E5=A4=A7=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WorkbenchTicketReleaseTable 加 user filter DO release / FloorLanePanel:Truck X 票 + 搜尋依樓層 --- src/app/api/doworkbench/actions.ts | 10 +- .../DoWorkbench/WorkbenchFloorLanePanel.tsx | 42 ++++- .../WorkbenchGoodPickExecutionDetail.tsx | 163 ++++++++++++------ .../WorkbenchTicketReleaseTable.tsx | 87 +++++++++- .../ReleasedDoPickOrderSelectModal.tsx | 22 ++- .../WorkbenchPickExecution.tsx | 23 ++- src/i18n/en/ticketReleaseTable.json | 3 + src/i18n/zh/pickOrder.json | 33 ++-- src/i18n/zh/ticketReleaseTable.json | 3 + 9 files changed, 304 insertions(+), 82 deletions(-) diff --git a/src/app/api/doworkbench/actions.ts b/src/app/api/doworkbench/actions.ts index 6eb140e..ad5ce91 100644 --- a/src/app/api/doworkbench/actions.ts +++ b/src/app/api/doworkbench/actions.ts @@ -242,13 +242,16 @@ export async function fetchWorkbenchReleasedDoPickOrdersForSelection( shopName?: string, storeId?: string, truck?: string, - releaseType?: string + releaseType?: string, + /** Optional `2F`/`4F`: Truck X split by DO supplier preferred floor (storeId stays null). */ + floor?: string ): Promise { const params = new URLSearchParams(); if (shopName?.trim()) params.append("shopName", shopName.trim()); if (storeId?.trim()) params.append("storeId", storeId.trim()); if (truck?.trim()) params.append("truck", truck.trim()); if (releaseType?.trim()) params.append("releaseType", releaseType.trim()); + if (floor?.trim()) params.append("floor", floor.trim().replace("/", "").toUpperCase()); const query = params.toString(); const url = `${BASE_API_URL}/doPickOrder/workbench/released${query ? `?${query}` : ""}`; const response = await serverFetchJson(url, { method: "GET" }); @@ -261,7 +264,9 @@ export async function fetchWorkbenchReleasedDoPickOrdersForSelectionToday( storeId?: string, truck?: string, requiredDeliveryDate?: string, - releaseType?: string + releaseType?: string, + /** Optional `2F`/`4F`: Truck X split by DO supplier preferred floor (storeId stays null). */ + floor?: string ): Promise { const params = new URLSearchParams(); if (shopName?.trim()) params.append("shopName", shopName.trim()); @@ -269,6 +274,7 @@ export async function fetchWorkbenchReleasedDoPickOrdersForSelectionToday( if (truck?.trim()) params.append("truck", truck.trim()); if (requiredDeliveryDate?.trim()) params.append("requiredDate", requiredDeliveryDate.trim()); if (releaseType?.trim()) params.append("releaseType", releaseType.trim()); + if (floor?.trim()) params.append("floor", floor.trim().replace("/", "").toUpperCase()); const query = params.toString(); const url = `${BASE_API_URL}/doPickOrder/workbench/released-today${query ? `?${query}` : ""}`; const response = await serverFetchJson(url, { method: "GET" }); diff --git a/src/components/DoWorkbench/WorkbenchFloorLanePanel.tsx b/src/components/DoWorkbench/WorkbenchFloorLanePanel.tsx index 0c1e3f3..9d74845 100644 --- a/src/components/DoWorkbench/WorkbenchFloorLanePanel.tsx +++ b/src/components/DoWorkbench/WorkbenchFloorLanePanel.tsx @@ -85,7 +85,13 @@ const WorkbenchFloorLanePanel: React.FC = ({ const [modalReleaseTypeFilter, setModalReleaseTypeFilter] = useState(undefined); const [modalFilterRequiredDeliveryDate, setModalFilterRequiredDeliveryDate] = useState(undefined); const [modalInitialShopSearch, setModalInitialShopSearch] = useState(undefined); - const defaultTruckCount = summary4F?.defaultTruckCount ?? 0; + const [modalTruckXFloor, setModalTruckXFloor] = useState(undefined); + const ticketFloorApiKey = useMemo( + () => ticketFloor.replace("/", "").trim().toUpperCase(), + [ticketFloor], + ); + const defaultTruckCount = + (ticketFloor === "2/F" ? summary2F?.defaultTruckCount : summary4F?.defaultTruckCount) ?? 0; const etraEnterInFlightRef = useRef(false); const [etraMergeDialogOpen, setEtraMergeDialogOpen] = useState(false); @@ -108,16 +114,25 @@ const WorkbenchFloorLanePanel: React.FC = ({ shopName?: string, storeId?: string, truck?: string, - releaseType?: string - ) => fetchWorkbenchReleasedDoPickOrdersForSelection(shopName, storeId, truck, releaseType), + releaseType?: string, + floor?: string + ) => fetchWorkbenchReleasedDoPickOrdersForSelection(shopName, storeId, truck, releaseType, floor), loadToday: ( shopName?: string, storeId?: string, truck?: string, requiredDeliveryDate?: string, - releaseType?: string + releaseType?: string, + floor?: string ) => - fetchWorkbenchReleasedDoPickOrdersForSelectionToday(shopName, storeId, truck, requiredDeliveryDate, releaseType), + fetchWorkbenchReleasedDoPickOrdersForSelectionToday( + shopName, + storeId, + truck, + requiredDeliveryDate, + releaseType, + floor + ), assignByListItemId: assignByDeliveryOrderPickOrderId, }), [], @@ -234,7 +249,14 @@ const WorkbenchFloorLanePanel: React.FC = ({ pendingRef.current += 1; startFullTimer(); try { - const list = await fetchWorkbenchReleasedDoPickOrdersForSelection(undefined, undefined, "車線-X"); + // storeId stays undefined (Truck X); floor splits by DO supplier preferred floor. + const list = await fetchWorkbenchReleasedDoPickOrdersForSelection( + undefined, + undefined, + "車線-X", + undefined, + ticketFloorApiKey + ); setBeforeTodayTruckXCount(list.length); } catch { setBeforeTodayTruckXCount(0); @@ -244,12 +266,13 @@ const WorkbenchFloorLanePanel: React.FC = ({ } }; void loadBeforeTodayTruckX(); - }, [inEtraUi]); + }, [inEtraUi, ticketFloorApiKey]); const clearModalEtraContext = useCallback(() => { setModalReleaseTypeFilter(undefined); setModalFilterRequiredDeliveryDate(undefined); setModalInitialShopSearch(undefined); + setModalTruckXFloor(undefined); }, []); const openEnterEtraView = useCallback(async () => { @@ -538,6 +561,7 @@ const WorkbenchFloorLanePanel: React.FC = ({ setSelectedTruck("車線-X"); setIsDefaultTruck(true); setDefaultDateScope("today"); + setModalTruckXFloor(ticketFloorApiKey); setModalOpen(true); }} > @@ -638,10 +662,11 @@ const WorkbenchFloorLanePanel: React.FC = ({ variant="outlined" onClick={() => { clearModalEtraContext(); - setSelectedStore("4/F"); + setSelectedStore(""); setSelectedTruck("車線-X"); setIsDefaultTruck(true); setDefaultDateScope("before"); + setModalTruckXFloor(ticketFloorApiKey); setModalOpen(true); }} > @@ -746,6 +771,7 @@ const WorkbenchFloorLanePanel: React.FC = ({ releaseTypeFilter={modalReleaseTypeFilter} filterRequiredDeliveryDate={modalFilterRequiredDeliveryDate} initialShopSearch={modalInitialShopSearch} + truckXFloor={modalTruckXFloor} onClose={() => { setModalOpen(false); clearModalEtraContext(); diff --git a/src/components/DoWorkbench/WorkbenchGoodPickExecutionDetail.tsx b/src/components/DoWorkbench/WorkbenchGoodPickExecutionDetail.tsx index 84af6b6..4b6015d 100644 --- a/src/components/DoWorkbench/WorkbenchGoodPickExecutionDetail.tsx +++ b/src/components/DoWorkbench/WorkbenchGoodPickExecutionDetail.tsx @@ -1223,13 +1223,21 @@ const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdO }, [fetchAllCombinedLotData]); const openWorkbenchLotLabelModalForLot = useCallback( - (lot: any, reminderText?: string | null) => { + ( + lot: any, + reminderText?: string | null, + options?: { markAsScanIssue?: boolean }, + ) => { const itemId = Number(lot?.itemId); const stockInLineId = Number(lot?.stockInLineId); const solId = Number(lot?.stockOutLineId); if (!Number.isFinite(itemId) || itemId <= 0 || !Number.isFinite(solId) || solId <= 0) { return; } + // 掃碼 issue 才標記:手動「批號二維碼」不傳 markAsScanIssue,批號不變紅 + if (options?.markAsScanIssue) { + rememberWorkbenchScanReject(solId, reminderText); + } setWorkbenchLotLabelContextLot(lot); if (Number.isFinite(stockInLineId) && stockInLineId > 0) { setWorkbenchLotLabelInitialPayload({ itemId, stockInLineId }); @@ -1241,7 +1249,7 @@ const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdO setQrScanSuccess(false); setWorkbenchLotLabelModalOpen(true); }, - [], + [rememberWorkbenchScanReject], ); const shouldOpenWorkbenchLotLabelModalForFailure = useCallback( @@ -1826,6 +1834,7 @@ const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdO openWorkbenchLotLabelModalForLot( scannedLot, t("This lot is not available, please scan another lot."), + { markAsScanIssue: true }, ); return; } @@ -1841,6 +1850,7 @@ const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdO openWorkbenchLotLabelModalForLot( scannedLot, `Lot is expired (expiry=${scannedLot.expiryDate || "-"})`, + { markAsScanIssue: true }, ); return; } @@ -1963,7 +1973,9 @@ const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdO shouldOpenWorkbenchLotLabelModalForFailure(res.code, failMsg) && expectedLot ) { - openWorkbenchLotLabelModalForLot(expectedLot, failMsg); + openWorkbenchLotLabelModalForLot(expectedLot, failMsg, { + markAsScanIssue: true, + }); return; } if (workbenchMode && expectedLot.stockOutLineId != null) { @@ -2135,7 +2147,9 @@ const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdO shouldOpenWorkbenchLotLabelModalForFailure(res.code, failMsg) && expectedLot ) { - openWorkbenchLotLabelModalForLot(expectedLot, failMsg); + openWorkbenchLotLabelModalForLot(expectedLot, failMsg, { + markAsScanIssue: true, + }); return; } if (workbenchMode && expectedLot.stockOutLineId != null) { @@ -2330,7 +2344,9 @@ const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdO shouldOpenWorkbenchLotLabelModalForFailure(res.code, failMsg) && exactMatch ) { - openWorkbenchLotLabelModalForLot(exactMatch, failMsg); + openWorkbenchLotLabelModalForLot(exactMatch, failMsg, { + markAsScanIssue: true, + }); return; } if (workbenchMode && exactMatch.stockOutLineId != null) { @@ -2474,7 +2490,9 @@ const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdO shouldOpenWorkbenchLotLabelModalForFailure(res.code, failMsg) && expectedLot ) { - openWorkbenchLotLabelModalForLot(expectedLot, failMsg); + openWorkbenchLotLabelModalForLot(expectedLot, failMsg, { + markAsScanIssue: true, + }); return; } if (workbenchMode && expectedLot.stockOutLineId != null) { @@ -4154,10 +4172,21 @@ const handleSubmitAllScanned = useCallback(async () => { {t("Item Code")} {t("Item Name")} - {t("Route")} - {t("Suggest Lot No.")} - {t("Lot Required Pick Qty")} - {t("Scan Result")} + + {`${t("Route")} / ${t("Suggest Lot No.")}`} + + + {t("Lot Required Pick Qty")} + + + {t("Scan Result")} + {/*{t("Qty will submit")}*/} {t("Submit Required Pick Qty")} @@ -4165,7 +4194,7 @@ const handleSubmitAllScanned = useCallback(async () => { {paginatedData.length === 0 ? ( - + {t("No data available")} @@ -4226,36 +4255,49 @@ paginatedData.map((row, index) => { {row.isGroupFirst ? lot.itemName + '(' + lot.stockUnit + ')' : ""} - - - {lot.routerRoute || '-'} - - - - - + + + + + {lot.routerRoute || "-"} + {(() => { const hasLotNo = Boolean(lot.lotNo); const isExpired = lot.lotAvailability === 'expired'; const isUnavailable = isInventoryLotLineUnavailable(lot); const isNoLotHint = !hasLotNo && !rejectDisplay; // 顯示「請檢查周圍…」那種 - const isIssueText = - Boolean(rejectDisplay) || !hasLotNo || isExpired || isUnavailable; - const textColor = - isNoLotHint - ? 'error.main' // 或 'text.primary':固定黑,不受 handled / unavailable 影響 - : rejectDisplay || isSolRejected || isUnavailable - ? 'error.main' - : isExpired - ? 'warning.main' - : 'inherit'; + const solStatus = String(lot.stockOutLineStatus ?? "").toLowerCase(); + const isComplete = + solStatus === "completed" || + solStatus === "checked" || + solStatus === "partially_completed" || + solStatus === "partially_complete"; + const textColor = + isNoLotHint + ? "error.main" + : rejectDisplay || isSolRejected || isUnavailable + ? "error.main" + : isExpired + ? "warning.main" + : isComplete + ? "success.main" + : "inherit"; + const lotOnly = + hasLotNo && !rejectDisplay && !isExpired && !isUnavailable; return ( {hasLotNo ? ( @@ -4299,7 +4341,6 @@ paginatedData.map((row, index) => { sx={{ flexShrink: 0, fontSize: "0.75rem", - //py: 0.25, minWidth: "auto", px: 1, whiteSpace: "nowrap", @@ -4310,23 +4351,34 @@ paginatedData.map((row, index) => { )} - - {(() => { - const requiredQty = lot.requiredQty || 0; - return requiredQty.toLocaleString() + '(' + lot.uomShortDesc + ')'; - })()} + + + + {(() => { + const requiredQty = lot.requiredQty || 0; + return requiredQty.toLocaleString() + '(' + lot.uomShortDesc + ')'; + })()} + - + {(() => { const status = lot.stockOutLineStatus?.toLowerCase(); const isRejected = status === 'rejected' || lot.lotAvailability === 'rejected'; const isNoLot = !lot.lotNo; + const scanResultSlotSx = { + width: 42, + height: 42, + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + mx: 'auto', + } as const; // rejected lot:显示红色勾选(已扫描但被拒绝) if (isRejected && !isNoLot) { return ( - + { status !== "partially_completed" && status !== "partially_complete" ) { - return null; + return ; } // 正常 lot:已扫描(checked/partially_completed/completed) if (!isNoLot && status !== 'pending' && status !== 'rejected') { return ( - + { // noLot 且已完成/部分完成:显示红色勾选 if (isNoLot && (status === 'partially_completed' || status === 'completed')) { return ( - + { ); } - return null; + return ; })()} {/* @@ -4477,11 +4529,10 @@ paginatedData.map((row, index) => { {isRowPicked ? ( @@ -4512,7 +4563,16 @@ paginatedData.map((row, index) => { inputProps={{ min: 0, step: 1 }} sx={{ width: 96, - "& .MuiInputBase-input": { fontSize: "0.75rem", py: 0.5, textAlign: "center" }, + "& .MuiInputBase-input": { + typography: "body1", + py: 0.5, + textAlign: "center", + "&::-webkit-outer-spin-button, &::-webkit-inner-spin-button": { + WebkitAppearance: "none", + margin: 0, + }, + MozAppearance: "textfield", + }, }} /> )} @@ -4557,7 +4617,14 @@ paginatedData.map((row, index) => { } - sx={{ fontSize: '0.7rem', py: 0.5, minHeight: '28px', minWidth: '60px' }} + sx={{ + fontSize: "0.7rem", + py: 0.5, + minHeight: "28px", + minWidth: "72px", + whiteSpace: "nowrap", + flexShrink: 0, + }} > {t("Just Completed")} diff --git a/src/components/DoWorkbench/WorkbenchTicketReleaseTable.tsx b/src/components/DoWorkbench/WorkbenchTicketReleaseTable.tsx index 53c824a..60f554b 100644 --- a/src/components/DoWorkbench/WorkbenchTicketReleaseTable.tsx +++ b/src/components/DoWorkbench/WorkbenchTicketReleaseTable.tsx @@ -5,13 +5,16 @@ import { Box, Card, CardContent, + Checkbox, Chip, CircularProgress, FormControl, InputLabel, + ListItemText, MenuItem, Paper, Select, + SelectChangeEvent, Stack, Table, TableBody, @@ -65,6 +68,14 @@ function showDoPickOpsButtons(row: WorkbenchTicketReleaseTable): boolean { ); } +const HANDLER_UNASSIGNED = "__UNASSIGNED__"; +const HANDLER_SELECT_ALL = "__ALL__"; + +function handlerFilterKey(handlerName: string | null | undefined): string { + const name = handlerName?.trim() ?? ""; + return name || HANDLER_UNASSIGNED; +} + const WorkbenchTicketReleaseTableTab: React.FC = () => { const { t } = useTranslation("ticketReleaseTable"); const { data: session } = useSession() as { data: SessionWithTokens | null }; @@ -73,6 +84,7 @@ const WorkbenchTicketReleaseTableTab: React.FC = () => { const [queryDate, setQueryDate] = useState(() => dayjs()); const [selectedFloor, setSelectedFloor] = useState(""); const [selectedStatus, setSelectedStatus] = useState("released"); + const [selectedHandlers, setSelectedHandlers] = useState([]); const [data, setData] = useState([]); const [loading, setLoading] = useState(true); const [now, setNow] = useState(dayjs()); @@ -111,6 +123,28 @@ const WorkbenchTicketReleaseTableTab: React.FC = () => { }, []); const dayStr = queryDate.format("YYYY-MM-DD"); + + const handlerOptions = useMemo(() => { + const names = new Set(); + let hasUnassigned = false; + for (const item of data) { + if (selectedFloor && item.storeId !== selectedFloor) continue; + const name = item.handlerName?.trim(); + if (name) names.add(name); + else hasUnassigned = true; + } + const sorted = Array.from(names).sort((a, b) => a.localeCompare(b, "zh-Hant")); + return hasUnassigned ? [HANDLER_UNASSIGNED, ...sorted] : sorted; + }, [data, selectedFloor]); + + useEffect(() => { + setSelectedHandlers((prev) => { + if (prev.length === 0) return prev; + const next = prev.filter((name) => handlerOptions.includes(name)); + return next.length === prev.length ? prev : next; + }); + }, [handlerOptions]); + const filteredData = useMemo(() => { return data.filter((item) => { if (selectedFloor && item.storeId !== selectedFloor) return false; @@ -119,9 +153,27 @@ const WorkbenchTicketReleaseTableTab: React.FC = () => { if (itemDate !== dayStr) return false; } if (selectedStatus && item.ticketStatus?.toLowerCase() !== selectedStatus.toLowerCase()) return false; + if (selectedHandlers.length > 0 && !selectedHandlers.includes(handlerFilterKey(item.handlerName))) { + return false; + } return true; }); - }, [data, dayStr, selectedFloor, selectedStatus]); + }, [data, dayStr, selectedFloor, selectedStatus, selectedHandlers]); + + const allHandlersSelected = + handlerOptions.length > 0 && selectedHandlers.length === handlerOptions.length; + + const handleHandlersChange = (event: SelectChangeEvent) => { + const value = event.target.value; + const next = typeof value === "string" ? value.split(",") : value; + if (next.includes(HANDLER_SELECT_ALL)) { + setSelectedHandlers(allHandlersSelected ? [] : handlerOptions); + setPaginationController((prev) => ({ ...prev, pageNum: 0 })); + return; + } + setSelectedHandlers(next.filter((v) => v !== HANDLER_SELECT_ALL)); + setPaginationController((prev) => ({ ...prev, pageNum: 0 })); + }; const paginatedData = useMemo(() => { const startIndex = paginationController.pageNum * paginationController.pageSize; @@ -247,6 +299,39 @@ const WorkbenchTicketReleaseTableTab: React.FC = () => { {t("completed")} + + + {t("Handler Name")} + + + diff --git a/src/components/FinishedGoodSearch/ReleasedDoPickOrderSelectModal.tsx b/src/components/FinishedGoodSearch/ReleasedDoPickOrderSelectModal.tsx index 125ece2..53cad14 100644 --- a/src/components/FinishedGoodSearch/ReleasedDoPickOrderSelectModal.tsx +++ b/src/components/FinishedGoodSearch/ReleasedDoPickOrderSelectModal.tsx @@ -36,7 +36,9 @@ export type ReleasedDoPickListBridge = { shopName?: string, storeId?: string, truck?: string, - releaseType?: string + releaseType?: string, + /** Optional `2F`/`4F` for Truck X supplier-floor split */ + floor?: string ) => Promise; /** Optional 4th arg: workbench `requiredDeliveryDate` (YYYY-MM-DD) for default truck list; omit = calendar today. */ loadToday: ( @@ -44,7 +46,9 @@ export type ReleasedDoPickListBridge = { storeId?: string, truck?: string, requiredDeliveryDate?: string, - releaseType?: string + releaseType?: string, + /** Optional `2F`/`4F` for Truck X supplier-floor split */ + floor?: string ) => Promise; assignByListItemId: (userId: number, id: number) => Promise; }; @@ -70,6 +74,10 @@ interface Props { * requiredDate instead of historical released (delivery date before calendar today). */ filterRequiredDeliveryDate?: string; + /** + * Truck X only: `2F`/`4F` (or `2/F`/`4/F`) — filter by DO supplier preferred floor; storeId stays null. + */ + truckXFloor?: string; } const ReleasedDoPickOrderSelectModal: React.FC = ({ @@ -85,6 +93,7 @@ const ReleasedDoPickOrderSelectModal: React.FC = ({ releaseTypeFilter, initialShopSearch, filterRequiredDeliveryDate, + truckXFloor, }) => { const { t } = useTranslation("pickOrder"); const { data: session } = useSession() as { data: SessionWithTokens | null }; @@ -102,6 +111,7 @@ const ReleasedDoPickOrderSelectModal: React.FC = ({ const loadReleased = listBridge?.loadBeforeToday ?? fetchReleasedDoPickOrdersForSelection; const loadTodayFn = listBridge?.loadToday ?? fetchReleasedDoPickOrdersForSelectionToday; + const floorArg = truckXFloor?.trim() || undefined; if (isDefaultTruck) { if (defaultDateScopeProp === "today") { data = await loadTodayFn( @@ -109,14 +119,16 @@ const ReleasedDoPickOrderSelectModal: React.FC = ({ undefined, "車線-X", defaultTruckRequiredDeliveryDate?.trim() || undefined, - releaseTypeFilter?.trim() || undefined + releaseTypeFilter?.trim() || undefined, + floorArg ); } else { data = await loadReleased( undefined, undefined, "車線-X", - releaseTypeFilter?.trim() || undefined + releaseTypeFilter?.trim() || undefined, + floorArg ); } } else if (filterRequiredDeliveryDate?.trim() && listBridge?.loadToday) { @@ -143,7 +155,7 @@ const ReleasedDoPickOrderSelectModal: React.FC = ({ } finally { setLoading(false); } - }, [open, shopSearch, storeId, truck, isDefaultTruck, defaultDateScopeProp, listBridge, defaultTruckRequiredDeliveryDate, releaseTypeFilter, filterRequiredDeliveryDate]); + }, [open, shopSearch, storeId, truck, isDefaultTruck, defaultDateScopeProp, listBridge, defaultTruckRequiredDeliveryDate, releaseTypeFilter, filterRequiredDeliveryDate, truckXFloor]); useEffect(() => { loadList(); diff --git a/src/components/PickOrderSearch/WorkbenchPickExecution.tsx b/src/components/PickOrderSearch/WorkbenchPickExecution.tsx index 30d95d6..40843a7 100644 --- a/src/components/PickOrderSearch/WorkbenchPickExecution.tsx +++ b/src/components/PickOrderSearch/WorkbenchPickExecution.tsx @@ -1655,6 +1655,11 @@ const WorkbenchPickExecution: React.FC = ({ filterArgs }) => { const isRowExpired = isWorkbenchSourceLotExpired(r) && !isRowRejected; const isRowUnavailable = isInventoryLotLineUnavailable(r); + const isRowComplete = + rowStatus === "completed" || + rowStatus === "checked" || + rowStatus === "partially_completed" || + rowStatus === "partially_complete"; return ( @@ -1678,7 +1683,9 @@ const WorkbenchPickExecution: React.FC = ({ filterArgs }) => { ? "error.main" : isRowExpired || isLotAvailabilityExpired(r) ? "warning.main" - : "inherit", + : isRowComplete + ? "success.main" + : "inherit", }} > {r.lotNo ? ( @@ -1833,7 +1840,19 @@ const WorkbenchPickExecution: React.FC = ({ filterArgs }) => { return { ...prev, [r.stockOutLineId]: n }; }); }} - sx={{ width: 96 }} + sx={{ + width: 96, + "& .MuiInputBase-input": { + typography: "body1", + py: 0.5, + textAlign: "center", + "&::-webkit-outer-spin-button, &::-webkit-inner-spin-button": { + WebkitAppearance: "none", + margin: 0, + }, + MozAppearance: "textfield", + }, + }} disabled={!r.stockOutLineId || qtyEditableBySolId[r.stockOutLineId] !== true} inputProps={{ min: 0, step: 1 }} /> diff --git a/src/i18n/en/ticketReleaseTable.json b/src/i18n/en/ticketReleaseTable.json index 7af4ded..6fc5468 100644 --- a/src/i18n/en/ticketReleaseTable.json +++ b/src/i18n/en/ticketReleaseTable.json @@ -1,6 +1,7 @@ { "Actions": "Actions", "All Floors": "All Floors", + "All Handlers": "All Handlers", "All Statuses": "All Statuses", "Auto-refresh every 1 minute": "Auto-refresh every 1 minute", "Auto-refresh every 10 minutes": "Auto-refresh every 10 minutes", @@ -30,9 +31,11 @@ "Revert assignment": "Revert assignment", "Revert assignment hint": "Revert assignment hint", "Rows per page": "Rows per page", + "Select All": "Select All", "Select Date": "Select Date", "Shop Name": "Shop Name", "Status": "Status", + "Unassigned": "Unassigned", "Store ID": "Store ID", "Target Date": "Target Date", "Ticket Information": "Ticket Information", diff --git a/src/i18n/zh/pickOrder.json b/src/i18n/zh/pickOrder.json index 0a95cb6..d9465c5 100644 --- a/src/i18n/zh/pickOrder.json +++ b/src/i18n/zh/pickOrder.json @@ -12,7 +12,7 @@ "N/A": "不適用", "Release Pick Orders": "放單", "released": "已放單", - "is unavailable. Please check around have available QR code or not.": "不可用。請檢查周圍是否有可用的 QR 碼。", + "is unavailable. Please check around have available QR code or not.": "不可用。請檢查周圍是否有可用的二維碼。", "No lot rows. Select a pick order above.": "沒有批次行。請選擇一個提料單。", "Loading...": "載入中...", "Suggestion success": "建議成功", @@ -65,7 +65,7 @@ "items": "項目", "Select Pick Order:": "選擇提料單:", "No Stock Available": "沒有庫存", - "is expired. Please check around have available QR code or not.": "已過期。請檢查周圍是否有可用的 QR 碼。", + "is expired. Please check around have available QR code or not.": "已過期。請檢查周圍是否有可用的二維碼。", "Start Fail": "開始失敗", "Start PO": "開始採購訂單", "Do you want to complete?": "確定完成嗎?", @@ -136,7 +136,7 @@ "LotNo": "批號", "Po Code": "採購訂單編號", "No Warehouse": "沒有倉庫", - "Please scan warehouse qr code.": "請掃描倉庫 QR 碼。", + "Please scan warehouse qr code.": "請掃描倉庫二維碼。", "Reject": "拒絕", "submit": "確認提交", @@ -270,7 +270,7 @@ "Pick order completed successfully!": "提料單完成成功!", "Lot has been rejected and marked as unavailable.": "批號已拒絕並標記為不可用。", "This order is insufficient, please pick another lot.": "此訂單不足,請選擇其他批號。", - "Please finish QR code scan, QC check and pick order.": "請完成 QR 碼掃描、QC 檢查和提料。", + "Please finish QR code scan, QC check and pick order.": "請完成二維碼掃描、QC 檢查和提料。", "No data available": "沒有資料", "Please submit the pick order.": "請提交提料單。", "Item lot to be Pick:": "批次貨品提料:", @@ -293,13 +293,13 @@ "Original Available Qty": "原可用數", "Remaining Available Qty": "剩餘可用數", "Please submit pick order.": "請提交提料單。", - "Please finish QR code scan and pick order.": "請完成 QR 碼掃描和提料。", - "Please finish QR code scanand pick order.": "請完成 QR 碼掃描和提料。", + "Please finish QR code scan and pick order.": "請完成二維碼掃描和提料。", + "Please finish QR code scanand pick order.": "請完成二維碼掃描和提料。", "First created group": "首次建立分組", "Latest created group": "最新建立分組", "Manual Input": "手動輸入", - "QR Code Scan for Lot": " QR 碼掃描批次", - "Processing QR code...": "處理 QR 碼...", + "QR Code Scan for Lot": "二維碼掃描批次", + "Processing QR code...": "處理二維碼...", "The input is not the same as the expected lot number.": "輸入的批次號碼與預期的不符。", "Verified successfully!": "驗證成功!", "Cancel": "取消", @@ -370,7 +370,7 @@ "Scanning...":"掃描中...", "Print DN/Label":"列印送貨單/標籤", "Store ID":"儲存編號", - "QR code does not match any item in current orders.":"QR 碼不符合當前訂單中的任何貨品。", + "QR code does not match any item in current orders.":"二維碼不符合當前訂單中的任何貨品。", "Lot Number Mismatch":"批次號碼不符", "The scanned item matches the expected item, but the lot number is different. Do you want to proceed with this different lot?":"掃描的貨品與預期的貨品相同,但批次號碼不同。您是否要繼續使用不同的批次?", "The scanned item matches the expected item, but the lot number is different. Scan again to confirm: scan the expected lot QR to keep the suggested lot, or scan the other lot QR again to switch.":"掃描貨品相同但批次不同。請再掃描一次以確認:掃描「建議批次」的 QR 可沿用該批次;再掃描「另一批次」的 QR 則切換為該批次。", @@ -389,7 +389,7 @@ "Lot switch failed":"批次切換失敗", "The system could not switch to the scanned lot. Review the lots below, then tap Confirm to retry.":"系統無法切換至掃描的批次。請核對下方批次後按「確認」重試。", "You can also scan again: expected lot QR keeps the suggested line; scanned lot QR retries the switch.":"您也可以再掃描:掃描建議批次 QR 可保留該行;掃描欲切換批次 QR 可再次嘗試切換。", - "QR code verified.":"QR 碼驗證成功。", + "QR code verified.":"二維碼驗證成功。", "Order Finished":"訂單完成", "Submitted Status":"提交狀態", "Pick Execution Record":"提料執行記錄", @@ -507,16 +507,17 @@ "packaging": "提料中", "No Stock Available": "沒有庫存可用", "This lot is not available, please scan another lot.": "此批號不可用,請掃描其他批號。", - "This lot UOM does not match the pick line. Please scan another lot.": "此批號單位與提貨行不符,請掃描其他批號。", - "Scanned lot UOM does not match pick line UOM": "此批號單位與提貨行不符,請掃描其他批號。", + "This lot UOM does not match the pick line. Please scan another lot.": "此批號單位不符,請掃描其他批號。", + "Scanned lot UOM does not match pick line UOM": "此批號單位不符,請掃描其他批號。", "This lot has already been picked. Please scan another lot.": "此批號已提貨,請掃描其他批號。", "Scanned lot is already completed or checked": "此物料已提貨,請掃描其他批號。", - "Please check around have QR code or not, may be have just now stock in or transfer in or transfer out.": "請檢查周圍是否有其他可用 QR 碼。", + "Please check around have QR code or not, may be have just now stock in or transfer in or transfer out.": "請檢查周圍是否有其他可用二維碼。", "Lot is expired (expiry={{expiry}})": "掃描批號已過期(到期日={{expiry}})", "Day After Tomorrow": "後日", "Lot line is unavailable": "掃描批次不可用", "Select Date": "請選擇日期", - "Suggest Lot No.": "推薦批號", + + "Suggest Lot No.": "推薦批號/實際批號", "Search by Shop": "搜索商店", "Search by Truck": "搜索貨車", "Print DN & Label": "列印提料單和送貨單標籤", @@ -545,13 +546,13 @@ "4F ticket": "4/F 票", "4F lane panel legend": "貨車班次 — 裝載序(未撳數/總單數)", "Loading sequence n": "板{{n}}", - "lot QR code": "批號 QR 碼", + "lot QR code": "批號二維碼", "label Printer" : "標籤打印機", "A4 Printer" : "A4 打印機", "Loading Sequence": "裝載序", "Ticket No": "提票號碼", "The scanned lot inventory line is unavailable. Cannot switch or bind; pick line was not updated.": "掃描的庫存批行為「不可用」,無法換批或綁定;揀貨行未更新。", - "is unavable. Please check around have available QR code or not.": "此批號不可用,請檢查周圍是否有可用的 QR 碼。", + "is unavable. Please check around have available QR code or not.": "此批號不可用,請檢查周圍是否有可用的二維碼。", "Lot switch failed; pick line was not marked as checked.": "換批失敗;揀貨行未標為已核對。", "Lot confirmation failed. Please try again.": "確認批號失敗,請重試。", "Powder Mixture": "箱料粉", diff --git a/src/i18n/zh/ticketReleaseTable.json b/src/i18n/zh/ticketReleaseTable.json index fcdaa61..a0469a9 100644 --- a/src/i18n/zh/ticketReleaseTable.json +++ b/src/i18n/zh/ticketReleaseTable.json @@ -25,6 +25,9 @@ "Released Time": "開始時間", "Completed Time": "完成時間", "Handler Name": "負責員工", + "All Handlers": "所有員工", + "Select All": "全選", + "Unassigned": "未分配", "Number of FG Items (Order Item(s) Count)": "訂單項目數量", "No data available": "沒有資料", "Rows per page": "每頁行數",