using Admin.NET.Application.CommonHelper;
|
using Admin.NET.Application.Entity;
|
using Admin.NET.Application.TransferDto;
|
using DocumentFormat.OpenXml.Drawing;
|
using DocumentFormat.OpenXml.Spreadsheet;
|
using Furion.DatabaseAccessor;
|
using System.Collections.Generic;
|
using System.Data;
|
using System.Linq;
|
|
namespace Admin.NET.Application;
|
/// <summary>
|
/// 容器分拣信息服务
|
/// </summary>
|
[ApiDescriptionSettings(ApplicationConst.WmsTaskGroupName, Order = 100)]
|
public class WmsIssueService : IDynamicApiController, ITransient
|
{
|
/// <summary>
|
/// 是否正在执行下发方法
|
/// </summary>
|
private static bool isRuning_Fun_Issue = false;
|
private static SemaphoreSlim semaphoreSlimIssue = new SemaphoreSlim(1, 1);
|
|
private readonly UserManager _userManager;
|
private readonly SqlSugarRepository<WmsContainerSort> _wmsContainerSortRep;
|
private readonly SqlSugarRepository<v_wms_stock_quan> _v_wms_stock_quanRep;
|
private readonly SqlSugarRepository<v_wms_stock_quan_for_use> _v_wms_stock_quan_for_useRep;
|
private readonly SqlSugarRepository<WmsOrderSortDetails> _wmsOrderSortDetailsRep;
|
private readonly SqlSugarRepository<WmsOrderSort> _wmsOrderSortRep;
|
private readonly SqlSugarRepository<WmsOrderMovementDetails> _wmsOrderMovementDetailsRep;
|
private readonly SqlSugarRepository<WmsOrderMovement> _wmsOrderMovementRep;
|
private readonly SqlSugarRepository<WmsTask> _wmsTaskRep;
|
private readonly SqlSugarRepository<WmsRecordPredetermineDispense> _wmsRecordPredetermineDispenseRep;
|
private readonly SqlSugarRepository<WmsStockQuan> _wmsStockQuanRep;
|
private readonly SqlSugarRepository<WmsRecordTrans> _wmsRecordTransRep;
|
|
private readonly SqlSugarRepository<WmsBaseBusinessType> _WmsBaseBusinessTypeRep;
|
private readonly SqlSugarRepository<WmsBasePlace> _wmsPlaceRep;
|
|
private readonly SqlSugarRepository<WmsLogAction> _wmsLogActionRep;
|
|
private readonly SqlSugarRepository<WmsConfigUnshelveStrategy> _wmsConfigUnshelveStrategyRep;
|
private readonly SqlSugarRepository<WmsConfigUnshelveStrategyChoose> _wmsConfigUnshelveStrategyChooseRep;
|
private readonly SqlSugarRepository<WmsConfigUnshelveStrategyRange> _wmsConfigUnshelveStrategyRangeRep;
|
private readonly SqlSugarRepository<v_wms_config_unshelve_strategy_range> _v_wms_config_unshelve_strategy_rangeRep;
|
|
private readonly SqlSugarRepository<v_empty_place> _v_empty_placeRep;
|
private readonly SqlSugarRepository<WmsBasePlace> _wmsBasePlaceRep;
|
public WmsIssueService(
|
|
UserManager userManager,
|
SqlSugarRepository<WmsStockQuan> wmsStockQuanRep,
|
SqlSugarRepository<WmsRecordPredetermineDispense> wmsRecordPredetermineDispenseRep,
|
SqlSugarRepository<WmsContainerSort> wmsContainerSortRep,
|
SqlSugarRepository<v_wms_stock_quan> v_wms_stock_quanRep,
|
|
SqlSugarRepository<v_wms_stock_quan_for_use> v_wms_stock_quan_for_useRep,
|
SqlSugarRepository<WmsOrderSortDetails> wmsOrderSortDetailsRep,
|
SqlSugarRepository<WmsOrderSort> wmsOrderSortRep,
|
SqlSugarRepository<WmsOrderMovementDetails> wmsOrderMovementDetailsRep,
|
SqlSugarRepository<WmsOrderMovement> wmsOrderMovementRep,
|
SqlSugarRepository<WmsTask> wmsTaskRep,
|
SqlSugarRepository<WmsRecordTrans> wmsRecordTransRep,
|
SqlSugarRepository<WmsBaseBusinessType> WmsBaseBusinessTypeRep,
|
SqlSugarRepository<WmsBasePlace> wmsPlaceRep,
|
SqlSugarRepository<WmsLogAction> wmsLogActionRep,
|
SqlSugarRepository<WmsConfigUnshelveStrategy> wmsConfigUnshelveStrategyRep,
|
SqlSugarRepository<WmsConfigUnshelveStrategyChoose> wmsConfigUnshelveStrategyChooseRep,
|
SqlSugarRepository<WmsConfigUnshelveStrategyRange> wmsConfigUnshelveStrategyRangeRep,
|
SqlSugarRepository<v_empty_place> v_empty_placeRep,
|
SqlSugarRepository<WmsBasePlace> wmsBasePlaceRep,
|
SqlSugarRepository<v_wms_config_unshelve_strategy_range> v_wms_config_unshelve_strategy_rangeRep
|
)
|
{
|
|
_userManager = userManager;
|
_wmsStockQuanRep = wmsStockQuanRep;
|
_wmsContainerSortRep = wmsContainerSortRep;
|
_v_wms_stock_quanRep = v_wms_stock_quanRep;
|
_v_wms_stock_quan_for_useRep = v_wms_stock_quan_for_useRep;
|
_wmsOrderSortDetailsRep = wmsOrderSortDetailsRep;
|
_wmsOrderSortRep = wmsOrderSortRep;
|
_wmsOrderMovementDetailsRep = wmsOrderMovementDetailsRep;
|
_wmsOrderMovementRep = wmsOrderMovementRep;
|
_wmsTaskRep = wmsTaskRep;
|
_wmsRecordPredetermineDispenseRep = wmsRecordPredetermineDispenseRep;
|
_wmsRecordTransRep = wmsRecordTransRep;
|
_WmsBaseBusinessTypeRep = WmsBaseBusinessTypeRep;
|
_wmsPlaceRep = wmsPlaceRep;
|
_wmsLogActionRep = wmsLogActionRep;
|
_wmsConfigUnshelveStrategyRep = wmsConfigUnshelveStrategyRep;
|
_wmsConfigUnshelveStrategyChooseRep = wmsConfigUnshelveStrategyChooseRep;
|
_wmsConfigUnshelveStrategyRangeRep = wmsConfigUnshelveStrategyRangeRep;
|
_v_empty_placeRep = v_empty_placeRep;
|
_wmsBasePlaceRep = wmsBasePlaceRep;
|
_v_wms_config_unshelve_strategy_rangeRep = v_wms_config_unshelve_strategy_rangeRep;
|
}
|
|
|
|
/// <summary>
|
/// 下发
|
/// </summary>
|
/// <param name="input"></param>
|
/// <returns></returns>
|
[HttpPost]
|
[ApiDescriptionSettings(Name = "Issue")]
|
[Description("WmsIssue/Issue")]
|
[UnitOfWork]
|
public async Task<int> Issue(List<IssueInput> input)
|
{
|
//if (input.Count > 1) throw Oops.Oh("波次单下发数量超过1");
|
try
|
{
|
if (isRuning_Fun_Issue)
|
{
|
throw Oops.Oh("程序正忙,请稍后再试");
|
}
|
await semaphoreSlimIssue.WaitAsync();
|
|
isRuning_Fun_Issue = true;
|
|
return await TaskIssue(input);
|
|
}
|
catch (Exception ex)
|
{
|
throw Oops.Oh(ex.Message);
|
}
|
finally
|
{
|
semaphoreSlimIssue.Release();
|
isRuning_Fun_Issue = false;
|
}
|
}
|
|
|
|
|
#region 私有方法
|
|
|
/// <summary>
|
/// 波次单下发
|
/// </summary>
|
/// <param name="input"></param>
|
/// <returns></returns>
|
private async Task<int> TaskIssue(List<IssueInput> input)
|
{
|
/*
|
* 1. 获取所有要下发的波次单、明细
|
* 2.校验波次单是否存在、单据状态、波次单明细剩余可下发数(考虑一个波次单明细是否可以多次下发)
|
* 3.获取所有要下发的下架单、明细
|
* 4.校验下架单是否存在、单据状态
|
* 5.根据库位、批次、容器查询库存(查询库存视图:根据容器分组)
|
* 7.计算可用库存=实际库存-锁定库存, 校验库存是否足够下发。
|
* 8.库存根据容器 库位 入库时间排序(使用遵循先进先出原则)
|
* 目前库存没有 排属性的应用
|
* 9.循环库存,判断每个容器的物料是否足够下发,不够就循环下一个容器。
|
* 10.校验库存容器是否存在未完成的调度任务,存在就不能下发
|
*
|
* 11.更新波次单
|
*/
|
|
//下架策略
|
/**
|
*1.获取所有物料 生效的 下架策略
|
*2. 根据优先级循环处理策略
|
*3.先进先出(天):先按照入库时间(年-月-日)格式排序 升序
|
*4.先进先出(小时):先按照入库时间(年-月-日-时)格式排序 升序
|
*5.满托推荐:按照 容器号分组
|
5.1 物料在一个容器库存正好= 下发的库存的 先出
|
5.2 物料在一个容器库存> 下发的库存的先出
|
5.3 物料在一个容器库存< 下发的库存的先出
|
|
*6.半托推荐:按照 容器号分组 ,库存最少的先出
|
*
|
*
|
*
|
*/
|
|
|
//新增事务记录
|
List<WmsRecordTrans> addWmsRecordTransList = new List<WmsRecordTrans>();
|
//新增操作履历
|
List<WmsLogAction> addWmsLogActionList = new List<WmsLogAction>();
|
var addWmsTaskList = new List<WmsTask>();//新增的下架任务
|
var addWmsContainerSortList = new List<WmsContainerSort>();//新增的分拣信息
|
|
var updateWmsOrderSortDetailsList = new List<WmsOrderSortDetails>();//修改的波次单明细
|
var updateWmsOrderSortList = new List<WmsOrderSort>();//修改的波次单
|
//TODO 添加事务记录
|
|
List<string> sortNoList = input.Select(x => x.SortNo).ToList();
|
|
if (sortNoList.Distinct().Count() > 1)
|
{
|
throw Oops.Oh("一次只能操作一个波次单下发");
|
}
|
|
// 获取所有波次单
|
|
//Expression<Func<WmsOrderSort, bool>> predicate = u => sortNoList.Contains(u.SortNo);
|
var allWmsOrderSortList = await _wmsOrderSortRep.GetListAsync(u => sortNoList.Contains(u.SortNo));
|
if (allWmsOrderSortList?.Count <= 0)
|
{
|
throw Oops.Oh("波次单不存在");
|
}
|
|
//获取波次单所有明细
|
var allOrderSortDetails = await _wmsOrderSortDetailsRep.GetListAsync(u => sortNoList.Contains(u.SortNo));
|
if (allOrderSortDetails?.Count <= 0)
|
{
|
throw Oops.Oh("波次单明细不存在");
|
}
|
|
//本次下发波次单明细
|
var currentOrderSortDetails = allOrderSortDetails.Where(u => input.Select(x => x.SortDetailsId).Contains(u.Id)).ToList();
|
|
|
|
if (currentOrderSortDetails?.Count <= 0)
|
{
|
throw Oops.Oh("本次下发的波次单明细不存在");
|
}
|
|
// 获取业务类型
|
BusinessTypeEnum businessTypeEnum = BusinessTypeEnum.波次下发;
|
var wmsBaseBusinessType = BusinessTypeHelper.GetBusinessTypeInfoFromDB((int)businessTypeEnum, _WmsBaseBusinessTypeRep);
|
|
|
var movementDetailsKeyList = currentOrderSortDetails.Select(x => x.RelationNo + x.RelationNoLineNumber).ToList();
|
List<string> materialCodeList = currentOrderSortDetails.Select(x => x.MaterialCode).Distinct().ToList();
|
|
var db_movementDetailsList = await _wmsOrderMovementDetailsRep.GetListAsync(x => movementDetailsKeyList.Contains(x.MovementNo + x.LineNumber));
|
var queryOrderIds = db_movementDetailsList.Select(x => x.MovementId).Distinct().ToList();
|
var orderMovementList = await _wmsOrderMovementRep.GetListAsync(x => queryOrderIds.Contains(x.Id));
|
|
//查询 所有该物料的 分配表的所有可用数据
|
var db_all_wmsRecordPredetermineDispenseList = await _wmsRecordPredetermineDispenseRep.GetListAsync(x => x.PDRecordType == PDRecordTypeEnum.分配 && x.PDRecordStatus == PDRecordStatusEnum.已分配
|
&& materialCodeList.Contains(x.MaterialCode)
|
);
|
|
|
|
////获取可操作下发的库存信息-(未排除锁定库存)库存状态已上架、质检状态合格、非虚拟库位
|
//var queryParam = ExpressionHelper.GetStockQuanForIssueOutTask();
|
//queryParam.And(x => materialCodeList.Contains(x.MaterialCode));
|
//var allStockQuanList = await _v_wms_stock_quanRep.GetListAsync(queryParam);
|
|
////根据物料编码获取物料跟踪码的可用库存信息-已排除掉分配、下发锁定库存
|
var allStockQuanUseList = await _v_wms_stock_quan_for_useRep.GetListAsync(x => materialCodeList.Contains(x.MaterialCode));
|
|
var allContainerCodeList = allStockQuanUseList.Select(s => s.ContainerCode).ToList();
|
//获取所有库存视图信息 添加事务记录用
|
List<v_wms_stock_quan> allStockQuanViewList = await _v_wms_stock_quanRep.GetListAsync(x => allContainerCodeList.Contains(x.ContainerCode));
|
|
|
if (allStockQuanViewList?.Count <= 0)
|
{
|
throw Oops.Oh("所有物料都没有库存");
|
}
|
|
|
|
|
|
|
|
|
|
|
//获取库存库位相信
|
var placeCodeList = allStockQuanViewList.Select(s => s.PlaceCode).Distinct().ToList();
|
var allPlaeList = await _wmsPlaceRep.GetListAsync(u => placeCodeList.Contains(u.PlaceCode) && u.IsDelete == false);
|
|
#region 验证
|
|
var validateInput = GetWmsOrderMovementDetailsForIssueInput(input, currentOrderSortDetails, allWmsOrderSortList, orderMovementList, db_movementDetailsList);
|
await StockQuanHelper.ValdiateStock(1, validateInput, _wmsOrderMovementDetailsRep, _v_wms_stock_quan_for_useRep);
|
|
#endregion
|
|
var containerCodeList = allStockQuanUseList.Select(s => s.ContainerCode).ToList();
|
//获取库存未完成的调度任务
|
var allTaskList = await _wmsTaskRep.GetListAsync(x => x.TaskStatus != TaskStatusEnum.已完成 && x.TaskStatus != TaskStatusEnum.已取消 && x.IsDelete == false && containerCodeList.Contains(x.ContainerCode));
|
|
|
|
|
|
//获取物料下架策略
|
#region 获取物料下架策略
|
|
//获取生效的下架策略物料范围
|
List<v_wms_config_unshelve_strategy_range> allUnshelveStrategyRangeList = await ConfigUnshelveStrategyHelper.GetUseUnshelveStrategyRangeList(_v_wms_config_unshelve_strategy_rangeRep, materialCodeList);
|
//获取生效的下架策略选项
|
List<WmsConfigUnshelveStrategyChoose> allUnshelveStrategyChooseList = await ConfigUnshelveStrategyHelper.GetUnshelveStrategyChooseListByRange(_wmsConfigUnshelveStrategyChooseRep, allUnshelveStrategyRangeList);
|
|
#endregion
|
|
foreach (var inputItem in input)
|
{
|
|
|
var item = currentOrderSortDetails.FirstOrDefault(x => x.Id == inputItem.SortDetailsId);
|
WmsOrderSort sortOrder = allWmsOrderSortList.FirstOrDefault(p => p.SortNo == item.SortNo);
|
//TODO 校验波次单状态
|
if (sortOrder == null)
|
{
|
throw Oops.Oh("本次下发的波次单不存在");
|
}
|
|
WmsOrderMovement movementOrder = orderMovementList.FirstOrDefault(p => p.OrderNo == item.RelationNo);
|
if (movementOrder == null)
|
{
|
throw Oops.Oh("本次下发关联的下架单不存在");
|
}
|
//TODO 校验下架单状态
|
var sortMovementDetails = db_movementDetailsList.FirstOrDefault(f => f.MovementNo == item.RelationNo && f.LineNumber == item.RelationNoLineNumber);
|
if (sortMovementDetails == null)
|
{
|
throw Oops.Oh("本次下发关联的下架单明细不存在");
|
}
|
|
|
if (string.IsNullOrWhiteSpace(movementOrder.ToPlaceCode) && string.IsNullOrWhiteSpace(movementOrder.ToAreaCode))
|
{
|
throw Oops.Oh($"{movementOrder.OrderTypeName}类型移动单{movementOrder.OrderNo}目标库区、目标库位不能同时为空");
|
}
|
|
|
if (string.IsNullOrWhiteSpace(movementOrder.ToPlaceCode))
|
{
|
|
var handle = FindEmptyPlaceServiceFactory.GetHandle(MaterialClassifyFlagEnum.物料, _v_empty_placeRep, _wmsBasePlaceRep, _wmsTaskRep);
|
var emptyPlaceList = await handle.MainFindMultiEmptyLocation(new FindEmptyPlaceInput() { AreaList = new List<string>() { movementOrder.ToAreaCode } });
|
|
|
var returnPlaceCode = emptyPlaceList.FirstOrDefault();
|
|
if (returnPlaceCode == null)
|
{
|
throw Oops.Oh($"库区{movementOrder.ToAreaCode}没有可推荐库位!");
|
}
|
|
movementOrder.ToPlaceCode = returnPlaceCode.PlaceCode;
|
movementOrder.ToPlaceName = returnPlaceCode.PlaceName;
|
}
|
|
|
|
|
|
|
|
|
#region 处理下发
|
|
#region 验证下发需求数
|
|
decimal issueQuantity = item.IssueQuantity ?? 0M;//波次单的历史下发数量
|
|
decimal allQuantity = item.Quantity - issueQuantity;//当前未下发数量
|
decimal occQuantity = inputItem.SendQuantity;//当前实际下发数量
|
if (allQuantity <= 0)
|
{
|
throw Oops.Oh("波次单" + item.SortNo + "行号" + item.LineNumber + "物料编号" + item.MaterialCode + $"已全部下发");
|
}
|
if (occQuantity > allQuantity)
|
{
|
throw Oops.Oh("波次单" + item.SortNo + "行号" + item.LineNumber + "物料编号" + item.MaterialCode + $"本次下发需求数量{occQuantity}大于未下发数{allQuantity}");
|
}
|
#endregion
|
|
|
// 根据物料找出库存列表中数量大于0的库存
|
var allMaterialStockQuanList = allStockQuanUseList.Where(x => (x.Quantity) > 0 && x.MaterialCode == item.MaterialCode)
|
//// 如果传入了物料批次号,则筛选出批次号相同的库存项
|
//.Where(!string.IsNullOrEmpty(item.Batch), x => x.Batch == item.Batch)
|
//.Where(!string.IsNullOrEmpty(item.ErpCode), x => x.ErpCode == item.ErpCode)
|
.ToList();
|
|
|
|
|
//处理后未下发的数量,多个同一个物料多个库存数据的情况,循环处理下发
|
decimal currentIssueQuantity = occQuantity;
|
|
|
#region 处理分配
|
|
//分配原则:1、优先按照分配的库存搞 2、其次按照入库时间搞
|
var find_db_wmsRecordPredetermineDispenseList = db_all_wmsRecordPredetermineDispenseList.Where(x => (x.MovementNo + x.MovementLineNumber) == (sortMovementDetails.MovementNo + sortMovementDetails.LineNumber)).ToList();
|
foreach (var preDisRecord in find_db_wmsRecordPredetermineDispenseList)
|
{
|
var aa = allMaterialStockQuanList.Where(x => x.SNCode == preDisRecord.SNCode).FirstOrDefault();
|
if (aa == null)
|
{
|
throw Oops.Oh("物料编号" + preDisRecord.MaterialCode + $"的分配记录(跟踪码{preDisRecord.SNCode})在库存中不存在");
|
}
|
if (aa.Quantity < preDisRecord.Quantity)
|
{
|
throw Oops.Oh("物料编号" + preDisRecord.MaterialCode + $",跟踪码{preDisRecord.SNCode}的库存数量{aa.Quantity}小于分配数量{preDisRecord.Quantity},请检查");
|
}
|
|
var _remark = $"波次下发,分配抵消,波次单{inputItem.SortNo}";
|
|
decimal changeQty = 0;
|
if (preDisRecord.Quantity >= currentIssueQuantity)
|
{
|
changeQty = currentIssueQuantity;
|
currentIssueQuantity = 0;
|
}
|
else
|
{
|
changeQty = preDisRecord.Quantity;
|
currentIssueQuantity = currentIssueQuantity - changeQty;//剩下的待下发的数量
|
}
|
preDisRecord.Quantity -= changeQty;
|
|
//扣减库存
|
aa.Quantity -= changeQty;
|
aa.ActionRemark = _remark;
|
if (preDisRecord.Quantity == 0)
|
{
|
//修改分配表
|
PredetermineDispenseHelper.SetPDRecordStatus(preDisRecord, PDRecordStatusEnum.已取消);
|
}
|
|
preDisRecord.Remarks = _remark;
|
|
//修改移动单上的预配数
|
if ((sortMovementDetails.PredetermineQuantity - changeQty) < 0)
|
{
|
sortMovementDetails.PredetermineQuantity = 0;
|
}
|
else
|
{
|
sortMovementDetails.PredetermineQuantity -= changeQty;
|
}
|
|
|
CommonDoForIssue(_WmsBaseBusinessTypeRep, addWmsTaskList, aa, item, changeQty, movementOrder, sortMovementDetails, addWmsContainerSortList, sortOrder);
|
|
}
|
|
|
#endregion
|
|
|
var sortStockQuanList = GetSortStockQuanListByStrategy(allUnshelveStrategyRangeList, allUnshelveStrategyChooseList, item, db_all_wmsRecordPredetermineDispenseList, sortMovementDetails, currentIssueQuantity, allMaterialStockQuanList);
|
var old_Count = allMaterialStockQuanList.Count();
|
allMaterialStockQuanList = sortStockQuanList.Adapt<List<v_wms_stock_quan_for_use>>();//复制排序后的对象
|
var new_Count = allMaterialStockQuanList.Count();
|
if (old_Count != new_Count)
|
{
|
throw Oops.Oh($"排序后数量不对,排序前数量{old_Count},排序后数量{new_Count}");
|
}
|
|
|
|
////同一个物料尽量从同一个库位取
|
//if (allMaterialStockQuanList?.Count() <= 0)
|
//{
|
// //缺料
|
// throw Oops.Oh("物料编号" + item.MaterialCode + $"库存数量不足,下发需求数量:{occQuantity},库存可用数量:0");
|
//}
|
////这里的Quantity 没有排除掉其他单据的分配锁定库存
|
//var usedQty = allMaterialStockQuanList.Where(o => o.MaterialCode == item.MaterialCode).Sum(x => x.Quantity);
|
//if (usedQty < occQuantity)
|
//{
|
// throw Oops.Oh("物料编号" + item.MaterialCode + $"库存数量不足,下发需求数量:{occQuantity},库存可用数量:{usedQty}");
|
//}
|
|
|
|
|
foreach (var stockQuanItem in allMaterialStockQuanList)
|
{
|
//别的单子占用该跟踪号的数量,需要排除掉 别人占用的分配记录数据
|
decimal other_PredetermineQuery_Qty = GetOtherPredetermineQuery(db_all_wmsRecordPredetermineDispenseList, sortMovementDetails, stockQuanItem);
|
|
//库位属性是 封存和禁出的,不能操作下架
|
if (stockQuanItem.PlaceStatus == PlaceStatusEnum.封存)
|
{
|
throw Oops.Oh($"库位编号{stockQuanItem.PlaceCode}库位属性是{PlaceStatusEnum.封存.GetDescription()}");
|
}
|
if (stockQuanItem.PlaceStatus == PlaceStatusEnum.禁出)
|
{
|
throw Oops.Oh($"库位编号{stockQuanItem.PlaceCode}库位属性是{PlaceStatusEnum.禁出.GetDescription()}");
|
}
|
//校验容器是否 有未完成的任务。
|
if (allTaskList.Any(a => a.ContainerCode.Equals(stockQuanItem.ContainerCode)))
|
{
|
throw Oops.Oh($"容器{stockQuanItem.ContainerCode}存在未完成的调用任务");
|
}
|
|
|
if (currentIssueQuantity > 0)
|
{
|
|
//当前物料可用库存
|
var currentUsedQuantity = stockQuanItem.Quantity - other_PredetermineQuery_Qty;
|
currentUsedQuantity = currentUsedQuantity <= 0 ? 0 : currentUsedQuantity;
|
if (currentUsedQuantity <= 0)
|
{
|
continue;
|
}
|
|
|
|
|
|
//循环库存信息 处理下发数
|
decimal changQty = 0M;//实际本次分拣数量
|
if (currentUsedQuantity >= currentIssueQuantity)//下发数量小于等于库存
|
{
|
changQty = currentIssueQuantity;
|
currentIssueQuantity = 0;//全部下发,不在处理
|
|
}
|
else
|
{
|
//当前下发数未能完全下发,累计下发数
|
changQty = currentUsedQuantity;
|
currentIssueQuantity = currentIssueQuantity - currentUsedQuantity;
|
|
}
|
|
//修改移动单上的预配数
|
if ((sortMovementDetails.PredetermineQuantity - changQty) < 0)
|
{
|
sortMovementDetails.PredetermineQuantity = 0;
|
}
|
else
|
{
|
sortMovementDetails.PredetermineQuantity -= changQty;
|
}
|
|
|
CommonDoForIssue(_WmsBaseBusinessTypeRep, addWmsTaskList, stockQuanItem, item, changQty, movementOrder, sortMovementDetails, addWmsContainerSortList, sortOrder);
|
|
if (currentIssueQuantity == 0)
|
{
|
break;
|
}
|
|
|
}
|
else
|
{
|
break;
|
}
|
}
|
|
if (currentIssueQuantity > 0)
|
{
|
throw Oops.Oh("物料编号" + item.MaterialCode + $"库存数量不足");
|
|
}
|
|
//更新波次单的已下发数量
|
|
decimal lastIssueQuantity = issueQuantity + occQuantity;//波次单的历史下发数量
|
item.IssueQuantity += occQuantity;//累计下发数
|
item.OffShelvesQuantity += occQuantity;//下发完成就认为已下架
|
|
//更新波次单明细的状态
|
OrderHelper.updateOrderSortDetailsStatus(item);
|
updateWmsOrderSortDetailsList.Add(item);
|
|
//更新下架单明细的 下架数
|
sortMovementDetails.OffShelvesQuantity += occQuantity;
|
|
#endregion
|
}
|
|
foreach (var item in allWmsOrderSortList)
|
{
|
var updateOrderSortDetails = allOrderSortDetails.Where(w => w.SortId == item.Id).ToList();
|
////计算 波次单单的状态
|
OrderHelper.UpdatOrderSortStatus(updateOrderSortDetails, item);
|
updateWmsOrderSortList.Add(item);
|
}
|
|
|
|
|
|
if (addWmsContainerSortList?.Count <= 0)
|
{
|
|
|
throw Oops.Oh("没有可下发的物料,库存全部不足");
|
|
}
|
|
if (addWmsTaskList?.Count <= 0)
|
{
|
throw Oops.Oh("下架任务创建失败");
|
}
|
|
|
//把容器上的库存都锁定
|
var queryContainerCodeList = addWmsContainerSortList.Select(x => x.ContainerCode).Distinct().ToList();
|
var stockQunList = await _wmsStockQuanRep.GetListAsync(x => queryContainerCodeList.Contains(x.ContainerCode));
|
foreach (var item in stockQunList)
|
{
|
string recordTransRemarks = $"波次下发";
|
LockInfo freezeInfo = new LockInfo()
|
{
|
LockReason = "波次下发",
|
LockTime = DateTime.Now,
|
LockUser = _userManager.RealName,//登录人的真实姓名
|
LockStatus = LockStatusEnum.已锁定,//
|
};
|
StockQuanHelper.UpdateStockLockStatus(item, freezeInfo);
|
|
|
#region 转移库存添加事务、操作日志
|
|
TransferOtherDetail transferOtherDetail = new TransferOtherDetail()
|
{
|
RelationNo = sortNoList[0],//波次单号
|
RelationNoLineNumber = string.Empty,
|
Remarks = recordTransRemarks,
|
|
};
|
|
|
//根据库存跟踪码视图可以查询到信息
|
//获取源库存信息
|
var sourceStockView = StockQuanHelper.GetVMmsStockQuan(allStockQuanViewList, item.SNCode);
|
|
//update by liuwq 20240730
|
//获取目标库位
|
//波次下发 库存没有变更库位,目标库位还是源库位,调度任务处理后才会变更库位
|
var oldToPlace = BaseInfoHelper.GetPlace(sourceStockView.PlaceCode, allPlaeList);
|
|
|
|
|
//新增事务记录
|
WmsRecordTrans addWmsRecordTrans = LogRecordHelper.CreateWmsRecordTrans(wmsBaseBusinessType, sourceStockView, item, oldToPlace, transferOtherDetail);
|
|
|
addWmsRecordTransList.Add(addWmsRecordTrans);
|
|
//新增操作日志
|
WmsLogAction wareActionLog = LogActionHelper.CreateWmsLogAction(item.Id, recordTransRemarks, $"锁定库存跟踪码{item.SNCode}");
|
addWmsLogActionList.Add(wareActionLog);
|
#endregion
|
|
}
|
|
|
// var _tenant = _wmsContainerSortRep.AsTenant();
|
try
|
{
|
#region 事务内执行操作
|
await _wmsContainerSortRep.InsertRangeAsync(addWmsContainerSortList);
|
|
await _wmsTaskRep.InsertRangeAsync(addWmsTaskList);
|
await _wmsOrderSortRep.UpdateRangeAsync(updateWmsOrderSortList);
|
await _wmsOrderSortDetailsRep.UpdateRangeAsync(updateWmsOrderSortDetailsList);
|
|
|
//新增事务记录、收货记录、操作日志
|
await _wmsLogActionRep.InsertRangeAsync(addWmsLogActionList);
|
await _wmsRecordTransRep.InsertRangeAsync(addWmsRecordTransList);
|
|
//更新庫存
|
await _wmsRecordPredetermineDispenseRep.UpdateRangeAsync(db_all_wmsRecordPredetermineDispenseList);//更新分配表
|
//把容器上的库存都锁定
|
await _wmsStockQuanRep.UpdateRangeAsync(stockQunList);
|
|
await _wmsOrderMovementDetailsRep.UpdateRangeAsync(db_movementDetailsList);
|
|
#endregion
|
|
// await _tenant.CommitTranAsync();
|
}
|
catch (Exception ex)
|
{
|
|
// await _tenant.RollbackTranAsync();
|
throw;
|
}
|
|
|
|
return 1;
|
}
|
|
|
/// <summary>
|
/// 根据策略排序
|
/// </summary>
|
/// <param name="allUnshelveStrategyRangeList"></param>
|
/// <param name="allUnshelveStrategyChooseList"></param>
|
/// <param name="item"></param>
|
/// <param name="db_all_wmsRecordPredetermineDispenseList"></param>
|
/// <param name="sortMovementDetails"></param>
|
/// <param name="currentIssueQuantity"></param>
|
/// <param name="org_allMaterialStockQuanList">原始的对象</param>
|
/// <returns></returns>
|
private static List<StockForUnshelveStrategy> GetSortStockQuanListByStrategy(List<v_wms_config_unshelve_strategy_range> allUnshelveStrategyRangeList, List<WmsConfigUnshelveStrategyChoose> allUnshelveStrategyChooseList,
|
WmsOrderSortDetails item, List<WmsRecordPredetermineDispense> db_all_wmsRecordPredetermineDispenseList, WmsOrderMovementDetails sortMovementDetails, decimal currentIssueQuantity,
|
List<v_wms_stock_quan_for_use> org_allMaterialStockQuanList)
|
{
|
//这里不才走变更库存信息,仅仅排序,所以必须是复制对象,而不是直接修改对象
|
List<StockForUnshelveStrategy> allMaterialStockQuanList = org_allMaterialStockQuanList.Adapt<List<StockForUnshelveStrategy>>();//复制对象
|
foreach (var item2 in allMaterialStockQuanList)
|
{
|
item2.HanlerQty = item2.Quantity;
|
}
|
|
//策略是否可用
|
bool isUseStrategy = false;
|
|
List<StockForUnshelveStrategy> findStockQuanList = new List<StockForUnshelveStrategy>();
|
|
List<v_wms_config_unshelve_strategy_range> currentUnshelveStrategyRangeList = allUnshelveStrategyRangeList.Where(w => w.MaterialCode == item.MaterialCode).ToList();
|
List<WmsConfigUnshelveStrategyChoose> currentUnshelveStrategyChooseList = new List<WmsConfigUnshelveStrategyChoose>();
|
if (currentUnshelveStrategyRangeList?.Count > 0)
|
{
|
if (currentUnshelveStrategyRangeList.Where(w => w.MaterialCode == item.MaterialCode).Select(s => s.StrategyCode).Distinct().Count() > 1)
|
{
|
throw Oops.Oh($"物料编号" + item.MaterialCode + "存在多个下架策略,请检查");
|
}
|
//当前物料下架策略选项-- 根据优先级升序排序 级别越小 越优先
|
var allChooseList = allUnshelveStrategyChooseList.OrderBy(x => x.Priority).Where(w => w.StrategyCode == currentUnshelveStrategyRangeList[0].StrategyCode && w.IsDelete == false).ToList();
|
if (allChooseList?.Count <= 0)
|
{
|
//throw Oops.Oh($"下架策略{currentUnshelveStrategyRangeList[0].StrategyCode}物料编号" + item.MaterialCode + "下架策略选项不存在");
|
return UseDefaultOrderForIssue(allMaterialStockQuanList, ref findStockQuanList);
|
}
|
|
currentUnshelveStrategyChooseList.AddRange(allChooseList);
|
|
//if (currentUnshelveStrategyChooseList.Count() > 1)
|
//{
|
// throw Oops.Oh($"下架策略{currentUnshelveStrategyRangeList[0].StrategyCode}物料编号" + item.MaterialCode + "存在多个下架策略选项,请检查");
|
//}
|
|
isUseStrategy = true;
|
|
}
|
//有下架策略
|
if (isUseStrategy == true)
|
{
|
|
|
List<v_wms_stock_quan_for_use> groupStockQuanList = new List<v_wms_stock_quan_for_use>();
|
|
|
//库存数使用Quantity ,不使用可用库存AvailableQty
|
//循环扣减掉其他单据的分配占用的库存数,上面先完成了分逻辑,这里拿到的就是当前单据物料可以使用的库存数
|
foreach (var allItem in allMaterialStockQuanList)
|
{
|
//别的单子占用该跟踪号的数量,需要排除掉 别人占用的分配记录数据
|
decimal other_PredetermineQuery_Qty = GetOtherPredetermineQuery(db_all_wmsRecordPredetermineDispenseList, sortMovementDetails, allItem);
|
allItem.HanlerQty -= other_PredetermineQuery_Qty;
|
}
|
|
var firstUnshelveStrategyChoose = currentUnshelveStrategyChooseList[0].StrategyOption;
|
StrategyOptionEnum secondUnshelveStrategyChoose = 0;
|
if (currentUnshelveStrategyChooseList.Count >= 2)
|
{
|
secondUnshelveStrategyChoose = currentUnshelveStrategyChooseList[1].StrategyOption;
|
}
|
|
|
//共 7种情况
|
switch (firstUnshelveStrategyChoose)
|
{
|
case StrategyOptionEnum.整拖优先:
|
if (secondUnshelveStrategyChoose != 0)
|
{
|
if (secondUnshelveStrategyChoose == StrategyOptionEnum.先进先出_天)
|
{
|
FullContiner_FIFOByDay_OrderForIssue(allMaterialStockQuanList, ref findStockQuanList, currentIssueQuantity);
|
}
|
else
|
{
|
throw Oops.Oh($"策略有问题,第二项不是 先进先出_天");
|
}
|
}
|
else
|
{
|
FullContiner_OrderForIssue(allMaterialStockQuanList, ref findStockQuanList, currentIssueQuantity);
|
}
|
|
break;
|
case StrategyOptionEnum.零散优先:
|
if (secondUnshelveStrategyChoose != 0)
|
{
|
if (secondUnshelveStrategyChoose == StrategyOptionEnum.先进先出_天)
|
{
|
HalfContiner_FIFOByDay_OrderForIssue(allMaterialStockQuanList, ref findStockQuanList, currentIssueQuantity);
|
}
|
else
|
{
|
throw Oops.Oh($"策略有问题,第二项不是 先进先出_天");
|
}
|
}
|
else
|
{
|
HalfContiner_OrderForIssue(allMaterialStockQuanList, ref findStockQuanList, currentIssueQuantity);
|
}
|
break;
|
case StrategyOptionEnum.先进先出_天:
|
if (secondUnshelveStrategyChoose != 0)
|
{
|
if (secondUnshelveStrategyChoose == StrategyOptionEnum.整拖优先)
|
{
|
FIFOByDay_FullContiner_OrderForIssue(allMaterialStockQuanList, ref findStockQuanList, currentIssueQuantity);
|
}
|
else if (secondUnshelveStrategyChoose == StrategyOptionEnum.零散优先)
|
{
|
FIFOByDay_HalfContiner_OrderForIssue(allMaterialStockQuanList, ref findStockQuanList, currentIssueQuantity);
|
}
|
else
|
{
|
throw Oops.Oh($"策略有问题,第二项不是 满托推荐或半满推荐");
|
}
|
}
|
else
|
{
|
FIFOByDay_OrderForIssue(allMaterialStockQuanList, ref findStockQuanList, currentIssueQuantity);
|
}
|
break;
|
default:
|
throw Oops.Oh($"暂不支持");
|
}
|
}
|
else//没有下架策略
|
{
|
return UseDefaultOrderForIssue(allMaterialStockQuanList, ref findStockQuanList);
|
}
|
|
return findStockQuanList;
|
}
|
|
/// <summary>
|
/// 默认的下架策略-先进先出
|
/// </summary>
|
/// <param name="allMaterialStockQuanList"></param>
|
/// <param name="findStockQuanList"></param>
|
/// <returns></returns>
|
private static List<StockForUnshelveStrategy> UseDefaultOrderForIssue(List<StockForUnshelveStrategy> allMaterialStockQuanList, ref List<StockForUnshelveStrategy> findStockQuanList)
|
{
|
var lastResult = allMaterialStockQuanList.OrderBy(o => o.RecordInsertTime).ToList();
|
findStockQuanList.AddRange(lastResult);
|
return findStockQuanList;
|
}
|
|
|
#region 满托推荐=>先入先出(天)的下架策略
|
|
/// <summary>
|
/// 满托推荐=>先入先出(天)的下架策略 (2)
|
/// </summary>
|
/// <param name="allMaterialStockQuanList"></param>
|
/// <param name="findStockQuanList"></param>
|
/// <param name="currentIssueQuantity"></param>
|
/// <returns></returns>
|
private static List<StockForUnshelveStrategy> FullContiner_FIFOByDay_OrderForIssue(List<StockForUnshelveStrategy> allMaterialStockQuanList, ref List<StockForUnshelveStrategy> findStockQuanList, decimal currentIssueQuantity)
|
{
|
return Common_FullContiner_First_OrderForIssue(2, allMaterialStockQuanList, ref findStockQuanList, currentIssueQuantity);
|
}
|
/// <summary>
|
/// 满托推荐的下架策略 (1)
|
/// </summary>
|
/// <param name="allMaterialStockQuanList"></param>
|
/// <param name="findStockQuanList"></param>
|
/// <param name="currentIssueQuantity"></param>
|
/// <returns></returns>
|
private static List<StockForUnshelveStrategy> FullContiner_OrderForIssue(List<StockForUnshelveStrategy> allMaterialStockQuanList, ref List<StockForUnshelveStrategy> findStockQuanList, decimal currentIssueQuantity)
|
{
|
return Common_FullContiner_First_OrderForIssue(1, allMaterialStockQuanList, ref findStockQuanList, currentIssueQuantity);
|
}
|
|
/// <summary>
|
/// 满托推荐的下架策略 (1)
|
/// 满托推荐=>先入先出(天)的下架策略 (2)
|
/// </summary>
|
/// <param name="flag">1:满托推荐的下架策略 2:满托推荐=>先入先出(天)的下架策略</param>
|
/// <param name="allMaterialStockQuanList"></param>
|
/// <param name="findStockQuanList"></param>
|
/// <param name="currentIssueQuantity"></param>
|
/// <returns></returns>
|
private static List<StockForUnshelveStrategy> Common_FullContiner_First_OrderForIssue(int flag, List<StockForUnshelveStrategy> allMaterialStockQuanList, ref List<StockForUnshelveStrategy> findStockQuanList, decimal currentIssueQuantity)
|
{
|
var group = allMaterialStockQuanList.GroupBy(g => new { g.ContainerCode, g.MaterialCode });
|
|
var lastResult1 = group.Where(w => w.Sum(x => x.Quantity) == currentIssueQuantity);
|
if (flag == 2)
|
{
|
lastResult1 = lastResult1.OrderBy(x => x.Min(y => Convert.ToInt32(y.RecordInsertTime.ToString("yyyyMMdd"))));
|
}
|
foreach (var item in lastResult1)
|
{
|
findStockQuanList.AddRange(item.ToList());
|
}
|
|
var lastResult2 = group.Where(w => w.Sum(x => x.Quantity) > currentIssueQuantity).OrderByDescending(x => x.Sum(y => y.Quantity));
|
if (flag == 2)
|
{
|
lastResult2 = lastResult2.OrderBy(x => x.Min(y => Convert.ToInt32(y.RecordInsertTime.ToString("yyyyMMdd"))));
|
}
|
foreach (var item in lastResult2)
|
{
|
findStockQuanList.AddRange(item.ToList());
|
}
|
|
var lastResult3 = group.Where(w => w.Sum(x => x.Quantity) < currentIssueQuantity).OrderByDescending(x => x.Sum(y => y.Quantity));
|
if (flag == 2)
|
{
|
lastResult3 = lastResult3.OrderBy(x => x.Min(y => Convert.ToInt32(y.RecordInsertTime.ToString("yyyyMMdd"))));
|
}
|
foreach (var item in lastResult3)
|
{
|
findStockQuanList.AddRange(item.ToList());
|
}
|
|
|
return findStockQuanList;
|
}
|
|
#endregion
|
|
#region 半满推荐=>先入先出(天)的下架策略
|
|
|
/// <summary>
|
/// 半满推荐=>先入先出(天)的下架策略 (2)
|
/// </summary>
|
/// <param name="allMaterialStockQuanList"></param>
|
/// <param name="findStockQuanList"></param>
|
/// <param name="currentIssueQuantity"></param>
|
/// <returns></returns>
|
private static List<StockForUnshelveStrategy> HalfContiner_FIFOByDay_OrderForIssue(List<StockForUnshelveStrategy> allMaterialStockQuanList, ref List<StockForUnshelveStrategy> findStockQuanList, decimal currentIssueQuantity)
|
{
|
return Common_HalfContiner_First_OrderForIssue(2, allMaterialStockQuanList, ref findStockQuanList, currentIssueQuantity);
|
}
|
/// <summary>
|
///半满推荐的下架策略 (1)
|
/// </summary>
|
/// <param name="allMaterialStockQuanList"></param>
|
/// <param name="findStockQuanList"></param>
|
/// <param name="currentIssueQuantity"></param>
|
/// <returns></returns>
|
private static List<StockForUnshelveStrategy> HalfContiner_OrderForIssue(List<StockForUnshelveStrategy> allMaterialStockQuanList, ref List<StockForUnshelveStrategy> findStockQuanList, decimal currentIssueQuantity)
|
{
|
return Common_HalfContiner_First_OrderForIssue(1, allMaterialStockQuanList, ref findStockQuanList, currentIssueQuantity);
|
}
|
|
/// <summary>
|
/// (1)半满推荐的下架策略
|
/// (2) 半满推荐=>先入先出(天)的下架策略
|
/// </summary>
|
/// <param name="flag"></param>
|
/// <param name="allMaterialStockQuanList"></param>
|
/// <param name="findStockQuanList"></param>
|
/// <param name="currentIssueQuantity"></param>
|
/// <returns></returns>
|
private static List<StockForUnshelveStrategy> Common_HalfContiner_First_OrderForIssue(int flag, List<StockForUnshelveStrategy> allMaterialStockQuanList, ref List<StockForUnshelveStrategy> findStockQuanList, decimal currentIssueQuantity)
|
{
|
var group = allMaterialStockQuanList.GroupBy(g => new { g.ContainerCode, g.MaterialCode });
|
|
var lastResult3 = group.Where(w => w.Sum(x => x.Quantity) < currentIssueQuantity).OrderBy(x => x.Sum(y => y.Quantity));
|
if (flag == 2)
|
{
|
lastResult3 = lastResult3.OrderBy(x => x.Min(y => Convert.ToInt32(y.RecordInsertTime.ToString("yyyyMMdd"))));
|
}
|
foreach (var item in lastResult3)
|
{
|
findStockQuanList.AddRange(item.ToList());
|
}
|
|
var lastResult1 = group.Where(w => w.Sum(x => x.Quantity) == currentIssueQuantity);
|
if (flag == 2)
|
{
|
lastResult1 = lastResult1.OrderBy(x => x.Min(y => Convert.ToInt32(y.RecordInsertTime.ToString("yyyyMMdd"))));
|
}
|
foreach (var item in lastResult1)
|
{
|
findStockQuanList.AddRange(item.ToList());
|
}
|
|
var lastResult2 = group.Where(w => w.Sum(x => x.Quantity) > currentIssueQuantity).OrderBy(x => x.Sum(y => y.Quantity));
|
if (flag == 2)
|
{
|
lastResult2 = lastResult2.OrderBy(x => x.Min(y => Convert.ToInt32(y.RecordInsertTime.ToString("yyyyMMdd"))));
|
}
|
foreach (var item in lastResult2)
|
{
|
findStockQuanList.AddRange(item.ToList());
|
}
|
|
|
|
return findStockQuanList;
|
}
|
|
|
|
#endregion
|
|
#region 先入先出(天)的
|
|
/// <summary>
|
/// 只有 先入先出(天)
|
/// </summary>
|
/// <param name="allMaterialStockQuanList"></param>
|
/// <param name="findStockQuanList"></param>
|
/// <param name="currentIssueQuantity"></param>
|
/// <returns></returns>
|
private static List<StockForUnshelveStrategy> FIFOByDay_OrderForIssue(List<StockForUnshelveStrategy> allMaterialStockQuanList, ref List<StockForUnshelveStrategy> findStockQuanList, decimal currentIssueQuantity)
|
{
|
return Common_FIFOByDay_First_OrderForIssue(0, allMaterialStockQuanList, ref findStockQuanList, currentIssueQuantity);
|
}
|
|
/// <summary>
|
/// 先入先出(天)=>其次满托
|
/// </summary>
|
/// <param name="allMaterialStockQuanList"></param>
|
/// <param name="findStockQuanList"></param>
|
/// <param name="currentIssueQuantity"></param>
|
/// <returns></returns>
|
private static List<StockForUnshelveStrategy> FIFOByDay_FullContiner_OrderForIssue(List<StockForUnshelveStrategy> allMaterialStockQuanList, ref List<StockForUnshelveStrategy> findStockQuanList, decimal currentIssueQuantity)
|
{
|
return Common_FIFOByDay_First_OrderForIssue(1, allMaterialStockQuanList, ref findStockQuanList, currentIssueQuantity);
|
}
|
|
/// <summary>
|
/// 先入先出(天)=>其次半托
|
/// </summary>
|
/// <param name="allMaterialStockQuanList"></param>
|
/// <param name="findStockQuanList"></param>
|
/// <param name="currentIssueQuantity"></param>
|
/// <returns></returns>
|
private static List<StockForUnshelveStrategy> FIFOByDay_HalfContiner_OrderForIssue(List<StockForUnshelveStrategy> allMaterialStockQuanList, ref List<StockForUnshelveStrategy> findStockQuanList, decimal currentIssueQuantity)
|
{
|
return Common_FIFOByDay_First_OrderForIssue(2, allMaterialStockQuanList, ref findStockQuanList, currentIssueQuantity);
|
}
|
|
/// <summary>
|
/// 公共-先入先出(天)的
|
/// </summary>
|
/// <param name="flag">0:只有FIFODay 1:其次满托 2:其次半托</param>
|
/// <param name="allMaterialStockQuanList"></param>
|
/// <param name="findStockQuanList"></param>
|
/// <param name="currentIssueQuantity"></param>
|
/// <returns></returns>
|
private static List<StockForUnshelveStrategy> Common_FIFOByDay_First_OrderForIssue(int flag, List<StockForUnshelveStrategy> allMaterialStockQuanList, ref List<StockForUnshelveStrategy> findStockQuanList, decimal currentIssueQuantity)
|
{
|
var groupByDay = allMaterialStockQuanList.GroupBy(g => new { g.RecordInsertTime.Date });
|
|
//groupByDay 是按照 入库时间(天)分组
|
if (flag == 2 || flag == 1)
|
{
|
foreach (var item in groupByDay)
|
{
|
//在每个(天)分组中,再寻找相同天的优先满托或半托出库的
|
|
if (flag == 1)
|
{
|
Common_HalfContiner_First_OrderForIssue(1, item.ToList(), ref findStockQuanList, currentIssueQuantity);
|
}
|
if (flag == 2)
|
{
|
Common_HalfContiner_First_OrderForIssue(1, item.ToList(), ref findStockQuanList, currentIssueQuantity);
|
}
|
}
|
}
|
else
|
{
|
groupByDay = groupByDay.OrderBy(o => o.Key.Date);
|
foreach (var item in groupByDay)
|
{
|
findStockQuanList.AddRange(item.ToList());
|
}
|
}
|
|
return findStockQuanList;
|
}
|
|
|
#endregion
|
|
|
/// <summary>
|
/// 根据分拣明细获取下架策略选项
|
/// </summary>
|
/// <param name="allUnshelveStrategyRangeList"></param>
|
/// <param name="allUnshelveStrategyChooseList"></param>
|
/// <param name="item"></param>
|
/// <returns></returns>
|
|
private static List<WmsConfigUnshelveStrategyChoose> GetUnshelveStrategyChooseListBySortDetails(List<v_wms_config_unshelve_strategy_range> allUnshelveStrategyRangeList, List<WmsConfigUnshelveStrategyChoose> allUnshelveStrategyChooseList, WmsOrderSortDetails item)
|
{
|
List<v_wms_config_unshelve_strategy_range> currentUnshelveStrategyRangeList = allUnshelveStrategyRangeList.Where(w => w.MaterialCode == item.MaterialCode).ToList();
|
List<WmsConfigUnshelveStrategyChoose> currentUnshelveStrategyChooseList = new List<WmsConfigUnshelveStrategyChoose>();
|
if (currentUnshelveStrategyRangeList?.Count > 0)
|
{
|
if (currentUnshelveStrategyRangeList.Where(w => w.MaterialCode == item.MaterialCode).Select(s => s.StrategyCode).Distinct().Count() > 1)
|
{
|
throw Oops.Oh($"物料编号" + item.MaterialCode + "存在多个下架策略,请检查");
|
}
|
//当前物料下架策略选项-- 根据优先级升序排序 级别越小 越优先
|
var allChooseList = allUnshelveStrategyChooseList.OrderBy(x => x.Priority).Where(w => w.StrategyCode == currentUnshelveStrategyRangeList[0].StrategyCode && w.IsDelete == false).ToList();
|
if (currentUnshelveStrategyChooseList?.Count <= 0)
|
{
|
throw Oops.Oh($"下架策略{currentUnshelveStrategyRangeList[0].StrategyCode}物料编号" + item.MaterialCode + "下架策略选项不存在");
|
}
|
|
////if (currentUnshelveStrategyChooseList.Count() > 1)
|
////{
|
//// throw Oops.Oh($"下架策略{currentUnshelveStrategyRangeList[0].StrategyCode}物料编号" + item.MaterialCode + "存在多个下架策略选项,请检查");
|
////}
|
|
currentUnshelveStrategyChooseList.AddRange(allChooseList);
|
|
}
|
|
return currentUnshelveStrategyChooseList;
|
}
|
|
private static decimal GetOtherPredetermineQuery(List<WmsRecordPredetermineDispense> db_all_wmsRecordPredetermineDispenseList, WmsOrderMovementDetails sortMovementDetails, v_wms_stock_quan_for_use stockQuanItem)
|
{
|
return db_all_wmsRecordPredetermineDispenseList.Where(x => (x.MovementNo + x.MovementLineNumber) != (sortMovementDetails.MovementNo + sortMovementDetails.LineNumber)
|
&& x.MaterialCode == stockQuanItem.MaterialCode
|
&& x.SNCode == stockQuanItem.SNCode
|
).Sum(x => x.Quantity);
|
}
|
|
/// <summary>
|
/// 获取校验下发参数对象
|
/// </summary>
|
/// <param name="input"></param>
|
/// <param name="currentOrderSortDetails"></param>
|
/// <param name="allWmsOrderSortList"></param>
|
/// <param name="orderMovementList"></param>
|
/// <param name="movementDetailsList"></param>
|
/// <returns></returns>
|
private static List<WmsOrderMovementDetailsForIssueInput> GetWmsOrderMovementDetailsForIssueInput(List<IssueInput> input, List<WmsOrderSortDetails> currentOrderSortDetails, List<WmsOrderSort> allWmsOrderSortList,
|
List<WmsOrderMovement> orderMovementList, List<WmsOrderMovementDetails> movementDetailsList
|
)
|
{
|
List<WmsOrderMovementDetailsForIssueInput> result = new List<WmsOrderMovementDetailsForIssueInput>();
|
foreach (var inputItem in input)
|
{
|
var item = currentOrderSortDetails.FirstOrDefault(x => x.Id == inputItem.SortDetailsId);
|
WmsOrderSort sortOrder = allWmsOrderSortList.FirstOrDefault(p => p.SortNo == item.SortNo);
|
//TODO 校验波次单状态
|
if (sortOrder == null)
|
{
|
throw Oops.Oh("本次下发的波次单不存在");
|
}
|
|
WmsOrderMovement movementOrder = orderMovementList.FirstOrDefault(p => p.OrderNo == item.RelationNo);
|
if (movementOrder == null)
|
{
|
throw Oops.Oh("本次下发关联的下架单不存在");
|
}
|
//TODO 校验下架单状态
|
var sortMovementDetails = movementDetailsList.FirstOrDefault(f => f.MovementNo == item.RelationNo && f.LineNumber == item.RelationNoLineNumber);
|
if (sortMovementDetails == null)
|
{
|
throw Oops.Oh("本次下发关联的下架单明细不存在");
|
}
|
|
WmsOrderMovementDetailsForIssueInput res = sortMovementDetails.Adapt<WmsOrderMovementDetailsForIssueInput>();
|
res.SendQuantity = inputItem.SendQuantity;
|
|
result.Add(res);
|
}
|
return result;
|
}
|
|
|
/// <summary>
|
/// 波次下發統一處理
|
/// </summary>
|
/// <param name="addWmsTaskList"></param>
|
/// <param name="stockQuanItem"></param>
|
/// <param name="item"></param>
|
/// <param name="changeQty"></param>
|
/// <param name="movementOrder"></param>
|
/// <param name="sortMovementDetails"></param>
|
/// <param name="addWmsContainerSortList"></param>
|
private static void CommonDoForIssue(SqlSugarRepository<WmsBaseBusinessType> _WmsBaseBusinessTypeRep, List<WmsTask> addWmsTaskList, v_wms_stock_quan_for_use stockQuanItem,
|
WmsOrderSortDetails item, decimal changeQty,
|
WmsOrderMovement movementOrder, WmsOrderMovementDetails sortMovementDetails, List<WmsContainerSort> addWmsContainerSortList, WmsOrderSort sortOrder)
|
{
|
//创建锁定库存
|
var addWmsStockQuanLock = CommonCreateWmsStockQuanLock(item, changeQty, stockQuanItem);
|
|
//创建分拣信息
|
var addWmsContainerSort = CommonCreateWmsContainerSort(item, movementOrder, sortMovementDetails, changeQty, addWmsStockQuanLock);
|
addWmsContainerSortList.Add(addWmsContainerSort);
|
|
//创建下架调度任务
|
var addWmsTask = CommonCreateWmsTask(_WmsBaseBusinessTypeRep, sortOrder.SortNo, movementOrder, stockQuanItem);
|
|
//同一个容易创建一个 下架任务即可
|
CreateWmsTask(addWmsTaskList, addWmsTask, stockQuanItem);
|
|
}
|
|
/// <summary>
|
/// 公共創建調度任務
|
/// </summary>
|
/// <param name="addWmsTaskList"></param>
|
/// <param name="addWmsTask"></param>
|
/// <param name="stockQuanItem"></param>
|
private static void CreateWmsTask(List<WmsTask> addWmsTaskList, WmsTask addWmsTask, v_wms_stock_quan_for_use stockQuanItem)
|
{
|
//同一个容易创建一个 下架任务即可
|
if (!addWmsTaskList.Any(a => a.ContainerCode == stockQuanItem.ContainerCode))
|
{
|
addWmsTaskList.Add(addWmsTask);
|
}
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
/// 创建下架调度任务公共方法
|
/// </summary>
|
/// <param name="orderNo"></param>
|
/// <param name="moveTypeEnum"></param>
|
/// <param name="movementOrder"></param>
|
/// <param name="stockQuanItem"></param>
|
/// <returns></returns>
|
private static WmsTask CommonCreateWmsTask(SqlSugarRepository<WmsBaseBusinessType> _WmsBaseBusinessTypeRep, string orderNo, WmsOrderMovement movementOrder, v_wms_stock_quan_for_use stockQuanItem)
|
{
|
|
string toPlaceCode = movementOrder.ToPlaceCode;
|
string toAreaCode = movementOrder.ToAreaCode;
|
|
|
var businessTypeInfo = BusinessTypeHelper.GetBusinessTypeInfoFromDB((int)movementOrder.BusinessType, _WmsBaseBusinessTypeRep);
|
return new WmsTask()
|
{
|
MoveType = businessTypeInfo.MoveType,
|
MoveTypeName = businessTypeInfo.MoveTypeName,
|
BusinessType = businessTypeInfo.BusinessTypeValue,
|
BusinessTypeName = businessTypeInfo.BusinessTypeName,
|
|
ContainerCode = stockQuanItem.ContainerCode,
|
|
SourcePlaceCode = stockQuanItem.PlaceCode,//下架单指定的源库位
|
|
ToPlaceCode = toPlaceCode,//下架单指定的目标库位
|
ToAreaCode = toAreaCode,//下架单指定的目标库区
|
IsFlagFinish = false,
|
OrderNo = orderNo,//创建任务的单据号
|
RelationNo = movementOrder.OrderNo,//下架单号
|
TaskDescribe = businessTypeInfo.BusinessTypeName,
|
TaskStatus = TaskStatusEnum.新建,
|
TaskStatusName = TaskStatusEnum.新建.GetDescription(),
|
TaskNo = Yitter.IdGenerator.YitIdHelper.NextId().ToString(),
|
TaskPriority = 0,
|
IssueTime = DateTime.Now,
|
TaskName = businessTypeInfo.BusinessTypeName
|
|
|
};
|
|
|
|
}
|
|
/// <summary>
|
/// 创建锁定库存信息公共方法
|
/// </summary>
|
/// <param name="addWmsStockQuanLockList">锁定库存信息集合</param>
|
/// <param name="item">波次单明细对象</param>
|
/// <param name="currentIssueQuantity">下发数</param>
|
/// <param name="stockQuanItem">库存视图对象</param>
|
private static StockQuanLockOutput CommonCreateWmsStockQuanLock(WmsOrderSortDetails item, decimal occQuantity, v_wms_stock_quan_for_use stockQuanItem)
|
{
|
|
return new StockQuanLockOutput()
|
{
|
ContainerCode = stockQuanItem.ContainerCode,
|
ContainerName = stockQuanItem.ContainerName,
|
MaterialCode = stockQuanItem.MaterialCode,
|
MaterialName = stockQuanItem.MaterialName,
|
Quantity = occQuantity,
|
Batch = stockQuanItem.Batch,
|
SupplierBatch = stockQuanItem.SupplierBatch,
|
SNCode = stockQuanItem.SNCode,
|
ErpCode = stockQuanItem.ErpCode,
|
AreaCode = stockQuanItem.AreaCode,
|
AreaName = stockQuanItem.AreaName,
|
PlaceCode = stockQuanItem.PlaceCode,
|
PlaceName = stockQuanItem.PlaceName,
|
SortNo = item.SortNo,
|
SortNoLineNumber = item.LineNumber,
|
RelationNoLineNumber = item.RelationNoLineNumber,
|
RelationNo = item.RelationNo,
|
|
|
};
|
}
|
|
/// <summary>
|
/// 创建分拣信息公共方法
|
/// </summary>
|
/// <param name="addWmsContainerSortList">分拣信息集合</param>
|
/// <param name="item">波次单明细对象</param>
|
/// <param name="movementOrder">移动单对象</param>
|
/// <param name="sortMovementDetails">移动单明细对象</param>
|
/// <param name="occQuantity">下发数</param>
|
/// <param name="stockQuanLock">锁定库存对象</param>
|
private static WmsContainerSort CommonCreateWmsContainerSort(WmsOrderSortDetails item, WmsOrderMovement movementOrder,
|
WmsOrderMovementDetails sortMovementDetails, decimal occQuantity, StockQuanLockOutput stockQuanLock)
|
{
|
return new WmsContainerSort()
|
{
|
StockQuanLockId = 0,
|
ContainerCode = stockQuanLock.ContainerCode,
|
MaterialCode = stockQuanLock.MaterialCode,
|
MaterialName = stockQuanLock.MaterialName,
|
Quantity = occQuantity,
|
ErpCode = stockQuanLock.ErpCode,
|
RelationNo = item.RelationNo,
|
RelationNoLineNumber = item.RelationNoLineNumber,
|
RelationDetailsId = sortMovementDetails.Id,
|
Batch = stockQuanLock.Batch,
|
SupplierBatch = stockQuanLock.SupplierBatch,
|
SNCode = stockQuanLock.SNCode,
|
SortDetailsId = item.Id,
|
SortNo = item.SortNo,
|
SortNoLineNumber = item.LineNumber,
|
SortStatus = OrderStatusEnum.新建,
|
SortStatusName = OrderStatusEnum.新建.GetDescription(),
|
RelationOrderType = movementOrder.OrderType,
|
RelationOrderTypeName = movementOrder.OrderType.GetDescription()
|
};
|
}
|
|
#endregion
|
|
|
|
|
}
|