From 618030e495a9520291ebfd709c80c4c8a8b73451 Mon Sep 17 00:00:00 2001
From: quxin <592870522@qq.com>
Date: 星期五, 04 九月 2026 11:36:26 +0800
Subject: [PATCH] 修复: 生产领料后的退料应该是选择领料的记录,不是选择库存的记录(同时库存数据需要根据领料退料变动,比如库存100,领用80,然后领用80中退料30,库存应由原来的20变成50)

---
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockServiceImpl.java                |    9 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transaction/dto/MesWmTransactionSaveReqDTO.java                 |   15 ++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueServiceImpl.java                    |    3 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/returnissue/MesWmReturnIssueLineDO.java                  |    7 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueService.java                      |   10 ++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueLineServiceImpl.java                |   10 ++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/productissue/MesWmProductIssueMapper.java                     |    8 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/productissue/MesWmProductIssueLineMapper.java                 |    5 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueLineService.java                    |    9 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnissue/vo/line/MesWmReturnIssueLineRespVO.java    |    3 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/returnissue/MesWmReturnIssueLineMapper.java                   |    5 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnissue/vo/line/MesWmReturnIssueLineSaveReqVO.java |    3 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueLineService.java                  |    9 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueServiceImpl.java                  |    5 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productissue/MesWmProductIssueLineController.java      |   10 ++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockService.java                    |   18 +++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueLineServiceImpl.java              |   97 ++++++++++++++++++-
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transaction/MesWmTransactionServiceImpl.java                    |   16 ++
 18 files changed, 227 insertions(+), 15 deletions(-)

diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productissue/MesWmProductIssueLineController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productissue/MesWmProductIssueLineController.java
index 4547c0c..42a1e39 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productissue/MesWmProductIssueLineController.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productissue/MesWmProductIssueLineController.java
@@ -8,6 +8,7 @@
 import cn.iocoder.yudao.module.mes.controller.admin.wm.productissue.vo.line.MesWmProductIssueBomItemRespVO;
 import cn.iocoder.yudao.module.mes.controller.admin.wm.productissue.vo.line.MesWmProductIssueLinePageReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.wm.productissue.vo.line.MesWmProductIssueLineRespVO;
+import cn.iocoder.yudao.module.mes.controller.admin.wm.productissue.vo.line.MesWmProductIssueLineReturnableRespVO;
 import cn.iocoder.yudao.module.mes.controller.admin.wm.productissue.vo.line.MesWmProductIssueLineSaveReqVO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO;
@@ -155,6 +156,15 @@
         return success(result);
     }
 
