pan
2025-11-20 246622a6e9c2563bd21d627c21c6012017f0f04e
提交
已修改7个文件
1408 ■■■■ 文件已修改
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_Common/StockEnum/OutLockStockStatusEnum.cs 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_DTO/Outbound/OutboundOrderGetDTO.cs 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_Model/Models/Outbound/Dt_PickingRecord.cs 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundPickingService.cs 866 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_OutboundService/SplitPackageService.cs 476 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_StockService/StockInfoService.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Common/StockEnum/OutLockStockStatusEnum.cs
@@ -19,8 +19,9 @@
    public enum SplitPackageStatusEnum
    {
        å·²æ‹†åŒ… = 1,
        å·²æ‹£é€‰ = 2,
        å·²å›žåº“ = 3
        å·²æ’¤é”€ = 2,
        å·²æ‹£é€‰ = 3,
        å·²å›žåº“ = 4
    }
    public enum OutLockStockStatusEnum
    {
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_DTO/Outbound/OutboundOrderGetDTO.cs
@@ -15,7 +15,23 @@
        public int pageNo { get; set; }
    }
    // æ‹†åŒ…链DTO
    public class SplitPackageChainDto
    {
        public int Id { get; set; }
        public DateTime SplitTime { get; set; }
        public string Operator { get; set; }
        public string OriginalBarcode { get; set; }
        public string NewBarcode { get; set; }
        public decimal SplitQty { get; set; }
        public decimal RemainQuantity { get; set; }
        public bool IsReverted { get; set; }
        public DateTime? RevertTime { get; set; }
        public int? PreviousSplitRecordId { get; set; }
        public int NewLockInfoId { get; set; }
        public int Status { get; set; }
    }
    // æ‹†åŒ…请求
    public class SplitPackageRequest
    {
@@ -251,10 +267,20 @@
        public string PalletCode { get; set; }
        public string OriginalBarcode { get; set; }
        public decimal SplitQuantity { get; set; }
        public string MaterielCode { get; set; }
    }
    public class RevertSplitDto
    {
        public string OriginalBarcode { get; set; }
    }
    // æ‹†åŒ…结果类
    public class SplitResult
    {
        public string OriginalBarcode { get; set; }
        public string NewBarcode { get; set; }
        public decimal SplitQuantity { get; set; }
        public decimal RemainQuantity { get; set; }
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Model/Models/Outbound/Dt_PickingRecord.cs
@@ -104,6 +104,16 @@
        public DateTime SplitTime { get; set; } = DateTime.Now;
        public string Operator { get; set; } // æ“ä½œäºº
        public int Status { get; set; } // çŠ¶æ€ï¼š1-已拆包 2-已拣选 3-已回库
        public DateTime RevertTime { get; set; }
        public int PreviousSplitRecordId { get; set; }
        [SugarColumn(IsNullable = true)]
        public decimal? OriginalStockQuantity { get; set; }
       public decimal StockBeforeSplit { get; set; }
        public decimal AssignBeforeSplit { get; set; }
    }
 
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundPickingService.cs
@@ -45,6 +45,7 @@
        private readonly IRepository<Dt_Task> _taskRepository;
        private readonly IESSApiService _eSSApiService;
        private readonly IInvokeMESService _invokeMESService;
        private readonly IDailySequenceService _dailySequenceService;
        private readonly ILogger<OutboundPickingService> _logger;
@@ -62,7 +63,10 @@
        };
        public OutboundPickingService(IRepository<Dt_PickingRecord> BaseDal, IUnitOfWorkManage unitOfWorkManage, IStockInfoService stockInfoService, IStockService stockService, IOutStockLockInfoService outStockLockInfoService, IStockInfoDetailService stockInfoDetailService, ILocationInfoService locationInfoService, IOutboundOrderDetailService outboundOrderDetailService, ISplitPackageService splitPackageService, IOutboundOrderService outboundOrderService, IRepository<Dt_Task> taskRepository, IESSApiService eSSApiService, ILogger<OutboundPickingService> logger, IInvokeMESService invokeMESService) : base(BaseDal)
        public OutboundPickingService(IRepository<Dt_PickingRecord> BaseDal, IUnitOfWorkManage unitOfWorkManage, IStockInfoService stockInfoService, IStockService stockService,
            IOutStockLockInfoService outStockLockInfoService, IStockInfoDetailService stockInfoDetailService, ILocationInfoService locationInfoService,
            IOutboundOrderDetailService outboundOrderDetailService, ISplitPackageService splitPackageService, IOutboundOrderService outboundOrderService,
            IRepository<Dt_Task> taskRepository, IESSApiService eSSApiService, ILogger<OutboundPickingService> logger, IInvokeMESService invokeMESService, IDailySequenceService dailySequenceService) : base(BaseDal)
        {
            _unitOfWorkManage = unitOfWorkManage;
            _stockInfoService = stockInfoService;
@@ -77,6 +81,7 @@
            _eSSApiService = eSSApiService;
            _logger = logger;
            _invokeMESService = invokeMESService;
            _dailySequenceService = dailySequenceService;
        }
