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;
///
/// 容器分拣信息服务
///
[ApiDescriptionSettings(ApplicationConst.WmsTaskGroupName, Order = 100)]
public class WmsIssueService : IDynamicApiController, ITransient
{
///
/// 是否正在执行下发方法
///
private static bool isRuning_Fun_Issue = false;
private static SemaphoreSlim semaphoreSlimIssue = new SemaphoreSlim(1, 1);
private readonly UserManager _userManager;
private readonly SqlSugarRepository _wmsContainerSortRep;
private readonly SqlSugarRepository _v_wms_stock_quanRep;
private readonly SqlSugarRepository _v_wms_stock_quan_for_useRep;
private readonly SqlSugarRepository _wmsOrderSortDetailsRep;
private readonly SqlSugarRepository _wmsOrderSortRep;
private readonly SqlSugarRepository _wmsOrderMovementDetailsRep;
private readonly SqlSugarRepository _wmsOrderMovementRep;
private readonly SqlSugarRepository _wmsTaskRep;
private readonly SqlSugarRepository _wmsRecordPredetermineDispenseRep;
private readonly SqlSugarRepository _wmsStockQuanRep;
private readonly SqlSugarRepository _wmsRecordTransRep;
private readonly SqlSugarRepository _WmsBaseBusinessTypeRep;
private readonly SqlSugarRepository _wmsPlaceRep;
private readonly SqlSugarRepository _wmsLogActionRep;
private readonly SqlSugarRepository _wmsConfigUnshelveStrategyRep;
private readonly SqlSugarRepository _wmsConfigUnshelveStrategyChooseRep;
private readonly SqlSugarRepository _wmsConfigUnshelveStrategyRangeRep;
private readonly SqlSugarRepository _v_wms_config_unshelve_strategy_rangeRep;
private readonly SqlSugarRepository _v_empty_placeRep;
private readonly SqlSugarRepository _wmsBasePlaceRep;
public WmsIssueService(
UserManager userManager,
SqlSugarRepository wmsStockQuanRep,
SqlSugarRepository wmsRecordPredetermineDispenseRep,
SqlSugarRepository wmsContainerSortRep,
SqlSugarRepository v_wms_stock_quanRep,
SqlSugarRepository v_wms_stock_quan_for_useRep,
SqlSugarRepository wmsOrderSortDetailsRep,
SqlSugarRepository wmsOrderSortRep,
SqlSugarRepository wmsOrderMovementDetailsRep,
SqlSugarRepository wmsOrderMovementRep,
SqlSugarRepository wmsTaskRep,
SqlSugarRepository wmsRecordTransRep,
SqlSugarRepository WmsBaseBusinessTypeRep,
SqlSugarRepository wmsPlaceRep,
SqlSugarRepository wmsLogActionRep,
SqlSugarRepository wmsConfigUnshelveStrategyRep,
SqlSugarRepository wmsConfigUnshelveStrategyChooseRep,
SqlSugarRepository wmsConfigUnshelveStrategyRangeRep,
SqlSugarRepository v_empty_placeRep,
SqlSugarRepository wmsBasePlaceRep,
SqlSugarRepository 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;
}
///
/// 下发
///
///
///
[HttpPost]
[ApiDescriptionSettings(Name = "Issue")]
[Description("WmsIssue/Issue")]
[UnitOfWork]
public async Task Issue(List 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 私有方法
///
/// 波次单下发
///
///
///
private async Task TaskIssue(List input)
{
/*
* 1. 获取所有要下发的波次单、明细
* 2.校验波次单是否存在、单据状态、波次单明细剩余可下发数(考虑一个波次单明细是否可以多次下发)
* 3.获取所有要下发的下架单、明细
* 4.校验下架单是否存在、单据状态
* 5.根据库位、批次、容器查询库存(查询库存视图:根据容器分组)
* 7.计算可用库存=实际库存-锁定库存, 校验库存是否足够下发。
* 8.库存根据容器 库位 入库时间排序(使用遵循先进先出原则)
* 目前库存没有 排属性的应用
* 9.循环库存,判断每个容器的物料是否足够下发,不够就循环下一个容器。
* 10.校验库存容器是否存在未完成的调度任务,存在就不能下发
*
* 11.更新波次单
*/
//下架策略
/**
*1.获取所有物料 生效的 下架策略
*2. 根据优先级循环处理策略
*3.先进先出(天):先按照入库时间(年-月-日)格式排序 升序
*4.先进先出(小时):先按照入库时间(年-月-日-时)格式排序 升序
*5.满托推荐:按照 容器号分组
5.1 物料在一个容器库存正好= 下发的库存的 先出
5.2 物料在一个容器库存> 下发的库存的先出
5.3 物料在一个容器库存< 下发的库存的先出
*6.半托推荐:按照 容器号分组 ,库存最少的先出
*
*
*
*/
//新增事务记录
List addWmsRecordTransList = new List();
//新增操作履历
List addWmsLogActionList = new List();
var addWmsTaskList = new List();//新增的下架任务
var addWmsContainerSortList = new List();//新增的分拣信息
var updateWmsOrderSortDetailsList = new List();//修改的波次单明细
var updateWmsOrderSortList = new List();//修改的波次单
//TODO 添加事务记录
List sortNoList = input.Select(x => x.SortNo).ToList();
if (sortNoList.Distinct().Count() > 1)
{
throw Oops.Oh("一次只能操作一个波次单下发");
}
// 获取所有波次单
//Expression> 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 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 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 allUnshelveStrategyRangeList = await ConfigUnshelveStrategyHelper.GetUseUnshelveStrategyRangeList(_v_wms_config_unshelve_strategy_rangeRep, materialCodeList);
//获取生效的下架策略选项
List 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() { 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>();//复制排序后的对象
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;
}
///
/// 根据策略排序
///
///
///
///
///
///
///
/// 原始的对象
///
private static List GetSortStockQuanListByStrategy(List allUnshelveStrategyRangeList, List allUnshelveStrategyChooseList,
WmsOrderSortDetails item, List db_all_wmsRecordPredetermineDispenseList, WmsOrderMovementDetails sortMovementDetails, decimal currentIssueQuantity,
List org_allMaterialStockQuanList)
{
//这里不才走变更库存信息,仅仅排序,所以必须是复制对象,而不是直接修改对象
List allMaterialStockQuanList = org_allMaterialStockQuanList.Adapt>();//复制对象
foreach (var item2 in allMaterialStockQuanList)
{
item2.HanlerQty = item2.Quantity;
}
//策略是否可用
bool isUseStrategy = false;
List findStockQuanList = new List();
List currentUnshelveStrategyRangeList = allUnshelveStrategyRangeList.Where(w => w.MaterialCode == item.MaterialCode).ToList();
List currentUnshelveStrategyChooseList = new List();
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 groupStockQuanList = new List();
//库存数使用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;
}
///
/// 默认的下架策略-先进先出
///
///
///
///
private static List UseDefaultOrderForIssue(List allMaterialStockQuanList, ref List findStockQuanList)
{
var lastResult = allMaterialStockQuanList.OrderBy(o => o.RecordInsertTime).ToList();
findStockQuanList.AddRange(lastResult);
return findStockQuanList;
}
#region 满托推荐=>先入先出(天)的下架策略
///
/// 满托推荐=>先入先出(天)的下架策略 (2)
///
///
///
///
///
private static List FullContiner_FIFOByDay_OrderForIssue(List allMaterialStockQuanList, ref List findStockQuanList, decimal currentIssueQuantity)
{
return Common_FullContiner_First_OrderForIssue(2, allMaterialStockQuanList, ref findStockQuanList, currentIssueQuantity);
}
///
/// 满托推荐的下架策略 (1)
///
///
///
///
///
private static List FullContiner_OrderForIssue(List allMaterialStockQuanList, ref List findStockQuanList, decimal currentIssueQuantity)
{
return Common_FullContiner_First_OrderForIssue(1, allMaterialStockQuanList, ref findStockQuanList, currentIssueQuantity);
}
///
/// 满托推荐的下架策略 (1)
/// 满托推荐=>先入先出(天)的下架策略 (2)
///
/// 1:满托推荐的下架策略 2:满托推荐=>先入先出(天)的下架策略
///
///
///
///
private static List Common_FullContiner_First_OrderForIssue(int flag, List allMaterialStockQuanList, ref List 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 半满推荐=>先入先出(天)的下架策略
///
/// 半满推荐=>先入先出(天)的下架策略 (2)
///
///
///
///
///
private static List HalfContiner_FIFOByDay_OrderForIssue(List allMaterialStockQuanList, ref List findStockQuanList, decimal currentIssueQuantity)
{
return Common_HalfContiner_First_OrderForIssue(2, allMaterialStockQuanList, ref findStockQuanList, currentIssueQuantity);
}
///
///半满推荐的下架策略 (1)
///
///
///
///
///
private static List HalfContiner_OrderForIssue(List allMaterialStockQuanList, ref List findStockQuanList, decimal currentIssueQuantity)
{
return Common_HalfContiner_First_OrderForIssue(1, allMaterialStockQuanList, ref findStockQuanList, currentIssueQuantity);
}
///
/// (1)半满推荐的下架策略
/// (2) 半满推荐=>先入先出(天)的下架策略
///
///
///
///
///
///
private static List Common_HalfContiner_First_OrderForIssue(int flag, List allMaterialStockQuanList, ref List 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 先入先出(天)的
///
/// 只有 先入先出(天)
///
///
///
///
///
private static List FIFOByDay_OrderForIssue(List allMaterialStockQuanList, ref List findStockQuanList, decimal currentIssueQuantity)
{
return Common_FIFOByDay_First_OrderForIssue(0, allMaterialStockQuanList, ref findStockQuanList, currentIssueQuantity);
}
///
/// 先入先出(天)=>其次满托
///
///
///
///
///
private static List FIFOByDay_FullContiner_OrderForIssue(List allMaterialStockQuanList, ref List findStockQuanList, decimal currentIssueQuantity)
{
return Common_FIFOByDay_First_OrderForIssue(1, allMaterialStockQuanList, ref findStockQuanList, currentIssueQuantity);
}
///
/// 先入先出(天)=>其次半托
///
///
///
///
///
private static List FIFOByDay_HalfContiner_OrderForIssue(List allMaterialStockQuanList, ref List findStockQuanList, decimal currentIssueQuantity)
{
return Common_FIFOByDay_First_OrderForIssue(2, allMaterialStockQuanList, ref findStockQuanList, currentIssueQuantity);
}
///
/// 公共-先入先出(天)的
///
/// 0:只有FIFODay 1:其次满托 2:其次半托
///
///
///
///
private static List Common_FIFOByDay_First_OrderForIssue(int flag, List allMaterialStockQuanList, ref List 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
///
/// 根据分拣明细获取下架策略选项
///
///
///
///
///
private static List GetUnshelveStrategyChooseListBySortDetails(List allUnshelveStrategyRangeList, List allUnshelveStrategyChooseList, WmsOrderSortDetails item)
{
List currentUnshelveStrategyRangeList = allUnshelveStrategyRangeList.Where(w => w.MaterialCode == item.MaterialCode).ToList();
List currentUnshelveStrategyChooseList = new List();
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 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);
}
///
/// 获取校验下发参数对象
///
///
///
///
///
///
///
private static List GetWmsOrderMovementDetailsForIssueInput(List input, List currentOrderSortDetails, List allWmsOrderSortList,
List orderMovementList, List movementDetailsList
)
{
List result = new List();
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();
res.SendQuantity = inputItem.SendQuantity;
result.Add(res);
}
return result;
}
///
/// 波次下發統一處理
///
///
///
///
///
///
///
///
private static void CommonDoForIssue(SqlSugarRepository _WmsBaseBusinessTypeRep, List addWmsTaskList, v_wms_stock_quan_for_use stockQuanItem,
WmsOrderSortDetails item, decimal changeQty,
WmsOrderMovement movementOrder, WmsOrderMovementDetails sortMovementDetails, List 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);
}
///
/// 公共創建調度任務
///
///
///
///
private static void CreateWmsTask(List addWmsTaskList, WmsTask addWmsTask, v_wms_stock_quan_for_use stockQuanItem)
{
//同一个容易创建一个 下架任务即可
if (!addWmsTaskList.Any(a => a.ContainerCode == stockQuanItem.ContainerCode))
{
addWmsTaskList.Add(addWmsTask);
}
}
///
/// 创建下架调度任务公共方法
///
///
///
///
///
///
private static WmsTask CommonCreateWmsTask(SqlSugarRepository _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
};
}
///
/// 创建锁定库存信息公共方法
///
/// 锁定库存信息集合
/// 波次单明细对象
/// 下发数
/// 库存视图对象
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,
};
}
///
/// 创建分拣信息公共方法
///
/// 分拣信息集合
/// 波次单明细对象
/// 移动单对象
/// 移动单明细对象
/// 下发数
/// 锁定库存对象
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
}