+    @GetMapping("/returnable-list")
+    @Operation(summary = "鑾峰緱棰嗘枡鍑哄簱鍗曞彲閫�鏂欒鍒楄〃",
+            description = "杩斿洖鎸囧畾宸ュ崟涓嬪凡瀹屾垚棰嗘枡鍗曠殑鍙��鏂欒锛屽彲閫�鏁伴噺 = 宸查鏁伴噺 - 宸查��鏁伴噺")
+    @Parameter(name = "workOrderId", description = "鐢熶骇宸ュ崟ID", required = true, example = "1")
+    @PreAuthorize("@ss.hasPermission('mes:wm-product-issue:query')")
+    public CommonResult<List<MesWmProductIssueLineReturnableRespVO>> getReturnableList(@RequestParam("workOrderId") Long workOrderId) {
+        return success(issueLineService.getReturnableList(workOrderId));
+    }
+
     // ==================== 鎷兼帴 VO ====================
 
     private List<MesWmProductIssueLineRespVO> buildRespVOList(List<MesWmProductIssueLineDO> list) {
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnissue/vo/line/MesWmReturnIssueLineRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnissue/vo/line/MesWmReturnIssueLineRespVO.java
index 3962f79..de54ebf 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnissue/vo/line/MesWmReturnIssueLineRespVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnissue/vo/line/MesWmReturnIssueLineRespVO.java
@@ -16,6 +16,9 @@
     @Schema(description = "閫�鏂欏崟 ID", example = "1")
     private Long issueId;
 
+    @Schema(description = "棰嗘枡鍗曡 ID", example = "1")
+    private Long issueLineId;
+
     @Schema(description = "鐢熶骇浠诲姟 ID", example = "1")
     private Long taskId;
 
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnissue/vo/line/MesWmReturnIssueLineSaveReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnissue/vo/line/MesWmReturnIssueLineSaveReqVO.java
index 197efe5..4a9f422 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnissue/vo/line/MesWmReturnIssueLineSaveReqVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnissue/vo/line/MesWmReturnIssueLineSaveReqVO.java
@@ -18,6 +18,9 @@
     @NotNull(message = "閫�鏂欏崟 ID 涓嶈兘涓虹┖")
     private Long issueId;
 
+    @Schema(description = "棰嗘枡鍗曡 ID", example = "1")
+    private Long issueLineId;
+
     @Schema(description = "鐢熶骇浠诲姟 ID", example = "1")
     private Long taskId;
 
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/returnissue/MesWmReturnIssueLineDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/returnissue/MesWmReturnIssueLineDO.java
index 11c07fa..9f986ba 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/returnissue/MesWmReturnIssueLineDO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/returnissue/MesWmReturnIssueLineDO.java
@@ -39,6 +39,13 @@
      */
     private Long issueId;
     /**
+     * 棰嗘枡鍗曡 ID
+     *
+     * 鍏宠仈 {@link cn.iocoder.yudao.module.mes.dal.dataobject.wm.productissue.MesWmProductIssueLineDO#getId()}
+     * 鐢ㄤ簬杩借釜閫�鏂欐潵婧�
+     */
+    private Long issueLineId;
+    /**
      * 鐢熶骇浠诲姟 ID
      *
      * 鍏宠仈 {@link MesProTaskDO#getId()}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/productissue/MesWmProductIssueLineMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/productissue/MesWmProductIssueLineMapper.java
index 1d3ae22..70e8993 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/productissue/MesWmProductIssueLineMapper.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/productissue/MesWmProductIssueLineMapper.java
@@ -7,6 +7,7 @@
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productissue.MesWmProductIssueLineDO;
 import org.apache.ibatis.annotations.Mapper;
 
+import java.util.Collection;
 import java.util.List;
 
 /**
@@ -29,6 +30,10 @@
         return selectList(MesWmProductIssueLineDO::getIssueId, issueId);
     }
 
+    default List<MesWmProductIssueLineDO> selectListByIssueIds(Collection<Long> issueIds) {
+        return selectList(MesWmProductIssueLineDO::getIssueId, issueIds);
+    }
+
     default void deleteByIssueId(Long issueId) {
         delete(MesWmProductIssueLineDO::getIssueId, issueId);
     }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/productissue/MesWmProductIssueMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/productissue/MesWmProductIssueMapper.java
index 7634eaf..9b72cd6 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/productissue/MesWmProductIssueMapper.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/productissue/MesWmProductIssueMapper.java
@@ -7,6 +7,8 @@
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productissue.MesWmProductIssueDO;
 import org.apache.ibatis.annotations.Mapper;
 
+import java.util.List;
+
 /**
  * MES 棰嗘枡鍑哄簱鍗� Mapper
  */
@@ -29,4 +31,10 @@
         return selectOne(MesWmProductIssueDO::getCode, code);
     }
 
+    default List<MesWmProductIssueDO> selectListByWorkOrderIdAndStatus(Long workOrderId, Integer status) {
+        return selectList(new LambdaQueryWrapperX<MesWmProductIssueDO>()
+                .eq(MesWmProductIssueDO::getWorkOrderId, workOrderId)
+                .eq(MesWmProductIssueDO::getStatus, status));
+    }
+
 }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/returnissue/MesWmReturnIssueLineMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/returnissue/MesWmReturnIssueLineMapper.java
