using JSMachine.WMS.App.Dto; using JSMachine.WMS.App.Dto.Enum; using JSMachine.WMS.App.IService; using JSMachine.WMS.Business.Common.IService; using JSMachine.WMS.Business.StockOut.IService; using JSMachine.WMS.Business.StockOut.Models; using JSMachine.WMS.Domain.Entity; using JSMachine.WMS.Infrastructure; using JSMachine.WMS.Infrastructure.Helper; using JSMachine.WMS.PLC.ModbusImpl.Elevator.ModbusIO.Read; using JSMachine.WMS.PLC.ModbusImpl.Elevator.Model; using JSMachine.WMS.RPC.ErpRPC.Dto.In; using JSMachine.WMS.RPC.ErpRPC.Dto.In.XRBusinessResult; using JSMachine.WMS.RPC.ErpRPC.Dto.Out; using JSMachine.WMS.RPC.ErpRPC.Dto.Out.XRBusinessResult; using Microsoft.EntityFrameworkCore.Metadata.Internal; using NPOI.SS.Formula.Functions; using SqlSugar; namespace JSMachine.WMS.Business.StockOut.ServiceImpl { /// /// 出库服务类 /// public class StockOutService : IStockOutService { private IERPTaskService _erpTaskService; private IStorageRackService _storageRackService; private IAGVTaskService _agvTaskService; private ICommonService _commonService; public StockOutService( IERPTaskService erpTaskService, IStorageRackService storageRackService, IAGVTaskService agvTaskService, ICommonService commonService) { _erpTaskService = erpTaskService; _storageRackService = storageRackService; _agvTaskService = agvTaskService; _commonService = commonService; } #region 销售出库 /// /// 销售出库 /// /// /// public async Task Stockout(StockOutParam stockOutParam) { //1.验证参数,检查销售订单号是否重复 //(同批次物料库存(排除锁定禁用)+同批次erp任务(状态为未完结且一天内的)关联的agv任务总数+同批次撤单总数)-erp出库命令总数<=本次出库数 //3.生成erp任务 //1.查询物料信息是否存在 (批次、数量) //2.退库区库位是否有货(若全部有货,则全部更新成无货) //3.生成ErpTask string errMsg = string.Empty; errMsg = await ValidateStockOutParam(stockOutParam); if(!string.IsNullOrEmpty(errMsg)) { LogHelper.Warn(errMsg); return errMsg; } errMsg = await CalStockQty(stockOutParam); if (!string.IsNullOrEmpty(errMsg)) { LogHelper.Warn($"出库业务:批次号为{stockOutParam.materiaBatch}的库存不足,出库失败!"); return errMsg; } //生成ErpTask bool res = await CreateStockOutErpTask(stockOutParam); if (!res) { errMsg = "出库业务,生成Erp任务,数据库操作失败!"; LogHelper.Warn(errMsg); return errMsg; } return errMsg; } private TaskTypeEnum CalStockOutTaskType(StockOutParam stockOutParam) { TaskTypeEnum taskType = 0; if (stockOutParam.transport == (int)TransportEnum.SouthArea || stockOutParam.transport == (int)TransportEnum.NorthArea || stockOutParam.transport == (int)TransportEnum.MiddleOutArea) { taskType = TaskTypeEnum.SaleOut; } if (stockOutParam.transport == (int)TransportEnum.ReturnStockArea) { if(string.IsNullOrEmpty(stockOutParam.produceReturnCode)) stockOutParam.produceReturnCode = stockOutParam.saleNo; stockOutParam.saleNo = null; taskType = TaskTypeEnum.ProductionOut; } return taskType; } private async Task ValidateStockOutParam(StockOutParam stockOutParam) { TaskTypeEnum taskType = CalStockOutTaskType(stockOutParam); if (taskType == 0) return "出库码头值错误"; string errMsg = string.Empty; if (stockOutParam.num <= 0) { errMsg = "出库数要大于0!"; LogHelper.Warn(errMsg); } if (string.IsNullOrEmpty(stockOutParam.materiaBatch)) { errMsg = "批次号不能为空!"; LogHelper.Warn(errMsg); } if (taskType == TaskTypeEnum.SaleOut && string.IsNullOrEmpty(stockOutParam.saleNo)) { errMsg = "销售单号不能为空!"; LogHelper.Warn(errMsg); } if (taskType == TaskTypeEnum.ProductionOut && string.IsNullOrEmpty(stockOutParam.produceReturnCode)) { errMsg = "生产退库单号不能为空!"; LogHelper.Warn(errMsg); } if (taskType == TaskTypeEnum.SaleOut) { bool exist = await _erpTaskService.Exist(p => p.SaleNo == stockOutParam.saleNo); if (exist) { errMsg = $"销售单号{stockOutParam.saleNo}重复!"; LogHelper.Warn(errMsg); } } if (taskType == TaskTypeEnum.ProductionOut) { bool exist = await _erpTaskService.Exist(p => p.ProduceReturnCode == stockOutParam.produceReturnCode); if (exist) { errMsg = $"生产退库单号{stockOutParam.produceReturnCode}重复!"; LogHelper.Warn(errMsg); } } return errMsg; } private async Task CalStockQty(StockOutParam stockOutParam) { List storageRacks = await _storageRackService.GetListByExpression(p => p.MateriaBatch == stockOutParam.materiaBatch && !p.IsLock && !p.IsDisabled && p.Transport == TransportEnum.NormalArea); if(storageRacks.IsNullOrEmpty()) { return "无可用库存"; } List erpTaks = await _erpTaskService.GetListByExpression(p => p.CreationTime >= DateTime.Now.AddDays(-1) && (p.TaskType == TaskTypeEnum.SaleOut || p.TaskType == TaskTypeEnum.ProductionOut) && p.TaskStatus == TaskStatusEnum.InExecution && p.MateriaBatch == stockOutParam.materiaBatch); int stockQty= storageRacks.Sum(p=>p.MateriaNum); int taskTotalNum = 0; int cancelTotalNum = 0; if (!erpTaks.IsNullOrEmpty()) { cancelTotalNum = erpTaks.Sum(p=>p.ErpCancelNum); List agvtTasks = await _agvTaskService .GetListByExpression(p => erpTaks.Select(x => x.Id).Contains(p.ErpTaskId)); taskTotalNum = agvtTasks.GroupBy(p=>p.BarCode).Select(p=>p.First()).Sum(p=>p.MateriaNum); } bool enough = stockQty + taskTotalNum + cancelTotalNum - erpTaks.Sum(p => p.ErpOrderNum) >= stockOutParam.num; return enough ? "" : "库存不足"; } /// /// 根据销售出库单,扣减库存 /// /// /// /// private async Task GetSumStockNum(string saleNo, TaskTypeEnum taskType) { var stockResult = new SaleOutStockDto(); var erpTask = new ERPTaskDto(); //销售出库 if (taskType == TaskTypeEnum.SaleOut) { erpTask = await _erpTaskService.GetSingalByExpression(p => p.SaleNo == saleNo); } //生产退库 else if (taskType == TaskTypeEnum.ProductionOut) { erpTask = await _erpTaskService.GetSingalByExpression(p => p.ProduceReturnCode == saleNo); } if (erpTask != null) { var erpOrderNum = erpTask.ErpOrderNum; stockResult.SumCancelNum = erpTask.ErpCancelNum; stockResult.SumReulstNum = erpOrderNum - erpTask.ErpCancelNum; } return stockResult; } /// /// 销售出库,计算目标库位 /// /// /// public async Task CalculateDestWarehouse_SaleOut(TransportEnum transport) { //1 出库区是否有同批次货物,有则查看其列上是否有空余库位 //2 锁定目标库位 StorageRackDto destStorageRack = null; if (transport==TransportEnum.MiddleOutArea) { destStorageRack = await CalculateDestWarehouse_SaleOutMiddle(); return destStorageRack; } //所有出库区库位 List wareHouses = await _storageRackService.GetListByExpression(p => p.Floor == FloorEnum.FirstFloor && p.Transport == transport); //没有空库位 if (wareHouses.All(p => p.IsLock || p.IsDisabled)) { var clearWardHoses = wareHouses.OrderBy(p => p.AreaNumSort).ToList(); foreach (var item in clearWardHoses) { item.MateriaBatch = ""; item.MateriaNum = 0; item.OrderNo = ""; item.MateriaName = ""; item.MateriaType = ""; item.MateriaCode = ""; item.BarCode = ""; item.IsLock = false; } await _storageRackService.EditBatch(clearWardHoses); //锁定库位 var tempStorageRack = clearWardHoses.OrderBy(p => p.AreaNumSort).ThenByDescending(p => p.NumSort).FirstOrDefault(); destStorageRack = tempStorageRack; destStorageRack.IsLock = true; await _storageRackService.EditSingal(destStorageRack); } else { //有空库位,按照AreaNumSort、NumSort排序,依次放 var tempWareHouses = wareHouses.Where(p => !p.IsLock && !p.IsDisabled).ToList(); var sortWareHoses = tempWareHouses.OrderBy(p => p.AreaNumSort).ThenByDescending(p => p.NumSort).ToList(); foreach (var item in sortWareHoses) { if (string.IsNullOrEmpty(item.MateriaBatch)) { //锁定库位 item.IsLock = true; destStorageRack = item; break; } } //锁定库位 await _storageRackService.EditSingal(destStorageRack); } return destStorageRack; } /// /// 计算中间出库区的出库目标库位 /// /// public async Task CalculateDestWarehouse_SaleOutMiddle() { //获取楼层的所有库位 List wareHouses = await _storageRackService.GetListByExpression(p => p.Floor == FloorEnum.FirstFloor && p.Transport == TransportEnum.MiddleOutArea); if (wareHouses.IsNullOrEmpty()) { LogHelper.Warn($"中间出库区没有空余库位"); return null; } wareHouses = wareHouses.OrderBy(p => p.AreaNumSort).OrderBy(p => p.ReservoirAreaColumnId).ToList(); if (wareHouses.IsNullOrEmpty()) return null; var groupByColumn = wareHouses.GroupBy(p => p.ReservoirAreaColumnId); StorageRackDto destStorageRack = null; foreach (var columnGroup in groupByColumn) { bool unlokedColumn = columnGroup.All(p => !p.IsLock && !p.IsDisabled); if (unlokedColumn) { destStorageRack = columnGroup.OrderByDescending(p => p.NumSort).FirstOrDefault(p => string.IsNullOrEmpty(p.BarCode)); if (destStorageRack != null) break; } } return destStorageRack; } /// ///出库,生成ErpTask /// /// /// private async Task CreateStockOutErpTask(StockOutParam stockOutParam) { TaskTypeEnum taskType = CalStockOutTaskType(stockOutParam); var erpTask = new ERPTaskDto { BarCode = stockOutParam.barCode, SaleNo = stockOutParam.saleNo, ProduceReturnCode = stockOutParam.produceReturnCode, CustomName = stockOutParam.customName, MateriaName = stockOutParam.materiaName, MateriaType = stockOutParam.materiaType, MateriaBatch = stockOutParam.materiaBatch, MateriaCode = stockOutParam.materiaCode, Transport = (TransportEnum)stockOutParam.transport, ErpOrderNum = stockOutParam.num, Priority = PriorityEnum.High, TaskStatus = TaskStatusEnum.Initial, TaskType = taskType, }; bool res = await _erpTaskService.Add(erpTask); return res; } private async Task CalculateOutMaterial(ERPTaskDto erpTask) { //1. 筛选出该批次的所有物料库位 //2. 按照列号分组,并按照同一列的拖数从小到大排序(优先出拖数少的列) //3. 存在锁定列(同批次物料),先判断锁定列是否足够 //3.1 如果锁定列足够,则返回空,等待解锁 //3.2 如果锁定列不够,取第一个未锁定列 //3.3 未锁定列前面存在不同批次物料,则生成中转掏货任务 //3.4 未锁定列前面不存在不同批次物料,则直接出库 List storageRacksAll = await _storageRackService .GetListByExpression(p => p.Transport == TransportEnum.NormalArea); if (storageRacksAll.IsNullOrEmpty()) return null; List storageRacksSameBatch = storageRacksAll .Where(p => p.MateriaBatch == erpTask.MateriaBatch && !p.IsDisabled) .ToList(); if (storageRacksSameBatch.IsNullOrEmpty()) return null; var groupByCol = storageRacksSameBatch .GroupBy(p => p.ReservoirAreaColumnId) .OrderBy(p => p.Count()); var groupByColInclude = storageRacksAll .Where(p => storageRacksSameBatch.Select(x => x.ReservoirAreaColumnId).Distinct().Contains(p.ReservoirAreaColumnId)) .GroupBy(p => p.ReservoirAreaColumnId) .OrderBy(p => p.Count()); List agvTasks = await _commonService.GetCreatedAgvTasksFromErpTask(erpTask, erpTask.TaskType); //已生成任务的数量 int createdNum = agvTasks.Sum(p => p.MateriaNum); //还需要的数量 int stillNeedNum = erpTask.ErpOrderNum - erpTask.ErpCancelNum - createdNum; var lockedCols = groupByColInclude.Where(p => p.Any(x => x.IsLock)); int unlockedInLockedCol = lockedCols.SelectMany(p => p).Where(p => !p.IsLock && !p.IsDisabled).Sum(p => p.MateriaNum); if (unlockedInLockedCol >= stillNeedNum) { LogHelper.Info($"出库业务,已锁定的列上物料足够,等待列解锁后继续出库"); return null; } var unlockedCol = groupByColInclude.Where(p => p.All(x => !x.IsLock)).OrderBy(p => p.Count()).FirstOrDefault(); if (unlockedCol == null) { LogHelper.Info($"出库业务,已锁定列上物料不够,但不存在未锁定列物料,确认出库命令是否有误"); return null; } //判断该列上是否存在不同批次未锁定的物料,有则掏货 //判断该列上是否存在不同批次被锁定的物料,有则等待解锁 StorageRackDto destMaterial = unlockedCol.OrderBy(p => p.NumSort).First(p=>!string.IsNullOrEmpty(p.BarCode)); List allInThisCol = unlockedCol.ToList(); if (allInThisCol.Any(p => p.IsLock)) { LogHelper.Info($"出库业务 列 {destMaterial.ReservoirAreaColumnId} 上存在被锁定的物料,将等待解锁后继续取后续的物料"); return null; } List diffrentBatchInThisCol = allInThisCol .Where(p => !string.IsNullOrEmpty(p.MateriaBatch) && p.MateriaBatch != destMaterial.MateriaBatch) .ToList(); if (diffrentBatchInThisCol.IsNullOrEmpty()) return destMaterial; List diffrentBatchInThisColFront = diffrentBatchInThisCol .Where(p => p.NumSort < destMaterial.NumSort) .ToList(); if (diffrentBatchInThisColFront.IsNullOrEmpty()) return destMaterial; StorageRackDto diffrentBatchInThisColFirst = diffrentBatchInThisColFront.OrderBy(p => p.NumSort).First(); if (diffrentBatchInThisColFirst.IsLock) { LogHelper.Info($"出库业务 列 {diffrentBatchInThisColFirst.ReservoirAreaColumnId} 上存在不同批次的前序物料,且该前序物料被锁定," + $"将等待物料解锁后继续取后续的物料"); return null; } //掏货 return diffrentBatchInThisColFirst; } /// /// 销售出库,根据ERPTask生成二楼/一楼 AgvTask /// /// erp任务 /// public async Task CreateAgvTaskSecondFloor_SaleOut(ERPTaskDto erpTask) { //1.计算要出库的物料 //2.如果物料在一楼,直接生成到出库码头的任务 //3.如果物料在二楼 //3.1 有可用提升机库位,生成到提升机的任务 //3.2 没有可用提升机库位,生成中转任务 StorageRackDto stockOutMaterial = await CalculateOutMaterial(erpTask); if (stockOutMaterial == null) { LogHelper.Warn($"出库业务,批次号 {erpTask.MateriaBatch} 暂无可直接出库的物料"); _ = _erpTaskService.UpdateTaskMsg(erpTask.Id, $"暂无可直接出库的物料"); return false; } if (stockOutMaterial.MateriaBatch == erpTask.MateriaBatch) { if (stockOutMaterial.Floor == FloorEnum.FirstFloor) return await CreateTaskToWharf(stockOutMaterial, erpTask); else return await CreateTaskToElevator(stockOutMaterial, erpTask); } else { return await CreateTaskToTransfer(stockOutMaterial, TaskTypeEnum.RemoveStockOutObstruction, erpTask); } } /// /// 生成直接去出库码头的任务 /// /// private async Task CreateTaskToWharf(StorageRackDto stockOutWarehouse, ERPTaskDto erpTask) { if(stockOutWarehouse.Floor!=FloorEnum.FirstFloor) { return false; } //2.计算目标库位 StorageRackDto wareHouse = await CalculateDestWarehouse_SaleOut(erpTask.Transport); if (wareHouse == null) { LogHelper.Warn($"批次号 {erpTask.MateriaBatch} 暂无合适的码头库位"); _ = _erpTaskService.UpdateTaskMsg(erpTask.Id, "暂无合适的码头库位"); return false; } AGVTaskDto agvTask = new() { ErpTaskId = erpTask.Id, BarCode = stockOutWarehouse.BarCode, OrderNo = stockOutWarehouse.OrderNo, CustomName = stockOutWarehouse.CustomName, MateriaName = stockOutWarehouse.MateriaName, MateriaType = stockOutWarehouse.MateriaType, MateriaNum = stockOutWarehouse.MateriaNum, MateriaBatch = stockOutWarehouse.MateriaBatch, MateriaCode = stockOutWarehouse.MateriaCode, TaskType = erpTask.TaskType, TaskStatus = TaskStatusEnum.Initial, Priority = PriorityEnum.High, Floor = FloorEnum.FirstFloor, StartPositionCode = stockOutWarehouse.StorageRackNo, EndPositionCode = wareHouse.StorageRackNo, Transport = erpTask.Transport }; //锁定终点库位 await _storageRackService.Lock(wareHouse); //锁定起点库位 await _storageRackService.Lock(stockOutWarehouse); _ = _erpTaskService.UpdateTaskMsg(erpTask.Id, ""); //生成agvTask return await _agvTaskService.Add(agvTask); } /// /// 生成去提升机的任务 /// /// private async Task CreateTaskToElevator(StorageRackDto stockOutWarehouse, ERPTaskDto erpTask) { if (stockOutWarehouse.Floor != FloorEnum.SecondFloor) { return false; } StorageRackDto elevator = await _commonService.GetUsableElevator(FloorEnum.SecondFloor,2); if (elevator == null) { LogHelper.Warn("无可用提升机库位"); return false; //return await CreateTaskToTransfer(stockOutWarehouse, TaskTypeEnum.TransferStockOut, erpTask); } await _commonService.CreateAndInsertAgvTask(new Common.Models.AgvTaskCreationParam { StartPosition = stockOutWarehouse, EndPosition = elevator, TaskType = erpTask.TaskType, ErpTaskId = erpTask.Id, StockOutWharf= erpTask.Transport, ErpUploadStatus=ErpUploadStatusEnum.Unnecessary }); _ = _erpTaskService.UpdateTaskMsg(erpTask.Id, ""); return true; } /// /// 生成去中转区的任务 /// /// private async Task CreateTaskToTransfer(StorageRackDto stockOutWarehouse, TaskTypeEnum taskType, ERPTaskDto erpTask) { bool bRet = await _commonService.CreateAgvTaskToTransferArea(stockOutWarehouse, taskType, erpTask); if (!bRet) { LogHelper.Error($"出库业务 erpTaskId {erpTask.Id} 生成中转任务失败," + $"批次 {stockOutWarehouse.MateriaBatch}" + $"合格证号 {stockOutWarehouse.BarCode}"); _ = _erpTaskService.UpdateTaskMsg(erpTask.Id, $"{taskType.GetDescription()}任务生成失败"); } else { _ = _erpTaskService.UpdateTaskMsg(erpTask.Id, "中转进行中"); await _storageRackService.Lock(stockOutWarehouse); } return bRet; } #endregion #region 搬运物料 /// /// 物料搬运,起点库位验证 /// /// /// public async Task> CheckCarryStartRankNo(string startRankNo) { var result = new ApiResult() { ResultCode = "500" }; if (string.IsNullOrEmpty(startRankNo)) { result.ErrorMsg = $"请扫描起点库位码!"; return result; } //起点物料信息 StorageRackDto startMaterial = await _storageRackService.GetSingalByExpression(p => p.StorageRackNo == startRankNo); if (startMaterial == null) { result.ErrorMsg = $"起点库位 {startRankNo}不存在,无法搬运!"; return result; } else { if (startMaterial.IsLock) { result.ErrorMsg = $"起点库位 {startRankNo}被锁定,无法搬运!"; return result; } if (startMaterial.IsDisabled) { result.ErrorMsg = $"起点库位 {startRankNo}被禁用,无法搬运!"; return result; } if (startMaterial.Transport != TransportEnum.NormalArea) { result.ErrorMsg = $"起点库位 {startRankNo}不在存储区,无法搬运!"; return result; } if (startMaterial.MateriaNum <= 0) { result.ErrorMsg = $"起点库位 {startRankNo}没有物料信息,无法搬运!"; return result; } } result.ResultCode = "200"; return result; } /// /// 物料搬运,终点库位验证 /// /// /// public async Task> CheckCarryEndRankNo(string endRankNo) { var result = new ApiResult() { ResultCode = "500" }; if (string.IsNullOrEmpty(endRankNo)) { result.ErrorMsg = $"请扫描终点库位码!"; return result; } //终点物料信息 StorageRackDto endMaterial = await _storageRackService.GetSingalByExpression(p => p.StorageRackNo == endRankNo); if (endMaterial == null) { result.ErrorMsg = $"终点库位 {endRankNo}不存在,无法搬运!"; return result; } else { if (endMaterial.IsLock) { result.ErrorMsg = $"终点库位 {endMaterial}被锁定,无法搬运!"; return result; } if (endMaterial.IsDisabled) { result.ErrorMsg = $"终点库位 {endMaterial}被禁用,无法搬运!"; return result; } //if (endMaterial.Transport != TransportEnum.NormalArea) //{ // result.ErrorMsg = $"终点库位 {endRankNo}不在存储区,无法搬运!"; // return result; //} //if (endMaterial.MateriaNum > 0) //{ // result.ErrorMsg = $"终点库位 {endRankNo}存在物料信息,无法搬运!"; // return result; //} } result.ResultCode = "200"; return result; } /// /// 搬运物料 /// 同楼层的存储区,才能搬运 /// /// /// public async Task> CarryMaterialInfo(CarryInParam carryInParam) { //1.查询起点物料信息是否存在 //2.查询终点物料信息是否存在 //3.直接生成AgvTask var result = new ApiResult() { ResultCode = "500" }; //起点库位信息 StorageRackDto start = await _storageRackService.GetSingalByExpression(p => !p.IsLock && !p.IsDisabled && p.StorageRackNo == carryInParam.StartStorageRackNo); //终点库位信息 StorageRackDto end = await _storageRackService.GetSingalByExpression(p => !p.IsLock && !p.IsDisabled && p.StorageRackNo == carryInParam.EndStorageRackNo); //是否存在,起点、终点正在执行的AgvTask var isExsit = await _agvTaskService.Exist(p => p.StartPositionCode == start.StorageRackNo && p.EndPositionCode == end.StorageRackNo && p.TaskType == TaskTypeEnum.Carry && (p.TaskStatus == TaskStatusEnum.Initial || p.TaskStatus == TaskStatusEnum.InExecution)); if (isExsit) { result.ErrorMsg = $"已经存在起点{start.StorageRackNo} 到 终点{end.StorageRackNo}的搬运任务"; return result; } //直接生成AgvTask bool res = await CreateAgvTask_Carry(start, end); if (!res) { result.ErrorMsg = $"搬运物料,生成Agv任务,数据库操作失败!"; return result; } else { result.ResultCode = "200"; return result; } } /// /// 搬运物料,生成AgvTask /// /// /// /// private async Task CreateAgvTask_Carry(StorageRackDto start, StorageRackDto end) { var excuteResult = false; AGVTaskDto agvTask = new() { ErpTaskId = Guid.Empty, BarCode = start.BarCode, OrderNo = start.OrderNo, CustomName = start.CustomName, MateriaName = start.MateriaName, MateriaType = start.MateriaType, MateriaNum = start.MateriaNum, MateriaBatch = start.MateriaBatch, MateriaCode = start.MateriaCode, TaskType = TaskTypeEnum.Carry, TaskStatus = TaskStatusEnum.Initial, Priority = PriorityEnum.Middle, Floor = start.Floor, StartPositionCode = start.StorageRackNo, EndPositionCode = end.StorageRackNo, Transport = TransportEnum.NormalArea }; //锁定起点库位 start.IsLock = true; await _storageRackService.EditSingal(start); //锁定终点库位 end.IsLock = true; await _storageRackService.EditSingal(end); //生成agvTask excuteResult = await _agvTaskService.Add(agvTask); return excuteResult; } #endregion #region 出库撤单 /// /// 出库撤单 /// 只有销售出库,才能撤单 /// /// /// public async Task> ErpCancelOrder(ErpCancelOrderParam cancelOrderParam) { var result = new XRNewResultDto() { success = false, status = 500, data = 0 }; if (string.IsNullOrEmpty(cancelOrderParam.saleNo)) { result.errors = "销售出库单号不能为空!"; LogHelper.Warn(result.errors); return result; } if (string.IsNullOrEmpty(cancelOrderParam.materiaBatch)) { result.errors = "批次号不能为空!"; LogHelper.Warn(result.errors); return result; } if (string.IsNullOrEmpty(cancelOrderParam.materiaType)) { result.errors = "物料类型不能为空!"; LogHelper.Warn(result.errors); return result; } if (string.IsNullOrEmpty(cancelOrderParam.customName)) { result.errors = "客户名称不能为空!"; LogHelper.Warn(result.errors); return result; } if (string.IsNullOrEmpty(cancelOrderParam.materiaName)) { result.errors = "物料名称不能为空!"; LogHelper.Warn(result.errors); return result; } var erpTaskList = await _erpTaskService.GetListByExpression(p => p.SaleNo == cancelOrderParam.saleNo); if (erpTaskList.IsNullOrEmpty()) { result.errors = $"销售出库单号{cancelOrderParam.saleNo},不存在任务!"; LogHelper.Warn(result.errors); return result; } var sumCancelNum = erpTaskList == null ? 0 : erpTaskList.Sum(p => p.ErpCancelNum); if (sumCancelNum > 0) { result.errors = $"销售出库单号{cancelOrderParam.saleNo},不能重复撤单!"; LogHelper.Warn(result.errors); return result; } var erpTask = erpTaskList?.FirstOrDefault(); if (erpTask.TaskType != TaskTypeEnum.SaleOut) { result.errors = $"单号{cancelOrderParam.saleNo},不是销售单号,无法撤单"; LogHelper.Warn(result.errors); return result; } if (erpTask.TaskStatus == TaskStatusEnum.Completed) { result.errors = $"批次号为{cancelOrderParam.materiaBatch} 销售单号为{cancelOrderParam.saleNo},任务已经完成,无法撤单"; LogHelper.Warn(result.errors); return result; } var erpCancelNum = await GetErpCancelNum(cancelOrderParam); if (erpCancelNum <= 0) { result.errors = $"批次号为{cancelOrderParam.materiaBatch} 销售单号为{cancelOrderParam.saleNo},没有可以撤回的数量"; LogHelper.Warn(result.errors); return result; } //更新Erp出库撤单数量 if (erpTask != null) { erpTask.ErpCancelNum = erpCancelNum; bool res = await _erpTaskService.EditSingal(erpTask); if (!res) { result.errors = "更新Erp出库撤单数量,数据库操作失败!"; LogHelper.Warn(result.errors); return result; } } result.success = true; result.status = 200; result.data = erpTask.ErpCancelNum; result.message = $"撤单成功:批次号为{cancelOrderParam.materiaBatch} 销售单号为{cancelOrderParam.saleNo},撤单数量为{erpTask.ErpCancelNum}"; return result; } /// /// 根据销售出库单,计算可以撤单的数量 /// /// /// private async Task GetErpCancelNum(ErpCancelOrderParam cancelOrderParam) { var result = 0; var erpTask = await _erpTaskService.GetSingalByExpression(p => p.SaleNo == cancelOrderParam.saleNo); if (erpTask != null) { var agvTaskList = await _agvTaskService.GetListByExpression(p => p.ErpTaskId == erpTask.Id && (p.TaskStatus == TaskStatusEnum.Initial || p.TaskStatus == TaskStatusEnum.InExecution || p.TaskStatus == TaskStatusEnum.Completed) && (p.TaskType == TaskTypeEnum.SaleOut)); var sumMateriaNum = 0; if (agvTaskList.Count > 0) { var groupAgvTaskList = agvTaskList.GroupBy(p => p.BarCode); foreach (var groupAgvTask in groupAgvTaskList) { sumMateriaNum += groupAgvTask.FirstOrDefault().MateriaNum; } } if (erpTask.ErpOrderNum - sumMateriaNum > 0) { result = erpTask.ErpOrderNum - sumMateriaNum; } } return result; } #endregion } }