|
|
@@ -23,7 +23,7 @@ import { |
|
|
} from "@mui/material"; |
|
|
} from "@mui/material"; |
|
|
import ArrowBackIcon from '@mui/icons-material/ArrowBack'; |
|
|
import ArrowBackIcon from '@mui/icons-material/ArrowBack'; |
|
|
import { useTranslation } from "react-i18next"; |
|
|
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 ProductionProcessDetail from "./ProductionProcessDetail"; |
|
|
import { BomCombo } from "@/app/api/bom"; |
|
|
import { BomCombo } from "@/app/api/bom"; |
|
|
import { fetchBomCombo } from "@/app/api/bom/index"; |
|
|
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 { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; |
|
|
import { dayjsToDateString } from "@/app/utils/formatUtil"; |
|
|
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 { |
|
|
interface ProductProcessJobOrderDetailProps { |
|
|
jobOrderId: number; |
|
|
jobOrderId: number; |
|
|
@@ -73,7 +60,7 @@ const ProductionProcessJobOrderDetail: React.FC<ProductProcessJobOrderDetailProp |
|
|
const { t } = useTranslation(); |
|
|
const { t } = useTranslation(); |
|
|
const [loading, setLoading] = useState(false); |
|
|
const [loading, setLoading] = useState(false); |
|
|
const [processData, setProcessData] = useState<any>(null); |
|
|
const [processData, setProcessData] = useState<any>(null); |
|
|
const [jobOrderLines, setJobOrderLines] = useState<JobOrderLine[]>([]); |
|
|
|
|
|
|
|
|
const [jobOrderLines, setJobOrderLines] = useState<JobOrderLineInfo[]>([]); |
|
|
const [inventoryData, setInventoryData] = useState<InventoryResult[]>([]); |
|
|
const [inventoryData, setInventoryData] = useState<InventoryResult[]>([]); |
|
|
const [tabIndex, setTabIndex] = useState(0); |
|
|
const [tabIndex, setTabIndex] = useState(0); |
|
|
const [selectedProcessId, setSelectedProcessId] = useState<number | null>(null); |
|
|
const [selectedProcessId, setSelectedProcessId] = useState<number | null>(null); |
|
|
@@ -85,7 +72,7 @@ const ProductionProcessJobOrderDetail: React.FC<ProductProcessJobOrderDetailProp |
|
|
const [reqQtyMultiplier, setReqQtyMultiplier] = useState<number>(1); |
|
|
const [reqQtyMultiplier, setReqQtyMultiplier] = useState<number>(1); |
|
|
const [selectedBomForReqQty, setSelectedBomForReqQty] = useState<BomCombo | null>(null); |
|
|
const [selectedBomForReqQty, setSelectedBomForReqQty] = useState<BomCombo | null>(null); |
|
|
const [bomCombo, setBomCombo] = useState<BomCombo[]>([]); |
|
|
const [bomCombo, setBomCombo] = useState<BomCombo[]>([]); |
|
|
|
|
|
|
|
|
|
|
|
const [showBaseQty, setShowBaseQty] = useState<boolean>(false); |
|
|
|
|
|
|
|
|
const fetchData = useCallback(async () => { |
|
|
const fetchData = useCallback(async () => { |
|
|
setLoading(true); |
|
|
setLoading(true); |
|
|
@@ -102,7 +89,9 @@ const [bomCombo, setBomCombo] = useState<BomCombo[]>([]); |
|
|
setLoading(false); |
|
|
setLoading(false); |
|
|
} |
|
|
} |
|
|
}, [jobOrderId]); |
|
|
}, [jobOrderId]); |
|
|
|
|
|
|
|
|
|
|
|
const toggleBaseQty = useCallback(() => { |
|
|
|
|
|
setShowBaseQty(prev => !prev); |
|
|
|
|
|
}, []); |
|
|
|
|
|
|
|
|
// 4. 添加处理函数(约第 166 行后) |
|
|
// 4. 添加处理函数(约第 166 行后) |
|
|
const handleOpenReqQtyDialog = useCallback(async () => { |
|
|
const handleOpenReqQtyDialog = useCallback(async () => { |
|
|
@@ -181,7 +170,7 @@ const [bomCombo, setBomCombo] = useState<BomCombo[]>([]); |
|
|
fetchData(); |
|
|
fetchData(); |
|
|
}, [fetchData]); |
|
|
}, [fetchData]); |
|
|
// PickTable 组件内容 |
|
|
// PickTable 组件内容 |
|
|
const getStockAvailable = (line: JobOrderLine) => { |
|
|
|
|
|
|
|
|
const getStockAvailable = (line: JobOrderLineInfo) => { |
|
|
if (line.type?.toLowerCase() === "consumables" || line.type?.toLowerCase() === "nm") { |
|
|
if (line.type?.toLowerCase() === "consumables" || line.type?.toLowerCase() === "nm") { |
|
|
return null; |
|
|
return null; |
|
|
} |
|
|
} |
|
|
@@ -244,7 +233,7 @@ const handleConfirmPriority = async () => { |
|
|
await handleUpdateOperationPriority(processData.id, Number(operationPriority)); |
|
|
await handleUpdateOperationPriority(processData.id, Number(operationPriority)); |
|
|
setOpenOperationPriorityDialog(false); |
|
|
setOpenOperationPriorityDialog(false); |
|
|
}; |
|
|
}; |
|
|
const isStockSufficient = (line: JobOrderLine) => { |
|
|
|
|
|
|
|
|
const isStockSufficient = (line: JobOrderLineInfo) => { |
|
|
if (line.type?.toLowerCase() === "consumables") { |
|
|
if (line.type?.toLowerCase() === "consumables") { |
|
|
return false; |
|
|
return false; |
|
|
} |
|
|
} |
|
|
@@ -489,8 +478,8 @@ const handleRelease = useCallback(async ( jobOrderId: number) => { |
|
|
field: "itemName", |
|
|
field: "itemName", |
|
|
headerName: t("Item Name"), |
|
|
headerName: t("Item Name"), |
|
|
flex: 1, |
|
|
flex: 1, |
|
|
renderCell: (params: GridRenderCellParams<JobOrderLine>) => { |
|
|
|
|
|
return `${params.value} (${params.row.uom})`; |
|
|
|
|
|
|
|
|
renderCell: (params: GridRenderCellParams<JobOrderLineInfo>) => { |
|
|
|
|
|
return `${params.value} (${params.row.reqUom})`; |
|
|
}, |
|
|
}, |
|
|
}, |
|
|
}, |
|
|
{ |
|
|
{ |
|
|
@@ -499,12 +488,53 @@ const handleRelease = useCallback(async ( jobOrderId: number) => { |
|
|
flex: 0.7, |
|
|
flex: 0.7, |
|
|
align: "right", |
|
|
align: "right", |
|
|
headerAlign: "right", |
|
|
headerAlign: "right", |
|
|
renderCell: (params: GridRenderCellParams<JobOrderLine>) => { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
renderCell: (params: GridRenderCellParams<JobOrderLineInfo>) => { |
|
|
|
|
|
const qty = showBaseQty ? params.row.baseReqQty : params.value; |
|
|
|
|
|
const uom = showBaseQty ? params.row.reqBaseUom : params.row.reqUom; |
|
|
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
|
<Box |
|
|
|
|
|
onClick={toggleBaseQty} |
|
|
|
|
|
sx={{ |
|
|
|
|
|
cursor: "pointer", |
|
|
|
|
|
userSelect: "none", |
|
|
|
|
|
"&:hover": { |
|
|
|
|
|
textDecoration: "underline", |
|
|
|
|
|
}, |
|
|
|
|
|
}} |
|
|
|
|
|
> |
|
|
|
|
|
{decimalFormatter.format(qty || 0)} ({uom || ""}) |
|
|
|
|
|
</Box> |
|
|
|
|
|
); |
|
|
|
|
|
}, |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
field: "stockReqQty", |
|
|
|
|
|
headerName: t("Stock Req. Qty"), |
|
|
|
|
|
flex: 0.7, |
|
|
|
|
|
align: "right", |
|
|
|
|
|
headerAlign: "right", |
|
|
|
|
|
renderCell: (params: GridRenderCellParams<JobOrderLineInfo>) => { |
|
|
|
|
|
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 ( |
|
|
|
|
|
<Box |
|
|
|
|
|
onClick={toggleBaseQty} |
|
|
|
|
|
sx={{ |
|
|
|
|
|
cursor: "pointer", |
|
|
|
|
|
userSelect: "none", |
|
|
|
|
|
"&:hover": { |
|
|
|
|
|
textDecoration: "underline", |
|
|
|
|
|
}, |
|
|
|
|
|
}} |
|
|
|
|
|
> |
|
|
|
|
|
{decimalFormatter.format(qty || 0)} ({uom || ""}) |
|
|
|
|
|
</Box> |
|
|
|
|
|
); |
|
|
}, |
|
|
}, |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
{ |
|
|
{ |
|
|
field: "stockAvailable", |
|
|
field: "stockAvailable", |
|
|
headerName: t("Stock Available"), |
|
|
headerName: t("Stock Available"), |
|
|
@@ -512,12 +542,25 @@ const handleRelease = useCallback(async ( jobOrderId: number) => { |
|
|
align: "right", |
|
|
align: "right", |
|
|
headerAlign: "right", |
|
|
headerAlign: "right", |
|
|
type: "number", |
|
|
type: "number", |
|
|
renderCell: (params: GridRenderCellParams<JobOrderLine>) => { |
|
|
|
|
|
// 如果是 consumables,显示 N/A |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
renderCell: (params: GridRenderCellParams<JobOrderLineInfo>) => { |
|
|
const stockAvailable = getStockAvailable(params.row); |
|
|
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 ( |
|
|
|
|
|
<Box |
|
|
|
|
|
onClick={toggleBaseQty} |
|
|
|
|
|
sx={{ |
|
|
|
|
|
cursor: "pointer", |
|
|
|
|
|
userSelect: "none", |
|
|
|
|
|
"&:hover": { |
|
|
|
|
|
textDecoration: "underline", |
|
|
|
|
|
}, |
|
|
|
|
|
}} |
|
|
|
|
|
> |
|
|
|
|
|
{decimalFormatter.format(qty || 0)} ({uom || ""}) |
|
|
|
|
|
</Box> |
|
|
|
|
|
); |
|
|
}, |
|
|
}, |
|
|
}, |
|
|
}, |
|
|
{ |
|
|
{ |
|
|
@@ -545,7 +588,7 @@ const handleRelease = useCallback(async ( jobOrderId: number) => { |
|
|
align: "center", |
|
|
align: "center", |
|
|
headerAlign: "center", |
|
|
headerAlign: "center", |
|
|
type: "boolean", |
|
|
type: "boolean", |
|
|
renderCell: (params: GridRenderCellParams<JobOrderLine>) => { |
|
|
|
|
|
|
|
|
renderCell: (params: GridRenderCellParams<JobOrderLineInfo>) => { |
|
|
|
|
|
|
|
|
return isStockSufficient(params.row) |
|
|
return isStockSufficient(params.row) |
|
|
? <CheckCircleOutlineOutlinedIcon fontSize={"large"} color="success" /> |
|
|
? <CheckCircleOutlineOutlinedIcon fontSize={"large"} color="success" /> |
|
|
|