index 6e9397c..935d8fd 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/returnissue/MesWmReturnIssueLineMapper.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/returnissue/MesWmReturnIssueLineMapper.java
@@ -10,6 +10,7 @@
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 
+import java.util.Collection;
 import java.util.List;
 
 /**
@@ -31,6 +32,10 @@
         return selectList(MesWmReturnIssueLineDO::getIssueId, issueId);
     }
 
+    default List<MesWmReturnIssueLineDO> selectListByIssueLineIds(Collection<Long> issueLineIds) {
+        return selectList(MesWmReturnIssueLineDO::getIssueLineId, issueLineIds);
+    }
+
     default void deleteByIssueId(Long issueId) {
         delete(MesWmReturnIssueLineDO::getIssueId, issueId);
     }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockService.java
index aead9ee..f58e2a5 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockService.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockService.java
@@ -117,6 +117,24 @@
                                                   Long batchId, String batchCode, Long vendorId, LocalDateTime receiptTime);
 
     /**
+     * 鍒涘缓鏂扮殑搴撳瓨璁板綍锛堜笉鏌ユ壘宸叉湁璁板綍锛屽缁堝垱寤烘柊璁板綍锛�
+     *
+     * 鐢ㄤ簬閫�鏂欑瓑鍦烘櫙锛岄渶瑕佷负姣忔閫�鏂欏垱寤虹嫭绔嬬殑搴撳瓨璁板綍
+     *
+     * @param itemId      鐗╂枡缂栧彿
+     * @param warehouseId 浠撳簱缂栧彿
+     * @param locationId  搴撳尯缂栧彿
+     * @param areaId      搴撲綅缂栧彿
+     * @param batchId     鎵规缂栧彿
+     * @param batchCode   鎵规鍙�
+     * @param vendorId    渚涘簲鍟嗙紪鍙�
+     * @param receiptTime 鍏ュ簱鏃堕棿锛堜负绌哄垯榛樿褰撳墠鏃堕棿锛�
+     * @return 鏂板垱寤虹殑搴撳瓨璁板綍
+     */
+    MesWmMaterialStockDO createMaterialStock(Long itemId, Long warehouseId, Long locationId, Long areaId,
+                                              Long batchId, String batchCode, Long vendorId, LocalDateTime receiptTime);
+
+    /**
      * 鎸夌粍鍚堥敭鏌ユ壘搴撳瓨璁板綍锛堝彧鏌ヤ笉鍒涘缓锛�
      *
      * @param itemId      鐗╂枡缂栧彿
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockServiceImpl.java
index 1aa86c9..4f368d9 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockServiceImpl.java
@@ -203,6 +203,13 @@
         }
 
         // 2. 涓嶅瓨鍦ㄥ垯鏂板缓
+        return createMaterialStock(itemId, warehouseId, locationId, areaId, batchId, batchCode, vendorId, receiptTime);
+    }
+
+    @Override
+    public MesWmMaterialStockDO createMaterialStock(Long itemId, Long warehouseId, Long locationId, Long areaId,
+                                                     Long batchId, String batchCode, Long vendorId, LocalDateTime receiptTime) {
+        // 濮嬬粓鍒涘缓鏂板簱瀛樿褰�
         MesMdmItemDO item = mdmItemService.validateItemExists(itemId);
         MesWmMaterialStockDO newStock = MesWmMaterialStockDO.builder()
                 .itemId(itemId).itemTypeId(item.getCategoryId())
@@ -213,7 +220,7 @@
                 .frozen(false)
                 .build();
         materialStockMapper.insert(newStock);
-        // 3. 閲嶆柊鏌ヨ锛岀‘淇濇嬁鍒版暟鎹簱鐢熸垚鐨勫畬鏁磋褰曪紙id銆乧reateTime 绛夛級
+        // 閲嶆柊鏌ヨ锛岀‘淇濇嬁鍒版暟鎹簱鐢熸垚鐨勫畬鏁磋褰曪紙id銆乧reateTime 绛夛級
         MesWmMaterialStockDO inserted = materialStockMapper.selectById(newStock.getId());
         return inserted != null ? inserted : newStock;
     }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueLineService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueLineService.java
index ab9b47d..27115c6 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueLineService.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueLineService.java
@@ -2,6 +2,7 @@
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.mes.controller.admin.wm.productissue.vo.line.MesWmProductIssueLinePageReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.wm.productissue.vo.line.MesWmProductIssueLineReturnableRespVO;
 import cn.iocoder.yudao.module.mes.controller.admin.wm.productissue.vo.line.MesWmProductIssueLineSaveReqVO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productissue.MesWmProductIssueLineDO;
 import jakarta.validation.Valid;
@@ -74,4 +75,12 @@
      */
     MesWmProductIssueLineDO validateProductIssueLineExists(Long id);
 
