From cf3050083e157819b94781d0445547ffc73e21f2 Mon Sep 17 00:00:00 2001
From: pan <antony1029@163.com>
Date: 星期五, 28 十一月 2025 21:17:28 +0800
Subject: [PATCH] 提交

---
 项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_WMSServer/Jobs/InventoryLockJob.cs                      |   22 +
 项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_Model/Models/Outbound/Dt_OutboundBatch.cs               |   68 +++
 项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_OutboundService/OutStockLockInfoService.cs              |    7 
 项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Inbound/InboundOrderController.cs |   12 
 项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_Outbound.cs                 |  195 +++++++++
 项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_WMSServer/Program.cs                                    |    9 
 项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_Common/StockEnum/OutLockStockStatusEnum.cs              |   11 
 项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundPickingService.cs               |    2 
 项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WIDESEA_TaskInfoService.csproj          |    1 
 项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_Model/Models/Outbound/Dt_OutboundOrderDetail.cs         |    4 
 项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_BasicService/LocationInfoService.cs                     |    2 
 项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_IBasicService/IMaterialUnitService.cs                   |    2 
 项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_Model/Models/Outbound/Dt_OutboundLockInfo.cs            |    2 
 项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundOrderDetailService.cs           |  183 +++++++++
 项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_BasicService/MaterialUnitService.cs                     |   12 
 项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundBatchPickingService.cs          |  571 ++++++++++++++++++++++++++++
 项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_InboundService/InboundOrderService.cs                   |   37 
 项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_IOutboundService/IOutStockLockInfoService.cs            |    2 
 项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundOrderService.cs                 |   18 
 项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_IOutboundService/IOutboundOrderDetailService.cs         |    2 
 项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs                          |    2 
 项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_OutboundService/WIDESEA_OutboundService.csproj          |    1 
 22 files changed, 1,128 insertions(+), 37 deletions(-)

diff --git "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_BasicService/LocationInfoService.cs" "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_BasicService/LocationInfoService.cs"
index dc62c83..edb4c46 100644
--- "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_BasicService/LocationInfoService.cs"
+++ "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_BasicService/LocationInfoService.cs"
@@ -217,7 +217,7 @@
         {
             return Repository.QueryData(x => locationCodes.Contains(x.LocationCode));
         }
-
+         
         public List<LocationTypeDto> GetLocationTypes()
         {
             return _locationTypeRepository.Db.Queryable<Dt_LocationType>().Select(x =>
diff --git "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_BasicService/MaterialUnitService.cs" "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_BasicService/MaterialUnitService.cs"
index e0c46ef..7c48c96 100644
--- "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_BasicService/MaterialUnitService.cs"
+++ "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_BasicService/MaterialUnitService.cs"
@@ -156,6 +156,18 @@
             return ConvertAsync(materialData, quantity, materialData.PurchaseUnit, materialData.StockUnit);
         }
 
+        public async Task<MaterialWithUnitConversionResult> ConvertFromToStockAsync(string materialCode,string  fromUom, decimal quantity)
+        {
+            var materialData = await GetMaterialWithUnitsAsync(materialCode);
+
+            // 濡傛灉棰嗘枡鍗曚綅鍜屽簱瀛樺崟浣嶇浉鍚岋紝鐩存帴杩斿洖
+            if (fromUom.Equals(materialData.StockUnit, StringComparison.OrdinalIgnoreCase))
+                return new MaterialWithUnitConversionResult(quantity, materialData.StockUnit, false);
+
+            return ConvertAsync(materialData, quantity, fromUom, materialData.StockUnit);
+        }
+
+
         /// <summary>
         /// 棰嗘枡鍗曚綅杞簱瀛樺崟浣�
         /// </summary>
diff --git "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_Common/StockEnum/OutLockStockStatusEnum.cs" "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_Common/StockEnum/OutLockStockStatusEnum.cs"
index 2cc1115..32471a8 100644
--- "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_Common/StockEnum/OutLockStockStatusEnum.cs"
+++ "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_Common/StockEnum/OutLockStockStatusEnum.cs"
@@ -16,6 +16,17 @@
     //    宸插嚭搴� = 4,
     //    宸插洖搴� = 5
     //}
+
+    // 鏋氫妇瀹氫箟
+    public enum BatchStatusEnum
+    {
+        鍒嗛厤涓� = 0,
+        鎵ц涓� = 1,
+        宸插畬鎴� = 2,
+        宸插洖搴� = 3,
+        宸插彇娑� = 4
+    }
+
     public enum SplitPackageStatusEnum
     {
         宸叉媶鍖� = 1,
diff --git "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_IBasicService/IMaterialUnitService.cs" "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_IBasicService/IMaterialUnitService.cs"
index 9e544d6..d06b50e 100644
--- "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_IBasicService/IMaterialUnitService.cs"
+++ "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_IBasicService/IMaterialUnitService.cs"
@@ -20,6 +20,8 @@
         Task<Dictionary<string, MaterialWithUnitConversionResult>> BatchConvertPurchaseToStockAsync(List<BatchConversionRequest> requests);
         Task<MaterialWithUnitConversionResult> ConvertAsync(string materialCode, decimal quantity, string fromUnit, string toUnit);
         Task<MaterialWithUnitConversionResult> ConvertIssueToStockAsync(string materialCode, decimal quantity);
+         
+        Task<MaterialWithUnitConversionResult> ConvertFromToStockAsync(string materialCode, string fromUom, decimal quantity);
         Task<MaterialWithUnitConversionResult> ConvertPurchaseToStockAsync(string materialCode, decimal quantity);
         Task<decimal?> GetConversionRatioAsync(string materialCode, string fromUnit, string toUnit);
         Task<string> GetIssueUnitAsync(string materialCode);
diff --git "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_IOutboundService/IOutStockLockInfoService.cs" "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_IOutboundService/IOutStockLockInfoService.cs"
index a97e67f..d4e589f 100644
--- "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_IOutboundService/IOutStockLockInfoService.cs"
+++ "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_IOutboundService/IOutStockLockInfoService.cs"
@@ -26,7 +26,7 @@
         Task<List<Dt_OutStockLockInfo>> GetByPalletCode(string palletCode, int? status = null);
 
         Task<LockInfoDetailDto> GetLockInfoDetail(int lockInfoId);
-        Dt_OutStockLockInfo GetOutStockLockInfo(Dt_OutboundOrder outboundOrder,Dt_OutboundOrderDetail outboundOrderDetail,Dt_StockInfo outStock, decimal assignQuantity, string barcode = null);
+        Dt_OutStockLockInfo GetOutStockLockInfo(Dt_OutboundOrder outboundOrder,Dt_OutboundOrderDetail outboundOrderDetail,Dt_StockInfo outStock, decimal assignQuantity, string barcode = null, string outboundBatchNo = "");
 
         List<Dt_OutStockLockInfo> GetOutStockLockInfos(Dt_OutboundOrder outboundOrder, Dt_OutboundOrderDetail outboundOrderDetail, List<Dt_StockInfo> outStocks, int? taskNum = null);
         Task<List<Dt_OutStockLockInfo>> GetPalletLockInfos(string palletCode);
diff --git "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_IOutboundService/IOutboundOrderDetailService.cs" "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_IOutboundService/IOutboundOrderDetailService.cs"
index 3525497..3cb776c 100644
--- "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_IOutboundService/IOutboundOrderDetailService.cs"
+++ "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_IOutboundService/IOutboundOrderDetailService.cs"
@@ -23,6 +23,6 @@
         WebResponseContent LockOutboundStockDataUpdate(List<Dt_StockInfo> stockInfos, List<Dt_OutboundOrderDetail> outboundOrderDetails, List<Dt_OutStockLockInfo> outStockLockInfos, List<Dt_LocationInfo> locationInfos, LocationStatusEnum locationStatus = LocationStatusEnum.Lock, List<Dt_Task>? tasks = null);
         (List<Dt_StockInfo>, Dt_OutboundOrderDetail, List<Dt_OutStockLockInfo>, List<Dt_LocationInfo>) AssignStockOutbound(Dt_OutboundOrderDetail outboundOrderDetail, List<StockSelectViewDTO> stockSelectViews);
         //List<Dt_OutboundOrderDetail> GetOutboundStockDataById(int id);
-
+        Task<(List<Dt_StockInfo>, List<Dt_OutboundOrderDetail>, List<Dt_OutStockLockInfo>, List<Dt_LocationInfo>)> AssignStockForBatch(Dt_OutboundOrderDetail orderDetail, decimal batchQuantity, string batchNo);
     }
 }
diff --git "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_InboundService/InboundOrderService.cs" "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_InboundService/InboundOrderService.cs"
index 15294a8..729b4e1 100644
--- "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_InboundService/InboundOrderService.cs"
+++ "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_InboundService/InboundOrderService.cs"
@@ -96,11 +96,11 @@
                         item.Unit = purchaseToStockResult.Unit;
                         item.OrderQuantity = purchaseToStockResult.Quantity;
                     }
-                    if (model .OrderType != InOrderTypeEnum.Allocat.ObjToInt())
+                    if (model.OrderType != InOrderTypeEnum.Allocat.ObjToInt())
                     {
                         model.InboundOrderNo = CreateCodeByRule(nameof(RuleCodeEnum.InboundOrderRule));
                     }
-                  
+
                     Db.InsertNav(model).Include(x => x.Details).ExecuteCommand();
                 }
                 return WebResponseContent.Instance.OK();
@@ -341,12 +341,12 @@
                 if (!result2.Item1) return content = WebResponseContent.Instance.Error(result2.Item2);
 
                 //  materielGroupDTO.WarehouseCode