@@ -141,117 +146,6 @@
            }
        }
        public async Task<WebResponseContent> ConfirmPicking(string orderNo, string palletCode, string barcode)
        {
            try
            {
                _unitOfWorkManage.BeginTran();
                var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                  .Where(it => it.OrderNo == orderNo &&
                             it.Status == (int)OutLockStockStatusEnum.出库中 &&
                             it.PalletCode == palletCode &&
                             it.CurrentBarcode == barcode)
                  .FirstAsync();
                if (lockInfo == null)
                {
                    var splitBarcode = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
                   .Where(it => it.NewBarcode == barcode && it.Status == 1)
                   .FirstAsync();
                    if (splitBarcode != null)
                    {
                        // é€šè¿‡æ‹†åŒ…条码记录找到对应的出库锁定记录
                        lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                            .Where(it => it.ParentLockId == splitBarcode.OutStockLockInfoId)
                            .FirstAsync();
                        if (lockInfo == null)
                            throw new Exception($"未找到拆包条码{barcode}对应的出库锁定记录");
                    }
                    else
                    {
                        throw new Exception($"条码{barcode}不属于托盘{palletCode}或不存在待分拣记录");
                    }
                }
                if (lockInfo.PalletCode != palletCode)
                    throw new Exception($"条码{barcode}不属于托盘{palletCode}");
                var outorderdetail = _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>().First(x => x.Id == lockInfo.OrderDetailId);
                if (outorderdetail != null && lockInfo.AssignQuantity > outorderdetail.OrderQuantity)
                {
                    throw new Exception($"条码{barcode}的出库数量大于订单的数量");
                }
                var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                        .Where(x => x.Barcode == barcode && x.StockId == lockInfo.StockId)
                        .FirstAsync();
                if (stockDetail == null)
                    return WebResponseContent.Instance.Error("无效的条码或物料编码");
                decimal actualQty = lockInfo.AssignQuantity - lockInfo.PickedQty;
                // 4. æ›´æ–°åº“å­˜
                stockDetail.StockQuantity -= actualQty;
                stockDetail.OutboundQuantity -= actualQty;
                await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
                lockInfo.PickedQty += actualQty;
                lockInfo.Status = (int)OutLockStockStatusEnum.拣选完成;
                await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
                var splitBarcodeRecord = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
               .Where(it => it.NewBarcode == barcode)
               .FirstAsync();
                if (splitBarcodeRecord != null)
                {
                    splitBarcodeRecord.Status = 2;
                    await _splitPackageService.Db.Updateable(splitBarcodeRecord).ExecuteCommandAsync();
                }
                await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
               .SetColumns(it => it.PickedQty == it.PickedQty + actualQty)
               .Where(it => it.Id == lockInfo.OrderDetailId)
               .ExecuteCommandAsync();
                await CheckAndUpdateOrderStatus(orderNo);
                //查询任务表
                var task = _taskRepository.QueryData(x => x.OrderNo == orderNo && x.PalletCode == palletCode).FirstOrDefault();
                // 9. è®°å½•拣选历史
                var pickingHistory = new Dt_PickingRecord
                {
                    FactoryArea = lockInfo.FactoryArea,
                    TaskNo = task?.TaskNum ?? 0,
                    LocationCode = task?.SourceAddress ?? "",
                    StockId = stockDetail.Id,
                    OrderNo = orderNo,
                    OrderDetailId = lockInfo.OrderDetailId,
                    PalletCode = palletCode,
                    Barcode = barcode,
                    MaterielCode = lockInfo.MaterielCode,
                    PickQuantity = lockInfo.AssignQuantity,
                    PickTime = DateTime.Now,
                    Operator = App.User.UserName,
                    OutStockLockId = lockInfo.Id
                };
                await Db.Insertable(pickingHistory).ExecuteCommandAsync();
                _unitOfWorkManage.CommitTran();
                return WebResponseContent.Instance.OK("拣选确认成功");
            }
            catch (Exception ex)
            {
                return WebResponseContent.Instance.Error($"拣选确认失败:{ex.Message}");
            }
        }
        // æ£€æŸ¥å¹¶æ›´æ–°è®¢å•状态
        private async Task CheckAndUpdateOrderStatus(string orderNo)
        {
@@ -340,14 +234,231 @@
                        }
                    }
                }
                catch (Exception ex) {
                catch (Exception ex)
                {
                    _logger.LogError(" OutboundPickingService  FeedbackOutbound : " + ex.Message);
                }
            }
        }
        public async Task<WebResponseContent> ConfirmPicking(string orderNo, string palletCode, string barcode)
        {
            try
            {
                _unitOfWorkManage.BeginTran();
                // 1. æŸ¥æ‰¾å‡ºåº“锁定信息
                var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .Where(it => it.OrderNo == orderNo &&
                               it.Status == (int)OutLockStockStatusEnum.出库中 &&
                               it.PalletCode == palletCode &&
                               it.CurrentBarcode == barcode)
                    .FirstAsync();
                if (lockInfo == null)
                {
                    lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
    .Where(it => it.CurrentBarcode == barcode &&
               it.Status == (int)OutLockStockStatusEnum.出库中)
    .FirstAsync();
                    if (lockInfo == null)
                        throw new Exception($"条码{barcode}不属于托盘{palletCode}或不存在待分拣记录");
                }
                if (lockInfo.PalletCode != palletCode)
                    throw new Exception($"条码{barcode}不属于托盘{palletCode}");
                var outorderdetail = _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>().First(x => x.Id == lockInfo.OrderDetailId);
                if (outorderdetail != null && lockInfo.AssignQuantity > outorderdetail.OrderQuantity)
                {
                    throw new Exception($"条码{barcode}的出库数量大于订单的数量");
                }
                var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                        .Where(x => x.Barcode == barcode && x.StockId == lockInfo.StockId)
                        .FirstAsync();
                if (stockDetail == null)
                    return WebResponseContent.Instance.Error("无效的条码或物料编码");
                decimal actualQty = lockInfo.AssignQuantity - lockInfo.PickedQty;
                decimal stockQuantity = stockDetail.StockQuantity;
                List<SplitResult> splitResults = new List<SplitResult>();
                if (actualQty < stockQuantity)
                {
                    // æƒ…况1: åˆ†é…æ•°é‡å°äºŽåº“存数量,需要自动拆包
                    // è®¡ç®—剩余库存数量
                    decimal remainingStockQty = stockQuantity - actualQty;
                    // æ›´æ–°åŽŸæ¡ç åº“å­˜ä¸ºå‰©ä½™æ•°é‡
                    stockDetail.StockQuantity = remainingStockQty;
                    stockDetail.OutboundQuantity = remainingStockQty;
                    await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
                    // ç”Ÿæˆæ–°æ¡ç ç”¨äºŽè®°å½•拣选数量(但不创建库存记录)
                    var seq = await _dailySequenceService.GetNextSequenceAsync();
                    string newBarcode = "WSLOT" + DateTime.Now.ToString("yyyyMMdd") + seq.ToString()?.PadLeft(5, '0');
                    // ä¸ºæ–°æ¡ç åˆ›å»ºå‡ºåº“锁定信息(用于记录拣选)
                    var newLockInfo = new Dt_OutStockLockInfo
                    {
                        OrderNo = lockInfo.OrderNo,
                        OrderDetailId = lockInfo.OrderDetailId,
                        BatchNo = lockInfo.BatchNo,
                        MaterielCode = lockInfo.MaterielCode,
                        MaterielName = lockInfo.MaterielName,
                        StockId = lockInfo.StockId,
                        OrderQuantity = actualQty,
                        OriginalQuantity = actualQty,
                        AssignQuantity = actualQty,
                        PickedQty = actualQty,
                        LocationCode = lockInfo.LocationCode,
                        PalletCode = lockInfo.PalletCode,
                        TaskNum = lockInfo.TaskNum,
                        Status = (int)OutLockStockStatusEnum.拣选完成,
                        Unit = lockInfo.Unit,
                        SupplyCode = lockInfo.SupplyCode,
                        OrderType = lockInfo.OrderType,
                        CurrentBarcode = newBarcode,
                        OriginalLockQuantity = actualQty,
                        IsSplitted = 1,
                        ParentLockId = lockInfo.Id
                    };
                    await _outStockLockInfoService.Db.Insertable(newLockInfo).ExecuteCommandAsync();
                    // è®°å½•拆包历史(用于追踪)
                    var splitHistory = new Dt_SplitPackageRecord
                    {
                        FactoryArea = lockInfo.FactoryArea,
                        TaskNum = lockInfo.TaskNum,
                        OutStockLockInfoId = lockInfo.Id,
                        StockId = stockDetail.StockId,
                        Operator = App.User.UserName,
                        IsReverted = false,
                        OriginalBarcode = barcode,
                        NewBarcode = newBarcode,
                        SplitQty = actualQty,
                        RemainQuantity = remainingStockQty,
                        MaterielCode = lockInfo.MaterielCode,
                        SplitTime = DateTime.Now,
                        OrderNo = lockInfo.OrderNo,
                        PalletCode = lockInfo.PalletCode,
                        Status = (int)SplitPackageStatusEnum.已拣选
                    };
                    await _splitPackageService.Db.Insertable(splitHistory).ExecuteCommandAsync();
                    // æ›´æ–°åŽŸé”å®šä¿¡æ¯ä¸ºå‰©ä½™åº“å­˜æ•°é‡
                    lockInfo.AssignQuantity = remainingStockQty;
                    lockInfo.PickedQty = 0;
                    await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
                    splitResults.Add(new SplitResult
                    {
                        OriginalBarcode = barcode,
                        NewBarcode = newBarcode,
                        SplitQuantity = actualQty,
                        RemainQuantity = remainingStockQty
                    });
                    // æ›´æ–°æ‹£é€‰è®°å½•中的条码为新条码
                    barcode = newBarcode;
                    lockInfo = newLockInfo;
                }
                else if (actualQty == stockQuantity)
                {
                    // æƒ…况2: åˆ†é…æ•°é‡ç­‰äºŽåº“存数量,整包出库
                    stockDetail.StockQuantity = 0;
                    stockDetail.OutboundQuantity = 0;
                    await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
                    lockInfo.PickedQty += actualQty;
                    lockInfo.Status = (int)OutLockStockStatusEnum.拣选完成;
                    await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
                }
                else
                {
                    // æƒ…况3: åˆ†é…æ•°é‡å¤§äºŽåº“存数量,库存整包出库
                    // æ•´åŒ…出库当前库存
                    decimal stockOutQty = stockQuantity;
                    stockDetail.StockQuantity = 0;
                    stockDetail.OutboundQuantity = 0;
                    await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
                    // è®¡ç®—剩余分配数量
                    decimal remainingAssignQty = actualQty - stockQuantity;
                    // æ›´æ–°é”å®šä¿¡æ¯ï¼ˆåªå®Œæˆåº“存部分)
                    lockInfo.PickedQty += stockOutQty;
                    lockInfo.AssignQuantity = remainingAssignQty;
                    await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
                    var _relatedSplitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
        .Where(it => it.OriginalBarcode == barcode || it.NewBarcode == barcode)
        .Where(it => !it.IsReverted)
        .ToListAsync();
                    foreach (var record in _relatedSplitRecords)
                    {
                        record.Status = (int)SplitPackageStatusEnum.已拣选;
                        await _splitPackageService.Db.Updateable(record).ExecuteCommandAsync();
                    }
                }
                await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                    .SetColumns(it => it.PickedQty == it.PickedQty + actualQty)
                    .Where(it => it.Id == lockInfo.OrderDetailId)
                    .ExecuteCommandAsync();
                await CheckAndUpdateOrderStatus(orderNo);
                // æŸ¥è¯¢ä»»åŠ¡è¡¨
                var task = _taskRepository.QueryData(x => x.OrderNo == orderNo && x.PalletCode == palletCode).FirstOrDefault();
                // è®°å½•拣选历史
                var pickingHistory = new Dt_PickingRecord
                {
                    FactoryArea = lockInfo.FactoryArea,
                    TaskNo = task?.TaskNum ?? 0,
                    LocationCode = task?.SourceAddress ?? "",
                    StockId = stockDetail.Id,
                    OrderNo = orderNo,
                    OrderDetailId = lockInfo.OrderDetailId,
                    PalletCode = palletCode,
                    Barcode = barcode,
                    MaterielCode = lockInfo.MaterielCode,
                    PickQuantity = actualQty,
                    PickTime = DateTime.Now,
                    Operator = App.User.UserName,
                    OutStockLockId = lockInfo.Id
                };
                await Db.Insertable(pickingHistory).ExecuteCommandAsync();
                _unitOfWorkManage.CommitTran();
                // å¦‚果有拆包结果,返回拆包信息
                if (splitResults.Any())
                {
                    return WebResponseContent.Instance.OK("拣选确认成功,已自动拆包", new { SplitResults = splitResults });
                }
                return WebResponseContent.Instance.OK("拣选确认成功");
            }
            catch (Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                return WebResponseContent.Instance.Error($"拣选确认失败:{ex.Message}");
            }
        }
        /// <summary>
        /// å›žåº“操作  
        /// </summary>