+    /**
+     * 鑾峰彇鎸囧畾宸ュ崟涓嬪凡瀹屾垚棰嗘枡鍗曠殑鍙��鏂欒鍒楄〃
+     *
+     * @param workOrderId 鐢熶骇宸ュ崟ID
+     * @return 鍙��鏂欒鍒楄〃锛堝彲閫�鏁伴噺 > 0锛�
+     */
+    List<MesWmProductIssueLineReturnableRespVO> getReturnableList(Long workOrderId);
+
 }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueLineServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueLineServiceImpl.java
index 4ed4684..6b5141b 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueLineServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueLineServiceImpl.java
@@ -2,25 +2,41 @@
 
 import cn.hutool.core.collection.CollUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
+import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.module.mes.controller.admin.wm.productissue.vo.line.MesWmProductIssueLinePageReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.wm.productissue.vo.line.MesWmProductIssueLineRespVO;
+import cn.iocoder.yudao.module.mes.controller.admin.wm.productissue.vo.line.MesWmProductIssueLineReturnableRespVO;
 import cn.iocoder.yudao.module.mes.controller.admin.wm.productissue.vo.line.MesWmProductIssueLineSaveReqVO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderBomDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productissue.MesWmProductIssueDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productissue.MesWmProductIssueLineDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnissue.MesWmReturnIssueLineDO;
 import cn.iocoder.yudao.module.mes.dal.mysql.wm.productissue.MesWmProductIssueLineMapper;
-import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService;
+import cn.iocoder.yudao.module.mes.enums.wm.MesWmProductIssueStatusEnum;
+import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
+import cn.iocoder.yudao.module.mes.service.md.unitmeasure.MesMdUnitMeasureService;
 import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderBomService;
+import cn.iocoder.yudao.module.mes.service.wm.productissue.MesWmProductIssueDetailService;
+import cn.iocoder.yudao.module.mes.service.wm.productissue.MesWmProductIssueLineService;
+import cn.iocoder.yudao.module.mes.service.wm.productissue.MesWmProductIssueService;
+import cn.iocoder.yudao.module.mes.service.wm.returnissue.MesWmReturnIssueLineService;
 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.ArrayList;
