From ddea73b709a57e2719e722cabf2d2c1518e36685 Mon Sep 17 00:00:00 2001
From: 647556386 <647556386@qq.com>
Date: 星期四, 19 三月 2026 17:00:01 +0800
Subject: [PATCH] 1

---
 项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundService.cs |  630 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 607 insertions(+), 23 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_OutboundService/OutboundService.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/OutboundService.cs"
index 7d7eec4..81372d7 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/OutboundService.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/OutboundService.cs"
@@ -1,14 +1,16 @@
-锘縰sing System.Reflection.Emit;
-using AutoMapper;
+锘縰sing AutoMapper;
 using Dm.filter;
 using MailKit.Search;
 using Mapster;
 using Microsoft.IdentityModel.Tokens;
 using Newtonsoft.Json;
 using Newtonsoft.Json.Serialization;
+using OfficeOpenXml.FormulaParsing.Excel.Functions.Math;
 using Org.BouncyCastle.Asn1.Ocsp;
 using Org.BouncyCastle.Crypto;
 using SqlSugar;
+using System;
+using System.Reflection.Emit;
 using WIDESEA_BasicService;
 using WIDESEA_Common.CommonEnum;
 using WIDESEA_Common.LocationEnum;
@@ -29,6 +31,7 @@
 using WIDESEA_IRecordService;
 using WIDESEA_IStockService;
 using WIDESEA_Model.Models;
+using WIDESEA_Model.Models.Basic;
 using WIDESEA_Model.Models.Check;
 using static HslCommunication.Profinet.Knx.KnxCode;
 
@@ -65,6 +68,8 @@
         private readonly IRepository<Dt_AllocateMaterialInfo> _allocateMaterialInfoRepository;
         public readonly IRepository<Dt_InboundOrderDetail> _inboundOrderDetailRepository;
         public readonly IRepository<Dt_InboundOrder> _inboundOrderRepository;
+        public readonly IRepository<Dt_WarehouseArea> _warehouseAreaRepository;
+        public readonly IRepository<Dt_LocationType> _locationTypeRepository;
 
         private Dictionary<string, string> stations = new Dictionary<string, string>
         {
@@ -78,7 +83,7 @@
             {"3-1","3-5" },
         };
 
-        public OutboundService(IMapper mapper, IUnitOfWorkManage unitOfWorkManage, IRepository<Dt_OutboundOrderDetail> detailRepository, IRepository<Dt_OutboundOrder> outboundRepository, IRepository<Dt_OutStockLockInfo> outboundLockInfoRepository, IRepository<Dt_StockInfo> stockInfoRepository, IRepository<Dt_StockInfoDetail> stockDetailRepository, IRepository<Dt_StockQuantityChangeRecord> stockChangeRepository, IRepository<Dt_StockInfoDetail_Hty> stockDetailHistoryRepository, IBasicService basicService, IOutboundOrderDetailService outboundOrderDetailService, IOutboundOrderService outboundOrderService, IOutStockLockInfoService outboundStockLockInfoService, IFeedbackMesService feedbackMesService, IRepository<Dt_Task> taskRepository, ILocationInfoService locationInfoService, IESSApiService eSSApiService, IRepository<Dt_AllocateOrder> allocateOrderRepository, IRepository<Dt_AllocateMaterialInfo> allocateMaterialInfoRepository, IRepository<Dt_InboundOrderDetail> inboundOrderDetailRepository, IRepository<Dt_InboundOrder> inboundOrderRepository)
+        public OutboundService(IMapper mapper, IUnitOfWorkManage unitOfWorkManage, IRepository<Dt_OutboundOrderDetail> detailRepository, IRepository<Dt_OutboundOrder> outboundRepository, IRepository<Dt_OutStockLockInfo> outboundLockInfoRepository, IRepository<Dt_StockInfo> stockInfoRepository, IRepository<Dt_StockInfoDetail> stockDetailRepository, IRepository<Dt_StockQuantityChangeRecord> stockChangeRepository, IRepository<Dt_StockInfoDetail_Hty> stockDetailHistoryRepository, IBasicService basicService, IOutboundOrderDetailService outboundOrderDetailService, IOutboundOrderService outboundOrderService, IOutStockLockInfoService outboundStockLockInfoService, IFeedbackMesService feedbackMesService, IRepository<Dt_Task> taskRepository, ILocationInfoService locationInfoService, IESSApiService eSSApiService, IRepository<Dt_AllocateOrder> allocateOrderRepository, IRepository<Dt_AllocateMaterialInfo> allocateMaterialInfoRepository, IRepository<Dt_InboundOrderDetail> inboundOrderDetailRepository, IRepository<Dt_InboundOrder> inboundOrderRepository, IRepository<Dt_LocationType> locationTypeRepository, IRepository<Dt_WarehouseArea> warehouseAreaRepository)
         {
             _mapper = mapper;
             _unitOfWorkManage = unitOfWorkManage;
@@ -103,6 +108,8 @@
             _allocateMaterialInfoRepository = allocateMaterialInfoRepository;
             _inboundOrderDetailRepository = inboundOrderDetailRepository;
             _inboundOrderRepository = inboundOrderRepository;
+            _locationTypeRepository = locationTypeRepository;
+            _warehouseAreaRepository = warehouseAreaRepository;
         }
 
         public WebResponseContent PrintFromData (string barcode)
@@ -144,6 +151,14 @@
             PickingOutboundResponseDTO response = new PickingOutboundResponseDTO();
             decimal totalNeedAllocate = 0; // 鎬婚渶姹傚垎閰嶉噺
             decimal totalActualAllocate = 0; // 瀹為檯鎬诲垎閰嶉噺