@@ -356,13 +467,14 @@
        {
            try
            {
                //  èŽ·å–æ‰€æœ‰æœªåˆ†æ‹£çš„å‡ºåº“é”å®šè®°å½•ï¼ŒåŒ…æ‹¬æ‹†åŒ…äº§ç”Ÿçš„è®°å½•
                _unitOfWorkManage.BeginTran();
                // èŽ·å–æ‰€æœ‰æœªåˆ†æ‹£çš„å‡ºåº“é”å®šè®°å½•ï¼ŒåŒ…æ‹¬æ‹†åŒ…äº§ç”Ÿçš„è®°å½•
                var remainingLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .Where(it => it.OrderNo == orderNo && it.Status == 1)
                    .Where(it => it.OrderNo == orderNo && it.Status == (int)OutLockStockStatusEnum.出库中)
                    .ToListAsync();
                var stockinfo = _stockInfoService.Db.Queryable<Dt_StockInfo>().First(x => x.PalletCode == palletCode);
                var tasks = new List<Dt_Task>();
@@ -376,26 +488,68 @@
                    return WebResponseContent.Instance.Error("未找到对应的任务信息");
                }
                //  æ£€æŸ¥æ‰˜ç›˜ä¸Šæ˜¯å¦æœ‰å…¶ä»–非出库货物(库存货物)
                // æ£€æŸ¥æ‰˜ç›˜ä¸Šæ˜¯å¦æœ‰å…¶ä»–非出库货物(库存货物)
                var palletStockGoods = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                    .Where(it => it.StockId == stockinfo.Id && (it.Status == StockStatusEmun.入库确认.ObjToInt() || it.Status == StockStatusEmun.入库完成.ObjToInt() || it.Status == StockStatusEmun.出库锁定.ObjToInt()))
                    .Where(it => it.StockId == stockinfo.Id &&
                                (it.Status == StockStatusEmun.入库确认.ObjToInt() ||
                                 it.Status == StockStatusEmun.入库完成.ObjToInt() ||
                                 it.Status == StockStatusEmun.出库锁定.ObjToInt()))
                    .Where(it => it.OutboundQuantity == 0 || it.OutboundQuantity < it.StockQuantity) // æœªå®Œå…¨å‡ºåº“çš„
                    .ToListAsync();
                //  å¦‚果没有需要回库的货物(既无未分拣出库货物,也无其他库存货物)
                if (!remainingLocks.Any() && !palletStockGoods.Any())
                // æ£€æŸ¥æ‹†åŒ…记录,找出需要回库的条码
                var splitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
                    .Where(it => it.OrderNo == orderNo && it.PalletCode == palletCode && !it.IsReverted)
                    .ToListAsync();
                // è®¡ç®—需要回库的拆包条码
                var splitBarcodesToReturn = new List<string>();
                foreach (var splitRecord in splitRecords)
                {
                    //是否自动回库,把之前出库的任务删除,然后组个空盘入库。
                    return WebResponseContent.Instance.Error("没有需要回库的剩余货物");
                    // æ£€æŸ¥åŽŸæ¡ç æ˜¯å¦è¿˜æœ‰åº“å­˜éœ€è¦å›žåº“
                    var originalStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                        .Where(it => it.Barcode == splitRecord.OriginalBarcode && it.StockId == stockinfo.Id)
                        .FirstAsync();
                    if (originalStock != null && originalStock.StockQuantity > 0)
                    {
                        splitBarcodesToReturn.Add(splitRecord.OriginalBarcode);
                    }
                    // æ£€æŸ¥æ–°æ¡ç æ˜¯å¦è¿˜æœ‰åº“存需要回库
                    var newStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                        .Where(it => it.Barcode == splitRecord.NewBarcode && it.StockId == stockinfo.Id)
                        .FirstAsync();
                    if (newStock != null && newStock.StockQuantity > 0)
                    {
                        splitBarcodesToReturn.Add(splitRecord.NewBarcode);
                    }
                }
                // å¦‚果没有需要回库的货物(既无未分拣出库货物,也无其他库存货物,也无拆包剩余货物)
                if (!remainingLocks.Any() && !palletStockGoods.Any() && !splitBarcodesToReturn.Any())
                {
                    // æ£€æŸ¥æ˜¯å¦æ‰€æœ‰è´§ç‰©éƒ½å·²æ‹£é€‰å®Œæˆ
                    var allPicked = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                        .Where(it => it.OrderNo == orderNo && it.PalletCode == palletCode)
                        .AnyAsync(it => it.Status == (int)OutLockStockStatusEnum.拣选完成);
                    if (allPicked)
                    {
                        return WebResponseContent.Instance.OK("所有货物已拣选完成,托盘为空");
                    }
                    else
                    {
                        return WebResponseContent.Instance.Error("没有需要回库的剩余货物");
                    }
                }
                var firstlocation = _locationInfoService.Db.Queryable<Dt_LocationInfo>().First(x => x.LocationCode == task.SourceAddress);
                decimal totalReturnQty = 0;
                var hasRemainingLocks = remainingLocks.Any(x => x.PalletCode == palletCode);
                // æƒ…况1:处理未分拣的出库锁定记录
                if (hasRemainingLocks)
                if (remainingLocks.Any(x => x.PalletCode == palletCode))
                {
                    var palletLocks = remainingLocks.Where(x => x.PalletCode == palletCode).ToList();
                    totalReturnQty = palletLocks.Sum(x => x.AssignQuantity - x.PickedQty);
@@ -408,20 +562,11 @@
                        // æ›´æ–°å‡ºåº“锁定记录状态
                        var lockIds = palletLocks.Select(x => x.Id).ToList();
                        await _outStockLockInfoService.Db.Updateable<Dt_OutStockLockInfo>()
                            .SetColumns(it => new Dt_OutStockLockInfo { Status = OutLockStockStatusEnum.回库中.ObjToInt() })
                            .SetColumns(it => new Dt_OutStockLockInfo { Status = (int)OutLockStockStatusEnum.回库中 })
                            .Where(it => lockIds.Contains(it.Id))
                            .ExecuteCommandAsync();
                        // æ›´æ–°æ‹†åŒ…条码记录状态
                        var splitBarcodes = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
                            .Where(it => lockIds.Contains(it.OutStockLockInfoId))
                            .ToListAsync();
                        foreach (var splitBarcode in splitBarcodes)
                        {
                            splitBarcode.Status = 3;
                            await _splitPackageService.Db.Updateable(splitBarcode).ExecuteCommandAsync();
                        }
                        // å¤„理库存记录
                        foreach (var lockInfo in palletLocks)
@@ -436,13 +581,9 @@
                            if (existingStock != null)
                            {
                                // åº“存记录存在,恢复锁定数量
                                await _stockInfoDetailService.Db.Updateable<Dt_StockInfoDetail>()
                                    .SetColumns(it => new Dt_StockInfoDetail
                                    {
                                        OutboundQuantity = it.OutboundQuantity - returnQty
                                    })
                                    .Where(it => it.Barcode == lockInfo.CurrentBarcode && it.StockId == lockInfo.StockId)
                                    .ExecuteCommandAsync();
                                existingStock.OutboundQuantity = 0;
                                await _stockInfoDetailService.Db.Updateable(existingStock).ExecuteCommandAsync();
                            }
                            else
                            {
@@ -451,14 +592,17 @@
                                {
                                    StockId = lockInfo.StockId,
                                    MaterielCode = lockInfo.MaterielCode,
                                    MaterielName = lockInfo.MaterielName,
                                    OrderNo = lockInfo.OrderNo,
                                    BatchNo = lockInfo.BatchNo,
                                    StockQuantity = returnQty,
                                    OutboundQuantity = 0,
                                    Barcode = lockInfo.CurrentBarcode,
                                    InboundOrderRowNo = "0",
                                    Status = StockStatusEmun.入库确认.ObjToInt(),
                                    InboundOrderRowNo = "",
                                    Status = StockStatusEmun.入库完成.ObjToInt(),
                                    SupplyCode = lockInfo.SupplyCode,
                                    WarehouseCode = lockInfo.WarehouseCode,
                                    Unit = lockInfo.Unit
                                };
                                await _stockInfoDetailService.Db.Insertable(newStockDetail).ExecuteCommandAsync();
                            }
@@ -469,18 +613,69 @@
                    }
                }
                // æƒ…况2:出库货物已分拣完,但托盘上还有其他库存货物需要回库
                if (!hasRemainingLocks && palletStockGoods.Any())
                // æƒ…况2:处理拆包剩余的库存货物
                if (splitBarcodesToReturn.Any())
                {
                    // åˆ†é…æ–°è´§ä½
                    var newLocation = _locationInfoService.AssignLocation(firstlocation.LocationType);
                    decimal splitReturnQty = 0;
                    // åˆ›å»ºå›žåº“任务
                    CreateReturnTask(tasks, task, palletCode, newLocation);
                    foreach (var barcode in splitBarcodesToReturn)
                    {
                        var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                            .Where(it => it.Barcode == barcode && it.StockId == stockinfo.Id)
                            .FirstAsync();
                    totalReturnQty = palletStockGoods.Sum(x => x.StockQuantity - x.OutboundQuantity);
                        if (stockDetail != null && stockDetail.StockQuantity > 0)
                        {
                            splitReturnQty += stockDetail.StockQuantity;
                            // æ¢å¤åº“存状态为入库完成
                            stockDetail.OutboundQuantity = 0;
                            await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
                        }
                    }
                    totalReturnQty += splitReturnQty;
                    // å¦‚果没有创建任务,创建回库任务
                    if (!tasks.Any())
                    {
                        var newLocation = _locationInfoService.AssignLocation(firstlocation.LocationType);
                        CreateReturnTask(tasks, task, palletCode, newLocation);
                    }
                }
                // æƒ…况3:出库货物已分拣完,但托盘上还有其他库存货物需要回库
                if (palletStockGoods.Any() && !remainingLocks.Any(x => x.PalletCode == palletCode))
                {
                    decimal otherReturnQty = palletStockGoods.Sum(x => x.StockQuantity - x.OutboundQuantity);
                    totalReturnQty += otherReturnQty;
                    // æ›´æ–°è¿™äº›åº“存货物的状态
                    foreach (var stockGood in palletStockGoods)
                    {
                        stockGood.OutboundQuantity = 0;
                        await _stockInfoDetailService.Db.Updateable(stockGood).ExecuteCommandAsync();
                    }
                    // å¦‚果没有创建任务,创建回库任务
                    if (!tasks.Any())
                    {
                        var newLocation = _locationInfoService.AssignLocation(firstlocation.LocationType);
                        CreateReturnTask(tasks, task, palletCode, newLocation);
                    }
                }
                var allSplitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
                    .Where(it => it.OrderNo == orderNo && it.PalletCode == palletCode && !it.IsReverted)
                    .ToListAsync();
                foreach (var record in allSplitRecords)
                {
                    record.Status = (int)SplitPackageStatusEnum.已回库;
                    await _splitPackageService.Db.Updateable(record).ExecuteCommandAsync();
                }
                // ä¿å­˜ä»»åŠ¡ ç»™ESS下发任务
                if (tasks.Any())
                {
@@ -489,6 +684,7 @@
                        await _taskRepository.Db.Insertable(tasks).ExecuteCommandAsync();
                        var targetAddress = task.TargetAddress;
                        _taskRepository.DeleteData(task);
                        // ç»™ ESS æµåŠ¨ä¿¡å·å’Œåˆ›å»ºä»»åŠ¡
                        try
                        {
@@ -497,6 +693,7 @@
                                slotCode = movestations[targetAddress],
                                containerCode = palletCode
                            });
                            if (result)
                            {
                                TaskModel esstask = new TaskModel()
@@ -505,51 +702,52 @@
                                    taskGroupCode = "",
                                    groupPriority = 0,
                                    tasks = new List<TasksType>
                                    {
                                        new()
                                        {
                                            taskCode = tasks.First().TaskNum.ToString(),
                                            taskPriority = 0,
                                            taskDescribe = new TaskDescribeType {
                                                containerCode = palletCode,
                                                containerType = "CT_KUBOT_STANDARD",
                                                fromLocationCode = stations.GetValueOrDefault(targetAddress) ?? "",
                                                toStationCode = "",
                                                toLocationCode = tasks.First().TargetAddress,
                                                deadline = 0, storageTag = ""
                                            }
                                        }
                            {
                                new()
                                {
                                    taskCode = tasks.First().TaskNum.ToString(),
                                    taskPriority = 0,
                                    taskDescribe = new TaskDescribeType {
                                        containerCode = palletCode,
                                        containerType = "CT_KUBOT_STANDARD",
                                        fromLocationCode = stations.GetValueOrDefault(targetAddress) ?? "",
                                        toStationCode = "",
                                        toLocationCode = tasks.First().TargetAddress,
                                        deadline = 0, storageTag = ""
                                    }
                                }
                            }
                                };
                                var resulttask = await _eSSApiService.CreateTaskAsync(esstask);
                                _logger.LogInformation("ReturnRemaining åˆ›å»ºä»»åŠ¡è¿”å›ž:  " + resulttask);
                            }
                        }
                        catch (Exception ex)
                        {
                            _logger.LogInformation("ReturnRemaining åˆ›å»ºä»»åŠ¡è¿”å›ž catch err:  " + ex.Message);
                        }
                        _unitOfWorkManage.CommitTran();
                        return WebResponseContent.Instance.OK($"回库操作成功,共回库数量:{totalReturnQty}");
                    }
                    catch (Exception ex)
                    {
                        _unitOfWorkManage.RollbackTran();
                        return WebResponseContent.Instance.Error($"创建回库任务失败: {ex.Message}");
                    }
                }
                _unitOfWorkManage.RollbackTran();
                return WebResponseContent.Instance.Error("未创建任何回库任务");
            }
            catch (Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                return WebResponseContent.Instance.Error($"回库操作失败: {ex.Message}");
            }
        }
        /// <summary>
        /// åˆ›å»ºå›žåº“任务