-                var code = _warehouseAreaRepository.Db.Queryable<Dt_WarehouseArea>().Where(x => x.Code == materielGroupDTO.WarehouseType).Select(x=>x.Code).First();
-                if(string.IsNullOrEmpty(code))
+                var code = _warehouseAreaRepository.Db.Queryable<Dt_WarehouseArea>().Where(x => x.Code == materielGroupDTO.WarehouseType).Select(x => x.Code).First();
+                if (string.IsNullOrEmpty(code))
                 {
                     return content = WebResponseContent.Instance.Error($"浠撳簱涓病鏈夎{materielGroupDTO.WarehouseType}缂栧彿銆�");
                 }
-                
+
 
                 Dt_InboundOrder inboundOrder = GetInboundOrder(materielGroupDTO.OrderNo);
 
@@ -368,10 +368,10 @@
 
                 if (stockInfo == null)
                 {
-                    stockInfo = new Dt_StockInfo() { PalletType = (int)PalletTypeEnum.None,LocationType=materielGroupDTO.locationType.ObjToInt() };
+                    stockInfo = new Dt_StockInfo() { PalletType = (int)PalletTypeEnum.None, LocationType = materielGroupDTO.locationType.ObjToInt() };
                     stockInfo.Details = new List<Dt_StockInfoDetail>();
                 }
-               
+
                 foreach (var item in dbinboundOrderDetails)
                 {
                     stockInfo.Details.Add(new Dt_StockInfoDetail
@@ -385,15 +385,15 @@
                         SupplyCode = item.SupplyCode,
                         WarehouseCode = materielGroupDTO.WarehouseType,
                         StockQuantity = item.OrderQuantity,
-                        BarcodeQty=item.BarcodeQty,
-                        BarcodeUnit=item.BarcodeUnit,
-                        FactoryArea= inboundOrder.FactoryArea,
-                        Status = 0,                         
+                        BarcodeQty = item.BarcodeQty,
+                        BarcodeUnit = item.BarcodeUnit,
+                        FactoryArea = inboundOrder.FactoryArea,
+                        Status = 0,
                         OrderNo = inboundOrder.InboundOrderNo,
                         BusinessType = inboundOrder.BusinessType,
-                   
+
                     });
-                   
+
                     item.ReceiptQuantity = item.BarcodeQty;
                     item.OrderDetailStatus = OrderDetailStatusEnum.Over.ObjToInt();
                     item.WarehouseCode = materielGroupDTO.WarehouseType;
@@ -440,7 +440,7 @@
 
             WebResponseContent content = new WebResponseContent();
             try
-            {                                           
+            {
                 (bool, string, object?) result2 = ModelValidate.ValidateModelData(materielGroupDTO);
                 if (!result2.Item1) return content = WebResponseContent.Instance.Error(result2.Item2);
 
@@ -450,7 +450,8 @@
                     return content = WebResponseContent.Instance.Error($"鍖哄煙涓病鏈夎{materielGroupDTO.WarehouseCode}缂栧彿銆�");
                 }
 
-                if(_stockRepository.QueryFirst(x=>x.PalletCode == materielGroupDTO.PalletCode)!=null){
+                if (_stockRepository.QueryFirst(x => x.PalletCode == materielGroupDTO.PalletCode) != null)
+                {
                     return WebResponseContent.Instance.Error("璇ユ墭鐩樺凡缁忕粍杩囩洏");
                 }
 
@@ -469,7 +470,7 @@
                 {
                     if (stockInfo == null)
                     {
-                        stockInfo = new Dt_StockInfo() { PalletType = PalletTypeEnum.Empty.ObjToInt(), StockStatus = StockStatusEmun.缁勭洏鏆傚瓨.ObjToInt(), PalletCode = materielGroupDTO.PalletCode,LocationType= materielGroupDTO.WarehouseCode.ObjToInt() };
+                        stockInfo = new Dt_StockInfo() { PalletType = PalletTypeEnum.Empty.ObjToInt(), StockStatus = StockStatusEmun.缁勭洏鏆傚瓨.ObjToInt(), PalletCode = materielGroupDTO.PalletCode, LocationType = materielGroupDTO.WarehouseCode.ObjToInt() };
                         stockInfo.Details = new List<Dt_StockInfoDetail>();
                     }
                     else
@@ -650,13 +651,13 @@
             {
                 return WebResponseContent.Instance.Error("鎵樼洏鍙蜂笉鑳戒负绌�");
             }
-           var stock= _stockRepository.Db.Queryable<Dt_StockInfo>().Includes(o=>o.Details).First(x => x.PalletCode == palletCode &&  x.StockStatus ==(int)StockStatusEmun.缁勭洏鏆傚瓨);
+            var stock = _stockRepository.Db.Queryable<Dt_StockInfo>().Includes(o => o.Details).First(x => x.PalletCode == palletCode && x.StockStatus == (int)StockStatusEmun.缁勭洏鏆傚瓨);
             if (stock == null)
             {
                 return WebResponseContent.Instance.Error($"鏈壘鍒版墭鐩樺彿{palletCode}瀵瑰簲鐨勫簱瀛樿褰�");
             }
 
-            if(stock.Details == null || !stock.Details.Any())
+            if (stock.Details == null || !stock.Details.Any())
             {
                 _stockRepository.DeleteData(stock);
                 return WebResponseContent.Instance.OK();
diff --git "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_Model/Models/Outbound/Dt_OutboundBatch.cs" "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_Model/Models/Outbound/Dt_OutboundBatch.cs"
new file mode 100644
index 0000000..7f75efd
--- /dev/null
+++ "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_Model/Models/Outbound/Dt_OutboundBatch.cs"
@@ -0,0 +1,68 @@
+锘縰sing SqlSugar;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using WIDESEA_Core.DB.Models;
+
+namespace WIDESEA_Model.Models.Outbound
+{
+
+
+    /// <summary>
+    /// 鍑哄簱鎵规琛�
+    /// </summary>
+    [SugarTable("Dt_OutboundBatch")]  
+    public class Dt_OutboundBatch : BaseEntity
+    {
+        /// <summary>
+        /// 涓婚敭ID锛堣嚜澧烇級
+        /// </summary>
+        [SugarColumn(IsPrimaryKey = true, IsIdentity = true)] //  
+        public int Id { get; set; }
+
+        /// <summary>
+        /// 鎵规鍙�
+        /// </summary>
+        [SugarColumn(ColumnName = "BatchNo", Length = 50, IsNullable = false)]  
+        public string BatchNo { get; set; }
+
+        /// <summary>
+        /// 璁㈠崟鍙�
+        /// </summary>
+        [SugarColumn(ColumnName = "OrderNo", Length = 50, IsNullable = false)]
+        public string OrderNo { get; set; }
+
+        /// <summary>
+        /// 璁㈠崟鏄庣粏ID
+        /// </summary>
+        [SugarColumn(ColumnName = "OrderDetailId", IsNullable = false)]
+        public int OrderDetailId { get; set; }
+
+        /// <summary>
+        /// 鎵规鍒嗛厤鏁伴噺
+        /// </summary>
+        [SugarColumn(ColumnName = "BatchQuantity",   IsNullable = false)] // 绮惧害18锛屽皬鏁颁綅2
+        public decimal BatchQuantity { get; set; }
+
+        /// <summary>
+        /// 宸插畬鎴愭暟閲忥紙榛樿0锛�
+        /// </summary>
+        [SugarColumn(ColumnName = "CompletedQuantity",   DefaultValue = "0")] // 榛樿鍊�0
+        public decimal CompletedQuantity { get; set; } = 0; // 浠g爜灞傞粯璁ゅ�硷紝涓庢暟鎹簱榛樿鍊间竴鑷�
+
+        /// <summary>
+        /// 鎵规鐘舵�侊紙榛樿0锛�
+        /// </summary>
+        [SugarColumn(ColumnName = "BatchStatus", DefaultValue = "0")]
+        public int BatchStatus { get; set; } = 0;
+
+ 
+        /// <summary>
+        /// 鎿嶄綔浜�
+        /// </summary>
+        [SugarColumn(ColumnName = "Operator", Length = 50, IsNullable = true)] // 鍙┖
+        public string Operator { get; set; }
+    }
+}
diff --git "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_Model/Models/Outbound/Dt_OutboundLockInfo.cs" "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_Model/Models/Outbound/Dt_OutboundLockInfo.cs"
index 120729c..50a2684 100644
--- "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_Model/Models/Outbound/Dt_OutboundLockInfo.cs"
+++ "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_Model/Models/Outbound/Dt_OutboundLockInfo.cs"
@@ -136,6 +136,8 @@
 
         public string BarcodeUnit { get; set; }
 
+        public string OutboundBatchNo { get; set; }
+
         [Navigate(NavigateType.OneToOne, nameof(StockInfo))]//涓�瀵逛竴 SchoolId鏄疭tudentA绫婚噷闈㈢殑
         public Dt_StockInfo StockInfo { get; set; } //涓嶈兘璧嬪�煎彧鑳芥槸null
 
diff --git "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_Model/Models/Outbound/Dt_OutboundOrderDetail.cs" "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_Model/Models/Outbound/Dt_OutboundOrderDetail.cs"
index 2fa8b35..95fcf3b 100644
--- "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_Model/Models/Outbound/Dt_OutboundOrderDetail.cs"
+++ "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_Model/Models/Outbound/Dt_OutboundOrderDetail.cs"
@@ -145,5 +145,9 @@
         public decimal PickedQty { get; set; }
 
         public string documentsNO { get; set; }
+
+        public decimal AllocatedQuantity { get; set; }
+
+        public string BatchAllocateStatus { get; set; }
     }
 }
diff --git "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_OutboundService/OutStockLockInfoService.cs" "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_OutboundService/OutStockLockInfoService.cs"
index d4a584d..ef15c2e 100644
--- "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_OutboundService/OutStockLockInfoService.cs"
+++ "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_OutboundService/OutStockLockInfoService.cs"
@@ -43,7 +43,7 @@
             Dt_OutboundOrderDetail outboundOrderDetail,
             Dt_StockInfo outStock,
             decimal assignQuantity,
-            string barcode = null)
+            string barcode = null,string outboundBatchNo = "")
         {
             // 鑾峰彇搴撳瓨鏄庣粏淇℃伅
             var stockDetails = outStock.Details
@@ -107,9 +107,12 @@
                 IsSplitted = 0,
                 MaterielCode = outboundOrderDetail.MaterielCode,
                 BatchNo = firstAvailableDetail.BatchNo,
-                Unit = firstAvailableDetail.BarcodeUnit,
+                Unit = firstAvailableDetail.Unit,
+                BarcodeQty = firstAvailableDetail.BarcodeQty,
+                BarcodeUnit = firstAvailableDetail.BarcodeUnit,
                 FactoryArea = firstAvailableDetail.FactoryArea,
                 lineNo = outboundOrderDetail.lineNo,
+                OutboundBatchNo= outboundBatchNo
             };
         }
 
