diff --git a/src/app/api/jo/actions.ts b/src/app/api/jo/actions.ts index 0955a79..8113b1d 100644 --- a/src/app/api/jo/actions.ts +++ b/src/app/api/jo/actions.ts @@ -267,4 +267,17 @@ export const manualCreateJo = cache(async (data: SaveJo) => { body: JSON.stringify(data), headers: { "Content-Type": "application/json" } }) +}) + +export const fetchCompletedJobOrderPickOrdersWithCompletedSecondScan = cache(async (userId: number) => { + return serverFetchJson(`${BASE_API_URL}/jo/completed-job-order-pick-orders-with-completed-second-scan/${userId}`, { + method: "GET", + headers: { "Content-Type": "application/json" } + }) +}) +export const fetchCompletedJobOrderPickOrderLotDetails = cache(async (pickOrderId: number) => { + return serverFetchJson(`${BASE_API_URL}/jo/completed-job-order-pick-order-lot-details/${pickOrderId}`, { + method: "GET", + headers: { "Content-Type": "application/json" } + }) }) \ No newline at end of file diff --git a/src/components/Jodetail/FInishedJobOrderRecord.tsx b/src/components/Jodetail/FInishedJobOrderRecord.tsx index 4756e95..5845588 100644 --- a/src/components/Jodetail/FInishedJobOrderRecord.tsx +++ b/src/components/Jodetail/FInishedJobOrderRecord.tsx @@ -24,102 +24,101 @@ import { Accordion, AccordionSummary, AccordionDetails, + Checkbox, // ✅ Add Checkbox import } from "@mui/material"; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import { useCallback, useEffect, useState, useRef, useMemo } from "react"; import { useTranslation } from "react-i18next"; import { useRouter } from "next/navigation"; import { - fetchALLPickOrderLineLotDetails, - updateStockOutLineStatus, - createStockOutLine, - recordPickExecutionIssue, - fetchFGPickOrders, - FGPickOrderResponse, - autoAssignAndReleasePickOrder, - AutoAssignReleaseResponse, - checkPickOrderCompletion, - PickOrderCompletionResponse, - checkAndCompletePickOrderByConsoCode, - fetchCompletedDoPickOrders, // ✅ 新增:使用新的 API - CompletedDoPickOrderResponse, - CompletedDoPickOrderSearchParams, - fetchLotDetailsByPickOrderId // ✅ 修复:导入类型 -} from "@/app/api/pickOrder/actions"; + fetchCompletedJobOrderPickOrdersWithCompletedSecondScan, + fetchCompletedJobOrderPickOrderLotDetails +} from "@/app/api/jo/actions"; import { fetchNameList, NameList } from "@/app/api/user/actions"; import { FormProvider, useForm, } from "react-hook-form"; import SearchBox, { Criterion } from "../SearchBox"; -import { CreateStockOutLine } from "@/app/api/pickOrder/actions"; -import { updateInventoryLotLineQuantities } from "@/app/api/inventory/actions"; -import QrCodeIcon from '@mui/icons-material/QrCode'; -import { useQrCodeScannerContext } from '../QrCodeScannerProvider/QrCodeScannerProvider'; import { useSession } from "next-auth/react"; import { SessionWithTokens } from "@/config/authConfig"; -import { fetchStockInLineInfo } from "@/app/api/po/actions"; -import GoodPickExecutionForm from "./JobPickExecutionForm"; -import FGPickOrderCard from "./FGPickOrderCard"; interface Props { filterArgs: Record; } -// ✅ 新增:已完成的 DO Pick Order 接口 -interface CompletedDoPickOrder { +// ✅ 修改:已完成的 Job Order Pick Order 接口 +interface CompletedJobOrderPickOrder { id: number; pickOrderId: number; pickOrderCode: string; pickOrderConsoCode: string; + pickOrderTargetDate: string; pickOrderStatus: string; - deliveryOrderId: number; - deliveryNo: string; - deliveryDate: string; - shopId: number; - shopCode: string; - shopName: string; - shopAddress: string; - ticketNo: string; - shopPoNo: string; - numberOfCartons: number; - truckNo: string; - storeId: string; completedDate: string; - fgPickOrders: FGPickOrderResponse[]; + jobOrderId: number; + jobOrderCode: string; + jobOrderName: string; + reqQty: number; + uom: string; + planStart: string; + planEnd: string; + secondScanCompleted: boolean; + totalItems: number; + completedItems: number; } -// ✅ 新增:Pick Order 数据接口 -interface PickOrderData { +// ✅ 新增:Lot 详情接口 +interface LotDetail { + lotId: number; + lotNo: string; + expiryDate: string; + location: string; + availableQty: number; + requiredQty: number; + actualPickQty: number; + processingStatus: string; + lotAvailability: string; pickOrderId: number; pickOrderCode: string; pickOrderConsoCode: string; - pickOrderStatus: string; - completedDate: string; - lots: any[]; + pickOrderLineId: number; + stockOutLineId: number; + stockOutLineStatus: string; + routerIndex: number; + routerArea: string; + routerRoute: string; + uomShortDesc: string; + secondQrScanStatus: string; + itemId: number; + itemCode: string; + itemName: string; + uomCode: string; + uomDesc: string; } const FInishedJobOrderRecord: React.FC = ({ filterArgs }) => { - const { t } = useTranslation("pickOrder"); + const { t } = useTranslation("jo"); const router = useRouter(); const { data: session } = useSession() as { data: SessionWithTokens | null }; const currentUserId = session?.id ? parseInt(session.id) : undefined; - // ✅ 新增:已完成 DO Pick Orders 状态 - const [completedDoPickOrders, setCompletedDoPickOrders] = useState([]); - const [completedDoPickOrdersLoading, setCompletedDoPickOrdersLoading] = useState(false); + // ✅ 修改:已完成 Job Order Pick Orders 状态 + const [completedJobOrderPickOrders, setCompletedJobOrderPickOrders] = useState([]); + const [completedJobOrderPickOrdersLoading, setCompletedJobOrderPickOrdersLoading] = useState(false); - // ✅ 新增:详情视图状态 - const [selectedDoPickOrder, setSelectedDoPickOrder] = useState(null); + // ✅ 修改:详情视图状态 + const [selectedJobOrderPickOrder, setSelectedJobOrderPickOrder] = useState(null); const [showDetailView, setShowDetailView] = useState(false); - const [detailLotData, setDetailLotData] = useState([]); + const [detailLotData, setDetailLotData] = useState([]); + const [detailLotDataLoading, setDetailLotDataLoading] = useState(false); - // ✅ 新增:搜索状态 + // ✅ 修改:搜索状态 const [searchQuery, setSearchQuery] = useState>({}); - const [filteredDoPickOrders, setFilteredDoPickOrders] = useState([]); + const [filteredJobOrderPickOrders, setFilteredJobOrderPickOrders] = useState([]); - // ✅ 新增:分页状态 + // ✅ 修改:分页状态 const [paginationController, setPaginationController] = useState({ pageNum: 0, pageSize: 10, @@ -128,58 +127,82 @@ const FInishedJobOrderRecord: React.FC = ({ filterArgs }) => { const formProps = useForm(); const errors = formProps.formState.errors; - // ✅ 修改:使用新的 API 获取已完成的 DO Pick Orders - const fetchCompletedDoPickOrdersData = useCallback(async (searchParams?: CompletedDoPickOrderSearchParams) => { + // ✅ 修改:使用新的 Job Order API 获取已完成的 Job Order Pick Orders + const fetchCompletedJobOrderPickOrdersData = useCallback(async () => { if (!currentUserId) return; - setCompletedDoPickOrdersLoading(true); + setCompletedJobOrderPickOrdersLoading(true); try { - console.log("🔍 Fetching completed DO pick orders with params:", searchParams); + console.log("🔍 Fetching completed Job Order pick orders..."); - const completedDoPickOrders = await fetchCompletedDoPickOrders(currentUserId, searchParams); + const completedJobOrderPickOrders = await fetchCompletedJobOrderPickOrdersWithCompletedSecondScan(currentUserId); - setCompletedDoPickOrders(completedDoPickOrders); - setFilteredDoPickOrders(completedDoPickOrders); - console.log("✅ Fetched completed DO pick orders:", completedDoPickOrders); + setCompletedJobOrderPickOrders(completedJobOrderPickOrders); + setFilteredJobOrderPickOrders(completedJobOrderPickOrders); + console.log("✅ Fetched completed Job Order pick orders:", completedJobOrderPickOrders); } catch (error) { - console.error("❌ Error fetching completed DO pick orders:", error); - setCompletedDoPickOrders([]); - setFilteredDoPickOrders([]); + console.error("❌ Error fetching completed Job Order pick orders:", error); + setCompletedJobOrderPickOrders([]); + setFilteredJobOrderPickOrders([]); } finally { - setCompletedDoPickOrdersLoading(false); + setCompletedJobOrderPickOrdersLoading(false); } }, [currentUserId]); - // ✅ 初始化时获取数据 + // ✅ 新增:获取 lot 详情数据 + const fetchLotDetailsData = useCallback(async (pickOrderId: number) => { + setDetailLotDataLoading(true); + try { + console.log("🔍 Fetching lot details for pick order:", pickOrderId); + + const lotDetails = await fetchCompletedJobOrderPickOrderLotDetails(pickOrderId); + + setDetailLotData(lotDetails); + console.log("✅ Fetched lot details:", lotDetails); + } catch (error) { + console.error("❌ Error fetching lot details:", error); + setDetailLotData([]); + } finally { + setDetailLotDataLoading(false); + } + }, []); + + // ✅ 修改:初始化时获取数据 useEffect(() => { if (currentUserId) { - fetchCompletedDoPickOrdersData(); + fetchCompletedJobOrderPickOrdersData(); } - }, [currentUserId, fetchCompletedDoPickOrdersData]); + }, [currentUserId, fetchCompletedJobOrderPickOrdersData]); - // ✅ 修改:搜索功能使用新的 API + // ✅ 修改:搜索功能 const handleSearch = useCallback((query: Record) => { setSearchQuery({ ...query }); console.log("Search query:", query); - const searchParams: CompletedDoPickOrderSearchParams = { - pickOrderCode: query.pickOrderCode || undefined, - shopName: query.shopName || undefined, - deliveryNo: query.deliveryNo || undefined, - //ticketNo: query.ticketNo || undefined, - }; - - // 使用新的 API 进行搜索 - fetchCompletedDoPickOrdersData(searchParams); - }, [fetchCompletedDoPickOrdersData]); + const filtered = completedJobOrderPickOrders.filter((pickOrder) => { + const pickOrderCodeMatch = !query.pickOrderCode || + pickOrder.pickOrderCode?.toLowerCase().includes((query.pickOrderCode || "").toLowerCase()); + + const jobOrderCodeMatch = !query.jobOrderCode || + pickOrder.jobOrderCode?.toLowerCase().includes((query.jobOrderCode || "").toLowerCase()); + + const jobOrderNameMatch = !query.jobOrderName || + pickOrder.jobOrderName?.toLowerCase().includes((query.jobOrderName || "").toLowerCase()); + + return pickOrderCodeMatch && jobOrderCodeMatch && jobOrderNameMatch; + }); + + setFilteredJobOrderPickOrders(filtered); + console.log("Filtered Job Order pick orders count:", filtered.length); + }, [completedJobOrderPickOrders]); - // ✅ 修复:重命名函数避免重复声明 + // ✅ 修改:重置搜索 const handleSearchReset = useCallback(() => { setSearchQuery({}); - fetchCompletedDoPickOrdersData(); // 重新获取所有数据 - }, [fetchCompletedDoPickOrdersData]); + setFilteredJobOrderPickOrders(completedJobOrderPickOrders); + }, [completedJobOrderPickOrders]); - // ✅ 分页功能 + // ✅ 修改:分页功能 const handlePageChange = useCallback((event: unknown, newPage: number) => { setPaginationController(prev => ({ ...prev, @@ -195,14 +218,14 @@ const FInishedJobOrderRecord: React.FC = ({ filterArgs }) => { }); }, []); - // ✅ 分页数据 + // ✅ 修改:分页数据 const paginatedData = useMemo(() => { const startIndex = paginationController.pageNum * paginationController.pageSize; const endIndex = startIndex + paginationController.pageSize; - return filteredDoPickOrders.slice(startIndex, endIndex); - }, [filteredDoPickOrders, paginationController]); + return filteredJobOrderPickOrders.slice(startIndex, endIndex); + }, [filteredJobOrderPickOrders, paginationController]); - // ✅ 搜索条件 + // ✅ 修改:搜索条件 const searchCriteria: Criterion[] = [ { label: t("Pick Order Code"), @@ -210,59 +233,42 @@ const FInishedJobOrderRecord: React.FC = ({ filterArgs }) => { type: "text", }, { - label: t("Shop Name"), - paramName: "shopName", + label: t("Job Order Code"), + paramName: "jobOrderCode", type: "text", }, { - label: t("Delivery No"), - paramName: "deliveryNo", + label: t("Job Order Item Name"), + paramName: "jobOrderName", type: "text", } ]; - const handleDetailClick = useCallback(async (doPickOrder: CompletedDoPickOrder) => { - setSelectedDoPickOrder(doPickOrder); + // ✅ 修改:详情点击处理 + const handleDetailClick = useCallback(async (jobOrderPickOrder: CompletedJobOrderPickOrder) => { + setSelectedJobOrderPickOrder(jobOrderPickOrder); 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); - - // ✅ 触发打印按钮状态更新 - 基于详情数据 - const allCompleted = lotDetails.length > 0 && lotDetails.every(lot => - lot.processingStatus === 'completed' - ); - - // ✅ 发送事件,包含标签页信息 - window.dispatchEvent(new CustomEvent('pickOrderCompletionStatus', { - detail: { - allLotsCompleted: allCompleted, - tabIndex: 2 // ✅ 明确指定这是来自标签页 2 的事件 - } - })); - - } catch (error) { - console.error("❌ Error loading detail lot data:", error); - setDetailLotData([]); - - // ✅ 如果加载失败,禁用打印按钮 - window.dispatchEvent(new CustomEvent('pickOrderCompletionStatus', { - detail: { - allLotsCompleted: false, - tabIndex: 2 - } - })); - } - }, []); - + // ✅ 获取 lot 详情数据 + await fetchLotDetailsData(jobOrderPickOrder.pickOrderId); + + // ✅ 触发打印按钮状态更新 - 基于详情数据 + const allCompleted = jobOrderPickOrder.secondScanCompleted; + + // ✅ 发送事件,包含标签页信息 + window.dispatchEvent(new CustomEvent('pickOrderCompletionStatus', { + detail: { + allLotsCompleted: allCompleted, + tabIndex: 2 // ✅ 明确指定这是来自标签页 2 的事件 + } + })); + + }, [fetchLotDetailsData]); - // ✅ 返回列表视图 + // ✅ 修改:返回列表视图 const handleBackToList = useCallback(() => { setShowDetailView(false); - setSelectedDoPickOrder(null); + setSelectedJobOrderPickOrder(null); setDetailLotData([]); // ✅ 返回列表时禁用打印按钮 @@ -274,9 +280,8 @@ const FInishedJobOrderRecord: React.FC = ({ filterArgs }) => { })); }, []); - - // ✅ 如果显示详情视图,渲染类似 GoodPickExecution 的表格 - if (showDetailView && selectedDoPickOrder) { + // ✅ 修改:如果显示详情视图,渲染 Job Order 详情和 Lot 信息 + if (showDetailView && selectedJobOrderPickOrder) { return ( @@ -286,64 +291,166 @@ const FInishedJobOrderRecord: React.FC = ({ filterArgs }) => { {t("Back to List")} - {t("Pick Order Details")}: {selectedDoPickOrder.pickOrderCode} + {t("Job Order Pick Order Details")}: {selectedJobOrderPickOrder.pickOrderCode} - {/* FG Pick Orders 信息 */} - - {selectedDoPickOrder.fgPickOrders.map((fgOrder, index) => ( - {}} // 只读模式 - /> - ))} - + {/* Job Order 信息卡片 */} + + + + + {t("Pick Order Code")}: {selectedJobOrderPickOrder.pickOrderCode} + + + {t("Job Order Code")}: {selectedJobOrderPickOrder.jobOrderCode} + + + {t("Job Order Item Name")}: {selectedJobOrderPickOrder.jobOrderName} + + + {t("Target Date")}: {selectedJobOrderPickOrder.pickOrderTargetDate} + + + + + + {t("Required Qty")}: {selectedJobOrderPickOrder.reqQty} {selectedJobOrderPickOrder.uom} + + + + + - {/* 类似 GoodPickExecution 的表格 */} - - - - - {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} - {lot.itemCode} - {lot.itemName} - {lot.lotNo} - {lot.location} - {lot.requiredQty} - {lot.actualPickQty} - - - - - ))} - -
-
+ {/* ✅ 修改:Lot 详情表格 - 添加复选框列 */} + + + + {t("Lot Details")} + + + {detailLotDataLoading ? ( + + + + ) : ( + + + + + {t("Index")} + {t("Route")} + {t("Item Code")} + {t("Item Name")} + {t("Lot No")} + {t("Location")} + {t("Required Qty")} + {t("Actual Pick Qty")} + {t("Processing Status")} + {t("Second Scan Status")} + + + + {detailLotData.length === 0 ? ( + + {/* ✅ 恢复原来的 colSpan */} + + {t("No lot details available")} + + + + ) : ( + detailLotData.map((lot, index) => ( + + + + {index + 1} + + + + + {lot.routerRoute || '-'} + + + {lot.itemCode} + {lot.itemName} + {lot.lotNo} + {lot.location} + + {lot.requiredQty?.toLocaleString() || 0} ({lot.uomShortDesc}) + + + {lot.actualPickQty?.toLocaleString() || 0} ({lot.uomShortDesc}) + + {/* ✅ 修改:Processing Status 使用复选框 */} + + + + + + {/* ✅ 修改:Second Scan Status 使用复选框 */} + + + + + + + )) + )} + +
+
+ )} +
+
); } - // ✅ 默认列表视图 + // ✅ 修改:默认列表视图 return ( @@ -357,7 +464,7 @@ const FInishedJobOrderRecord: React.FC = ({ filterArgs }) => { {/* 加载状态 */} - {completedDoPickOrdersLoading ? ( + {completedJobOrderPickOrdersLoading ? ( @@ -365,50 +472,59 @@ const FInishedJobOrderRecord: React.FC = ({ filterArgs }) => { {/* 结果统计 */} - {t("Total")}: {filteredDoPickOrders.length} {t("completed DO pick orders")} + {t("Total")}: {filteredJobOrderPickOrders.length} {t("completed Job Order pick orders with matching")} {/* 列表 */} - {filteredDoPickOrders.length === 0 ? ( + {filteredJobOrderPickOrders.length === 0 ? ( - {t("No completed DO pick orders found")} + {t("No completed Job Order pick orders with matching found")} ) : ( - {paginatedData.map((doPickOrder) => ( - + {paginatedData.map((jobOrderPickOrder) => ( + - {doPickOrder.pickOrderCode} + {jobOrderPickOrder.pickOrderCode} + + + {jobOrderPickOrder.jobOrderName} - {jobOrderPickOrder.jobOrderCode} - {doPickOrder.shopName} - {doPickOrder.deliveryNo} + {t("Completed")}: {new Date(jobOrderPickOrder.completedDate).toLocaleString()} - {t("Completed")}: {new Date(doPickOrder.completedDate).toLocaleString()} + {t("Target Date")}: {jobOrderPickOrder.pickOrderTargetDate} - {doPickOrder.fgPickOrders.length} {t("FG orders")} + {jobOrderPickOrder.completedItems}/{jobOrderPickOrder.totalItems} {t("items completed")} + @@ -419,10 +535,10 @@ const FInishedJobOrderRecord: React.FC = ({ filterArgs }) => { )} {/* 分页 */} - {filteredDoPickOrders.length > 0 && ( + {filteredJobOrderPickOrders.length > 0 && (