+import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_PRODUCT_ISSUE_LINE_ITEM_NOT_IN_BOM;
-import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_PRODUCT_ISSUE_LINE_NOT_EXISTS;
+import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
 
 /**
  * MES 棰嗘枡鍑哄簱鍗曡 Service 瀹炵幇绫�
@@ -36,12 +52,17 @@
     @Lazy
     private MesWmProductIssueService issueService;
     @Resource
-    private MesMdmItemService mdmItemService;
+    private MesMdItemService mdmItemService;
     @Resource
-    private MesProWorkOrderBomService workOrderBomService;
+    @Lazy
+    private MesWmReturnIssueLineService returnIssueLineService;
+    @Resource
+    private MesMdUnitMeasureService unitMeasureService;
     @Resource
     @Lazy
     private MesWmProductIssueDetailService issueDetailService;
+    @Resource
+    private MesProWorkOrderBomService workOrderBomService;
 
     @Override
     public Long createProductIssueLine(MesWmProductIssueLineSaveReqVO createReqVO) {
@@ -114,7 +135,7 @@
         // 鏍¢獙鐗╂枡鍦ㄥ伐鍗� BOM 涓�
         validateItemInWorkOrderBom(reqVO.getIssueId(), reqVO.getItemId());
         // 鏍¢獙鐗╂枡瀛樺湪
-        mdmItemService.mesValidateItemExists(reqVO.getItemId());
+        mdmItemService.validateItemExists(reqVO.getItemId());
     }
 
     private void validateItemInWorkOrderBom(Long issueId, Long itemId) {
@@ -132,4 +153,68 @@
         }
     }
 
+    @Override
+    public List<MesWmProductIssueLineReturnableRespVO> getReturnableList(Long workOrderId) {
+        // 1. 鏌ヨ璇ュ伐鍗曚笅宸插畬鎴愮殑棰嗘枡鍗�
+        List<MesWmProductIssueDO> finishedIssues = issueService.getFinishedIssuesByWorkOrderId(workOrderId);
+        if (CollUtil.isEmpty(finishedIssues)) {
+            return Collections.emptyList();
+        }
+
+        // 2. 鑾峰彇杩欎簺棰嗘枡鍗曠殑鎵�鏈夎
+        List<Long> issueIds = CollectionUtils.convertList(finishedIssues, MesWmProductIssueDO::getId);
+        List<MesWmProductIssueLineDO> lines = issueLineMapper.selectListByIssueIds(issueIds);
+        if (CollUtil.isEmpty(lines)) {
+            return Collections.emptyList();
+        }
+
+        // 3. 鎵归噺鏌ヨ宸查��鏁伴噺锛堟寜 issueLineId 姹囨�伙級
+        List<Long> lineIds = CollectionUtils.convertList(lines, MesWmProductIssueLineDO::getId);
+        List<MesWmReturnIssueLineDO> returnLines = returnIssueLineService.getReturnIssueLineListByIssueLineIds(lineIds);
+        // 鎸� issueLineId 姹囨�婚��鏂欐暟閲�
+        Map<Long, BigDecimal> returnedQtyMap = new java.util.HashMap<>();
+        for (MesWmReturnIssueLineDO returnLine : returnLines) {
+            if (returnLine.getIssueLineId() != null && returnLine.getQuantity() != null) {
+                returnedQtyMap.merge(returnLine.getIssueLineId(), returnLine.getQuantity(), BigDecimal::add);
+            }
+        }
+
+        // 4. 鎵归噺鏌ヨ鐗╂枡鍜屽崟浣嶄俊鎭�
+        Map<Long, MesMdItemDO> itemMap = mdmItemService.getItemMap(
+                CollectionUtils.convertSet(lines, MesWmProductIssueLineDO::getItemId));
+        Map<Long, MesMdUnitMeasureDO> unitMap = unitMeasureService.getUnitMeasureMap(
+                CollectionUtils.convertSet(itemMap.values(), MesMdItemDO::getUnitMeasureId));
+
+        // 5. 缁勮缁撴灉锛岃繃婊ゅ彲閫�鏁伴噺 > 0 鐨勮
+        List<MesWmProductIssueLineReturnableRespVO> result = new ArrayList<>();
+        for (MesWmProductIssueLineDO line : lines) {
+            BigDecimal issuedQty = line.getQuantity() != null ? line.getQuantity() : BigDecimal.ZERO;
+            BigDecimal returnedQty = returnedQtyMap.getOrDefault(line.getId(), BigDecimal.ZERO);
+            BigDecimal returnableQty = issuedQty.subtract(returnedQty);
+
+            if (returnableQty.compareTo(BigDecimal.ZERO) > 0) {
+                MesWmProductIssueLineReturnableRespVO vo = new MesWmProductIssueLineReturnableRespVO();
+                vo.setId(line.getId());
+                vo.setItemId(line.getItemId());
+                vo.setBatchId(line.getBatchId());
+                vo.setQuantity(returnableQty);
+
+                // 濉厖鐗╂枡淇℃伅
+                MesMdItemDO item = itemMap.get(line.getItemId());
+                if (item != null) {
+                    vo.setItemCode(item.getCode());
+                    vo.setItemName(item.getName());
+                    vo.setSpecification(item.getSpecification());
+                    // 濉厖鍗曚綅淇℃伅
+                    MesMdUnitMeasureDO unit = unitMap.get(item.getUnitMeasureId());
+                    vo.setUnitMeasureName(unit != null ? unit.getName() : null);
+                }
+
+                result.add(vo);
+            }
+        }
+
+        return result;
+    }
+
 }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueService.java
index 3ed9ecb..719ac56 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueService.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueService.java
@@ -6,6 +6,8 @@
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productissue.MesWmProductIssueDO;
 import jakarta.validation.Valid;
 
+import java.util.List;
+
 /**
  * MES 棰嗘枡鍑哄簱鍗� Service 鎺ュ彛
  */
@@ -101,4 +103,12 @@
      */
     Boolean checkProductIssueQuantity(Long id);
 
+    /**
+     * 鑾峰彇鎸囧畾宸ュ崟涓嬪凡瀹屾垚鐨勯鏂欏崟鍒楄〃
+     *
+     * @param workOrderId 鐢熶骇宸ュ崟ID
+     * @return 宸插畬鎴愮殑棰嗘枡鍗曞垪琛�
+     */
+    List<MesWmProductIssueDO> getFinishedIssuesByWorkOrderId(Long workOrderId);
+
 }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueServiceImpl.java
index ee24926..3164436 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueServiceImpl.java
@@ -410,4 +410,9 @@
         }
     }
 
