From 67c5d3ea86cfaad45c0150b96949dbcb6729b4d0 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期一, 13 七月 2026 11:58:11 +0800
Subject: [PATCH] 1.仓储物流-库存可用量管理

---
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productsales/vo/MesWmProductSalesRespVO.java    |    6 
 docs/stock_available_quantity_integration.md                                                                                   |   96 ++++++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/salesnotice/vo/MesWmSalesNoticeRespVO.java      |    6 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmStockReserveBizTypeEnum.java                          |   38 ++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/salesnotice/MesWmSalesNoticeController.java     |   14 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/salesnotice/MesWmSalesNoticeDO.java               |    2 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesServiceImpl.java           |   29 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/materialstock/MesWmMaterialStockMapper.java            |   34 ++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/productsales/MesWmProductSalesDO.java             |    2 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/stockreserve/MesWmStockReserveService.java               |   73 +++++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmStockReserveStatusEnum.java                           |   36 ++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnsales/MesWmReturnSalesController.java     |   14 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/stockreserve/MesWmStockReserveMapper.java              |   64 ++++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/materialstock/MesWmMaterialStockDO.java           |   12 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/stockreserve/MesWmStockReserveDO.java             |   83 +++++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnsales/vo/MesWmReturnSalesRespVO.java      |    8 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/stockreserve/MesWmStockReserveServiceImpl.java           |  152 ++++++++++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productsales/MesWmProductSalesController.java   |   14 
 yudao-module-mes/pom.xml                                                                                                       |    7 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockRespVO.java  |   16 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/returnsales/MesWmReturnSalesDO.java               |    3 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueServiceImpl.java           |   30 ++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/MesWmMaterialStockController.java |    6 
 .claude/rules/frontend-doc-generation.md                                                                                       |   49 ++-
 24 files changed, 730 insertions(+), 64 deletions(-)

diff --git a/.claude/rules/frontend-doc-generation.md b/.claude/rules/frontend-doc-generation.md
index 56922b4..c9e3663 100644
--- a/.claude/rules/frontend-doc-generation.md
+++ b/.claude/rules/frontend-doc-generation.md
@@ -11,13 +11,24 @@
 
 鍚庣鎺ュ彛鍙樻洿鏃讹紝**涓嶇敓鎴愬墠绔唬鐮�**锛屽彧鐢熸垚鍓嶇鑱旇皟鏂规鏂囨。锛岃緭鍑哄埌 `docs/` 鐩綍銆�
 
+## 绂佹鍐呭
+
+- 鉂� 绂佹鍐� Vue 妯℃澘浠g爜锛坄<el-table-column>`銆乣<el-form-item>` 绛夛級
+- 鉂� 绂佹鍐� JavaScript/TypeScript 浠g爜锛堟柟娉曘�佸嚱鏁般�侀�昏緫鍒ゆ柇绛夛級
+- 鉂� 绂佹鍐� CSS 鏍峰紡浠g爜
+- 鉂� 绂佹鍐欏墠绔粍浠朵娇鐢ㄧず渚�
+
 ## 鑱旇皟鏂规鍐呭瑕佹眰
 
+鏂囨。搴斿彧鍖呭惈浠ヤ笅鍐呭锛�
+
 - **API 璇存槑**锛氳矾寰勩�佹柟娉曘�佽姹傚弬鏁般�佸搷搴旀牸寮�
-- **娑夊強鐨勫墠绔〉闈�**锛氬垪鍑洪渶瑕佷慨鏀圭殑椤甸潰
+- **娑夊強鐨勫墠绔〉闈�**锛氬垪鍑洪渶瑕佷慨鏀圭殑椤甸潰鍚嶇О
 - **鏁版嵁甯﹀叆璇存槑**锛氶噸鐐瑰己璋冧俊鎭浣曚粠涓婁竴姝ュ甫鍏ワ紙濡傞噰璐鍗� -> 鍒拌揣閫氱煡 -> 閲囪喘鍏ュ簱锛�
 - **鎺ュ彛璋冪敤鏃跺簭**锛氳鏄庝綍鏃惰皟鐢ㄥ摢涓帴鍙h幏鍙栧叧鑱旀暟鎹�
-- **琛ㄥ崟瀛楁鏄犲皠**锛氭槑纭摢浜涘瓧娈典粠涓婃父鏁版嵁鑷姩濉厖锛屽摢浜涢渶瑕佺敤鎴锋墜鍔ㄨ緭鍏�
+- **瀛楁灞曠ず瑙勫垯**锛氱敤琛ㄦ牸璇存槑瀛楁灞曠ず浣嶇疆鍜岃鏄庯紝涓嶅啓浠g爜
+- **涓氬姟瑙勫垯璇存槑**锛氱敤鏂囧瓧鎴栬〃鏍艰鏄庝笟鍔¢�昏緫
+- **娉ㄦ剰浜嬮」**锛氶渶瑕佸墠绔敞鎰忕殑杈圭晫鎯呭喌
 
 ## 鏂囨。妯℃澘
 
@@ -46,26 +57,34 @@
 |------|------|------|------|
 | xxx | String | 鏄� | xxx |
 
-**鍝嶅簲锛�** `{ "code": 200, "msg": "鎿嶄綔鎴愬姛" }`
+**鍝嶅簲瀛楁鏂板锛�**
 
-## 鍓嶇瀹炵幇瑕佺偣
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| xxx | String | xxx |
 
-### 1. 鏁版嵁鑾峰彇鏃舵満
+**鍝嶅簲绀轰緥锛�**
 
-- 椤甸潰鎵撳紑鏃惰皟鐢� XXX 鎺ュ彛鑾峰彇閲囪喘璁㈠崟淇℃伅
-- 閫夋嫨閲囪喘璁㈠崟鍚庯紝璋冪敤 YYY 鎺ュ彛鑾峰彇鐗╂枡鏄庣粏
+```json
+{
+  "code": 0,
+  "data": {}
+}
+```
 
-### 2. 瀛楁鑷姩濉厖瑙勫垯
+## 瀛楁灞曠ず瑙勫垯
 
-| 鐩爣瀛楁 | 鏁版嵁鏉ユ簮 | 濉厖鏃舵満 |
-|----------|----------|----------|
-| 鐗╂枡缂栫爜 | 閲囪喘璁㈠崟琛�.itemCode | 閫夋嫨璁㈠崟琛屽悗 |
-| 鐗╂枡鍚嶇О | 閲囪喘璁㈠崟琛�.itemName | 閫夋嫨璁㈠崟琛屽悗 |
+| 瀛楁 | 灞曠ず浣嶇疆 | 璇存槑 |
+|------|----------|------|
+| 瀛楁A | 鍒楄〃銆佽鎯� | 璇存槑 |
+| 瀛楁B | 璇︽儏 | 璇存槑 |
 
-### 3. 鐢ㄦ埛鍙紪杈戝瓧娈�
+## 涓氬姟瑙勫垯璇存槑
 
-- 瀛楁A锛氱敤鎴峰彲淇敼
-- 瀛楁B锛氱敤鎴峰彲淇敼
+| 鍦烘櫙 | 瑙勫垯 |
+|------|------|
+| 鍦烘櫙1 | 瑙勫垯璇存槑 |
+| 鍦烘櫙2 | 瑙勫垯璇存槑 |
 
 ## 娉ㄦ剰浜嬮」
 
