From 06c312e584b99df613728d5ea8b32df4e156f3cc Mon Sep 17 00:00:00 2001
From: liu <2021943741@qq.com>
Date: 星期一, 07 九月 2026 17:40:09 +0800
Subject: [PATCH] feat(mes): 采购退货关联采购订单后自动带入物料并锁定选料

---
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnvendor/MesWmReturnVendorLineServiceImpl.java |   81 ++++++++++++++++++++++++++-
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java                                 |    1 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnvendor/MesWmReturnVendorServiceImpl.java     |   59 ++++++++++++++++++-
 3 files changed, 134 insertions(+), 7 deletions(-)

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 0beb4df..34ddf34 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
@@ -636,6 +636,7 @@
     ErrorCode WM_RETURN_VENDOR_DETAIL_LINE_NOT_MATCH = new ErrorCode(1_040_711_202, "鎷h揣鏄庣粏涓嶅睘浜庢寚瀹氱殑渚涘簲鍟嗛��璐у崟");
     ErrorCode WM_RETURN_VENDOR_PURCHASE_ORDER_REQUIRED = new ErrorCode(1_040_711_008, "璇峰厛閫夋嫨閲囪喘璁㈠崟");
     ErrorCode WM_RETURN_VENDOR_PURCHASE_ORDER_NO_ITEM = new ErrorCode(1_040_711_009, "閲囪喘璁㈠崟鏃犳槑缁嗘暟鎹紝鏃犳硶甯﹀叆鐗╂枡");
+    ErrorCode WM_RETURN_VENDOR_ITEM_NOT_IN_PURCHASE_ORDER = new ErrorCode(1_040_711_010, "閫�璐х墿鏂欎笉鍦ㄦ墍鍏宠仈閲囪喘璁㈠崟鍐咃紝鏃犳硶娣诲姞");
 
     // ========== MES 浠撳簱绠$悊-浜у搧鏀惰揣鍗曪紙1-040-712-000锛� ==========
     ErrorCode WM_PRODUCT_RECPT_NOT_EXISTS = new ErrorCode(1_040_712_000, "浜у搧鏀惰揣鍗曚笉瀛樺湪");
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnvendor/MesWmReturnVendorLineServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnvendor/MesWmReturnVendorLineServiceImpl.java
index 7a9ee75..39a10d4 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnvendor/MesWmReturnVendorLineServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnvendor/MesWmReturnVendorLineServiceImpl.java
@@ -1,9 +1,18 @@
 package cn.iocoder.yudao.module.mes.service.wm.returnvendor;
 
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.exception.ServiceException;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 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.dto.ErpPurchaseOrderItemRespDTO;
+import cn.iocoder.yudao.module.erp.api.purchase.dto.ErpPurchaseOrderRespDTO;
 import cn.iocoder.yudao.module.mes.controller.admin.wm.returnvendor.vo.line.MesWmReturnVendorLinePageReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.wm.returnvendor.vo.line.MesWmReturnVendorLineSaveReqVO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.mdm.MesMdmItemDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnvendor.MesWmReturnVendorLineDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnvendor.MesWmReturnVendorDO;
 import cn.iocoder.yudao.module.mes.dal.mysql.wm.returnvendor.MesWmReturnVendorLineMapper;
@@ -16,8 +25,10 @@
 import org.springframework.validation.annotation.Validated;
 
 import java.util.List;