+            string targetWarehouse = string.Empty;// 鐩爣浠撳簱
+            string targetLocationCode = string.Empty; // 鐩爣璐т綅
+            bool isWholeCaseOutbound = false; // 鏄惁鏁寸鍑哄簱
+            List<string> wholeCasePallets = new List<string>();
+            Dictionary<string, string> palletLocationMap = new Dictionary<string, string>();
+            Dictionary<string, bool> palletIsWholeCaseMap = new Dictionary<string, bool>();
+            int? targetLocationType = null;
+
 
             try
             {
@@ -159,6 +174,7 @@
                 // 璁板綍鎬婚渶姹傛暟閲�
                 totalNeedAllocate = calculationResult.MaterielCalculations.Sum(x => x.UnallocatedQuantity);
 
+                
                 // 2. 澶勭悊鐗╂枡鍒嗛厤
                 List<PickedStockDetailDTO> pickedDetails = new List<PickedStockDetailDTO>();
                 Dt_OutboundOrder outboundOrder = calculationResult.OutboundOrder;
@@ -192,11 +208,116 @@
                             materielCalc.OutStockLockInfos.Add(item);
                         }
                         outStockLockInfos.Add(item);
-                    }
+
+                        if (outboundOrder.OrderType == 117)
+                        {
+                            // 鍖归厤褰撳墠鍗曟嵁鐨勯攣瀹氳褰�
+                            if (outboundOrder.OrderNo == item.OrderNo)
+                            {
+                                // 鏌ヨ搴撳瓨淇℃伅
+                                var stockInfo = _stockInfoRepository.QueryFirst(x => x.PalletCode == item.PalletCode);
+                                if (stockInfo == null)
+                                {
+                                    content = WebResponseContent.Instance.Error($"鎵樼洏{item.PalletCode}鏈煡璇㈠埌搴撳瓨淇℃伅锛屾棤娉曞鐞嗘暣绠卞嚭搴�");
+                                    _unitOfWorkManage.RollbackTran();
+                                    return content;
+                                }
+
+                                // 璁$畻搴撳瓨鎬婚噺锛屽垽鏂槸鍚︽暣绠憋紙澧炲姞0鍊间繚鎶わ級
+                                decimal stockQuantity = _stockDetailRepository.QueryData(x => x.StockId == stockInfo.Id).Sum(x => x.StockQuantity);
+                                if (stockQuantity > 0 && stockQuantity == item.AssignQuantity)
+                                {
+                                    // 鏍囪褰撳墠鎵樼洏涓烘暣绠憋紙鏍稿績淇锛氭寜鎵樼洏缁村害璁板綍鐘舵�侊級
+                                    if (!palletIsWholeCaseMap.ContainsKey(item.PalletCode))
+                                    {
+                                        palletIsWholeCaseMap.Add(item.PalletCode, true);
+                                    }
+                                    else
+                                    {
+                                        palletIsWholeCaseMap[item.PalletCode] = true;
+                                    }
+
+                                    // 鐩爣浠撳簱/璐т綅绫诲瀷鍙煡璇竴娆★紙鎬ц兘浼樺寲+绌哄�间繚鎶わ級
+                                    if (string.IsNullOrEmpty(targetWarehouse))
+                                    {
+                                        targetWarehouse = GetToWarehouseByOrderNo(request.OrderNo);
+                                        if (string.IsNullOrEmpty(targetWarehouse))
+                                        {
+                                            content = WebResponseContent.Instance.Error("鏅轰粨璋冩櫤浠撴暣绠卞嚭搴撳崟鎹湭閰嶇疆鐩爣浠撳簱");
+                                            _unitOfWorkManage.RollbackTran();
+                                            return content;
+                                        }
+
+                                        // 鏇挎崲First()涓篎irstOrDefault()锛岄伩鍏嶇┖鍊煎紓甯�
+                                        string warehouseAreaName = _warehouseAreaRepository.Db.Queryable<Dt_WarehouseArea>()
+                                            .Where(x => x.Code == targetWarehouse)
+                                            .Select(x => x.Name)
+                                            .First();
+                                        if (string.IsNullOrEmpty(warehouseAreaName))
+                                        {
+                                            content = WebResponseContent.Instance.Error($"鐩爣浠撳簱{targetWarehouse}鏈煡璇㈠埌瀵瑰簲鐨勫簱鍖哄悕绉�");
+                                            _unitOfWorkManage.RollbackTran();
+                                            return content;
+                                        }
+
+                                        int? locationType = _locationTypeRepository.Db.Queryable<Dt_LocationType>()
+                                            .Where(x => string.Equals(x.LocationTypeDesc, warehouseAreaName, StringComparison.OrdinalIgnoreCase))
+                                            .Select(x => x.LocationType)
+                                            .First();
+                                        if (!locationType.HasValue)
+                                        {
+                                            content = WebResponseContent.Instance.Error($"搴撳尯{warehouseAreaName}鏈尮閰嶅埌瀵瑰簲鐨勮揣浣嶇被鍨�");
+                                            _unitOfWorkManage.RollbackTran();
+                                            return content;
+                                        }
+                                        targetLocationType = locationType.Value;
+                                    }
+
+                                    // 鍒嗛厤鐩爣璐т綅锛堟瘡涓墭鐩樼嫭绔嬭揣浣嶏級
+                                    if (!palletLocationMap.ContainsKey(item.PalletCode) && targetLocationType.HasValue)
+                                    {
+                                        Dt_LocationInfo locationInfo = _locationInfoService.AssignLocation(targetLocationType.Value);
+                                        if (locationInfo == null || string.IsNullOrEmpty(locationInfo.LocationCode))
+                                        {
+                                            content = WebResponseContent.Instance.Error($"璐т綅绫诲瀷{targetLocationType.Value}鏈垎閰嶅埌鍙敤璐т綅锛堟墭鐩橈細{item.PalletCode}锛�");
+                                            _unitOfWorkManage.RollbackTran();
+                                            return content;
+                                        }
+                                        palletLocationMap.Add(item.PalletCode, locationInfo.LocationCode);
+                                    }
+
+                                    // 鍔犲叆鏁寸鎵樼洏鍒楄〃
+                                    if (!wholeCasePallets.Contains(item.PalletCode))
+                                    {
+                                        wholeCasePallets.Add(item.PalletCode);
+                                    }
+                                }
+                                else
+                                {
+                                    if (!palletIsWholeCaseMap.ContainsKey(item.PalletCode))
+                                    {
+                                        palletIsWholeCaseMap.Add(item.PalletCode, false);
+                                    }
+                                    else
+                                    {
+                                        palletIsWholeCaseMap[item.PalletCode] = false;
+                                    }
+                                }
+                            }
+                          }
+                        }
 
                     // 澶勭悊浠诲姟
                     foreach (var item in materielPickedDetails.Tasks)
                     {
+                        if (outboundOrder.OrderType == 117
+                           && palletIsWholeCaseMap.ContainsKey(item.PalletCode)
+                           && palletIsWholeCaseMap[item.PalletCode]
+                           && palletLocationMap.ContainsKey(item.PalletCode))
+                        {
+                            item.TaskType = (int)TaskTypeEnum.Relocation;
+                            item.TargetAddress = palletLocationMap[item.PalletCode];
+                        }
                         if (tasks.FirstOrDefault(x => x.PalletCode == item.PalletCode) == null)
                             tasks.Add(item);
                     }
