Files
ww-jst/wmsjst/JSMachine.WMS.Business/StockOut/ServiceImpl/StockOutService.cs
T
2026-09-02 16:31:50 +08:00

947 lines
37 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
{
/// <summary>
/// 出库服务类
/// </summary>
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 销售出库
/// <summary>
/// 销售出库
/// </summary>
/// <param name="erpSaleOutParam"></param>
/// <returns></returns>
public async Task<string> 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<string> 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<string> CalStockQty(StockOutParam stockOutParam)
{
List<StorageRackDto> storageRacks = await _storageRackService.GetListByExpression(p =>
p.MateriaBatch == stockOutParam.materiaBatch &&
!p.IsLock &&
!p.IsDisabled &&
p.Transport == TransportEnum.NormalArea);
if(storageRacks.IsNullOrEmpty())
{
return "无可用库存";
}
List<ERPTaskDto> 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<AGVTaskDto> 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 ? "" : "库存不足";
}
/// <summary>
/// 根据销售出库单,扣减库存
/// </summary>
/// <param name="saleNo"></param>
/// <param name="taskType"></param>
/// <returns></returns>
private async Task<SaleOutStockDto> 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;
}
/// <summary>
/// 销售出库,计算目标库位
/// </summary>
/// <param name="transport"></param>
/// <returns></returns>
public async Task<StorageRackDto> CalculateDestWarehouse_SaleOut(TransportEnum transport)
{
//1 出库区是否有同批次货物,有则查看其列上是否有空余库位
//2 锁定目标库位
StorageRackDto destStorageRack = null;
if (transport==TransportEnum.MiddleOutArea)
{
destStorageRack = await CalculateDestWarehouse_SaleOutMiddle();
return destStorageRack;
}
//所有出库区库位
List<StorageRackDto> 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;
}
/// <summary>
/// 计算中间出库区的出库目标库位
/// </summary>
/// <returns></returns>
public async Task<StorageRackDto> CalculateDestWarehouse_SaleOutMiddle()
{
//获取楼层的所有库位
List<StorageRackDto> 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;
}
/// <summary>
///出库,生成ErpTask
/// </summary>
/// <param name="erpSaleOutParam"></param>
/// <returns></returns>
private async Task<bool> 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<StorageRackDto> CalculateOutMaterial(ERPTaskDto erpTask)
{
//1. 筛选出该批次的所有物料库位
//2. 按照列号分组,并按照同一列的拖数从小到大排序(优先出拖数少的列)
//3. 存在锁定列(同批次物料),先判断锁定列是否足够
//3.1 如果锁定列足够,则返回空,等待解锁
//3.2 如果锁定列不够,取第一个未锁定列
//3.3 未锁定列前面存在不同批次物料,则生成中转掏货任务
//3.4 未锁定列前面不存在不同批次物料,则直接出库
List<StorageRackDto> storageRacksAll = await _storageRackService
.GetListByExpression(p => p.Transport == TransportEnum.NormalArea);
if (storageRacksAll.IsNullOrEmpty())
return null;
List<StorageRackDto> 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<AGVTaskDto> 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<StorageRackDto> allInThisCol = unlockedCol.ToList();
if (allInThisCol.Any(p => p.IsLock))
{
LogHelper.Info($"出库业务 列 {destMaterial.ReservoirAreaColumnId} 上存在被锁定的物料,将等待解锁后继续取后续的物料");
return null;
}
List<StorageRackDto> diffrentBatchInThisCol = allInThisCol
.Where(p => !string.IsNullOrEmpty(p.MateriaBatch) && p.MateriaBatch != destMaterial.MateriaBatch)
.ToList();
if (diffrentBatchInThisCol.IsNullOrEmpty())
return destMaterial;
List<StorageRackDto> 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;
}
/// <summary>
/// 销售出库,根据ERPTask生成二楼/一楼 AgvTask
/// </summary>
/// <param name="erpTask">erp任务</param>
/// <returns></returns>
public async Task<bool> 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);
}
}
/// <summary>
/// 生成直接去出库码头的任务
/// </summary>
/// <returns></returns>
private async Task<bool> 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);
}
/// <summary>
/// 生成去提升机的任务
/// </summary>
/// <returns></returns>
private async Task<bool> 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;
}
/// <summary>
/// 生成去中转区的任务
/// </summary>
/// <returns></returns>
private async Task<bool> 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 搬运物料
/// <summary>
/// 物料搬运,起点库位验证
/// </summary>
/// <param name="startRankNo"></param>
/// <returns></returns>
public async Task<ApiResult<string>> CheckCarryStartRankNo(string startRankNo)
{
var result = new ApiResult<string>() { 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;
}
/// <summary>
/// 物料搬运,终点库位验证
/// </summary>
/// <param name="endRankNo"></param>
/// <returns></returns>
public async Task<ApiResult<string>> CheckCarryEndRankNo(string endRankNo)
{
var result = new ApiResult<string>() { 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;
}
/// <summary>
/// 搬运物料
/// 同楼层的存储区,才能搬运
/// </summary>
/// <param name="carryInParam"></param>
/// <returns></returns>
public async Task<ApiResult<string>> CarryMaterialInfo(CarryInParam carryInParam)
{
//1.查询起点物料信息是否存在
//2.查询终点物料信息是否存在
//3.直接生成AgvTask
var result = new ApiResult<string>() { 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;
}
}
/// <summary>
/// 搬运物料,生成AgvTask
/// </summary>
/// <param name="start"></param>
/// <param name="end"></param>
/// <returns></returns>
private async Task<bool> 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 出库撤单
/// <summary>
/// 出库撤单
/// 只有销售出库,才能撤单
/// </summary>
/// <param name="cancelOrderParam"></param>
/// <returns></returns>
public async Task<XRNewResultDto<int>> ErpCancelOrder(ErpCancelOrderParam cancelOrderParam)
{
var result = new XRNewResultDto<int>() { 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;
}
/// <summary>
/// 根据销售出库单,计算可以撤单的数量
/// </summary>
/// <param name="cancelOrderParam"></param>
/// <returns></returns>
private async Task<int> 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
}
}