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 System;
using System.Collections.Generic;
using System.Linq;
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="" )
{
// 1. 获取所有库存和库位信息
var stockList = await _wmsMaterialStockRepository.GetListAsync(new WmsMaterialStock());
var allPlaceList = ObjectMapper.Map, List>(await _wmsPlaceRepository.GetListAsync(new WmsPlace()));
// 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 stockList = (await _wmsMaterialStockRepository.GetListAsync(new WmsMaterialStock()))
.Where(x => x.IsLock == Domain.Shared.Enums.YesNoEnum.N)
.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;
}
}
}