+    @Override
+    public List<MesWmProductIssueDO> getFinishedIssuesByWorkOrderId(Long workOrderId) {
+        return issueMapper.selectListByWorkOrderIdAndStatus(workOrderId, MesWmProductIssueStatusEnum.FINISHED.getStatus());
+    }
+
 }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueLineService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueLineService.java
index 3657104..f7f175e 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueLineService.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueLineService.java
@@ -8,6 +8,7 @@
 import jakarta.validation.Valid;
 
 import java.math.BigDecimal;
+import java.util.Collection;
 import java.util.List;
 
 /**
@@ -104,4 +105,12 @@
     void updateReturnIssueLineWhenRqcFinish(Long sourceLineId, Long sourceDocId, Integer checkResult,
                                             BigDecimal qualifiedQuantity, BigDecimal unqualifiedQuantity);
 
+    /**
+     * 鏍规嵁棰嗘枡鍗曡ID鍒楄〃鑾峰彇閫�鏂欏崟琛屽垪琛�
+     *
+     * @param issueLineIds 棰嗘枡鍗曡ID鍒楄〃
+     * @return 閫�鏂欏崟琛屽垪琛�
+     */
+    List<MesWmReturnIssueLineDO> getReturnIssueLineListByIssueLineIds(Collection<Long> issueLineIds);
+
 }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueLineServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueLineServiceImpl.java
index 8d1d856..4d93f66 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueLineServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueLineServiceImpl.java
@@ -20,6 +20,8 @@
 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.Objects;
 
@@ -205,4 +207,12 @@
         }
     }
 
+    @Override
+    public List<MesWmReturnIssueLineDO> getReturnIssueLineListByIssueLineIds(Collection<Long> issueLineIds) {
+        if (CollUtil.isEmpty(issueLineIds)) {
+            return Collections.emptyList();
+        }
+        return issueLineMapper.selectListByIssueLineIds(issueLineIds);
+    }
+
 }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueServiceImpl.java
index c84bcc0..a00a8fd 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueServiceImpl.java
@@ -268,12 +268,13 @@
                 }
             }
 
-            // 2.3 鍏ヤ笂鏋剁洰鏍囦粨搴擄紙搴撳瓨澧炲姞锛�
+            // 2.3 鍏ヤ笂鏋剁洰鏍囦粨搴擄紙搴撳瓨澧炲姞锛夆�� 濮嬬粓鍒涘缓鏂扮殑搴撳瓨璁板綍锛屼笉鏇存柊宸叉湁璁板綍
             wmTransactionService.createTransaction(new MesWmTransactionSaveReqDTO()
                     .setType(MesWmTransactionTypeEnum.IN.getType()).setItemId(detail.getItemId())
                     .setQuantity(detail.getQuantity()) // 搴撳瓨澧炲姞锛堥��鏂欏洖浠擄級
                     .setBatchId(detail.getBatchId()).setBatchCode(detail.getBatchCode())
                     .setWarehouseId(targetWarehouseId).setLocationId(targetLocationId).setAreaId(targetAreaId)
+                    .setForceCreateNewStock(true) // 寮哄埗鍒涘缓鏂板簱瀛樿褰�
                     .setBizType(MesBizTypeConstants.WM_RETURN_ISSUE).setBizId(issue.getId())
                     .setBizCode(issue.getCode()).setBizLineId(detail.getLineId())
                     .setRelatedTransactionId(outTransactionId)); // 鍏宠仈鍑哄簱浜嬪姟
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transaction/MesWmTransactionServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transaction/MesWmTransactionServiceImpl.java
index 031dc5d..775a75e 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transaction/MesWmTransactionServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transaction/MesWmTransactionServiceImpl.java
@@ -81,6 +81,8 @@
         MesWmMaterialStockDO materialStock;
         MesWmTransactionTypeEnum transactionType = MesWmTransactionTypeEnum.valueOf(reqDTO.getType());
         boolean isInbound = transactionType != null && transactionType.isInbound();