diff --git a/docs/stock_available_quantity_integration.md b/docs/stock_available_quantity_integration.md
new file mode 100644
index 0000000..ee074f6
--- /dev/null
+++ b/docs/stock_available_quantity_integration.md
@@ -0,0 +1,96 @@
+# 搴撳瓨鍙敤閲忕鐞� - 鍓嶇鑱旇皟鏂规
+
+## 娑夊強椤甸潰
+
+- 搴撳瓨鐜版湁閲忛〉闈�
+
+## 涓氬姟娴佺▼涓庢暟鎹甫鍏�
+
+鏈涓哄簱瀛樺彴璐﹀瓧娈垫墿灞曪紝鏃犺法椤甸潰鏁版嵁娴佽浆銆�
+
+## API
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| GET | /mes/wm/material-stock/page | 搴撳瓨鍙拌处鍒嗛〉鏌ヨ |
+| GET | /mes/wm/material-stock/get | 鍗曟潯搴撳瓨鏌ヨ |
+
+**鍝嶅簲瀛楁鏂板锛�**
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| frozenQuantity | Decimal | 鍐荤粨鏁伴噺 |
+| reservedQuantity | Decimal | 鍗犵敤閲忥紙宸插垎閰嶅緟鍑哄簱锛� |
+| inTransitQuantity | Decimal | 鍦ㄩ�旈噺锛堣皟鎷ㄥ湪閫旓級 |
+| availableQuantity | Decimal | 鍙敤閲忥紙鍚庣璁$畻锛氬湪搴撴暟閲� - 鍐荤粨鏁伴噺 - 鍗犵敤閲忥級 |
+
+**鍝嶅簲绀轰緥锛�**
+
+```json
+{
+  "code": 0,
+  "data": {
+    "list": [
+      {
+        "id": 1,
+        "itemCode": "M001",
+        "itemName": "閽㈡澘",
+        "quantity": 100.00,
+        "frozenQuantity": 10.00,
+        "reservedQuantity": 20.00,
+        "inTransitQuantity": 5.00,
+        "availableQuantity": 70.00
+      }
+    ],
+    "total": 1
+  }
+}
+```
+
+## 鍓嶇瀹炵幇瑕佺偣
+
+### 1. 鏁版嵁鑾峰彇鏃舵満
+
+- 椤甸潰鎵撳紑鏃惰皟鐢ㄥ簱瀛樺彴璐︽煡璇㈡帴鍙o紝鍝嶅簲涓凡鍖呭惈鏂板瀛楁
+- 鏃犻渶棰濆鎺ュ彛璋冪敤
+
+### 2. 瀛楁灞曠ず瑙勫垯
+
+| 瀛楁 | 灞曠ず浣嶇疆 | 璇存槑 |
+|------|----------|------|
+| 鍦ㄥ簱鏁伴噺 | 鍒楄〃銆佽鎯� | 鐜版湁瀛楁锛屼繚鎸佷笉鍙� |
+| 鍐荤粨鏁伴噺 | 鍒楄〃銆佽鎯� | 鏂板瀛楁锛屽彲鑳戒负 null 鏃舵樉绀� 0 |
+| 鍗犵敤閲� | 鍒楄〃銆佽鎯� | 鏂板瀛楁锛屽彲鑳戒负 null 鏃舵樉绀� 0 |
+| 鍦ㄩ�旈噺 | 鍒楄〃銆佽鎯� | 鏂板瀛楁锛屽彲鑳戒负 null 鏃舵樉绀� 0 |
+| 鍙敤閲� | 鍒楄〃銆佽鎯� | 鍚庣宸茶绠楋紝鐩存帴灞曠ず锛涗负璐熸暟鏃跺缓璁珮浜彁绀� |
+
+### 3. 鐢ㄦ埛鍙紪杈戝瓧娈�
+
+鏃犳柊澧炲彲缂栬緫瀛楁锛屾湰娆′粎涓哄睍绀虹被瀛楁鎵╁睍銆�
+
+## 涓氬姟瑙勫垯璇存槑
+
+### 1. 鍚勫瓧娈靛惈涔�
+
+| 瀛楁 | 鍚箟 |
+|------|------|
+| 鍦ㄥ簱鏁伴噺 | 浠撳簱涓疄闄呭瓨鍦ㄧ殑搴撳瓨 |
+| 鍐荤粨鏁伴噺 | 鍥犵洏鐐广�佽川妫�绛夊師鍥犺鍐荤粨鐨勫簱瀛� |
+| 鍗犵敤閲� | 宸插垎閰嶅緟鍑哄簱鐨勬暟閲忥紙鎷h揣鍚庢湭鍑哄簱锛� |
+| 鍦ㄩ�旈噺 | 璋冩嫧鍦ㄩ�旓紙婧愪粨搴撳凡鍑哄簱锛岀洰鏍囦粨搴撴湭鍏ュ簱锛� |
+| 鍙敤閲� | 瀹為檯鍙敤浜庡垎閰嶇殑搴撳瓨 |
+
+### 2. 鍗犵敤閲忎骇鐢熶笌閲婃斁鍦烘櫙
+
+| 鍦烘櫙 | 鏃舵満 | 褰卞搷 |
+|------|------|------|
+| 閿�鍞嚭搴撴嫞璐� | 鎷h揣鎿嶄綔鏃� | 鍗犵敤閲忓鍔狅紝鍙敤閲忓噺灏� |
+| 鐢熶骇棰嗘枡鎷h揣 | 鎷h揣鎿嶄綔鏃� | 鍗犵敤閲忓鍔狅紝鍙敤閲忓噺灏� |
+| 鍑哄簱瀹屾垚 | 鎵ц鍑哄簱鏃� | 鍗犵敤閲忓噺灏戯紝鍦ㄥ簱鏁伴噺鍑忓皯 |
+| 鍗曟嵁鍙栨秷 | 鍙栨秷鍑哄簱鍗曟椂 | 鍗犵敤閲忛噴鏀撅紝鍙敤閲忔仮澶� |
+
+## 娉ㄦ剰浜嬮」
+
+- 鏂板瀛楁鍦ㄥ巻鍙叉暟鎹腑鍙兘涓� null锛屽墠绔睍绀烘椂闇�鍋氱┖鍊煎鐞嗭紝榛樿鏄剧ず 0
+- 鍙敤閲忕敱鍚庣璁$畻锛屽墠绔洿鎺ヤ娇鐢紝鏃犻渶鑷璁$畻
+- 瀵煎嚭 Excel 宸插寘鍚柊瀛楁锛屾棤闇�棰濆澶勭悊
\ No newline at end of file
diff --git a/yudao-module-mes/pom.xml b/yudao-module-mes/pom.xml
index 74c0908..4f6e41f 100644
--- a/yudao-module-mes/pom.xml
+++ b/yudao-module-mes/pom.xml
@@ -31,6 +31,13 @@
             <version>${revision}</version>
         </dependency>
 
+        <!-- CRM 瀹㈡埛绠$悊 API -->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-module-crm-api</artifactId>
+            <version>${revision}</version>
+        </dependency>
+
         <!-- BPM 宸ヤ綔娴� API -->
         <dependency>
             <groupId>cn.iocoder.boot</groupId>
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/MesWmMaterialStockController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/MesWmMaterialStockController.java
index 16ce9b8..a665b66 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/MesWmMaterialStockController.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/MesWmMaterialStockController.java
@@ -36,6 +36,7 @@
 import org.springframework.web.bind.annotation.*;
 
 import java.io.IOException;
+import java.math.BigDecimal;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -141,6 +142,11 @@
                     area -> vo.setAreaName(area.getName()));
             MapUtils.findAndThen(vendorMap, vo.getVendorId(),
                     vendor -> vo.setVendorName(vendor.getName()));
