pan
5 天以前 7d3d385f0bdcf40b0c42d14ab526df318e04a433
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundBatchPickingService.cs
@@ -261,7 +261,7 @@
                _unitOfWorkManage.BeginTran();
                // 1. éªŒè¯åˆ†æ‹£è¯·æ±‚
                // éªŒè¯åˆ†æ‹£è¯·æ±‚
                var validationResult = await ValidatePickingRequest(orderNo, palletCode, barcode);
                if (!validationResult.IsValid)
                {
@@ -284,7 +284,7 @@
                decimal originalStockQty = stockDetail.StockQuantity;
                decimal originalOutboundQty = stockDetail.OutboundQuantity;
                // 2. æ£€æŸ¥æ˜¯å¦éœ€è¦è‡ªåŠ¨æ‹†åŒ…
                // æ£€æŸ¥æ˜¯å¦éœ€è¦è‡ªåŠ¨æ‹†åŒ…
                var autoSplitResult = await CheckAndAutoSplitIfNeeded(lockInfo, stockDetail, palletCode);
                if (autoSplitResult != null)
                {
@@ -301,7 +301,7 @@
                    (lockInfo, orderDetail, stockDetail, batch) = refreshedValidation.Data;
                                
                    // ã€é‡è¦ã€‘调用自动拆包后验证
                    // è°ƒç”¨è‡ªåŠ¨æ‹†åŒ…åŽéªŒè¯
                    decimal splitQuantity =  autoSplitResult.FirstOrDefault()?.quantityTotal.ObjToDecimal()??0 ;
                    bool autoSplitValid = await ValidateAfterAutoSplit(lockInfo, orderDetail, stockDetail, splitQuantity,originalStockQtyBeforeSplit);
@@ -314,7 +314,7 @@
                    _logger.LogInformation($"自动拆包验证通过,继续执行分拣");
                }
                // 3. è®¡ç®—实际拣选数量
                // è®¡ç®—实际拣选数量
                decimal actualPickedQty = lockInfo.AssignQuantity - lockInfo.PickedQty;
                if (actualPickedQty <= 0)
@@ -325,16 +325,16 @@
                _logger.LogInformation($"开始拣选 - æ•°é‡: {actualPickedQty}");
                // 4. æ‰§è¡Œåˆ†æ‹£é€»è¾‘
                // æ‰§è¡Œåˆ†æ‹£é€»è¾‘
                var pickingResult = await ExecutePickingLogic(lockInfo, orderDetail, stockDetail, actualPickedQty);
                // 5. æ›´æ–°æ‰¹æ¬¡å’Œè®¢å•数据
                // æ›´æ–°æ‰¹æ¬¡å’Œè®¢å•数据
                await UpdateBatchAndOrderData(batch, orderDetail, actualPickedQty, orderNo);
                // 6. è®°å½•拣选历史
                //记录拣选历史
                await RecordPickingHistory(pickingResult, orderNo, palletCode);
                // 7. æ‹£é€‰åŽéªŒè¯
                // æ‹£é€‰åŽéªŒè¯
                await ValidateAfterPicking(orderNo, palletCode, barcode, actualPickedQty);
                _unitOfWorkManage.CommitTran();
@@ -361,8 +361,7 @@
        /// <summary>
        /// è‡ªåŠ¨æ‹†åŒ…åŽéªŒè¯æ•°æ®ä¸€è‡´æ€§ - ä¿®æ­£ç‰ˆ
        /// é‡ç‚¹ä¿®æ­£ï¼šæ­£ç¡®çš„原库存期望值计算
        /// è‡ªåŠ¨æ‹†åŒ…åŽéªŒè¯æ•°æ®ä¸€è‡´æ€§
        /// </summary>
        private async Task<bool> ValidateAfterAutoSplit(Dt_OutStockLockInfo lockInfo, Dt_OutboundOrderDetail orderDetail,
            Dt_StockInfoDetail originalStockDetail, decimal splitQuantity, decimal originalStockQtyBeforeSplit)
@@ -375,7 +374,7 @@
                bool allValid = true;
                List<string> validationErrors = new List<string>();
                // 1. é‡æ–°èŽ·å–æœ€æ–°çš„æ•°æ®ï¼ˆæ‹†åŒ…åŽçš„å½“å‰çŠ¶æ€ï¼‰
                // é‡æ–°èŽ·å–æœ€æ–°çš„æ•°æ®ï¼ˆæ‹†åŒ…åŽçš„å½“å‰çŠ¶æ€ï¼‰
                var refreshedOrderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                    .FirstAsync(x => x.Id == orderDetail.Id);
@@ -385,9 +384,8 @@
                var refreshedStockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                    .FirstAsync(x => x.Id == originalStockDetail.Id);
                // 2. ã€æ ¸å¿ƒä¿®æ­£ã€‘验证原库存明细数据
                // é‡è¦ï¼šæ‹†åŒ…后原库存的期望值 = åˆ†é…æ•°é‡ (因为自动拆包时,原库存应只保留分配数量)
                decimal expectedOriginalStockQty = lockInfo.AssignQuantity; // åº”该是120,而不是用计算
                // éªŒè¯åŽŸåº“å­˜æ˜Žç»†æ•°æ®
                decimal expectedOriginalStockQty = lockInfo.AssignQuantity;
                _logger.LogInformation($"库存验证基准:");
                _logger.LogInformation($"  æ‹†åŒ…前原库存: {originalStockQtyBeforeSplit}");
@@ -430,7 +428,7 @@
                    _logger.LogError(error);
                }
                // 3. éªŒè¯æ–°åº“存明细(拆包产生的)
                // éªŒè¯æ–°åº“存明细(拆包产生的)
                // æŸ¥æ‰¾æ–°æ¡ç ï¼ˆé€šè¿‡æ‹†åŒ…记录)
                var splitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
                    .Where(x => x.OutStockLockInfoId == lockInfo.Id &&
@@ -493,7 +491,7 @@
                    _logger.LogError(error);
                }
                // 5. éªŒè¯è®¢å•明细数据未改变
                // éªŒè¯è®¢å•明细数据未改变
                if (Math.Abs(refreshedOrderDetail.AllocatedQuantity - orderDetail.AllocatedQuantity) > 0.01m)
                {
                    string error = $"订单明细分配数量异常变化!拆包前: {orderDetail.AllocatedQuantity}, æ‹†åŒ…后: {refreshedOrderDetail.AllocatedQuantity}";
@@ -502,7 +500,7 @@
                    _logger.LogError(error);
                }
                // 6. éªŒè¯åŽŸé”å®šè®°å½•æ•°æ®æœªæ”¹å˜ï¼ˆåˆ†é…æ•°é‡ä¸å˜ï¼‰
                // éªŒè¯åŽŸé”å®šè®°å½•æ•°æ®æœªæ”¹å˜ï¼ˆåˆ†é…æ•°é‡ä¸å˜ï¼‰
                if (Math.Abs(refreshedLockInfo.AssignQuantity - lockInfo.AssignQuantity) > 0.01m)
                {
                    string error = $"锁定记录分配数量异常变化!拆包前: {lockInfo.AssignQuantity}, æ‹†åŒ…后: {refreshedLockInfo.AssignQuantity}";
@@ -511,7 +509,7 @@
                    _logger.LogError(error);
                }
                // 7. ã€æ–°å¢žã€‘验证总库存守恒
                // æ–°å¢žã€‘验证总库存守恒
                // æ‹†åŒ…前总库存 = åŽŸåº“å­˜æ•°é‡
                // æ‹†åŒ…后总库存 = åŽŸåº“å­˜çŽ°æœ‰æ•°é‡ + æ–°åº“存数量
                decimal totalStockAfterSplit = refreshedStockDetail.StockQuantity;
@@ -576,11 +574,11 @@
            {
                _logger.LogInformation($"开始拣选后验证");
                // 1. éªŒè¯åº“存明细
                // éªŒè¯åº“存明细
                var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                    .FirstAsync(x => x.Barcode == barcode);
                // 2. æŸ¥æ‰¾è¯¥æ¡ç çš„æ‰€æœ‰æ‹£é€‰è®°å½•
                // æŸ¥æ‰¾è¯¥æ¡ç çš„æ‰€æœ‰æ‹£é€‰è®°å½•
                var pickingRecords = await Db.Queryable<Dt_PickingRecord>()
                    .Where(x => x.Barcode == barcode && x.OrderNo == orderNo && !x.IsCancelled)
                    .ToListAsync();
@@ -660,95 +658,7 @@
        #region å–走空箱逻辑
        /// <summary>
        /// éªŒè¯ç©ºç®±å–走条件
        /// </summary>
        private async Task<ValidationResult<List<Dt_OutStockLockInfo>>> ValidateEmptyPalletRemoval(string orderNo, string palletCode)
        {
            _logger.LogInformation($"开始验证空托盘取走 - è®¢å•: {orderNo}, æ‰˜ç›˜: {palletCode}");
            // èŽ·å–æ‰˜ç›˜çš„æ‰€æœ‰é”å®šè®°å½•
            var lockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode)
                .ToListAsync();
            if (!lockInfos.Any())
                return ValidationResult<List<Dt_OutStockLockInfo>>.Error("该托盘没有锁定记录");
            // èŽ·å–åº“å­˜ä¿¡æ¯
            var stockInfo = await _stockInfoService.Db.Queryable<Dt_StockInfo>()
                .FirstAsync(x => x.PalletCode == palletCode);
            if (stockInfo == null)
            {
                _logger.LogWarning($"未找到托盘的库存信息,可能已被清理");
                // å¦‚果找不到库存信息,只检查锁定记录
            }
            else
            {
                // æ£€æŸ¥æ‰˜ç›˜ä¸Šæ˜¯å¦è¿˜æœ‰åº“存货物
                var remainingStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                    .Where(x => x.StockId == stockInfo.Id &&
                               x.StockQuantity > 0)  // åªæ£€æŸ¥æ•°é‡å¤§äºŽ0的库存
                    .ToListAsync();
                if (remainingStock.Any())
                {
                    var remainingQty = remainingStock.Sum(x => x.StockQuantity);
                    var barcodes = string.Join(", ", remainingStock.Select(x => x.Barcode).Take(5)); // åªæ˜¾ç¤ºå‰5个
                    return ValidationResult<List<Dt_OutStockLockInfo>>.Error(
                        $"托盘上还有库存货物,数量{remainingQty},条码: {barcodes},不能取走空箱");
                }
            }
            // æ£€æŸ¥æ˜¯å¦æœ‰æœªå®Œæˆçš„锁定记录(状态为出库中或回库中)
            var unfinishedLocks = lockInfos.Where(x =>
                x.Status == (int)OutLockStockStatusEnum.出库中 ||
                x.Status == (int)OutLockStockStatusEnum.回库中).ToList();
            if (unfinishedLocks.Any())
            {
                var unfinishedCount = unfinishedLocks.Count;
                var unfinishedQty = unfinishedLocks.Sum(x => x.AssignQuantity - x.PickedQty);
                return ValidationResult<List<Dt_OutStockLockInfo>>.Error(
                    $"托盘还有{unfinishedCount}条未完成记录,剩余数量{unfinishedQty},不能取走空箱");
            }
            // èŽ·å–å·²å®Œæˆçš„é”å®šè®°å½•ï¼ˆçŠ¶æ€ä¸ºæ‹£é€‰å®Œæˆæˆ–å·²å–èµ°ï¼‰
            var completedLocks = lockInfos.Where(x =>
                x.Status == (int)OutLockStockStatusEnum.拣选完成 ||
                x.Status == (int)OutLockStockStatusEnum.已取走).ToList();
            if (!completedLocks.Any())
                return ValidationResult<List<Dt_OutStockLockInfo>>.Error("该托盘没有已完成拣选的记录");
            _logger.LogInformation($"空托盘验证通过 - æ‰¾åˆ° {completedLocks.Count} æ¡å·²å®Œæˆè®°å½•");
            return ValidationResult<List<Dt_OutStockLockInfo>>.Success(completedLocks);
        }
        /// <summary>
        /// æ¸…理已完成的锁定记录
        /// </summary>
        private async Task CleanupCompletedLocks(List<Dt_OutStockLockInfo> completedLocks)
        {
            foreach (var lockInfo in completedLocks)
            {
                // æ ‡è®°é”å®šè®°å½•为已取走
                lockInfo.Status = (int)OutLockStockStatusEnum.已取走;
                lockInfo.Operator = App.User.UserName;
                await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
                // æ¸…理对应的库存记录状态
                await CleanupStockInfo(lockInfo);
            }
        }
        /// <summary>
         /// <summary>
        /// æ¸…理库存信息 - å®Œæ•´ä¿®æ­£ç‰ˆ
        /// ç¡®ä¿OutboundQuantity正确清零
        /// </summary>
@@ -772,7 +682,6 @@
                    _logger.LogInformation($"清理前状态 - åº“å­˜: {originalStockQty}, å‡ºåº“: {originalOutboundQty}, çŠ¶æ€: {GetStockStatusName(originalStatus)}");
                    // ã€é‡è¦ã€‘检查库存数量是否应该为0
                    // å¦‚果锁定状态是拣选完成,理论上库存应该为0
                    if (lockInfo.Status == (int)OutLockStockStatusEnum.拣选完成)
                    {
                        if (stockDetail.StockQuantity > 0)
@@ -783,7 +692,7 @@
                    // æ¸…理库存和出库数量
                    stockDetail.StockQuantity = 0;
                    stockDetail.OutboundQuantity = 0;  // ã€ä¿®æ­£ã€‘确保出库数量清零
                    stockDetail.OutboundQuantity = 0;
                    stockDetail.Status = (int)StockStatusEmun.已清理;
                    await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
@@ -813,7 +722,7 @@
                        decimal originalOutbound = stock.OutboundQuantity;
                        stock.StockQuantity = 0;
                        stock.OutboundQuantity = 0;  // ã€ä¿®æ­£ã€‘确保出库数量清零
                        stock.OutboundQuantity = 0;
                        stock.Status = (int)StockStatusEmun.已清理;
                        await _stockInfoDetailService.Db.Updateable(stock).ExecuteCommandAsync();
@@ -1022,7 +931,6 @@
            if (orderDetail.AllocatedQuantity != totalLockAssignQuantity)
            {
                _logger.LogWarning($"订单明细分配数量与锁定信息不一致 - è®¢å•明细分配数量: {orderDetail.AllocatedQuantity}, é”å®šä¿¡æ¯æ€»åˆ†é…æ•°é‡: {totalLockAssignQuantity}");
                // è¿™é‡Œä¸ç›´æŽ¥è¿”回错误,因为拆包操作本身不会导致不一致,只是记录警告
            }
            _logger.LogInformation($"拆包验证通过 - åŽŸæ¡ç : {originalBarcode}, æ‹†åŒ…数量: {splitQuantity}");
@@ -1118,7 +1026,7 @@
                await _stockInfoDetailService.Db.Insertable(newStockDetail).ExecuteCommandAsync();
                _logger.LogInformation($"创建新库存明细成功 - æ¡ç : {newBarcode}, åº“存数量: {newStockQuantity}");
                // ä¿®æ­£ï¼šæ›´æ–°åŽŸåº“å­˜æ˜Žç»† - ç¡®ä¿æ•°æ®ä¸€è‡´æ€§
                // æ›´æ–°åŽŸåº“å­˜æ˜Žç»†
                stockDetail.StockQuantity = originalRemainingStockQuantity;
                // ç¡®ä¿ä¸ä¼šä¸ºè´Ÿæ•°
@@ -1126,10 +1034,7 @@
                {
                    _logger.LogWarning($"原库存数量出现负数,重置为0");
                    stockDetail.StockQuantity = 0;
                }
                // å‡ºåº“数量保持不变,因为是拆包,不是实际出库
                // stockDetail.OutboundQuantity = stockDetail.OutboundQuantity;
                }
                await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
                _logger.LogInformation($"更新原库存明细 - æ¡ç : {stockDetail.Barcode}, " +
@@ -1197,46 +1102,7 @@
            }
        }
        /// <summary>
        /// éªŒè¯æ‹†åŒ…后订单明细的分配数量是否保持不变
        /// </summary>
        private async Task ValidateOrderDetailAllocationAfterSplit(long orderDetailId, decimal originalTotalAssignQty)
        {
            try
            {
                // èŽ·å–è®¢å•æ˜Žç»†çš„æ‰€æœ‰é”å®šä¿¡æ¯çš„æ€»åˆ†é…æ•°é‡
                var allLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .Where(x => x.OrderDetailId == orderDetailId)
                    .ToListAsync();
                decimal totalLockAssignQty = allLocks.Sum(x => x.AssignQuantity);
                _logger.LogInformation($"拆包后分配数量验证 - è®¢å•明细ID: {orderDetailId}");
                _logger.LogInformation($"原始总分配数量: {originalTotalAssignQty}, å½“前总分配数量: {totalLockAssignQty}");
                // æ‰‹åŠ¨æ‹†åŒ…åŽæ€»åˆ†é…æ•°é‡åº”è¯¥ä¿æŒä¸å˜
                if (Math.Abs(originalTotalAssignQty - totalLockAssignQty) > 0.01m)
                {
                    _logger.LogWarning($"拆包后总分配数量发生变化 - æœŸæœ›: {originalTotalAssignQty}, å®žé™…: {totalLockAssignQty}");
                    // å¦‚果变化很小,可能是精度问题,记录但不抛出异常
                    if (Math.Abs(originalTotalAssignQty - totalLockAssignQty) > 1.0m)
                    {
                        throw new InvalidOperationException($"拆包后总分配数量异常变化,期望: {originalTotalAssignQty}, å®žé™…: {totalLockAssignQty}");
                    }
                }
                else
                {
                    _logger.LogInformation($"拆包后分配数量验证通过 - æ€»åˆ†é…æ•°é‡ä¿æŒä¸å˜");
                }
            }
            catch (Exception ex)
            {
                _logger.LogError($"验证拆包后分配数量失败 - OrderDetailId: {orderDetailId}, Error: {ex.Message}");
                throw;
            }
        }
        /// <summary>
      /// <summary>
        /// éªŒè¯æ‹†åŒ…后数据一致性
        /// </summary>
        private async Task ValidateDataConsistencyAfterSplit(long orderDetailId, decimal expectedAllocatedQty, decimal expectedLockQty)
@@ -1287,18 +1153,18 @@
            {
                _unitOfWorkManage.BeginTran();
                // 1. æŸ¥æ‰¾æ‹†åŒ…记录并验证
                // æŸ¥æ‰¾æ‹†åŒ…记录并验证
                var validationResult = await ValidateCancelSplitRequest(orderNo, palletCode, newBarcode);
                if (!validationResult.IsValid)
                    return WebResponseContent.Instance.Error(validationResult.ErrorMessage);
                var (splitRecord, newLockInfo, newStockDetail) = validationResult.Data;
                // 2. æŸ¥æ‰¾åŽŸå§‹é”å®šä¿¡æ¯
                // æŸ¥æ‰¾åŽŸå§‹é”å®šä¿¡æ¯
                var originalLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .FirstAsync(x => x.Id == splitRecord.OutStockLockInfoId);
                // 3. æ£€æŸ¥è¯¥æ¡ç æ˜¯å¦è¢«å†æ¬¡æ‹†åŒ…
                //  æ£€æŸ¥è¯¥æ¡ç æ˜¯å¦è¢«å†æ¬¡æ‹†åŒ…
                var childSplitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
                    .Where(x => x.OriginalBarcode == newBarcode && !x.IsReverted)
                    .ToListAsync();
@@ -1308,7 +1174,7 @@
                    return WebResponseContent.Instance.Error("该条码已被再次拆包,请先取消后续的拆包操作");
                }
                // 4. æ‰§è¡Œå–消拆包逻辑
                //  æ‰§è¡Œå–消拆包逻辑
                await ExecuteCancelSplitLogic(splitRecord, originalLockInfo, newLockInfo, newStockDetail);
                _unitOfWorkManage.CommitTran();
