pan
2025-11-25 bbc4a3a07baf111c9074ceee7728158fb3eedf1a
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_StockService/StockInfoService.cs
@@ -1,7 +1,13 @@
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;
@@ -15,11 +21,15 @@
        private readonly IRecordService _recordService;
        public IRepository<Dt_StockInfo> Repository => BaseDal;
        private readonly IRepository<Dt_StockInfoDetail> _stockInfoDetailRepository;
        public StockInfoService(IRepository<Dt_StockInfo> BaseDal, IMapper mapper, IRepository<Dt_StockInfoDetail> stockInfoDetailRepository, IRecordService recordService) : base(BaseDal)
        private readonly IOutboundOrderService _outboundOrderService;
        private readonly ILocationInfoService _locationInfoService;
        public StockInfoService(IRepository<Dt_StockInfo> BaseDal, IMapper mapper, IRepository<Dt_StockInfoDetail> stockInfoDetailRepository, IRecordService recordService, ILocationInfoService locationInfoService, IOutboundOrderService outboundOrderService) : base(BaseDal)
        {
            _mapper = mapper;
            _stockInfoDetailRepository = stockInfoDetailRepository;
            _recordService = recordService;
            _locationInfoService = locationInfoService;
            _outboundOrderService = outboundOrderService;
        }
        /// <summary>
@@ -72,5 +82,193 @@
            _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 == (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();
        }
        /// <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;
            }
        }
    }
}