using CMS.Plugin.HIAWms.Application.Contracts.Dtos.CommonDto; using CMS.Plugin.HIAWms.Application.Contracts.Dtos.WmsMaterialStocks; using CMS.Plugin.HIAWms.Application.Contracts.Dtos.WmsPlaces; using CMS.Plugin.HIAWms.Application.Contracts.Services; using CMS.Plugin.HIAWms.Domain.WmsMaterials; using CMS.Plugin.HIAWms.Domain.WmsMaterialStocks; using CMS.Plugin.HIAWms.Domain.WmsPlaces; using CmsQueryExtensions.Extension; using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Text; using System.Threading.Tasks; using Volo.Abp; namespace CMS.Plugin.HIAWms.Application.Implements { /// /// 公共操作服务 /// public class WmsCommonAppService : CMSPluginAppService,IWmsCommonAppService { private readonly IWmsMaterialRepository _wmsMaterialRepository; private readonly IWmsPlaceRepository _wmsPlaceRepository; private readonly IWmsMaterialStockRepository _wmsMaterialStockRepository; public WmsCommonAppService(IWmsMaterialRepository wmsMaterialRepository, IWmsPlaceRepository wmsPlaceRepository, IWmsMaterialStockRepository wmsMaterialStockRepository ) { _wmsMaterialRepository = wmsMaterialRepository; _wmsPlaceRepository = wmsPlaceRepository; _wmsMaterialStockRepository = wmsMaterialStockRepository; } /// /// 查找空库位 /// /// /// /// /// /// public async Task> FindAvailablePlacesAsync(string materialModel,int requiredNum, string materialNo="" ) { var whereConditions = DynamicGetQueryParams(new GetWmsMaterialStockInput { StorageTypeNo = Domain.Shared.Enums.PlaceTypeEnum.YUANLIAOKUWEI }); // 1. 获取所有库存和库位信息 var stockList = await _wmsMaterialStockRepository.GetListAsync(whereConditions); var placeList = await _wmsPlaceRepository.GetListAsync(new WmsPlace { StorageTypeNo = Domain.Shared.Enums.PlaceTypeEnum.YUANLIAOKUWEI}); var allPlaceList = ObjectMapper.Map, List>(placeList.Where(x => !x.IsDisabled).ToList()); // 2. 查找相同物料型号和编号的库存(按库存量降序) var sameModelStocks = stockList .Where(x => x.MaterialModel == materialModel) .WhereIf(!string.IsNullOrEmpty(materialNo), x => x.MaterialNo == materialNo) .OrderByDescending(x => x.StockNumber) .ToList(); var availablePlaces = new Dictionary(); int remainingNum = requiredNum; // 3. 优先检查已有库存的库位是否能存放(相同 MaterialNo) foreach (var stock in sameModelStocks) { if (remainingNum <= 0) break; // 数量已分配完 var placeInfo = allPlaceList.FirstOrDefault(x => x.PlaceNo == stock.PlaceNo); if (placeInfo == null) continue; int availableSpace = placeInfo.MaxStockNumber - stock.StockNumber; if (availableSpace <= 0) continue; int allocatedNum = Math.Min(availableSpace, remainingNum); availablePlaces.Add(placeInfo, allocatedNum); remainingNum -= allocatedNum; } // 4. 如果仍有剩余,查找空库位 if (remainingNum > 0) { var usedPlaceNos = stockList.Select(x => x.PlaceNo).Distinct().ToList(); var emptyPlaces = allPlaceList .Where(x => !usedPlaceNos.Contains(x.PlaceNo)) .ToList(); foreach (var place in emptyPlaces) { if (remainingNum <= 0) break; int allocatedNum = Math.Min(place.MaxStockNumber, remainingNum); availablePlaces.Add(place, allocatedNum); remainingNum -= allocatedNum; } } // 5. 如果仍有剩余,说明库位不足 if (remainingNum > 0) { throw new UserFriendlyException($"库位不足,还差 {remainingNum} 个无法存放!"); } return availablePlaces; } /// /// 查找库存 /// /// /// /// /// public async Task> FindStockAsync(string materialModel, int requiredNum, string materialNo = "") { // 1. 获取所有库存(排除锁定库存) var whereConditions = DynamicGetQueryParams(new GetWmsMaterialStockInput()); var stockList = (await _wmsMaterialStockRepository.GetListAsync(whereConditions)) .Where(x => x.IsLock == Domain.Shared.Enums.YesNoEnum.N) .Where(x=>x.StorageTypeNo == Domain.Shared.Enums.PlaceTypeEnum.YUANLIAOKUWEI) .ToList(); // 2. 筛选匹配物料 var availableStocklist = stockList .Where(x => x.MaterialModel == materialModel) .WhereIf(!string.IsNullOrEmpty(materialNo), x => x.MaterialNo == materialNo) .OrderBy(x => x.StockNumber) // 优先从库存少的库位出 .ToList(); var availableStocks = ObjectMapper.Map, List>(availableStocklist); // 3. 检查总库存是否足够 int totalAvailable = availableStocks.Sum(x => x.StockNumber); if (totalAvailable < requiredNum) { throw new UserFriendlyException( $"库存不足!需求: {requiredNum}, 可用: {totalAvailable}, 缺: {requiredNum - totalAvailable}"); } // 4. 计算各库位出库数量 var allocation = new Dictionary(); // <库位号, 出库数> int remaining = requiredNum; foreach (var stock in availableStocks) { if (remaining <= 0) break; int deductAmount = Math.Min(stock.StockNumber, remaining); allocation.Add(stock, deductAmount); remaining -= deductAmount; } return allocation; } private FunReturnResultModel>> DynamicGetQueryParams(GetWmsMaterialStockInput input) { //动态构造查询条件 var whereConditions = WhereConditionsExtensions.GetWhereConditions(input); if (!whereConditions.IsSuccess) { throw new Exception("动态构造查询条件失败:" + whereConditions.ErrMsg); } //也可再次自定义构建查询条件 Expression> extendExpression = a => a.IsDeleted == false; // 使用 System.Linq.PredicateBuilder 的 And var pres = (System.Linq.Expressions.Expression>)(whereConditions.data); whereConditions.data = System.Linq.PredicateBuilder.And(pres, extendExpression); return whereConditions; } } }