From 1dfdc3b0611211feba0570ec6d8cfad078c37135 Mon Sep 17 00:00:00 2001
From: xiaoyi <908147524@qq.com>
Date: 星期三, 26 八月 2026 15:13:28 +0800
Subject: [PATCH] feat 功能修正

---
 yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/PurchasePlanApi.java                               |   12 ++
 yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/statistics/ErpPurchaseStatisticsController.java    |    2 
 yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/ErpPurchaseOrderApiImpl.java                           |    4 
 yudao-module-srm/src/main/java/cn/iocoder/yudao/module/srm/service/tender/SrmTenderProjectServiceImpl.java                     |    8 
 yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/controller/admin/decision/DecisionForecastController.java             |    8 +
 yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseInvoiceController.java         |    2 
 yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpSupplierController.java                |    2 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/product/MesPdProductServiceImpl.java                     |    7 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderServiceImpl.java               |   14 ++
 yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/dal/mysql/decision/BiForecastDataMapper.java                          |   10 ++
 yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchasePlanController.java            |    2 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductServiceImpl.java                |   25 ++++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/mps/MesProMpsServiceImpl.java                           |   26 ++++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/arrivalnotice/MesWmArrivalNoticeController.java |    6 
 yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java                |    1 
 yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/controller/admin/decision/vo/DecisionForecastHistorySaveReqVO.java    |   50 ++++++++++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeServiceImpl.java         |    4 
 yudao-module-srm/src/main/java/cn/iocoder/yudao/module/srm/service/tender/SrmBidAwardServiceImpl.java                          |    8 
 yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/enums/ErrorCodeConstants.java                                         |    1 
 yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/ErpPurchasePlanApiImpl.java                            |    2 
 yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseRequestController.java         |    2 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/itemreceipt/MesWmItemReceiptServiceImpl.java             |    4 
 yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/PurchaseOrderApi.java                              |   12 ++
 yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/BiForecastService.java                               |    3 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java                                       |    3 
 yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseOrderController.java           |    2 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/maintenanceplan/MesProMaintenancePlanServiceImpl.java   |    6 
 yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/BiForecastServiceImpl.java                           |   50 ++++++++-
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderBomController.java    |    4 
 29 files changed, 236 insertions(+), 44 deletions(-)

diff --git a/yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/controller/admin/decision/DecisionForecastController.java b/yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/controller/admin/decision/DecisionForecastController.java
index 3b45950..2f003bb 100644
--- a/yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/controller/admin/decision/DecisionForecastController.java
+++ b/yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/controller/admin/decision/DecisionForecastController.java
@@ -2,6 +2,7 @@
 
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.bi.controller.admin.decision.vo.DecisionForecastHistorySaveReqVO;
 import cn.iocoder.yudao.module.bi.controller.admin.decision.vo.DecisionForecastReqVO;
 import cn.iocoder.yudao.module.bi.controller.admin.decision.vo.DecisionForecastRespVO;
 import cn.iocoder.yudao.module.bi.controller.admin.decision.vo.DecisionForecastWorkOrderCreateReqVO;
@@ -39,6 +40,13 @@
         return success(forecastService.getForecastList(reqVO.getForecastCode(), beginTime, endTime));
     }
 
+    @PostMapping("/history")
+    @Operation(summary = "鏂板鍘嗗彶棰勬祴鏁版嵁")
+    @PreAuthorize("@ss.hasPermission('bi:decision:kpi:update')")
+    public CommonResult<Long> createForecastHistory(@Valid @RequestBody DecisionForecastHistorySaveReqVO reqVO) {
+        return success(forecastService.createForecastHistory(reqVO));
+    }
+
     @PostMapping("/generate")
     @Operation(summary = "鐢熸垚棰勬祴锛堝崰浣嶏級")
     @PreAuthorize("@ss.hasPermission('bi:decision:kpi:query')")
diff --git a/yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/controller/admin/decision/vo/DecisionForecastHistorySaveReqVO.java b/yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/controller/admin/decision/vo/DecisionForecastHistorySaveReqVO.java
new file mode 100644
index 0000000..2515f5d
--- /dev/null
+++ b/yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/controller/admin/decision/vo/DecisionForecastHistorySaveReqVO.java
@@ -0,0 +1,50 @@
+package cn.iocoder.yudao.module.bi.controller.admin.decision.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.DecimalMin;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Schema(description = "绠$悊鍚庡彴 - 璐熻嵎棰勬祴鍘嗗彶鏁版嵁鏂板 Request VO")
+@Data
+public class DecisionForecastHistorySaveReqVO {
+
+    @Schema(description = "棰勬祴缂栫爜", requiredMode = Schema.RequiredMode.REQUIRED)
+    @Size(max = 64)
+    @jakarta.validation.constraints.NotBlank
+    private String forecastCode;
+
+    @Schema(description = "棰勬祴鍚嶇О", requiredMode = Schema.RequiredMode.REQUIRED)
+    @Size(max = 128)
+    @jakarta.validation.constraints.NotBlank
+    private String forecastName;
+
+    @Schema(description = "棰勬祴鏃堕棿鐐�", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotNull
+    private LocalDateTime pointTime;
+
+    @Schema(description = "棰勬祴鍊�", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotNull
+    @DecimalMin(value = "0", inclusive = true)
+    private BigDecimal forecastValue;
+
+    @Schema(description = "瀹為檯鍊�")
+    @DecimalMin(value = "0", inclusive = true)
+    private BigDecimal actualValue;
+
+    @Schema(description = "缁村害")
+    @Size(max = 32)
+    private String dimension;
+
+    @Schema(description = "缁村害鍊�")
+    @Size(max = 128)
+    private String dimensionValue;
+
+    @Schema(description = "澶囨敞")
+    @Size(max = 500)
+    private String remark;
+}
diff --git a/yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/dal/mysql/decision/BiForecastDataMapper.java b/yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/dal/mysql/decision/BiForecastDataMapper.java
index efd1fa8..b71692d 100644
--- a/yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/dal/mysql/decision/BiForecastDataMapper.java
+++ b/yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/dal/mysql/decision/BiForecastDataMapper.java
@@ -19,4 +19,14 @@
                 .between(BiForecastDataDO::getPointTime, beginTime, endTime)
                 .orderByAsc(BiForecastDataDO::getPointTime));
     }