diff --git "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundBatchPickingService.cs" "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundBatchPickingService.cs"
new file mode 100644
index 0000000..445c6e5
--- /dev/null
+++ "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundBatchPickingService.cs"
@@ -0,0 +1,571 @@
+锘縰sing Microsoft.Extensions.Logging;
+using SqlSugar;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using WIDESEA_BasicService;
+using WIDESEA_Common.OrderEnum;
+using WIDESEA_Common.StockEnum;
+using WIDESEA_Core;
+using WIDESEA_Core.BaseRepository;
+using WIDESEA_Core.BaseServices;
+using WIDESEA_IAllocateService;
+using WIDESEA_IBasicService;
+using WIDESEA_IOutboundService;
+using WIDESEA_IStockService;
+using WIDESEA_Model.Models;
+using WIDESEA_Model.Models.Basic;
+using WIDESEA_Model.Models.Outbound;
+
+namespace WIDESEA_OutboundService
+{
+    public  class OutboundBatchPickingService : ServiceBase<Dt_PickingRecord, IRepository<Dt_PickingRecord>>
+    {
+
+ 
+        private readonly IUnitOfWorkManage _unitOfWorkManage;
+        public IRepository<Dt_PickingRecord> Repository => BaseDal;
+
+        private readonly IStockInfoService _stockInfoService;
+        private readonly IStockService _stockService;
+        private readonly IOutStockLockInfoService _outStockLockInfoService;
+        private readonly IStockInfoDetailService _stockInfoDetailService;
+        private readonly ILocationInfoService _locationInfoService;
+        private readonly IOutboundOrderDetailService _outboundOrderDetailService;
+        private readonly IOutboundOrderService _outboundOrderService;
+        private readonly ISplitPackageService _splitPackageService;
+        private readonly IRepository<Dt_Task> _taskRepository;
+        private readonly IESSApiService _eSSApiService;
+        private readonly IInvokeMESService _invokeMESService;
+        private readonly IDailySequenceService _dailySequenceService;
+        private readonly IAllocateService _allocateService;
+        private readonly IRepository<Dt_OutboundBatch> _outboundBatchRepository;
+        private readonly ILogger<OutboundPickingService> _logger;
+
+        private Dictionary<string, string> stations = new Dictionary<string, string>
+        {
+            {"2-1","2-9" },
+            {"3-1","3-9" },
+
+        };
+
+        private Dictionary<string, string> movestations = new Dictionary<string, string>
+        {
+            {"2-1","2-5" },
+            {"3-1","3-5" },
+
+        };
+
+        public OutboundBatchPickingService(IRepository<Dt_PickingRecord> BaseDal, IUnitOfWorkManage unitOfWorkManage, IStockInfoService stockInfoService, IStockService stockService,
+            IOutStockLockInfoService outStockLockInfoService, IStockInfoDetailService stockInfoDetailService, ILocationInfoService locationInfoService,
+            IOutboundOrderDetailService outboundOrderDetailService, ISplitPackageService splitPackageService, IOutboundOrderService outboundOrderService,
+            IRepository<Dt_Task> taskRepository, IESSApiService eSSApiService, ILogger<OutboundPickingService> logger, IInvokeMESService invokeMESService, IDailySequenceService dailySequenceService, IAllocateService allocateService) : base(BaseDal)
+        {
+            _unitOfWorkManage = unitOfWorkManage;
+            _stockInfoService = stockInfoService;
+            _stockService = stockService;
+            _outStockLockInfoService = outStockLockInfoService;
+            _stockInfoDetailService = stockInfoDetailService;
+            _locationInfoService = locationInfoService;
+            _outboundOrderDetailService = outboundOrderDetailService;
+            _splitPackageService = splitPackageService;
+            _outboundOrderService = outboundOrderService;
+            _taskRepository = taskRepository;
+            _eSSApiService = eSSApiService;
+            _logger = logger;
+            _invokeMESService = invokeMESService;
+            _dailySequenceService = dailySequenceService;
+            _allocateService = allocateService;
+        }
+
+
+        #region 鍒嗘壒鍒嗘嫞
+
+        /// <summary>
+        /// 鍒嗘壒鍒嗘嫞纭
+        /// </summary>
+        public async Task<WebResponseContent> ConfirmBatchPicking(string orderNo, string batchNo, string palletCode, string barcode, decimal actualPickedQty)
+        {
+            try
+            {
+                _unitOfWorkManage.BeginTran();
+
+                // 1. 楠岃瘉鍒嗘嫞璇锋眰
+                var validationResult = await ValidateBatchPickingRequest(orderNo, batchNo, palletCode, barcode, actualPickedQty);
+                if (!validationResult.IsValid)
+                    return WebResponseContent.Instance.Error(validationResult.ErrorMessage);
+
+                var (lockInfo, orderDetail, stockDetail) = validationResult.Data;
+
+                // 2. 鎵ц鍒嗘嫞閫昏緫
+                var pickingResult = await ExecuteBatchPickingLogic(lockInfo, orderDetail, stockDetail, actualPickedQty);
+
+                // 3. 鏇存柊鎵规瀹屾垚鏁伴噺
+                await UpdateBatchCompletedQuantity(batchNo, actualPickedQty);
+
+                // 4. 鏇存柊璁㈠崟鐩稿叧鏁版嵁
+                await UpdateOrderRelatedData(orderDetail.Id, actualPickedQty, orderNo);
+
+                // 5. 璁板綍鎷i�夊巻鍙�
+                await RecordPickingHistory(pickingResult, orderNo, palletCode, batchNo);
+
+                _unitOfWorkManage.CommitTran();
+
+                return WebResponseContent.Instance.OK("鍒嗘壒鍒嗘嫞鎴愬姛");
+            }
+            catch (Exception ex)
+            {
+                _unitOfWorkManage.RollbackTran();
+                _logger.LogError($"鍒嗘壒鍒嗘嫞澶辫触 - OrderNo: {orderNo}, BatchNo: {batchNo}, Error: {ex.Message}");
+                return WebResponseContent.Instance.Error($"鍒嗘壒鍒嗘嫞澶辫触锛歿ex.Message}");
+            }
+        }
+
+        private async Task<ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail)>> ValidateBatchPickingRequest(
+            string orderNo, string batchNo, string palletCode, string barcode, decimal actualPickedQty)
+        {
+            // 鏌ユ壘鎵规閿佸畾淇℃伅
+            var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
+                .Where(x => x.OrderNo == orderNo &&
+                           x.BatchNo == batchNo &&
+                           x.PalletCode == palletCode &&
+                           x.CurrentBarcode == barcode &&
+                           x.Status == (int)OutLockStockStatusEnum.鍑哄簱涓�)
+                .FirstAsync();
+
+            if (lockInfo == null)
+                return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail)>.Error("鏈壘鍒版湁鏁堢殑鎵规閿佸畾淇℃伅");
+
+            if (actualPickedQty <= 0 || actualPickedQty > lockInfo.AssignQuantity - lockInfo.PickedQty)
+                return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail)>.Error("鍒嗘嫞鏁伴噺鏃犳晥");
+
+            // 鑾峰彇璁㈠崟鏄庣粏鍜屽簱瀛樻槑缁�
+            var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
+                .FirstAsync(x => x.Id == lockInfo.OrderDetailId);
+
+            var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
+                .FirstAsync(x => x.Barcode == barcode && x.StockId == lockInfo.StockId);
+
+            return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail)>.Success((lockInfo, orderDetail, stockDetail));
+        }
+
+        private async Task<PickingResult> ExecuteBatchPickingLogic(
+            Dt_OutStockLockInfo lockInfo, Dt_OutboundOrderDetail orderDetail,
+            Dt_StockInfoDetail stockDetail, decimal actualPickedQty)
+        {
+            // 鏇存柊閿佸畾淇℃伅
+            lockInfo.PickedQty += actualPickedQty;
+            if (lockInfo.PickedQty >= lockInfo.AssignQuantity)
+            {
+                lockInfo.Status = (int)OutLockStockStatusEnum.鎷i�夊畬鎴�;
+            }
+            await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
+
+            // 鏇存柊搴撳瓨
+            stockDetail.StockQuantity -= actualPickedQty;
+            stockDetail.OutboundQuantity += actualPickedQty;
+
+            if (stockDetail.StockQuantity <= 0)
+            {
+                stockDetail.Status = (int)StockStatusEmun.鍑哄簱瀹屾垚;
+            }
+            await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
+
+            return new PickingResult
+            {
+                FinalLockInfo = lockInfo,
+                ActualPickedQty = actualPickedQty
+            };
+        }
+
+        private async Task UpdateBatchCompletedQuantity(string batchNo, decimal pickedQty)
+        {
+            await _outboundBatchRepository.Db.Updateable<Dt_OutboundBatch>()
+                .SetColumns(x => x.CompletedQuantity == x.CompletedQuantity + pickedQty)
+                .Where(x => x.BatchNo == batchNo)
+                .ExecuteCommandAsync();
+
+            // 妫�鏌ユ壒娆℃槸鍚﹀畬鎴�
+            var batch = await _outboundBatchRepository.Db.Queryable<Dt_OutboundBatch>()
+                .FirstAsync(x => x.BatchNo == batchNo);
+
+            if (batch.CompletedQuantity >= batch.BatchQuantity)
+            {
+                batch.BatchStatus = (int)BatchStatusEnum.宸插畬鎴�;
+                await _outboundBatchRepository.Db.Updateable(batch).ExecuteCommandAsync();
+            }
+        }
+
+        #endregion
+
+        #region 鎵嬪姩鎷嗗寘
+
+        /// <summary>
+        /// 鎵嬪姩鎷嗗寘
+        /// </summary>
+        public async Task<WebResponseContent> ManualSplitPackage(string orderNo, string batchNo, string originalBarcode, decimal splitQuantity)
+        {
+            try
+            {
+                _unitOfWorkManage.BeginTran();
+
+                // 1. 楠岃瘉鎷嗗寘璇锋眰
+                var validationResult = await ValidateManualSplitRequest(orderNo, batchNo, originalBarcode, splitQuantity);
+                if (!validationResult.IsValid)
+                    return WebResponseContent.Instance.Error(validationResult.ErrorMessage);
+
+                var (lockInfo, stockDetail) = validationResult.Data;
+
+                // 2. 鎵ц鎷嗗寘閫昏緫
+                var splitResult = await ExecuteManualSplit(lockInfo, stockDetail, splitQuantity, batchNo);
+
+                _unitOfWorkManage.CommitTran();
+
+                return WebResponseContent.Instance.OK("鎵嬪姩鎷嗗寘鎴愬姛", new { NewBarcode = splitResult.NewBarcode });
+            }
+            catch (Exception ex)
+            {
+                _unitOfWorkManage.RollbackTran();
+                _logger.LogError($"鎵嬪姩鎷嗗寘澶辫触 - OrderNo: {orderNo}, BatchNo: {batchNo}, Barcode: {originalBarcode}, Error: {ex.Message}");
+                return WebResponseContent.Instance.Error($"鎵嬪姩鎷嗗寘澶辫触锛歿ex.Message}");
+            }
+        }
+
+        private async Task<ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>> ValidateManualSplitRequest(
+            string orderNo, string batchNo, string originalBarcode, decimal splitQuantity)
+        {
+            var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
+                .Where(x => x.OrderNo == orderNo &&
+                           x.BatchNo == batchNo &&
+                           x.CurrentBarcode == originalBarcode &&
+                           x.Status == (int)OutLockStockStatusEnum.鍑哄簱涓�)
+                .FirstAsync();
+
+            if (lockInfo == null)
+                return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("鏈壘鍒版湁鏁堢殑閿佸畾淇℃伅");
+
+            var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
+                .FirstAsync(x => x.Barcode == originalBarcode && x.StockId == lockInfo.StockId);
+
+            if (stockDetail.StockQuantity < splitQuantity)
+                return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("鎷嗗寘鏁伴噺涓嶈兘澶т簬搴撳瓨鏁伴噺");
+
+            if (lockInfo.AssignQuantity - lockInfo.PickedQty < splitQuantity)
+                return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("鎷嗗寘鏁伴噺涓嶈兘澶т簬鏈嫞閫夋暟閲�");
+
+            return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Success((lockInfo, stockDetail));
+        }
+
+        private async Task<SplitResultDto> ExecuteManualSplit(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail,
+            decimal splitQuantity, string batchNo)
+        {
+            // 鐢熸垚鏂版潯鐮�
+            string newBarcode = await GenerateNewBarcode();
+
+            // 鍒涘缓鏂板簱瀛樻槑缁�
+            var newStockDetail = new Dt_StockInfoDetail
+            {
+                StockId = stockDetail.StockId,
+                MaterielCode = stockDetail.MaterielCode,
+                OrderNo = stockDetail.OrderNo,
+                BatchNo = stockDetail.BatchNo,
+                StockQuantity = splitQuantity,
+                OutboundQuantity = 0,
+                Barcode = newBarcode,
+                Status = (int)StockStatusEmun.鍑哄簱閿佸畾,
+                SupplyCode = stockDetail.SupplyCode,
+                Unit = stockDetail.Unit
+            };
+            await _stockInfoDetailService.Db.Insertable(newStockDetail).ExecuteCommandAsync();
+
+            // 鏇存柊鍘熷簱瀛樻槑缁�
+            stockDetail.StockQuantity -= splitQuantity;
+            await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
+
+            // 鍒涘缓鏂伴攣瀹氫俊鎭�
+            var newLockInfo = new Dt_OutStockLockInfo
+            {
+                OrderNo = lockInfo.OrderNo,
+                OrderDetailId = lockInfo.OrderDetailId,
+                BatchNo = batchNo,
+                MaterielCode = lockInfo.MaterielCode,
+                StockId = lockInfo.StockId,
+                OrderQuantity = splitQuantity,
+                AssignQuantity = splitQuantity,
+                PickedQty = 0,
+                LocationCode = lockInfo.LocationCode,
+                PalletCode = lockInfo.PalletCode,
+                Status = (int)OutLockStockStatusEnum.鍑哄簱涓�,
+                CurrentBarcode = newBarcode,
+                Operator = App.User.UserName, 
+            };
+            await _outStockLockInfoService.Db.Insertable(newLockInfo).ExecuteCommandAsync();
+
+            // 鏇存柊鍘熼攣瀹氫俊鎭�
+            lockInfo.AssignQuantity -= splitQuantity;
+            await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
+
+            // 璁板綍鎷嗗寘鍘嗗彶
+            await RecordSplitHistory(lockInfo, stockDetail, splitQuantity, newBarcode);
+
+            return new SplitResultDto { NewBarcode = newBarcode };
+        }
+
+        #endregion
+
+        #region 鍙栨秷鎷嗗寘
+
+        /// <summary>
+        /// 鍙栨秷鎷嗗寘
+        /// </summary>
+        public async Task<WebResponseContent> CancelSplitPackage(string orderNo, string batchNo, string newBarcode)
+        {
+            try
+            {
+                _unitOfWorkManage.BeginTran();
+
+                // 鏌ユ壘鎷嗗寘璁板綍鍜屾柊閿佸畾淇℃伅
+                var splitRecord = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
+                    .Where(x => x.NewBarcode == newBarcode && x.OrderNo == orderNo && !x.IsReverted)
+                    .FirstAsync();
+
+                if (splitRecord == null)
+                    return WebResponseContent.Instance.Error("鏈壘鍒版媶鍖呰褰�");
+
+                var newLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
+                    .Where(x => x.CurrentBarcode == newBarcode && x.BatchNo == batchNo)
+                    .FirstAsync();
+
+                if (newLockInfo == null)
+                    return WebResponseContent.Instance.Error("鏈壘鍒版柊閿佸畾淇℃伅");
+
+                // 鎭㈠鍘熷簱瀛�
+                var originalStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
+                    .FirstAsync(x => x.Barcode == splitRecord.OriginalBarcode && x.StockId == splitRecord.StockId);
+
+                originalStock.StockQuantity += splitRecord.SplitQty;
+                await _stockInfoDetailService.Db.Updateable(originalStock).ExecuteCommandAsync();
+
+                // 鎭㈠鍘熼攣瀹氫俊鎭�
+                var originalLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
+                    .FirstAsync(x => x.Id == splitRecord.OutStockLockInfoId);
+
+                originalLockInfo.AssignQuantity += splitRecord.SplitQty;
+                await _outStockLockInfoService.Db.Updateable(originalLockInfo).ExecuteCommandAsync();
+
+                // 鍒犻櫎鏂板簱瀛樻槑缁�
+                await _stockInfoDetailService.Db.Deleteable<Dt_StockInfoDetail>()
+                    .Where(x => x.Barcode == newBarcode)
+                    .ExecuteCommandAsync();
+
+                // 鍒犻櫎鏂伴攣瀹氫俊鎭�
+                await _outStockLockInfoService.Db.Deleteable<Dt_OutStockLockInfo>()
+                    .Where(x => x.Id == newLockInfo.Id)
+                    .ExecuteCommandAsync();
+
+                // 鏍囪鎷嗗寘璁板綍涓哄凡鎾ら攢
+                splitRecord.IsReverted = true;
+                splitRecord.RevertTime = DateTime.Now;
+                splitRecord.Operator = App.User.UserName;
+                await _splitPackageService.Db.Updateable(splitRecord).ExecuteCommandAsync();
+
+                _unitOfWorkManage.CommitTran();
+
+                return WebResponseContent.Instance.OK("鍙栨秷鎷嗗寘鎴愬姛");
+            }
+            catch (Exception ex)
+            {
+                _unitOfWorkManage.RollbackTran();
+                _logger.LogError($"鍙栨秷鎷嗗寘澶辫触 - OrderNo: {orderNo}, BatchNo: {batchNo}, Barcode: {newBarcode}, Error: {ex.Message}");
+                return WebResponseContent.Instance.Error($"鍙栨秷鎷嗗寘澶辫触锛歿ex.Message}");
+            }
+        }
+
+        #endregion
+
+        #region 鍒嗘壒鍥炲簱
+
+        /// <summary>
+        /// 鍒嗘壒鍥炲簱 - 閲婃斁鏈嫞閫夌殑搴撳瓨
+        /// </summary>
+        public async Task<WebResponseContent> BatchReturnStock(string orderNo, string batchNo)
+        {
+            try
+            {
+                _unitOfWorkManage.BeginTran();
+
+                // 1. 鏌ユ壘鎵规鏈畬鎴愮殑閿佸畾璁板綍
+                var unfinishedLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
+                    .Where(x => x.OrderNo == orderNo &&
+                               x.BatchNo == batchNo &&
+                               x.Status == (int)OutLockStockStatusEnum.鍑哄簱涓�)
+                    .ToListAsync();
+
+                if (!unfinishedLocks.Any())
+                    return WebResponseContent.Instance.Error("璇ユ壒娆℃病鏈夋湭瀹屾垚鐨勯攣瀹氳褰�");
+
+                // 2. 閲婃斁搴撳瓨鍜岄攣瀹氳褰�
+                foreach (var lockInfo in unfinishedLocks)
+                {
+                    await ReleaseLockAndStock(lockInfo);
+                }
+
+                // 3. 鏇存柊鎵规鐘舵��
+                await UpdateBatchStatusForReturn(batchNo);
+
+                // 4. 鏇存柊璁㈠崟鏄庣粏鐨勫凡鍒嗛厤鏁伴噺
+                await UpdateOrderDetailAfterReturn(unfinishedLocks);
+
+                _unitOfWorkManage.CommitTran();
+
+                return WebResponseContent.Instance.OK("鍒嗘壒鍥炲簱鎴愬姛");
+            }
+            catch (Exception ex)
+            {
+                _unitOfWorkManage.RollbackTran();
+                _logger.LogError($"鍒嗘壒鍥炲簱澶辫触 - OrderNo: {orderNo}, BatchNo: {batchNo}, Error: {ex.Message}");
+                return WebResponseContent.Instance.Error($"鍒嗘壒鍥炲簱澶辫触锛歿ex.Message}");
+            }
+        }
+
+        private async Task ReleaseLockAndStock(Dt_OutStockLockInfo lockInfo)
+        {
+            // 鎭㈠搴撳瓨鐘舵��
+            var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
+                .FirstAsync(x => x.Barcode == lockInfo.CurrentBarcode && x.StockId == lockInfo.StockId);
+
+            if (stockDetail != null)
+            {
+                stockDetail.Status = (int)StockStatusEmun.鍏ュ簱瀹屾垚;
+                await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
+            }
+
+            // 鏇存柊閿佸畾璁板綍鐘舵�佷负鍥炲簱
+            lockInfo.Status = (int)OutLockStockStatusEnum.鍥炲簱涓�;
+            await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
+        }
+
+        private async Task UpdateBatchStatusForReturn(string batchNo)
+        {
+            await _outboundBatchRepository.Db.Updateable<Dt_OutboundBatch>()
+                .SetColumns(x => new Dt_OutboundBatch
+                {
+                    BatchStatus = (int)BatchStatusEnum.宸插洖搴�,
+                    Operator = App.User.UserName
+                })
+                .Where(x => x.BatchNo == batchNo)
+                .ExecuteCommandAsync();
+        }
+
+        private async Task UpdateOrderDetailAfterReturn(List<Dt_OutStockLockInfo> returnedLocks)
+        {
+            var orderDetailGroups = returnedLocks.GroupBy(x => x.OrderDetailId);
+
+            foreach (var group in orderDetailGroups)
+            {
+                var orderDetailId = group.Key;
+                var returnedQty = group.Sum(x => x.AssignQuantity - x.PickedQty);
+
+                await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
+                    .SetColumns(x => x.AllocatedQuantity == x.AllocatedQuantity - returnedQty)
+                    .Where(x => x.Id == orderDetailId)
+                    .ExecuteCommandAsync();
+            }
+        }
+
+        #endregion
+
+        #region 杈呭姪鏂规硶
+
+        private async Task<string> GenerateNewBarcode()
+        {
+            var seq = await _dailySequenceService.GetNextSequenceAsync();
+            return "WSLOT" + DateTime.Now.ToString("yyyyMMdd") + seq.ToString()?.PadLeft(5, '0');
+        }
+
+        private async Task RecordSplitHistory(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail,decimal splitQty, string newBarcode)
+        {
+            var splitHistory = new Dt_SplitPackageRecord
+            {
+                OrderNo = lockInfo.OrderNo,
+                OutStockLockInfoId = lockInfo.Id,
+                StockId = stockDetail.StockId,
+                Operator = App.User.UserName,
+                OriginalBarcode = stockDetail.Barcode,
+                NewBarcode = newBarcode,
+                SplitQty = splitQty,
+                SplitTime = DateTime.Now,
+                Status = (int)SplitPackageStatusEnum.宸叉媶鍖�
+            };
+
+            await _splitPackageService.Db.Insertable(splitHistory).ExecuteCommandAsync();
+        }
+
+        private async Task RecordPickingHistory(PickingResult result, string orderNo, string palletCode, string batchNo)
+        {
+            var pickingRecord = new Dt_PickingRecord
+            {
+                OrderNo = orderNo,
+               // BatchNo = batchNo,
+                OrderDetailId = result.FinalLockInfo.OrderDetailId,
+                PalletCode = palletCode,
+                Barcode = result.FinalLockInfo.CurrentBarcode,
+                MaterielCode = result.FinalLockInfo.MaterielCode,
+                PickQuantity = result.ActualPickedQty,
+                PickTime = DateTime.Now,
+                Operator = App.User.UserName,
+                OutStockLockId = result.FinalLockInfo.Id
+            };
+
+            await Db.Insertable(pickingRecord).ExecuteCommandAsync();
+        }
+
+        private async Task UpdateOrderRelatedData(int orderDetailId, decimal pickedQty, string orderNo)
+        {
+            // 鏇存柊璁㈠崟鏄庣粏鐨勫凡鍑哄簱鏁伴噺
+            await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
+                .SetColumns(x => new Dt_OutboundOrderDetail
+                {
+                    OverOutQuantity = x.OverOutQuantity + pickedQty,
+                    AllocatedQuantity = x.AllocatedQuantity - pickedQty
+                })
+                .Where(x => x.Id == orderDetailId)
+                .ExecuteCommandAsync();
+
+            // 妫�鏌ヨ鍗曠姸鎬�
+            await CheckAndUpdateOrderStatus(orderNo);
+        }
+
+        private async Task CheckAndUpdateOrderStatus(string orderNo)
+        {
+            var orderDetails = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
+                  .LeftJoin<Dt_OutboundOrder>((o, item) => o.OrderId == item.Id)
+                  .Where((o, item) => item.OrderNo == orderNo)
+                  .Select((o, item) => o)
+                  .ToListAsync();
+
+
+
+            bool allCompleted = orderDetails.All(x => x.OverOutQuantity >= x.NeedOutQuantity);
+
+            if (allCompleted)
+            {
+                await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
+                    .SetColumns(x => new Dt_OutboundOrder { OrderStatus = (int)OutOrderStatusEnum.鍑哄簱瀹屾垚 })
+                    .Where(x => x.OrderNo == orderNo)
+                    .ExecuteCommandAsync();
+            }
+        }
+
+        #endregion
+    }
+
+    
+    // 鏀寔绫�
+    public class SplitResultDto
+    {
+        public string NewBarcode { get; set; }
+    }
+}
diff --git "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundOrderDetailService.cs" "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundOrderDetailService.cs"
index 121a74e..450ce11 100644
--- "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundOrderDetailService.cs"
+++ "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundOrderDetailService.cs"
@@ -227,6 +227,189 @@
             return (true, "鍒嗛厤鎴愬姛");
         }
 