@@ -1378,7 +1244,7 @@
            if (orderDetail == null)
                throw new InvalidOperationException("未找到订单明细");
            // 1. æ¢å¤è®¢å•明细的分配数量(自动拆包会增加分配数量)
            //  æ¢å¤è®¢å•明细的分配数量
            decimal originalAllocatedQty = orderDetail.AllocatedQuantity;
            decimal originalLockQty = orderDetail.LockQuantity;
@@ -1392,7 +1258,7 @@
            await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync();
            _logger.LogInformation($"自动拆包取消恢复订单明细 - åˆ†é…æ•°é‡: {originalAllocatedQty} -> {orderDetail.AllocatedQuantity}");
            // 2. æ¢å¤åŽŸåº“å­˜ï¼ˆå°†æ‹†åŒ…çš„æ•°é‡åŠ å›žåŽŸåº“å­˜ï¼‰
            //  æ¢å¤åŽŸåº“å­˜
            var originalStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .FirstAsync(x => x.Barcode == splitRecord.OriginalBarcode && x.StockId == splitRecord.StockId);
@@ -1411,7 +1277,7 @@
                _logger.LogInformation($"自动拆包取消恢复原库存 - æ¡ç : {originalStock.Barcode}, æ•°é‡: {originalStockQty} -> {originalStock.StockQuantity}");
            }
            // 3. åˆ é™¤æ–°é”å®šä¿¡æ¯å’Œåº“存明细
            // åˆ é™¤æ–°é”å®šä¿¡æ¯å’Œåº“存明细
            await DeleteNewSplitRecords(newLockInfo, newStockDetail);
        }
@@ -1424,7 +1290,7 @@
        {
            _logger.LogInformation($"处理手动拆包取消 - åŽŸæ¡ç : {splitRecord.OriginalBarcode}, æ–°æ¡ç : {splitRecord.NewBarcode}");
            // 1. æ¢å¤åŽŸé”å®šä¿¡æ¯çš„åˆ†é…æ•°é‡
            //  æ¢å¤åŽŸé”å®šä¿¡æ¯çš„åˆ†é…æ•°é‡
            decimal originalAssignQty = originalLockInfo.AssignQuantity;
            decimal originalOrderQty = originalLockInfo.OrderQuantity;
@@ -1440,7 +1306,7 @@
            await _outStockLockInfoService.Db.Updateable(originalLockInfo).ExecuteCommandAsync();
            _logger.LogInformation($"手动拆包取消恢复原锁定信息 - åˆ†é…æ•°é‡: {originalAssignQty} -> {originalLockInfo.AssignQuantity}");
            // 2. æ¢å¤åŽŸåº“å­˜æ˜Žç»†
            // æ¢å¤åŽŸåº“å­˜æ˜Žç»†
            var originalStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .FirstAsync(x => x.Barcode == splitRecord.OriginalBarcode && x.StockId == splitRecord.StockId);
@@ -1459,7 +1325,7 @@
                _logger.LogInformation($"手动拆包取消恢复原库存 - æ¡ç : {originalStock.Barcode}, æ•°é‡: {originalStockQty} -> {originalStock.StockQuantity}");
            }
            // 3. åˆ é™¤æ–°é”å®šä¿¡æ¯å’Œåº“存明细
            // åˆ é™¤æ–°é”å®šä¿¡æ¯å’Œåº“存明细
            await DeleteNewSplitRecords(newLockInfo, newStockDetail);
        }
@@ -1486,6 +1352,7 @@
                    .ExecuteCommandAsync();
            }
        }
        /// <summary>
        /// éªŒè¯å–消拆包后数据一致性 - æœ€æ–°ç‰ˆæœ¬
        /// </summary>
@@ -1664,7 +1531,7 @@
            {
                _unitOfWorkManage.BeginTran();
                // 1. æŸ¥æ‰¾æ‰€æœ‰ç›¸å…³çš„æ‹†åŒ…记录(形成拆包链)
                // æŸ¥æ‰¾æ‰€æœ‰ç›¸å…³çš„æ‹†åŒ…记录(形成拆包链)
                var splitChain = await GetSplitPackageChain(orderNo, startBarcode);
                if (!splitChain.Any())
@@ -1672,11 +1539,11 @@
                _logger.LogInformation($"找到拆包链,共 {splitChain.Count} æ¡è®°å½•");
                // 2. æ”¶é›†æ‹†åŒ…链中涉及的所有条码(包括原条码和新条码)
                // æ”¶é›†æ‹†åŒ…链中涉及的所有条码(包括原条码和新条码)
                var allBarcodesInChain = new List<string> { startBarcode };
                allBarcodesInChain.AddRange(splitChain.Select(x => x.NewBarcode));
                // 3. æ£€æŸ¥æ‹†åŒ…链中是否有已被分拣的条码
                // æ£€æŸ¥æ‹†åŒ…链中是否有已被分拣的条码
                var pickedBarcodesInfo = await GetPickedBarcodesInfo(orderNo, allBarcodesInChain);
                if (pickedBarcodesInfo.Any())
@@ -1686,7 +1553,7 @@
                        $"以下条码已被分拣,请先取消分拣:{pickedBarcodes}");
                }
                // 4. æŒ‰æ‹†åŒ…顺序倒序取消(从最新的开始取消)
                // æŒ‰æ‹†åŒ…顺序倒序取消(从最新的开始取消)
                var reversedChain = splitChain.OrderByDescending(x => x.SplitTime).ToList();
                foreach (var splitRecord in reversedChain)
@@ -1707,106 +1574,7 @@
            }
        }
        /// <summary>
        /// èŽ·å–æ¡ç çš„æ‹†åŒ…å’Œæ‹£é€‰çŠ¶æ€
        /// </summary>
        public async Task<WebResponseContent> GetBarcodeSplitAndPickStatus(string orderNo, string barcode)
        {
            try
            {
                // 1. èŽ·å–æ‹†åŒ…ä¿¡æ¯
                var splitChain = await GetSplitPackageChain(orderNo, barcode);
                var isOriginalBarcode = !splitChain.Any(x => x.NewBarcode == barcode);
                // 2. èŽ·å–æ‹£é€‰ä¿¡æ¯
                var pickingRecords = await Db.Queryable<Dt_PickingRecord>()
                    .Where(x => x.Barcode == barcode && x.OrderNo == orderNo && !x.IsCancelled)
                    .ToListAsync();
                var totalPickedQty = pickingRecords.Sum(x => x.PickQuantity);
                // 3. èŽ·å–é”å®šä¿¡æ¯
                var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .Where(x => x.CurrentBarcode == barcode && x.OrderNo == orderNo)
                    .FirstAsync();
                // 4. èŽ·å–åº“å­˜ä¿¡æ¯
                var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                    .Where(x => x.Barcode == barcode)
                    .FirstAsync();
                var statusInfo = new BarcodeStatusInfoDto
                {
                    Barcode = barcode,
                    OrderNo = orderNo,
                    IsOriginalBarcode = isOriginalBarcode,
                    SplitChainCount = splitChain.Count,
                    HasBeenPicked = pickingRecords.Any(),
                    TotalPickedQuantity = totalPickedQty,
                    PickRecordCount = pickingRecords.Count,
                    LockInfoStatus = lockInfo?.Status ?? 0,
                    LockInfoPickedQty = lockInfo?.PickedQty ?? 0,
                    LockInfoAssignQty = lockInfo?.AssignQuantity ?? 0,
                    StockQuantity = stockDetail?.StockQuantity ?? 0,
                    StockStatus = stockDetail?.Status ?? 0,
                    CanCancelSplit = !pickingRecords.Any(), // æœªè¢«åˆ†æ‹£æ‰èƒ½å–消拆包
                    NeedCancelPickFirst = pickingRecords.Any() // éœ€è¦å…ˆå–消分拣
                };
                // 5. èŽ·å–æ“ä½œå»ºè®®
                statusInfo.OperationSuggestions = GetOperationSuggestions(statusInfo);
                return WebResponseContent.Instance.OK("获取状态成功", statusInfo);
            }
            catch (Exception ex)
            {
                _logger.LogError($"获取条码状态失败 - OrderNo: {orderNo}, Barcode: {barcode}, Error: {ex.Message}");
                return WebResponseContent.Instance.Error("获取条码状态失败");
            }
        }
        /// <summary>
        /// èŽ·å–æ“ä½œå»ºè®®
        /// </summary>
        private List<string> GetOperationSuggestions(BarcodeStatusInfoDto statusInfo)
        {
            var suggestions = new List<string>();
            if (statusInfo.HasBeenPicked)
            {
                suggestions.Add($"该条码已被分拣(数量:{statusInfo.TotalPickedQuantity}),如需取消拆包,请先取消分拣");
                if (statusInfo.IsOriginalBarcode)
                {
                    suggestions.Add("这是原条码,取消分拣后将恢复为可分拣状态");
                }
                else
                {
                    suggestions.Add("这是拆包生成的新条码,取消分拣后才能取消拆包");
                }
            }
            else
            {
                if (statusInfo.IsOriginalBarcode && statusInfo.SplitChainCount > 0)
                {
                    suggestions.Add("这是原条码,可以取消拆包链");
                }
                else if (!statusInfo.IsOriginalBarcode)
                {
                    suggestions.Add("这是拆包生成的新条码,可以单独取消拆包");
                }
            }
            if (statusInfo.LockInfoStatus == (int)OutLockStockStatusEnum.拣选完成)
            {
                suggestions.Add("锁定状态:拣选完成");
            }
            else if (statusInfo.LockInfoStatus == (int)OutLockStockStatusEnum.出库中)
            {
                suggestions.Add("锁定状态:出库中");
            }
            return suggestions;
        }
        /// <summary>
        /// èŽ·å–å·²è¢«åˆ†æ‹£çš„æ¡ç ä¿¡æ¯
        /// </summary>
@@ -2031,153 +1799,13 @@
        #endregion
        #region ç»Ÿä¸€å›žåº“逻辑
        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<PalletStatusAnalysis> AnalyzePalletStatus(string orderNo, string palletCode, int stockId)
        {
            var result = new PalletStatusAnalysis
            {
                OrderNo = orderNo,
                PalletCode = palletCode,
                StockId = stockId
            };
            // åˆ†æžæœªåˆ†æ‹£çš„出库锁定记录
            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);
                _logger.LogInformation($"发现{remainingLocks.Count}条未分拣锁定记录,总数量: {result.RemainingLocksReturnQty}");
            }
            // åˆ†æžæ‰˜ç›˜ä¸Šçš„库存货物
            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);
                _logger.LogInformation($"发现{palletStockGoods.Count}个库存货物,总数量: {result.PalletStockReturnQty}");
                // è®°å½•详细状态分布
                var statusGroups = palletStockGoods.GroupBy(x => x.Status);
                foreach (var group in statusGroups)
                {
                    _logger.LogInformation($"库存状态{group.Key}: {group.Count()}个货物,数量: {group.Sum(x => x.StockQuantity)}");
                }
            }
            //分析拆包记录
            var splitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
                .Where(it => it.OrderNo == orderNo &&
                           it.PalletCode == palletCode &&
                           !it.IsReverted && it.Status != (int)SplitPackageStatusEnum.已拣选 &&
                           it.Status != (int)SplitPackageStatusEnum.已回库)
                .ToListAsync();
            if (splitRecords.Any())
            {
                result.HasSplitRecords = true;
                result.SplitRecords = splitRecords;
                result.SplitReturnQty = await CalculateSplitReturnQuantity(splitRecords, stockId);
                _logger.LogInformation($"发现{splitRecords.Count}条未拣选拆包记录,总数量: {result.SplitReturnQty}");
            }
            // 4. è®¡ç®—总回库数量和空托盘状态
            result.TotalReturnQty = result.RemainingLocksReturnQty + result.PalletStockReturnQty + result.SplitReturnQty;
            result.HasItemsToReturn = result.TotalReturnQty > 0;
            result.IsEmptyPallet = !result.HasItemsToReturn;
            // 5. æ£€æŸ¥æ˜¯å¦æœ‰è¿›è¡Œä¸­çš„任务
            result.HasActiveTasks = await _taskRepository.Db.Queryable<Dt_Task>()
                .Where(x => x.OrderNo == orderNo && x.TaskType == TaskTypeEnum.InPick.ObjToInt() &&
                           x.PalletCode == palletCode &&
                           x.TaskStatus == (int)TaskStatusEnum.New)
                .AnyAsync();
            _logger.LogInformation($"托盘状态分析完成 - è®¢å•: {orderNo}, æ‰˜ç›˜: {palletCode}, " +
                                  $"总回库数量: {result.TotalReturnQty}, æ˜¯å¦ç©ºæ‰˜ç›˜: {result.IsEmptyPallet}, " +
                                  $"有进行中任务: {result.HasActiveTasks}");
            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 (splitRecord.Status != (int)SplitPackageStatusEnum.已撤销)
                    continue;
                // æ£€æŸ¥åŽŸæ¡ç 
                if (!processedBarcodes.Contains(splitRecord.OriginalBarcode))
                {
                    var originalStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                        .Where(it => it.Barcode == splitRecord.OriginalBarcode && it.StockId == stockId &&
                           it.Status != StockStatusEmun.出库完成.ObjToInt())
                        .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 && it.Status != StockStatusEmun.出库完成.ObjToInt())
                        .FirstAsync();
                    if (newStock != null && newStock.StockQuantity > 0)
                    {
                        totalQty += newStock.StockQuantity;
                        processedBarcodes.Add(splitRecord.NewBarcode);
                    }
                }
            }
            return totalQty;
        }
             
        /// <summary>
        /// ç»Ÿä¸€å›žåº“方法
        /// </summary>
        public async Task<WebResponseContent> ExecutePalletReturn(string orderNo, string palletCode, string returnReason = "分批回库")
        {
            ReturnTaskInfo returnTaskInfo = null;
            try
            {
                _logger.LogInformation($"【增强回库开始】订单: {orderNo}, æ‰˜ç›˜: {palletCode}");
@@ -2189,8 +1817,7 @@
                    return WebResponseContent.Instance.Error("订单号和托盘码不能为空");
                // èŽ·å–åº“å­˜ä¿¡æ¯
                var stockInfo = await _stockInfoService.Db.Queryable<Dt_StockInfo>()
                    .FirstAsync(x => x.PalletCode == palletCode);
                var stockInfo = await _stockInfoService.Db.Queryable<Dt_StockInfo>().FirstAsync(x => x.PalletCode == palletCode);
                if (stockInfo == null)
                    return WebResponseContent.Instance.Error($"未找到托盘 {palletCode} å¯¹åº”的库存信息");
@@ -2201,8 +1828,7 @@
                var validationResult = await ValidateDataBeforeReturn(orderNo, palletCode, stockId);
                if (!validationResult.IsValid)
                {
                    _logger.LogWarning($"回库前数据验证失败: {validationResult.ErrorMessage}");
                    // å¯ä»¥æ ¹æ®å®žé™…情况决定是否继续
                    _logger.LogWarning($"回库前数据验证失败: {validationResult.ErrorMessage}");
                }
                // åˆ†æžæ‰˜ç›˜çŠ¶æ€
@@ -2215,13 +1841,19 @@
                        _logger.LogInformation($"【无回库物品】处理空托盘");
                        var result = await HandleEmptyPalletReturn(orderNo, palletCode, stockInfo);
                        _unitOfWorkManage.CommitTran();
                        // åœ¨äº‹åŠ¡æäº¤åŽå¤„ç†ESS命令
                        if (result.Status && result.Data is ReturnTaskInfo taskInfo)
                        {
                            await ProcessESSAfterTransaction(palletCode, taskInfo);
                        }
                        return result;
                    }
                    catch (Exception ex)
                    {
                        _unitOfWorkManage.RollbackTran();
                        _logger.LogError($"空箱回库失败: {ex.Message}");
                        return WebResponseContent.Instance.Error($"空箱回库失败:{ex.Message}");
                        _logger.LogError($"空箱回库ExecutePalletReturn失败: {ex.Message}");
                        return WebResponseContent.Instance.Error($"空箱回库ExecutePalletReturn失败:{ex.Message}");
                    }
                }
@@ -2241,21 +1873,27 @@
                //  æ›´æ–°è®¢å•状态
                await UpdateOrderStatusAfterReturn(orderNo);
                //  åˆ›å»ºå›žåº“任务
                try
                {
                    await CreateReturnTask(orderNo, palletCode, stockInfo);
                    // åˆ›å»ºå›žåº“任务,但不发送ESS命令
                    returnTaskInfo = await CreateReturnTaskWithoutESS(orderNo, palletCode, stockInfo);
                }
                catch (Exception taskEx)
                {
                    _logger.LogError($"回库任务创建失败: {taskEx.Message}");
                    _logger.LogError($"回库任务CreateReturnTaskWithoutESS创建失败: {taskEx.Message}");
                    // ä»»åŠ¡åˆ›å»ºå¤±è´¥ä¸å½±å“æ•°æ®å›žåº“
                }
                _unitOfWorkManage.CommitTran();
                // 8. å›žåº“后验证
                // åœ¨äº‹åŠ¡æäº¤åŽå¤„ç†ESS命令
                if (returnTaskInfo != null && returnTaskInfo.ShouldSendESS)
                {
                    await ProcessESSAfterTransaction(palletCode, returnTaskInfo);
                }
                //  å›žåº“后验证
                await ValidateDataAfterReturn(orderNo, palletCode, stockId);
                return WebResponseContent.Instance.OK($"回库成功,回库数量:{statusAnalysis.TotalReturnQty}", new
@@ -2270,11 +1908,112 @@
            catch (Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                _logger.LogError($"回库失败: {ex.Message}");
                return WebResponseContent.Instance.Error($"回库失败:{ex.Message}");
                _logger.LogError($"ExecutePalletReturn å›žåº“失败: {ex.Message}");
                return WebResponseContent.Instance.Error($"ExecutePalletReturn å›žåº“失败:{ex.Message}");
            }
        }
        /// <summary>
        /// äº‹åŠ¡æäº¤åŽå¤„ç†ESS命令 - ç‹¬ç«‹æ–¹æ³•,避免事务锁
        /// </summary>
        private async Task ProcessESSAfterTransaction(string palletCode, ReturnTaskInfo taskInfo)
        {
            try
            {
                _logger.LogInformation($"开始处理ESS命令 - æ‰˜ç›˜: {palletCode}");
                if (taskInfo == null || !taskInfo.ShouldSendESS || taskInfo.ReturnTask == null)
                {
                    _logger.LogWarning($"无需发送ESS命令或任务信息不完整");
                    return;
                }
                // å‘送ESS命令
                await SendESSCommands(palletCode, taskInfo.OriginalTaskTargetAddress, taskInfo.ReturnTask);
                _logger.LogInformation($"ESS命令处理完成 - æ‰˜ç›˜: {palletCode}, ä»»åŠ¡å·: {taskInfo.ReturnTask.TaskNum}");
            }
            catch (Exception ex)
            {
                _logger.LogError($"处理ESS命令失败 - æ‰˜ç›˜: {palletCode}, Error: {ex.Message}");
                // è¿™é‡Œä¸æŠ›å‡ºå¼‚常,因为数据回库已经成功,ESS命令发送失败可以稍后重试
            }
        }
        /// <summary>
        /// åˆ›å»ºå›žåº“任务(不发送ESS命令)- ç”¨äºŽäº‹åŠ¡å†…å¤„ç†
        /// </summary>
        private async Task<ReturnTaskInfo> CreateReturnTaskWithoutESS(string orderNo, string palletCode, Dt_StockInfo stockInfo)
        {
            try
            {
                // èŽ·å–å½“å‰ä»»åŠ¡ä¿¡æ¯
                var currentTask = await _taskRepository.Db.Queryable<Dt_Task>()
                    .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode)
                    .FirstAsync();
                if (currentTask == null)
                {
                    _logger.LogWarning($"未找到当前任务 - è®¢å•: {orderNo}, æ‰˜ç›˜: {palletCode}");
                    return null;
                }
                // åˆ†é…æ–°è´§ä½
                var newLocation = _locationInfoService.AssignLocation(stockInfo.LocationType);
                var returnTask = new Dt_Task()
                {
                    CurrentAddress = stations[currentTask.TargetAddress],
                    Grade = 0,
                    PalletCode = palletCode,
                    NextAddress = "",
                    OrderNo = orderNo,
                    Roadway = newLocation.RoadwayNo,
                    SourceAddress = stations[currentTask.TargetAddress],
                    TargetAddress = newLocation.LocationCode,
                    TaskStatus = TaskStatusEnum.New.ObjToInt(),
                    TaskType = TaskTypeEnum.InPick.ObjToInt(),
                    PalletType = stockInfo.PalletType,
                    WarehouseId = currentTask.WarehouseId
                };
                try
                {
                    await _taskRepository.Db.Insertable(returnTask).ExecuteCommandAsync();
                    _logger.LogInformation($"创建回库任务成功: {returnTask.TaskNum}, è®¢å•: {orderNo}, æ‰˜ç›˜: {palletCode}");
                    // åˆ é™¤åŽŸå§‹å‡ºåº“ä»»åŠ¡
                    _logger.LogInformation($"开始删除历史任务: {orderNo}, {currentTask.TaskNum}");
                    var result = _task_HtyService.DeleteAndMoveIntoHty(currentTask, OperateTypeEnum.人工删除);
                    await _taskRepository.Db.Deleteable(currentTask).ExecuteCommandAsync();
                    if (!result)
                    {
                        await _taskRepository.Db.Deleteable(currentTask).ExecuteCommandAsync();
                    }
                    _logger.LogInformation($"删除历史任务完成: {currentTask.TaskNum}, å½±å“è¡Œæ•°: {result}");
                    // è¿”回任务信息,但不发送ESS命令
                    return new ReturnTaskInfo
                    {
                        ShouldSendESS = true,
                        PalletCode = palletCode,
                        OriginalTaskTargetAddress = currentTask.TargetAddress,
                        ReturnTask = returnTask
                    };
                }
                catch (Exception ex)
                {
                    _logger.LogError($"创建回库任务失败 - è®¢å•: {orderNo}, æ‰˜ç›˜: {palletCode}, Error: {ex.Message}");
                    throw new Exception($"创建回库任务失败 - è®¢å•: {orderNo}, æ‰˜ç›˜: {palletCode}", ex);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError($"创建回库任务失败 - è®¢å•: {orderNo}, æ‰˜ç›˜: {palletCode}, Error: {ex.Message}");
                return null;
            }
        }
        /// <summary>
        /// å¢žå¼ºçš„回库前数据验证
        /// </summary>