+            // 璁$畻鍙敤閲� = 鍦ㄥ簱鏁伴噺 - 鍐荤粨鏁伴噺 - 鍗犵敤閲�
+            BigDecimal quantity = vo.getQuantity() != null ? vo.getQuantity() : BigDecimal.ZERO;
+            BigDecimal frozenQty = vo.getFrozenQuantity() != null ? vo.getFrozenQuantity() : BigDecimal.ZERO;
+            BigDecimal reservedQty = vo.getReservedQuantity() != null ? vo.getReservedQuantity() : BigDecimal.ZERO;
+            vo.setAvailableQuantity(quantity.subtract(frozenQty).subtract(reservedQty));
         });
     }
 
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockRespVO.java
index 03349b3..8552e20 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockRespVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockRespVO.java
@@ -81,6 +81,22 @@
     @ExcelProperty("鍦ㄥ簱鏁伴噺")
     private BigDecimal quantity;
 
+    @Schema(description = "鍐荤粨鏁伴噺", example = "10.0000")
+    @ExcelProperty("鍐荤粨鏁伴噺")
+    private BigDecimal frozenQuantity;
+
+    @Schema(description = "鍗犵敤閲忥紙宸插垎閰嶅緟鍑哄簱锛�", example = "20.0000")
+    @ExcelProperty("鍗犵敤閲�")
+    private BigDecimal reservedQuantity;
+
+    @Schema(description = "鍦ㄩ�旈噺锛堣皟鎷ㄥ湪閫旓級", example = "5.0000")
+    @ExcelProperty("鍦ㄩ�旈噺")
+    private BigDecimal inTransitQuantity;
+
+    @Schema(description = "鍙敤閲忥紙鍦ㄥ簱鏁伴噺 - 鍐荤粨鏁伴噺 - 鍗犵敤閲忥級", example = "70.0000")
+    @ExcelProperty("鍙敤閲�")
+    private BigDecimal availableQuantity;
+
     @Schema(description = "鍏ュ簱鏃堕棿")
     @ExcelProperty("鍏ュ簱鏃堕棿")
     private LocalDateTime receiptTime;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productsales/MesWmProductSalesController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productsales/MesWmProductSalesController.java
index cdd84a8..21ed7b7 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productsales/MesWmProductSalesController.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productsales/MesWmProductSalesController.java
@@ -8,14 +8,14 @@
 import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi;
+import cn.iocoder.yudao.module.crm.api.customer.dto.CrmCustomerRespDTO;
 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesPageReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesRespVO;
 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesSaveReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesShippingReqVO;
-import cn.iocoder.yudao.module.mes.dal.dataobject.md.client.MesMdClientDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.salesnotice.MesWmSalesNoticeDO;
-import cn.iocoder.yudao.module.mes.service.md.client.MesMdClientService;
 import cn.iocoder.yudao.module.mes.service.wm.productsales.MesWmProductSalesService;
 import cn.iocoder.yudao.module.mes.service.wm.salesnotice.MesWmSalesNoticeService;
 import io.swagger.v3.oas.annotations.Operation;
@@ -44,7 +44,7 @@
     @Resource
     private MesWmProductSalesService productSalesService;
     @Resource
-    private MesMdClientService clientService;
+    private CrmCustomerApi crmCustomerApi;
     @Resource
     private MesWmSalesNoticeService salesNoticeService;
 
@@ -164,17 +164,17 @@
             return Collections.emptyList();
         }
         // 1. 鑾峰緱鍏宠仈鏁版嵁
-        Map<Long, MesMdClientDO> clientMap = clientService.getClientMap(
+        Map<Long, CrmCustomerRespDTO> customerMap = crmCustomerApi.getCustomerMap(
                 convertSet(list, MesWmProductSalesDO::getClientId));
         Map<Long, MesWmSalesNoticeDO> noticeMap = salesNoticeService.getSalesNoticeMap(
                 convertSet(list, MesWmProductSalesDO::getNoticeId));
         // 2. 鏋勫缓缁撴灉
         return BeanUtils.toBean(list, MesWmProductSalesRespVO.class, vo -> {
-            MapUtils.findAndThen(clientMap, vo.getClientId(), client ->
-                    vo.setClientName(client.getName()).setClientCode(client.getCode()));
+            MapUtils.findAndThen(customerMap, vo.getClientId(), customer ->
+                    vo.setClientName(customer.getName()));
             MapUtils.findAndThen(noticeMap, vo.getNoticeId(),
                     notice -> vo.setNoticeCode(notice.getCode()));
         });
     }
 
-}
+}
\ No newline at end of file
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productsales/vo/MesWmProductSalesRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productsales/vo/MesWmProductSalesRespVO.java
index 4d67b5e..5fc1d88 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productsales/vo/MesWmProductSalesRespVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productsales/vo/MesWmProductSalesRespVO.java
@@ -46,10 +46,6 @@
     @ExcelProperty("鍙戣揣閫氱煡鍗曠紪鍙�")
     private String noticeCode;
 
-    @Schema(description = "瀹㈡埛缂栫爜", example = "C001")
-    @ExcelProperty("瀹㈡埛缂栫爜")
-    private String clientCode;
-
     @Schema(description = "鏀惰揣浜�", example = "寮犱笁")
     @ExcelProperty("鏀惰揣浜�")
     private String contactName;
@@ -82,4 +78,4 @@
     @ExcelProperty("鍒涘缓鏃堕棿")
     private LocalDateTime createTime;
 
-}
+}
\ No newline at end of file
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnsales/MesWmReturnSalesController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnsales/MesWmReturnSalesController.java
index 074e7b1..3738d85 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnsales/MesWmReturnSalesController.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnsales/MesWmReturnSalesController.java
@@ -8,12 +8,12 @@
 import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi;
+import cn.iocoder.yudao.module.crm.api.customer.dto.CrmCustomerRespDTO;
 import cn.iocoder.yudao.module.mes.controller.admin.wm.returnsales.vo.MesWmReturnSalesPageReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.wm.returnsales.vo.MesWmReturnSalesRespVO;
 import cn.iocoder.yudao.module.mes.controller.admin.wm.returnsales.vo.MesWmReturnSalesSaveReqVO;
-import cn.iocoder.yudao.module.mes.dal.dataobject.md.client.MesMdClientDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnsales.MesWmReturnSalesDO;
-import cn.iocoder.yudao.module.mes.service.md.client.MesMdClientService;
 import cn.iocoder.yudao.module.mes.service.wm.returnsales.MesWmReturnSalesService;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
@@ -43,7 +43,7 @@
     @Resource
     private MesWmReturnSalesService returnSalesService;
     @Resource
-    private MesMdClientService clientService;
+    private CrmCustomerApi crmCustomerApi;
 
     @PostMapping("/create")
     @Operation(summary = "鍒涘缓閿�鍞��璐у崟")
@@ -153,12 +153,12 @@
             return Collections.emptyList();
         }
         // 1. 鑾峰緱鍏宠仈鏁版嵁
-        Map<Long, MesMdClientDO> clientMap = clientService.getClientMap(
+        Map<Long, CrmCustomerRespDTO> customerMap = crmCustomerApi.getCustomerMap(
                 convertSet(list, MesWmReturnSalesDO::getClientId));
         // 2. 鏋勫缓缁撴灉
         return BeanUtils.toBean(list, MesWmReturnSalesRespVO.class, vo ->
-                MapUtils.findAndThen(clientMap, vo.getClientId(), client ->
-                        vo.setClientCode(client.getCode()).setClientName(client.getName()).setClientNickname(client.getNickname())));
+                MapUtils.findAndThen(customerMap, vo.getClientId(), customer ->
+                        vo.setClientName(customer.getName())));
     }
 