+
+        /// <summary>
+        /// 涓哄垎鎵瑰垎閰嶅簱瀛�
+        /// </summary>
+        public  async Task<(List<Dt_StockInfo>, List<Dt_OutboundOrderDetail>, List<Dt_OutStockLockInfo>, List<Dt_LocationInfo>)>
+            AssignStockForBatch(Dt_OutboundOrderDetail orderDetail, decimal batchQuantity, string batchNo)
+        {
+            if (orderDetail == null)
+            {
+                throw new Exception("鏈壘鍒板嚭搴撳崟鏄庣粏淇℃伅");
+            }
+
+            var outboundOrder = await _outboundOrderService.Db.Queryable<Dt_OutboundOrder>()
+                .FirstAsync(x => x.Id == orderDetail.OrderId);
+
+            List<Dt_StockInfo> outStocks = new List<Dt_StockInfo>();
+            List<Dt_OutStockLockInfo> outStockLockInfos = new List<Dt_OutStockLockInfo>();
+            List<Dt_LocationInfo> locationInfos = new List<Dt_LocationInfo>();
+
+            // 鎸夌墿鏂欏拰鎵规鍒嗙粍澶勭悊锛堣繖閲屽彧鏈変竴涓槑缁嗭級
+            var groupDetails = new List<Dt_OutboundOrderDetail> { orderDetail }
+                .GroupBy(x => new { x.MaterielCode, x.BatchNo, x.SupplyCode })
+                .Select(x => new
+                {
+                    MaterielCode = x.Key.MaterielCode,
+                    BatchNo = x.Key.BatchNo,
+                    SupplyCode = x.Key.SupplyCode,
+                    Details = x.ToList(),
+                    TotalNeedQuantity = batchQuantity  // 浣跨敤鍒嗘壒鏁伴噺
+                })
+                .Where(x => x.TotalNeedQuantity > 0)
+                .ToList();
+
+            foreach (var item in groupDetails)
+            {
+                var needQuantity = item.TotalNeedQuantity;
+                               
+                // 鑾峰彇鍙敤搴撳瓨锛堟寜鍏堣繘鍏堝嚭鎺掑簭锛�
+                List<Dt_StockInfo> stockInfos = _stockService.StockInfoService.GetUseableStocks(item.MaterielCode, item.BatchNo, item.SupplyCode);
+                if (!stockInfos.Any())
+                {
+                    throw new Exception($"鐗╂枡[{item.MaterielCode}]鎵规[{item.BatchNo}]鏈壘鍒板彲鍒嗛厤搴撳瓨");
+                }
+
+                // 鍒嗛厤搴撳瓨锛堟寜鍏堣繘鍏堝嚭锛�
+                var (autoAssignStocks, stockAllocations) = _stockService.StockInfoService.GetOutboundStocks  (stockInfos, item.MaterielCode, needQuantity, out decimal residueQuantity);
+
+                // 妫�鏌ュ垎閰嶇粨鏋�
+                decimal allocatedQuantity = needQuantity - residueQuantity;
+                if (allocatedQuantity <= 0)
+                {
+                    throw new Exception($"鐗╂枡[{item.MaterielCode}]鎵规[{item.BatchNo}]搴撳瓨涓嶈冻锛岄渶瑕亄needQuantity}锛屼絾鏃犳硶鍒嗛厤浠讳綍搴撳瓨");
+                }
+
+                outStocks.AddRange(autoAssignStocks);
+
+                // 鎸夊厛杩涘厛鍑哄師鍒欏垎閰嶉攣瀹氭暟閲忓埌鍚勪釜鏄庣粏
+                var distributionResult = DistributeLockQuantityByFIFO(item.Details, autoAssignStocks, stockAllocations, outStockLockInfos, outboundOrder, batchNo);
+
+                if (!distributionResult.success)
+                {
+                    throw new Exception(distributionResult.message);
+                }
+
+                // 鏇存柊鍑哄簱鍗曟槑缁嗙姸鎬�
+                UpdateOrderDetailStatus(item.Details, allocatedQuantity, needQuantity);
+            }
+
+            if (outStocks.Any())
+            {
+                locationInfos.AddRange(_locationInfoService.GetLocationInfos(
+                  outStocks.Select(x => x.LocationCode).Distinct().ToList()));
+
+             
+            }
+
+            return (outStocks, groupDetails.SelectMany(x => x.Details).ToList(), outStockLockInfos, locationInfos);
+        }
+
+        /// <summary>
+        /// 鎸夊厛杩涘厛鍑哄師鍒欏垎閰嶉攣瀹氭暟閲�
+        /// </summary>
+        private (bool success, string message) DistributeLockQuantityByFIFO(
+            List<Dt_OutboundOrderDetail> details,
+            List<Dt_StockInfo> assignStocks,
+            Dictionary<int, decimal> stockAllocations,
+            List<Dt_OutStockLockInfo> outStockLockInfos,
+            Dt_OutboundOrder outboundOrder,
+            string batchNo)
+        {
+            var sortedDetails = details
+                .Where(d => d.OrderQuantity - d.OverOutQuantity - d.AllocatedQuantity > 0)
+                .OrderBy(x => x.Id)
+                .ToList();
+
+            if (!sortedDetails.Any())
+                return (true, "鏃犻渶鍒嗛厤");
+
+            // 鑾峰彇鎵�鏈夊垎閰嶄簡搴撳瓨鐨勬槑缁嗭紝鎸夊厛杩涘厛鍑烘帓搴�
+            var allocatedStockDetails = assignStocks
+                .SelectMany(x => x.Details)
+                .Where(x => stockAllocations.ContainsKey(x.Id) && stockAllocations[x.Id] > 0)
+                .OrderBy(x => x.CreateDate)
+                .ThenBy(x => x.StockId)
+                .ToList();
+
+            if (!allocatedStockDetails.Any())
+            {
+                return (false, "娌℃湁鍙垎閰嶇殑搴撳瓨鏄庣粏");
+            }
+
+            decimal totalNeedQuantity = sortedDetails.Sum(d =>
+                d.OrderQuantity - d.OverOutQuantity - d.AllocatedQuantity);
+            decimal allocatedQuantity = 0;
+
+            // 涓烘瘡涓簱瀛樻槑缁嗗垱寤哄垎閰嶈褰�
+            foreach (var stockDetail in allocatedStockDetails)
+            {
+                if (!stockAllocations.TryGetValue(stockDetail.Id, out decimal allocatedQuantityForStock))
+                    continue;
+
+                if (allocatedQuantityForStock <= 0) continue;
+
+                var stockInfo = assignStocks.First(x => x.Id == stockDetail.StockId);
+                decimal remainingAllocate = allocatedQuantityForStock;
+
+                // 鎸夐『搴忓垎閰嶇粰鍚勪釜鍑哄簱鍗曟槑缁�
+                foreach (var detail in sortedDetails)
+                {
+                    if (remainingAllocate <= 0) break;
+
+                    // 璁$畻杩欎釜鏄庣粏杩橀渶瑕佸垎閰嶇殑鏁伴噺
+                    var detailNeed = detail.OrderQuantity - detail.OverOutQuantity - detail.AllocatedQuantity;
+                    if (detailNeed <= 0) continue;
+
+                    // 鍒嗛厤鏁伴噺
+                    var assignQuantity = Math.Min(remainingAllocate, detailNeed);
+
+                    // 楠岃瘉鏉$爜鏄惁瀛樺湪
+                    if (string.IsNullOrEmpty(stockDetail.Barcode))
+                    {
+                        return (false, $"搴撳瓨鏄庣粏ID[{stockDetail.Id}]鐨勬潯鐮佷负绌�");
+                    }
+                                 
+
+                    // 鍒涘缓鍑哄簱閿佸畾淇℃伅
+                    var lockInfo = _outStockLockInfoService.GetOutStockLockInfo(
+                        outboundOrder, detail, stockInfo, assignQuantity, stockDetail.Barcode,batchNo);
+                    outStockLockInfos.Add(lockInfo);
+ 
+                    // 鏇存柊鏄庣粏鐨勫凡鍒嗛厤鏁伴噺
+                    detail.AllocatedQuantity += assignQuantity;
+                    remainingAllocate -= assignQuantity;
+                    allocatedQuantity += assignQuantity;
+                }
+
+                // 濡傛灉杩樻湁鍓╀綑鍒嗛厤鏁伴噺锛岃褰曡鍛�
+                if (remainingAllocate > 0)
+                {
+                    _logger.LogWarning($"搴撳瓨鍒嗛厤鍚庝粛鏈夊墿浣欐暟閲忔湭鍒嗛厤: {remainingAllocate}, 鏉$爜: {stockDetail.Barcode}");
+                }
+            }
+
+            // 楠岃瘉鏄惁鑷冲皯鍒嗛厤浜嗕竴閮ㄥ垎
+            if (allocatedQuantity <= 0)
+            {
+                return (false, "搴撳瓨鍒嗛厤澶辫触锛屾棤娉曞垎閰嶄换浣曟暟閲�");
+            }
+
+            // 璁板綍鍒嗛厤缁撴灉
+            if (allocatedQuantity < totalNeedQuantity)
+            {
+                _logger.LogWarning($"搴撳瓨閮ㄥ垎鍒嗛厤锛岄渶瑕亄totalNeedQuantity}锛屽疄闄呭垎閰峽allocatedQuantity}");
+            }
+            else
+            {
+                _logger.LogInformation($"搴撳瓨瀹屽叏鍒嗛厤锛屽垎閰嶆暟閲弡allocatedQuantity}");
+            }
+
+            return (true, "鍒嗛厤鎴愬姛");
+        }
+
+
         private void UpdateOrderDetailStatus(List<Dt_OutboundOrderDetail> details,
     decimal allocatedQuantity, decimal needQuantity)
         {
diff --git "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundOrderService.cs" "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundOrderService.cs"
index 15ba41a..ab67148 100644
--- "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundOrderService.cs"
+++ "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundOrderService.cs"
@@ -65,15 +65,17 @@
                 }
                 foreach (var item in model.Details)
                 {
-                    var issueoStockResult = await _materialUnitService.ConvertIssueToStockAsync(item.MaterielCode, item.BarcodeQty);
+                    var issueoStockResult = await _materialUnitService.ConvertFromToStockAsync(item.MaterielCode,item.BarcodeUnit, item.BarcodeQty);
                     item.Unit = issueoStockResult.Unit;
                     item.OrderQuantity = issueoStockResult.Quantity;
-                    var moveissueoStockResult = await _materialUnitService.ConvertIssueToStockAsync(item.MaterielCode, item.BarcodeMoveQty);
+                    var moveissueoStockResult = await _materialUnitService.ConvertFromToStockAsync(item.MaterielCode, item.BarcodeUnit, item.BarcodeMoveQty);
                     item.MoveQty = moveissueoStockResult.Quantity;
                 }
 