+import java.util.Set;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_RETURN_VENDOR_ITEM_NOT_IN_PURCHASE_ORDER;
 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_RETURN_VENDOR_LINE_NOT_EXISTS;
 
 /**
@@ -40,11 +51,15 @@
     private MesMdmItemService mdmItemService;
     @Resource
     private MesWmBatchService batchService;
+    @Resource
+    private ErpPurchaseOrderApi purchaseOrderApi;
 
     @Override
     public Long createReturnVendorLine(MesWmReturnVendorLineSaveReqVO createReqVO) {
         // 鏍¢獙鏁版嵁
-        validateReturnVendorLineSaveData(createReqVO);
+        MesWmReturnVendorDO returnVendor = validateReturnVendorLineSaveData(createReqVO);
+        // 閫�璐у崟鑻ュ凡鍏宠仈閲囪喘璁㈠崟锛岀墿鏂欏繀椤诲睘浜庤閲囪喘璁㈠崟锛岀姝㈣法璁㈠崟闅忔剰閫夋枡
+        validateReturnVendorItemInPurchaseOrder(returnVendor, createReqVO.getItemId(), null);
 
         // 鎻掑叆
         MesWmReturnVendorLineDO line = BeanUtils.toBean(createReqVO, MesWmReturnVendorLineDO.class);
@@ -59,7 +74,9 @@
         // 鍥哄畾鐖跺崟 ID锛岄槻姝㈤�氳繃鎺ュ彛绡℃敼
         updateReqVO.setReturnId(oldLine.getReturnId());
         // 鏍¢獙鏁版嵁
-        validateReturnVendorLineSaveData(updateReqVO);
+        MesWmReturnVendorDO returnVendor = validateReturnVendorLineSaveData(updateReqVO);
+        // 鍏宠仈閲囪喘璁㈠崟鏃讹紝涓嶅厑璁告妸琛岀墿鏂欐敼鎴愯鍗曞鐨勭墿鏂欙紱浠呮敼鏁伴噺绛夋搷浣滀笉鍙楀奖鍝�
+        validateReturnVendorItemInPurchaseOrder(returnVendor, updateReqVO.getItemId(), oldLine.getItemId());
 
         // 鏇存柊
         MesWmReturnVendorLineDO updateObj = BeanUtils.toBean(updateReqVO, MesWmReturnVendorLineDO.class);
@@ -109,7 +126,7 @@
         return line;
     }
 
-    private void validateReturnVendorLineSaveData(MesWmReturnVendorLineSaveReqVO reqVO) {
+    private MesWmReturnVendorDO validateReturnVendorLineSaveData(MesWmReturnVendorLineSaveReqVO reqVO) {
         // 鏍¢獙鐖舵暟鎹瓨鍦ㄤ笖涓鸿崏绋跨姸鎬�
         MesWmReturnVendorDO returnVendor = returnVendorService.validateReturnVendorExistsAndPrepare(reqVO.getReturnId());
         // 鏍¢獙鐗╂枡瀛樺湪
@@ -118,6 +135,64 @@
         if (reqVO.getBatchId() != null) {
             batchService.validateBatchExists(reqVO.getBatchId(), reqVO.getItemId(), null, returnVendor.getVendorId());
         }
+        return returnVendor;
+    }
+
+    /**
+     * 鏍¢獙閫�璐ц鐗╂枡灞炰簬鎵�鍏宠仈閲囪喘璁㈠崟锛堥��璐у崟宸插叧鑱旈噰璐鍗曟椂锛夈��
+     *
+     * 閲囪喘閫�璐х殑鐗╂枡搴斿彇鑷叧鑱旈噰璐鍗曪紝姝ゅ涓哄悗绔厹搴曪紝闃叉閫氳繃鎺ュ彛璺ㄨ鍗曢殢鎰忛�夋枡銆�
+     * 鐗╂枡涓庡甫鍏ラ�昏緫淇濇寔涓�鑷达細浠ラ噰璐鍗曟槑缁嗙殑浜у搧 ID 浣滀负鐗╂枡闆嗗悎銆�
+     *
+     * @param oldItemId 淇敼鍓嶇墿鏂� ID锛涗负绌鸿〃绀烘柊澧炪�傜墿鏂欐湭鍙樺寲鏃讹紙浠呮敼鏁伴噺绛夛級涓嶉噸澶嶆牎楠�
+     */
+    private void validateReturnVendorItemInPurchaseOrder(MesWmReturnVendorDO returnVendor, Long itemId, Long oldItemId) {
+        Long purchaseOrderId = returnVendor.getPurchaseOrderId();
+        if (purchaseOrderId == null) {
+            return;
+        }
+        // 缂栬緫琛屾椂鐗╂枡鏈彉锛堝彧鏀规暟閲�/鎵规绛夛級锛屾棤闇�鏍¢獙
+        if (ObjUtil.equal(itemId, oldItemId)) {
+            return;
+        }
+        List<ErpPurchaseOrderItemRespDTO> poItems = purchaseOrderApi
+                .getPurchaseOrderItemList(purchaseOrderId).getCheckedData();
+        if (CollUtil.isEmpty(poItems)) {
+            // 璁㈠崟鏃犳槑缁嗘椂鐢�"甯﹀叆鐗╂枡"娴佺▼缁熶竴缁欏嚭鎻愮ず锛屾澶勪笉鎷︽埅
+            return;
+        }
+        Set<Long> poProductIds = CollectionUtils.convertSet(poItems, ErpPurchaseOrderItemRespDTO::getProductId);
+        if (poProductIds.contains(itemId)) {
+            return;
+        }
+        throw new ServiceException(WM_RETURN_VENDOR_ITEM_NOT_IN_PURCHASE_ORDER.getCode(),
+                String.format("閫�璐у崟[%s]宸插叧鑱旈噰璐鍗昜%s]锛岄��璐х墿鏂�%s涓嶅湪璇ラ噰璐鍗曟槑缁嗕腑銆�"
+                                + "閫�璐у彧鑳介拡瀵硅閲囪喘璁㈠崟鐨勭墿鏂欙紝璇烽�夋嫨璁㈠崟鍐呯殑鐗╂枡骞朵粎璋冩暣閫�璐ф暟閲忥紱"
+                                + "濡傞渶閫�鍥炶鍗曞鐗╂枡锛岃鍏堣В闄ゅ綋鍓嶉噰璐鍗曞叧鑱斻��",
+                        returnVendor.getCode(), resolvePurchaseOrderNo(purchaseOrderId), resolveItemDesc(itemId)));
+    }
+
+    /**
+     * 瑙f瀽閲囪喘璁㈠崟鍙凤紝瑙f瀽澶辫触鏃跺洖閫�涓哄彲璇� ID
+     */
+    private String resolvePurchaseOrderNo(Long purchaseOrderId) {
+        ErpPurchaseOrderRespDTO purchaseOrder = purchaseOrderApi.getPurchaseOrder(purchaseOrderId).getCheckedData();
+        if (purchaseOrder != null && StrUtil.isNotBlank(purchaseOrder.getNo())) {
+            return purchaseOrder.getNo();
+        }
+        return "ID=" + purchaseOrderId;
+    }
+
+    /**
+     * 瑙f瀽鐗╂枡鍙鎻忚堪锛堢紪鐮�+鍚嶇О锛夛紝瑙f瀽澶辫触鏃跺洖閫�涓哄彲璇� ID
+     */
+    private String resolveItemDesc(Long itemId) {
+        MesMdmItemDO item = mdmItemService.getItem(itemId);
+        if (item == null) {
+            return "ID=" + itemId;
+        }
+        String name = StrUtil.nullToDefault(item.getName(), "");
+        return (StrUtil.isNotBlank(item.getCode()) ? "[" + item.getCode() + "]" : "[ID=" + itemId + "]") + name;
     }
 
 }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnvendor/MesWmReturnVendorServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnvendor/MesWmReturnVendorServiceImpl.java
