pan
2025-11-25 bbc4a3a07baf111c9074ceee7728158fb3eedf1a
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundOrderDetailService.cs
@@ -68,14 +68,14 @@
            List<Dt_OutStockLockInfo> outStockLockInfos = new List<Dt_OutStockLockInfo>();
            List<Dt_LocationInfo> locationInfos = new List<Dt_LocationInfo>();
            // æŒ‰ç‰©æ–™å’Œæ‰¹æ¬¡åˆ†ç»„处理
            // æŒ‰ç‰©æ–™ã€æ‰¹æ¬¡ã€ä¾›åº”商分组
            var groupDetails = outboundOrderDetails
                .GroupBy(x => new { x.MaterielCode, x.BatchNo ,x.SupplyCode})
                .Select(x => new
                {
                    MaterielCode = x.Key.MaterielCode,
                    BatchNo = x.Key.BatchNo,
                    SupplyCode = x.Key.SupplyCode,
                    x.Key.MaterielCode,
                    x.Key.BatchNo,
                    x.Key.SupplyCode,
                    Details = x.ToList(),
                    TotalNeedQuantity = x.Sum(v => v.OrderQuantity - v.OverOutQuantity - v.LockQuantity-v.MoveQty)
                })
@@ -86,29 +86,35 @@
            {
                var needQuantity = item.TotalNeedQuantity;
                // èŽ·å–å¯ç”¨åº“å­˜ï¼ˆæŒ‰å…ˆè¿›å…ˆå‡ºæŽ’åºï¼‰
                List<Dt_StockInfo> stockInfos = _stockService.StockInfoService.GetUseableStocks(item.MaterielCode, item.BatchNo,item.SupplyCode);
                // èŽ·å–å¯ç”¨åº“å­˜ï¼ˆå·²æŒ‰å…ˆè¿›å…ˆå‡ºæŽ’åºï¼‰
                List<Dt_StockInfo> stockInfos = _stockService.StockInfoService.GetUseableStocks(
                    item.MaterielCode, item.BatchNo, item.SupplyCode);
                if (!stockInfos.Any())
                {
                    throw new Exception($"物料[{item.MaterielCode}]批次[{item.BatchNo}]未找到可分配库存");
                }
                // åˆ†é…åº“存(按先进先出)
                // åˆ†é…åº“å­˜
                var (autoAssignStocks, stockAllocations) = _stockService.StockInfoService.GetOutboundStocks(
                    stockInfos, item.MaterielCode, needQuantity, out decimal residueQuantity);
                if (residueQuantity > 0 && residueQuantity == needQuantity)
                // æ£€æŸ¥åˆ†é…ç»“æžœ
                if (residueQuantity > 0)
                {
                    throw new Exception($"物料[{item.MaterielCode}]库存不足,需要{needQuantity},可用{needQuantity - residueQuantity}");
                    var allocatedQuantity = needQuantity - residueQuantity;
                    throw new Exception($"物料[{item.MaterielCode}]库存不足,需要{needQuantity},实际分配{allocatedQuantity}");
                }
                outStocks.AddRange(autoAssignStocks);
                // æŒ‰å…ˆè¿›å…ˆå‡ºåŽŸåˆ™åˆ†é…é”å®šæ•°é‡åˆ°å„ä¸ªæ˜Žç»†
                DistributeLockQuantityByFIFO(item.Details, autoAssignStocks, stockAllocations, outStockLockInfos, outboundOrder);
                // æŒ‰å…ˆè¿›å…ˆå‡ºåˆ†é…é”å®šæ•°é‡
                DistributeLockQuantityByFIFO(item.Details, autoAssignStocks, stockAllocations,
                    outStockLockInfos, outboundOrder);
            }
            locationInfos.AddRange(_locationInfoService.GetLocationInfos(outStocks.Select(x => x.LocationCode).Distinct().ToList()));
            locationInfos.AddRange(_locationInfoService.GetLocationInfos(
                outStocks.Select(x => x.LocationCode).Distinct().ToList()));
            return (outStocks, outboundOrderDetails, outStockLockInfos, locationInfos);
        }
