using Microsoft.Extensions.Logging; using Newtonsoft.Json; using OfficeOpenXml.FormulaParsing.Excel.Functions.RefAndLookup; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using WIDESEA_BasicService; using WIDESEA_Common.CommonEnum; using WIDESEA_Common.LocationEnum; using WIDESEA_Common.OrderEnum; using WIDESEA_Common.OtherEnum; using WIDESEA_Common.StockEnum; using WIDESEA_Common.TaskEnum; using WIDESEA_Core; using WIDESEA_Core.Helper; using WIDESEA_DTO.Basic; using WIDESEA_DTO.Stock; using WIDESEA_Model.Models; using WIDESEA_Model.Models.Basic; using WIDESEA_Model.Models.Check; using WIDESEA_Model.Models.Outbound; namespace WIDESEA_TaskInfoService { public partial class TaskService { /// /// 空托盘出库任务 /// /// /// public async Task PalletOutboundTask(int num) { WebResponseContent content = new WebResponseContent(); try { var stockInfos = _stockRepository.Db.Queryable().Where(x => x.PalletType == PalletTypeEnum.Empty.ObjToInt() && x.StockStatus == StockStatusEmun.入库完成.ObjToInt()).ToList(); if (stockInfos.Count() == 0) { return WebResponseContent.Instance.Error("未找到空托盘库存"); } for (int i = 0; i < num; i++) { var stockInfo = stockInfos.Where(x=>x.StockStatus != StockStatusEmun.出库锁定.ObjToInt()).FirstOrDefault(); Dt_LocationInfo locationInfo = _locationInfoService.Repository.QueryFirst(x => x.LocationCode == stockInfo.LocationCode); if (locationInfo == null) { return WebResponseContent.Instance.Error("未找到空托盘库存对应的货位信息"); } Dt_Task task = new Dt_Task() { CurrentAddress = stockInfo.LocationCode, Grade = 0, NextAddress = "1-2", PalletCode = stockInfo.PalletCode, Roadway = locationInfo.RoadwayNo, SourceAddress = stockInfo.LocationCode, TargetAddress = "1-2", TaskStatus = TaskStatusEnum.New.ObjToInt(), TaskType = TaskTypeEnum.OutEmpty.ObjToInt(), WarehouseId = stockInfo.WarehouseId, PalletType = stockInfo.PalletType }; int beforeStatus = locationInfo.LocationStatus; _unitOfWorkManage.BeginTran(); stockInfo.StockStatus = StockStatusEmun.出库锁定.ObjToInt(); locationInfo.LocationStatus = LocationStatusEnum.Lock.ObjToInt(); int taskId = BaseDal.AddData(task); task.TaskId = taskId; _stockService.StockInfoService.UpdateData(stockInfo); _locationInfoService.UpdateData(locationInfo); _recordService.LocationStatusChangeRecordSetvice.AddLocationStatusChangeRecord(locationInfo, beforeStatus, StockChangeType.Outbound.ObjToInt(), "", task.TaskNum); _unitOfWorkManage.CommitTran(); TaskModel esstask = new TaskModel() { taskType = "carry", taskGroupCode = "", groupPriority = 0, tasks = new List { new() { taskCode=task.TaskNum.ToString(), taskPriority=0, taskDescribe=new TaskDescribeType{ containerCode=stockInfo.PalletCode, containerType= "CT_KUBOT_STANDARD", fromLocationCode=stockInfo.LocationCode??"", toStationCode="", toLocationCode="1-2", deadline=0,storageTag="" } } } }; var result = await _eSSApiService.CreateTaskAsync(esstask); _logger.LogInformation("创建任务PalletOutboundTask 返回: " + result); if (result) { return WebResponseContent.Instance.OK(200); } else { return WebResponseContent.Instance.Error("下发机器人任务失败!"); } } return content.OK("空托出库成功!"); } catch (Exception ex) { return WebResponseContent.Instance.Error(ex.Message); } } /// /// 出库任务数据处理 /// /// /// /// /// public (List, List?, List?, List?, List?) OutboundTaskDataHandle(int[] keys, string outStation) { List tasks = new List(); List outboundOrderDetails = _outboundOrderDetailService.Repository.QueryData(x => keys.Contains(x.Id)); if (outboundOrderDetails == null || outboundOrderDetails.Count == 0) { throw new Exception("未找到出库单明细信息"); } //if (outboundOrderDetails.FirstOrDefault(x => x.OrderDetailStatus > OrderDetailStatusEnum.New.ObjToInt() && x.OrderDetailStatus != OrderDetailStatusEnum.AssignOverPartial.ObjToInt()) != null) //{ // throw new Exception("所选出库单明细存在出库中或已完成"); //} if (outboundOrderDetails.FirstOrDefault(x => x.OrderDetailStatus > OrderDetailStatusEnum.Outbound.ObjToInt() && x.OrderDetailStatus != OrderDetailStatusEnum.AssignOverPartial.ObjToInt()) != null) { throw new Exception("所选出库单明细存在已完成状态,无法重新分配"); } List? stockInfos = null; List? orderDetails = null; List? outStockLockInfos = null; List? locationInfos = null; CleanupPreviousInvalidLocks(outboundOrderDetails); (List, List, List, List) result = _outboundOrderDetailService.AssignStockOutbound(outboundOrderDetails); if (result.Item1 != null && result.Item1.Count > 0) { Dt_OutboundOrder outboundOrder = _outboundOrderService.Repository.QueryFirst(x => x.Id == outboundOrderDetails.FirstOrDefault().OrderId); TaskTypeEnum typeEnum = outboundOrder.OrderType switch { (int)OutOrderTypeEnum.Issue => TaskTypeEnum.Outbound, (int)OutOrderTypeEnum.Allocate => TaskTypeEnum.OutAllocate, (int)OutOrderTypeEnum.Quality => TaskTypeEnum.OutQuality, _ => TaskTypeEnum.Outbound }; tasks = GetTasks(result.Item1, typeEnum, outStation); tasks.ForEach(x => { x.OrderNo = outboundOrder.OrderNo; }); result.Item2.ForEach(x => { x.OrderDetailStatus = OrderDetailStatusEnum.Outbound.ObjToInt(); }); result.Item3.ForEach(x => { x.Status = OutLockStockStatusEnum.出库中.ObjToInt(); }); stockInfos = result.Item1; orderDetails = result.Item2; outStockLockInfos = result.Item3; locationInfos = result.Item4; } else { throw new Exception("无库存"); } return (tasks, stockInfos, orderDetails, outStockLockInfos, locationInfos); } /// /// 清理之前的无效锁定记录 /// private void CleanupPreviousInvalidLocks(List orderDetails) { var orderIds = orderDetails.Select(x => x.OrderId).Distinct().ToList(); var orderNos = _outboundOrderService.Db.Queryable() .Where(x => orderIds.Contains(x.Id)) .Select(x => x.OrderNo) .ToList(); // 清理状态为"已释放"或"回库中"的旧锁定记录 foreach (var orderNo in orderNos) { _outStockLockInfoService.Db.Updateable() .SetColumns(x => new Dt_OutStockLockInfo { Status = (int)OutLockStockStatusEnum.已释放 }) .Where(x => x.OrderNo == orderNo && (x.Status == (int)OutLockStockStatusEnum.回库中 || x.Status == (int)OutLockStockStatusEnum.已释放)) .ExecuteCommand(); } } /// /// 生成出库任务后数据更新到数据库 /// /// /// /// /// /// /// public async Task GenerateOutboundTaskDataUpdateAsync(List tasks, List? stockInfos = null, List? outboundOrderDetails = null, List? outStockLockInfos = null, List? locationInfos = null) { try { _unitOfWorkManage.BeginTran(); BaseDal.AddData(tasks); if (stockInfos != null && stockInfos.Count > 0 && outboundOrderDetails != null && outboundOrderDetails.Count > 0 && outStockLockInfos != null && outStockLockInfos.Count > 0 && locationInfos != null && locationInfos.Count > 0) { stockInfos.ForEach(x => { x.StockStatus = StockStatusEmun.出库锁定.ObjToInt(); }); outboundOrderDetails.ForEach(x => { x.OrderDetailStatus = OrderDetailStatusEnum.Outbound.ObjToInt(); }); Dt_OutboundOrder outboundOrder = _outboundOrderService.Repository.QueryFirst(x => x.Id == outboundOrderDetails.FirstOrDefault().OrderId); if (outboundOrder.OrderStatus != OutOrderStatusEnum.出库中.ObjToInt()) { _outboundOrderService.Repository.UpdateData(outboundOrder); } else { outboundOrder.OrderStatus = OutOrderStatusEnum.出库中.ObjToInt(); } outboundOrder.Operator = App.User.UserName; _outboundOrderService.Repository.UpdateData(outboundOrder); WebResponseContent content = _outboundOrderDetailService.LockOutboundStockDataUpdate(stockInfos, outboundOrderDetails, outStockLockInfos, locationInfos, tasks: tasks); if (!content.Status) { _unitOfWorkManage.RollbackTran(); return content; } } else if (outboundOrderDetails != null && outboundOrderDetails.Count > 0) { outboundOrderDetails.ForEach(x => { x.OrderDetailStatus = OrderDetailStatusEnum.Outbound.ObjToInt(); }); Dt_OutboundOrder outboundOrder = _outboundOrderService.Repository.QueryFirst(x => x.Id == outboundOrderDetails.FirstOrDefault().OrderId); if (outboundOrder.OrderStatus != OutOrderStatusEnum.出库中.ObjToInt()) { _outboundOrderService.Repository.UpdateData(outboundOrder); } else { outboundOrder.OrderStatus = OutOrderStatusEnum.出库中.ObjToInt(); } outboundOrder.Operator = App.User.UserName; _outboundOrderService.Repository.UpdateData(outboundOrder); _outboundOrderDetailService.Repository.UpdateData(outboundOrderDetails); } _unitOfWorkManage.CommitTran(); TaskModel esstask = new TaskModel() { taskType = "carry", taskGroupCode = "", groupPriority = 0, tasks = new List() }; foreach (var task in tasks) { esstask. tasks.Add(new TasksType { taskCode = task.TaskNum.ToString(), taskPriority = 0, taskDescribe = new TaskDescribeType { containerCode = task.PalletCode, containerType = "CT_KUBOT_STANDARD", fromLocationCode = task.SourceAddress ?? "", toStationCode = "", toLocationCode = task.TargetAddress, deadline = 0, storageTag = "" } } ); } var result = await _eSSApiService.CreateTaskAsync(esstask); _logger.LogInformation("创建任务PalletOutboundTask 返回: " + result); if (result) { return WebResponseContent.Instance.OK(); } else { return WebResponseContent.Instance.Error("下发机器人任务失败!"); } } catch (Exception ex) { _unitOfWorkManage.RollbackTran(); return WebResponseContent.Instance.Error(ex.Message); } } /// /// 库存数据转出库任务 /// /// /// public List GetTasks(List stockInfos, TaskTypeEnum taskType, string outStation) { List tasks = new List(); List locationInfos = _locationInfoService.Repository.QueryData(x => stockInfos.Select(x => x.LocationCode).Contains(x.LocationCode)); for (int i = 0; i < stockInfos.Count; i++) { Dt_StockInfo stockInfo = stockInfos[i]; if (stockInfo != null) { Dt_LocationInfo? locationInfo = locationInfos.FirstOrDefault(x => x.LocationCode == stockInfo.LocationCode); if (!tasks.Exists(x => x.PalletCode == stockInfo.PalletCode)) { Dt_Task task = new() { CurrentAddress = stockInfo.LocationCode, Grade = 0, PalletCode = stockInfo.PalletCode, NextAddress = "", Roadway = locationInfo.RoadwayNo, SourceAddress = stockInfo.LocationCode, TargetAddress = outStation, TaskStatus = TaskStatusEnum.New.ObjToInt(), TaskType = taskType.ObjToInt(), // TaskNum = BaseDal.GetTaskNum(nameof(SequenceEnum.SeqTaskNum)), PalletType = stockInfo.PalletType, WarehouseId = stockInfo.WarehouseId, }; //if (taskType != TaskTypeEnum.OutEmpty) //{ // task.MaterielCode = stockInfo.Details?.Where(x => x.StockId == stockInfo.Id).FirstOrDefault()?.MaterielCode; // task.Quantity = (float)stockInfo.Details?.Where(x => x.StockId == stockInfo.Id).Sum(x => x.StockQuantity); // task.BatchNo = stockInfo.Details?.Where(x => x.StockId == stockInfo.Id).FirstOrDefault()?.BatchNo; //} tasks.Add(task); } } } return tasks; } public List GetTasks(List stockInfos, TaskTypeEnum taskType) { List tasks = new List(); List locationInfos = _locationInfoService.Repository.QueryData(x => stockInfos.Select(x => x.LocationCode).Contains(x.LocationCode)); for (int i = 0; i < stockInfos.Count; i++) { Dt_StockInfo stockInfo = stockInfos[i]; if (stockInfo != null) { Dt_LocationInfo? locationInfo = locationInfos.FirstOrDefault(x => x.LocationCode == stockInfo.LocationCode); if (!tasks.Exists(x => x.PalletCode == stockInfo.PalletCode)) { Dt_Task task = new() { CurrentAddress = stockInfo.LocationCode, Grade = 0, PalletCode = stockInfo.PalletCode, NextAddress = "", Roadway = locationInfo.RoadwayNo, SourceAddress = stockInfo.LocationCode, TargetAddress = "", TaskStatus = TaskStatusEnum.New.ObjToInt(), TaskType = taskType.ObjToInt(), //TaskNum = BaseDal.GetTaskNum(nameof(SequenceEnum.SeqTaskNum)), PalletType = stockInfo.PalletType, WarehouseId = stockInfo.WarehouseId, }; //if (taskType != TaskTypeEnum.OutEmpty) //{ // task.MaterielCode = stockInfo.Details?.Where(x => x.StockId == stockInfo.Id).FirstOrDefault()?.MaterielCode; // task.Quantity = (float)stockInfo.Details?.Where(x => x.StockId == stockInfo.Id).Sum(x => x.StockQuantity); // task.BatchNo = stockInfo.Details?.Where(x => x.StockId == stockInfo.Id).FirstOrDefault()?.BatchNo; //} //if (stockInfo.StockLength > 0) //{ // task.TaskLength = stockInfo.StockLength; //} tasks.Add(task); } } } return tasks; } /// /// 生成出库任务 /// /// 出库单明细主键 /// public async Task GenerateOutboundTasksAsync(int[] keys, string outStation) { try { List tasks = new List(); List stockSelectViews = new List(); List stockInfos = new List(); List outboundOrderDetails = new List(); List outStockLockInfos = new List(); List locationInfos = new List(); (List, List?, List?, List?, List?) result = OutboundTaskDataHandle(keys, outStation); if (result.Item2 != null && result.Item2.Count > 0) { stockInfos.AddRange(result.Item2); } if (result.Item3 != null && result.Item3.Count > 0) { outboundOrderDetails.AddRange(result.Item3); } if (result.Item4 != null && result.Item4.Count > 0) { outStockLockInfos.AddRange(result.Item4); } if (result.Item5 != null && result.Item5.Count > 0) { locationInfos.AddRange(result.Item5); } if (result.Item1 != null && result.Item1.Count > 0) { tasks.AddRange(result.Item1); } WebResponseContent content = await GenerateOutboundTaskDataUpdateAsync(tasks, stockInfos, outboundOrderDetails, outStockLockInfos, locationInfos); return content; } catch (Exception ex) { _unitOfWorkManage.RollbackTran(); return WebResponseContent.Instance.Error(ex.Message); } } public async Task GenerateAllocatOutboundTask(int orderDetailId, List stockSelectViews) { try { var allocorder = _allocateOrderRepository.Db.Queryable().Includes(x => x.Details).Where(x => x.Details.Any(o => o.Id == orderDetailId)).First(); //var allocorder = _allocateOrderDetailRepository.Db.Queryable() // .LeftJoin((detail, order) => detail.OrderId == order.Id) // .Where((detail, order) => order.Id == orderDetailId) // .Select((detail, order) => order) // .First(); if (allocorder == null) { return WebResponseContent.Instance.Error("找不到单据"); } var outboundOrder = _outboundOrderService.Db.Queryable().Includes(x => x.Details).First(x => x.OrderNo == allocorder.OrderNo); if (outboundOrder == null) { return WebResponseContent.Instance.Error("找不到出库单据"); } var orderdetail = outboundOrder.Details.Where(outItem => allocorder.Details .Any(allocItem => allocItem.MaterielCode == outItem.MaterielCode && allocItem.LineNo == outItem.lineNo && allocItem.BarcodeQty==outItem.BarcodeQty && allocItem.WarehouseCode==outItem.WarehouseCode && allocItem.BarcodeUnit==outItem.BarcodeUnit) ) .First(); if (orderdetail == null) { return WebResponseContent.Instance.Error("找不到出库明细单据"); } (List, List?, List?, List?, List?) result = OutboundTaskDataHandle(outboundOrder.Details.First().Id, stockSelectViews); WebResponseContent content =await GenerateOutboundTaskDataUpdate(result.Item1, result.Item2, result.Item3, result.Item4, result.Item5); return content; } catch (Exception ex) { return WebResponseContent.Instance.Error(ex.Message); } } /// /// 生成出库任务 /// /// /// /// public async Task GenerateOutboundTask(int orderDetailId, List stockSelectViews,string station=null) { try { var orderNo = _reCheckOrderRepository.Db.Queryable().First(x => x.Id == orderDetailId)?.OrderNo; var outboundOrder = _outboundOrderService.Db.Queryable().Includes(x => x.Details).First(x => x.UpperOrderNo == orderNo); if (outboundOrder == null) { return WebResponseContent.Instance.Error("找不到单据"); } (List, List?, List?, List?, List?) result = OutboundTaskDataHandle(outboundOrder.Details.First().Id, stockSelectViews,station); WebResponseContent content =await GenerateOutboundTaskDataUpdate(result.Item1, result.Item2, result.Item3, result.Item4, result.Item5); return content; } catch (Exception ex) { return WebResponseContent.Instance.Error(ex.Message); } } /// /// 出库任务数据处理 /// /// /// /// /// public (List, List?, List?, List?, List?) OutboundTaskDataHandle(int orderDetailId, List stockSelectViews,string station=null) { List tasks = new List(); Dt_OutboundOrderDetail outboundOrderDetail = _outboundOrderDetailService.Repository.QueryFirst(x => x.Id == orderDetailId); if (outboundOrderDetail == null) { throw new Exception("未找到出库单明细信息"); } //if (stockSelectViews.Sum(x => x.UseableQuantity) > outboundOrderDetail.OrderQuantity - outboundOrderDetail.LockQuantity) //{ // throw new Exception("选择数量超出单据数量"); //} List? stockInfos = null; Dt_OutboundOrderDetail? orderDetail = null; List? outStockLockInfos = null; List? locationInfos = null; if (outboundOrderDetail.OrderDetailStatus == OrderDetailStatusEnum.New.ObjToInt()) { (List, Dt_OutboundOrderDetail, List, List) result = _outboundOrderDetailService.AssignStockOutbound(outboundOrderDetail, stockSelectViews); if (result.Item1 != null && result.Item1.Count > 0) { Dt_OutboundOrder outboundOrder = _outboundOrderService.Repository.QueryFirst(x => x.Id == outboundOrderDetail.OrderId); TaskTypeEnum typeEnum = outboundOrder.OrderType switch { (int)OutOrderTypeEnum.Issue => TaskTypeEnum.Outbound, (int)OutOrderTypeEnum.Allocate => TaskTypeEnum.OutAllocate, (int)OutOrderTypeEnum.Quality => TaskTypeEnum.OutQuality, _ => TaskTypeEnum.Outbound }; tasks = GetTasks(result.Item1, typeEnum,station); result.Item2.OrderDetailStatus = OrderDetailStatusEnum.Outbound.ObjToInt(); result.Item3.ForEach(x => { x.Status = OutLockStockStatusEnum.出库中.ObjToInt(); }); stockInfos = result.Item1; orderDetail = result.Item2; outStockLockInfos = result.Item3; locationInfos = result.Item4; } else { throw new Exception("无库存"); } } else { List stockLockInfos = _outStockLockInfoService.GetByOrderDetailId(outboundOrderDetail.OrderId, OutLockStockStatusEnum.已分配); if (stockLockInfos != null && stockLockInfos.Count > 0) { List stocks = _stockService.StockInfoService.GetStockInfosByPalletCodes(stockLockInfos.Select(x => x.PalletCode).Distinct().ToList()); tasks = GetTasks(stocks, TaskTypeEnum.Outbound); } } return (tasks, stockInfos, orderDetail == null ? null : new List { orderDetail }, outStockLockInfos, locationInfos); } /// /// 生成出库任务后数据更新到数据库 /// /// /// /// /// /// /// public async Task< WebResponseContent> GenerateOutboundTaskDataUpdate(List tasks, List? stockInfos = null, List? outboundOrderDetails = null, List? outStockLockInfos = null, List? locationInfos = null) { try { _unitOfWorkManage.BeginTran(); BaseDal.AddData(tasks); if (stockInfos != null && stockInfos.Count > 0 && outboundOrderDetails != null && outboundOrderDetails.Count > 0 && outStockLockInfos != null && outStockLockInfos.Count > 0 && locationInfos != null && locationInfos.Count > 0) { stockInfos.ForEach(x => { x.StockStatus = StockStatusEmun.出库锁定.ObjToInt(); }); outboundOrderDetails.ForEach(x => { x.OrderDetailStatus = OrderDetailStatusEnum.Outbound.ObjToInt(); }); Dt_OutboundOrder outboundOrder = _outboundOrderService.Repository.QueryFirst(x => x.Id == outboundOrderDetails.FirstOrDefault().OrderId); if (outboundOrder.OrderStatus != OutOrderStatusEnum.出库中.ObjToInt()) { _outboundOrderService.Repository.UpdateData(outboundOrder); } WebResponseContent content = _outboundOrderDetailService.LockOutboundStockDataUpdate(stockInfos, outboundOrderDetails, outStockLockInfos, locationInfos, tasks: tasks); if (!content.Status) { _unitOfWorkManage.RollbackTran(); return content; } } else if (outboundOrderDetails != null && outboundOrderDetails.Count > 0) { outboundOrderDetails.ForEach(x => { x.OrderDetailStatus = OrderDetailStatusEnum.Outbound.ObjToInt(); }); Dt_OutboundOrder outboundOrder = _outboundOrderService.Repository.QueryFirst(x => x.Id == outboundOrderDetails.FirstOrDefault().OrderId); if (outboundOrder.OrderStatus != OutOrderStatusEnum.出库中.ObjToInt()) { _outboundOrderService.Repository.UpdateData(outboundOrder); } _outboundOrderDetailService.Repository.UpdateData(outboundOrderDetails); } _unitOfWorkManage.CommitTran(); //PushTasksToWCS(tasks); TaskModel esstask = new TaskModel() { taskType = "carry", taskGroupCode = "", groupPriority = 0, tasks = new List() }; foreach (var task in tasks) { esstask. tasks.Add(new TasksType { taskCode = task.TaskNum.ToString(), taskPriority = 0, taskDescribe = new TaskDescribeType { containerCode = task.PalletCode, containerType = "CT_KUBOT_STANDARD", fromLocationCode = task.SourceAddress ?? "", toStationCode = "", toLocationCode = task.TargetAddress, deadline = 0, storageTag = "" } } ); } var result = await _eSSApiService.CreateTaskAsync(esstask); _logger.LogInformation("创建任务PalletOutboundTask 返回: " + result); if (result) { return WebResponseContent.Instance.OK(); } else { return WebResponseContent.Instance.Error("下发机器人任务失败!"); } } catch (Exception ex) { _unitOfWorkManage.RollbackTran(); return WebResponseContent.Instance.Error(ex.Message); } } #region 分批分配库存 /// /// 分批分配库存 /// public async Task GenerateOutboundBatchTasksAsync(int orderDetailId, decimal batchQuantity, string outStation) { try { List tasks = new List(); List stockInfos = new List(); List outboundOrderDetails = new List(); List outStockLockInfos = new List(); List locationInfos = new List(); (List, List?, List?, List?, List?) result = await BatchAllocateStockDataHandle(orderDetailId, batchQuantity, outStation); if (result.Item2 != null && result.Item2.Count > 0) { stockInfos.AddRange(result.Item2); } if (result.Item3 != null && result.Item3.Count > 0) { outboundOrderDetails.AddRange(result.Item3); } if (result.Item4 != null && result.Item4.Count > 0) { outStockLockInfos.AddRange(result.Item4); } if (result.Item5 != null && result.Item5.Count > 0) { locationInfos.AddRange(result.Item5); } if (result.Item1 != null && result.Item1.Count > 0) { tasks.AddRange(result.Item1); } WebResponseContent content = await GenerateOutboundTaskDataUpdateAsync(tasks, stockInfos, outboundOrderDetails, outStockLockInfos, locationInfos); return content; } catch (Exception ex) { _unitOfWorkManage.RollbackTran(); _logger.LogError($"分批分配库存失败 - OrderDetailId: {orderDetailId}, Quantity: {batchQuantity}, Error: {ex.Message}"); return WebResponseContent.Instance.Error($"分批分配失败:{ex.Message}"); } } /// /// 分批分配库存数据处理 /// public async Task<(List, List?, List?, List?, List?)> BatchAllocateStockDataHandle(int orderDetailId, decimal batchQuantity, string outStation) { List tasks = new List(); // 获取订单明细 var outboundOrderDetail = await _outboundOrderDetailService.Db.Queryable() .FirstAsync(x => x.Id == orderDetailId); if (outboundOrderDetail == null) { throw new Exception("未找到出库单明细信息"); } var outboundOrder = await _outboundOrderService.Db.Queryable().FirstAsync(x => x.Id == outboundOrderDetail.OrderId); if (outboundOrder == null) { throw new Exception("未找到出库单信息"); } // 验证订单明细状态 if (outboundOrderDetail.OrderDetailStatus > OrderDetailStatusEnum.Outbound.ObjToInt() && outboundOrderDetail.OrderDetailStatus != OrderDetailStatusEnum.AssignOverPartial.ObjToInt()) { throw new Exception("所选出库单明细存在出库中或已完成"); } // 验证分配数量 decimal allocatedQty = outboundOrderDetail.AllocatedQuantity; decimal overOutQty = outboundOrderDetail.OverOutQuantity; decimal needOutQty = outboundOrderDetail.NeedOutQuantity; decimal availableQty = needOutQty - allocatedQty - overOutQty; if (availableQty <= 0) throw new Exception("无可分配数量"); if (batchQuantity > availableQty) throw new Exception($"分配数量不能超过可分配数量{availableQty}"); List? stockInfos = null; List? orderDetails = null; List? outStockLockInfos = null; List? locationInfos = null; // 生成批次号 string batchNo = await GenerateBatchNo(); // 分配库存 (List, List, List, List) allocateResult = await _outboundOrderDetailService.AssignStockForBatch(outboundOrderDetail, batchQuantity, batchNo); if (allocateResult.Item1 != null && allocateResult.Item1.Count > 0) { // 创建分批记录 await CreateBatchRecord(outboundOrder.OrderNo, orderDetailId, batchQuantity, batchNo); TaskTypeEnum typeEnum = outboundOrder.OrderType switch { (int)OutOrderTypeEnum.Issue => TaskTypeEnum.Outbound, (int)OutOrderTypeEnum.Allocate => TaskTypeEnum.OutAllocate, (int)OutOrderTypeEnum.Quality => TaskTypeEnum.OutQuality, _ => TaskTypeEnum.Outbound }; tasks = GetTasks(allocateResult.Item1, typeEnum, outStation); tasks.ForEach(x => { x.OrderNo = outboundOrder.OrderNo; }); allocateResult.Item2.ForEach(x => { x.OrderDetailStatus = OrderDetailStatusEnum.Outbound.ObjToInt(); }); allocateResult.Item3.ForEach(x => { x.Status = OutLockStockStatusEnum.出库中.ObjToInt(); }); stockInfos = allocateResult.Item1; orderDetails = allocateResult.Item2; outStockLockInfos = allocateResult.Item3; locationInfos = allocateResult.Item4; } else { throw new Exception("无库存"); } return (tasks, stockInfos, orderDetails, outStockLockInfos, locationInfos); } /// /// 更新订单明细状态 /// private void UpdateOrderDetailStatus(List details, decimal allocatedQuantity, decimal needQuantity) { foreach (var detail in details) { // 根据分配情况更新状态 if (allocatedQuantity >= needQuantity) { detail.OrderDetailStatus = OrderDetailStatusEnum.Outbound.ObjToInt(); } else { detail.OrderDetailStatus = OrderDetailStatusEnum.AssignOverPartial.ObjToInt(); } } } private async Task GenerateBatchNo() { var batchNo = UniqueValueGenerator.Generate(); return $"Out{batchNo} "; } private async Task CreateBatchRecord(string orderNo, int orderDetailId, decimal batchQuantity, string batchNo) { var batchRecord = new Dt_OutboundBatch { BatchNo = batchNo, OrderNo = orderNo, OrderDetailId = orderDetailId, BatchQuantity = batchQuantity, BatchStatus = (int)BatchStatusEnum.分配中, Operator = App.User.UserName }; await _OutboundBatchRepository.Db.Insertable(batchRecord).ExecuteCommandAsync(); return batchRecord; } #endregion } }