@@ -209,7 +330,7 @@
                     foreach (var detail in materielCalc.Details)
                     {
                         if (remainingToLock <= 0) break;
-                        decimal maxLockableQty = detail.OrderQuantity - detail.OverOutQuantity;
+                        decimal maxLockableQty = detail.OrderQuantity - detail.OverOutQuantity-detail.LockQuantity;
                         if (maxLockableQty <= 0) continue;
                         decimal currentLockQty = Math.Min(remainingToLock, maxLockableQty);
                         detail.LockQuantity += currentLockQty;
@@ -234,14 +355,33 @@
                 
                 if (tasks.Any()) _taskRepository.AddData(tasks);
 
-                
-                    _unitOfWorkManage.CommitTran();
+                if (outboundOrder.OrderType == 117 && wholeCasePallets.Any())
+                {
+                    foreach (var palletCode in wholeCasePallets)
+                    {
+                        var completeReq = new OutboundCompletePalletRequestDTO
+                        {
+                            OrderNo = request.OrderNo,
+                            PalletCode = palletCode
+                        };
+
+                        var res = CompleteOutboundWithPallet(completeReq);
+                        if (!res.Status)
+                        {
+                            _unitOfWorkManage.RollbackTran();
+                            return res;
+                        }
+                    }
+                }
+
+                _unitOfWorkManage.CommitTran();
 
                 // 4. 鏋勯�犲搷搴旓細鍖哄垎銆屽叏閮ㄥ垎閰嶃�嶅拰銆岄儴鍒嗗垎閰嶃��
                 string responseMsg = totalActualAllocate == totalNeedAllocate
                     ? "鍒嗘嫞浠诲姟鍒嗛厤鎴愬姛"
                     : $"鍒嗘嫞浠诲姟鍒嗛厤瀹屾垚锛堝疄闄呭垎閰峽totalActualAllocate}锛岄渶姹倇totalNeedAllocate}锛屽簱瀛樹笉瓒抽儴鍒嗘湭鍒嗛厤锛�";
-                if(totalActualAllocate == 0 && !outboundOrder.Details.Any(x=>x.LockQuantity >0))
+                Dt_OutboundOrder outboundOrder1 = _outboundRepository.Db.Queryable<Dt_OutboundOrder>().Where(x => x.OrderNo == request.OrderNo).Includes(x=>x.Details).First();
+                if(totalActualAllocate == 0 && !outboundOrder1.Details.Any(x=>x.LockQuantity >0))
                 {
                     UpdateOutboundOrderStatus(request.OrderNo, (int)OutOrderStatusEnum.鏈紑濮�);
                     return WebResponseContent.Instance.Error("鍒嗛厤搴撳瓨鏁伴噺涓�0锛屾棤娉曞嚭搴�");
@@ -258,6 +398,18 @@
                 content = WebResponseContent.Instance.Error("澶勭悊鎷h揣鍑哄簱澶辫触锛�" + ex.Message);
             }
             return content;
+        }
+
+
+        /// <summary>
+        /// 鏍规嵁鍗曟嵁鍙疯幏鍙栫洰鏍囦粨搴�
+        /// </summary>
+        /// <param name="orderNo"></param>
+        /// <returns></returns>
+        public String GetToWarehouseByOrderNo(string orderNo)
+        {
+            var order =_allocateOrderRepository.QueryFirst(x => x.OrderNo == orderNo);
+            return order.ToWarehouse;
         }
 
         /// <summary>
@@ -1156,7 +1308,7 @@
                             }
                             else
                             {
-                                item.CurrentDeliveryQty = item.LockQuantity - item.OverOutQuantity;
+                                item.CurrentDeliveryQty += item.LockQuantity - item.OverOutQuantity;
                             }
                             item.OverOutQuantity = item.LockQuantity;
                         }
@@ -1205,7 +1357,25 @@
                     response.Success = true;
                     response.Message = "鍑哄簱瀹屾垚";
                     response.UpdatedDetails = updateDetails;
