using Admin.NET.Core.Service; using Admin.NET.Application.Entity; using Microsoft.AspNetCore.Http; using System.Data; using System.Web; using System.Text; using static SKIT.FlurlHttpClient.Wechat.Api.Models.ChannelsECLeagueHeadSupplierOrderGetResponse.Types.CommssionOrder.Types; using System.Linq.Expressions; using Furion.LinqBuilder; using System.Linq; using AngleSharp.Dom; using Admin.NET.Application.CommonHelper; namespace Admin.NET.Application; /// /// 下架单分配服务 /// [ApiDescriptionSettings(ApplicationConst.WmsTaskGroupName, Order = 100)] public class WmsDispenseService : IDynamicApiController, ITransient { /// /// 是否正在执行分配方法 /// private static bool isRuning_Fun_Dispense = false; /// /// 是否正在执行修改分配方法 /// private static bool isRuning_Fun_UpdateDispense = false; private static SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1); private static SemaphoreSlim semaphoreSlimForUpdate = new SemaphoreSlim(1, 1); 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 _wmsStockQuanLockRep; private readonly SqlSugarRepository _wmsOrderSortRep; private readonly SqlSugarRepository _wmsOrderMovementDetailsRep; private readonly SqlSugarRepository _wmsOrderMovementRep; private readonly SqlSugarRepository _wmsTaskRep; private readonly SqlSugarRepository _wmsRecordPredetermineDispenseRep; private readonly SqlSugarRepository _wmsRecordPredDispHistoryRep; private readonly SqlSugarRepository _v_wmsOrderMovementDetailsRep; public WmsDispenseService( SqlSugarRepository wmsRecordPredDispHistoryRep, SqlSugarRepository v_wmsOrderMovementDetailsRep, SqlSugarRepository wmsContainerSortRep, SqlSugarRepository v_wms_stock_quanRep, SqlSugarRepository v_wms_stock_quan_for_useRep, //SqlSugarRepository wmsStockQuanLockRep, SqlSugarRepository wmsOrderSortDetailsRep, SqlSugarRepository wmsOrderSortRep, SqlSugarRepository wmsOrderMovementDetailsRep, SqlSugarRepository wmsOrderMovementRep, SqlSugarRepository wmsTaskRep, SqlSugarRepository wmsRecordPredetermineDispenseRep) { _wmsRecordPredDispHistoryRep = wmsRecordPredDispHistoryRep; _v_wmsOrderMovementDetailsRep = v_wmsOrderMovementDetailsRep; _wmsContainerSortRep = wmsContainerSortRep; _v_wms_stock_quanRep = v_wms_stock_quanRep; _v_wms_stock_quan_for_useRep = v_wms_stock_quan_for_useRep; //_wmsStockQuanLockRep = wmsStockQuanLockRep; _wmsOrderSortDetailsRep = wmsOrderSortDetailsRep; _wmsOrderSortRep = wmsOrderSortRep; _wmsOrderMovementDetailsRep = wmsOrderMovementDetailsRep; _wmsOrderMovementRep = wmsOrderMovementRep; _wmsTaskRep = wmsTaskRep; _wmsRecordPredetermineDispenseRep = wmsRecordPredetermineDispenseRep; } /// /// 自动分配 /// /// /// [HttpPost] [ApiDescriptionSettings(Name = "Dispense")] [Description("WmsDispense/Dispense")] public async Task Dispense(List input) { try { if (isRuning_Fun_Dispense) { throw Oops.Oh("程序正忙,请稍后再试"); } await semaphoreSlim.WaitAsync(); isRuning_Fun_Dispense = true; return await DispenseFaction(input); } catch (Exception ex) { throw Oops.Oh(ex.Message); } finally { semaphoreSlim.Release(); isRuning_Fun_Dispense = false; } } /// /// 取消分配 /// /// /// [HttpPost] [ApiDescriptionSettings(Name = "CancelDispenseForRecord")] [Description("WmsDispense/CancelDispenseForRecord")] public async Task CancelDispenseForRecord(CancelDispenseInput input) { try { if (isRuning_Fun_Dispense) { throw Oops.Oh("程序正忙,请稍后再试"); } await semaphoreSlim.WaitAsync(); isRuning_Fun_Dispense = true; return await CancelDispenseForRecordFaction(input); } catch (Exception ex) { throw Oops.Oh(ex.Message); } finally { semaphoreSlim.Release(); isRuning_Fun_Dispense = false; } } /// /// 修改分配 /// /// /// [HttpPost] [ApiDescriptionSettings(Name = "UpdateDispense")] [Description("WmsDispense/UpdateDispense")] public async Task UpdateDispense(UpdateDispenseInput input) { throw Oops.Oh("此方法已废弃"); /* try { if (isRuning_Fun_UpdateDispense) { throw Oops.Oh("程序正忙,请稍后再试"); } await semaphoreSlimForUpdate.WaitAsync(); isRuning_Fun_UpdateDispense = true; return await UpdateDispenseFaction(input); } catch (Exception ex) { throw Oops.Oh(ex.Message); } finally { semaphoreSlimForUpdate.Release(); isRuning_Fun_UpdateDispense = false; } //*/ } #region 私有方法 /// /// 自动分配/指定库存分配 /// /// /// private async Task DispenseFaction(List input) { /** * 获取分配物料库存信息 * 获取当前分配锁定库存信息 * 校验可用库存-历史分配锁定库存 是否满足分配 * 新增分配锁定库存 * 修改下架单明细分配数 * 新增事务 * 新增操作日期 */ if (input.First().Flag != 1 && input.First().Flag != 2) { throw Oops.Oh("分配标记不正确"); } bool isHandFlag = false; if (input.First().Flag == 2) { isHandFlag = true; //明细不能为空 foreach (var item in input) { if (item.dispenseDetailsForHand?.Count <= 0) { throw Oops.Oh("下架单" + item.MovementNo + "行号" + item.LineNumber + "物料编号" + item.MaterialCode + $"分配的明细不能为空"); } item.SendQuantity = item.dispenseDetailsForHand.Sum(x => x.DispenseQuantity); } } //var addWmsStockQuanLockList = new List();//新增的锁定库存信息 //var updateWmsStockQuanLockList = new List();//修改的锁定库存信息 var updateWmsOrderMoveDetailsList = new List();//修改的移动单明细 var updateWmsOrderMoveList = new List();//修改的移动单 var addWmsRecordPredetermineDispenseList = new List(); List moveNoList = input.Select(s => s.MovementNo).ToList(); var allWmsOrderSortList = await _wmsOrderMovementRep.GetListAsync(u => moveNoList.Contains(u.OrderNo)); if (allWmsOrderSortList?.Count <= 0) { throw Oops.Oh("下架单不存在"); } //获取下架单所有明细 var allOrderSortDetails = await _wmsOrderMovementDetailsRep.GetListAsync(u => moveNoList.Contains(u.MovementNo)); if (allOrderSortDetails?.Count <= 0) { throw Oops.Oh("下架单明细不存在"); } // 查找物料库存 List materialCodeList = input.Select(x => x.MaterialCode).Distinct().ToList(); //获取本次操作的下架单和下架单明细 var currentMovementDetailsKeyList = input.Select(x => x.MovementNo + x.LineNumber).ToList(); var currentMovementDetailsList = await _wmsOrderMovementDetailsRep.GetListAsync(x => currentMovementDetailsKeyList.Contains(x.MovementNo + x.LineNumber)); var v_currentMovementDetailsList = await _v_wmsOrderMovementDetailsRep.GetListAsync(x => currentMovementDetailsKeyList.Contains(x.MovementNo + x.LineNumber)); var queryOrderIds = currentMovementDetailsList.Select(x => x.MovementId).Distinct().ToList(); var currentOrderMovementList = await _wmsOrderMovementRep.GetListAsync(x => queryOrderIds.Contains(x.Id)); ////根据物料编码获取物料跟踪码的可用库存信息-已排除掉分配、下发锁定库存 var allStockQuanUseList = await _v_wms_stock_quan_for_useRep.GetListAsync(x => materialCodeList.Contains(x.MaterialCode)); //获取下架单明细的物料预配锁定库存 //var allDetailsPredetermineList = await _wmsStockQuanLockRep.GetListAsync(x => x.LockType == LockTypeEnum.预配 && x.IsDelete == false); var allDetailsPredetermineList = await LockStroreHelper.GetPreListAsync(materialCodeList, _wmsOrderMovementDetailsRep); foreach (var inputItem in input) { WmsOrderMovement movementOrder = currentOrderMovementList.FirstOrDefault(p => p.OrderNo == inputItem.MovementNo); if (movementOrder == null) { throw Oops.Oh("本次分配的下架单不存在"); } //TODO 校验下架单状态 var movementDetails = currentMovementDetailsList.FirstOrDefault(x => x.MovementNo == inputItem.MovementNo && x.LineNumber == inputItem.LineNumber); if (movementDetails == null) { throw Oops.Oh("本次分配的下架单明细不存在"); } var v_movementDetails = v_currentMovementDetailsList.FirstOrDefault(x => x.MovementNo == inputItem.MovementNo && x.LineNumber == inputItem.LineNumber); if (v_movementDetails == null) { throw Oops.Oh("本次分配的下架单明细不存在"); } if (!isHandFlag) { #region 处理自动分配 decimal historyDispenseQuantity = v_movementDetails.DispenseQuantity;//下架单的历史分配数量 decimal allQuantity = movementDetails.Quantity - historyDispenseQuantity;//下架单明细剩余可分配数量 decimal occQuantity = inputItem.SendQuantity;//当前实际分配数量 if ((occQuantity) <= 0) { throw Oops.Oh("下架单" + movementDetails.MovementNo + "行号" + movementDetails.LineNumber + "物料编号" + movementDetails.MaterialCode + $"(本次分配数{occQuantity})不能小于等于0"); } if (allQuantity <= 0) { throw Oops.Oh("下架单" + movementDetails.MovementNo + "行号" + movementDetails.LineNumber + "物料编号" + movementDetails.MaterialCode + $"已全部分配"); } if (occQuantity > allQuantity) { throw Oops.Oh("下架单" + movementDetails.MovementNo + "行号" + movementDetails.LineNumber + "物料编号" + movementDetails.MaterialCode + $"本次分配数量{occQuantity}大于剩余可分配数{allQuantity}"); } // 根据物料找出库存列表中数量大于0的库存 List querySNList = new List(); if (isHandFlag) { querySNList = inputItem.dispenseDetailsForHand.Select(x => x.SNCode).ToList(); } var findStockQuanList = allStockQuanUseList.Where(x => x.AvailableQty > 0 && x.MaterialCode == movementDetails.MaterialCode) .WhereIF(querySNList.Count > 0, x => querySNList.Contains(x.SNCode)) // 支持 手动分配的情况 【Editby shaocx,2024-07-12】 .OrderBy(x => x.ContainerCode).ThenBy(x => x.PlaceCode).ThenBy(x => x.RecordInsertTime).ToList(); if (findStockQuanList?.Count == 0) { throw Oops.Oh("下架单" + movementDetails.MovementNo + "行号" + movementDetails.LineNumber + "物料编号" + movementDetails.MaterialCode + $"没有找到可分配的库存"); } //寻找库存原则:按照先入先出原则 RecordInsertTime 排升序 //物料跟踪码可用库存-没有排除预配锁定库存 var sumQty = findStockQuanList.Sum(x => x.AvailableQty); var itemMovementDetailsKey = inputItem.MovementNo + inputItem.LineNumber; //获取当前下架单明细的物料预配锁定库存 var currentDetailsPredetermine = allDetailsPredetermineList.Where(x => itemMovementDetailsKey.Equals(x.MovementNo + x.LineNumber)).ToList(); //获取其他下架单明细的物料预配锁定库存 var otherDetailsPredetermineList = allDetailsPredetermineList.Where(x => !itemMovementDetailsKey.Equals(x.MovementNo + x.LineNumber)).ToList(); //其他单据预配锁定库存 var orherPredetermineQty = otherDetailsPredetermineList.Sum(u => u.Quantity); orherPredetermineQty = orherPredetermineQty < 0 ? 0 : orherPredetermineQty; //当前可分配库存要排除掉其他单据预配锁定库存 if (sumQty < occQuantity - orherPredetermineQty) { throw Oops.Oh("物料编号" + movementDetails.MaterialCode + $"库存数量不足,分配数量:{occQuantity},库存可用数量:{sumQty}"); } //处理后未分配的数量,多个同一个物料多个库存数据的情况,循环处理下发 decimal currentDispenseQuantity = occQuantity; decimal changeQty = 0;//本次变更数量 List snCodeList = new List();//下发的库存SNCode,存在使用多个情况 foreach (var stockQuanItem in findStockQuanList) { if (currentDispenseQuantity > 0) { //当前物料波次锁定库存 var currentStockQuanLockQty = findStockQuanList.Where(x => x.SNCode == stockQuanItem.SNCode) .Sum(x => x.Quantity); //当前物料可用库存 var currentUsedQuantity = stockQuanItem.AvailableQty; //循环库存信息 处理下发数 if (currentUsedQuantity >= currentDispenseQuantity)//分配数量小于等于库存 { changeQty = currentDispenseQuantity; currentDispenseQuantity = 0;//全部分配,不在处理 } else { changeQty = currentUsedQuantity; //当前分配数未能完全分配,累计分配数 currentDispenseQuantity = currentDispenseQuantity - currentUsedQuantity; } //累计使用的库存跟踪码 snCodeList.Add(stockQuanItem.SNCode); //创建分配记录 var addWmsRecordPredetermineDispense = CommonCreateWmsRecordPredetermineDispense(movementDetails, changeQty, stockQuanItem); addWmsRecordPredetermineDispenseList.Add(addWmsRecordPredetermineDispense); if (currentDispenseQuantity == 0) { break; } } else { break; } } if (currentDispenseQuantity > 0) { throw Oops.Oh("下架单" + movementDetails.MovementNo + "行号" + movementDetails.LineNumber + "物料编号" + movementDetails.MaterialCode + $"(本次需求分配数{occQuantity}),还剩余{currentDispenseQuantity}未分配"); } v_movementDetails.DispenseQuantity += occQuantity;//累计分配数 /* //处理当前下架单明细实际使用的预配锁定库存 //根据分配数扣减预配锁定库存的锁定使用数 decimal currentPredetermineLockQty = occQuantity; if (currentDetailsPredetermine?.Count == 0) { ////创建锁定库存 var addWmsStockQuanLock = LockStroreHelper.CommonCreateWmsStockQuanLockForPredetermine(movementDetails, occQuantity); addWmsStockQuanLockList.Add(addWmsStockQuanLock); } else { currentDetailsPredetermine.First().Quantity += occQuantity; updateWmsStockQuanLockList.Add(currentDetailsPredetermine.First()); } //*/ //预配数等于分配数 if (movementDetails.PredetermineQuantity < v_movementDetails.DispenseQuantity) { movementDetails.PredetermineQuantity = v_movementDetails.DispenseQuantity; } movementOrder.IsDispense = true;//标记为已分配 updateWmsOrderMoveList.Add(movementOrder);//更新下架单 updateWmsOrderMoveDetailsList.Add(movementDetails);//更新下架单明细 #endregion } else { #region 处理指定库存分配 decimal historyDispenseQuantity = v_movementDetails.DispenseQuantity;//下架单的历史分配数量 decimal allQuantity = movementDetails.Quantity - historyDispenseQuantity;//下架单明细剩余可分配数量 decimal occQuantity = inputItem.SendQuantity;//当前实际分配数量 if ((occQuantity) <= 0) { throw Oops.Oh("下架单" + movementDetails.MovementNo + "行号" + movementDetails.LineNumber + "物料编号" + movementDetails.MaterialCode + $"(本次分配数{occQuantity})不能小于等于0"); } if (allQuantity <= 0) { throw Oops.Oh("下架单" + movementDetails.MovementNo + "行号" + movementDetails.LineNumber + "物料编号" + movementDetails.MaterialCode + $"已全部分配"); } if (occQuantity > allQuantity) { throw Oops.Oh("下架单" + movementDetails.MovementNo + "行号" + movementDetails.LineNumber + "物料编号" + movementDetails.MaterialCode + $"本次分配数量{occQuantity}大于剩余可分配数{allQuantity}"); } // 根据物料找出库存列表中数量大于0的库存 List querySNList = new List(); if (isHandFlag) { querySNList = inputItem.dispenseDetailsForHand.Select(x => x.SNCode).ToList(); } var findStockQuanList = allStockQuanUseList.Where(x => x.AvailableQty > 0 && x.MaterialCode == movementDetails.MaterialCode) .WhereIF(querySNList.Count > 0, x => querySNList.Contains(x.SNCode)) // 支持 手动分配的情况 【Editby shaocx,2024-07-12】 .OrderBy(x => x.ContainerCode).ThenBy(x => x.PlaceCode).ThenBy(x => x.RecordInsertTime).ToList(); if (findStockQuanList?.Count == 0) { throw Oops.Oh("下架单" + movementDetails.MovementNo + "行号" + movementDetails.LineNumber + "物料编号" + movementDetails.MaterialCode + $"没有找到可分配的库存"); } if (findStockQuanList.Count != inputItem.dispenseDetailsForHand.Count) { throw Oops.Oh("下架单" + movementDetails.MovementNo + "行号" + movementDetails.LineNumber + "物料编号" + movementDetails.MaterialCode + $",寻找的库存条数{findStockQuanList.Count}不等于预期处理条数{inputItem.dispenseDetailsForHand.Count}"); } //物料跟踪码可用库存-没有排除预配锁定库存 var sumQty = findStockQuanList.Sum(x => x.AvailableQty); var itemMovementDetailsKey = inputItem.MovementNo + inputItem.LineNumber; //获取当前下架单明细的物料预配锁定库存 var currentDetailsPredetermine = allDetailsPredetermineList.Where(x => itemMovementDetailsKey.Equals(x.MovementNo + x.LineNumber)).ToList(); //获取其他下架单明细的物料预配锁定库存 var otherDetailsPredetermineList = allDetailsPredetermineList.Where(x => !itemMovementDetailsKey.Equals(x.MovementNo + x.LineNumber)).ToList(); //其他单据预配锁定库存 var orherPredetermineQty = otherDetailsPredetermineList.Sum(u => u.Quantity); orherPredetermineQty = orherPredetermineQty < 0 ? 0 : orherPredetermineQty; //当前可分配库存要排除掉其他单据预配锁定库存 if (sumQty < occQuantity - orherPredetermineQty) { throw Oops.Oh("物料编号" + movementDetails.MaterialCode + $"库存数量不足,分配数量:{occQuantity},库存可用数量:{sumQty}"); } foreach (var stockQuanItem in findStockQuanList) {//这里的每次循环就是处理 每个指定库存的数据 var singleDispenseDetailsItem = inputItem.dispenseDetailsForHand.Where(x => x.SNCode == stockQuanItem.SNCode).FirstOrDefault(); decimal currentDispenseQuantity = singleDispenseDetailsItem.DispenseQuantity; //当前物料波次锁定库存 var currentStockQuanLockQty = findStockQuanList.Where(x => x.SNCode == stockQuanItem.SNCode) .Sum(x => x.Quantity); //当前物料可用库存 var currentUsedQuantity = stockQuanItem.AvailableQty; //循环库存信息 处理下发数 if (currentUsedQuantity >= currentDispenseQuantity)//分配数量小于等于库存 {//正常 } else { throw Oops.Oh("物料编号" + movementDetails.MaterialCode + $"计算库存数量不足,分配数量:{currentDispenseQuantity},库存可用数量:{currentUsedQuantity}"); } //创建分配记录 var addWmsRecordPredetermineDispense = CommonCreateWmsRecordPredetermineDispense(movementDetails, currentDispenseQuantity, stockQuanItem); addWmsRecordPredetermineDispenseList.Add(addWmsRecordPredetermineDispense); } v_movementDetails.DispenseQuantity += occQuantity;//累计分配数 /* //处理当前下架单明细实际使用的预配锁定库存 //根据分配数扣减预配锁定库存的锁定使用数 decimal currentPredetermineLockQty = occQuantity; if (currentDetailsPredetermine?.Count == 0) { ////创建锁定库存 var addWmsStockQuanLock = LockStroreHelper.CommonCreateWmsStockQuanLockForPredetermine(movementDetails, occQuantity); addWmsStockQuanLockList.Add(addWmsStockQuanLock); } else { currentDetailsPredetermine.First().Quantity += occQuantity; updateWmsStockQuanLockList.Add(currentDetailsPredetermine.First()); } //*/ //预配数等于分配数 if (movementDetails.PredetermineQuantity < v_movementDetails.DispenseQuantity) { movementDetails.PredetermineQuantity = v_movementDetails.DispenseQuantity; } movementOrder.IsDispense = true;//标记为已分配 updateWmsOrderMoveList.Add(movementOrder);//更新下架单 updateWmsOrderMoveDetailsList.Add(movementDetails);//更新下架单明细 #endregion } } //处理分配历史表 List addWmsRecordPredDispHistoryList = addWmsRecordPredetermineDispenseList.Adapt>(); foreach (var item in addWmsRecordPredDispHistoryList) { item.Remarks = "分配操作"; } var _tenant = _wmsOrderMovementDetailsRep.AsTenant(); try { #region 事务内执行操作 //await _wmsStockQuanLockRep.InsertRangeAsync(addWmsStockQuanLockList); //await _wmsStockQuanLockRep.UpdateRangeAsync(updateWmsStockQuanLockList); await _wmsOrderMovementDetailsRep.UpdateRangeAsync(updateWmsOrderMoveDetailsList); await _wmsOrderMovementRep.UpdateRangeAsync(updateWmsOrderMoveList); await _wmsRecordPredetermineDispenseRep.InsertRangeAsync(addWmsRecordPredetermineDispenseList); await _wmsRecordPredDispHistoryRep.InsertRangeAsync(addWmsRecordPredDispHistoryList); #endregion await _tenant.CommitTranAsync(); } catch (Exception ex) { await _tenant.RollbackTranAsync(); throw; } return 1; } /// /// 取消分配 /// /// /// private async Task CancelDispenseForRecordFaction(CancelDispenseInput input) { //var addWmsStockQuanLockList = new List();//新增的锁定库存信息 List allStockQuanUseList = new List();//选择分配的可用库存信息 //获取当前修改的分配记录 List updateRecordList = await _wmsRecordPredetermineDispenseRep.GetListAsync(u => u.MovementNo == input.MovementNo); WmsRecordPredetermineDispense updateRecord = updateRecordList.FirstOrDefault(u => u.Id == input.Id); if (updateRecord == null) { throw Oops.Oh($"下架单{updateRecord.MovementNo}不存在"); } #region 处理旧的的分配 //取消要修改的分配记录 updateRecord.PDRecordStatus = PDRecordStatusEnum.已取消; updateRecord.PDRecordStatusName = PDRecordStatusEnum.已取消.GetDescription(); updateRecord.Remarks = "取消分配"; #endregion WmsOrderMovementDetails movementDetails = await _wmsOrderMovementDetailsRep.AsQueryable().FirstAsync(p => p.MovementNo == updateRecord.MovementNo && p.LineNumber == updateRecord.MovementLineNumber); if (movementDetails == null) { throw Oops.Oh($"下架单{updateRecord.MovementNo}的行号{updateRecord.MovementLineNumber}不存在"); } V_WmsOrderMovementDetails v_movementDetails = await _v_wmsOrderMovementDetailsRep.AsQueryable().FirstAsync(p => p.MovementNo == updateRecord.MovementNo && p.LineNumber == updateRecord.MovementLineNumber); if (v_movementDetails == null) { throw Oops.Oh($"下架单{updateRecord.MovementNo}的行号{updateRecord.MovementLineNumber}不存在"); } v_movementDetails.DispenseQuantity -= updateRecord.Quantity; if (v_movementDetails.DispenseQuantity < 0) { throw Oops.Oh($"下架单{updateRecord.MovementNo}的行号{updateRecord.MovementLineNumber}的已分配数量{v_movementDetails.DispenseQuantity}不能小于要取消的分配数量{updateRecord.Quantity}"); } WmsOrderMovement movementOrder = await _wmsOrderMovementRep.AsQueryable().FirstAsync(p => p.OrderNo == updateRecord.MovementNo); if (movementOrder == null) { throw Oops.Oh($"下架单{updateRecord.MovementNo}不存在"); } //判断如果没有任何分配记录了,就更新状态 if (updateRecordList.Where(x => x.PDRecordStatus == PDRecordStatusEnum.已分配).Count() > 0) { } else { movementOrder.IsDispense = false;//标记为未分配 } //处理分配历史表 WmsRecordPredDispHistory addWmsRecordPredDispHistory = updateRecord.Adapt(); addWmsRecordPredDispHistory.Id = Yitter.IdGenerator.YitIdHelper.NextId(); addWmsRecordPredDispHistory.Quantity = -addWmsRecordPredDispHistory.Quantity; addWmsRecordPredDispHistory.Remarks = "取消分配"; var _tenant = _wmsOrderMovementRep.AsTenant(); try { #region 事务内执行操作 await _wmsOrderMovementRep.UpdateAsync(movementOrder); await _wmsOrderMovementDetailsRep.UpdateAsync(movementDetails); //await _wmsStockQuanLockRep.InsertRangeAsync(addWmsStockQuanLockList); await _wmsRecordPredetermineDispenseRep.UpdateAsync(updateRecord); await _wmsRecordPredDispHistoryRep.InsertAsync(addWmsRecordPredDispHistory); #endregion await _tenant.CommitTranAsync(); } catch (Exception ex) { await _tenant.RollbackTranAsync(); throw; } return 1; } /// /// 修改分配 验证分配操作入参 /// /// /// /// /// private async Task ValidateInput(UpdateDispenseInput input, List allStockQuanUseList) { //一次只能编辑一个分配记录 if (string.IsNullOrWhiteSpace(input.SNCode)) { throw Oops.Oh("分配的库存跟踪码不能为空"); } if (input.Id <= 0) { throw Oops.Oh("要修改的分配记录不能为空"); } //获取当前修改的分配记录 WmsRecordPredetermineDispense updateRecord = await _wmsRecordPredetermineDispenseRep.GetFirstAsync(u => u.Id == input.Id); if (updateRecord == null) { throw Oops.Oh("分配记录不存在"); } //TODO 暂不支持修改分配数 //if (input.SendQuantity <= 0) //{ // throw Oops.Oh("修改后的分配数量必须大于0"); //} // 查找物料库存 List snCodeList = ParamHelper.GetStringParamToList(input.SNCode); if (updateRecord.PDRecordStatus != PDRecordStatusEnum.已分配) { throw Oops.Oh($"分配记录状态是{updateRecord.PDRecordStatus.GetDescription()}"); } //根据物料编码获取物料跟踪码的可用库存信息) var stockQuanUseList = await _v_wms_stock_quan_for_useRep.GetListAsync(x => snCodeList.Contains(x.SNCode)); if (stockQuanUseList?.Count <= 0) { throw Oops.Oh("修改分配的库存跟踪码未获取到可用库存"); } allStockQuanUseList.AddRange(stockQuanUseList); return updateRecord; } ///// ///// 移动单分配 ///// ///// ///// //private async Task LockMovePredetermine(List input) //{ // var addWmsStockQuanLockList = new List();//新增的锁定库存信息 // var updateWmsOrderMoveDetailsList = new List();//修改的移动单明细 // var updateWmsOrderMoveList = new List();//修改的移动单 // //TODO 添加事务记录 // List sortNoList = input.Select(x => x.movementNo).ToList(); // // 获取所有波次单 // //Expression> predicate = u => sortNoList.Contains(u.SortNo); // var allWmsOrderSortList = await _wmsOrderMovementRep.GetListAsync(u => sortNoList.Contains(u.OrderNo)); // if (allWmsOrderSortList?.Count <= 0) // { // throw Oops.Oh("下架单不存在"); // } // //获取波次单所有明细 // var allOrderSortDetails = await _wmsOrderMovementDetailsRep.GetListAsync(u => sortNoList.Contains(u.MovementNo)); // if (allOrderSortDetails?.Count <= 0) // { // throw Oops.Oh("下架单明细不存在"); // } // //本次下发波次单明细 // var currentOrderSortDetails = allOrderSortDetails.Where(u => input.Select(x => x.Id).Contains(u.Id)).ToList(); // if (currentOrderSortDetails?.Count <= 0) // { // throw Oops.Oh("本次操作的下架单明细不存在"); // } // // 查找物料库存 // List materialCodeList = currentOrderSortDetails.Select(x => x.MaterialCode).Distinct().ToList(); // //获取可操作下发的库存信息-(未排除锁定库存)库存状态已上架、质检状态合格、非虚拟库位 // var queryParam = ExpressionHelper.GetStockQuanForIssueOutTask(); // queryParam.And(x => materialCodeList.Contains(x.MaterialCode)); // var allStockQuanList = await _v_wms_stock_quanRep.GetListAsync(queryParam); // if (allStockQuanList?.Count <= 0) // { // throw Oops.Oh("所有物料都没有库存"); // } // //获取所有物料锁定库存 // var allStockQuanLockList = await _wmsStockQuanLockRep.GetListAsync(x => materialCodeList.Contains(x.MaterialCode)); // //波次下发物料锁定库存 // var allStockQuanLockForSortList = allStockQuanLockList.Where(x => x.LockType == LockTypeEnum.分配 && x.IsDelete == false).ToList(); // //TODO 分配分配锁定库存 // var movementDetailsKeyList = currentOrderSortDetails.Select(x => x.RelationNo + x.RelationNoLineNumber).ToList(); // var movementDetailsList = await _wmsOrderMovementDetailsRep.GetListAsync(x => movementDetailsKeyList.Contains(x.MovementNo + x.LineNumber)); // var queryOrderIds = movementDetailsList.Select(x => x.MovementId).Distinct().ToList(); // var orderMovementList = await _wmsOrderMovementRep.GetListAsync(x => queryOrderIds.Contains(x.Id)); // foreach (var inputItem in input) // { // var item = currentOrderSortDetails.FirstOrDefault(x => x.Id == inputItem.Id); // WmsOrderMovement movementOrder = orderMovementList.FirstOrDefault(p => p.OrderNo == item.MovementNo); // 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("本次分配的下架单明细不存在"); // } // #region 处理下发 // decimal issueQuantity = item.PredetermineQuantity ;//下架单的历史分配数量 // decimal allQuantity = item.Quantity - issueQuantity;//当前未下发数量 // decimal occQuantity = inputItem.SendQuantity;//当前实际下发数量 // if (allQuantity <= 0) // { // throw Oops.Oh("下架单" + item.MovementNo + "行号" + item.LineNumber + "物料编号" + item.MaterialCode + $"已全部分配"); // } // if (occQuantity > allQuantity) // { // throw Oops.Oh("下架单" + item.MovementNo + "行号" + item.LineNumber + "物料编号" + item.MaterialCode + $"本次分配需求数量{occQuantity}大于未分配数{allQuantity}"); // } // // 根据物料找出库存列表中数量大于0的库存 // var findStockQuanList = allStockQuanList.Where(x => (x.Quantity) > 0 && x.MaterialCode == item.MaterialCode) // .OrderBy(x => x.ContainerCode).ThenBy(x => x.PlaceCode).ThenBy(x => x.RecordInsertTime).ToList(); // //寻找库存原则:按照先入先出原则 RecordInsertTime 排升序 // //同一个物料尽量从同一个库位取 // if (findStockQuanList.Count() <= 0) // { // //缺料 // throw Oops.Oh("物料编号" + item.MaterialCode + $"库存数量不足,分配需求数量:{occQuantity},库存可用数量:0"); // } // var sumQty = findStockQuanList.Where(o => o.MaterialCode == item.MaterialCode).Sum(x => x.Quantity); // //波次下发 实际锁定库存 // var lockQtyForSort = allStockQuanLockForSortList.Where(o => o.MaterialCode == item.MaterialCode).Sum(x => x.Quantity); // //TODO 去掉分配分配锁定库存 // var usedQty = (sumQty - lockQtyForSort) <= 0 ? 0 : (sumQty - lockQtyForSort); // if (usedQty < occQuantity) // { // throw Oops.Oh("物料编号" + item.MaterialCode + $"库存数量不足,分配需求数量:{occQuantity},库存可用数量:{usedQty}"); // } // //处理后未下发的数量,多个同一个物料多个库存数据的情况,循环处理下发 // decimal currentDispenseQuantity = occQuantity; // List snCodeList = new List();//下发的库存SNCode,存在使用多个情况 // foreach (var stockQuanItem in findStockQuanList) // { // //库位属性是 封存和禁出的,不能操作下架 // if (stockQuanItem.PlaceStatus == PlaceStatusEnum.封存) // { // throw Oops.Oh($"库位编号{stockQuanItem.PlaceCode}库位属性是{PlaceStatusEnum.封存.GetDescription()}"); // } // if (stockQuanItem.PlaceStatus == PlaceStatusEnum.禁出) // { // throw Oops.Oh($"库位编号{stockQuanItem.PlaceCode}库位属性是{PlaceStatusEnum.禁出.GetDescription()}"); // } // if (currentDispenseQuantity > 0) // { // //当前物料波次锁定库存 // var currentStockQuanLockQty = allStockQuanLockForSortList.Where(x => x.SNCode == stockQuanItem.SNCode).Sum(x => x.Quantity); // //当前物料可用库存 // var currentUsedQuantity = (stockQuanItem.Quantity - currentStockQuanLockQty); // currentUsedQuantity = currentUsedQuantity <= 0 ? 0 : currentUsedQuantity; // //循环库存信息 处理下发数 // if (currentUsedQuantity >= currentDispenseQuantity)//下发数量小于等于库存 // { // currentDispenseQuantity = 0;//全部下发,不在处理 // } // else // { // //当前下发数未能完全下发,累计下发数 // currentDispenseQuantity = currentDispenseQuantity - currentUsedQuantity; // } // //累计使用的库存跟踪码 // snCodeList.Add(stockQuanItem.SNCode); // //创建锁定库存 // var addWmsStockQuanLock = CommonCreateWmsStockQuanLock(item, occQuantity, stockQuanItem); // //创建分拣信息 // var addWmsContainerSort = CommonCreateWmsContainerSort(item, movementOrder, sortMovementDetails, occQuantity, addWmsStockQuanLock); // addWmsStockQuanLockList.Add(addWmsStockQuanLock); // if (currentDispenseQuantity == 0) // { // break; // } // } // else // { // break; // } // } // if (currentDispenseQuantity > 0) // { // throw Oops.Oh("物料编号" + item.MaterialCode + $"库存数量不足"); // } // //更新波次单的已下发数量 // item.SNCode = string.Join(",", snCodeList.Distinct().ToList()); // decimal lastIssueQuantity = issueQuantity + occQuantity;//下架单单的历史分配数 // item.PredetermineQuantity = lastIssueQuantity;//累计分配数 // //更新波次单明细的状态 // OrderHelper.UpdateOrderMovementDetailsStatus(item); // updateWmsOrderSortDetailsList.Add(item); // #endregion // } // foreach (var item in allWmsOrderSortList) // { // var updateOrderSortDetails = allOrderSortDetails.Where(w => w.MovementId == item.Id).ToList(); // ////计算 波次单单的状态 // OrderHelper.UpdatOrderSortStatus(updateOrderSortDetails, item); // updateWmsOrderSortList.Add(item); // } // if (addWmsContainerSortList?.Count <= 0) // { // throw Oops.Oh("没有可下发的物料,库存全部不足"); // } // var _tenant = _wmsContainerSortRep.AsTenant(); // try // { // #region 事务内执行操作 // await _wmsStockQuanLockRep.InsertRangeAsync(addWmsStockQuanLockList); // await _wmsOrderSortRep.UpdateRangeAsync(updateWmsOrderSortList); // await _wmsOrderSortDetailsRep.UpdateRangeAsync(updateWmsOrderSortDetailsList); // #endregion // await _tenant.CommitTranAsync(); // } // catch (Exception ex) // { // await _tenant.RollbackTranAsync(); // throw; // } // return 1; //} /// /// 创建记录分配信息-新建分配用 /// /// /// /// /// private static WmsRecordPredetermineDispense CommonCreateWmsRecordPredetermineDispense(WmsOrderMovementDetails item, decimal changeQty, v_wms_stock_quan_for_use stockQuanItem) { return new WmsRecordPredetermineDispense() { Id = Yitter.IdGenerator.YitIdHelper.NextId(), PDRecordType = PDRecordTypeEnum.分配, PDRecordTypeName = PDRecordTypeEnum.分配.GetDescription(), PDRecordStatus = PDRecordStatusEnum.已分配, PDRecordStatusName = PDRecordStatusEnum.已分配.GetDescription(), MaterialCode = item.MaterialCode, MaterialName = item.MaterialName, Quantity = changeQty, MovementLineNumber = item.LineNumber,//下架单行号 MovementNo = item.MovementNo,//下架单号 SNCode = stockQuanItem.SNCode,//用于分配的库存跟踪码 Batch = stockQuanItem.Batch,//批次号 SupplierBatch = stockQuanItem.SupplierBatch,//供应商批次号 AreaCode = stockQuanItem.AreaCode,//库区编码 AreaName = stockQuanItem.AreaName,//库区名称 PlaceCode = stockQuanItem.PlaceCode, PlaceName = stockQuanItem.PlaceName, ContainerCode = stockQuanItem.ContainerCode, ContainerName = stockQuanItem.ContainerName, Remarks = "", }; } #endregion }