@@ -2284,7 +2023,7 @@
            try
            {
                // 1. éªŒè¯åº“存数据
                //  éªŒè¯åº“存数据
                var stockDetails = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                    .Where(x => x.StockId == stockId)
                    .ToListAsync();
@@ -2296,7 +2035,7 @@
                    errors.Add($"发现负数库存: {string.Join(", ", negativeStocks.Select(x => $"{x.Barcode}:{x.StockQuantity}"))}");
                }
                // 2. éªŒè¯é”å®šè®°å½•
                //  éªŒè¯é”å®šè®°å½•
                var lockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode)
                    .ToListAsync();
@@ -2308,7 +2047,7 @@
                    errors.Add($"发现已拣选数量大于分配数量的锁定记录");
                }
                // 3. éªŒè¯æ‹†åŒ…记录
                //  éªŒè¯æ‹†åŒ…记录
                var splitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
                    .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode)
                    .ToListAsync();
@@ -2364,7 +2103,7 @@
            var locallyProcessedBarcodes = new HashSet<string>();
            decimal totalProcessedQty = 0;
            // 1. å¤„理已分配的锁定记录
            // å¤„理已分配的锁定记录
            if (statusAnalysis.HasRemainingLocks)
            {
                _logger.LogInformation($"处理已分配锁定记录 - {statusAnalysis.RemainingLocks.Count} æ¡");
@@ -2389,7 +2128,7 @@
                }
            }
            // 2. å¤„理未分配的锁定记录
            //  å¤„理未分配的锁定记录
            if (statusAnalysis.HasUnallocatedLocks)
            {
                _logger.LogInformation($"处理未分配锁定记录 - {statusAnalysis.UnallocatedLocks.Count} æ¡");
@@ -2414,7 +2153,7 @@
                }
            }
            // 3. å¤„理未分配的库存货物
            // å¤„理未分配的库存货物
            if (statusAnalysis.HasPalletStockGoods)
            {
                _logger.LogInformation($"处理未分配库存货物 - {statusAnalysis.PalletStockGoods.Count} ä¸ª");
@@ -2438,7 +2177,7 @@
                }
            }
            // 4. ã€ä¿®æ­£ã€‘处理拆包记录 - åªå¤„理未被其他逻辑覆盖的条码
            // å¤„理拆包记录 - åªå¤„理未被其他逻辑覆盖的条码
            if (statusAnalysis.HasSplitRecords && statusAnalysis.SplitReturnQty > 0)
            {
                _logger.LogInformation($"处理拆包记录相关库存 - æ–°å¢žæ•°é‡: {statusAnalysis.SplitReturnQty}");
@@ -2483,282 +2222,6 @@
        /// <summary>
        /// å¤„理已分配锁定记录的回库
        /// å·²åˆ†é…é”å®šè®°å½•需要:1.恢复库存 2.更新锁定状态 3.减少订单明细分配数量
        /// </summary>
        private async Task ProcessSingleLockReturn(Dt_OutStockLockInfo lockInfo, decimal returnQty)
        {
            try
            {
                _logger.LogInformation($"开始处理已分配锁定记录回库 - é”å®šID: {lockInfo.Id}, æ¡ç : {lockInfo.CurrentBarcode}, å›žåº“数量: {returnQty}");
                if (returnQty <= 0)
                {
                    _logger.LogInformation($"回库数量无效({returnQty}),跳过处理");
                    return;
                }
                // 1. éªŒè¯é”å®šè®°å½•状态
                if (lockInfo.Status != (int)OutLockStockStatusEnum.出库中 &&
                    lockInfo.Status != (int)OutLockStockStatusEnum.回库中)
                {
                    _logger.LogWarning($"锁定记录状态不是出库中或回库中,跳过处理 - çŠ¶æ€: {lockInfo.Status}");
                    return;
                }
                // 2. èŽ·å–å…³è”çš„åº“å­˜æ˜Žç»†
                var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                    .FirstAsync(x => x.Barcode == lockInfo.CurrentBarcode && x.StockId == lockInfo.StockId);
                if (stockDetail == null)
                {
                    _logger.LogError($"未找到库存明细 - æ¡ç : {lockInfo.CurrentBarcode}, StockId: {lockInfo.StockId}");
                    throw new InvalidOperationException($"库存明细不存在: {lockInfo.CurrentBarcode}");
                }
                // 3. èŽ·å–å…³è”çš„è®¢å•æ˜Žç»†
                var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                    .FirstAsync(x => x.Id == lockInfo.OrderDetailId);
                if (orderDetail == null)
                {
                    _logger.LogError($"未找到订单明细 - OrderDetailId: {lockInfo.OrderDetailId}");
                    throw new InvalidOperationException($"订单明细不存在: {lockInfo.OrderDetailId}");
                }
                // è®°å½•原始值(用于日志和回滚检查)
                decimal originalStockQty = stockDetail.StockQuantity;
                decimal originalOutboundQty = stockDetail.OutboundQuantity;
                int originalStockStatus = stockDetail.Status;
                decimal originalAllocatedQty = orderDetail.AllocatedQuantity;
                decimal originalLockQty = orderDetail.LockQuantity;
                decimal originalOverOutQty = orderDetail.OverOutQuantity;
                decimal originalLockPickedQty = lockInfo.PickedQty;
                decimal originalLockAssignQty = lockInfo.AssignQuantity;
                int originalLockStatus = lockInfo.Status;
                _logger.LogInformation($"回库前数据状态:");
                _logger.LogInformation($"  åº“å­˜ - æ¡ç : {stockDetail.Barcode}, æ•°é‡: {originalStockQty}, å‡ºåº“: {originalOutboundQty}, çŠ¶æ€: {GetStockStatusName(originalStockStatus)}");
                _logger.LogInformation($"  è®¢å•明细 - åˆ†é…: {originalAllocatedQty}, é”å®š: {originalLockQty}, å·²å‡ºåº“: {originalOverOutQty}");
                _logger.LogInformation($"  é”å®šè®°å½• - åˆ†é…: {originalLockAssignQty}, å·²æ‹£é€‰: {originalLockPickedQty}, çŠ¶æ€: {GetLockStatusName(originalLockStatus)}");
                // 4. ã€æ ¸å¿ƒé€»è¾‘】恢复库存数据
                // 4.1 å¢žåŠ åº“å­˜æ•°é‡
                stockDetail.StockQuantity += returnQty;
                // 4.2 å‡å°‘出库数量(但需确保不会出现负数)
                if (stockDetail.OutboundQuantity >= returnQty)
                {
                    stockDetail.OutboundQuantity -= returnQty;
                }
                else
                {
                    // å¦‚果出库数量小于回库数量,说明数据异常
                    _logger.LogWarning($"出库数量({stockDetail.OutboundQuantity})小于回库数量({returnQty}),数据异常");
                    stockDetail.OutboundQuantity = 0;
                }
                // 4.3 æ›´æ–°åº“存状态
                if (stockDetail.OutboundQuantity <= 0 && stockDetail.StockQuantity > 0)
                {
                    stockDetail.Status = (int)StockStatusEmun.入库完成;
                    _logger.LogInformation($"库存状态更新为: å…¥åº“完成");
                }
                else if (stockDetail.StockQuantity > 0)
                {
                    stockDetail.Status = (int)StockStatusEmun.出库锁定;
                    _logger.LogInformation($"库存状态保持为: å‡ºåº“锁定");
                }
                await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
                _logger.LogInformation($"库存更新完成 - æ¡ç : {stockDetail.Barcode}");
                _logger.LogInformation($"  åº“存数量: {originalStockQty} -> {stockDetail.StockQuantity}");
                _logger.LogInformation($"  å‡ºåº“数量: {originalOutboundQty} -> {stockDetail.OutboundQuantity}");
                _logger.LogInformation($"  çŠ¶æ€: {GetStockStatusName(originalStockStatus)} -> {GetStockStatusName(stockDetail.Status)}");
                // 5. æ›´æ–°é”å®šè®°å½•状态
                lockInfo.Status = (int)OutLockStockStatusEnum.已回库;
                lockInfo.Operator = App.User.UserName;
                // ã€é‡è¦ã€‘如果锁定记录是部分拣选后回库,需要调整已拣选数量
                // ä½†ä¸€èˆ¬æƒ…况下,回库的是未拣选的部分,所以PickedQty保持不变
                await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
                _logger.LogInformation($"锁定记录更新完成 - ID: {lockInfo.Id}");
                _logger.LogInformation($"  çŠ¶æ€: {GetLockStatusName(originalLockStatus)} -> {GetLockStatusName(lockInfo.Status)}");
                _logger.LogInformation($"  åˆ†é…æ•°é‡: {originalLockAssignQty} (不变)");
                _logger.LogInformation($"  å·²æ‹£é€‰æ•°é‡: {originalLockPickedQty} (不变)");
                // 6. æ›´æ–°è®¢å•明细数据
                // 6.1 å‡å°‘已分配数量
                if (orderDetail.AllocatedQuantity >= returnQty)
                {
                    orderDetail.AllocatedQuantity -= returnQty;
                }
                else
                {
                    // å¦‚果分配数量小于回库数量,说明数据异常
                    _logger.LogWarning($"分配数量({orderDetail.AllocatedQuantity})小于回库数量({returnQty}),重置为0");
                    orderDetail.AllocatedQuantity = 0;
                }
                // 6.2 å‡å°‘锁定数量(应与分配数量保持同步)
                if (orderDetail.LockQuantity >= returnQty)
                {
                    orderDetail.LockQuantity -= returnQty;
                }
                else
                {
                    _logger.LogWarning($"锁定数量({orderDetail.LockQuantity})小于回库数量({returnQty}),重置为0");
                    orderDetail.LockQuantity = 0;
                }
                // 6.3 å·²å‡ºåº“数量保持不变(回库不影响已出库数量)
                // orderDetail.OverOutQuantity = orderDetail.OverOutQuantity;
                // 6.4 æ›´æ–°æ‰¹æ¬¡åˆ†é…çŠ¶æ€
                await UpdateBatchAllocateStatus(orderDetail);
                await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync();
                _logger.LogInformation($"订单明细更新完成 - ID: {orderDetail.Id}");
                _logger.LogInformation($"  åˆ†é…æ•°é‡: {originalAllocatedQty} -> {orderDetail.AllocatedQuantity}");
                _logger.LogInformation($"  é”å®šæ•°é‡: {originalLockQty} -> {orderDetail.LockQuantity}");
                _logger.LogInformation($"  å·²å‡ºåº“数量: {originalOverOutQty} (保持不变)");
                // 7. éªŒè¯å›žåº“后的数据一致性
                await ValidateAfterLockReturn(lockInfo, stockDetail, orderDetail, returnQty);
                _logger.LogInformation($"已分配锁定记录回库处理完成 - é”å®šID: {lockInfo.Id}, å›žåº“数量: {returnQty}");
            }
            catch (Exception ex)
            {
                _logger.LogError($"处理已分配锁定记录回库失败 - é”å®šID: {lockInfo.Id}, Error: {ex.Message}");
                throw new InvalidOperationException($"处理锁定记录回库失败: {ex.Message}", ex);
            }
        }
        /// <summary>
        /// é”å®šè®°å½•回库后验证数据一致性
        /// </summary>
        private async Task ValidateAfterLockReturn(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail,
            Dt_OutboundOrderDetail orderDetail, decimal returnQty)
        {
            try
            {
                _logger.LogInformation($"开始回库后数据验证 - é”å®šID: {lockInfo.Id}");
                bool allValid = true;
                List<string> validationErrors = new List<string>();
                // 1. é‡æ–°èŽ·å–æœ€æ–°æ•°æ®
                var refreshedStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                    .FirstAsync(x => x.Id == stockDetail.Id);
                var refreshedOrder = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                    .FirstAsync(x => x.Id == orderDetail.Id);
                var refreshedLock = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .FirstAsync(x => x.Id == lockInfo.Id);
                // 2. éªŒè¯åº“存数据
                // è®¡ç®—库存数量应该增加回库数量
                decimal expectedStockQty = stockDetail.StockQuantity + returnQty;
                if (Math.Abs(refreshedStock.StockQuantity - expectedStockQty) > 0.01m)
                {
                    string error = $"库存数量不正确!期望: {expectedStockQty}, å®žé™…: {refreshedStock.StockQuantity}";
                    validationErrors.Add(error);
                    allValid = false;
                    _logger.LogError(error);
                }
                // éªŒè¯åº“存状态应为入库完成或出库锁定
                if (refreshedStock.Status != (int)StockStatusEmun.入库完成 &&
                    refreshedStock.Status != (int)StockStatusEmun.出库锁定)
                {
                    string error = $"库存状态异常!期望:入库完成或出库锁定, å®žé™…:{GetStockStatusName(refreshedStock.Status)}";
                    validationErrors.Add(error);
                    allValid = false;
                    _logger.LogError(error);
                }
                // 3. éªŒè¯é”å®šè®°å½•状态
                if (refreshedLock.Status != (int)OutLockStockStatusEnum.已回库)
                {
                    string error = $"锁定记录状态异常!期望:已回库, å®žé™…:{GetLockStatusName(refreshedLock.Status)}";
                    validationErrors.Add(error);
                    allValid = false;
                    _logger.LogError(error);
                }
                // 4. éªŒè¯è®¢å•明细数据
                // è®¡ç®—分配数量应该减少回库数量
                decimal expectedAllocatedQty = Math.Max(0, orderDetail.AllocatedQuantity - returnQty);
                if (Math.Abs(refreshedOrder.AllocatedQuantity - expectedAllocatedQty) > 0.01m)
                {
                    string error = $"订单分配数量不正确!期望: {expectedAllocatedQty}, å®žé™…: {refreshedOrder.AllocatedQuantity}";
                    validationErrors.Add(error);
                    allValid = false;
                    _logger.LogError(error);
                }
                // åˆ†é…æ•°é‡åº”与锁定数量一致
                if (Math.Abs(refreshedOrder.AllocatedQuantity - refreshedOrder.LockQuantity) > 0.01m)
                {
                    string error = $"订单分配数量与锁定数量不一致!分配: {refreshedOrder.AllocatedQuantity}, é”å®š: {refreshedOrder.LockQuantity}";
                    validationErrors.Add(error);
                    allValid = false;
                    _logger.LogError(error);
                }
                // 5. æ•°æ®å…³è”性验证
                // é”å®šè®°å½•的已拣选数量 + å½“前库存的出库数量应等于原始出库数量
                decimal totalOutboundFromLock = refreshedLock.PickedQty + refreshedStock.OutboundQuantity;
                // æŸ¥æ‰¾è¯¥æ¡ç çš„æ‰€æœ‰æœªå–消拣选记录
                var pickingRecords = await Db.Queryable<Dt_PickingRecord>()
                    .Where(x => x.Barcode == stockDetail.Barcode && !x.IsCancelled)
                    .ToListAsync();
                decimal totalPickedFromRecords = pickingRecords.Sum(x => x.PickQuantity);
                if (Math.Abs(totalOutboundFromLock - totalPickedFromRecords) > 0.01m)
                {
                    string error = $"数据关联性异常!锁定拣选({refreshedLock.PickedQty})+库存出库({refreshedStock.OutboundQuantity})={totalOutboundFromLock}, ä½†æ‹£é€‰è®°å½•总和={totalPickedFromRecords}";
                    validationErrors.Add(error);
                    allValid = false;
                    _logger.LogError(error);
                }
                // 6. è¾“出验证结果
                if (allValid)
                {
                    _logger.LogInformation($"回库后数据验证全部通过");
                }
                else
                {
                    _logger.LogError($"回库后数据验证失败,发现{validationErrors.Count}个问题");
                    foreach (var error in validationErrors.Take(3)) // åªæ˜¾ç¤ºå‰3个问题
                    {
                        _logger.LogError($"验证问题: {error}");
                    }
                    // å¦‚果问题严重,可以抛出异常
                    if (validationErrors.Any(e => e.Contains("异常")))
                    {
                        throw new InvalidOperationException($"回库后数据验证失败: {string.Join("; ", validationErrors.Take(2))}");
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError($"回库后验证异常: {ex.Message}");
                // ä¸é‡æ–°æŠ›å‡ºï¼Œé¿å…å½±å“ä¸»æµç¨‹
            }
        }
        /// <summary>
        /// å¤„理单个条码回库(通用方法)- åŒºåˆ†å·²åˆ†é…å’Œæœªåˆ†é…
        /// </summary>
        private async Task ProcessSingleBarcodeReturn(string barcode, int stockId, decimal returnQty, bool isUnallocated = false)
@@ -2782,7 +2245,7 @@
                _logger.LogInformation($"回库前状态 - åº“å­˜: {originalStockQty}, å‡ºåº“: {originalOutboundQty}, çŠ¶æ€: {GetStockStatusName(originalStatus)}");
                // ã€ä¿®å¤ã€‘根据是否未分配决定处理逻辑
                // æ ¹æ®æ˜¯å¦æœªåˆ†é…å†³å®šå¤„理逻辑
                if (isUnallocated)
                {
                    // æœªåˆ†é…é”å®šï¼šåªæ¢å¤çŠ¶æ€ï¼Œä¸æ”¹å˜åº“å­˜æ•°é‡
@@ -2847,7 +2310,7 @@
                    return;
                }
                // ã€ä¿®å¤ã€‘获取库存明细
                // èŽ·å–åº“å­˜æ˜Žç»†
                var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                    .FirstAsync(x => x.Barcode == lockInfo.CurrentBarcode && x.StockId == lockInfo.StockId);
@@ -2865,7 +2328,7 @@
                _logger.LogInformation($"未分配锁定回库前状态:");
                _logger.LogInformation($"  åº“å­˜ - æ¡ç : {stockDetail.Barcode}, æ•°é‡: {originalStockQty}, å‡ºåº“: {originalOutboundQty}, çŠ¶æ€: {GetStockStatusName(originalStatus)}");
                // ã€ä¿®å¤å…³é”®ã€‘对于未分配锁定记录,库存数量不应改变!
                // å¯¹äºŽæœªåˆ†é…é”å®šè®°å½•,库存数量不应改变!
                // å› ä¸ºåº“存本来就存在,只是状态被锁定
                // stockDetail.StockQuantity ä¿æŒä¸å˜
@@ -2923,7 +2386,7 @@
            {
                _logger.LogInformation($"开始回库后数据验证");
                // 1. éªŒè¯åº“存状态和数量
                //  éªŒè¯åº“存状态和数量
                var stockDetails = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                    .Where(x => x.StockId == stockId)
                    .ToListAsync();
@@ -2937,7 +2400,7 @@
                    _logger.LogError($"发现负数库存数量!条码: {string.Join(", ", unreasonableStocks.Select(x => x.Barcode))}");
                }
                // 2. éªŒè¯é”å®šè®°å½•状态
                //  éªŒè¯é”å®šè®°å½•状态
                var lockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode)
                    .ToListAsync();
@@ -2956,7 +2419,7 @@
                    }
                }
                // 3. æ•°æ®ä¸€è‡´æ€§éªŒè¯
                // æ•°æ®ä¸€è‡´æ€§éªŒè¯
                decimal totalExpectedReturnQty = lockInfos
                    .Where(x => x.Status == (int)OutLockStockStatusEnum.已回库)
                    .Sum(x => x.AssignQuantity - x.PickedQty);