-}
+}
\ No newline at end of file
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnsales/vo/MesWmReturnSalesRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnsales/vo/MesWmReturnSalesRespVO.java
index c2f11b0..14b8f86 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnsales/vo/MesWmReturnSalesRespVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnsales/vo/MesWmReturnSalesRespVO.java
@@ -29,14 +29,8 @@
     @Schema(description = "瀹㈡埛 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
     private Long clientId;
 
-    @Schema(description = "瀹㈡埛缂栫爜", example = "C001")
-    private String clientCode;
-
     @Schema(description = "瀹㈡埛鍚嶇О", example = "XX瀹㈡埛")
     private String clientName;
-
-    @Schema(description = "瀹㈡埛绠�绉�", example = "XX")
-    private String clientNickname;
 
     @Schema(description = "閫�璐ф棩鏈�")
     private LocalDateTime returnDate;
@@ -53,4 +47,4 @@
     @Schema(description = "鍒涘缓鏃堕棿", requiredMode = Schema.RequiredMode.REQUIRED)
     private LocalDateTime createTime;
 
-}
+}
\ No newline at end of file
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/salesnotice/MesWmSalesNoticeController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/salesnotice/MesWmSalesNoticeController.java
index 07c0e03..53d0b5a 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/salesnotice/MesWmSalesNoticeController.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/salesnotice/MesWmSalesNoticeController.java
@@ -8,12 +8,12 @@
 import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi;
+import cn.iocoder.yudao.module.crm.api.customer.dto.CrmCustomerRespDTO;
 import cn.iocoder.yudao.module.mes.controller.admin.wm.salesnotice.vo.MesWmSalesNoticePageReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.wm.salesnotice.vo.MesWmSalesNoticeRespVO;
 import cn.iocoder.yudao.module.mes.controller.admin.wm.salesnotice.vo.MesWmSalesNoticeSaveReqVO;
-import cn.iocoder.yudao.module.mes.dal.dataobject.md.client.MesMdClientDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.salesnotice.MesWmSalesNoticeDO;
-import cn.iocoder.yudao.module.mes.service.md.client.MesMdClientService;
 import cn.iocoder.yudao.module.mes.service.wm.salesnotice.MesWmSalesNoticeService;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
@@ -43,7 +43,7 @@
     @Resource
     private MesWmSalesNoticeService salesNoticeService;
     @Resource
-    private MesMdClientService clientService;
+    private CrmCustomerApi crmCustomerApi;
 
     @PostMapping("/create")
     @Operation(summary = "鍒涘缓鍙戣揣閫氱煡鍗�")
@@ -126,13 +126,13 @@
             return Collections.emptyList();
         }
         // 1. 鑾峰緱鍏宠仈鏁版嵁
-        Map<Long, MesMdClientDO> clientMap = clientService.getClientMap(
+        Map<Long, CrmCustomerRespDTO> customerMap = crmCustomerApi.getCustomerMap(
                 convertSet(list, MesWmSalesNoticeDO::getClientId));
         // 2. 鏋勫缓缁撴灉
         return BeanUtils.toBean(list, MesWmSalesNoticeRespVO.class, vo -> {
-            MapUtils.findAndThen(clientMap, vo.getClientId(), client ->
-                    vo.setClientCode(client.getCode()).setClientName(client.getName()));
+            MapUtils.findAndThen(customerMap, vo.getClientId(), customer ->
+                    vo.setClientName(customer.getName()));
         });
     }
 
-}
+}
\ No newline at end of file
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/salesnotice/vo/MesWmSalesNoticeRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/salesnotice/vo/MesWmSalesNoticeRespVO.java
index 7d09503..05dda66 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/salesnotice/vo/MesWmSalesNoticeRespVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/salesnotice/vo/MesWmSalesNoticeRespVO.java
@@ -27,10 +27,6 @@
     @Schema(description = "瀹㈡埛缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
     private Long clientId;
 
-    @Schema(description = "瀹㈡埛缂栫爜", example = "C001")
-    @ExcelProperty("瀹㈡埛缂栫爜")
-    private String clientCode;
-
     @Schema(description = "瀹㈡埛鍚嶇О", example = "鏌愬鎴�")
     @ExcelProperty("瀹㈡埛鍚嶇О")
     private String clientName;
@@ -70,4 +66,4 @@
     @ExcelProperty("鍒涘缓鏃堕棿")
     private LocalDateTime createTime;
 
-}
+}
\ No newline at end of file
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/materialstock/MesWmMaterialStockDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/materialstock/MesWmMaterialStockDO.java
index 95b7c5f..95b8d30 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/materialstock/MesWmMaterialStockDO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/materialstock/MesWmMaterialStockDO.java
@@ -95,6 +95,18 @@
      */
     private BigDecimal quantity;
     /**
+     * 鍐荤粨鏁伴噺锛堢洏鐐广�佽川妫�绛夊喕缁擄級
+     */
+    private BigDecimal frozenQuantity;
+    /**
+     * 鍗犵敤閲忥紙宸插垎閰嶅緟鍑哄簱锛�
+     */
+    private BigDecimal reservedQuantity;
+    /**
+     * 鍦ㄩ�旈噺锛堣皟鎷ㄥ湪閫旓級
+     */
+    private BigDecimal inTransitQuantity;
+    /**
      * 鍏ュ簱鏃堕棿
      */
     private LocalDateTime receiptTime;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/productsales/MesWmProductSalesDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/productsales/MesWmProductSalesDO.java
