pan
2025-11-21 482aa82a99419383848cabbdf135744259b17c77
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundPickingService.cs
@@ -1,6 +1,7 @@
using Dm.filter;
using MailKit.Search;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using SqlSugar;
using System;
@@ -17,6 +18,7 @@
using WIDESEA_Core.BaseServices;
using WIDESEA_Core.Helper;
using WIDESEA_DTO.Basic;
using WIDESEA_DTO.Inbound;
using WIDESEA_DTO.Outbound;
using WIDESEA_IBasicService;
using WIDESEA_IOutboundService;
@@ -43,7 +45,8 @@
        private readonly ISplitPackageService _splitPackageService;
        private readonly IRepository<Dt_Task> _taskRepository;
        private readonly IESSApiService _eSSApiService;
        private readonly IInvokeMESService _invokeMESService;
        private readonly IDailySequenceService _dailySequenceService;
        private readonly ILogger<OutboundPickingService> _logger;
@@ -61,7 +64,10 @@
        };
        public OutboundPickingService(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) : base(BaseDal)
        public OutboundPickingService(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) : base(BaseDal)
        {
            _unitOfWorkManage = unitOfWorkManage;
            _stockInfoService = stockInfoService;
@@ -75,6 +81,8 @@
            _taskRepository = taskRepository;
            _eSSApiService = eSSApiService;
            _logger = logger;
            _invokeMESService = invokeMESService;
            _dailySequenceService = dailySequenceService;
        }