@@ -2966,7 +2429,7 @@
                _logger.LogInformation($"  å·²å›žåº“锁定记录数量: {lockInfos.Count(x => x.Status == (int)OutLockStockStatusEnum.已回库)}");
                _logger.LogInformation($"  æ€»å›žåº“数量(锁定记录计算): {totalExpectedReturnQty}");
                // 4. éªŒè¯åº“存数量与锁定记录的一致性
                // éªŒè¯åº“存数量与锁定记录的一致性
                foreach (var lockInfo in lockInfos.Where(x => !string.IsNullOrEmpty(x.CurrentBarcode)))
                {
                    var stock = stockDetails.FirstOrDefault(x => x.Barcode == lockInfo.CurrentBarcode);
@@ -2989,76 +2452,7 @@
            }
        }
        /// <summary>
        /// éªŒè¯å›žåº“前后数据一致性
        /// </summary>
        private async Task<bool> ValidateReturnData(string orderNo, string palletCode, int stockId, bool isBefore = true)
        {
            string phase = isBefore ? "回库前" : "回库后";
            try
            {
                _logger.LogInformation($"【{phase}数据验证】开始 - è®¢å•: {orderNo}, æ‰˜ç›˜: {palletCode}");
                // 1. æ£€æŸ¥åº“存明细
                var stockDetails = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                    .Where(x => x.StockId == stockId)
                    .ToListAsync();
                decimal totalStockQty = stockDetails.Sum(x => x.StockQuantity);
                _logger.LogInformation($"{phase}库存总量: {totalStockQty}");
                // 2. æ£€æŸ¥é”å®šè®°å½•
                var lockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode)
                    .ToListAsync();
                // æ£€æŸ¥é”å®šè®°å½•状态分布
                var statusGroups = lockInfos.GroupBy(x => x.Status)
                    .Select(g => new { Status = g.Key, Count = g.Count() })
                    .ToList();
                foreach (var group in statusGroups)
                {
                    _logger.LogInformation($"{phase}锁定状态 {GetLockStatusName(group.Status)}: {group.Count} æ¡");
                }
                // 3. åŸºæœ¬éªŒè¯
                bool isValid = true;
                // éªŒè¯1: å¦‚果锁定记录状态为"拣选完成",对应库存应该为0
                var completedLocks = lockInfos.Where(x => x.Status == (int)OutLockStockStatusEnum.拣选完成).ToList();
                foreach (var lockInfo in completedLocks)
                {
                    if (!string.IsNullOrEmpty(lockInfo.CurrentBarcode))
                    {
                        var stock = stockDetails.FirstOrDefault(x => x.Barcode == lockInfo.CurrentBarcode);
                        if (stock != null && stock.StockQuantity > 0)
                        {
                            _logger.LogWarning($"{phase}验证警告 - é”å®šID {lockInfo.Id} æ‹£é€‰å®Œæˆä½†åº“存不为0: {stock.StockQuantity}");
                        }
                    }
                }
                // éªŒè¯2: åº“存状态一致性
                foreach (var stock in stockDetails)
                {
                    if (stock.Status == (int)StockStatusEmun.出库锁定 && stock.StockQuantity == 0)
                    {
                        _logger.LogWarning($"{phase}验证警告 - æ¡ç  {stock.Barcode} çŠ¶æ€ä¸ºå‡ºåº“é”å®šä½†åº“å­˜ä¸º0");
                    }
                }
                _logger.LogInformation($"【{phase}数据验证】完成 - çŠ¶æ€: {(isValid ? "通过" : "有警告")}");
                return isValid;
            }
            catch (Exception ex)
            {
                _logger.LogError($"{phase} æ•°æ®éªŒè¯å¤±è´¥: {ex.Message}");
                return false;
            }
        }
        private string GetLockStatusName(int status)
        {
            return status switch
@@ -3173,464 +2567,6 @@
        /// <summary>
        /// æ‰§è¡Œå›žåº“数据操作 - ç®€åŒ–版本
        /// </summary>
        private async Task ExecuteReturnDataOperations(PalletStatusAnalysis statusAnalysis)
        {
            _logger.LogInformation($"开始执行回库数据操作 - è®¢å•: {statusAnalysis.OrderNo}, æ‰˜ç›˜: {statusAnalysis.PalletCode}");
            try
            {
                // ä½¿ç”¨ HashSet é¿å…é‡å¤å¤„理条码
                var processedBarcodes = new HashSet<string>();
                decimal totalReturnedQty = 0;
                // 1. å¤„理已分配的未分拣锁定记录
                if (statusAnalysis.HasRemainingLocks)
                {
                    _logger.LogInformation($"处理 {statusAnalysis.RemainingLocks.Count} æ¡å·²åˆ†é…æœªåˆ†æ‹£é”å®šè®°å½•");
                    foreach (var lockInfo in statusAnalysis.RemainingLocks)
                    {
                        if (string.IsNullOrEmpty(lockInfo.CurrentBarcode) || processedBarcodes.Contains(lockInfo.CurrentBarcode))
                        {
                            _logger.LogInformation($"跳过重复或空条码的锁定记录 - ID: {lockInfo.Id}");
                            continue;
                        }
                        // è®¡ç®—回库数量(未拣选的部分)
                        decimal returnQty = lockInfo.AssignQuantity - lockInfo.PickedQty;
                        if (returnQty > 0)
                        {
                            _logger.LogInformation($"处理锁定记录回库 - ID: {lockInfo.Id}, æ¡ç : {lockInfo.CurrentBarcode}, å›žåº“数量: {returnQty}");
                            // å¤„理库存
                            await ProcessStockForReturn(lockInfo.CurrentBarcode, statusAnalysis.StockId, returnQty);
                            // æ ‡è®°ä¸ºå·²å›žåº“
                            lockInfo.Status = (int)OutLockStockStatusEnum.已回库;
                            lockInfo.Operator = App.User.UserName;
                            await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
                            // å‡å°‘订单明细的分配数量
                            if (lockInfo.OrderDetailId > 0)
                            {
                                var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>().FirstAsync(x => x.Id == lockInfo.OrderDetailId);
                                await ReduceOrderDetailAllocation(orderDetail, returnQty);
                            }
                            processedBarcodes.Add(lockInfo.CurrentBarcode);
                            totalReturnedQty += returnQty;
                            _logger.LogInformation($"锁定记录回库完成 - ID: {lockInfo.Id}, å›žåº“数量: {returnQty}");
                        }
                        else
                        {
                            _logger.LogInformation($"锁定记录无需回库 - ID: {lockInfo.Id}, å·²æ‹£é€‰å®Œæˆ");
                        }
                    }
                }
                // 2. å¤„理未分配的库存货物
                if (statusAnalysis.HasPalletStockGoods)
                {
                    _logger.LogInformation($"处理 {statusAnalysis.PalletStockGoods.Count} ä¸ªæœªåˆ†é…åº“存货物");
                    foreach (var stockDetail in statusAnalysis.PalletStockGoods)
                    {
                        if (string.IsNullOrEmpty(stockDetail.Barcode) || processedBarcodes.Contains(stockDetail.Barcode))
                        {
                            _logger.LogInformation($"跳过重复或空条码的库存 - åº“å­˜ID: {stockDetail.Id}");
                            continue;
                        }
                        if (stockDetail.StockQuantity > 0)
                        {
                            decimal returnQty = stockDetail.StockQuantity;
                            _logger.LogInformation($"处理未分配库存回库 - æ¡ç : {stockDetail.Barcode}, å›žåº“数量: {returnQty}");
                            // ç›´æŽ¥æ¢å¤åº“存状态
                            stockDetail.Status = (int)StockStatusEmun.入库完成;
                            await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
                            processedBarcodes.Add(stockDetail.Barcode);
                            totalReturnedQty += returnQty;
                            _logger.LogInformation($"未分配库存回库完成 - æ¡ç : {stockDetail.Barcode}, æ•°é‡: {returnQty}");
                        }
                    }
                }
                // 3. å¤„理拆包记录相关的条码
                if (statusAnalysis.HasSplitRecords)
                {
                    _logger.LogInformation($"处理 {statusAnalysis.SplitRecords.Count} æ¡æ‹†åŒ…记录");
                    // æ”¶é›†æ‹†åŒ…相关的所有条码
                    var splitBarcodes = new List<string>();
                    foreach (var splitRecord in statusAnalysis.SplitRecords)
                    {
                        if (!string.IsNullOrEmpty(splitRecord.OriginalBarcode))
                            splitBarcodes.Add(splitRecord.OriginalBarcode);
                        if (!string.IsNullOrEmpty(splitRecord.NewBarcode))
                            splitBarcodes.Add(splitRecord.NewBarcode);
                    }
                    // åŽ»é‡
                    splitBarcodes = splitBarcodes.Distinct().ToList();
                    foreach (var barcode in splitBarcodes)
                    {
                        if (processedBarcodes.Contains(barcode))
                        {
                            _logger.LogInformation($"拆包条码已处理: {barcode}");
                            continue;
                        }
                        // æŸ¥æ‰¾åº“å­˜
                        var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                            .FirstAsync(x => x.Barcode == barcode && x.StockId == statusAnalysis.StockId);
                        if (stockDetail != null && stockDetail.StockQuantity > 0)
                        {
                            decimal returnQty = stockDetail.StockQuantity;
                            _logger.LogInformation($"处理拆包相关库存回库 - æ¡ç : {barcode}, å›žåº“数量: {returnQty}");
                            // æ¢å¤åº“存状态
                            if (stockDetail.Status == (int)StockStatusEmun.出库锁定)
                            {
                                stockDetail.Status = (int)StockStatusEmun.入库完成;
                                await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
                                processedBarcodes.Add(barcode);
                                totalReturnedQty += returnQty;
                                _logger.LogInformation($"拆包库存回库完成 - æ¡ç : {barcode}, æ•°é‡: {returnQty}");
                            }
                        }
                    }
                }
                _logger.LogInformation($"回库数据操作完成 - æ€»å›žåº“数量: {totalReturnedQty}, å¤„理条码数: {processedBarcodes.Count}");
            }
            catch (Exception ex)
            {
                _logger.LogError($"回库数据操作失败 - è®¢å•: {statusAnalysis.OrderNo}, æ‰˜ç›˜: {statusAnalysis.PalletCode}, Error: {ex.Message}");
                throw;
            }
        }
        /// <summary>
        /// å¤„理库存回库 - å®Œæ•´ä¿®æ­£ç‰ˆ
        /// ç¡®ä¿OutboundQuantity正确减少
        /// </summary>
        private async Task ProcessStockForReturn(string barcode, int stockId, decimal returnQty)
        {
            try
            {
                _logger.LogInformation($"处理库存回库 - æ¡ç : {barcode}, å›žåº“数量: {returnQty}");
                var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                    .FirstAsync(x => x.Barcode == barcode && x.StockId == stockId);
                if (stockDetail == null)
                {
                    _logger.LogWarning($"未找到库存明细 - æ¡ç : {barcode}");
                    return;
                }
                // è®°å½•原始值
                decimal originalStockQty = stockDetail.StockQuantity;
                decimal originalOutboundQty = stockDetail.OutboundQuantity;
                int originalStatus = stockDetail.Status;
                _logger.LogInformation($"回库前状态 - åº“å­˜: {originalStockQty}, å‡ºåº“: {originalOutboundQty}, çŠ¶æ€: {GetStockStatusName(originalStatus)}");
                // ã€æ ¸å¿ƒä¿®æ­£ã€‘确保回库操作正确处理
                // 1. åº“存数量增加(回库的货物回到库存)
                stockDetail.StockQuantity += returnQty;
                // 2. å‡ºåº“数量减少(因为货物没有出库,而是回库了)
                // ä½†éœ€è¦ç¡®ä¿ä¸ä¼šå‡ºçŽ°è´Ÿæ•°
                if (stockDetail.OutboundQuantity >= returnQty)
                {
                    stockDetail.OutboundQuantity -= returnQty;
                }
                else
                {
                    // å¦‚果出库数量小于回库数量,说明数据异常
                    _logger.LogWarning($"出库数量({stockDetail.OutboundQuantity})小于回库数量({returnQty}),重置出库数量为0");
                    stockDetail.OutboundQuantity = 0;
                }
                // 3. æ›´æ–°çŠ¶æ€ä¸ºå…¥åº“å®Œæˆ
                stockDetail.Status = (int)StockStatusEmun.入库完成;
                await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
                _logger.LogInformation($"库存回库完成 - æ¡ç : {barcode}");
                _logger.LogInformation($"  åº“存数量: {originalStockQty} -> {stockDetail.StockQuantity}");
                _logger.LogInformation($"  å‡ºåº“数量: {originalOutboundQty} -> {stockDetail.OutboundQuantity}");
                _logger.LogInformation($"  çŠ¶æ€: {GetStockStatusName(originalStatus)} -> {GetStockStatusName(stockDetail.Status)}");
                // éªŒè¯å›žåº“后的数据
                await ValidateStockAfterReturn(barcode, stockId, returnQty);
            }
            catch (Exception ex)
            {
                _logger.LogError($"处理库存回库失败 - æ¡ç : {barcode}, Error: {ex.Message}");
                throw;
            }
        }
        /// <summary>
        /// éªŒè¯å›žåº“后库存数据
        /// </summary>
        private async Task ValidateStockAfterReturn(string barcode, int stockId, decimal returnQty)
        {
            try
            {
                var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                    .FirstAsync(x => x.Barcode == barcode && x.StockId == stockId);
                // æ£€æŸ¥å‡ºåº“数量是否为0或合理
                if (stockDetail.OutboundQuantity > stockDetail.StockQuantity)
                {
                    _logger.LogWarning($"回库后数据异常 - å‡ºåº“数量({stockDetail.OutboundQuantity})大于库存数量({stockDetail.StockQuantity})");
                }
                // æ£€æŸ¥çŠ¶æ€æ˜¯å¦æ­£ç¡®
                if (stockDetail.Status != (int)StockStatusEmun.入库完成)
                {
                    _logger.LogWarning($"回库后状态异常 - æœŸæœ›:入库完成, å®žé™…:{GetStockStatusName(stockDetail.Status)}");
                }
            }
            catch (Exception ex)
            {
                _logger.LogError($"验证回库后数据失败: {ex.Message}");
            }
        }
        /// <summary>
        /// ä¸ºé”å®šè®°å½•减少订单明细的分配数量
        /// </summary>
        private async Task ReduceOrderDetailAllocationForLock(long orderDetailId, decimal reduceQty)
        {
            if (orderDetailId <= 0)
                return;
            var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                .FirstAsync(x => x.Id == orderDetailId);
            if (orderDetail == null)
                return;
            decimal originalAllocated = orderDetail.AllocatedQuantity;
            decimal originalLock = orderDetail.LockQuantity;
            // éªŒè¯å‡å°‘数量不会导致负数
            if (orderDetail.AllocatedQuantity < reduceQty)
            {
                _logger.LogWarning($"分配数量不足,调整减少数量 - åŽŸè®¡åˆ’å‡å°‘: {reduceQty}, å®žé™…可用: {orderDetail.AllocatedQuantity}");
                reduceQty = orderDetail.AllocatedQuantity;
            }
            // å‡å°‘分配数量和锁定数量
            orderDetail.AllocatedQuantity -= reduceQty;
            orderDetail.LockQuantity -= reduceQty;
            // ç¡®ä¿æ•°é‡ä¸ä¼šä¸ºè´Ÿæ•°
            if (orderDetail.AllocatedQuantity < 0)
            {
                _logger.LogWarning($"分配数量出现负数,重置为0。原值: {orderDetail.AllocatedQuantity + reduceQty}, å‡å°‘: {reduceQty}");
                orderDetail.AllocatedQuantity = 0;
            }
            if (orderDetail.LockQuantity < 0)
            {
                _logger.LogWarning($"锁定数量出现负数,重置为0。原值: {orderDetail.LockQuantity + reduceQty}, å‡å°‘: {reduceQty}");
                orderDetail.LockQuantity = 0;
            }
            // æ›´æ–°æ‰¹æ¬¡åˆ†é…çŠ¶æ€
            await UpdateBatchAllocateStatus(orderDetail);
            await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync();
            _logger.LogInformation($"减少订单明细分配 - OrderDetailId: {orderDetail.Id}, " +
                                  $"分配数量: {originalAllocated} -> {orderDetail.AllocatedQuantity}, " +
                                  $"锁定数量: {originalLock} -> {orderDetail.LockQuantity}, " +
                                  $"减少数量: {reduceQty}");
        }
        /// <summary>
        /// å¤„理未分配的锁定记录回库 - ä¿®æ­£ç‰ˆ
        /// æ³¨æ„ï¼šæœªåˆ†é…é”å®šè®°å½•没有绑定订单明细,不需要减少订单明细的分配数量
        /// </summary>
        private async Task HandleUnallocatedLocksReturn(List<Dt_OutStockLockInfo> unallocatedLocks)
        {
            _logger.LogInformation($"开始处理未分配锁定记录回库 - å…± {unallocatedLocks.Count} æ¡è®°å½•");
            foreach (var lockInfo in unallocatedLocks)
            {
                // è®¡ç®—回库数量(未拣选的部分)
                decimal returnQty = lockInfo.AssignQuantity - lockInfo.PickedQty;
                if (returnQty <= 0)
                {
                    _logger.LogInformation($"跳过未分配锁定记录 - é”å®šID: {lockInfo.Id}, å·²æ‹£é€‰å®Œæˆæˆ–无需回库");
                    continue;
                }
                _logger.LogInformation($"处理未分配锁定记录回库 - é”å®šID: {lockInfo.Id}, æ¡ç : {lockInfo.CurrentBarcode}, å›žåº“数量: {returnQty}");
                // æ¢å¤åº“存状态
                var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                    .FirstAsync(x => x.Barcode == lockInfo.CurrentBarcode && x.StockId == lockInfo.StockId);
                if (stockDetail != null)
                {
                    // ã€ä¿®æ­£ã€‘恢复库存数量,出库数量保持不变(因为未分配锁定的出库数量本来就是0)
                    decimal originalStockQty = stockDetail.StockQuantity;
                    stockDetail.StockQuantity += returnQty;
                    // å‡ºåº“数量保持为0不变!
                    // stockDetail.OutboundQuantity = stockDetail.OutboundQuantity; // ä¿æŒä¸å˜
                    // æ¢å¤åº“存状态为可用状态
                    if (stockDetail.Status == (int)StockStatusEmun.出库锁定)
                    {
                        stockDetail.Status = (int)StockStatusEmun.入库完成;
                    }
                    await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
                    _logger.LogInformation($"恢复未分配库存 - æ¡ç : {stockDetail.Barcode}, åº“存数量: {originalStockQty} -> {stockDetail.StockQuantity}");
                }
                // æ›´æ–°é”å®šè®°å½•状态为已回库
                lockInfo.Status = (int)OutLockStockStatusEnum.已回库;
                lockInfo.Operator = App.User.UserName;
                await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
                _logger.LogInformation($"更新未分配锁定状态 - é”å®šID: {lockInfo.Id}, çŠ¶æ€: å‡ºåº“中 -> å·²å›žåº“");
            }
            _logger.LogInformation($"未分配锁定记录回库处理完成 - å…±å¤„理 {unallocatedLocks.Count} æ¡è®°å½•");
        }
        private async Task HandleAllocatedLocksReturn(List<Dt_OutStockLockInfo> allocatedLocks)
        {
            _logger.LogInformation($"开始处理已分配锁定记录回库 - å…± {allocatedLocks.Count} æ¡è®°å½•");
            // æŒ‰è®¢å•明细分组处理
            var orderDetailGroups = allocatedLocks.GroupBy(x => x.OrderDetailId);
            foreach (var group in orderDetailGroups)
            {
                var orderDetailId = group.Key;
                var groupLocks = group.ToList();
                _logger.LogInformation($"处理订单明细 {orderDetailId} çš„ {groupLocks.Count} æ¡é”å®šè®°å½•");
                // èŽ·å–è®¢å•æ˜Žç»†
                var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                    .FirstAsync(x => x.Id == orderDetailId);
                if (orderDetail == null)
                {
                    _logger.LogWarning($"未找到订单明细 - OrderDetailId: {orderDetailId}");
                    continue;
                }
                decimal totalReturnQtyForDetail = 0;
                foreach (var lockInfo in groupLocks)
                {
                    // è®¡ç®—回库数量(未拣选的部分)
                    decimal returnQty = lockInfo.AssignQuantity - lockInfo.PickedQty;
                    if (returnQty <= 0)
                    {
                        _logger.LogInformation($"跳过锁定记录 - é”å®šID: {lockInfo.Id}, å·²æ‹£é€‰å®Œæˆæˆ–无需回库");
                        continue;
                    }
                    _logger.LogInformation($"处理已分配锁定记录回库 - é”å®šID: {lockInfo.Id}, æ¡ç : {lockInfo.CurrentBarcode}, å›žåº“数量: {returnQty}");
                    // æ¢å¤åº“存状态
                    await RestoreStockForLockInfo(lockInfo, returnQty);
                    // æ›´æ–°é”å®šè®°å½•状态为已回库
                    lockInfo.Status = (int)OutLockStockStatusEnum.已回库;
                    lockInfo.Operator = App.User.UserName;
                    await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
                    _logger.LogInformation($"更新已分配锁定状态 - é”å®šID: {lockInfo.Id}, çŠ¶æ€: å‡ºåº“中 -> å·²å›žåº“");
                    totalReturnQtyForDetail += returnQty;
                }
                // å‡å°‘订单明细的分配数量
                if (totalReturnQtyForDetail > 0)
                {
                    await ReduceOrderDetailAllocation(orderDetail, totalReturnQtyForDetail);
                }
            }
            _logger.LogInformation($"已分配锁定记录回库处理完成 - å…±å¤„理 {allocatedLocks.Count} æ¡è®°å½•");
        }
        /// <summary>
        /// æ¢å¤é”å®šè®°å½•对应的库存
        /// </summary>
        private async Task RestoreStockForLockInfo(Dt_OutStockLockInfo lockInfo, decimal returnQty)
        {
            var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .FirstAsync(x => x.Barcode == lockInfo.CurrentBarcode && x.StockId == lockInfo.StockId);
            if (stockDetail != null)
            {
                // è®°å½•恢复前的库存状态
                decimal originalStockQty = stockDetail.StockQuantity;
                decimal originalOutboundQty = stockDetail.OutboundQuantity;
                // æ¢å¤åº“存数量:出库数量减少,库存数量增加
                stockDetail.OutboundQuantity -= returnQty;
                stockDetail.StockQuantity += returnQty;
                // ç¡®ä¿æ•°é‡ä¸ä¼šä¸ºè´Ÿæ•°
                if (stockDetail.OutboundQuantity < 0)
                {
                    _logger.LogWarning($"出库数量出现负数,重置为0。原值: {stockDetail.OutboundQuantity + returnQty}");
                    stockDetail.OutboundQuantity = 0;
                }
                // æ¢å¤åº“存状态为可用状态
                if (stockDetail.Status == (int)StockStatusEmun.出库锁定)
                {
                    stockDetail.Status = (int)StockStatusEmun.入库完成;
                    _logger.LogInformation($"库存状态更新为入库完成 - æ¡ç : {stockDetail.Barcode}");
                }
                await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
                _logger.LogInformation($"恢复库存状态 - æ¡ç : {stockDetail.Barcode}, " +
                                     $"库存数量: {originalStockQty} -> {stockDetail.StockQuantity}, " +
                                     $"出库数量: {originalOutboundQty} -> {stockDetail.OutboundQuantity}");
            }
            else
            {
                _logger.LogWarning($"未找到对应的库存信息 - æ¡ç : {lockInfo.CurrentBarcode}, StockId: {lockInfo.StockId}");
            }
        }
        /// <summary>
        /// åˆ›å»ºå›žåº“任务
        /// </summary>
        private async Task CreateReturnTask(string orderNo, string palletCode, Dt_StockInfo stockInfo)
@@ -3691,6 +2627,7 @@
                _logger.LogInformation($"创建回库任务成功 - è®¢å•: {orderNo}, æ‰˜ç›˜: {palletCode}");
            }
        }
        /// <summary>
        /// æ›´æ–°å›žåº“后的订单状态
        /// </summary>