-                    
+
+                    if (CheckOutboundOrderDetailCompletedByMatCode(request.OrderNo, lockInfo.MaterielCode, outboundOrderDetails))
+                    {
+                        Func<Dt_OutStockLockInfo, bool> supWhere = x => string.IsNullOrEmpty(outboundOrderDetails.First().SupplyCode) ? true : x.SupplyCode == outboundOrderDetails.First().SupplyCode;
+
+                        Func<Dt_OutStockLockInfo, bool> wareWhere = x => string.IsNullOrEmpty(outboundOrderDetails.First().WarehouseCode) ? true : x.WarehouseCode == outboundOrderDetails.First().WarehouseCode;
+
+                        List<Dt_OutStockLockInfo> stockLockInfos = _outboundLockInfoRepository.QueryData(x =>
+                                x.OrderNo == request.OrderNo &&
+                                x.MaterielCode == stockInfoDetail.MaterielCode).Where(supWhere).Where(wareWhere).ToList();
+                        if (stockLockInfos != null && stockLockInfos.Any())
+                        {
+                            _outboundLockInfoRepository.DeleteAndMoveIntoHty(stockLockInfos, WIDESEA_Core.Enums.OperateTypeEnum.鑷姩鍒犻櫎);
+                        }
+
+                        outboundOrderDetails.FirstOrDefault().OrderDetailStatus = (int)OrderDetailStatusEnum.Over;
+                        _detailRepository.UpdateData(outboundOrderDetails);
+                    }
+
                     // 妫�鏌ュ嚭搴撳崟鏄惁瀹屾垚
                     if (CheckOutboundOrderCompleted(request.OrderNo))
                     {
@@ -1311,9 +1481,15 @@
             _stockDetailHistoryRepository.AddData(historyRecords);
 
             // 鍒犻櫎搴撳瓨鏄庣粏璁板綍
-            _stockDetailRepository.DeleteData(stockInfo.Details);
+            var orderNo =_outboundRepository.QueryFirst(x => x.OrderNo == request.OrderNo);
+            if(orderNo.OrderType != 117)
+            {
+                _stockDetailRepository.DeleteData(stockInfo.Details);
+            }
             _stockChangeRepository.AddData(changeRecords);
         }
+
+
         #endregion
 
         #region 鎷i��
@@ -1533,6 +1709,10 @@
                             item.OverOutQuantity = item.LockQuantity;
                         }
 
+                        if (item.OverOutQuantity == item.OrderQuantity)
+                        {
+                            item.OrderDetailStatus = (int)OrderDetailStatusEnum.Over;
+                        }
                         updateDetails.Add(item);
 
                         List<Barcodes> barcodesList = new List<Barcodes>();
@@ -1606,7 +1786,7 @@
                         _feedbackMesService.BarcodeFeedback(newBarcode);
                     }
 
-                    if (CheckOutboundOrderDetailCompletedByMatCode(request.OrderNo, lockInfo.MaterielCode, outboundOrderDetails.First()))
+                    if (CheckOutboundOrderDetailCompletedByMatCode(request.OrderNo, lockInfo.MaterielCode, outboundOrderDetails))
                     {
                         Func<Dt_OutStockLockInfo, bool> supWhere = x => string.IsNullOrEmpty(outboundOrderDetails.First().SupplyCode) ? true : x.SupplyCode == outboundOrderDetails.First().SupplyCode;
 
@@ -1625,11 +1805,14 @@
                     // 妫�鏌ュ嚭搴撳崟鏄惁瀹屾垚
                     if (CheckOutboundOrderCompleted(request.OrderNo))
                     {
-                        UpdateOutboundOrderStatus(request.OrderNo, OutOrderStatusEnum.鍑哄簱瀹屾垚.ObjToInt());
-
-                        if (outboundOrder.OrderType != OutOrderTypeEnum.InternalAllocat.ObjToInt() && outboundOrder.CreateType!=OrderCreateTypeEnum.CreateInSystem.ObjToInt())
+                        if(outboundOrder.OrderType != OutOrderTypeEnum.InternalAllocat.ObjToInt())
                         {
-                            _feedbackMesService.OutboundFeedback(outboundOrder.OrderNo);
+                            UpdateOutboundOrderStatus(request.OrderNo, OutOrderStatusEnum.鍑哄簱瀹屾垚.ObjToInt());
+
+                            if (outboundOrder.CreateType != OrderCreateTypeEnum.CreateInSystem.ObjToInt())
+                            {
+                                _feedbackMesService.OutboundFeedback(outboundOrder.OrderNo);
+                            }
                         }
                     }
                 }
@@ -1787,6 +1970,7 @@
                 OperateType = "鍑哄簱瀹屾垚",
                 InsertTime = DateTime.Now,
                 StockId = stockDetail.StockId,
+                Barcode = stockDetail.Barcode,
                 MaterielCode = stockDetail.MaterielCode,
                 MaterielName = stockDetail.MaterielName,
                 OrderNo = stockDetail.OrderNo,
@@ -1804,6 +1988,7 @@
                 Creater = stockDetail.Creater,
                 CreateDate = stockDetail.CreateDate,
                 WarehouseCode = stockDetail.WarehouseCode,
+                ValidDate = stockDetail.ValidDate,
                 Remark = $"鍑哄簱瀹屾垚鍒犻櫎锛屾潯鐮侊細{request.Barcode}锛屽師鏁伴噺锛歿stockDetail.StockQuantity}锛屽嚭搴撴暟閲忥細{actualOutboundQuantity}锛屾搷浣滆�咃細{request.Operator}"
             };
             _stockDetailHistoryRepository.AddData(historyRecord);
@@ -1960,14 +2145,30 @@
         /// <summary>
         /// 妫�鏌ュ嚭搴撳崟鏄庣粏鏄惁瀹屾垚
         /// </summary>