index 14f265f..c7982f2 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/productsales/MesWmProductSalesDO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/productsales/MesWmProductSalesDO.java
@@ -43,7 +43,7 @@
     /**
      * 瀹㈡埛ID
      *
-     * 鍏宠仈 {@link MesMdClientDO#getId()}
+     * 鍏宠仈 CRM 瀹㈡埛 {@link cn.iocoder.yudao.module.crm.api.customer.dto.CrmCustomerRespDTO#getId()}
      */
     private Long clientId;
     /**
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/returnsales/MesWmReturnSalesDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/returnsales/MesWmReturnSalesDO.java
index 679a81b..baa5daa 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/returnsales/MesWmReturnSalesDO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/returnsales/MesWmReturnSalesDO.java
@@ -1,7 +1,6 @@
 package cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnsales;
 
 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
-import cn.iocoder.yudao.module.mes.dal.dataobject.md.client.MesMdClientDO;
 import cn.iocoder.yudao.module.mes.enums.wm.MesWmReturnSalesStatusEnum;
 import com.baomidou.mybatisplus.annotation.KeySequence;
 import com.baomidou.mybatisplus.annotation.TableId;
@@ -46,7 +45,7 @@
     /**
      * 瀹㈡埛 ID
      *
-     * 鍏宠仈 {@link MesMdClientDO#getId()}
+     * 鍏宠仈 CRM 瀹㈡埛 {@link cn.iocoder.yudao.module.crm.api.customer.dto.CrmCustomerRespDTO#getId()}
      */
     private Long clientId;
     /**
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/salesnotice/MesWmSalesNoticeDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/salesnotice/MesWmSalesNoticeDO.java
index 1281884..665d5b2 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/salesnotice/MesWmSalesNoticeDO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/salesnotice/MesWmSalesNoticeDO.java
@@ -43,7 +43,7 @@
     /**
      * 瀹㈡埛缂栧彿
      *
-     * 鍏宠仈 {@link MesMdClientDO#getId()}
+     * 鍏宠仈 CRM 瀹㈡埛 {@link cn.iocoder.yudao.module.crm.api.customer.dto.CrmCustomerRespDTO#getId()}
      */
     private Long clientId;
     /**
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/stockreserve/MesWmStockReserveDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/stockreserve/MesWmStockReserveDO.java
new file mode 100644
index 0000000..0996274
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/stockreserve/MesWmStockReserveDO.java
@@ -0,0 +1,83 @@
+package cn.iocoder.yudao.module.mes.dal.dataobject.wm.stockreserve;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import cn.iocoder.yudao.module.mes.enums.wm.MesWmStockReserveBizTypeEnum;
+import cn.iocoder.yudao.module.mes.enums.wm.MesWmStockReserveStatusEnum;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+import java.math.BigDecimal;
+
+/**
+ * MES 搴撳瓨鍗犵敤璁板綍 DO
+ */
+@TableName("mes_wm_stock_reserve")
+@KeySequence("mes_wm_stock_reserve_seq")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class MesWmStockReserveDO extends BaseDO {
+
+    /**
+     * 缂栧彿
+     */
+    @TableId
+    private Long id;
+
+    /**
+     * 搴撳瓨璁板綍ID
+     *
+     * 鍏宠仈 {@link cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO#getId()}
+     */
+    private Long materialStockId;
+
+    /**
+     * 涓氬姟绫诲瀷
+     *
+     * 鏋氫妇 {@link MesWmStockReserveBizTypeEnum}
+     */
+    private Integer bizType;
+
+    /**
+     * 涓氬姟鍗曟嵁ID
+     */
+    private Long bizId;
+
+    /**
+     * 涓氬姟鍗曞彿
+     */
+    private String bizCode;
+
+    /**
+     * 涓氬姟鍗曟嵁琛孖D
+     */
+    private Long bizLineId;
+
+    /**
+     * 鐗╂枡ID
+     */
+    private Long itemId;
+
+    /**
+     * 鍗犵敤鏁伴噺
+     */
+    private BigDecimal quantity;
+
+    /**
+     * 鐘舵��
+     *
+     * 鏋氫妇 {@link MesWmStockReserveStatusEnum}
+     */
+    private Integer status;
+
+    /**
+     * 澶囨敞
+     */
+    private String remark;
+
+}
\ No newline at end of file
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/materialstock/MesWmMaterialStockMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/materialstock/MesWmMaterialStockMapper.java
index a22847d..fa1bda7 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/materialstock/MesWmMaterialStockMapper.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/materialstock/MesWmMaterialStockMapper.java
@@ -86,6 +86,40 @@
     }
 
     /**
+     * 澧為噺鏇存柊搴撳瓨鍗犵敤閲�
+     *
+     * @param id        搴撳瓨璁板綍缂栧彿
+     * @param count     鍙樺姩鏁伴噺锛堟鏁�=澧炲姞鍗犵敤锛岃礋鏁�=鍑忓皯鍗犵敤锛�
+     * @param checkFlag 鏄惁鏍¢獙鍙敤閲忓厖瓒筹紙涓� true 鏃跺崰鐢ㄤ笉鍏佽瓒呰繃鍙敤閲忥級
+     * @return 褰卞搷琛屾暟
+     */
+    default int updateReservedQuantity(Long id, BigDecimal count, boolean checkFlag) {
+        LambdaUpdateWrapper<MesWmMaterialStockDO> updateWrapper = new LambdaUpdateWrapper<MesWmMaterialStockDO>()
+                .eq(MesWmMaterialStockDO::getId, id)
+                .setSql("reserved_quantity = reserved_quantity + " + count);
+        if (checkFlag && count.compareTo(BigDecimal.ZERO) > 0) {
+            // 鏍¢獙鍙敤閲忓厖瓒筹細quantity - frozen_quantity - reserved_quantity >= count
+            updateWrapper.setSql("reserved_quantity = reserved_quantity + " + count +
+                    " WHERE quantity - IFNULL(frozen_quantity, 0) - IFNULL(reserved_quantity, 0) >= " + count);
+        }
+        return update(null, updateWrapper);
+    }
+
+    /**
+     * 澧為噺鏇存柊搴撳瓨鍦ㄩ�旈噺
+     *
+     * @param id    搴撳瓨璁板綍缂栧彿
+     * @param count 鍙樺姩鏁伴噺锛堟鏁�=澧炲姞鍦ㄩ�旓紝璐熸暟=鍑忓皯鍦ㄩ�旓級
+     * @return 褰卞搷琛屾暟
+     */
+    default int updateInTransitQuantity(Long id, BigDecimal count) {
+        LambdaUpdateWrapper<MesWmMaterialStockDO> updateWrapper = new LambdaUpdateWrapper<MesWmMaterialStockDO>()
+                .eq(MesWmMaterialStockDO::getId, id)
+                .setSql("in_transit_quantity = in_transit_quantity + " + count);
+        return update(null, updateWrapper);
+    }
+
+    /**
      * 鎸夌粍鍚堥敭鏌ヨ搴撳瓨璁板綍
      *
      * 鍞竴閿細(itemId, warehouseId, locationId, areaId, batchId, vendorId)
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/stockreserve/MesWmStockReserveMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/stockreserve/MesWmStockReserveMapper.java
new file mode 100644
index 0000000..d4664bd
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/stockreserve/MesWmStockReserveMapper.java
@@ -0,0 +1,64 @@
+package cn.iocoder.yudao.module.mes.dal.mysql.wm.stockreserve;
+
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.stockreserve.MesWmStockReserveDO;
+import cn.iocoder.yudao.module.mes.enums.wm.MesWmStockReserveStatusEnum;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * MES 搴撳瓨鍗犵敤璁板綍 Mapper
+ */
+@Mapper
+public interface MesWmStockReserveMapper extends BaseMapperX<MesWmStockReserveDO> {
+
+    /**
+     * 鏌ヨ鎸囧畾涓氬姟鍗曟嵁鐨勫崰鐢ㄨ褰�
+     */
+    default List<MesWmStockReserveDO> selectListByBiz(Integer bizType, Long bizId) {
+        return selectList(new LambdaQueryWrapperX<MesWmStockReserveDO>()
+                .eq(MesWmStockReserveDO::getBizType, bizType)
+                .eq(MesWmStockReserveDO::getBizId, bizId));
+    }
+
+    /**
+     * 鏌ヨ鎸囧畾涓氬姟鍗曟嵁鐨勫崰鐢ㄤ腑璁板綍
+     */
+    default List<MesWmStockReserveDO> selectReservingListByBiz(Integer bizType, Long bizId) {
+        return selectList(new LambdaQueryWrapperX<MesWmStockReserveDO>()
+                .eq(MesWmStockReserveDO::getBizType, bizType)
+                .eq(MesWmStockReserveDO::getBizId, bizId)
+                .eq(MesWmStockReserveDO::getStatus, MesWmStockReserveStatusEnum.RESERVING.getStatus()));
+    }
+
+    /**
+     * 鏌ヨ鎸囧畾搴撳瓨璁板綍鐨勫崰鐢ㄤ腑璁板綍
+     */
+    default List<MesWmStockReserveDO> selectReservingListByMaterialStockId(Long materialStockId) {
+        return selectList(new LambdaQueryWrapperX<MesWmStockReserveDO>()
+                .eq(MesWmStockReserveDO::getMaterialStockId, materialStockId)
+                .eq(MesWmStockReserveDO::getStatus, MesWmStockReserveStatusEnum.RESERVING.getStatus()));
+    }
+
+    /**
+     * 鏌ヨ鎸囧畾鐗╂枡鍦ㄦ寚瀹氫粨搴撶殑鍗犵敤鏁伴噺姹囨��
+     */
+    @Select("SELECT IFNULL(SUM(sr.quantity), 0) FROM mes_wm_stock_reserve sr " +
+            "INNER JOIN mes_wm_material_stock ms ON sr.material_stock_id = ms.id " +
+            "WHERE sr.deleted = 0 AND sr.status = 1 AND ms.deleted = 0 " +
+            "AND sr.item_id = #{itemId} AND ms.warehouse_id = #{warehouseId}")
+    BigDecimal sumReservedQuantityByItemAndWarehouse(@Param("itemId") Long itemId, @Param("warehouseId") Long warehouseId);
+
+    /**
+     * 鏌ヨ鎸囧畾鐗╂枡鐨勫崰鐢ㄦ暟閲忔眹鎬�
+     */
+    @Select("SELECT IFNULL(SUM(quantity), 0) FROM mes_wm_stock_reserve " +
+            "WHERE deleted = 0 AND status = 1 AND item_id = #{itemId}")
+    BigDecimal sumReservedQuantityByItem(@Param("itemId") Long itemId);
+
+}
\ No newline at end of file
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmStockReserveBizTypeEnum.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmStockReserveBizTypeEnum.java
new file mode 100644
index 0000000..c021bdb
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmStockReserveBizTypeEnum.java
@@ -0,0 +1,38 @@
+package cn.iocoder.yudao.module.mes.enums.wm;
+
+import cn.iocoder.yudao.framework.common.core.ArrayValuable;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+
+/**
+ * MES 搴撳瓨鍗犵敤涓氬姟绫诲瀷鏋氫妇
+ */
+@Getter
+@AllArgsConstructor
+public enum MesWmStockReserveBizTypeEnum implements ArrayValuable<Integer> {
+
+    PRODUCT_SALES(1, "閿�鍞嚭搴�"),
+    PRODUCT_ISSUE(2, "鐢熶骇棰嗘枡"),
+    OUTSOURCE_ISSUE(3, "澶栧崗鍙戞枡"),
+    MISC_ISSUE(4, "鏉傞」鍑哄簱"),
+    TRANSFER(5, "杞Щ璋冩嫧");
+
+    public static final Integer[] ARRAYS = Arrays.stream(values()).map(MesWmStockReserveBizTypeEnum::getType).toArray(Integer[]::new);
+
+    /**
+     * 绫诲瀷鍊�
+     */
+    private final Integer type;
+    /**
+     * 绫诲瀷鍚�
+     */
+    private final String name;
+
+    @Override
+    public Integer[] array() {
+        return ARRAYS;
+    }
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmStockReserveStatusEnum.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmStockReserveStatusEnum.java
new file mode 100644
index 0000000..2872f1a
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmStockReserveStatusEnum.java
@@ -0,0 +1,36 @@
+package cn.iocoder.yudao.module.mes.enums.wm;
+
+import cn.iocoder.yudao.framework.common.core.ArrayValuable;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+
+/**
+ * MES 搴撳瓨鍗犵敤鐘舵�佹灇涓�
+ */
+@Getter
+@AllArgsConstructor
+public enum MesWmStockReserveStatusEnum implements ArrayValuable<Integer> {
+
+    RESERVING(1, "鍗犵敤涓�"),
+    RELEASED(2, "宸查噴鏀�"),
+    OUTBOUND(3, "宸插嚭搴�");
+
+    public static final Integer[] ARRAYS = Arrays.stream(values()).map(MesWmStockReserveStatusEnum::getStatus).toArray(Integer[]::new);
+
+    /**
+     * 鐘舵�佸��
+     */
+    private final Integer status;
+    /**
+     * 鐘舵�佸悕
+     */
+    private final String name;
+
+    @Override
+    public Integer[] array() {
+        return ARRAYS;
+    }
+
+}
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 e794078..b193252 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
@@ -14,9 +14,11 @@
 import cn.iocoder.yudao.module.mes.dal.mysql.wm.productissue.MesWmProductIssueMapper;
 import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
 import cn.iocoder.yudao.module.mes.enums.wm.MesWmProductIssueStatusEnum;
+import cn.iocoder.yudao.module.mes.enums.wm.MesWmStockReserveBizTypeEnum;
 import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum;
 import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationService;
 import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
+import cn.iocoder.yudao.module.mes.service.wm.stockreserve.MesWmStockReserveService;
 import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService;
 import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
 import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
@@ -63,6 +65,8 @@
     private MesWmWarehouseLocationService locationService;
     @Resource
     private MesWmWarehouseAreaService areaService;
+    @Resource
+    private MesWmStockReserveService stockReserveService;
 
     @Override
     @Transactional(rollbackFor = Exception.class)
@@ -143,6 +147,22 @@
             throw exception(WM_PRODUCT_ISSUE_DETAIL_QUANTITY_MISMATCH);
         }
 