index ae6d530..18296c8 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnvendor/MesWmReturnVendorServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnvendor/MesWmReturnVendorServiceImpl.java
@@ -2,6 +2,7 @@
 
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.ObjUtil;
+import cn.iocoder.yudao.framework.common.exception.ServiceException;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
@@ -69,20 +70,36 @@
         MesWmReturnVendorDO returnVendor = BeanUtils.toBean(createReqVO, MesWmReturnVendorDO.class);
         returnVendor.setStatus(MesWmReturnVendorStatusEnum.PREPARE.getStatus());
         returnVendorMapper.insert(returnVendor);
+
+        // 2.1. 鍏宠仈浜嗛噰璐鍗曪細鑷姩鎶婅鍗曠墿鏂欏甫鍏ラ��璐ц锛堟棤鍙��鐗╂枡鏃堕潤榛橈紝涓嶅奖鍝嶅崟鎹垱寤猴級
+        if (returnVendor.getPurchaseOrderId() != null) {
+            autoImportLinesFromLinkedOrder(returnVendor.getId(), false);
+        }
         return returnVendor.getId();
     }
 
     @Override
     @Transactional(rollbackFor = Exception.class)
     public void updateReturnVendor(MesWmReturnVendorSaveReqVO updateReqVO) {
-        // 1. 鏍¢獙鏁版嵁
-        validateReturnVendorSaveData(updateReqVO);
+        // 1. 鏍¢獙鏁版嵁锛堝惈瀛樺湪 + 鑽夌鐘舵�侊級
+        MesWmReturnVendorDO oldReturnVendor = validateReturnVendorSaveData(updateReqVO);
         // 1.1. 鑻ラ噰璐鍗曞彿涓虹┖浣嗗叧鑱斾簡閲囪喘璁㈠崟锛屽垯浠� ERP 閲囪喘璁㈠崟琛ラ綈璁㈠崟鍙�
         fillPurchaseOrderCode(updateReqVO);
+
+        Long oldPurchaseOrderId = oldReturnVendor != null ? oldReturnVendor.getPurchaseOrderId() : null;
+        Long newPurchaseOrderId = updateReqVO.getPurchaseOrderId();
+        boolean purchaseOrderChanged = !ObjUtil.equal(oldPurchaseOrderId, newPurchaseOrderId);
 
         // 2. 鏇存柊涓昏〃
         MesWmReturnVendorDO updateObj = BeanUtils.toBean(updateReqVO, MesWmReturnVendorDO.class);
         returnVendorMapper.updateById(updateObj);
+
+        // 3. 鍏宠仈浜嗛噰璐鍗曪細鑷姩鍚屾鐗╂枡琛岋紝淇濊瘉琛岀墿鏂欎笌璇ラ噰璐鍗曚竴鑷�
+        //    3.1 鏇存崲浜嗛噰璐鍗� 鈫� 鍏堟竻绌烘棫璁㈠崟甯﹀叆鐨勮锛屽啀鎸夋柊璁㈠崟閲嶆柊甯﹀叆
+        //    3.2 閲囪喘璁㈠崟鏈彉浣嗗綋鍓嶆棤琛� 鈫� 鍏滃簳鑷姩甯﹀叆锛堝巻鍙叉暟鎹垨甯︽枡琚腑鏂殑鍦烘櫙锛�
+        if (newPurchaseOrderId != null) {
+            autoImportLinesFromLinkedOrder(updateReqVO.getId(), purchaseOrderChanged);
+        }
     }
 
     /**
@@ -239,16 +256,50 @@
 
     /**
      * 鏍¢獙閫�璐у崟鍒涘缓/淇敼鐨勫叕鍏辨暟鎹�
+     *
+     * @return 淇敼鏃惰繑鍥炴棫鍗曟嵁锛堢敤浜庢瘮杈冮噰璐鍗曟槸鍚﹀彉鏇达級锛涘垱寤烘椂杩斿洖 null
      */