@@ -3730,6 +2667,12 @@
                // æ¸…理零库存数据
                await CleanupZeroStockData(stockInfo.Id);
                // èŽ·å–å½“å‰ä»»åŠ¡
                var currentTask = await GetCurrentTask(orderNo, palletCode);
                if (currentTask == null)
                {
                    return WebResponseContent.Instance.Error("未找到当前任务");
                }
                // åˆ›å»ºç©ºæ‰˜ç›˜åº“存记录
                var emptyStockInfo = new Dt_StockInfo()
                {
@@ -3741,21 +2684,90 @@
                emptyStockInfo.Details = new List<Dt_StockInfoDetail>();
                _stockInfoService.AddMaterielGroup(emptyStockInfo);
                // åˆ›å»ºç©ºæ‰˜ç›˜å›žåº“任务
                await CreateReturnTask(orderNo, palletCode, emptyStockInfo);
                // åˆ›å»ºå›žåº“任务(不发送ESS命令)
                var returnTaskInfo = await CreateEmptyPalletReturnTask(orderNo, palletCode, emptyStockInfo, currentTask);
                return WebResponseContent.Instance.OK("空托盘回库成功");
                return WebResponseContent.Instance.OK("空托盘回库成功", returnTaskInfo);
            }
            catch (Exception ex)
            {
                _logger.LogError($"空托盘回库失败: {ex.Message}");
                return WebResponseContent.Instance.Error($"空托盘回库失败: {ex.Message}");
                _logger.LogError($"空托盘回库失败 HandleEmptyPalletReturn: {ex.Message}");
                return WebResponseContent.Instance.Error($"空托盘回库失败 HandleEmptyPalletReturn: {ex.Message}");
            }
        }
        /// <summary>
        /// åˆ†æžæ‰˜ç›˜çŠ¶æ€ç”¨äºŽå›žåº“
        /// ç¡®ä¿ä¸ä¼šé”™è¯¯è¯†åˆ«éœ€è¦å›žåº“的物品
        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;
        }
        // <summary>
        /// åˆ›å»ºç©ºæ‰˜ç›˜å›žåº“任务(不发送ESS命令)
        /// </summary>
        private async Task<ReturnTaskInfo> CreateEmptyPalletReturnTask(string orderNo, string palletCode, Dt_StockInfo emptyStockInfo, Dt_Task currentTask)
        {
            try
            {
                // åˆ†é…æ–°è´§ä½
                var newLocation = _locationInfoService.AssignLocation(emptyStockInfo.LocationType);
                var returnTask = new Dt_Task()
                {
                    CurrentAddress = stations[currentTask.TargetAddress],
                    Grade = 0,
                    PalletCode = palletCode,
                    NextAddress = "",
                    OrderNo = orderNo,
                    Roadway = newLocation.RoadwayNo,
                    SourceAddress = stations[currentTask.TargetAddress],
                    TargetAddress = newLocation.LocationCode,
                    TaskStatus = TaskStatusEnum.New.ObjToInt(),
                    TaskType = TaskTypeEnum.InEmpty.ObjToInt(),
                    PalletType = PalletTypeEnum.Empty.ObjToInt(),
                    WarehouseId = currentTask.WarehouseId
                };
                await _taskRepository.Db.Insertable(returnTask).ExecuteCommandAsync();
                _logger.LogInformation($"创建空托盘回库任务成功: {returnTask.TaskNum}");
                // åˆ é™¤åŽŸå§‹å‡ºåº“ä»»åŠ¡
                var result = _task_HtyService.DeleteAndMoveIntoHty(currentTask, OperateTypeEnum.人工删除);
                await _taskRepository.Db.Deleteable(currentTask).ExecuteCommandAsync();
                if (!result)
                {
                    await _taskRepository.Db.Deleteable(currentTask).ExecuteCommandAsync();
                }
                // è¿”回任务信息
                return new ReturnTaskInfo
                {
                    ShouldSendESS = true,
                    PalletCode = palletCode,
                    OriginalTaskTargetAddress = currentTask.TargetAddress,
                    ReturnTask = returnTask
                };
            }
            catch (Exception ex)
            {
                _logger.LogError($"创建空托盘回库任务失败: {ex.Message}");
                throw;
            }
        }
        /// <summary>
        /// åˆ†æžæ‰˜ç›˜çŠ¶æ€ç”¨äºŽå›žåº“ - ä¿®æ­£ç‰ˆï¼ˆè§£å†³æ‹†åŒ…记录重复计算问题)
@@ -3775,14 +2787,14 @@
            try
            {
                // 1. é¦–先获取托盘上所有的库存明细(基础数据)
                //  é¦–先获取托盘上所有的库存明细(基础数据)
                var allStockDetails = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                    .Where(x => x.StockId == stockId && x.StockQuantity > 0)
                    .ToListAsync();
                _logger.LogInformation($"找到 {allStockDetails.Count} ä¸ªæœ‰åº“存的明细记录");
                // 2. åˆ†æžæ‰€æœ‰é”å®šè®°å½•(已分配和未分配)
                //  åˆ†æžæ‰€æœ‰é”å®šè®°å½•(已分配和未分配)
                var allLockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode)
                    .ToListAsync();
@@ -3804,7 +2816,7 @@
                    result.HasRemainingLocks = true;
                    result.RemainingLocks = allocatedLocks;
                    // ã€ä¿®å¤ã€‘对于已分配锁定记录,回库数量是未拣选的部分
                    // å¯¹äºŽå·²åˆ†é…é”å®šè®°å½•,回库数量是未拣选的部分
                    result.RemainingLocksReturnQty = allocatedLocks.Sum(x =>
                    {
                        var returnQty = x.AssignQuantity - x.PickedQty;
@@ -3828,7 +2840,7 @@
                    result.HasUnallocatedLocks = true;
                    result.UnallocatedLocks = unallocatedLocks;
                    // ã€ä¿®å¤ã€‘对于未分配锁定记录,回库数量是它的分配数量(因为未拣选过)
                    // å¯¹äºŽæœªåˆ†é…é”å®šè®°å½•,回库数量是它的分配数量(因为未拣选过)
                    // ä½†å®žé™…上,库存本来就存在,只是状态需要恢复
                    result.UnallocatedLocksReturnQty = unallocatedLocks.Sum(x => x.AssignQuantity);
@@ -3844,7 +2856,7 @@
                    _logger.LogInformation($"发现 {unallocatedLocks.Count} æ¡æœªåˆ†é…é”å®šè®°å½•,回库数量(状态恢复): {result.UnallocatedLocksReturnQty}");
                }
                // 3. ã€é‡è¦ä¿®å¤ã€‘重新计算总回库数量
                // é‡æ–°è®¡ç®—总回库数量
                // å¯¹äºŽå·²åˆ†é…é”å®šï¼šå›žåº“数量 = æœªæ‹£é€‰æ•°é‡
                // å¯¹äºŽæœªåˆ†é…é”å®šï¼šæ²¡æœ‰å®žé™…的库存数量变化,只是状态恢复
                result.TotalReturnQty = result.RemainingLocksReturnQty; // åªè®¡ç®—已分配锁定的回库数量
@@ -3868,358 +2880,8 @@
                _logger.LogError($"回库分析失败 - è®¢å•: {orderNo}, æ‰˜ç›˜: {palletCode}, Error: {ex.Message}");
                throw;
            }
        }
        /// <summary>
        /// éªŒè¯æ²¡æœ‰é‡å¤æ¡ç 
        /// </summary>
        private async Task ValidateNoDuplicateBarcodes(PalletStatusAnalysis analysis, List<Dt_StockInfoDetail> allStockDetails)
        {
            try
            {
                // æ£€æŸ¥AllBarcodes中是否有重复
                var duplicateBarcodes = analysis.AllBarcodes
                    .GroupBy(b => b)
                    .Where(g => g.Count() > 1)
                    .Select(g => g.Key)
                    .ToList();
                if (duplicateBarcodes.Any())
                {
                    _logger.LogError($"发现重复条码: {string.Join(", ", duplicateBarcodes)}");
                    // è‡ªåŠ¨åŽ»é‡
                    analysis.AllBarcodes = analysis.AllBarcodes.Distinct().ToList();
                    _logger.LogWarning($"已自动去重,条码数: {analysis.AllBarcodes.Count}");
                }
                // æ£€æŸ¥æ¯ä¸ªæ¡ç çš„实际库存
                decimal totalStockFromBarcodes = 0;
                foreach (var barcode in analysis.AllBarcodes)
                {
                    var stock = allStockDetails.FirstOrDefault(x => x.Barcode == barcode);
                    if (stock != null)
                    {
                        totalStockFromBarcodes += stock.StockQuantity;
                        _logger.LogInformation($"条码库存 - {barcode}: {stock.StockQuantity}");
                    }
                }
                _logger.LogInformation($"回库分析总库存: {analysis.TotalReturnQty}, æ¡ç å®žé™…总库存: {totalStockFromBarcodes}");
                // å¦‚果分析的数量大于实际库存,说明有重复计算
                if (analysis.TotalReturnQty > totalStockFromBarcodes * 1.1m) // å…è®¸10%的误差
                {
                    _logger.LogError($"回库数量({analysis.TotalReturnQty})明显大于实际库存({totalStockFromBarcodes}),可能存在重复计算!");
                }
            }
            catch (Exception ex)
            {
                _logger.LogError($"验证重复条码失败: {ex.Message}");
            }
        }
        /// <summary>
        /// éªŒè¯åˆ†æžç»“果,确保数据一致性
        /// </summary>
        private async Task ValidateAnalysisResults(PalletStatusAnalysis analysis, int stockId)
        {
            _logger.LogInformation($"开始验证分析结果 - è®¢å•: {analysis.OrderNo}, æ‰˜ç›˜: {analysis.PalletCode}");
            try
            {
                // 1. éªŒè¯é”å®šè®°å½•和库存明细的匹配
                foreach (var lockInfo in analysis.RemainingLocks.Concat(analysis.UnallocatedLocks))
                {
                    if (!string.IsNullOrEmpty(lockInfo.CurrentBarcode))
                    {
                        var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                            .FirstAsync(x => x.Barcode == lockInfo.CurrentBarcode && x.StockId == lockInfo.StockId);
                        if (stockDetail == null)
                        {
                            _logger.LogWarning($"锁定记录 {lockInfo.Id} çš„æ¡ç  {lockInfo.CurrentBarcode} åœ¨åº“存明细中不存在");
                        }
                        else if (stockDetail.StockQuantity <= 0)
                        {
                            _logger.LogWarning($"锁定记录 {lockInfo.Id} çš„æ¡ç  {lockInfo.CurrentBarcode} åº“存数量为0或负数");
                        }
                    }
                }
                // 2. éªŒè¯æ€»å›žåº“数量的合理性
                // èŽ·å–æ‰˜ç›˜ä¸Šçš„æ€»åº“å­˜æ•°é‡
                var totalStockOnPallet = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                    .Where(x => x.StockId == stockId)
                    .SumAsync(x => x.StockQuantity);
                if (analysis.TotalReturnQty > totalStockOnPallet)
                {
                    _logger.LogWarning($"总回库数量 {analysis.TotalReturnQty} å¤§äºŽæ‰˜ç›˜æ€»åº“å­˜ {totalStockOnPallet},可能存在计算错误");
                }
                // 3. éªŒè¯æ¡ç çš„唯一性
                var duplicateBarcodes = analysis.AllBarcodes
                    .GroupBy(x => x)
                    .Where(g => g.Count() > 1)
                    .Select(g => g.Key)
                    .ToList();
                if (duplicateBarcodes.Any())
                {
                    _logger.LogWarning($"发现重复的条码: {string.Join(", ", duplicateBarcodes)}");
                }
                _logger.LogInformation($"分析结果验证完成");
            }
            catch (Exception ex)
            {
                _logger.LogError($"分析结果验证失败 - Error: {ex.Message}");
                // ä¸æŠ›å‡ºå¼‚常,只记录错误
            }
        }
        /// <summary>
        /// å¤„理未分拣的锁定记录回库
        /// ç¡®ä¿ä¸ä¼šé”™è¯¯ç»‘定条码数量到锁定数量
        /// </summary>
        private async Task HandleRemainingLocksReturn(List<Dt_OutStockLockInfo> remainingLocks)
        {
            _logger.LogInformation($"开始处理未分拣锁定记录回库 - å…± {remainingLocks.Count} æ¡è®°å½•");
            // æŒ‰è®¢å•明细分组处理,确保订单明细数据的一致性
            var orderDetailGroups = remainingLocks.GroupBy(x => x.OrderDetailId);
            foreach (var group in orderDetailGroups)
            {
                var orderDetailId = group.Key;
                var groupLocks = group.ToList();
                _logger.LogInformation($"处理订单明细 {orderDetailId} çš„ {groupLocks.Count} æ¡é”å®šè®°å½•");
                // èŽ·å–è®¢å•æ˜Žç»†
                var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                    .FirstAsync(x => x.Id == orderDetailId);
                if (orderDetail == null)
                {
                    _logger.LogWarning($"未找到订单明细 - OrderDetailId: {orderDetailId}");
                    continue;
                }
                decimal totalReturnQtyForDetail = 0;
                foreach (var lockInfo in groupLocks)
                {
                    // åªå¤„理状态为出库中的锁定记录
                    if (lockInfo.Status != (int)OutLockStockStatusEnum.出库中)
                    {
                        _logger.LogInformation($"跳过非出库中状态的锁定记录 - é”å®šID: {lockInfo.Id}, çŠ¶æ€: {lockInfo.Status}");
                        continue;
                    }
                    // è®¡ç®—回库数量(未拣选的部分)
                    decimal returnQty = lockInfo.AssignQuantity - lockInfo.PickedQty;
                    if (returnQty <= 0)
                    {
                        _logger.LogInformation($"跳过锁定记录 - é”å®šID: {lockInfo.Id}, å·²æ‹£é€‰å®Œæˆæˆ–无需回库");
                        continue;
                    }
                    _logger.LogInformation($"处理锁定记录回库 - é”å®šID: {lockInfo.Id}, æ¡ç : {lockInfo.CurrentBarcode}, å›žåº“数量: {returnQty}");
                    // æ¢å¤åº“存状态
                    var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                        .FirstAsync(x => x.Barcode == lockInfo.CurrentBarcode && x.StockId == lockInfo.StockId);
                    if (stockDetail != null)
                    {
                        // è®°å½•恢复前的库存状态
                        decimal originalStockQty = stockDetail.StockQuantity;
                        decimal originalOutboundQty = stockDetail.OutboundQuantity;
                        // åªæ¢å¤å®žé™…的库存数量,不创建新的条码或绑定
                        // æ¢å¤åº“存数量:出库数量减少,库存数量增加
                        stockDetail.OutboundQuantity -= returnQty;
                        stockDetail.StockQuantity += returnQty;
                        // ç¡®ä¿æ•°é‡ä¸ä¼šä¸ºè´Ÿæ•°
                        if (stockDetail.OutboundQuantity < 0)
                        {
                            _logger.LogWarning($"出库数量出现负数,重置为0。原值: {stockDetail.OutboundQuantity + returnQty}");
                            stockDetail.OutboundQuantity = 0;
                        }
                        // æ¢å¤åº“存状态为可用状态
                        if (stockDetail.Status == (int)StockStatusEmun.出库锁定)
                        {
                            stockDetail.Status = (int)StockStatusEmun.入库完成;
                            _logger.LogInformation($"库存状态更新为入库完成 - æ¡ç : {stockDetail.Barcode}");
                        }
                        await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
                        _logger.LogInformation($"恢复库存状态 - æ¡ç : {stockDetail.Barcode}, " +
                                             $"库存数量: {originalStockQty} -> {stockDetail.StockQuantity}, " +
                                             $"出库数量: {originalOutboundQty} -> {stockDetail.OutboundQuantity}");
                    }
                    else
                    {
                        _logger.LogWarning($"未找到对应的库存信息 - æ¡ç : {lockInfo.CurrentBarcode}, StockId: {lockInfo.StockId}");
                        // é‡è¦ï¼šå¦‚果找不到库存信息,跳过此锁定记录,避免数据不一致
                        continue;
                    }
                    // æ›´æ–°é”å®šè®°å½•状态为已回库,但不修改分配数量
                    // åˆ†é…æ•°é‡åœ¨è®¢å•明细层面统一处理
                    var originalStatus = lockInfo.Status;
                    lockInfo.Status = (int)OutLockStockStatusEnum.已回库;
                    lockInfo.Operator = App.User.UserName;
                    await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
                    _logger.LogInformation($"更新锁定状态 - é”å®šID: {lockInfo.Id}, çŠ¶æ€: {originalStatus} -> {lockInfo.Status}");
                    totalReturnQtyForDetail += returnQty;
                    _logger.LogInformation($"锁定记录回库完成 - é”å®šID: {lockInfo.Id}, å›žåº“数量: {returnQty}");
                }
                // å‡å°‘订单明细的分配数量
                if (totalReturnQtyForDetail > 0)
                {
                    await ReduceOrderDetailAllocation(orderDetail, totalReturnQtyForDetail);
                }
            }
            _logger.LogInformation($"未分拣锁定记录回库处理完成 - å…±å¤„理 {remainingLocks.Count} æ¡è®°å½•");
        }
        /// <summary>
        /// å¤„理未分配的库存货物回库
        /// ç¡®ä¿ä¸ä¼šåˆ›å»ºæ–°çš„锁定记录
        /// </summary>
        private async Task HandleUnallocatedStockReturn(List<Dt_StockInfoDetail> stockGoods)
        {
            _logger.LogInformation($"开始处理未分配库存回库 - å…± {stockGoods.Count} ä¸ªè´§ç‰©");
            foreach (var stockDetail in stockGoods)
            {
                if (stockDetail.StockQuantity <= 0)
                {
                    _logger.LogInformation($"跳过零库存货物 - æ¡ç : {stockDetail.Barcode}");
                    continue;
                }
                _logger.LogInformation($"处理未分配库存回库 - æ¡ç : {stockDetail.Barcode}, æ•°é‡: {stockDetail.StockQuantity}");
                // æ£€æŸ¥æ˜¯å¦å·²ç»æœ‰å¯¹åº”的锁定记录
                var existingLock = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .Where(x => x.CurrentBarcode == stockDetail.Barcode &&
                               x.Status == (int)OutLockStockStatusEnum.出库中)
                    .FirstAsync();
                if (existingLock != null)
                {
                    _logger.LogWarning($"库存条码 {stockDetail.Barcode} å·²æœ‰é”å®šè®°å½•,跳过直接回库处理");
                    continue;
                }
                // è®°å½•恢复前的状态
                var originalStatus = stockDetail.Status;
                // ç›´æŽ¥æ¢å¤åº“存状态为可用状态,不创建任何锁定记录
                stockDetail.Status = (int)StockStatusEmun.入库完成;
                await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
                _logger.LogInformation($"未分配库存回库完成 - æ¡ç : {stockDetail.Barcode}, çŠ¶æ€: {originalStatus} -> {stockDetail.Status}");
            }
            _logger.LogInformation($"未分配库存回库处理完成 - å…±å¤„理 {stockGoods.Count} ä¸ªè´§ç‰©");
        }
        /// <summary>
        /// å‡å°‘订单明细的分配数量
        /// ç¡®ä¿åˆ†é…æ•°é‡çš„减少是准确的
        /// </summary>
        private async Task ReduceOrderDetailAllocation(Dt_OutboundOrderDetail orderDetail, decimal reduceQty)
        {
            if (orderDetail == null)
                return;
            decimal originalAllocated = orderDetail.AllocatedQuantity;
            decimal originalLock = orderDetail.LockQuantity;
            // éªŒè¯å‡å°‘数量不会导致负数
            if (orderDetail.AllocatedQuantity < reduceQty)
            {
                _logger.LogWarning($"分配数量不足,调整减少数量 - åŽŸè®¡åˆ’å‡å°‘: {reduceQty}, å®žé™…可用: {orderDetail.AllocatedQuantity}");
                reduceQty = orderDetail.AllocatedQuantity;
            }
            // å‡å°‘分配数量和锁定数量
            orderDetail.AllocatedQuantity -= reduceQty;
            orderDetail.LockQuantity -= reduceQty;
            // ç¡®ä¿æ•°é‡ä¸ä¼šä¸ºè´Ÿæ•°
            if (orderDetail.AllocatedQuantity < 0)
            {
                _logger.LogWarning($"分配数量出现负数,重置为0。原值: {orderDetail.AllocatedQuantity + reduceQty}, å‡å°‘: {reduceQty}");
                orderDetail.AllocatedQuantity = 0;
            }
            if (orderDetail.LockQuantity < 0)
            {
                _logger.LogWarning($"锁定数量出现负数,重置为0。原值: {orderDetail.LockQuantity + reduceQty}, å‡å°‘: {reduceQty}");
                orderDetail.LockQuantity = 0;
            }
            // æ›´æ–°æ‰¹æ¬¡åˆ†é…çŠ¶æ€
            await UpdateBatchAllocateStatus(orderDetail);
            await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync();
            _logger.LogInformation($"减少订单明细分配 - OrderDetailId: {orderDetail.Id}, " +
                                 $"分配数量: {originalAllocated} -> {orderDetail.AllocatedQuantity}, " +
                                 $"锁定数量: {originalLock} -> {orderDetail.LockQuantity}, " +
                                 $"减少数量: {reduceQty}");
            // éªŒè¯æ•°æ®ä¸€è‡´æ€§
            await ValidateOrderDetailConsistency(orderDetail.Id);
        }
        /// <summary>
        /// éªŒè¯è®¢å•明细数据一致性
        /// </summary>
        private async Task ValidateOrderDetailConsistency(long orderDetailId)
        {
            var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                .FirstAsync(x => x.Id == orderDetailId);
            if (orderDetail == null)
                return;
            // è®¡ç®—所有相关锁定记录的总分配数量
            var relatedLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(x => x.OrderDetailId == orderDetailId &&
                           x.Status != (int)OutLockStockStatusEnum.已回库)
                .ToListAsync();
            decimal totalLockAssignQty = relatedLocks.Sum(x => x.AssignQuantity);
            // éªŒè¯è®¢å•明细分配数量与锁定记录总分配数量的一致性
            if (Math.Abs(orderDetail.AllocatedQuantity - totalLockAssignQty) > 0.01m)
            {
                _logger.LogWarning($"数据不一致警告 - OrderDetailId: {orderDetailId}, " +
                                  $"订单明细分配数量: {orderDetail.AllocatedQuantity}, " +
                                  $"锁定记录总分配数量: {totalLockAssignQty}");
            }
            else
            {
                _logger.LogInformation($"数据一致性验证通过 - OrderDetailId: {orderDetailId}");
            }
        }
        }
        /// <summary>
        /// åˆ†æ‰¹å›žåº“ - è°ƒç”¨ç»Ÿä¸€å›žåº“方法
        /// </summary>