@@ -139,212 +147,19 @@
            }
        }
        /// <summary>
        /// æ‰«ç æ‹£é€‰ç¡®è®¤
        /// </summary>
        public async Task<WebResponseContent> ConfirmPicking(PickingConfirmRequest request)
        {
            try
            {
                _unitOfWorkManage.BeginTran();
                // 1. éªŒè¯æ¡ç æœ‰æ•ˆæ€§
                var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                    .Where(x => x.Barcode == request.Barcode && x.MaterielCode == request.MaterielCode)
                    .FirstAsync();
                if (stockDetail == null)
                    return WebResponseContent.Instance.Error("无效的条码或物料编码");
                // 2. æ£€æŸ¥åº“存可用数量
                decimal availableQuantity = stockDetail.StockQuantity - stockDetail.OutboundQuantity;
                if (request.PickQuantity > availableQuantity)
                    return WebResponseContent.Instance.Error($"拣选数量超过可用库存,可用数量:{availableQuantity}");
                // 3. æŸ¥æ‰¾ç›¸å…³çš„出库锁定信息(支持拆包后的新条码)
                var lockInfo = await FindLockInfoByBarcode(request.OrderDetailId, request.Barcode, request.MaterielCode);
                if (lockInfo == null)
                    return WebResponseContent.Instance.Error("未找到相关的出库锁定信息");
                // 4. æ£€æŸ¥é”å®šæ•°é‡
                decimal remainingLockQuantity = lockInfo.AssignQuantity - lockInfo.PickedQty;
                if (request.PickQuantity > remainingLockQuantity)
                    return WebResponseContent.Instance.Error($"拣选数量超过锁定数量,剩余可拣选:{remainingLockQuantity}");
                // 5. æ›´æ–°é”å®šä¿¡æ¯çš„已拣选数量
                lockInfo.PickedQty += request.PickQuantity;
                await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
                // 6. æ›´æ–°åº“存出库数量 - å®žé™…减少库存
                stockDetail.OutboundQuantity += request.PickQuantity;
                await _stockInfoService.Db.Updateable(stockDetail).ExecuteCommandAsync();
                // 7. æ›´æ–°å‡ºåº“单明细
                var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                    .Where(x => x.Id == request.OrderDetailId)
                    .FirstAsync();
                orderDetail.OverOutQuantity += request.PickQuantity;
                orderDetail.LockQuantity -= request.PickQuantity;
                // æ£€æŸ¥æ˜¯å¦å®Œæˆå‡ºåº“
                if (Math.Abs(orderDetail.OverOutQuantity - orderDetail.OrderQuantity) < 0.001m)
                {
                    orderDetail.OrderDetailStatus = (int)OrderDetailStatusEnum.Over;
                    orderDetail.LockQuantity = 0;
                    // æ›´æ–°ç›¸å…³çš„锁定信息状态为已出库
                    var relatedLockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                        .Where(x => x.OrderDetailId == request.OrderDetailId &&
                                   x.Status == (int)OutLockStockStatusEnum.出库中)
                        .ToListAsync();
                    foreach (var relatedLock in relatedLockInfos)
                    {
                        relatedLock.Status = (int)OutLockStockStatusEnum.已出库;
                    }
                    await _outStockLockInfoService.Db.Updateable(relatedLockInfos).ExecuteCommandAsync();
                }
                await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync();
                // 8. è®°å½•拣选历史
                var pickHistory = new Dt_PickingRecord
                {
                    OrderDetailId = request.OrderDetailId,
                    Barcode = request.Barcode,
                    PickQuantity = request.PickQuantity,
                    PickTime = DateTime.Now,
                    LocationCode = request.LocationCode,
                    StockId = stockDetail.StockId
                };
                await Db.Insertable(pickHistory).ExecuteCommandAsync();
                _unitOfWorkManage.CommitTran();
                return WebResponseContent.Instance.OK("拣选确认成功");
            }
            catch (Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                return WebResponseContent.Instance.Error($"拣选确认失败: {ex.Message}");
            }
        }
        public async Task<WebResponseContent> ConfirmPicking(string orderNo, string palletCode, string barcode)
        {
            try
            {
                _unitOfWorkManage.BeginTran();
                var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                  .Where(it => it.OrderNo == orderNo &&
                             it.Status == (int)OutLockStockStatusEnum.出库中 &&
                             it.PalletCode == palletCode &&
                             it.CurrentBarcode == barcode)
                  .FirstAsync();
                if (lockInfo == null)
                {
                    var splitBarcode = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
                   .Where(it => it.NewBarcode == barcode && it.Status == 1)
                   .FirstAsync();
                    if (splitBarcode != null)
                    {
                        // é€šè¿‡æ‹†åŒ…条码记录找到对应的出库锁定记录
                        lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                            .Where(it => it.ParentLockId == splitBarcode.OutStockLockInfoId)
                            .FirstAsync();
                        if (lockInfo == null)
                            throw new Exception($"未找到拆包条码{barcode}对应的出库锁定记录");
                    }
                    else
                    {
                        throw new Exception($"条码{barcode}不属于托盘{palletCode}或不存在待分拣记录");
                    }
                }
                if (lockInfo.PalletCode != palletCode)
                    throw new Exception($"条码{barcode}不属于托盘{palletCode}");
                var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                        .Where(x => x.Barcode == barcode && x.StockId == lockInfo.StockId)
                        .FirstAsync();
                if (stockDetail == null)
                    return WebResponseContent.Instance.Error("无效的条码或物料编码");
                decimal actualQty = lockInfo.AssignQuantity - lockInfo.PickedQty;
                // 4. æ›´æ–°åº“å­˜
                stockDetail.StockQuantity -= actualQty;
                stockDetail.OutboundQuantity -= actualQty;
                await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
                lockInfo.PickedQty += actualQty;
                lockInfo.Status = (int)OutLockStockStatusEnum.拣选完成;
                await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
                var splitBarcodeRecord = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
               .Where(it => it.NewBarcode == barcode)
               .FirstAsync();
                if (splitBarcodeRecord != null)
                {
                    splitBarcodeRecord.Status = 2;
                    await _splitPackageService.Db.Updateable(splitBarcodeRecord).ExecuteCommandAsync();
                }
                await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
               .SetColumns(it => it.PickedQty == it.PickedQty + actualQty)
               .Where(it => it.Id == lockInfo.OrderDetailId)
               .ExecuteCommandAsync();
                await CheckAndUpdateOrderStatus(orderNo);
                //查询任务表
                var task = _taskRepository.QueryData(x => x.OrderNo == orderNo && x.PalletCode == palletCode).FirstOrDefault();
                // 9. è®°å½•拣选历史
                var pickingHistory = new Dt_PickingRecord
                {
                    FactoryArea = lockInfo.FactoryArea,
                    TaskNo = task?.TaskNum ?? 0,
                    LocationCode = task?.SourceAddress ?? "",
                    StockId = stockDetail.Id,
                    OrderNo = orderNo,
                    OrderDetailId = lockInfo.OrderDetailId,
                    PalletCode = palletCode,
                    Barcode = barcode,
                    MaterielCode = lockInfo.MaterielCode,
                    PickQuantity = lockInfo.AssignQuantity,
                    PickTime = DateTime.Now,
                    Operator = App.User.UserName,
                    OutStockLockId = lockInfo.Id
                };
                await Db.Insertable(pickingHistory).ExecuteCommandAsync();
                _unitOfWorkManage.CommitTran();
                return WebResponseContent.Instance.OK("拣选确认成功");
            }
            catch (Exception ex)
            {
                return WebResponseContent.Instance.Error($"拣选确认失败:{ex.Message}");
            }
        }
        // æ£€æŸ¥å¹¶æ›´æ–°è®¢å•状态
        private async Task CheckAndUpdateOrderStatus(string orderNo)
        {
            var orderDetails = _stockInfoDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                      .LeftJoin<Dt_OutboundOrder>((o, item) => o.OrderId == item.Id) // å…³è”条件:父表 Id = å­è¡¨ OrderId
                      .Where((o, item) => item.OrderNo == orderNo) // è¿‡æ»¤çˆ¶è¡¨ OrderNo
                      .Select((o, item) => o) // åªè¿”回子表数据
                      .ToList();
            var orderDetails = await _stockInfoDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                .Where(x => x.OrderId == orderNo.ObjToInt())
                .ToListAsync();
            //var orderDetails = await _stockInfoDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
            //    .Where(x => x.OrderId == orderNo.ObjToInt())
            //    .ToListAsync();
            bool allCompleted = true;
            foreach (var detail in orderDetails)
@@ -358,347 +173,1291 @@
            if (allCompleted)
            {
                await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
                    .SetColumns(x => x.OrderStatus == 2) // å·²å®Œæˆ
                    .Where(x => x.OrderNo == orderNo)
                    .ExecuteCommandAsync();
                try
                {
                    await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
                        .SetColumns(x => x.OrderStatus == 2) // å·²å®Œæˆ
                        .Where(x => x.OrderNo == orderNo)
                        .ExecuteCommandAsync();
                    var outboundOrder = _stockInfoService.Db.Queryable<Dt_OutboundOrder>().First(x => x.OrderNo == orderNo);
                    if (outboundOrder != null && outboundOrder.OrderStatus == OutOrderStatusEnum.出库完成.ObjToInt())
                    {
                        if (outboundOrder.OrderType == OutOrderTypeEnum.Allocate.ObjToInt().ObjToInt())//调拨出库
                        {
                        }
                        else if (outboundOrder.OrderType == OutOrderTypeEnum.ReCheck.ObjToInt()) //重检出库
                        {
                        }
                        else
                        {
                            var feedmodel = new FeedbackOutboundRequestModel
                            {
                                reqCode = Guid.NewGuid().ToString(),
                                reqTime = DateTime.Now.ToString(),
                                business_type = outboundOrder.BusinessType,
                                factoryArea = outboundOrder.FactoryArea,
                                operationType = 1,
                                Operator = outboundOrder.Operator,
                                orderNo = outboundOrder.UpperOrderNo,
                                status = outboundOrder.OrderStatus,
                                details = new List<FeedbackOutboundDetailsModel>()
                            };
                            var lists = _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>().Where(x => x.OrderNo == orderNo && x.Status == (int)OutLockStockStatusEnum.拣选完成).ToList();
                            var groupedData = lists.GroupBy(item => new { item.MaterielCode, item.lineNo, item.Unit, item.WarehouseCode })
                               .Select(group => new FeedbackOutboundDetailsModel
                               {
                                   materialCode = group.Key.MaterielCode,
                                   lineNo = group.Key.lineNo,
                                   warehouseCode = group.Key.WarehouseCode,
                                   currentDeliveryQty = group.Sum(x => x.OrderQuantity),
                                   // warehouseCode= "1072",
                                   unit = group.Key.Unit,
                                   barcodes = group.Select(row => new WIDESEA_DTO.Outbound.BarcodesModel
                                   {
                                       barcode = row.CurrentBarcode,
                                       supplyCode = row.SupplyCode,
                                       batchNo = row.BatchNo,
                                       unit = row.Unit,
                                       qty = row.PickedQty
                                   }).ToList()
                               }).ToList();
                            feedmodel.details = groupedData;
                            var result = await _invokeMESService.FeedbackOutbound(feedmodel);
                            if (result != null && result.code == 200)
                            {
                                await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>().SetColumns(x => x.ReturnToMESStatus == 1) // å·²å®Œæˆ
                                            .Where(x => x.OrderId == outboundOrder.Id).ExecuteCommandAsync();
                                await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>() .SetColumns(x => x.ReturnToMESStatus == 1) // å·²å®Œæˆ
                                              .Where(x => x.OrderNo == orderNo) .ExecuteCommandAsync();
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogError(" OutboundPickingService  FeedbackOutbound : " + ex.Message);
                }
            }
        }
        public async Task<WebResponseContent> ConfirmPicking(string orderNo, string palletCode, string barcode)
        {
            try
            {
                _unitOfWorkManage.BeginTran();
        /// <summary>
        /// å›žåº“操作
        /// </summary>
        //public async Task<WebResponseContent> ReturnRemaining(string orderNo, string palletCode, string reason)
        //{
        //    try
        //    {
        //        // 1. èŽ·å–æ‰€æœ‰æœªåˆ†æ‹£çš„å‡ºåº“é”å®šè®°å½•ï¼ŒåŒ…æ‹¬æ‹†åŒ…äº§ç”Ÿçš„è®°å½•
        //        var remainingLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
        //            .Where(it => it.OrderNo == orderNo && it.Status == 1)
        //            .ToListAsync();
                // 1. éªŒè¯è¾“入参数
                if (string.IsNullOrEmpty(orderNo) || string.IsNullOrEmpty(palletCode) || string.IsNullOrEmpty(barcode))
                {
                    throw new Exception("订单号、托盘码和条码不能为空");
                }
        //        if (!remainingLocks.Any())
        //        {
        //            return WebResponseContent.Instance.Error("没有需要回库的剩余货物");
        //        }
        //        var tasks = new List<Dt_Task>();
        //        // æŒ‰æ‰˜ç›˜åˆ†ç»„
        //        var palletGroups = remainingLocks.GroupBy(x => x.PalletCode);
        //        //查询任务表
        //        var task = _taskRepository.QueryData(x => x.TaskNum == remainingLocks.First().TaskNum).FirstOrDefault();
        //        foreach (var group in palletGroups)
        //        {
        //            if (group.Key == palletCode)
        //            {
        //                var totalReturnQty = group.Sum(x => x.AssignQuantity - x.PickedQty);
                // 2. æŸ¥æ‰¾å‡ºåº“锁定信息
                var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .Where(it => it.OrderNo == orderNo &&
                               it.Status == (int)OutLockStockStatusEnum.出库中 &&
                               it.PalletCode == palletCode &&
                               it.CurrentBarcode == barcode &&
                               it.AssignQuantity > it.PickedQty)
                    .FirstAsync();
        //                if (totalReturnQty <= 0) continue;
                if (lockInfo == null)
                {
                    lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                        .Where(it => it.CurrentBarcode == barcode &&
                                   it.Status == (int)OutLockStockStatusEnum.出库中 &&
                                   it.AssignQuantity > it.PickedQty)
                        .FirstAsync();
        //                // åˆ†é…æ–°è´§ä½
        //                var newLocation = _locationInfoService.AssignLocation();
                    if (lockInfo == null)
                    {
                        var completedLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                            .Where(it => it.CurrentBarcode == barcode &&
                                       (it.Status == (int)OutLockStockStatusEnum.拣选完成 ||
                                        it.PickedQty >= it.AssignQuantity))
                            .FirstAsync();
        //                // æ›´æ–°å‡ºåº“锁定记录状态
        //                var lockIds = group.Where(x => x.PalletCode == palletCode).Select(x => x.Id).ToList();
        //                await _outStockLockInfoService.Db.Updateable<Dt_OutStockLockInfo>()
        //                    .SetColumns(it => new Dt_OutStockLockInfo { Status = OutLockStockStatusEnum.回库中.ObjToInt() })
        //                    .Where(it => lockIds.Contains(it.Id))
        //                    .ExecuteCommandAsync();
                        if (completedLockInfo != null)
                            throw new Exception($"条码{barcode}已经完成分拣,不能重复分拣");
                        else
                            throw new Exception($"条码{barcode}不属于托盘{palletCode}或不存在待分拣记录");
                    }
                }
        //                // æ›´æ–°æ‹†åŒ…条码记录状态
        //                var splitBarcodes = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
        //                    .Where(it => lockIds.Contains(it.OutStockLockInfoId))
        //                    .ToListAsync();
                if (lockInfo.PalletCode != palletCode)
                    throw new Exception($"条码{barcode}不属于托盘{palletCode}");
        //                foreach (var splitBarcode in splitBarcodes)
        //                {
        //                    splitBarcode.Status = 3;
        //                    await _splitPackageService.Db.Updateable(splitBarcode).ExecuteCommandAsync();
        //                }
                // æ£€æŸ¥æ‹£é€‰åŽ†å²ï¼Œé˜²æ­¢é‡å¤åˆ†æ‹£
                var existingPicking = await Db.Queryable<Dt_PickingRecord>()
                    .Where(x => x.Barcode == barcode &&
                               x.OrderNo == orderNo &&
                               x.PalletCode == palletCode &&
                               x.OutStockLockId == lockInfo.Id)
                    .FirstAsync();
        //                foreach (var lockInfo in group)
        //                {
        //                    if (lockInfo.PalletCode == palletCode)
        //                    {
        //                        decimal returnQty = lockInfo.AssignQuantity - lockInfo.PickedQty;
                if (existingPicking != null)
                {
                    throw new Exception($"条码{barcode}已经分拣过,不能重复分拣");
                }
        //                        // æ£€æŸ¥åº“存记录是否存在
        //                        var existingStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
        //                            .Where(it => it.Barcode == lockInfo.CurrentBarcode && it.StockId == lockInfo.StockId)
        //                            .FirstAsync();
                // èŽ·å–è®¢å•æ˜Žç»†å¹¶æ£€æŸ¥æ•°é‡é™åˆ¶
                var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                    .FirstAsync(x => x.Id == lockInfo.OrderDetailId);
        //                        if (existingStock != null)
        //                        {
        //                            // åº“存记录存在,恢复锁定数量
        //                            await _stockInfoDetailService.Db.Updateable<Dt_StockInfoDetail>()
        //                                .SetColumns(it => new Dt_StockInfoDetail
        //                                {
        //                                    OutboundQuantity = it.OutboundQuantity - returnQty
        //                                })
        //                                .Where(it => it.Barcode == lockInfo.CurrentBarcode && it.StockId == lockInfo.StockId)
        //                                .ExecuteCommandAsync();
        //                        }
        //                        else
        //                        {
        //                            // åº“存记录不存在(可能是拆包产生的新条码),创建新的库存记录
        //                            var newStockDetail = new Dt_StockInfoDetail
        //                            {
        //                                StockId = lockInfo.StockId,
        //                                MaterielCode = lockInfo.MaterielCode,
        //                                OrderNo = lockInfo.OrderNo,
        //                                BatchNo = lockInfo.BatchNo,
        //                                StockQuantity = returnQty, // å®žé™…库存数量
        //                                OutboundQuantity = 0, // å›žåº“后不再锁定
        //                                Barcode = lockInfo.CurrentBarcode,
        //                                InboundOrderRowNo = "0",
        //                                Status = StockStatusEmun.入库确认.ObjToInt(),
                if (orderDetail == null)
                    throw new Exception($"未找到订单明细,ID: {lockInfo.OrderDetailId}");
        //                            };
        //                            await _stockInfoDetailService.Db.Insertable(newStockDetail).ExecuteCommandAsync();
        //                        }
        //                    }
        //                }
                // å…³é”®ä¿®å¤ï¼šæ£€æŸ¥ç´¯è®¡æ‹£é€‰æ•°é‡æ˜¯å¦ä¼šè¶…过订单数量
                decimal actualQty = lockInfo.AssignQuantity - lockInfo.PickedQty;
                decimal remainingOrderQty = orderDetail.NeedOutQuantity - orderDetail.OverOutQuantity;
                if (actualQty > remainingOrderQty)
                {
                    // å¦‚果分配数量大于剩余订单数量,调整实际拣选数量
                    actualQty = remainingOrderQty;
        //                Dt_Task newtask = new()
        //                {
        //                    CurrentAddress = stations[task.TargetAddress],
        //                    Grade = 0,
        //                    PalletCode = palletCode,
        //                    NextAddress = "",
        //                    OrderNo= task.OrderNo,
        //                    Roadway = newLocation.RoadwayNo,
        //                    SourceAddress = stations[task.TargetAddress],
        //                    TargetAddress = newLocation.LocationCode,
        //                    TaskStatus = TaskStatusEnum.New.ObjToInt(),
        //                    TaskType = TaskTypeEnum.InPick.ObjToInt(),
        //                    // TaskNum = BaseDal.GetTaskNum(nameof(SequenceEnum.SeqTaskNum)),
        //                    PalletType = task.PalletType,
        //                    WarehouseId = task.WarehouseId,
        //                };
        //                tasks.Add(newtask);
                    if (actualQty <= 0)
                    {
                        throw new Exception($"订单{orderNo}的需求数量已满足,无法继续分拣");
                    }
        //            }
        //        }
        //        try
        //        {
        //            await _taskRepository.Db.Insertable(tasks).ExecuteCommandAsync();
        //            //删除 å‡ºåº“çš„  task
                    _logger.LogWarning($"调整分拣数量:原分配{lockInfo.AssignQuantity - lockInfo.PickedQty},调整为{actualQty},订单需求{orderDetail.NeedOutQuantity},已出库{orderDetail.OverOutQuantity}");
                }
                var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                    .Where(x => x.Barcode == barcode && x.StockId == lockInfo.StockId)
                    .FirstAsync();
        //            //给 ess  æµåŠ¨ä¿¡å·  å’Œåˆ›å»ºä»»åŠ¡
                if (stockDetail == null)
                    return WebResponseContent.Instance.Error("无效的条码或物料编码");
                decimal stockQuantity = stockDetail.StockQuantity;
        //        }
        //        catch (Exception ex)
        //        {
                List<SplitResult> splitResults = new List<SplitResult>();
                Dt_OutStockLockInfo finalLockInfo = lockInfo;
                var finalBarcode = barcode;
                var finalStockId = stockDetail.Id;
                decimal actualPickedQty = actualQty;
        //        }
                if (actualQty < stockQuantity)
                {
                    // æƒ…况1: åˆ†é…æ•°é‡å°äºŽåº“存数量,需要自动拆包
                    decimal remainingStockQty = stockQuantity - actualQty;
                    // æ›´æ–°åŽŸæ¡ç åº“å­˜ä¸ºå‰©ä½™æ•°é‡
                    stockDetail.StockQuantity = remainingStockQty;
                    stockDetail.OutboundQuantity = remainingStockQty;
                    await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
                    // ç”Ÿæˆæ–°æ¡ç ç”¨äºŽè®°å½•拣选数量
                    var seq = await _dailySequenceService.GetNextSequenceAsync();
                    string newBarcode = "WSLOT" + DateTime.Now.ToString("yyyyMMdd") + seq.ToString()?.PadLeft(5, '0');
                    // ä¸ºæ–°æ¡ç åˆ›å»ºå‡ºåº“锁定信息
                    var newLockInfo = new Dt_OutStockLockInfo
                    {
                        OrderNo = lockInfo.OrderNo,
                        OrderDetailId = lockInfo.OrderDetailId,
                        BatchNo = lockInfo.BatchNo,
                        MaterielCode = lockInfo.MaterielCode,
                        MaterielName = lockInfo.MaterielName,
                        StockId = lockInfo.StockId,
                        OrderQuantity = actualQty,
                        OriginalQuantity = actualQty,
                        AssignQuantity = actualQty,
                        PickedQty = actualQty,
                        LocationCode = lockInfo.LocationCode,
                        PalletCode = lockInfo.PalletCode,
                        TaskNum = lockInfo.TaskNum,
                        Status = (int)OutLockStockStatusEnum.拣选完成,
                        Unit = lockInfo.Unit,
                        SupplyCode = lockInfo.SupplyCode,
                        OrderType = lockInfo.OrderType,
                        CurrentBarcode = newBarcode,
                        OriginalLockQuantity = actualQty,
                        IsSplitted = 1,
                        ParentLockId = lockInfo.Id
                    };
        //        return WebResponseContent.Instance.OK();
        //    }
        //    catch (Exception ex)
        //    {
        //        return WebResponseContent.Instance.Error($"回库操作失败: {ex.Message}");
        //    }
        //}
                    var newLockId = await _outStockLockInfoService.Db.Insertable(newLockInfo).ExecuteReturnIdentityAsync();
                    newLockInfo.Id = newLockId;
                    // è®°å½•拆包历史
                    var splitHistory = new Dt_SplitPackageRecord
                    {
                        FactoryArea = lockInfo.FactoryArea,
                        TaskNum = lockInfo.TaskNum,
                        OutStockLockInfoId = lockInfo.Id,
                        StockId = stockDetail.StockId,
                        Operator = App.User.UserName,
                        IsReverted = false,
                        OriginalBarcode = barcode,
                        NewBarcode = newBarcode,
                        SplitQty = actualQty,
                        RemainQuantity = remainingStockQty,
                        MaterielCode = lockInfo.MaterielCode,
                        SplitTime = DateTime.Now,
                        OrderNo = lockInfo.OrderNo,
                        PalletCode = lockInfo.PalletCode,
                        Status = (int)SplitPackageStatusEnum.已拣选
                    };
                    await _splitPackageService.Db.Insertable(splitHistory).ExecuteCommandAsync();
                    // æ›´æ–°åŽŸé”å®šä¿¡æ¯ä¸ºå‰©ä½™åº“å­˜æ•°é‡
                    lockInfo.AssignQuantity = remainingStockQty;
                    lockInfo.PickedQty = 0;
                    await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
                    splitResults.Add(new SplitResult
                    {
                        materialCode = lockInfo.MaterielCode,
                        supplierCode = lockInfo.SupplyCode,
                        quantityTotal = actualQty.ToString("F2"),
                        batchNumber = newBarcode,
                        batch = lockInfo.BatchNo,
                        factory = lockInfo.FactoryArea,
                        date = DateTime.Now.ToString("yyyy-MM-dd"),
                    });
                    splitResults.Add(new SplitResult
                    {
                        materialCode = lockInfo.MaterielCode,
                        supplierCode = lockInfo.SupplyCode,
                        quantityTotal = remainingStockQty.ToString("F2"),
                        batchNumber = barcode,
                        batch = lockInfo.BatchNo,
                        factory = lockInfo.FactoryArea,
                        date = DateTime.Now.ToString("yyyy-MM-dd"),
                    });
                    finalLockInfo = newLockInfo;
                    finalBarcode = newBarcode;
                }
                else if (actualQty == stockQuantity)
                {
                    // æƒ…况2: åˆ†é…æ•°é‡ç­‰äºŽåº“存数量,整包出库
                    stockDetail.StockQuantity = 0;
                    stockDetail.OutboundQuantity = 0;
                    await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
                    lockInfo.PickedQty += actualQty;
                    lockInfo.Status = (int)OutLockStockStatusEnum.拣选完成;
                    await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
                }
                else
                {
                    // æƒ…况3: åˆ†é…æ•°é‡å¤§äºŽåº“存数量,库存整包出库
                    decimal stockOutQty = stockQuantity;
                    stockDetail.StockQuantity = 0;
                    stockDetail.OutboundQuantity = 0;
                    await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
                    decimal remainingAssignQty = actualQty - stockQuantity;
                    lockInfo.PickedQty += stockOutQty;
                    lockInfo.AssignQuantity = remainingAssignQty;
                    await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
                    var relatedSplitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
                        .Where(it => it.OriginalBarcode == barcode || it.NewBarcode == barcode)
                        .Where(it => !it.IsReverted)
                        .ToListAsync();
                    foreach (var record in relatedSplitRecords)
                    {
                        record.Status = (int)SplitPackageStatusEnum.已拣选;
                        await _splitPackageService.Db.Updateable(record).ExecuteCommandAsync();
                    }
                    actualPickedQty = stockOutQty;
                }
                // å…³é”®ä¿®å¤ï¼šå†æ¬¡æ£€æŸ¥è®¢å•数量限制
                decimal newOverOutQuantity = orderDetail.OverOutQuantity + actualPickedQty;
                decimal newPickedQty = orderDetail.PickedQty + actualPickedQty;
                if (newOverOutQuantity > orderDetail.NeedOutQuantity)
                {
                    throw new Exception($"分拣后将导致已出库数量({newOverOutQuantity})超过订单需求数量({orderDetail.NeedOutQuantity})");
                }
                // æ›´æ–°è®¢å•明细
                await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                    .SetColumns(it => new Dt_OutboundOrderDetail
                    {
                        PickedQty = newPickedQty,
                        OverOutQuantity = newOverOutQuantity
                    })
                    .Where(it => it.Id == lockInfo.OrderDetailId)
                    .ExecuteCommandAsync();
                await CheckAndUpdateOrderStatus(orderNo);
                // æŸ¥è¯¢ä»»åŠ¡è¡¨
                var task = _taskRepository.QueryData(x => x.OrderNo == orderNo && x.PalletCode == palletCode).FirstOrDefault();
                if (finalLockInfo.Id <= 0)
                {
                    throw new Exception($"锁定信息ID无效: {finalLockInfo.Id},无法记录拣选历史");
                }
                // è®°å½•拣选历史
                var pickingHistory = new Dt_PickingRecord
                {
                    FactoryArea = finalLockInfo.FactoryArea,
                    TaskNo = task?.TaskNum ?? 0,
                    LocationCode = task?.SourceAddress ?? "",
                    StockId = finalStockId,
                    OrderNo = orderNo,
                    OrderDetailId = finalLockInfo.OrderDetailId,
                    PalletCode = palletCode,
                    Barcode = finalBarcode,
                    MaterielCode = finalLockInfo.MaterielCode,
                    PickQuantity = actualPickedQty,
                    PickTime = DateTime.Now,
                    Operator = App.User.UserName,
                    OutStockLockId = finalLockInfo.Id
                };
                await Db.Insertable(pickingHistory).ExecuteCommandAsync();
                _unitOfWorkManage.CommitTran();
                if (splitResults.Any())
                {
                    return WebResponseContent.Instance.OK("拣选确认成功,已自动拆包", new { SplitResults = splitResults });
                }
                return WebResponseContent.Instance.OK("拣选确认成功", new { SplitResults = new List<SplitResult>() });
            }
            catch (Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                _logger.LogError($"ConfirmPicking失败 - OrderNo: {orderNo}, PalletCode: {palletCode}, Barcode: {barcode}, Error: {ex.Message}");
                return WebResponseContent.Instance.Error($"拣选确认失败:{ex.Message}");
            }
        }
        public async Task<WebResponseContent> CancelPicking(string orderNo, string palletCode, string barcode)
        {
            try
            {
                _unitOfWorkManage.BeginTran();
                // æŸ¥æ‰¾æ‹£é€‰è®°å½•
                var pickingRecord = await Db.Queryable<Dt_PickingRecord>()
                    .Where(it => it.OrderNo == orderNo &&
                               it.PalletCode == palletCode &&
                               it.Barcode == barcode)
                    .OrderByDescending(it => it.PickTime)
                    .FirstAsync();
                if (pickingRecord == null)
                    return WebResponseContent.Instance.Error("未找到对应的拣选记录");
                // æŸ¥æ‰¾å‡ºåº“锁定信息
                var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .Where(it => it.Id == pickingRecord.OutStockLockId)
                    .FirstAsync();
                if (lockInfo == null)
                    return WebResponseContent.Instance.Error("未找到对应的出库锁定信息");
                // æ£€æŸ¥æ˜¯å¦å¯ä»¥å–消
                if (lockInfo.Status != (int)OutLockStockStatusEnum.拣选完成)
                    return WebResponseContent.Instance.Error("当前状态不允许取消分拣");
                decimal cancelQty = pickingRecord.PickQuantity;
                // èŽ·å–è®¢å•æ˜Žç»†
                var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                    .FirstAsync(x => x.Id == pickingRecord.OrderDetailId);
                if (orderDetail == null)
                    throw new Exception($"未找到订单明细,ID: {pickingRecord.OrderDetailId}");
                // å…³é”®ä¿®å¤ï¼šæ£€æŸ¥å–消后数量不会为负数
                decimal newOverOutQuantity = orderDetail.OverOutQuantity - cancelQty;
                decimal newPickedQty = orderDetail.PickedQty - cancelQty;
                if (newOverOutQuantity < 0 || newPickedQty < 0)
                {
                    throw new Exception($"取消分拣将导致已出库数量或已拣选数量为负数");
                }
                // å¤„理取消逻辑
                if (lockInfo.IsSplitted == 1 && lockInfo.ParentLockId.HasValue)
                {
                    await HandleSplitBarcodeCancel(lockInfo, pickingRecord, cancelQty);
                }
                else
                {
                    await HandleNormalBarcodeCancel(lockInfo, pickingRecord, cancelQty);
                }
                // æ›´æ–°è®¢å•明细
                await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                    .SetColumns(it => new Dt_OutboundOrderDetail
                    {
                        PickedQty = newPickedQty,
                        OverOutQuantity = newOverOutQuantity
                    })
                    .Where(it => it.Id == pickingRecord.OrderDetailId)
                    .ExecuteCommandAsync();
                // åˆ é™¤æ‹£é€‰è®°å½•
                await Db.Deleteable<Dt_PickingRecord>()
                    .Where(x => x.Id == pickingRecord.Id)
                    .ExecuteCommandAsync();
                // é‡æ–°æ£€æŸ¥è®¢å•状态
                await CheckAndUpdateOrderStatus(orderNo);
                _unitOfWorkManage.CommitTran();
                return WebResponseContent.Instance.OK($"取消分拣成功,恢复数量:{cancelQty}");
            }
            catch (Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                _logger.LogError($"CancelPicking失败 - OrderNo: {orderNo}, PalletCode: {palletCode}, Barcode: {barcode}, Error: {ex.Message}");
                return WebResponseContent.Instance.Error($"取消分拣失败:{ex.Message}");
            }
        }
        private async Task HandleSplitBarcodeCancel(Dt_OutStockLockInfo lockInfo, Dt_PickingRecord pickingRecord, decimal cancelQty)
        {
            // æŸ¥æ‰¾çˆ¶é”å®šä¿¡æ¯
            var parentLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(x => x.Id == lockInfo.ParentLockId.Value)
                .FirstAsync();
            if (parentLockInfo == null)
            {
                throw new Exception("未找到父锁定信息,无法取消拆包分拣");
            }
            // æ¢å¤çˆ¶é”å®šä¿¡æ¯çš„分配数量
            parentLockInfo.AssignQuantity += cancelQty;
            await _outStockLockInfoService.Db.Updateable(parentLockInfo).ExecuteCommandAsync();
            // æ¢å¤åº“å­˜
            var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .Where(x => x.Barcode == parentLockInfo.CurrentBarcode && x.StockId == parentLockInfo.StockId)
                .FirstAsync();
            if (stockDetail != null)
            {
                stockDetail.StockQuantity += cancelQty;
                stockDetail.OutboundQuantity += cancelQty;
                await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
            }
            // æ›´æ–°æ‹†åŒ…记录状态
            await _splitPackageService.Db.Updateable<Dt_SplitPackageRecord>()
                .SetColumns(x => new Dt_SplitPackageRecord
                {
                    Status = (int)SplitPackageStatusEnum.已撤销,
                    IsReverted = true
                })
                .Where(x => x.NewBarcode == lockInfo.CurrentBarcode && !x.IsReverted)
                .ExecuteCommandAsync();
            // åˆ é™¤æ‹†åŒ…产生的锁定信息
            await _outStockLockInfoService.Db.Deleteable<Dt_OutStockLockInfo>()
                .Where(x => x.Id == lockInfo.Id)
                .ExecuteCommandAsync();
        }
        private async Task HandleNormalBarcodeCancel(Dt_OutStockLockInfo lockInfo, Dt_PickingRecord pickingRecord, decimal cancelQty)
        {
            // æ¢å¤é”å®šä¿¡æ¯
            lockInfo.PickedQty -= cancelQty;
            if (lockInfo.PickedQty < 0) lockInfo.PickedQty = 0;
            lockInfo.Status = (int)OutLockStockStatusEnum.出库中;
            await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
            // æ¢å¤åº“å­˜
            var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .Where(x => x.Barcode == pickingRecord.Barcode && x.StockId == pickingRecord.StockId)
                .FirstAsync();
            if (stockDetail != null)
            {
                stockDetail.StockQuantity += cancelQty;
                stockDetail.OutboundQuantity += cancelQty;
                await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
            }
        }
        public async Task<WebResponseContent> ReturnRemaining(string orderNo, string palletCode, string reason)
        {
            try
            {
                // 1. èŽ·å–æ‰€æœ‰æœªåˆ†æ‹£çš„å‡ºåº“é”å®šè®°å½•ï¼ŒåŒ…æ‹¬æ‹†åŒ…äº§ç”Ÿçš„è®°å½•
                var remainingLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .Where(it => it.OrderNo == orderNo && it.Status == 1)
                    .ToListAsync();
                var stockinfo = _stockInfoService.Db.Queryable<Dt_StockInfo>().First(x => x.PalletCode == palletCode);
                // 2. æ£€æŸ¥æ‰˜ç›˜ä¸Šæ˜¯å¦æœ‰å…¶ä»–非出库货物(库存货物)
                var palletStockGoods = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                    .Where(it => it.StockId == stockinfo.Id && it.Status == StockStatusEmun.入库确认.ObjToInt())
                    .Where(it => it.OutboundQuantity == 0 || it.OutboundQuantity < it.StockQuantity) // æœªå®Œå…¨å‡ºåº“çš„
                    .ToListAsync();
                // 3. å¦‚果没有需要回库的货物(既无未分拣出库货物,也无其他库存货物)
                if (!remainingLocks.Any() && !palletStockGoods.Any())
                _unitOfWorkManage.BeginTran();
                if (string.IsNullOrEmpty(orderNo) || string.IsNullOrEmpty(palletCode))
                {
                    return WebResponseContent.Instance.Error("没有需要回库的剩余货物");
                }
                    return WebResponseContent.Instance.Error("订单号和托盘码不能为空");
                }
                var stockInfo = await _stockInfoService.Db.Queryable<Dt_StockInfo>()
                    .FirstAsync(x => x.PalletCode == palletCode);
                var tasks = new List<Dt_Task>();
                // æŸ¥è¯¢ä»»åŠ¡è¡¨
                var task = remainingLocks.Any()
                    ? _taskRepository.QueryData(x => x.TaskNum == remainingLocks.First().TaskNum).FirstOrDefault()
                    : _taskRepository.QueryData(x => x.PalletCode == palletCode).FirstOrDefault();
                if (stockInfo == null)
                {
                    return WebResponseContent.Instance.Error($"未找到托盘 {palletCode} å¯¹åº”的库存信息");
                }
                var task = await GetCurrentTask(orderNo, palletCode);
                if (task == null)
                {
                    return WebResponseContent.Instance.Error("未找到对应的任务信息");
                }
                var firstlocation = _locationInfoService.Db.Queryable<Dt_LocationInfo>().First(x => x.LocationCode == task.SourceAddress);
                decimal totalReturnQty = 0;
                var hasRemainingLocks = remainingLocks.Any(x => x.PalletCode == palletCode);
                // æƒ…况1:处理未分拣的出库锁定记录
                if (hasRemainingLocks)
                // åˆ†æžéœ€è¦å›žåº“的货物
                var returnAnalysis = await AnalyzeReturnItems(orderNo, palletCode, stockInfo.Id);
                if (!returnAnalysis.HasItemsToReturn)
                {
                    var palletLocks = remainingLocks.Where(x => x.PalletCode == palletCode).ToList();
                    totalReturnQty = palletLocks.Sum(x => x.AssignQuantity - x.PickedQty);
                    if (totalReturnQty > 0)
                    {
                        // åˆ†é…æ–°è´§ä½
                        var newLocation = _locationInfoService.AssignLocation(firstlocation.LocationType);
                        // æ›´æ–°å‡ºåº“锁定记录状态
                        var lockIds = palletLocks.Select(x => x.Id).ToList();
                        await _outStockLockInfoService.Db.Updateable<Dt_OutStockLockInfo>()
                            .SetColumns(it => new Dt_OutStockLockInfo { Status = OutLockStockStatusEnum.回库中.ObjToInt() })
                            .Where(it => lockIds.Contains(it.Id))
                            .ExecuteCommandAsync();
                        // æ›´æ–°æ‹†åŒ…条码记录状态
                        var splitBarcodes = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
                            .Where(it => lockIds.Contains(it.OutStockLockInfoId))
                            .ToListAsync();
                        foreach (var splitBarcode in splitBarcodes)
                        {
                            splitBarcode.Status = 3;
                            await _splitPackageService.Db.Updateable(splitBarcode).ExecuteCommandAsync();
                        }
                        // å¤„理库存记录
                        foreach (var lockInfo in palletLocks)
                        {
                            decimal returnQty = lockInfo.AssignQuantity - lockInfo.PickedQty;
                            // æ£€æŸ¥åº“存记录是否存在
                            var existingStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                                .Where(it => it.Barcode == lockInfo.CurrentBarcode && it.StockId == lockInfo.StockId)
                                .FirstAsync();
                            if (existingStock != null)
                            {
                                // åº“存记录存在,恢复锁定数量
                                await _stockInfoDetailService.Db.Updateable<Dt_StockInfoDetail>()
                                    .SetColumns(it => new Dt_StockInfoDetail
                                    {
                                        OutboundQuantity = it.OutboundQuantity - returnQty
                                    })
                                    .Where(it => it.Barcode == lockInfo.CurrentBarcode && it.StockId == lockInfo.StockId)
                                    .ExecuteCommandAsync();
                            }
                            else
                            {
                                // åº“存记录不存在(可能是拆包产生的新条码),创建新的库存记录
                                var newStockDetail = new Dt_StockInfoDetail
                                {
                                    StockId = lockInfo.StockId,
                                    MaterielCode = lockInfo.MaterielCode,
                                    OrderNo = lockInfo.OrderNo,
                                    BatchNo = lockInfo.BatchNo,
                                    StockQuantity = returnQty,
                                    OutboundQuantity = 0,
                                    Barcode = lockInfo.CurrentBarcode,
                                    InboundOrderRowNo = "0",
                                    Status = StockStatusEmun.入库确认.ObjToInt(),
                                };
                                await _stockInfoDetailService.Db.Insertable(newStockDetail).ExecuteCommandAsync();
                            }
                        }
                        // åˆ›å»ºå›žåº“任务
                        CreateReturnTask(tasks, task, palletCode, newLocation);
                    }
                    return await HandleNoReturnItems(orderNo, palletCode);
                }
                // æƒ…况2:出库货物已分拣完,但托盘上还有其他库存货物需要回库
                if (!hasRemainingLocks && palletStockGoods.Any())
                {
                    // åˆ†é…æ–°è´§ä½
                    var newLocation = _locationInfoService.AssignLocation(firstlocation.LocationType);
                // æ‰§è¡Œå›žåº“操作
                await ExecuteReturnOperations(orderNo, palletCode, stockInfo, task, returnAnalysis);
                    // åˆ›å»ºå›žåº“任务
                    CreateReturnTask(tasks, task, palletCode, newLocation);
                //创建回库任务并处理ESS
                await CreateReturnTaskAndHandleESS(orderNo, palletCode, task, returnAnalysis);
                    totalReturnQty = palletStockGoods.Sum(x => x.StockQuantity - x.OutboundQuantity);
                }
                _unitOfWorkManage.CommitTran();
                // ä¿å­˜ä»»åŠ¡
                if (tasks.Any())
                {
                    try
                    {
                        await _taskRepository.Db.Insertable(tasks).ExecuteCommandAsync();
                // æ›´æ–°è®¢å•状态
                await UpdateOrderStatusForReturn(orderNo);
                        // ç»™ ESS æµåŠ¨ä¿¡å·å’Œåˆ›å»ºä»»åŠ¡
                        try
                        {
                            var result = await _eSSApiService.MoveContainerAsync(new WIDESEA_DTO.Basic.MoveContainerRequest
                            {
                                slotCode = movestations[task.TargetAddress],
                                containerCode = palletCode
                            });
                            if (result)
                            {
                                TaskModel esstask = new TaskModel()
                                {
                                    taskType = "putaway",
                                    taskGroupCode = "",
                                    groupPriority = 0,
                                    tasks = new List<TasksType>
                                    {
                                        new()
                                        {
                                            taskCode = tasks.First().TaskNum.ToString(),
                                            taskPriority = 0,
                                            taskDescribe = new TaskDescribeType {
                                                containerCode = palletCode,
                                                containerType = "CT_KUBOT_STANDARD",
                                                fromLocationCode = stations.GetValueOrDefault(task.TargetAddress) ?? "",
                                                toStationCode = "",
                                                toLocationCode = tasks.First().TargetAddress,
                                                deadline = 0, storageTag = ""
                                            }
                                        }
                                    }
                                };
                return WebResponseContent.Instance.OK($"回库操作成功,共回库数量:{returnAnalysis.TotalReturnQty}");
                                var resulttask = await _eSSApiService.CreateTaskAsync(esstask);
                                _logger.LogInformation("ReturnRemaining åˆ›å»ºä»»åŠ¡è¿”å›ž:  " + resulttask);
                            }
                        }
                        catch (Exception ex)
                        {
                            _logger.LogInformation("ReturnRemaining åˆ›å»ºä»»åŠ¡è¿”å›ž catch err:  " + ex.Message);
                        }
                        return WebResponseContent.Instance.OK($"回库操作成功,共回库数量:{totalReturnQty}");
                    }
                    catch (Exception ex)
                    {
                        return WebResponseContent.Instance.Error($"创建回库任务失败: {ex.Message}");
                    }
                }
                return WebResponseContent.Instance.Error("未创建任何回库任务");
            }
            catch (Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                _logger.LogError($"ReturnRemaining失败 - OrderNo: {orderNo}, PalletCode: {palletCode}, Error: {ex.Message}");
                return WebResponseContent.Instance.Error($"回库操作失败: {ex.Message}");
            }
        }
        #region è®¢å•状态
        private async Task UpdateOrderStatusForReturn(string orderNo)
        {
            try
            {
                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 = true;
                foreach (var detail in orderDetails)
                {
                    if (detail.OverOutQuantity < detail.NeedOutQuantity)
                    {
                        allCompleted = false;
                        break;
                    }
                }
                var outboundOrder = await _outboundOrderService.Db.Queryable<Dt_OutboundOrder>()
                    .FirstAsync(x => x.OrderNo == orderNo);
                if (outboundOrder == null) return;
                // åªæœ‰å½“状态确实发生变化时才更新
                int newStatus = allCompleted ? (int)OutOrderStatusEnum.出库完成 : (int)OutOrderStatusEnum.出库中;
                if (outboundOrder.OrderStatus != newStatus)
                {
                    await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
                        .SetColumns(x => x.OrderStatus == newStatus)
                        .Where(x => x.OrderNo == orderNo)
                        .ExecuteCommandAsync();
                    _logger.LogInformation($"回库操作更新订单状态 - OrderNo: {orderNo}, æ–°çŠ¶æ€: {newStatus}");
                }
            }
            catch (Exception ex)
            {
                _logger.LogError($"UpdateOrderStatusForReturn失败 - OrderNo: {orderNo}, Error: {ex.Message}");
            }
        }
        #endregion
        #region Private Methods
        private async Task<Dt_Task> GetCurrentTask(string orderNo, string palletCode)
        {
            // å…ˆå°è¯•通过订单号和托盘号查找任务
            var task = await _taskRepository.Db.Queryable<Dt_Task>()
                .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode)
                .FirstAsync();
            if (task == null)
            {
                // å¦‚果找不到,再通过托盘号查找
                task = await _taskRepository.Db.Queryable<Dt_Task>()
                    .Where(x => x.PalletCode == palletCode)
                    .FirstAsync();
            }
            return task;
        }
        private async Task<ReturnAnalysisResult> AnalyzeReturnItems(string orderNo, string palletCode, int stockId)
        {
            var result = new ReturnAnalysisResult();
            // èŽ·å–æœªåˆ†æ‹£çš„å‡ºåº“é”å®šè®°å½•
            var remainingLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(it => it.OrderNo == orderNo &&
                           it.PalletCode == palletCode &&
                           it.Status == (int)OutLockStockStatusEnum.出库中)
                .ToListAsync();
            if (remainingLocks.Any())
            {
                result.HasRemainingLocks = true;
                result.RemainingLocks = remainingLocks;
                result.RemainingLocksReturnQty = remainingLocks.Sum(x => x.AssignQuantity - x.PickedQty);
            }
            // æ£€æŸ¥æ‰˜ç›˜ä¸Šæ˜¯å¦æœ‰å…¶ä»–库存货物
            var palletStockGoods = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .Where(it => it.StockId == stockId &&
                            (it.Status == StockStatusEmun.入库确认.ObjToInt() ||
                             it.Status == StockStatusEmun.入库完成.ObjToInt() ||
                             it.Status == StockStatusEmun.出库锁定.ObjToInt()))
                .Where(it => it.StockQuantity > 0) // æœ‰åº“存的
                .ToListAsync();
            if (palletStockGoods.Any())
            {
                result.HasPalletStockGoods = true;
                result.PalletStockGoods = palletStockGoods;
                result.PalletStockReturnQty = palletStockGoods.Sum(x => x.StockQuantity);
            }
            //  æ£€æŸ¥æ‹†åŒ…记录
            var splitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
                .Where(it => it.OrderNo == orderNo && it.PalletCode == palletCode && !it.IsReverted)
                .ToListAsync();
            if (splitRecords.Any())
            {
                result.HasSplitRecords = true;
                result.SplitRecords = splitRecords;
                result.SplitReturnQty = await CalculateSplitReturnQuantity(splitRecords, stockId);
            }
            result.TotalReturnQty = result.RemainingLocksReturnQty + result.PalletStockReturnQty + result.SplitReturnQty;
            result.HasItemsToReturn = result.TotalReturnQty > 0;
            return result;
        }
        private async Task<decimal> CalculateSplitReturnQuantity(List<Dt_SplitPackageRecord> splitRecords, int stockId)
        {
            decimal totalQty = 0;
            var processedBarcodes = new HashSet<string>();
            foreach (var splitRecord in splitRecords)
            {
                // æ£€æŸ¥åŽŸæ¡ç 
                if (!processedBarcodes.Contains(splitRecord.OriginalBarcode))
                {
                    var originalStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                        .Where(it => it.Barcode == splitRecord.OriginalBarcode && it.StockId == stockId)
                        .FirstAsync();
                    if (originalStock != null && originalStock.StockQuantity > 0)
                    {
                        totalQty += originalStock.StockQuantity;
                        processedBarcodes.Add(splitRecord.OriginalBarcode);
                    }
                }
                // æ£€æŸ¥æ–°æ¡ç 
                if (!processedBarcodes.Contains(splitRecord.NewBarcode))
                {
                    var newStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                        .Where(it => it.Barcode == splitRecord.NewBarcode && it.StockId == stockId)
                        .FirstAsync();
                    if (newStock != null && newStock.StockQuantity > 0)
                    {
                        totalQty += newStock.StockQuantity;
                        processedBarcodes.Add(splitRecord.NewBarcode);
                    }
                }
            }
            return totalQty;
        }
        private async Task<WebResponseContent> HandleNoReturnItems(string orderNo, string palletCode)
        {
            // æ£€æŸ¥æ˜¯å¦æ‰€æœ‰è´§ç‰©éƒ½å·²æ‹£é€‰å®Œæˆ
            var allPicked = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(it => it.OrderNo == orderNo && it.PalletCode == palletCode)
                .AnyAsync(it => it.Status == (int)OutLockStockStatusEnum.拣选完成);
            if (allPicked)
            {
                return WebResponseContent.Instance.OK("所有货物已拣选完成,托盘为空");
            }
            else
            {
                return WebResponseContent.Instance.Error("没有需要回库的剩余货物");
            }
        }
        private async Task ExecuteReturnOperations(string orderNo, string palletCode, Dt_StockInfo stockInfo,
            Dt_Task task, ReturnAnalysisResult analysis)
        {
            // æƒ…况1:处理未分拣的出库锁定记录
            if (analysis.HasRemainingLocks)
            {
                await HandleRemainingLocksReturn(analysis.RemainingLocks, stockInfo.Id);
                // å…³é”®ï¼šæ›´æ–°è®¢å•明细的已拣选数量
                await UpdateOrderDetailsOnReturn(analysis.RemainingLocks);
            }
            // æƒ…况2:处理托盘上其他库存货物
            if (analysis.HasPalletStockGoods)
            {
                await HandlePalletStockGoodsReturn(analysis.PalletStockGoods);
            }
            // æƒ…况3:处理拆包记录
            if (analysis.HasSplitRecords)
            {
                await HandleSplitRecordsReturn(analysis.SplitRecords, orderNo, palletCode);
            }
            // æ›´æ–°åº“存主表状态
            await UpdateStockInfoStatus(stockInfo);
        }
        private async Task HandleRemainingLocksReturn(List<Dt_OutStockLockInfo> remainingLocks, int stockId)
        {
            var lockIds = remainingLocks.Select(x => x.Id).ToList();
            // æ›´æ–°å‡ºåº“锁定记录状态为回库中
            await _outStockLockInfoService.Db.Updateable<Dt_OutStockLockInfo>()
                .SetColumns(it => new Dt_OutStockLockInfo
                {
                    Status = (int)OutLockStockStatusEnum.回库中
                })
                .Where(it => lockIds.Contains(it.Id))
                .ExecuteCommandAsync();
            // å¤„理库存记录
            foreach (var lockInfo in remainingLocks)
            {
                decimal returnQty = lockInfo.AssignQuantity - lockInfo.PickedQty;
                // æŸ¥æ‰¾å¯¹åº”的库存明细
                var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                    .Where(it => it.Barcode == lockInfo.CurrentBarcode && it.StockId == lockInfo.StockId)
                    .FirstAsync();
                if (stockDetail != null)
                {
                    // æ¢å¤åº“存状态
                    stockDetail.OutboundQuantity = Math.Max(0, stockDetail.OutboundQuantity - returnQty);
                    stockDetail.Status = StockStatusEmun.入库完成.ObjToInt();
                    await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
                }
                else
                {
                    // åˆ›å»ºæ–°çš„库存记录(拆包产生的新条码)
                    var newStockDetail = new Dt_StockInfoDetail
                    {
                        StockId = lockInfo.StockId,
                        MaterielCode = lockInfo.MaterielCode,
                        MaterielName = lockInfo.MaterielName,
                        OrderNo = lockInfo.OrderNo,
                        BatchNo = lockInfo.BatchNo,
                        StockQuantity = returnQty,
                        OutboundQuantity = 0,
                        Barcode = lockInfo.CurrentBarcode,
                        InboundOrderRowNo = "",
                        Status = StockStatusEmun.入库完成.ObjToInt(),
                        SupplyCode = lockInfo.SupplyCode,
                        WarehouseCode = lockInfo.WarehouseCode,
                        Unit = lockInfo.Unit
                    };
                    await _stockInfoDetailService.Db.Insertable(newStockDetail).ExecuteCommandAsync();
                }
            }
        }
        private async Task UpdateOrderDetailsOnReturn(List<Dt_OutStockLockInfo> remainingLocks)
        {
            // æŒ‰è®¢å•明细分组
            var orderDetailGroups = remainingLocks.GroupBy(x => x.OrderDetailId);
            foreach (var group in orderDetailGroups)
            {
                var orderDetailId = group.Key;
                var totalReturnQty = group.Sum(x => x.AssignQuantity - x.PickedQty);
                // èŽ·å–å½“å‰è®¢å•æ˜Žç»†
                var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                    .FirstAsync(x => x.Id == orderDetailId);
                if (orderDetail != null)
                {
                    // è°ƒæ•´å·²æ‹£é€‰æ•°é‡å’Œå·²å‡ºåº“数量
                    decimal newPickedQty = Math.Max(0, orderDetail.PickedQty - totalReturnQty);
                    decimal newOverOutQuantity = Math.Max(0, orderDetail.OverOutQuantity - totalReturnQty);
                    await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                        .SetColumns(it => new Dt_OutboundOrderDetail
                        {
                            PickedQty = newPickedQty,
                            OverOutQuantity = newOverOutQuantity
                        })
                        .Where(it => it.Id == orderDetailId)
                        .ExecuteCommandAsync();
                }
            }
        }
        private async Task HandlePalletStockGoodsReturn(List<Dt_StockInfoDetail> palletStockGoods)
        {
            foreach (var stockGood in palletStockGoods)
            {
                // æ¢å¤åº“存状态
                stockGood.OutboundQuantity = 0;
                stockGood.Status = StockStatusEmun.入库完成.ObjToInt();
                await _stockInfoDetailService.Db.Updateable(stockGood).ExecuteCommandAsync();
            }
        }
        private async Task HandleSplitRecordsReturn(List<Dt_SplitPackageRecord> splitRecords, string orderNo, string palletCode)
        {
            // æ›´æ–°æ‹†åŒ…记录状态
            await _splitPackageService.Db.Updateable<Dt_SplitPackageRecord>()
                .SetColumns(x => new Dt_SplitPackageRecord
                {
                    Status = (int)SplitPackageStatusEnum.已回库
                })
                .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode && !x.IsReverted)
                .ExecuteCommandAsync();
        }
        private async Task UpdateStockInfoStatus(Dt_StockInfo stockInfo)
        {
            // æ›´æ–°åº“存主表状态
            stockInfo.StockStatus = StockStatusEmun.入库完成.ObjToInt();
            await _stockInfoService.Db.Updateable(stockInfo).ExecuteCommandAsync();
        }
        private async Task CreateReturnTaskAndHandleESS(string orderNo, string palletCode, Dt_Task originalTask, ReturnAnalysisResult analysis)
        {
            var firstLocation = await _locationInfoService.Db.Queryable<Dt_LocationInfo>()
                .FirstAsync(x => x.LocationCode == originalTask.SourceAddress);
            // åˆ†é…æ–°è´§ä½ï¼ˆå›žåº“到存储位)
            var newLocation = _locationInfoService.AssignLocation(firstLocation.LocationType);
            Dt_Task returnTask = new()
            {
                CurrentAddress = stations[originalTask.TargetAddress],
                Grade = 0,
                PalletCode = palletCode,
                NextAddress = "",
                OrderNo = originalTask.OrderNo,
                Roadway = newLocation.RoadwayNo,
                SourceAddress = stations[originalTask.TargetAddress],
                TargetAddress = newLocation.LocationCode,
                TaskStatus = TaskStatusEnum.New.ObjToInt(),
                TaskType = TaskTypeEnum.InPick.ObjToInt(),
                PalletType = originalTask.PalletType,
                WarehouseId = originalTask.WarehouseId,
            };
            // ä¿å­˜å›žåº“任务
            await _taskRepository.Db.Insertable(returnTask).ExecuteCommandAsync();
            // åˆ é™¤åŽŸå§‹å‡ºåº“ä»»åŠ¡
              _taskRepository.Db.Deleteable(originalTask);
            // ç»™ ESS å‘送流动信号和创建任务
            await SendESSCommands(palletCode, originalTask, returnTask);
        }
        private async Task<int> GenerateTaskNumber()
        {
            return await _dailySequenceService.GetNextSequenceAsync();
        }
        private async Task SendESSCommands(string palletCode, Dt_Task originalTask, Dt_Task returnTask)
        {
            try
            {
                // 1. å‘送流动信号
                var moveResult = await _eSSApiService.MoveContainerAsync(new WIDESEA_DTO.Basic.MoveContainerRequest
                {
                    slotCode = movestations[originalTask.TargetAddress],
                    containerCode = palletCode
                });
                if (moveResult)
                {
                    // 2. åˆ›å»ºå›žåº“任务
                    var essTask = new TaskModel()
                    {
                        taskType = "putaway",
                        taskGroupCode = "",
                        groupPriority = 0,
                        tasks = new List<TasksType>
                {
                    new()
                    {
                        taskCode = returnTask.TaskNum.ToString(),
                        taskPriority = 0,
                        taskDescribe = new TaskDescribeType
                        {
                            containerCode = palletCode,
                            containerType = "CT_KUBOT_STANDARD",
                            fromLocationCode = stations.GetValueOrDefault(originalTask.TargetAddress) ?? "",
                            toStationCode = "",
                            toLocationCode = returnTask.TargetAddress,
                            deadline = 0,
                            storageTag = ""
                        }
                    }
                }
                    };
                    var resultTask = await _eSSApiService.CreateTaskAsync(essTask);
                    _logger.LogInformation($"ReturnRemaining åˆ›å»ºä»»åŠ¡æˆåŠŸ: {resultTask}");
                }
            }
            catch (Exception ex)
            {
                _logger.LogError($"ReturnRemaining ESS命令发送失败: {ex.Message}");
                throw new Exception($"ESS系统通信失败: {ex.Message}");
            }
        }
        #endregion
        /// <summary>
        /// å›žåº“操作
        /// </summary>
        //public async Task<WebResponseContent> ReturnRemaining(string orderNo, string palletCode, string reason)
        //{
        //    try
        //    {
        //        _unitOfWorkManage.BeginTran();
        //        // èŽ·å–æ‰€æœ‰æœªåˆ†æ‹£çš„å‡ºåº“é”å®šè®°å½•ï¼ŒåŒ…æ‹¬æ‹†åŒ…äº§ç”Ÿçš„è®°å½•
        //        var remainingLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
        //            .Where(it => it.OrderNo == orderNo && it.Status == (int)OutLockStockStatusEnum.出库中)
        //            .ToListAsync();
        //        var stockinfo = _stockInfoService.Db.Queryable<Dt_StockInfo>().First(x => x.PalletCode == palletCode);
        //        var tasks = new List<Dt_Task>();
        //        // æŸ¥è¯¢ä»»åŠ¡è¡¨
        //        var task = remainingLocks.Any()
        //            ? _taskRepository.QueryData(x => x.TaskNum == remainingLocks.First().TaskNum).FirstOrDefault()
        //            : _taskRepository.QueryData(x => x.PalletCode == palletCode).FirstOrDefault();
        //        if (task == null)
        //        {
        //            return WebResponseContent.Instance.Error("未找到对应的任务信息");
        //        }
        //        // æ£€æŸ¥æ‰˜ç›˜ä¸Šæ˜¯å¦æœ‰å…¶ä»–非出库货物(库存货物)
        //        var palletStockGoods = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
        //            .Where(it => it.StockId == stockinfo.Id &&
        //                        (it.Status == StockStatusEmun.入库确认.ObjToInt() ||
        //                         it.Status == StockStatusEmun.入库完成.ObjToInt() ||
        //                         it.Status == StockStatusEmun.出库锁定.ObjToInt()))
        //            .Where(it => it.OutboundQuantity == 0 || it.OutboundQuantity < it.StockQuantity) // æœªå®Œå…¨å‡ºåº“çš„
        //            .ToListAsync();
        //        // æ£€æŸ¥æ‹†åŒ…记录,找出需要回库的条码
        //        var splitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
        //            .Where(it => it.OrderNo == orderNo && it.PalletCode == palletCode && !it.IsReverted)
        //            .ToListAsync();
        //        // è®¡ç®—需要回库的拆包条码
        //        var splitBarcodesToReturn = new List<string>();
        //        foreach (var splitRecord in splitRecords)
        //        {
        //            // æ£€æŸ¥åŽŸæ¡ç æ˜¯å¦è¿˜æœ‰åº“å­˜éœ€è¦å›žåº“
        //            var originalStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
        //                .Where(it => it.Barcode == splitRecord.OriginalBarcode && it.StockId == stockinfo.Id)
        //                .FirstAsync();
        //            if (originalStock != null && originalStock.StockQuantity > 0)
        //            {
        //                splitBarcodesToReturn.Add(splitRecord.OriginalBarcode);
        //            }
        //            // æ£€æŸ¥æ–°æ¡ç æ˜¯å¦è¿˜æœ‰åº“存需要回库
        //            var newStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
        //                .Where(it => it.Barcode == splitRecord.NewBarcode && it.StockId == stockinfo.Id)
        //                .FirstAsync();
        //            if (newStock != null && newStock.StockQuantity > 0)
        //            {
        //                splitBarcodesToReturn.Add(splitRecord.NewBarcode);
        //            }
        //        }
        //        // å¦‚果没有需要回库的货物(既无未分拣出库货物,也无其他库存货物,也无拆包剩余货物)
        //        if (!remainingLocks.Any() && !palletStockGoods.Any() && !splitBarcodesToReturn.Any())
        //        {
        //            // æ£€æŸ¥æ˜¯å¦æ‰€æœ‰è´§ç‰©éƒ½å·²æ‹£é€‰å®Œæˆ
        //            var allPicked = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
        //                .Where(it => it.OrderNo == orderNo && it.PalletCode == palletCode)
        //                .AnyAsync(it => it.Status == (int)OutLockStockStatusEnum.拣选完成);
        //            if (allPicked)
        //            {
        //                return WebResponseContent.Instance.OK("所有货物已拣选完成,托盘为空");
        //            }
        //            else
        //            {
        //                return WebResponseContent.Instance.Error("没有需要回库的剩余货物");
        //            }
        //        }
        //        var firstlocation = _locationInfoService.Db.Queryable<Dt_LocationInfo>().First(x => x.LocationCode == task.SourceAddress);
        //        decimal totalReturnQty = 0;
        //        // æƒ…况1:处理未分拣的出库锁定记录
        //        if (remainingLocks.Any(x => x.PalletCode == palletCode))
        //        {
        //            var palletLocks = remainingLocks.Where(x => x.PalletCode == palletCode).ToList();
        //            totalReturnQty = palletLocks.Sum(x => x.AssignQuantity - x.PickedQty);
        //            if (totalReturnQty > 0)
        //            {
        //                // åˆ†é…æ–°è´§ä½
        //                var newLocation = _locationInfoService.AssignLocation(firstlocation.LocationType);
        //                // æ›´æ–°å‡ºåº“锁定记录状态
        //                var lockIds = palletLocks.Select(x => x.Id).ToList();
        //                await _outStockLockInfoService.Db.Updateable<Dt_OutStockLockInfo>()
        //                    .SetColumns(it => new Dt_OutStockLockInfo { Status = (int)OutLockStockStatusEnum.回库中 })
        //                    .Where(it => lockIds.Contains(it.Id))
        //                    .ExecuteCommandAsync();
        //                // å¤„理库存记录
        //                foreach (var lockInfo in palletLocks)
        //                {
        //                    decimal returnQty = lockInfo.AssignQuantity - lockInfo.PickedQty;
        //                    // æ£€æŸ¥åº“存记录是否存在
        //                    var existingStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
        //                        .Where(it => it.Barcode == lockInfo.CurrentBarcode && it.StockId == lockInfo.StockId)
        //                        .FirstAsync();
        //                    if (existingStock != null)
        //                    {
        //                        // åº“存记录存在,恢复锁定数量
        //                        existingStock.OutboundQuantity = 0;
        //                        await _stockInfoDetailService.Db.Updateable(existingStock).ExecuteCommandAsync();
        //                    }
        //                    else
        //                    {
        //                        // åº“存记录不存在(可能是拆包产生的新条码),创建新的库存记录
        //                        var newStockDetail = new Dt_StockInfoDetail
        //                        {
        //                            StockId = lockInfo.StockId,
        //                            MaterielCode = lockInfo.MaterielCode,
        //                            MaterielName = lockInfo.MaterielName,
        //                            OrderNo = lockInfo.OrderNo,
        //                            BatchNo = lockInfo.BatchNo,
        //                            StockQuantity = returnQty,
        //                            OutboundQuantity = 0,
        //                            Barcode = lockInfo.CurrentBarcode,
        //                            InboundOrderRowNo = "",
        //                            Status = StockStatusEmun.入库完成.ObjToInt(),
        //                            SupplyCode = lockInfo.SupplyCode,
        //                            WarehouseCode = lockInfo.WarehouseCode,
        //                            Unit = lockInfo.Unit
        //                        };
        //                        await _stockInfoDetailService.Db.Insertable(newStockDetail).ExecuteCommandAsync();
        //                    }
        //                }
        //                // åˆ›å»ºå›žåº“任务
        //                CreateReturnTask(tasks, task, palletCode, newLocation);
        //            }
        //        }
        //        // æƒ…况2:处理拆包剩余的库存货物
        //        if (splitBarcodesToReturn.Any())
        //        {
        //            decimal splitReturnQty = 0;
        //            foreach (var barcode in splitBarcodesToReturn)
        //            {
        //                var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
        //                    .Where(it => it.Barcode == barcode && it.StockId == stockinfo.Id)
        //                    .FirstAsync();
        //                if (stockDetail != null && stockDetail.StockQuantity > 0)
        //                {
        //                    splitReturnQty += stockDetail.StockQuantity;
        //                    // æ¢å¤åº“存状态为入库完成
        //                    stockDetail.OutboundQuantity = 0;
        //                    await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
        //                }
        //            }
        //            totalReturnQty += splitReturnQty;
        //            // å¦‚果没有创建任务,创建回库任务
        //            if (!tasks.Any())
        //            {
        //                var newLocation = _locationInfoService.AssignLocation(firstlocation.LocationType);
        //                CreateReturnTask(tasks, task, palletCode, newLocation);
        //            }
        //        }
        //        // æƒ…况3:出库货物已分拣完,但托盘上还有其他库存货物需要回库
        //        if (palletStockGoods.Any() && !remainingLocks.Any(x => x.PalletCode == palletCode))
        //        {
        //            decimal otherReturnQty = palletStockGoods.Sum(x => x.StockQuantity - x.OutboundQuantity);
        //            totalReturnQty += otherReturnQty;
        //            // æ›´æ–°è¿™äº›åº“存货物的状态
        //            foreach (var stockGood in palletStockGoods)
        //            {
        //                stockGood.OutboundQuantity = 0;
        //                await _stockInfoDetailService.Db.Updateable(stockGood).ExecuteCommandAsync();
        //            }
        //            // å¦‚果没有创建任务,创建回库任务
        //            if (!tasks.Any())
        //            {
        //                var newLocation = _locationInfoService.AssignLocation(firstlocation.LocationType);
        //                CreateReturnTask(tasks, task, palletCode, newLocation);
        //            }
        //        }
        //        var allSplitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
        //            .Where(it => it.OrderNo == orderNo && it.PalletCode == palletCode && !it.IsReverted)
        //            .ToListAsync();
        //        foreach (var record in allSplitRecords)
        //        {
        //            record.Status = (int)SplitPackageStatusEnum.已回库;
        //            await _splitPackageService.Db.Updateable(record).ExecuteCommandAsync();
        //        }
        //        // ä¿å­˜ä»»åŠ¡ ç»™ESS下发任务
        //        if (tasks.Any())
        //        {
        //            try
        //            {
        //                await _taskRepository.Db.Insertable(tasks).ExecuteCommandAsync();
        //                var targetAddress = task.TargetAddress;
        //                _taskRepository.DeleteData(task);
        //                // ç»™ ESS æµåŠ¨ä¿¡å·å’Œåˆ›å»ºä»»åŠ¡
        //                try
        //                {
        //                    var result = await _eSSApiService.MoveContainerAsync(new WIDESEA_DTO.Basic.MoveContainerRequest
        //                    {
        //                        slotCode = movestations[targetAddress],
        //                        containerCode = palletCode
        //                    });
        //                    if (result)
        //                    {
        //                        TaskModel esstask = new TaskModel()
        //                        {
        //                            taskType = "putaway",
        //                            taskGroupCode = "",
        //                            groupPriority = 0,
        //                            tasks = new List<TasksType>
        //                    {
        //                        new()
        //                        {
        //                            taskCode = tasks.First().TaskNum.ToString(),
        //                            taskPriority = 0,
        //                            taskDescribe = new TaskDescribeType {
        //                                containerCode = palletCode,
        //                                containerType = "CT_KUBOT_STANDARD",
        //                                fromLocationCode = stations.GetValueOrDefault(targetAddress) ?? "",
        //                                toStationCode = "",
        //                                toLocationCode = tasks.First().TargetAddress,
        //                                deadline = 0, storageTag = ""
        //                            }
        //                        }
        //                    }
        //                        };
        //                        var resulttask = await _eSSApiService.CreateTaskAsync(esstask);
        //                        _logger.LogInformation("ReturnRemaining åˆ›å»ºä»»åŠ¡è¿”å›ž:  " + resulttask);
        //                    }
        //                }
        //                catch (Exception ex)
        //                {
        //                    _logger.LogInformation("ReturnRemaining åˆ›å»ºä»»åŠ¡è¿”å›ž catch err:  " + ex.Message);
        //                }
        //                _unitOfWorkManage.CommitTran();
        //                return WebResponseContent.Instance.OK($"回库操作成功,共回库数量:{totalReturnQty}");
        //            }
        //            catch (Exception ex)
        //            {
        //                _unitOfWorkManage.RollbackTran();
        //                return WebResponseContent.Instance.Error($"创建回库任务失败: {ex.Message}");
        //            }
        //        }
        //        _unitOfWorkManage.RollbackTran();
        //        return WebResponseContent.Instance.Error("未创建任何回库任务");
        //    }
        //    catch (Exception ex)
        //    {
        //        _unitOfWorkManage.RollbackTran();
        //        return WebResponseContent.Instance.Error($"回库操作失败: {ex.Message}");
        //    }
        //}
        /// <summary>
        /// åˆ›å»ºå›žåº“任务