+
+    /**
+     * 鏌ヨ鎸囧畾棰勬祴缂栫爜涓嬫渶杩� n 鏉¢娴嬫暟鎹紙鎸夋椂闂村�掑簭锛岀敤浜庣Щ鍔ㄥ钩鍧囷級
+     */
+    default List<BiForecastDataDO> selectRecentByCode(String forecastCode, int limit) {
+        return selectList(new LambdaQueryWrapper<BiForecastDataDO>()
+                .eq(BiForecastDataDO::getForecastCode, forecastCode)
+                .orderByDesc(BiForecastDataDO::getPointTime)
+                .last("LIMIT " + limit));
+    }
 }
\ No newline at end of file
diff --git a/yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/enums/ErrorCodeConstants.java b/yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/enums/ErrorCodeConstants.java
index fae7fbc..87631ac 100644
--- a/yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/enums/ErrorCodeConstants.java
+++ b/yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/enums/ErrorCodeConstants.java
@@ -39,5 +39,6 @@
 
     // ========== 鍐崇瓥鏀寔 - 璐熻嵎棰勬祴 1_080_221 ~ 1_080_240 ==========
     ErrorCode BI_FORECAST_NOT_EXISTS = new ErrorCode(1_080_221, "璐熻嵎棰勬祴鏁版嵁涓嶅瓨鍦�");
+    ErrorCode BI_FORECAST_NO_HISTORY = new ErrorCode(1_080_222, "璇ラ娴嬬紪鐮佷笅鏆傛棤鍘嗗彶鏁版嵁锛屾棤娉曠敓鎴愰娴�");
 
 }
diff --git a/yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/BiForecastService.java b/yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/BiForecastService.java
index de4abcf..3026616 100644
--- a/yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/BiForecastService.java
+++ b/yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/BiForecastService.java
@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.module.bi.service.decision;
 
+import cn.iocoder.yudao.module.bi.controller.admin.decision.vo.DecisionForecastHistorySaveReqVO;
 import cn.iocoder.yudao.module.bi.controller.admin.decision.vo.DecisionForecastWorkOrderCreateReqVO;
 import cn.iocoder.yudao.module.bi.dal.dataobject.decision.BiForecastDataDO;
 
@@ -17,6 +18,8 @@
 
     void generateForecast(String forecastCode, LocalDateTime pointTime);
 
+    Long createForecastHistory(DecisionForecastHistorySaveReqVO reqVO);
+
     /**
      * 璐熻嵎棰勬祴鑱斿姩鍒涘缓鐢熶骇宸ュ崟锛堣繍缁�/鍙戠數/妫�淇被锛�
      *
diff --git a/yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/BiForecastServiceImpl.java b/yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/BiForecastServiceImpl.java
index 6f183d3..478da23 100644
--- a/yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/BiForecastServiceImpl.java
+++ b/yudao-module-bi/src/main/java/cn/iocoder/yudao/module/bi/service/decision/BiForecastServiceImpl.java
@@ -1,6 +1,8 @@
 package cn.iocoder.yudao.module.bi.service.decision;
 
+import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.date.DateUtil;
+import cn.iocoder.yudao.module.bi.controller.admin.decision.vo.DecisionForecastHistorySaveReqVO;
 import cn.iocoder.yudao.module.bi.controller.admin.decision.vo.DecisionForecastWorkOrderCreateReqVO;
 import cn.iocoder.yudao.module.bi.dal.dataobject.decision.BiForecastDataDO;
 import cn.iocoder.yudao.module.bi.dal.mysql.decision.BiForecastDataMapper;
@@ -12,10 +14,12 @@
 import org.springframework.transaction.annotation.Transactional;
 
 import java.math.BigDecimal;
+import java.math.RoundingMode;
 import java.time.LocalDateTime;
 import java.util.List;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.bi.enums.ErrorCodeConstants.BI_FORECAST_NO_HISTORY;
 import static cn.iocoder.yudao.module.bi.enums.ErrorCodeConstants.BI_FORECAST_NOT_EXISTS;
 
 /**
@@ -44,21 +48,51 @@
         return forecastDataMapper.selectListByCodeAndTimeRange(forecastCode, beginTime, endTime);
     }
 
+    /**
+     * 绉诲姩骞冲潎绐楀彛澶у皬
+     */
+    private static final int SMA_WINDOW = 12;
+
+    @Override
+    public Long createForecastHistory(DecisionForecastHistorySaveReqVO reqVO) {
+        BiForecastDataDO data = BiForecastDataDO.builder()
+                .forecastCode(reqVO.getForecastCode())
+                .forecastName(reqVO.getForecastName())
+                .pointTime(reqVO.getPointTime())
+                .forecastValue(reqVO.getForecastValue())
+                .actualValue(reqVO.getActualValue())
+                .dimension(reqVO.getDimension())
+                .dimensionValue(reqVO.getDimensionValue())
+                .remark(reqVO.getRemark())
+                .modelVersion("manual-history")
+                .build();
+        forecastDataMapper.insert(data);
+        return data.getId();
+    }
+
     @Override
     public void generateForecast(String forecastCode, LocalDateTime pointTime) {
-        // 鏈湡鍗犱綅锛氱畝鍗曠Щ鍔ㄥ钩鍧囬娴�
-        // 瀹為檯椤圭洰搴旈泦鎴� ML 妯″瀷锛圓RIMA/Prophet/LSTM锛�
-        log.info("[generateForecast][鐢熸垚棰勬祴] forecastCode={}, pointTime={}, 褰撳墠涓哄崰浣嶅疄鐜�", forecastCode, pointTime);
+        // 鍩轰簬鍚岄娴嬬紪鐮佺殑鍘嗗彶棰勬祴鏁版嵁鍋氱畝鍗曠Щ鍔ㄥ钩鍧囷紱鏃犲巻鍙叉暟鎹椂涓嶄吉閫犻浂鍊�
+        List<BiForecastDataDO> history = forecastDataMapper.selectRecentByCode(forecastCode, SMA_WINDOW);
+        if (CollUtil.isEmpty(history)) {
+            throw exception(BI_FORECAST_NO_HISTORY);
+        }
+        BigDecimal sum = history.stream()
+                .map(BiForecastDataDO::getForecastValue)
+                .filter(value -> value != null)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+        BigDecimal avg = sum.divide(BigDecimal.valueOf(history.size()), 4, RoundingMode.HALF_UP);
+        BigDecimal bound = avg.multiply(new BigDecimal("0.10"));
 
         BiForecastDataDO forecast = BiForecastDataDO.builder()
                 .forecastCode(forecastCode)
-                .forecastName("璐熻嵎棰勬祴(鍗犱綅)")
+                .forecastName(history.get(0).getForecastName())
                 .pointTime(pointTime)
-                .forecastValue(BigDecimal.ZERO)
-                .lowerBound(BigDecimal.ZERO)
-                .upperBound(BigDecimal.ZERO)
+                .forecastValue(avg)
+                .lowerBound(avg.subtract(bound))
+                .upperBound(avg.add(bound))
                 .confidenceLevel(new BigDecimal("95.00"))
-                .modelVersion("v1.0-placeholder")
+                .modelVersion("v1.0-sma")
                 .build();
         forecastDataMapper.insert(forecast);
     }
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java
index 279b947..411a69a 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java
@@ -252,6 +252,7 @@
             }
             // 2.8 璁剧疆鎸囦护涓嬪彂鏁伴噺
             contractVO.setCommandCount(contractCommandService.countByContractId(contractVO.getId()));