+        boolean forceCreateNewStock = Boolean.TRUE.equals(reqDTO.getForceCreateNewStock());
+
         if (reqDTO.getMaterialStockId() != null) {
             // 浼犲叆浜� materialStockId锛岀洿鎺ヤ娇鐢�
             materialStock = materialStockService.getMaterialStock(reqDTO.getMaterialStockId());
@@ -95,10 +97,16 @@
                 throw exception(WM_MATERIAL_STOCK_SELECTION_MISMATCH);
             }
         } else if (isInbound) {
-            // 鍏ュ簱浜嬪姟锛氭壘涓嶅埌鍒欒嚜鍔ㄥ垱寤哄簱瀛樿褰�
-            materialStock = materialStockService.getOrCreateMaterialStock(
-                    reqDTO.getItemId(), reqDTO.getWarehouseId(), reqDTO.getLocationId(), reqDTO.getAreaId(),
-                    reqDTO.getBatchId(), reqDTO.getBatchCode(), reqDTO.getVendorId(), reqDTO.getReceiptTime());
+            // 鍏ュ簱浜嬪姟锛氭牴鎹� forceCreateNewStock 鏍囧織鍐冲畾鏄垱寤烘柊璁板綍杩樻槸鏌ユ壘/鍒涘缓
+            if (forceCreateNewStock) {
+                materialStock = materialStockService.createMaterialStock(
+                        reqDTO.getItemId(), reqDTO.getWarehouseId(), reqDTO.getLocationId(), reqDTO.getAreaId(),
+                        reqDTO.getBatchId(), reqDTO.getBatchCode(), reqDTO.getVendorId(), reqDTO.getReceiptTime());
+            } else {
+                materialStock = materialStockService.getOrCreateMaterialStock(
+                        reqDTO.getItemId(), reqDTO.getWarehouseId(), reqDTO.getLocationId(), reqDTO.getAreaId(),
+                        reqDTO.getBatchId(), reqDTO.getBatchCode(), reqDTO.getVendorId(), reqDTO.getReceiptTime());
+            }
         } else {
             // 鍑哄簱浜嬪姟锛氬簱瀛樿褰曞繀椤诲凡瀛樺湪锛屼笉鍏佽鑷姩鍒涘缓锛堥櫎闈� checkFlag=false 鍏佽璐熷簱瀛橈級
             materialStock = materialStockService.getMaterialStockByCompositeKey(
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transaction/dto/MesWmTransactionSaveReqDTO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transaction/dto/MesWmTransactionSaveReqDTO.java
index d0ae46a..62e95c8 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transaction/dto/MesWmTransactionSaveReqDTO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transaction/dto/MesWmTransactionSaveReqDTO.java
@@ -107,14 +107,23 @@
     private Long materialStockId;
 
     /**
-     * 鍏宠仈浜嬪姟 ID锛堟垚瀵逛簨鍔′腑锛屽叆搴撲簨鍔″叧鑱斿嚭搴撲簨鍔★級
+     * 鍏宠仈浜嬪姟 ID
      */
     private Long relatedTransactionId;
 
-    // ===== 鍙�夋帶鍒讹紙闈炲瓨鍌ㄥ瓧娈碉級 =====
+    /**
+     * 鏄惁寮哄埗鍒涘缓鏂板簱瀛樿褰曪紙浠呭叆搴撲簨鍔℃湁鏁堬級
+     *
+     * 涓� true 鏃讹紝鍏ュ簱浜嬪姟濮嬬粓鍒涘缓鏂扮殑搴撳瓨璁板綍锛屼笉鏌ユ壘宸叉湁璁板綍
+     * 鐢ㄤ簬閫�鏂欑瓑鍦烘櫙锛岄渶瑕佷负姣忔閫�鏂欏垱寤虹嫭绔嬬殑搴撳瓨璁板綍
+     */
+    private Boolean forceCreateNewStock;
 
     /**
-     * 鏄惁鏍¢獙搴撳瓨鍏呰冻锛堥粯璁� true锛涚嚎杈瑰簱娑堣�楃瓑鍦烘櫙璁� false 鍏佽璐熷簱瀛橈級
+     * 鏄惁鏍¢獙搴撳瓨鍏呰冻锛堜粎鍑哄簱浜嬪姟鏈夋晥锛�
+     *
+     * 涓� true锛堥粯璁わ級鏃讹紝鍑哄簱鏁伴噺涓嶈兘瓒呰繃鐜版湁搴撳瓨
+     * 涓� false 鏃讹紝鍏佽璐熷簱瀛橈紙濡傝櫄鎷熺嚎杈瑰簱鍦烘櫙锛�
      */
     private Boolean checkFlag;
 

--
Gitblit v1.9.3