@@ -610,56 +808,306 @@
            try
            {
                _unitOfWorkManage.BeginTran();
                // æŸ¥æ‰¾æ‹£é€‰è®°å½•
                var outStockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                        .Where(x => x.OrderNo == orderNo &&
                                   x.PalletCode == palletCode &&
                                   x.CurrentBarcode == barcode &&
                                   x.Status == 6)
                        .FirstAsync();
                if (outStockInfo == null)
                    return WebResponseContent.Instance.Error("未找到已拣选记录");
                // è¿˜åŽŸå‡ºåº“è¯¦æƒ…çŠ¶æ€
                outStockInfo.PickedQty = 0;
                outStockInfo.Status = 1;
                await _outStockLockInfoService.Db.Updateable(outStockInfo).ExecuteCommandAsync();
                var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                       .Where(x => x.Barcode == barcode && x.StockId == outStockInfo.StockId)
                       .FirstAsync();
                stockDetail.StockQuantity += outStockInfo.AssignQuantity;
                stockDetail.OutboundQuantity += outStockInfo.AssignQuantity;
                await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
                // è¿˜åŽŸå‡ºåº“å•æ˜Žç»†
                var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                    .Where(x => x.Id == outStockInfo.OrderDetailId)
                //查找拣选记录
                var pickingRecord = await Db.Queryable<Dt_PickingRecord>()
                    .Where(it => it.OrderNo == orderNo &&
                               it.PalletCode == palletCode &&
                               it.Barcode == barcode)
                    .OrderByDescending(it => it.PickTime)
                    .FirstAsync();
                orderDetail.OverOutQuantity -= outStockInfo.AssignQuantity;
                orderDetail.PickedQty = 0;
                await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync();
                if (pickingRecord == null)
                    return WebResponseContent.Instance.Error("未找到对应的拣选记录");
                // åˆ é™¤æ‹£é€‰åŽ†å²
                await Db.Deleteable<Dt_PickingRecord>()
                    .Where(x => x.OutStockLockId == outStockInfo.Id)
                    .ExecuteCommandAsync();
                // æŸ¥æ‰¾å‡ºåº“锁定信息
                var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .Where(it => it.Id == pickingRecord.OutStockLockId)
                    .FirstAsync();
                if (lockInfo == null)
                    return WebResponseContent.Instance.Error("未找到对应的出库锁定信息");
                //检查是否可以取消(状态必须是拣选完成)
                if (lockInfo.Status != (int)OutLockStockStatusEnum.拣选完成)
                    return WebResponseContent.Instance.Error("当前状态不允许取消分拣");
                decimal cancelQty = pickingRecord.PickQuantity;
                // æ£€æŸ¥æ‹†åŒ…链关系
                var splitChain = await GetSplitChain(barcode);
                if (splitChain.Any())
                {
                    // æƒ…况A:处理拆包链的取消(多次手动拆包)
                    await HandleSplitChainCancel(orderNo, palletCode, barcode, cancelQty, lockInfo, pickingRecord, splitChain);
                }
                else
                {
                    // æƒ…况B:处理普通条码的取消
                    await HandleNormalBarcodeCancel(orderNo, palletCode, barcode, cancelQty, lockInfo, pickingRecord);
                }
                _unitOfWorkManage.CommitTran();
                return WebResponseContent.Instance.OK("取消拣选成功");
                return WebResponseContent.Instance.OK($"取消分拣成功,恢复数量:{cancelQty}");
            }
            catch (Exception ex)
            {
                return WebResponseContent.Instance.Error($"取消拣选失败:{ex.Message}");
                _unitOfWorkManage.RollbackTran();
                return WebResponseContent.Instance.Error($"取消分拣失败:{ex.Message}");
            }
        }
        /// <summary>
        /// èŽ·å–æ‹†åŒ…é“¾ï¼ˆä»Žå½“å‰æ¡ç è¿½æº¯åˆ°åŽŸå§‹æ¡ç ï¼‰
        /// </summary>
        // åœ¨ GetSplitChain æ–¹æ³•中添加更严格的验证
        private async Task<List<SplitChainItem>> GetSplitChain(string currentBarcode)
        {
            var chain = new List<SplitChainItem>();
            var visited = new HashSet<string>();
            string current = currentBarcode;
            int maxDepth = 10; // é˜²æ­¢æ— é™å¾ªçޝ
            while (!string.IsNullOrEmpty(current) && maxDepth > 0)
            {
                maxDepth--;
                if (visited.Contains(current))
                {
                    _logger.LogWarning($"检测到循环引用在拆包链中: {current}");
                    break;
                }
                visited.Add(current);
                var splitRecord = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
                    .Where(it => it.NewBarcode == current && !it.IsReverted)
                    .FirstAsync();
                if (splitRecord == null)
                    break;
                // éªŒè¯æ‹†åŒ…记录的完整性
                if (string.IsNullOrEmpty(splitRecord.OriginalBarcode))
                {
                    _logger.LogError($"拆包记录 {splitRecord.Id} ç¼ºå°‘原始条码");
                    break;
                }
                var item = new SplitChainItem
                {
                    SplitRecord = splitRecord,
                    OriginalBarcode = splitRecord.OriginalBarcode,
                    NewBarcode = splitRecord.NewBarcode,
                    SplitQuantity = splitRecord.SplitQty
                };
                chain.Add(item);
                current = splitRecord.OriginalBarcode;
            }
            if (maxDepth <= 0)
            {
                _logger.LogWarning($"拆包链追溯达到最大深度: {currentBarcode}");
            }
            chain.Reverse();
            return chain;
        }
        /// <summary>
        /// å¤„理拆包链的取消分拣
        /// </summary>
        private async Task HandleSplitChainCancel(string orderNo, string palletCode, string barcode,
            decimal cancelQty, Dt_OutStockLockInfo lockInfo, Dt_PickingRecord pickingRecord, List<SplitChainItem> splitChain)
        {
            if (!splitChain.Any())
                return;
            //  æ‰¾åˆ°åŽŸå§‹æ¡ç ï¼ˆé“¾çš„ç¬¬ä¸€ä¸ªï¼‰
            var originalSplitItem = splitChain.First();
            var originalBarcode = originalSplitItem.OriginalBarcode;
            // æŸ¥æ‰¾åŽŸå§‹æ¡ç çš„é”å®šä¿¡æ¯å’Œåº“å­˜
            var originalLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(it => it.CurrentBarcode == originalBarcode && it.Status == (int)OutLockStockStatusEnum.出库中)
                .FirstAsync();
            var originalStockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .Where(it => it.Barcode == originalBarcode && it.StockId == originalLockInfo.StockId)
                .FirstAsync();
            if (originalLockInfo == null || originalStockDetail == null)
                throw new Exception("未找到原始条码的锁定信息或库存信息");
            // æ¢å¤åŽŸå§‹æ¡ç åº“å­˜ï¼ˆå°†å–æ¶ˆçš„æ•°é‡åŠ å›žåŽ»ï¼‰
            originalStockDetail.StockQuantity += cancelQty;
            originalStockDetail.OutboundQuantity += cancelQty;
            await _stockInfoDetailService.Db.Updateable(originalStockDetail).ExecuteCommandAsync();
            // æ¢å¤åŽŸå§‹æ¡ç é”å®šä¿¡æ¯
            originalLockInfo.AssignQuantity += cancelQty;
            await _outStockLockInfoService.Db.Updateable(originalLockInfo).ExecuteCommandAsync();
            //  åˆ é™¤æ‹†åŒ…链中所有新条码的锁定信息和库存记录
            var allNewBarcodes = splitChain.Select(x => x.NewBarcode).ToList();
            // åˆ é™¤é”å®šä¿¡æ¯
            await _outStockLockInfoService.Db.Deleteable<Dt_OutStockLockInfo>()
                .Where(it => allNewBarcodes.Contains(it.CurrentBarcode))
                .ExecuteCommandAsync();
            // åˆ é™¤åº“存记录(只删除拆包产生的新条码库存,保留原始条码)
            await _stockInfoDetailService.Db.Deleteable<Dt_StockInfoDetail>()
                .Where(it => allNewBarcodes.Contains(it.Barcode) && it.Barcode != originalBarcode)
                .ExecuteCommandAsync();
            // æ›´æ–°æ‹†åŒ…链中所有拆包记录状态为已拆包
            foreach (var chainItem in splitChain)
            {
                chainItem.SplitRecord.Status = (int)SplitPackageStatusEnum.已拆包;
                await _splitPackageService.Db.Updateable(chainItem.SplitRecord).ExecuteCommandAsync();
            }
            //  æ¢å¤è®¢å•明细拣选数量(使用原始锁定信息的订单明细ID)
            await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                .SetColumns(it => it.PickedQty == it.PickedQty - cancelQty)
                .Where(it => it.Id == originalLockInfo.OrderDetailId)
                .ExecuteCommandAsync();
            //   æ¢å¤è®¢å•状态
            await CheckAndRevertOrderStatus(orderNo);
            //  åˆ é™¤æ‹£é€‰è®°å½•
            await Db.Deleteable<Dt_PickingRecord>()
                .Where(it => it.Id == pickingRecord.Id)
                .ExecuteCommandAsync();
            ////  è®°å½•取消操作历史
            //await RecordCancelHistory(orderNo, palletCode, barcode, cancelQty, pickingRecord.Id,
            //    lockInfo.MaterielCode, "取消拆包链分拣");
        }
        /// <summary>
        /// å¤„理普通条码的取消分拣
        /// </summary>
        private async Task HandleNormalBarcodeCancel(string orderNo, string palletCode, string barcode,
            decimal cancelQty, Dt_OutStockLockInfo lockInfo, Dt_PickingRecord pickingRecord)
        {
            // 1. æŸ¥æ‰¾åº“存信息
            var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .Where(it => it.Barcode == barcode && it.StockId == lockInfo.StockId)
                .FirstAsync();
            if (stockDetail == null)
                throw new Exception("未找到对应的库存信息");
            // 2. æ¢å¤åº“存数量
            if (stockDetail.StockQuantity == 0)
            {
                // æ•´åŒ…出库的情况
                stockDetail.StockQuantity = cancelQty;
                stockDetail.OutboundQuantity = cancelQty;
            }
            else
            {
                // éƒ¨åˆ†å‡ºåº“的情况
                stockDetail.StockQuantity += cancelQty;
                stockDetail.OutboundQuantity += cancelQty;
            }
            await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
            // 3. æ¢å¤é”å®šä¿¡æ¯çŠ¶æ€
            lockInfo.AssignQuantity += cancelQty;
            lockInfo.PickedQty -= cancelQty;
            if (lockInfo.PickedQty == 0)
            {
                lockInfo.Status = (int)OutLockStockStatusEnum.出库中;
            }
            await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
            // 4. å¤„理相关的拆包记录状态恢复
            var relatedSplitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
                .Where(it => it.OriginalBarcode == barcode &&
                           it.Status == (int)SplitPackageStatusEnum.已拣选)
                .ToListAsync();
            foreach (var record in relatedSplitRecords)
            {
                record.Status = (int)SplitPackageStatusEnum.已拆包;
                await _splitPackageService.Db.Updateable(record).ExecuteCommandAsync();
            }
            // 5. æ¢å¤è®¢å•明细的拣选数量
            await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                .SetColumns(it => it.PickedQty == it.PickedQty - cancelQty)
                .Where(it => it.Id == lockInfo.OrderDetailId)
                .ExecuteCommandAsync();
            // 6. æ¢å¤è®¢å•状态
            await CheckAndRevertOrderStatus(orderNo);
            // 7. åˆ é™¤æ‹£é€‰è®°å½•
            await Db.Deleteable<Dt_PickingRecord>().Where(it => it.Id == pickingRecord.Id).ExecuteCommandAsync();
            //// 8. è®°å½•取消操作历史
            //await RecordCancelHistory(orderNo, palletCode, barcode, cancelQty, pickingRecord.Id,
            //    lockInfo.MaterielCode, "取消分拣");
        }
        /// <summary>
        /// æ£€æŸ¥å¹¶æ¢å¤è®¢å•状态
        /// </summary>
        private async Task CheckAndRevertOrderStatus(string orderNo)
        {
            var order = await _outboundOrderService.Db.Queryable<Dt_OutboundOrder>()
                .Where(x => x.OrderNo == orderNo)
                .FirstAsync();
            if (order != null && order.OrderStatus == OutOrderStatusEnum.出库完成.ObjToInt())
            {
                await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
                    .SetColumns(x => x.OrderStatus == OutOrderStatusEnum.出库中.ObjToInt())
                    .Where(x => x.OrderNo == orderNo)
                    .ExecuteCommandAsync();
            }
        }
        /// <summary>
        /// è®°å½•取消操作历史
        /// </summary>
        private async Task RecordCancelHistory(string orderNo, string palletCode, string barcode,
            decimal cancelQty, int pickingRecordId, string materielCode, string reason)
        {
            //var cancelHistory = new Dt_PickingCancelRecord
            //{
            //    OrderNo = orderNo,
            //    PalletCode = palletCode,
            //    Barcode = barcode,
            //    CancelQuantity = cancelQty,
            //    CancelTime = DateTime.Now,
            //    Operator = App.User.UserName,
            //    OriginalPickingRecordId = pickingRecordId,
            //    MaterielCode = materielCode,
            //    Reason = reason
            //};
            //await Db.Insertable(cancelHistory).ExecuteCommandAsync();
        }
        /// <summary>
        /// æ‹†åŒ…链项
        /// </summary>
        public class SplitChainItem
        {
            public Dt_SplitPackageRecord SplitRecord { get; set; }
            public string OriginalBarcode { get; set; }
            public string NewBarcode { get; set; }
            public decimal SplitQuantity { get; set; }
        }
        // èŽ·å–æœªæ‹£é€‰åˆ—è¡¨
        public async Task<List<Dt_OutStockLockInfo>> GetUnpickedList(string orderNo, string palletCode)
        {
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/SplitPackageService.cs
@@ -1,6 +1,7 @@
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Org.BouncyCastle.Asn1.Ocsp;
using SqlSugar.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -45,6 +46,168 @@
        /// <summary>
        /// æ‹†åŒ…拆箱操作
        /// </summary>
        //public async Task<WebResponseContent> SplitPackage(SplitPackageDto request)
        //{
        //    try
        //    {
        //        _unitOfWorkManage.BeginTran();
        //        // 1. éªŒè¯å‡ºåº“锁定信息
        //        var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
        //                  .Where(x => x.OrderNo == request.OrderNo &&
        //                   x.PalletCode == request.PalletCode &&
        //                   x.CurrentBarcode == request.OriginalBarcode &&
        //                   x.Status == 1)
        //            .FirstAsync();
        //        if (lockInfo == null)
        //            return WebResponseContent.Instance.Error("未找到有效的出库锁定信息");
        //        // 2. æ£€æŸ¥å‰©ä½™é”å®šæ•°é‡
        //        decimal remainingLockQuantity = lockInfo.OriginalQuantity - lockInfo.PickedQty;
        //        if (request.SplitQuantity > remainingLockQuantity)
        //            return WebResponseContent.Instance.Error($"拆包数量不能大于剩余锁定数量,剩余:{remainingLockQuantity}");
        //        var baseStockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
        //       .Where(x => x.Barcode == request.OriginalBarcode && x.StockId == lockInfo.StockId)
        //       .FirstAsync();
        //        if (baseStockDetail == null)
        //            throw new Exception($"未找到条码{request.OriginalBarcode}对应的库存记录");
        //        // 4. è®¡ç®—拆分后的数量
        //        decimal remainingQty = baseStockDetail.StockQuantity - request.SplitQuantity;
        //        // æ›´æ–°åŸºç¡€æ¡ç çš„库存数量为剩余数量
        //        baseStockDetail.StockQuantity = remainingQty;
        //        baseStockDetail.OutboundQuantity = remainingQty;
        //        await _stockInfoDetailService.Db.Updateable(baseStockDetail).ExecuteCommandAsync();
        //        var seq = await _dailySequenceService.GetNextSequenceAsync();
        //        // 3. ç”Ÿæˆæ–°æ¡ç 
        //        string newBarcode = "WSLOT" + DateTime.Now.ToString("yyyyMMdd") + seq.ToString()?.PadLeft(5, '0');
        //        // ä¸ºæ‹†åŒ…产生的新条码创建库存记录
        //        var newStockDetail = new Dt_StockInfoDetail
        //        {
        //            SupplyCode = baseStockDetail.SupplyCode,
        //            WarehouseCode = baseStockDetail.WarehouseCode,
        //            BarcodeQty = baseStockDetail.BarcodeQty,
        //            BarcodeUnit = baseStockDetail.BarcodeUnit,
        //            BusinessType = baseStockDetail.BusinessType,
        //            Unit = baseStockDetail.Unit,
        //            StockId = lockInfo.StockId,
        //            MaterielCode = baseStockDetail.MaterielCode,
        //            OrderNo = baseStockDetail.OrderNo,
        //            BatchNo = baseStockDetail.BatchNo,
        //            StockQuantity = request.SplitQuantity, // æ–°æ¡ç èŽ·å¾—æ‹†åˆ†æ•°é‡
        //            OutboundQuantity = request.SplitQuantity,
        //            Barcode = newBarcode,
        //            InboundOrderRowNo = baseStockDetail.InboundOrderRowNo,
        //        };
        //        await _outStockLockInfoService.Db.Insertable(newStockDetail).ExecuteCommandAsync();
        //        // 4. åˆ›å»ºæ–°çš„出库锁定信息(新条码)
        //        var newLockInfo = new Dt_OutStockLockInfo
        //        {
        //            OrderNo = lockInfo.OrderNo,
        //            OrderDetailId = lockInfo.OrderDetailId,
        //            BatchNo = lockInfo.BatchNo,
        //            MaterielCode = lockInfo.MaterielCode,
        //            MaterielName = lockInfo.MaterielName,
        //            StockId = lockInfo.StockId,
        //            OrderQuantity = request.SplitQuantity,
        //            OriginalQuantity = request.SplitQuantity,
        //            AssignQuantity = request.SplitQuantity, // æ–°æ¡ç åˆ†é…æ•°é‡
        //            PickedQty = 0, // æ–°æ¡ç æœªæ‹£é€‰
        //            LocationCode = lockInfo.LocationCode,
        //            PalletCode = lockInfo.PalletCode,
        //            TaskNum = lockInfo.TaskNum,
        //            Status = (int)OutLockStockStatusEnum.出库中,
        //            Unit = lockInfo.Unit,
        //            SupplyCode = lockInfo.SupplyCode,
        //            OrderType = lockInfo.OrderType,
        //            CurrentBarcode = newBarcode, // æ–°æ¡ç 
        //            OriginalLockQuantity = request.SplitQuantity,
        //            IsSplitted = 1,
        //            ParentLockId = lockInfo.Id // è®°å½•父级锁定ID
        //        };
        //        await _outStockLockInfoService.Db.Insertable(newLockInfo).ExecuteCommandAsync();
        //        lockInfo.AssignQuantity = remainingQty;
        //        lockInfo.IsSplitted = 1; // æ ‡è®°ä¸ºå·²æ‹†åŒ…
        //        await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
        //        var previousSplitRecord = await Db.Queryable<Dt_SplitPackageRecord>()
        //      .Where(x => x.OriginalBarcode == request.OriginalBarcode && !x.IsReverted)
        //      .OrderByDescending(x => x.SplitTime)
        //      .FirstAsync();
        //        // 6. è®°å½•拆包历史(用于追踪)
        //        var splitHistory = new Dt_SplitPackageRecord
        //        {
        //            FactoryArea = lockInfo.FactoryArea,
        //            TaskNum = lockInfo.TaskNum,
        //            OutStockLockInfoId = lockInfo.Id,
        //            StockId = baseStockDetail.StockId,
        //            Operator = App.User.UserName,
        //            IsReverted = false,
        //            OriginalBarcode = request.OriginalBarcode,
        //            NewBarcode = newBarcode,
        //            SplitQty = request.SplitQuantity,
        //            RemainQuantity = remainingQty, // è®°å½•拆分后的剩余数量
        //            MaterielCode = lockInfo.MaterielCode,
        //            SplitTime = DateTime.Now,
        //            OrderNo = request.OrderNo,
        //            PalletCode = request.PalletCode,
        //            Status = (int)SplitPackageStatusEnum.已拆包,
        //            PreviousSplitRecordId = previousSplitRecord?.Id??0 // è®°å½•前一次拆包ID,建立拆包链
        //        };
        //        await Db.Insertable(splitHistory).ExecuteCommandAsync();
        //        _unitOfWorkManage.CommitTran();
        //        try
        //        {
        //            MaterielToMesDTO dto = new MaterielToMesDTO
        //            {
        //                batchNo = baseStockDetail.BatchNo,
        //                factoryArea = baseStockDetail.FactoryArea,
        //                materialCode = baseStockDetail.MaterielCode,
        //                newmaterialCode = newBarcode,
        //                oldmaterialCode = request.OriginalBarcode,
        //                operationType = 1,
        //                qty = remainingQty,
        //                supplyCode = baseStockDetail.SupplyCode,
        //                unit = baseStockDetail.BarcodeUnit,
        //                warehouseCode = baseStockDetail.WarehouseCode,
        //                reqCode = Guid.NewGuid().ToString(),
        //                reqTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")
        //            };
        //            _invokeMESService.NewMaterielToMes(dto);
        //        }
        //        catch(Exception ex)
        //        {
        //            _logger.LogError("SplitPackage å›žä¼ MES:  " + ex.Message);
        //        }
        //        return WebResponseContent.Instance.OK("拆包成功", new
        //        {
        //            NewBarcode = newBarcode,
        //            NewLockInfoId = newLockInfo.Id
        //        });
        //    }
        //    catch (Exception ex)
        //    {
        //        _unitOfWorkManage.RollbackTran();
        //        return WebResponseContent.Instance.Error($"拆包失败: {ex.Message}");
        //    }
        //}
        // èŽ·å–æ¡ç çš„æ‹†åŒ…åŽ†å²é“¾
        public async Task<WebResponseContent> SplitPackage(SplitPackageDto request)
        {
            try
@@ -67,58 +230,63 @@
                if (request.SplitQuantity > remainingLockQuantity)
                    return WebResponseContent.Instance.Error($"拆包数量不能大于剩余锁定数量,剩余:{remainingLockQuantity}");
                var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
               .Where(x => x.Barcode == request.OriginalBarcode && x.StockId == lockInfo.StockId)
               .FirstAsync();
                if (stockDetail == null)
                // 3. æŸ¥æ‰¾å½“前条码的库存信息
                var baseStockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                    .Where(x => x.Barcode == request.OriginalBarcode && x.StockId == lockInfo.StockId)
                    .FirstAsync();
                if (baseStockDetail == null)
                    throw new Exception($"未找到条码{request.OriginalBarcode}对应的库存记录");
                stockDetail.StockQuantity = request.SplitQuantity;
                stockDetail.OutboundQuantity = request.SplitQuantity;
                _stockInfoDetailService.Db.Updateable<Dt_StockInfoDetail>(stockDetail).ExecuteCommand();
                // 4. è®°å½•拆包前的库存数量和锁定信息
                decimal stockBeforeSplit = baseStockDetail.StockQuantity;
                decimal assignBeforeSplit = lockInfo.AssignQuantity;
                // 5. è®¡ç®—拆分后的剩余数量
                decimal remainingQty = baseStockDetail.StockQuantity - request.SplitQuantity;
                // æ›´æ–°åŸºç¡€æ¡ç çš„库存数量为剩余数量
                baseStockDetail.StockQuantity = remainingQty;
                baseStockDetail.OutboundQuantity = remainingQty;
                await _stockInfoDetailService.Db.Updateable(baseStockDetail).ExecuteCommandAsync();
                // 6. ç”Ÿæˆæ–°æ¡ç 
                var seq = await _dailySequenceService.GetNextSequenceAsync();
                // 3. ç”Ÿæˆæ–°æ¡ç 
                string newBarcode = "WSLOT" + DateTime.Now.ToString("yyyyMMdd") + seq.ToString()?.PadLeft(5, '0');
                decimal remainingQty = remainingLockQuantity - request.SplitQuantity;
                // ä¸ºæ‹†åŒ…产生的新条码创建库存记录
                // 7. ä¸ºæ–°æ¡ç åˆ›å»ºåº“存记录(拆分出的数量)
                var newStockDetail = new Dt_StockInfoDetail
                {
                    SupplyCode = stockDetail.SupplyCode,
                    WarehouseCode = stockDetail.WarehouseCode,
                    BarcodeQty = stockDetail.BarcodeQty,
                    BarcodeUnit = stockDetail.BarcodeUnit,
                    BusinessType = stockDetail.BusinessType,
                    Unit = stockDetail.Unit,
                    SupplyCode = baseStockDetail.SupplyCode,
                    WarehouseCode = baseStockDetail.WarehouseCode,
                    BarcodeQty = baseStockDetail.BarcodeQty,
                    BarcodeUnit = baseStockDetail.BarcodeUnit,
                    BusinessType = baseStockDetail.BusinessType,
                    Unit = baseStockDetail.Unit,
                    StockId = lockInfo.StockId,
                    MaterielCode = stockDetail.MaterielCode,
                    OrderNo = stockDetail.OrderNo,
                    BatchNo = stockDetail.BatchNo,
                    StockQuantity = remainingQty,
                    OutboundQuantity = remainingQty, // é”å®šå…¨éƒ¨æ•°é‡
                    MaterielCode = baseStockDetail.MaterielCode,
                    OrderNo = baseStockDetail.OrderNo,
                    BatchNo = baseStockDetail.BatchNo,
                    StockQuantity = request.SplitQuantity,
                    OutboundQuantity = request.SplitQuantity,
                    Barcode = newBarcode,
                    InboundOrderRowNo = stockDetail.InboundOrderRowNo,
                    InboundOrderRowNo = baseStockDetail.InboundOrderRowNo,
                };
                await _outStockLockInfoService.Db.Insertable(newStockDetail).ExecuteCommandAsync();
                await _stockInfoDetailService.Db.Insertable(newStockDetail).ExecuteCommandAsync();
                // 4. åˆ›å»ºæ–°çš„出库锁定信息(新条码)
                // 8. åˆ›å»ºæ–°çš„出库锁定信息(新条码)
                var newLockInfo = new Dt_OutStockLockInfo
                {
                    OrderNo = lockInfo.OrderNo,
                    OrderDetailId = lockInfo.OrderDetailId,
                    BatchNo = lockInfo.BatchNo,
                    MaterielCode = lockInfo.MaterielCode,
                    MaterielName = lockInfo.MaterielName,
                    StockId = lockInfo.StockId,
                    OrderQuantity = remainingQty,
                    OriginalQuantity = remainingQty,
                    AssignQuantity = remainingQty, // æ–°æ¡ç åˆ†é…æ•°é‡
                    PickedQty = 0, // æ–°æ¡ç æœªæ‹£é€‰
                    OrderQuantity = request.SplitQuantity,
                    OriginalQuantity = request.SplitQuantity,
                    AssignQuantity = request.SplitQuantity,
                    PickedQty = 0,
                    LocationCode = lockInfo.LocationCode,
                    PalletCode = lockInfo.PalletCode,
                    TaskNum = lockInfo.TaskNum,
@@ -126,30 +294,33 @@
                    Unit = lockInfo.Unit,
                    SupplyCode = lockInfo.SupplyCode,
                    OrderType = lockInfo.OrderType,
                    CurrentBarcode = newBarcode, // æ–°æ¡ç 
                    CurrentBarcode = newBarcode,
                    OriginalLockQuantity = request.SplitQuantity,
                    IsSplitted = 1,
                    ParentLockId = lockInfo.Id // è®°å½•父级锁定ID
                    ParentLockId = lockInfo.Id
                };
                await _outStockLockInfoService.Db.Insertable(newLockInfo).ExecuteCommandAsync();
                // 5. æ›´æ–°åŽŸé”å®šä¿¡æ¯çš„åˆ†é…æ•°é‡ï¼ˆå‡å°‘æ‹†åŒ…æ•°é‡ï¼‰
                lockInfo.AssignQuantity = request.SplitQuantity;
                // 9. æ›´æ–°åŽŸé”å®šä¿¡æ¯çš„åˆ†é…æ•°é‡ï¼ˆå‡å°‘æ‹†åŒ…æ•°é‡ï¼‰
                lockInfo.AssignQuantity = remainingQty;
                lockInfo.IsSplitted = 1;
                await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
                // 6. è®°å½•拆包历史(用于追踪)
                // 10. è®°å½•拆包历史
                var splitHistory = new Dt_SplitPackageRecord
                {
                    FactoryArea = lockInfo.FactoryArea,
                    TaskNum = lockInfo.TaskNum,
                    OutStockLockInfoId = lockInfo.Id,
                    StockId = stockDetail?.StockId ?? 0,
                    StockId = baseStockDetail.StockId,
                    Operator = App.User.UserName,
                    IsReverted = false,
                    OriginalBarcode = lockInfo.CurrentBarcode,
                    OriginalBarcode = request.OriginalBarcode,
                    NewBarcode = newBarcode,
                    SplitQty = request.SplitQuantity,
                    RemainQuantity = lockInfo.OriginalQuantity - request.SplitQuantity,
                    RemainQuantity = remainingQty,
                    StockBeforeSplit = stockBeforeSplit, // è®°å½•拆包前的库存数量
                    AssignBeforeSplit = assignBeforeSplit, // è®°å½•拆包前的分配数量
                    MaterielCode = lockInfo.MaterielCode,
                    SplitTime = DateTime.Now,
                    OrderNo = request.OrderNo,
@@ -159,34 +330,12 @@
                await Db.Insertable(splitHistory).ExecuteCommandAsync();
                _unitOfWorkManage.CommitTran();
                try
                {
                    MaterielToMesDTO dto = new MaterielToMesDTO
                    {
                        batchNo = stockDetail.BatchNo,
                        factoryArea = stockDetail.FactoryArea,
                        materialCode = stockDetail.MaterielCode,
                        newmaterialCode = newBarcode,
                        oldmaterialCode = request.OriginalBarcode,
                        operationType = 1,
                        qty = remainingQty,
                        supplyCode = stockDetail.SupplyCode,
                        unit = stockDetail.BarcodeUnit,
                        warehouseCode = stockDetail.WarehouseCode,
                        reqCode = Guid.NewGuid().ToString(),
                        reqTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")
                    };
                    _invokeMESService.NewMaterielToMes(dto);
                }
                catch(Exception ex)
                {
                    _logger.LogError("SplitPackage å›žä¼ MES:  " + ex.Message);
                }
                return WebResponseContent.Instance.OK("拆包成功", new
                return WebResponseContent.Instance.OK("拆包成功", new SplitPackageChainDto
                {
                    NewBarcode = newBarcode,
                    NewLockInfoId = newLockInfo.Id
                    NewLockInfoId = newLockInfo.Id,
                    RemainQuantity = remainingQty
                });
            }
            catch (Exception ex)
@@ -196,7 +345,96 @@
            }
        }
        public async Task<WebResponseContent> RevertSplitPackage(string originalBarcode)
        {
            try
            {
                _unitOfWorkManage.BeginTran();
                // 1. æŸ¥æ‰¾è¯¥æ¡ç ä½œä¸ºåŽŸå§‹æ¡ç çš„æ‰€æœ‰æœªæ’¤é”€æ‹†åŒ…è®°å½•ï¼ŒæŒ‰æ—¶é—´å€’åº
                var splitRecords = await Db.Queryable<Dt_SplitPackageRecord>()
                        .Where(x => x.OriginalBarcode == originalBarcode && !x.IsReverted)
                        .OrderByDescending(x => x.SplitTime)
                        .ToListAsync();
                if (splitRecords == null || !splitRecords.Any())
                    return WebResponseContent.Instance.Error("未找到可撤销的拆包记录");
                // 2. æ£€æŸ¥æ‰€æœ‰æ–°æ¡ç æ˜¯å¦å·²æ‹£é€‰
                var newBarcodes = splitRecords.Select(x => x.NewBarcode).ToList();
                var newLockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .Where(x => newBarcodes.Contains(x.CurrentBarcode))
                    .ToListAsync();
                var pickedBarcodes = newLockInfos.Where(x => x.Status == 2).Select(x => x.CurrentBarcode).ToList();
                if (pickedBarcodes.Any())
                    return WebResponseContent.Instance.Error($"以下条码已拣选,无法撤销拆包:{string.Join(",", pickedBarcodes)}");
                // 3. èŽ·å–åŽŸæ¡ç çš„é”å®šä¿¡æ¯å’Œåº“å­˜ä¿¡æ¯
                var originalLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .Where(x => x.CurrentBarcode == originalBarcode)
                    .FirstAsync();
                if (originalLockInfo == null)
                    return WebResponseContent.Instance.Error("未找到原条码锁定信息");
                var originalStockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                    .Where(x => x.Barcode == originalBarcode && x.StockId == originalLockInfo.StockId)
                    .FirstAsync();
                if (originalStockDetail == null)
                    return WebResponseContent.Instance.Error("未找到原条码库存信息");
                // 4. æŸ¥æ‰¾ç¬¬ä¸€æ¬¡æ‹†åŒ…记录,获取拆包前的状态
                var firstSplitRecord = splitRecords.OrderBy(x => x.SplitTime).FirstOrDefault();
                if (firstSplitRecord == null)
                    return WebResponseContent.Instance.Error("未找到有效的拆包记录");
                // 5. æ¢å¤åŽŸæ¡ç åˆ°æ‹†åŒ…å‰çš„çŠ¶æ€
                originalStockDetail.StockQuantity = firstSplitRecord.StockBeforeSplit;
                originalStockDetail.OutboundQuantity = firstSplitRecord.StockBeforeSplit;
                await _stockInfoDetailService.Db.Updateable(originalStockDetail).ExecuteCommandAsync();
                // 6. æ¢å¤åŽŸæ¡ç é”å®šä¿¡æ¯åˆ°æ‹†åŒ…å‰çš„çŠ¶æ€
                originalLockInfo.AssignQuantity = firstSplitRecord.AssignBeforeSplit;
                originalLockInfo.OriginalQuantity = firstSplitRecord.AssignBeforeSplit;
                originalLockInfo.OrderQuantity = firstSplitRecord.AssignBeforeSplit;
                originalLockInfo.Status = (int)OutLockStockStatusEnum.出库中;
                originalLockInfo.IsSplitted = 0; // æ¢å¤ä¸ºæœªæ‹†åŒ…状态
                await _outStockLockInfoService.Db.Updateable(originalLockInfo).ExecuteCommandAsync();
                // 7. åˆ é™¤æ‰€æœ‰æ–°æ¡ç çš„锁定信息
                await _outStockLockInfoService.Db.Deleteable<Dt_OutStockLockInfo>()
                    .Where(x => newBarcodes.Contains(x.CurrentBarcode))
                    .ExecuteCommandAsync();
                // 8. åˆ é™¤æ‰€æœ‰æ–°æ¡ç çš„库存记录
                await _stockInfoDetailService.Db.Deleteable<Dt_StockInfoDetail>()
                    .Where(x => newBarcodes.Contains(x.Barcode))
                    .ExecuteCommandAsync();
                // 9. æ ‡è®°æ‰€æœ‰æ‹†åŒ…记录为已撤销
                foreach (var record in splitRecords)
                {
                    record.IsReverted = true;
                    record.RevertTime = DateTime.Now;
                    record.Status = (int)SplitPackageStatusEnum.已撤销;
                }
                await Db.Updateable(splitRecords).ExecuteCommandAsync();
                _unitOfWorkManage.CommitTran();
                return WebResponseContent.Instance.OK($"撤销拆包成功,共撤销{splitRecords.Count}次拆包,{originalBarcode}恢复为拆包前数量:{firstSplitRecord.StockBeforeSplit}");
            }
            catch (Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                return WebResponseContent.Instance.Error($"撤销拆包失败:{ex.Message}");
            }
        }
        //public async Task<WebResponseContent> RevertSplitPackage(string originalBarcode)
        //{
        //    try
@@ -228,7 +466,7 @@
        //        if (originalLockInfo == null)
        //            return WebResponseContent.Instance.Error("未找到原条码锁定信息");
        //        originalLockInfo.AssignQuantity  += splitRecord.RemainQuantity;  
        //       // originalLockInfo.Status = (int)OutLockStockStatusEnum.出库中;
        //        originalLockInfo.IsSplitted = 0; // æ ‡è®°ä¸ºæœªæ‹†åŒ…
@@ -269,7 +507,7 @@
        //        // 8. æ›´æ–°æ‹†åŒ…记录为已撤销
        //        splitRecord.IsReverted = true;              
        //        await Db.Updateable(splitRecord).ExecuteCommandAsync();
        //        _unitOfWorkManage.CommitTran();
@@ -283,101 +521,19 @@
        //        return WebResponseContent.Instance.Error($"撤销拆包失败:{ex.Message}");
        //    }
        //}
        public async Task<WebResponseContent> RevertSplitPackage(string originalBarcode)
        // èŽ·å–å¯æ’¤é”€çš„æ‹†åŒ…è®°å½•åˆ—è¡¨
        public Dt_SplitPackageRecord GetRevertableSplitRecords(string originalBarcode)
        {
            try
            {
                _unitOfWorkManage.BeginTran();
            var revertableRecords =   Db.Queryable<Dt_SplitPackageRecord>()
                    .Where(x => x.OriginalBarcode == originalBarcode && !x.IsReverted)
                    .OrderBy(x => x.SplitTime)
                   .First();
                // 1. æŸ¥æ‰¾æ‰€æœ‰æœªæ’¤é”€çš„æ‹†åŒ…记录
                var splitRecords = await Db.Queryable<Dt_SplitPackageRecord>()
                        .Where(x => x.OriginalBarcode == originalBarcode && !x.IsReverted)
                        .OrderBy(x => x.SplitTime) // æŒ‰æ—¶é—´æ­£åºï¼Œä»Žæœ€æ—©å¼€å§‹æ’¤é”€
                        .ToListAsync();
                if (splitRecords == null || !splitRecords.Any())
                    return WebResponseContent.Instance.Error("未找到可撤销的拆包记录");
                // 2. æ£€æŸ¥æ‰€æœ‰æ–°æ¡ç æ˜¯å¦å·²æ‹£é€‰
                var newBarcodes = splitRecords.Select(x => x.NewBarcode).ToList();
                var newLockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .Where(x => newBarcodes.Contains(x.CurrentBarcode))
                    .ToListAsync();
                var pickedBarcodes = newLockInfos.Where(x => x.Status == 2).Select(x => x.CurrentBarcode).ToList();
                if (pickedBarcodes.Any())
                    return WebResponseContent.Instance.Error($"以下条码已拣选,无法撤销:{string.Join(",", pickedBarcodes)}");
                // 3. èŽ·å–åŽŸæ¡ç ä¿¡æ¯
                var originalLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .Where(x => x.CurrentBarcode == originalBarcode)
                    .FirstAsync();
                if (originalLockInfo == null)
                    return WebResponseContent.Instance.Error("未找到原条码锁定信息");
                var originalStockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                    .Where(x => x.Barcode == originalBarcode && x.StockId == originalLockInfo.StockId)
                    .FirstAsync();
                // 4. èŽ·å–æ‰€æœ‰æ–°æ¡ç çš„åº“å­˜è®°å½•
                var newStockDetails = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                    .Where(x => newBarcodes.Contains(x.Barcode))
                    .ToListAsync();
                // 5. è®¡ç®—总还原数量
                decimal totalRevertQty = newStockDetails.Sum(x => x.StockQuantity);
                // 6. è¿˜åŽŸåŽŸæ¡ç åº“å­˜è®°å½•
                if (originalStockDetail != null)
                {
                    // åŽŸæ¡ç å½“å‰æ•°é‡åŠ ä¸Šæ‰€æœ‰æ–°æ¡ç çš„æ•°é‡
                    originalStockDetail.StockQuantity += totalRevertQty;
                    originalStockDetail.OutboundQuantity += totalRevertQty;
                    await _stockInfoDetailService.Db.Updateable(originalStockDetail).ExecuteCommandAsync();
                }
                // 7. è¿˜åŽŸåŽŸæ¡ç é”å®šä¿¡æ¯
                decimal totalAssignQty = newLockInfos.Sum(x => x.AssignQuantity);
                originalLockInfo.AssignQuantity += totalAssignQty;
                if (originalLockInfo.OrderQuantity < originalLockInfo.AssignQuantity)
                {
                    originalLockInfo.AssignQuantity=originalLockInfo.OrderQuantity;
                }
                originalLockInfo.Status = (int)OutLockStockStatusEnum.出库中;
                originalLockInfo.IsSplitted = 0;
                await _outStockLockInfoService.Db.Updateable(originalLockInfo).ExecuteCommandAsync();
                // 8. åˆ é™¤æ‰€æœ‰æ–°æ¡ç çš„锁定信息
                await _outStockLockInfoService.Db.Deleteable<Dt_OutStockLockInfo>()
                    .Where(x => newBarcodes.Contains(x.CurrentBarcode))
                    .ExecuteCommandAsync();
                // 9. åˆ é™¤æ‰€æœ‰æ–°æ¡ç çš„库存记录
                await _stockInfoDetailService.Db.Deleteable<Dt_StockInfoDetail>()
                    .Where(x => newBarcodes.Contains(x.Barcode))
                    .ExecuteCommandAsync();
                // 10. æ ‡è®°æ‰€æœ‰æ‹†åŒ…记录为已撤销
                foreach (var record in splitRecords)
                {
                    record.IsReverted = true;
                }
                await Db.Updateable(splitRecords).ExecuteCommandAsync();
                _unitOfWorkManage.CommitTran();
                return WebResponseContent.Instance.OK($"撤销拆包成功,共还原{splitRecords.Count}次拆包,总数量:{totalRevertQty}");
            }
            catch (Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                return WebResponseContent.Instance.Error($"撤销拆包失败:{ex.Message}");
            }
            return   revertableRecords ;
        }
        // èŽ·å–æ‹†åŒ…ä¿¡æ¯
        public async Task<WebResponseContent> GetSplitPackageInfo(string orderNo, string palletCode, string barcode)
        {
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_StockService/StockInfoService.cs
@@ -100,7 +100,7 @@
                .SelectMany(x => x.Details)
                .Where(x => x.MaterielCode == materielCode &&
                           x.StockQuantity > x.OutboundQuantity) // æœ‰å¯ç”¨åº“å­˜
                .OrderBy(x => x.ProductionDate) // æŒ‰ç”Ÿäº§æ—¥æœŸæŽ’序,先进先出
                .OrderBy(x => x.CreateDate) // æŒ‰æ—¥æœŸæŽ’序,先进先出
                .ThenBy(x => x.StockId)         // ç›¸åŒç”Ÿäº§æ—¥æœŸæŒ‰åº“å­˜ID排序
                .ToList();
@@ -179,7 +179,7 @@
                query = query.Where(x => x.BatchNo == batchNo);
            }
            return await query.OrderBy(x => x.ProductionDate).ToListAsync();
            return await query.OrderBy(x => x.CreateDate).ToListAsync();
        }
        public List<Dt_StockInfo> GetStockInfos(string materielCode, string lotNo, string supplyCode, List<string> locationCodes)
        {
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs
@@ -293,11 +293,11 @@
                               }).ToList();
                            feedmodel.details = groupedData;
                            var result= await _invokeMESService.FeedbackInbound(feedmodel);
                            var result = await _invokeMESService.FeedbackInbound(feedmodel);
                            if (result != null && result.code == 200)
                            {
                                _inboundOrderService.Db.Updateable<Dt_InboundOrder>().SetColumns(it => new Dt_InboundOrder { ReturnToMESStatus = 1 })
                                .Where(it => it.Id== inboundOrder.Id).ExecuteCommand();
                                .Where(it => it.Id == inboundOrder.Id).ExecuteCommand();
                                _inboundOrderDetailService.Db.Updateable<Dt_InboundOrderDetail>().SetColumns(it => new Dt_InboundOrderDetail { ReturnToMESStatus = 1 })
                                .Where(it => it.OrderId == inboundOrder.Id).ExecuteCommand();
                            }