-
-                model.OrderNo = CreateCodeByRule(nameof(RuleCodeEnum.OutboundOrderRule));
+                if (model.OrderType != InOrderTypeEnum.Allocat.ObjToInt() || model.OrderType != InOrderTypeEnum.InternalAllocat.ObjToInt())
+                {
+                    model.OrderNo = CreateCodeByRule(nameof(RuleCodeEnum.OutboundOrderRule));
+                }
                 Db.InsertNav(model).Include(x => x.Details).ExecuteCommand();
 
                 return WebResponseContent.Instance.OK();
@@ -125,10 +127,10 @@
                             BarcodeQty = item.OrderQuantity,
                             BarcodeUnit = item.Unit,
                         };
-                        var issueoStockResult = await _materialUnitService.ConvertIssueToStockAsync(item.MaterielCode, item.BarcodeQty);
+                        var issueoStockResult = await _materialUnitService.ConvertFromToStockAsync(item.MaterielCode,item.BarcodeUnit, item.BarcodeQty);
                         item.Unit = issueoStockResult.Unit;
                         item.OrderQuantity = issueoStockResult.Quantity;
-                        var moveissueoStockResult = await _materialUnitService.ConvertIssueToStockAsync(item.MaterielCode, item.BarcodeMoveQty);
+                        var moveissueoStockResult = await _materialUnitService.ConvertFromToStockAsync(item.MaterielCode, item.BarcodeUnit, item.BarcodeMoveQty);
                         item.MoveQty = moveissueoStockResult.Quantity;
 
                         outboundOrderDetails.Add(outboundOrderDetail);
