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
|
{
|
/// <summary>
|
/// 公共操作服务
|
/// </summary>
|
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;
|
}
|
|
|
/// <summary>
|
/// 查找空库位
|
/// </summary>
|
/// <param name="materialModel"></param>
|
/// <param name="materialNo"></param>
|
/// <param name="requiredNum"></param>
|
/// <returns></returns>
|
/// <exception cref="UserFriendlyException"></exception>
|
public async Task<Dictionary<WmsPlaceDto, int>> 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<WmsPlace>, List<WmsPlaceDto>>(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<WmsPlaceDto, int>();
|
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;
|
}
|
|
/// <summary>
|
/// 查找库存
|
/// </summary>
|
/// <param name="materialModel"></param>
|
/// <param name="requiredNum"></param>
|
/// <param name="materialNo"></param>
|
/// <returns></returns>
|
public async Task<Dictionary<WmsMaterialStockDto, int>> 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<WmsMaterialStock>, List<WmsMaterialStockDto>>(availableStocklist);
|
|
// 3. 检查总库存是否足够
|
int totalAvailable = availableStocks.Sum(x => x.StockNumber);
|
if (totalAvailable < requiredNum)
|
{
|
throw new UserFriendlyException(
|
$"库存不足!需求: {requiredNum}, 可用: {totalAvailable}, 缺: {requiredNum - totalAvailable}");
|
}
|
|
// 4. 计算各库位出库数量
|
var allocation = new Dictionary<WmsMaterialStockDto, int>(); // <库位号, 出库数>
|
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<Expression<Func<WmsMaterialStock, bool>>> DynamicGetQueryParams(GetWmsMaterialStockInput input)
|
{
|
//动态构造查询条件
|
var whereConditions = WhereConditionsExtensions.GetWhereConditions<WmsMaterialStock, GetWmsMaterialStockInput>(input);
|
if (!whereConditions.IsSuccess)
|
{
|
throw new Exception("动态构造查询条件失败:" + whereConditions.ErrMsg);
|
}
|
|
//也可再次自定义构建查询条件
|
Expression<Func<WmsMaterialStock, bool>> extendExpression = a => a.IsDeleted == false;
|
// 使用 System.Linq.PredicateBuilder 的 And
|
var pres = (System.Linq.Expressions.Expression<Func<WmsMaterialStock, bool>>)(whereConditions.data);
|
whereConditions.data = System.Linq.PredicateBuilder.And(pres, extendExpression);
|
|
return whereConditions;
|
}
|
}
|
}
|