diff --git a/src/app/api/jo/actions.ts b/src/app/api/jo/actions.ts index 4d268d5..6572efc 100644 --- a/src/app/api/jo/actions.ts +++ b/src/app/api/jo/actions.ts @@ -246,6 +246,7 @@ export interface ProductProcessLineResponse { postProdTimeInMinutes: number, startTime: string, endTime: string, + isOringinal: boolean, } export interface ProductProcessWithLinesResponse { @@ -454,18 +455,29 @@ export interface JobOrderProcessLineDetailResponse { } export interface JobOrderLineInfo { id: number, - jobOrderId: number, - jobOrderCode: string, itemId: number, itemCode: string, itemName: string, + type: string, + reqQty: number, + baseReqQty: number, + + stockReqQty: number, + stockQty: number, - uom: string, - shortUom: string, + baseStockQty: number, + + reqUom: string, + reqBaseUom: string, + + stockUom: string, + stockBaseUom: string, + availableStatus: string, bomProcessId: number, bomProcessSeqNo: number, + isOringinal: boolean } export interface ProductProcessLineInfoResponse { @@ -1208,6 +1220,7 @@ export interface JobProcessStatusResponse { jobOrderCode: string; itemCode: string; itemName: string; + status: string; processingTime: number | null; setupTime: number | null; changeoverTime: number | null; @@ -1225,5 +1238,13 @@ export const fetchJobProcessStatus = cache(async () => { } ); }); - +export const deleteProductProcessLine = async (lineId: number) => { + return serverFetchJson( + `${BASE_API_URL}/product-process/Demo/ProcessLine/delete/${lineId}`, + { + method: "POST", + headers: { "Content-Type": "application/json" }, + } + ); +}; ; \ No newline at end of file diff --git a/src/app/api/stockIn/index.ts b/src/app/api/stockIn/index.ts index c1655d5..e1a1982 100644 --- a/src/app/api/stockIn/index.ts +++ b/src/app/api/stockIn/index.ts @@ -124,6 +124,8 @@ export interface StockInLine { lotNo?: string; poCode?: string; uom?: Uom; + joCode?: string; + warehouseCode?: string; defaultWarehouseId: number; // id for now dnNo?: string; dnDate?: number[]; diff --git a/src/components/ProductionProcess/JobProcessStatus.tsx b/src/components/ProductionProcess/JobProcessStatus.tsx index e0fc43b..7f2e219 100644 --- a/src/components/ProductionProcess/JobProcessStatus.tsx +++ b/src/components/ProductionProcess/JobProcessStatus.tsx @@ -263,7 +263,7 @@ const JobProcessStatus: React.FC = () => { - {calculateRemainingTime(row.planEndTime, row.processingTime, row.setupTime, row.changeoverTime)} + {row.status === 'pending' ? '-' : calculateRemainingTime(row.planEndTime, row.processingTime, row.setupTime, row.changeoverTime)} {row.processes.map((process, index) => { const isLastProcess = index === row.processes.length - 1 || diff --git a/src/components/ProductionProcess/ProductionProcessDetail.tsx b/src/components/ProductionProcess/ProductionProcessDetail.tsx index 345040d..6080e52 100644 --- a/src/components/ProductionProcess/ProductionProcessDetail.tsx +++ b/src/components/ProductionProcess/ProductionProcessDetail.tsx @@ -2,6 +2,7 @@ import React, { useCallback, useEffect, useState, useRef } from "react"; import EditIcon from "@mui/icons-material/Edit"; import AddIcon from '@mui/icons-material/Add'; +import DeleteIcon from '@mui/icons-material/Delete'; import Fab from '@mui/material/Fab'; import { Box, @@ -50,6 +51,7 @@ import { newProductProcessLine, updateProductProcessLineProcessingTimeSetupTimeChangeoverTime, UpdateProductProcessLineProcessingTimeSetupTimeChangeoverTimeRequest, + deleteProductProcessLine, } from "@/app/api/jo/actions"; import { updateProductProcessLineStatus } from "@/app/api/jo/actions"; @@ -265,7 +267,19 @@ const fetchProcessDetailRef = useRef<() => Promise>(); alert(t("Failed to create new line. Please try again.")); } }, [fetchProcessDetail, t]); -// 提交产出数据 + const handleDeleteLine = useCallback(async (lineId: number) => { + if (!confirm(t("Are you sure you want to delete this process?"))) { + return; + } + try { + await deleteProductProcessLine(lineId); + // 刷新数据 + await fetchProcessDetail(); + } catch (error) { + console.error("Error deleting line:", error); + alert(t("Failed to delete line. Please try again.")); + } + }, [fetchProcessDetail, t]); const processQrCode = useCallback((qrValue: string, lineId: number) => { // 设备快捷格式:{2fitesteXXX} - XXX 直接作为设备代码 // 格式:{2fitesteXXX} = equipmentCode: "XXX" @@ -614,7 +628,7 @@ const processQrCode = useCallback((qrValue: string, lineId: number) => { const status = (line as any).status || ''; const statusLower = status.toLowerCase(); const equipmentName = line.equipment_name || "-"; - + const isPlanning = processData?.jobOrderStatus === "planning"; const isCompleted = statusLower === 'completed'; const isInProgress = statusLower === 'inprogress' || statusLower === 'in progress'; const isPaused = statusLower === 'paused'; @@ -624,6 +638,7 @@ const processQrCode = useCallback((qrValue: string, lineId: number) => { return ( + {isPlanning && ( { > + )} + {isPlanning && line.isOringinal !== true && ( + handleDeleteLine(line.id)} + sx={{ padding: 0.5 }} + > + + + )} diff --git a/src/components/ProductionProcess/ProductionProcessJobOrderDetail.tsx b/src/components/ProductionProcess/ProductionProcessJobOrderDetail.tsx index f7b2b5d..9ed33f6 100644 --- a/src/components/ProductionProcess/ProductionProcessJobOrderDetail.tsx +++ b/src/components/ProductionProcess/ProductionProcessJobOrderDetail.tsx @@ -23,7 +23,7 @@ import { } from "@mui/material"; import ArrowBackIcon from '@mui/icons-material/ArrowBack'; import { useTranslation } from "react-i18next"; -import { fetchProductProcessesByJobOrderId ,deleteJobOrder, updateProductProcessPriority, updateJoPlanStart,updateJoReqQty,newProductProcessLine} from "@/app/api/jo/actions"; +import { fetchProductProcessesByJobOrderId ,deleteJobOrder, updateProductProcessPriority, updateJoPlanStart,updateJoReqQty,newProductProcessLine,JobOrderLineInfo} from "@/app/api/jo/actions"; import ProductionProcessDetail from "./ProductionProcessDetail"; import { BomCombo } from "@/app/api/bom"; import { fetchBomCombo } from "@/app/api/bom/index"; @@ -44,20 +44,7 @@ import { DatePicker, LocalizationProvider } from "@mui/x-date-pickers"; import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; import { dayjsToDateString } from "@/app/utils/formatUtil"; -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; @@ -73,7 +60,7 @@ const ProductionProcessJobOrderDetail: React.FC(null); - const [jobOrderLines, setJobOrderLines] = useState([]); + const [jobOrderLines, setJobOrderLines] = useState([]); const [inventoryData, setInventoryData] = useState([]); const [tabIndex, setTabIndex] = useState(0); const [selectedProcessId, setSelectedProcessId] = useState(null); @@ -85,7 +72,7 @@ const ProductionProcessJobOrderDetail: React.FC(1); const [selectedBomForReqQty, setSelectedBomForReqQty] = useState(null); const [bomCombo, setBomCombo] = useState([]); - +const [showBaseQty, setShowBaseQty] = useState(false); const fetchData = useCallback(async () => { setLoading(true); @@ -102,7 +89,9 @@ const [bomCombo, setBomCombo] = useState([]); setLoading(false); } }, [jobOrderId]); - + const toggleBaseQty = useCallback(() => { + setShowBaseQty(prev => !prev); + }, []); // 4. 添加处理函数(约第 166 行后) const handleOpenReqQtyDialog = useCallback(async () => { @@ -181,7 +170,7 @@ const [bomCombo, setBomCombo] = useState([]); fetchData(); }, [fetchData]); // PickTable 组件内容 -const getStockAvailable = (line: JobOrderLine) => { +const getStockAvailable = (line: JobOrderLineInfo) => { if (line.type?.toLowerCase() === "consumables" || line.type?.toLowerCase() === "nm") { return null; } @@ -244,7 +233,7 @@ const handleConfirmPriority = async () => { await handleUpdateOperationPriority(processData.id, Number(operationPriority)); setOpenOperationPriorityDialog(false); }; -const isStockSufficient = (line: JobOrderLine) => { +const isStockSufficient = (line: JobOrderLineInfo) => { if (line.type?.toLowerCase() === "consumables") { return false; } @@ -489,8 +478,8 @@ const handleRelease = useCallback(async ( jobOrderId: number) => { field: "itemName", headerName: t("Item Name"), flex: 1, - renderCell: (params: GridRenderCellParams) => { - return `${params.value} (${params.row.uom})`; + renderCell: (params: GridRenderCellParams) => { + return `${params.value} (${params.row.reqUom})`; }, }, { @@ -499,12 +488,53 @@ const handleRelease = useCallback(async ( jobOrderId: number) => { flex: 0.7, align: "right", headerAlign: "right", - renderCell: (params: GridRenderCellParams) => { - + renderCell: (params: GridRenderCellParams) => { + const qty = showBaseQty ? params.row.baseReqQty : params.value; + const uom = showBaseQty ? params.row.reqBaseUom : params.row.reqUom; + + return ( + + {decimalFormatter.format(qty || 0)} ({uom || ""}) + + ); + }, + }, + { + field: "stockReqQty", + headerName: t("Stock Req. Qty"), + flex: 0.7, + align: "right", + headerAlign: "right", + renderCell: (params: GridRenderCellParams) => { + const qty = showBaseQty ? params.row.baseReqQty : params.value; + const uom = showBaseQty ? params.row.reqBaseUom : params.row.stockUom; - return `${decimalFormatter.format(params.value)} (${params.row.shortUom})`; + return ( + + {decimalFormatter.format(qty || 0)} ({uom || ""}) + + ); }, }, + { field: "stockAvailable", headerName: t("Stock Available"), @@ -512,12 +542,25 @@ const handleRelease = useCallback(async ( jobOrderId: number) => { align: "right", headerAlign: "right", type: "number", - renderCell: (params: GridRenderCellParams) => { - // 如果是 consumables,显示 N/A - + renderCell: (params: GridRenderCellParams) => { const stockAvailable = getStockAvailable(params.row); - - return `${decimalFormatter.format(stockAvailable || 0)} (${params.row.shortUom})`; + const qty = showBaseQty ? params.row.baseStockQty : (stockAvailable || 0); + const uom = showBaseQty ? params.row.stockBaseUom : params.row.stockUom; + + return ( + + {decimalFormatter.format(qty || 0)} ({uom || ""}) + + ); }, }, { @@ -545,7 +588,7 @@ const handleRelease = useCallback(async ( jobOrderId: number) => { align: "center", headerAlign: "center", type: "boolean", - renderCell: (params: GridRenderCellParams) => { + renderCell: (params: GridRenderCellParams) => { return isStockSufficient(params.row) ? diff --git a/src/components/Qc/QcStockInModal.tsx b/src/components/Qc/QcStockInModal.tsx index 035325b..13d834c 100644 --- a/src/components/Qc/QcStockInModal.tsx +++ b/src/components/Qc/QcStockInModal.tsx @@ -172,7 +172,7 @@ const QcStockInModal: React.FC = ({ expiryDate: d.expiryDate ? (Array.isArray(d.expiryDate) ? arrayToDateString(d.expiryDate, "input") : d.expiryDate) : undefined, receiptDate: d.receiptDate ? arrayToDateString(d.receiptDate, "input") : dayjs().add(0, "month").format(INPUT_DATE_FORMAT), - acceptQty: d.status != StockInStatus.REJECTED ? (d.demandQty?? d.acceptedQty) : 0, + acceptQty: d.status != StockInStatus.REJECTED ? (d.acceptedQty ?? d.receivedQty ?? d.demandQty) : 0, // escResult: (d.escResult && d.escResult?.length > 0) ? d.escResult : [], // qcResult: (d.qcResult && d.qcResult?.length > 0) ? d.qcResult : [],//[...dummyQCData], warehouseId: d.defaultWarehouseId ?? 1,