@@ -146,10 +148,10 @@
                         outboundOrderDetail.BarcodeMoveQty = item.MoveQty;
                         outboundOrderDetail.BarcodeQty = item.OrderQuantity;
                         outboundOrderDetail.BarcodeUnit = item.Unit;
-                        var issueoStockResult = await _materialUnitService.ConvertIssueToStockAsync(item.MaterielCode, item.BarcodeQty);
+                        var issueoStockResult = await _materialUnitService.ConvertFromToStockAsync(item.MaterielCode, item.BarcodeUnit, item.BarcodeQty);
                         outboundOrderDetail.Unit = issueoStockResult.Unit;
                         outboundOrderDetail.OrderQuantity = issueoStockResult.Quantity;
-                        var moveissueoStockResult = await _materialUnitService.ConvertIssueToStockAsync(item.MaterielCode, item.BarcodeMoveQty);
+                        var moveissueoStockResult = await _materialUnitService.ConvertFromToStockAsync(item.MaterielCode, item.BarcodeUnit, item.BarcodeMoveQty);
                         outboundOrderDetail.MoveQty = moveissueoStockResult.Quantity;
 
 
diff --git "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundPickingService.cs" "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundPickingService.cs"
index f289724..a0497ba 100644
--- "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundPickingService.cs"
+++ "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundPickingService.cs"
@@ -2168,6 +2168,8 @@
                 FactoryArea = originalLock.FactoryArea,
                 lineNo = originalLock.lineNo,
                 WarehouseCode = originalLock.WarehouseCode,
