diff --git a/src/app/api/pickOrder/actions.ts b/src/app/api/pickOrder/actions.ts index ea0b95e..d05db53 100644 --- a/src/app/api/pickOrder/actions.ts +++ b/src/app/api/pickOrder/actions.ts @@ -246,6 +246,15 @@ export interface UpdateSuggestedLotLineIdRequest { newLotLineId: number; } export interface FGPickOrderResponse { + // ✅ 新增:支持多个 pick orders + doPickOrderId: number; // ✅ 新增:do_pick_order 的 ID + pickOrderIds?: number[]; // ✅ 新增:所有 pick order IDs + pickOrderCodes?: string; // ✅ 新增:所有 pick order codes(逗号分隔) + deliveryOrderIds?: number[]; // ✅ 新增:所有 delivery order IDs + deliveryNos?: string; // ✅ 新增:所有 delivery order codes(逗号分隔) + numberOfPickOrders?: number; // ✅ 新增:pick order 数量 + + // ✅ 保留原有字段用于向后兼容(显示第一个 pick order) pickOrderId: number; pickOrderCode: string; pickOrderConsoCode: string; @@ -266,6 +275,37 @@ export interface FGPickOrderResponse { storeId: string; qrCodeData: number; } +export interface DoPickOrderDetail { + doPickOrder: { + id: number; + store_id: string; + ticket_no: string; + ticket_status: string; + truck_id: number; + truck_departure_time: string; + shop_id: number; + handled_by: number | null; + loading_sequence: number; + ticket_release_time: string | null; + TruckLanceCode: string; + ShopCode: string; + ShopName: string; + RequiredDeliveryDate: string; + }; + pickOrders: Array<{ + pick_order_id: number; + pick_order_code: string; + do_order_id: number; + delivery_order_code: string; + consoCode: string; + status: string; + targetDate: string; + }>; + selectedPickOrderId: number; + lotDetails: any[]; // 使用现有的 lot detail 结构 + pickOrderCodes?: string; + deliveryNos?: string; +} export interface AutoAssignReleaseByStoreRequest { userId: number; storeId: string; // "2/F" | "4/F" @@ -280,12 +320,17 @@ export interface UpdateDoPickOrderHideStatusRequest { } export interface CompletedDoPickOrderResponse { id: number; + doPickOrderRecordId: number; // ✅ 新增 pickOrderId: number; + pickOrderIds: number[]; // ✅ 新增:所有 pick order IDs pickOrderCode: string; + pickOrderCodes: string; // ✅ 新增:所有 pick order codes (逗号分隔) pickOrderConsoCode: string; pickOrderStatus: string; deliveryOrderId: number; + deliveryOrderIds: number[]; // ✅ 新增:所有 delivery order IDs deliveryNo: string; + deliveryNos: string; // ✅ 新增:所有 delivery order codes (逗号分隔) deliveryDate: string; shopId: number; shopCode: string; @@ -295,6 +340,7 @@ export interface CompletedDoPickOrderResponse { shopPoNo: string; numberOfCartons: number; truckLanceCode: string; + DepartureTime: string; // ✅ 新增 storeId: string; completedDate: string; fgPickOrders: FGPickOrderResponse[]; @@ -386,6 +432,20 @@ export interface LaneBtn { unassigned: number; total: number; } +export const fetchDoPickOrderDetail = async ( + doPickOrderId: number, + selectedPickOrderId?: number +): Promise => { + const url = selectedPickOrderId + ? `${BASE_API_URL}/pickOrder/do-pick-order-detail/${doPickOrderId}?selectedPickOrderId=${selectedPickOrderId}` + : `${BASE_API_URL}/pickOrder/do-pick-order-detail/${doPickOrderId}`; + + const response = await serverFetchJson(url, { + method: "GET", + }); + + return response; +}; export const updatePickExecutionIssueStatus = async ( data: UpdatePickExecutionIssueRequest ): Promise => { @@ -739,6 +799,40 @@ interface SuggestionWithStatus { stockOutLineQty?: number; suggestionStatus: 'active' | 'completed' | 'rejected' | 'in_progress' | 'unknown'; } +// 在 actions.ts 中修改接口定义 +export interface FGPickOrderHierarchicalResponse { + fgInfo: { + doPickOrderId: number; + ticketNo: string; + storeId: string; + shopCode: string; + shopName: string; + truckLanceCode: string; + departureTime: string; + }; + pickOrders: Array<{ + pickOrderId: number; + pickOrderCode: string; + doOrderId: number; + deliveryOrderCode: string; + consoCode: string; + status: string; + targetDate: string; + pickOrderLines: Array<{ + id: number; + requiredQty: number; + status: string; + item: { + id: number; + code: string; + name: string; + uomCode: string; + uomDesc: string; + }; + lots: Array; // 可以是空数组 + }>; + }>; +} export interface CheckCompleteResponse { id: number | null; name: string; @@ -823,23 +917,32 @@ export const fetchAllPickOrderLotsHierarchical = cache(async (userId: number): P }; } }); -export const fetchLotDetailsByPickOrderId = async (pickOrderId: number): Promise => { +export const fetchLotDetailsByDoPickOrderRecordId = async (doPickOrderRecordId: number): Promise<{ + fgInfo: any; + pickOrders: any[]; +}> => { try { - console.log("🔍 Fetching lot details for pickOrderId:", pickOrderId); + console.log("🔍 Fetching lot details for doPickOrderRecordId:", doPickOrderRecordId); - const data = await serverFetchJson( - `${BASE_API_URL}/pickOrder/lot-details-by-pick-order/${pickOrderId}`, + const data = await serverFetchJson<{ + fgInfo: any; + pickOrders: any[]; + }>( + `${BASE_API_URL}/pickOrder/lot-details-by-do-pick-order-record/${doPickOrderRecordId}`, { method: 'GET', next: { tags: ["pickorder"] }, } ); - console.log("✅ Fetched lot details for pickOrderId:", data); + console.log("✅ Fetched hierarchical lot details:", data); return data; } catch (error) { - console.error("❌ Error fetching lot details for pickOrderId:", error); - return []; + console.error("❌ Error fetching lot details:", error); + return { + fgInfo: null, + pickOrders: [] + }; } }; // Update the existing function to use the non-auto-assign endpoint diff --git a/src/components/FinishedGoodSearch/FGPickOrderInfoCard.tsx b/src/components/FinishedGoodSearch/FGPickOrderInfoCard.tsx index d5f95c1..3d88dce 100644 --- a/src/components/FinishedGoodSearch/FGPickOrderInfoCard.tsx +++ b/src/components/FinishedGoodSearch/FGPickOrderInfoCard.tsx @@ -2,14 +2,22 @@ import { Box, Card, CardContent, Grid, TextField, Stack } from "@mui/material"; import { useTranslation } from "react-i18next"; -import { FGPickOrderResponse } from "@/app/api/pickOrder/actions"; +import { FGPickOrderResponse, DoPickOrderDetail } from "@/app/api/pickOrder/actions"; interface Props { fgOrder: FGPickOrderResponse; + doPickOrderDetail?: DoPickOrderDetail | null; } -const FGPickOrderInfoCard: React.FC = ({ fgOrder }) => { +const FGPickOrderInfoCard: React.FC = ({ fgOrder, doPickOrderDetail }) => { const { t } = useTranslation("pickOrder"); + if (!fgOrder) { + return null; + } + + const pickOrderCodes = fgOrder.pickOrderCodes || ""; + + const deliveryOrderCodes = fgOrder.deliveryNos || ""; return ( @@ -17,21 +25,25 @@ const FGPickOrderInfoCard: React.FC = ({ fgOrder }) => { - + diff --git a/src/components/FinishedGoodSearch/FinishedGoodFloorLanePanel.tsx b/src/components/FinishedGoodSearch/FinishedGoodFloorLanePanel.tsx index bc843ff..1961041 100644 --- a/src/components/FinishedGoodSearch/FinishedGoodFloorLanePanel.tsx +++ b/src/components/FinishedGoodSearch/FinishedGoodFloorLanePanel.tsx @@ -22,6 +22,7 @@ const FinishedGoodFloorLanePanel: React.FC = ({ onPickOrderAssigned }) => const [summary4F, setSummary4F] = useState(null); const [isLoadingSummary, setIsLoadingSummary] = useState(false); const [isAssigning, setIsAssigning] = useState(false); + //const [selectedDate, setSelectedDate] = useState("today"); const [selectedDate, setSelectedDate] = useState("today"); const loadSummaries = useCallback(async () => { @@ -132,6 +133,7 @@ const FinishedGoodFloorLanePanel: React.FC = ({ onPickOrderAssigned }) => {t("Select Date")} + + diff --git a/src/components/FinishedGoodSearch/GoodPickExecution.tsx b/src/components/FinishedGoodSearch/GoodPickExecution.tsx index 1f54bcc..9b04ae9 100644 --- a/src/components/FinishedGoodSearch/GoodPickExecution.tsx +++ b/src/components/FinishedGoodSearch/GoodPickExecution.tsx @@ -32,7 +32,9 @@ import { AutoAssignReleaseResponse, checkPickOrderCompletion, PickOrderCompletionResponse, - checkAndCompletePickOrderByConsoCode + checkAndCompletePickOrderByConsoCode, + fetchDoPickOrderDetail, + DoPickOrderDetail, } from "@/app/api/pickOrder/actions"; import { fetchNameList, NameList } from "@/app/api/user/actions"; import { @@ -50,7 +52,8 @@ import { fetchStockInLineInfo } from "@/app/api/po/actions"; import GoodPickExecutionForm from "./GoodPickExecutionForm"; import FGPickOrderCard from "./FGPickOrderCard"; import FinishedGoodFloorLanePanel from "./FinishedGoodFloorLanePanel"; -import FGPickOrderInfoCard from "./FGPickOrderInfoCard"; +import FGPickOrderInfoCard from "./FGPickOrderInfoCard"; +import GoodPickExecutiondetail from "./GoodPickExecutiondetail"; interface Props { filterArgs: Record; onFgPickOrdersChange?: (fgPickOrders: FGPickOrderResponse[]) => void; @@ -322,7 +325,9 @@ const PickExecution: React.FC = ({ filterArgs, onFgPickOrdersChange }) => const [originalCombinedData, setOriginalCombinedData] = useState([]); const { values: qrValues, isScanning, startScan, stopScan, resetScan } = useQrCodeScannerContext(); - + const [doPickOrderDetail, setDoPickOrderDetail] = useState(null); +const [selectedPickOrderId, setSelectedPickOrderId] = useState(null); +const [pickOrderSwitching, setPickOrderSwitching] = useState(false); const [qrScanInput, setQrScanInput] = useState(''); const [qrScanError, setQrScanError] = useState(false); const [qrScanSuccess, setQrScanSuccess] = useState(false); @@ -352,25 +357,28 @@ const PickExecution: React.FC = ({ filterArgs, onFgPickOrdersChange }) => const [selectedLotForExecutionForm, setSelectedLotForExecutionForm] = useState(null); const [fgPickOrders, setFgPickOrders] = useState([]); const [fgPickOrdersLoading, setFgPickOrdersLoading] = useState(false); - const fetchFgPickOrdersData = useCallback(async () => { - if (!currentUserId) return; +// 在 GoodPickExecutiondetail.tsx 中修改 fetchFgPickOrdersData +// 修改 fetchFgPickOrdersData 函数: +const fetchFgPickOrdersData = useCallback(async () => { + if (!currentUserId) return; + + setFgPickOrdersLoading(true); + try { + const fgPickOrders = await fetchFGPickOrdersByUserId(currentUserId); - setFgPickOrdersLoading(true); - try { - // ✅ 简化:直接使用 userId 调用 API,不需要循环 - const fgPickOrders = await fetchFGPickOrdersByUserId(currentUserId); - - setFgPickOrders(fgPickOrders); - onFgPickOrdersChange?.(fgPickOrders); - console.log("✅ Fetched FG pick orders for user:", fgPickOrders); - } catch (error) { - console.error("❌ Error fetching FG pick orders:", error); - setFgPickOrders([]); - onFgPickOrdersChange?.([]); - } finally { - setFgPickOrdersLoading(false); - } - }, [currentUserId, onFgPickOrdersChange]); + console.log("🔍 DEBUG: Fetched FG pick orders:", fgPickOrders); + console.log("🔍 DEBUG: First order numberOfPickOrders:", fgPickOrders[0]?.numberOfPickOrders); + + setFgPickOrders(fgPickOrders); + + + } catch (error) { + console.error("❌ Error fetching FG pick orders:", error); + setFgPickOrders([]); + } finally { + setFgPickOrdersLoading(false); + } +}, [currentUserId, selectedPickOrderId]); // ✅ 简化:移除复杂的 useEffect 依赖 useEffect(() => { @@ -972,13 +980,16 @@ return ( ) : ( // ✅ 有活动订单,显示 FG 订单信息 - {fgPickOrders.map((fgOrder) => ( - - ))} - + {fgPickOrders.map((fgOrder) => ( + + + + + + ))} + )} {/* Modals */} diff --git a/src/components/FinishedGoodSearch/GoodPickExecutionRecord.tsx b/src/components/FinishedGoodSearch/GoodPickExecutionRecord.tsx index 8ea96b2..6829595 100644 --- a/src/components/FinishedGoodSearch/GoodPickExecutionRecord.tsx +++ b/src/components/FinishedGoodSearch/GoodPickExecutionRecord.tsx @@ -44,7 +44,7 @@ import { fetchCompletedDoPickOrders, CompletedDoPickOrderResponse, CompletedDoPickOrderSearchParams, - fetchLotDetailsByPickOrderId + fetchLotDetailsByDoPickOrderRecordId } from "@/app/api/pickOrder/actions"; import { fetchNameList, NameList } from "@/app/api/user/actions"; import { @@ -407,30 +407,54 @@ const GoodPickExecutionRecord: React.FC = ({ filterArgs }) => { setSelectedDoPickOrder(doPickOrder); setShowDetailView(true); - // ✅ 修复:使用新的 API 根据 pickOrderId 获取 lot 详情 try { - const lotDetails = await fetchLotDetailsByPickOrderId(doPickOrder.pickOrderId); - setDetailLotData(lotDetails); - console.log("✅ Loaded detail lot data for pick order:", doPickOrder.pickOrderCode, lotDetails); + // ✅ 使用 doPickOrderRecordId 而不是 pickOrderId + const hierarchicalData = await fetchLotDetailsByDoPickOrderRecordId(doPickOrder.doPickOrderRecordId); + console.log("✅ Loaded hierarchical lot data:", hierarchicalData); - // ✅ 触发打印按钮状态更新 - 基于详情数据 - const allCompleted = lotDetails.length > 0 && lotDetails.every(lot => + // ✅ 转换为平铺格式 + const flatLotData: any[] = []; + + if (hierarchicalData.pickOrders && hierarchicalData.pickOrders.length > 0) { + hierarchicalData.pickOrders.forEach((po: any) => { + po.pickOrderLines?.forEach((line: any) => { + if (line.lots && line.lots.length > 0) { + line.lots.forEach((lot: any) => { + flatLotData.push({ + pickOrderCode: po.pickOrderCode, + itemCode: line.item.code, + itemName: line.item.name, + lotNo: lot.lotNo, + location: lot.location, + requiredQty: lot.requiredQty, + actualPickQty: lot.actualPickQty, + processingStatus: lot.processingStatus, + stockOutLineStatus: lot.stockOutLineStatus + }); + }); + } + }); + }); + } + + setDetailLotData(flatLotData); + + // ✅ 计算完成状态 + const allCompleted = flatLotData.length > 0 && flatLotData.every(lot => lot.processingStatus === 'completed' ); - // ✅ 发送事件,包含标签页信息 window.dispatchEvent(new CustomEvent('pickOrderCompletionStatus', { detail: { allLotsCompleted: allCompleted, - tabIndex: 2 // ✅ 明确指定这是来自标签页 2 的事件 + tabIndex: 2 } })); - } catch (error) { + } catch (error) { // ✅ 添加 catch 块 console.error("❌ Error loading detail lot data:", error); setDetailLotData([]); - // ✅ 如果加载失败,禁用打印按钮 window.dispatchEvent(new CustomEvent('pickOrderCompletionStatus', { detail: { allLotsCompleted: false, @@ -458,86 +482,132 @@ const GoodPickExecutionRecord: React.FC = ({ filterArgs }) => { // ✅ 如果显示详情视图,渲染类似 GoodPickExecution 的表格 - if (showDetailView && selectedDoPickOrder) { - return ( - - - {/* 返回按钮和标题 */} - - - - {t("Pick Order Details")}: {selectedDoPickOrder.pickOrderCode} - - + // ✅ 如果显示详情视图,渲染层级结构 +if (showDetailView && selectedDoPickOrder) { + return ( + + + {/* 返回按钮和标题 */} + + + + {t("Pick Order Details")}: {selectedDoPickOrder.ticketNo} + + - {/* 订单基本信息 */} - - - {t("Order Information")} - - + {/* FG 订单基本信息 */} + + + {t("Shop Name")}: {selectedDoPickOrder.shopName} - - {t("Delivery No")}: {selectedDoPickOrder.deliveryNo} + + {t("Store ID")}: {selectedDoPickOrder.storeId} - + + {t("Ticket No.")}: {selectedDoPickOrder.ticketNo} + + + {t("Truck Lance Code")}: {selectedDoPickOrder.truckLanceCode} + + {t("Completed Date")}: {dayjs(selectedDoPickOrder.completedDate).format(OUTPUT_DATE_FORMAT)} - - - {/* ✅ 添加数据检查 */} - {detailLotData.length === 0 ? ( - - - {t("No lot details found for this order")} - + + + + {/* ✅ 添加:多个 Pick Orders 信息(如果有) */} + {selectedDoPickOrder.pickOrderIds && selectedDoPickOrder.pickOrderIds.length > 1 && ( + + + {t("This ticket contains")} {selectedDoPickOrder.pickOrderIds.length} {t("pick orders")}: + + + {selectedDoPickOrder.pickOrderCodes?.split(', ').map((code, idx) => ( + + ))} - ) : ( - /* 显示完成数据的表格 */ - - - - - {t("Pick Order Code")} - {t("Item Code")} - {t("Item Name")} - {t("Lot No")} - {t("Location")} - {t("Required Qty")} - {t("Actual Pick Qty")} - {t("Submitted Status")} - - - - {detailLotData.map((lot, index) => ( - - {lot.pickOrderCode || 'N/A'} - {lot.itemCode || 'N/A'} - {lot.itemName || 'N/A'} - {lot.lotNo || 'N/A'} - {lot.location || 'N/A'} - {lot.requiredQty || 0} - {lot.actualPickQty || 0} - - - - - ))} - -
-
- )} -
-
- ); - } + + )} + + {/* ✅ 数据检查 */} + {detailLotData.length === 0 ? ( + + + {t("No lot details found for this order")} + + + ) : ( + /* ✅ 按 Pick Order 分组显示 */ + + {/* ✅ 按 pickOrderCode 分组 */} + {Object.entries( + detailLotData.reduce((acc: any, lot: any) => { + const key = lot.pickOrderCode || 'Unknown'; + if (!acc[key]) acc[key] = []; + acc[key].push(lot); + return acc; + }, {}) + ).map(([pickOrderCode, lots]: [string, any]) => ( + + }> + + {t("Pick Order")}: {pickOrderCode} ({(lots as any[]).length} {t("items")}) + + + + + + + + {t("Index")} + {t("Item Code")} + {t("Item Name")} + {t("Lot No")} + {t("Location")} + {t("Required Qty")} + {t("Actual Pick Qty")} + {t("Status")} + + + + {(lots as any[]).map((lot: any, index: number) => ( + + {index + 1} + {lot.itemCode || 'N/A'} + {lot.itemName || 'N/A'} + {lot.lotNo || 'N/A'} + {lot.location || 'N/A'} + {lot.requiredQty || 0} + {lot.actualPickQty || 0} + + + + + ))} + +
+
+
+
+ ))} +
+ )} +
+
+ ); +} // ✅ 默认列表视图 return ( diff --git a/src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx b/src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx index 1d97df1..637a2fe 100644 --- a/src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx +++ b/src/components/FinishedGoodSearch/GoodPickExecutiondetail.tsx @@ -34,14 +34,18 @@ import { fetchFGPickOrders, // ✅ Add this import FGPickOrderResponse, + checkPickOrderCompletion, fetchAllPickOrderLotsHierarchical, PickOrderCompletionResponse, checkAndCompletePickOrderByConsoCode, updateSuggestedLotLineId, - confirmLotSubstitution, // ✅ 必须添加 + confirmLotSubstitution, + fetchDoPickOrderDetail, // ✅ 必须添加 + DoPickOrderDetail, // ✅ 必须添加 fetchFGPickOrdersByUserId } from "@/app/api/pickOrder/actions"; + import FGPickOrderInfoCard from "./FGPickOrderInfoCard"; import LotConfirmationModal from "./LotConfirmationModal"; //import { fetchItem } from "@/app/api/settings/item"; @@ -76,7 +80,7 @@ const QrCodeModal: React.FC<{ const { t } = useTranslation("pickOrder"); const { values: qrValues, isScanning, startScan, stopScan, resetScan } = useQrCodeScannerContext(); const [manualInput, setManualInput] = useState(''); - + const [doPickOrderDetail, setDoPickOrderDetail] = useState(null); const [selectedPickOrderId, setSelectedPickOrderId] = useState(null); const [pickOrderSwitching, setPickOrderSwitching] = useState(false); const [manualInputSubmitted, setManualInputSubmitted] = useState(false); @@ -325,7 +329,7 @@ const PickExecution: React.FC = ({ filterArgs }) => { const { t } = useTranslation("pickOrder"); const router = useRouter(); const { data: session } = useSession() as { data: SessionWithTokens | null }; - const [availablePickOrders, setAvailablePickOrders] = useState([]); + const [doPickOrderDetail, setDoPickOrderDetail] = useState(null); const [selectedPickOrderId, setSelectedPickOrderId] = useState(null); const [pickOrderSwitching, setPickOrderSwitching] = useState(false); const currentUserId = session?.id ? parseInt(session.id) : undefined; @@ -381,6 +385,9 @@ const [isConfirmingLot, setIsConfirmingLot] = useState(false); try { const fgPickOrders = await fetchFGPickOrdersByUserId(currentUserId); + console.log("🔍 DEBUG: Fetched FG pick orders:", fgPickOrders); + console.log("🔍 DEBUG: First order numberOfPickOrders:", fgPickOrders[0]?.numberOfPickOrders); + setFgPickOrders(fgPickOrders); // ✅ 移除:不需要再单独调用 fetchDoPickOrderDetail @@ -437,11 +444,13 @@ const [isConfirmingLot, setIsConfirmingLot] = useState(false); return allCompleted; }, []); const fetchAllCombinedLotData = useCallback(async (userId?: number, pickOrderIdOverride?: number) => { + setCombinedDataLoading(true); try { const userIdToUse = userId || currentUserId; console.log("🔍 fetchAllCombinedLotData called with userId:", userIdToUse); + if (!userIdToUse) { console.warn("⚠️ No userId available, skipping API call"); @@ -451,6 +460,7 @@ const [isConfirmingLot, setIsConfirmingLot] = useState(false); return; } + // ✅ 获取新结构的层级数据 // ✅ 获取新结构的层级数据 const hierarchicalData = await fetchAllPickOrderLotsHierarchical(userIdToUse); console.log("✅ Hierarchical data (new structure):", hierarchicalData); @@ -466,6 +476,7 @@ const [isConfirmingLot, setIsConfirmingLot] = useState(false); // ✅ 设置 FG info 到 fgPickOrders(用于显示 FG 信息卡片) const fgOrder: FGPickOrderResponse = { + doPickOrderId: hierarchicalData.fgInfo.doPickOrderId, ticketNo: hierarchicalData.fgInfo.ticketNo, storeId: hierarchicalData.fgInfo.storeId, shopCode: hierarchicalData.fgInfo.shopCode, @@ -489,17 +500,54 @@ const [isConfirmingLot, setIsConfirmingLot] = useState(false); qrCodeData: hierarchicalData.fgInfo.doPickOrderId, // ✅ 新增:多个 pick orders 信息 - // numberOfPickOrders: hierarchicalData.pickOrders.length, - // pickOrderIds: hierarchicalData.pickOrders.map((po: any) => po.pickOrderId), - //pickOrderCodes: hierarchicalData.pickOrders.map((po: any) => po.pickOrderCode).join(", "), - // deliveryOrderIds: hierarchicalData.pickOrders.map((po: any) => po.doOrderId), - //deliveryNos: hierarchicalData.pickOrders.map((po: any) => po.deliveryOrderCode).join(", ") + numberOfPickOrders: hierarchicalData.pickOrders.length, + pickOrderIds: hierarchicalData.pickOrders.map((po: any) => po.pickOrderId), + pickOrderCodes: hierarchicalData.pickOrders.map((po: any) => po.pickOrderCode).join(", "), + deliveryOrderIds: hierarchicalData.pickOrders.map((po: any) => po.doOrderId), + deliveryNos: hierarchicalData.pickOrders.map((po: any) => po.deliveryOrderCode).join(", ") }; setFgPickOrders([fgOrder]); - setAvailablePickOrders(hierarchicalData.pickOrders); + // ✅ 构建 doPickOrderDetail(用于 switcher) - + if (hierarchicalData.pickOrders.length > 1) { + const detail: DoPickOrderDetail = { + doPickOrder: { + id: hierarchicalData.fgInfo.doPickOrderId, + store_id: hierarchicalData.fgInfo.storeId, + ticket_no: hierarchicalData.fgInfo.ticketNo, + ticket_status: "", + truck_id: 0, + truck_departure_time: hierarchicalData.fgInfo.departureTime, + shop_id: 0, + handled_by: null, + loading_sequence: 0, + ticket_release_time: null, + TruckLanceCode: hierarchicalData.fgInfo.truckLanceCode, + ShopCode: hierarchicalData.fgInfo.shopCode, + ShopName: hierarchicalData.fgInfo.shopName, + RequiredDeliveryDate: "" + }, + pickOrders: hierarchicalData.pickOrders.map((po: any) => ({ + pick_order_id: po.pickOrderId, + pick_order_code: po.pickOrderCode, + do_order_id: po.doOrderId, + delivery_order_code: po.deliveryOrderCode, + consoCode: po.consoCode, + status: po.status, + targetDate: po.targetDate + })), + selectedPickOrderId: pickOrderIdOverride || hierarchicalData.pickOrders[0]?.pickOrderId || 0, + lotDetails: [] + }; + + setDoPickOrderDetail(detail); + + // ✅ 设置默认选中的 pick order ID + if (!selectedPickOrderId) { + setSelectedPickOrderId(pickOrderIdOverride || hierarchicalData.pickOrders[0]?.pickOrderId); + } + } // ✅ 确定要显示的 pick order const targetPickOrderId = pickOrderIdOverride || selectedPickOrderId || hierarchicalData.pickOrders[0]?.pickOrderId; @@ -615,14 +663,16 @@ const [isConfirmingLot, setIsConfirmingLot] = useState(false); }); } }); - + console.log("✅ Transformed flat lot data:", flatLotData); console.log("🔍 Total items (including null stock):", flatLotData.length); + setCombinedLotData(flatLotData); setOriginalCombinedData(flatLotData); checkAllLotsCompleted(flatLotData); + } catch (error) { console.error("❌ Error fetching combined lot data:", error); setCombinedLotData([]); @@ -1463,8 +1513,9 @@ const handleSubmitPickQtyWithQty = useCallback(async (lot: any, submitQty: numbe console.error("Error switching pick order:", error); } finally { setPickOrderSwitching(false); - } - }, [pickOrderSwitching, currentUserId, fetchAllCombinedLotData]); + } + }, [pickOrderSwitching, currentUserId, fetchAllCombinedLotData]); + const handleStopScan = useCallback(() => { console.log("⏹️ Stopping manual QR scan..."); setIsManualScanning(false); @@ -1629,6 +1680,8 @@ const handleSubmitPickQtyWithQty = useCallback(async (lot: any, submitQty: numbe {t("Departure Time")}: {fgPickOrders[0].DepartureTime || '-'} + + @@ -1638,28 +1691,28 @@ const handleSubmitPickQtyWithQty = useCallback(async (lot: any, submitQty: numbe {/* ✅ FG Info Card */} {/* ✅ Pick Order Switcher - 放在 FG Info 下面,QR 按钮上面 */} - {availablePickOrders.length > 1 && ( - - - {t("Select Pick Order:")} - - - {availablePickOrders.map((po: any) => ( - handlePickOrderSwitch(po.pickOrderId)} - color={selectedPickOrderId === po.pickOrderId ? "primary" : "default"} - variant={selectedPickOrderId === po.pickOrderId ? "filled" : "outlined"} - sx={{ - cursor: 'pointer', - '&:hover': { backgroundColor: 'primary.light', color: 'white' } - }} - /> - ))} - - -)} + {doPickOrderDetail && doPickOrderDetail.pickOrders.length > 1 && ( + + + {t("Select Pick Order:")} + + + {doPickOrderDetail.pickOrders.map((po: any) => ( + handlePickOrderSwitch(po.pick_order_id)} + color={selectedPickOrderId === po.pick_order_id ? "primary" : "default"} + variant={selectedPickOrderId === po.pick_order_id ? "filled" : "outlined"} + sx={{ + cursor: 'pointer', + '&:hover': { backgroundColor: 'primary.light', color: 'white' } + }} + /> + ))} + + + )} {/* ✅ 保留:Combined Lot Table - 包含所有 QR 扫描功能 */} @@ -1967,8 +2020,12 @@ paginatedData.map((lot, index) => { /> )} + + + + ); }; -export default PickExecution; \ No newline at end of file +export default PickExecution; diff --git a/src/components/JoSearch/JoSearch.tsx b/src/components/JoSearch/JoSearch.tsx index 2cb7fd7..bb6d596 100644 --- a/src/components/JoSearch/JoSearch.tsx +++ b/src/components/JoSearch/JoSearch.tsx @@ -25,17 +25,19 @@ import dayjs from "dayjs"; import { fetchInventories } from "@/app/api/inventory/actions"; import { InventoryResult } from "@/app/api/inventory"; +import { PrinterCombo } from "@/app/api/settings/printer"; interface Props { defaultInputs: SearchJoResultRequest, bomCombo: BomCombo[] + printerCombo: PrinterCombo[]; } type SearchQuery = Partial>; type SearchParamNames = keyof SearchQuery; -const JoSearch: React.FC = ({ defaultInputs, bomCombo }) => { +const JoSearch: React.FC = ({ defaultInputs, bomCombo, printerCombo }) => { const { t } = useTranslation("jo"); const router = useRouter() const [filteredJos, setFilteredJos] = useState([]); @@ -426,7 +428,7 @@ const JoSearch: React.FC = ({ defaultInputs, bomCombo }) => { open={openModal} onClose={closeNewModal} inputDetail={modalInfo} - printerCombo={[]} + printerCombo={printerCombo} // skipQc={true} /> diff --git a/src/components/JoSearch/JoSearchWrapper.tsx b/src/components/JoSearch/JoSearchWrapper.tsx index 7972d56..78470d6 100644 --- a/src/components/JoSearch/JoSearchWrapper.tsx +++ b/src/components/JoSearch/JoSearchWrapper.tsx @@ -3,6 +3,7 @@ import GeneralLoading from "../General/GeneralLoading"; import JoSearch from "./JoSearch"; import { SearchJoResultRequest } from "@/app/api/jo/actions"; import { fetchBomCombo } from "@/app/api/bom"; +import { fetchPrinterCombo } from "@/app/api/settings/printer"; interface SubComponents { Loading: typeof GeneralLoading; @@ -15,12 +16,14 @@ const JoSearchWrapper: React.FC & SubComponents = async () => { } const [ - bomCombo + bomCombo, + printerCombo ] = await Promise.all([ - fetchBomCombo() + fetchBomCombo(), + fetchPrinterCombo() ]) - return + return } JoSearchWrapper.Loading = GeneralLoading; diff --git a/src/components/Logo/Logo.tsx b/src/components/Logo/Logo.tsx index 4750e72..de2ea41 100644 --- a/src/components/Logo/Logo.tsx +++ b/src/components/Logo/Logo.tsx @@ -14,16 +14,16 @@ const Logo: React.FC = ({ width, height }) => { diff --git a/src/components/MailField/MailField.css b/src/components/MailField/MailField.css index 50237e1..96e7fb9 100644 --- a/src/components/MailField/MailField.css +++ b/src/components/MailField/MailField.css @@ -123,7 +123,7 @@ /* Input styles */ /* .tiptap-input { - font-size: 14px; + fontSize: 14px; font-weight: 500; line-height: 12px; } */ \ No newline at end of file diff --git a/src/components/StockIn/FgStockInForm.tsx b/src/components/StockIn/FgStockInForm.tsx index af46970..467f3e6 100644 --- a/src/components/StockIn/FgStockInForm.tsx +++ b/src/components/StockIn/FgStockInForm.tsx @@ -67,7 +67,7 @@ const textfieldSx = { transform: "translate(14px, 1.2rem) scale(1)", "&.MuiInputLabel-shrink": { fontSize: 24, - transform: "translate(14px, -0.5rem) scale(1)", + transform: "translate(14px, -9px) scale(1)", }, // [theme.breakpoints.down("sm")]: { // fontSize: "1rem", diff --git a/src/components/StockIn/StockInForm.tsx b/src/components/StockIn/StockInForm.tsx index 8b88692..e48c0d9 100644 --- a/src/components/StockIn/StockInForm.tsx +++ b/src/components/StockIn/StockInForm.tsx @@ -60,7 +60,7 @@ const textfieldSx = { transform: "translate(14px, 1.2rem) scale(1)", "&.MuiInputLabel-shrink": { fontSize: 24, - transform: "translate(14px, -0.5rem) scale(1)", + transform: "translate(14px, -9px) scale(1)", }, // [theme.breakpoints.down("sm")]: { // fontSize: "1rem", diff --git a/src/i18n/zh/pickOrder.json b/src/i18n/zh/pickOrder.json index 31f6e3e..4515ad2 100644 --- a/src/i18n/zh/pickOrder.json +++ b/src/i18n/zh/pickOrder.json @@ -13,11 +13,14 @@ "Assigned To": "已分配", "Do you want to start?": "確定開始嗎?", "Start": "開始", + "Pick Order Code(s)": "提料單編號", + "Delivery Order Code(s)": "送貨單編號", "Start Success": "開始成功", "Truck Lance Code": "車牌號碼", "Completed Date": "完成日期", "Completed Time": "完成時間", - + "Select Pick Order:": "選擇提料單:", + "⚠️ No Stock Available": "⚠️ 沒有庫存", "Start Fail": "開始失敗", "Start PO": "開始採購訂單", "Do you want to complete?": "確定完成嗎?", diff --git a/src/i18n/zh/purchaseOrder.json b/src/i18n/zh/purchaseOrder.json index 806a1e8..6dd7d9c 100644 --- a/src/i18n/zh/purchaseOrder.json +++ b/src/i18n/zh/purchaseOrder.json @@ -164,5 +164,6 @@ "Expiry Date cannot be earlier than Production Date": "到期日不可早於生產日期", "Production Date must be earlier than Expiry Date": "生產日期必須早於到期日", "confirm expiry date": "確認到期日", - "Invalid Date": "無效日期" + "Invalid Date": "無效日期", + "Missing QC Template, please contact administrator": "找不到品檢模板,請聯絡管理員" }