@@ -4228,13 +2890,7 @@
            return await ExecutePalletReturn(orderNo, palletCode, "分批回库");
        }
        /// <summary>
        /// å‰©ä½™å›žåº“ - è°ƒç”¨ç»Ÿä¸€å›žåº“方法
        /// </summary>
        public async Task<WebResponseContent> ReturnRemaining(string orderNo, string palletCode, string reason)
        {
            return await ExecutePalletReturn(orderNo, palletCode, reason);
        }
        /// <summary>
        /// å–走空箱 - ä¿®æ­£ç‰ˆï¼Œæ­£ç¡®å¤„理未分配锁定记录
@@ -4247,7 +2903,7 @@
                _unitOfWorkManage.BeginTran();
                // 1. å…ˆå°è¯•执行回库操作,确保所有物品都回库
                //  å…ˆå°è¯•执行回库操作,确保所有物品都回库
                _logger.LogInformation($"步骤1: å…ˆæ‰§è¡Œå›žåº“操作");
                var returnResult = await ExecutePalletReturn(orderNo, palletCode, "取走空箱前回库");
@@ -4257,7 +2913,7 @@
                    _logger.LogWarning($"回库操作可能失败或无物品: {returnResult.Message}");
                }
                // 2. éªŒè¯ç©ºç®±å–走条件(必须全部完成拣选或已回库)
                // éªŒè¯ç©ºç®±å–走条件(必须全部完成拣选或已回库)
                _logger.LogInformation($"步骤2: éªŒè¯ç©ºç®±å–走条件");
                // èŽ·å–æ‰˜ç›˜çš„æ‰€æœ‰é”å®šè®°å½•ï¼ˆåŒ…æ‹¬å·²å›žåº“å’Œå·²å–èµ°çš„ï¼‰
@@ -4271,7 +2927,7 @@
                    return WebResponseContent.Instance.Error("该托盘没有锁定记录");
                }
                // ã€ä¿®æ­£ã€‘检查是否有未完成的锁定记录
                // æ£€æŸ¥æ˜¯å¦æœ‰æœªå®Œæˆçš„锁定记录
                var unfinishedLocks = allLockInfos.Where(x =>
                    x.Status == (int)OutLockStockStatusEnum.出库中 ||
                    x.Status == (int)OutLockStockStatusEnum.回库中).ToList();
@@ -4316,7 +2972,7 @@
                _logger.LogInformation($"验证通过,找到 {completedLocks.Count} æ¡å·²å®Œæˆè®°å½•");
                // 3. æ¸…理已完成的锁定记录(标记为已取走)
                //  æ¸…理已完成的锁定记录(标记为已取走)
                _logger.LogInformation($"步骤3: æ¸…理锁定记录");
                foreach (var lockInfo in completedLocks)
                {
@@ -4330,20 +2986,20 @@
                    }
                }
                // 4. æ¸…理对应的库存记录状态
                // æ¸…理对应的库存记录状态
                _logger.LogInformation($"步骤4: æ¸…理库存记录");
                foreach (var lockInfo in completedLocks)
                {
                    await CleanupStockInfo(lockInfo);
                }
                // 5. æ›´æ–°ç›¸å…³è®¢å•状态
                // æ›´æ–°ç›¸å…³è®¢å•状态
                _logger.LogInformation($"步骤5: æ›´æ–°è®¢å•状态");
                await UpdateOrderStatusAfterPalletRemoval(orderNo);
                // 6. è®°å½•操作历史
                _logger.LogInformation($"步骤6: è®°å½•操作历史");
                await RecordEmptyPalletRemoval(orderNo, palletCode, completedLocks);
                // è®°å½•操作历史
                //_logger.LogInformation($"步骤6: è®°å½•操作历史");
                //await RecordEmptyPalletRemoval(orderNo, palletCode, completedLocks);
                _unitOfWorkManage.CommitTran();