+            contractVO.setAuditStatusName(CrmAuditStatusEnum.getNameByStatus(contractVO.getAuditStatus()));
         });
     }
 
diff --git a/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/PurchaseOrderApi.java b/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/PurchaseOrderApi.java
new file mode 100644
index 0000000..534ed9e
--- /dev/null
+++ b/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/PurchaseOrderApi.java
@@ -0,0 +1,12 @@
+package cn.iocoder.yudao.module.erp.api.purchase;
+
+/**
+ * 閲囪喘璁㈠崟 API 鎺ュ彛銆�
+ *
+ * <p>閲囪喘涓氬姟瀵瑰濂戠害锛屼笉鏆撮湶 ERP 涓氬姟璺敱锛涙棫 ERP API 浣滀负鍏煎濂戠害淇濈暀銆�</p>
+ */
+public interface PurchaseOrderApi extends ErpPurchaseOrderApi {
+
+    String PREFIX = "/rpc-api/purchase/order";
+
+}
diff --git a/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/PurchasePlanApi.java b/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/PurchasePlanApi.java
new file mode 100644
index 0000000..582fab3
--- /dev/null
+++ b/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/PurchasePlanApi.java
@@ -0,0 +1,12 @@
+package cn.iocoder.yudao.module.erp.api.purchase;
+
+/**
+ * 閲囪喘璁″垝 API 鎺ュ彛銆�
+ *
+ * <p>閲囪喘涓氬姟瀵瑰濂戠害锛屾棫 ERP API 浣滀负鍏煎濂戠害淇濈暀銆�</p>
+ */
+public interface PurchasePlanApi extends ErpPurchasePlanApi {
+
+    String PREFIX = "/rpc-api/purchase/plan";
+
+}
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/ErpPurchaseOrderApiImpl.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/ErpPurchaseOrderApiImpl.java
index 3a81eae..609354e 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/ErpPurchaseOrderApiImpl.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/ErpPurchaseOrderApiImpl.java
@@ -28,9 +28,9 @@
  * @author 瓒呯骇绠$悊鍛�
  */
 @RestController
-@RequestMapping(ErpPurchaseOrderApi.PREFIX)
+@RequestMapping({ErpPurchaseOrderApi.PREFIX, PurchaseOrderApi.PREFIX})
 @Validated
