From 1ba0dbfde5634c8bf2ec20891d6b226b996f6b59 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期三, 02 九月 2026 14:24:38 +0800
Subject: [PATCH] refactor(mes): 优化MES生产扫码入库功能支持多类型扫码

---
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transaction/dto/MesWmTransactionSaveReqDTO.java                 |   12 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/seedquality/vo/MesQcSeedQualityPageReqVO.java          |   21 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/productsales/MesWmProductSalesLineDO.java                |    4 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/trace/batch/MesTraceBatchController.java                  |   39 
 .playwright-mcp/page-2026-09-02T06-08-53-561Z.yml                                                                                     |    1 
 .playwright-mcp/page-2026-09-02T06-16-10-960Z.yml                                                                                     |  190 +++
 .playwright-mcp/page-2026-09-02T06-11-00-277Z.yml                                                                                     |    1 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/pub/trace/vo/MesProTracePublicRespVO.java                       |   38 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/materialstockbag/MesWmMaterialStockBagMapper.java             |   82 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/transaction/MesWmTransactionMapper.java                       |    1 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmWarehouseTypeEnum.java                                       |   20 
 sql/mysql/mes_qc_seed_quality_record_menu.sql                                                                                         |   14 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productsales/vo/MesWmProductSalesRespVO.java           |    4 
 .playwright-mcp/page-2026-09-02T06-14-34-873Z.yml                                                                                     |  190 +++
 yudao-module-aftersales/src/main/java/cn/iocoder/yudao/module/aftersales/service/returnobj/AfterSaleReturnServiceImpl.java            |    2 
 sql/mysql/mes_wm_qualified_warehouse.sql                                                                                              |   85 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/salesnotice/MesWmSalesNoticeController.java            |    8 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/salesnotice/MesWmSalesNoticeDO.java                      |    8 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/materialstock/MesWmMaterialStockMapper.java                   |   93 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/productsales/MesWmProductSalesDO.java                    |    8 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/warehouse/vo/MesWmWarehouseRespVO.java                 |    4 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/batch/MesProBatchMapper.java                                 |    6 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockBagRespVO.java      |   29 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/pallet/MesProPalletMapper.java                               |    5 
 .playwright-mcp/page-2026-09-02T06-07-41-739Z.yml                                                                                     |    1 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/salesnotice/vo/line/MesWmSalesNoticeLineRespVO.java    |    3 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/pallet/MesProPalletDO.java                              |    8 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/seedquality/vo/MesQcSeedQualityRespVO.java             |   22 
 .playwright-mcp/page-2026-09-02T06-16-32-193Z.yml                                                                                     |    1 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesServiceImpl.java                  |   61 
 docs/sales_notice_to_product_sales.md                                                                                                 |   14 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/sn/MesWmSnDO.java                                        |   25 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/pro/MesProBagCodeQcStatusEnum.java                                   |   40 
 docs/scan_in_multi_type_inbound.md                                                                                                    |   80 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/salesnotice/vo/MesWmSalesNoticeSaveReqVO.java          |    6 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnsales/vo/MesWmReturnSalesSaveReqVO.java          |    6 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/bagcode/MesProBagCodeDO.java                            |    1 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnsales/vo/MesWmReturnSalesRespVO.java             |    6 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/scan/vo/MesProScanEventCreateReqVO.java               |   12 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/warehouse/vo/MesWmWarehousePageReqVO.java              |    3 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstockbag/MesWmMaterialStockBagServiceImpl.java          |  165 ++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/home/MesBagCodeHomeStatisticsMapper.java                         |   17 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/warehouse/MesWmWarehouseMapper.java                           |    7 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/salesnotice/MesWmSalesNoticeMapper.java                       |   16 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/warehouse/MesWmWarehouseDO.java                          |    7 
 docs/batch_trace_refactor_integration.md                                                                                              |  102 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/salesnotice/MesWmSalesNoticeService.java                        |    8 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/bagcode/MesProBagCodeMapper.java                             |   24 
 .playwright-mcp/page-2026-09-02T06-03-42-523Z.yml                                                                                     |    1 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/transaction/vo/MesWmTransactionPageReqVO.java          |    3 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/scan/MesProScanEventServiceImpl.java                           |  101 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/batch/MesWmBatchDO.java                                  |    7 
 sql/mysql/mes_wm_product_sales_add_columns.sql                                                                                        |   12 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockRespVO.java         |    6 
 docs/sql/trace_platform_01_schema.sql                                                                                                 |   79 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/MesWmMaterialStockController.java        |   22 
 yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOrderController.java                          |   59 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockServiceImpl.java                |   84 +
 .playwright-mcp/page-2026-09-02T06-14-13-812Z.yml                                                                                     |    1 
 sql/mysql/mes_pro_scan_event_scan_type.sql                                                                                            |   14 
 docs/mes_wm_material_stock_bag_api.md                                                                                                 |   69 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockPageReqVO.java      |    3 
 .playwright-mcp/page-2026-09-02T05-27-28-818Z.yml                                                                                     |    1 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/scan/vo/MesProScanEventRespVO.java                    |    1 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmMaterialStockBagStatusEnum.java                              |   15 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/trace/MesProTracePublicServiceImpl.java                            |   45 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/warehouse/MesWmWarehouseServiceImpl.java                        |   20 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockBagPageReqVO.java   |   18 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/salesnotice/MesWmSalesNoticeServiceImpl.java                    |   65 -
 .playwright-mcp/page-2026-09-02T05-56-04-640Z.yml                                                                                     |    1 
 yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/returnsales/dto/MesWmReturnSalesCreateReqDTO.java                  |    6 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/salesnotice/MesWmSalesNoticeLineServiceImpl.java                |   37 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/materialstockbag/MesWmMaterialStockBagDO.java            |   44 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/transaction/MesWmTransactionDO.java                      |    4 
 .playwright-mcp/page-2026-09-02T06-08-22-302Z.yml                                                                                     |  196 +++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/salesnotice/vo/MesWmSalesNoticeRespVO.java             |    7 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstockbag/MesWmMaterialStockBagService.java              |   59 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/seedquality/MesQcSeedQualityServiceImpl.java                    |  239 +++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java                                              |   13 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productsales/vo/MesWmProductSalesSaveReqVO.java        |    3 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/MesBizTypeConstants.java                                             |    1 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/seedquality/MesQcSeedQualityService.java                        |    4 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/productsales/MesWmProductSalesLineMapper.java                 |   21 
 yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderRespVO.java                     |   15 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockListReqVO.java      |    3 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockService.java                    |   22 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transaction/MesWmTransactionServiceImpl.java                    |   52 
 yudao-module-mes/src/main/resources/mapper/home/MesBagCodeHomeStatisticsMapper.xml                                                    |   39 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/seedquality/MesQcSeedQualityController.java            |   11 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/salesnotice/MesWmSalesNoticeLineDO.java                  |    4 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/salesnotice/vo/MesWmSalesNoticePageReqVO.java          |    3 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/seedquality/vo/MesQcSeedQualitySubmitReqVO.java        |    3 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/scan/MesProScanEventDO.java                             |    7 
 .playwright-mcp/page-2026-09-02T06-08-26-480Z.yml                                                                                     |    1 
 .playwright-mcp/page-2026-09-02T06-08-42-531Z.yml                                                                                     |  196 +++
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/home/MesBagCodeHomeStatisticsServiceImpl.java                      |   11 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/returnsales/MesWmReturnSalesMapper.java                       |    2 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/warehouse/MesWmWarehouseService.java                            |   22 
 docs/seed_quality_bag_granularity.md                                                                                                  |   88 +
 sql/mysql/mes_wm_material_stock_bag.sql                                                                                               |   31 
 sql/mysql/wls_sales_outbound_menu_extract.sql                                                                                         |   25 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/returnsales/MesWmReturnSalesDO.java                      |    8 
 .playwright-mcp/page-2026-09-02T05-57-05-545Z.yml                                                                                     |    1 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/home/vo/MesBagCodeHomeSummaryRespVO.java                  |   19 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/qc/seedquality/MesQcSeedQualityDO.java                      |    2 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnsales/vo/MesWmReturnSalesPageReqVO.java          |    6 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/seedquality/MesQcSeedQualityMapper.java                       |   11 
 docs/bagcode_home_dashboard_integration.md                                                                                            |   83 +
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/batch/MesWmBatchServiceImpl.java                                |   12 
 .playwright-mcp/page-2026-09-02T05-54-09-946Z.yml                                                                                     |    1 
 sql/mysql/mes_wm_transaction_bag_code.sql                                                                                             |   17 
 /dev/null                                                                                                                             |   41 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/warehouse/vo/MesWmWarehouseSaveReqVO.java              |    4 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/transaction/vo/MesWmTransactionRespVO.java             |    6 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/scan/MesProScanEventController.java                   |    2 
 docs/wm_transaction_query.md                                                                                                          |   50 
 sql/mysql/mes_pro_bag_code_qc_status.sql                                                                                              |   13 
 yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/salesnotice/vo/line/MesWmSalesNoticeLineSaveReqVO.java |    3 
 118 files changed, 3,128 insertions(+), 447 deletions(-)

diff --git a/.playwright-mcp/page-2026-09-02T05-27-28-818Z.yml b/.playwright-mcp/page-2026-09-02T05-27-28-818Z.yml
new file mode 100644
index 0000000..cd16c02
--- /dev/null
+++ b/.playwright-mcp/page-2026-09-02T05-27-28-818Z.yml
@@ -0,0 +1 @@
+- generic [active] [ref=e1]: 宸ヤ笟AI鏅洪�犲钩鍙�
\ No newline at end of file
diff --git a/.playwright-mcp/page-2026-09-02T05-54-09-946Z.yml b/.playwright-mcp/page-2026-09-02T05-54-09-946Z.yml
new file mode 100644
index 0000000..6adf37e
--- /dev/null
+++ b/.playwright-mcp/page-2026-09-02T05-54-09-946Z.yml
@@ -0,0 +1 @@
+- generic [active]: 宸ヤ笟AI鏅洪�犲钩鍙�
\ No newline at end of file
diff --git a/.playwright-mcp/page-2026-09-02T05-56-04-640Z.yml b/.playwright-mcp/page-2026-09-02T05-56-04-640Z.yml
new file mode 100644
index 0000000..6adf37e
--- /dev/null
+++ b/.playwright-mcp/page-2026-09-02T05-56-04-640Z.yml
@@ -0,0 +1 @@
+- generic [active]: 宸ヤ笟AI鏅洪�犲钩鍙�
\ No newline at end of file
diff --git a/.playwright-mcp/page-2026-09-02T05-57-05-545Z.yml b/.playwright-mcp/page-2026-09-02T05-57-05-545Z.yml
new file mode 100644
index 0000000..6adf37e
--- /dev/null
+++ b/.playwright-mcp/page-2026-09-02T05-57-05-545Z.yml
@@ -0,0 +1 @@
+- generic [active]: 宸ヤ笟AI鏅洪�犲钩鍙�
\ No newline at end of file
diff --git a/.playwright-mcp/page-2026-09-02T06-03-42-523Z.yml b/.playwright-mcp/page-2026-09-02T06-03-42-523Z.yml
new file mode 100644
index 0000000..6adf37e
--- /dev/null
+++ b/.playwright-mcp/page-2026-09-02T06-03-42-523Z.yml
@@ -0,0 +1 @@
+- generic [active]: 宸ヤ笟AI鏅洪�犲钩鍙�
\ No newline at end of file
diff --git a/.playwright-mcp/page-2026-09-02T06-07-41-739Z.yml b/.playwright-mcp/page-2026-09-02T06-07-41-739Z.yml
new file mode 100644
index 0000000..6adf37e
--- /dev/null
+++ b/.playwright-mcp/page-2026-09-02T06-07-41-739Z.yml
@@ -0,0 +1 @@
+- generic [active]: 宸ヤ笟AI鏅洪�犲钩鍙�
\ No newline at end of file
diff --git a/.playwright-mcp/page-2026-09-02T06-08-22-302Z.yml b/.playwright-mcp/page-2026-09-02T06-08-22-302Z.yml
new file mode 100644
index 0000000..6b5b078
--- /dev/null
+++ b/.playwright-mcp/page-2026-09-02T06-08-22-302Z.yml
@@ -0,0 +1,196 @@
+- generic [ref=f10e7]:
+  - complementary [ref=f10e9]:
+    - generic [ref=f10e10]:
+      - link [ref=f10e13] [cursor=pointer]:
+        - /url: javascript:void 0
+        - img "宸ヤ笟AI鏅洪�犲钩鍙�" [ref=f10e16]
+      - menu [ref=f10e19]:
+        - menuitem [ref=f10e20] [cursor=pointer]
+        - listitem [ref=f10e24]:
+          - generic [ref=f10e25] [cursor=pointer]
+        - listitem [ref=f10e31]:
+          - generic [ref=f10e32] [cursor=pointer]
+        - listitem [ref=f10e36]:
+          - generic [ref=f10e37] [cursor=pointer]
+        - listitem [ref=f10e43]:
+          - generic [ref=f10e44] [cursor=pointer]
+        - listitem [ref=f10e48]:
+          - generic [ref=f10e49] [cursor=pointer]
+        - listitem [ref=f10e53]:
+          - generic [ref=f10e54] [cursor=pointer]
+        - listitem [ref=f10e58]:
+          - generic [ref=f10e59] [cursor=pointer]
+        - listitem [ref=f10e63]:
+          - generic [ref=f10e64] [cursor=pointer]
+        - listitem [ref=f10e68]:
+          - generic [ref=f10e69] [cursor=pointer]
+      - generic [ref=f10e74] [cursor=pointer]
+  - generic [ref=f10e79]:
+    - generic [ref=f10e80]:
+      - banner [ref=f10e81]:
+        - button [ref=f10e82] [cursor=pointer]
+        - button [ref=f10e85] [cursor=pointer]
+        - navigation "breadcrumb" [ref=f10e90]:
+          - list [ref=f10e91]:
+            - listitem [ref=f10e625]:
+              - link "鍑哄簱娴侀�氱鐞�" [ref=f10e626] [cursor=pointer]:
+                - /url: javascript:void 0
+              - listitem [ref=f10e630]
+            - listitem [ref=f10e633]:
+              - link "閿�鍞嚭搴�" [disabled] [ref=f10e634]
+        - generic [ref=f10e98]:
+          - generic [ref=f10e100] [cursor=pointer]:
+            - generic [ref=f10e104]: 鎼滅储
+            - generic [ref=f10e105]: Ctrl K
+          - button [ref=f10e107] [cursor=pointer]
+          - button "dark" [ref=f10e112] [cursor=pointer]
+          - button [ref=f10e121] [cursor=pointer]
+          - button [ref=f10e127] [cursor=pointer]
+          - button [ref=f10e130] [cursor=pointer]
+          - button [ref=f10e136] [cursor=pointer]:
+            - button [ref=f10e138]
+          - button "宸ヤ綔鍙�" [ref=f10e142] [cursor=pointer]
+          - button "鐞嗗憳" [ref=f10e146] [cursor=pointer]
+      - generic [ref=f10e153]:
+        - generic [ref=f10e158]:
+          - generic [ref=f10e159] [cursor=pointer]: 棣栭〉
+          - generic [ref=f10e641] [cursor=pointer]: 閿�鍞嚭搴�
+        - generic [ref=f10e173]:
+          - button [ref=f10e174] [cursor=pointer]
+          - generic [ref=f10e181] [cursor=pointer]
+          - generic [ref=f10e185] [cursor=pointer]
+    - main [ref=f10e192]:
+      - generic [ref=f10e196]:
+        - generic [ref=f10e197]:
+          - generic [ref=f10e198]: 涓氬姟鍏ㄩ摼璺�
+          - generic [ref=f10e210]:
+            - generic [ref=f10e217] [cursor=pointer]:
+              - generic [ref=f10e218]: 鍝佺
+              - generic [ref=f10e219]: "1"
+            - generic [ref=f10e227] [cursor=pointer]:
+              - generic [ref=f10e228]: 浜х嚎
+              - generic [ref=f10e229]: "1"
+            - generic [ref=f10e240] [cursor=pointer]:
+              - generic [ref=f10e241]: 鐢熶骇鎵规
+              - generic [ref=f10e242]: "103"
+            - generic [ref=f10e250] [cursor=pointer]:
+              - generic [ref=f10e251]: 琚嬬爜
+              - generic [ref=f10e252]: 1,101
+            - generic [ref=f10e262] [cursor=pointer]:
+              - generic [ref=f10e263]: 鎵樼洏鐮�
+              - generic [ref=f10e264]: "11"
+            - generic [ref=f10e275] [cursor=pointer]:
+              - generic [ref=f10e276]: 閿�鍞嚭搴�
+              - generic [ref=f10e277]: "0"
+            - generic [ref=f10e285] [cursor=pointer]:
+              - generic [ref=f10e286]: 娴侀�氭壂鐮�
+              - generic [ref=f10e287]: "23"
+            - generic [ref=f10e295] [cursor=pointer]:
+              - generic [ref=f10e296]: 闃茬獪/婧簮
+              - generic [ref=f10e297]: "0"
+        - generic [ref=f10e298]:
+          - generic [ref=f10e308] [cursor=pointer]:
+            - generic [ref=f10e309]: 鍝佺鎬绘暟
+            - generic [ref=f10e310]: "1"
+            - generic [ref=f10e312]: 鍩虹鏁版嵁
+          - generic [ref=f10e323] [cursor=pointer]:
+            - generic [ref=f10e324]: 鐢熶骇鎵规
+            - generic [ref=f10e325]: "103"
+            - generic [ref=f10e327]: 涓�琚嬩竴鐮�
+          - generic [ref=f10e335] [cursor=pointer]:
+            - generic [ref=f10e336]: 琚嬬爜
+            - generic [ref=f10e337]: 1,101
+            - generic [ref=f10e339]: 璧嬬爜
+          - generic [ref=f10e349] [cursor=pointer]:
+            - generic [ref=f10e350]: 鎵樼洏鐮�
+            - generic [ref=f10e351]: "11"
+            - generic [ref=f10e353]: 鎵樼洏
+          - generic [ref=f10e364] [cursor=pointer]:
+            - generic [ref=f10e365]: 閿�鍞嚭搴撳崟
+            - generic [ref=f10e366]: "0"
+            - generic [ref=f10e368]: 鍑哄簱娴侀��
+          - generic [ref=f10e376] [cursor=pointer]:
+            - generic [ref=f10e377]: 绱鎵爜
+            - generic [ref=f10e378]: "23"
+            - generic [ref=f10e380]: 婧簮
+          - generic [ref=f10e387] [cursor=pointer]:
+            - generic [ref=f10e388]: 搴撳瓨棰勮
+            - generic [ref=f10e389]: "0"
+            - generic [ref=f10e391]: 浠撳偍
+          - generic [ref=f10e399] [cursor=pointer]:
+            - generic [ref=f10e400]: 寰呰瘎瀹′笉鍚堟牸
+            - generic [ref=f10e401]: "0"
+            - generic [ref=f10e403]: 璐ㄩ噺
+        - generic [ref=f10e404]:
+          - generic [ref=f10e408]:
+            - generic [ref=f10e409]: 琚嬬爜鐢熸垚瓒嬪娍
+            - generic [ref=f10e411]:
+              - generic [ref=f10e412] [cursor=pointer]:
+                - generic [ref=f10e413]:
+                  - radio "杩� 7 澶�" [checked]
+                - generic [ref=f10e414]: 杩� 7 澶�
+              - generic [ref=f10e415] [cursor=pointer]:
+                - generic [ref=f10e416]:
+                  - radio "杩� 30 澶�"
+                - generic [ref=f10e417]: 杩� 30 澶�
+          - generic [ref=f10e422]: 娑堣垂鑰呮壂鐮佸湴鍖哄垎甯�
+        - generic [ref=f10e431]:
+          - generic [ref=f10e432]: 鎵规鐘舵�佸垎甯�
+          - generic [ref=f10e441]: 琚嬬爜鐘舵�佸垎甯�
+          - generic [ref=f10e451]:
+            - generic [ref=f10e452]: 寰呭姙涓庨璀�
+            - generic [ref=f10e456]:
+              - generic [ref=f10e461] [cursor=pointer]:
+                - generic [ref=f10e462]: 绐滆揣棰勮
+                - generic [ref=f10e463]: 寰呭鐞嗙殑绐滆揣棰勮
+              - generic [ref=f10e467] [cursor=pointer]:
+                - generic [ref=f10e468]: 搴撳瓨棰勮
+                - generic [ref=f10e469]: 鏈鐞嗙殑搴撳瓨棰勮
+              - generic [ref=f10e474] [cursor=pointer]:
+                - generic [ref=f10e475]: 涓嶅悎鏍煎搧
+                - generic [ref=f10e476]: 寰呰瘎瀹$殑涓嶅悎鏍煎搧鍗�
+        - generic [ref=f10e477]:
+          - generic [ref=f10e478]: 蹇嵎鍏ュ彛
+          - generic [ref=f10e482]:
+            - generic [ref=f10e484]:
+              - generic [ref=f10e485]: 鍩虹鏁版嵁
+              - generic [ref=f10e486]:
+                - generic [ref=f10e487] [cursor=pointer]: 鍝佺绠$悊
+                - generic [ref=f10e493] [cursor=pointer]: 鍝佺鍒嗙被
+                - generic [ref=f10e499] [cursor=pointer]: 璁¢噺鍗曚綅
+            - generic [ref=f10e504]:
+              - generic [ref=f10e505]: 涓�琚嬩竴鐮佺敓浜�
+              - generic [ref=f10e506]:
+                - generic [ref=f10e507] [cursor=pointer]: 浜х嚎缁存姢
+                - generic [ref=f10e511] [cursor=pointer]: 鎵规鐢熶骇
+                - generic [ref=f10e518] [cursor=pointer]: 璧嬬爜绠$悊
+                - generic [ref=f10e522] [cursor=pointer]: 鎵樼洏鐮�
+                - generic [ref=f10e528] [cursor=pointer]: 鎵爜鍏ュ簱
+            - generic [ref=f10e533]:
+              - generic [ref=f10e534]: 鍑哄簱娴侀��
+              - generic [ref=f10e535]:
+                - generic [ref=f10e536] [cursor=pointer]: 鍙戣揣閫氱煡
+                - generic [ref=f10e540] [cursor=pointer]: 閿�鍞嚭搴�
+                - generic [ref=f10e547] [cursor=pointer]: 閿�鍞��璐�
+            - generic [ref=f10e554]:
+              - generic [ref=f10e555]: 闃蹭吉闃茬獪璐�
+              - generic [ref=f10e556]:
+                - generic [ref=f10e557] [cursor=pointer]: 绐滆揣棰勮
+                - generic [ref=f10e561] [cursor=pointer]: 鎵爜鍦扮悊鍒嗗竷
+                - generic [ref=f10e567] [cursor=pointer]: 瀹屾暣鎬ф牎楠�
+            - generic [ref=f10e575]:
+              - generic [ref=f10e576]: 婧簮鍒嗘瀽
+              - generic [ref=f10e577]: 娑堣垂鑰呮壂鐮佺粺璁�
+            - generic [ref=f10e583]:
+              - generic [ref=f10e584]: 浠撳偍鐗╂祦
+              - generic [ref=f10e585]:
+                - generic [ref=f10e586] [cursor=pointer]: 浠撳簱璁剧疆
+                - generic [ref=f10e592] [cursor=pointer]: 搴撳瓨鐜版湁閲�
+                - generic [ref=f10e598] [cursor=pointer]: 搴撳瓨棰勮
+                - generic [ref=f10e601] [cursor=pointer]: 杞Щ璋冩嫧
+            - generic [ref=f10e606]:
+              - generic [ref=f10e607]: 璐ㄩ噺绠$悊
+              - generic [ref=f10e608]:
+                - generic [ref=f10e609] [cursor=pointer]: 鎵规杩芥函
+                - generic [ref=f10e615] [cursor=pointer]: 涓嶅悎鏍煎搧
+                - generic [ref=f10e619] [cursor=pointer]: 绉嶅瓙璐ㄦ
\ No newline at end of file
diff --git a/.playwright-mcp/page-2026-09-02T06-08-26-480Z.yml b/.playwright-mcp/page-2026-09-02T06-08-26-480Z.yml
new file mode 100644
index 0000000..6adf37e
--- /dev/null
+++ b/.playwright-mcp/page-2026-09-02T06-08-26-480Z.yml
@@ -0,0 +1 @@
+- generic [active]: 宸ヤ笟AI鏅洪�犲钩鍙�
\ No newline at end of file
diff --git a/.playwright-mcp/page-2026-09-02T06-08-42-531Z.yml b/.playwright-mcp/page-2026-09-02T06-08-42-531Z.yml
new file mode 100644
index 0000000..2559c12
--- /dev/null
+++ b/.playwright-mcp/page-2026-09-02T06-08-42-531Z.yml
@@ -0,0 +1,196 @@
+- generic [ref=f11e7]:
+  - complementary [ref=f11e9]:
+    - generic [ref=f11e10]:
+      - link [ref=f11e13] [cursor=pointer]:
+        - /url: javascript:void 0
+        - img "宸ヤ笟AI鏅洪�犲钩鍙�" [ref=f11e16]
+      - menu [ref=f11e19]:
+        - menuitem [ref=f11e20] [cursor=pointer]
+        - listitem [ref=f11e24]:
+          - generic [ref=f11e25] [cursor=pointer]
+        - listitem [ref=f11e31]:
+          - generic [ref=f11e32] [cursor=pointer]
+        - listitem [ref=f11e36]:
+          - generic [ref=f11e37] [cursor=pointer]
+        - listitem [ref=f11e43]:
+          - generic [ref=f11e44] [cursor=pointer]
+        - listitem [ref=f11e48]:
+          - generic [ref=f11e49] [cursor=pointer]
+        - listitem [ref=f11e53]:
+          - generic [ref=f11e54] [cursor=pointer]
+        - listitem [ref=f11e58]:
+          - generic [ref=f11e59] [cursor=pointer]
+        - listitem [ref=f11e63]:
+          - generic [ref=f11e64] [cursor=pointer]
+        - listitem [ref=f11e68]:
+          - generic [ref=f11e69] [cursor=pointer]
+      - generic [ref=f11e74] [cursor=pointer]
+  - generic [ref=f11e79]:
+    - generic [ref=f11e80]:
+      - banner [ref=f11e81]:
+        - button [ref=f11e82] [cursor=pointer]
+        - button [ref=f11e85] [cursor=pointer]
+        - navigation "breadcrumb" [ref=f11e90]:
+          - list [ref=f11e91]:
+            - listitem [ref=f11e630]:
+              - link "闃蹭吉闃茬獪璐х鐞�" [ref=f11e631] [cursor=pointer]:
+                - /url: javascript:void 0
+              - listitem [ref=f11e635]
+            - listitem [ref=f11e638]:
+              - link "绐滆揣棰勮" [disabled] [ref=f11e639]
+        - generic [ref=f11e98]:
+          - generic [ref=f11e100] [cursor=pointer]:
+            - generic [ref=f11e104]: 鎼滅储
+            - generic [ref=f11e105]: Ctrl K
+          - button [ref=f11e107] [cursor=pointer]
+          - button "dark" [ref=f11e112] [cursor=pointer]
+          - button [ref=f11e121] [cursor=pointer]
+          - button [ref=f11e127] [cursor=pointer]
+          - button [ref=f11e130] [cursor=pointer]
+          - button [ref=f11e136] [cursor=pointer]:
+            - button [ref=f11e138]
+          - button "宸ヤ綔鍙�" [ref=f11e142] [cursor=pointer]
+          - button "鐞嗗憳" [ref=f11e146] [cursor=pointer]
+      - generic [ref=f11e153]:
+        - generic [ref=f11e158]:
+          - generic [ref=f11e159] [cursor=pointer]: 棣栭〉
+          - generic [ref=f11e645] [cursor=pointer]: 绐滆揣棰勮
+        - generic [ref=f11e173]:
+          - button [ref=f11e174] [cursor=pointer]
+          - generic [ref=f11e181] [cursor=pointer]
+          - generic [ref=f11e185] [cursor=pointer]
+    - main [ref=f11e192]:
+      - generic [ref=f11e196]:
+        - generic [ref=f11e197]:
+          - generic [ref=f11e198]: 涓氬姟鍏ㄩ摼璺�
+          - generic [ref=f11e210]:
+            - generic [ref=f11e217] [cursor=pointer]:
+              - generic [ref=f11e218]: 鍝佺
+              - generic [ref=f11e219]: "1"
+            - generic [ref=f11e227] [cursor=pointer]:
+              - generic [ref=f11e228]: 浜х嚎
+              - generic [ref=f11e229]: "1"
+            - generic [ref=f11e240] [cursor=pointer]:
+              - generic [ref=f11e241]: 鐢熶骇鎵规
+              - generic [ref=f11e242]: "103"
+            - generic [ref=f11e250] [cursor=pointer]:
+              - generic [ref=f11e251]: 琚嬬爜
+              - generic [ref=f11e252]: 1,101
+            - generic [ref=f11e262] [cursor=pointer]:
+              - generic [ref=f11e263]: 鎵樼洏鐮�
+              - generic [ref=f11e264]: "11"
+            - generic [ref=f11e275] [cursor=pointer]:
+              - generic [ref=f11e276]: 閿�鍞嚭搴�
+              - generic [ref=f11e277]: "0"
+            - generic [ref=f11e285] [cursor=pointer]:
+              - generic [ref=f11e286]: 娴侀�氭壂鐮�
+              - generic [ref=f11e287]: "23"
+            - generic [ref=f11e295] [cursor=pointer]:
+              - generic [ref=f11e296]: 闃茬獪/婧簮
+              - generic [ref=f11e297]: "0"
+        - generic [ref=f11e298]:
+          - generic [ref=f11e308] [cursor=pointer]:
+            - generic [ref=f11e309]: 鍝佺鎬绘暟
+            - generic [ref=f11e310]: "1"
+            - generic [ref=f11e312]: 鍩虹鏁版嵁
+          - generic [ref=f11e323] [cursor=pointer]:
+            - generic [ref=f11e324]: 鐢熶骇鎵规
+            - generic [ref=f11e325]: "103"
+            - generic [ref=f11e327]: 涓�琚嬩竴鐮�
+          - generic [ref=f11e335] [cursor=pointer]:
+            - generic [ref=f11e336]: 琚嬬爜
+            - generic [ref=f11e337]: 1,101
+            - generic [ref=f11e339]: 璧嬬爜
+          - generic [ref=f11e349] [cursor=pointer]:
+            - generic [ref=f11e350]: 鎵樼洏鐮�
+            - generic [ref=f11e351]: "11"
+            - generic [ref=f11e353]: 鎵樼洏
+          - generic [ref=f11e364] [cursor=pointer]:
+            - generic [ref=f11e365]: 閿�鍞嚭搴撳崟
+            - generic [ref=f11e366]: "0"
+            - generic [ref=f11e368]: 鍑哄簱娴侀��
+          - generic [ref=f11e376] [cursor=pointer]:
+            - generic [ref=f11e377]: 绱鎵爜
+            - generic [ref=f11e378]: "23"
+            - generic [ref=f11e380]: 婧簮
+          - generic [ref=f11e387] [cursor=pointer]:
+            - generic [ref=f11e388]: 搴撳瓨棰勮
+            - generic [ref=f11e389]: "0"
+            - generic [ref=f11e391]: 浠撳偍
+          - generic [ref=f11e399] [cursor=pointer]:
+            - generic [ref=f11e400]: 寰呰瘎瀹′笉鍚堟牸
+            - generic [ref=f11e401]: "0"
+            - generic [ref=f11e403]: 璐ㄩ噺
+        - generic [ref=f11e404]:
+          - generic [ref=f11e408]:
+            - generic [ref=f11e409]: 琚嬬爜鐢熸垚瓒嬪娍
+            - generic [ref=f11e411]:
+              - generic [ref=f11e412] [cursor=pointer]:
+                - generic [ref=f11e413]:
+                  - radio "杩� 7 澶�" [checked]
+                - generic [ref=f11e414]: 杩� 7 澶�
+              - generic [ref=f11e415] [cursor=pointer]:
+                - generic [ref=f11e416]:
+                  - radio "杩� 30 澶�"
+                - generic [ref=f11e417]: 杩� 30 澶�
+          - generic [ref=f11e422]: 娑堣垂鑰呮壂鐮佸湴鍖哄垎甯�
+        - generic [ref=f11e431]:
+          - generic [ref=f11e432]: 鎵规鐘舵�佸垎甯�
+          - generic [ref=f11e441]: 琚嬬爜鐘舵�佸垎甯�
+          - generic [ref=f11e451]:
+            - generic [ref=f11e452]: 寰呭姙涓庨璀�
+            - generic [ref=f11e456]:
+              - generic [ref=f11e461] [cursor=pointer]:
+                - generic [ref=f11e462]: 绐滆揣棰勮
+                - generic [ref=f11e463]: 寰呭鐞嗙殑绐滆揣棰勮
+              - generic [ref=f11e467] [cursor=pointer]:
+                - generic [ref=f11e468]: 搴撳瓨棰勮
+                - generic [ref=f11e469]: 鏈鐞嗙殑搴撳瓨棰勮
+              - generic [ref=f11e474] [cursor=pointer]:
+                - generic [ref=f11e475]: 涓嶅悎鏍煎搧
+                - generic [ref=f11e476]: 寰呰瘎瀹$殑涓嶅悎鏍煎搧鍗�
+        - generic [ref=f11e477]:
+          - generic [ref=f11e478]: 蹇嵎鍏ュ彛
+          - generic [ref=f11e482]:
+            - generic [ref=f11e484]:
+              - generic [ref=f11e485]: 鍩虹鏁版嵁
+              - generic [ref=f11e486]:
+                - generic [ref=f11e487] [cursor=pointer]: 鍝佺绠$悊
+                - generic [ref=f11e493] [cursor=pointer]: 鍝佺鍒嗙被
+                - generic [ref=f11e499] [cursor=pointer]: 璁¢噺鍗曚綅
+            - generic [ref=f11e504]:
+              - generic [ref=f11e505]: 涓�琚嬩竴鐮佺敓浜�
+              - generic [ref=f11e506]:
+                - generic [ref=f11e507] [cursor=pointer]: 浜х嚎缁存姢
+                - generic [ref=f11e511] [cursor=pointer]: 鎵规鐢熶骇
+                - generic [ref=f11e518] [cursor=pointer]: 璧嬬爜绠$悊
+                - generic [ref=f11e522] [cursor=pointer]: 鎵樼洏鐮�
+                - generic [ref=f11e528] [cursor=pointer]: 鎵爜鍏ュ簱
+            - generic [ref=f11e533]:
+              - generic [ref=f11e534]: 鍑哄簱娴侀��
+              - generic [ref=f11e535]:
+                - generic [ref=f11e536] [cursor=pointer]: 鍙戣揣閫氱煡
+                - generic [ref=f11e540] [cursor=pointer]: 閿�鍞嚭搴�
+                - generic [ref=f11e547] [cursor=pointer]: 閿�鍞��璐�
+            - generic [ref=f11e554]:
+              - generic [ref=f11e555]: 闃蹭吉闃茬獪璐�
+              - generic [ref=f11e556]:
+                - generic [ref=f11e557] [cursor=pointer]: 绐滆揣棰勮
+                - generic [ref=f11e561] [cursor=pointer]: 鎵爜鍦扮悊鍒嗗竷
+                - generic [ref=f11e567] [cursor=pointer]: 瀹屾暣鎬ф牎楠�
+            - generic [ref=f11e575]:
+              - generic [ref=f11e576]: 婧簮鍒嗘瀽
+              - generic [ref=f11e577]: 娑堣垂鑰呮壂鐮佺粺璁�
+            - generic [ref=f11e583]:
+              - generic [ref=f11e584]: 浠撳偍鐗╂祦
+              - generic [ref=f11e585]:
+                - generic [ref=f11e586] [cursor=pointer]: 浠撳簱璁剧疆
+                - generic [ref=f11e592] [cursor=pointer]: 搴撳瓨鐜版湁閲�
+                - generic [ref=f11e598] [cursor=pointer]: 搴撳瓨棰勮
+                - generic [ref=f11e601] [cursor=pointer]: 杞Щ璋冩嫧
+            - generic [ref=f11e606]:
+              - generic [ref=f11e607]: 璐ㄩ噺绠$悊
+              - generic [ref=f11e608]:
+                - generic [ref=f11e609] [cursor=pointer]: 鎵规杩芥函
+                - generic [ref=f11e615] [cursor=pointer]: 涓嶅悎鏍煎搧
+                - generic [ref=f11e619] [cursor=pointer]: 绉嶅瓙璐ㄦ
\ No newline at end of file
diff --git a/.playwright-mcp/page-2026-09-02T06-08-53-561Z.yml b/.playwright-mcp/page-2026-09-02T06-08-53-561Z.yml
new file mode 100644
index 0000000..6adf37e
--- /dev/null
+++ b/.playwright-mcp/page-2026-09-02T06-08-53-561Z.yml
@@ -0,0 +1 @@
+- generic [active]: 宸ヤ笟AI鏅洪�犲钩鍙�
\ No newline at end of file
diff --git a/.playwright-mcp/page-2026-09-02T06-11-00-277Z.yml b/.playwright-mcp/page-2026-09-02T06-11-00-277Z.yml
new file mode 100644
index 0000000..6adf37e
--- /dev/null
+++ b/.playwright-mcp/page-2026-09-02T06-11-00-277Z.yml
@@ -0,0 +1 @@
+- generic [active]: 宸ヤ笟AI鏅洪�犲钩鍙�
\ No newline at end of file
diff --git a/.playwright-mcp/page-2026-09-02T06-14-13-812Z.yml b/.playwright-mcp/page-2026-09-02T06-14-13-812Z.yml
new file mode 100644
index 0000000..6adf37e
--- /dev/null
+++ b/.playwright-mcp/page-2026-09-02T06-14-13-812Z.yml
@@ -0,0 +1 @@
+- generic [active]: 宸ヤ笟AI鏅洪�犲钩鍙�
\ No newline at end of file
diff --git a/.playwright-mcp/page-2026-09-02T06-14-34-873Z.yml b/.playwright-mcp/page-2026-09-02T06-14-34-873Z.yml
new file mode 100644
index 0000000..63c6e2e
--- /dev/null
+++ b/.playwright-mcp/page-2026-09-02T06-14-34-873Z.yml
@@ -0,0 +1,190 @@
+- generic [ref=f15e7]:
+  - complementary [ref=f15e9]:
+    - generic [ref=f15e10]:
+      - link [ref=f15e13] [cursor=pointer]:
+        - /url: javascript:void 0
+        - img "宸ヤ笟AI鏅洪�犲钩鍙�" [ref=f15e16]
+      - menu [ref=f15e19]:
+        - menuitem [ref=f15e20] [cursor=pointer]
+        - listitem [ref=f15e24]:
+          - generic [ref=f15e25] [cursor=pointer]
+        - listitem [ref=f15e31]:
+          - generic [ref=f15e32] [cursor=pointer]
+        - listitem [ref=f15e36]:
+          - generic [ref=f15e37] [cursor=pointer]
+        - listitem [ref=f15e43]:
+          - generic [ref=f15e44] [cursor=pointer]
+        - listitem [ref=f15e48]:
+          - generic [ref=f15e49] [cursor=pointer]
+        - listitem [ref=f15e53]:
+          - generic [ref=f15e54] [cursor=pointer]
+        - listitem [ref=f15e58]:
+          - generic [ref=f15e59] [cursor=pointer]
+        - listitem [ref=f15e63]:
+          - generic [ref=f15e64] [cursor=pointer]
+        - listitem [ref=f15e68]:
+          - generic [ref=f15e69] [cursor=pointer]
+      - generic [ref=f15e74] [cursor=pointer]
+  - generic [ref=f15e79]:
+    - generic [ref=f15e80]:
+      - banner [ref=f15e81]:
+        - button [ref=f15e82] [cursor=pointer]
+        - button [ref=f15e85] [cursor=pointer]
+        - navigation "breadcrumb" [ref=f15e90]:
+          - list [ref=f15e91]:
+            - listitem [ref=f15e92]:
+              - link "棣栭〉" [disabled] [ref=f15e93]
+        - generic [ref=f15e98]:
+          - generic [ref=f15e100] [cursor=pointer]:
+            - generic [ref=f15e104]: 鎼滅储
+            - generic [ref=f15e105]: Ctrl K
+          - button [ref=f15e107] [cursor=pointer]
+          - button "dark" [ref=f15e112] [cursor=pointer]
+          - button [ref=f15e121] [cursor=pointer]
+          - button [ref=f15e127] [cursor=pointer]
+          - button [ref=f15e130] [cursor=pointer]
+          - button [ref=f15e136] [cursor=pointer]:
+            - button [ref=f15e138]
+          - button "宸ヤ綔鍙�" [ref=f15e142] [cursor=pointer]
+          - button "鐞嗗憳" [ref=f15e146] [cursor=pointer]
+      - generic [ref=f15e153]:
+        - generic [ref=f15e154]: 棣栭〉
+        - generic [ref=f15e173]:
+          - button [ref=f15e174] [cursor=pointer]
+          - generic [ref=f15e181] [cursor=pointer]
+          - generic [ref=f15e185] [cursor=pointer]
+    - main [ref=f15e192]:
+      - generic [ref=f15e196]:
+        - generic [ref=f15e197]:
+          - generic [ref=f15e198]: 涓氬姟鍏ㄩ摼璺�
+          - generic [ref=f15e210]:
+            - generic [ref=f15e217] [cursor=pointer]:
+              - generic [ref=f15e218]: 鍝佺
+              - generic [ref=f15e219]: "1"
+            - generic [ref=f15e227] [cursor=pointer]:
+              - generic [ref=f15e228]: 浜х嚎
+              - generic [ref=f15e229]: "1"
+            - generic [ref=f15e240] [cursor=pointer]:
+              - generic [ref=f15e241]: 鐢熶骇鎵规
+              - generic [ref=f15e242]: "103"
+            - generic [ref=f15e250] [cursor=pointer]:
+              - generic [ref=f15e251]: 琚嬬爜
+              - generic [ref=f15e252]: 1,101
+            - generic [ref=f15e262] [cursor=pointer]:
+              - generic [ref=f15e263]: 鎵樼洏鐮�
+              - generic [ref=f15e264]: "11"
+            - generic [ref=f15e275] [cursor=pointer]:
+              - generic [ref=f15e276]: 閿�鍞嚭搴�
+              - generic [ref=f15e277]: "0"
+            - generic [ref=f15e285] [cursor=pointer]:
+              - generic [ref=f15e286]: 娴侀�氭壂鐮�
+              - generic [ref=f15e287]: "23"
+            - generic [ref=f15e295] [cursor=pointer]:
+              - generic [ref=f15e296]: 闃茬獪/婧簮
+              - generic [ref=f15e297]: "0"
+        - generic [ref=f15e298]:
+          - generic [ref=f15e308] [cursor=pointer]:
+            - generic [ref=f15e309]: 鍝佺鎬绘暟
+            - generic [ref=f15e310]: "1"
+            - generic [ref=f15e311]: 鍩虹鏁版嵁
+          - generic [ref=f15e322] [cursor=pointer]:
+            - generic [ref=f15e323]: 鐢熶骇鎵规
+            - generic [ref=f15e324]: "92"
+            - generic [ref=f15e325]: 涓�琚嬩竴鐮�
+          - generic [ref=f15e333] [cursor=pointer]:
+            - generic [ref=f15e334]: 琚嬬爜
+            - generic [ref=f15e335]: "981"
+            - generic [ref=f15e336]: 璧嬬爜
+          - generic [ref=f15e346] [cursor=pointer]:
+            - generic [ref=f15e347]: 鎵樼洏鐮�
+            - generic [ref=f15e348]: "10"
+            - generic [ref=f15e349]: 鎵樼洏
+          - generic [ref=f15e360] [cursor=pointer]:
+            - generic [ref=f15e361]: 閿�鍞嚭搴撳崟
+            - generic [ref=f15e362]: "0"
+            - generic [ref=f15e363]: 鍑哄簱娴侀��
+          - generic [ref=f15e371] [cursor=pointer]:
+            - generic [ref=f15e372]: 绱鎵爜
+            - generic [ref=f15e373]: "20"
+            - generic [ref=f15e374]: 婧簮
+          - generic [ref=f15e381] [cursor=pointer]:
+            - generic [ref=f15e382]: 搴撳瓨棰勮
+            - generic [ref=f15e383]: "0"
+            - generic [ref=f15e384]: 浠撳偍
+          - generic [ref=f15e392] [cursor=pointer]:
+            - generic [ref=f15e393]: 寰呰瘎瀹′笉鍚堟牸
+            - generic [ref=f15e394]: "0"
+            - generic [ref=f15e395]: 璐ㄩ噺
+        - generic [ref=f15e396]:
+          - generic [ref=f15e400]:
+            - generic [ref=f15e401]: 琚嬬爜鐢熸垚瓒嬪娍
+            - generic [ref=f15e403]:
+              - generic [ref=f15e404] [cursor=pointer]:
+                - generic [ref=f15e405]:
+                  - radio "杩� 7 澶�" [checked]
+                - generic [ref=f15e406]: 杩� 7 澶�
+              - generic [ref=f15e407] [cursor=pointer]:
+                - generic [ref=f15e408]:
+                  - radio "杩� 30 澶�"
+                - generic [ref=f15e409]: 杩� 30 澶�
+          - generic [ref=f15e414]: 娑堣垂鑰呮壂鐮佸湴鍖哄垎甯�
+        - generic [ref=f15e423]:
+          - generic [ref=f15e424]: 鎵规鐘舵�佸垎甯�
+          - generic [ref=f15e433]: 琚嬬爜鐘舵�佸垎甯�
+          - generic [ref=f15e443]:
+            - generic [ref=f15e444]: 寰呭姙涓庨璀�
+            - generic [ref=f15e448]:
+              - generic [ref=f15e453] [cursor=pointer]:
+                - generic [ref=f15e454]: 绐滆揣棰勮
+                - generic [ref=f15e455]: 寰呭鐞嗙殑绐滆揣棰勮
+              - generic [ref=f15e459] [cursor=pointer]:
+                - generic [ref=f15e460]: 搴撳瓨棰勮
+                - generic [ref=f15e461]: 鏈鐞嗙殑搴撳瓨棰勮
+              - generic [ref=f15e466] [cursor=pointer]:
+                - generic [ref=f15e467]: 涓嶅悎鏍煎搧
+                - generic [ref=f15e468]: 寰呰瘎瀹$殑涓嶅悎鏍煎搧鍗�
+        - generic [ref=f15e469]:
+          - generic [ref=f15e470]: 蹇嵎鍏ュ彛
+          - generic [ref=f15e474]:
+            - generic [ref=f15e476]:
+              - generic [ref=f15e477]: 鍩虹鏁版嵁
+              - generic [ref=f15e478]:
+                - generic [ref=f15e479] [cursor=pointer]: 鍝佺绠$悊
+                - generic [ref=f15e485] [cursor=pointer]: 鍝佺鍒嗙被
+                - generic [ref=f15e491] [cursor=pointer]: 璁¢噺鍗曚綅
+            - generic [ref=f15e496]:
+              - generic [ref=f15e497]: 涓�琚嬩竴鐮佺敓浜�
+              - generic [ref=f15e498]:
+                - generic [ref=f15e499] [cursor=pointer]: 浜х嚎缁存姢
+                - generic [ref=f15e503] [cursor=pointer]: 鎵规鐢熶骇
+                - generic [ref=f15e510] [cursor=pointer]: 璧嬬爜绠$悊
+                - generic [ref=f15e514] [cursor=pointer]: 鎵樼洏鐮�
+                - generic [ref=f15e520] [cursor=pointer]: 鎵爜鍏ュ簱
+            - generic [ref=f15e525]:
+              - generic [ref=f15e526]: 鍑哄簱娴侀��
+              - generic [ref=f15e527]:
+                - generic [ref=f15e528] [cursor=pointer]: 鍙戣揣閫氱煡
+                - generic [ref=f15e532] [cursor=pointer]: 閿�鍞嚭搴�
+                - generic [ref=f15e539] [cursor=pointer]: 閿�鍞��璐�
+            - generic [ref=f15e546]:
+              - generic [ref=f15e547]: 闃蹭吉闃茬獪璐�
+              - generic [ref=f15e548]:
+                - generic [ref=f15e549] [cursor=pointer]: 绐滆揣棰勮
+                - generic [ref=f15e553] [cursor=pointer]: 鎵爜鍦扮悊鍒嗗竷
+                - generic [ref=f15e559] [cursor=pointer]: 瀹屾暣鎬ф牎楠�
+            - generic [ref=f15e567]:
+              - generic [ref=f15e568]: 婧簮鍒嗘瀽
+              - generic [ref=f15e569]: 娑堣垂鑰呮壂鐮佺粺璁�
+            - generic [ref=f15e575]:
+              - generic [ref=f15e576]: 浠撳偍鐗╂祦
+              - generic [ref=f15e577]:
+                - generic [ref=f15e578] [cursor=pointer]: 浠撳簱璁剧疆
+                - generic [ref=f15e584] [cursor=pointer]: 搴撳瓨鐜版湁閲�
+                - generic [ref=f15e590] [cursor=pointer]: 搴撳瓨棰勮
+                - generic [ref=f15e593] [cursor=pointer]: 杞Щ璋冩嫧
+            - generic [ref=f15e598]:
+              - generic [ref=f15e599]: 璐ㄩ噺绠$悊
+              - generic [ref=f15e600]:
+                - generic [ref=f15e601] [cursor=pointer]: 鎵规杩芥函
+                - generic [ref=f15e607] [cursor=pointer]: 涓嶅悎鏍煎搧
+                - generic [ref=f15e611] [cursor=pointer]: 绉嶅瓙璐ㄦ
\ No newline at end of file
diff --git a/.playwright-mcp/page-2026-09-02T06-16-10-960Z.yml b/.playwright-mcp/page-2026-09-02T06-16-10-960Z.yml
new file mode 100644
index 0000000..4c7a07d
--- /dev/null
+++ b/.playwright-mcp/page-2026-09-02T06-16-10-960Z.yml
@@ -0,0 +1,190 @@
+- generic [ref=f15e7]:
+  - complementary [ref=f15e9]:
+    - generic [ref=f15e10]:
+      - link [ref=f15e13] [cursor=pointer]:
+        - /url: javascript:void 0
+        - img "宸ヤ笟AI鏅洪�犲钩鍙�" [ref=f15e16]
+      - menu [ref=f15e19]:
+        - menuitem [ref=f15e20] [cursor=pointer]
+        - listitem [ref=f15e24]:
+          - generic [ref=f15e25] [cursor=pointer]
+        - listitem [ref=f15e31]:
+          - generic [ref=f15e32] [cursor=pointer]
+        - listitem [ref=f15e36]:
+          - generic [ref=f15e37] [cursor=pointer]
+        - listitem [ref=f15e43]:
+          - generic [ref=f15e44] [cursor=pointer]
+        - listitem [ref=f15e48]:
+          - generic [ref=f15e49] [cursor=pointer]
+        - listitem [ref=f15e53]:
+          - generic [ref=f15e54] [cursor=pointer]
+        - listitem [ref=f15e58]:
+          - generic [ref=f15e59] [cursor=pointer]
+        - listitem [ref=f15e63]:
+          - generic [ref=f15e64] [cursor=pointer]
+        - listitem [ref=f15e68]:
+          - generic [ref=f15e69] [cursor=pointer]
+      - generic [ref=f15e74] [cursor=pointer]
+  - generic [ref=f15e79]:
+    - generic [ref=f15e80]:
+      - banner [ref=f15e81]:
+        - button [ref=f15e82] [cursor=pointer]
+        - button [ref=f15e85] [cursor=pointer]
+        - navigation "breadcrumb" [ref=f15e90]:
+          - list [ref=f15e91]:
+            - listitem [ref=f15e92]:
+              - link "棣栭〉" [disabled] [ref=f15e93]
+        - generic [ref=f15e98]:
+          - generic [ref=f15e100] [cursor=pointer]:
+            - generic [ref=f15e104]: 鎼滅储
+            - generic [ref=f15e105]: Ctrl K
+          - button [ref=f15e107] [cursor=pointer]
+          - button "dark" [ref=f15e112] [cursor=pointer]
+          - button [ref=f15e121] [cursor=pointer]
+          - button [ref=f15e127] [cursor=pointer]
+          - button [ref=f15e130] [cursor=pointer]
+          - button [ref=f15e136] [cursor=pointer]:
+            - button [ref=f15e138]
+          - button "宸ヤ綔鍙�" [ref=f15e142] [cursor=pointer]
+          - button "鐞嗗憳" [ref=f15e146] [cursor=pointer]
+      - generic [ref=f15e153]:
+        - generic [ref=f15e154]: 棣栭〉
+        - generic [ref=f15e173]:
+          - button [ref=f15e174] [cursor=pointer]
+          - generic [ref=f15e181] [cursor=pointer]
+          - generic [ref=f15e185] [cursor=pointer]
+    - main [ref=f15e192]:
+      - generic [ref=f15e196]:
+        - generic [ref=f15e197]:
+          - generic [ref=f15e198]: 涓氬姟鍏ㄩ摼璺�
+          - generic [ref=f15e210]:
+            - generic [ref=f15e217] [cursor=pointer]:
+              - generic [ref=f15e218]: 鍝佺
+              - generic [ref=f15e219]: "1"
+            - generic [ref=f15e227] [cursor=pointer]:
+              - generic [ref=f15e228]: 浜х嚎
+              - generic [ref=f15e229]: "1"
+            - generic [ref=f15e240] [cursor=pointer]:
+              - generic [ref=f15e241]: 鐢熶骇鎵规
+              - generic [ref=f15e242]: "103"
+            - generic [ref=f15e250] [cursor=pointer]:
+              - generic [ref=f15e251]: 琚嬬爜
+              - generic [ref=f15e252]: 1,101
+            - generic [ref=f15e262] [cursor=pointer]:
+              - generic [ref=f15e263]: 鎵樼洏鐮�
+              - generic [ref=f15e264]: "11"
+            - generic [ref=f15e275] [cursor=pointer]:
+              - generic [ref=f15e276]: 閿�鍞嚭搴�
+              - generic [ref=f15e277]: "0"
+            - generic [ref=f15e285] [cursor=pointer]:
+              - generic [ref=f15e286]: 娴侀�氭壂鐮�
+              - generic [ref=f15e287]: "23"
+            - generic [ref=f15e295] [cursor=pointer]:
+              - generic [ref=f15e296]: 闃茬獪/婧簮
+              - generic [ref=f15e297]: "0"
+        - generic [ref=f15e298]:
+          - generic [ref=f15e308] [cursor=pointer]:
+            - generic [ref=f15e309]: 鍝佺鎬绘暟
+            - generic [ref=f15e310]: "1"
+            - generic [ref=f15e311]: 鍩虹鏁版嵁
+          - generic [ref=f15e322] [cursor=pointer]:
+            - generic [ref=f15e323]: 鐢熶骇鎵规
+            - generic [ref=f15e324]: "103"
+            - generic [ref=f15e325]: 涓�琚嬩竴鐮�
+          - generic [ref=f15e333] [cursor=pointer]:
+            - generic [ref=f15e334]: 琚嬬爜
+            - generic [ref=f15e335]: 1,101
+            - generic [ref=f15e336]: 璧嬬爜
+          - generic [ref=f15e346] [cursor=pointer]:
+            - generic [ref=f15e347]: 鎵樼洏鐮�
+            - generic [ref=f15e348]: "11"
+            - generic [ref=f15e349]: 鎵樼洏
+          - generic [ref=f15e360] [cursor=pointer]:
+            - generic [ref=f15e361]: 閿�鍞嚭搴撳崟
+            - generic [ref=f15e362]: "0"
+            - generic [ref=f15e363]: 鍑哄簱娴侀��
+          - generic [ref=f15e371] [cursor=pointer]:
+            - generic [ref=f15e372]: 绱鎵爜
+            - generic [ref=f15e373]: "23"
+            - generic [ref=f15e374]: 婧簮
+          - generic [ref=f15e381] [cursor=pointer]:
+            - generic [ref=f15e382]: 搴撳瓨棰勮
+            - generic [ref=f15e383]: "0"
+            - generic [ref=f15e384]: 浠撳偍
+          - generic [ref=f15e392] [cursor=pointer]:
+            - generic [ref=f15e393]: 寰呰瘎瀹′笉鍚堟牸
+            - generic [ref=f15e394]: "0"
+            - generic [ref=f15e395]: 璐ㄩ噺
+        - generic [ref=f15e396]:
+          - generic [ref=f15e400]:
+            - generic [ref=f15e401]: 琚嬬爜鐢熸垚瓒嬪娍
+            - generic [ref=f15e403]:
+              - generic [ref=f15e404] [cursor=pointer]:
+                - generic [ref=f15e405]:
+                  - radio "杩� 7 澶�" [checked]
+                - generic [ref=f15e406]: 杩� 7 澶�
+              - generic [ref=f15e407] [cursor=pointer]:
+                - generic [ref=f15e408]:
+                  - radio "杩� 30 澶�"
+                - generic [ref=f15e409]: 杩� 30 澶�
+          - generic [ref=f15e414]: 娑堣垂鑰呮壂鐮佸湴鍖哄垎甯�
+        - generic [ref=f15e423]:
+          - generic [ref=f15e424]: 鎵规鐘舵�佸垎甯�
+          - generic [ref=f15e433]: 琚嬬爜鐘舵�佸垎甯�
+          - generic [ref=f15e443]:
+            - generic [ref=f15e444]: 寰呭姙涓庨璀�
+            - generic [ref=f15e448]:
+              - generic [ref=f15e453] [cursor=pointer]:
+                - generic [ref=f15e454]: 绐滆揣棰勮
+                - generic [ref=f15e455]: 寰呭鐞嗙殑绐滆揣棰勮
+              - generic [ref=f15e459] [cursor=pointer]:
+                - generic [ref=f15e460]: 搴撳瓨棰勮
+                - generic [ref=f15e461]: 鏈鐞嗙殑搴撳瓨棰勮
+              - generic [ref=f15e466] [cursor=pointer]:
+                - generic [ref=f15e467]: 涓嶅悎鏍煎搧
+                - generic [ref=f15e468]: 寰呰瘎瀹$殑涓嶅悎鏍煎搧鍗�
+        - generic [ref=f15e469]:
+          - generic [ref=f15e470]: 蹇嵎鍏ュ彛
+          - generic [ref=f15e474]:
+            - generic [ref=f15e476]:
+              - generic [ref=f15e477]: 鍩虹鏁版嵁
+              - generic [ref=f15e478]:
+                - generic [ref=f15e479] [cursor=pointer]: 鍝佺绠$悊
+                - generic [ref=f15e485] [cursor=pointer]: 鍝佺鍒嗙被
+                - generic [ref=f15e491] [cursor=pointer]: 璁¢噺鍗曚綅
+            - generic [ref=f15e496]:
+              - generic [ref=f15e497]: 涓�琚嬩竴鐮佺敓浜�
+              - generic [ref=f15e498]:
+                - generic [ref=f15e499] [cursor=pointer]: 浜х嚎缁存姢
+                - generic [ref=f15e503] [cursor=pointer]: 鎵规鐢熶骇
+                - generic [ref=f15e510] [cursor=pointer]: 璧嬬爜绠$悊
+                - generic [ref=f15e514] [cursor=pointer]: 鎵樼洏鐮�
+                - generic [ref=f15e520] [cursor=pointer]: 鎵爜鍏ュ簱
+            - generic [ref=f15e525]:
+              - generic [ref=f15e526]: 鍑哄簱娴侀��
+              - generic [ref=f15e527]:
+                - generic [ref=f15e528] [cursor=pointer]: 鍙戣揣閫氱煡
+                - generic [ref=f15e532] [cursor=pointer]: 閿�鍞嚭搴�
+                - generic [ref=f15e539] [cursor=pointer]: 閿�鍞��璐�
+            - generic [ref=f15e546]:
+              - generic [ref=f15e547]: 闃蹭吉闃茬獪璐�
+              - generic [ref=f15e548]:
+                - generic [ref=f15e549] [cursor=pointer]: 绐滆揣棰勮
+                - generic [ref=f15e553] [cursor=pointer]: 鎵爜鍦扮悊鍒嗗竷
+                - generic [ref=f15e559] [cursor=pointer]: 瀹屾暣鎬ф牎楠�
+            - generic [ref=f15e567]:
+              - generic [ref=f15e568]: 婧簮鍒嗘瀽
+              - generic [ref=f15e569]: 娑堣垂鑰呮壂鐮佺粺璁�
+            - generic [ref=f15e575]:
+              - generic [ref=f15e576]: 浠撳偍鐗╂祦
+              - generic [ref=f15e577]:
+                - generic [ref=f15e578] [cursor=pointer]: 浠撳簱璁剧疆
+                - generic [ref=f15e584] [cursor=pointer]: 搴撳瓨鐜版湁閲�
+                - generic [ref=f15e590] [cursor=pointer]: 搴撳瓨棰勮
+                - generic [ref=f15e593] [cursor=pointer]: 杞Щ璋冩嫧
+            - generic [ref=f15e598]:
+              - generic [ref=f15e599]: 璐ㄩ噺绠$悊
+              - generic [ref=f15e600]:
+                - generic [ref=f15e601] [cursor=pointer]: 鎵规杩芥函
+                - generic [ref=f15e607] [cursor=pointer]: 涓嶅悎鏍煎搧
+                - generic [ref=f15e611] [cursor=pointer]: 绉嶅瓙璐ㄦ
\ No newline at end of file
diff --git a/.playwright-mcp/page-2026-09-02T06-16-32-193Z.yml b/.playwright-mcp/page-2026-09-02T06-16-32-193Z.yml
new file mode 100644
index 0000000..6adf37e
--- /dev/null
+++ b/.playwright-mcp/page-2026-09-02T06-16-32-193Z.yml
@@ -0,0 +1 @@
+- generic [active]: 宸ヤ笟AI鏅洪�犲钩鍙�
\ No newline at end of file
diff --git a/docs/bagcode_home_dashboard_integration.md b/docs/bagcode_home_dashboard_integration.md
new file mode 100644
index 0000000..bb11421
--- /dev/null
+++ b/docs/bagcode_home_dashboard_integration.md
@@ -0,0 +1,83 @@
+# 涓�琚嬩竴鐮侀椤垫敼鐗�(鍏ㄩ摼璺┚椹惰埍) 鈥� 鍓嶇鑱旇皟鏂规
+
+## 鑳屾櫙
+
+灏� `/bagcode-home` 棣栭〉浠�"鍩虹鏁版嵁 + 涓�琚嬩竴鐮佺敓浜�"鐨勫眬閮ㄧ粺璁¢〉,閲嶆瀯涓鸿鐩�**瀹屾暣浠峰�奸摼**鐨勯┚椹惰埍:鍝佺 鈫� 浜х嚎/鎵规 鈫� 琚嬬爜/鎵樼洏 鈫� 閿�鍞嚭搴� 鈫� 娴侀�氭壂鐮� 鈫� 闃茬獪婧簮,骞朵覆鑱斾粨鍌ㄥ簱瀛樸�佽川閲忛璀︺�佸揩鎹峰叆鍙c�傛暣浣撹瑙夊榻愮櫥褰曢〉鐨�**鍐滀笟缈犵豢鐜荤拑**椋庢牸銆�
+
+## 娑夊強椤甸潰
+
+- 棣栭〉(椹鹃┒鑸�):`src/views/dashboard/bagCodeHome/index.vue` 鈥斺�� 鍏ㄩ摼璺噸鏋�
+- 鍏宠仈璺宠浆椤甸潰:鍩虹鏁版嵁銆佷竴琚嬩竴鐮佺敓浜с�佸嚭搴撴祦閫氥�侀槻浼槻绐滆揣銆佹函婧愬垎鏋愩�佷粨鍌ㄧ墿娴併�佽川閲忕鐞� 涓嬬殑鍚勪笟鍔¢〉
+
+## 鎺ュ彛鍙樻洿
+
+### 1. 鎵╁睍姹囨�绘帴鍙�(杩斿洖鏂板瀛楁)
+
+`GET /mes/bag-code-home-statistics/summary`
+
+鏂板鍝嶅簲瀛楁(鍧� `Long`,鏃犻粯璁ゅ�兼椂杩斿洖 0):
+
+| 瀛楁 | 璇存槑 | 鏁版嵁鏉ユ簮琛� |
+|------|------|-----------|
+| `salesNoticeCount` | 鍙戣揣閫氱煡鍗曟暟 | `mes_wm_sales_notice` |
+| `productSalesCount` | 閿�鍞嚭搴撳崟鏁� | `mes_wm_product_sales` |
+| `returnSalesCount` | 閿�鍞��璐у崟鏁� | `mes_wm_return_sales` |
+| `stockWarningPendingCount` | 鏈鐞嗗簱瀛橀璀︽暟(`status=10`) | `mes_wm_stock_warning` |
+| `ncrPendingCount` | 寰呰瘎瀹′笉鍚堟牸鍝佸崟鏁�(`status=0`) | `mes_qc_ncr` |
+
+鍘熸湁瀛楁(`mdmItemCount/mesItemCount/itemTypeCount/unitMeasureCount/lineCount/batchCount/bagCodeCount/palletCount/batchStatusList/bagCodeStatusList`)淇濇寔涓嶅彉銆�
+
+### 2. 澶嶇敤宸叉湁缁熻鎺ュ彛(鏃犻渶鏀瑰姩)
+
+| 鎺ュ彛 | 鐢ㄩ�� | 鍓嶇 API |
+|------|------|---------|
+| `GET /mes/trace-cross-region/summary` | 绐滆揣棰勮姹囨��(`total/pending/handled/falseAlarm/crossProvince/crossCity`) | `api/mes/trace-cross-region`(鏂板) |
+| `GET /mes/trace-consumer-scan/summary` | 绱鎵爜/鎴愬姛/澶辫触/娑夊強鎵规 | 宸叉湁 |
+| `GET /mes/trace-consumer-scan/distribution?groupBy=province` | 娑堣垂鑰呮壂鐮佸湴鍖哄垎甯� | 宸叉湁 |
+
+## 鍓嶇淇敼鐐�
+
+### 1. 棣栭〉缁撴瀯(index.vue)
+
+| 鍖哄潡 | 缁勪欢 | 鏁版嵁 |
+|------|------|------|
+| 涓氬姟鍏ㄩ摼璺� | `modules/business-flow.vue` | summary + 鎵爜鎬绘暟 + 绐滆揣鎬绘暟 |
+| 鍏ㄩ摼璺� KPI 鎬昏(8 鍗�) | `modules/kpi-cards.vue` | summary + 鎵爜鎬绘暟 |
+| 琚嬬爜鐢熸垚瓒嬪娍 | `modules/trend-chart.vue`(澶嶇敤) | `summary/trend` |
+| 娑堣垂鑰呮壂鐮佸湴鍖哄垎甯� | `modules/scan-region-chart.vue`(鏂板) | `distribution/groupBy=province` |
+| 鎵规鐘舵�佸垎甯� / 琚嬬爜鐘舵�佸垎甯� | `batch-status-chart.vue` / `bag-code-status-chart.vue`(澶嶇敤) | summary 鐘舵�佸垪琛� |
+| 寰呭姙涓庨璀� | `modules/alert-panel.vue`(鏂板) | summary + 绐滆揣姹囨�� |
+| 鍒嗙粍蹇嵎鍏ュ彛 | `modules/shortcuts.vue`(閲嶅啓涓哄垎缁�) | 闈欐�佽矾鐢遍厤缃� |
+
+### 2. 鏂板/鍒犻櫎鏂囦欢
+
+- 鏂板:`api/mes/trace-cross-region/index.ts`
+- 鏂板:棣栭〉 `modules/business-flow.vue`銆乣kpi-cards.vue`銆乣scan-region-chart.vue`銆乣alert-panel.vue`
+- 閲嶅啓:棣栭〉 `index.vue`銆乣modules/shortcuts.vue`
+- 鏇存柊:`api/mes/bag-code-home/index.ts`(Summary 鍔� 5 瀛楁)銆乣data.ts`銆乣chart-options.ts`
+- 鍒犻櫎:`modules/basic-cards.vue`銆乣modules/bag-code-cards.vue`(涓嶅啀浣跨敤)
+
+### 3. 鏍峰紡涓婚
+
+- 棣栭〉鍐呭鍖虹粺涓�"鍐滀笟缈犵豢鐜荤拑"涓婚:娴呰壊缈犵豢娓愬彉鑳屾櫙(鏃犺儗鏅浘)銆佺櫧鑹茬(鐮傜幓鐠冨崱鐗囥�佽杽鑽风櫧/澧ㄧ豢鏂囧瓧銆�
+- 鍥捐〃缁熶竴缈犵豢涓昏壊(`#10b981`)骞堕�傞厤娴呭簳鏂囧瓧鑹层��
+- 涓婚鏍峰紡闆嗕腑鍦� `index.vue` 鐨勯潪 scoped `<style>` 涓�(`.agri-dashboard` 瀹瑰櫒涓轰綔鐢ㄥ煙),鍚勫崱鐗囩粍浠朵娇鐢ㄥ搴旂幓鐠冪被銆�
+
+## 瀛楁灞曠ず瑙勫垯
+
+| 瀛楁 | 灞曠ず浣嶇疆 | 璇存槑 |
+|------|----------|------|
+| 鍝佺鎬绘暟 | KPI + 涓氬姟閾捐矾 | 鐐规寜璺宠浆 `MdmItem` |
+| 鐢熶骇鎵规 | KPI + 涓氬姟閾捐矾 | 璺宠浆 `MesProBatch` |
+| 琚嬬爜 / 鎵樼洏鐮� | KPI + 涓氬姟閾捐矾 | 璺宠浆 `MesProBagCode` / `MesProPallet` |
+| 閿�鍞嚭搴撳崟鏁� | KPI + 涓氬姟閾捐矾 | 璺宠浆 `MesWmProductSales` |
+| 绱鎵爜 | KPI + 涓氬姟閾捐矾 | 鏉ヨ嚜 `trace-consumer-scan/summary`,璺宠浆 `MesConsumerScanStatistics` |
+| 绐滆揣棰勮鎬绘暟 | 涓氬姟閾捐矾(闃茬獪/婧簮) | 鏉ヨ嚜 `trace-cross-region/summary`,璺宠浆 `MesTraceCrossRegion` |
+| 搴撳瓨棰勮(鏈鐞�) | KPI + 寰呭姙棰勮 | 璺宠浆 `MesWmStockWarning` |
+| 寰呰瘎瀹′笉鍚堟牸 | KPI + 寰呭姙棰勮 | 璺宠浆 `MesQcNcr` |
+
+## 娉ㄦ剰浜嬮」
+
+- 鏂板鐨� 5 涓眹鎬诲瓧娈甸渶瑕�**鍚庣閲嶆柊缂栬瘧骞堕噸鍚�**鍚庢墠浼氳繑鍥�;閲嶅惎鍓嶅墠绔凡鍋� `?? 0` 鍏滃簳,鐩稿叧鍗$墖/鑺傜偣鏄剧ず 0,涓嶅奖鍝嶅叾瀹冩暟鎹��
+- 璺敱璺宠浆浣跨敤**璺敱 name**(宸蹭笌鑿滃崟 `component_name` 瀵归綈),鏈姞杞界殑鑿滃崟娌跨敤 `message.warning('褰撳墠鑿滃崟灏氭湭鍔犺浇...')` 鍏滃簳銆�
+- 娑堣垂鑰呮壂鐮�/绐滆揣鏃犵粡绾害瀛楁,浠呮敮鎸佺渷/甯傛枃鏈垎甯冨浘,涓嶆敮鎸佸湴鍥惧潗鏍囥��
diff --git a/docs/batch_trace_refactor_integration.md b/docs/batch_trace_refactor_integration.md
new file mode 100644
index 0000000..85f11b5
--- /dev/null
+++ b/docs/batch_trace_refactor_integration.md
@@ -0,0 +1,102 @@
+# 鎵规杩芥函缁熶竴鑱氬悎鎺ュ彛 - 鍓嶇鑱旇皟鏂规
+
+## 娑夊強椤甸潰
+
+- MES 鈫� 璐ㄦ 鈫� 鎵规杩芥函锛坄/qc/batch-trace`锛�
+- 鎵规杩芥函璇︽儏
+- 鎵规鐮併�佹墭鐩樼爜銆佽鐮佹壂鐮佽拷婧叆鍙�
+
+## 涓氬姟娴佺▼涓庢暟鎹甫鍏�
+
+1. 鍦ㄦ壒娆¤拷婧垪琛ㄨ緭鍏ョ敓浜ф壒娆$爜銆佷复鏃舵壒娆$爜銆佽鐮佹垨鎵樼洏鐮併��
+2. 椤甸潰璋冪敤缁熶竴璇︽儏鎺ュ彛锛屽悗绔皢鍏ュ彛鐮佽В鏋愬埌鐢熶骇鎵规锛屽苟鑱氬悎鐢熶骇銆佽川妫�銆佷粨鍌ㄣ�侀攢鍞拰闃蹭吉淇℃伅銆�
+3. 璇︽儏椤典互鐢熶骇鎵规涓烘牴灞曠ず鍏ㄩ摼璺暟鎹紱琚嬬爜鍜屾墭鐩樹俊鎭潵鑷敓浜ц祴鐮佹暟鎹紝浠撳偍涓庨攢鍞俊鎭潵鑷粨鍌ㄤ笟鍔℃暟鎹��
+4. 鍓嶅悜杩芥函灞曠ず褰撳墠鎵规浜у嚭鐨勪笅娓告壒娆★紱鍚庡悜杩芥函灞曠ず褰撳墠鎵规娑堣�楃殑涓婃父鎵规銆�
+5. 鏃堕棿绾裤�侀樁娈佃鍥惧拰鍐崇瓥鎽樿鍧囦娇鐢ㄥ悓涓�娆¤仛鍚堝搷搴旓紝閬垮厤鎵撳紑璇︽儏鍚庨噸澶嶈姹傚涓棫鎺ュ彛銆�
+
+## API
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|---|---|---|
+| GET | `/mes/trace/batch/detail` | 鏌ヨ缁熶竴鎵规杩芥函璇︽儏 |
+
+### 璇锋眰鍙傛暟
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|---|---|---|---|
+| `code` | String | 鏄� | 鐢熶骇鎵规鐮併�佷复鏃舵壒娆$爜銆佽鐮佹垨鎵樼洏鐮� |
+
+绀轰緥锛歚code=BAG-202608280001`
+
+### 鍝嶅簲涓昏瀛楁
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|---|---|---|
+| `sourceType` | String | 鍏ュ彛绫诲瀷锛歚BAG`銆乣PALLET`銆乣BATCH` |
+| `requestCode` | String | 鍘熷杈撳叆鐮� |
+| `itemCode` / `itemName` | String | 鍝佺缂栫爜銆佸悕绉� |
+| `itemSpecification` | String | 鍝佺瑙勬牸 |
+| `batchCode` / `tempCode` | String | 姝e紡鎵规鍙枫�佷复鏃舵壒娆$爜 |
+| `batchStatusName` | String | 鎵规鐘舵�佸悕绉� |
+| `produceDate` | DateTime | 鐢熶骇鏃ユ湡 |
+| `lineCode` / `lineName` | String | 浜х嚎缂栫爜銆佸悕绉� |
+| `ownerUserName` | String | 鐢熶骇璐d换浜� |
+| `planQuantity` | Decimal | 璁″垝鏁伴噺 |
+| `bagQuantity` / `palletQuantity` | Integer | 琚嬬爜鏁伴噺銆佹墭鐩樻暟閲� |
+| `workers` | Array | 鐢熶骇浜哄憳 |
+| `pallets` | Array | 鎵樼洏鎽樿 |
+| `bagTotal` / `bags` | Long / Array | 琚嬬爜鎬绘暟鍙婃渶澶� 100 鏉℃槑缁� |
+| `inbound` | Object | 鏈�鏂板叆搴撲簨浠� |
+| `quality` | Object | 绉嶅瓙璐ㄩ噺妫�楠屼笌鎸囨爣 |
+| `antiFake` | Object | 褰撳墠杩芥函鐮佹壂鐮佹鏁般�侀娆″拰鏈�杩戞壂鐮佹椂闂� |
+| `purchaseSources` | Array | 閲囪喘鏉ユ簮銆佷緵搴斿晢鍜� IQC 淇℃伅 |
+| `salesTargets` | Array | 閿�鍞嚭搴撱�佸鎴峰拰 OQC 淇℃伅 |
+| `processTraces` | Array | 宸ュ簭鎶曞叆銆佷骇鍑烘祦杞俊鎭� |
+
+### 鍝嶅簲绀轰緥
+
+```json
+{
+  "code": 0,
+  "data": {
+    "sourceType": "BAG",
+    "requestCode": "BAG-202608280001",
+    "batchCode": "B20260828001",
+    "itemName": "绀轰緥鍝佺",
+    "batchStatusName": "宸插畬鎴�",
+    "workers": [],
+    "pallets": [],
+    "bags": [],
+    "purchaseSources": [],
+    "salesTargets": [],
+    "processTraces": []
+  }
+}
+```
+
+## 椤甸潰灞曠ず瑙勫垯
+
+| 瑙嗗浘 | 灞曠ず鍐呭 |
+|---|---|
+| 闃舵鎬昏 | 鏉ユ簮銆佺敓浜с�佽川妫�銆佽祴鐮併�佸叆搴撱�侀攢鍞�佹秷璐广�侀槻浼叓涓樁娈电殑鐘舵�佸拰鏁伴噺 |
+| 鍏ㄩ摼璺椂闂寸嚎 | 鎸夋椂闂村睍绀洪噰璐�佺敓浜с�佽川妫�銆佽祴鐮併�佸叆搴撱�佸嚭搴撳拰鎵爜浜嬩欢锛涘紓甯哥姸鎬佷紭鍏堥珮浜� |
+| 娴佸姩鍥捐氨 | 浠ユ壒娆°�佸伐搴忋�佷粨搴撱�佹墭鐩樸�佽鐮併�侀攢鍞崟鍜屽鎴蜂负鑺傜偣锛涢粯璁ゅ睍绀烘壒娆$骇鍏崇郴 |
+| 鍐崇瓥鍒嗘瀽 | 灞曠ず璐ㄩ噺缁撹銆佸簱瀛樻暟閲忋�侀攢鍞幓鍚戙�侀槻浼壂鐮佹鏁板拰绐滆揣椋庨櫓 |
+| 閲囪喘鏉ユ簮 | 灞曠ず閲囪喘鍏ュ簱銆佷緵搴斿晢銆侀噰璐鍗曘�両QC 鍗曞彿鍙婃楠岀粨璁� |
+| 閿�鍞幓鍚� | 灞曠ず閿�鍞嚭搴撱�佸鎴枫�侀攢鍞鍗曘�丱QC 鍗曞彿鍙婅川閲忕姸鎬� |
+| 宸ュ簭娴佽浆 | `IN` 鏄剧ず涓烘姇鍏ワ紝`OUT` 鏄剧ず涓轰骇鍑� |
+
+## 涓氬姟瑙勫垯
+
+- 杈撳叆琚嬬爜鎴栨墭鐩樼爜鏃讹紝璇︽儏浠嶄互鍏舵墍灞炵敓浜ф壒娆′负鏍硅繘琛岃仛鍚堛��
+- 褰撳墠鎺ュ彛杩斿洖鐨勮鐮佹槑缁嗘渶澶� 100 鏉★紝`bagTotal` 鐢ㄤ簬灞曠ず瀹屾暣鏁伴噺锛涘ぇ鎵规涓嶅緱鎸夋槑缁嗘暟閲忓垽鏂�婚噺銆�
+- 绌烘暟缁勮〃绀鸿涓氬姟闃舵鏆傛棤璁板綍锛沗quality`銆乣inbound`銆乣antiFake` 涓虹┖琛ㄧず鏆傛棤瀵瑰簲淇℃伅銆�
+- 闃蹭吉鎵爜缁熻浠呯敤浜庡睍绀猴紝涓嶈兘鏇夸唬璐ㄩ噺妫�楠岀粨璁烘垨鍑哄簱鐘舵�併��
+- 瀹㈡埛銆佷緵搴斿晢銆佹搷浣滀汉绛夊悕绉板簲浠ユ帴鍙h繑鍥炲�间负鍑嗭紝涓嶅湪鍓嶇閲嶅鏌ヨ鏃ф壒娆℃帴鍙c��
+
+## 娉ㄦ剰浜嬮」
+
+- 绠$悊绔帴鍙i渶瑕佹壒娆¤拷婧煡璇㈡潈闄愶細`mes:trace-batch:query`銆�
+- 鏃х殑 `/mes/wm/batch/*` 鎺ュ彛缁х画渚涗粨鍌ㄦ壒娆¢〉闈娇鐢紝鎵规杩芥函椤甸潰鍒囨崲鍚庝笉搴斿啀骞惰璇锋眰鏃х殑浜斾釜璇︽儏鎺ュ彛銆�
+- 鍚庣画鎵╁睍鏃堕棿绾垮拰鍥捐氨鏃讹紝寤鸿鍚庣澧炲姞鍒嗛〉涓庤妭鐐瑰眰绾у弬鏁帮紝閬垮厤涓�娆℃�у姞杞藉叏閮ㄨ鐮併��
+- 灞曠ず娑堣垂鑰呮壂鐮佷俊鎭椂涓嶅緱灞曠ず IP 鍦板潃鍜� User-Agent 鍘熸枃銆�
\ No newline at end of file
diff --git a/docs/mes_wm_material_stock_bag_api.md b/docs/mes_wm_material_stock_bag_api.md
new file mode 100644
index 0000000..bbda426
--- /dev/null
+++ b/docs/mes_wm_material_stock_bag_api.md
@@ -0,0 +1,69 @@
+# 搴撳瓨琚嬬爜鏄庣粏鑱旇皟鏂规
+
+## 涓氬姟娴佺▼
+
+绉嶅瓙璐ㄦ浠ヨ鐮佹彁浜ゃ�傝川妫�鍚堟牸鏃讹紝鏆傚瓨搴撳瓨姹囨�绘暟閲忓噺灏� 1锛屽悎鏍煎簱瀛樻眹鎬绘暟閲忓鍔� 1锛屽悓鏃惰琚嬬爜鏄庣粏杩佺Щ鍒板悎鏍间粨锛涜川妫�涓嶅悎鏍兼椂锛屾殏瀛樺簱瀛樻眹鎬绘暟閲忓噺灏� 1锛岃鐮佹槑缁嗘爣璁颁负鎶ュ簾銆傚叾浠栬鐮佷笉鍙楀奖鍝嶃��
+
+搴撳瓨鍙拌处缁х画鎸夌墿鏂欍�佹壒娆°�佷粨搴撱�佸簱鍖恒�佸簱浣嶅拰渚涘簲鍟嗘眹鎬伙紱琚嬬爜鏄庣粏鐢ㄤ簬璁板綍姣忎釜琚嬬爜鐨勫叿浣撶姸鎬佸拰浣嶇疆銆�
+
+## 琚嬬爜鏄庣粏鐢熷懡鍛ㄦ湡锛堢粺涓�鏈嶅姟鎵樼锛�
+
+鎵�鏈夊甫琚嬬爜鐨勫簱瀛樺彉鍔ㄧ粺涓�閫氳繃搴撳瓨浜嬪姟鏈嶅姟鍦ㄥ悓涓�浜嬪姟鍐呭悓姝ョ淮鎶� **姹囨�诲簱瀛� + 琚嬬爜鏄庣粏 + 搴撳瓨娴佹按** 涓夎�呬竴鑷达紝鍚勪笟鍔″叆鍙f棤闇�鍚勮嚜瀹炵幇琚嬬爜閫昏緫銆傜敓鍛藉懆鏈燂細
+
+| 闃舵 | 鍔ㄤ綔 | 琚嬬爜鏄庣粏鍙樺寲 |
+|------|------|--------------|
+| 鍗曡鎵爜鍏ュ簱 | 鐢熶骇鎵爜鍏ュ簱锛堟壂鐮佺被鍨� BAG锛� | 寤虹珛娲诲姩鏄庣粏锛堟殏瀛樺簱锛� |
+| 绉嶅瓙璐ㄦ鍚堟牸 | 鏆傚瓨鍑哄簱 + 鍚堟牸鍏ュ簱 | 鏄庣粏鐢辨殏瀛樺簱杩佺Щ鍒板悎鏍煎簱 |
+| 绉嶅瓙璐ㄦ涓嶅悎鏍� | 鏆傚瓨鍑哄簱 | 鏄庣粏鎶ュ簾 |
+| 閿�鍞嚭搴撳畬鎴� | 鍑哄簱鎵e噺 | 鏄庣粏缃负宸插嚭搴� |
+| 鏁翠綅绉诲簱 | 璋冩嫧绉诲嚭 + 绉诲叆 | 鏄庣粏浣嶇疆杩佺Щ鍒扮洰鏍囧簱浣� |
+
+璇存槑锛�
+- 鎵樼洏锛圥ALLET锛�/鎵规锛圔ATCH锛夋暣鎵瑰叆搴撱�侀噰璐�/鏉傞」/閫�璐х瓑鎸夎鎸夋暟閲忓叆搴撶殑鍑哄叆鍙o紝涓烘壒閲忔眹鎬伙紝涓嶉�愯寤虹珛鏄庣粏锛涜繖浜涜鐮佸湪棣栨绉嶅瓙璐ㄦ鏃堕�氳繃鎸夋壒娆″畾浣嶆殏瀛樺簱瀛樿ˉ寤烘槑缁嗐��
+- 浜嬪姟娴佹按 DTO 澧炲姞 `bagCodeId`/`bagCode` 瀛楁锛涘嚭搴�/绉诲叆鎸夎鐮佽嚜鍔ㄦ洿鏂拌鐮佹槑缁嗐��
+- 姣忕瑪甯﹁鐮佺殑鍏ュ簱銆佸嚭搴撱�佺Щ搴撱�佽川妫�閮戒細鐢熸垚瀵瑰簲鐨� `mes_wm_transaction` 娴佹按骞跺洖濉鐮侊紝渚夸簬鍦ㄥ簱瀛樻祦姘撮〉闈㈡寜琚嬬爜绛涢�夎拷婧��
+
+
+## API
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| GET | `/mes/wm/material-stock/page` | 搴撳瓨姹囨�诲垎椤碉紝鍝嶅簲澧炲姞琚嬬爜缁熻瀛楁 |
+| GET | `/mes/wm/material-stock/bag-details` | 鏌ヨ鎸囧畾搴撳瓨姹囨�昏褰曚笅鐨勫湪搴撹鐮佹槑缁� |
+| GET | `/mes/wm/transaction/page` | 搴撳瓨浜嬪姟娴佹按锛屾敮鎸� `bagCode` 绛涢�夊苟杩斿洖琚嬬爜 |
+
+### 琚嬬爜鏄庣粏鏌ヨ
+
+璇锋眰鍙傛暟锛�
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| materialStockId | Long | 鏄� | 搴撳瓨姹囨�昏褰� ID |
+| bagCode | String | 鍚� | 琚嬬爜妯$硦鏌ヨ |
+| pageNo | Integer | 鏄� | 椤电爜 |
+| pageSize | Integer | 鏄� | 姣忛〉鏉℃暟 |
+
+鍝嶅簲瀛楁鍖呮嫭 `bagCode`銆乣batchCode`銆乣stockState`銆乣status`銆乣quantity`銆乣warehouseId`銆乣locationId`銆乣areaId`銆乣inboundTime` 鍜� `outboundTime`銆�
+
+搴撳瓨姹囨�诲垎椤靛搷搴旀柊澧烇細
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| bagCount | Long | 褰撳墠鍦ㄥ簱琚嬬爜鏄庣粏鏁伴噺 |
+| bagDetailSupported | Boolean | 鏄惁瀛樺湪琚嬬爜鏄庣粏锛涘巻鍙叉棤娉曡繕鍘熻鐮佺殑搴撳瓨涓� false |
+
+## 鍓嶇璋冪敤鏃跺簭
+
+1. 璋冪敤搴撳瓨鍙拌处鍒嗛〉鎺ュ彛鑾峰彇姹囨�昏褰曘��
+2. 瀵� `bagDetailSupported=true` 鐨勮褰曟樉绀衡�滄煡鐪嬭鐮佲�濄��
+3. 鐐瑰嚮鍚庤皟鐢� `/mes/wm/material-stock/bag-details`锛屼紶鍏ュ綋鍓嶈 `id` 浣滀负 `materialStockId`銆�
+4. 琚嬬爜鏄庣粏涓殑 `bagCode` 鍙户缁皟鐢ㄦ壒娆�/琚嬬爜杩芥函鎺ュ彛鏌ョ湅璐ㄦ鍜岀敓浜т俊鎭��
+5. 搴撳瓨娴佹按椤甸潰浣跨敤 `bagCode` 绛涢�夋椂锛屽睍绀鸿川妫�杩佺Щ浜х敓鐨勮鐮佺骇娴佹按銆�
+
+## 涓氬姟瑙勫垯
+
+- 鍚屼竴琚嬬爜鍙兘瀛樺湪涓�鏉″簱瀛樿鐮佹槑缁嗐��
+- 璐ㄦ鎸夊疄闄呰鐮佸畾浣嶆殏瀛樺簱瀛橈紝涓嶆寜鎵规闅忔満鎵e噺銆�
+- 鎶ュ簾琚嬬爜涓嶈鍏ュ湪搴撴槑缁嗘煡璇紝浣嗕繚鐣欐槑缁嗚褰曠敤浜庤拷婧��
+- 鏃犳硶浠庡巻鍙叉眹鎬诲簱瀛樿繕鍘熻鐮佹椂锛屼笉浼�犺鐮佹槑缁嗐��
+- SQL 鑴氭湰涓嶅寘鍚� `tenant_id` 瀛楁銆�
diff --git a/docs/sales_notice_to_product_sales.md b/docs/sales_notice_to_product_sales.md
index ff668cf..1eca419 100644
--- a/docs/sales_notice_to_product_sales.md
+++ b/docs/sales_notice_to_product_sales.md
@@ -101,6 +101,20 @@
 
 **娉ㄦ剰锛�** 鎷h揣鏄庣粏闇�瑕佹墜鍔ㄦ坊鍔�
 
+## 鍙戣揣閫氱煡琛岀墿鏂欐牎楠�
+
+鏂板鎴栦慨鏀瑰彂璐ч�氱煡琛屾椂锛岀墿鏂欏繀椤诲叧鑱旀壒娆★紝涓旇鐗╂枡鎵规蹇呴』瀛樺湪姝e紡搴撳簱瀛樸�傛寮忓簱鎸囧簱瀛樼姸鎬佷负鍚堟牸搴擄紙`stockState = 20`锛夌殑搴撳瓨璁板綍銆�
+
+| 鏍¢獙椤� | 瑙勫垯 |
+|--------|------|
+| 鐗╂枡 | 蹇呴』瀛樺湪涓斿惎鐢� |
+| 鎵规 | `batchId` 鎴� `batchCode` 蹇呴』鑳藉尮閰嶆湁鏁堟壒娆★紝涓旀壒娆″睘浜庢墍閫夌墿鏂� |
+| 姝e紡搴撳瓨 | 浠呯粺璁″悎鏍煎簱搴撳瓨锛屼笉缁熻鏆傚瓨搴撳簱瀛� |
+| 鏁伴噺 | 鍚屼竴鐗╂枡銆佹壒娆$殑鍚堟牸搴撳簱瀛樺悎璁″繀椤讳笉灏戜簬鍙戣揣鏁伴噺 |
+| 搴撳瓨鍙樻洿 | 淇濆瓨鍙戣揣閫氱煡琛屾椂涓嶆墸鍑忋�佷笉棰勫崰搴撳瓨锛屽疄闄呭嚭搴撴椂鍐嶆鏍¢獙 |
+
+搴撳瓨涓嶈冻鏃讹紝鍓嶇搴旂洿鎺ュ睍绀哄悗绔繑鍥炵殑鈥滃簱瀛樻暟閲忎笉瓒斥�濅笟鍔℃彁绀猴紝骞朵繚鐣欑敤鎴峰~鍐欑殑琛ㄥ崟鍐呭銆�
+
 ## 瀛楁鏄犲皠琛�
 
 ### 涓昏〃瀛楁鏄犲皠
diff --git a/docs/scan_in_multi_type_inbound.md b/docs/scan_in_multi_type_inbound.md
new file mode 100644
index 0000000..283635c
--- /dev/null
+++ b/docs/scan_in_multi_type_inbound.md
@@ -0,0 +1,80 @@
+# 鐢熶骇绾挎壂鐮佸叆搴擄細鏀寔琚嬬爜/鎵樼洏鐮�/鎵规鐮� + 鍘绘帀璁惧缂栫爜 - 鍓嶇鑱旇皟鏂规
+
+## 娑夊強椤甸潰
+
+- 鐢熶骇绾挎壂鐮佸叆搴擄紙`src/views/mes/pro/scan-in/index.vue`锛�
+
+## 涓氬姟鍙樻洿
+
+1. **鍘绘帀璁惧缂栫爜**锛氭壂鐮佸叆搴撲笉鍐嶅綍鍏�/灞曠ず璁惧缂栫爜锛屾帴鍙� `deviceCode` 鏀逛负闈炲繀濉紝鍓嶇涓嶅啀浼犮��
+2. **浜х嚎銆佸簱鍖恒�佸簱浣嶆敼涓轰笅鎷夐�夋嫨**锛氭暟鎹簮涓虹幇鏈夌簿绠�鍒楄〃鎺ュ彛锛屼笉鍐嶆墜濉紪鍙枫��
+3. **绛夊緟鎵爜鍖哄鍔�"鎵爜绫诲瀷"閫夋嫨**锛氳鐮� / 鎵樼洏鐮� / 鎵规鐮佷笁閫変竴锛屾壂鐮佹闅忕被鍨嬫彁浜ゅ搴旂爜鍊笺��
+
+## API
+
+### 涓绘帴鍙o細杩芥函鐮佹壂鐮佸叆搴�
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| POST | /mes/pro/scan-in/inbound | 杩芥函鐮佹壂鐮佸叆搴擄紙琚嬬爜/鎵樼洏鐮�/鎵规鐮侊級 |
+
+**璇锋眰鍙傛暟锛堟湰娆″彉鏇达級锛�**
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 鍙樻洿 | 璇存槑 |
+|------|------|------|------|------|
+| scanType | String | 鍚� | 鏂板 | 鎵爜绫诲瀷锛歚BAG` 琚嬬爜 / `PALLET` 鎵樼洏鐮� / `BATCH` 鎵规鐮侊紝涓嶄紶榛樿 `BAG` |
+| deviceCode | String | 鍚� | 鏀逛负闈炲繀濉� | 璁惧缂栫爜锛屽墠绔笉鍐嶄紶 |
+| bagCode | String | 鏄� | 璇箟鎵╁睍 | 鎵爜鍐呭锛氳鐮�/鎵樼洏鐮�/鎵规鐮佺殑鐮佸�硷紙鍚复鏃剁爜锛� |
+| eventId | String | 鏄� | - | 浜嬩欢鍙凤紙寤鸿鐢� `{scanType}-{鏃堕棿鎴硙` 淇濊瘉鍞竴锛� |
+| quantity | Number | 鏄� | - | 琚嬬爜鍦烘櫙浼� 1锛涙墭鐩�/鎵规鍦烘櫙鍚庣蹇界暐璇ュ�硷紝鎸夊凡瑁呰鏁�/鎵规琚嬫暟璁$畻 |
+| lineCode | String | 鍚� | - | 浜х嚎缂栫爜锛堝墠绔笅鎷夐�夋嫨锛� |
+| locationId | Long | 鏄� | - | WIP 鏆傚瓨搴撳尯缂栧彿锛堝墠绔笅鎷夐�夋嫨锛� |
+| areaId | Long | 鏄� | - | WIP 鏆傚瓨搴撲綅缂栧彿锛堝墠绔笅鎷夐�夋嫨锛� |
+
+**鍝嶅簲鏂板瀛楁锛�**
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| scanType | String | 鏈鎵爜绫诲瀷锛圔AG/PALLET/BATCH锛� |
+
+**鍏ュ簱鏁伴噺鍙e緞锛�**
+
+| scanType | 鏁伴噺 | 璇存槑 |
+|----------|------|------|
+| BAG | 鍓嶇浼� 1 | 鍗曡鍏ュ簱 |
+| PALLET | 鎵樼洏宸茶琚嬫暟 `bag_count` | 鏁存墭涓�娆℃�у叆鏆傚瓨搴� |
+| BATCH | 鎵规琚嬫暟 `bag_quantity` | 鏁存壒涓�娆℃�у叆鏆傚瓨搴� |
+
+**鍏ュ簱鍘婚噸锛�** 琚嬬爜銆佹墭鐩樼爜銆佹壒娆$爜鍧囦互鍚勮嚜 `inbound_event_id` 鍒ゆ柇鏄惁宸插叆搴擄紝閲嶅鎵爜杩斿洖瀵瑰簲"宸插叆搴�"閿欒鐮侊紝涓嶄細閲嶅鍐欏叆搴撳瓨浜嬪姟銆�
+
+### 涓嬫媺鏁版嵁婧愶紙澶嶇敤鐜版湁鎺ュ彛锛屾棤鏂板锛�
+
+| 鐢ㄩ�� | 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|------|
+| 浜х嚎 | GET | /mes/pro/line/simple-list | 杩斿洖鍚敤浜х嚎 `{id, code, name}` |
+| 搴撳尯 | GET | /mes/wm/warehouse-location/simple-list | 杩斿洖搴撳尯 `{id, code, name}`锛涢�夊簱鍖哄悗鑱斿姩鍔犺浇鍏朵笅搴撲綅 |
+| 搴撲綅 | GET | /mes/wm/warehouse-area/simple-list?locationId= | 杩斿洖搴撲綅 `{id, code, name}` |
+
+## 鏁版嵁甯﹀叆璇存槑
+
+- 浜х嚎/搴撳尯/搴撲綅锛氫笅鎷夐�夋嫨锛岄〉闈� `onMounted` 涓�娆℃�у姞杞戒骇绾裤�佸簱鍖猴紱閫夋嫨搴撳尯鍚庢寜 `locationId` 鑱斿姩鍔犺浇搴撲綅銆�
+- 鎵爜鎻愪氦锛歚locationId`銆乣areaId`銆乣scanType`銆乣bagCode` 涓�骞跺甫鍏ュ叆搴撴帴鍙o紱浜х嚎 `lineCode` 鏈夊�兼墠浼犮��
+- 浜嬩欢鍒楄〃/鐪嬫澘锛氫互 `scanType` 鍖哄垎杩芥函鐮佺被鍨嬪睍绀猴紙鍘嗗彶鏃犵被鍨嬬殑鏁版嵁榛樿鏄剧ず"琚嬬爜"锛夈��
+
+## 瀛楁灞曠ず瑙勫垯
+
+| 瀛楁 | 灞曠ず浣嶇疆 | 璇存槑 |
+|------|----------|------|
+| 鎵爜绫诲瀷 | 绛夊緟鎵爜鍖轰笅鎷� | 涓夐�変竴锛氳鐮�/鎵樼洏鐮�/鎵规鐮� |
+| 鐢熶骇浜х嚎 | 鎵爜鍗¢厤缃尯 + 鐘舵�佸崱 | 涓嬫媺閫夋嫨锛屽彲娓呯┖ |
+| WIP 鏆傚瓨搴撳尯/搴撲綅 | 鎵爜鍗¢厤缃尯 + 鐘舵�佸崱 | 涓嬫媺閫夋嫨锛屽簱浣嶉殢搴撳尯鑱斿姩绂佺敤/鍚敤 |
+| 鏈�杩戝叆搴撹拷婧爜 | 缁熻鍗� | 灞曠ず鏈�鏂颁竴鏉℃壂鐮佸唴瀹� |
+| 杩芥函鐮�/绫诲瀷/浜х嚎 | 鏈�杩戞壂鐮佷簨浠惰〃 | 琛ㄦ牸鏂板"绫诲瀷"鍒楋紝鍘�"璁惧/浜х嚎"鍒楁敼涓�"浜х嚎" |
+| 鏈�杩戞壂鐮佺被鍨� | 鐘舵�佸崱 | 灞曠ず鏈�鏂颁簨浠舵壂鐮佺被鍨� |
+
+## 娉ㄦ剰浜嬮」
+
+1. 鍚庣闇�**鎵ц SQL 鑴氭湰** `sql/mysql/mes_pro_scan_event_scan_type.sql`锛坄mes_pro_scan_event` 澧炲姞 `scan_type` 鍒楋紝`mes_pro_pallet` 澧炲姞 `inbound_event_id`/`inbound_time` 鍒楋級锛屽啀**閲嶆柊缂栬瘧骞堕噸鍚�**銆�
+2. 閲嶅惎鍓嶆棫鎺ュ彛浼氬拷鐣� `scanType`锛堟寜琚嬬爜澶勭悊锛夛紝鎵樼洏/鎵规鐮佹壂鐮佷細杩斿洖"琚嬬爜涓嶅瓨鍦�"锛涘墠绔被鍨嬩笅鎷夌収甯稿彲閫夛紝鍔熻兘闄嶇骇涓轰粎琚嬬爜銆�
+3. 浜х嚎/搴撳尯/搴撲綅涓嬫媺鑻ユ帴鍙� 404锛堝悗绔湭閲嶅惎锛夛紝涓嬫媺涓虹┖锛岄渶鍏堜繚闅滃悗绔彲鐢ㄣ��
+4. 鎵樼洏銆佹壒娆℃暣鎵�/鏁存壒鍏ュ簱浼氬悓姝ユ妸鍏朵笅鏈叆搴撹鐮佹爣璁颁负宸插叆搴擄紝閬垮厤鍚庣画鍗曡鎵爜閲嶅鍏ュ簱銆�
diff --git a/docs/seed_quality_bag_granularity.md b/docs/seed_quality_bag_granularity.md
new file mode 100644
index 0000000..c6c0bc3
--- /dev/null
+++ b/docs/seed_quality_bag_granularity.md
@@ -0,0 +1,88 @@
+# 绉嶅瓙璐ㄦ鎸夎鐮佺矑搴︽祦杞簱瀛� - 鍓嶇鑱旇皟鏂规
+
+## 鑳屾櫙
+
+绉嶅瓙璐ㄦ鍘熼�昏緫锛氳川妫�鍚堟牸鍚�**鏁存壒**灏嗕粨搴撴壒娆″簱瀛樹粠鏆傚瓨搴�(10)杞负鍚堟牸搴�(20)锛屼竴琚嬪悎鏍煎嵆鏁存壒鏀捐锛岄绮掑害杩囩矖銆�
+
+鏈鏀归�犱负**鎸夎鐮佺矑搴�**锛氳川妫�鍙拡瀵硅鎵殑閭d竴琚嬶紝鍚堟牸浠呭皢璇ヨ瀵瑰簲搴撳瓨浠庢殏瀛樺簱杞叆鍚堟牸搴擄紝鍚屾壒娆″叾浠栬淇濇寔鏆傚瓨搴撳緟妫�锛涘悓鏃剁粰姣忚鎵撲笂璐ㄦ鏍囪锛坄qc_status`锛夛紝閿�鍞嚭搴撴壂琚嬬爜/鎵樼洏鏃舵牎楠屽凡璐ㄦ鍚堟牸鎵嶅厑璁稿嚭搴撱��
+
+## 娑夊強椤甸潰
+
+- 璐ㄩ噺绠$悊-绉嶅瓙璐ㄦ锛堟彁浜よ川妫�琛ㄥ崟锛�
+- 閿�鍞嚭搴�-鎵爜鎷h揣锛堟壂琚嬬爜/鎵樼洏锛�
+- 搴撳瓨鍙拌处锛坄mes_wm_material_stock` 鍒嗛〉锛氬悓鎵规鍙兘鍚屾椂鍑虹幇鏆傚瓨搴撲笌鍚堟牸搴撲袱鏉¤褰曪紝鐘舵�佸垪宸叉寜瀛楀吀灞曠ず鏂囧瓧锛�
+
+## API
+
+鏈**鏃犳柊澧�/淇敼鎺ュ彛**锛屽潎涓烘棦鏈夋帴鍙g殑鏍¢獙璇箟涓庢暟鎹祦杞彉鍖栥��
+
+### 1. 鎻愪氦绉嶅瓙璐ㄦ
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| POST | `/mes/qc/seed-quality/submit` | 鎻愪氦绉嶅瓙璐ㄦ锛堟寜琚嬬爜锛� |
+
+**璇锋眰鍙傛暟锛堜笉鍙橈級锛�**
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| code | String | 鏄� | 琚嬬爜锛堟壂鐮佸唴瀹癸級锛�**蹇呴』涓烘湁鏁堣鐮�** |
+| batchCode | String | 鍚� | 鐢熶骇鎵规鍙凤紙鍏煎棰勭暀锛� |
+| indicators | Array | 鏄� | 鍥涢」鎸囨爣锛坧urity/cleanliness/germination_rate/moisture锛� |
+| reason | String | 鍚� | 澶囨敞 |
+
+**鍝嶅簲锛堜笉鍙橈級锛�** `{ "code": 0, "data": 璐ㄦ璁板綍ID }`
+
+### 2. 鎵爜鎷h揣
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| POST | `/mes/wm/product-sales/scan` | 鎵爜鎷h揣锛堣鐮�/鎵樼洏锛� |
+
+**璇锋眰鍙傛暟锛堜笉鍙橈級锛�**
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| salesId | Long | 鏄� | 閿�鍞嚭搴撳崟ID |
+| scanContent | String | 鏄� | 琚嬬爜鎴栨墭鐩樼爜 |
+| quantity | BigDecimal | 鍚� | 鍑哄簱鏁伴噺锛堢己鐪佺敤琛屾暟閲忥級 |
+
+**鍝嶅簲锛堜笉鍙橈級锛�** 娌跨敤 `MesWmProductSalesScanRespVO`锛宍availableQuantity`/`outboundQuantity` 鍧囧熀浜�**鍚堟牸搴�**搴撳瓨璁$畻銆�
+
+## 涓氬姟瑙勫垯鍙樺寲
+
+| 鍦烘櫙 | 鍘熻涓� | 鏂拌涓� |
+|------|--------|--------|
+| 绉嶅瓙璐ㄦ鎻愪氦闈炶鐮� | 鍏煎鎵规鐮佹彁浜� | 鎶ラ敊銆屾湭瑙f瀽鍒版湁鏁堣鐮侊紝绉嶅瓙璐ㄦ蹇呴』浠ヨ鐮佹彁浜ゃ�� |
+| 璐ㄦ鍚堟牸 | 鏁存壒鏆傚瓨搴撯啋鍚堟牸搴� | 浠呰琚嬶細鏆傚瓨搴� -1銆佸悎鏍煎簱 +1锛堝悓鎵规鍏朵粬琚嬩粛涓烘殏瀛樺簱锛夛紱琚嬬爜 qc_status=20 |
+| 璐ㄦ涓嶅悎鏍� | 鏃犲簱瀛樺彉鍖� | 璇ヨ浠庢殏瀛樺簱瀛樻竻闄わ紙鎶ュ簾锛夛紱琚嬬爜 qc_status=30 |
+| 閿�鍞嚭搴撴壂琚嬬爜 | 涓嶆牎楠岃川妫� | qc_status 蹇呴』涓� 20锛屽惁鍒欐姤銆岃琚嬬爜鏈川妫�鍚堟牸锛岀姝㈠嚭搴撱�� |
+| 閿�鍞嚭搴撴壂鎵樼洏 | 涓嶆牎楠岃川妫� | 鎵樼洏涓嬫墍鏈夎鐮� qc_status 蹇呴』涓� 20锛屽惁鍒欐姤銆屾墭鐩樹笅瀛樺湪鏈川妫�琚嬬爜锛岀姝㈡暣鎵樺嚭搴撱�� |
+| 閿�鍞嚭搴撻�夊簱瀛� | 鍙兘閫変腑鏆傚瓨搴撹褰� | 浠呬粠鍚堟牸搴�(20)璁板綍涓�夋暟閲忚冻澶熺殑 |
+
+## 搴撳瓨璇箟璇存槑
+
+鍚屼竴娆★紙鍏ュ簱浠嶆寜鎵规鑱氬悎锛夎川妫�鍚堟牸鍚庯紝鍚屾壒娆� `mes_wm_material_stock` 浼氬悓鏃跺瓨鍦ㄤ袱鏉¤褰曪細
+
+| 璁板綍 | stock_state | 鍚箟 | 鏄惁鍙嚭搴� |
+|------|-------------|------|-----------|
+| 鏆傚瓨搴� | 10 | 璇ユ壒娆℃湭璐ㄦ鐨勫墿浣欒鏁� | 鍚� |
+| 鍚堟牸搴� | 20 | 宸茶川妫�鍚堟牸銆佸彲閿�鍞殑琚嬫暟 | 鏄� |
+
+绀轰緥锛氭壒娆″叆搴� 3 琚嬶紙quantity=3銆佹殏瀛樺簱锛夈�傝川妫�鍚堟牸 1 琚嬪悗 鈫� 鏆傚瓨搴� quantity=2 + 鍚堟牸搴� quantity=1銆�
+
+## 瀛楁灞曠ず瑙勫垯
+
+| 瀛楁 | 灞曠ず浣嶇疆 | 璇存槑 |
+|------|----------|------|
+| 璐ㄦ鐘舵�侊紙qc_status锛� | 绉嶅瓙璐ㄦ鎻愪氦缁撴灉鍙嶉 | 鍓嶇鏃犻渶灞曠ず锛屽悗绔湪绗簩娆℃彁浜ゅ悓涓�琚嬬爜鏃舵姤銆岃琚嬬爜宸茶川妫�锛岀姝㈤噸澶嶆彁浜ゃ�� |
+| 搴撳瓨鐘舵�� | 搴撳瓨鍙拌处鍒楄〃 | 鍚屼竴鐗╂枡/鎵规鍙兘鍑虹幇涓ゆ潯涓嶅悓鐘舵�佽褰曪紝鍧囦负姝e父鐜拌薄 |
+| 鍙嚭搴撴暟閲� | 鎵爜鎷h揣缁撴灉 | 浠� `availableQuantity` 涓哄噯锛屽彧鍙嶆槧鍚堟牸搴撳簱瀛� |
+
+## 娉ㄦ剰浜嬮」
+
+1. **绉嶅瓙璐ㄦ鎻愪氦蹇呴』浼犺鐮�**锛歚code` 瀛楁鐜板湪蹇呴』鏄郴缁熶腑瀛樺湪鐨勬湁鏁堣鐮侊紱浠呬紶鎵规鍙蜂笉鍐嶈鎺ュ彈銆�
+2. **璐ㄦ鎴愬姛鍚庡簱瀛樻�绘暟涓嶅彉**锛氭殏瀛樺簱 -1 涓庡悎鏍煎簱 +1 鍚屾鍙戠敓锛岀墿鏂欏湪搴撴�绘暟瀹堟亽锛屽彧鏄粠"寰呮"鍙樹负"鍙敭"銆�
+3. **鍚屾壒娆″弻鐘舵�佸苟瀛�**锛氭殏瀛樺簱涓庡悎鏍煎簱璁板綍鍚屾椂瀛樺湪鏄鏈熻涓猴紝鍓嶇鍦ㄥ簱瀛樺彴璐︺�佸嚭搴撻�夊簱瀛樻椂搴旇兘鎺ュ彈鍚屼竴鐗╂枡/鎵规鍑虹幇澶氳銆�
+4. **鍑哄簱鏁伴噺鎻愮ず**锛氳嫢鏌愭壒娆′粎閮ㄥ垎琚嬭川妫�鍚堟牸锛屾壂鐮佹嫞璐у彲鍑哄簱鏁伴噺鍙寘鍚悎鏍艰锛屽墠绔彁绀哄簲浠ユ帴鍙h繑鍥炵殑 `availableQuantity` 涓哄噯銆�
+5. **涓嶅悎鏍艰鎶ュ簾**锛氳川妫�涓嶅悎鏍肩殑琚嬩細浠庢殏瀛樺簱瀛樹腑鎵i櫎锛屼笖璇ヨ鐮� qc_status=30锛岀姝㈠嚭搴撱��
diff --git a/docs/sql/trace_platform_01_schema.sql b/docs/sql/trace_platform_01_schema.sql
new file mode 100644
index 0000000..b4bd50f
--- /dev/null
+++ b/docs/sql/trace_platform_01_schema.sql
@@ -0,0 +1,79 @@
+-- 鎵规杩芥函鏁版嵁妯″瀷琛ュ己
+-- 璇存槑锛氬瓧娈靛厛鍏佽涓虹┖锛屽巻鍙叉暟鎹洖濉畬鎴愬悗鍐嶈瘎浼版槸鍚︽敹绱х害鏉熴��
+-- 娉ㄦ剰锛氶」鐩笉浣跨敤 tenant_id锛涙墽琛屽墠璇风‘璁ゅ瓧娈靛拰绱㈠紩涓嶅瓨鍦ㄣ��
+
+ALTER TABLE mes_wm_batch
+    ADD COLUMN pro_batch_id BIGINT NULL COMMENT '瀵瑰簲鐢熶骇鎵规ID' AFTER id;
+ALTER TABLE mes_wm_batch
+    ADD INDEX idx_mes_wm_batch_pro_batch_id (pro_batch_id);
+
+ALTER TABLE mes_wm_sn
+    ADD COLUMN pro_batch_id BIGINT NULL COMMENT '鐢熶骇鎵规ID' AFTER id;
+ALTER TABLE mes_wm_sn
+    ADD COLUMN bag_code_id BIGINT NULL COMMENT '琚嬬爜ID' AFTER pro_batch_id;
+ALTER TABLE mes_wm_sn
+    ADD COLUMN pallet_id BIGINT NULL COMMENT '鎵樼洏ID' AFTER bag_code_id;
+ALTER TABLE mes_wm_sn
+    ADD COLUMN sales_id BIGINT NULL COMMENT '閿�鍞嚭搴撳崟ID' AFTER pallet_id;
+ALTER TABLE mes_wm_sn
+    ADD INDEX idx_mes_wm_sn_pro_batch_id (pro_batch_id);
+ALTER TABLE mes_wm_sn
+    ADD INDEX idx_mes_wm_sn_bag_code_id (bag_code_id);
+
+ALTER TABLE mes_qc_seed_quality
+    ADD COLUMN batch_id BIGINT NULL COMMENT '鐢熶骇鎵规ID' AFTER item_id;
+ALTER TABLE mes_qc_seed_quality
+    ADD INDEX idx_mes_qc_seed_quality_batch_id (batch_id);
+
+CREATE TABLE mes_trace_event (
+    id BIGINT NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+    event_no VARCHAR(64) NOT NULL COMMENT '浜嬩欢缂栧彿',
+    event_type VARCHAR(32) NOT NULL COMMENT '浜嬩欢绫诲瀷',
+    event_category VARCHAR(32) NULL COMMENT '浜嬩欢鍒嗙被',
+    event_time DATETIME NOT NULL COMMENT '浜嬩欢鏃堕棿',
+    pro_batch_id BIGINT NULL COMMENT '鐢熶骇鎵规ID',
+    wm_batch_id BIGINT NULL COMMENT '浠撳偍鎵规ID',
+    bag_code_id BIGINT NULL COMMENT '琚嬬爜ID',
+    pallet_id BIGINT NULL COMMENT '鎵樼洏ID',
+    sn_id BIGINT NULL COMMENT 'SN ID',
+    item_id BIGINT NULL COMMENT '鐗╂枡ID',
+    biz_type VARCHAR(32) NULL COMMENT '涓氬姟绫诲瀷',
+    biz_id BIGINT NULL COMMENT '涓氬姟ID',
+    biz_code VARCHAR(100) NULL COMMENT '涓氬姟缂栫爜',
+    biz_line_id BIGINT NULL COMMENT '涓氬姟鏄庣粏ID',
+    from_node_type VARCHAR(32) NULL COMMENT '鏉ユ簮鑺傜偣绫诲瀷',
+    from_node_id BIGINT NULL COMMENT '鏉ユ簮鑺傜偣ID',
+    from_node_code VARCHAR(100) NULL COMMENT '鏉ユ簮鑺傜偣缂栫爜',
+    to_node_type VARCHAR(32) NULL COMMENT '鐩爣鑺傜偣绫诲瀷',
+    to_node_id BIGINT NULL COMMENT '鐩爣鑺傜偣ID',
+    to_node_code VARCHAR(100) NULL COMMENT '鐩爣鑺傜偣缂栫爜',
+    quantity DECIMAL(18, 6) NULL COMMENT '鏁伴噺',
+    warehouse_id BIGINT NULL COMMENT '浠撳簱ID',
+    location_id BIGINT NULL COMMENT '搴撳尯ID',
+    area_id BIGINT NULL COMMENT '搴撲綅ID',
+    line_id BIGINT NULL COMMENT '浜х嚎ID',
+    work_order_id BIGINT NULL COMMENT '宸ュ崟ID',
+    process_id BIGINT NULL COMMENT '宸ュ簭ID',
+    sales_id BIGINT NULL COMMENT '閿�鍞嚭搴撳崟ID',
+    quality_id BIGINT NULL COMMENT '璐ㄦ璁板綍ID',
+    operator_id BIGINT NULL COMMENT '鎿嶄綔浜篒D',
+    payload_json JSON NULL COMMENT '浜嬩欢鎵╁睍鏁版嵁',
+    content_hash VARCHAR(64) NULL COMMENT '浜嬩欢鍐呭SHA-256鎽樿',
+    prev_hash VARCHAR(64) NULL COMMENT '鍓嶄竴浜嬩欢鎽樿',
+    creator VARCHAR(64) NULL DEFAULT '' COMMENT '鍒涘缓鑰�',
+    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+    updater VARCHAR(64) NULL DEFAULT '' COMMENT '鏇存柊鑰�',
+    update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '鏇存柊鏃堕棿',
+    deleted BIT(1) NOT NULL DEFAULT b'0' COMMENT '鏄惁鍒犻櫎',
+    PRIMARY KEY (id),
+    UNIQUE KEY uk_mes_trace_event_no (event_no),
+    KEY idx_mes_trace_event_batch_time (pro_batch_id, event_time),
+    KEY idx_mes_trace_event_biz (biz_type, biz_id, biz_line_id),
+    KEY idx_mes_trace_event_nodes (from_node_id, to_node_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES缁熶竴杩芥函浜嬩欢';
+
+-- 鍘嗗彶鏁版嵁鍥炲~绀轰緥锛岀‘璁ゆ暟鎹川閲忓悗鎵ц锛�
+-- UPDATE mes_wm_batch w JOIN mes_pro_batch p ON p.code = w.code AND p.deleted = b'0'
+-- SET w.pro_batch_id = p.id WHERE w.pro_batch_id IS NULL AND w.deleted = b'0';
+-- UPDATE mes_qc_seed_quality q JOIN mes_pro_batch p ON p.code = q.batch_code AND p.deleted = b'0'
+-- SET q.batch_id = p.id WHERE q.batch_id IS NULL AND q.deleted = b'0';
diff --git a/docs/wm_transaction_query.md b/docs/wm_transaction_query.md
index 33b616f..c48c2b9 100644
--- a/docs/wm_transaction_query.md
+++ b/docs/wm_transaction_query.md
@@ -9,6 +9,7 @@
 | 鏂规硶 | 璺緞 | 璇存槑 |
 |------|------|------|
 | GET | /mes/wm/transaction/page | 鍒嗛〉鏌ヨ搴撳瓨浜嬪姟娴佹按 |
+| GET | /mes/pro/batch/get-by-code | 鐐瑰嚮鎵规鍙锋煡璇㈢敓浜ф壒娆°�佹墭鐩樺強琚嬬爜淇℃伅 |
 
 ### 璇锋眰鍙傛暟
 
@@ -23,10 +24,37 @@
 | bizType | Integer | 鍚� | 涓氬姟绫诲瀷 |
 | bizCode | String | 鍚� | 涓氬姟鍗曞彿锛屾ā绯婂尮閰� |
 | materialStockId | Long | 鍚� | 搴撳瓨璁板綍ID |
+| bagCode | String | 鍚� | 琚嬬爜锛屾ā绯婂尮閰嶏紙璐ㄦ銆侀攢鍞嚭搴撶瓑琚嬬爜绾ф祦姘达級 |
 | startTime | String | 鍚� | 寮�濮嬫椂闂达紙鏍煎紡锛歽yyy-MM-dd HH:mm:ss锛� |
 | endTime | String | 鍚� | 缁撴潫鏃堕棿锛堟牸寮忥細yyyy-MM-dd HH:mm:ss锛� |
 | pageNo | Integer | 鏄� | 椤电爜锛岄粯璁�1 |
 | pageSize | Integer | 鏄� | 姣忛〉鏉℃暟锛岄粯璁�10 |
+
+### 鎵规杩芥函鏌ヨ
+
+鐐瑰嚮娴佹按鍒楄〃涓殑鎵规鍙锋椂锛屼娇鐢ㄥ綋鍓嶈鐨� `batchCode` 璋冪敤浠ヤ笅鎺ュ彛锛�
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| code | String | 鏄� | 鎵规鍙锋垨鎵规浜岀淮鐮佸唴瀹� |
+
+璇锋眰绀轰緥锛歚GET /mes/pro/batch/get-by-code?code={batchCode}`
+
+鎺ュ彛杩斿洖鐨勯噸鐐瑰瓧娈碉細
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| id | Long | 鐢熶骇鎵规ID |
+| code | String | 姝e紡鎵规鍙� |
+| tempCode | String | 涓存椂鎵规鐮� |
+| itemCode | String | 鐗╂枡缂栫爜 |
+| itemName | String | 鐗╂枡鍚嶇О |
+| workers | Array | 鎵规浜哄憳閰嶇疆 |
+| pallets | Array | 鎵规涓嬬殑鎵樼洏鍒楄〃 |
+| bagTotal | Integer | 琚嬬爜鎬绘暟 |
+| bags | Array | 鎵规涓嬬殑琚嬬爜鍒楄〃 |
+
+璇ユ帴鍙e悓鏃舵敮鎸佹寮忔壒娆″彿鍜屼复鏃舵壒娆$爜銆傝嫢鎵规鍙蜂负绌猴紝涓嶆樉绀虹偣鍑诲叆鍙o紱鑻ユ煡璇笉鍒扮敓浜ф壒娆★紝鎸夋帴鍙h繑鍥炵殑涓氬姟閿欒鎻愮ず鐢ㄦ埛銆�
 
 ### 鍝嶅簲瀛楁
 
@@ -44,6 +72,8 @@
 | quantity | BigDecimal | 鍙樺姩鏁伴噺锛堟鏁�=鍏ュ簱锛岃礋鏁�=鍑哄簱锛� |
 | batchId | Long | 鎵规ID |
 | batchCode | String | 鎵规鍙� |
+| bagCodeId | Long | 琚嬬爜ID锛堣鐮佺骇娴佹按闈炵┖锛� |
+| bagCode | String | 琚嬬爜锛堣鐮佺骇娴佹按闈炵┖锛屽璐ㄦ銆侀攢鍞嚭搴撱�佺Щ搴擄級 |
 | warehouseId | Long | 浠撳簱ID |
 | warehouseName | String | 浠撳簱鍚嶇О |
 | locationId | Long | 搴撳尯ID |
@@ -111,6 +141,24 @@
 | 121 | 澶栧崗鍙戞枡 | 澶栧崗鍙戞枡 |
 | 122 | 澶栧崗鍏ュ簱 | 澶栧崗鍏ュ簱 |
 
+## 璋冪敤鏃跺簭
+
+1. 杩涘叆搴撳瓨浜嬪姟娴佹按椤甸潰鏃讹紝璋冪敤 `/mes/wm/transaction/page` 鑾峰彇娴佹按鍒楄〃銆�
+2. 鍒楄〃涓睍绀� `batchCode`锛涙壒娆″彿闈炵┖鏃跺厑璁哥偣鍑汇��
+3. 鐐瑰嚮鎵规鍙峰悗锛屽皢褰撳墠琛岀殑 `batchCode` 浣滀负 `code` 璋冪敤 `/mes/pro/batch/get-by-code`銆�
+4. 鏍规嵁杩斿洖缁撴灉灞曠ず鐢熶骇鎵规淇℃伅銆佹墭鐩樺垪琛ㄥ拰琚嬬爜鍒楄〃锛沗bagTotal` 鐢ㄤ簬鏄剧ず琚嬬爜鎬绘暟锛宍bags` 鐢ㄤ簬灞曠ず琚嬬爜鏄庣粏銆�
+
+## 瀛楁灞曠ず瑙勫垯
+
+| 瀛楁 | 灞曠ず浣嶇疆 | 璇存槑 |
+|------|----------|------|
+| batchCode | 搴撳瓨娴佹按鍒楄〃 | 鎵规鍙烽潪绌烘椂浣滀负鍙偣鍑诲叆鍙� |
+| code | 鎵规璇︽儏 | 姝e紡鎵规鍙� |
+| tempCode | 鎵规璇︽儏 | 涓存椂鎵规鐮侊紝鏈夊�兼椂灞曠ず |
+| bagTotal | 鎵规璇︽儏 | 灞曠ず璇ユ壒娆¤鐮佹�绘暟 |
+| bags[].code | 鎵规璇︽儏 | 灞曠ず琚嬬爜鏄庣粏 |
+| pallets | 鎵规璇︽儏 | 灞曠ず璇ユ壒娆″叧鑱旂殑鎵樼洏淇℃伅 |
+
 ## 鎼滅储鏉′欢
 
 | 鏉′欢瀛楁 | 缁勪欢绫诲瀷 | 璇存槑 |
@@ -140,6 +188,7 @@
 | 搴撳尯 | locationName | 100 | - |
 | 搴撲綅 | areaName | 100 | - |
 | 浜嬪姟鏃堕棿 | transactionTime | 160 | 鏍煎紡锛歽yyy-MM-dd HH:mm:ss |
+| 琚嬬爜 | bagCode | 150 | 琚嬬爜绾ф祦姘寸殑琚嬬爜锛涙壒娆�/琚嬬爜鍙户缁拷婧� |
 
 ## 涓氬姟瑙勫垯璇存槑
 
@@ -150,6 +199,7 @@
 | 鏁伴噺鏄剧ず | quantity > 0 鏄剧ず缁胯壊锛宷uantity < 0 鏄剧ず绾㈣壊 |
 | 绫诲瀷鏍囩 | 鍏ュ簱鏄剧ず缁胯壊 success 鏍囩锛屽嚭搴撴樉绀虹孩鑹� danger 鏍囩 |
 | 涓氬姟鍗曞彿璺宠浆 | 鐐瑰嚮 bizCode 鍙烦杞埌瀵瑰簲涓氬姟鍗曟嵁璇︽儏椤� |
+| 琚嬬爜绛涢�� | 浼� bagCode 鍙煡鐪嬫寚瀹氳鐮佺殑瀹屾暣娴佽浆锛堣川妫�鏆傚瓨鍑哄簱/鍚堟牸鍏ュ簱/閿�鍞嚭搴撶瓑锛� |
 
 ## 鏉冮檺閰嶇疆
 
diff --git a/sql/mysql/mes_pro_bag_code_qc_status.sql b/sql/mysql/mes_pro_bag_code_qc_status.sql
new file mode 100644
index 0000000..54e3fe4
--- /dev/null
+++ b/sql/mysql/mes_pro_bag_code_qc_status.sql
@@ -0,0 +1,13 @@
+-- =========================================
+-- 琚嬬爜璐ㄦ鐘舵�侊細鏀寔绉嶅瓙璐ㄦ鎸夎鐮佺矑搴︽祦杞簱瀛�
+-- qc_status锛�10 鏈川妫� / 20 璐ㄦ鍚堟牸 / 30 璐ㄦ涓嶅悎鏍�
+-- 鏂板鍒楃敤浜庤褰曟瘡琚嬬殑璐ㄦ缁撴灉锛岄攢鍞嚭搴撴壂琚嬬爜鏃舵牎楠岃琚嬪凡璐ㄦ鍚堟牸銆�
+-- =========================================
+
+ALTER TABLE `mes_pro_bag_code`
+    ADD COLUMN `qc_status` TINYINT NOT NULL DEFAULT 10 COMMENT '璐ㄦ鐘舵�侊紙10鏈川妫� 20璐ㄦ鍚堟牸 30璐ㄦ涓嶅悎鏍硷級' AFTER `status`;
+
+-- 鍥炲~瀛橀噺鏁版嵁锛氭寜宸叉湁绉嶅瓙璐ㄦ璁板綍鍚屾琚嬬爜璐ㄦ鐘舵��
+UPDATE `mes_pro_bag_code` b
+    JOIN `mes_qc_seed_quality` q ON b.code = q.code AND b.deleted = 0
+SET b.qc_status = IF(q.result = 1, 20, 30), b.update_time = NOW();
diff --git a/sql/mysql/mes_pro_scan_event_scan_type.sql b/sql/mysql/mes_pro_scan_event_scan_type.sql
new file mode 100644
index 0000000..cf2b728
--- /dev/null
+++ b/sql/mysql/mes_pro_scan_event_scan_type.sql
@@ -0,0 +1,14 @@
+-- 鐢熶骇绾挎壂鐮佸叆搴擄細鏀寔琚嬬爜/鎵樼洏鐮�/鎵规鐮佷笁绉嶆壂鐮佺被鍨嬶紝骞跺幓鎺夎澶囩紪鐮佸繀濉�
+-- 璇存槑锛�
+-- 1. mes_pro_scan_event 澧炲姞 scan_type 鍒楋紝鏍囪鎵爜绫诲瀷锛圔AG 琚嬬爜 / PALLET 鎵樼洏鐮� / BATCH 鎵规鐮侊級锛屽巻鍙茶鐮佹暟鎹洖濉� BAG
+-- 2. mes_pro_pallet 澧炲姞鍏ュ簱鏍囪鍒楋紙鎵樼洏鍘熷厛鏃犲叆搴撴爣璁板瓧娈碉紝鏁存墭鍏ュ簱闇�鍘婚噸锛岄槻姝㈠悓涓�鎵樼洏閲嶅鎵爜鍏ュ簱锛�
+
+ALTER TABLE mes_pro_scan_event ADD COLUMN scan_type VARCHAR(16) NULL COMMENT '鎵爜绫诲瀷锛圔AG琚嬬爜/PALLET鎵樼洏鐮�/BATCH鎵规鐮侊級' AFTER bag_code;
+
+-- 璁惧缂栫爜涓嶅啀蹇呭~锛堝墠绔幓鎺夎澶囩紪鐮侊紝鍘嗗彶鍒椾繚鎸佸吋瀹癸級
+ALTER TABLE mes_pro_scan_event MODIFY COLUMN device_code VARCHAR(64) NULL DEFAULT NULL COMMENT '璁惧缂栫爜锛堜笉鍐嶅繀濉級';
+
+ALTER TABLE mes_pro_pallet ADD COLUMN inbound_event_id BIGINT NULL COMMENT '鏈�杩戝叆搴撴壂鐮佷簨浠剁紪鍙�' AFTER bag_count;
+ALTER TABLE mes_pro_pallet ADD COLUMN inbound_time DATETIME NULL COMMENT '鏈�杩戝叆搴撴椂闂�' AFTER inbound_event_id;
+
+UPDATE mes_pro_scan_event SET scan_type = 'BAG' WHERE scan_type IS NULL;
diff --git a/sql/mysql/mes_qc_seed_quality_record_menu.sql b/sql/mysql/mes_qc_seed_quality_record_menu.sql
new file mode 100644
index 0000000..11bd3f2
--- /dev/null
+++ b/sql/mysql/mes_qc_seed_quality_record_menu.sql
@@ -0,0 +1,14 @@
+-- =============================================
+-- 绉嶅瓙璐ㄦ璁板綍鐙珛鑿滃崟
+-- 灏嗚川妫�璁板綍浠庛�岀瀛愯川妫�銆嶉〉(鎻愪氦+璁板綍鍚堜竴)鎷嗗嚭锛岀嫭绔嬫垚鑿滃崟椤甸潰
+-- 璇存槑锛�
+-- 1. 绉嶅瓙璐ㄦ鎻愪氦椤碉細seed-quality -> mes/qc/seedQuality/index锛堜粎鎻愪氦锛�
+-- 2. 璐ㄦ璁板綍椤碉細seed-quality-record -> mes/qc/seedQuality/record锛堜粎璁板綍鏌ヨ锛�
+-- 鎸傚湪銆岃川閲忕鐞嗐��(5500) 鐩綍涓嬶紱骞傜瓑锛屽彲閲嶅鎵ц锛涙棤绉熸埛瀛楁銆�
+-- =============================================
+
+INSERT INTO system_menu
+(id, name, parent_id, type, sort, status, path, icon, component, component_name, permission, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted)
+SELECT 1065224, '璐ㄦ璁板綍', 5500, 2, 95, 0, 'seed-quality-record', 'ep:list', 'mes/qc/seedQuality/record', 'MesQcSeedQualityRecord', '', b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0'
+FROM DUAL
+WHERE NOT EXISTS (SELECT 1 FROM system_menu WHERE id = 1065224 AND deleted = b'0');
diff --git a/sql/mysql/mes_wm_material_stock_bag.sql b/sql/mysql/mes_wm_material_stock_bag.sql
new file mode 100644
index 0000000..e659a95
--- /dev/null
+++ b/sql/mysql/mes_wm_material_stock_bag.sql
@@ -0,0 +1,31 @@
+CREATE TABLE mes_wm_material_stock_bag (
+    id BIGINT NOT NULL AUTO_INCREMENT COMMENT '缂栧彿',
+    material_stock_id BIGINT NOT NULL COMMENT '搴撳瓨姹囨�昏褰旾D',
+    bag_code_id BIGINT NOT NULL COMMENT '鐢熶骇琚嬬爜ID',
+    bag_code VARCHAR(64) NOT NULL COMMENT '琚嬬爜',
+    item_id BIGINT NOT NULL COMMENT '鐗╂枡ID',
+    batch_id BIGINT NULL COMMENT '浠撳簱鎵规ID',
+    batch_code VARCHAR(64) NULL COMMENT '鎵规鍙�',
+    vendor_id BIGINT NULL COMMENT '渚涘簲鍟咺D',
+    warehouse_id BIGINT NOT NULL COMMENT '浠撳簱ID',
+    location_id BIGINT NULL COMMENT '搴撳尯ID',
+    area_id BIGINT NULL COMMENT '搴撲綅ID',
+    stock_state TINYINT NOT NULL COMMENT '搴撳瓨鐘舵�侊細10鏆傚瓨锛�20鍚堟牸锛�30宸插嚭搴擄紝40鎶ュ簾',
+    status TINYINT NOT NULL DEFAULT 1 COMMENT '鏄庣粏鐘舵�侊細1鍦ㄥ簱锛�2宸插嚭搴擄紝3宸叉姤搴�',
+    quantity DECIMAL(18, 6) NOT NULL DEFAULT 1 COMMENT '琚嬬爜搴撳瓨鏁伴噺',
+    frozen BIT NOT NULL DEFAULT 0 COMMENT '鏄惁鍐荤粨',
+    last_transaction_id BIGINT NULL COMMENT '鏈�杩戝簱瀛樹簨鍔D',
+    inbound_time DATETIME NULL COMMENT '鍏ュ簱鏃堕棿',
+    outbound_time DATETIME NULL COMMENT '鍑哄簱鏃堕棿',
+    creator VARCHAR(64) DEFAULT '' COMMENT '鍒涘缓鑰�',
+    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+    updater VARCHAR(64) DEFAULT '' COMMENT '鏇存柊鑰�',
+    update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '鏇存柊鏃堕棿',
+    deleted BIT NOT NULL DEFAULT 0 COMMENT '鏄惁鍒犻櫎',
+    PRIMARY KEY (id),
+    UNIQUE KEY uk_bag_code (bag_code),
+    KEY idx_material_stock (material_stock_id),
+    KEY idx_batch_state (batch_id, stock_state, status),
+    KEY idx_location_state (warehouse_id, location_id, area_id, stock_state, status),
+    KEY idx_bag_code_id (bag_code_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES搴撳瓨琚嬬爜鏄庣粏';
diff --git a/sql/mysql/mes_wm_product_sales_add_columns.sql b/sql/mysql/mes_wm_product_sales_add_columns.sql
new file mode 100644
index 0000000..04f072d
--- /dev/null
+++ b/sql/mysql/mes_wm_product_sales_add_columns.sql
@@ -0,0 +1,12 @@
+-- MES 閿�鍞嚭搴撳崟琛ㄧ粨鏋勪慨鏀�
+-- 鏍规嵁 MesWmProductSalesDO 琛ュ厖缂哄け瀛楁
+-- 璇存槑锛氫慨澶嶆煡璇㈡姤閿� Unknown column 'client_name'锛屽悓鏃惰ˉ鍏� outbound_time銆乷utbound_user_id
+
+-- 1. 缁忛攢鍟嗗悕绉板揩鐓�
+ALTER TABLE mes_wm_product_sales ADD COLUMN client_name VARCHAR(128) DEFAULT NULL COMMENT '缁忛攢鍟嗗悕绉板揩鐓�' AFTER client_id;
+
+-- 2. 瀹為檯鍑哄簱鏃堕棿
+ALTER TABLE mes_wm_product_sales ADD COLUMN outbound_time DATETIME DEFAULT NULL COMMENT '瀹為檯鍑哄簱鏃堕棿';
+
+-- 3. 鎵ц鍑哄簱鎿嶄綔浜篒D
+ALTER TABLE mes_wm_product_sales ADD COLUMN outbound_user_id BIGINT DEFAULT NULL COMMENT '鎵ц鍑哄簱鎿嶄綔浜篒D';
\ No newline at end of file
diff --git a/sql/mysql/mes_wm_qualified_warehouse.sql b/sql/mysql/mes_wm_qualified_warehouse.sql
new file mode 100644
index 0000000..29379d1
--- /dev/null
+++ b/sql/mysql/mes_wm_qualified_warehouse.sql
@@ -0,0 +1,85 @@
+-- =====================================================================
+-- 鏂板缓鍚堟牸浠撳簱 + 浠撳簱绫诲瀷 + 璐ㄦ鍏ュ悎鏍煎簱鏀归��
+--
+-- 1) mes_wm_warehouse 澧炲姞 type 瀛楁锛�10 鏆傚瓨搴� / 20 鍚堟牸搴擄級
+-- 2) 鐜版湁浠撳簱鍒嗙被锛氳櫄鎷熺嚎杈逛粨搴�=10锛堟殏瀛樺簱锛夛紝浜у搧搴�/鐗╂枡搴�=20锛堝悎鏍煎簱锛�
+-- 3) 鏂板缓"鎴愬搧鍚堟牸搴�"(type=20) + 涓�涓簱鍖� + 涓�涓簱浣�
+-- 4) 铏氭嫙浠撳簱涓� stock_state=20 鐨勫悎鏍煎簱瀛樿縼绉诲埌鏂板悎鏍煎簱
+--
+-- 璇存槑锛氭湰鑴氭湰骞傜瓑锛屽彲閲嶅鎵ц锛涙棤绉熸埛瀛楁銆�
+-- =====================================================================
+
+-- 1) 澧炲姞浠撳簱绫诲瀷瀛楁锛圡ySQL 涓嶆敮鎸� ALTER ADD COLUMN IF NOT EXISTS锛屽厛鏌ュ瓨鍦ㄦ�э級
+SET @type_exists = (
+  SELECT COUNT(*)
+  FROM information_schema.columns
+  WHERE table_schema = DATABASE()
+    AND table_name = 'mes_wm_warehouse'
+    AND column_name = 'type'
+);
+SET @type_sql = IF(
+  @type_exists = 0,
+  'ALTER TABLE mes_wm_warehouse ADD COLUMN type tinyint NOT NULL DEFAULT 10 COMMENT ''浠撳簱绫诲瀷锛�10鏆傚瓨搴擄紝20鍚堟牸搴�'' AFTER frozen',
+  'SELECT 1'
+);
+PREPARE type_stmt FROM @type_sql;
+EXECUTE type_stmt;
+DEALLOCATE PREPARE type_stmt;
+
+-- 2) 鐜版湁浠撳簱鍒嗙被
+UPDATE mes_wm_warehouse SET type = 10 WHERE code = 'WIP_VIRTUAL_WAREHOUSE' AND deleted = 0;
+UPDATE mes_wm_warehouse SET type = 20 WHERE code IN ('1212', 'WHS000001') AND deleted = 0;
+
+-- 3) 鏂板缓"鎴愬搧鍚堟牸搴�"锛坱ype=20锛�
+SET @wh_exists = (
+  SELECT COUNT(*) FROM mes_wm_warehouse WHERE code = 'FG_QUALIFIED_WH' AND deleted = 0
+);
+INSERT INTO mes_wm_warehouse (code, name, frozen, type, remark)
+SELECT 'FG_QUALIFIED_WH', '鎴愬搧鍚堟牸搴�', 0, 20, '璐ㄦ鍚堟牸鐩存帴鍏ュ簱鐨勭洰鏍囦粨搴擄紙type=20 鍚堟牸搴擄級'
+WHERE @wh_exists = 0;
+SET @wh_id = (SELECT id FROM mes_wm_warehouse WHERE code = 'FG_QUALIFIED_WH' AND deleted = 0 LIMIT 1);
+
+-- 鏂板缓"鎴愬搧鍚堟牸搴撳尯"
+SET @loc_exists = (
+  SELECT COUNT(*) FROM mes_wm_warehouse_location WHERE code = 'FG_QUALIFIED_WH_LOC' AND deleted = 0
+);
+INSERT INTO mes_wm_warehouse_location (code, name, warehouse_id, frozen, remark)
+SELECT 'FG_QUALIFIED_WH_LOC', '鎴愬搧鍚堟牸搴撳尯', @wh_id, 0, '璐ㄦ鍚堟牸鍏ュ簱榛樿搴撳尯'
+WHERE @loc_exists = 0;
+SET @loc_id = (SELECT id FROM mes_wm_warehouse_location WHERE code = 'FG_QUALIFIED_WH_LOC' AND deleted = 0 LIMIT 1);
+
+-- 鏂板缓"鎴愬搧鍚堟牸搴撲綅"
+SET @area_exists = (
+  SELECT COUNT(*) FROM mes_wm_warehouse_area WHERE code = 'FG_QUALIFIED_WH_AREA' AND deleted = 0
+);
+INSERT INTO mes_wm_warehouse_area (code, name, location_id, frozen, remark)
+SELECT 'FG_QUALIFIED_WH_AREA', '鎴愬搧鍚堟牸搴撲綅', @loc_id, 0, '璐ㄦ鍚堟牸鍏ュ簱榛樿搴撲綅'
+WHERE @area_exists = 0;
+SET @area_id = (SELECT id FROM mes_wm_warehouse_area WHERE code = 'FG_QUALIFIED_WH_AREA' AND deleted = 0 LIMIT 1);
+
+-- 4) 杩佺Щ铏氭嫙浠撳簱鍚堟牸搴撳瓨鍒版柊鍚堟牸搴�
+-- 4.1 鑻ユ柊鍚堟牸搴撳凡瀛樺湪鍚� (item_id, batch_id, vendor_id) 鐨� state=20 璁板綍锛屽厛鏁伴噺绱姞鍚堝苟
+UPDATE mes_wm_material_stock t
+JOIN mes_wm_material_stock s
+  ON s.warehouse_id = 3 AND s.stock_state = 20 AND s.deleted = 0
+ AND t.warehouse_id = @wh_id AND t.stock_state = 20 AND t.deleted = 0
+ AND t.item_id = s.item_id
+ AND IFNULL(t.batch_id, 0) = IFNULL(s.batch_id, 0)
+ AND IFNULL(t.vendor_id, 0) = IFNULL(s.vendor_id, 0)
+SET t.quantity = t.quantity + s.quantity,
+    t.reserved_quantity = t.reserved_quantity + s.reserved_quantity,
+    t.frozen_quantity = t.frozen_quantity + s.frozen_quantity;
+
+-- 4.2 鍒犻櫎宸插悎骞跺埌鏂板悎鏍煎簱鐨勬簮璁板綍锛堣櫄鎷熶粨搴撲腑锛�
+DELETE s FROM mes_wm_material_stock s
+JOIN mes_wm_material_stock t
+  ON s.warehouse_id = 3 AND s.stock_state = 20 AND s.deleted = 0
+ AND t.warehouse_id = @wh_id AND t.stock_state = 20 AND t.deleted = 0
+ AND t.item_id = s.item_id
+ AND IFNULL(t.batch_id, 0) = IFNULL(s.batch_id, 0)
+ AND IFNULL(t.vendor_id, 0) = IFNULL(s.vendor_id, 0);
+
+-- 4.3 鍏朵綑鍚堟牸搴撳瓨鐩存帴鏀逛粨搴�/搴撳尯/搴撲綅
+UPDATE mes_wm_material_stock
+SET warehouse_id = @wh_id, location_id = @loc_id, area_id = @area_id
+WHERE warehouse_id = 3 AND stock_state = 20 AND deleted = 0;
diff --git a/sql/mysql/mes_wm_transaction_bag_code.sql b/sql/mysql/mes_wm_transaction_bag_code.sql
new file mode 100644
index 0000000..4b974c9
--- /dev/null
+++ b/sql/mysql/mes_wm_transaction_bag_code.sql
@@ -0,0 +1,17 @@
+SET @exists_bag_code_id = (SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'mes_wm_transaction' AND column_name = 'bag_code_id');
+SET @sql_bag_code_id = IF(@exists_bag_code_id = 0, 'ALTER TABLE mes_wm_transaction ADD COLUMN bag_code_id BIGINT NULL COMMENT ''鐢熶骇琚嬬爜ID'' AFTER material_stock_id', 'SELECT 1');
+PREPARE stmt FROM @sql_bag_code_id;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @exists_bag_code = (SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'mes_wm_transaction' AND column_name = 'bag_code');
+SET @sql_bag_code = IF(@exists_bag_code = 0, 'ALTER TABLE mes_wm_transaction ADD COLUMN bag_code VARCHAR(64) NULL COMMENT ''琚嬬爜'' AFTER bag_code_id', 'SELECT 1');
+PREPARE stmt FROM @sql_bag_code;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @exists_bag_code_index = (SELECT COUNT(*) FROM information_schema.statistics WHERE table_schema = DATABASE() AND table_name = 'mes_wm_transaction' AND index_name = 'idx_bag_code');
+SET @sql_bag_code_index = IF(@exists_bag_code_index = 0, 'ALTER TABLE mes_wm_transaction ADD KEY idx_bag_code (bag_code)', 'SELECT 1');
+PREPARE stmt FROM @sql_bag_code_index;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
diff --git a/sql/mysql/wls_sales_outbound_menu_extract.sql b/sql/mysql/wls_sales_outbound_menu_extract.sql
new file mode 100644
index 0000000..6df47c8
--- /dev/null
+++ b/sql/mysql/wls_sales_outbound_menu_extract.sql
@@ -0,0 +1,25 @@
+-- =============================================
+-- 鍑哄簱娴侀�氱鐞嗚彍鍗曟彁鍙栬剼鏈�
+-- 灏�"閿�鍞粨搴�"(602479) 鎻愬彇涓轰竴绾ц彍鍗�"鍑哄簱娴侀�氱鐞�"
+-- 鍘熺粨鏋勶細浠撳偍鐗╂祦(5780) -> 閿�鍞粨搴�(602479) -> 鍙戣揣閫氱煡/閿�鍞嚭搴�/閿�鍞��璐�
+-- =============================================
+
+-- 1. 鎻愬彇涓轰竴绾ц彍鍗�
+-- 璇存槑锛氶《绾�"钀ラ攢绠$悊"(2617) 宸插崰鐢� path=sale锛屾晠鏀圭敤 sales-outbound 閬垮厤璺敱鍐茬獊
+UPDATE system_menu SET
+  parent_id = 0,
+  name = '鍑哄簱娴侀�氱鐞�',
+  path = 'sales-outbound',
+  sort = 10  -- 鏀惧湪浠撳偍鐗╂祦(sort=9)涔嬪悗
+WHERE id = 602479 AND deleted = b'0';
+
+-- =============================================
+-- 璇存槑锛�
+-- 1. 瀛愯彍鍗�(鍙戣揣閫氱煡 5867 / 閿�鍞嚭搴� 5970 / 閿�鍞��璐� 5847) 鏃犻渶鏀瑰姩锛�
+--    鍓嶇鍔ㄦ�佽矾鐢变細鑷姩鎷兼帴涓� /sales-outbound/sales-notice 绛夎矾寰�
+-- 2. 椤剁骇鐩綍鑿滃崟鐢卞墠绔嚜鍔ㄦ覆鏌撲负 BasicLayout锛宑omponent 瀛楁鏃犻渶濉啓
+-- 3. 濡傞渶鎭㈠锛屾墽琛屼互涓嬪弽鍚戣剼鏈細
+-- =============================================
+
+-- 鍙嶅悜鑴氭湰锛堝闇�鎭㈠锛夛細
+-- UPDATE system_menu SET parent_id = 5780, name = '閿�鍞粨搴�', path = 'sale', sort = 5 WHERE id = 602479;
diff --git a/yudao-module-aftersales/src/main/java/cn/iocoder/yudao/module/aftersales/service/returnobj/AfterSaleReturnServiceImpl.java b/yudao-module-aftersales/src/main/java/cn/iocoder/yudao/module/aftersales/service/returnobj/AfterSaleReturnServiceImpl.java
index ed5fcd6..e820f5d 100644
--- a/yudao-module-aftersales/src/main/java/cn/iocoder/yudao/module/aftersales/service/returnobj/AfterSaleReturnServiceImpl.java
+++ b/yudao-module-aftersales/src/main/java/cn/iocoder/yudao/module/aftersales/service/returnobj/AfterSaleReturnServiceImpl.java
@@ -130,8 +130,6 @@
         MesWmReturnSalesCreateReqDTO mesReqDTO = new MesWmReturnSalesCreateReqDTO();
         mesReqDTO.setCode(returnDO.getNo());
         mesReqDTO.setName("鍞悗閫�璐�-" + returnDO.getNo());
-        mesReqDTO.setSaleOrderId(returnDO.getSaleOrderId());
-        mesReqDTO.setSalesOrderCode(ticket != null ? ticket.getSaleOrderNo() : null);
         mesReqDTO.setClientId(returnDO.getCustomerId());
         mesReqDTO.setReturnDate(LocalDate.now());
         mesReqDTO.setReturnReason(returnDO.getReturnReason());
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOrderController.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOrderController.java
index 3cacd73..43748cd 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOrderController.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOrderController.java
@@ -21,9 +21,6 @@
 import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO;
 import cn.iocoder.yudao.module.mdm.api.unit.MdmUnitMeasureApi;
 import cn.iocoder.yudao.module.mdm.api.unit.dto.MdmUnitMeasureRespDTO;
-import cn.iocoder.yudao.module.mes.api.mps.MesProMpsApi;
-import cn.iocoder.yudao.module.mes.api.productsales.MesWmProductSalesLineApi;
-import cn.iocoder.yudao.module.mes.api.salesnotice.MesWmSalesNoticeApi;
 import cn.iocoder.yudao.module.mes.api.wms.MesWmStockApi;
 import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi;
 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
@@ -63,12 +60,6 @@
     private MdmItemApi mdmItemApi;
     @Resource
     private AdminUserApi adminUserApi;
-    @Resource
-    private MesWmSalesNoticeApi salesNoticeApi;
-    @Resource
-    private MesWmProductSalesLineApi productSalesLineApi;
-    @Resource
-    private MesProMpsApi mpsApi;
     @Resource
     private StorageAttachmentApi storageAttachmentApi;
     @Resource
@@ -155,14 +146,11 @@
         // 2. 鎵归噺鏌ヨ鍏宠仈鏁版嵁
         Map<Long, MdmItemRespDTO> itemMap = mdmItemApi.getItemMap(
                 convertSet(saleOrderItemList, ErpSaleOrderItemDO::getProductId));
-        Map<Long, String> batchCodeMap = productSalesLineApi.getBatchCodesBySaleOrderItemIds(
-                convertSet(saleOrderItemList, ErpSaleOrderItemDO::getId));
         Map<Long, MdmUnitMeasureRespDTO> unitMeasureMap = mdmUnitMeasureApi.getUnitMeasureMap(
                 convertSet(saleOrderItemList, ErpSaleOrderItemDO::getProductUnitId));
         // 3. 杞崲璁㈠崟椤�
         List<ErpSaleOrderRespVO.Item> itemVOList = BeanUtils.toBean(saleOrderItemList, ErpSaleOrderRespVO.Item.class, item -> {
             item.setStockCount(mesWmStockApi.getStockQuantity(item.getProductId()));
-            item.setBatchCode(batchCodeMap.get(item.getId()));
             MapUtils.findAndThen(itemMap, item.getProductId(), mdmItem ->
                     item.setProductName(mdmItem.getName()).setProductBarCode(mdmItem.getBarCode())
                             .setProductSpecification(mdmItem.getSpecification()));
@@ -213,54 +201,13 @@
         // 1.4 绠$悊鍛樹俊鎭�
         Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
                 convertSet(pageResult.getList(), saleOrder -> Long.parseLong(saleOrder.getCreator())));
-        // 1.5 鍙戣揣閫氱煡鍗曚俊鎭�
-        Map<Long, Boolean> salesNoticeMap = salesNoticeApi.checkSalesNoticeBySaleOrderIds(
-                convertSet(pageResult.getList(), ErpSaleOrderDO::getId));
-        // 1.6 鍑哄簱鎵瑰彿
-        Map<Long, String> batchCodeMap = productSalesLineApi.getBatchCodesBySaleOrderItemIds(
-                convertSet(saleOrderItemList, ErpSaleOrderItemDO::getId));
-        // 1.7 璁¢噺鍗曚綅
+        // 1.5 璁¢噺鍗曚綅
         Map<Long, MdmUnitMeasureRespDTO> unitMeasureMap = mdmUnitMeasureApi.getUnitMeasureMap(
                 convertSet(saleOrderItemList, ErpSaleOrderItemDO::getProductUnitId));
-        // 1.8 妫�鏌ユ槸鍚﹀彲浠ョ敓鎴愬彂璐ч�氱煡鍗曪紙闇�瑕佺敓浜х殑鏄庣粏瀵瑰簲鐨� MPS 鏄惁宸插畬鎴愶級
-        // 绛涢�夊嚭闇�瑕佺敓浜х殑鏄庣粏ID
-        List<Long> needProductionItemIds = saleOrderItemList.stream()
-                .filter(item -> item.getNeedProduction() != null && item.getNeedProduction() == 1)
-                .map(ErpSaleOrderItemDO::getId)
-                .toList();
-        Map<Long, Boolean> canCreateSalesNoticeMap = new java.util.HashMap<>();
-        if (!needProductionItemIds.isEmpty()) {
-            // 鎸夎鍗曞垎缁勬槑缁咺D
-            Map<Long, List<Long>> orderItemIdsMap = new java.util.HashMap<>();
-            for (ErpSaleOrderItemDO item : saleOrderItemList) {
-                orderItemIdsMap.computeIfAbsent(item.getOrderId(), k -> new java.util.ArrayList<>())
-                        .add(item.getId());
-            }
-            // 妫�鏌ユ瘡涓鍗�
-            for (Map.Entry<Long, List<Long>> entry : orderItemIdsMap.entrySet()) {
-                List<Long> itemIds = entry.getValue().stream()
-                        .filter(id -> {
-                            ErpSaleOrderItemDO item = saleOrderItemList.stream()
-                                    .filter(i -> i.getId().equals(id))
-                                    .findFirst().orElse(null);
-                            return item != null && item.getNeedProduction() != null && item.getNeedProduction() == 1;
-                        }
-                ).toList();
-                if (itemIds.isEmpty()) {
-                    canCreateSalesNoticeMap.put(entry.getKey(), true);
-                } else {
-                    canCreateSalesNoticeMap.put(entry.getKey(), mpsApi.checkCanCreateSalesNotice(itemIds));
-                }
-            }
-        } else {
-            // 娌℃湁闇�瑕佺敓浜х殑鏄庣粏锛岄兘鍙互鐢熸垚鍙戣揣閫氱煡鍗�
-            pageResult.getList().forEach(order -> canCreateSalesNoticeMap.put(order.getId(), true));
-        }
         // 2. 寮�濮嬫嫾鎺�
         return BeanUtils.toBean(pageResult, ErpSaleOrderRespVO.class, saleOrder -> {
             saleOrder.setItems(BeanUtils.toBean(saleOrderItemMap.get(saleOrder.getId()), ErpSaleOrderRespVO.Item.class,
                     item -> {
-                        item.setBatchCode(batchCodeMap.get(item.getId()));
                         MapUtils.findAndThen(itemMap, item.getProductId(), mdmItem ->
                                 item.setProductName(mdmItem.getName()).setProductBarCode(mdmItem.getBarCode())
                                         .setProductSpecification(mdmItem.getSpecification()));
@@ -270,10 +217,6 @@
             saleOrder.setProductNames(CollUtil.join(saleOrder.getItems(), "锛�", ErpSaleOrderRespVO.Item::getProductName));
             MapUtils.findAndThen(customerMap, saleOrder.getCustomerId(), supplier -> saleOrder.setCustomerName(supplier.getName()));
             MapUtils.findAndThen(userMap, Long.parseLong(saleOrder.getCreator()), user -> saleOrder.setCreatorName(user.getNickname()));
-            // 璁剧疆鏄惁宸茬敓鎴愬彂璐ч�氱煡鍗�
-            saleOrder.setHasSalesNotice(salesNoticeMap.getOrDefault(saleOrder.getId(), false));
-            // 璁剧疆鏄惁鍙互鐢熸垚鍙戣揣閫氱煡鍗曪細宸茬敓鎴愬彂璐ч�氱煡鍗曟椂涓嶆樉绀烘寜閽�
-            saleOrder.setCanCreateSalesNotice(!saleOrder.getHasSalesNotice() && canCreateSalesNoticeMap.getOrDefault(saleOrder.getId(), true));
             // 璁剧疆鏄惁鏈夐渶瑕佺敓浜х殑鏄庣粏
             boolean hasNeedProduction = saleOrder.getItems().stream()
                     .anyMatch(item -> item.getNeedProduction() != null && item.getNeedProduction() == 1);
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderRespVO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderRespVO.java
index dd38717..73e35e7 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderRespVO.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderRespVO.java
@@ -104,18 +104,10 @@
     @Schema(description = "鍑哄簱鐘舵��", example = "0")
     private Integer outStatus;
 
-    // ========== 鍙戣揣閫氱煡 ==========
-
-    @Schema(description = "鏄惁宸茬敓鎴愬彂璐ч�氱煡鍗�", example = "false")
-    private Boolean hasSalesNotice;
-
-    @Schema(description = "鏄惁鍙互鐢熸垚鍙戣揣閫氱煡鍗曪紙鏈夐渶瑕佺敓浜т笖鏈畬鎴愮殑鏄庣粏鏃朵负 false锛�", example = "true")
-    private Boolean canCreateSalesNotice;
+    // ========== 鐢熶骇鍏宠仈 ==========
 
     @Schema(description = "鏄惁鏈夐渶瑕佺敓浜х殑鏄庣粏锛�0=鍚� 1=鏄級", example = "1")
     private Integer needProduction;
-
-    // ========== 鐢熶骇鍏宠仈 ==========
 
     @Schema(description = "鐢熶骇鐘舵�侊細0-鏈畬鎴愶紝1-宸插畬鎴�", example = "1")
     private Integer productionStatus;
@@ -163,11 +155,6 @@
 
         @Schema(description = "鍏宠仈鐨� MPS 缂栧彿", example = "1024")
         private Long mpsId;
-
-        // ========== 鍑哄簱鎵瑰彿 ==========
-
-        @Schema(description = "鍑哄簱鎵瑰彿", example = "B2026001銆丅2026002")
-        private String batchCode;
 
         // ========== 鍏宠仈瀛楁 ==========
 
diff --git a/yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/productsales/MesWmProductSalesLineApi.java b/yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/productsales/MesWmProductSalesLineApi.java
deleted file mode 100644
index 42d7636..0000000
--- a/yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/productsales/MesWmProductSalesLineApi.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package cn.iocoder.yudao.module.mes.api.productsales;
-
-import java.util.Collection;
-import java.util.Map;
-
-/**
- * MES 閿�鍞嚭搴撳崟琛� API 鎺ュ彛
- *
- * @author 瓒呯骇绠$悊鍛�
- */
-public interface MesWmProductSalesLineApi {
-
-    /**
-     * 鏍规嵁閿�鍞鍗曟槑缁� ID 鍒楄〃鏌ヨ鍑哄簱鎵瑰彿
-     *
-     * @param saleOrderItemIds 閿�鍞鍗曟槑缁� ID 鍒楄〃
-     * @return Map<閿�鍞鍗曟槑缁咺D, 鎵瑰彿涓茶仈瀛楃涓�>
-     */
-    Map<Long, String> getBatchCodesBySaleOrderItemIds(Collection<Long> saleOrderItemIds);
-
-}
diff --git a/yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/returnsales/dto/MesWmReturnSalesCreateReqDTO.java b/yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/returnsales/dto/MesWmReturnSalesCreateReqDTO.java
index ce7e494..bdc8482 100644
--- a/yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/returnsales/dto/MesWmReturnSalesCreateReqDTO.java
+++ b/yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/returnsales/dto/MesWmReturnSalesCreateReqDTO.java
@@ -20,12 +20,6 @@
     @Schema(description = "閫�璐у崟鍚嶇О", requiredMode = Schema.RequiredMode.REQUIRED, example = "鍞悗閫�璐�")
     private String name;
 
-    @Schema(description = "閿�鍞鍗� ID", example = "1")
-    private Long saleOrderId;
-
-    @Schema(description = "閿�鍞鍗曠紪鍙�", example = "SO202603010001")
-    private String salesOrderCode;
-
     @Schema(description = "瀹㈡埛 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
     private Long clientId;
 
diff --git a/yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/salesnotice/MesWmSalesNoticeApi.java b/yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/salesnotice/MesWmSalesNoticeApi.java
deleted file mode 100644
index b6c5825..0000000
--- a/yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/salesnotice/MesWmSalesNoticeApi.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package cn.iocoder.yudao.module.mes.api.salesnotice;
-
-import java.util.Collection;
-import java.util.Map;
-
-/**
- * MES 鍙戣揣閫氱煡鍗� API 鎺ュ彛
- *
- * @author 瓒呯骇绠$悊鍛�
- */
-public interface MesWmSalesNoticeApi {
-
-    /**
-     * 鏍规嵁閿�鍞鍗� ID 鍒楄〃妫�鏌ユ槸鍚﹀凡鐢熸垚鍙戣揣閫氱煡鍗�
-     *
-     * @param saleOrderIds 閿�鍞鍗� ID 鍒楄〃
-     * @return Map<閿�鍞鍗旾D, 鏄惁宸茬敓鎴愬彂璐ч�氱煡鍗�>
-     */
-    Map<Long, Boolean> checkSalesNoticeBySaleOrderIds(Collection<Long> saleOrderIds);
-
-}
\ No newline at end of file
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/api/productsales/MesWmProductSalesLineApiImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/api/productsales/MesWmProductSalesLineApiImpl.java
deleted file mode 100644
index 005e87e..0000000
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/api/productsales/MesWmProductSalesLineApiImpl.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package cn.iocoder.yudao.module.mes.api.productsales;
-
-import cn.iocoder.yudao.module.mes.dal.mysql.wm.productsales.MesWmProductSalesLineMapper;
-import jakarta.annotation.Resource;
-import org.springframework.stereotype.Service;
-import org.springframework.validation.annotation.Validated;
-
-import java.util.Collection;
-import java.util.Map;
-
-/**
- * MES 閿�鍞嚭搴撳崟琛� API 瀹炵幇绫�
- *
- * @author 瓒呯骇绠$悊鍛�
- */
-@Service
-@Validated
-public class MesWmProductSalesLineApiImpl implements MesWmProductSalesLineApi {
-
-    @Resource
-    private MesWmProductSalesLineMapper productSalesLineMapper;
-
-    @Override
-    public Map<Long, String> getBatchCodesBySaleOrderItemIds(Collection<Long> saleOrderItemIds) {
-        return productSalesLineMapper.selectBatchCodesBySaleOrderItemIds(saleOrderItemIds);
-    }
-
-}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/api/salesnotice/MesWmSalesNoticeApiImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/api/salesnotice/MesWmSalesNoticeApiImpl.java
deleted file mode 100644
index 943fb7b..0000000
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/api/salesnotice/MesWmSalesNoticeApiImpl.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package cn.iocoder.yudao.module.mes.api.salesnotice;
-
-import cn.iocoder.yudao.module.mes.dal.mysql.wm.salesnotice.MesWmSalesNoticeMapper;
-import jakarta.annotation.Resource;
-import org.springframework.stereotype.Service;
-import org.springframework.validation.annotation.Validated;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * MES 鍙戣揣閫氱煡鍗� API 瀹炵幇绫�
- *
- * @author 瓒呯骇绠$悊鍛�
- */
-@Service
-@Validated
-public class MesWmSalesNoticeApiImpl implements MesWmSalesNoticeApi {
-
-    @Resource
-    private MesWmSalesNoticeMapper salesNoticeMapper;
-
-    @Override
-    public Map<Long, Boolean> checkSalesNoticeBySaleOrderIds(Collection<Long> saleOrderIds) {
-        if (saleOrderIds == null || saleOrderIds.isEmpty()) {
-            return Collections.emptyMap();
-        }
-        // 鏌ヨ宸插瓨鍦ㄧ殑閿�鍞鍗� ID
-        List<Long> existingSaleOrderIds = salesNoticeMapper.selectExistingSaleOrderIds(saleOrderIds);
-        // 鏋勫缓杩斿洖缁撴灉
-        Map<Long, Boolean> result = new HashMap<>();
-        for (Long saleOrderId : saleOrderIds) {
-            result.put(saleOrderId, existingSaleOrderIds.contains(saleOrderId));
-        }
-        return result;
-    }
-
-}
\ No newline at end of file
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/home/vo/MesBagCodeHomeSummaryRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/home/vo/MesBagCodeHomeSummaryRespVO.java
index cf6c323..4bb5491 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/home/vo/MesBagCodeHomeSummaryRespVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/home/vo/MesBagCodeHomeSummaryRespVO.java
@@ -37,6 +37,25 @@
     @Schema(description = "鎵樼洏鐮佹�绘暟", requiredMode = Schema.RequiredMode.REQUIRED, example = "800")
     private Long palletCount;
 
+    // ========== 鍑哄簱娴侀�� ==========
+
+    @Schema(description = "鍙戣揣閫氱煡鍗曟暟锛坢es_wm_sales_notice锛�", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
+    private Long salesNoticeCount;
+
+    @Schema(description = "閿�鍞嚭搴撳崟鏁帮紙mes_wm_product_sales锛�", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
+    private Long productSalesCount;
+
+    @Schema(description = "閿�鍞��璐у崟鏁帮紙mes_wm_return_sales锛�", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
+    private Long returnSalesCount;
+
+    // ========== 浠撳偍棰勮 / 璐ㄩ噺 ==========
+
+    @Schema(description = "鏈鐞嗗簱瀛橀璀︽暟锛坢es_wm_stock_warning.status=10锛�", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
+    private Long stockWarningPendingCount;
+
+    @Schema(description = "寰呰瘎瀹′笉鍚堟牸鍝佸崟鏁帮紙mes_qc_ncr.status=0锛�", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
+    private Long ncrPendingCount;
+
     // ========== 鐘舵�佸垎甯� ==========
 
     @Schema(description = "鎵规鐘舵�佸垎甯�")
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/scan/MesProScanEventController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/scan/MesProScanEventController.java
index 4566021..f3dfe25 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/scan/MesProScanEventController.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/scan/MesProScanEventController.java
@@ -23,7 +23,7 @@
 @RestController @RequestMapping("/mes/pro/scan-in")
 public class MesProScanEventController {
     @Resource private MesProScanEventService service;
-    @PostMapping("/inbound") @PreAuthorize("@ss.hasPermission('mes:pro-scan-in:inbound')") @Operation(summary = "琚嬬爜鎵爜鍏ュ簱")
+    @PostMapping("/inbound") @PreAuthorize("@ss.hasPermission('mes:pro-scan-in:inbound')") @Operation(summary = "杩芥函鐮佹壂鐮佸叆搴擄紙琚嬬爜/鎵樼洏鐮�/鎵规鐮侊級")
     public CommonResult<MesProScanEventRespVO> inbound(@Valid @RequestBody MesProScanEventCreateReqVO req) {
         return success(BeanUtils.toBean(service.createInbound(req), MesProScanEventRespVO.class));
     }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/scan/vo/MesProScanEventCreateReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/scan/vo/MesProScanEventCreateReqVO.java
index 99ded7b..c6c2565 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/scan/vo/MesProScanEventCreateReqVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/scan/vo/MesProScanEventCreateReqVO.java
@@ -8,9 +8,19 @@
 @Data
 public class MesProScanEventCreateReqVO {
     @NotBlank private String eventId;
+    /**
+     * 鎵爜鍐呭锛堣鐮� / 鎵樼洏鐮� / 鎵规鐮佺殑鐮佸�硷紝鍚复鏃剁爜锛�
+     */
     @NotBlank private String bagCode;
+    /**
+     * 鎵爜绫诲瀷锛欱AG 琚嬬爜 / PALLET 鎵樼洏鐮� / BATCH 鎵规鐮侊紝榛樿 BAG
+     */
+    private String scanType;
     @NotNull @DecimalMin("0.000001") private BigDecimal quantity;
-    @NotBlank private String deviceCode;
+    /**
+     * 璁惧缂栫爜锛堜笉鍐嶅繀濉紝浜х嚎鎵爜浠ヤ骇绾�+搴撳尯搴撲綅瀹氫綅锛�
+     */
+    @Size(max = 64) private String deviceCode;
     private String lineCode;
     @NotNull private Long locationId;
     @NotNull private Long areaId;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/scan/vo/MesProScanEventRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/scan/vo/MesProScanEventRespVO.java
index 18ca711..2cfb498 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/scan/vo/MesProScanEventRespVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/scan/vo/MesProScanEventRespVO.java
@@ -10,6 +10,7 @@
     private Long id;
     private String eventId;
     private String bagCode;
+    private String scanType;
     private String batchCode;
     private BigDecimal quantity;
     private String deviceCode;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/seedquality/MesQcSeedQualityController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/seedquality/MesQcSeedQualityController.java
index 2d35909..3bbd79e 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/seedquality/MesQcSeedQualityController.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/seedquality/MesQcSeedQualityController.java
@@ -1,6 +1,10 @@
 package cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality;
 
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedQualityPageReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedQualityRespVO;
 import cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedQualitySubmitReqVO;
 import cn.iocoder.yudao.module.mes.service.qc.seedquality.MesQcSeedQualityService;
 import io.swagger.v3.oas.annotations.Operation;
@@ -16,6 +20,13 @@
 @RestController @RequestMapping("/mes/qc/seed-quality") @Validated
 public class MesQcSeedQualityController {
     @Resource private MesQcSeedQualityService service;
+    @GetMapping("/page")
+    @Operation(summary = "鑾峰緱绉嶅瓙璐ㄦ璁板綍鍒嗛〉")
+    @PreAuthorize("@ss.hasPermission('mes:qc-seed-quality:query')")
+    public CommonResult<PageResult<MesQcSeedQualityRespVO>> getQualityPage(@Valid MesQcSeedQualityPageReqVO pageReqVO) {
+        return success(BeanUtils.toBean(service.getQualityPage(pageReqVO), MesQcSeedQualityRespVO.class));
+    }
+
     @PostMapping("/submit") @Operation(summary = "鎻愪氦绉嶅瓙璐ㄦ")
     @PreAuthorize("@ss.hasPermission('mes:qc-seed-quality:submit')")
     public CommonResult<Long> submit(@Valid @RequestBody MesQcSeedQualitySubmitReqVO reqVO) { return success(service.submit(reqVO)); }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/seedquality/vo/MesQcSeedQualityPageReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/seedquality/vo/MesQcSeedQualityPageReqVO.java
new file mode 100644
index 0000000..2645d67
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/seedquality/vo/MesQcSeedQualityPageReqVO.java
@@ -0,0 +1,21 @@
+package cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Schema(description = "绠$悊鍚庡彴 - MES 绉嶅瓙璐ㄦ璁板綍鍒嗛〉 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class MesQcSeedQualityPageReqVO extends PageParam {
+
+    @Schema(description = "璐ㄦ琚嬬爜")
+    private String code;
+
+    @Schema(description = "鐢熶骇鎵规")
+    private String batchCode;
+
+    @Schema(description = "妫�楠岀粨鏋滐細1鍚堟牸 2涓嶅悎鏍�")
+    private Integer result;
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/seedquality/vo/MesQcSeedQualityRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/seedquality/vo/MesQcSeedQualityRespVO.java
new file mode 100644
index 0000000..6102954
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/seedquality/vo/MesQcSeedQualityRespVO.java
@@ -0,0 +1,22 @@
+package cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "绠$悊鍚庡彴 - MES 绉嶅瓙璐ㄦ璁板綍 Response VO")
+@Data
+public class MesQcSeedQualityRespVO {
+
+    private Long id;
+    private String code;
+    private Long itemId;
+    private Long batchId;
+    private String batchCode;
+    private Integer result;
+    private String reason;
+    private Long inspectorId;
+    private LocalDateTime inspectedAt;
+    private LocalDateTime createTime;
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/seedquality/vo/MesQcSeedQualitySubmitReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/seedquality/vo/MesQcSeedQualitySubmitReqVO.java
index d3b0978..1d8d9d3 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/seedquality/vo/MesQcSeedQualitySubmitReqVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/seedquality/vo/MesQcSeedQualitySubmitReqVO.java
@@ -9,10 +9,11 @@
 @Data
 public class MesQcSeedQualitySubmitReqVO {
     @NotBlank private String code;
-    @NotNull private Long itemId;
     private String batchCode;
     @NotEmpty @Valid private List<Indicator> indicators;
     private String reason;
+    /** 鐩爣鍚堟牸搴撶紪鍙凤紙璐ㄦ鍚堟牸鍚庡叆璇ヤ粨搴擄級锛屽繀椤绘槸浠撳簱绫诲瀷涓哄悎鏍煎簱(20)鐨勪粨搴� */
+    @NotNull private Long qualifiedWarehouseId;
     @Data public static class Indicator {
         @NotBlank private String code;
         @NotNull @DecimalMin("0") private BigDecimal value;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/trace/batch/MesTraceBatchController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/trace/batch/MesTraceBatchController.java
new file mode 100644
index 0000000..4ce1163
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/trace/batch/MesTraceBatchController.java
@@ -0,0 +1,39 @@
+package cn.iocoder.yudao.module.mes.controller.admin.trace.batch;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.module.mes.controller.pub.trace.vo.MesProTracePublicRespVO;
+import cn.iocoder.yudao.module.mes.service.trace.MesProTracePublicService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.validation.constraints.NotBlank;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+@Tag(name = "绠$悊鍚庡彴 - MES 鎵规杩芥函")
+@RestController
+@RequestMapping("/mes/trace/batch")
+@Validated
+public class MesTraceBatchController {
+
+    @Resource
+    private MesProTracePublicService traceService;
+
+    @GetMapping("/detail")
+    @Operation(summary = "鏌ヨ缁熶竴鎵规杩芥函璇︽儏")
+    @Parameter(name = "code", description = "鎵规鐮併�佷复鏃舵壒娆$爜銆佽鐮佹垨鎵樼洏鐮�", required = true,
+            example = "BAG-202608280001")
+    @PreAuthorize("@ss.hasPermission('mes:trace-batch:query')")
+    public CommonResult<MesProTracePublicRespVO> getBatchTraceDetail(
+            @RequestParam("code") @NotBlank(message = "杩芥函鐮佷笉鑳戒负绌�") String code) {
+        return success(traceService.queryTrace(code));
+    }
+
+}
\ No newline at end of file
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 26cb544..6c23287 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
@@ -14,6 +14,9 @@
 import cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo.MesWmMaterialStockPageReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo.MesWmMaterialStockRespVO;
 import cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo.MesWmMaterialStockListReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo.MesWmMaterialStockBagPageReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo.MesWmMaterialStockBagRespVO;
+import cn.iocoder.yudao.module.mes.dal.mysql.wm.materialstockbag.MesWmMaterialStockBagMapper;
 import cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo.MesWmStockSummaryRespVO;
 import cn.iocoder.yudao.module.srm.api.supplier.SrmSupplierApi;
 import cn.iocoder.yudao.module.srm.api.supplier.dto.SrmSupplierRespDTO;
@@ -21,6 +24,7 @@
 import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO;
 
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstockbag.MesWmMaterialStockBagDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLocationDO;
@@ -72,6 +76,8 @@
     private MesWmWarehouseAreaService areaService;
     @Resource
     private SrmSupplierApi srmSupplierApi;
+    @Resource
+    private MesWmMaterialStockBagMapper materialStockBagMapper;
 
     @GetMapping("/get")
     @Operation(summary = "鑾峰緱搴撳瓨璁板綍")
@@ -92,6 +98,17 @@
             @Valid MesWmMaterialStockPageReqVO pageReqVO) {
         PageResult<MesWmMaterialStockDO> pageResult = materialStockService.getMaterialStockPage(pageReqVO);
         return success(new PageResult<>(buildRespVOList(pageResult.getList()), pageResult.getTotal()));
+    }
+
+    @GetMapping("/bag-details")
+    @Operation(summary = "鏌ヨ搴撳瓨琚嬬爜鏄庣粏")
+    @PreAuthorize("@ss.hasPermission('mes:wm-material-stock:query')")
+    public CommonResult<PageResult<MesWmMaterialStockBagRespVO>> getMaterialStockBagDetails(
+            @Valid MesWmMaterialStockBagPageReqVO pageReqVO) {
+        PageResult<MesWmMaterialStockBagRespVO> result = new PageResult<>();
+        PageResult<cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstockbag.MesWmMaterialStockBagDO> page =
+                materialStockBagMapper.selectPage(pageReqVO);
+        return success(new PageResult<>(BeanUtils.toBean(page.getList(), MesWmMaterialStockBagRespVO.class), page.getTotal()));
     }
 
     @PutMapping("/update-frozen")
@@ -179,6 +196,9 @@
                 convertSet(list, MesWmMaterialStockDO::getLocationId));
         Map<Long, MesWmWarehouseAreaDO> areaMap = areaService.getWarehouseAreaMap(
                 convertSet(list, MesWmMaterialStockDO::getAreaId));
+        Map<Long, Long> bagCountMap = materialStockBagMapper.selectListByMaterialStockIds(
+                convertSet(list, MesWmMaterialStockDO::getId)).stream()
+                .collect(Collectors.groupingBy(MesWmMaterialStockBagDO::getMaterialStockId, Collectors.counting()));
         Map<Long, SrmSupplierRespDTO> vendorMap = srmSupplierApi.getSupplierList(
                 convertSet(list, MesWmMaterialStockDO::getVendorId)).getCheckedData().stream()
                 .collect(Collectors.toMap(SrmSupplierRespDTO::getId, s -> s));
@@ -197,6 +217,8 @@
                     area -> vo.setAreaName(area.getName()));
             MapUtils.findAndThen(vendorMap, vo.getVendorId(),
                     vendor -> vo.setVendorName(vendor.getName()));
+            vo.setBagCount(bagCountMap.getOrDefault(vo.getId(), 0L));
+            vo.setBagDetailSupported(bagCountMap.containsKey(vo.getId()));
             // 璁$畻鍙敤閲� = 鍦ㄥ簱鏁伴噺 - 鍐荤粨鏁伴噺 - 鍗犵敤閲�
             BigDecimal quantity = vo.getQuantity() != null ? vo.getQuantity() : BigDecimal.ZERO;
             BigDecimal frozenQty = vo.getFrozenQuantity() != null ? vo.getFrozenQuantity() : BigDecimal.ZERO;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockBagPageReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockBagPageReqVO.java
new file mode 100644
index 0000000..7b6c868
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockBagPageReqVO.java
@@ -0,0 +1,18 @@
+package cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Schema(description = "绠$悊鍚庡彴 - 搴撳瓨琚嬬爜鏄庣粏鍒嗛〉 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class MesWmMaterialStockBagPageReqVO extends PageParam {
+
+    @Schema(description = "搴撳瓨姹囨�昏褰旾D", requiredMode = Schema.RequiredMode.REQUIRED)
+    private Long materialStockId;
+
+    @Schema(description = "琚嬬爜")
+    private String bagCode;
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockBagRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockBagRespVO.java
new file mode 100644
index 0000000..e1b8041
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockBagRespVO.java
@@ -0,0 +1,29 @@
+package cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo;
+
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstockbag.MesWmMaterialStockBagDO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Schema(description = "绠$悊鍚庡彴 - 搴撳瓨琚嬬爜鏄庣粏 Response VO")
+@Data
+public class MesWmMaterialStockBagRespVO {
+    private Long id;
+    private Long materialStockId;
+    private Long bagCodeId;
+    private String bagCode;
+    private Long itemId;
+    private Long batchId;
+    private String batchCode;
+    private Long warehouseId;
+    private Long locationId;
+    private Long areaId;
+    private Integer stockState;
+    private Integer status;
+    private BigDecimal quantity;
+    private Boolean frozen;
+    private LocalDateTime inboundTime;
+    private LocalDateTime outboundTime;
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockListReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockListReqVO.java
index 8be7fce..cc8d0b6 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockListReqVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockListReqVO.java
@@ -12,6 +12,9 @@
     @Schema(description = "浠撳簱缂栧彿")
     private Long warehouseId;
 
+    @Schema(description = "浠撳簱绫诲瀷锛�10鏆傚瓨搴擄紝20鍚堟牸搴擄紙鎸夌被鍨嬭繃婊や粨搴擄紝涓� warehouseId 浜岄�変竴锛�", example = "20")
+    private Integer warehouseType;
+
     @Schema(description = "搴撳尯缂栧彿")
     private Long locationId;
 
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockPageReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockPageReqVO.java
index 6549016..c4b9862 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockPageReqVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/vo/MesWmMaterialStockPageReqVO.java
@@ -39,6 +39,9 @@
     @Schema(description = "浠撳簱缂栧彿", example = "1")
     private Long warehouseId;
 
+    @Schema(description = "浠撳簱绫诲瀷锛�10鏆傚瓨搴擄紝20鍚堟牸搴擄紙鎸夌被鍨嬭繃婊や粨搴擄紝涓� warehouseId 浜岄�変竴锛�", example = "20")
+    private Integer warehouseType;
+
     @Schema(description = "搴撳尯缂栧彿", example = "1")
     private Long locationId;
 
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 5cec30c..c3d901e 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
@@ -46,6 +46,12 @@
     @ExcelProperty("鎵规鍙�")
     private String batchCode;
 
+    @Schema(description = "褰撳墠琚嬬爜鏁伴噺", example = "10")
+    private Long bagCount;
+
+    @Schema(description = "鏄惁瀛樺湪琚嬬爜鏄庣粏", example = "true")
+    private Boolean bagDetailSupported;
+
     @Schema(description = "浠撳簱缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
     private Long warehouseId;
 
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 a3f44a6..885e7fd 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
@@ -42,10 +42,6 @@
     @ExcelProperty("鍑哄簱鎿嶄綔浜�")
     private String outboundUserName;
 
-    @Schema(description = "閿�鍞鍗曞彿", example = "SO2026030001")
-    @ExcelProperty("閿�鍞鍗曞彿")
-    private String salesOrderCode;
-
     @Schema(description = "鍑哄簱鏃ユ湡")
     @ExcelProperty("鍑哄簱鏃ユ湡")
     private LocalDateTime salesDate;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productsales/vo/MesWmProductSalesSaveReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productsales/vo/MesWmProductSalesSaveReqVO.java
index 1c94e13..996c873 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productsales/vo/MesWmProductSalesSaveReqVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productsales/vo/MesWmProductSalesSaveReqVO.java
@@ -26,9 +26,6 @@
     @NotNull(message = "瀹㈡埛涓嶈兘涓虹┖")
     private Long clientId;
 
-    @Schema(description = "閿�鍞鍗曞彿", example = "SO2026030001")
-    private String salesOrderCode;
-
     @Schema(description = "鍑哄簱鏃ユ湡", requiredMode = Schema.RequiredMode.REQUIRED)
     @NotNull(message = "鍑哄簱鏃ユ湡涓嶈兘涓虹┖")
     private LocalDateTime salesDate;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnsales/vo/MesWmReturnSalesPageReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnsales/vo/MesWmReturnSalesPageReqVO.java
index 1008ffc..f581a93 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnsales/vo/MesWmReturnSalesPageReqVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnsales/vo/MesWmReturnSalesPageReqVO.java
@@ -26,12 +26,6 @@
     @Schema(description = "閫�璐у崟鍚嶇О", example = "閿�鍞��璐�")
     private String name;
 
-    @Schema(description = "閿�鍞鍗� ID", example = "1")
-    private Long saleOrderId;
-
-    @Schema(description = "閿�鍞鍗曠紪鍙�", example = "SO202603010001")
-    private String salesOrderCode;
-
     @Schema(description = "瀹㈡埛 ID", example = "1")
     private Long clientId;
 
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 8d50c3c..dbd8d3f 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
@@ -24,12 +24,6 @@
     @Schema(description = "閫�璐у崟鍚嶇О", requiredMode = Schema.RequiredMode.REQUIRED, example = "閿�鍞��璐�")
     private String name;
 
-    @Schema(description = "閿�鍞鍗� ID", example = "1")
-    private Long saleOrderId;
-
-    @Schema(description = "閿�鍞鍗曠紪鍙�", example = "SO202603010001")
-    private String salesOrderCode;
-
     @Schema(description = "瀹㈡埛 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
     private Long clientId;
 
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnsales/vo/MesWmReturnSalesSaveReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnsales/vo/MesWmReturnSalesSaveReqVO.java
index b77d34f..a267c62 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnsales/vo/MesWmReturnSalesSaveReqVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnsales/vo/MesWmReturnSalesSaveReqVO.java
@@ -27,12 +27,6 @@
     @NotBlank(message = "閫�璐у崟鍚嶇О涓嶈兘涓虹┖")
     private String name;
 
-    @Schema(description = "閿�鍞鍗� ID", example = "1")
-    private Long saleOrderId;
-
-    @Schema(description = "閿�鍞鍗曠紪鍙�", example = "SO202603010001")
-    private String salesOrderCode;
-
     @Schema(description = "瀹㈡埛 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
     @NotNull(message = "瀹㈡埛 ID 涓嶈兘涓虹┖")
     private Long clientId;
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 2bf826e..17e2662 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
@@ -111,14 +111,6 @@
         return success(true);
     }
 
-    @PostMapping("/generate-from-sale-order")
-    @Operation(summary = "浠庨攢鍞鍗曠敓鎴愬彂璐ч�氱煡鍗�")
-    @PreAuthorize("@ss.hasPermission('mes:wm-sales-notice:create')")
-    public CommonResult<Long> generateFromSaleOrder(@RequestBody Map<String, Long> params) {
-        Long saleOrderId = params.get("saleOrderId");
-        return success(salesNoticeService.generateSalesNoticeFromSaleOrder(saleOrderId));
-    }
-
     @PostMapping("/generate-product-sales")
     @Operation(summary = "浠庡彂璐ч�氱煡鍗曠敓鎴愰攢鍞嚭搴撳崟")
     @Parameter(name = "id", description = "鍙戣揣閫氱煡鍗曠紪鍙�", required = true)
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/salesnotice/vo/MesWmSalesNoticePageReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/salesnotice/vo/MesWmSalesNoticePageReqVO.java
index f3ce710..80d4881 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/salesnotice/vo/MesWmSalesNoticePageReqVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/salesnotice/vo/MesWmSalesNoticePageReqVO.java
@@ -18,9 +18,6 @@
     @Schema(description = "閫氱煡鍗曞悕绉�", example = "娴嬭瘯鍙戣揣閫氱煡")
     private String name;
 
-    @Schema(description = "閿�鍞鍗曠紪鍙�", example = "SO202603010001")
-    private String saleOrderCode;
-
     @Schema(description = "瀹㈡埛缂栧彿", example = "1")
     private Long clientId;
 
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 ad39bbd..53445d2 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
@@ -31,13 +31,6 @@
     @ExcelProperty("瀹㈡埛鍚嶇О")
     private String clientName;
 
-    @Schema(description = "閿�鍞鍗旾D", example = "1")
-    private Long saleOrderId;
-
-    @Schema(description = "閿�鍞鍗曞彿", example = "SO202603010001")
-    @ExcelProperty("閿�鍞鍗曞彿")
-    private String saleOrderCode;
-
     @Schema(description = "鍙戣揣鏃ユ湡", requiredMode = Schema.RequiredMode.REQUIRED)
     @ExcelProperty("鍙戣揣鏃ユ湡")
     private LocalDateTime salesDate;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/salesnotice/vo/MesWmSalesNoticeSaveReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/salesnotice/vo/MesWmSalesNoticeSaveReqVO.java
index 4ffa6e2..444d3d9 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/salesnotice/vo/MesWmSalesNoticeSaveReqVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/salesnotice/vo/MesWmSalesNoticeSaveReqVO.java
@@ -26,12 +26,6 @@
     @NotNull(message = "瀹㈡埛缂栧彿涓嶈兘涓虹┖")
     private Long clientId;
 
-    @Schema(description = "閿�鍞鍗旾D", example = "1")
-    private Long saleOrderId;
-
-    @Schema(description = "閿�鍞鍗曞彿", example = "SO202603010001")
-    private String saleOrderCode;
-
     @Schema(description = "鍙戣揣鏃ユ湡", requiredMode = Schema.RequiredMode.REQUIRED)
     @NotNull(message = "鍙戣揣鏃ユ湡涓嶈兘涓虹┖")
     private LocalDateTime salesDate;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/salesnotice/vo/line/MesWmSalesNoticeLineRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/salesnotice/vo/line/MesWmSalesNoticeLineRespVO.java
index 93201b1..3a3f837 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/salesnotice/vo/line/MesWmSalesNoticeLineRespVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/salesnotice/vo/line/MesWmSalesNoticeLineRespVO.java
@@ -16,9 +16,6 @@
     @Schema(description = "鍙戣揣閫氱煡鍗曠紪鍙�", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
     private Long noticeId;
 
-    @Schema(description = "閿�鍞鍗曟槑缁咺D", example = "1")
-    private Long saleOrderItemId;
-
     @Schema(description = "鐗╂枡缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
     private Long itemId;
 
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/salesnotice/vo/line/MesWmSalesNoticeLineSaveReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/salesnotice/vo/line/MesWmSalesNoticeLineSaveReqVO.java
index 35af7ca..d6ba341 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/salesnotice/vo/line/MesWmSalesNoticeLineSaveReqVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/salesnotice/vo/line/MesWmSalesNoticeLineSaveReqVO.java
@@ -18,9 +18,6 @@
     @NotNull(message = "鍙戣揣閫氱煡鍗曠紪鍙蜂笉鑳戒负绌�")
     private Long noticeId;
 
-    @Schema(description = "閿�鍞鍗曟槑缁咺D", example = "1")
-    private Long saleOrderItemId;
-
     @Schema(description = "鐗╂枡缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
     @NotNull(message = "鐗╂枡缂栧彿涓嶈兘涓虹┖")
     private Long itemId;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/transaction/vo/MesWmTransactionPageReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/transaction/vo/MesWmTransactionPageReqVO.java
index 0cabd59..20b4b0f 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/transaction/vo/MesWmTransactionPageReqVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/transaction/vo/MesWmTransactionPageReqVO.java
@@ -26,6 +26,9 @@
     @Schema(description = "鎵规鍙�", example = "BATCH001")
     private String batchCode;
 
+    @Schema(description = "琚嬬爜", example = "BG202608180001")
+    private String bagCode;
+
     @Schema(description = "浠撳簱ID", example = "1")
     private Long warehouseId;
 
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/transaction/vo/MesWmTransactionRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/transaction/vo/MesWmTransactionRespVO.java
index e40218c..3ec6568 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/transaction/vo/MesWmTransactionRespVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/transaction/vo/MesWmTransactionRespVO.java
@@ -51,6 +51,12 @@
     @Schema(description = "鎵规鍙�", example = "BATCH001")
     private String batchCode;
 
+    @Schema(description = "琚嬬爜ID", example = "1")
+    private Long bagCodeId;
+
+    @Schema(description = "琚嬬爜", example = "BG202608180001")
+    private String bagCode;
+
     // ========== 搴撳瓨浣嶇疆 ==========
 
     @Schema(description = "浠撳簱ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/warehouse/vo/MesWmWarehousePageReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/warehouse/vo/MesWmWarehousePageReqVO.java
index e6861e3..1b2e826 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/warehouse/vo/MesWmWarehousePageReqVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/warehouse/vo/MesWmWarehousePageReqVO.java
@@ -21,4 +21,7 @@
     @Schema(description = "鏄惁鍐荤粨", example = "false")
     private Boolean frozen;
 
+    @Schema(description = "浠撳簱绫诲瀷锛�10鏆傚瓨搴� 20鍚堟牸搴�", example = "20")
+    private Integer type;
+
 }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/warehouse/vo/MesWmWarehouseRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/warehouse/vo/MesWmWarehouseRespVO.java
index 6c85e3b..10edec2 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/warehouse/vo/MesWmWarehouseRespVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/warehouse/vo/MesWmWarehouseRespVO.java
@@ -40,6 +40,10 @@
     @ExcelProperty("鏄惁鍐荤粨")
     private Boolean frozen;
 
+    @Schema(description = "浠撳簱绫诲瀷锛�10鏆傚瓨搴� 20鍚堟牸搴�", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
+    @ExcelProperty("浠撳簱绫诲瀷")
+    private Integer type;
+
     @Schema(description = "澶囨敞", example = "澶囨敞")
     @ExcelProperty("澶囨敞")
     private String remark;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/warehouse/vo/MesWmWarehouseSaveReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/warehouse/vo/MesWmWarehouseSaveReqVO.java
index a05c861..ddfe51e 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/warehouse/vo/MesWmWarehouseSaveReqVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/warehouse/vo/MesWmWarehouseSaveReqVO.java
@@ -35,6 +35,10 @@
     @NotNull(message = "鏄惁鍐荤粨涓嶈兘涓虹┖")
     private Boolean frozen;
 
+    @Schema(description = "浠撳簱绫诲瀷锛�10鏆傚瓨搴� 20鍚堟牸搴�", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
+    @NotNull(message = "浠撳簱绫诲瀷涓嶈兘涓虹┖")
+    private Integer type;
+
     @Schema(description = "澶囨敞", example = "澶囨敞")
     private String remark;
 
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/pub/trace/vo/MesProTracePublicRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/pub/trace/vo/MesProTracePublicRespVO.java
index 6f5d570..bf9329f 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/pub/trace/vo/MesProTracePublicRespVO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/pub/trace/vo/MesProTracePublicRespVO.java
@@ -119,6 +119,44 @@
     @Schema(description = "鍔犲伐娴佽浆锛堝彲閫夛級")
     private List<MesWmBatchProcessTraceRespVO> processTraces;
 
+    @Schema(description = "杩芥函鏃堕棿绾�")
+    private List<TimelineVO> timeline;
+
+    @Schema(description = "杩芥函鍐崇瓥鎽樿")
+    private DecisionVO decision;
+
+    @Schema(description = "杩芥函鏃堕棿绾夸簨浠�")
+    @Data
+    public static class TimelineVO {
+        @Schema(description = "闃舵")
+        private String stage;
+        @Schema(description = "浜嬩欢绫诲瀷")
+        private String eventType;
+        @Schema(description = "浜嬩欢鍚嶇О")
+        private String eventName;
+        @Schema(description = "浜嬩欢鏃堕棿")
+        private LocalDateTime eventTime;
+        @Schema(description = "鏁伴噺")
+        private BigDecimal quantity;
+        @Schema(description = "鐘舵��")
+        private String status;
+    }
+
+    @Schema(description = "杩芥函鍐崇瓥鎽樿")
+    @Data
+    public static class DecisionVO {
+        @Schema(description = "璐ㄩ噺鏄惁瀛樺湪椋庨櫓")
+        private Boolean qualityRisk;
+        @Schema(description = "搴撳瓨鍏ュ簱鏁伴噺")
+        private BigDecimal inboundQuantity;
+        @Schema(description = "閿�鍞幓鍚戞暟閲�")
+        private Integer salesTargetCount;
+        @Schema(description = "娑堣垂鑰呮壂鐮佹鏁�")
+        private Long consumerScanCount;
+        @Schema(description = "椋庨櫓绛夌骇")
+        private String riskLevel;
+    }
+
     @Schema(description = "鐢熶骇浜哄憳")
     @Data
     public static class WorkerVO {
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/bagcode/MesProBagCodeDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/bagcode/MesProBagCodeDO.java
index 50e4df7..7da62ad 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/bagcode/MesProBagCodeDO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/bagcode/MesProBagCodeDO.java
@@ -29,6 +29,7 @@
     private Long palletId;
     private String palletCode;
     private Integer status;
+    private Integer qcStatus;
     private LocalDateTime printTime;
     private LocalDateTime cancelTime;
     private String cancelReason;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/pallet/MesProPalletDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/pallet/MesProPalletDO.java
index 43024f1..dfeae62 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/pallet/MesProPalletDO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/pallet/MesProPalletDO.java
@@ -62,6 +62,14 @@
      */
     private Integer bagCount;
     /**
+     * 鏈�杩戝叆搴撴壂鐮佷簨浠剁紪鍙凤紙鏁存墭鎵爜鍏ュ簱鍚庡洖鍐欙紝鐢ㄤ簬闃查噸澶嶅叆搴擄級
+     */
+    private Long inboundEventId;
+    /**
+     * 鏈�杩戝叆搴撴椂闂�
+     */
+    private java.time.LocalDateTime inboundTime;
+    /**
      * 鏄惁鑷姩鐢熸垚锛堟寜鏈夋晥琚嬬爜绱闃堝�肩敓鎴愶紝瀹归噺鍥哄畾涓洪槇鍊硷級
      */
     private Boolean autoGenerated;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/scan/MesProScanEventDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/scan/MesProScanEventDO.java
index d0be21d..6bf1274 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/scan/MesProScanEventDO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/scan/MesProScanEventDO.java
@@ -14,7 +14,14 @@
 public class MesProScanEventDO extends BaseDO {
     @TableId private Long id;
     private String eventId;
+    /**
+     * 鎵爜鍐呭锛堣鐮� / 鎵樼洏鐮� / 鎵规鐮佺殑鐮佸�硷紝鍚复鏃剁爜锛�
+     */
     private String bagCode;
+    /**
+     * 鎵爜绫诲瀷锛欱AG 琚嬬爜 / PALLET 鎵樼洏鐮� / BATCH 鎵规鐮�
+     */
+    private String scanType;
     private Long bagCodeId;
     private Long batchId;
     private String batchCode;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/qc/seedquality/MesQcSeedQualityDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/qc/seedquality/MesQcSeedQualityDO.java
index 0c79b74..1ca77b7 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/qc/seedquality/MesQcSeedQualityDO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/qc/seedquality/MesQcSeedQualityDO.java
@@ -12,6 +12,8 @@
     @TableId private Long id;
     private String code;
     private Long itemId;
+    /** 鐢熶骇鎵规 ID */
+    private Long batchId;
     private String batchCode;
     private Integer result;
     private String reason;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/batch/MesWmBatchDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/batch/MesWmBatchDO.java
index ac0e186..3f67ca8 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/batch/MesWmBatchDO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/batch/MesWmBatchDO.java
@@ -3,6 +3,7 @@
 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.batch.MesProBatchDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.tm.tool.MesTmToolDO;
@@ -34,6 +35,12 @@
     @TableId
     private Long id;
     /**
+     * 瀵瑰簲鐢熶骇鎵规 ID
+     *
+     * 鍏宠仈 {@link MesProBatchDO#getId()}
+     */
+    private Long proBatchId;
+    /**
      * 鎵规缂栫爜
      */
     private String code;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/materialstockbag/MesWmMaterialStockBagDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/materialstockbag/MesWmMaterialStockBagDO.java
new file mode 100644
index 0000000..fb40f0b
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/materialstockbag/MesWmMaterialStockBagDO.java
@@ -0,0 +1,44 @@
+package cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstockbag;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * MES 搴撳瓨琚嬬爜鏄庣粏 DO
+ */
+@TableName("mes_wm_material_stock_bag")
+@KeySequence("mes_wm_material_stock_bag_seq")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class MesWmMaterialStockBagDO extends BaseDO {
+
+    @TableId
+    private Long id;
+    private Long materialStockId;
+    private Long bagCodeId;
+    private String bagCode;
+    private Long itemId;
+    private Long batchId;
+    private String batchCode;
+    private Long vendorId;
+    private Long warehouseId;
+    private Long locationId;
+    private Long areaId;
+    private Integer stockState;
+    private Integer status;
+    private BigDecimal quantity;
+    private Boolean frozen;
+    private Long lastTransactionId;
+    private LocalDateTime inboundTime;
+    private LocalDateTime outboundTime;
+}
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 d266de7..211f40e 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
@@ -50,14 +50,6 @@
      */
     private String clientName;
     /**
-     * 閿�鍞鍗旾D
-     */
-    private Long saleOrderId;
-    /**
-     * 閿�鍞鍗曞彿
-     */
-    private String salesOrderCode;
-    /**
      * 鍙戣揣閫氱煡鍗� ID
      *
      * 鍏宠仈 {@link MesWmSalesNoticeDO#getId()}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/productsales/MesWmProductSalesLineDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/productsales/MesWmProductSalesLineDO.java
index f76011b..29fd0a7 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/productsales/MesWmProductSalesLineDO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/productsales/MesWmProductSalesLineDO.java
@@ -42,10 +42,6 @@
      */
     private Long salesId;
     /**
-     * 閿�鍞鍗曟槑缁咺D
-     */
-    private Long saleOrderItemId;
-    /**
      * 鍙戣揣閫氱煡鍗曡ID
      *
      * 鍏宠仈 {@link MesWmSalesNoticeLineDO#getId()}
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 aa3bb74..d26f69b 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
@@ -39,14 +39,6 @@
      */
     private String name;
     /**
-     * 閿�鍞鍗� ID
-     */
-    private Long saleOrderId;
-    /**
-     * 閿�鍞鍗曠紪鍙�
-     */
-    private String salesOrderCode;
-    /**
      * 瀹㈡埛 ID
      *
      * 鍏宠仈 CRM 瀹㈡埛 {@link cn.iocoder.yudao.module.crm.api.customer.dto.CrmCustomerRespDTO#getId()}
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 4c69b80..a6d6c2d 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
@@ -42,14 +42,6 @@
      */
     private Long clientId;
     /**
-     * 閿�鍞鍗旾D
-     */
-    private Long saleOrderId;
-    /**
-     * 閿�鍞鍗曞彿
-     */
-    private String saleOrderCode;
-    /**
      * 鍙戣揣鏃ユ湡
      */
     private LocalDateTime salesDate;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/salesnotice/MesWmSalesNoticeLineDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/salesnotice/MesWmSalesNoticeLineDO.java
index 1219bf6..288cc75 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/salesnotice/MesWmSalesNoticeLineDO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/salesnotice/MesWmSalesNoticeLineDO.java
@@ -35,10 +35,6 @@
      */
     private Long noticeId;
     /**
-     * 閿�鍞鍗曟槑缁咺D
-     */
-    private Long saleOrderItemId;
-    /**
      * 鐗╂枡缂栧彿
      *
      * 鍏宠仈 {@link MesMdItemDO#getId()}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/sn/MesWmSnDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/sn/MesWmSnDO.java
index abfce45..8174699 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/sn/MesWmSnDO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/sn/MesWmSnDO.java
@@ -2,6 +2,9 @@
 
 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.bagcode.MesProBagCodeDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.batch.MesProBatchDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.pallet.MesProPalletDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
 import com.baomidou.mybatisplus.annotation.KeySequence;
 import com.baomidou.mybatisplus.annotation.TableId;
@@ -29,6 +32,28 @@
     @TableId
     private Long id;
     /**
+     * 鐢熶骇鎵规 ID
+     *
+     * 鍏宠仈 {@link MesProBatchDO#getId()}
+     */
+    private Long proBatchId;
+    /**
+     * 琚嬬爜 ID
+     *
+     * 鍏宠仈 {@link MesProBagCodeDO#getId()}
+     */
+    private Long bagCodeId;
+    /**
+     * 鎵樼洏 ID
+     *
+     * 鍏宠仈 {@link MesProPalletDO#getId()}
+     */
+    private Long palletId;
+    /**
+     * 閿�鍞嚭搴撳崟 ID
+     */
+    private Long salesId;
+    /**
      * 鎵规 UUID锛堢敤浜庢爣璁板悓涓�鎵规鐢熸垚鐨� SN 鐮侊級
      */
     private String uuid;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/transaction/MesWmTransactionDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/transaction/MesWmTransactionDO.java
index f574a36..b3cce53 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/transaction/MesWmTransactionDO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/transaction/MesWmTransactionDO.java
@@ -107,6 +107,10 @@
      * 鍏宠仈 {@link MesWmBatchDO#getCode()}
      */
     private String batchCode;
+    /** 琚嬬爜ID */
+    private Long bagCodeId;
+    /** 琚嬬爜 */
+    private String bagCode;
 
     // ==================== 搴撳瓨浣嶇疆 ====================
 
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/warehouse/MesWmWarehouseDO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/warehouse/MesWmWarehouseDO.java
index 0c8a9b2..b2f75da 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/warehouse/MesWmWarehouseDO.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/warehouse/MesWmWarehouseDO.java
@@ -62,6 +62,13 @@
      */
     private Boolean frozen;
     /**
+     * 浠撳簱绫诲瀷
+     *
+     * 鍏宠仈 {@link cn.iocoder.yudao.module.mes.enums.wm.MesWmWarehouseTypeEnum}锛�
+     * 10 鏆傚瓨搴擄紙濡傝櫄鎷熺嚎杈逛粨搴擄紝瀛樻斁鎵爜鍏ュ簱寰呮/鏆傚瓨搴撳瓨锛夛紱20 鍚堟牸搴擄紙瀛樻斁璐ㄦ鍚堟牸銆佸彲鎷h揣搴撳瓨锛�
+     */
+    private Integer type;
+    /**
      * 澶囨敞
      */
     private String remark;
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/home/MesBagCodeHomeStatisticsMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/home/MesBagCodeHomeStatisticsMapper.java
index e5a7643..5bb3749 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/home/MesBagCodeHomeStatisticsMapper.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/home/MesBagCodeHomeStatisticsMapper.java
@@ -43,6 +43,23 @@
     /** 缁熻鎵樼洏鐮佹�绘暟锛坢es_pro_pallet锛� */
     Long selectPalletCount();
 
+    // ========== 鍑哄簱娴侀�� / 浠撳偍 / 璐ㄩ噺 ==========
+
+    /** 缁熻鍙戣揣閫氱煡鍗曟暟锛坢es_wm_sales_notice锛� */
+    Long selectSalesNoticeCount();
+
+    /** 缁熻閿�鍞嚭搴撳崟鏁帮紙mes_wm_product_sales锛� */
+    Long selectProductSalesCount();
+
+    /** 缁熻閿�鍞��璐у崟鏁帮紙mes_wm_return_sales锛� */
+    Long selectReturnSalesCount();
+
+    /** 缁熻鏈鐞嗗簱瀛橀璀︽暟锛坢es_wm_stock_warning.status=10锛� */
+    Long selectStockWarningPendingCount();
+
+    /** 缁熻寰呰瘎瀹′笉鍚堟牸鍝佸崟鏁帮紙mes_qc_ncr.status=0锛� */
+    Long selectNcrPendingCount();
+
     // ========== 鐘舵�佸垎甯� ==========
 
     /**
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/bagcode/MesProBagCodeMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/bagcode/MesProBagCodeMapper.java
index 0675ba6..c0ae4ce 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/bagcode/MesProBagCodeMapper.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/bagcode/MesProBagCodeMapper.java
@@ -36,6 +36,30 @@
     int markInbound(@org.apache.ibatis.annotations.Param("id") Long id, @org.apache.ibatis.annotations.Param("eventId") Long eventId,
                     @org.apache.ibatis.annotations.Param("deviceCode") String deviceCode, @org.apache.ibatis.annotations.Param("lineCode") String lineCode);
 
+    /** 鎵规鏁存壒鍏ュ簱鏃舵壒閲忔爣璁板叾涓嬫湭鍏ュ簱琚嬬爜锛堝箓绛夛級锛屼繚鎸佽鐮佷笌搴撳瓨鐘舵�佷竴鑷� */
+    @Update("UPDATE mes_pro_bag_code SET inbound_time = NOW(), inbound_event_id = #{eventId}, inbound_line_code = #{lineCode}, update_time = NOW() "
+            + "WHERE deleted = 0 AND inbound_event_id IS NULL AND batch_id = #{batchId}")
+    int markInboundByBatchId(@Param("batchId") Long batchId, @Param("eventId") Long eventId,
+                             @Param("lineCode") String lineCode);
+
+    /** 鏁存墭鍏ュ簱鏃舵壒閲忔爣璁版墭鐩樹笅鏈叆搴撹鐮侊紙骞傜瓑锛� */
+    @Update("UPDATE mes_pro_bag_code SET inbound_time = NOW(), inbound_event_id = #{eventId}, inbound_line_code = #{lineCode}, update_time = NOW() "
+            + "WHERE deleted = 0 AND inbound_event_id IS NULL AND pallet_id = #{palletId}")
+    int markInboundByPalletId(@Param("palletId") Long palletId, @Param("eventId") Long eventId,
+                              @Param("lineCode") String lineCode);
+
+    /**
+     * 鏇存柊琚嬬爜璐ㄦ鐘舵�侊紙绉嶅瓙璐ㄦ鍚堟牸/涓嶅悎鏍兼椂璁板綍璇ヨ缁撴灉锛�
+     *
+     * @param id       琚嬬爜缂栧彿
+     * @param qcStatus 璐ㄦ鐘舵�侊紙瑙� MesProBagCodeQcStatusEnum锛�
+     */
+    default void updateQcStatus(Long id, Integer qcStatus) {
+        update(null, new LambdaUpdateWrapper<MesProBagCodeDO>()
+                .eq(MesProBagCodeDO::getId, id)
+                .set(MesProBagCodeDO::getQcStatus, qcStatus));
+    }
+
     default List<MesProBagCodeDO> selectListByUuid(String uuid) {
         return selectList(MesProBagCodeDO::getUuid, uuid);
     }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/batch/MesProBatchMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/batch/MesProBatchMapper.java
index 5c3026e..e672278 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/batch/MesProBatchMapper.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/batch/MesProBatchMapper.java
@@ -40,6 +40,12 @@
     int markInbound(@org.apache.ibatis.annotations.Param("id") Long id, @org.apache.ibatis.annotations.Param("eventId") Long eventId,
                     @org.apache.ibatis.annotations.Param("deviceCode") String deviceCode, @org.apache.ibatis.annotations.Param("lineCode") String lineCode);
 
+    /** 鎵规鏁存壒鎵爜鍏ュ簱鍥炲啓锛氬叆搴撹鏁扮洿鎺ョ疆涓烘壒娆¤鏁帮紝骞傜瓑锛堝凡鏈夊叆搴撴爣璁扮殑涓嶅啀鏇存柊锛� */
+    @org.apache.ibatis.annotations.Update("UPDATE mes_pro_batch SET inbound_time = NOW(), inbound_event_id = #{eventId}, inbound_line_code = #{lineCode}, inbound_bag_quantity = #{bagQuantity}, update_time = NOW() "
+            + "WHERE id = #{id} AND deleted = 0 AND inbound_event_id IS NULL")
+    int markBatchInbound(@org.apache.ibatis.annotations.Param("id") Long id, @org.apache.ibatis.annotations.Param("eventId") Long eventId,
+                         @org.apache.ibatis.annotations.Param("lineCode") String lineCode, @org.apache.ibatis.annotations.Param("bagQuantity") Integer bagQuantity);
+
     default PageResult<MesProBatchDO> selectPage(MesProBatchPageReqVO reqVO) {
         return selectPage(reqVO, new LambdaQueryWrapperX<MesProBatchDO>()
                 .likeIfPresent(MesProBatchDO::getCode, reqVO.getCode())
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/pallet/MesProPalletMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/pallet/MesProPalletMapper.java
index cbbda3f..ab03c10 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/pallet/MesProPalletMapper.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/pallet/MesProPalletMapper.java
@@ -64,6 +64,11 @@
     void markOutbound(@Param("id") Long id, @Param("salesId") Long salesId,
                       @Param("outboundTime") java.time.LocalDateTime outboundTime);
 
+    /** 鏁存墭鎵爜鍏ュ簱鍥炲啓锛氬箓绛夛紙宸叉湁鍏ュ簱鏍囪鐨勪笉鍐嶆洿鏂帮級锛岀敤浜庨槻閲嶅鍏ュ簱 */
+    @org.apache.ibatis.annotations.Update("UPDATE mes_pro_pallet SET inbound_event_id = #{eventId}, inbound_time = NOW(), update_time = NOW() "
+            + "WHERE id = #{id} AND deleted = 0 AND inbound_event_id IS NULL")
+    int markInbound(@Param("id") Long id, @Param("eventId") Long eventId);
+
     @org.apache.ibatis.annotations.Update("UPDATE mes_pro_pallet SET sales_id = NULL, outbound_time = NULL, update_time = NOW() "
             + "WHERE id = #{id} AND deleted = 0")
     void clearOutbound(@Param("id") Long id);
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/seedquality/MesQcSeedQualityMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/seedquality/MesQcSeedQualityMapper.java
index 314133b..98e3ea2 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/seedquality/MesQcSeedQualityMapper.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/seedquality/MesQcSeedQualityMapper.java
@@ -1,6 +1,8 @@
 package cn.iocoder.yudao.module.mes.dal.mysql.qc.seedquality;
 
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedQualityPageReqVO;
 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.seedquality.MesQcSeedQualityDO;
 import org.apache.ibatis.annotations.Mapper;
@@ -9,6 +11,15 @@
 
 @Mapper
 public interface MesQcSeedQualityMapper extends BaseMapperX<MesQcSeedQualityDO> {
+
+    default PageResult<MesQcSeedQualityDO> selectPage(MesQcSeedQualityPageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<MesQcSeedQualityDO>()
+                .likeIfPresent(MesQcSeedQualityDO::getCode, reqVO.getCode())
+                .likeIfPresent(MesQcSeedQualityDO::getBatchCode, reqVO.getBatchCode())
+                .eqIfPresent(MesQcSeedQualityDO::getResult, reqVO.getResult())
+                .orderByDesc(MesQcSeedQualityDO::getInspectedAt)
+                .orderByDesc(MesQcSeedQualityDO::getId));
+    }
     default MesQcSeedQualityDO selectByCode(String code) {
         return selectOne(new LambdaQueryWrapperX<MesQcSeedQualityDO>().eq(MesQcSeedQualityDO::getCode, code));
     }
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 cc2413d..d3a7a25 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
@@ -6,6 +6,7 @@
 import cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo.MesWmMaterialStockListReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo.MesWmMaterialStockPageReqVO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO;
+import cn.iocoder.yudao.module.mes.enums.wm.MesWmStockStateEnum;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import org.apache.ibatis.annotations.Mapper;
 
@@ -22,13 +23,15 @@
     default PageResult<MesWmMaterialStockDO> selectPage(MesWmMaterialStockPageReqVO reqVO,
                                                          Collection<Long> itemTypeIds,
                                                          Collection<Long> itemIds,
-                                                         Long virtualWarehouseId) {
+                                                         Long virtualWarehouseId,
+                                                         Collection<Long> warehouseIds) {
         LambdaQueryWrapperX<MesWmMaterialStockDO> wrapper = new LambdaQueryWrapperX<MesWmMaterialStockDO>()
                 .inIfPresent(MesWmMaterialStockDO::getItemTypeId, itemTypeIds)
                 .inIfPresent(MesWmMaterialStockDO::getItemId, itemIds)
                 .likeIfPresent(MesWmMaterialStockDO::getBatchCode, reqVO.getBatchCode())
                 .eqIfPresent(MesWmMaterialStockDO::getBatchId, reqVO.getBatchId())
                 .eqIfPresent(MesWmMaterialStockDO::getWarehouseId, reqVO.getWarehouseId())
+                .inIfPresent(MesWmMaterialStockDO::getWarehouseId, warehouseIds)
                 .eqIfPresent(MesWmMaterialStockDO::getLocationId, reqVO.getLocationId())
                 .eqIfPresent(MesWmMaterialStockDO::getAreaId, reqVO.getAreaId())
                 .eqIfPresent(MesWmMaterialStockDO::getVendorId, reqVO.getVendorId())
@@ -149,9 +152,10 @@
     }
 
     /**
-     * 鎸夌粍鍚堥敭鏌ヨ搴撳瓨璁板綍锛堝拷鐣ヤ緵搴斿晢缁村害锛�
+     * 鎸夌粍鍚堥敭鏌ヨ搴撳瓨璁板綍锛堝拷鐣ヤ緵搴斿晢缁村害锛屼粎鍚堟牸搴擄級
      *
-     * 鐢ㄤ簬绉诲簱鍦烘櫙锛氱Щ搴撴椂灏嗗簱瀛樺悎骞跺埌鐩爣搴撲綅宸叉湁搴撳瓨锛屼笉鍖哄垎渚涘簲鍟�
+     * 鐢ㄤ簬绉诲簱鍦烘櫙锛氱Щ搴撴椂灏嗗悎鏍煎簱瀛樺悎骞跺埌鐩爣搴撲綅宸叉湁鍚堟牸搴撳瓨锛屼笉鍖哄垎渚涘簲鍟�
+     * 鍙尮閰嶅悎鏍煎簱(20)锛岄伩鍏嶅皢绉诲叆鏁伴噺鍚堝苟鍒板悓鎵规鐨勬殏瀛樺簱璁板綍
      */
     default MesWmMaterialStockDO selectByCompositeKeyIgnoreVendor(Long itemId, Long warehouseId, Long locationId,
                                                                     Long areaId, Long batchId) {
@@ -159,7 +163,8 @@
                 .eq(MesWmMaterialStockDO::getItemId, itemId)
                 .eq(MesWmMaterialStockDO::getWarehouseId, warehouseId)
                 .eq(MesWmMaterialStockDO::getLocationId, locationId)
-                .eq(MesWmMaterialStockDO::getAreaId, areaId);
+                .eq(MesWmMaterialStockDO::getAreaId, areaId)
+                .eq(MesWmMaterialStockDO::getStockState, MesWmStockStateEnum.QUALIFIED.getState());
         // batchId=null 鏃剁簿纭尮閰� is null
         if (batchId != null) {
             wrapper.eq(MesWmMaterialStockDO::getBatchId, batchId);
@@ -170,9 +175,41 @@
         return selectOne(wrapper);
     }
 
+    /**
+     * 鎸夌粍鍚堥敭鏌ヨ鍚堟牸搴撳簱瀛樿褰曪紙鐢ㄤ簬鍑哄簱浜嬪姟鍖归厤锛�
+     *
+     * 鍚屾壒娆″彲鑳藉悓鏃跺瓨鍦ㄦ殏瀛樺簱(10)涓庡悎鏍煎簱(20)涓ゆ潯璁板綍锛堢瀛愯川妫�鎸夎鎷嗘暟閲忥級锛�
+     * 鍑哄簱蹇呴』鍙粠鍚堟牸搴撳尮閰嶏紝閬垮厤鎵e埌鏈川妫�鐨勬殏瀛樺簱瀛樸��
+     */
+    default MesWmMaterialStockDO selectQualifiedByCompositeKey(Long itemId, Long warehouseId, Long locationId,
+                                                                 Long areaId, Long batchId, Long vendorId) {
+        LambdaQueryWrapperX<MesWmMaterialStockDO> wrapper = new LambdaQueryWrapperX<MesWmMaterialStockDO>()
+                .eq(MesWmMaterialStockDO::getItemId, itemId)
+                .eq(MesWmMaterialStockDO::getWarehouseId, warehouseId)
+                .eq(MesWmMaterialStockDO::getLocationId, locationId)
+                .eq(MesWmMaterialStockDO::getAreaId, areaId)
+                .eq(MesWmMaterialStockDO::getStockState, MesWmStockStateEnum.QUALIFIED.getState());
+        // batchId=null 鏃剁簿纭尮閰� is null
+        if (batchId != null) {
+            wrapper.eq(MesWmMaterialStockDO::getBatchId, batchId);
+        } else {
+            wrapper.isNull(MesWmMaterialStockDO::getBatchId);
+        }
+        if (vendorId != null) {
+            wrapper.eq(MesWmMaterialStockDO::getVendorId, vendorId);
+        }
+        wrapper.last("ORDER BY quantity DESC LIMIT 1");
+        return selectOne(wrapper);
+    }
+
     default List<MesWmMaterialStockDO> selectList(MesWmMaterialStockListReqVO reqVO) {
+        return selectList(reqVO, null);
+    }
+
+    default List<MesWmMaterialStockDO> selectList(MesWmMaterialStockListReqVO reqVO, Collection<Long> warehouseIds) {
         return selectList(new LambdaQueryWrapperX<MesWmMaterialStockDO>()
                 .eqIfPresent(MesWmMaterialStockDO::getWarehouseId, reqVO.getWarehouseId())
+                .inIfPresent(MesWmMaterialStockDO::getWarehouseId, warehouseIds)
                 .eqIfPresent(MesWmMaterialStockDO::getLocationId, reqVO.getLocationId())
                 .eqIfPresent(MesWmMaterialStockDO::getAreaId, reqVO.getAreaId())
                 .eqIfPresent(MesWmMaterialStockDO::getItemId, reqVO.getItemId())
@@ -197,6 +234,36 @@
                 .orderByAsc(MesWmMaterialStockDO::getReceiptTime));
     }
 
+    /**
+     * 鎸夌墿鏂�/鎵规鏌ヨ鍚堟牸搴撳簱瀛樿褰曪紙浠� stock_state=20锛屼笖浠呴檺鍚堟牸浠撳簱锛�
+     *
+     * <p>鍚屾壒娆″彲鑳藉悓鏃跺瓨鍦ㄦ殏瀛樺簱(10)涓庡悎鏍煎簱(20)涓ゆ潯璁板綍锛堢瀛愯川妫�鎸夎鎷嗘暟閲忥級锛�
+     * 閿�鍞嚭搴撻�夊簱瀛樺彧鑳戒粠鍚堟牸搴撹褰曚腑閫夛紝鏉滅粷閫変腑鏈川妫�鐨勬殏瀛樺簱瀛橈紱
+     * 鍚屾椂闄愬畾鍚堟牸浠撳簱锛坱ype=20锛夛紝閬垮厤浠庢殏瀛樹粨搴撲腑璇�夊悎鏍兼�佸簱瀛樸��</p>
+     */
+    default List<MesWmMaterialStockDO> selectQualifiedListByItemAndBatch(Long itemId, Long batchId,
+                                                                          Collection<Long> qualifiedWarehouseIds) {
+        return selectList(new LambdaQueryWrapperX<MesWmMaterialStockDO>()
+                .eq(MesWmMaterialStockDO::getItemId, itemId)
+                .eqIfPresent(MesWmMaterialStockDO::getBatchId, batchId)
+                .eq(MesWmMaterialStockDO::getStockState, MesWmStockStateEnum.QUALIFIED.getState())
+                .in(MesWmMaterialStockDO::getWarehouseId, qualifiedWarehouseIds)
+                .gt(MesWmMaterialStockDO::getQuantity, BigDecimal.ZERO)
+                .orderByAsc(MesWmMaterialStockDO::getReceiptTime));
+    }
+
+    /**
+     * 鎸変粨搴撴壒娆℃煡璇㈠叏閮ㄥ簱瀛樿褰曪紙鍚殏瀛樺簱涓庡悎鏍煎簱锛宷uantity>0锛�
+     *
+     * <p>鐢ㄤ簬绉嶅瓙璐ㄦ鎸夎鐮佹祦杞細瀹氫綅璇ユ壒娆℃殏瀛樺簱璁板綍鎵e噺 1 琚嬶紝浠ュ強瀹氫綅/鏂板缓鍚堟牸搴撹褰曠疮鍔� 1 琚嬨��</p>
+     */
+    default List<MesWmMaterialStockDO> selectStockListByBatchId(Long batchId) {
+        return selectList(new LambdaQueryWrapperX<MesWmMaterialStockDO>()
+                .eq(MesWmMaterialStockDO::getBatchId, batchId)
+                .gt(MesWmMaterialStockDO::getQuantity, BigDecimal.ZERO)
+                .orderByAsc(MesWmMaterialStockDO::getReceiptTime));
+    }
+
     default List<MesWmMaterialStockDO> selectListByItemIds(Collection<Long> itemIds) {
         if (itemIds == null || itemIds.isEmpty()) {
             return java.util.Collections.emptyList();
@@ -207,18 +274,16 @@
     }
 
     /**
-     * 鎸夋壒娆″彿鏇存柊搴撳瓨鐘舵�侊紙鐢ㄤ簬绉嶅瓙璐ㄦ鍚堟牸鍚庯紝灏嗘殏瀛樺簱鏁存壒杞负鍚堟牸搴擄級
+     * 缁熻鎵规鍦ㄦ殏瀛樺簱涓殑搴撳瓨璁板綍鏁帮紙鐢ㄤ簬绉嶅瓙璐ㄦ鍓嶆牎楠岃鐮�/鎵规鏄惁澶勪簬寰呰川妫�鐘舵�侊級
      *
-     * @param batchId   浠撳簱鎵规ID
-     * @param toState   鐩爣搴撳瓨鐘舵��
-     * @param fromState 鍘熷簱瀛樼姸鎬侊紙闄愬畾鍙洿鏂拌鐘舵�佺殑璁板綍锛�
-     * @return 褰卞搷琛屾暟
+     * @param batchId 浠撳簱鎵规ID
+     * @return 鏆傚瓨搴撳簱瀛樿褰曟暟
      */
-    default int updateStockStateByBatchId(Long batchId, Integer toState, Integer fromState) {
-        return update(new MesWmMaterialStockDO().setStockState(toState),
-                new LambdaUpdateWrapper<MesWmMaterialStockDO>()
-                        .eq(MesWmMaterialStockDO::getBatchId, batchId)
-                        .eq(MesWmMaterialStockDO::getStockState, fromState));
+    default Long selectTempStockCountByBatchId(Long batchId) {
+        return selectCount(new LambdaQueryWrapperX<MesWmMaterialStockDO>()
+                .eq(MesWmMaterialStockDO::getBatchId, batchId)
+                .eq(MesWmMaterialStockDO::getStockState, MesWmStockStateEnum.TEMP.getState())
+                .gt(MesWmMaterialStockDO::getQuantity, BigDecimal.ZERO));
     }
 
 }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/materialstockbag/MesWmMaterialStockBagMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/materialstockbag/MesWmMaterialStockBagMapper.java
new file mode 100644
index 0000000..d1727b5
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/materialstockbag/MesWmMaterialStockBagMapper.java
@@ -0,0 +1,82 @@
+package cn.iocoder.yudao.module.mes.dal.mysql.wm.materialstockbag;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo.MesWmMaterialStockBagPageReqVO;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstockbag.MesWmMaterialStockBagDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.Collection;
+import java.util.List;
+
+@Mapper
+public interface MesWmMaterialStockBagMapper extends BaseMapperX<MesWmMaterialStockBagDO> {
+
+    default PageResult<MesWmMaterialStockBagDO> selectPage(MesWmMaterialStockBagPageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<MesWmMaterialStockBagDO>()
+                .eq(MesWmMaterialStockBagDO::getMaterialStockId, reqVO.getMaterialStockId())
+                .likeIfPresent(MesWmMaterialStockBagDO::getBagCode, reqVO.getBagCode())
+                .eq(MesWmMaterialStockBagDO::getStatus, 1)
+                .orderByAsc(MesWmMaterialStockBagDO::getInboundTime));
+    }
+
+    default MesWmMaterialStockBagDO selectByBagCode(String bagCode) {
+        return selectOne(new LambdaQueryWrapperX<MesWmMaterialStockBagDO>()
+                .eq(MesWmMaterialStockBagDO::getBagCode, bagCode)
+                .orderByDesc(MesWmMaterialStockBagDO::getId)
+                .last("LIMIT 1"));
+    }
+
+    default MesWmMaterialStockBagDO selectByBagCodeId(Long bagCodeId) {
+        return selectOne(new LambdaQueryWrapperX<MesWmMaterialStockBagDO>()
+                .eq(MesWmMaterialStockBagDO::getBagCodeId, bagCodeId)
+                .orderByDesc(MesWmMaterialStockBagDO::getId)
+                .last("LIMIT 1"));
+    }
+
+    default MesWmMaterialStockBagDO selectOneActiveByMaterialStockId(Long materialStockId) {
+        return selectOne(new LambdaQueryWrapperX<MesWmMaterialStockBagDO>()
+                .eq(MesWmMaterialStockBagDO::getMaterialStockId, materialStockId)
+                .eq(MesWmMaterialStockBagDO::getStatus, 1)
+                .last("LIMIT 1"));
+    }
+
+    default MesWmMaterialStockBagDO selectActiveByBagCode(String bagCode) {
+        return selectOne(new LambdaQueryWrapperX<MesWmMaterialStockBagDO>()
+                .eq(MesWmMaterialStockBagDO::getBagCode, bagCode)
+                .eq(MesWmMaterialStockBagDO::getStatus, 1)
+                .last("LIMIT 1"));
+    }
+
+    default MesWmMaterialStockBagDO selectActiveByBagCodeId(Long bagCodeId) {
+        return selectOne(new LambdaQueryWrapperX<MesWmMaterialStockBagDO>()
+                .eq(MesWmMaterialStockBagDO::getBagCodeId, bagCodeId)
+                .eq(MesWmMaterialStockBagDO::getStatus, 1)
+                .last("LIMIT 1"));
+    }
+
+    default List<MesWmMaterialStockBagDO> selectActiveListByItemAndBatch(Long itemId, Long batchId, Integer stockState) {
+        return selectList(new LambdaQueryWrapperX<MesWmMaterialStockBagDO>()
+                .eq(MesWmMaterialStockBagDO::getItemId, itemId)
+                .eqIfPresent(MesWmMaterialStockBagDO::getBatchId, batchId)
+                .eqIfPresent(MesWmMaterialStockBagDO::getStockState, stockState)
+                .eq(MesWmMaterialStockBagDO::getStatus, 1)
+                .eq(MesWmMaterialStockBagDO::getFrozen, false)
+                .orderByAsc(MesWmMaterialStockBagDO::getInboundTime));
+    }
+
+    default List<MesWmMaterialStockBagDO> selectListByMaterialStockIds(Collection<Long> materialStockIds) {
+        return selectList(new LambdaQueryWrapperX<MesWmMaterialStockBagDO>()
+                .in(MesWmMaterialStockBagDO::getMaterialStockId, materialStockIds)
+                .eq(MesWmMaterialStockBagDO::getStatus, 1)
+                .orderByAsc(MesWmMaterialStockBagDO::getInboundTime));
+    }
+
+    default List<MesWmMaterialStockBagDO> selectListByMaterialStockId(Long materialStockId) {
+        return selectList(new LambdaQueryWrapperX<MesWmMaterialStockBagDO>()
+                .eq(MesWmMaterialStockBagDO::getMaterialStockId, materialStockId)
+                .eq(MesWmMaterialStockBagDO::getStatus, 1)
+                .orderByAsc(MesWmMaterialStockBagDO::getInboundTime));
+    }
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/productsales/MesWmProductSalesLineMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/productsales/MesWmProductSalesLineMapper.java
index 1659343..3dd15dc 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/productsales/MesWmProductSalesLineMapper.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/productsales/MesWmProductSalesLineMapper.java
@@ -7,7 +7,6 @@
 import org.apache.ibatis.annotations.Mapper;
 
 import java.util.List;
-import java.util.Map;
 
 /**
  * MES 閿�鍞嚭搴撳崟琛� Mapper
@@ -31,26 +30,6 @@
                 .eqIfPresent(MesWmProductSalesLineDO::getSalesId, reqVO.getSalesId())
                 .inIfPresent(MesWmProductSalesLineDO::getSalesId, reqVO.getSalesIds())
                 .orderByDesc(MesWmProductSalesLineDO::getId));
-    }
-
-    /**
-     * 鏍规嵁閿�鍞鍗曟槑缁� ID 鍒楄〃鏌ヨ鍑哄簱琛岋紝鎸� saleOrderItemId 鍒嗙粍杩斿洖鎵瑰彿
-     */
-    default Map<Long, String> selectBatchCodesBySaleOrderItemIds(java.util.Collection<Long> saleOrderItemIds) {
-        if (saleOrderItemIds == null || saleOrderItemIds.isEmpty()) {
-            return java.util.Collections.emptyMap();
-        }
-        List<MesWmProductSalesLineDO> lines = selectList(new LambdaQueryWrapperX<MesWmProductSalesLineDO>()
-                .in(MesWmProductSalesLineDO::getSaleOrderItemId, saleOrderItemIds));
-        return lines.stream()
-                .filter(l -> l.getBatchCode() != null && !l.getBatchCode().isEmpty())
-                .collect(java.util.stream.Collectors.groupingBy(
-                        MesWmProductSalesLineDO::getSaleOrderItemId,
-                        java.util.LinkedHashMap::new,
-                        java.util.stream.Collectors.collectingAndThen(
-                                java.util.stream.Collectors.mapping(MesWmProductSalesLineDO::getBatchCode,
-                                        java.util.stream.Collectors.toSet()),
-                                codes -> String.join("銆�", codes))));
     }
 
     default void updateQualityStatusByIds(List<Long> ids, Integer qualityStatus) {
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/returnsales/MesWmReturnSalesMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/returnsales/MesWmReturnSalesMapper.java
index 988d14d..c25314f 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/returnsales/MesWmReturnSalesMapper.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/returnsales/MesWmReturnSalesMapper.java
@@ -22,7 +22,6 @@
         return selectPage(reqVO, new LambdaQueryWrapperX<MesWmReturnSalesDO>()
                 .likeIfPresent(MesWmReturnSalesDO::getCode, reqVO.getCode())
                 .likeIfPresent(MesWmReturnSalesDO::getName, reqVO.getName())
-                .likeIfPresent(MesWmReturnSalesDO::getSalesOrderCode, reqVO.getSalesOrderCode())
                 .eqIfPresent(MesWmReturnSalesDO::getClientId, reqVO.getClientId())
                 .eqIfPresent(MesWmReturnSalesDO::getStatus, reqVO.getStatus())
                 .betweenIfPresent(MesWmReturnSalesDO::getReturnDate, reqVO.getReturnDate())
@@ -36,7 +35,6 @@
         LambdaQueryWrapperX<MesWmReturnSalesDO> wrapper = new LambdaQueryWrapperX<MesWmReturnSalesDO>()
                 .likeIfPresent(MesWmReturnSalesDO::getCode, reqVO.getCode())
                 .likeIfPresent(MesWmReturnSalesDO::getName, reqVO.getName())
-                .likeIfPresent(MesWmReturnSalesDO::getSalesOrderCode, reqVO.getSalesOrderCode())
                 .eqIfPresent(MesWmReturnSalesDO::getClientId, reqVO.getClientId())
                 .eqIfPresent(MesWmReturnSalesDO::getStatus, reqVO.getStatus())
                 .betweenIfPresent(MesWmReturnSalesDO::getReturnDate, reqVO.getReturnDate())
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/salesnotice/MesWmSalesNoticeMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/salesnotice/MesWmSalesNoticeMapper.java
index 23f7af4..088bd2b 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/salesnotice/MesWmSalesNoticeMapper.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/salesnotice/MesWmSalesNoticeMapper.java
@@ -8,7 +8,6 @@
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.salesnotice.MesWmSalesNoticeDO;
 import org.apache.ibatis.annotations.Mapper;
 
-import java.util.Collection;
 import java.util.List;
 
 /**
@@ -21,7 +20,6 @@
         return selectPage(reqVO, new LambdaQueryWrapperX<MesWmSalesNoticeDO>()
                 .likeIfPresent(MesWmSalesNoticeDO::getCode, reqVO.getCode())
                 .likeIfPresent(MesWmSalesNoticeDO::getName, reqVO.getName())
-                .likeIfPresent(MesWmSalesNoticeDO::getSaleOrderCode, reqVO.getSaleOrderCode())
                 .eqIfPresent(MesWmSalesNoticeDO::getClientId, reqVO.getClientId())
                 .eqIfPresent(MesWmSalesNoticeDO::getStatus, reqVO.getStatus())
                 .orderByDesc(MesWmSalesNoticeDO::getId));
@@ -38,7 +36,6 @@
         LambdaQueryWrapperX<MesWmSalesNoticeDO> wrapper = new LambdaQueryWrapperX<MesWmSalesNoticeDO>()
                 .likeIfPresent(MesWmSalesNoticeDO::getCode, reqVO.getCode())
                 .likeIfPresent(MesWmSalesNoticeDO::getName, reqVO.getName())
-                .likeIfPresent(MesWmSalesNoticeDO::getSaleOrderCode, reqVO.getSaleOrderCode())
                 .eqIfPresent(MesWmSalesNoticeDO::getClientId, reqVO.getClientId())
                 .eqIfPresent(MesWmSalesNoticeDO::getStatus, reqVO.getStatus())
                 .orderByDesc(MesWmSalesNoticeDO::getId);
@@ -55,19 +52,6 @@
 
     default MesWmSalesNoticeDO selectByCode(String code) {
         return selectOne(MesWmSalesNoticeDO::getCode, code);
-    }
-
-    default MesWmSalesNoticeDO selectBySaleOrderId(Long saleOrderId) {
-        return selectOne(MesWmSalesNoticeDO::getSaleOrderId, saleOrderId);
-    }
-
-    /**
-     * 鏌ヨ宸插瓨鍦ㄥ彂璐ч�氱煡鍗曠殑閿�鍞鍗� ID 鍒楄〃
-     */
-    default List<Long> selectExistingSaleOrderIds(Collection<Long> saleOrderIds) {
-        return selectObjs(new LambdaQueryWrapperX<MesWmSalesNoticeDO>()
-                .select(MesWmSalesNoticeDO::getSaleOrderId)
-                .in(MesWmSalesNoticeDO::getSaleOrderId, saleOrderIds));
     }
 
 }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/transaction/MesWmTransactionMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/transaction/MesWmTransactionMapper.java
index 878347e..1d18be0 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/transaction/MesWmTransactionMapper.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/transaction/MesWmTransactionMapper.java
@@ -17,6 +17,7 @@
         return selectPage(reqVO, new LambdaQueryWrapperX<MesWmTransactionDO>()
                 .eqIfPresent(MesWmTransactionDO::getItemId, reqVO.getItemId())
                 .likeIfPresent(MesWmTransactionDO::getBatchCode, reqVO.getBatchCode())
+                .likeIfPresent(MesWmTransactionDO::getBagCode, reqVO.getBagCode())
                 .eqIfPresent(MesWmTransactionDO::getWarehouseId, reqVO.getWarehouseId())
                 .eqIfPresent(MesWmTransactionDO::getLocationId, reqVO.getLocationId())
                 .eqIfPresent(MesWmTransactionDO::getAreaId, reqVO.getAreaId())
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/warehouse/MesWmWarehouseMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/warehouse/MesWmWarehouseMapper.java
index 2353b9e..3a3cfc1 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/warehouse/MesWmWarehouseMapper.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/warehouse/MesWmWarehouseMapper.java
@@ -21,9 +21,16 @@
                 .likeIfPresent(MesWmWarehouseDO::getCode, reqVO.getCode())
                 .likeIfPresent(MesWmWarehouseDO::getName, reqVO.getName())
                 .eqIfPresent(MesWmWarehouseDO::getFrozen, reqVO.getFrozen())
+                .eqIfPresent(MesWmWarehouseDO::getType, reqVO.getType())
                 .orderByDesc(MesWmWarehouseDO::getId));
     }
 
+    default List<MesWmWarehouseDO> selectListByType(Integer type) {
+        return selectList(new LambdaQueryWrapperX<MesWmWarehouseDO>()
+                .eq(MesWmWarehouseDO::getType, type)
+                .orderByAsc(MesWmWarehouseDO::getId));
+    }
+
     default MesWmWarehouseDO selectByCode(String code) {
         return selectOne(MesWmWarehouseDO::getCode, code);
     }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
index 1d17581..392cd33 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
@@ -455,6 +455,8 @@
     ErrorCode WM_WAREHOUSE_HAS_WORKSTATION = new ErrorCode(1_040_700_004, "浠撳簱宸茶宸ヤ綔绔欏紩鐢紝鏃犳硶鍒犻櫎");
     ErrorCode WM_WAREHOUSE_HAS_MATERIAL_STOCK = new ErrorCode(1_040_700_005, "浠撳簱涓嬫湁搴撳瓨璁板綍锛屾棤娉曞垹闄�");
     ErrorCode WM_WAREHOUSE_IS_VIRTUAL = new ErrorCode(1_040_700_006, "铏氭嫙浠撳簱涓嶅厑璁告搷浣�");
+    ErrorCode WM_WAREHOUSE_TYPE_NOT_QUALIFIED = new ErrorCode(1_040_700_007, "鎵�閫変粨搴撲笉鏄悎鏍煎簱");
+    ErrorCode WM_QUALIFIED_WAREHOUSE_NOT_CONFIGURED = new ErrorCode(1_040_700_008, "鏈厤缃悎鏍煎簱锛岃鍏堝湪浠撳簱绠$悊涓垱寤哄苟璁剧疆浠撳簱绫诲瀷涓哄悎鏍煎簱");
 
     // ========== MES 浠撳簱绠$悊-搴撳尯锛�1-040-701-000锛� ==========
     ErrorCode WM_WAREHOUSE_LOCATION_NOT_EXISTS = new ErrorCode(1_040_701_000, "搴撳尯涓嶅瓨鍦�");
@@ -496,6 +498,12 @@
     ErrorCode WM_MATERIAL_STOCK_SELECTION_MISMATCH = new ErrorCode(1_040_703_014, "搴撳瓨璁板綍涓庢彁浜ょ殑鐗╂枡銆佹壒娆℃垨搴撲綅淇℃伅涓嶄竴鑷�");
     ErrorCode WM_MATERIAL_STOCK_QUANTITY_INVALID = new ErrorCode(1_040_703_015, "璋冩暣鏁伴噺涓嶈兘涓虹┖鎴栭浂");
     ErrorCode WM_MATERIAL_STOCK_FROZEN = new ErrorCode(1_040_703_016, "搴撳瓨璁板綍宸插喕缁擄紝鏃犳硶绉诲簱");
+    ErrorCode WM_MATERIAL_STOCK_NOT_QUALIFIED = new ErrorCode(1_040_703_017, "闈炲悎鏍煎簱瀛樹笉鍙嚭搴�");
+    ErrorCode WM_MATERIAL_STOCK_BAG_NOT_EXISTS = new ErrorCode(1_040_703_018, "搴撳瓨琚嬬爜鏄庣粏涓嶅瓨鍦�");
+    ErrorCode WM_MATERIAL_STOCK_BAG_DUPLICATE = new ErrorCode(1_040_703_019, "琚嬬爜({})宸插湪搴擄紝绂佹閲嶅鍏ュ簱");
+    ErrorCode WM_MATERIAL_STOCK_BAG_NOT_ACTIVE = new ErrorCode(1_040_703_020, "琚嬬爜({})涓嶅湪搴撴垨宸插嚭搴�/鎶ュ簾锛屾棤娉曟搷浣�");
+    ErrorCode WM_MATERIAL_STOCK_BAG_REQUIRED = new ErrorCode(1_040_703_021, "璇ョ墿鏂欏惎鐢ㄨ鐮佺鐞嗭紝蹇呴』鎼哄甫琚嬬爜");
+    ErrorCode WM_MATERIAL_STOCK_BAG_INSUFFICIENT = new ErrorCode(1_040_703_022, "鍦ㄥ簱琚嬬爜鏄庣粏鏁伴噺涓嶈冻锛屾棤娉曞嚭搴�");
 
     // ========== MES 浠撳簱绠$悊-鍒拌揣閫氱煡鍗曪紙1-040-704-000锛� ==========
     ErrorCode WM_ARRIVAL_NOTICE_NOT_EXISTS = new ErrorCode(1_040_704_000, "鍒拌揣閫氱煡鍗曚笉瀛樺湪");
@@ -727,6 +735,8 @@
     ErrorCode WM_PRODUCT_SALES_SCAN_STOCK_NOT_FOUND = new ErrorCode(1_040_714_105, "鎵弿鎵规鍦ㄥ悎鏍煎簱涓湭鎵惧埌瀵瑰簲搴撳瓨璁板綍");
     ErrorCode WM_PRODUCT_SALES_SCAN_NO_MATCH_LINE = new ErrorCode(1_040_714_106, "鍑哄簱鍗曚腑娌℃湁涓庤鐗╂枡/鎵规鍖归厤鐨勮");
     ErrorCode WM_PRODUCT_SALES_SCAN_BAG_ALREADY_SCANNED = new ErrorCode(1_040_714_107, "璇ヨ鐮佸凡鎵弿鍑哄簱锛岃鍕块噸澶嶆壂鎻�");
+    ErrorCode WM_PRODUCT_SALES_SCAN_BAG_NOT_QUALIFIED = new ErrorCode(1_040_714_108, "璇ヨ鐮佹湭璐ㄦ鍚堟牸锛岀姝㈠嚭搴�");
+    ErrorCode WM_PRODUCT_SALES_SCAN_PALLET_NOT_ALL_QUALIFIED = new ErrorCode(1_040_714_109, "鎵樼洏涓嬪瓨鍦ㄦ湭璐ㄦ琚嬬爜锛岀姝㈡暣鎵樺嚭搴�");
 
     // ========== MES 浠撳簱绠$悊-鏉傞」鍑哄簱鍗曪紙1-040-715-000锛� ==========
     ErrorCode WM_MISC_ISSUE_NOT_EXISTS = new ErrorCode(1_040_715_000, "鏉傞」鍑哄簱鍗曚笉瀛樺湪");
@@ -760,9 +770,6 @@
     ErrorCode WM_SALES_NOTICE_LINE_NOT_EXISTS = new ErrorCode(1_040_720_010, "鍙戣揣閫氱煡鍗曡涓嶅瓨鍦�");
     ErrorCode WM_SALES_NOTICE_LINE_EMPTY = new ErrorCode(1_040_720_011, "鍙戣揣閫氱煡鍗曡涓虹┖锛屼笉鑳芥彁浜�");
     ErrorCode WM_SALES_NOTICE_LINE_NOT_MATCH = new ErrorCode(1_040_720_012, "鍙戣揣閫氱煡鍗曡涓嶅睘浜庢寚瀹氱殑鍙戣揣閫氱煡鍗�");
-    ErrorCode WM_SALES_NOTICE_SALE_ORDER_NOT_EXISTS = new ErrorCode(1_040_720_013, "閿�鍞鍗曚笉瀛樺湪");
-    ErrorCode WM_SALES_NOTICE_SALE_ORDER_ITEM_EMPTY = new ErrorCode(1_040_720_014, "閿�鍞鍗曟槑缁嗕负绌�");
-    ErrorCode WM_SALES_NOTICE_ALREADY_EXISTS = new ErrorCode(1_040_720_015, "璇ラ攢鍞鍗曞凡鐢熸垚鍙戣揣閫氱煡鍗曪紝涓嶈兘閲嶅鐢熸垚");
 
     // ========== MES 浠撳簱绠$悊-鏉$爜閰嶇疆锛�1-040-730-000锛� ==========
     ErrorCode WM_BARCODE_CONFIG_NOT_EXISTS = new ErrorCode(1_040_730_000, "鏉$爜閰嶇疆涓嶅瓨鍦�");
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/MesBizTypeConstants.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/MesBizTypeConstants.java
index b089810..26e6ab1 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/MesBizTypeConstants.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/MesBizTypeConstants.java
@@ -18,6 +18,7 @@
     public static final int QC_IPQC = 2; // 杩囩▼妫�楠岋細MesQcIpqcDO
     public static final int QC_OQC = 3; // 鍑鸿揣妫�楠岋細MesQcOqcDO
     public static final int QC_RQC = 4; // 閫�璐ф楠岋細MesQcRqcDO
+    public static final int QC_SEED_QUALITY = 5; // 绉嶅瓙璐ㄦ锛歁esQcSeedQualityDO锛堝惈鏆傚瓨鍑哄簱/鍚堟牸鍏ュ簱/鎶ュ簾锛�
 
     // ========== WM 浠撳簱妯″潡 [100, 200) ==========
 
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/pro/MesProBagCodeQcStatusEnum.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/pro/MesProBagCodeQcStatusEnum.java
new file mode 100644
index 0000000..2713dc8
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/pro/MesProBagCodeQcStatusEnum.java
@@ -0,0 +1,40 @@
+package cn.iocoder.yudao.module.mes.enums.pro;
+
+import cn.iocoder.yudao.framework.common.core.ArrayValuable;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+
+/**
+ * MES 琚嬬爜锛堜竴琚嬩竴鐮侊級璐ㄦ鐘舵�佹灇涓�
+ *
+ * <p>璁板綍姣忚鐨勭瀛愯川妫�缁撴灉锛岄攢鍞嚭搴撴壂琚嬬爜鏃舵牎楠岃琚嬪凡璐ㄦ鍚堟牸銆�</p>
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@Getter
+@AllArgsConstructor
+public enum MesProBagCodeQcStatusEnum implements ArrayValuable<Integer> {
+
+    UNCHECKED(10, "鏈川妫�"),
+    QUALIFIED(20, "璐ㄦ鍚堟牸"),
+    UNQUALIFIED(30, "璐ㄦ涓嶅悎鏍�");
+
+    public static final Integer[] ARRAYS = Arrays.stream(values()).map(MesProBagCodeQcStatusEnum::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/enums/wm/MesWmMaterialStockBagStatusEnum.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmMaterialStockBagStatusEnum.java
new file mode 100644
index 0000000..bf6bd86
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmMaterialStockBagStatusEnum.java
@@ -0,0 +1,15 @@
+package cn.iocoder.yudao.module.mes.enums.wm;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public enum MesWmMaterialStockBagStatusEnum {
+    ACTIVE(1, "鍦ㄥ簱"),
+    OUT(2, "宸插嚭搴�"),
+    SCRAPPED(3, "宸叉姤搴�");
+
+    private final Integer status;
+    private final String name;
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmWarehouseTypeEnum.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmWarehouseTypeEnum.java
new file mode 100644
index 0000000..d0cd8f9
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmWarehouseTypeEnum.java
@@ -0,0 +1,20 @@
+package cn.iocoder.yudao.module.mes.enums.wm;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * MES 浠撳簱绫诲瀷
+ *
+ * 涓� {@link MesWmStockStateEnum} 璇箟瀵归綈锛氭殏瀛樺簱瀛樻斁寰呮/鏆傚瓨搴撳瓨锛屽悎鏍煎簱瀛樻斁璐ㄦ鍚堟牸銆佸彲鎷h揣搴撳瓨銆�
+ */
+@Getter
+@AllArgsConstructor
+public enum MesWmWarehouseTypeEnum {
+
+    TEMP(10, "鏆傚瓨搴�"),
+    QUALIFIED(20, "鍚堟牸搴�");
+
+    private final Integer type;
+    private final String name;
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/home/MesBagCodeHomeStatisticsServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/home/MesBagCodeHomeStatisticsServiceImpl.java
index 4a14ca7..0282554 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/home/MesBagCodeHomeStatisticsServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/home/MesBagCodeHomeStatisticsServiceImpl.java
@@ -42,7 +42,16 @@
         summary.setBagCodeCount(getCount(homeStatisticsMapper.selectBagCodeCount()));
         summary.setPalletCount(getCount(homeStatisticsMapper.selectPalletCount()));
 
-        // 3. 鐘舵�佸垎甯�
+        // 3. 鍑哄簱娴侀��
+        summary.setSalesNoticeCount(getCount(homeStatisticsMapper.selectSalesNoticeCount()));
+        summary.setProductSalesCount(getCount(homeStatisticsMapper.selectProductSalesCount()));
+        summary.setReturnSalesCount(getCount(homeStatisticsMapper.selectReturnSalesCount()));
+
+        // 4. 浠撳偍棰勮 / 璐ㄩ噺
+        summary.setStockWarningPendingCount(getCount(homeStatisticsMapper.selectStockWarningPendingCount()));
+        summary.setNcrPendingCount(getCount(homeStatisticsMapper.selectNcrPendingCount()));
+
+        // 5. 鐘舵�佸垎甯�
         summary.setBatchStatusList(toStatusList(homeStatisticsMapper.selectBatchCountGroupByStatus()));
         summary.setBagCodeStatusList(toStatusList(homeStatisticsMapper.selectBagCodeCountGroupByStatus()));
 
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/scan/MesProScanEventServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/scan/MesProScanEventServiceImpl.java
index 6ee0402..4657035 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/scan/MesProScanEventServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/scan/MesProScanEventServiceImpl.java
@@ -5,11 +5,13 @@
 import cn.iocoder.yudao.module.mes.controller.admin.pro.scan.vo.MesProScanEventPageReqVO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.bagcode.MesProBagCodeDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.batch.MesProBatchDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.pallet.MesProPalletDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.scan.MesProScanEventDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO;
 import cn.iocoder.yudao.module.mes.dal.mysql.pro.bagcode.MesProBagCodeMapper;
 import cn.iocoder.yudao.module.mes.dal.mysql.pro.batch.MesProBatchMapper;
+import cn.iocoder.yudao.module.mes.dal.mysql.pro.pallet.MesProPalletMapper;
 import cn.iocoder.yudao.module.mes.dal.mysql.pro.scan.MesProScanEventMapper;
 import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
 import cn.iocoder.yudao.module.mes.enums.MesProScanEventStatusEnum;
@@ -20,47 +22,102 @@
 import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
 import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseService;
 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.time.LocalDateTime;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+@Slf4j
 @Service
 @Validated
 public class MesProScanEventServiceImpl implements MesProScanEventService {
     @Resource private MesProScanEventMapper eventMapper;
     @Resource private MesProBagCodeMapper bagCodeMapper;
     @Resource private MesProBatchMapper batchMapper;
+    @Resource private MesProPalletMapper palletMapper;
     @Resource private MesWmTransactionService transactionService;
     @Resource private MesWmWarehouseService warehouseService;
     @Resource private MesWmBatchService mesWmBatchService;
+
+    /** 鎵爜绫诲瀷锛氳鐮侊紙榛樿锛� */
+    private static final String SCAN_TYPE_BAG = "BAG";
+    /** 鎵爜绫诲瀷锛氭墭鐩樼爜锛堟寜鎵樼洏宸茶琚嬫暟鏁存墭鍏ュ簱锛� */
+    private static final String SCAN_TYPE_PALLET = "PALLET";
+    /** 鎵爜绫诲瀷锛氭壒娆$爜锛堟寜鎵规琚嬫暟鏁存壒鍏ュ簱锛� */
+    private static final String SCAN_TYPE_BATCH = "BATCH";
 
     @Override
     @Transactional(rollbackFor = Exception.class)
     public MesProScanEventDO createInbound(MesProScanEventCreateReqVO req) {
         MesProScanEventDO existing = eventMapper.selectByEventIdForUpdate(req.getEventId());
         if (existing != null) {
-            if (!req.getBagCode().equals(existing.getBagCode()) || !req.getDeviceCode().equals(existing.getDeviceCode())) {
+            boolean contentMatch = req.getBagCode().equals(existing.getBagCode())
+                    && (req.getDeviceCode() == null || req.getDeviceCode().equals(existing.getDeviceCode()));
+            if (!contentMatch) {
                 return failed(existing, "MES_SCAN_EVENT_ID_CONFLICT", "浜嬩欢鍙峰凡瀛樺湪浣嗗唴瀹逛笉涓�鑷�");
             }
             return existing;
         }
+        String scanType = (req.getScanType() == null || req.getScanType().isBlank()) ? SCAN_TYPE_BAG : req.getScanType();
         MesProScanEventDO event = new MesProScanEventDO();
-        event.setEventId(req.getEventId()).setBagCode(req.getBagCode()).setQuantity(req.getQuantity())
-                .setDeviceCode(req.getDeviceCode()).setLineCode(req.getLineCode()).setRemark(req.getRemark())
-                .setStatus(MesProScanEventStatusEnum.FAILED).setReceivedTime(LocalDateTime.now());
+        event.setEventId(req.getEventId()).setBagCode(req.getBagCode()).setScanType(scanType)
+                .setQuantity(req.getQuantity()).setDeviceCode(req.getDeviceCode()).setLineCode(req.getLineCode())
+                .setRemark(req.getRemark()).setStatus(MesProScanEventStatusEnum.FAILED).setReceivedTime(LocalDateTime.now());
         eventMapper.insert(event);
-        MesProBagCodeDO bag = bagCodeMapper.selectByCodeForUpdate(req.getBagCode());
-        if (bag == null) return failed(event, "MES_SCAN_BAG_NOT_EXISTS", "琚嬬爜涓嶅瓨鍦�");
-        event.setBagCodeId(bag.getId());
-        if (bag.getInboundEventId() != null) return failed(event, "MES_SCAN_BAG_ALREADY_INBOUND", "琚嬬爜宸插叆搴�");
-        MesProBatchDO batch = batchMapper.selectByIdForUpdate(bag.getBatchId());
-        if (batch == null) return failed(event, "MES_SCAN_BATCH_NOT_EXISTS", "鐢熶骇鎵规涓嶅瓨鍦�");
-        event.setBatchId(batch.getId()).setBatchCode(batch.getCode()).setItemId(batch.getItemId());
+        // 1. 鎸夋壂鐮佺被鍨嬭В鏋愭壂鐮佺洰鏍囦笌鍏ュ簱鏁伴噺锛堟墭鐩樻寜宸茶琚嬫暟銆佹壒娆℃寜鎵规琚嬫暟鏁存壒/鏁存墭鍏ュ簱锛�
+        Long targetId;
+        Long batchId;
+        BigDecimal quantity;
+        MesProBatchDO batch;
+        // 鍗曡鎵爜鍏ュ簱鏃朵紶閫掕鐮侊紝渚涘簱瀛樿鐮佹槑缁嗗湪鍏ュ簱绗竴鏃堕棿寤虹珛锛涙墭鐩�/鎵规鏁存壒鍏ュ簱涓烘壒閲忥紝涓嶉�愯寤烘槑缁�
+        Long bagCodeId = null;
+        String bagCode = null;
+        if (SCAN_TYPE_PALLET.equals(scanType)) {
+            MesProPalletDO pallet = palletMapper.selectByCode(req.getBagCode());
+            if (pallet == null) {
+                pallet = palletMapper.selectByTempCode(req.getBagCode());
+            }
+            if (pallet == null) return failed(event, "MES_SCAN_PALLET_NOT_EXISTS", "鎵樼洏鐮佷笉瀛樺湪");
+            if (pallet.getInboundEventId() != null) return failed(event, "MES_SCAN_PALLET_ALREADY_INBOUND", "鎵樼洏宸插叆搴�");
+            batch = batchMapper.selectByIdForUpdate(pallet.getBatchId());
+            if (batch == null) return failed(event, "MES_SCAN_BATCH_NOT_EXISTS", "鐢熶骇鎵规涓嶅瓨鍦�");
+            batchId = batch.getId();
+            targetId = pallet.getId();
+            quantity = pallet.getBagCount() != null && pallet.getBagCount() > 0
+                    ? BigDecimal.valueOf(pallet.getBagCount())
+                    : BigDecimal.valueOf(bagCodeMapper.selectCountByPalletId(pallet.getId()));
+        } else if (SCAN_TYPE_BATCH.equals(scanType)) {
+            batch = batchMapper.selectByCode(req.getBagCode());
+            if (batch == null) {
+                batch = batchMapper.selectByTempCode(req.getBagCode());
+            }
+            if (batch == null) return failed(event, "MES_SCAN_BATCH_NOT_EXISTS", "鐢熶骇鎵规涓嶅瓨鍦�");
+            if (batch.getInboundEventId() != null) return failed(event, "MES_SCAN_BATCH_ALREADY_INBOUND", "鎵规宸叉暣鎵瑰叆搴�");
+            batchId = batch.getId();
+            targetId = batch.getId();
+            quantity = batch.getBagQuantity() != null && batch.getBagQuantity() > 0
+                    ? BigDecimal.valueOf(batch.getBagQuantity())
+                    : BigDecimal.valueOf(bagCodeMapper.selectCountByBatchId(batch.getId()));
+        } else {
+            MesProBagCodeDO bag = bagCodeMapper.selectByCodeForUpdate(req.getBagCode());
+            if (bag == null) return failed(event, "MES_SCAN_BAG_NOT_EXISTS", "琚嬬爜涓嶅瓨鍦�");
+            event.setBagCodeId(bag.getId());
+            bagCodeId = bag.getId();
+            bagCode = req.getBagCode();
+            if (bag.getInboundEventId() != null) return failed(event, "MES_SCAN_BAG_ALREADY_INBOUND", "琚嬬爜宸插叆搴�");
+            batch = batchMapper.selectByIdForUpdate(bag.getBatchId());
+            if (batch == null) return failed(event, "MES_SCAN_BATCH_NOT_EXISTS", "鐢熶骇鎵规涓嶅瓨鍦�");
+            batchId = batch.getId();
+            targetId = bag.getId();
+            quantity = req.getQuantity();
+        }
+        event.setBatchId(batchId).setBatchCode(batch.getCode()).setItemId(batch.getItemId());
         MesWmWarehouseDO warehouse = warehouseService.getWarehouseByCode(MesWmWarehouseDO.WIP_VIRTUAL_WAREHOUSE);
         if (warehouse == null) return failed(event, "MES_SCAN_WIP_WAREHOUSE_NOT_EXISTS", "WIP铏氭嫙浠撲笉瀛樺湪");
         event.setWarehouseId(warehouse.getId()).setLocationId(req.getLocationId()).setAreaId(req.getAreaId());
@@ -72,19 +129,31 @@
             }
             Long txId = transactionService.createTransaction(new MesWmTransactionSaveReqDTO()
                     .setType(MesWmTransactionTypeEnum.IN.getType()).setBizType(MesBizTypeConstants.WM_PRODUCT_PRODUCE)
-                    .setBizId(batch.getId()).setBizCode(batch.getCode()).setBizLineId(bag.getId())
-                    .setItemId(batch.getItemId()).setQuantity(req.getQuantity())
+                    .setBizId(batchId).setBizCode(batch.getCode()).setBizLineId(targetId)
+                    .setItemId(batch.getItemId()).setQuantity(quantity)
                     .setBatchId(wmBatch.getId()).setBatchCode(batch.getCode())
+                    .setBagCodeId(bagCodeId).setBagCode(bagCode)
                     .setWarehouseId(warehouse.getId()).setLocationId(req.getLocationId()).setAreaId(req.getAreaId())
                     .setReceiptTime(LocalDateTime.now()).setCheckFlag(true)
                     .setStockState(MesWmStockStateEnum.TEMP.getState()));
             event.setTransactionId(txId).setStatus(MesProScanEventStatusEnum.SUCCESS).setEventTime(LocalDateTime.now());
             eventMapper.updateById(event);
-            bagCodeMapper.markInbound(bag.getId(), event.getId(), req.getDeviceCode(), req.getLineCode());
-            batchMapper.markInbound(batch.getId(), event.getId(), req.getDeviceCode(), req.getLineCode());
+            // 2. 鍥炲啓鍏ュ簱鏍囪锛堟墭鐩�/鎵规鏁存墭/鏁存壒鍏ュ簱鏃跺悓姝ユ爣璁板叾涓嬫湭鍏ュ簱琚嬬爜锛屼繚鎸佺姸鎬佷竴鑷达級
+            if (SCAN_TYPE_PALLET.equals(scanType)) {
+                palletMapper.markInbound(targetId, event.getId());
+                bagCodeMapper.markInboundByPalletId(targetId, event.getId(), req.getLineCode());
+            } else if (SCAN_TYPE_BATCH.equals(scanType)) {
+                batchMapper.markBatchInbound(targetId, event.getId(), req.getLineCode(), batch.getBagQuantity());
+                bagCodeMapper.markInboundByBatchId(targetId, event.getId(), req.getLineCode());
+            } else {
+                bagCodeMapper.markInbound(targetId, event.getId(), req.getDeviceCode(), req.getLineCode());
+                batchMapper.markInbound(batchId, event.getId(), req.getDeviceCode(), req.getLineCode());
+            }
             return event;
         } catch (Exception ex) {
-            return failed(event, "MES_SCAN_TRANSACTION_FAILED", "搴撳瓨浜嬪姟澶勭悊澶辫触");
+            log.error("[createInbound] 搴撳瓨浜嬪姟澶勭悊澶辫触锛宔ventId={}, bagCode={}, batchId={}, itemId={}",
+                    req.getEventId(), req.getBagCode(), batchId, batch.getItemId(), ex);
+            throw ex;
         }
     }
 
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/seedquality/MesQcSeedQualityService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/seedquality/MesQcSeedQualityService.java
index 8fbf16c..ed096f1 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/seedquality/MesQcSeedQualityService.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/seedquality/MesQcSeedQualityService.java
@@ -1,5 +1,7 @@
 package cn.iocoder.yudao.module.mes.service.qc.seedquality;
 
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedQualityPageReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedQualitySubmitReqVO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.seedquality.MesQcSeedQualityDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.seedquality.MesQcSeedQualityIndicatorDO;
@@ -10,6 +12,8 @@
 
     Long submit(MesQcSeedQualitySubmitReqVO reqVO);
 
+    PageResult<MesQcSeedQualityDO> getQualityPage(MesQcSeedQualityPageReqVO reqVO);
+
     /**
      * 鏍规嵁鎵规鍙锋煡璇㈣川妫�璁板綍锛堟寜妫�楠屾椂闂村�掑簭锛屽彇鏈�鏂帮級锛岀敤浜庡叕寮�婧簮銆�
      * 绉嶅瓙璐ㄦ琛ㄦ湭鍒濆鍖栨椂杩斿洖绌哄垪琛紙瀹归敊锛屼笉褰卞搷杩芥函锛夈��
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/seedquality/MesQcSeedQualityServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/seedquality/MesQcSeedQualityServiceImpl.java
index 60b78c1..492d1d0 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/seedquality/MesQcSeedQualityServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/seedquality/MesQcSeedQualityServiceImpl.java
@@ -1,41 +1,117 @@
 package cn.iocoder.yudao.module.mes.service.qc.seedquality;
 
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedQualityPageReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedQualitySubmitReqVO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.bagcode.MesProBagCodeDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.pro.batch.MesProBatchDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.qc.seedquality.*;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLocationDO;
+import cn.iocoder.yudao.module.mes.dal.mysql.pro.bagcode.MesProBagCodeMapper;
 import cn.iocoder.yudao.module.mes.dal.mysql.qc.seedquality.*;
+import cn.iocoder.yudao.module.mes.dal.mysql.wm.batch.MesWmBatchMapper;
+import cn.iocoder.yudao.module.mes.dal.mysql.wm.materialstock.MesWmMaterialStockMapper;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstockbag.MesWmMaterialStockBagDO;
+import cn.iocoder.yudao.module.mes.dal.mysql.wm.materialstockbag.MesWmMaterialStockBagMapper;
+import cn.iocoder.yudao.module.mes.enums.pro.MesProBagCodeQcStatusEnum;
 import cn.iocoder.yudao.module.mes.enums.wm.MesWmStockStateEnum;
+import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum;
+import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
+import cn.iocoder.yudao.module.mes.enums.wm.MesWmWarehouseTypeEnum;
 import cn.iocoder.yudao.module.mes.service.pro.batch.MesProBatchService;
-import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService;
-import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService;
+import cn.iocoder.yudao.module.mes.service.wm.materialstockbag.MesWmMaterialStockBagService;
+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;
+import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseLocationService;
+import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseService;
 import jakarta.annotation.Resource;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.jdbc.BadSqlGrammarException;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.validation.annotation.Validated;
+import java.math.BigDecimal;
 import java.time.LocalDateTime;
 import java.util.*;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_QUALIFIED_WAREHOUSE_NOT_CONFIGURED;
+import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_WAREHOUSE_NOT_EXISTS;
+import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_WAREHOUSE_TYPE_NOT_QUALIFIED;
 
 @Service @Validated @Slf4j
 public class MesQcSeedQualityServiceImpl implements MesQcSeedQualityService {
     private static final Set<String> REQUIRED = Set.of("purity", "cleanliness", "germination_rate", "moisture");
     @Resource private MesQcSeedQualityMapper qualityMapper;
     @Resource private MesQcSeedQualityIndicatorMapper indicatorMapper;
+    @Resource private MesProBagCodeMapper bagCodeMapper;
     @Resource private MesProBatchService proBatchService;
-    @Resource private MesWmBatchService mesWmBatchService;
-    @Resource private MesWmMaterialStockService materialStockService;
+    @Resource private MesWmBatchMapper mesWmBatchMapper;
+    @Resource private MesWmMaterialStockMapper materialStockMapper;
+    @Resource private MesWmMaterialStockBagMapper materialStockBagMapper;
+    @Resource private MesWmMaterialStockBagService materialStockBagService;
+    @Resource private MesWmTransactionService wmTransactionService;
+    @Resource private MesWmWarehouseService warehouseService;
+    @Resource private MesWmWarehouseLocationService warehouseLocationService;
+    @Resource private MesWmWarehouseAreaService warehouseAreaService;
+
+    @Override
+    public PageResult<MesQcSeedQualityDO> getQualityPage(MesQcSeedQualityPageReqVO reqVO) {
+        return qualityMapper.selectPage(reqVO);
+    }
 
     @Override @Transactional(rollbackFor = Exception.class)
     public Long submit(MesQcSeedQualitySubmitReqVO reqVO) {
         if (reqVO.getIndicators().size() != 4) throw new IllegalArgumentException("蹇呴』鎻愪氦鍥涢」鎸囨爣");
         if (qualityMapper.selectByCode(reqVO.getCode()) != null) throw new IllegalArgumentException("璇ヨ鐮佸凡璐ㄦ锛岀姝㈤噸澶嶆彁浜�");
+        // 绉嶅瓙璐ㄦ蹇呴』浠ヨ鐮侊紙鍗曡锛夋彁浜わ紝鐗╂枡淇℃伅鐢辫鐮佽嚜鍔ㄨВ鏋愶紝鍓嶇涓嶅啀鍗曠嫭閫夋嫨鐗╂枡
+        MesProBagCodeDO bag = bagCodeMapper.selectByCode(reqVO.getCode());
+        if (bag == null) {
+            throw new IllegalArgumentException("鏈В鏋愬埌鏈夋晥琚嬬爜锛岀瀛愯川妫�蹇呴』浠ヨ鐮佹彁浜�");
+        }
+        MesProBatchDO batch = resolveBatch(reqVO.getCode(), reqVO.getBatchCode());
+        if (batch == null || batch.getItemId() == null) {
+            throw new IllegalArgumentException("鏈牴鎹鐮佽В鏋愬埌鐗╂枡淇℃伅锛岃纭鎵爜鍐呭鎴栨壒娆″彿姝g‘");
+        }
+        // 琚嬬爜瀵瑰簲鎵规鐨勫簱瀛樺繀椤诲浜庢殏瀛樺簱锛堝緟璐ㄦ锛変腑锛屾墠鑳借繘琛岀瀛愯川妫�
+        MesWmBatchDO wmBatch = mesWmBatchMapper.selectByCode(batch.getCode());
+        if (wmBatch == null || materialStockMapper.selectTempStockCountByBatchId(wmBatch.getId()) == 0) {
+            throw new IllegalArgumentException("璇ヨ鐮�/鎵规瀵瑰簲鐨勫簱瀛樹笉鍦ㄦ殏瀛樺簱涓紝鏃犳硶杩涜璐ㄦ");
+        }
+        // 鏍¢獙鐩爣鍚堟牸搴擄細蹇呴』瀛樺湪涓斾粨搴撶被鍨嬩负鍚堟牸搴�(20)
+        if (warehouseService.getFirstQualifiedWarehouse() == null) {
+            throw exception(WM_QUALIFIED_WAREHOUSE_NOT_CONFIGURED);
+        }
+        MesWmWarehouseDO qualifiedWarehouse = warehouseService.getWarehouse(reqVO.getQualifiedWarehouseId());
+        if (qualifiedWarehouse == null) {
+            throw exception(WM_WAREHOUSE_NOT_EXISTS);
+        }
+        if (!MesWmWarehouseTypeEnum.QUALIFIED.getType().equals(qualifiedWarehouse.getType())) {
+            throw exception(WM_WAREHOUSE_TYPE_NOT_QUALIFIED);
+        }
+        // 瑙f瀽鍚堟牸搴撻粯璁ゅ簱鍖�/搴撲綅锛堝彇璇ヤ粨搴撲笅绗竴涓簱鍖恒�佸叾涓嬬涓�涓簱浣嶏紝鏈厤缃垯涓� null锛�
+        Long qualifiedLocationId = null;
+        Long qualifiedAreaId = null;
+        List<MesWmWarehouseLocationDO> locations = warehouseLocationService.getWarehouseLocationList(qualifiedWarehouse.getId());
+        if (CollUtil.isNotEmpty(locations)) {
+            qualifiedLocationId = locations.get(0).getId();
+            List<MesWmWarehouseAreaDO> areas = warehouseAreaService.getWarehouseAreaList(qualifiedLocationId);
+            if (CollUtil.isNotEmpty(areas)) {
+                qualifiedAreaId = areas.get(0).getId();
+            }
+        }
         Set<String> codes = new HashSet<>();
         boolean passed = true;
-        MesQcSeedQualityDO quality = MesQcSeedQualityDO.builder().code(reqVO.getCode()).itemId(reqVO.getItemId())
-                .batchCode(reqVO.getBatchCode()).reason(reqVO.getReason()).result(0)
+        MesQcSeedQualityDO quality = MesQcSeedQualityDO.builder().code(reqVO.getCode()).itemId(batch.getItemId())
+                .batchCode(batch.getCode()).reason(reqVO.getReason()).result(0)
                 .inspectedAt(LocalDateTime.now()).build();
         qualityMapper.insert(quality);
         for (MesQcSeedQualitySubmitReqVO.Indicator item : reqVO.getIndicators()) {
@@ -47,20 +123,151 @@
         if (!codes.equals(REQUIRED)) throw new IllegalArgumentException("鍥涢」鎸囨爣涓嶅畬鏁�");
         quality.setResult(passed ? 1 : 2);
         qualityMapper.updateById(quality);
-        // 璐ㄦ鍚堟牸 鈫� 搴撳瓨鏁存壒鐢辨殏瀛樺簱锛堝緟璐ㄦ锛夎浆涓哄悎鏍煎簱锛堟寮忓簱瀛橈級
+        // 鎸夎鐮佺矑搴︽祦杞簱瀛橈細閿佸畾璇ヨ鍦ㄦ殏瀛樺簱涓殑娲诲姩鏄庣粏锛岀姝㈡寜鎵规浠绘剰鎵e噺
+        MesWmMaterialStockBagDO stockBag = materialStockBagMapper.selectActiveByBagCode(bag.getCode());
+        MesWmMaterialStockDO tempStock = resolveTempStock(bag, wmBatch, stockBag);
+        // A. 鏆傚瓨鍑哄簱锛�-1锛夛細璇ヨ绂诲紑鏆傚瓨搴擄紝鍐欏叆璐ㄦ鏆傚瓨鍑哄簱浜嬪姟锛堣烦杩囪鐮佽嚜鍔ㄧ淮鎶わ紝鐢辫川妫�缁熶竴鎺у埗琚嬬爜鏈�缁堣惤鐐癸級
+        MesWmTransactionSaveReqDTO tempOutReq = new MesWmTransactionSaveReqDTO()
+                .setType(MesWmTransactionTypeEnum.OUT.getType())
+                .setBizType(MesBizTypeConstants.QC_SEED_QUALITY)
+                .setBizId(quality.getId())
+                .setBizCode(reqVO.getCode())
+                .setBizLineId(quality.getId())
+                .setItemId(batch.getItemId())
+                .setQuantity(BigDecimal.ONE.negate())
+                .setBatchId(tempStock.getBatchId())
+                .setBatchCode(tempStock.getBatchCode())
+                .setBagCodeId(bag.getId())
+                .setBagCode(bag.getCode())
+                .setWarehouseId(tempStock.getWarehouseId())
+                .setLocationId(tempStock.getLocationId())
+                .setAreaId(tempStock.getAreaId())
+                .setVendorId(tempStock.getVendorId())
+                .setMaterialStockId(tempStock.getId())
+                .setCheckFlag(true)
+                .setSkipBagManage(true);
+        Long tempOutTxId = wmTransactionService.createTransaction(tempOutReq);
         if (passed) {
-            MesProBatchDO proBatch = proBatchService.getBatchByCode(reqVO.getBatchCode());
-            if (proBatch != null) {
-                MesWmBatchDO wmBatch = mesWmBatchService.getOrCreateByProBatch(proBatch);
-                if (wmBatch != null) {
-                    int updated = materialStockService.updateStockStateByBatchId(wmBatch.getId(),
-                            MesWmStockStateEnum.QUALIFIED.getState(), MesWmStockStateEnum.TEMP.getState());
-                    log.info("[submit][璐ㄦ鍚堟牸锛屾壒娆� {} 搴撳瓨鐢辨殏瀛樺簱杞悎鏍煎簱锛屽奖鍝� {} 鏉", reqVO.getBatchCode(), updated);
+            // B1. 璐ㄦ鍚堟牸 鈫� 璇ヨ杞叆鎿嶄綔鍛橀�夋嫨鐨勫悎鏍煎簱锛屽啓鍏ュ悎鏍煎叆搴撲簨鍔★紙璺宠繃琚嬬爜鑷姩缁存姢锛�
+            MesWmMaterialStockDO qualifiedStock = getOrCreateQualifiedStock(tempStock, qualifiedWarehouse.getId(), qualifiedLocationId, qualifiedAreaId);
+            MesWmTransactionSaveReqDTO qualInReq = new MesWmTransactionSaveReqDTO()
+                    .setType(MesWmTransactionTypeEnum.IN.getType())
+                    .setBizType(MesBizTypeConstants.QC_SEED_QUALITY)
+                    .setBizId(quality.getId())
+                    .setBizCode(reqVO.getCode())
+                    .setBizLineId(quality.getId())
+                    .setItemId(batch.getItemId())
+                    .setQuantity(BigDecimal.ONE)
+                    .setBatchId(qualifiedStock.getBatchId())
+                    .setBatchCode(qualifiedStock.getBatchCode())
+                    .setBagCodeId(bag.getId())
+                    .setBagCode(bag.getCode())
+                    .setWarehouseId(qualifiedStock.getWarehouseId())
+                    .setLocationId(qualifiedStock.getLocationId())
+                    .setAreaId(qualifiedStock.getAreaId())
+                    .setVendorId(qualifiedStock.getVendorId())
+                    .setMaterialStockId(qualifiedStock.getId())
+                    .setCheckFlag(false)
+                    .setStockState(MesWmStockStateEnum.QUALIFIED.getState())
+                    .setSkipBagManage(true);
+            Long qualInTxId = wmTransactionService.createTransaction(qualInReq);
+            // B2. 鍚屾杩佺Щ琚嬬爜鏄庣粏鍒板悎鏍煎簱瀛樿惤鐐�
+            materialStockBagService.moveIn(qualInReq, qualifiedStock, qualInTxId);
+            bagCodeMapper.updateQcStatus(bag.getId(), MesProBagCodeQcStatusEnum.QUALIFIED.getStatus());
+            log.info("[submit][璐ㄦ鍚堟牸锛岃鐮� {} 鐢辨殏瀛樺簱杞悎鏍煎簱 {}]", bag.getCode(), qualifiedWarehouse.getName());
+        } else {
+            // C. 璐ㄦ涓嶅悎鏍� 鈫� 璇ヨ鎶ュ簾锛涙殏瀛樺嚭搴撲簨鍔″凡鎵e噺鏆傚瓨搴撳瓨锛屾爣璁拌鐮佹姤搴�
+            materialStockBagService.scrap(tempOutReq, tempOutTxId);
+            bagCodeMapper.updateQcStatus(bag.getId(), MesProBagCodeQcStatusEnum.UNQUALIFIED.getStatus());
+            log.info("[submit][璐ㄦ涓嶅悎鏍硷紝琚嬬爜 {} 鎶ュ簾]", bag.getCode());
+        }
+        return quality.getId();
+    }
+
+    /**
+     * 瀹氫綅璇ヨ鐨勬殏瀛樺簱瀛樻眹鎬昏褰曪紙鍙畾浣嶏紝涓嶆墸鍑忥紱鎵e噺鐢辫川妫�鏆傚瓨鍑哄簱浜嬪姟瀹屾垚锛夈��
+     * <p>
+     * 浼樺厛涓ユ牸鎸夎鐮侀攣瀹氬叾娲诲姩鏄庣粏瀵瑰簲鐨勬殏瀛樿褰曪紱鍘嗗彶鏃犳槑缁嗘椂鍥為��鎸夋壒娆″畾浣嶅苟琛ュ缓鏄庣粏銆�
+     */
+    private MesWmMaterialStockDO resolveTempStock(MesProBagCodeDO bag, MesWmBatchDO wmBatch, MesWmMaterialStockBagDO stockBag) {
+        if (stockBag != null && stockBag.getMaterialStockId() != null) {
+            MesWmMaterialStockDO stock = materialStockMapper.selectById(stockBag.getMaterialStockId());
+            if (stock != null && MesWmStockStateEnum.TEMP.getState().equals(stock.getStockState())) {
+                return stock;
+            }
+        }
+        MesWmMaterialStockDO tempStock = materialStockMapper.selectStockListByBatchId(wmBatch.getId()).stream()
+                .filter(s -> MesWmStockStateEnum.TEMP.getState().equals(s.getStockState()))
+                .max(Comparator.comparing(MesWmMaterialStockDO::getQuantity))
+                .orElse(null);
+        if (tempStock == null) {
+            throw new IllegalArgumentException("璇ユ壒娆℃殏瀛樺簱瀛樹笉瓒筹紝鏃犳硶璐ㄦ");
+        }
+        getOrCreateStockBag(bag, tempStock);
+        return tempStock;
+    }
+
+    /**
+     * 璐ㄦ鍚堟牸锛氬皢璐ㄦ閫氳繃鐨勯偅琚嬭浆鍏ユ搷浣滃憳閫夋嫨鐨勫悎鏍煎簱锛堝悓缁勫悎閿悎鏍煎簱璁板綍 +1锛屼笉瀛樺湪鍒欐柊寤猴級銆�
+     *
+     * @param tempStock            浠庢殏瀛樺簱鎵e噺鍚庤繑鍥炵殑鏆傚瓨璁板綍锛堟惡甯� item/batch/vendor 缁村害锛�
+     * @param qualifiedWarehouseId 鐩爣鍚堟牸搴撲粨搴撶紪鍙�
+     * @param qualifiedLocationId  鐩爣鍚堟牸搴撳簱鍖虹紪鍙凤紙鏈厤缃垯涓� null锛�
+     * @param qualifiedAreaId      鐩爣鍚堟牸搴撳簱浣嶇紪鍙凤紙鏈厤缃垯涓� null锛�
+     */
+    private MesWmMaterialStockBagDO getOrCreateStockBag(MesProBagCodeDO bag, MesWmMaterialStockDO tempStock) {
+        MesWmMaterialStockBagDO stockBag = materialStockBagMapper.selectByBagCode(bag.getCode());
+        if (stockBag != null) {
+            return stockBag;
+        }
+        stockBag = MesWmMaterialStockBagDO.builder().materialStockId(tempStock.getId())
+                .bagCodeId(bag.getId()).bagCode(bag.getCode()).itemId(tempStock.getItemId())
+                .batchId(tempStock.getBatchId()).batchCode(tempStock.getBatchCode()).vendorId(tempStock.getVendorId())
+                .warehouseId(tempStock.getWarehouseId()).locationId(tempStock.getLocationId()).areaId(tempStock.getAreaId())
+                .stockState(MesWmStockStateEnum.TEMP.getState()).status(1).quantity(BigDecimal.ONE)
+                .frozen(false).inboundTime(tempStock.getReceiptTime()).build();
+        materialStockBagMapper.insert(stockBag);
+        return stockBag;
+    }
+
+    private MesWmMaterialStockDO getOrCreateQualifiedStock(MesWmMaterialStockDO tempStock, Long qualifiedWarehouseId,
+                                         Long qualifiedLocationId, Long qualifiedAreaId) {
+        MesWmMaterialStockDO qualified = materialStockMapper.selectQualifiedByCompositeKey(
+                tempStock.getItemId(), qualifiedWarehouseId, qualifiedLocationId,
+                qualifiedAreaId, tempStock.getBatchId(), tempStock.getVendorId());
+        if (qualified != null) {
+            return qualified;
+        }
+        // 涓嶅瓨鍦ㄥ垯鏂板缓鍚堟牸搴撹褰曪紙浠撳簱/搴撳尯/搴撲綅閲囩敤鎵�閫夊悎鏍煎簱鐨勭淮搴︼紝鏁伴噺鐢辫川妫�鍏ュ簱浜嬪姟 +1锛�
+        MesWmMaterialStockDO newStock = MesWmMaterialStockDO.builder()
+                .itemId(tempStock.getItemId()).itemTypeId(tempStock.getItemTypeId())
+                .warehouseId(qualifiedWarehouseId).locationId(qualifiedLocationId).areaId(qualifiedAreaId)
+                .batchId(tempStock.getBatchId()).batchCode(tempStock.getBatchCode()).vendorId(tempStock.getVendorId())
+                .quantity(BigDecimal.ZERO).receiptTime(LocalDateTime.now())
+                .stockState(MesWmStockStateEnum.QUALIFIED.getState())
+                .frozen(false)
+                .build();
+        materialStockMapper.insert(newStock);
+        return newStock;
+    }
+
+    /**
+     * 鐢辫鐮佹垨鐢熶骇鎵规瑙f瀽鎵�灞炵敓浜ф壒娆★紙琚嬬爜浼樺厛锛屾壒娆$爜鍏煎涓存椂鐮侊級
+     */
+    private MesProBatchDO resolveBatch(String code, String batchCode) {
+        if (StrUtil.isNotBlank(code)) {
+            MesProBagCodeDO bag = bagCodeMapper.selectByCode(code);
+            if (bag != null && bag.getBatchId() != null) {
+                MesProBatchDO batch = proBatchService.getBatch(bag.getBatchId());
+                if (batch != null) {
+                    return batch;
                 }
             }
         }
-        // 搴撲綅鐢卞悗鍙伴厤缃湇鍔℃帴绠★紝褰撳墠鐗堟湰淇濈暀瀹¤闂幆锛岀姝㈠墠绔紶鍏ュ簱浣嶃��
-        return quality.getId();
+        if (StrUtil.isNotBlank(batchCode)) {
+            return proBatchService.getBatchByCode(batchCode);
+        }
+        return null;
     }
 
     @Override
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/trace/MesProTracePublicServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/trace/MesProTracePublicServiceImpl.java
index 110c7b1..04ff12f 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/trace/MesProTracePublicServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/trace/MesProTracePublicServiceImpl.java
@@ -38,6 +38,9 @@
 
 import java.net.URLDecoder;
 import java.nio.charset.StandardCharsets;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -212,6 +215,7 @@
         fillAntiFake(resp, traceCode);
         // 鍙�夋墿灞曞尯鍧楋紙鏈夋暟鎹墠杩斿洖锛�
         fillOptionalModules(resp, batch);
+        fillTimelineAndDecision(resp);
         return resp;
     }
 
@@ -338,6 +342,47 @@
         }
     }
 
+    private void fillTimelineAndDecision(MesProTracePublicRespVO resp) {
+        List<MesProTracePublicRespVO.TimelineVO> timeline = new ArrayList<>();
+        if (resp.getProduceDate() != null) {
+            timeline.add(timeline("鐢熶骇", "BATCH_CREATED", "鐢熶骇鎵规寤虹珛", resp.getProduceDate(), null,
+                    resp.getBatchStatusName()));
+        }
+        if (resp.getProcessTraces() != null) {
+            resp.getProcessTraces().forEach(item -> timeline.add(timeline("鍔犲伐", item.getEventType(),
+                    item.getEventType() == null ? "宸ュ簭娴佽浆" : ("IN".equals(item.getEventType()) ? "宸ュ簭鎶曞叆" : "宸ュ簭浜у嚭"),
+                    item.getEventTime(), item.getQuantity(), item.getQualityStatus() == null ? null : String.valueOf(item.getQualityStatus()))));
+        }
+        if (resp.getInbound() != null) {
+            timeline.add(timeline("浠撳偍", "INBOUND", "浠撳偍鍏ュ簱", resp.getInbound().getEventTime(),
+                    resp.getInbound().getQuantity(), resp.getInbound().getStatus()));
+        }
+        if (resp.getSalesTargets() != null) {
+            resp.getSalesTargets().forEach(item -> timeline.add(timeline("閿�鍞�", "OUTBOUND", "閿�鍞嚭搴�",
+                    item.getSalesDate(), item.getQuantity(), item.getQualityStatus() == null ? null : String.valueOf(item.getQualityStatus()))));
+        }
+        timeline.sort(Comparator.comparing(MesProTracePublicRespVO.TimelineVO::getEventTime,
+                Comparator.nullsLast(Comparator.naturalOrder())));
+        resp.setTimeline(timeline);
+
+        MesProTracePublicRespVO.DecisionVO decision = new MesProTracePublicRespVO.DecisionVO();
+        decision.setQualityRisk(resp.getQuality() != null && Integer.valueOf(2).equals(resp.getQuality().getResult()));
+        decision.setInboundQuantity(resp.getInbound() == null ? null : resp.getInbound().getQuantity());
+        decision.setSalesTargetCount(resp.getSalesTargets() == null ? 0 : resp.getSalesTargets().size());
+        decision.setConsumerScanCount(resp.getAntiFake() == null ? 0L : resp.getAntiFake().getScanCount());
+        decision.setRiskLevel(Boolean.TRUE.equals(decision.getQualityRisk()) ? "HIGH" : "NORMAL");
+        resp.setDecision(decision);
+    }
+
+    private MesProTracePublicRespVO.TimelineVO timeline(String stage, String eventType, String eventName,
+                                                         java.time.LocalDateTime eventTime, BigDecimal quantity,
+                                                         String status) {
+        MesProTracePublicRespVO.TimelineVO vo = new MesProTracePublicRespVO.TimelineVO();
+        vo.setStage(stage).setEventType(eventType).setEventName(eventName).setEventTime(eventTime)
+                .setQuantity(quantity).setStatus(status);
+        return vo;
+    }
+
     // ==================== 鍚嶇О鏄犲皠 ====================
 
     private String userName(Long userId) {
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/batch/MesWmBatchServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/batch/MesWmBatchServiceImpl.java
index 313063f..f4ecec0 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/batch/MesWmBatchServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/batch/MesWmBatchServiceImpl.java
@@ -423,13 +423,21 @@
         if (proBatch == null || StrUtil.isBlank(proBatch.getCode())) {
             return null;
         }
-        // 1. 澶嶇敤宸叉湁浠撳簱鎵规锛堝悓涓�鐢熶骇鎵规鍙凤級
-        MesWmBatchDO existing = batchMapper.selectByCode(proBatch.getCode());
+        // 1. 浼樺厛鎸夌敓浜ф壒娆� ID 澶嶇敤锛屽吋瀹瑰巻鍙叉暟鎹啀鎸夋壒娆″彿鍖归厤
+        MesWmBatchDO existing = batchMapper.selectOne(MesWmBatchDO::getProBatchId, proBatch.getId());
+        if (existing == null) {
+            existing = batchMapper.selectByCode(proBatch.getCode());
+        }
         if (existing != null) {
+            if (existing.getProBatchId() == null && proBatch.getId() != null) {
+                existing.setProBatchId(proBatch.getId());
+                batchMapper.updateById(existing);
+            }
             return existing;
         }
         // 2. 鏂板缓浠撳簱鎵规锛氫互鐢熶骇鎵规鍙蜂綔涓轰粨搴撴壒娆″彿锛岃川閲忕姸鎬佺疆涓哄緟妫�楠�
         MesWmBatchDO batch = MesWmBatchDO.builder()
+                .proBatchId(proBatch.getId())
                 .code(proBatch.getCode())
                 .itemId(proBatch.getItemId())
                 .produceDate(proBatch.getProduceDate())
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 2e6a51f..8d9a29d 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
@@ -132,6 +132,18 @@
     List<MesWmMaterialStockDO> getMaterialStockListByItemAndBatch(Long itemId, Long batchId);
 
     /**
+     * 鏌ヨ鐗╂枡/鎵规鐨勫悎鏍煎簱搴撳瓨璁板綍锛堜粎 stock_state=20锛�
+     *
+     * <p>鍚屾壒娆″彲鑳藉悓鏃跺瓨鍦ㄦ殏瀛樺簱涓庡悎鏍煎簱涓ゆ潯璁板綍锛堢瀛愯川妫�鎸夎鎷嗘暟閲忥級锛�
+     * 閿�鍞嚭搴撻�夊簱瀛樺繀椤讳粠鍚堟牸搴撹褰曚腑閫夛紝閬垮厤閫変腑鏈川妫�鐨勬殏瀛樺簱瀛樸��</p>
+     *
+     * @param itemId  鐗╂枡缂栧彿
+     * @param batchId 鎵规缂栧彿
+     * @return 鍚堟牸搴撳簱瀛樿褰曞垪琛�
+     */
+    List<MesWmMaterialStockDO> getQualifiedMaterialStockListByItemAndBatch(Long itemId, Long batchId);
+
+    /**
      * 鏇存柊搴撳瓨鏁伴噺
      *
      * @param id 搴撳瓨璁板綍缂栧彿
@@ -165,16 +177,6 @@
      * @return 浠撳簱姹囨�诲垪琛�
      */
     List<MesWmStockSummaryRespVO> getStockSummaryByWarehouse(MesWmMaterialStockListReqVO reqVO);
-
-    /**
-     * 鎸変粨搴撴壒娆℃洿鏂板簱瀛樼姸鎬侊紙鐢ㄤ簬绉嶅瓙璐ㄦ鍚堟牸鍚庯紝灏嗘殏瀛樺簱鏁存壒杞负鍚堟牸搴擄級
-     *
-     * @param batchId   浠撳簱鎵规ID
-     * @param toState   鐩爣搴撳瓨鐘舵��
-     * @param fromState 鍘熷簱瀛樼姸鎬侊紙闄愬畾鍙洿鏂拌鐘舵�佺殑璁板綍锛�
-     * @return 褰卞搷琛屾暟
-     */
-    int updateStockStateByBatchId(Long batchId, Integer toState, Integer fromState);
 
     /**
      * 搴撲綅娣锋斁瑙勫垯鏍¢獙
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 4318153..b3b7ec6 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
@@ -29,6 +29,9 @@
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO;
 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.materialstockbag.MesWmMaterialStockBagService;
+import cn.iocoder.yudao.module.mes.dal.mysql.wm.materialstockbag.MesWmMaterialStockBagMapper;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstockbag.MesWmMaterialStockBagDO;
 import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
 import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseService;
 import jakarta.annotation.Resource;
@@ -44,6 +47,7 @@
 import java.util.function.Function;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
 
@@ -74,6 +78,11 @@
     @Resource
     @Lazy
     private MesWmBatchService batchService;
+
+    @Resource
+    private MesWmMaterialStockBagMapper materialStockBagMapper;
+    @Resource
+    private MesWmMaterialStockBagService materialStockBagService;
 
     @Override
     public MesWmMaterialStockDO getMaterialStock(Long id) {
@@ -121,9 +130,27 @@
             Assert.notNull(virtualWarehouse, "铏氭嫙浠撳簱锛圵IP_VIRTUAL_WAREHOUSE锛変笉瀛樺湪");
             virtualWarehouseId = virtualWarehouse.getId();
         }
+        // 1.4 瑙f瀽 warehouseType锛氳浆鎹负璇ョ被鍨嬩粨搴� warehouseIds锛堜笌 warehouseId / virtualFilter 骞跺瓨锛屼繚鐣欏吋瀹癸級
+        List<Long> warehouseIds = resolveWarehouseIdsByType(pageReqVO.getWarehouseType());
+        if (warehouseIds != null && warehouseIds.isEmpty()) {
+            return PageResult.empty();
+        }
 
         // 2. 鍒嗛〉鏌ヨ
-        return materialStockMapper.selectPage(pageReqVO, itemTypeIds, itemIds, virtualWarehouseId);
+        return materialStockMapper.selectPage(pageReqVO, itemTypeIds, itemIds, virtualWarehouseId, warehouseIds);
+    }
+
+    /**
+     * 灏嗕粨搴撶被鍨嬭В鏋愪负浠撳簱 id 闆嗗悎锛坵arehouseType 涓� null 鏃朵笉鍔犺繃婊わ紝杩斿洖 null锛�
+     *
+     * @param warehouseType 浠撳簱绫诲瀷锛岃 {@link cn.iocoder.yudao.module.mes.enums.wm.MesWmWarehouseTypeEnum}
+     * @return 璇ョ被鍨嬩粨搴� id 闆嗗悎锛泈arehouseType 涓� null 杩斿洖 null锛涜绫诲瀷鏃犱粨搴撹繑鍥炵┖闆嗗悎
+     */
+    private List<Long> resolveWarehouseIdsByType(Integer warehouseType) {
+        if (warehouseType == null) {
+            return null;
+        }
+        return convertList(warehouseService.getWarehouseListByType(warehouseType), MesWmWarehouseDO::getId);
     }
 
     @Override
@@ -169,7 +196,11 @@
 
     @Override
     public List<MesWmMaterialStockDO> getMaterialStockList(MesWmMaterialStockListReqVO reqVO) {
-        return materialStockMapper.selectList(reqVO);
+        List<Long> warehouseIds = resolveWarehouseIdsByType(reqVO.getWarehouseType());
+        if (warehouseIds != null && warehouseIds.isEmpty()) {
+            return Collections.emptyList();
+        }
+        return materialStockMapper.selectList(reqVO, warehouseIds);
     }
 
     @Override
@@ -282,7 +313,17 @@
     @Override
     public MesWmMaterialStockDO getMaterialStockByCompositeKey(Long itemId, Long warehouseId, Long locationId, Long areaId,
                                                                 Long batchId, Long vendorId) {
-        return materialStockMapper.selectByCompositeKey(itemId, warehouseId, locationId, areaId, batchId, vendorId);
+        // 鍑哄簱/鍗犵敤鍙粠鍚堟牸搴撳尮閰嶏紝閬垮厤鎵e埌鍚屾壒娆$殑鏆傚瓨搴擄紙绉嶅瓙璐ㄦ鎸夎鎷嗘暟閲忓悗鍚屾壒娆″弻鐘舵�佸苟瀛橈級
+        return materialStockMapper.selectQualifiedByCompositeKey(itemId, warehouseId, locationId, areaId, batchId, vendorId);
+    }
+
+    @Override
+    public List<MesWmMaterialStockDO> getQualifiedMaterialStockListByItemAndBatch(Long itemId, Long batchId) {
+        List<Long> qualifiedWarehouseIds = warehouseService.getQualifiedWarehouseIds();
+        if (CollUtil.isEmpty(qualifiedWarehouseIds)) {
+            return Collections.emptyList();
+        }
+        return materialStockMapper.selectQualifiedListByItemAndBatch(itemId, batchId, qualifiedWarehouseIds);
     }
 
     @Override
@@ -302,11 +343,6 @@
     @Override
     public void updateMaterialStockState(Long id, Integer stockState) {
         materialStockMapper.updateById(new MesWmMaterialStockDO().setId(id).setStockState(stockState));
-    }
-
-    @Override
-    public int updateStockStateByBatchId(Long batchId, Integer toState, Integer fromState) {
-        return materialStockMapper.updateStockStateByBatchId(batchId, toState, fromState);
     }
 
     @Override
@@ -356,7 +392,11 @@
                 || ObjUtil.notEqual(stock.getAreaId(), areaId)) {
             throw exception(WM_MATERIAL_STOCK_SELECTION_MISMATCH);
         }
-        // 3. 鏍¢獙搴撳瓨鏁伴噺鍏呰冻锛堝鏋滃墠绔紶浜� quantity锛屽垯蹇呴』淇濊瘉搴撳瓨鏁伴噺 >= quantity锛�
+        // 3. 鏍¢獙搴撳瓨璁板綍蹇呴』鏄悎鏍煎簱锛堟殏瀛樺簱瀛樻湭璐ㄦ锛屼笉鍙敤浜庡嚭搴�/鍗犵敤锛�
+        if (!MesWmStockStateEnum.QUALIFIED.getState().equals(stock.getStockState())) {
+            throw exception(WM_MATERIAL_STOCK_NOT_QUALIFIED);
+        }
+        // 4. 鏍¢獙搴撳瓨鏁伴噺鍏呰冻锛堝鏋滃墠绔紶浜� quantity锛屽垯蹇呴』淇濊瘉搴撳瓨鏁伴噺 >= quantity锛�
         if (quantity != null && stock.getQuantity() != null && stock.getQuantity().compareTo(quantity) < 0) {
             throw exception(WM_MATERIAL_STOCK_INSUFFICIENT);
         }
@@ -592,6 +632,32 @@
                 .setRelatedTransactionId(outTransactionId)
                 .setCheckFlag(false);
         wmTransactionService.createTransaction(inTransaction);
+
+        // 9. 鏁翠綅缃Щ搴撴椂锛岄�愯杩佺Щ琚嬬爜鏄庣粏浣嶇疆锛堟暟閲忕瓑浜庢簮搴撲綅娲诲姩琚嬫暟涓斾负鏁存暟鎵嶈涓烘暣浣嶇Щ搴擄級
+        MesWmMaterialStockDO finalTargetStock = materialStockMapper.selectByCompositeKeyIgnoreVendor(
+                sourceStock.getItemId(), reqVO.getToWarehouseId(), reqVO.getToLocationId(),
+                reqVO.getToAreaId(), sourceStock.getBatchId());
+        relocateBagsForMove(sourceStock, reqVO.getQuantity(), inTransaction, finalTargetStock);
+    }
+
+    /**
+     * 鏁翠綅绉诲簱锛氬皢婧愬簱浣嶇殑娲诲姩琚嬬爜鏄庣粏杩佺Щ鍒扮洰鏍囧簱浣嶃��
+     * <p>
+     * 浠呭綋绉诲簱鏁伴噺绛変簬婧愬簱浣嶆椿鍔ㄨ鏁颁笖涓烘暣鏁版椂鎵ц锛堟暣浣嶇疆鎼Щ锛夛紝灏嗘瘡琚嬫槑缁嗙殑搴撳瓨姹囨�汇�佷粨搴撱�佸簱鍖恒�佸簱浣�
+     * 鏇存柊涓虹洰鏍囦綅缃紱閮ㄥ垎绉诲簱/鏃犺鐮佹槑缁嗙殑鏃у簱瀛樹笉鍋氳鐮佽縼绉伙紝淇濇寔姹囨�诲簱瀛樺彉鍔ㄥ嵆鍙��
+     */
+    private void relocateBagsForMove(MesWmMaterialStockDO sourceStock, BigDecimal quantity,
+                                     MesWmTransactionSaveReqDTO moveInReq, MesWmMaterialStockDO targetStock) {
+        List<MesWmMaterialStockBagDO> bags = materialStockBagMapper.selectActiveListByItemAndBatch(
+                sourceStock.getItemId(), sourceStock.getBatchId(), sourceStock.getStockState());
+        if (CollUtil.isEmpty(bags) || targetStock == null || quantity == null
+                || quantity.compareTo(BigDecimal.valueOf(bags.size())) != 0) {
+            return;
+        }
+        for (MesWmMaterialStockBagDO bag : bags) {
+            moveInReq.setBagCodeId(bag.getBagCodeId()).setBagCode(bag.getBagCode());
+            materialStockBagService.moveIn(moveInReq, targetStock, null);
+        }
     }
 
 }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstockbag/MesWmMaterialStockBagService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstockbag/MesWmMaterialStockBagService.java
new file mode 100644
index 0000000..ce3eb1d
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstockbag/MesWmMaterialStockBagService.java
@@ -0,0 +1,59 @@
+package cn.iocoder.yudao.module.mes.service.wm.materialstockbag;
+
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstockbag.MesWmMaterialStockBagDO;
+import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
+
+/**
+ * MES 搴撳瓨琚嬬爜鏄庣粏 Service 鎺ュ彛
+ *
+ * 缁熶竴灏佽琚嬬爜搴撳瓨鏄庣粏鐨勫缓绔嬨�佽縼绉汇�佸嚭搴撱�佹姤搴熼�昏緫锛屼緵鎵�鏈夊甫琚嬬爜鐨勫簱瀛樺彉鍔ㄥ叆鍙e鐢ㄣ��
+ * 姣忎釜琚嬬爜鍦ㄥ悓涓�鏃堕棿鍙湁涓�鏉″湪搴撴槑缁嗭紙鍞竴绾︽潫 bag_code锛夛紝娴佽浆閫氳繃鏇存柊鏄庣粏浣嶇疆/鐘舵�佸疄鐜般��
+ */
+public interface MesWmMaterialStockBagService {
+
+    /**
+     * 鍏ュ簱锛氬缓绔嬫垨婵�娲绘椿鍔ㄨ鐮佹槑缁嗐��
+     * <p>
+     * 琚嬬爜棣栨鍏ュ簱鏃舵柊寤烘槑缁嗭紱宸插嚭搴�/宸叉姤搴熺殑琚嬬爜鍐嶆鍏ュ簱鏃舵縺娲讳负鍦ㄥ簱骞舵洿鏂颁綅缃紱宸插湪搴撳垯瑙嗕负閲嶅鍏ュ簱鎶涘嚭寮傚父銆�
+     *
+     * @param reqDTO        浜嬪姟璇锋眰
+     * @param stock         鐩爣搴撳瓨姹囨�昏褰�
+     * @param transactionId 鏈瑪浜嬪姟 ID
+     * @return 琚嬬爜鏄庣粏
+     */
+    MesWmMaterialStockBagDO inbound(MesWmTransactionSaveReqDTO reqDTO, MesWmMaterialStockDO stock, Long transactionId);
+
+    /**
+     * 鍑哄簱锛氬皢鎸囧畾琚嬬爜鏄庣粏缃负宸插嚭搴撱��
+     *
+     * @param reqDTO        浜嬪姟璇锋眰锛堥渶鎼哄甫 bagCode/bagCodeId锛�
+     * @param transactionId 鏈瑪浜嬪姟 ID
+     */
+    void outbound(MesWmTransactionSaveReqDTO reqDTO, Long transactionId);
+
+    /**
+     * 绉诲叆/杩佺Щ锛氭洿鏂版椿鍔ㄨ鐮佹槑缁嗙殑浣嶇疆涓庡簱瀛樼姸鎬侊紙绉诲簱銆佽皟鎷ㄣ�佽川妫�鍚堟牸杩佺Щ锛夈��
+     *
+     * @param reqDTO        浜嬪姟璇锋眰锛堥渶鎼哄甫 bagCode锛�
+     * @param targetStock   鐩爣搴撳瓨姹囨�昏褰�
+     * @param transactionId 鏈瑪浜嬪姟 ID
+     */
+    void moveIn(MesWmTransactionSaveReqDTO reqDTO, MesWmMaterialStockDO targetStock, Long transactionId);
+
+    /**
+     * 鎶ュ簾锛氬皢鎸囧畾琚嬬爜鏄庣粏缃负鎶ュ簾銆�
+     *
+     * @param reqDTO        浜嬪姟璇锋眰锛堥渶鎼哄甫 bagCode锛�
+     * @param transactionId 鏈瑪浜嬪姟 ID
+     */
+    void scrap(MesWmTransactionSaveReqDTO reqDTO, Long transactionId);
+
+    /**
+     * 鏍规嵁搴撳瓨姹囨�� ID 鏌ヨ鍦ㄥ簱琚嬬爜鏄庣粏銆�
+     *
+     * @param materialStockId 搴撳瓨姹囨�� ID
+     * @return 琚嬬爜鏄庣粏锛堟棤鍒� null锛�
+     */
+    MesWmMaterialStockBagDO getActiveByMaterialStockId(Long materialStockId);
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstockbag/MesWmMaterialStockBagServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstockbag/MesWmMaterialStockBagServiceImpl.java
new file mode 100644
index 0000000..3929602
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstockbag/MesWmMaterialStockBagServiceImpl.java
@@ -0,0 +1,165 @@
+package cn.iocoder.yudao.module.mes.service.wm.materialstockbag;
+
+import cn.hutool.core.util.ObjUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstockbag.MesWmMaterialStockBagDO;
+import cn.iocoder.yudao.module.mes.dal.mysql.wm.materialstockbag.MesWmMaterialStockBagMapper;
+import cn.iocoder.yudao.module.mes.enums.wm.MesWmMaterialStockBagStatusEnum;
+import cn.iocoder.yudao.module.mes.enums.wm.MesWmStockStateEnum;
+import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.validation.annotation.Validated;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
+
+/**
+ * MES 搴撳瓨琚嬬爜鏄庣粏 Service 瀹炵幇绫�
+ *
+ * 琚嬬爜鏄庣粏鐢熷懡鍛ㄦ湡锛氶娆″叆搴撳缓绔嬶紙ACTIVE锛夆啋 绉诲簱/璐ㄦ杩佺Щ浣嶇疆鐘舵�� 鈫� 鍑哄簱/鎶ュ簾缁撴潫锛圤UT/SCRAPPED锛夈��
+ * 鍞竴绾︽潫 bag_code 淇濊瘉姣忎釜琚嬬爜浠呬竴鏉℃槑缁嗭紝澶嶇敤鍚屼竴琛屽畬鎴愬叏鐢熷懡鍛ㄦ湡娴佽浆銆�
+ */
+@Service
+@Validated
+public class MesWmMaterialStockBagServiceImpl implements MesWmMaterialStockBagService {
+
+    @Resource
+    private MesWmMaterialStockBagMapper materialStockBagMapper;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public MesWmMaterialStockBagDO inbound(MesWmTransactionSaveReqDTO reqDTO, MesWmMaterialStockDO stock, Long transactionId) {
+        String bagCode = reqDTO.getBagCode();
+        if (StrUtil.isBlank(bagCode)) {
+            throw exception(WM_MATERIAL_STOCK_BAG_REQUIRED);
+        }
+        // 1. 鏌ヨ璇ヨ鐮佹棦鏈夋槑缁嗭細浼樺厛鎸� bagCodeId锛屽叾娆℃寜 bagCode
+        MesWmMaterialStockBagDO existing = materialStockBagMapper.selectByBagCode(bagCode);
+        if (existing == null && reqDTO.getBagCodeId() != null) {
+            existing = materialStockBagMapper.selectByBagCodeId(reqDTO.getBagCodeId());
+        }
+        if (existing != null) {
+            // 2. 宸插湪搴� 鈫� 閲嶅鍏ュ簱锛岀姝�
+            if (MesWmMaterialStockBagStatusEnum.ACTIVE.getStatus().equals(existing.getStatus())) {
+                throw exception(WM_MATERIAL_STOCK_BAG_DUPLICATE, bagCode);
+            }
+            // 3. 宸插嚭搴�/鎶ュ簾 鈫� 閲嶆柊婵�娲诲苟鏇存柊浣嶇疆
+            return reactivate(existing, reqDTO, stock, transactionId);
+        }
+        // 4. 棣栨鍏ュ簱 鈫� 鏂板缓鏄庣粏
+        MesWmMaterialStockBagDO bag = MesWmMaterialStockBagDO.builder()
+                .materialStockId(stock.getId())
+                .bagCodeId(reqDTO.getBagCodeId())
+                .bagCode(bagCode)
+                .itemId(stock.getItemId())
+                .batchId(stock.getBatchId())
+                .batchCode(stock.getBatchCode())
+                .vendorId(stock.getVendorId())
+                .warehouseId(stock.getWarehouseId())
+                .locationId(stock.getLocationId())
+                .areaId(stock.getAreaId())
+                .stockState(ObjUtil.defaultIfNull(reqDTO.getStockState(), stock.getStockState()))
+                .status(MesWmMaterialStockBagStatusEnum.ACTIVE.getStatus())
+                .quantity(BigDecimal.ONE)
+                .frozen(false)
+                .lastTransactionId(transactionId)
+                .inboundTime(LocalDateTime.now())
+                .build();
+        materialStockBagMapper.insert(bag);
+        return bag;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void outbound(MesWmTransactionSaveReqDTO reqDTO, Long transactionId) {
+        MesWmMaterialStockBagDO bag = requireActiveBag(reqDTO);
+        MesWmMaterialStockBagDO update = new MesWmMaterialStockBagDO();
+        update.setId(bag.getId());
+        update.setStatus(MesWmMaterialStockBagStatusEnum.OUT.getStatus());
+        update.setStockState(MesWmStockStateEnum.OUT.getState());
+        update.setLastTransactionId(transactionId);
+        update.setOutboundTime(LocalDateTime.now());
+        materialStockBagMapper.updateById(update);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void moveIn(MesWmTransactionSaveReqDTO reqDTO, MesWmMaterialStockDO targetStock, Long transactionId) {
+        MesWmMaterialStockBagDO bag = requireActiveBag(reqDTO);
+        MesWmMaterialStockBagDO update = new MesWmMaterialStockBagDO();
+        update.setId(bag.getId());
+        update.setMaterialStockId(targetStock.getId());
+        update.setWarehouseId(targetStock.getWarehouseId());
+        update.setLocationId(targetStock.getLocationId());
+        update.setAreaId(targetStock.getAreaId());
+        update.setStockState(ObjUtil.defaultIfNull(reqDTO.getStockState(), targetStock.getStockState()));
+        update.setLastTransactionId(transactionId);
+        materialStockBagMapper.updateById(update);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void scrap(MesWmTransactionSaveReqDTO reqDTO, Long transactionId) {
+        MesWmMaterialStockBagDO bag = requireActiveBag(reqDTO);
+        MesWmMaterialStockBagDO update = new MesWmMaterialStockBagDO();
+        update.setId(bag.getId());
+        update.setStatus(MesWmMaterialStockBagStatusEnum.SCRAPPED.getStatus());
+        update.setStockState(MesWmStockStateEnum.OUT.getState());
+        update.setQuantity(BigDecimal.ZERO);
+        update.setLastTransactionId(transactionId);
+        update.setOutboundTime(LocalDateTime.now());
+        materialStockBagMapper.updateById(update);
+    }
+
+    @Override
+    public MesWmMaterialStockBagDO getActiveByMaterialStockId(Long materialStockId) {
+        return materialStockBagMapper.selectOneActiveByMaterialStockId(materialStockId);
+    }
+
+    /**
+     * 鏌ヨ鎸囧畾琚嬬爜鐨勬椿鍔ㄦ槑缁嗭紝涓嶅瓨鍦ㄦ垨宸插嚭搴�/鎶ュ簾鍒欐姏寮傚父銆�
+     */
+    private MesWmMaterialStockBagDO requireActiveBag(MesWmTransactionSaveReqDTO reqDTO) {
+        MesWmMaterialStockBagDO bag = null;
+        if (reqDTO.getBagCodeId() != null) {
+            bag = materialStockBagMapper.selectActiveByBagCodeId(reqDTO.getBagCodeId());
+        }
+        if (bag == null && StrUtil.isNotBlank(reqDTO.getBagCode())) {
+            bag = materialStockBagMapper.selectActiveByBagCode(reqDTO.getBagCode());
+        }
+        if (bag == null) {
+            String code = StrUtil.blankToDefault(reqDTO.getBagCode(), String.valueOf(reqDTO.getBagCodeId()));
+            throw exception(WM_MATERIAL_STOCK_BAG_NOT_ACTIVE, code);
+        }
+        return bag;
+    }
+
+    private MesWmMaterialStockBagDO reactivate(MesWmMaterialStockBagDO existing, MesWmTransactionSaveReqDTO reqDTO,
+                                               MesWmMaterialStockDO stock, Long transactionId) {
+        MesWmMaterialStockBagDO update = new MesWmMaterialStockBagDO();
+        update.setId(existing.getId());
+        update.setMaterialStockId(stock.getId());
+        update.setItemId(stock.getItemId());
+        update.setBatchId(stock.getBatchId());
+        update.setBatchCode(stock.getBatchCode());
+        update.setVendorId(stock.getVendorId());
+        update.setWarehouseId(stock.getWarehouseId());
+        update.setLocationId(stock.getLocationId());
+        update.setAreaId(stock.getAreaId());
+        update.setStockState(ObjUtil.defaultIfNull(reqDTO.getStockState(), stock.getStockState()));
+        update.setStatus(MesWmMaterialStockBagStatusEnum.ACTIVE.getStatus());
+        update.setQuantity(BigDecimal.ONE);
+        update.setFrozen(false);
+        update.setLastTransactionId(transactionId);
+        update.setInboundTime(LocalDateTime.now());
+        update.setOutboundTime(null);
+        materialStockBagMapper.updateById(update);
+        return existing;
+    }
+}
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 a54a1eb..628720f 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
@@ -27,6 +27,7 @@
 import cn.iocoder.yudao.module.mes.dal.mysql.wm.productsales.MesWmProductSalesMapper;
 import cn.iocoder.yudao.module.mes.dal.mysql.wm.productsales.MesWmProductSalesLineMapper;
 import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
+import cn.iocoder.yudao.module.mes.enums.pro.MesProBagCodeQcStatusEnum;
 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;
@@ -43,8 +44,6 @@
 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.qc.oqc.MesQcOqcService;
-import cn.iocoder.yudao.module.erp.api.sale.ErpSaleOrderApi;
-import cn.iocoder.yudao.module.erp.api.sale.enums.ErpSaleOrderOutStatusEnum;
 import jakarta.annotation.Resource;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.context.annotation.Lazy;
@@ -104,9 +103,6 @@
 
     @Resource
     private MesTraceIntegrityService integrityService;
-
-    @Resource
-    private ErpSaleOrderApi saleOrderApi;
 
     @Override
     public Long createProductSales(MesWmProductSalesSaveReqVO createReqVO) {
@@ -242,6 +238,15 @@
         if (pallet != null && pallet.getBatchId() == null) {
             throw exception(PRO_PALLET_NOT_BIND_BATCH);
         }
+        // 绉嶅瓙璐ㄦ鎸夎鐮佺矑搴︽祦杞悗锛屽嚭搴撳彧鍏佽宸茶川妫�鍚堟牸鐨勮鐮�/鎵樼洏
+        if (pallet != null) {
+            List<MesProBagCodeDO> palletBags = bagCodeService.getBagCodeListByPalletId(pallet.getId());
+            if (palletBags.stream().anyMatch(b -> !MesProBagCodeQcStatusEnum.QUALIFIED.getStatus().equals(b.getQcStatus()))) {
+                throw exception(WM_PRODUCT_SALES_SCAN_PALLET_NOT_ALL_QUALIFIED);
+            }
+        } else if (!MesProBagCodeQcStatusEnum.QUALIFIED.getStatus().equals(bag.getQcStatus())) {
+            throw exception(WM_PRODUCT_SALES_SCAN_BAG_NOT_QUALIFIED);
+        }
         Long codeId = pallet != null ? pallet.getId() : bag.getId();
         MesWmProductSalesDetailDO existed = pallet != null
                 ? productSalesDetailMapper.selectBySalesIdAndPalletCodeId(reqVO.getSalesId(), codeId)
@@ -270,7 +275,8 @@
         if (quantity == null) {
             quantity = line.getQuantity();
         }
-        List<MesWmMaterialStockDO> stocks = materialStockService.getMaterialStockListByItemAndBatch(batch.getItemId(), wmBatch.getId());
+        // 鍙粠鍚堟牸搴撹褰曚腑閫夊簱瀛橈紙鍚屾壒娆℃殏瀛樺簱璁板綍涓嶅彲鍑哄簱锛�
+        List<MesWmMaterialStockDO> stocks = materialStockService.getQualifiedMaterialStockListByItemAndBatch(batch.getItemId(), wmBatch.getId());
         BigDecimal outboundQuantity = quantity;
         MesWmMaterialStockDO stock = stocks.stream().filter(item -> item.getQuantity().compareTo(outboundQuantity) >= 0).findFirst().orElse(null);
         if (stock == null) {
@@ -338,12 +344,6 @@
         // 鎵ц鎷h揣锛堝緟鎷h揣 鈫� 寰呭~鍐欒繍鍗曪級
         productSalesMapper.updateById(new MesWmProductSalesDO()
                 .setId(id).setStatus(MesWmProductSalesStatusEnum.SHIPPING.getStatus()));
-
-        // 鍥炲啓 ERP 閿�鍞鍗曞嚭搴撶姸鎬佷负"閮ㄥ垎鍑哄簱"锛堣〃绀哄凡鎷h揣/鍗犵敤锛�
-        if (sales.getSaleOrderId() != null) {
-            saleOrderApi.updateOrderOutStatusTo(sales.getSaleOrderId(),
-                    ErpSaleOrderOutStatusEnum.PART_OUT.getStatus());
-        }
     }
 
     @Override
@@ -392,10 +392,7 @@
             updateSalesNoticeOutCount(sales);
         }
 
-        // 6. 鍥炲啓 ERP 閿�鍞鍗曠殑鍑哄簱鏁伴噺鍜岀姸鎬�
-        updateErpSaleOrderOutCount(sales);
-
-        // 7. 鍥炲啓琚嬬爜/鎵樼洏鍑哄簱鍘诲悜锛堜緵绐滆揣绋芥煡瀹氫綅缁忛攢鍟嗗湴鍖猴級锛屽苟鐢熸垚鍑哄簱瀹屾暣鎬ф憳瑕�
+        // 6. 鍥炲啓琚嬬爜/鎵樼洏鍑哄簱鍘诲悜锛堜緵绐滆揣绋芥煡瀹氫綅缁忛攢鍟嗗湴鍖猴級锛屽苟鐢熸垚鍑哄簱瀹屾暣鎬ф憳瑕�
         List<MesWmProductSalesDetailDO> outDetails = productSalesDetailService.getProductSalesDetailListBySalesId(id);
         for (MesWmProductSalesDetailDO detail : outDetails) {
             if (detail.getBagCodeId() != null) {
@@ -425,43 +422,13 @@
         }
     }
 
-    /**
-     * 鍥炲啓 ERP 閿�鍞鍗曠殑鍑哄簱鏁伴噺鍜岀姸鎬�
-     */
-    private void updateErpSaleOrderOutCount(MesWmProductSalesDO sales) {
-        // 鑾峰彇鍑哄簱鍗曡
-        List<MesWmProductSalesLineDO> lines = productSalesLineService.getProductSalesLineListBySalesId(sales.getId());
-        if (CollUtil.isEmpty(lines)) {
-            return;
-        }
-        // 鐢ㄤ簬瀛樺偍閿�鍞鍗� ID锛岄伩鍏嶉噸澶嶆洿鏂�
-        Long saleOrderId = null;
-        // 閬嶅巻琛岋紝鎸� saleOrderItemId 鏇存柊鍑哄簱鏁伴噺
-        for (MesWmProductSalesLineDO line : lines) {
-            if (line.getSaleOrderItemId() != null && line.getQuantity() != null) {
-                saleOrderApi.updateOrderItemOutCount(line.getSaleOrderItemId(), line.getQuantity());
-                // 鑾峰彇閿�鍞鍗� ID锛堜粠绗竴涓湁鏁堢殑鏄庣粏椤硅幏鍙栵級
-                if (saleOrderId == null) {
-                    cn.iocoder.yudao.module.erp.api.sale.dto.ErpSaleOrderItemRespDTO itemDTO =
-                            saleOrderApi.getSaleOrderItem(line.getSaleOrderItemId()).getCheckedData();
-                    if (itemDTO != null && itemDTO.getOrderId() != null) {
-                        saleOrderId = itemDTO.getOrderId();
-                    }
-                }
-            }
-        }
-        // 鏇存柊閿�鍞鍗曠殑鍑哄簱鐘舵��
-        if (saleOrderId != null) {
-            saleOrderApi.updateOrderOutStatus(saleOrderId);
-        }
-    }
-
     private void createTransactionList(MesWmProductSalesDO sales) {
         List<MesWmProductSalesDetailDO> details = productSalesDetailService.getProductSalesDetailListBySalesId(sales.getId());
         wmTransactionService.createTransactionList(convertList(details, detail -> new MesWmTransactionSaveReqDTO()
                 .setType(MesWmTransactionTypeEnum.OUT.getType()).setItemId(detail.getItemId())
                 .setQuantity(detail.getQuantity().negate()) // 鍑哄簱鏁伴噺涓鸿礋鏁�
                 .setBatchId(detail.getBatchId()).setBatchCode(detail.getBatchCode())
+                .setBagCodeId(detail.getBagCodeId()) // 琚嬬爜鍨嬫槑缁嗗嚭搴撴椂缃鐮佸凡鍑哄簱锛涙墭鐩樺瀷鏄庣粏涓嶉�愯澶勭悊
                 .setWarehouseId(detail.getWarehouseId()).setLocationId(detail.getLocationId()).setAreaId(detail.getAreaId())
                 .setBizType(MesBizTypeConstants.WM_PRODUCT_SALES).setBizId(sales.getId())
                 .setBizCode(sales.getCode()).setBizLineId(detail.getLineId())
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/salesnotice/MesWmSalesNoticeLineServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/salesnotice/MesWmSalesNoticeLineServiceImpl.java
index 7f40f15..fed11df 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/salesnotice/MesWmSalesNoticeLineServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/salesnotice/MesWmSalesNoticeLineServiceImpl.java
@@ -7,16 +7,19 @@
 import cn.iocoder.yudao.module.mes.controller.admin.wm.salesnotice.vo.line.MesWmSalesNoticeLinePageReqVO;
 import cn.iocoder.yudao.module.mes.controller.admin.wm.salesnotice.vo.line.MesWmSalesNoticeLineSaveReqVO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.salesnotice.MesWmSalesNoticeDO;
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.salesnotice.MesWmSalesNoticeLineDO;
 import cn.iocoder.yudao.module.mes.dal.mysql.wm.salesnotice.MesWmSalesNoticeLineMapper;
 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.materialstock.MesWmMaterialStockService;
 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.List;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -38,6 +41,8 @@
     private MesWmSalesNoticeService salesNoticeService;
     @Resource
     private MesWmBatchService batchService;
+    @Resource
+    private MesWmMaterialStockService materialStockService;
     @Resource
     private MesMdmItemService mdmItemService;
 
@@ -118,12 +123,36 @@
     }
 
     private void validateLineSaveData(MesWmSalesNoticeLineSaveReqVO reqVO) {
-        // 鏍¢獙鐖跺崟鎹瓨鍦ㄤ笖涓鸿崏绋跨姸鎬�
         validateNoticeStatusDraft(reqVO.getNoticeId());
-        // 鏍¢獙鐗╂枡瀛樺湪
-        if (reqVO.getItemId() != null) {
-            mdmItemService.validateItemExistsAndEnable(reqVO.getItemId());
+        mdmItemService.validateItemExistsAndEnable(reqVO.getItemId());
+
+        MesWmBatchDO batch = resolveBatch(reqVO);
+        if (batch == null) {
+            throw exception(WM_MATERIAL_STOCK_NOT_EXISTS);
         }
+        if (ObjUtil.notEqual(batch.getItemId(), reqVO.getItemId())) {
+            throw exception(WM_MATERIAL_STOCK_NOT_EXISTS);
+        }
+        BigDecimal qualifiedQuantity = materialStockService
+                .getQualifiedMaterialStockListByItemAndBatch(reqVO.getItemId(), batch.getId()).stream()
+                .map(MesWmMaterialStockDO::getQuantity)
+                .filter(quantity -> quantity != null)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+        if (qualifiedQuantity.compareTo(reqVO.getQuantity()) < 0) {
+            throw exception(WM_MATERIAL_STOCK_INSUFFICIENT);
+        }
+        reqVO.setBatchId(batch.getId());
+        reqVO.setBatchCode(batch.getCode());
+    }
+
+    private MesWmBatchDO resolveBatch(MesWmSalesNoticeLineSaveReqVO reqVO) {
+        if (reqVO.getBatchId() != null) {
+            return batchService.getBatch(reqVO.getBatchId());
+        }
+        if (StrUtil.isNotBlank(reqVO.getBatchCode())) {
+            return batchService.getBatchByCode(reqVO.getBatchCode());
+        }
+        return null;
     }
 
     /**
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/salesnotice/MesWmSalesNoticeService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/salesnotice/MesWmSalesNoticeService.java
index be5338c..18e1cd2 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/salesnotice/MesWmSalesNoticeService.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/salesnotice/MesWmSalesNoticeService.java
@@ -89,14 +89,6 @@
     }
 
     /**
-     * 浠庨攢鍞鍗曠敓鎴愬彂璐ч�氱煡鍗�
-     *
-     * @param saleOrderId 閿�鍞鍗旾D
-     * @return 鍙戣揣閫氱煡鍗旾D
-     */
-    Long generateSalesNoticeFromSaleOrder(Long saleOrderId);
-
-    /**
      * 瀹屾垚鍙戣揣閫氱煡鍗曪紙鍑哄簱瀹屾垚鍚庤皟鐢級
      * 绱姞鍑哄簱鏁伴噺骞惰绠楀嚭搴撶姸鎬�
      *
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/salesnotice/MesWmSalesNoticeServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/salesnotice/MesWmSalesNoticeServiceImpl.java
index a233210..115bebf 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/salesnotice/MesWmSalesNoticeServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/salesnotice/MesWmSalesNoticeServiceImpl.java
@@ -18,10 +18,6 @@
 import cn.iocoder.yudao.module.mes.enums.wm.MesWmProductSalesStatusEnum;
 import cn.iocoder.yudao.module.mes.service.md.autocode.MesMdAutoCodeRecordService;
 import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi;
-import cn.iocoder.yudao.module.erp.api.sale.ErpSaleOrderApi;
-import cn.iocoder.yudao.module.erp.api.sale.dto.ErpSaleOrderItemRespDTO;
-import cn.iocoder.yudao.module.erp.api.sale.dto.ErpSaleOrderRespDTO;
-import cn.iocoder.yudao.module.erp.api.sale.enums.ErpSaleOrderOutStatusEnum;
 import jakarta.annotation.Resource;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
@@ -53,8 +49,6 @@
     @Resource
     private CrmCustomerApi customerApi;
 
-    @Resource
-    private ErpSaleOrderApi saleOrderApi;
     @Resource
     private MesMdAutoCodeRecordService autoCodeRecordService;
 
@@ -125,7 +119,7 @@
     @Transactional(rollbackFor = Exception.class)
     public void submitSalesNotice(Long id) {
         // 1.1 鏍¢獙瀛樺湪 + 鑽夌鐘舵��
-        MesWmSalesNoticeDO notice = validateSalesNoticeExistsAndDraft(id);
+        validateSalesNoticeExistsAndDraft(id);
         // 1.2 妫�鏌ユ槸鍚︽湁琛岄」鐩�
         List<MesWmSalesNoticeLineDO> lines = salesNoticeLineService.getSalesNoticeLineListByNoticeId(id);
         if (CollUtil.isEmpty(lines)) {
@@ -135,12 +129,6 @@
         // 2. 鏇存柊鐘舵�侊細鑽夌 鈫� 寰呭嚭搴�
         salesNoticeMapper.updateById(new MesWmSalesNoticeDO()
                 .setId(id).setStatus(APPROVED.getStatus()));
-
-        // 3. 鍥炲啓 ERP 閿�鍞鍗曞嚭搴撶姸鎬佷负"宸查鐣�"
-        if (notice.getSaleOrderId() != null) {
-            saleOrderApi.updateOrderOutStatusTo(notice.getSaleOrderId(),
-                    ErpSaleOrderOutStatusEnum.RESERVED.getStatus());
-        }
     }
 
     @Override
@@ -188,54 +176,6 @@
         if (ObjUtil.notEqual(id, notice.getId())) {
             throw exception(WM_SALES_NOTICE_CODE_DUPLICATE);
         }
-    }
-
-    @Override
-    @Transactional(rollbackFor = Exception.class)
-    public Long generateSalesNoticeFromSaleOrder(Long saleOrderId) {
-        // 1. 鏍¢獙鏄惁宸茬敓鎴愯繃鍙戣揣閫氱煡鍗曪紙闃叉閲嶅鐢熸垚锛�
-        MesWmSalesNoticeDO existNotice = salesNoticeMapper.selectBySaleOrderId(saleOrderId);
-        if (existNotice != null) {
-            throw exception(WM_SALES_NOTICE_ALREADY_EXISTS);
-        }
-
-        // 2. 鑾峰彇閿�鍞鍗曚俊鎭�
-        ErpSaleOrderRespDTO saleOrder = saleOrderApi.getSaleOrder(saleOrderId).getCheckedData();
-        if (saleOrder == null) {
-            throw exception(WM_SALES_NOTICE_SALE_ORDER_NOT_EXISTS);
-        }
-
-        // 3. 鑾峰彇閿�鍞鍗曟槑缁�
-        List<ErpSaleOrderItemRespDTO> saleOrderItems = saleOrderApi.getSaleOrderItemList(saleOrderId).getCheckedData();
-        if (CollUtil.isEmpty(saleOrderItems)) {
-            throw exception(WM_SALES_NOTICE_SALE_ORDER_ITEM_EMPTY);
-        }
-
-        // 4. 鐢熸垚鍙戣揣閫氱煡鍗曠紪鐮�
-        String code = autoCodeRecordService.generateAutoCode("SALESNOTICE_CODE", null);
-
-        // 5. 鍒涘缓鍙戣揣閫氱煡鍗曚富琛�
-        MesWmSalesNoticeDO notice = new MesWmSalesNoticeDO();
-        notice.setCode(code);
-        notice.setName("閿�鍞鍗昜" + saleOrder.getNo() + "]鍙戣揣閫氱煡");
-        notice.setClientId(saleOrder.getCustomerId());
-        notice.setSaleOrderId(saleOrderId);
-        notice.setSaleOrderCode(saleOrder.getNo());
-        notice.setSalesDate(java.time.LocalDateTime.now());
-        notice.setStatus(PREPARE.getStatus());
-        salesNoticeMapper.insert(notice);
-
-        // 6. 閬嶅巻鏄庣粏鍒涘缓鍙戣揣閫氱煡鍗曡
-        for (ErpSaleOrderItemRespDTO saleOrderItem : saleOrderItems) {
-            MesWmSalesNoticeLineDO line = new MesWmSalesNoticeLineDO();
-            line.setNoticeId(notice.getId());
-            line.setSaleOrderItemId(saleOrderItem.getId());
-            line.setItemId(saleOrderItem.getProductId());
-            line.setQuantity(saleOrderItem.getCount());
-            salesNoticeLineService.createSalesNoticeLine(line);
-        }
-
-        return notice.getId();
     }
 
     @Override
@@ -350,8 +290,6 @@
         productSales.setCode(code);
         productSales.setName("鍙戣揣閫氱煡鍗昜" + notice.getCode() + "]鍑哄簱");
         productSales.setClientId(notice.getClientId());
-        productSales.setSaleOrderId(notice.getSaleOrderId());
-        productSales.setSalesOrderCode(notice.getSaleOrderCode());
         productSales.setNoticeId(noticeId);
         productSales.setSalesDate(notice.getSalesDate());
         // 鏀惰揣淇℃伅浠庡彂璐ч�氱煡鍗曞甫鍏�
@@ -365,7 +303,6 @@
         for (MesWmSalesNoticeLineDO noticeLine : noticeLines) {
             MesWmProductSalesLineDO salesLine = new MesWmProductSalesLineDO();
             salesLine.setSalesId(productSales.getId());
-            salesLine.setSaleOrderItemId(noticeLine.getSaleOrderItemId());
             salesLine.setNoticeLineId(noticeLine.getId());
             salesLine.setItemId(noticeLine.getItemId());
             salesLine.setQuantity(noticeLine.getQuantity());
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 fd75003..eb43c7e 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
@@ -16,6 +16,7 @@
 import cn.iocoder.yudao.module.mes.dal.mysql.wm.transaction.MesWmTransactionMapper;
 import cn.iocoder.yudao.module.mes.enums.wm.MesWmStockStateEnum;
 import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum;
+import cn.iocoder.yudao.module.mes.enums.wm.MesWmWarehouseTypeEnum;
 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.materialstock.MesWmMaterialStockService;
@@ -23,6 +24,7 @@
 import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
 import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseLocationService;
 import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseService;
+import cn.iocoder.yudao.module.mes.service.wm.materialstockbag.MesWmMaterialStockBagService;
 import jakarta.annotation.Resource;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -57,6 +59,9 @@
     private MesWmWarehouseLocationService locationService;
     @Resource
     private MesWmWarehouseAreaService areaService;
+
+    @Resource
+    private MesWmMaterialStockBagService materialStockBagService;
 
     @Override
     @Transactional(rollbackFor = Exception.class)
@@ -127,11 +132,20 @@
         materialStockService.updateMaterialStockQuantity(materialStock.getId(), reqDTO.getQuantity(), checkFlag);
         MesWmMaterialStockDO updatedStock = materialStockService.getMaterialStock(materialStock.getId());
         BigDecimal newQuantity = updatedStock.getQuantity();
-        // 搴撳瓨鐘舵�侊細鏄惧紡鎸囧畾浼樺厛锛堝鎵爜鍏ユ殏瀛樺簱锛夛紝鍚﹀垯鎸夋暟閲忚嚜鍔ㄥ垽瀹氾紙鈮�0 宸插嚭搴擄紝>0 鍚堟牸搴擄級
+        // 搴撳瓨鐘舵�侊細鏄惧紡鎸囧畾浼樺厛锛堝鎵爜鍏ユ殏瀛樺簱锛夛紝鍚﹀垯鑷姩鍒ゅ畾鈥斺�斺墹0 宸插嚭搴擄紱
+        // >0 鏃朵粎鐩爣浠撳簱涓哄悎鏍煎簱(20)鎵嶇疆鍚堟牸鎬侊紝鍚﹀垯淇濇寔鏆傚瓨鎬�(10)锛�
+        // 閬垮厤鐩樼偣/璋冩暣/璋冩嫧绛夋搷浣滄妸鏆傚瓨浠撳簱搴撳瓨缈绘垚鍚堟牸鎬�
         Integer stockState = reqDTO.getStockState();
         if (stockState == null) {
-            stockState = newQuantity.signum() <= 0
-                    ? MesWmStockStateEnum.OUT.getState() : MesWmStockStateEnum.QUALIFIED.getState();
+            if (newQuantity.signum() <= 0) {
+                stockState = MesWmStockStateEnum.OUT.getState();
+            } else {
+                MesWmWarehouseDO warehouse = warehouseService.getWarehouse(reqDTO.getWarehouseId());
+                boolean qualifiedWarehouse = warehouse != null
+                        && MesWmWarehouseTypeEnum.QUALIFIED.getType().equals(warehouse.getType());
+                stockState = qualifiedWarehouse
+                        ? MesWmStockStateEnum.QUALIFIED.getState() : MesWmStockStateEnum.TEMP.getState();
+            }
         }
         materialStockService.updateMaterialStockState(updatedStock.getId(), stockState);
 
@@ -139,14 +153,46 @@
         MesWmTransactionDO transaction = MesWmTransactionDO.builder()
                 .type(reqDTO.getType()).quantity(reqDTO.getQuantity()).transactionTime(LocalDateTime.now())
                 .itemId(reqDTO.getItemId()).batchId(reqDTO.getBatchId()).batchCode(reqDTO.getBatchCode())
+                .bagCodeId(reqDTO.getBagCodeId()).bagCode(reqDTO.getBagCode())
                 .warehouseId(reqDTO.getWarehouseId()).locationId(reqDTO.getLocationId()).areaId(reqDTO.getAreaId())
                 .bizType(reqDTO.getBizType()).bizId(reqDTO.getBizId()).bizCode(reqDTO.getBizCode()).bizLineId(reqDTO.getBizLineId())
                 .materialStockId(materialStock.getId()).relatedTransactionId(reqDTO.getRelatedTransactionId())
                 .build();
         transactionMapper.insert(transaction);
+        // 4. 甯﹁鐮佹椂锛屽悓姝ョ淮鎶ゅ簱瀛樿鐮佹槑缁嗭紙鍏ュ簱寤虹珛/婵�娲汇�佸嚭搴撶疆宸插嚭搴撱�佺Щ鍏ヨ縼绉汇�佹姤搴燂級
+        boolean skipBagManage = ObjUtil.defaultIfNull(reqDTO.getSkipBagManage(), false);
+        if (!skipBagManage && (StrUtil.isNotBlank(reqDTO.getBagCode()) || reqDTO.getBagCodeId() != null)) {
+            handleStockBag(reqDTO, materialStock, transaction.getId(), transactionType);
+        }
         return transaction.getId();
     }
 
+    /**
+     * 鎸変簨鍔$被鍨嬬淮鎶よ鐮佹槑缁嗭紝淇濊瘉姹囨�诲簱瀛樸�佽鐮佹槑缁嗐�佷簨鍔℃祦姘翠笁鑰呬竴鑷淬��
+     */
+    private void handleStockBag(MesWmTransactionSaveReqDTO reqDTO, MesWmMaterialStockDO materialStock,
+                                Long transactionId, MesWmTransactionTypeEnum transactionType) {
+        switch (transactionType) {
+            case MOVE_IN:
+                // 绉诲叆/璋冩嫧/璐ㄦ杩佺Щ锛氭洿鏂版槑缁嗕綅缃笌鐘舵��
+                materialStockBagService.moveIn(reqDTO, materialStock, transactionId);
+                break;
+            case IN:
+            case ADJUST_IN:
+                // 鍏ュ簱/鐩樼泩锛氬缓绔嬫垨婵�娲绘槑缁�
+                materialStockBagService.inbound(reqDTO, materialStock, transactionId);
+                break;
+            case MOVE_OUT:
+            case OUT:
+            case ADJUST_OUT:
+                // 绉诲嚭/鍑哄簱/鐩樹簭锛氱疆涓哄凡鍑哄簱
+                materialStockBagService.outbound(reqDTO, transactionId);
+                break;
+            default:
+                break;
+        }
+    }
+
     @Override
     @Transactional(rollbackFor = Exception.class)
     public void createTransactionList(List<MesWmTransactionSaveReqDTO> reqDTOs) {
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 39a7693..6ac1ead 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
@@ -69,6 +69,10 @@
      * 鎵规鍙�
      */
     private String batchCode;
+    /** 琚嬬爜ID */
+    private Long bagCodeId;
+    /** 琚嬬爜 */
+    private String bagCode;
 
     // ===== 搴撳瓨浣嶇疆 =====
 
@@ -128,4 +132,12 @@
      */
     private Integer stockState;
 
+    /**
+     * 鏄惁璺宠繃琚嬬爜鏄庣粏鑷姩缁存姢锛堥粯璁� false锛夈��
+     * <p>
+     * 璐ㄦ绛夐渶瑕佽嚜琛屾帶鍒惰鐮佺敓鍛藉懆鏈燂紙濡傛殏瀛樺嚭搴撲笌鍚堟牸鍏ュ簱鎴愬鍙戠敓銆佽鐮佷粎鍦ㄦ渶缁堣惤鐐硅縼绉伙級鐨勫満鏅紝
+     * 鍦ㄥ啓浜嬪姟鏃剁疆 true锛岄伩鍏� createTransaction 鎸夊崟绗斾簨鍔$被鍨嬭嚜鍔ㄧ疆宸插嚭搴�/杩佺Щ锛屽鑷磋鐮佺姸鎬侀敊涔便��
+     */
+    private Boolean skipBagManage;
+
 }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/warehouse/MesWmWarehouseService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/warehouse/MesWmWarehouseService.java
index c038d5a..d983b1c 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/warehouse/MesWmWarehouseService.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/warehouse/MesWmWarehouseService.java
@@ -90,4 +90,26 @@
      */
     MesWmWarehouseDO getWarehouseByCode(String code);
 
+    /**
+     * 鎸変粨搴撶被鍨嬭幏寰椾粨搴撳垪琛紙鎸� id 鍗囧簭锛�
+     *
+     * @param type 浠撳簱绫诲瀷锛岃 {@link cn.iocoder.yudao.module.mes.enums.wm.MesWmWarehouseTypeEnum}
+     * @return 浠撳簱鍒楄〃
+     */
+    List<MesWmWarehouseDO> getWarehouseListByType(Integer type);
+
+    /**
+     * 鑾峰緱鍚堟牸搴擄紙type=QUALIFIED锛夌殑缂栧彿闆嗗悎锛岀敤浜庢寜浠撳簱绫诲瀷杩囨护鍚堟牸搴撳瓨
+     *
+     * @return 鍚堟牸浠撳簱 id 闆嗗悎
+     */
+    List<Long> getQualifiedWarehouseIds();
+
+    /**
+     * 鑾峰緱绗竴涓悎鏍煎簱锛坱ype=QUALIFIED锛屾寜 id 鍗囧簭锛�
+     *
+     * @return 鍚堟牸浠撳簱锛屾湭閰嶇疆杩斿洖 null
+     */
+    MesWmWarehouseDO getFirstQualifiedWarehouse();
+
 }
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/warehouse/MesWmWarehouseServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/warehouse/MesWmWarehouseServiceImpl.java
index bd85c14..5ca72bd 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/warehouse/MesWmWarehouseServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/warehouse/MesWmWarehouseServiceImpl.java
@@ -9,6 +9,7 @@
 import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO;
 import cn.iocoder.yudao.module.mes.dal.mysql.wm.warehouse.MesWmWarehouseMapper;
 import cn.iocoder.yudao.module.mes.enums.wm.BarcodeBizTypeEnum;
+import cn.iocoder.yudao.module.mes.enums.wm.MesWmWarehouseTypeEnum;
 import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationService;
 import cn.iocoder.yudao.module.mes.service.wm.barcode.MesWmBarcodeService;
 import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService;
@@ -22,6 +23,7 @@
 import java.util.List;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
 import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
 
 /**
@@ -175,6 +177,7 @@
         if (MesWmWarehouseDO.WIP_VIRTUAL_WAREHOUSE.equals(code)) {
             MesWmWarehouseDO newWarehouse = MesWmWarehouseDO.builder()
                     .code(code).name("铏氭嫙绾胯竟浠撳簱").frozen(false)
+                    .type(MesWmWarehouseTypeEnum.TEMP.getType())
                     .remark("绯荤粺鑷姩鍒濆鍖栫殑铏氭嫙绾胯竟浠撳簱锛堢敤浜庣敓浜ф姤宸ヤ笌鍦ㄥ埗鍝佺鐞嗚В鑰︼級")
                     .build();
             warehouseMapper.insert(newWarehouse);
@@ -183,4 +186,21 @@
         return null;
     }
 
+    @Override
+    public List<MesWmWarehouseDO> getWarehouseListByType(Integer type) {
+        return warehouseMapper.selectListByType(type);
+    }
+
+    @Override
+    public List<Long> getQualifiedWarehouseIds() {
+        return convertList(getWarehouseListByType(MesWmWarehouseTypeEnum.QUALIFIED.getType()),
+                MesWmWarehouseDO::getId);
+    }
+
+    @Override
+    public MesWmWarehouseDO getFirstQualifiedWarehouse() {
+        List<MesWmWarehouseDO> list = getWarehouseListByType(MesWmWarehouseTypeEnum.QUALIFIED.getType());
+        return CollUtil.isNotEmpty(list) ? list.get(0) : null;
+    }
+
 }
diff --git a/yudao-module-mes/src/main/resources/mapper/home/MesBagCodeHomeStatisticsMapper.xml b/yudao-module-mes/src/main/resources/mapper/home/MesBagCodeHomeStatisticsMapper.xml
index 8e19a64..b59a960 100644
--- a/yudao-module-mes/src/main/resources/mapper/home/MesBagCodeHomeStatisticsMapper.xml
+++ b/yudao-module-mes/src/main/resources/mapper/home/MesBagCodeHomeStatisticsMapper.xml
@@ -62,6 +62,45 @@
         WHERE deleted = 0
     </select>
 
+    <!-- ========== 鍑哄簱娴侀�� / 浠撳偍 / 璐ㄩ噺 ========== -->
+
+    <!-- 缁熻鍙戣揣閫氱煡鍗曟暟锛坢es_wm_sales_notice锛� -->
+    <select id="selectSalesNoticeCount" resultType="java.lang.Long">
+        SELECT COUNT(*)
+        FROM mes_wm_sales_notice
+        WHERE deleted = 0
+    </select>
+
+    <!-- 缁熻閿�鍞嚭搴撳崟鏁帮紙mes_wm_product_sales锛� -->
+    <select id="selectProductSalesCount" resultType="java.lang.Long">
+        SELECT COUNT(*)
+        FROM mes_wm_product_sales
+        WHERE deleted = 0
+    </select>
+
+    <!-- 缁熻閿�鍞��璐у崟鏁帮紙mes_wm_return_sales锛� -->
+    <select id="selectReturnSalesCount" resultType="java.lang.Long">
+        SELECT COUNT(*)
+        FROM mes_wm_return_sales
+        WHERE deleted = 0
+    </select>
+
+    <!-- 缁熻鏈鐞嗗簱瀛橀璀︽暟锛坢es_wm_stock_warning.status=10锛� -->
+    <select id="selectStockWarningPendingCount" resultType="java.lang.Long">
+        SELECT COUNT(*)
+        FROM mes_wm_stock_warning
+        WHERE deleted = 0
+          AND status = 10
+    </select>
+
+    <!-- 缁熻寰呰瘎瀹′笉鍚堟牸鍝佸崟鏁帮紙mes_qc_ncr.status=0锛� -->
+    <select id="selectNcrPendingCount" resultType="java.lang.Long">
+        SELECT COUNT(*)
+        FROM mes_qc_ncr
+        WHERE deleted = 0
+          AND status = 0
+    </select>
+
     <!-- ========== 鐘舵�佸垎甯� ========== -->
 
     <!-- 鎸夌姸鎬佺粺璁℃壒娆℃暟閲� -->

--
Gitblit v1.9.3