@@ -4358,687 +3014,16 @@
                return WebResponseContent.Instance.Error($"取走空箱失败:{ex.Message}");
            }
        }
        /// <summary>
        /// æ”¶é›†éœ€è¦å›žåº“的条码
        /// </summary>
        private async Task<List<string>> CollectReturnBarcodes(PalletStatusAnalysis status)
        {
            var returnBarcodes = new HashSet<string>();
            try
            {
                _logger.LogInformation($"开始收集回库条码 - è®¢å•: {status.OrderNo}, æ‰˜ç›˜: {status.PalletCode}");
                // 1. æ”¶é›†æœªåˆ†æ‹£é”å®šè®°å½•的条码
                if (status.HasRemainingLocks)
                {
                    foreach (var lockInfo in status.RemainingLocks)
                    {
                        if (!string.IsNullOrEmpty(lockInfo.CurrentBarcode))
                        {
                            returnBarcodes.Add(lockInfo.CurrentBarcode);
                            _logger.LogInformation($"添加锁定记录条码: {lockInfo.CurrentBarcode}");
                        }
                    }
                }
                // 2. æ”¶é›†æ‰˜ç›˜ä¸Šåº“存货物的条码
                if (status.HasPalletStockGoods)
                {
                    foreach (var stockDetail in status.PalletStockGoods)
                    {
                        if (!string.IsNullOrEmpty(stockDetail.Barcode) && stockDetail.StockQuantity > 0)
                        {
                            returnBarcodes.Add(stockDetail.Barcode);
                            _logger.LogInformation($"添加库存货物条码: {stockDetail.Barcode}, æ•°é‡: {stockDetail.StockQuantity}");
                        }
                    }
                }
                // 3. æ”¶é›†æ‹†åŒ…记录相关的条码
                if (status.HasSplitRecords)
                {
                    foreach (var splitRecord in status.SplitRecords)
                    {
                        // æ·»åŠ åŽŸæ¡ç 
                        if (!string.IsNullOrEmpty(splitRecord.OriginalBarcode))
                        {
                            var originalStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                                .FirstAsync(x => x.Barcode == splitRecord.OriginalBarcode && x.StockId == status.StockId);
                            if (originalStock != null && originalStock.StockQuantity > 0)
                            {
                                returnBarcodes.Add(splitRecord.OriginalBarcode);
                                _logger.LogInformation($"添加拆包原条码: {splitRecord.OriginalBarcode}, æ•°é‡: {originalStock.StockQuantity}");
                            }
                        }
                        // æ·»åŠ æ–°æ¡ç 
                        if (!string.IsNullOrEmpty(splitRecord.NewBarcode))
                        {
                            var newStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                                .FirstAsync(x => x.Barcode == splitRecord.NewBarcode && x.StockId == status.StockId);
                            if (newStock != null && newStock.StockQuantity > 0)
                            {
                                returnBarcodes.Add(splitRecord.NewBarcode);
                                _logger.LogInformation($"添加拆包新条码: {splitRecord.NewBarcode}, æ•°é‡: {newStock.StockQuantity}");
                            }
                        }
                    }
                }
                _logger.LogInformation($"回库条码收集完成 - å…± {returnBarcodes.Count} ä¸ªæ¡ç : {string.Join(", ", returnBarcodes)}");
                return returnBarcodes.ToList();
            }
            catch (Exception ex)
            {
                _logger.LogError($"收集回库条码失败 - Error: {ex.Message}");
                return returnBarcodes.ToList();
            }
        }
        #endregion
        #region å›žåº“操作核心方法
        /// <summary>
        /// æ‰§è¡Œå›žåº“操作 - å¢žå¼ºç‰ˆæœ¬
        /// </summary>
        private async Task ExecuteReturnOperations(string orderNo, string palletCode, Dt_StockInfo stockInfo,
            Dt_Task task, PalletStatusAnalysis statusAnalysis)
        {
            _logger.LogInformation($"开始执行回库操作 - è®¢å•: {orderNo}, æ‰˜ç›˜: {palletCode}");
            //  å¤„理未分拣的锁定记录
            if (statusAnalysis.HasRemainingLocks)
            {
                _logger.LogInformation($"处理 {statusAnalysis.RemainingLocks.Count} æ¡æœªåˆ†æ‹£é”å®šè®°å½•");
                await HandleRemainingLocksReturn(statusAnalysis.RemainingLocks);
            }
            //处理托盘上的库存货物
            if (statusAnalysis.HasPalletStockGoods)
            {
                _logger.LogInformation($"处理 {statusAnalysis.PalletStockGoods.Count} ä¸ªåº“存货物");
                await HandlePalletStockGoodsReturn(statusAnalysis.PalletStockGoods, stockInfo.Id);
            }
            //处理拆包记录
            if (statusAnalysis.HasSplitRecords)
            {
                _logger.LogInformation($"处理 {statusAnalysis.SplitRecords.Count} æ¡æ‹†åŒ…记录");
                await HandleSplitRecordsReturn(statusAnalysis.SplitRecords, stockInfo.Id);
            }
            _logger.LogInformation($"回库操作完成 - æ€»å›žåº“数量: {statusAnalysis.TotalReturnQty}");
        }
        /// <summary>
        /// å¤„理未分拣的锁定记录回库
        /// </summary>
        /// <summary>
        /// å¤„理托盘上的库存货物回库
        /// </summary>
        private async Task HandlePalletStockGoodsReturn(List<Dt_StockInfoDetail> palletStockGoods, int stockId)
        {
            foreach (var stockDetail in palletStockGoods)
            {
                // åªå¤„理出库锁定状态的库存
                if (stockDetail.Status == (int)StockStatusEmun.出库锁定 && stockDetail.StockQuantity > 0)
                {
                    // æ¢å¤åº“存状态为可用状态
                    stockDetail.Status = (int)StockStatusEmun.入库完成;
                    await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
                    _logger.LogInformation($"恢复库存货物 - æ¡ç : {stockDetail.Barcode}, æ•°é‡: {stockDetail.StockQuantity}");
                }
            }
        }
        /// <summary>
        /// å¤„理拆包记录回库
        /// </summary>
        private async Task HandleSplitRecordsReturn(List<Dt_SplitPackageRecord> splitRecords, int stockId)
        {
            foreach (var splitRecord in splitRecords)
            {
                // å¤„理新条码
                var newLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .Where(x => x.CurrentBarcode == splitRecord.NewBarcode &&
                               x.Status == (int)OutLockStockStatusEnum.出库中)
                    .FirstAsync();
                if (newLockInfo != null)
                {
                    await HandleSingleLockReturn(newLockInfo);
                }
                // å¤„理原条码
                var originalLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .Where(x => x.CurrentBarcode == splitRecord.OriginalBarcode &&
                               x.Status == (int)OutLockStockStatusEnum.出库中)
                    .FirstAsync();
                if (originalLockInfo != null)
                {
                    await HandleSingleLockReturn(originalLockInfo);
                }
                // æ›´æ–°æ‹†åŒ…记录状态为已回库
                splitRecord.Status = (int)SplitPackageStatusEnum.已回库;
                await _splitPackageService.Db.Updateable(splitRecord).ExecuteCommandAsync();
            }
        }
        private async Task HandleSingleLockReturn(Dt_OutStockLockInfo lockInfo)
        {
            decimal returnQty = lockInfo.AssignQuantity - lockInfo.PickedQty;
            if (returnQty <= 0)
            {
                _logger.LogInformation($"跳过锁定记录 - é”å®šID: {lockInfo.Id}, å·²æ‹£é€‰å®Œæˆæˆ–无需回库");
                return;
            }
            _logger.LogInformation($"处理拆包相关锁定记录 - é”å®šID: {lockInfo.Id}, æ¡ç : {lockInfo.CurrentBarcode}, å›žåº“数量: {returnQty}");
            // æ¢å¤åº“å­˜
            var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .FirstAsync(x => x.Barcode == lockInfo.CurrentBarcode && x.StockId == lockInfo.StockId);
            if (stockDetail != null)
            {
                decimal originalStockQty = stockDetail.StockQuantity;
                decimal originalOutboundQty = stockDetail.OutboundQuantity;
                stockDetail.StockQuantity += returnQty;
                stockDetail.OutboundQuantity -= returnQty;
                if (stockDetail.OutboundQuantity < 0)
                {
                    stockDetail.OutboundQuantity = 0;
                }
                stockDetail.Status = (int)StockStatusEmun.入库完成;
                await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
                _logger.LogInformation($"恢复拆包相关库存 - æ¡ç : {stockDetail.Barcode}, " +
                                     $"库存数量: {originalStockQty} -> {stockDetail.StockQuantity}");
            }
            // æ›´æ–°é”å®šçŠ¶æ€
            lockInfo.Status = (int)OutLockStockStatusEnum.已回库;
            lockInfo.Operator = App.User.UserName;
            await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
            // å‡å°‘订单明细的分配数量
            if (lockInfo.OrderDetailId > 0)
            {
                var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                    .FirstAsync(x => x.Id == lockInfo.OrderDetailId);
                if (orderDetail != null)
                {
                    await ReduceOrderDetailAllocation(orderDetail, returnQty);
                }
            }
            _logger.LogInformation($"拆包相关锁定记录回库完成 - é”å®šID: {lockInfo.Id}, å›žåº“数量: {returnQty}");
        }
        /// <summary>
        /// é‡Šæ”¾æ‰€æœ‰é”å®šä»¥ä¾¿é‡æ–°åˆ†é…
        /// </summary>
        private async Task ReleaseAllLocksForReallocation(string orderNo, string palletCode, PalletStatusAnalysis statusAnalysis)
        {
            // æ›´æ–°è®¢å•明细的已分配数量
            if (statusAnalysis.HasRemainingLocks)
            {
                var orderDetailGroups = statusAnalysis.RemainingLocks.GroupBy(x => x.OrderDetailId);
                foreach (var group in orderDetailGroups)
                {
                    var orderDetailId = group.Key;
                    var returnedQty = group.Sum(x => x.AssignQuantity - x.PickedQty);
                    var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                        .FirstAsync(x => x.Id == orderDetailId);
                    if (orderDetail != null)
                    {
                        orderDetail.AllocatedQuantity -= returnedQty;
                        orderDetail.LockQuantity = orderDetail.AllocatedQuantity;
                        await UpdateBatchAllocateStatus(orderDetail);
                        await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync();
                        _logger.LogInformation($"更新订单明细 - OrderDetailId: {orderDetailId}, å‡å°‘分配数量: {returnedQty}");
                    }
                }
            }
        }
        /// <summary>
        /// æ”¶é›†éœ€è¦å›žåº“的条码(避免重复)
        /// </summary>
        private async Task<HashSet<string>> CollectBarcodesForReturn(string orderNo, string palletCode, int stockId)
        {
            var barcodes = new HashSet<string>();
            try
            {
                _logger.LogInformation($"开始收集回库条码 - è®¢å•: {orderNo}, æ‰˜ç›˜: {palletCode}, StockId: {stockId}");
                // 1. ä»Žé”å®šè®°å½•收集
                var lockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .Where(x => x.OrderNo == orderNo &&
                               x.PalletCode == palletCode &&
                               (x.Status == (int)OutLockStockStatusEnum.出库中
                              // || x.Status == (int)OutLockStockStatusEnum.出库锁定)
                              ))
                    .ToListAsync();
                foreach (var lockInfo in lockInfos)
                {
                    if (!string.IsNullOrEmpty(lockInfo.CurrentBarcode))
                    {
                        barcodes.Add(lockInfo.CurrentBarcode);
                        _logger.LogInformation($"从锁定记录添加条码: {lockInfo.CurrentBarcode}");
                    }
                }
                // 2. ä»Žåº“存明细收集(状态为出库锁定的)
                var stockDetails = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                    .Where(x => x.StockId == stockId &&
                               x.Status == (int)StockStatusEmun.出库锁定 &&
                               x.StockQuantity > 0)
                    .ToListAsync();
                foreach (var stockDetail in stockDetails)
                {
                    if (!barcodes.Contains(stockDetail.Barcode) && !string.IsNullOrEmpty(stockDetail.Barcode))
                    {
                        barcodes.Add(stockDetail.Barcode);
                        _logger.LogInformation($"从库存明细添加条码: {stockDetail.Barcode}, æ•°é‡: {stockDetail.StockQuantity}");
                    }
                }
                // 3. ä»Žæ‹†åŒ…记录收集
                var splitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
                    .Where(x => x.OrderNo == orderNo &&
                               x.PalletCode == palletCode &&
                               !x.IsReverted &&
                               x.Status != (int)SplitPackageStatusEnum.已拣选)
                    .ToListAsync();
                foreach (var splitRecord in splitRecords)
                {
                    // æ·»åŠ åŽŸæ¡ç 
                    if (!string.IsNullOrEmpty(splitRecord.OriginalBarcode) && !barcodes.Contains(splitRecord.OriginalBarcode))
                    {
                        barcodes.Add(splitRecord.OriginalBarcode);
                        _logger.LogInformation($"从拆包记录添加原条码: {splitRecord.OriginalBarcode}");
                    }
                    // æ·»åŠ æ–°æ¡ç 
                    if (!string.IsNullOrEmpty(splitRecord.NewBarcode) && !barcodes.Contains(splitRecord.NewBarcode))
                    {
                        barcodes.Add(splitRecord.NewBarcode);
                        _logger.LogInformation($"从拆包记录添加新条码: {splitRecord.NewBarcode}");
                    }
                }
                _logger.LogInformation($"条码收集完成 - å…± {barcodes.Count} ä¸ªæ¡ç : {string.Join(", ", barcodes)}");
                return barcodes;
            }
            catch (Exception ex)
            {
                _logger.LogError($"收集回库条码失败 - Error: {ex.Message}");
                return barcodes;
            }
        }
        /// <summary>
        /// ç»Ÿä¸€å¤„理条码回库(避免重复处理)
        /// </summary>
        private async Task ProcessBarcodeReturn(string barcode, int stockId, decimal returnQty, HashSet<string> processedBarcodes)
        {
            if (returnQty <= 0)
                return;
            // æ£€æŸ¥æ˜¯å¦å·²å¤„理过
            if (processedBarcodes.Contains(barcode))
            {
                _logger.LogInformation($"跳过已处理的条码: {barcode}");
                return;
            }
            // èŽ·å–åº“å­˜æ˜Žç»†
            var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .FirstAsync(x => x.Barcode == barcode && x.StockId == stockId);
            if (stockDetail == null)
            {
                _logger.LogWarning($"未找到条码对应的库存明细: {barcode}");
                return;
            }
            // è®°å½•原始值
            decimal originalStockQty = stockDetail.StockQuantity;
            decimal originalOutboundQty = stockDetail.OutboundQuantity;
            int originalStatus = stockDetail.Status;
            _logger.LogInformation($"处理条码回库 - {barcode}: åŽŸå§‹åº“å­˜={originalStockQty}, åŽŸå§‹å‡ºåº“={originalOutboundQty}, çŠ¶æ€={originalStatus}");
            // éªŒè¯æ•°æ®ä¸€è‡´æ€§
            if (originalOutboundQty < returnQty)
            {
                _logger.LogWarning($"出库数量小于回库数量,调整回库数量 - æ¡ç : {barcode}, å‡ºåº“数量: {originalOutboundQty}, å›žåº“数量: {returnQty}");
                returnQty = originalOutboundQty;
            }
            // æ›´æ–°åº“存:出库数量减少,库存数量增加
            stockDetail.OutboundQuantity -= returnQty;
            stockDetail.StockQuantity += returnQty;
            // ç¡®ä¿ä¸ä¼šå‡ºçŽ°è´Ÿæ•°
            if (stockDetail.OutboundQuantity < 0)
            {
                _logger.LogWarning($"出库数量出现负数,重置为0 - æ¡ç : {barcode}");
                stockDetail.OutboundQuantity = 0;
            }
            // æ›´æ–°çŠ¶æ€
            if (stockDetail.OutboundQuantity <= 0 && stockDetail.StockQuantity > 0)
            {
                stockDetail.Status = (int)StockStatusEmun.入库完成;
                _logger.LogInformation($"库存状态更新为入库完成 - æ¡ç : {barcode}");
            }
            else if (stockDetail.StockQuantity > 0)
            {
                stockDetail.Status = (int)StockStatusEmun.出库锁定;
                _logger.LogInformation($"库存状态保持为出库锁定 - æ¡ç : {barcode}");
            }
            await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
            // æ ‡è®°ä¸ºå·²å¤„理
            processedBarcodes.Add(barcode);
            _logger.LogInformation($"条码回库完成 - {barcode}: " +
                                  $"库存 {originalStockQty} -> {stockDetail.StockQuantity}, " +
                                  $"出库 {originalOutboundQty} -> {stockDetail.OutboundQuantity}, " +
                                  $"状态 {originalStatus} -> {stockDetail.Status}");
        }
        /// <summary>
        /// å¤„理拆包记录回库 - é¿å…é‡å¤
        /// </summary>
        private async Task HandleSplitRecordsReturn(List<Dt_SplitPackageRecord> splitRecords, int stockId, HashSet<string> processedBarcodes)
        {
            if (!splitRecords.Any())
                return;
            _logger.LogInformation($"开始处理拆包记录回库 - å…± {splitRecords.Count} æ¡è®°å½•");
            foreach (var splitRecord in splitRecords)
            {
                // åªå¤„理未撤销的拆包记录
                if (splitRecord.IsReverted)
                {
                    _logger.LogInformation($"跳过已撤销的拆包记录 - ID: {splitRecord.Id}");
                    continue;
                }
                // å¤„理新条码
                if (!string.IsNullOrEmpty(splitRecord.NewBarcode) && !processedBarcodes.Contains(splitRecord.NewBarcode))
                {
                    var newStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                        .FirstAsync(x => x.Barcode == splitRecord.NewBarcode && x.StockId == stockId);
                    if (newStock != null && newStock.StockQuantity > 0)
                    {
                        // æ‹†åŒ…的新条码回库数量应该是其库存数量
                        await ProcessBarcodeReturn(splitRecord.NewBarcode, stockId, newStock.StockQuantity, processedBarcodes);
                    }
                }
                // å¤„理原条码
                if (!string.IsNullOrEmpty(splitRecord.OriginalBarcode) && !processedBarcodes.Contains(splitRecord.OriginalBarcode))
                {
                    var originalStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                        .FirstAsync(x => x.Barcode == splitRecord.OriginalBarcode && x.StockId == stockId);
                    if (originalStock != null && originalStock.StockQuantity > 0)
                    {
                        // åŽŸæ¡ç çš„å›žåº“æ•°é‡åº”è¯¥æ˜¯æ‹†åŒ…åŽå‰©ä½™çš„æ•°é‡
                        await ProcessBarcodeReturn(splitRecord.OriginalBarcode, stockId, originalStock.StockQuantity, processedBarcodes);
                    }
                }
                // æ›´æ–°æ‹†åŒ…记录状态为已回库
                splitRecord.Status = (int)SplitPackageStatusEnum.已回库;
                await _splitPackageService.Db.Updateable(splitRecord).ExecuteCommandAsync();
                _logger.LogInformation($"拆包记录状态更新为已回库 - è®°å½•ID: {splitRecord.Id}");
            }
            _logger.LogInformation($"拆包记录回库处理完成");
        }
        /// <summary>
        /// ç®€åŒ–版回库方法 - ç»•过复杂验证
        /// </summary>
        public async Task<WebResponseContent> SimplePalletReturn(string orderNo, string palletCode, string returnReason = "简化回库")
        {
            try
            {
                _logger.LogInformation($"【简化回库开始】订单: {orderNo}, æ‰˜ç›˜: {palletCode}");
                _unitOfWorkManage.BeginTran();
                // 1. èŽ·å–åº“å­˜ä¿¡æ¯ï¼ˆè·³è¿‡å¤æ‚éªŒè¯ï¼‰
                var stockInfo = await _stockInfoService.Db.Queryable<Dt_StockInfo>()
                    .FirstAsync(x => x.PalletCode == palletCode);
                if (stockInfo == null)
                {
                    _unitOfWorkManage.RollbackTran();
                    return WebResponseContent.Instance.Error($"未找到托盘 {palletCode} å¯¹åº”的库存信息");
                }
                // 2. ç›´æŽ¥æŸ¥æ‰¾éœ€è¦å›žåº“的条码(简化逻辑)
                var barcodesToReturn = await GetBarcodesForSimpleReturn(orderNo, palletCode, stockInfo.Id);
                if (!barcodesToReturn.Any())
                {
                    try
                    {
                        _logger.LogInformation($"【无回库物品】处理空托盘");
                        var result = await HandleEmptyPalletReturn(orderNo, palletCode, stockInfo);
                        _unitOfWorkManage.CommitTran();
                        return result;
                    }
                    catch (Exception ex)
                    {
                        _unitOfWorkManage.RollbackTran();
                        _logger.LogError($"空箱回库失败: {ex.Message}");
                        return WebResponseContent.Instance.Error($"空箱回库失败:{ex.Message}");
                    }
                }
                // 3. ç®€åŒ–处理每个条码
                foreach (var barcode in barcodesToReturn)
                {
                    await ProcessSimpleBarcodeReturn(barcode, stockInfo.Id);
                }
                // 4. æ›´æ–°è®¢å•状态(简化)
                await UpdateOrderStatusAfterReturn(orderNo);
                // 5. åˆ›å»ºå›žåº“任务
                await CreateReturnTask(orderNo, palletCode, stockInfo);
                _unitOfWorkManage.CommitTran();
                return WebResponseContent.Instance.OK($"简化回库成功,处理 {barcodesToReturn.Count} ä¸ªæ¡ç ");
            }
            catch (Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                _logger.LogError($"简化回库失败: {ex.Message}");
                return WebResponseContent.Instance.Error($"回库失败: {ex.Message}");
            }
        }
        /// <summary>
        /// ç®€åŒ–获取回库条码
        /// </summary>
        private async Task<List<string>> GetBarcodesForSimpleReturn(string orderNo, string palletCode, int stockId)
        {
            var barcodes = new List<string>();
            try
            {
                // 1. ä»Žé”å®šè®°å½•获取
                var lockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode)
                    .Select(x => x.CurrentBarcode)
                    .ToListAsync();
                barcodes.AddRange(lockInfos.Where(b => !string.IsNullOrEmpty(b)));
                // 2. ä»Žåº“存明细获取
                var stockDetails = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                    .Where(x => x.StockId == stockId && x.StockQuantity > 0)
                    .Select(x => x.Barcode)
                    .ToListAsync();
                barcodes.AddRange(stockDetails.Where(b => !string.IsNullOrEmpty(b)));
                // åŽ»é‡
                return barcodes.Distinct().ToList();
            }
            catch (Exception ex)
            {
                _logger.LogError($"获取回库条码失败: {ex.Message}");
                return barcodes;
            }
        }
        /// <summary>
        /// ç®€åŒ–处理条码回库
        /// </summary>
        private async Task ProcessSimpleBarcodeReturn(string barcode, int stockId)
        {
            try
            {
                // 1. èŽ·å–åº“å­˜æ˜Žç»†
                var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                    .FirstAsync(x => x.Barcode == barcode && x.StockId == stockId);
                if (stockDetail == null)
                {
                    _logger.LogWarning($"未找到条码对应的库存明细: {barcode}");
                    return;
                }
                // 2. å¦‚果是出库锁定状态,恢复为入库完成
                if (stockDetail.Status == (int)StockStatusEmun.出库锁定)
                {
                    stockDetail.Status = (int)StockStatusEmun.入库完成;
                    await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
                    _logger.LogInformation($"条码状态恢复 - {barcode}: å‡ºåº“锁定 -> å…¥åº“完成");
                }
                // 3. æ›´æ–°ç›¸å…³çš„锁定记录
                var lockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .Where(x => x.CurrentBarcode == barcode &&
                               (x.Status == (int)OutLockStockStatusEnum.出库中 ||
                                x.Status == (int)OutLockStockStatusEnum.拣选完成))
                    .ToListAsync();
                foreach (var lockInfo in lockInfos)
                {
                    lockInfo.Status = (int)OutLockStockStatusEnum.已回库;
                    lockInfo.Operator = App.User.UserName;
                    await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
                    _logger.LogInformation($"锁定记录状态更新 - ID: {lockInfo.Id}: å·²å›žåº“");
                }
            }
            catch (Exception ex)
            {
                _logger.LogError($"处理条码回库失败 - æ¡ç : {barcode}, Error: {ex.Message}");
            }
        }
        #endregion
        #region è¾…助方法
        /// <summary>
        /// å¤„理没有回库物品的情况
        /// </summary>
        private async Task<WebResponseContent> HandleNoReturnItems(string orderNo, string palletCode, Dt_Task originalTask, int stockId)
        {
            _logger.LogInformation($"托盘 {palletCode} æ²¡æœ‰éœ€è¦å›žåº“的物品");
            // æ£€æŸ¥æ˜¯å¦æ˜¯ç©ºæ‰˜ç›˜
            var statusAnalysis = await AnalyzePalletStatus(orderNo, palletCode, stockId);
            if (statusAnalysis.IsEmptyPallet)
            {
                try
                {
                    var locationtype = 0;
                    var stockInfo = await _stockInfoService.Db.Queryable<Dt_StockInfo>()
                            .Where(x => x.PalletCode == palletCode)
                            .FirstAsync();
                    if (stockInfo == null)
                    {
                        var firstLocation = await _locationInfoService.Db.Queryable<Dt_LocationInfo>().FirstAsync(x => x.LocationCode == originalTask.SourceAddress);
                        locationtype = firstLocation?.LocationType ?? 1;
                    }
                    else
                    {
                        locationtype = stockInfo.LocationType;
                        _stockInfoService.DeleteData(stockInfo);
                    }
                    var targetAddress = originalTask.TargetAddress;
                    await CleanupZeroStockData(stockId);
                    var emptystockInfo = new Dt_StockInfo() { PalletType = PalletTypeEnum.Empty.ObjToInt(), StockStatus = StockStatusEmun.组盘暂存.ObjToInt(), PalletCode = palletCode, LocationType = locationtype };
                    emptystockInfo.Details = new List<Dt_StockInfoDetail>();
                    _stockInfoService.AddMaterielGroup(emptystockInfo);
                    //空托盘如何处理  è¿˜æœ‰ä¸€ä¸ªå‡ºåº“任务要处理。
                    originalTask.PalletType = PalletTypeEnum.Empty.ObjToInt();
                    await CreateReturnTaskAndHandleESS(orderNo, palletCode, originalTask, TaskTypeEnum.InEmpty, PalletTypeEnum.Empty.ObjToInt());
                }
                catch (Exception ex)
                {
                    _logger.LogError($" HandleNoReturnItems  å¤±è´¥: {ex.Message}");
                    return WebResponseContent.Instance.Error($" å›žåº“空托盘失败!");
                }
                return WebResponseContent.Instance.OK("空托盘回库任务创建成功");
            }
            else
            {
                return WebResponseContent.Instance.Error("托盘状态异常:有物品但无法计算回库数量");
            }
        }
       #region è¾…助方法
        private async Task CleanupZeroStockData(int stockId)
        {
            try
            {
                // 1. åˆ é™¤åº“存数量为0的明细记录
                // åˆ é™¤åº“存数量为0的明细记录
                var deleteDetailCount = await _stockInfoDetailService.Db.Deleteable<Dt_StockInfoDetail>()
            .Where(x => x.StockId == stockId && x.StockQuantity == 0)
            .ExecuteCommandAsync();
@@ -5054,38 +3039,10 @@
            catch (Exception ex)
            {
                _logger.LogWarning($"清理零库存数据失败 - StockId: {stockId}, Error: {ex.Message}");
                // æ³¨æ„ï¼šæ¸…理失败不应该影响主流程
            }
        }
        /// <summary>
        /// æ›´æ–°è®¢å•状态(回库后)
        /// </summary>
        private async Task UpdateOrderStatusForReturn(string orderNo)
        {
            // æ£€æŸ¥è®¢å•是否所有托盘都已完成或已回库
            var allLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(x => x.OrderNo == orderNo)
                .ToListAsync();
            var activeLocks = allLocks.Where(x =>
                x.Status == (int)OutLockStockStatusEnum.出库中 ||
                x.Status == (int)OutLockStockStatusEnum.回库中).ToList();
            // å¦‚果没有活跃的锁定记录,更新订单状态
            if (!activeLocks.Any())
            {
                await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
                    .SetColumns(x => new Dt_OutboundOrder
                    {
                        OrderStatus = (int)OutOrderStatusEnum.出库完成,
                    })
                    .Where(x => x.OrderNo == orderNo)
                    .ExecuteCommandAsync();
                _logger.LogInformation($"更新订单状态为出库完成 - è®¢å•: {orderNo}");
            }
        }
        #endregion
        #region éªŒè¯æ–¹æ³•
@@ -5235,18 +3192,18 @@
            try
            {
                // 1. éªŒè¯æ‹†åŒ…数量合理性
                //  éªŒè¯æ‹†åŒ…数量合理性
                if (splitQuantity <= 0)
                    throw new InvalidOperationException($"拆包数量必须大于0,当前值: {splitQuantity}");
                if (stockDetail.StockQuantity < lockInfo.AssignQuantity + splitQuantity)
                    throw new InvalidOperationException($"库存数量不足以进行自动拆包,库存: {stockDetail.StockQuantity}, éœ€è¦: {lockInfo.AssignQuantity + splitQuantity}");
                // 2. ç”Ÿæˆæ–°æ¡ç 
                //  ç”Ÿæˆæ–°æ¡ç 
                string newBarcode = await GenerateNewBarcode();
                _logger.LogInformation($"生成新条码: {newBarcode}");
                // 3. ã€æ ¸å¿ƒä¿®æ­£ã€‘更新原库存明细:只减少物理库存,不影响出库数量
                // ã€æ ¸å¿ƒä¿®æ­£ã€‘更新原库存明细:只减少物理库存,不影响出库数量
                decimal originalStockQty = stockDetail.StockQuantity;
                stockDetail.StockQuantity -= splitQuantity; // ä»…库存减少
                                                            // stockDetail.OutboundQuantity ä¿æŒä¸å˜ï¼
@@ -5254,7 +3211,7 @@
                await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
                _logger.LogInformation($"更新原库存明细:条码 {stockDetail.Barcode} åº“å­˜ {originalStockQty} -> {stockDetail.StockQuantity},出库数量不变({stockDetail.OutboundQuantity})");
                // 4. åˆ›å»ºæ–°åº“存明细(多余部分)- å‡ºåº“数量为0
                //  åˆ›å»ºæ–°åº“存明细(多余部分)- å‡ºåº“数量为0
                var newStockDetail = new Dt_StockInfoDetail
                {
                    StockId = stockDetail.StockId,
@@ -5262,7 +3219,7 @@
                    OrderNo = stockDetail.OrderNo,
                    BatchNo = stockDetail.BatchNo,
                    StockQuantity = splitQuantity, // æ–°åº“存数量
                    OutboundQuantity = 0, // ã€é‡ç‚¹ã€‘初始出库数量为0
                    OutboundQuantity = 0, // åˆå§‹å‡ºåº“数量为0
                    Barcode = newBarcode,
                    Status = (int)StockStatusEmun.出库锁定, // ä»ä¸ºé”å®šçŠ¶æ€ï¼Œä½†æœªç»‘å®šè®¢å•
                    SupplyCode = stockDetail.SupplyCode,
@@ -5276,11 +3233,11 @@
                await _stockInfoDetailService.Db.Insertable(newStockDetail).ExecuteCommandAsync();
                _logger.LogInformation($"创建新库存明细:条码 {newBarcode},库存 {splitQuantity},出库 0");
                // 5. åˆ›å»ºæ–°é”å®šä¿¡æ¯ - æ ‡è®°ä¸ºæœªåˆ†é…
                //  åˆ›å»ºæ–°é”å®šä¿¡æ¯ - æ ‡è®°ä¸ºæœªåˆ†é…
                var newLockInfo = new Dt_OutStockLockInfo
                {
                    OrderNo = lockInfo.OrderNo,
                    OrderDetailId = 0, // ã€é‡ç‚¹ã€‘不绑定到具体订单明细,表示未分配
                    OrderDetailId = 0, // ä¸ç»‘定到具体订单明细,表示未分配
                    OutboundBatchNo = lockInfo.OutboundBatchNo,
                    MaterielCode = lockInfo.MaterielCode,
                    MaterielName = lockInfo.MaterielName,
@@ -5304,13 +3261,13 @@
                    WarehouseCode = lockInfo.WarehouseCode,
                    BarcodeQty = lockInfo.BarcodeQty,
                    BarcodeUnit = lockInfo.BarcodeUnit,
                    IsUnallocated = 1 // ã€é‡ç‚¹ã€‘明确标记为"未分配"的锁定记录
                    IsUnallocated = 1 // æ˜Žç¡®æ ‡è®°ä¸º"未分配"的锁定记录
                };
                await _outStockLockInfoService.Db.Insertable(newLockInfo).ExecuteCommandAsync();
                _logger.LogInformation($"创建未分配锁定记录:ID {newLockInfo.Id},条码 {newBarcode},数量 {splitQuantity}");
                // 6. ã€å…³é”®ä¿®æ­£ã€‘原锁定记录和原订单明细数据完全保持不变!
                //原锁定记录和原订单明细数据完全保持不变!
                // - ä¸ä¿®æ”¹ lockInfo çš„任何字段
                // - ä¸ä¿®æ”¹å…³è”çš„ Dt_OutboundOrderDetail çš„ AllocatedQuantity å’Œ LockQuantity
@@ -5329,64 +3286,6 @@
                _logger.LogError($"自动拆包逻辑执行失败 - åŽŸæ¡ç : {stockDetail.Barcode}, Error: {ex.Message}");
                throw;
            }
        }
        /// <summary>
        /// éªŒè¯è‡ªåŠ¨æ‹†åŒ…åŽæ•°æ®ä¸€è‡´æ€§ - ä¿®æ­£ç‰ˆ
        /// åŽŸåˆ™ï¼šéªŒè¯æœªåˆ†é…é”å®šè®°å½•çš„åˆ›å»ºï¼Œè€Œä¸æ˜¯è®¢å•æ˜Žç»†æ•°é‡çš„å˜åŒ–
        /// </summary>
        private async Task ValidateDataConsistencyAfterAutoSplit(long orderDetailId, decimal originalAllocatedQty, decimal originalLockQty, decimal splitQuantity)
        {
            // é‡æ–°èŽ·å–è®¢å•æ˜Žç»†æ•°æ®
            var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                .FirstAsync(x => x.Id == orderDetailId);
            if (orderDetail == null)
                return;
            // ã€ä¿®æ­£ã€‘自动拆包后,订单明细分配数量应该保持不变!
            if (Math.Abs(orderDetail.AllocatedQuantity - originalAllocatedQty) > 0.01m)
            {
                _logger.LogError($"自动拆包后订单明细分配数量异常变化!期望保持不变: {originalAllocatedQty}, å®žé™…: {orderDetail.AllocatedQuantity}");
                // è®°å½•严重错误,但不抛出异常(生产环境可能需要告警)
            }
            if (Math.Abs(orderDetail.LockQuantity - originalLockQty) > 0.01m)
            {
                _logger.LogError($"自动拆包后订单明细锁定数量异常变化!期望保持不变: {originalLockQty}, å®žé™…: {orderDetail.LockQuantity}");
            }
            // éªŒè¯æœªåˆ†é…é”å®šè®°å½•的创建
            // æŸ¥æ‰¾çˆ¶é”å®šè®°å½•
            var parentLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .FirstAsync(x => x.OrderDetailId == orderDetailId);
            if (parentLockInfo != null)
            {
                // æŸ¥æ‰¾æœªåˆ†é…çš„子锁定记录(自动拆包生成的)
                var unallocatedChildLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .Where(x => x.ParentLockId == parentLockInfo.Id &&
                               x.IsUnallocated == 1 &&
                               x.OrderDetailId == 0)
                    .ToListAsync();
                if (unallocatedChildLocks.Any())
                {
                    decimal totalUnallocatedQty = unallocatedChildLocks.Sum(x => x.AssignQuantity);
                    _logger.LogInformation($"验证通过:创建了{unallocatedChildLocks.Count}条未分配锁定记录,总数量: {totalUnallocatedQty}");
                    if (Math.Abs(totalUnallocatedQty - splitQuantity) > 0.01m)
                    {
                        _logger.LogWarning($"未分配锁定记录总数量与拆包数量不匹配,拆包数量: {splitQuantity}, æœªåˆ†é…æ€»æ•°: {totalUnallocatedQty}");
                    }
                }
                else
                {
                    _logger.LogWarning($"未找到自动拆包生成的未分配锁定记录");
                }
            }
            _logger.LogInformation($"自动拆包数据一致性验证完成");
        }
        #endregion
@@ -5414,7 +3313,7 @@
            decimal originalOutboundQty = stockDetail.OutboundQuantity;
            int originalStatus = stockDetail.Status;
            // ã€æ ¸å¿ƒä¿®æ­£ã€‘确保OutboundQuantity只增加本次拣选数量,不包含其他
            // ç¡®ä¿OutboundQuantity只增加本次拣选数量,不包含其他
            stockDetail.StockQuantity -= actualPickedQty;
            stockDetail.OutboundQuantity += actualPickedQty;  // åªå¢žåŠ æœ¬æ¬¡æ‹£é€‰æ•°é‡
@@ -5475,7 +3374,7 @@
        {
            _logger.LogInformation($"验证拣选数据一致性 - æ¡ç : {stockDetail.Barcode}");
            // 1. éªŒè¯åº“存明细的OutboundQuantity增加量等于拣选数量
            //  éªŒè¯åº“存明细的OutboundQuantity增加量等于拣选数量
            var refreshedStockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .FirstAsync(x => x.Id == stockDetail.Id);
@@ -5484,13 +3383,13 @@
            if (Math.Abs(outboundIncrease - pickedQty) > 0.01m)
            {
                _logger.LogError($"拣选数据不一致:出库数量增加 {outboundIncrease},但拣选数量是 {pickedQty}");
                // ä¿®å¤ï¼šç¡®ä¿OutboundQuantity正确
                // ç¡®ä¿OutboundQuantity正确
                refreshedStockDetail.OutboundQuantity = stockDetail.OutboundQuantity + pickedQty;
                await _stockInfoDetailService.Db.Updateable(refreshedStockDetail).ExecuteCommandAsync();
                _logger.LogWarning($"已修复出库数量:{stockDetail.OutboundQuantity} -> {refreshedStockDetail.OutboundQuantity}");
            }
            // 2. éªŒè¯é”å®šè®°å½•的已拣选数量
            // éªŒè¯é”å®šè®°å½•的已拣选数量
            var refreshedLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .FirstAsync(x => x.Id == lockInfo.Id);
@@ -5506,7 +3405,7 @@
        {
            _logger.LogInformation($"开始恢复拣选数据 - æ‹£é€‰è®°å½•ID: {pickingRecord.Id}, æ¡ç : {pickingRecord.Barcode}, æ‹£é€‰æ•°é‡: {pickingRecord.PickQuantity}");
            // 1. æ¢å¤é”å®šä¿¡æ¯
            // æ¢å¤é”å®šä¿¡æ¯
            var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .FirstAsync(x => x.Id == pickingRecord.OutStockLockId);
@@ -5545,7 +3444,7 @@
            await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
            // 2. æ¢å¤åº“存信息
            // æ¢å¤åº“存信息
            var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .FirstAsync(x => x.Barcode == pickingRecord.Barcode);
@@ -5738,7 +3637,7 @@
                await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync();
            }
            // 3. é‡æ–°æ£€æŸ¥è®¢å•状态
            // é‡æ–°æ£€æŸ¥è®¢å•状态
            await CheckAndUpdateOrderStatus(pickingRecord.OrderNo);
            _logger.LogInformation($"恢复批次和订单数据完成");
@@ -5759,136 +3658,9 @@
                orderDetail.BatchAllocateStatus = OrderDetailStatusEnum.New.ObjToInt();
            }
        }
        private async Task ReleaseLockAndStock(Dt_OutStockLockInfo lockInfo)
        {
            // æ¢å¤åº“存状态 - å›žåº“后库存变为可用状态
            var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .FirstAsync(x => x.Barcode == lockInfo.CurrentBarcode && x.StockId == lockInfo.StockId);
            if (stockDetail != null)
            {
                // å›žåº“后库存状态恢复为入库完成(可用状态)
                stockDetail.Status = (int)StockStatusEmun.入库完成;
                await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
            }
            // æ›´æ–°é”å®šè®°å½•状态为已回库
            lockInfo.Status = (int)OutLockStockStatusEnum.已回库;
            lockInfo.Operator = App.User.UserName;
            await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
        }
        /// <summary>
        /// æ›´æ–°æ‰¹æ¬¡çŠ¶æ€ï¼ˆå›žåº“ï¼‰
        /// </summary>
        private async Task UpdateBatchStatusForReturn(string outboundBatchNo, List<Dt_OutStockLockInfo> returnedLocks)
        {
            var batch = await _outboundBatchRepository.Db.Queryable<Dt_OutboundBatch>()
                .FirstAsync(x => x.BatchNo == outboundBatchNo);
            if (batch != null)
            {
                // è®¡ç®—回库数量(未拣选的部分)
                var returnedQty = returnedLocks.Sum(x => x.AssignQuantity - x.PickedQty);
                batch.CompletedQuantity -= returnedQty;
                // æ›´æ–°æ‰¹æ¬¡çŠ¶æ€
                if (batch.CompletedQuantity <= 0)
                {
                    batch.BatchStatus = (int)BatchStatusEnum.已回库;
                }
                else if (batch.CompletedQuantity < batch.BatchQuantity)
                {
                    batch.BatchStatus = (int)BatchStatusEnum.执行中;
                }
                else
                {
                    batch.BatchStatus = (int)BatchStatusEnum.已完成;
                }
                batch.Operator = App.User.UserName;
                await _outboundBatchRepository.Db.Updateable(batch).ExecuteCommandAsync();
            }
        }
        /// <summary>
        /// æ›´æ–°è®¢å•明细(回库后)
        /// </summary>
        private async Task UpdateOrderDetailAfterReturn(List<Dt_OutStockLockInfo> returnedLocks)
        {
            var orderDetailGroups = returnedLocks.GroupBy(x => x.OrderDetailId);
            foreach (var group in orderDetailGroups)
            {
                var orderDetailId = group.Key;
                var returnedQty = group.Sum(x => x.AssignQuantity - x.PickedQty);
                var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                    .FirstAsync(x => x.Id == orderDetailId);
                if (orderDetail != null)
                {
                    orderDetail.AllocatedQuantity -= returnedQty;
                    // LockQuantity åŒæ­¥å‡å°‘,保持与已分配数量一致
                    orderDetail.LockQuantity = orderDetail.AllocatedQuantity;
                    await UpdateBatchAllocateStatus(orderDetail);
                    await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync();
                }
            }
        }
        #endregion
        private async Task CreateReturnTaskAndHandleESS(string orderNo, string palletCode, Dt_Task originalTask, TaskTypeEnum taskTypeEnum, int palletType)
        {
            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,
                OrderNo = orderNo,
                Roadway = newLocation.RoadwayNo,
                SourceAddress = stations[originalTask.TargetAddress],
                TargetAddress = newLocation.LocationCode,
                TaskStatus = TaskStatusEnum.New.ObjToInt(),
                TaskType = taskTypeEnum.ObjToInt(),
                PalletType = palletType,
                WarehouseId = originalTask.WarehouseId
            };
            // ä¿å­˜å›žåº“任务
            var insertcount = await _taskRepository.Db.Insertable(returnTask).ExecuteCommandAsync();
            if (insertcount <= 0)
            {
                throw new Exception("创建任务失败!");
            }
            var targetAddress = originalTask.TargetAddress;
            _logger.LogInformation($"CreateReturnTaskAndHandleESS  åˆ†æ‰¹åˆ é™¤åŽ†å²ä»»åŠ¡: {orderNo} ï¼Œ {originalTask.TaskNum}");
            // åˆ é™¤åŽŸå§‹å‡ºåº“ä»»åŠ¡
            //_taskRepository.DeleteAndMoveIntoHty(originalTask, OperateTypeEnum.自动完成);
            var result = _task_HtyService.DeleteAndMoveIntoHty(originalTask, OperateTypeEnum.人工删除);
            await _taskRepository.Db.Deleteable(originalTask).ExecuteCommandAsync();
            if (!result)
            {
                await _taskRepository.Db.Deleteable(originalTask).ExecuteCommandAsync();
            }
            _logger.LogInformation($"CreateReturnTaskAndHandleESS  åˆ†æ‰¹åˆ é™¤åŽ†å²ä»»åŠ¡: {orderNo} ï¼Œ {originalTask.TaskNum},影响行  {result}");
            // ç»™ ESS å‘送流动信号和创建任务
            await SendESSCommands(palletCode, targetAddress, returnTask);
        }
        /// <summary>
        /// ç»™ESS下任务
        /// </summary>
