"use client"; import React, { useCallback, useEffect, useState, useMemo } from "react"; import { Box, Button, Paper, Stack, Typography, TextField, Grid, Card, CardContent, CircularProgress, Tabs, Tab, TabsProps, } from "@mui/material"; import ArrowBackIcon from '@mui/icons-material/ArrowBack'; import { useTranslation } from "react-i18next"; import { fetchProductProcessesByJobOrderId ,deleteJobOrder} from "@/app/api/jo/actions"; import ProductionProcessDetail from "./ProductionProcessDetail"; import dayjs from "dayjs"; import { OUTPUT_DATE_FORMAT, integerFormatter, arrayToDateString } from "@/app/utils/formatUtil"; import StyledDataGrid from "../StyledDataGrid/StyledDataGrid"; import { GridColDef, GridRenderCellParams } from "@mui/x-data-grid"; import { decimalFormatter } from "@/app/utils/formatUtil"; import CheckCircleOutlineOutlinedIcon from '@mui/icons-material/CheckCircleOutlineOutlined'; import DoDisturbAltRoundedIcon from '@mui/icons-material/DoDisturbAltRounded'; import { fetchInventories } from "@/app/api/inventory/actions"; import { InventoryResult } from "@/app/api/inventory"; import { releaseJo, startJo } from "@/app/api/jo/actions"; import JobPickExecutionsecondscan from "../Jodetail/JobPickExecutionsecondscan"; import ProcessSummaryHeader from "./ProcessSummaryHeader"; interface JobOrderLine { id: number; jobOrderId: number; jobOrderCode: string; itemId: number; itemCode: string; itemName: string; reqQty: number; stockQty: number; uom: string; shortUom: string; availableStatus: string; type: string; } interface ProductProcessJobOrderDetailProps { jobOrderId: number; onBack: () => void; fromJosave?: boolean; } const ProductionProcessJobOrderDetail: React.FC = ({ jobOrderId, onBack, fromJosave, }) => { const { t } = useTranslation(); const [loading, setLoading] = useState(false); const [processData, setProcessData] = useState(null); const [jobOrderLines, setJobOrderLines] = useState([]); const [inventoryData, setInventoryData] = useState([]); const [tabIndex, setTabIndex] = useState(0); const [selectedProcessId, setSelectedProcessId] = useState(null); // 获取数据 const fetchData = useCallback(async () => { setLoading(true); try { const data = await fetchProductProcessesByJobOrderId(jobOrderId); if (data && data.length > 0) { const firstProcess = data[0]; setProcessData(firstProcess); setJobOrderLines((firstProcess as any).jobOrderLines || []); } } catch (error) { console.error("Error loading data:", error); } finally { setLoading(false); } }, [jobOrderId]); // 获取库存数据 useEffect(() => { const fetchInventoryData = async () => { try { const inventoryResponse = await fetchInventories({ code: "", name: "", type: "", pageNum: 0, pageSize: 1000 }); setInventoryData(inventoryResponse.records); } catch (error) { console.error("Error fetching inventory data:", error); } }; fetchInventoryData(); }, []); useEffect(() => { fetchData(); }, [fetchData]); // PickTable 组件内容 const getStockAvailable = (line: JobOrderLine) => { if (line.type?.toLowerCase() === "consumables") { return null; } const inventory = inventoryData.find(inv => inv.itemCode === line.itemCode || inv.itemName === line.itemName ); if (inventory) { return inventory.availableQty || (inventory.onHandQty - inventory.onHoldQty - inventory.unavailableQty); } return line.stockQty || 0; }; const isStockSufficient = (line: JobOrderLine) => { if (line.type?.toLowerCase() === "consumables") { return false; } const stockAvailable = getStockAvailable(line); if (stockAvailable === null) { return false; } return stockAvailable >= line.reqQty; }; const stockCounts = useMemo(() => { // 过滤掉 consumables 类型的 lines const nonConsumablesLines = jobOrderLines.filter( line => line.type?.toLowerCase() !== "consumables" && line.type?.toLowerCase() !== "cmb" ); const total = nonConsumablesLines.length; const sufficient = nonConsumablesLines.filter(isStockSufficient).length; return { total, sufficient, insufficient: total - sufficient, }; }, [jobOrderLines, inventoryData]); const status = processData?.status?.toLowerCase?.() ?? ""; const handleDeleteJobOrder = useCallback(async ( jobOrderId: number) => { const response = await deleteJobOrder(jobOrderId) if (response) { //setProcessData(response.entity); await fetchData(); } }, [jobOrderId]); const handleRelease = useCallback(async ( jobOrderId: number) => { // TODO: 替换为实际的 release 调用 console.log("Release clicked for jobOrderId:", jobOrderId); const response = await releaseJo({ id: jobOrderId }) if (response) { //setProcessData(response.entity); await fetchData(); } }, [jobOrderId]); const handleTabChange = useCallback>( (_e, newValue) => { setTabIndex(newValue); }, [], ); // 如果选择了 process detail,显示 detail 页面 if (selectedProcessId !== null) { return ( { setSelectedProcessId(null); fetchData(); // 刷新数据 }} /> ); } if (loading) { return ( ); } if (!processData) { return ( {t("No data found")} ); } // InfoCard 组件内容 const InfoCardContent = () => ( ); const productionProcessesLineRemarkTableColumns: GridColDef[] = [ { field: "seqNo", headerName: t("Seq"), flex: 0.2, align: "left", headerAlign: "center", type: "number", renderCell: (params) => { return {params.value}; }, }, { field: "description", headerName: t("Remark"), flex: 1, align: "left", headerAlign: "center", renderCell: (params) => { return {params.value || ""}; }, }, ]; const productionProcessesLineRemarkTableRows = processData?.productProcessLines?.map((line: any) => ({ id: line.seqNo, seqNo: line.seqNo, description: line.description ?? "", })) ?? []; const pickTableColumns: GridColDef[] = [ { field: "id", headerName: t("id"), flex: 0.2, align: "left", headerAlign: "left", type: "number", }, { field: "itemCode", headerName: t("Item Code"), flex: 0.6, }, { field: "itemName", headerName: t("Item Name"), flex: 1, renderCell: (params: GridRenderCellParams) => { return `${params.value} (${params.row.uom})`; }, }, { field: "reqQty", headerName: t("Req. Qty"), flex: 0.7, align: "right", headerAlign: "right", renderCell: (params: GridRenderCellParams) => { if (params.row.type?.toLowerCase() === "consumables"|| params.row.type?.toLowerCase() === "cmb") { return t("N/A"); } return `${decimalFormatter.format(params.value)} (${params.row.shortUom})`; }, }, { field: "stockAvailable", headerName: t("Stock Available"), flex: 0.7, align: "right", headerAlign: "right", type: "number", renderCell: (params: GridRenderCellParams) => { // 如果是 consumables,显示 N/A if (params.row.type?.toLowerCase() === "consumables"|| params.row.type?.toLowerCase() === "cmb") { return t("N/A"); } const stockAvailable = getStockAvailable(params.row); if (stockAvailable === null) { return t("N/A"); } return `${decimalFormatter.format(stockAvailable)} (${params.row.shortUom})`; }, }, { field: "bomProcessSeqNo", headerName: t("Seq No"), flex: 0.5, align: "right", headerAlign: "right", type: "number", }, /* { field: "seqNoRemark", headerName: t("Seq No Remark"), flex: 1, align: "left", headerAlign: "left", type: "string", }, */ { field: "stockStatus", headerName: t("Stock Status"), flex: 0.5, align: "center", headerAlign: "center", type: "boolean", renderCell: (params: GridRenderCellParams) => { if (params.row.type?.toLowerCase() === "consumables"|| params.row.type?.toLowerCase() === "cmb") { return {t("N/A")}; } return isStockSufficient(params.row) ? : ; }, }, ]; const pickTableRows = jobOrderLines.map((line, index) => ({ ...line, //id: line.id || index, id: index + 1, })); const PickTableContent = () => ( {t("Total lines: ")}{stockCounts.total} {t("Lines with sufficient stock: ")}{stockCounts.sufficient} {t("Lines with insufficient stock: ")}{stockCounts.insufficient} {fromJosave && ( )} {fromJosave && ( )} "auto"} /> ); const ProductionProcessesLineRemarkTableContent = () => ( 'auto'} /> ); return ( {/* 返回按钮 */} {/* 标签页 */} {!fromJosave && ( )} {/* 标签页内容 */} {tabIndex === 0 && } {tabIndex === 1 && } {tabIndex === 2 && ( { // 切换回第一个标签页,或者什么都不做 setTabIndex(0); }} fromJosave={fromJosave} /> )} {tabIndex === 3 && } {tabIndex === 4 && } ); }; export default ProductionProcessJobOrderDetail;