using Dm.filter; using MailKit.Search; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using SqlSugar; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using WIDESEA_Common.LocationEnum; using WIDESEA_Common.OrderEnum; using WIDESEA_Common.StockEnum; using WIDESEA_Common.TaskEnum; using WIDESEA_Core; using WIDESEA_Core.BaseRepository; using WIDESEA_Core.BaseServices; using WIDESEA_Core.Helper; using WIDESEA_DTO.Basic; using WIDESEA_DTO.Outbound; using WIDESEA_IBasicService; using WIDESEA_IOutboundService; using WIDESEA_IStockService; using WIDESEA_Model.Models; namespace WIDESEA_OutboundService { /// /// /// public class OutboundPickingService : ServiceBase>, IOutboundPickingService { private readonly IUnitOfWorkManage _unitOfWorkManage; public IRepository Repository => BaseDal; private readonly IStockInfoService _stockInfoService; private readonly IStockService _stockService; private readonly IOutStockLockInfoService _outStockLockInfoService; private readonly IStockInfoDetailService _stockInfoDetailService; private readonly ILocationInfoService _locationInfoService; private readonly IOutboundOrderDetailService _outboundOrderDetailService; private readonly IOutboundOrderService _outboundOrderService; private readonly ISplitPackageService _splitPackageService; private readonly IRepository _taskRepository; private readonly IESSApiService _eSSApiService; private readonly ILogger _logger; private Dictionary stations = new Dictionary { {"2-1","2-9" }, {"3-1","3-9" }, }; private Dictionary movestations = new Dictionary { {"2-1","2-5" }, {"3-1","3-5" }, }; public OutboundPickingService(IRepository BaseDal, IUnitOfWorkManage unitOfWorkManage, IStockInfoService stockInfoService, IStockService stockService, IOutStockLockInfoService outStockLockInfoService, IStockInfoDetailService stockInfoDetailService, ILocationInfoService locationInfoService, IOutboundOrderDetailService outboundOrderDetailService, ISplitPackageService splitPackageService, IOutboundOrderService outboundOrderService, IRepository taskRepository, IESSApiService eSSApiService, ILogger logger) : base(BaseDal) { _unitOfWorkManage = unitOfWorkManage; _stockInfoService = stockInfoService; _stockService = stockService; _outStockLockInfoService = outStockLockInfoService; _stockInfoDetailService = stockInfoDetailService; _locationInfoService = locationInfoService; _outboundOrderDetailService = outboundOrderDetailService; _splitPackageService = splitPackageService; _outboundOrderService = outboundOrderService; _taskRepository = taskRepository; _eSSApiService = eSSApiService; _logger = logger; } #region 查询出库详情列表 public async Task> GetOutStockLockListAsync(string orderNo) { var locks = await _outStockLockInfoService.Db.Queryable() .Where(t => t.OrderNo == orderNo) .ToListAsync(); return locks.Select(t => new OutStockLockListResp { Id = t.Id, // TaskNum = t.TaskNum, PalletCode = t.PalletCode, CurrentBarcode = t.CurrentBarcode, AssignQuantity = t.AssignQuantity, PickedQty = t.PickedQty, Status = t.Status, // IsSplitted = t.IsSplitted }).ToList(); } #endregion public async Task ValidateBarcode(string barcode) { try { if (string.IsNullOrEmpty(barcode)) { return WebResponseContent.Instance.Error("条码不能为空"); } // 根据条码查询库存明细 var stockDetail = await _stockInfoDetailService.Db.Queryable() .Includes(x => x.StockInfo) .Where(x => x.Barcode == barcode) .FirstAsync(); if (stockDetail == null) { return WebResponseContent.Instance.Error("条码不存在"); } var result = new { Barcode = barcode, MaterielCode = stockDetail.MaterielCode, BatchNo = stockDetail.BatchNo, AvailableQuantity = stockDetail.StockQuantity - stockDetail.OutboundQuantity, LocationCode = stockDetail.StockInfo?.LocationCode, PalletCode = stockDetail.StockInfo?.PalletCode }; return WebResponseContent.Instance.OK(null, result); } catch (Exception ex) { return WebResponseContent.Instance.Error($"条码验证失败: {ex.Message}"); } } public async Task ConfirmPicking(string orderNo, string palletCode, string barcode) { try { _unitOfWorkManage.BeginTran(); var lockInfo = await _outStockLockInfoService.Db.Queryable() .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() .Where(it => it.NewBarcode == barcode && it.Status == 1) .FirstAsync(); if (splitBarcode != null) { // 通过拆包条码记录找到对应的出库锁定记录 lockInfo = await _outStockLockInfoService.Db.Queryable() .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().First(x => x.Id == lockInfo.OrderDetailId); if (outorderdetail != null && lockInfo.AssignQuantity > outorderdetail.OrderQuantity) { throw new Exception($"条码{barcode}的出库数量大于订单的数量"); } var stockDetail = await _stockInfoDetailService.Db.Queryable() .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() .Where(it => it.NewBarcode == barcode) .FirstAsync(); if (splitBarcodeRecord != null) { splitBarcodeRecord.Status = 2; await _splitPackageService.Db.Updateable(splitBarcodeRecord).ExecuteCommandAsync(); } await _outboundOrderDetailService.Db.Updateable() .SetColumns(it => it.PickedQty == it.PickedQty + actualQty) .Where(it => it.Id == lockInfo.OrderDetailId) .ExecuteCommandAsync(); await CheckAndUpdateOrderStatus(orderNo); //查询任务表 var task = _taskRepository.QueryData(x => x.OrderNo == orderNo && x.PalletCode == palletCode).FirstOrDefault(); // 9. 记录拣选历史 var pickingHistory = new Dt_PickingRecord { FactoryArea = lockInfo.FactoryArea, TaskNo = task?.TaskNum ?? 0, LocationCode = task?.SourceAddress ?? "", StockId = stockDetail.Id, OrderNo = orderNo, OrderDetailId = lockInfo.OrderDetailId, PalletCode = palletCode, Barcode = barcode, MaterielCode = lockInfo.MaterielCode, PickQuantity = lockInfo.AssignQuantity, PickTime = DateTime.Now, Operator = App.User.UserName, OutStockLockId = lockInfo.Id }; await Db.Insertable(pickingHistory).ExecuteCommandAsync(); _unitOfWorkManage.CommitTran(); return WebResponseContent.Instance.OK("拣选确认成功"); } catch (Exception ex) { return WebResponseContent.Instance.Error($"拣选确认失败:{ex.Message}"); } } // 检查并更新订单状态 private async Task CheckAndUpdateOrderStatus(string orderNo) { var orderDetails = _stockInfoDetailService.Db.Queryable() .LeftJoin((o, item) => o.OrderId == item.Id) // 关联条件:父表 Id = 子表 OrderId .Where((o, item) => item.OrderNo == orderNo) // 过滤父表 OrderNo .Select((o, item) => o) // 只返回子表数据 .ToList(); //var orderDetails = await _stockInfoDetailService.Db.Queryable() // .Where(x => x.OrderId == orderNo.ObjToInt()) // .ToListAsync(); bool allCompleted = true; foreach (var detail in orderDetails) { if (detail.OverOutQuantity < detail.NeedOutQuantity) { allCompleted = false; break; } } if (allCompleted) { await _outboundOrderService.Db.Updateable() .SetColumns(x => x.OrderStatus == 2) // 已完成 .Where(x => x.OrderNo == orderNo) .ExecuteCommandAsync(); //回传MES } } /// /// 回库操作 /// public async Task ReturnRemaining(string orderNo, string palletCode, string reason) { try { // 获取所有未分拣的出库锁定记录,包括拆包产生的记录 var remainingLocks = await _outStockLockInfoService.Db.Queryable() .Where(it => it.OrderNo == orderNo && it.Status == 1) .ToListAsync(); var stockinfo = _stockInfoService.Db.Queryable().First(x => x.PalletCode == palletCode); var tasks = new List(); // 查询任务表 var task = remainingLocks.Any() ? _taskRepository.QueryData(x => x.TaskNum == remainingLocks.First().TaskNum).FirstOrDefault() : _taskRepository.QueryData(x => x.PalletCode == palletCode).FirstOrDefault(); if (task == null) { return WebResponseContent.Instance.Error("未找到对应的任务信息"); } // 检查托盘上是否有其他非出库货物(库存货物) var palletStockGoods = await _stockInfoDetailService.Db.Queryable() .Where(it => it.StockId == stockinfo.Id &&( it.Status == StockStatusEmun.入库确认.ObjToInt() || it.Status == StockStatusEmun.入库完成.ObjToInt())) .Where(it => it.OutboundQuantity == 0 || it.OutboundQuantity < it.StockQuantity) // 未完全出库的 .ToListAsync(); // 如果没有需要回库的货物(既无未分拣出库货物,也无其他库存货物) if (!remainingLocks.Any() && !palletStockGoods.Any()) { //是否自动回库,把之前出库的任务删除,然后组个空盘入库。 return WebResponseContent.Instance.Error("没有需要回库的剩余货物"); } var firstlocation = _locationInfoService.Db.Queryable().First(x => x.LocationCode == task.SourceAddress); decimal totalReturnQty = 0; var hasRemainingLocks = remainingLocks.Any(x => x.PalletCode == palletCode); // 情况1:处理未分拣的出库锁定记录 if (hasRemainingLocks) { var palletLocks = remainingLocks.Where(x => x.PalletCode == palletCode).ToList(); totalReturnQty = palletLocks.Sum(x => x.AssignQuantity - x.PickedQty); if (totalReturnQty > 0) { // 分配新货位 var newLocation = _locationInfoService.AssignLocation(firstlocation.LocationType); // 更新出库锁定记录状态 var lockIds = palletLocks.Select(x => x.Id).ToList(); await _outStockLockInfoService.Db.Updateable() .SetColumns(it => new Dt_OutStockLockInfo { Status = OutLockStockStatusEnum.回库中.ObjToInt() }) .Where(it => lockIds.Contains(it.Id)) .ExecuteCommandAsync(); // 更新拆包条码记录状态 var splitBarcodes = await _splitPackageService.Db.Queryable() .Where(it => lockIds.Contains(it.OutStockLockInfoId)) .ToListAsync(); foreach (var splitBarcode in splitBarcodes) { splitBarcode.Status = 3; await _splitPackageService.Db.Updateable(splitBarcode).ExecuteCommandAsync(); } // 处理库存记录 foreach (var lockInfo in palletLocks) { decimal returnQty = lockInfo.AssignQuantity - lockInfo.PickedQty; // 检查库存记录是否存在 var existingStock = await _stockInfoDetailService.Db.Queryable() .Where(it => it.Barcode == lockInfo.CurrentBarcode && it.StockId == lockInfo.StockId) .FirstAsync(); if (existingStock != null) { // 库存记录存在,恢复锁定数量 await _stockInfoDetailService.Db.Updateable() .SetColumns(it => new Dt_StockInfoDetail { OutboundQuantity = it.OutboundQuantity - returnQty }) .Where(it => it.Barcode == lockInfo.CurrentBarcode && it.StockId == lockInfo.StockId) .ExecuteCommandAsync(); } else { // 库存记录不存在(可能是拆包产生的新条码),创建新的库存记录 var newStockDetail = new Dt_StockInfoDetail { StockId = lockInfo.StockId, MaterielCode = lockInfo.MaterielCode, OrderNo = lockInfo.OrderNo, BatchNo = lockInfo.BatchNo, StockQuantity = returnQty, OutboundQuantity = 0, Barcode = lockInfo.CurrentBarcode, InboundOrderRowNo = "0", Status = StockStatusEmun.入库确认.ObjToInt(), }; await _stockInfoDetailService.Db.Insertable(newStockDetail).ExecuteCommandAsync(); } } // 创建回库任务 CreateReturnTask(tasks, task, palletCode, newLocation); } } // 情况2:出库货物已分拣完,但托盘上还有其他库存货物需要回库 if (!hasRemainingLocks && palletStockGoods.Any()) { // 分配新货位 var newLocation = _locationInfoService.AssignLocation(firstlocation.LocationType); // 创建回库任务 CreateReturnTask(tasks, task, palletCode, newLocation); totalReturnQty = palletStockGoods.Sum(x => x.StockQuantity - x.OutboundQuantity); } // 保存任务 if (tasks.Any()) { try { await _taskRepository.Db.Insertable(tasks).ExecuteCommandAsync(); var targetAddress = task.TargetAddress; _taskRepository.DeleteData(task); // 给 ESS 流动信号和创建任务 try { var result = await _eSSApiService.MoveContainerAsync(new WIDESEA_DTO.Basic.MoveContainerRequest { slotCode = movestations[targetAddress], containerCode = palletCode }); if (result) { TaskModel esstask = new TaskModel() { taskType = "putaway", taskGroupCode = "", groupPriority = 0, tasks = new List { 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); } return WebResponseContent.Instance.OK($"回库操作成功,共回库数量:{totalReturnQty}"); } catch (Exception ex) { return WebResponseContent.Instance.Error($"创建回库任务失败: {ex.Message}"); } } return WebResponseContent.Instance.Error("未创建任何回库任务"); } catch (Exception ex) { return WebResponseContent.Instance.Error($"回库操作失败: {ex.Message}"); } } /// /// 创建回库任务 /// private void CreateReturnTask(List tasks, Dt_Task originalTask, string palletCode, Dt_LocationInfo newLocation) { Dt_Task newTask = new() { CurrentAddress = stations[originalTask.TargetAddress], Grade = 0, PalletCode = palletCode, NextAddress = "", OrderNo = originalTask.OrderNo, Roadway = newLocation.RoadwayNo, SourceAddress = stations[originalTask.TargetAddress], TargetAddress = newLocation.LocationCode, TaskStatus = TaskStatusEnum.New.ObjToInt(), TaskType = TaskTypeEnum.InPick.ObjToInt(), PalletType = originalTask.PalletType, WarehouseId = originalTask.WarehouseId, }; tasks.Add(newTask); } /// /// 检查托盘是否需要回库的辅助方法 /// public async Task CheckPalletNeedReturn(string orderNo, string palletCode) { // 1. 检查是否有未分拣的出库记录 var hasUnpickedLocks = await _outStockLockInfoService.Db.Queryable() .Where(it => it.OrderNo == orderNo && it.PalletCode == palletCode && it.Status == 1) .AnyAsync(); if (hasUnpickedLocks) return true; // 2. 检查出库是否已完成但托盘还有库存货物 var outboundFinished = !await _outStockLockInfoService.Db.Queryable() .Where(it => it.PalletCode == palletCode && it.Status == 1) .AnyAsync(); var stockinfo = _stockInfoService.Db.Queryable().First(x => x.PalletCode == palletCode); var hasRemainingGoods = await _stockInfoDetailService.Db.Queryable() .Where(it => it.StockId == stockinfo.Id && it.Status == StockStatusEmun.入库确认.ObjToInt()) .Where(it => it.OutboundQuantity == 0 || it.OutboundQuantity < it.StockQuantity) .AnyAsync(); return outboundFinished && hasRemainingGoods; } // 取消拣选功能 public async Task CancelPicking(string orderNo, string palletCode, string barcode) { try { _unitOfWorkManage.BeginTran(); // 查找拣选记录 var outStockInfo = await _outStockLockInfoService.Db.Queryable() .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() .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() .Where(x => x.Id == outStockInfo.OrderDetailId) .FirstAsync(); orderDetail.OverOutQuantity -= outStockInfo.AssignQuantity; orderDetail.PickedQty = 0; await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync(); // 删除拣选历史 await Db.Deleteable() .Where(x => x.OutStockLockId == outStockInfo.Id) .ExecuteCommandAsync(); _unitOfWorkManage.CommitTran(); return WebResponseContent.Instance.OK("取消拣选成功"); } catch (Exception ex) { return WebResponseContent.Instance.Error($"取消拣选失败:{ex.Message}"); } } // 获取未拣选列表 public async Task> GetUnpickedList(string orderNo, string palletCode) { var list = await _outStockLockInfoService.Db.Queryable() .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode && x.Status == 1) .ToListAsync(); return list.Where(x => x.RemainQuantity > 0).ToList(); } // 获取已拣选列表 public async Task> GetPickedList(string orderNo, string palletCode) { var list = await _outStockLockInfoService.Db.Queryable() .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode && x.Status == 6) .ToListAsync(); return list; } // 获取拣选汇总 public async Task GetPickingSummary(ConfirmPickingDto dto) { var picked = await _outStockLockInfoService.Db.Queryable() .WhereIF(!string.IsNullOrEmpty(dto.OrderNo), x => x.OrderNo == dto.OrderNo) .WhereIF(!string.IsNullOrEmpty(dto.PalletCode), x => x.PalletCode == dto.PalletCode) .Where(x => x.Status == 6) .GroupBy(x => new { x.PalletCode, x.MaterielCode }) .Select(x => new SummaryPickingDto { PalletCode = x.PalletCode, MaterielCode = x.MaterielCode, pickedCount = SqlFunc.AggregateCount(x.Id) }).FirstAsync(); if (picked == null) { picked = new SummaryPickingDto { pickedCount = 0 }; } var summary = await _outStockLockInfoService.Db.Queryable() .WhereIF(!string.IsNullOrEmpty(dto.OrderNo), x => x.OrderNo == dto.OrderNo) .WhereIF(!string.IsNullOrEmpty(dto.PalletCode), x => x.PalletCode == dto.PalletCode) .Where(x => x.Status == 1) .GroupBy(x => new { x.PalletCode, x.MaterielCode }) .Select(x => new SummaryPickingDto { PalletCode = x.PalletCode, MaterielCode = x.MaterielCode, UnpickedCount = SqlFunc.AggregateCount(x.Id), UnpickedQuantity = SqlFunc.AggregateSum(x.AssignQuantity) - SqlFunc.AggregateSum(x.PickedQty), }).FirstAsync(); if (summary == null) { summary = new SummaryPickingDto { pickedCount = 0 }; } summary.pickedCount = picked.pickedCount; return summary; } /// /// 获取拣选历史 /// public async Task> GetPickingHistory(int orderId) { // 通过出库单ID查询相关的拣选历史 // 注意:Dt_PickingRecord 中没有直接存储OrderId,需要通过出库单明细关联 var detailIds = await _outboundOrderDetailService.Db.Queryable() .Where(d => d.OrderId == orderId) .Select(d => d.Id) .ToListAsync(); return await Db.Queryable() .Where(p => detailIds.Contains(p.OrderDetailId)) .OrderByDescending(p => p.PickTime) .ToListAsync(); } /// /// 获取托盘的出库状态信息 /// public async Task GetPalletOutboundStatus(string palletCode) { // 获取托盘的锁定信息 var lockInfos = await _outStockLockInfoService.Db.Queryable() .Where(x => x.PalletCode == palletCode) .ToListAsync(); // 获取托盘库存信息 var stockInfo = await _stockInfoService.Db.Queryable() .Includes(x => x.Details) .Where(x => x.PalletCode == palletCode) .FirstAsync(); if (stockInfo == null) return WebResponseContent.Instance.Error("未找到托盘信息"); // 计算各种数量 var totalStockQuantity = stockInfo.Details.Sum(x => x.StockQuantity); var totalOutboundQuantity = stockInfo.Details.Sum(x => x.OutboundQuantity); var totalLockedQuantity = lockInfos.Where(x => x.Status == (int)OutLockStockStatusEnum.出库中) .Sum(x => x.AssignQuantity - x.PickedQty); var totalPickedQuantity = lockInfos.Sum(x => x.PickedQty); var result = new { PalletCode = palletCode, LocationCode = stockInfo.LocationCode, StockStatus = stockInfo.StockStatus, TotalStockQuantity = totalStockQuantity, TotalOutboundQuantity = totalOutboundQuantity, TotalLockedQuantity = totalLockedQuantity, TotalPickedQuantity = totalPickedQuantity, AvailableQuantity = totalStockQuantity - totalOutboundQuantity, LockInfos = lockInfos.Select(x => new { x.Id, x.MaterielCode, x.OrderDetailId, x.AssignQuantity, x.PickedQty, x.Status, x.CurrentBarcode, x.IsSplitted }).ToList(), StockDetails = stockInfo.Details.Select(x => new { x.Barcode, x.MaterielCode, StockQuantity = x.StockQuantity, OutboundQuantity = x.OutboundQuantity, AvailableQuantity = x.StockQuantity - x.OutboundQuantity }).ToList() }; return WebResponseContent.Instance.OK(null, result); } /// /// 直接出库 - 整个托盘出库,清空库存 /// public async Task DirectOutbound(DirectOutboundRequest request) { try { _unitOfWorkManage.BeginTran(); var stockInfo = await _stockInfoService.Db.Queryable() .Includes(x => x.Details) .Where(x => x.PalletCode == request.PalletCode).FirstAsync(); if (stockInfo == null) return WebResponseContent.Instance.Error("未找到托盘库存信息"); var lockInfos = await _outStockLockInfoService.Db.Queryable() .Where(x => x.OrderNo == request.OrderNo && x.PalletCode == request.PalletCode) .ToListAsync(); foreach (var lockInfo in lockInfos) { if (lockInfo.Status == (int)OutLockStockStatusEnum.出库中) { lockInfo.PickedQty = lockInfo.AssignQuantity; } lockInfo.Status = (int)OutLockStockStatusEnum.已出库; await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync(); var orderDetail = await _outboundOrderDetailService.Db.Queryable() .Where(x => x.Id == lockInfo.OrderDetailId) .FirstAsync(); if (orderDetail != null) { orderDetail.OverOutQuantity += lockInfo.PickedQty; orderDetail.LockQuantity -= lockInfo.PickedQty; orderDetail.OrderDetailStatus = (int)OrderDetailStatusEnum.Over; orderDetail.LockQuantity = 0; await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync(); } } var groupDetails = lockInfos.GroupBy(x => x.OrderDetailId).Select(x => new { OrderDetailId = x.Key, TotalQuantity = x.Sum(o => o.PickedQty) }).ToList(); foreach (var item in groupDetails) { var orderDetail = await _outboundOrderDetailService.Db.Queryable().Where(x => x.Id == item.OrderDetailId).FirstAsync(); if (orderDetail != null) { orderDetail.OverOutQuantity = item.TotalQuantity; orderDetail.LockQuantity = 0; orderDetail.OrderDetailStatus = (int)OrderDetailStatusEnum.Over; await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync(); } } await CheckAndUpdateOrderStatus(request.OrderNo); var lockInfoIds = lockInfos.Select(x => x.Id).ToList(); var splitRecords = await _splitPackageService.Db.Queryable() .Where(x => lockInfoIds.Contains(x.OutStockLockInfoId) && x.Status == (int)SplitPackageStatusEnum.已拆包) .ToListAsync(); foreach (var record in splitRecords) { record.Status = (int)SplitPackageStatusEnum.已拣选; await _splitPackageService.Db.Updateable(record).ExecuteCommandAsync(); } var location = await _locationInfoService.Db.Queryable() .Where(x => x.LocationCode == stockInfo.LocationCode) .FirstAsync(); if (location != null) { location.LocationStatus = (int)LocationStatusEnum.Free; await _locationInfoService.Db.Updateable(location).ExecuteCommandAsync(); } foreach (var detail in stockInfo.Details) { await _stockInfoDetailService.Db.Deleteable(detail).ExecuteCommandAsync(); } await _stockInfoService.Db.Deleteable(stockInfo).ExecuteCommandAsync(); _unitOfWorkManage.CommitTran(); return WebResponseContent.Instance.OK("直接出库成功"); } catch (Exception ex) { _unitOfWorkManage.RollbackTran(); return WebResponseContent.Instance.Error($"直接出库失败: {ex.Message}"); } } } }