From 5d818f39c862884b7087488d588fef5b7ce47386 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期三, 28 十月 2026 18:33:55 +0800
Subject: [PATCH] feat(mes): 增加销售退货单与出库单关联功能

---
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnsales/MesWmReturnSalesLineServiceImpl.java |   69 +++++++++++++++++++++++++++++++---
 1 files changed, 62 insertions(+), 7 deletions(-)

diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnsales/MesWmReturnSalesLineServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnsales/MesWmReturnSalesLineServiceImpl.java
index d56b908..d022448 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnsales/MesWmReturnSalesLineServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnsales/MesWmReturnSalesLineServiceImpl.java
@@ -2,30 +2,36 @@
 
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.ObjUtil;
+import cn.iocoder.yudao.framework.common.core.KeyValue;
 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.mes.controller.admin.wm.returnsales.vo.line.MesWmReturnSalesLinePageReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.wm.returnsales.vo.line.MesWmReturnSalesLineSaveReqVO;
-import cn.iocoder.yudao.module.mes.dal.dataobject.mdm.MesMdmItemDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesLineDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnsales.MesWmReturnSalesLineDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnsales.MesWmReturnSalesDO;
 import cn.iocoder.yudao.module.mes.dal.mysql.wm.returnsales.MesWmReturnSalesLineMapper;
 import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum;
 import cn.iocoder.yudao.module.mes.enums.wm.MesWmReturnSalesStatusEnum;
-import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService;
 import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService;
+import cn.iocoder.yudao.module.mes.service.wm.productsales.MesWmProductSalesLineService;
+import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi;
+import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO;
 import jakarta.annotation.Resource;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 
 import java.math.BigDecimal;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.MD_ITEM_BATCH_REQUIRED;
-import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_RETURN_SALES_LINE_NOT_EXISTS;
+import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
 
 /**
  * MES 閿�鍞��璐у崟琛� Service 瀹炵幇绫�
@@ -43,12 +49,15 @@
     @Lazy
     private MesWmReturnSalesService returnSalesService;
     @Resource
-    private MesMdmItemService mdmItemService;
+    private MdmItemApi mdmItemApi;
     @Resource
     private MesWmBatchService batchService;
     @Resource
     @Lazy
     private MesWmReturnSalesDetailService returnSalesDetailService;
+    @Resource
+    @Lazy
+    private MesWmProductSalesLineService productSalesLineService;
 
     @Override
     public Long createReturnSalesLine(MesWmReturnSalesLineSaveReqVO createReqVO) {
@@ -79,12 +88,46 @@
 
     /**
      * 鏍¢獙淇濆瓨鏃剁殑鍏宠仈鏁版嵁
+     *
+     * <p>閫�璐у崟鍏宠仈浜嗛攢鍞嚭搴撳崟鏃讹紝鐗╂枡銆佹壒娆′竴寰嬪彇鑷嚭搴撹锛屽苟鏍¢獙鍙��鏁伴噺锛�
+     * 鏈叧鑱斿嚭搴撳崟锛堝鍞悗妯″潡鍒涘缓锛夋椂锛屼繚鎸佸師鏈夌墿鏂�/鎵规鏍¢獙閫昏緫銆�
      */
     private void validateLineSaveData(MesWmReturnSalesLineSaveReqVO reqVO) {
         // 鏍¢獙閫�璐у崟瀛樺湪 + 鑽夌鐘舵��
-        returnSalesService.validateReturnSalesExistsAndPrepare(reqVO.getReturnId());
+        MesWmReturnSalesDO returnSales = returnSalesService.validateReturnSalesExistsAndPrepare(reqVO.getReturnId());
+        if (returnSales.getProductSalesId() != null) {
+            validateOutboundLine(reqVO, returnSales);
+            return;
+        }
         // 鏍¢獙鐗╂枡瀛樺湪 + 鎵规绠$悊
         validateItemBatchManagement(reqVO.getReturnId(), reqVO.getItemId(), reqVO.getBatchId());
+    }
+
+    /**
+     * 鏍¢獙鍑哄簱琛屽綊灞炰笌鍙��鏁伴噺锛屽苟浠庡嚭搴撹鍥炲~鐗╂枡銆佹壒娆�
+     */
+    private void validateOutboundLine(MesWmReturnSalesLineSaveReqVO reqVO, MesWmReturnSalesDO returnSales) {
+        if (reqVO.getProductSalesLineId() == null) {
+            throw exception(WM_RETURN_SALES_LINE_PRODUCT_SALES_REQUIRED);
+        }
+        MesWmProductSalesLineDO outboundLine =
+                productSalesLineService.validateProductSalesLineExists(reqVO.getProductSalesLineId());
+        if (ObjUtil.notEqual(outboundLine.getSalesId(), returnSales.getProductSalesId())) {
+            throw exception(WM_RETURN_SALES_LINE_PRODUCT_SALES_NOT_MATCH);
+        }
+        // 鐗╂枡銆佹壒娆″繀椤讳笌鍑哄簱琛屼竴鑷�
+        reqVO.setItemId(outboundLine.getItemId());
+        reqVO.setBatchId(outboundLine.getBatchId());
+        reqVO.setBatchCode(outboundLine.getBatchCode());
+
+        // 鍙��鏁伴噺 = 鍑哄簱鏁伴噺 - 鍏跺畠閫�璐у崟宸查��鏁伴噺锛堟帓闄ゅ綋鍓嶉��璐у崟锛�
+        BigDecimal returned = getReturnedQuantityMap(
+                Collections.singletonList(outboundLine.getId()), returnSales.getId())
+                .getOrDefault(outboundLine.getId(), BigDecimal.ZERO);
+        BigDecimal returnable = outboundLine.getQuantity().subtract(returned);
+        if (reqVO.getQuantity().compareTo(returnable) > 0) {
+            throw exception(WM_RETURN_SALES_LINE_QUANTITY_EXCEED);
+        }
     }
 
     @Override