+        // 鍒涘缓搴撳瓨鍗犵敤锛堟寜鏄庣粏鍗犵敤搴撳瓨锛�
+        List<MesWmProductIssueDetailDO> details = issueDetailService.getProductIssueDetailListByIssueId(id);
+        for (MesWmProductIssueDetailDO detail : details) {
+            if (detail.getMaterialStockId() != null && detail.getQuantity() != null) {
+                stockReserveService.createReserve(
+                        detail.getMaterialStockId(),
+                        MesWmStockReserveBizTypeEnum.PRODUCT_ISSUE.getType(),
+                        id,
+                        issue.getCode(),
+                        detail.getId(),
+                        detail.getItemId(),
+                        detail.getQuantity()
+                );
+            }
+        }
+
         // 鎵ц鎷h揣锛堝緟鎷h揣 鈫� 寰呮墽琛岄鍑猴級
         issueMapper.updateById(new MesWmProductIssueDO()
                 .setId(id).setStatus(MesWmProductIssueStatusEnum.APPROVED.getStatus()));
@@ -166,10 +186,13 @@
             throw exception(WM_PRODUCT_ISSUE_DETAIL_QUANTITY_MISMATCH);
         }
 
-        // 2. 閬嶅巻鎵�鏈夋槑缁嗭紝鍒涘缓搴撳瓨浜嬪姟锛堟墸鍑忓簱瀛� + 璁板綍娴佹按锛�
+        // 2. 纭鍗犵敤杞嚭搴擄紙閲婃斁鍗犵敤閲忥級
+        stockReserveService.confirmReserveToOutbound(MesWmStockReserveBizTypeEnum.PRODUCT_ISSUE.getType(), id);
+
+        // 3. 閬嶅巻鎵�鏈夋槑缁嗭紝鍒涘缓搴撳瓨浜嬪姟锛堟墸鍑忓簱瀛� + 璁板綍娴佹按锛�
         createTransactionList(issue);
 
-        // 3. 鏇存柊鍑哄簱鍗曠姸鎬� + 棰嗘枡鏃ユ湡
+        // 4. 鏇存柊鍑哄簱鍗曠姸鎬� + 棰嗘枡鏃ユ湡
         issueMapper.updateById(new MesWmProductIssueDO()
                 .setId(id).setStatus(MesWmProductIssueStatusEnum.FINISHED.getStatus())
                 .setIssueDate(LocalDateTime.now()));
@@ -216,6 +239,9 @@
             throw exception(WM_PRODUCT_ISSUE_CANCEL_NOT_ALLOWED);
         }
 
+        // 閲婃斁搴撳瓨鍗犵敤
+        stockReserveService.releaseReserve(MesWmStockReserveBizTypeEnum.PRODUCT_ISSUE.getType(), id);
+
         // 鍙栨秷
         issueMapper.updateById(new MesWmProductIssueDO()
                 .setId(id).setStatus(MesWmProductIssueStatusEnum.CANCELED.getStatus()));
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesServiceImpl.java
index 1694b78..baec481 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesServiceImpl.java
@@ -18,10 +18,12 @@
 import cn.iocoder.yudao.module.mes.enums.wm.MesWmProductSalesStatusEnum;
 import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum;
 import cn.iocoder.yudao.module.mes.enums.wm.MesWmSalesNoticeStatusEnum;
+import cn.iocoder.yudao.module.mes.enums.wm.MesWmStockReserveBizTypeEnum;
 import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum;
 import cn.iocoder.yudao.module.mes.service.md.client.MesMdClientService;
 import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService;
 import cn.iocoder.yudao.module.mes.service.wm.salesnotice.MesWmSalesNoticeService;