+                BarcodeQty=originalLock.BarcodeQty,
+                BarcodeUnit=originalLock.BarcodeUnit,
 
             };
 
diff --git "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_OutboundService/WIDESEA_OutboundService.csproj" "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_OutboundService/WIDESEA_OutboundService.csproj"
index ebf59e5..a9892c1 100644
--- "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_OutboundService/WIDESEA_OutboundService.csproj"
+++ "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_OutboundService/WIDESEA_OutboundService.csproj"
@@ -7,6 +7,7 @@
   </PropertyGroup>
 
   <ItemGroup>
+    <ProjectReference Include="..\WIDESEA_BasicService\WIDESEA_BasicService.csproj" />
     <ProjectReference Include="..\WIDESEA_IAllocateService\WIDESEA_IAllocateService.csproj" />
     <ProjectReference Include="..\WIDESEA_IBasicService\WIDESEA_IBasicService.csproj" />
     <ProjectReference Include="..\WIDESEA_IOutboundService\WIDESEA_IOutboundService.csproj" />
diff --git "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs" "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs"
index 3021a88..d8690fd 100644
--- "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs"
+++ "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs"
@@ -51,6 +51,7 @@
 using WIDESEA_IStockService;
 using WIDESEA_ITaskInfoService;
 using WIDESEA_Model.Models;
+using WIDESEA_Model.Models.Outbound;
 
 namespace WIDESEA_TaskInfoService
 {
@@ -65,6 +66,7 @@
         private readonly IInboundOrderService _inboundOrderService;
         private readonly IInboundOrderDetailService _inboundOrderDetailService;
 
+        private readonly IRepository<Dt_OutboundBatch> _OutboundBatchRepository;
         private readonly IOutboundOrderService _outboundOrderService;
         private readonly IOutboundOrderDetailService _outboundOrderDetailService;
         private readonly IOutStockLockInfoService _outStockLockInfoService;
diff --git "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_Outbound.cs" "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_Outbound.cs"
index d479cef..6cbf577 100644
--- "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_Outbound.cs"
+++ "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_Outbound.cs"
@@ -6,6 +6,7 @@
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
+using WIDESEA_BasicService;
 using WIDESEA_Common.CommonEnum;
 using WIDESEA_Common.LocationEnum;
 using WIDESEA_Common.OrderEnum;
@@ -17,6 +18,8 @@
 using WIDESEA_DTO.Basic;
 using WIDESEA_DTO.Stock;
 using WIDESEA_Model.Models;
+using WIDESEA_Model.Models.Basic;
+using WIDESEA_Model.Models.Outbound;
 
 namespace WIDESEA_TaskInfoService
 {
@@ -596,5 +599,197 @@
 
         }
 
+
+        #region 鍒嗘壒鍒嗛厤搴撳瓨
+
+        /// <summary>
+        /// 鍒嗘壒鍒嗛厤搴撳瓨
+        /// </summary>
+        public async Task<WebResponseContent> BatchAllocateStock(string orderNo, int orderDetailId, decimal batchQuantity, string outStation)
+        {
+            try
+            {
+                List<Dt_Task> tasks = new List<Dt_Task>();
+                List<Dt_StockInfo> stockInfos = new List<Dt_StockInfo>();
+                List<Dt_OutboundOrderDetail> outboundOrderDetails = new List<Dt_OutboundOrderDetail>();
+                List<Dt_OutStockLockInfo> outStockLockInfos = new List<Dt_OutStockLockInfo>();
+                List<Dt_LocationInfo> locationInfos = new List<Dt_LocationInfo>();
+
+                (List<Dt_Task>, List<Dt_StockInfo>?, List<Dt_OutboundOrderDetail>?, List<Dt_OutStockLockInfo>?, List<Dt_LocationInfo>?) result = await BatchAllocateStockDataHandle(orderNo, orderDetailId, batchQuantity, outStation);
+
+                if (result.Item2 != null && result.Item2.Count > 0)
+                {
+                    stockInfos.AddRange(result.Item2);
+                }
+                if (result.Item3 != null && result.Item3.Count > 0)
+                {
+                    outboundOrderDetails.AddRange(result.Item3);
+                }
+                if (result.Item4 != null && result.Item4.Count > 0)
+                {
+                    outStockLockInfos.AddRange(result.Item4);
+                }
+                if (result.Item5 != null && result.Item5.Count > 0)
+                {
+                    locationInfos.AddRange(result.Item5);
+                }
+                if (result.Item1 != null && result.Item1.Count > 0)
+                {
+                    tasks.AddRange(result.Item1);
+                }
+
+                WebResponseContent content = await GenerateOutboundTaskDataUpdateAsync(tasks, stockInfos, outboundOrderDetails, outStockLockInfos, locationInfos);
+                return content;
+            }
+            catch (Exception ex)
+            {
+                _unitOfWorkManage.RollbackTran();
+                _logger.LogError($"鍒嗘壒鍒嗛厤搴撳瓨澶辫触 - OrderNo: {orderNo}, OrderDetailId: {orderDetailId}, Quantity: {batchQuantity}, Error: {ex.Message}");
+                return WebResponseContent.Instance.Error($"鍒嗘壒鍒嗛厤澶辫触锛歿ex.Message}");
+            }
+        }
+
+        /// <summary>
+        /// 鍒嗘壒鍒嗛厤搴撳瓨鏁版嵁澶勭悊
+        /// </summary>
+        public async Task<(List<Dt_Task>, List<Dt_StockInfo>?, List<Dt_OutboundOrderDetail>?, List<Dt_OutStockLockInfo>?, List<Dt_LocationInfo>?)>
+            BatchAllocateStockDataHandle(string orderNo, int orderDetailId, decimal batchQuantity, string outStation)
+        {
+            List<Dt_Task> tasks = new List<Dt_Task>();
+
+            // 鑾峰彇璁㈠崟鏄庣粏
+            var outboundOrderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
+                .FirstAsync(x => x.Id == orderDetailId );
+
+            if (outboundOrderDetail == null)
+            {
+                throw new Exception("鏈壘鍒板嚭搴撳崟鏄庣粏淇℃伅");
+            }
+
+            // 楠岃瘉璁㈠崟鏄庣粏鐘舵��
+            if (outboundOrderDetail.OrderDetailStatus > OrderDetailStatusEnum.New.ObjToInt() &&
+                outboundOrderDetail.OrderDetailStatus != OrderDetailStatusEnum.AssignOverPartial.ObjToInt())
+            {
+                throw new Exception("鎵�閫夊嚭搴撳崟鏄庣粏瀛樺湪鍑哄簱涓垨宸插畬鎴�");
+            }
+
+            // 楠岃瘉鍒嗛厤鏁伴噺
+            decimal allocatedQty = outboundOrderDetail.AllocatedQuantity;
+            decimal overOutQty = outboundOrderDetail.OverOutQuantity;
+            decimal needOutQty = outboundOrderDetail.NeedOutQuantity;
+            decimal availableQty = needOutQty - allocatedQty - overOutQty;
+
+            if (availableQty <= 0)
+                throw new Exception("鏃犲彲鍒嗛厤鏁伴噺");
+
+            if (batchQuantity > availableQty)
+                throw new Exception($"鍒嗛厤鏁伴噺涓嶈兘瓒呰繃鍙垎閰嶆暟閲弡availableQty}");
+
+            List<Dt_StockInfo>? stockInfos = null;
+            List<Dt_OutboundOrderDetail>? orderDetails = null;
+            List<Dt_OutStockLockInfo>? outStockLockInfos = null;
+            List<Dt_LocationInfo>? locationInfos = null;
+
+            // 鐢熸垚鎵规鍙�
+            string batchNo = await GenerateBatchNo();
+
+            // 鍒嗛厤搴撳瓨
+            (List<Dt_StockInfo>, List<Dt_OutboundOrderDetail>, List<Dt_OutStockLockInfo>, List<Dt_LocationInfo>) allocateResult =
+                await _outboundOrderDetailService.AssignStockForBatch(outboundOrderDetail, batchQuantity, batchNo);
+
+            if (allocateResult.Item1 != null && allocateResult.Item1.Count > 0)
+            {
+                // 鍒涘缓鍒嗘壒璁板綍
+                await CreateBatchRecord(orderNo, orderDetailId, batchQuantity, batchNo);
+
+                Dt_OutboundOrder outboundOrder = await _outboundOrderService.Repository.QueryFirstAsync(x => x.Id == outboundOrderDetail.OrderId);
+                TaskTypeEnum typeEnum = outboundOrder.OrderType switch
+                {
+                    (int)OutOrderTypeEnum.Issue => TaskTypeEnum.Outbound,
+                    (int)OutOrderTypeEnum.Allocate => TaskTypeEnum.OutAllocate,
+                    (int)OutOrderTypeEnum.Quality => TaskTypeEnum.OutQuality,
+                    _ => TaskTypeEnum.Outbound
+                };
+
+                tasks = GetTasks(allocateResult.Item1, typeEnum, outStation);
+                tasks.ForEach(x =>
+                {
+                    x.OrderNo = outboundOrder.OrderNo;
+                });
+
+                allocateResult.Item2.ForEach(x =>
+                {
+                    x.OrderDetailStatus = OrderDetailStatusEnum.Outbound.ObjToInt();
+                });
+
+                allocateResult.Item3.ForEach(x =>
+                {
+                    x.Status = OutLockStockStatusEnum.鍑哄簱涓�.ObjToInt();
+                });
+
+                stockInfos = allocateResult.Item1;
+                orderDetails = allocateResult.Item2;
+                outStockLockInfos = allocateResult.Item3;
+                locationInfos = allocateResult.Item4;
+            }
+            else
+            {
+                throw new Exception("鏃犲簱瀛�");
+            }
+
+            return (tasks, stockInfos, orderDetails, outStockLockInfos, locationInfos);
+        }
+
+ 
+ 
+
+      
+        /// <summary>
+        /// 鏇存柊璁㈠崟鏄庣粏鐘舵��
+        /// </summary>
+        private void UpdateOrderDetailStatus(List<Dt_OutboundOrderDetail> details, decimal allocatedQuantity, decimal needQuantity)
+        {
+            foreach (var detail in details)
+            {
+                // 鏍规嵁鍒嗛厤鎯呭喌鏇存柊鐘舵��
+                if (allocatedQuantity >= needQuantity)
+                {
+                    detail.OrderDetailStatus = OrderDetailStatusEnum.Outbound.ObjToInt();
+                }
+                else
+                {
+                    detail.OrderDetailStatus = OrderDetailStatusEnum.AssignOverPartial.ObjToInt();
+                }
+            }
+        }
+
+
+
+        private async Task<string> GenerateBatchNo()
+        {
+            var batchNo = UniqueValueGenerator.Generate();
+
+            return $"Out{batchNo} ";
+        }
+
+        private async Task<Dt_OutboundBatch> CreateBatchRecord(string orderNo, int orderDetailId, decimal batchQuantity, string batchNo)
+        {
+            var batchRecord = new Dt_OutboundBatch
+            {
+                BatchNo = batchNo,
+                OrderNo = orderNo,
+                OrderDetailId = orderDetailId,
+                BatchQuantity = batchQuantity,
+                BatchStatus = (int)BatchStatusEnum.鍒嗛厤涓�,              
+                Operator = App.User.UserName
+            };
+
+            await _OutboundBatchRepository.Db.Insertable(batchRecord).ExecuteCommandAsync();
+            return batchRecord;
+        }
+
+        #endregion
+
+
     }
 }