@@ -129,6 +172,16 @@
     }
 
     @Override
+    public Map<Long, BigDecimal> getReturnedQuantityMap(Collection<Long> productSalesLineIds, Long excludeReturnId) {
+        if (CollUtil.isEmpty(productSalesLineIds)) {
+            return Collections.emptyMap();
+        }
+        List<KeyValue<Long, BigDecimal>> list = returnSalesLineMapper.selectReturnedQuantityList(
+                productSalesLineIds, MesWmReturnSalesStatusEnum.CANCELED.getStatus(), excludeReturnId);
+        return CollectionUtils.convertMap(list, KeyValue::getKey, KeyValue::getValue);
+    }
+
+    @Override
     public void updateQualityStatusByReturnId(Long returnId, Integer qualityStatus) {
         List<MesWmReturnSalesLineDO> lines = returnSalesLineMapper.selectListByReturnId(returnId);
         for (MesWmReturnSalesLineDO line : lines) {
@@ -155,7 +208,7 @@
      * @param batchId  鎵规ID
      */
     private void validateItemBatchManagement(Long returnId, Long itemId, Long batchId) {
-        MesMdmItemDO item = mdmItemService.validateItemExistsAndEnable(itemId);
+        MdmItemRespDTO item = mdmItemApi.validateItemExistsAndEnable(itemId).getCheckedData();
         // 濡傛灉鐗╂枡鍚敤浜嗘壒娆$鐞嗭紝鍒欏繀椤婚�夋嫨鎵规
         if (Boolean.TRUE.equals(item.getIsBatchManaged()) && batchId == null) {
             throw exception(MD_ITEM_BATCH_REQUIRED);
@@ -189,9 +242,11 @@
             // 1.C.1 鏂板涓�琛屼笉鍚堟牸鍝�
             MesWmReturnSalesLineDO unqualifiedLine = new MesWmReturnSalesLineDO()
                     .setReturnId(sourceLine.getReturnId())
+                    .setProductSalesLineId(sourceLine.getProductSalesLineId())
                     .setItemId(sourceLine.getItemId())
                     .setQuantity(unqualifiedQuantity)
                     .setBatchId(sourceLine.getBatchId())
+                    .setBatchCode(sourceLine.getBatchCode())
                     .setRqcId(sourceLine.getRqcId())
                     .setRqcCheckFlag(sourceLine.getRqcCheckFlag())
                     .setQualityStatus(MesWmQualityStatusEnum.FAIL.getStatus())

--
Gitblit v1.9.3