-        public bool CheckOutboundOrderDetailCompletedByMatCode(string orderNo, string materialCode, Dt_OutboundOrderDetail outboundOrderDetail)
+        public bool CheckOutboundOrderDetailCompletedByMatCode(string orderNo, string materialCode, List<Dt_OutboundOrderDetail> outboundOrderDetails)
         {
+            if (string.IsNullOrEmpty(orderNo) || string.IsNullOrEmpty(materialCode) || outboundOrderDetails == null || !outboundOrderDetails.Any())
+                return false;
+
+            // 鏌ヨ涓昏鍗曪紝涓嶅瓨鍦ㄧ洿鎺ヨ繑鍥瀎alse
             Dt_OutboundOrder outboundOrder = _outboundRepository.QueryFirst(x => x.OrderNo == orderNo);
             if (outboundOrder == null) return false;
 
-            List<Dt_OutboundOrderDetail> details = _detailRepository.QueryData(x => x.OrderId == outboundOrder.Id && x.MaterielCode == materialCode && (string.IsNullOrEmpty(outboundOrderDetail.SupplyCode) || x.SupplyCode == outboundOrderDetail.SupplyCode) && (string.IsNullOrEmpty(outboundOrderDetail.WarehouseCode) || x.WarehouseCode == outboundOrderDetail.WarehouseCode));
+            var firstDetail = outboundOrderDetails.FirstOrDefault();
+            string supplyCode = firstDetail.SupplyCode;
+            string warehouseCode = firstDetail.WarehouseCode;
+            List<int> ids = outboundOrderDetails.Select(x => x.Id).ToList();
 
-            // 妫�鏌ユ墍鏈夋槑缁嗙殑宸插嚭鏁伴噺鏄惁閮界瓑浜庡崟鎹暟閲�
+            List<Dt_OutboundOrderDetail> details = _detailRepository.QueryData(x =>
+                x.OrderId == outboundOrder.Id
+                && x.MaterielCode == materialCode
+                && ids.Contains(x.Id)
+                && (string.IsNullOrEmpty(supplyCode) || x.SupplyCode == supplyCode)
+                && (string.IsNullOrEmpty(warehouseCode) || x.WarehouseCode == warehouseCode)
+            );
+
+            if (!details.Any()) return false;
+
             return details.All(x => x.OverOutQuantity >= x.OrderQuantity - x.MoveQty);
         }
 
@@ -2031,21 +2232,105 @@
                 if (stock.Details.Count <= 0)
                 {
                     stock.PalletType = (int)PalletTypeEnum.Empty;
-                    stock.StockStatus = (int)StockStatusEmun.缁勭洏鏆傚瓨;
+                    stock.StockStatus = (int)StockStatusEmun.鍏ュ簱纭;
                     stock.LocationCode = "";
                 }
                 else if (stock.Details.Count > 0)
                 {
                     Dt_OutStockLockInfo lockInfo = _outboundLockInfoRepository.QueryFirst(x =>
-                       x.OrderNo == OrderNo &&
                        x.StockId == stock.Id &&
                        x.PalletCode == palletCode);
 
                     if (lockInfo != null && lockInfo.SortedQuantity != lockInfo.AssignQuantity)
                     {
-                        return content.Error($"鎵樼洏{palletCode}搴撳瓨鏈嫞閫夊畬涓嶅厑璁稿洖搴�");
+                        // 1. 璁$畻闇�瑕佸洖婊氱殑鎬绘暟閲�
+                        decimal? rollbackTotalQuantity = lockInfo.AssignQuantity - lockInfo.SortedQuantity;
+                        // 纭繚鍥炴粴鏁伴噺涓烘鏁�
+                        if (rollbackTotalQuantity <= 0)
+                        {
+                            // 娌℃湁闇�瑕佸洖婊氱殑鏁伴噺
+                            stock.StockStatus = (int)StockStatusEmun.鍏ュ簱纭;
+                            stock.LocationCode = "";
+                        }
+
+                        try
+                        {
+                            //澶勭悊OrderDetailIds锛屽垎鍓插苟杞崲涓篒D鍒楄〃
+                            List<long> orderDetailIds = new List<long>();
+                            if (!string.IsNullOrEmpty(lockInfo.OrderDetailIds))
+                            {
+                                orderDetailIds = lockInfo.OrderDetailIds.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
+                                    .Select(id =>
+                                    {
+                                        if (long.TryParse(id.Trim(), out long result))
+                                        {
+                                            return result;
+                                        }
+                                        return 0; // 鏃犳晥ID鏍囪涓�0
+                                    })
+                                    .Where(id => id > 0)
+                                    .OrderByDescending(id => id)
+                                    .ToList();
+                            }
+
+                            if (orderDetailIds.Count == 0)
+                            {
+                                return WebResponseContent.Instance.Error("鍗曟嵁閿佸畾鍑哄簱鍗曟槑缁咺d鏃犳晥锛屾鏌ラ攣瀹氬嚭搴撴暟鎹槸鍚︽纭�");
+                            }
+
+                            //鏌ヨ瀵瑰簲鐨勮鍗曟槑缁�
+                            List<Dt_OutboundOrderDetail> orderDetails = _outboundRepository.Db.Queryable<Dt_OutboundOrderDetail>()
+                                .Where(x => orderDetailIds.Contains(x.Id))
+                                .ToList();
+
+                            if (orderDetails.Count == 0)
+                            {
+                                return WebResponseContent.Instance.Error("鏈壘鍒板彲鍥炴粴鏄庣粏锛岃妫�鏌ュ嚭搴撳崟鏄庣粏");
+                            }
+
+                            decimal remainingRollbackQty = (decimal)rollbackTotalQuantity;
+                            foreach (var detail in orderDetails)
+                            {
+                                if (remainingRollbackQty <= 0)
+                                {
+                                    break;
+                                }
+
+                                // 璁$畻璇ユ槑缁嗙殑鍙洖婊氭暟閲�
+                                decimal availableRollbackQty = detail.LockQuantity - detail.OverOutQuantity - detail.MoveQty;
+
+                                availableRollbackQty = Math.Max(0, availableRollbackQty);
+
+                                if (availableRollbackQty <= 0)
+                                {
+                                    continue; // 璇ユ槑缁嗘棤鍙洖婊氭暟閲忥紝璺宠繃
+                                }
+
+                                // 璁$畻鏈瀹為檯鍥炴粴鏁伴噺锛堝彇鍙洖婊氭暟閲忓拰鍓╀綑闇�瑕佸洖婊氭暟閲忕殑杈冨皬鍊硷級
+                                decimal actualRollbackQty = Math.Min(availableRollbackQty, remainingRollbackQty);
+
+                                detail.LockQuantity -= actualRollbackQty;
+                                detail.LockQuantity = Math.Max(0, detail.LockQuantity);
+
+                                _detailRepository.UpdateData(detail);
+
+                                //鏇存柊鍓╀綑闇�瑕佸洖婊氱殑鏁伴噺
+                                remainingRollbackQty -= actualRollbackQty;
+                            }
+                            
+                            if (remainingRollbackQty > 0)
+                            {
+                                return WebResponseContent.Instance.Error($"鍓╀綑鍥炴粴鏁伴噺{remainingRollbackQty}");
+                            }
+                            _outboundLockInfoRepository.DeleteAndMoveIntoHty(lockInfo, WIDESEA_Core.Enums.OperateTypeEnum.浜哄伐鍒犻櫎);
+                        }
+                        catch (Exception ex)
+                        {
+                            return WebResponseContent.Instance.Error(ex.Message);
+                        }
                     }
-                    stock.StockStatus = (int)StockStatusEmun.缁勭洏鏆傚瓨;
+
+                    stock.StockStatus = (int)StockStatusEmun.鍏ュ簱纭;
                     stock.LocationCode = "";
                 }
 