@@ -329,7 +329,7 @@
            var outloks = _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>().Where(x => x.TaskNum == task.TaskNum).ToList();
            var stockids = outloks.Select(x => x.StockId).ToList();
            _stockService.StockInfoService.Db.Updateable<Dt_StockInfo>()
                                  .SetColumns(it => new Dt_StockInfo
                                  {
@@ -343,9 +343,9 @@
                                  {
                                      Status = StockStatusEmun.出库锁定.ObjToInt()
                                  })
                                  .Where(it => stockids.Contains( it.StockId))
                                  .Where(it => stockids.Contains(it.StockId))
                                  .ExecuteCommand();
            return WebResponseContent.Instance.OK();
@@ -419,6 +419,17 @@
                return WebResponseContent.Instance.Error($"未找到该托盘库存明细信息");
            }
            // èŽ·å–æ‰€æœ‰å›žåº“ä¸­çš„å‡ºåº“é”å®šè®°å½•
            var returnLocks = _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(it => it.OrderNo == task.OrderNo && it.PalletCode == task.PalletCode && it.Status == (int)OutLockStockStatusEnum.回库中)
                .ToList();
            // æ›´æ–°å‡ºåº“锁定记录状态为回库完成
            foreach (var lockInfo in returnLocks)
            {
                lockInfo.Status = (int)OutLockStockStatusEnum.已回库;
            }
            _outStockLockInfoService.Db.Updateable(returnLocks).ExecuteCommand();
            //查货位
            Dt_LocationInfo locationInfo = _locationInfoService.Repository.QueryFirst(x => x.LocationCode == task.TargetAddress);
            if (locationInfo == null)