using Admin.NET.Core.Service;
using Admin.NET.Application.Entity;
using Microsoft.AspNetCore.Http;
using Admin.NET.Application;
using StackExchange.Redis;
using System.Linq.Expressions;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Qiniu.Storage;
using Mapster;
using Elastic.Clients.Elasticsearch;
using Furion.DatabaseAccessor;
using static SKIT.FlurlHttpClient.Wechat.Api.Models.ChannelsECMerchantAddFreightTemplateRequest.Types.FreightTemplate.Types;
using System.ComponentModel;
using static SKIT.FlurlHttpClient.Wechat.Api.Models.ChannelsECLogisticsEWaybillOrderCreateRequest.Types;
using NewLife.Security;
using Admin.NET.Application.CommonHelper;
using Admin.NET.Application.TransferDto;
namespace Admin.NET.Application;
///
/// 上架服务
///
[ApiDescriptionSettings(ApplicationConst.WmsOperationsGroupName, Order = 100)]
public class WmsPutawayService : IDynamicApiController, ITransient
{
private readonly SqlSugarRepository _wmsRecordTransRep;
private readonly SqlSugarRepository _wmsLogActionRep;
private readonly SqlSugarRepository _wmsContainerRep;
private readonly SqlSugarRepository _wmsPlaceRep;
private readonly SqlSugarRepository _wmsAreaRep;
private readonly SqlSugarRepository _wmsContainerPlaceRep;
private readonly SqlSugarRepository _v_wms_containerRep;
private readonly SqlSugarRepository _wmsStockQuanRep;
private readonly SqlSugarRepository _wmsTask;
private readonly SqlSugarRepository _wmsOrderMovementRep;
private readonly SqlSugarRepository _wmsOrderMovementDetailsRep;
private readonly SqlSugarRepository _WmsNoCreateRuleRep;
private readonly SqlSugarRepository _repSNRep;
private readonly SqlSugarRepository _wmsMaterialRep;
private readonly SqlSugarRepository _wmsContainerPackagingRep;
private readonly SqlSugarRepository _v_wms_stock_quanRep;
private readonly WmsGroupDiskService _wmsGroupDiskService;
private readonly SqlSugarRepository _repWmsContainerPlaceRep;
private readonly SqlSugarRepository _wmsBaseBusinessTypeRep;
private WmsPlaceService _wmsPlaceService;
///
/// 构造函数
///
public WmsPutawayService(
SqlSugarRepository wmsBaseBusinessTypeRep,
SqlSugarRepository repWmsContainerPlaceRep,
WmsGroupDiskService wmsGroupDiskService,
SqlSugarRepository v_wms_containerRep,
SqlSugarRepository wmsStockQuanRep,
SqlSugarRepository wmsRecordTransRep,
SqlSugarRepository wmsLogActionRep,
SqlSugarRepository wmsContainerPlaceRep,
SqlSugarRepository wmsContainerRep,
SqlSugarRepository wmsPlaceRep,
SqlSugarRepository wmsAreaRep,
SqlSugarRepository wmsTaskRep,
SqlSugarRepository wmsOrderMovementRep,
SqlSugarRepository wmsOrderMovementDetailsRep,
SqlSugarRepository wmsNoCreateRuleRep,
SqlSugarRepository repSNRep,
SqlSugarRepository wmsMaterialRep, SqlSugarRepository wmsContainerPackagingRep,
SqlSugarRepository v_wms_stock_quanRep, WmsPlaceService wmsPlaceService)
{
_wmsBaseBusinessTypeRep = wmsBaseBusinessTypeRep;
_repWmsContainerPlaceRep = repWmsContainerPlaceRep;
_wmsGroupDiskService = wmsGroupDiskService;
_v_wms_containerRep = v_wms_containerRep;
_wmsLogActionRep = wmsLogActionRep;
_wmsStockQuanRep = wmsStockQuanRep;
_wmsRecordTransRep = wmsRecordTransRep;
_wmsContainerRep = wmsContainerRep;
_wmsPlaceRep = wmsPlaceRep;
_wmsAreaRep = wmsAreaRep;
_wmsContainerPlaceRep = wmsContainerPlaceRep;
_wmsTask = wmsTaskRep;
_wmsOrderMovementRep = wmsOrderMovementRep;
_wmsOrderMovementDetailsRep = wmsOrderMovementDetailsRep;
_WmsNoCreateRuleRep = wmsNoCreateRuleRep;
_repSNRep = repSNRep;
_wmsMaterialRep = wmsMaterialRep;
_wmsContainerPackagingRep = wmsContainerPackagingRep;
_v_wms_stock_quanRep = v_wms_stock_quanRep;
_wmsLogActionRep = wmsLogActionRep;
_wmsPlaceService = wmsPlaceService;
}
#region PDA上架操作根据容器号查询要上架的库存信息
///
/// PDA上架查询-获取容器上的的库存信息
///
///
///
[HttpGet]
[ApiDescriptionSettings(Name = "GetPutawayStockQuan", Description = "PDA上架查询-获取容器上的的库存信息")]
[Description("/WmsPutaway/GetPutawayStockQuan")]
public async Task GetPutawayStockQuan([FromQuery] WmsPutawayInput input)
{
var wmsContainerItem = await _wmsContainerRep.GetFirstAsync(w => w.ContainerCode == input.ContainerCode &&
w.IsDelete == false);
if (wmsContainerItem == null)
{
throw Oops.Oh($"容器{input.ContainerCode}不存在!");
}
if (wmsContainerItem.IsDisabled == true)
{
throw Oops.Oh($"容器{input.ContainerCode}已禁用!");
}
//获取容器库位关系
var wmsContainerLocation = await _wmsContainerPlaceRep.GetFirstAsync(w => w.ContainerCode == input.ContainerCode);
WmsPutawayOutput result = new WmsPutawayOutput();
if (wmsContainerLocation != null)
{
result.PlaceCode = wmsContainerLocation.PlaceCode;
}
result.Details = await GetStockQuansByContainerCode(input.ContainerCode);
return result;
}
///
/// PDA上架查询-获取容器上的的库存信息(空容器组盘、上架专用)
///
///
///
[HttpPost]
[ApiDescriptionSettings(Name = "GetContainerInfoQuan", Description = "PDA上架查询-获取容器上的的库存信息")]
[Description("/Putaway/GetContainerInfoQuan")]
public async Task> GetContainerInfoQuan(GeContainerInput input)
{
var wmsContainerItem = await _wmsContainerRep.GetFirstAsync(w => w.ContainerCode == input.ContainerCode &&
w.IsDelete == false);
if (wmsContainerItem == null)
{
throw Oops.Oh($"容器{input.ContainerCode}不存在!");
}
if (wmsContainerItem.IsDisabled == true)
{
throw Oops.Oh($"容器{input.ContainerCode}已禁用!");
}
//如果该容器上有 物料,则报错
var stockQun = await _wmsStockQuanRep.GetFirstAsync(x => x.ContainerCode == input.ContainerCode && x.MaterialCode != ApplicationConst.DefaultContinerMaterialCode);
if (stockQun != null)
{
throw Oops.Oh($"容器{input.ContainerCode}上有物料,不允许操作!");
}
//ly0815-说明有底托了 两个叠托不能再合并组盘了
if (input.FontContainerList != null)
{
if (input.FontContainerList.Count > 0)
{
var ItemContainerCodeAdd = await _wmsStockQuanRep.GetFirstAsync(x => x.SNCode == input.ContainerCode);
if (ItemContainerCodeAdd != null)
{
//存在库存中,不是在途虚拟容器,就认为已组盘
var entityWmsContainerList = await _wmsContainerRep.GetListAsync(u => u.ContainerCode.Contains(ApplicationConst.DefaultZTContainerCode_Pre)
&& ItemContainerCodeAdd.ContainerCode == u.ContainerCode);
if (entityWmsContainerList == null || entityWmsContainerList.Count == 0) throw Oops.Oh($"容器{input.ContainerCode}已组盘,请去【容器解绑】!");
}
}
}
//容器必须有库位关系才能组盘 去pda库位绑定中 进行绑定
var entityWmsContainerPlace = await _repWmsContainerPlaceRep.GetFirstAsync(u => u.ContainerCode == input.ContainerCode);
ContainerPlaceHelper.ValidateContainerPlaceForGroupDisk(entityWmsContainerPlace, input.ContainerCode);
//查询该空容器下的所有有关库存
var dbstockQunList = await _wmsStockQuanRep.GetListAsync(x => x.ContainerCode == input.ContainerCode || x.SNCode == input.ContainerCode);
var wmsContainerItemList = await _wmsContainerRep.GetListAsync(w => dbstockQunList.Select(x => x.SNCode).Contains(w.ContainerCode));
var containerInfos = wmsContainerItemList.Adapt>();
if (containerInfos != null)
{
var curContiner = wmsContainerItem.Adapt();
if (!containerInfos.Exists(x => x.ContainerCode == curContiner.ContainerCode))
{
containerInfos.Add(curContiner);
}
}
return containerInfos;
}
///
/// PDA上架
///
///
///
[HttpPost]
[ApiDescriptionSettings(Name = "PutawayConfirm", Description = "PDA上架")]
[Description("/Putaway/PutawayConfirm")]
[UnitOfWork]
public async Task PutawayConfirm(WmsPutawayParamInput input)
{
var stockQuans = new List();
stockQuans = await _wmsStockQuanRep.GetListAsync(w => w.ContainerCode == input.ContainerCode);
return await CommonPutawayConfirm(input, stockQuans);
}
///
/// PDA组盘+上架
///
///
///
[HttpPost]
[ApiDescriptionSettings(Name = "BindPutawayConfirm", Description = "PDA组盘+上架")]
[Description("/Putaway/BindPutawayConfirm")]
[UnitOfWork]
public async Task BindPutawayConfirm(WmsBindPutawayParamInput input)
{
WmsGroupDiskBaseInput bindInput = input.Adapt();
PdaBindUpdateOutput pdaBindUpdateOutput = await _wmsGroupDiskService.PdaBindUpdate(bindInput);
WmsPutawayParamInput putawayInput = input.Adapt();
return await CommonPutawayConfirm(putawayInput, pdaBindUpdateOutput.StockQuans);
}
///
/// 公共上架功能
///
///
///
///
private async Task CommonPutawayConfirm(WmsPutawayParamInput input, List stockQuans)
{
List addWmsOrderMovementDetailsList = new List();
var wmsContainerItem = await _wmsContainerRep.GetFirstAsync(w => w.ContainerCode == input.ContainerCode &&
w.IsDelete == false);
if (wmsContainerItem == null)
{
throw Oops.Oh($"容器{input.ContainerCode}不存在!");
}
if (wmsContainerItem.IsDisabled == true)
{
throw Oops.Oh($"容器{input.ContainerCode}已禁用!");
}
if (wmsContainerItem.IsVirtually == true)
{
throw Oops.Oh($"容器{input.ContainerCode}是虚拟容器!");
}
PlaceTransferDto source_placeTransferDto = new PlaceTransferDto();
PlaceTransferDto to_placeTransferDto = new PlaceTransferDto();
//新增操作履历
List addWmsLogActionList = new List();
//校验库存
if (stockQuans?.Count <= 0)
{
throw Oops.Oh($"容器{input.ContainerCode}没有库存信息!");
}
//2.循环库存信息-校验
foreach (var stockQuan in stockQuans)
{
#region 校验
if (stockQuan.Quantity <= 0)
{
//暂时注释该验证 【Editby shaocx,2024-07-28】
// 如果存在库存数量为0的库存,则提示用户
//throw Oops.Oh($"容器{input.ContainerCode}物料{stockQuan.MaterialCode}库存数是{stockQuan.Quantity}不能上架!");
}
#endregion
}
var _v_wms_container = await _v_wms_containerRep.GetFirstAsync(u => u.ContainerCode == input.ContainerCode);
if (_v_wms_container == null)
{
throw Oops.Oh($"容器{input.ContainerCode}不存在容器,不可上架");
}
else
{
if (string.IsNullOrEmpty(_v_wms_container.PlaceCode))
{
throw Oops.Oh($"容器{input.ContainerCode}不存在容器和库位绑定关系,不可上架");
}
source_placeTransferDto = new PlaceTransferDto()
{
WarehouseCode = _v_wms_container.WarehouseCode,
WarehouseName = _v_wms_container.WarehouseName,
AreaCode = _v_wms_container.AreaCode,
AreaName = _v_wms_container.AreaName,
PlaceCode = _v_wms_container.PlaceCode,
PlaceName = _v_wms_container.PlaceName
};
}
//获取库位信息
WmsBasePlace toPlace = null;
if (!string.IsNullOrWhiteSpace(input.PlaceCode))
{
toPlace = await _wmsPlaceRep.GetFirstAsync(w => w.PlaceCode == input.PlaceCode && w.IsDelete == false);
if (toPlace == null)
{
throw Oops.Oh($"目标库位{input.PlaceCode}不存在!");
}
if (toPlace.IsDisabled == true)
{
throw Oops.Oh($"目标库位{input.PlaceCode}已禁用!");
}
if (toPlace.IsVirtually == true)
{
throw Oops.Oh($"目标库位{input.PlaceCode}是虚拟库位!");
}
//库位属性 正常和禁出的可以组盘上架
if (toPlace.PlaceStatus != PlaceStatusEnum.正常 && toPlace.PlaceStatus != PlaceStatusEnum.禁出)
{
throw Oops.Oh($"目标库位{input.PlaceCode}库位属性是{toPlace.PlaceStatus.GetDescription()}!");
}
}
else
{
//ly0802 - 不选择 目标库位,只需要选择目标库区,如果不选择目标库位,就系统自己寻找
//throw Oops.Oh($"目标库位不能为空!");
var WmsPlaceInput = new WmsPlaceInput();
WmsPlaceInput.AreaCode = input.AreaCode;
List WmsPlaceList = await _wmsPlaceService.RecommendPlaceList(WmsPlaceInput);
if (WmsPlaceList.Count > 0)
{
toPlace = await _wmsPlaceRep.GetFirstAsync(w => w.PlaceCode == WmsPlaceList[0].PlaceCode && w.IsDelete == false);
}
else
{
throw Oops.Oh($"目标库位不能为空!");
}
}
//查询上架的目标库区信息
WmsBaseArea toArea = await BaseInfoHelper.GetAreaByPlace(toPlace, _wmsAreaRep);
//1.1.校验容器号是否已经创建待上架状态的上架单
var isExsit = await _wmsOrderMovementDetailsRep.GetFirstAsync(x => x.ContainerCode == input.ContainerCode && (x.OrderStatus == OrderStatusEnum.处理中 || x.OrderStatus == OrderStatusEnum.新建));
if (isExsit != null)
{
throw Oops.Oh($"容器{input.ContainerCode}存在未完成的移动单,单号{isExsit.MovementNo}!");
}
var snCodeList = stockQuans.Select(s => s.SNCode).ToList();
//获取要盘点的库存视图-根据盘点库区的容器和盘点范围的物料获取盘点库存-包含库位信息、库区信息,新增事务用
var wmsStockQuanViewList = await _v_wms_stock_quanRep.GetListAsync(x => x.IsDelete == false && snCodeList.Contains(x.SNCode));
int lineNumber = 0;//移动单明细行号
var hearId = Yitter.IdGenerator.YitIdHelper.NextId();//上架单ID
//update by liuwq 20240725
string orderNo = await OrderHelper.CreateOrderNoByRuleCommon(OrderTypeEnum.上架单, (int)input.ActionType, _wmsOrderMovementRep, _WmsNoCreateRuleRep, _repSNRep);
//4 创建上架单
var addWmsOrderMovement = new WmsOrderMovement()
{
Id = hearId,
OrderNo = orderNo, //按照单号规则生成单号-ly update by liuwq 20240725
OrderType = OrderTypeEnum.上架单,
OrderTypeName = OrderTypeEnum.上架单.GetDescription(),
BusinessType = (int)input.ActionType,
BusinessTypeName = input.ActionType.GetDescription(),
OrderStatus = OrderStatusEnum.新建,
OrderStatusName = OrderStatusEnum.新建.GetDescription(),
SourceWarehouseCode = source_placeTransferDto.WarehouseCode,
SourceWarehouseName = source_placeTransferDto.WarehouseName,
ToAreaCode = toArea.AreaCode,
ToAreaName = toArea.AreaName,
ToPlaceCode = toPlace.PlaceCode,
ToPlaceName = toPlace.PlaceName,
OrderSocure = SourceByEnum.系统
};
addWmsOrderMovement.BusinessType = (int)input.ActionType;
addWmsOrderMovement.BusinessTypeName = input.ActionType.GetDescription();
//3.循环库存信息-创建上架单明细
foreach (var stockQuan in stockQuans)
{
lineNumber++;
#region 创建上架单明细
var currentStockQuanView = wmsStockQuanViewList.FirstOrDefault(f => f.SNCode.Equals(stockQuan.SNCode));
if (currentStockQuanView == null)
{
throw Oops.Oh($"跟踪码{stockQuan.SNCode}没有获取到库存信息");
}
//3.根据容器的库存创建上架单明细
var wmsOrderMovementDetails = new WmsOrderMovementDetails()
{
MovementId = hearId,
MovementNo = addWmsOrderMovement.OrderNo,
LineNumber = OrderHelper.AutoCompleEBELP(lineNumber.ToString(), 4),
SNCode = stockQuan.SNCode,
SupplierCode = stockQuan.SupplierCode,
SupplierName = stockQuan.SupplierName,
Batch = stockQuan.Batch,
ErpOrderNo = stockQuan.ErpOrderNo,
ErpCode = stockQuan.ErpCode,
Unit = currentStockQuanView.MaterialUnit,
SupplierBatch = stockQuan.SupplierBatch,
OrderStatus = OrderStatusEnum.新建,
OrderStatusName = OrderStatusEnum.新建.GetDescription(),
//ToAreaCode = input.ToAreaCode,
//ToPlaceCode = input.ToPlaceCode,
ContainerCode = wmsContainerItem.ContainerCode,
ContainerName = wmsContainerItem.ContainerName,
SourceWarehouseCode = source_placeTransferDto.WarehouseCode,
SourceWarehouseName = source_placeTransferDto.WarehouseName,
SourceAreaCode = source_placeTransferDto.AreaCode,
SourceAreaName = source_placeTransferDto.AreaName,
SourcePlaceCode = source_placeTransferDto.PlaceCode,
SourcePlaceName = source_placeTransferDto.PlaceName,
ToPlaceCode = toPlace.PlaceCode,
ToPlaceName = toPlace.PlaceName,
ToAreaCode = toPlace.AreaCode,
ToAreaName = toPlace.AreaName,
MaterialCode = stockQuan.MaterialCode,
MaterialName = stockQuan.MaterialName,
Quantity = stockQuan.Quantity,
ActionRemark = "PDA上架创建上架单",
ActionTime = DateTime.Now
};
addWmsOrderMovementDetailsList.Add(wmsOrderMovementDetails);
#endregion
#region 转移库存添加事务、操作日志
string recordTransRemarks = string.Empty;
recordTransRemarks = $"容器{wmsContainerItem.ContainerCode}跟踪码{stockQuan.SNCode}";
//上架操作仅产生 操作日志,事务在调度任务中产生
//新增操作日志
WmsLogAction wareActionLog = LogActionHelper.CreateWmsLogAction(stockQuan.Id, input.ActionType.GetDescription(), recordTransRemarks);
addWmsLogActionList.Add(wareActionLog);
#endregion
}
BusinessTypeEnum wmsTaskBusinessTypeEnum = input.ActionType;
var businessTypeInfo = BusinessTypeHelper.GetBusinessTypeInfoFromDB((int)wmsTaskBusinessTypeEnum, _wmsBaseBusinessTypeRep);
//5.创建调度任务
var wmsTask = new WmsTask()
{
MoveType = businessTypeInfo.MoveType,
MoveTypeName = businessTypeInfo.MoveTypeName,
BusinessType = businessTypeInfo.BusinessTypeValue,
BusinessTypeName = businessTypeInfo.BusinessTypeName,
ContainerCode = input.ContainerCode,
IsFlagFinish = false,
OrderNo = addWmsOrderMovement.OrderNo,
TaskDescribe = "PDA上架",
TaskStatus = TaskStatusEnum.新建,
TaskStatusName = TaskStatusEnum.新建.GetDescription(),
TaskNo = Yitter.IdGenerator.YitIdHelper.NextId().ToString(),
TaskPriority = 0,
TaskName = wmsTaskBusinessTypeEnum.GetDescription(),
SourcePlaceCode = source_placeTransferDto.PlaceCode,//源库位
ToAreaCode = toArea.AreaCode,
ToPlaceCode = toPlace.PlaceCode
};
try
{
#region 事务内执行操作
// 1、事务日志
//2、操作日志
//新增操作日志
await _wmsLogActionRep.InsertRangeAsync(addWmsLogActionList);
//新增任务调度
await _wmsTask.InsertAsync(wmsTask);
if (addWmsOrderMovementDetailsList?.Count > 0)
{
//新增上架单
await _wmsOrderMovementRep.InsertAsync(addWmsOrderMovement);
await _wmsOrderMovementDetailsRep.InsertRangeAsync(addWmsOrderMovementDetailsList);
}
else
{
throw Oops.Oh($"上架单明细为空!");
}
#endregion
}
catch
{
throw;
}
return 1;
}
///
/// 根据容器号获取库存信息
///
///
///
private async Task> GetStockQuansByContainerCode(string containerCode)
{
var query = _wmsStockQuanRep.AsQueryable()
.WhereIF(!string.IsNullOrWhiteSpace(containerCode), u => u.ContainerCode == containerCode.Trim())
.Select().OrderBy(o => o.MaterialCode);
var list = await query.ToListAsync();
return list;
}
#endregion
}