@@ -2160,5 +2445,304 @@
         }
 
         #endregion
+
+        #region 鎾ら攢鎷i��
+        /// <summary>
+        /// 鎾ら攢鎷i�夋潯鐮侊紙鍙嶅悜鍥炴粴鍑哄簱鎷i�夋搷浣滐級
+        /// </summary>
+        /// <param name="request">鎾ら攢鎷i�夎姹傦紙鑷冲皯鍖呭惈鏉$爜銆佽鍗曞彿銆佹墭鐩樺彿锛�</param>
+        /// <returns>鎾ら攢鍝嶅簲</returns>
+        public WebResponseContent ReversePicking(ReversePickingRequestDTO request)
+        {
+            WebResponseContent content = WebResponseContent.Instance;
+            ReversePickingResponseDTO response = new ReversePickingResponseDTO();
+
+            try
+            {
+                if (string.IsNullOrWhiteSpace(request.Barcode) || string.IsNullOrWhiteSpace(request.OrderNo) || string.IsNullOrWhiteSpace(request.PalletCode))
+                {
+                    response.Success = false;
+                    response.Message = "鏉$爜銆佽鍗曞彿銆佹墭鐩樺彿涓嶈兘涓虹┖";
+                    return WebResponseContent.Instance.Error(response.Message);
+                }
+
+                Dt_StockInfo stockInfo = _stockInfoRepository.QueryFirst(x => x.PalletCode == request.PalletCode);
+                if (stockInfo == null)
+                {
+                    response.Success = false;
+                    response.Message = $"鎵樼洏鍙� {request.PalletCode} 瀵瑰簲鐨勫簱瀛樹笉瀛樺湪";
+                    return WebResponseContent.Instance.Error(response.Message);
+                }
+
+                Dt_OutboundOrder outboundOrder = _outboundRepository.QueryFirst(o => o.OrderNo == request.OrderNo);
+                if (outboundOrder == null)
+                {
+                    response.Success = false;
+                    response.Message = $"鍑哄簱鍗� {request.OrderNo} 涓嶅瓨鍦�";
+                    return WebResponseContent.Instance.Error(response.Message);
+                }
+
+                Dt_StockInfoDetail_Hty historyDetail = _stockDetailHistoryRepository.QueryFirst(x =>
+                    x.Barcode == request.Barcode &&
+                 
+                    (x.OperateType == "鍑哄簱瀹屾垚" || x.OperateType == "鎷嗗寘-鍘熷璁板綍"));
+
+                if(historyDetail != null)
+                {
+                    double minutesDiff = (DateTime.Now - historyDetail.InsertTime).TotalMinutes;
+                    if (minutesDiff >= 30)
+                    {
+                        return WebResponseContent.Instance.Error($"鏉$爜{request.Barcode}宸叉棤娉曟挙閿�");
+                    }
+                }
+
+                Dt_OutStockLockInfo lockInfo = _outboundLockInfoRepository.QueryFirst(x =>
+                    x.OrderNo == request.OrderNo &&
+                    x.StockId == stockInfo.Id &&
+                    x.MaterielCode == historyDetail.MaterielCode &&
+                    x.PalletCode == stockInfo.PalletCode);
+
+                _unitOfWorkManage.BeginTran();
+                try
+                {
+                    if(lockInfo == null)
+                    {
+                        return WebResponseContent.Instance.Error("璇ユ墭鐩樺凡鍏ㄩ儴鎷i�夊畬,涓嶅厑璁告挙閿�");
+                    }
+                    bool isUnpack = historyDetail.OperateType == "鎷嗗寘-鍘熷璁板綍";
+                    if (isUnpack)
+                    {
+                        return WebResponseContent.Instance.Error("璇ユ潯鐮佸凡鎷嗗寘锛屼笉鍏佽鎾ら攢");
+                    }
+                    else
+                    {
+                        ReverseFullOutboundOperation(historyDetail, stockInfo, request);
+                    }
+                    RollbackOutboundOrderDetails(historyDetail.MaterielCode, request.OrderNo, historyDetail.StockQuantity, stockInfo.Id);
+
+                    if (lockInfo != null)
+                    {
+                        lockInfo.SortedQuantity = Math.Max(0, (decimal)(lockInfo.SortedQuantity - historyDetail.StockQuantity));
+
+                        if (lockInfo.SortedQuantity == 0)
+                        {
+                            _outboundLockInfoRepository.UpdateData(lockInfo);
+                        }
+                        else
+                        {
+                            _outboundLockInfoRepository.UpdateData(lockInfo);
+                        }
+                    }
+
+                    _stockDetailHistoryRepository.DeleteData(historyDetail);
+
+                    DeleteStockChangeRecord(request.Barcode, request.OrderNo);
+
+                    if (!CheckOutboundOrderCompleted(request.OrderNo))
+                    {
+                        UpdateOutboundOrderStatus(request.OrderNo, OutOrderStatusEnum.鍑哄簱涓�.ObjToInt());
+                    }
+
+                    _unitOfWorkManage.CommitTran();
+
+                    response.Success = true;
+                    response.Message = $"鏉$爜 {request.Barcode} 鎾ら攢鎷i�夋垚鍔�";
+                    response.Barcode = request.Barcode;
+                    response.RestoredQuantity = historyDetail.StockQuantity;
+
+                    content = WebResponseContent.Instance.OK(data: response);
+                }
+                catch (Exception ex)
+                {
+                    _unitOfWorkManage.RollbackTran();
+                    response.Success = false;
+                    response.Message = $"鎾ら攢鎷i�夊け璐ワ細{ex.Message}";
+                    content = WebResponseContent.Instance.Error(response.Message);
+                }
+            }
+            catch (Exception ex)
+            {
+                response.Success = false;
+                response.Message = $"澶勭悊鎾ら攢鎷i�夊け璐ワ細{ex.Message}";
+                content = WebResponseContent.Instance.Error(response.Message);
+            }
+
+            return content;
+        }
+
+        /// <summary>
+        /// 鎾ら攢鎷嗗寘鍑哄簱鎿嶄綔锛堝弽鍚戞仮澶嶆媶鍖呭墠鐘舵�侊級
+        /// </summary>
+        private void ReverseUnpackOperation(Dt_StockInfoDetail_Hty historyDetail, Dt_StockInfo stockInfo, ReversePickingRequestDTO request, Dt_OutboundOrder outboundOrder)
+        {
+            Dt_StockInfoDetail currentDetail = _stockDetailRepository.QueryFirst(x =>
+                x.Barcode == request.Barcode &&
+                x.StockId == stockInfo.Id &&
+                x.MaterielCode == historyDetail.MaterielCode);
+
+            if (currentDetail != null)
+            {
+                currentDetail.StockQuantity = historyDetail.StockQuantity;
+                currentDetail.Remark = $"鎾ら攢鎷嗗寘鎷i�夛紝鎭㈠鍘熷鏁伴噺锛歿historyDetail.StockQuantity}锛屾搷浣滆�咃細{request.Operator}";
+                _stockDetailRepository.UpdateData(currentDetail);
+            }
+            else
+            {
+                Dt_StockInfoDetail restoreDetail = new Dt_StockInfoDetail
+                {
+                    StockId = historyDetail.StockId,
+                    MaterielCode = historyDetail.MaterielCode,
+                    MaterielName = historyDetail.MaterielName,
+                    Barcode =historyDetail.Barcode,
+                    OrderNo = historyDetail.OrderNo,
+                    BatchNo = historyDetail.BatchNo,
+                    ProductionDate = historyDetail.ProductionDate,
+                    EffectiveDate = historyDetail.EffectiveDate,
+                    SerialNumber = historyDetail.SerialNumber,
+                    StockQuantity = historyDetail.StockQuantity,
+                    OutboundQuantity = historyDetail.OutboundQuantity,
+                    Status = historyDetail.Status,
+                    Unit = historyDetail.Unit,
+                    InboundOrderRowNo = historyDetail.InboundOrderRowNo,
+                    SupplyCode = historyDetail.SupplyCode,
+                    Creater = request.Operator,
+                    CreateDate = DateTime.Now,
+                    FactoryArea = historyDetail.FactoryArea,
+                    WarehouseCode = historyDetail.WarehouseCode,
+                    Remark = $"鎾ら攢鎷嗗寘鎷i�夛紝閲嶆柊鍒涘缓搴撳瓨鏄庣粏锛屾潯鐮侊細{request.Barcode}锛屾搷浣滆�咃細{request.Operator}"
+                };
+                _stockDetailRepository.AddData(restoreDetail);
+            }
+
+            List<Dt_MaterialCodeInfo> materialCodeInfos = _basicService.MaterielCodeInfoService.Repository.QueryData(x =>
+                x.OldBarcode == request.Barcode &&
+                x.OrderNo == request.OrderNo);
+            if (materialCodeInfos.Any())
+            {
+                _basicService.MaterielCodeInfoService.Repository.DeleteData(materialCodeInfos);
+            }
+
+            if (outboundOrder.OrderType == InOrderTypeEnum.InternalAllocat.ObjToInt())
+            {
+                Dt_AllocateMaterialInfo allocateMaterialInfo = _allocateMaterialInfoRepository.QueryFirst(x =>
+                    x.Barcode == request.Barcode &&
+                    x.OrderNo == request.OrderNo);
+                if (allocateMaterialInfo != null)
+                {
+                    _allocateMaterialInfoRepository.DeleteData(allocateMaterialInfo);
+                }
+            }
+        }
+
+        /// <summary>
+        /// 鎾ら攢瀹屾暣鍑哄簱鎿嶄綔锛堟仮澶嶈鍒犻櫎鐨勫簱瀛樻槑缁嗭級
+        /// </summary>
+        private void ReverseFullOutboundOperation(Dt_StockInfoDetail_Hty historyDetail, Dt_StockInfo stockInfo, ReversePickingRequestDTO request)
+        {
+            Dt_StockInfoDetail restoreDetail = new Dt_StockInfoDetail
+            {
+             
+                StockId = historyDetail.StockId,
+                MaterielCode = historyDetail.MaterielCode,
+                MaterielName = historyDetail.MaterielName,
+                Barcode = historyDetail.Barcode,
+                OrderNo = historyDetail.OrderNo,
+                BatchNo = historyDetail.BatchNo,
+                ProductionDate = historyDetail.ProductionDate,
+                EffectiveDate = historyDetail.EffectiveDate,
+                SerialNumber = historyDetail.SerialNumber,
+                StockQuantity = historyDetail.StockQuantity,
+                OutboundQuantity = historyDetail.OutboundQuantity - historyDetail.StockQuantity,
+                Status = historyDetail.Status,
+                Unit = historyDetail.Unit,
+                InboundOrderRowNo = historyDetail.InboundOrderRowNo,
+                SupplyCode = historyDetail.SupplyCode,
+                Creater = request.Operator,
+                CreateDate = DateTime.Now,
+                FactoryArea = historyDetail.FactoryArea,
+                WarehouseCode = historyDetail.WarehouseCode,
+                Remark = $"鎾ら攢瀹屾暣鍑哄簱鎷i�夛紝鎭㈠搴撳瓨鏄庣粏锛屾潯鐮侊細{request.Barcode}锛屾搷浣滆�咃細{request.Operator}"
+            };
+
+            _stockDetailRepository.AddData(restoreDetail);
+
+            if (request.OrderType == InOrderTypeEnum.InternalAllocat.ObjToInt())
+            {
+                Dt_AllocateMaterialInfo allocateMaterialInfo = _allocateMaterialInfoRepository.QueryFirst(x =>
+                    x.Barcode == request.Barcode &&
+                    x.OrderNo == request.OrderNo);
+                if (allocateMaterialInfo != null)
+                {
+                    _allocateMaterialInfoRepository.DeleteData(allocateMaterialInfo);
+                }
+            }
+        }
+
+        /// <summary>
+        /// 鍥炴粴鍑哄簱鍗曟槑缁嗭紙鎵e噺宸插嚭搴撴暟閲忥級
+        /// </summary>
+        private void RollbackOutboundOrderDetails(string materielCode, string orderNo, decimal rollbackQuantity, int stockId)
+        {
+            Dt_OutboundOrder outboundOrder = _outboundRepository.QueryFirst(x => x.OrderNo == orderNo);
+            List<Dt_OutboundOrderDetail> details = _detailRepository.QueryData(x =>
+                x.OrderId == outboundOrder.Id &&
+                x.MaterielCode == materielCode &&
+                x.OverOutQuantity > 0);
+
+            if (!details.Any()) return;
+
+            decimal remainingRollbackQty = rollbackQuantity;
+            foreach (var detail in details)
+            {
+                if (remainingRollbackQty <= 0) break;
+
+                decimal rollbackQty = Math.Min(remainingRollbackQty, detail.OverOutQuantity);
+
+                detail.OverOutQuantity -= rollbackQty;
+                detail.CurrentDeliveryQty -= rollbackQty;
+
+                if (detail.OrderDetailStatus == (int)OrderDetailStatusEnum.Over && detail.OverOutQuantity < detail.OrderQuantity - detail.MoveQty)
+                {
+                    detail.OrderDetailStatus = (int)OrderDetailStatusEnum.New;
+                }
+
+                if (!string.IsNullOrEmpty(detail.ReturnJsonData))
+                {
+                    List<Barcodes> barcodesList = JsonConvert.DeserializeObject<List<Barcodes>>(detail.ReturnJsonData) ?? new List<Barcodes>();
+                    var targetBarcode = barcodesList.FirstOrDefault(x => x.Barcode == materielCode);
+                    if (targetBarcode != null)
+                    {
+                        barcodesList.Remove(targetBarcode);
+                        detail.ReturnJsonData = JsonConvert.SerializeObject(barcodesList, new JsonSerializerSettings
+                        {
+                            ContractResolver = new CamelCasePropertyNamesContractResolver()
+                        });
+                    }
+                }
+
+                _detailRepository.UpdateData(detail);
+                remainingRollbackQty -= rollbackQty;
+            }
+        }
+
+        /// <summary>
+        /// 鍒犻櫎搴撳瓨鍙樺姩璁板綍锛堟嫞閫夋椂鐢熸垚鐨勶級
+        /// </summary>
+        private void DeleteStockChangeRecord(string barcode, string orderNo)
+        {
+            Dt_StockQuantityChangeRecord changeRecord = _stockChangeRepository.QueryFirst(x =>
+                x.OriginalSerilNumber == barcode &&
+                x.OrderNo == orderNo &&
+                x.ChangeType == (int)StockChangeTypeEnum.Outbound);
+            if (changeRecord != null)
+            {
+                _stockChangeRepository.DeleteData(changeRecord);
+            }
+        }
+
+       
+        #endregion
+
+       
     }
 }

--
Gitblit v1.9.3