@@ -753,69 +1512,227 @@
            return outboundFinished && hasRemainingGoods;
        }
        // å–消拣选功能
        public async Task<WebResponseContent> CancelPicking(string orderNo, string palletCode, string barcode)
        /// <summary>
        /// èŽ·å–æ‹†åŒ…é“¾ï¼ˆä»Žå½“å‰æ¡ç è¿½æº¯åˆ°åŽŸå§‹æ¡ç ï¼‰
        /// </summary>
        // åœ¨ GetSplitChain æ–¹æ³•中添加更严格的验证
        private async Task<List<SplitChainItem>> GetSplitChain(string currentBarcode)
        {
            try
            var chain = new List<SplitChainItem>();
            var visited = new HashSet<string>();
            string current = currentBarcode;
            int maxDepth = 10; // é˜²æ­¢æ— é™å¾ªçޝ
            while (!string.IsNullOrEmpty(current) && maxDepth > 0)
            {
                _unitOfWorkManage.BeginTran();
                // æŸ¥æ‰¾æ‹£é€‰è®°å½•
                var outStockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                        .Where(x => x.OrderNo == orderNo &&
                                   x.PalletCode == palletCode &&
                                   x.CurrentBarcode == barcode &&
                                   x.Status == 2)
                        .FirstAsync();
                maxDepth--;
                if (outStockInfo == null)
                    return WebResponseContent.Instance.Error("未找到已拣选记录");
                if (visited.Contains(current))
                {
                    _logger.LogWarning($"检测到循环引用在拆包链中: {current}");
                    break;
                }
                // è¿˜åŽŸå‡ºåº“è¯¦æƒ…çŠ¶æ€
                outStockInfo.PickedQty = 0;
                outStockInfo.Status = 1;
                await _outStockLockInfoService.Db.Updateable(outStockInfo).ExecuteCommandAsync();
                visited.Add(current);
                //// è¿˜åŽŸåº“å­˜å‡ºåº“æ•°é‡
                //await _stockInfoDetailService.Db.Updateable<Dt_StockInfoDetail>()
                //        .SetColumns(x => x.OutboundQuantity == x.OutboundQuantity - outStockInfo.AssignQuantity)
                //        .Where(x => x.Barcode == barcode)
                //        .ExecuteCommandAsync();
                // è¿˜åŽŸå‡ºåº“å•æ˜Žç»†
                var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                    .Where(x => x.Id == outStockInfo.OrderDetailId)
                var splitRecord = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
                    .Where(it => it.NewBarcode == current && !it.IsReverted)
                    .FirstAsync();
                orderDetail.OverOutQuantity -= outStockInfo.AssignQuantity;
                await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync();
                if (splitRecord == null)
                    break;
                // åˆ é™¤æ‹£é€‰åŽ†å²
                await Db.Deleteable<Dt_PickingRecord>()
                    .Where(x => x.OutStockLockId == outStockInfo.Id)
                    .ExecuteCommandAsync();
                // éªŒè¯æ‹†åŒ…记录的完整性
                if (string.IsNullOrEmpty(splitRecord.OriginalBarcode))
                {
                    _logger.LogError($"拆包记录 {splitRecord.Id} ç¼ºå°‘原始条码");
                    break;
                }
                _unitOfWorkManage.CommitTran();
                return WebResponseContent.Instance.OK("取消拣选成功");
                var item = new SplitChainItem
                {
                    SplitRecord = splitRecord,
                    OriginalBarcode = splitRecord.OriginalBarcode,
                    NewBarcode = splitRecord.NewBarcode,
                    SplitQuantity = splitRecord.SplitQty
                };
                chain.Add(item);
                current = splitRecord.OriginalBarcode;
            }
            catch (Exception ex)
            if (maxDepth <= 0)
            {
                return WebResponseContent.Instance.Error($"取消拣选失败:{ex.Message}");
                _logger.LogWarning($"拆包链追溯达到最大深度: {currentBarcode}");
            }
            chain.Reverse();
            return chain;
        }
        /// <summary>
        /// æ ¹æ®æ¡ç æŸ¥æ‰¾é”å®šä¿¡æ¯
        /// å¤„理拆包链的取消分拣
        /// </summary>
        private async Task<Dt_OutStockLockInfo> FindLockInfoByBarcode(int orderDetailId, string barcode, string materielCode)
        private async Task HandleSplitChainCancel(string orderNo, string palletCode, string barcode,
            decimal cancelQty, Dt_OutStockLockInfo lockInfo, Dt_PickingRecord pickingRecord, List<SplitChainItem> splitChain)
        {
            return await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(x => x.OrderDetailId == orderDetailId &&
                           x.MaterielCode == materielCode &&
                           x.CurrentBarcode == barcode &&
                           x.Status == (int)OutLockStockStatusEnum.出库中 &&
                           x.AssignQuantity > x.PickedQty)
            if (!splitChain.Any())
                return;
            //  æ‰¾åˆ°åŽŸå§‹æ¡ç ï¼ˆé“¾çš„ç¬¬ä¸€ä¸ªï¼‰
            var originalSplitItem = splitChain.First();
            var originalBarcode = originalSplitItem.OriginalBarcode;
            // æŸ¥æ‰¾åŽŸå§‹æ¡ç çš„é”å®šä¿¡æ¯å’Œåº“å­˜
            var originalLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(it => it.CurrentBarcode == originalBarcode && it.Status == (int)OutLockStockStatusEnum.出库中)
                .FirstAsync();
            var originalStockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .Where(it => it.Barcode == originalBarcode && it.StockId == originalLockInfo.StockId)
                .FirstAsync();
            if (originalLockInfo == null || originalStockDetail == null)
                throw new Exception("未找到原始条码的锁定信息或库存信息");
            // æ¢å¤åŽŸå§‹æ¡ç åº“å­˜ï¼ˆå°†å–æ¶ˆçš„æ•°é‡åŠ å›žåŽ»ï¼‰
            originalStockDetail.StockQuantity += cancelQty;
            originalStockDetail.OutboundQuantity += cancelQty;
            await _stockInfoDetailService.Db.Updateable(originalStockDetail).ExecuteCommandAsync();
            // æ¢å¤åŽŸå§‹æ¡ç é”å®šä¿¡æ¯
            originalLockInfo.AssignQuantity += cancelQty;
            await _outStockLockInfoService.Db.Updateable(originalLockInfo).ExecuteCommandAsync();
            //  åˆ é™¤æ‹†åŒ…链中所有新条码的锁定信息和库存记录
            var allNewBarcodes = splitChain.Select(x => x.NewBarcode).ToList();
            // åˆ é™¤é”å®šä¿¡æ¯
            await _outStockLockInfoService.Db.Deleteable<Dt_OutStockLockInfo>()
                .Where(it => allNewBarcodes.Contains(it.CurrentBarcode))
                .ExecuteCommandAsync();
            // åˆ é™¤åº“存记录(只删除拆包产生的新条码库存,保留原始条码)
            await _stockInfoDetailService.Db.Deleteable<Dt_StockInfoDetail>()
                .Where(it => allNewBarcodes.Contains(it.Barcode) && it.Barcode != originalBarcode)
                .ExecuteCommandAsync();
            // æ›´æ–°æ‹†åŒ…链中所有拆包记录状态为已拆包
            foreach (var chainItem in splitChain)
            {
                chainItem.SplitRecord.Status = (int)SplitPackageStatusEnum.已拆包;
                await _splitPackageService.Db.Updateable(chainItem.SplitRecord).ExecuteCommandAsync();
            }
            //  æ¢å¤è®¢å•明细拣选数量(使用原始锁定信息的订单明细ID)
            await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                .SetColumns(it => it.PickedQty == it.PickedQty - cancelQty)
                .Where(it => it.Id == originalLockInfo.OrderDetailId)
                .ExecuteCommandAsync();
            //   æ¢å¤è®¢å•状态
            await CheckAndRevertOrderStatus(orderNo);
            //  åˆ é™¤æ‹£é€‰è®°å½•
            await Db.Deleteable<Dt_PickingRecord>()
                .Where(it => it.Id == pickingRecord.Id)
                .ExecuteCommandAsync();
            ////  è®°å½•取消操作历史
            //await RecordCancelHistory(orderNo, palletCode, barcode, cancelQty, pickingRecord.Id,
            //    lockInfo.MaterielCode, "取消拆包链分拣");
        }
        /// <summary>
        /// å¤„理普通条码的取消分拣
        /// </summary>
        private async Task HandleNormalBarcodeCancel(string orderNo, string palletCode, string barcode,
            decimal cancelQty, Dt_OutStockLockInfo lockInfo, Dt_PickingRecord pickingRecord)
        {
            // 1. æŸ¥æ‰¾åº“存信息
            var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .Where(it => it.Barcode == barcode && it.StockId == lockInfo.StockId)
                .FirstAsync();
            if (stockDetail == null)
                throw new Exception("未找到对应的库存信息");
            // 2. æ¢å¤åº“存数量
            if (stockDetail.StockQuantity == 0)
            {
                // æ•´åŒ…出库的情况
                stockDetail.StockQuantity = cancelQty;
                stockDetail.OutboundQuantity = cancelQty;
            }
            else
            {
                // éƒ¨åˆ†å‡ºåº“的情况
                stockDetail.StockQuantity += cancelQty;
                stockDetail.OutboundQuantity += cancelQty;
            }
            await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
            // 3. æ¢å¤é”å®šä¿¡æ¯çŠ¶æ€
            lockInfo.AssignQuantity += cancelQty;
            lockInfo.PickedQty -= cancelQty;
            if (lockInfo.PickedQty == 0)
            {
                lockInfo.Status = (int)OutLockStockStatusEnum.出库中;
            }
            await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
            // 4. å¤„理相关的拆包记录状态恢复
            var relatedSplitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
                .Where(it => it.OriginalBarcode == barcode &&
                           it.Status == (int)SplitPackageStatusEnum.已拣选)
                .ToListAsync();
            foreach (var record in relatedSplitRecords)
            {
                record.Status = (int)SplitPackageStatusEnum.已拆包;
                await _splitPackageService.Db.Updateable(record).ExecuteCommandAsync();
            }
            // 5. æ¢å¤è®¢å•明细的拣选数量
            await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                .SetColumns(it => it.PickedQty == it.PickedQty - cancelQty)
                .Where(it => it.Id == lockInfo.OrderDetailId)
                .ExecuteCommandAsync();
            // 6. æ¢å¤è®¢å•状态
            await CheckAndRevertOrderStatus(orderNo);
            // 7. åˆ é™¤æ‹£é€‰è®°å½•
            await Db.Deleteable<Dt_PickingRecord>().Where(it => it.Id == pickingRecord.Id).ExecuteCommandAsync();
            //// 8. è®°å½•取消操作历史
            //await RecordCancelHistory(orderNo, palletCode, barcode, cancelQty, pickingRecord.Id,
            //    lockInfo.MaterielCode, "取消分拣");
        }
        /// <summary>
        /// æ£€æŸ¥å¹¶æ¢å¤è®¢å•状态
        /// </summary>
        private async Task CheckAndRevertOrderStatus(string orderNo)
        {
            var order = await _outboundOrderService.Db.Queryable<Dt_OutboundOrder>()
                .Where(x => x.OrderNo == orderNo)
                .FirstAsync();
            if (order != null && order.OrderStatus == OutOrderStatusEnum.出库完成.ObjToInt())
            {
                await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
                    .SetColumns(x => x.OrderStatus == OutOrderStatusEnum.出库中.ObjToInt())
                    .Where(x => x.OrderNo == orderNo)
                    .ExecuteCommandAsync();
            }
        }
        // èŽ·å–æœªæ‹£é€‰åˆ—è¡¨
        public async Task<List<Dt_OutStockLockInfo>> GetUnpickedList(string orderNo, string palletCode)