diff --git "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WIDESEA_TaskInfoService.csproj" "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WIDESEA_TaskInfoService.csproj"
index 51b297d..c586408 100644
--- "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WIDESEA_TaskInfoService.csproj"
+++ "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WIDESEA_TaskInfoService.csproj"
@@ -7,6 +7,7 @@
   </PropertyGroup>
 
   <ItemGroup>
+    <ProjectReference Include="..\WIDESEA_BasicService\WIDESEA_BasicService.csproj" />
     <ProjectReference Include="..\WIDESEA_IAllocateService\WIDESEA_IAllocateService.csproj" />
     <ProjectReference Include="..\WIDESEA_IBasicService\WIDESEA_IBasicService.csproj" />
     <ProjectReference Include="..\WIDESEA_IInboundService\WIDESEA_IInboundService.csproj" />
diff --git "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Inbound/InboundOrderController.cs" "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Inbound/InboundOrderController.cs"
index 4b98acf..d17e070 100644
--- "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Inbound/InboundOrderController.cs"
+++ "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Inbound/InboundOrderController.cs"
@@ -51,14 +51,14 @@
         [HttpPost, Route("Test"), AllowAnonymous, MethodParamsValidate]
         public async Task<WebResponseContent> Test()
         {
-          
-             Service.Db.Deleteable<Dt_InboundOrder>().Where(x=>x.UpperOrderNo== "12020251100040").ExecuteCommand();
-            _inboundService.InboundOrderDetailService.Db.Deleteable<Dt_InboundOrderDetail>()
-             .Where(p => SqlFunc.Subqueryable<Dt_InboundOrder>().Where(s => s.Id == p.OrderId && s.UpperOrderNo == "12020251100040").Any()).ExecuteCommand();
 
-            // var purchaseToStockResult = await _materialUnitService.ConvertPurchaseToStockAsync("101001-00002", 10);
+            // Service.Db.Deleteable<Dt_InboundOrder>().Where(x=>x.UpperOrderNo== "12020251100040").ExecuteCommand();
+            //_inboundService.InboundOrderDetailService.Db.Deleteable<Dt_InboundOrderDetail>()
+            // .Where(p => SqlFunc.Subqueryable<Dt_InboundOrder>().Where(s => s.Id == p.OrderId && s.UpperOrderNo == "12020251100040").Any()).ExecuteCommand();
 
-            // var pdddurchaseToStockResult = await _materialUnitService.ConvertPurchaseToStockAsync("100513-00210", 10);
+            var purchaseToStockResult = await _materialUnitService.ConvertPurchaseToStockAsync("100513-00303", 1);
+
+            var pdddurchaseToStockResult = await _materialUnitService.ConvertFromToStockAsync("100513-00303", "W013", 1);
 
             //var sddd = _locationInfoService.AssignLocation();
             //var code = sddd.LocationCode;
diff --git "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_WMSServer/Jobs/InventoryLockJob.cs" "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_WMSServer/Jobs/InventoryLockJob.cs"
new file mode 100644
index 0000000..248f47e
--- /dev/null
+++ "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_WMSServer/Jobs/InventoryLockJob.cs"
@@ -0,0 +1,22 @@
+锘縰sing Quartz;
+using SqlSugar;
+
+namespace WIDESEA_WMSServer.Jobs
+{
+    [DisallowConcurrentExecution]
+    public class InventoryLockJob : IJob
+    {
+        private readonly ILogger<ErpJob> _logger;
+        private readonly ISqlSugarClient _db;
+        public InventoryLockJob(ILogger<ErpJob> logger, ISqlSugarClient db )
+        {
+            _logger = logger;
+            _db = db;
+      
+        }
+        public Task Execute(IJobExecutionContext context)
+        {
+           return Task.CompletedTask;
+        }
+    }
+}
diff --git "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_WMSServer/Program.cs" "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_WMSServer/Program.cs"
index ef722e9..c284b28 100644
--- "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_WMSServer/Program.cs"
+++ "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_WMSServer/Program.cs"
@@ -161,6 +161,15 @@
         .ForJob(jobKey)
         .WithIdentity("ErpJob-trigger")
         .WithCronSchedule("0 0 10,14,20 * * ?"));
+
+    var inventoryLockJobKey = new JobKey("InventoryLockJob");
+    q.AddJob<InventoryLockJob>(opts => opts.WithIdentity(inventoryLockJobKey));
+
+    q.AddTrigger(opts => opts
+        .ForJob(inventoryLockJobKey)
+        .WithIdentity("InventoryLockJob-trigger")
+        .WithCronSchedule("0 0/10 * * * ?")); // 每10分钟执行一次
+
 });
 
  

--
Gitblit v1.9.3