-    private void validateReturnVendorSaveData(MesWmReturnVendorSaveReqVO reqVO) {
+    private MesWmReturnVendorDO validateReturnVendorSaveData(MesWmReturnVendorSaveReqVO reqVO) {
         // 鏍¢獙瀛樺湪 + 鑽夌鐘舵�侊紙浠呬慨鏀规椂锛�
+        MesWmReturnVendorDO oldReturnVendor = null;
         if (reqVO.getId() != null) {
-            validateReturnVendorExistsAndPrepare(reqVO.getId());
+            oldReturnVendor = validateReturnVendorExistsAndPrepare(reqVO.getId());
         }
         // 鏍¢獙渚涘簲鍟嗗瓨鍦�
         srmSupplierApi.getSupplier(reqVO.getVendorId());
         // 鏍¢獙缂栫爜鍞竴
         validateCodeUnique(reqVO.getId(), reqVO.getCode());
+        return oldReturnVendor;
+    }
+
+    /**
+     * 鎶婂叧鑱旈噰璐鍗曠殑鐗╂枡鑷姩甯﹀叆閫�璐у崟琛岋紙浠呰崏绋跨姸鎬佷笖宸插叧鑱旈噰璐鍗曟椂鐢熸晥锛夈��
+     *
+     * @param id         閫�璐у崟缂栧彿
+     * @param clearFirst 鏄惁鍏堟竻绌哄凡鏈夎鍐嶅甫鍏ワ紱鏇存崲閲囪喘璁㈠崟鏃朵紶 true
+     */
+    private void autoImportLinesFromLinkedOrder(Long id, boolean clearFirst) {
+        MesWmReturnVendorDO returnVendor = returnVendorMapper.selectById(id);
+        if (returnVendor == null || returnVendor.getPurchaseOrderId() == null) {
+            return;
+        }
+        List<MesWmReturnVendorLineDO> existingLines =
+                returnVendorLineService.getReturnVendorLineListByReturnId(id);
+        if (clearFirst) {
+            if (CollUtil.isNotEmpty(existingLines)) {
+                // 绾ц仈鍒犻櫎鏃ф槑缁嗕笌琛�
+                returnVendorDetailService.deleteReturnVendorDetailByReturnId(id);
+                returnVendorLineService.deleteReturnVendorLineByReturnId(id);
+            }
+        } else if (CollUtil.isNotEmpty(existingLines)) {
+            // 宸叉湁琛屼笖鏈洿鎹㈤噰璐鍗曪細涓嶉噸澶嶅甫鍏ワ紝閬垮厤瑕嗙洊鐢ㄦ埛宸茶皟鏁寸殑鏁伴噺
+            return;
+        }
+        try {
+            importPurchaseOrderItems(id);
+        } catch (ServiceException ignore) {
+            // 閲囪喘璁㈠崟鏃犳槑缁� / 鏃犲彲閫�鐗╂枡绛変笟鍔″紓甯革細鍗曟嵁涓讳綋浠嶆甯镐繚瀛橈紝鍙湪鐗╂枡鍖哄彟琛屽鐞�
+        }
     }
 
     /**

--
Gitblit v1.9.3