+import cn.iocoder.yudao.module.mes.service.wm.stockreserve.MesWmStockReserveService;
 import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService;
 import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
 import cn.iocoder.yudao.module.erp.api.sale.ErpSaleOrderApi;
@@ -61,6 +63,8 @@
     private MesWmTransactionService wmTransactionService;
     @Resource
     private MesWmMaterialStockService materialStockService;
+    @Resource
+    private MesWmStockReserveService stockReserveService;
 
     @Resource
     private ErpSaleOrderApi saleOrderApi;
@@ -181,7 +185,7 @@
             if (CollUtil.isEmpty(details)) {
                 throw exception(WM_PRODUCT_SALES_DETAILS_EMPTY);
             }
-            // 鏍¢獙搴撳瓨鍏呰冻
+            // 鏍¢獙搴撳瓨鍏呰冻骞跺垱寤哄崰鐢�
             for (MesWmProductSalesDetailDO detail : details) {
                 materialStockService.validateSelectedStock(
                         detail.getMaterialStockId(),
@@ -191,6 +195,16 @@
                         detail.getWarehouseId(),
                         detail.getLocationId(),
                         detail.getAreaId(),
+                        detail.getQuantity()
+                );
+                // 鍒涘缓搴撳瓨鍗犵敤
+                stockReserveService.createReserve(
+                        detail.getMaterialStockId(),
+                        MesWmStockReserveBizTypeEnum.PRODUCT_SALES.getType(),
+                        id,
+                        sales.getCode(),
+                        detail.getId(),
+                        detail.getItemId(),
                         detail.getQuantity()
                 );
             }
@@ -227,19 +241,22 @@
             throw exception(WM_PRODUCT_SALES_CANNOT_FINISH);
         }
 
-        // 2. 閬嶅巻鎵�鏈夋槑缁嗭紝鍒涘缓搴撳瓨浜嬪姟锛堟墸鍑忓簱瀛� + 璁板綍娴佹按锛�
+        // 2. 纭鍗犵敤杞嚭搴擄紙閲婃斁鍗犵敤閲忥級
+        stockReserveService.confirmReserveToOutbound(MesWmStockReserveBizTypeEnum.PRODUCT_SALES.getType(), id);
+
+        // 3. 閬嶅巻鎵�鏈夋槑缁嗭紝鍒涘缓搴撳瓨浜嬪姟锛堟墸鍑忓簱瀛� + 璁板綍娴佹按锛�
         createTransactionList(sales);
 
-        // 3. 鏇存柊鍑哄簱鍗曠姸鎬�
+        // 4. 鏇存柊鍑哄簱鍗曠姸鎬�
         productSalesMapper.updateById(new MesWmProductSalesDO()
                 .setId(id).setStatus(MesWmProductSalesStatusEnum.FINISHED.getStatus()));
 
-        // 4. 鏇存柊鍏宠仈鐨勫彂璐ч�氱煡鍗曠姸鎬�
+        // 5. 鏇存柊鍏宠仈鐨勫彂璐ч�氱煡鍗曠姸鎬�
         if (sales.getNoticeId() != null) {
             salesNoticeService.finishSalesNotice(sales.getNoticeId());
         }
 
-        // 5. 鍥炲啓 ERP 閿�鍞鍗曠殑鍑哄簱鏁伴噺鍜岀姸鎬�
+        // 6. 鍥炲啓 ERP 閿�鍞鍗曠殑鍑哄簱鏁伴噺鍜岀姸鎬�
         updateErpSaleOrderOutCount(sales);
     }
 
@@ -296,6 +313,8 @@
                 MesWmProductSalesStatusEnum.CANCELED.getStatus())) {
             throw exception(WM_PRODUCT_SALES_CANNOT_CANCEL);
         }
