using Admin.NET.Application.CommonHelper; using Admin.NET.Application.Entity; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Admin.NET.Application; /// /// 锁定信息 /// public class LockInfo { /// /// 锁定状态 /// public LockStatusEnum LockStatus { get; set; } /// /// 锁定原因 /// public string? LockReason { get; set; } /// /// 锁定人 /// public string? LockUser { get; set; } /// /// 锁定时间 /// public DateTime? LockTime { get; set; } } public class StockQuanHelper { /// /// 变更库存状态 /// /// /// /// /// public static void UpdateStockStatus(WmsStockQuan item, StockStatusEnum newStockStatus, string remark = "", string RealName = "") { if (!string.IsNullOrWhiteSpace(remark)) { item.Remarks = remark; } //记录变更前状态 if (newStockStatus == StockStatusEnum.已冻结 || newStockStatus == StockStatusEnum.已报废 || newStockStatus == StockStatusEnum.已隔离) { item.OldStockStatus = item.StockStatus; item.OldStockStatusName = item.StockStatus.GetDescription(); } else { //清空历史库存状态 item.OldStockStatus = null; item.OldStockStatusName = ""; } //ly0806 - 冻结解冻 需要赋值: 解冻人 解冻时间 if (!string.IsNullOrWhiteSpace(RealName)) { item.OperUser = RealName; //操作人 item.OperTime = DateTime.Now; //解冻时间 item.OperReason = remark; //原因 } item.StockStatus = newStockStatus; item.StockStatusName = newStockStatus.GetDescription(); } /// /// 变更库存锁定状态 /// /// /// public static void UpdateStockLockStatus(WmsStockQuan item, LockInfo lockInfo) { item.LockStatus = lockInfo.LockStatus; item.LockReason = lockInfo.LockReason; item.LockUser = lockInfo.LockUser; if (!item.LockTime.HasValue) { item.LockTime = DateTime.Now; } item.LockTime = lockInfo.LockTime; } /// /// 变更库存质检状态 /// /// /// /// public static void UpdateStockQcStatus(WmsStockQuan item, StockQcStatusEnum newStockQcStatus, string remark = "") { if (!string.IsNullOrWhiteSpace(remark)) { item.Remarks = remark; } item.QCStatus = newStockQcStatus; item.QCStatusName = newStockQcStatus.GetDescription(); } /// /// 盘点关闭- 库存解冻 /// /// /// /// /// /// /// /// /// /// /// public static async Task BackStockStatusForPD(WmsBaseBusinessType wmsBaseBusinessType, SqlSugarRepository _wmsInventoryCheckOrderDetailsRep, SqlSugarRepository _wmsStockQuanRep, SqlSugarRepository _v_wms_stock_quanRep, SqlSugarRepository _wmsPlaceRep, WmsInventoryCheckOrder entity, List upadteWmsStockQuanList, List addWmsRecordTransList, UserManager _userManager) { //ly-0716 //获取盘点明细 List checkOrderDetailsList = await _wmsInventoryCheckOrderDetailsRep.AsQueryable().Where(u => u.OrderId == entity.Id && u.IsDelete == false).Select().ToListAsync(); //ly-0716 //update by liuwq 20240730 //获取盘点明细 if (checkOrderDetailsList?.Count <= 0) { return;//没有盘点单明细 直接返回 } //获取盘点单据明细的 所有容器 List checkContainerCodeList = checkOrderDetailsList.Select(s => s.ContainerCode).Distinct().ToList(); //获取盘点下架容器的所有库存 var checkOutStockQuanList = await _wmsStockQuanRep.GetListAsync(u => checkContainerCodeList.Contains(u.ContainerCode) && u.IsDelete == false && u.LockStatus == LockStatusEnum.已锁定); List snCodeList = checkOutStockQuanList.Select(s => s.SNCode).ToList(); var checkOutStockQuanViewList = await _v_wms_stock_quanRep.GetListAsync(u => snCodeList.Contains(u.SNCode)); //获取库位相信 var placeCodeList = checkOutStockQuanViewList.Select(s => s.PlaceCode).Distinct().ToList(); var allPlaeList = await _wmsPlaceRep.GetListAsync(u => placeCodeList.Contains(u.PlaceCode) && u.IsDelete == false); //事务记录其他入参 TransferOtherDetail transferOtherDetail = new TransferOtherDetail() { RelationNo = entity.OrderNo,//盘点单号 RelationNoLineNumber = string.Empty, Remarks = "", }; List materialCodeList = checkOutStockQuanList.Select(s => s.MaterialCode).Distinct().ToList(); foreach (var item in checkOutStockQuanList) { var ss = GetVMmsStockQuan(checkOutStockQuanViewList, item.SNCode); LockInfo freezeInfo = new LockInfo() { LockReason = wmsBaseBusinessType.BusinessTypeName, LockTime = DateTime.Now, LockUser = _userManager.RealName,//登录人的真实姓名 LockStatus = LockStatusEnum.未锁定,//解冻 }; StockQuanHelper.UpdateStockLockStatus(item, freezeInfo); upadteWmsStockQuanList.Add(item); //新增事务记录 //仅变更库存冻结状态 //获取源库存信息 var sourceStockView = StockQuanHelper.GetVMmsStockQuan(checkOutStockQuanViewList, item.SNCode); //获取目标库位信息 //目标库存不变 新增的库存也是源库存的库位 var toPlaceInfo = BaseInfoHelper.GetPlace(sourceStockView.PlaceCode, allPlaeList); addWmsRecordTransList.Add(LogRecordHelper.CreateWmsRecordTrans(wmsBaseBusinessType, sourceStockView, item, toPlaceInfo, transferOtherDetail)); } } /// /// 盘点单据完成、盘点调差- 整个容器库存解冻 /// /// /// /// /// /// /// /// /// /// /// public static async Task BackStockStatusForPD(WmsBaseBusinessType wmsBaseBusinessType, List checkOrderDetailsList, SqlSugarRepository _wmsStockQuanRep, SqlSugarRepository _v_wms_stock_quanRep, SqlSugarRepository _wmsPlaceRep, WmsInventoryCheckOrder entity, List upadteWmsStockQuanList, List addWmsRecordTransList, UserManager _userManager) { //ly-0716 //update by liuwq 20240730 //获取盘点明细 if (checkOrderDetailsList?.Count <= 0) { return;//没有盘点单明细 直接返回 } //获取盘点单据明细的 所有容器 List checkContainerCodeList = checkOrderDetailsList.Select(s => s.ContainerCode).Distinct().ToList(); //获取盘点下架容器的所有库存 var checkOutStockQuanList = await _wmsStockQuanRep.GetListAsync(u => checkContainerCodeList.Contains(u.ContainerCode) && u.IsDelete == false && u.LockStatus == LockStatusEnum.已锁定); List snCodeList = checkOutStockQuanList.Select(s => s.SNCode).ToList(); var checkOutStockQuanViewList = await _v_wms_stock_quanRep.GetListAsync(u => snCodeList.Contains(u.SNCode)); //获取库位相信 var placeCodeList = checkOutStockQuanViewList.Select(s => s.PlaceCode).Distinct().ToList(); var allPlaeList = await _wmsPlaceRep.GetListAsync(u => placeCodeList.Contains(u.PlaceCode) && u.IsDelete == false); //事务记录其他入参 TransferOtherDetail transferOtherDetail = new TransferOtherDetail() { RelationNo = entity.OrderNo,//盘点单号 RelationNoLineNumber = string.Empty, Remarks = "", }; List materialCodeList = checkOutStockQuanList.Select(s => s.MaterialCode).Distinct().ToList(); foreach (var item in checkOutStockQuanList) { LockInfo freezeInfo = new LockInfo() { LockReason = wmsBaseBusinessType.BusinessTypeName, LockTime = DateTime.Now, LockUser = _userManager.RealName,//登录人的真实姓名 LockStatus = LockStatusEnum.未锁定,//解冻 }; StockQuanHelper.UpdateStockLockStatus(item, freezeInfo); // 调差 操作的库存跟踪码,也会存在更新的库存列表 不用重复添加,调差操作会 解冻 if (!upadteWmsStockQuanList.Any(a => a.SNCode == item.SNCode)) { upadteWmsStockQuanList.Add(item); //新增事务记录 //仅变更库存冻结状态 //获取源库存信息 var sourceStockView = StockQuanHelper.GetVMmsStockQuan(checkOutStockQuanViewList, item.SNCode); //获取目标库位信息 //目标库存不变 新增的库存也是源库存的库位 var toPlaceInfo = BaseInfoHelper.GetPlace(sourceStockView.PlaceCode, allPlaeList); addWmsRecordTransList.Add(LogRecordHelper.CreateWmsRecordTrans(wmsBaseBusinessType, sourceStockView, item, toPlaceInfo, transferOtherDetail)); } } } /// /// 校验库存 /// /// 1、波次下发验证 /// /// /// public static async Task ValdiateStock(int flag, List handlerDetaiList, SqlSugarRepository _wmsOrderMovementDetailsRep, SqlSugarRepository _v_wms_stock_quan_for_useRep ) { var movementMatericalCodeList = handlerDetaiList.Select(x => x.MaterialCode).ToList(); //查询该物料的预配库存 var preList = await LockStroreHelper.GetPreListAsync(movementMatericalCodeList, _wmsOrderMovementDetailsRep); //查询该物料的可用库存 var stockUseList = await _v_wms_stock_quan_for_useRep.GetListAsync(x => movementMatericalCodeList.Contains(x.MaterialCode)); if (flag == 1) { ValdiateStockForGroup(flag, handlerDetaiList, stockUseList, preList); } } /// /// 校验 库存汇总库存 /// /// /// /// /// /// private static void ValdiateStockForGroup(int flag, List handlerDetaiList, List _v_wms_stock_quan_for_useList, List preList) { //模拟挨个扣减,哪个不足就报错 foreach (var item in handlerDetaiList) { if (item.SendQuantity <= 0) { throw Oops.Oh($"数量大于0"); } string materialCode = item.MaterialCode; decimal validateQty = item.SendQuantity; var queryKey = (item.MovementNo + item.LineNumber); //首先查询 预配自己的库存数量 var pre_self_list = preList.Where(x => x.MaterialCode == materialCode && (x.MovementNo + x.LineNumber) == queryKey).FirstOrDefault(); //其次查询 预配其他的库存数量 var pre_other_list = preList.Where(x => x.MaterialCode == materialCode && (x.MovementNo + x.LineNumber) != queryKey).FirstOrDefault(); var pre_self_num = pre_self_list != null ? pre_self_list.PredetermineQuantity : 0; var pre_other_num = pre_other_list != null ? pre_other_list.PredetermineQuantity : 0; if (pre_self_num >= validateQty) { //说明预配给自己的库存数超过了自己需要的库存数 //扣减掉内存中的数据 pre_self_list.PredetermineQuantity -= validateQty; } else {//说明预配给自己的数 少于 自己需要的数 //查询库存 var stockQuanForUseList = _v_wms_stock_quan_for_useList.Where(x => x.MaterialCode == materialCode).ToList(); //首先要模拟减去 其他的预配数 foreach (var stockQuanForUse in stockQuanForUseList) { if (pre_other_num <= 0) break; if (stockQuanForUse.Quantity == 0) break; if (stockQuanForUse.Quantity < 0) { throw Oops.Oh($"物料{materialCode}可用库存计算其他预配数错误,小于0,{stockQuanForUse.Quantity}"); }; if (stockQuanForUse.Quantity >= pre_other_num) { stockQuanForUse.Quantity -= pre_other_num; pre_other_num = 0; break; } else { pre_other_num -= stockQuanForUse.Quantity; stockQuanForUse.Quantity = 0; } } if (pre_other_num > 0) { throw Oops.Oh($"物料{materialCode}可用库存计算不足,其他预配库存还有{pre_other_num}未能分配出来"); }; //其次要模拟扣减自己需求数 foreach (var stockQuanForUse in stockQuanForUseList) { if (validateQty <= 0) break; // if (stockQuanForUse.Quantity == 0) break; if (stockQuanForUse.Quantity == 0) continue;// 如果第一个库位没了,循环处理下一个 if (stockQuanForUse.Quantity < 0) { throw Oops.Oh($"物料{materialCode}可用库存计算实际需求数错误,小于0,{stockQuanForUse.Quantity}"); }; if (stockQuanForUse.Quantity >= validateQty) { stockQuanForUse.Quantity -= validateQty; validateQty = 0; break; } else { validateQty -= stockQuanForUse.Quantity; stockQuanForUse.Quantity = 0; } } if (validateQty > 0) { throw Oops.Oh($"物料{materialCode}可用库存计算不足,本次总需求数{item.SendQuantity},缺少{validateQty}数量的可用库存"); }; } } } ///// ///// 校验 库存汇总库存 ///// ///// ///// ///// ///// ///// ///// ///// //public static async Task ValdiateStockForGroup(int flag, // SqlSugarRepository _v_wms_stock_quan_for_useRep, // WmsOrderMovementDetails wmsOrderMovementDetails, string materialCode, decimal validateQty, List preList) //{ // //首先查询 预配自己的库存数量 // var pre_self_num = preList.Where(x => x.MaterialCode == materialCode && (x.MovementNo + x.LineNumber) == (wmsOrderMovementDetails.MovementNo + wmsOrderMovementDetails.LineNumber)).ToList().Sum(x => x.PredetermineQuantity); // //其次查询 预配其他的库存数量 // var pre_other_num = preList.Where(x => x.MaterialCode == materialCode && (x.MovementNo + x.LineNumber) != (wmsOrderMovementDetails.MovementNo + wmsOrderMovementDetails.LineNumber)).ToList().Sum(x => x.PredetermineQuantity); // if (pre_self_num >= validateQty) // { // //说明预配给自己的库存数超过了自己需要的库存数,验证通过 // } // else // { // var needQty = validateQty - pre_self_num;//自己还需要除预配之外的数据 // //查询库存 // var stockQuanForUseList = await _v_wms_stock_quan_for_useRep.GetListAsync(x => x.MaterialCode == materialCode); // var num_AvailableQty_stockQuanForUse = stockQuanForUseList.Sum(x => x.AvailableQty); // var valQty = num_AvailableQty_stockQuanForUse - pre_other_num; // if (valQty <= 0) // { // throw Oops.Oh($"物料{materialCode}可用库存不足,可用库存{num_AvailableQty_stockQuanForUse},并且预配库存为{pre_other_num}"); // } // } //} ///// ///// 校验 可用库存是否满足(细节到具体库存) ///// ///// ///// ///// ///// ///// ///// ///// //public static async Task ValdiateStockForGroup33(int flag, // SqlSugarRepository _v_wms_stock_quan_for_useRep, // WmsOrderMovementDetails wmsOrderMovementDetails, string materialCode, decimal validateQty, List dispenseList) //{ // //首先查询 分配自己的库存数量 // var pre_self_num = dispenseList.Where(x => x.MaterialCode == materialCode && (x.MovementNo + x.MovementLineNumber) == (wmsOrderMovementDetails.MovementNo + wmsOrderMovementDetails.LineNumber)).ToList().Sum(x => x.Quantity); // if (pre_self_num >= validateQty) // { // //说明分配给自己的库存数超过了自己需要的库存数,验证通过 // } // else // { // //查询库存 // var stockQuanForUseList = await _v_wms_stock_quan_for_useRep.GetListAsync(x => x.MaterialCode == materialCode); // var num_AvailableQty_stockQuanForUse = stockQuanForUseList.Sum(x => x.AvailableQty); // var valQty = num_AvailableQty_stockQuanForUse + pre_self_num - validateQty; // if (valQty <= 0) // { // throw Oops.Oh($"物料{materialCode}可用库存不足,可用库存{num_AvailableQty_stockQuanForUse}加已分配自己的库存{pre_self_num}小于需求数量{validateQty}"); // } // } //} /// /// 校验库存物料容器类型-组盘、上架用 /// /// /// /// /// /// public static async Task CheckContainerType(SqlSugarRepository _wmsMaterialRep, SqlSugarRepository _wmsContainerPackagingRep, WmsBaseContainer updateWmsContainerItem, List wmsStocks) { //获取库存物料基础信息的容器类型 List materialCodeList = wmsStocks.Select(v => v.MaterialCode).Distinct().ToList(); var stockQuanMaterialList = await _wmsMaterialRep.GetFirstAsync(u => materialCodeList.Contains(u.MaterialCode) && u.IsDelete == false); //获取物料的容器类型关系 var materialContainerTypeList = await _wmsContainerPackagingRep.GetListAsync(u => materialCodeList.Contains(u.MaterialCode) && u.IsDelete == false); //根据容器号分组 汇总相同容器的库存数 var checkStocks = wmsStocks.GroupBy(g => new { g.ContainerCode, g.MaterialCode }).Select(s => new { ContainerCode = s.Key.ContainerCode, MaterialCode = s.Key.MaterialCode, TotalQuantity = s.Sum(x => x.Quantity) }).ToList(); foreach (var item in checkStocks) { //获取库存的物料信息 var currentStockQuanMaterial = stockQuanMaterialList.Where(x => x.MaterialCode == item.MaterialCode).FirstOrDefault(); if (currentStockQuanMaterial == null) { //物料基础信息不存在 throw Oops.Oh($"物料{item.MaterialCode}基础信息不存在!"); } //获取物料与容器的绑定关系 var currentMaterialContainerType = materialContainerTypeList.Where(x => x.MaterialCode == item.MaterialCode).FirstOrDefault(); if (currentMaterialContainerType != null) { if (currentMaterialContainerType.ContainerTypeId != updateWmsContainerItem.ContainerTypeId) { throw Oops.Oh($"物料{item.MaterialCode}配置的容器类型是{currentMaterialContainerType.ContainerTypeName},组盘的容器类型是{updateWmsContainerItem.ContainerTypeName},两者不一致!"); } if (item.TotalQuantity > currentMaterialContainerType.BoxQty) { throw Oops.Oh($"容器类型{currentMaterialContainerType.ContainerTypeName}设置的物料容器容量最大值是{currentMaterialContainerType.BoxQty},物料{item.MaterialCode}库存数量{item.TotalQuantity}已超限!"); } } } } /// /// 校验在途库区和在途容器是否存在 /// /// /// /// /// public static async Task VerifyZTPlaceAndContainerIsExist(SqlSugarRepository _repWmsPlace, SqlSugarRepository _repWmsContainer, string wmsAreaCode) { var ztPlace = await _repWmsPlace.GetFirstAsync(n => n.PlaceCode == ApplicationConst.DefaultZTPlaceCode_Pre + wmsAreaCode); if (ztPlace == null) throw Oops.Oh($"在途库位{ApplicationConst.DefaultZTPlaceCode_Pre + wmsAreaCode}不存在!"); if (ztPlace.PlaceType != PlaceTypeEnum.在途库位) { throw Oops.Oh($"库位{ztPlace.PlaceCode}的库位类型不是在途库位!"); } var ztContainer = await _repWmsContainer.GetFirstAsync(n => n.ContainerCode == ApplicationConst.DefaultZTContainerCode_Pre + wmsAreaCode); if (ztContainer == null) throw Oops.Oh($"在途库容器{ApplicationConst.DefaultZTContainerCode_Pre + wmsAreaCode}不存在!"); return new ZtPlaceAndContainerOutput { ZtPlace = ztPlace, ZtContainer = ztContainer }; } /// /// 获取在途库位 /// /// /// /// public static async Task> GetZTPlace(SqlSugarRepository _wmsPlaceRep, WmsBasePlace placeModel) { //查询当前库区的在途库位信息 var placeZtList = await _wmsPlaceRep.AsQueryable().Where(z => z.AreaId == placeModel.AreaId && z.PlaceType == PlaceTypeEnum.在途库位).ToListAsync(); if (placeZtList.Count == 0) throw Oops.Oh("当前库区无在途库位,请联系管理人员进行处理!"); if (placeZtList.Count > 1) throw Oops.Oh("当前库区存在多个在途库位,请联系管理人员进行处理!"); return placeZtList; } /// /// 根据跟踪码获取库存详情-查询库存视图表集合 /// /// /// public static v_wms_stock_quan GetVMmsStockQuan(List allStockQuanView, string snCode) { v_wms_stock_quan currentStockQuanView = allStockQuanView.FirstOrDefault(f => f.SNCode.Equals(snCode)); if (currentStockQuanView == null) { throw Oops.Oh($"跟踪码{snCode}没有获取到库存信息"); } return currentStockQuanView; } }