using AutoMapper; 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_IBasicService; using WIDESEA_IOutboundService; using WIDESEA_IRecordService; using WIDESEA_IStockService; using WIDESEA_Model.Models; namespace WIDESEA_StockService { public partial class StockInfoService : ServiceBase>, IStockInfoService { private readonly IMapper _mapper; private readonly IRecordService _recordService; public IRepository Repository => BaseDal; private readonly IRepository _stockInfoDetailRepository; private readonly IOutboundOrderService _outboundOrderService; private readonly ILocationInfoService _locationInfoService; public StockInfoService(IRepository BaseDal, IMapper mapper, IRepository stockInfoDetailRepository, IRecordService recordService, ILocationInfoService locationInfoService, IOutboundOrderService outboundOrderService) : base(BaseDal) { _mapper = mapper; _stockInfoDetailRepository = stockInfoDetailRepository; _recordService = recordService; _locationInfoService = locationInfoService; _outboundOrderService = outboundOrderService; } /// /// 根据托盘号查询库存 /// /// /// 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 details = new List(); 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); } /// /// /// /// /// /// /// /// public (List, Dictionary) GetOutboundStocks(List stockInfos, string materielCode, decimal needQuantity, out decimal residueQuantity) { List outStocks = new List(); Dictionary stockAllocations = new Dictionary(); // 记录每个库存明细的分配数量 // 按先进先出排序所有相关的库存明细 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); } /// /// 根据条码获取库存信息 /// public async Task GetStockDetailByBarcode(string barcode, string materielCode) { return await Db.Queryable() .Includes(x => x.StockInfo) .Where(x => x.Barcode == barcode && x.MaterielCode == materielCode) .FirstAsync(); } /// /// 获取物料的所有条码信息 /// public async Task> GetMaterialBarcodes(string materielCode, string batchNo = null) { var query = Db.Queryable() .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 GetStockInfos(string materielCode, string lotNo, string supplyCode, List locationCodes) { var query = Db.Queryable() .Where(x => locationCodes.Contains(x.LocationCode) // && 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 sugarQueryable = Db.Queryable().Where(x => locationCodes.Contains(x.LocationCode)); //ISugarQueryable sugarQueryable1 = Db.Queryable().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 GetUseableStocks(string materielCode, string batchNo, string supplyCode) { List locationCodes = _locationInfoService.GetCanOutLocationCodes(); return GetStockInfos(materielCode, batchNo,supplyCode, locationCodes); } public Dt_StockInfo GetPalletStockInfo(int locationType) { return Db.Queryable().Where(x => x.StockStatus == StockStatusEmun.入库完成.ObjToInt() && SqlFunc.Subqueryable().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 GetStockInfosByPalletCodes(List palletCodes) { return Db.Queryable().Where(x => palletCodes.Contains(x.PalletCode)).Includes(x => x.Details).OrderBy(x => x.CreateDate).ToList(); } /// /// /// /// /// /// public List GetStockSelectViews(int orderId, string materielCode) { try { Dt_OutboundOrder outboundOrder = _outboundOrderService.Repository.QueryFirst(x => x.Id == orderId); if (outboundOrder == null) { throw new Exception($"未找到出库单信息"); } List locationCodes = _locationInfoService.GetCanOutLocationCodes(); return BaseDal.QueryTabs((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; } } } }