+        // 閲婃斁搴撳瓨鍗犵敤
+        stockReserveService.releaseReserve(MesWmStockReserveBizTypeEnum.PRODUCT_SALES.getType(), id);
         // 鍙栨秷
         productSalesMapper.updateById(new MesWmProductSalesDO()
                 .setId(id).setStatus(MesWmProductSalesStatusEnum.CANCELED.getStatus()));
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/stockreserve/MesWmStockReserveService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/stockreserve/MesWmStockReserveService.java
new file mode 100644
index 0000000..f575c39
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/stockreserve/MesWmStockReserveService.java
@@ -0,0 +1,73 @@
+package cn.iocoder.yudao.module.mes.service.wm.stockreserve;
+
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.stockreserve.MesWmStockReserveDO;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * MES 搴撳瓨鍗犵敤 Service 鎺ュ彛
+ */
+public interface MesWmStockReserveService {
+
+    /**
+     * 鍒涘缓搴撳瓨鍗犵敤
+     *
+     * @param materialStockId 搴撳瓨璁板綍ID
+     * @param bizType         涓氬姟绫诲瀷
+     * @param bizId           涓氬姟鍗曟嵁ID
+     * @param bizCode         涓氬姟鍗曞彿
+     * @param bizLineId       涓氬姟鍗曟嵁琛孖D
+     * @param itemId          鐗╂枡ID
+     * @param quantity        鍗犵敤鏁伴噺
+     */
+    void createReserve(Long materialStockId, Integer bizType, Long bizId, String bizCode, Long bizLineId, Long itemId, BigDecimal quantity);
+
+    /**
+     * 鎵归噺鍒涘缓搴撳瓨鍗犵敤
+     *
+     * @param reserves 鍗犵敤淇℃伅鍒楄〃
+     */
+    void createReserveBatch(List<ReserveInfo> reserves);
+
+    /**
+     * 閲婃斁搴撳瓨鍗犵敤锛堝彇娑堝崟鎹椂锛�
+     *
+     * @param bizType 涓氬姟绫诲瀷
+     * @param bizId   涓氬姟鍗曟嵁ID
+     */
+    void releaseReserve(Integer bizType, Long bizId);
+
+    /**
+     * 纭鍗犵敤杞嚭搴擄紙鎵ц鍑哄簱鏃讹級
+     * 鏇存柊鍗犵敤璁板綍鐘舵�佷负宸插嚭搴擄紝骞舵墸鍑忓簱瀛樺崰鐢ㄩ噺瀛楁
+     *
+     * @param bizType 涓氬姟绫诲瀷
+     * @param bizId   涓氬姟鍗曟嵁ID
+     */
+    void confirmReserveToOutbound(Integer bizType, Long bizId);
+
+    /**
+     * 鏌ヨ鎸囧畾涓氬姟鍗曟嵁鐨勫崰鐢ㄨ褰�
+     *
+     * @param bizType 涓氬姟绫诲瀷
+     * @param bizId   涓氬姟鍗曟嵁ID
+     * @return 鍗犵敤璁板綍鍒楄〃
+     */
+    List<MesWmStockReserveDO> getReserveListByBiz(Integer bizType, Long bizId);
+
+    /**
+     * 鏌ヨ鎸囧畾鐗╂枡鐨勫崰鐢ㄦ暟閲忔眹鎬�
+     *
+     * @param itemId      鐗╂枡ID
+     * @param warehouseId 浠撳簱ID锛堝彲閫夛級
+     * @return 鍗犵敤鏁伴噺
+     */
+    BigDecimal sumReservedQuantity(Long itemId, Long warehouseId);
+
+    /**
+     * 鍗犵敤淇℃伅
+     */
+    record ReserveInfo(Long materialStockId, Integer bizType, Long bizId, String bizCode, Long bizLineId, Long itemId, BigDecimal quantity) {}
+
+}
\ No newline at end of file
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/stockreserve/MesWmStockReserveServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/stockreserve/MesWmStockReserveServiceImpl.java
new file mode 100644
index 0000000..e210bb6
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/stockreserve/MesWmStockReserveServiceImpl.java
@@ -0,0 +1,152 @@
+package cn.iocoder.yudao.module.mes.service.wm.stockreserve;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.stockreserve.MesWmStockReserveDO;
+import cn.iocoder.yudao.module.mes.dal.mysql.wm.materialstock.MesWmMaterialStockMapper;
+import cn.iocoder.yudao.module.mes.dal.mysql.wm.stockreserve.MesWmStockReserveMapper;
+import cn.iocoder.yudao.module.mes.enums.wm.MesWmStockReserveStatusEnum;
+import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.validation.annotation.Validated;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
+
+/**
+ * MES 搴撳瓨鍗犵敤 Service 瀹炵幇绫�
+ */
+@Service
+@Validated
+@Slf4j
+public class MesWmStockReserveServiceImpl implements MesWmStockReserveService {
+
+    @Resource
+    private MesWmStockReserveMapper stockReserveMapper;
+
+    @Resource
+    private MesWmMaterialStockMapper materialStockMapper;
+
+    @Resource
+    private MesWmMaterialStockService materialStockService;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void createReserve(Long materialStockId, Integer bizType, Long bizId, String bizCode, Long bizLineId, Long itemId, BigDecimal quantity) {
+        // 1. 鏍¢獙搴撳瓨璁板綍瀛樺湪
+        MesWmMaterialStockDO stock = materialStockService.getMaterialStock(materialStockId);
+        if (stock == null) {
+            throw exception(WM_MATERIAL_STOCK_NOT_EXISTS);
+        }
+
+        // 2. 鏍¢獙鍙敤閲忓厖瓒筹紙鍦ㄥ簱鏁伴噺 - 鍐荤粨鏁伴噺 - 宸插崰鐢ㄩ噺 >= 鏈鍗犵敤鏁伴噺锛�
+        BigDecimal availableQty = stock.getQuantity()
+                .subtract(stock.getFrozenQuantity() != null ? stock.getFrozenQuantity() : BigDecimal.ZERO)
+                .subtract(stock.getReservedQuantity() != null ? stock.getReservedQuantity() : BigDecimal.ZERO);
+        if (availableQty.compareTo(quantity) < 0) {
+            throw exception(WM_MATERIAL_STOCK_INSUFFICIENT);
+        }
+
+        // 3. 鍒涘缓鍗犵敤璁板綍
+        MesWmStockReserveDO reserve = MesWmStockReserveDO.builder()
+                .materialStockId(materialStockId)
+                .bizType(bizType)
+                .bizId(bizId)
+                .bizCode(bizCode)
+                .bizLineId(bizLineId)
+                .itemId(itemId)
+                .quantity(quantity)
+                .status(MesWmStockReserveStatusEnum.RESERVING.getStatus())
+                .build();
+        stockReserveMapper.insert(reserve);
+
+        // 4. 鏇存柊搴撳瓨鍗犵敤閲忓瓧娈碉紙绱姞锛�
+        int updatedRows = materialStockMapper.updateReservedQuantity(materialStockId, quantity, true);
+        if (updatedRows == 0) {
+            throw exception(WM_MATERIAL_STOCK_INSUFFICIENT);
+        }
+
+        log.info("[createReserve] 鍒涘缓搴撳瓨鍗犵敤鎴愬姛锛宮aterialStockId={}, bizType={}, bizId={}, quantity={}",
+                materialStockId, bizType, bizId, quantity);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void createReserveBatch(List<ReserveInfo> reserves) {
+        if (CollUtil.isEmpty(reserves)) {
+            return;
+        }
+        for (ReserveInfo reserve : reserves) {
+            createReserve(reserve.materialStockId(), reserve.bizType(), reserve.bizId(),
+                    reserve.bizCode(), reserve.bizLineId(), reserve.itemId(), reserve.quantity());
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void releaseReserve(Integer bizType, Long bizId) {
+        // 1. 鏌ヨ鍗犵敤涓殑璁板綍
+        List<MesWmStockReserveDO> reserveList = stockReserveMapper.selectReservingListByBiz(bizType, bizId);
+        if (CollUtil.isEmpty(reserveList)) {
+            log.warn("[releaseReserve] 鏈壘鍒板崰鐢ㄨ褰曪紝bizType={}, bizId={}", bizType, bizId);
+            return;
+        }
+
+        // 2. 鏇存柊鍗犵敤璁板綍鐘舵�佷负宸查噴鏀撅紝骞舵墸鍑忓簱瀛樺崰鐢ㄩ噺瀛楁
+        for (MesWmStockReserveDO reserve : reserveList) {
+            // 2.1 鏇存柊鍗犵敤璁板綍鐘舵��
+            stockReserveMapper.updateById(new MesWmStockReserveDO()
+                    .setId(reserve.getId())
+                    .setStatus(MesWmStockReserveStatusEnum.RELEASED.getStatus()));
+
+            // 2.2 鎵e噺搴撳瓨鍗犵敤閲忥紙璐熸暟琛ㄧず鍑忓皯锛�
+            materialStockMapper.updateReservedQuantity(reserve.getMaterialStockId(), reserve.getQuantity().negate(), false);
+        }
+
+        log.info("[releaseReserve] 閲婃斁搴撳瓨鍗犵敤鎴愬姛锛宐izType={}, bizId={}, count={}", bizType, bizId, reserveList.size());
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void confirmReserveToOutbound(Integer bizType, Long bizId) {
+        // 1. 鏌ヨ鍗犵敤涓殑璁板綍
+        List<MesWmStockReserveDO> reserveList = stockReserveMapper.selectReservingListByBiz(bizType, bizId);
+        if (CollUtil.isEmpty(reserveList)) {
+            log.warn("[confirmReserveToOutbound] 鏈壘鍒板崰鐢ㄨ褰曪紝bizType={}, bizId={}", bizType, bizId);
+            return;
+        }
+
+        // 2. 鏇存柊鍗犵敤璁板綍鐘舵�佷负宸插嚭搴擄紝骞舵墸鍑忓簱瀛樺崰鐢ㄩ噺瀛楁
+        for (MesWmStockReserveDO reserve : reserveList) {
+            // 2.1 鏇存柊鍗犵敤璁板綍鐘舵��
+            stockReserveMapper.updateById(new MesWmStockReserveDO()
+                    .setId(reserve.getId())
+                    .setStatus(MesWmStockReserveStatusEnum.OUTBOUND.getStatus()));
+
+            // 2.2 鎵e噺搴撳瓨鍗犵敤閲忥紙璐熸暟琛ㄧず鍑忓皯锛�
+            materialStockMapper.updateReservedQuantity(reserve.getMaterialStockId(), reserve.getQuantity().negate(), false);
+        }
+
+        log.info("[confirmReserveToOutbound] 纭鍗犵敤杞嚭搴撴垚鍔燂紝bizType={}, bizId={}, count={}", bizType, bizId, reserveList.size());
+    }
+
+    @Override
+    public List<MesWmStockReserveDO> getReserveListByBiz(Integer bizType, Long bizId) {
+        return stockReserveMapper.selectListByBiz(bizType, bizId);
+    }
+
+    @Override
+    public BigDecimal sumReservedQuantity(Long itemId, Long warehouseId) {
+        if (warehouseId != null) {
+            return stockReserveMapper.sumReservedQuantityByItemAndWarehouse(itemId, warehouseId);
+        }
+        return stockReserveMapper.sumReservedQuantityByItem(itemId);
+    }
+
+}
\ No newline at end of file

--
Gitblit v1.9.3