| | |
| | | using Microsoft.Extensions.Logging; |
| | | using Autofac.Core; |
| | | using Microsoft.Extensions.Logging; |
| | | using SqlSugar; |
| | | using WIDESEA_Common.LocationEnum; |
| | | using WIDESEA_Common.OrderEnum; |
| | |
| | | |
| | | // æç©æåæ¹æ¬¡åç»å¤ç |
| | | var groupDetails = outboundOrderDetails |
| | | .GroupBy(x => new { x.MaterielCode, x.BatchNo, x.SupplyCode }) |
| | | .GroupBy(x => new { x.MaterielCode, x.BatchNo, x.SupplyCode,x.WarehouseCode }) |
| | | .Select(x => new |
| | | { |
| | | MaterielCode = x.Key.MaterielCode, |
| | | BatchNo = x.Key.BatchNo, |
| | | SupplyCode = x.Key.SupplyCode, |
| | | Details = x.ToList(), |
| | | WarehouseCode=x.Key.WarehouseCode, |
| | | TotalNeedQuantity = CalculateReassignNeedQuantity(x.ToList()) |
| | | }) |
| | | .Where(x => x.TotalNeedQuantity > 0) |
| | |
| | | foreach (var item in groupDetails) |
| | | { |
| | | 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,item.WarehouseCode, outboundOrder.FactoryArea); |
| | | |
| | | if (!stockInfos.Any()) |
| | | { |
| | |
| | | |
| | | // æ´æ°åºåºåæç»ç¶æ |
| | | UpdateOrderDetailStatus(item.Details, allocatedQuantity, needQuantity); |
| | | |
| | | |
| | | // æ´æ°åºååæçç¶æ |
| | | UpdateNormalStocksAndPalletsStatus(autoAssignStocks, stockAllocations); |
| | | } |
| | | |
| | | if (outStocks.Any()) |
| | |
| | | |
| | | return (outStocks, outboundOrderDetails, outStockLockInfos, locationInfos); |
| | | } |
| | | /// <summary> |
| | | /// æ´æ°åºååæçç¶æ |
| | | /// </summary> |
| | | private void UpdateNormalStocksAndPalletsStatus(List<Dt_StockInfo> stocks, Dictionary<int, decimal> stockAllocations) |
| | | { |
| | | foreach (var stock in stocks) |
| | | { |
| | | var stockDetail = stock.Details.First(); |
| | | if (stockAllocations.TryGetValue(stockDetail.Id, out decimal allocatedQty) && allocatedQty > 0) |
| | | { |
| | | |
| | | _stockService.StockInfoService.Db.Updateable<Dt_StockInfo>() |
| | | .SetColumns(x => new Dt_StockInfo |
| | | { |
| | | StockStatus = (int)StockStatusEmun.åºåºéå®, |
| | | }) |
| | | .Where(x => x.Id == stock.Id) |
| | | .ExecuteCommand(); |
| | | } |
| | | } |
| | | } |
| | | /// <summary> |
| | | /// æ£æ¥è®¢åæ¯å¦å
è®¸éæ°åé
|
| | | /// </summary> |
| | |
| | | |
| | | // æç©æåæ¹æ¬¡åç»å¤çï¼è¿éåªæä¸ä¸ªæç»ï¼ |
| | | var groupDetails = new List<Dt_OutboundOrderDetail> { orderDetail } |
| | | .GroupBy(x => new { x.MaterielCode, x.BatchNo, x.SupplyCode }) |
| | | .GroupBy(x => new { x.MaterielCode, x.BatchNo, x.SupplyCode ,x.WarehouseCode}) |
| | | .Select(x => new |
| | | { |
| | | MaterielCode = x.Key.MaterielCode, |
| | | BatchNo = x.Key.BatchNo, |
| | | SupplyCode = x.Key.SupplyCode, |
| | | WarehouseCode=x.Key.WarehouseCode, |
| | | Details = x.ToList(), |
| | | TotalNeedQuantity = batchQuantity // 使ç¨åæ¹æ°é |
| | | }) |
| | |
| | | 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,item.WarehouseCode,outboundOrder.FactoryArea); |
| | | if (!stockInfos.Any()) |
| | | { |
| | | throw new Exception($"ç©æ[{item.MaterielCode}]æ¹æ¬¡[{item.BatchNo}]æªæ¾å°å¯åé
åºå"); |
| | |
| | | |
| | | // æ´æ°åºåºåæç»ç¶æ |
| | | UpdateOrderDetailStatus(item.Details, allocatedQuantity, needQuantity); |
| | | |
| | | // æ´æ°åºååæçç¶æä¸ºåºåºéå® |
| | | await UpdateStocksAndPalletsStatus(autoAssignStocks, stockAllocations); |
| | | } |
| | | |
| | | if (outStocks.Any()) |
| | |
| | | |
| | | return (outStocks, groupDetails.SelectMany(x => x.Details).ToList(), outStockLockInfos, locationInfos); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// æ´æ°åºååæçç¶æ |
| | | /// </summary> |
| | | private async Task UpdateStocksAndPalletsStatus(List<Dt_StockInfo> stocks, Dictionary<int, decimal> stockAllocations) |
| | | { |
| | | foreach (var stock in stocks) |
| | | { |
| | | var stockDetail = stock.Details.First(); |
| | | if (stockAllocations.TryGetValue(stockDetail.Id, out decimal allocatedQty) && allocatedQty > 0) |
| | | { |
| | | |
| | | await _stockService.StockInfoService.Db.Updateable<Dt_StockInfo>() |
| | | .SetColumns(x => new Dt_StockInfo |
| | | { |
| | | StockStatus = (int)StockStatusEmun.åºåºéå®, |
| | | }) |
| | | .Where(x => x.Id == stock.Id) |
| | | .ExecuteCommandAsync(); |
| | | } |
| | | } |
| | | } |
| | | /// <summary> |
| | | /// æå
è¿å
åºåååé
é宿°é |
| | | /// </summary> |
| | |
| | | } |
| | | |
| | | |
| | | public (List<Dt_StockInfo>, Dt_OutboundOrderDetail, List<Dt_OutStockLockInfo>, List<Dt_LocationInfo>) |
| | | AssignStockOutbound(Dt_OutboundOrderDetail outboundOrderDetail, List<StockSelectViewDTO> stockSelectViews) |
| | | public (List<Dt_StockInfo>, Dt_OutboundOrderDetail, List<Dt_OutStockLockInfo>, List<Dt_LocationInfo>)AssignStockOutbound(Dt_OutboundOrderDetail outboundOrderDetail, List<StockSelectViewDTO> stockSelectViews) |
| | | { |
| | | // éªè¯ç¨æ·éæ© |
| | | (bool, string) checkResult = CheckSelectStockDeital(outboundOrderDetail, stockSelectViews); |
| | |
| | | decimal totalAssignedFromUserSelection = 0; |
| | | |
| | | // æå
è¿å
åºæåºç¨æ·éæ©çåºå |
| | | var userSelectedStocks = _stockService.StockInfoService.GetStockInfosByPalletCodes( |
| | | stockSelectViews.Select(x => x.PalletCode).ToList()); |
| | | var userSelectedStocks = _stockService.StockInfoService.GetStockInfosByPalletCodes(stockSelectViews.Select(x => x.PalletCode).ToList()); |
| | | |
| | | var sortedUserSelectedStocks = userSelectedStocks |
| | | .OrderBy(x => x.CreateDate) |
| | | .ToList(); |
| | | var sortedUserSelectedStocks = userSelectedStocks.OrderBy(x => x.CreateDate).ToList(); |
| | | |
| | | // åé
ç¨æ·éæ©çåºå |
| | | foreach (var stock in sortedUserSelectedStocks) |
| | |
| | | if (userSelection == null) continue; |
| | | |
| | | // 计ç®è¯¥æçå®é
å¯ç¨æ°é |
| | | var availableQuantity = CalculateAvailableQuantity(stock, outboundOrderDetail.MaterielCode, |
| | | outboundOrderDetail.BatchNo, outboundOrderDetail.SupplyCode); |
| | | var availableQuantity = CalculateAvailableQuantityByBarcode(stock, outboundOrderDetail.MaterielCode, |
| | | outboundOrderDetail.BatchNo, outboundOrderDetail.SupplyCode, userSelection.Barcode); |
| | | |
| | | // ç¡®å®åé
æ°éï¼åç¨æ·éæ©æ°éãå¯ç¨æ°éåå©ä½éæ±çæå°å¼ |
| | | var assignQuantity = Math.Min( |
| | | Math.Min(userSelection.UseableQuantity, availableQuantity), |
| | | remainingNeedQuantity); |
| | | var assignQuantity = Math.Min(Math.Min(userSelection.UseableQuantity, availableQuantity), remainingNeedQuantity); |
| | | |
| | | if (assignQuantity <= 0) continue; |
| | | |
| | | // æ§è¡åé
|
| | | var actualAssigned = AssignStockQuantity(stock, outboundOrderDetail, assignQuantity); |
| | | // æ§è¡åé
ï¼ä½¿ç¨ç¨æ·éæ©çæ¡ç |
| | | var actualAssigned = AssignStockQuantity(stock, outboundOrderDetail, assignQuantity, userSelection.Barcode); |
| | | |
| | | if (actualAssigned > 0) |
| | | { |
| | | outStocks.Add(stock); |
| | | totalAssignedFromUserSelection += actualAssigned; |
| | | remainingNeedQuantity -= actualAssigned; |
| | | |
| | | // å建éå®è®°å½ |
| | | var lockInfo = CreateOutStockLockInfo(outboundOrder, outboundOrderDetail, stock, actualAssigned); |
| | | // å建éå®è®°å½ï¼ä½¿ç¨ç¨æ·éæ©çæ¡ç |
| | | var lockInfo = CreateOutStockLockInfo(outboundOrder, outboundOrderDetail, stock, actualAssigned, userSelection.Barcode); |
| | | outStockLockInfos.Add(lockInfo); |
| | | } |
| | | } |
| | |
| | | } |
| | | |
| | | // å¦æç¨æ·éæ©çåºåä¸å¤ï¼èªå¨åé
å©ä½é¨å |
| | | decimal autoAssignedQuantity = 0; |
| | | |
| | | if (remainingNeedQuantity > 0) |
| | | { |
| | | List<Dt_StockInfo> autoStocks = _stockService.StockInfoService.GetUseableStocks( |
| | | outboundOrderDetail.MaterielCode, outboundOrderDetail.BatchNo, ""); |
| | | |
| | | // æé¤ç¨æ·å·²éæ©çæç |
| | | autoStocks = autoStocks |
| | | .Where(x => !stockSelectViews.Select(v => v.PalletCode).Contains(x.PalletCode)) |
| | | .ToList(); |
| | | |
| | | var (autoAssignStocks, stockAllocations) = _stockService.StockInfoService.GetOutboundStocks( |
| | | autoStocks, outboundOrderDetail.MaterielCode, remainingNeedQuantity, out decimal residueQuantity); |
| | | |
| | | // æ£æ¥èªå¨åé
ç»æ |
| | | autoAssignedQuantity = remainingNeedQuantity - residueQuantity; |
| | | if (autoAssignedQuantity <= 0 && remainingNeedQuantity > 0) |
| | | { |
| | | // é¨ååé
æ¯å¯ä»¥æ¥åçï¼è®°å½è¦åä½ä¸æ¥é |
| | | _logger.LogWarning($"èªå¨åé
失败ï¼å©ä½éæ±{remainingNeedQuantity}æ æ³æ»¡è¶³"); |
| | | } |
| | | else if (autoAssignedQuantity > 0) |
| | | { |
| | | outStocks.AddRange(autoAssignStocks); |
| | | |
| | | // 为èªå¨åé
çåºåå建éå®è®°å½ |
| | | var autoLockInfos = CreateLockInfosForAutoAssign(outboundOrder, outboundOrderDetail, autoAssignStocks, stockAllocations); |
| | | outStockLockInfos.AddRange(autoLockInfos); |
| | | } |
| | | |
| | | } |
| | | |
| | | // æ´æ°é宿°é |
| | | outboundOrderDetail.LockQuantity += totalAssignedFromUserSelection + autoAssignedQuantity; |
| | | outboundOrderDetail.LockQuantity += totalAssignedFromUserSelection; |
| | | |
| | | // æ´æ°ç¶æ |
| | | UpdateOrderDetailStatus(outboundOrderDetail, remainingNeedQuantity - autoAssignedQuantity); |
| | | UpdateOrderDetailStatus(outboundOrderDetail, remainingNeedQuantity); |
| | | |
| | | List<Dt_LocationInfo> locationInfos = _locationInfoService.GetLocationInfos(outStocks.Select(x => x.LocationCode).ToList()); |
| | | |
| | | return (outStocks, outboundOrderDetail, outStockLockInfos, locationInfos); |
| | | } |
| | | private decimal CalculateAvailableQuantityByBarcode(Dt_StockInfo stock, string materielCode, string batchNo, string supplyCode, string barcode) |
| | | { |
| | | var query = stock.Details.AsQueryable() |
| | | .Where(d => d.MaterielCode == materielCode && |
| | | (d.StockQuantity - d.OutboundQuantity) > 0 && |
| | | d.Barcode == barcode); |
| | | |
| | | if (!string.IsNullOrEmpty(batchNo)) |
| | | { |
| | | query = query.Where(x => x.BatchNo == batchNo); |
| | | } |
| | | |
| | | if (!string.IsNullOrEmpty(supplyCode)) |
| | | { |
| | | query = query.Where(d => d.SupplyCode == supplyCode); |
| | | } |
| | | |
| | | return query.Sum(d => d.StockQuantity - d.OutboundQuantity); |
| | | } |
| | | // è¾
婿¹æ³ |
| | | private decimal CalculateAvailableQuantity(Dt_StockInfo stock, string materielCode, string batchNo, string supplyCode) |
| | | { |
| | |
| | | return relevantDetails.Sum(d => d.StockQuantity - d.OutboundQuantity); |
| | | } |
| | | |
| | | private decimal AssignStockQuantity(Dt_StockInfo stock, Dt_OutboundOrderDetail detail, decimal assignQuantity) |
| | | private decimal AssignStockQuantity(Dt_StockInfo stock, Dt_OutboundOrderDetail detail, decimal assignQuantity, string barcode) |
| | | { |
| | | decimal remainingAssign = assignQuantity; |
| | | |
| | | // æå
è¿å
åºåé
åºåæç» |
| | | var sorteds = stock.Details |
| | | // æå
è¿å
åºåé
æå®æ¡ç çåºåæç» |
| | | var query = stock.Details.AsQueryable() |
| | | .Where(d => d.MaterielCode == detail.MaterielCode && |
| | | (d.StockQuantity - d.OutboundQuantity) > 0); |
| | | // .OrderBy(d => d.CreateDate); |
| | | (d.StockQuantity - d.OutboundQuantity) > 0 && |
| | | d.Barcode == barcode); // åªåé
æå®æ¡ç |
| | | |
| | | query = query.Where(x => x.WarehouseCode == detail.WarehouseCode); |
| | | |
| | | if (!string.IsNullOrEmpty(detail.BatchNo)) |
| | | { |
| | | sorteds= stock.Details.Where(x => x.BatchNo == detail.BatchNo); |
| | | query = query.Where(x => x.BatchNo == detail.BatchNo); |
| | | } |
| | | |
| | | var sortedDetails= sorteds.ToList().OrderBy(d => d.CreateDate); |
| | | |
| | | // 妿åºåºåæä¾åºåè¦æ±ï¼æä¾åºåè¿æ»¤ |
| | | if (!string.IsNullOrEmpty(detail.SupplyCode)) |
| | | { |
| | | query = query.Where(d => d.SupplyCode == detail.SupplyCode); |
| | | } |
| | | var sortedDetails = query.ToList().OrderBy(d => d.CreateDate); |
| | | |
| | | foreach (var stockDetail in sortedDetails) |
| | | { |
| | |
| | | return assignQuantity - remainingAssign; // è¿åå®é
åé
æ°é |
| | | } |
| | | |
| | | private Dt_OutStockLockInfo CreateOutStockLockInfo(Dt_OutboundOrder outboundOrder, Dt_OutboundOrderDetail detail, |
| | | Dt_StockInfo stock, decimal quantity) |
| | | private (decimal assignedQuantity, string barcode) AssignStockQuantity(Dt_StockInfo stock, Dt_OutboundOrderDetail detail, decimal assignQuantity) |
| | | { |
| | | var barcode = stock.Details |
| | | .Where(d => !string.IsNullOrEmpty(d.Barcode)) |
| | | .Select(d => d.Barcode) |
| | | .FirstOrDefault(); |
| | | decimal remainingAssign = assignQuantity; |
| | | string assignedBarcode = string.Empty; |
| | | |
| | | // æå
è¿å
åºåé
åºåæç» |
| | | var query = stock.Details.AsQueryable() |
| | | .Where(d => d.MaterielCode == detail.MaterielCode && |
| | | (d.StockQuantity - d.OutboundQuantity) > 0); |
| | | // .OrderBy(d => d.CreateDate); |
| | | |
| | | if (!string.IsNullOrEmpty(detail.BatchNo)) |
| | | { |
| | | query = query.Where(x => x.BatchNo == detail.BatchNo); |
| | | } |
| | | // 妿åºåºåæä¾åºåè¦æ±ï¼æä¾åºåè¿æ»¤ |
| | | if (!string.IsNullOrEmpty(detail.SupplyCode)) |
| | | { |
| | | query = query.Where(d => d.SupplyCode == detail.SupplyCode); |
| | | } |
| | | var sortedDetails= query.ToList().OrderBy(d => d.CreateDate); |
| | | |
| | | |
| | | foreach (var stockDetail in sortedDetails) |
| | | { |
| | | if (remainingAssign <= 0) break; |
| | | |
| | | var available = stockDetail.StockQuantity - stockDetail.OutboundQuantity; |
| | | var assign = Math.Min(available, remainingAssign); |
| | | |
| | | stockDetail.OutboundQuantity += assign; |
| | | remainingAssign -= assign; |
| | | if (string.IsNullOrEmpty(assignedBarcode) && assign > 0) |
| | | { |
| | | assignedBarcode = stockDetail.Barcode; |
| | | } |
| | | } |
| | | |
| | | |
| | | return (assignQuantity - remainingAssign, assignedBarcode); |
| | | } |
| | | |
| | | private Dt_OutStockLockInfo CreateOutStockLockInfo(Dt_OutboundOrder outboundOrder, Dt_OutboundOrderDetail detail, |
| | | Dt_StockInfo stock, decimal quantity,string barcode="") |
| | | { |
| | | |
| | | |
| | | return _outStockLockInfoService.GetOutStockLockInfo(outboundOrder, detail, stock, quantity, barcode); |
| | | } |
| | |
| | | return (false, $"æç[{selection.PalletCode}]ä¸åå¨"); |
| | | } |
| | | |
| | | var available = CalculateAvailableQuantity(stock, outboundOrderDetail.MaterielCode, |
| | | outboundOrderDetail.BatchNo, outboundOrderDetail.SupplyCode); |
| | | var available = CalculateAvailableQuantityByBarcode(stock, outboundOrderDetail.MaterielCode, |
| | | outboundOrderDetail.BatchNo, outboundOrderDetail.SupplyCode,selection.Barcode); |
| | | |
| | | if (available <= 0) |
| | | { |