@@ -5901,16 +3673,14 @@
        {
            try
            {
                // 1. å‘送流动信号
                // å‘送流动信号
                var moveResult = await _eSSApiService.MoveContainerAsync(new WIDESEA_DTO.Basic.MoveContainerRequest
                {
                    slotCode = movestations[targetAddress],
                    containerCode = palletCode
                });
                //if (moveResult)
                //{
                // 2. åˆ›å»ºå›žåº“任务
                var essTask = new TaskModel()
                {
                    taskType = "putaway",
@@ -5934,7 +3704,7 @@
                var resultTask = await _eSSApiService.CreateTaskAsync(essTask);
                _logger.LogInformation($"ReturnRemaining åˆ›å»ºä»»åŠ¡æˆåŠŸ: {resultTask}");
                //}
            }
            catch (Exception ex)
            {
@@ -6032,26 +3802,31 @@
        #endregion
        #region DTOç±»
        /// <summary>
        /// æ¡ç çŠ¶æ€ä¿¡æ¯DTO
        /// å›žåº“任务信息类
        /// </summary>
        public class BarcodeStatusInfoDto
        public class ReturnTaskInfo
        {
            public string Barcode { get; set; }
            public string OrderNo { get; set; }
            public bool IsOriginalBarcode { get; set; }
            public int SplitChainCount { get; set; }
            public bool HasBeenPicked { get; set; }
            public decimal TotalPickedQuantity { get; set; }
            public int PickRecordCount { get; set; }
            public int LockInfoStatus { get; set; }
            public decimal LockInfoPickedQty { get; set; }
            public decimal LockInfoAssignQty { get; set; }
            public decimal StockQuantity { get; set; }
            public int StockStatus { get; set; }
            public bool CanCancelSplit { get; set; }
            public bool NeedCancelPickFirst { get; set; }
            public List<string> OperationSuggestions { get; set; } = new List<string>();
            /// <summary>
            /// æ˜¯å¦éœ€è¦å‘送ESS命令
            /// </summary>
            public bool ShouldSendESS { get; set; }
            /// <summary>
            /// æ‰˜ç›˜ç 
            /// </summary>
            public string PalletCode { get; set; }
            /// <summary>
            /// åŽŸå§‹ä»»åŠ¡çš„ç›®æ ‡åœ°å€
            /// </summary>
            public string OriginalTaskTargetAddress { get; set; }
            /// <summary>
            /// å›žåº“任务
            /// </summary>
            public Dt_Task ReturnTask { get; set; }
        }
        public class PickedBarcodeInfo
        {
@@ -6059,14 +3834,7 @@
            public decimal PickedQty { get; set; }
            public int PickRecordCount { get; set; }
        }
        /// <summary>
        /// è‡ªåŠ¨æ‹†åŒ…ç»“æžœ
        /// </summary>
        public class AutoSplitResult
        {
            public string NewBarcode { get; set; }
            public decimal SplitQuantity { get; set; }
        }
        public class PickingResult
        {
@@ -6078,11 +3846,6 @@
        {
            public Dt_OutStockLockInfo LockInfo { get; set; }
            public Dt_StockInfoDetail StockDetail { get; set; }
        }
        public class SplitResultDto
        {
            public string NewBarcode { get; set; }
        }
        public class ValidationResult<T>