-public class ErpPurchaseOrderApiImpl implements ErpPurchaseOrderApi {
+public class ErpPurchaseOrderApiImpl implements PurchaseOrderApi {
 
     @Resource
     private ErpPurchaseOrderService purchaseOrderService;
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/ErpPurchasePlanApiImpl.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/ErpPurchasePlanApiImpl.java
index 8f85a34..2e5df39 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/ErpPurchasePlanApiImpl.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/api/purchase/ErpPurchasePlanApiImpl.java
@@ -16,7 +16,7 @@
  */
 @Service
 @Validated
-public class ErpPurchasePlanApiImpl implements ErpPurchasePlanApi {
+public class ErpPurchasePlanApiImpl implements PurchasePlanApi {
 
     @Resource
     private ErpPurchasePlanService purchasePlanService;
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseInvoiceController.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseInvoiceController.java
index 28c882a..0a2c755 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseInvoiceController.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseInvoiceController.java
@@ -51,7 +51,7 @@
 
 @Tag(name = "绠$悊鍚庡彴 - ERP 閲囪喘鏉ョエ")
 @RestController
-@RequestMapping("/erp/purchase-invoice")
+@RequestMapping({"/erp/purchase-invoice", "/purchase/invoice"})
 @Validated
 public class ErpPurchaseInvoiceController {
 
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseOrderController.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseOrderController.java
index df706ac..8837685 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseOrderController.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseOrderController.java
@@ -47,7 +47,7 @@
 
 @Tag(name = "绠$悊鍚庡彴 - ERP 閲囪喘璁㈠崟")
 @RestController
-@RequestMapping("/erp/purchase-order")
+@RequestMapping({"/erp/purchase-order", "/purchase/order"})
 @Validated
 public class ErpPurchaseOrderController {
 
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchasePlanController.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchasePlanController.java
index 47920de..858b26a 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchasePlanController.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchasePlanController.java
@@ -48,7 +48,7 @@
 
 @Tag(name = "绠$悊鍚庡彴 - ERP 閲囪喘璁″垝")
 @RestController
-@RequestMapping("/erp/purchase-plan")
+@RequestMapping({"/erp/purchase-plan", "/purchase/plan"})
 @Validated
 public class ErpPurchasePlanController {
 
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseRequestController.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseRequestController.java
index 8851644..e915b0e 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseRequestController.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseRequestController.java
@@ -47,7 +47,7 @@
 
 @Tag(name = "绠$悊鍚庡彴 - ERP 閲囪喘鐢宠")
 @RestController
-@RequestMapping("/erp/purchase-request")
+@RequestMapping({"/erp/purchase-request", "/purchase/request"})
 @Validated
 public class ErpPurchaseRequestController {
 
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpSupplierController.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpSupplierController.java
index 63c5006..6f6c7cb 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpSupplierController.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpSupplierController.java
@@ -33,7 +33,7 @@
 
 @Tag(name = "绠$悊鍚庡彴 - ERP 渚涘簲鍟�")
 @RestController
-@RequestMapping("/erp/supplier")
+@RequestMapping({"/erp/supplier", "/purchase/supplier"})
 @Validated
 public class ErpSupplierController {
 
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/statistics/ErpPurchaseStatisticsController.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/statistics/ErpPurchaseStatisticsController.java
index 5e0657a..3587e40 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/statistics/ErpPurchaseStatisticsController.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/statistics/ErpPurchaseStatisticsController.java
@@ -26,7 +26,7 @@
 
 @Tag(name = "绠$悊鍚庡彴 - ERP 閲囪喘缁熻")
 @RestController
-@RequestMapping("/erp/purchase-statistics")
+@RequestMapping({"/erp/purchase-statistics", "/purchase/statistics"})
 @Validated
 public class ErpPurchaseStatisticsController {
 
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderBomController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderBomController.java
index 2f2795d..edcb18a 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderBomController.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderBomController.java
@@ -20,7 +20,7 @@
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderProcessDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO;
 import cn.iocoder.yudao.module.mes.dal.mysql.wm.materialstock.MesWmMaterialStockMapper;
-import cn.iocoder.yudao.module.erp.api.purchase.ErpPurchaseOrderApi;
+import cn.iocoder.yudao.module.erp.api.purchase.PurchaseOrderApi;
 import cn.iocoder.yudao.module.erp.api.purchase.dto.ErpPurchaseOrderItemRespDTO;
 import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
 import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemTypeService;
@@ -71,7 +71,7 @@
     @Resource
     private MesWmMaterialStockMapper materialStockMapper;
     @Resource
-    private ErpPurchaseOrderApi purchaseOrderApi;
+    private PurchaseOrderApi purchaseOrderApi;
 
     @PostMapping("/create")
     @Operation(summary = "鍒涘缓宸ュ崟 BOM")
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/arrivalnotice/MesWmArrivalNoticeController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/arrivalnotice/MesWmArrivalNoticeController.java
index 61cfe88..d0d81c1 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/arrivalnotice/MesWmArrivalNoticeController.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/arrivalnotice/MesWmArrivalNoticeController.java
@@ -13,8 +13,8 @@
 import cn.iocoder.yudao.module.mes.controller.admin.wm.arrivalnotice.vo.MesWmArrivalNoticeSaveReqVO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.arrivalnotice.MesWmArrivalNoticeDO;
 import cn.iocoder.yudao.module.mes.service.wm.arrivalnotice.MesWmArrivalNoticeService;
-import cn.iocoder.yudao.module.erp.api.purchase.ErpPurchaseOrderApi;
-import cn.iocoder.yudao.module.erp.api.purchase.ErpPurchaseOrderApi;
+import cn.iocoder.yudao.module.erp.api.purchase.PurchaseOrderApi;
+import cn.iocoder.yudao.module.erp.api.purchase.PurchaseOrderApi;
 import cn.iocoder.yudao.module.erp.api.purchase.dto.ErpPurchaseOrderRespDTO;
 import cn.iocoder.yudao.module.srm.api.supplier.SrmSupplierApi;
 import cn.iocoder.yudao.module.srm.api.supplier.dto.SrmSupplierRespDTO;
@@ -48,7 +48,7 @@
     @Resource
     private SrmSupplierApi srmSupplierApi;
     @Resource
-    private ErpPurchaseOrderApi purchaseOrderApi;
+    private PurchaseOrderApi purchaseOrderApi;
 
     @PostMapping("/create")
     @Operation(summary = "鍒涘缓鍒拌揣閫氱煡鍗�")
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
index 261841f..aa01418 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
@@ -265,6 +265,7 @@
     // ========== MES 鐢熶骇绠$悊-宸ヨ壓璺嚎浜у搧锛�1-040-501-200锛� ==========
     ErrorCode PRO_ROUTE_PRODUCT_NOT_EXISTS = new ErrorCode(1_040_501_200, "宸ヨ壓璺嚎浜у搧涓嶅瓨鍦�");
     ErrorCode PRO_ROUTE_PRODUCT_ITEM_DUPLICATE = new ErrorCode(1_040_501_201, "姝や骇鍝佸凡閰嶇疆浜嗗伐鑹鸿矾绾�");
+    ErrorCode PRO_ROUTE_PRODUCT_ITEM_TYPE_INVALID = new ErrorCode(1_040_501_202, "宸ヨ壓璺嚎鍙兘鍏宠仈浜у搧绫诲瀷鐗╂枡");
     // ========== MES 鐢熶骇绠$悊-宸ヨ壓璺嚎浜у搧BOM锛�1-040-501-300锛� ==========
     ErrorCode PRO_ROUTE_PRODUCT_BOM_NOT_EXISTS = new ErrorCode(1_040_501_300, "宸ヨ壓璺嚎浜у搧 BOM 涓嶅瓨鍦�");
     ErrorCode PRO_ROUTE_PRODUCT_BOM_DUPLICATE = new ErrorCode(1_040_501_301, "褰撳墠 BOM 鐗╂枡鍦ㄦ宸ュ簭宸茬粡閰嶇疆杩�");
@@ -274,6 +275,7 @@
     ErrorCode PRO_WORK_ORDER_CODE_DUPLICATE = new ErrorCode(1_040_502_001, "鐢熶骇宸ュ崟缂栫爜宸插瓨鍦�");
     ErrorCode PRO_WORK_ORDER_NOT_PREPARE = new ErrorCode(1_040_502_002, "鍙湁鑽夌鐘舵�佺殑宸ュ崟鎵嶈兘鎵ц姝ゆ搷浣�");
     ErrorCode PRO_WORK_ORDER_NOT_CONFIRMED = new ErrorCode(1_040_502_003, "鍙湁宸茬‘璁ょ姸鎬佺殑宸ュ崟鎵嶈兘鎵ц姝ゆ搷浣�");
+    ErrorCode PRO_WORK_ORDER_PRODUCTION_NOT_COMPLETE = new ErrorCode(1_040_502_007, "宸ュ崟瀹為檯浜у嚭鏈揪鍒拌鍒掓暟閲忥紝涓嶈兘瀹屾垚");
     ErrorCode PRO_WORK_ORDER_HAS_CHILDREN = new ErrorCode(1_040_502_004, "瀛樺湪瀛愬伐鍗曪紝鏃犳硶鍒犻櫎");
     ErrorCode PRO_WORK_ORDER_PRODUCT_MISMATCH = new ErrorCode(1_040_502_005, "褰撳墠浜у搧鐗╂枡涓庣敓浜у伐鍗曚骇鍝佷笉涓�鑷�");
     ErrorCode PRO_WORK_ORDER_SCHEDULE_QUANTITY_EXCEED = new ErrorCode(1_040_502_006, "鎺掍骇鏁伴噺瓒呴檺锛氬伐鍗晎}(璁″垝{}锛屽凡鎺掍骇{}锛屾湰娆℃帓浜})");
@@ -356,6 +358,7 @@
     ErrorCode PRO_MPS_CODE_DUPLICATE = new ErrorCode(1_040_509_006, "MPS 缂栫爜宸插瓨鍦�");
     ErrorCode PRO_MPS_HAS_WORK_ORDER = new ErrorCode(1_040_509_007, "MPS 宸插叧鑱旂敓浜у伐鍗曪紝鏃犳硶鍒犻櫎");
     ErrorCode PRO_MPS_MES_PRODUCT_NOT_EXISTS = new ErrorCode(1_040_509_008, "MES 浜у搧涓嶅瓨鍦紝鏃犳硶鍒涘缓 MPS锛屼骇鍝侊細{}");
+    ErrorCode PRO_MPS_PRODUCT_TYPE_INVALID = new ErrorCode(1_040_509_009, "鎺掔▼浜у搧蹇呴』鏄骇鍝佺被鍨嬶紝绂佹浣跨敤鐗╂枡");
 
     // ========== MES 璐ㄩ噺绠$悊-璐ㄦ鏂规锛�1-040-600-000锛� ==========
     ErrorCode QC_TEMPLATE_NOT_EXISTS = new ErrorCode(1_040_600_000, "璐ㄦ鏂规涓嶅瓨鍦�");
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/product/MesPdProductServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/product/MesPdProductServiceImpl.java
index 28c8094..0ff2652 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/product/MesPdProductServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pd/product/MesPdProductServiceImpl.java
@@ -9,6 +9,7 @@
 import cn.iocoder.yudao.module.mes.dal.dataobject.pd.product.MesPdProductDO;
 import cn.iocoder.yudao.module.mes.dal.mysql.pd.product.MesPdProductMapper;
 import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
+import cn.iocoder.yudao.module.mes.enums.pd.MesPdAuditStatusEnum;
 import cn.iocoder.yudao.module.mes.enums.md.autocode.MesMdAutoCodeRuleCodeEnum;
 import cn.iocoder.yudao.module.mes.enums.pd.MesPdAuditStatusEnum;
 import cn.iocoder.yudao.module.mes.service.md.autocode.MesMdAutoCodeRecordService;
@@ -144,8 +145,10 @@
     private void fillDictName(MesPdProductRespVO respVO) {
         respVO.setArchiveTypeName(DictFrameworkUtils.parseDictDataLabel(
                 DictTypeConstants.MES_PD_PRODUCT_ARCHIVE_TYPE, respVO.getArchiveType()));
-        respVO.setAuditStatusName(DictFrameworkUtils.parseDictDataLabel(
-                DictTypeConstants.MES_PD_PRODUCT_AUDIT_STATUS, respVO.getAuditStatus()));
+        String auditStatusName = DictFrameworkUtils.parseDictDataLabel(
+                DictTypeConstants.MES_PD_PRODUCT_AUDIT_STATUS, respVO.getAuditStatus());
+        respVO.setAuditStatusName(auditStatusName != null
+                ? auditStatusName : MesPdAuditStatusEnum.getNameByStatus(respVO.getAuditStatus()));
     }
 
 }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/maintenanceplan/MesProMaintenancePlanServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/maintenanceplan/MesProMaintenancePlanServiceImpl.java
index ef5b5a1..4486b61 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/maintenanceplan/MesProMaintenancePlanServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/maintenanceplan/MesProMaintenancePlanServiceImpl.java
@@ -4,7 +4,7 @@
 import cn.hutool.core.date.DateUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
-import cn.iocoder.yudao.module.erp.api.purchase.ErpPurchasePlanApi;
+import cn.iocoder.yudao.module.erp.api.purchase.PurchasePlanApi;
 import cn.iocoder.yudao.module.erp.api.purchase.dto.ErpPurchasePlanCreateReqDTO;
 import cn.iocoder.yudao.module.mes.controller.admin.pro.maintenanceplan.vo.MesProMaintenancePlanPageReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.pro.maintenanceplan.vo.MesProMaintenancePlanSaveReqVO;
@@ -74,7 +74,7 @@
     private MesMdItemService itemService;
 
     @Resource
-    private ErpPurchasePlanApi erpPurchasePlanApi;
+    private PurchasePlanApi purchasePlanApi;
     @Resource
     private MesProWorkOrderService workOrderService;
 
@@ -199,7 +199,7 @@
         createReqDTO.setPlanDate(plan.getPlanDate());
         createReqDTO.setRemark("鏉ユ簮璁″垝: " + plan.getCode());
         createReqDTO.setItems(items);
-        return erpPurchasePlanApi.createPurchasePlan(createReqDTO);
+        return purchasePlanApi.createPurchasePlan(createReqDTO);
     }
 
     @Override
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/mps/MesProMpsServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/mps/MesProMpsServiceImpl.java
index be43cda..3100498 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/mps/MesProMpsServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/mps/MesProMpsServiceImpl.java
@@ -17,6 +17,10 @@
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductBomDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO;
+import cn.iocoder.yudao.module.mes.enums.md.MesMdItemTypeEnum;
+import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemTypeService;
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProcessDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
 import cn.iocoder.yudao.module.mes.enums.pro.MesProMpsStatusEnum;
@@ -74,6 +78,8 @@
     @Resource
     private MesMdItemService itemService;
     @Resource
+    private MesMdItemTypeService itemTypeService;
+    @Resource
     private MesMdAutoCodeRecordService autoCodeRecordService;
     @Resource
     private CrmCustomerApi customerApi;
@@ -94,11 +100,12 @@
                           Long mdmItemId, String productName, BigDecimal quantity, Long clientId,
                           String requestDate, String remark) {
         // 1. 鏍规嵁 MDM 鐗╂枡 ID 鏌ユ壘 MES 浜у搧
-        cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO mesItem = itemService.getItemByMdmItemId(mdmItemId);
+        MesMdItemDO mesItem = itemService.getItemByMdmItemId(mdmItemId);
         if (mesItem == null) {
             // MES 浜у搧涓嶅瓨鍦紝鎶涘嚭寮傚父骞跺寘鍚骇鍝佸悕绉版彁绀�
             throw exception(PRO_MPS_MES_PRODUCT_NOT_EXISTS, productName != null ? productName : mdmItemId);
         }
+        validateProductItem(mesItem.getId());
 
         // 2. 鐢熸垚 MPS 缂栫爜
         String code = generateMpsCode();
@@ -138,8 +145,8 @@
     @Override
     @Transactional(rollbackFor = Exception.class)
     public Long createMps(MesProMpsSaveReqVO createReqVO) {
-        // 鏍¢獙浜у搧瀛樺湪
-        itemService.validateItemExists(createReqVO.getProductId());
+        // 鏍¢獙浜у搧瀛樺湪銆佸惎鐢ㄤ笖涓轰骇鍝佺被鍨�
+        validateProductItem(createReqVO.getProductId());
 
         // 濡傛灉鏈~鍐欑紪鐮侊紝鑷姩鐢熸垚
         if (StrUtil.isBlank(createReqVO.getCode())) {
@@ -165,6 +172,8 @@
     public void updateMps(MesProMpsSaveReqVO updateReqVO) {
         // 鏍¢獙瀛樺湪 + 鑽夌鐘舵��
         validateMpsExistsAndDraft(updateReqVO.getId());
+        // 鏍¢獙浜у搧瀛樺湪銆佸惎鐢ㄤ笖涓轰骇鍝佺被鍨�
+        validateProductItem(updateReqVO.getProductId());
 
         // 鏍¢獙缂栫爜鍞竴
         validateMpsCodeUnique(updateReqVO.getId(), updateReqVO.getCode());
@@ -509,6 +518,17 @@
     }
 
     /**
+     * 鏍¢獙浜у搧瀛樺湪銆佸惎鐢ㄤ笖涓轰骇鍝佺被鍨�
+     */
+    private void validateProductItem(Long itemId) {
+        MesMdItemDO item = itemService.validateItemExistsAndEnable(itemId);
+        MesMdItemTypeDO itemType = itemTypeService.getItemType(item.getItemTypeId());
+        if (itemType == null || !MesMdItemTypeEnum.isProduct(itemType.getItemOrProduct())) {
+            throw exception(PRO_MPS_PRODUCT_TYPE_INVALID);
+        }
+    }
+
+    /**
      * 鏍规嵁 MPS 鍒涘缓鐢熶骇宸ュ崟
      */
     private Long createWorkOrderFromMps(MesProMpsDO mps, BigDecimal quantity) {
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductServiceImpl.java
index d5089de..c19f6b7 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProductServiceImpl.java
@@ -4,7 +4,12 @@
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.module.mes.controller.admin.pro.route.vo.product.MesProRouteProductSaveReqVO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.route.MesProRouteProductDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO;
 import cn.iocoder.yudao.module.mes.dal.mysql.pro.route.MesProRouteProductMapper;
+import cn.iocoder.yudao.module.mes.enums.md.MesMdItemTypeEnum;
+import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
+import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemTypeService;
 import jakarta.annotation.Resource;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
@@ -34,12 +39,18 @@
     @Resource
     @Lazy
     private MesProRouteProductBomService routeProductBomService;
+    @Resource
+    private MesMdItemService itemService;
+    @Resource
+    private MesMdItemTypeService itemTypeService;
 
     @Override
     public Long createRouteProduct(MesProRouteProductSaveReqVO createReqVO) {
         // 1.0 宸插惎鐢ㄧ殑宸ヨ壓璺嚎锛屼笉鍏佽鎿嶄綔
         routeService.validateRouteNotEnable(createReqVO.getRouteId());
-        // 1.1 鏍¢獙浜у搧鍞竴鎬э紙涓�涓骇鍝佸彧鑳藉叧鑱斾竴鏉″伐鑹鸿矾绾匡級
+        // 1.1 鏍¢獙浜у搧瀛樺湪銆佸惎鐢ㄤ笖绫诲瀷姝g‘
+        validateProductItem(createReqVO.getItemId());
+        // 1.2 鏍¢獙浜у搧鍞竴鎬э紙涓�涓骇鍝佸彧鑳藉叧鑱斾竴鏉″伐鑹鸿矾绾匡級
         validateItemUnique(null, createReqVO.getItemId());
 
         // 2. 鎻掑叆
@@ -54,7 +65,9 @@
         routeService.validateRouteNotEnable(updateReqVO.getRouteId());
         // 1.1 鏍¢獙瀛樺湪
         validateRouteProductExists(updateReqVO.getId());
-        // 1.2 鏍¢獙浜у搧鍞竴鎬�
+        // 1.2 鏍¢獙浜у搧瀛樺湪銆佸惎鐢ㄤ笖绫诲瀷姝g‘
+        validateProductItem(updateReqVO.getItemId());
+        // 1.3 鏍¢獙浜у搧鍞竴鎬�
         validateItemUnique(updateReqVO.getId(), updateReqVO.getItemId());
 
         // 2. 鏇存柊
@@ -89,6 +102,14 @@
         }
     }
 
+    private void validateProductItem(Long itemId) {
+        MesMdItemDO item = itemService.validateItemExistsAndEnable(itemId);
+        MesMdItemTypeDO itemType = itemTypeService.getItemType(item.getItemTypeId());
+        if (itemType == null || !MesMdItemTypeEnum.isProduct(itemType.getItemOrProduct())) {
+            throw exception(PRO_ROUTE_PRODUCT_ITEM_TYPE_INVALID);
+        }
+    }
+
     private void validateItemUnique(Long id, Long itemId) {
         MesProRouteProductDO existing = routeProductMapper.selectByItemId(itemId);
         if (existing == null) {
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderServiceImpl.java
index ad02742..efc1adf 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderServiceImpl.java
@@ -225,6 +225,20 @@
         if (ObjUtil.notEqual(workOrder.getStatus(), MesProWorkOrderStatusEnum.CONFIRMED.getStatus())) {
             throw exception(PRO_WORK_ORDER_NOT_CONFIRMED);
         }
+        if (workOrder.getQuantityProduced() == null
+                || workOrder.getQuantity() == null
+                || workOrder.getQuantityProduced().compareTo(workOrder.getQuantity()) < 0) {
+            throw exception(PRO_WORK_ORDER_PRODUCTION_NOT_COMPLETE);
+        }
+        // 鏍¢獙姣忎釜浠诲姟鐨勫疄闄呬骇鍑烘槸鍚﹀凡杈炬垚璁″垝鏁伴噺锛堜换鍔$姸鎬佸彧鏈夊湪宸ュ崟瀹屾垚鏃剁敱绾ц仈鏂规硶缃负宸插畬鎴愶紝涓嶈兘鍦ㄦ鏍¢獙鐘舵�侊級
+        List<MesProTaskDO> tasks = taskService.getTaskListByWorkOrderIds(Collections.singletonList(id));
+        boolean hasUnfinishedTask = CollUtil.isEmpty(tasks) || tasks.stream().anyMatch(task ->
+                task.getProducedQuantity() == null
+                        || task.getQuantity() == null
+                        || task.getProducedQuantity().compareTo(task.getQuantity()) < 0);
+        if (hasUnfinishedTask) {
+            throw exception(PRO_WORK_ORDER_PRODUCTION_NOT_COMPLETE);
+        }
 
         // 2. 绾ц仈瀹屾垚鎵�鏈夊叧鑱斾换鍔�
         taskService.finishTaskByOrderId(id);
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeServiceImpl.java
index d5bc3c3..7a7c30f 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/arrivalnotice/MesWmArrivalNoticeServiceImpl.java
@@ -11,7 +11,7 @@
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.arrivalnotice.MesWmArrivalNoticeLineDO;
 import cn.iocoder.yudao.module.mes.dal.mysql.wm.arrivalnotice.MesWmArrivalNoticeMapper;
 import cn.iocoder.yudao.module.mes.enums.wm.MesWmArrivalNoticeStatusEnum;
-import cn.iocoder.yudao.module.erp.api.purchase.ErpPurchaseOrderApi;
+import cn.iocoder.yudao.module.erp.api.purchase.PurchaseOrderApi;
 import cn.iocoder.yudao.module.srm.api.supplier.SrmSupplierApi;
 import cn.iocoder.yudao.module.erp.api.purchase.dto.ErpPurchaseOrderRespDTO;
 import cn.iocoder.yudao.module.erp.api.purchase.dto.ErpPurchaseOrderItemRespDTO;
@@ -51,7 +51,7 @@
     private SrmSupplierApi srmSupplierApi;
 
     @Resource
-    private ErpPurchaseOrderApi purchaseOrderApi;
+    private PurchaseOrderApi purchaseOrderApi;
 
     @Resource
     private MesMdmItemService mdmItemService;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/itemreceipt/MesWmItemReceiptServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/itemreceipt/MesWmItemReceiptServiceImpl.java
index aa30333..f6fd5bf 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/itemreceipt/MesWmItemReceiptServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/itemreceipt/MesWmItemReceiptServiceImpl.java
@@ -25,7 +25,7 @@
 import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
 import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
 import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
-import cn.iocoder.yudao.module.erp.api.purchase.ErpPurchaseOrderApi;
+import cn.iocoder.yudao.module.erp.api.purchase.PurchaseOrderApi;
 import cn.iocoder.yudao.module.srm.api.supplier.SrmSupplierApi;
 import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi;
 import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO;
@@ -81,7 +81,7 @@
     private BpmProcessInstanceApi processInstanceApi;
 
     @Resource
-    private ErpPurchaseOrderApi purchaseOrderApi;
+    private PurchaseOrderApi purchaseOrderApi;
 
     @Resource
     private MdmItemApi mdmItemApi;
diff --git a/yudao-module-srm/src/main/java/cn/iocoder/yudao/module/srm/service/tender/SrmBidAwardServiceImpl.java b/yudao-module-srm/src/main/java/cn/iocoder/yudao/module/srm/service/tender/SrmBidAwardServiceImpl.java
index aa836d3..47e3d0f 100644
--- a/yudao-module-srm/src/main/java/cn/iocoder/yudao/module/srm/service/tender/SrmBidAwardServiceImpl.java
+++ b/yudao-module-srm/src/main/java/cn/iocoder/yudao/module/srm/service/tender/SrmBidAwardServiceImpl.java
@@ -3,7 +3,7 @@
 import cn.hutool.core.collection.CollUtil;
 import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
-import cn.iocoder.yudao.module.erp.api.purchase.ErpPurchaseOrderApi;
+import cn.iocoder.yudao.module.erp.api.purchase.PurchaseOrderApi;
 import cn.iocoder.yudao.module.erp.api.purchase.dto.ErpPurchaseOrderCreateReqDTO;
 import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi;
 import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO;
@@ -61,7 +61,7 @@
     private MdmItemApi mdmItemApi;
 
     @Resource
-    private ErpPurchaseOrderApi erpPurchaseOrderApi;
+    private PurchaseOrderApi purchaseOrderApi;
 
     @Resource
     private SrmSupplierApplyService supplierApplyService;
@@ -233,12 +233,12 @@
         createReq.setItems(orderItems);
         createReq.setRemark("SRM 瀹氭爣鑷姩鐢熸垚, 瀹氭爣缂栧彿: " + award.getAwardNo());
 
-        Long purchaseOrderId = erpPurchaseOrderApi.createPurchaseOrder(createReq).getCheckedData();
+        Long purchaseOrderId = purchaseOrderApi.createPurchaseOrder(createReq).getCheckedData();
 
         // 鑾峰彇閲囪喘璁㈠崟鍙�
         String purchaseOrderNo = null;
         try {
-            purchaseOrderNo = erpPurchaseOrderApi.getPurchaseOrder(purchaseOrderId)
+            purchaseOrderNo = purchaseOrderApi.getPurchaseOrder(purchaseOrderId)
                     .getCheckedData().getNo();
         } catch (Exception e) {
             log.warn("[generatePurchaseOrder] 鑾峰彇閲囪喘璁㈠崟鍙峰け璐ワ紝purchaseOrderId={}", purchaseOrderId, e);
diff --git a/yudao-module-srm/src/main/java/cn/iocoder/yudao/module/srm/service/tender/SrmTenderProjectServiceImpl.java b/yudao-module-srm/src/main/java/cn/iocoder/yudao/module/srm/service/tender/SrmTenderProjectServiceImpl.java
index 73b86db..f1c4061 100644
--- a/yudao-module-srm/src/main/java/cn/iocoder/yudao/module/srm/service/tender/SrmTenderProjectServiceImpl.java
+++ b/yudao-module-srm/src/main/java/cn/iocoder/yudao/module/srm/service/tender/SrmTenderProjectServiceImpl.java
@@ -3,7 +3,7 @@
 import cn.hutool.core.collection.CollUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
-import cn.iocoder.yudao.module.erp.api.purchase.ErpPurchaseOrderApi;
+import cn.iocoder.yudao.module.erp.api.purchase.PurchaseOrderApi;
 import cn.iocoder.yudao.module.erp.api.purchase.dto.ErpPurchaseOrderCreateReqDTO;
 import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi;
 import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO;
@@ -66,7 +66,7 @@
     @Resource
     private MdmItemApi mdmItemApi;
     @Resource
-    private ErpPurchaseOrderApi erpPurchaseOrderApi;
+    private PurchaseOrderApi purchaseOrderApi;
     @Resource
     private SrmSupplierApplyService supplierApplyService;
     @Resource
@@ -376,12 +376,12 @@
         createReq.setItems(orderItems);
         createReq.setRemark("SRM 绠�鏄撴嫑鎶曟爣鑷姩鐢熸垚, 瀹氭爣缂栧彿: " + award.getAwardNo());
 
-        Long purchaseOrderId = erpPurchaseOrderApi.createPurchaseOrder(createReq).getCheckedData();
+        Long purchaseOrderId = purchaseOrderApi.createPurchaseOrder(createReq).getCheckedData();
 
         // 鑾峰彇閲囪喘璁㈠崟鍙�
         String purchaseOrderNo = null;
         try {
-            purchaseOrderNo = erpPurchaseOrderApi.getPurchaseOrder(purchaseOrderId)
+            purchaseOrderNo = purchaseOrderApi.getPurchaseOrder(purchaseOrderId)
                     .getCheckedData().getNo();
         } catch (Exception e) {
             log.warn("[generatePurchaseOrderForSimple] 鑾峰彇閲囪喘璁㈠崟鍙峰け璐ワ紝purchaseOrderId={}", purchaseOrderId, e);

--
Gitblit v1.9.3