@@ -896,88 +1813,6 @@
                .ToListAsync();
        }
        public async Task GetPalletPickingSummary(string orderNo, string palletCode)
        {
            var summary = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode)
                .GroupBy(x => new { x.PalletCode, x.Status })
                .Select(x => new
                {
                    PalletCode = x.PalletCode,
                    Status = x.Status,
                    TotalAssignQty = SqlFunc.AggregateSum(x.AssignQuantity),
                    TotalPickedQty = SqlFunc.AggregateSum(x.PickedQty)
                })
                .ToListAsync();
            //   return summary;
        }
        /// <summary>
        /// æ’¤é”€æ‹£é€‰
        /// </summary>
        public async Task<WebResponseContent> CancelPicking(CancelPickingRequest request)
        {
            // å®žçŽ°æ’¤é”€æ‹£é€‰çš„é€»è¾‘ï¼Œéœ€è¦ï¼š
            // 1. æ¢å¤åº“存出库数量
            // 2. æ¢å¤é”å®šä¿¡æ¯çš„已拣选数量
            // 3. æ¢å¤å‡ºåº“单明细的已出数量和锁定数量
            // 4. åˆ é™¤æˆ–标记拣选历史记录
            // æ³¨æ„ï¼šè¿™é‡Œéœ€è¦äº‹åŠ¡å¤„ç†
            try
            {
                _unitOfWorkManage.BeginTran();
                var pickHistory = await Db.Queryable<Dt_PickingRecord>()
                    .Where(x => x.Id == request.PickingHistoryId)
                    .FirstAsync();
                if (pickHistory == null)
                    return WebResponseContent.Instance.Error("未找到拣选记录");
                // æ¢å¤åº“å­˜
                var stockDetail = await _stockInfoService.Db.Queryable<Dt_StockInfoDetail>()
                    .Where(x => x.Barcode == pickHistory.Barcode && x.StockId == pickHistory.StockId)
                    .FirstAsync();
                if (stockDetail != null)
                {
                    stockDetail.OutboundQuantity -= pickHistory.PickQuantity;
                    await _stockInfoService.Db.Updateable(stockDetail).ExecuteCommandAsync();
                }
                // æ¢å¤é”å®šä¿¡æ¯
                var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .Where(x => x.OrderDetailId == pickHistory.OrderDetailId && x.StockId == pickHistory.StockId)
                    .FirstAsync();
                lockInfo.PickedQty -= pickHistory.PickQuantity;
                await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
                // æ¢å¤å‡ºåº“单明细
                var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                    .Where(x => x.Id == pickHistory.OrderDetailId)
                    .FirstAsync();
                orderDetail.OverOutQuantity -= pickHistory.PickQuantity;
                orderDetail.LockQuantity += pickHistory.PickQuantity;
                if (orderDetail.OverOutQuantity < orderDetail.OrderQuantity)
                {
                    orderDetail.OrderDetailStatus = orderDetail.LockQuantity > 0 ?
                        (int)OrderDetailStatusEnum.Outbound : (int)OrderDetailStatusEnum.AssignOverPartial;
                }
                await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync();
                // åˆ é™¤æ‹£é€‰åŽ†å²è®°å½•
                await Db.Deleteable<Dt_PickingRecord>().Where(x => x.Id == request.PickingHistoryId).ExecuteCommandAsync();
                _unitOfWorkManage.CommitTran();
                return WebResponseContent.Instance.OK("撤销成功");
            }
            catch (Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                return WebResponseContent.Instance.Error($"撤销失败: {ex.Message}");
            }
        }
        /// <summary>
        /// èŽ·å–æ‰˜ç›˜çš„å‡ºåº“çŠ¶æ€ä¿¡æ¯
@@ -1142,4 +1977,33 @@
        }
    }
    #region è¿”回
    /// <summary>
    /// æ‹†åŒ…链项
    /// </summary>
    public class SplitChainItem
    {
        public Dt_SplitPackageRecord SplitRecord { get; set; }
        public string OriginalBarcode { get; set; }
        public string NewBarcode { get; set; }
        public decimal SplitQuantity { get; set; }
    }
    public  class ReturnAnalysisResult
    {
        public bool HasItemsToReturn { get; set; }
        public bool HasRemainingLocks { get; set; }
        public bool HasPalletStockGoods { get; set; }
        public bool HasSplitRecords { get; set; }
        public decimal RemainingLocksReturnQty { get; set; }
        public decimal PalletStockReturnQty { get; set; }
        public decimal SplitReturnQty { get; set; }
        public decimal TotalReturnQty { get; set; }
        public List<Dt_OutStockLockInfo> RemainingLocks { get; set; } = new List<Dt_OutStockLockInfo>();
        public List<Dt_StockInfoDetail> PalletStockGoods { get; set; } = new List<Dt_StockInfoDetail>();
        public List<Dt_SplitPackageRecord> SplitRecords { get; set; } = new List<Dt_SplitPackageRecord>();
    }
    #endregion
}