using Autofac.Core;
|
using AutoMapper;
|
using MailKit.Search;
|
using SqlSugar;
|
using WIDESEA_Common.LocationEnum;
|
using WIDESEA_Common.StockEnum;
|
using WIDESEA_Core.BaseRepository;
|
using WIDESEA_Core.BaseServices;
|
using WIDESEA_Core.Helper;
|
using WIDESEA_DTO.Stock;
|
using WIDESEA_IAllocateService;
|
using WIDESEA_IBasicService;
|
using WIDESEA_IOutboundService;
|
using WIDESEA_IRecordService;
|
using WIDESEA_IStockService;
|
using WIDESEA_Model.Models;
|
|
namespace WIDESEA_StockService
|
{
|
public partial class StockInfoService : ServiceBase<Dt_StockInfo, IRepository<Dt_StockInfo>>, IStockInfoService
|
{
|
private readonly IMapper _mapper;
|
|
private readonly IRecordService _recordService;
|
public IRepository<Dt_StockInfo> Repository => BaseDal;
|
private readonly IRepository<Dt_StockInfoDetail> _stockInfoDetailRepository;
|
private readonly IOutboundOrderService _outboundOrderService;
|
//private readonly IOutboundOrderDetailService _outboundOrderDetailService;
|
|
private readonly IRepository<Dt_AllocateOrder> _allocateRepository;
|
private readonly ILocationInfoService _locationInfoService;
|
public StockInfoService(IRepository<Dt_StockInfo> BaseDal, IMapper mapper, IRepository<Dt_StockInfoDetail> stockInfoDetailRepository, IRecordService recordService, ILocationInfoService locationInfoService, IOutboundOrderService outboundOrderService, IRepository<Dt_AllocateOrder> allocateRepository) : base(BaseDal)
|
{
|
_mapper = mapper;
|
_stockInfoDetailRepository = stockInfoDetailRepository;
|
_recordService = recordService;
|
_locationInfoService = locationInfoService;
|
_outboundOrderService = outboundOrderService;
|
_allocateRepository = allocateRepository;
|
//_outboundOrderDetailService = outboundOrderDetailService;
|
}
|
|
/// <summary>
|
/// 根据托盘号查询库存
|
/// </summary>
|
/// <param name="palletCode"></param>
|
/// <returns></returns>
|
public Dt_StockInfo? GetStockByPalletCode(string palletCode)
|
{
|
Dt_StockInfo stockInfo = BaseDal.QueryFirst(x => x.PalletCode == palletCode);
|
if (stockInfo != null)
|
{
|
stockInfo.Details = _stockInfoDetailRepository.QueryData(x => x.StockId == stockInfo.Id);
|
}
|
return stockInfo;
|
}
|
|
|
public void AddMaterielGroup(Dt_StockInfo stockInfo)
|
{
|
decimal beforeQuantity = 0;
|
List<Dt_StockInfoDetail> details = new List<Dt_StockInfoDetail>();
|
if (stockInfo.Id == 0)
|
{
|
if (stockInfo.Details != null && stockInfo.Details.Any())
|
{
|
BaseDal.Db.InsertNav(stockInfo).Include(x => x.Details).ExecuteCommand();
|
}
|
else
|
{
|
BaseDal.AddData(stockInfo);
|
}
|
details = stockInfo.Details;
|
}
|
else
|
{
|
beforeQuantity = stockInfo.Details.Where(x => x.Id != 0).Sum(x => x.StockQuantity);
|
|
for (int i = 0; i < stockInfo.Details.Count; i++)
|
{
|
if (stockInfo.Details[i].Id == 0)
|
{
|
details.Add(_stockInfoDetailRepository.Db.Insertable(stockInfo.Details[i]).ExecuteReturnEntity());
|
}
|
|
}
|
|
}
|
stockInfo.Details = details;
|
_recordService.StockQuantityChangeRecordService.AddStockChangeRecord(stockInfo, stockInfo.Details, beforeQuantity, stockInfo.Details.Sum(x => x.StockQuantity) + beforeQuantity, WIDESEA_Common.StockEnum.StockChangeType.MaterielGroup);
|
}
|
|
/// <summary>
|
///
|
/// </summary>
|
/// <param name="stockInfos"></param>
|
/// <param name="materielCode"></param>
|
/// <param name="needQuantity"></param>
|
/// <param name="residueQuantity"></param>
|
/// <returns></returns>
|
public (List<Dt_StockInfo>, Dictionary<int, decimal>) GetOutboundStocks(List<Dt_StockInfo> stockInfos, string materielCode, decimal needQuantity, out decimal residueQuantity)
|
{
|
List<Dt_StockInfo> outStocks = new List<Dt_StockInfo>();
|
Dictionary<int, decimal> stockAllocations = new Dictionary<int, decimal>(); // 记录每个库存明细的分配数量
|
|
// 按先进先出排序所有相关的库存明细
|
var sortedStockDetails = stockInfos
|
.SelectMany(x => x.Details)
|
.Where(x => x.MaterielCode == materielCode &&
|
x.StockQuantity > x.OutboundQuantity) // 有可用库存
|
.OrderBy(x => x.CreateDate) // 按日期排序,先进先出
|
.ThenBy(x => x.StockId) // 相同生产日期按库存ID排序
|
.ToList();
|
|
if (!sortedStockDetails.Any())
|
{
|
residueQuantity = needQuantity;
|
return (outStocks, stockAllocations);
|
}
|
|
// 计算总可用库存
|
var stockTotalQuantity = sortedStockDetails.Sum(x => x.StockQuantity - x.OutboundQuantity);
|
|
if (stockTotalQuantity < needQuantity)
|
{
|
residueQuantity = needQuantity - stockTotalQuantity;
|
|
}
|
else
|
{
|
residueQuantity = 0;
|
}
|
|
decimal remainingNeed = needQuantity;
|
|
// 按先进先出顺序分配库存
|
foreach (var detail in sortedStockDetails)
|
{
|
if (remainingNeed <= 0) break;
|
|
decimal availableQuantity = detail.StockQuantity - detail.OutboundQuantity;
|
if (availableQuantity <= 0) continue;
|
|
decimal allocateQuantity = Math.Min(availableQuantity, remainingNeed);
|
|
// 更新出库数量
|
detail.OutboundQuantity += allocateQuantity;
|
remainingNeed -= allocateQuantity;
|
|
// 记录分配数量
|
stockAllocations[detail.Id] = allocateQuantity;
|
|
// 如果这个库存还没添加到出库列表中,就添加
|
var stockInfo = stockInfos.First(x => x.Id == detail.StockId);
|
if (!outStocks.Contains(stockInfo))
|
{
|
outStocks.Add(stockInfo);
|
}
|
}
|
|
residueQuantity = remainingNeed;
|
return (outStocks, stockAllocations);
|
}
|
|
/// <summary>
|
/// 根据条码获取库存信息
|
/// </summary>
|
public async Task<Dt_StockInfoDetail> GetStockDetailByBarcode(string barcode, string materielCode)
|
{
|
return await Db.Queryable<Dt_StockInfoDetail>()
|
.Includes(x => x.StockInfo)
|
.Where(x => x.Barcode == barcode && x.MaterielCode == materielCode)
|
.FirstAsync();
|
}
|
|
/// <summary>
|
/// 获取物料的所有条码信息
|
/// </summary>
|
public async Task<List<Dt_StockInfoDetail>> GetMaterialBarcodes(string materielCode, string batchNo = null)
|
{
|
var query = Db.Queryable<Dt_StockInfoDetail>()
|
.Includes(x => x.StockInfo)
|
.Where(x => x.MaterielCode == materielCode && x.StockQuantity > x.OutboundQuantity);
|
|
if (!string.IsNullOrEmpty(batchNo))
|
{
|
query = query.Where(x => x.BatchNo == batchNo);
|
}
|
|
return await query.OrderBy(x => x.CreateDate).ToListAsync();
|
}
|
public List<Dt_StockInfo> GetStockInfos(string materielCode, string lotNo, string supplyCode, List<string> locationCodes)
|
{
|
var query = Db.Queryable<Dt_StockInfo>()
|
.Where(x => locationCodes.Contains(x.LocationCode) && x.StockStatus == StockStatusEmun.入库完成.ObjToInt()
|
// && x.StockStatus == (int)StockStatusEmun.正常)
|
).Includes(x => x.Details);
|
|
if (!string.IsNullOrEmpty(materielCode))
|
{
|
query = query.Where(x => x.Details.Any(d => d.MaterielCode == materielCode));
|
}
|
|
if (!string.IsNullOrEmpty(lotNo))
|
{
|
query = query.Where(x => x.Details.Any(d => d.BatchNo == lotNo));
|
}
|
|
if (!string.IsNullOrEmpty(supplyCode))
|
{
|
query = query.Where(x => x.Details.Any(d => d.SupplyCode == supplyCode));
|
}
|
|
return query.OrderBy(x => x.CreateDate).ToList();
|
//ISugarQueryable<Dt_LocationInfo> sugarQueryable = Db.Queryable<Dt_LocationInfo>().Where(x => locationCodes.Contains(x.LocationCode));
|
//ISugarQueryable<Dt_StockInfo> sugarQueryable1 = Db.Queryable<Dt_StockInfo>().Includes(x => x.Details).Where(x => x.Details.Any(v => v.MaterielCode == materielCode));
|
//return sugarQueryable.InnerJoin(sugarQueryable1, (a, b) => a.LocationCode == b.LocationCode).Select((a, b) => b).OrderBy(a => a.CreateDate).Includes(a => a.Details).ToList();
|
}
|
|
public List<Dt_StockInfo> GetUseableStocks(string materielCode, string batchNo, string supplyCode)
|
{
|
List<string> locationCodes = _locationInfoService.GetCanOutLocationCodes();
|
|
return GetStockInfos(materielCode, batchNo, supplyCode, locationCodes);
|
}
|
|
public Dt_StockInfo GetPalletStockInfo(int locationType)
|
{
|
|
return Db.Queryable<Dt_StockInfo>().Where(x => x.StockStatus == StockStatusEmun.入库完成.ObjToInt() && SqlFunc.Subqueryable<Dt_LocationInfo>().Where(v => v.LocationCode == x.LocationCode && v.LocationType == locationType && v.LocationStatus == LocationStatusEnum.Pallet.ObjToInt() && (v.EnableStatus == EnableStatusEnum.OnlyOut.ObjToInt() || EnableStatusEnum.Normal.ObjToInt() == v.EnableStatus)).Any()).OrderBy(x => x.ModifyDate).First();
|
}
|
|
public List<Dt_StockInfo> GetStockInfosByPalletCodes(List<string> palletCodes)
|
{
|
return Db.Queryable<Dt_StockInfo>().Where(x => palletCodes.Contains(x.PalletCode)).Includes(x => x.Details).OrderBy(x => x.CreateDate).ToList();
|
}
|
// 添加获取单个托盘库存的方法(如果不存在)
|
public Dt_StockInfo GetStockInfoByPalletCode(string palletCode)
|
{
|
return Db.Queryable<Dt_StockInfo>()
|
.Where(x => x.PalletCode == palletCode)
|
.Includes(x => x.Details)
|
.First();
|
}
|
|
/// <summary>
|
///
|
/// </summary>
|
/// <param name="orderId"></param>
|
/// <param name="materielCode"></param>
|
/// <returns></returns>
|
//public List<StockSelectViewDTO> GetStockSelectViews(int orderId, string materielCode)
|
//{
|
// try
|
// {
|
// Dt_OutboundOrder outboundOrder = _outboundOrderService.Repository.QueryFirst(x => x.Id == orderId);
|
// if (outboundOrder == null)
|
// {
|
// throw new Exception($"未找到出库单信息");
|
// }
|
|
// List<string> locationCodes = _locationInfoService.GetCanOutLocationCodes();
|
|
// return BaseDal.QueryTabs<Dt_StockInfo, Dt_StockInfoDetail, StockSelectViewDTO>((a, b) => a.Id == b.StockId, (a, b) => new StockSelectViewDTO
|
// {
|
// LocationCode = a.LocationCode,
|
// MaterielCode = b.MaterielCode,
|
// MaterielName = b.MaterielName,
|
// Barcode=b.Barcode,
|
// PalletCode = a.PalletCode,
|
// UseableQuantity = b.StockQuantity - b.OutboundQuantity
|
// }, a => locationCodes.Contains(a.LocationCode), b => b.StockQuantity > b.OutboundQuantity && b.MaterielCode == materielCode, x => true).GroupBy(x => x.PalletCode).Select(x => new StockSelectViewDTO
|
// {
|
// LocationCode = x.FirstOrDefault()?.LocationCode ?? "",
|
// MaterielCode = x.FirstOrDefault()?.MaterielCode ?? "",
|
// MaterielName = x.FirstOrDefault()?.MaterielName ?? "",
|
// Barcode=x.FirstOrDefault()?.Barcode??"",
|
// PalletCode = x.Key,
|
// UseableQuantity = x.Sum(x => x.UseableQuantity)
|
// }).ToList();
|
// }
|
// catch (Exception ex)
|
// {
|
// return null;
|
// }
|
|
//}
|
|
public List<StockSelectViewDTO> GetAllocateSelectViewDTOs(int orderId, int orderdetailid, string materielCode)
|
{
|
//var dballocate = _allocateRepository.Db.Queryable<Dt_AllocateOrder>().Includes(x=>x.Details).Where(x=>x.Details.Any(o=>o.OrderId==orderId)) .First();
|
//if (dballocate == null)
|
//{
|
// throw new Exception($"未找到单据编号为{dballocate.OrderNo}的单信息");
|
|
//}
|
|
var outboundOrder = SqlSugarHelper.DbWMS.Queryable<Dt_OutboundOrder>().Includes(x=>x.Details) .Where(x => x.Details.Any(o=>o.OrderId==orderId)).First();
|
if (outboundOrder == null)
|
{
|
throw new Exception($"未找到单据编号为{outboundOrder.UpperOrderNo}的出库单信息");
|
}
|
return GetStockSelectViews(outboundOrder.Id,orderdetailid, materielCode);
|
}
|
|
public List<StockSelectViewDTO> GetStockSelectViews(int orderId,int orderdetailid, string materielCode)
|
{
|
Dt_OutboundOrder outboundOrder = _outboundOrderService.Repository.QueryFirst(x => x.Id == orderId);
|
if (outboundOrder == null)
|
{
|
throw new Exception($"未找到ID为{orderId}的出库单信息");
|
}
|
|
// 获取出库单明细,用于确定需要的批次和供应商
|
var orderDetails = SqlSugarHelper.DbWMS.Queryable<Dt_OutboundOrderDetail>().Where(x =>x.Id==orderdetailid && x.OrderId == orderId && x.MaterielCode == materielCode).ToList();
|
|
if (!orderDetails.Any())
|
{
|
throw new Exception($"出库单{orderId}中未找到物料{materielCode}的明细信息");
|
}
|
|
List<string> locationCodes = _locationInfoService.GetCanOutLocationCodes();
|
var result = new List<StockSelectViewDTO>();
|
|
// 为每个出库单明细查询对应的库存
|
foreach (var orderDetail in orderDetails)
|
{
|
// 构建查询条件
|
var stockQuery = Db.Queryable<Dt_StockInfo>()
|
.Where(x => locationCodes.Contains(x.LocationCode))
|
.Where(x => x.StockStatus == (int)StockStatusEmun.入库完成 || x.StockStatus == (int)StockStatusEmun.过期)
|
.Includes(x => x.Details);
|
|
// 执行查询并按先进先出排序
|
var Qstocks = stockQuery.Where(x => x.Details.Any(d => d.MaterielCode == materielCode && d.StockQuantity > d.OutboundQuantity));
|
|
if (!string.IsNullOrEmpty(orderDetail.BatchNo))
|
{
|
|
Qstocks = stockQuery.Where(x => x.Details.Any(d => d.BatchNo == orderDetail.BatchNo));
|
|
}
|
if (!string.IsNullOrEmpty(orderDetail.SupplyCode))
|
{
|
|
Qstocks = stockQuery.Where(x => x.Details.Any(d => d.SupplyCode == orderDetail.SupplyCode));
|
|
}
|
|
var stocks = Qstocks.OrderBy(x => x.CreateDate).ToList();
|
|
foreach (var stock in stocks)
|
{
|
var relevantDetails = stock.Details
|
.Where(d => d.MaterielCode == materielCode &&
|
string.IsNullOrEmpty(orderDetail.BatchNo) ? true : d.BatchNo == orderDetail.BatchNo &&
|
string.IsNullOrEmpty(orderDetail.SupplyCode) ? true : d.SupplyCode == orderDetail.SupplyCode &&
|
d.StockQuantity > d.OutboundQuantity)
|
.ToList();
|
|
if (relevantDetails.Any())
|
{
|
var firstDetail = relevantDetails.First();
|
var useableQuantity = relevantDetails.Sum(d => d.StockQuantity - d.OutboundQuantity);
|
|
result.Add(new StockSelectViewDTO
|
{
|
LocationCode = stock.LocationCode,
|
MaterielCode = firstDetail.MaterielCode,
|
MaterielName = firstDetail.MaterielName,
|
BatchNo = orderDetail.BatchNo,
|
SupplyCode = orderDetail.SupplyCode,
|
Barcode = firstDetail.Barcode,
|
PalletCode = stock.PalletCode,
|
UseableQuantity = useableQuantity,
|
StockCreateDate = stock.CreateDate,
|
StockId = stock.Id,
|
OrderDetailId = orderDetail.Id // 关联到具体的出库单明细
|
});
|
}
|
}
|
}
|
|
return result;
|
}
|
|
public List<StockSelectViewDTO> GetSelectViewDTOs(string orderNo, string materielCode)
|
{
|
var outboundOrder = SqlSugarHelper.DbWMS.Queryable<Dt_OutboundOrder>().Where(x => x.UpperOrderNo == orderNo).First();
|
if (outboundOrder == null)
|
{
|
throw new Exception($"未找到单据编号为{orderNo}的出库单信息");
|
}
|
return GetStockSelectViews(outboundOrder.Id, materielCode);
|
}
|
|
public List<StockSelectViewDTO> GetStockSelectViews(int orderId, string materielCode)
|
{
|
Dt_OutboundOrder outboundOrder = _outboundOrderService.Repository.QueryFirst(x => x.Id == orderId);
|
if (outboundOrder == null)
|
{
|
throw new Exception($"未找到ID为{orderId}的出库单信息");
|
}
|
|
// 获取出库单明细,用于确定需要的批次和供应商
|
var orderDetails = SqlSugarHelper.DbWMS.Queryable<Dt_OutboundOrderDetail>().Where(x => x.OrderId == orderId && x.MaterielCode == materielCode).ToList();
|
|
if (!orderDetails.Any())
|
{
|
throw new Exception($"出库单{orderId}中未找到物料{materielCode}的明细信息");
|
}
|
|
List<string> locationCodes = _locationInfoService.GetCanOutLocationCodes();
|
var result = new List<StockSelectViewDTO>();
|
|
// 为每个出库单明细查询对应的库存
|
foreach (var orderDetail in orderDetails)
|
{
|
// 构建查询条件
|
var stockQuery = Db.Queryable<Dt_StockInfo>()
|
.Where(x => locationCodes.Contains(x.LocationCode))
|
.Where(x => x.StockStatus == (int)StockStatusEmun.入库完成 || x.StockStatus == (int)StockStatusEmun.过期)
|
.Includes(x => x.Details);
|
|
// 执行查询并按先进先出排序
|
var Qstocks = stockQuery.Where(x => x.Details.Any(d => d.MaterielCode == materielCode && d.StockQuantity > d.OutboundQuantity));
|
|
if (!string.IsNullOrEmpty(orderDetail.BatchNo))
|
{
|
|
Qstocks = stockQuery.Where(x => x.Details.Any(d => d.BatchNo == orderDetail.BatchNo));
|
|
}
|
if (!string.IsNullOrEmpty(orderDetail.SupplyCode))
|
{
|
|
Qstocks = stockQuery.Where(x => x.Details.Any(d => d.SupplyCode == orderDetail.SupplyCode));
|
|
}
|
|
var stocks = Qstocks.OrderBy(x => x.CreateDate).ToList();
|
|
foreach (var stock in stocks)
|
{
|
var relevantDetails = stock.Details
|
.Where(d => d.MaterielCode == materielCode &&
|
string.IsNullOrEmpty(orderDetail.BatchNo) ? true : d.BatchNo == orderDetail.BatchNo &&
|
string.IsNullOrEmpty(orderDetail.SupplyCode) ? true : d.SupplyCode == orderDetail.SupplyCode &&
|
d.StockQuantity > d.OutboundQuantity)
|
.ToList();
|
|
if (relevantDetails.Any())
|
{
|
var firstDetail = relevantDetails.First();
|
var useableQuantity = relevantDetails.Sum(d => d.StockQuantity - d.OutboundQuantity);
|
|
result.Add(new StockSelectViewDTO
|
{
|
LocationCode = stock.LocationCode,
|
MaterielCode = firstDetail.MaterielCode,
|
MaterielName = firstDetail.MaterielName,
|
BatchNo = orderDetail.BatchNo,
|
SupplyCode = orderDetail.SupplyCode,
|
Barcode = firstDetail.Barcode,
|
PalletCode = stock.PalletCode,
|
UseableQuantity = useableQuantity,
|
StockCreateDate = stock.CreateDate,
|
StockId = stock.Id,
|
OrderDetailId = orderDetail.Id // 关联到具体的出库单明细
|
});
|
}
|
}
|
}
|
|
return result;
|
}
|
|
}
|
}
|