@@ -116,95 +122,62 @@
        /// <summary>
        /// æŒ‰å…ˆè¿›å…ˆå‡ºåŽŸåˆ™åˆ†é…é”å®šæ•°é‡åˆ°å„ä¸ªæ˜Žç»†  
        /// </summary>
        private void DistributeLockQuantityByFIFO(
            List<Dt_OutboundOrderDetail> details,
            List<Dt_StockInfo> assignStocks,
            Dictionary<int, decimal> stockAllocations,
            List<Dt_OutStockLockInfo> outStockLockInfos,
        private void DistributeLockQuantityByFIFO(List<Dt_OutboundOrderDetail> details,List<Dt_StockInfo> assignStocks,Dictionary<int, decimal> stockAllocations,List<Dt_OutStockLockInfo> outStockLockInfos,
            Dt_OutboundOrder outboundOrder)
        {
            // æŒ‰å…ˆè¿›å…ˆå‡ºæŽ’序出库单明细(假设先创建的明细需要优先满足)
            var sortedStocks = assignStocks.OrderBy(x => x.CreateDate).ToList();
            var totalNeedQuantity = details.Sum(d => d.OrderQuantity - d.OverOutQuantity - d.LockQuantity - d.MoveQty);
            decimal allocatedQuantity = 0;
            foreach (var stock in sortedStocks)
            {
                if (allocatedQuantity >= totalNeedQuantity) break;
                if (!stockAllocations.TryGetValue(stock.Id, out decimal stockAllocation) || stockAllocation <= 0)
                    continue;
            var sortedDetails = details
                .Where(d => d.OrderQuantity - d.OverOutQuantity - d.LockQuantity -d.MoveQty> 0) // åªå¤„理还需要分配的数量
                    .Where(d => d.OrderQuantity - d.OverOutQuantity - d.LockQuantity - d.MoveQty > 0)
                .OrderBy(x => x.Id)
                .ToList();
            if (!sortedDetails.Any()) return;
            // èŽ·å–æ‰€æœ‰åˆ†é…äº†åº“å­˜çš„æ˜Žç»†ï¼ŒæŒ‰å…ˆè¿›å…ˆå‡ºæŽ’åº
            var allocatedStockDetails = assignStocks
                .SelectMany(x => x.Details)
                .Where(x => stockAllocations.ContainsKey(x.Id))
                .OrderBy(x => x.CreateDate)
                .ThenBy(x => x.StockId)
                .ToList();
            // ä¸ºæ¯ä¸ªåº“存明细创建分配记录
            foreach (var stockDetail in allocatedStockDetails)
            {
                if (!stockAllocations.TryGetValue(stockDetail.Id, out decimal allocatedQuantity))
                    continue;
                if (allocatedQuantity <= 0) continue;
                var stockInfo = assignStocks.First(x => x.Id == stockDetail.StockId);
                decimal remainingAllocate = allocatedQuantity;
                // æŒ‰é¡ºåºåˆ†é…ç»™å„个出库单明细
                foreach (var detail in sortedDetails)
                {
                    if (remainingAllocate <= 0) break;
                    if (stockAllocation <= 0) break;
                    // è®¡ç®—这个明细还需要分配的数量
                    var detailNeed = detail.OrderQuantity - detail.OverOutQuantity - detail.LockQuantity-detail.MoveQty;
                    if (detailNeed <= 0) continue;
                    // åˆ†é…æ•°é‡
                    var assignQuantity = Math.Min(remainingAllocate, detailNeed);
                    var assignQuantity = Math.Min(stockAllocation, detailNeed);
                    // éªŒè¯æ¡ç æ˜¯å¦å­˜åœ¨
                    if (string.IsNullOrEmpty(stockDetail.Barcode))
                    // ä½¿ç”¨åº“存中的有效条码
                    var barcode = stock.Details
                        .Where(d => !string.IsNullOrEmpty(d.Barcode))
                        .Select(d => d.Barcode)
                        .FirstOrDefault();
                    if (string.IsNullOrEmpty(barcode))
                    {
                        throw new Exception($"库存明细ID[{stockDetail.Id}]的条码为空");
                        throw new Exception($"库存ID[{stock.Id}]的条码为空");
                    }
                    // åˆ›å»ºå‡ºåº“锁定信息
                    var lockInfo = _outStockLockInfoService.GetOutStockLockInfo(
                        outboundOrder, detail, stockInfo, assignQuantity, stockDetail.Barcode);
                    outStockLockInfos.Add(lockInfo);
                    // æ›´æ–°æ˜Žç»†çš„锁定数量
                    detail.LockQuantity += assignQuantity;
                    remainingAllocate -= assignQuantity;
                }
                // å¦‚果还有剩余分配数量,重新分配或记录警告
                if (remainingAllocate > 0)
                {
                    // é‡æ–°åˆ†é…ç»™å…¶ä»–需要分配的明细
                    foreach (var detail in sortedDetails)
                    {
                        if (remainingAllocate <= 0) break;
                        var detailNeed = detail.OrderQuantity - detail.OverOutQuantity - detail.LockQuantity;
                        if (detailNeed <= 0) continue;
                        var assignQuantity = Math.Min(remainingAllocate, detailNeed);
                        var lockInfo = _outStockLockInfoService.GetOutStockLockInfo(
                            outboundOrder, detail, stockInfo, assignQuantity, stockDetail.Barcode);
                        outboundOrder, detail, stock, assignQuantity, barcode);
                        outStockLockInfos.Add(lockInfo);
                        detail.LockQuantity += assignQuantity;
                        remainingAllocate -= assignQuantity;
                    stockAllocation -= assignQuantity;
                    allocatedQuantity += assignQuantity;
                    if (allocatedQuantity >= totalNeedQuantity) break;
                }
                    }
                    // å¦‚果还有剩余,记录警告但不抛出异常
                    if (remainingAllocate > 0)
            // éªŒè¯æ˜¯å¦å®Œå…¨åˆ†é…
            if (allocatedQuantity < totalNeedQuantity)
                    {
                        _logger.LogWarning($"库存分配后仍有剩余数量未分配: {remainingAllocate}, æ¡ç : {stockDetail.Barcode}");
                    }
                }
                _logger.LogWarning($"库存分配不完全,需要{totalNeedQuantity},实际分配{allocatedQuantity}");
            }
        }