| | |
| | | } |
| | | |
| | | |
| | | 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); |
| | | if (!checkResult.Item1) throw new Exception(checkResult.Item2); |
| | | |
| | | Dt_OutboundOrder outboundOrder = _outboundOrderService.Repository.QueryFirst(x => x.Id == outboundOrderDetail.OrderId); |
| | | var originalNeedQuantity = outboundOrderDetail.OrderQuantity - outboundOrderDetail.LockQuantity - outboundOrderDetail.MoveQty; |
| | | |
| | | var needQuantity = originalNeedQuantity; |
| | | List<Dt_StockInfo> outStocks = new List<Dt_StockInfo>(); |
| | | List<Dt_OutStockLockInfo> outStockLockInfos = new List<Dt_OutStockLockInfo>(); |
| | | |
| | | List<Dt_StockInfo> outStocks = _stockService.StockInfoService.GetStockInfosByPalletCodes(stockSelectViews.Select(x => x.PalletCode).ToList()); |
| | | var assignQuantity = 0m; |
| | | outStocks.ForEach(x => |
| | | { |
| | | x.Details.ForEach(v => |
| | | { |
| | | assignQuantity += v.StockQuantity - v.OutboundQuantity; |
| | | }); |
| | | }); |
| | | decimal remainingNeedQuantity = originalNeedQuantity; |
| | | decimal totalAssignedFromUserSelection = 0; |
| | | |
| | | outboundOrderDetail.LockQuantity += assignQuantity; |
| | | outStocks.ForEach(x => |
| | | // æå
è¿å
åºæåºç¨æ·éæ©çåºå |
| | | var userSelectedStocks = _stockService.StockInfoService.GetStockInfosByPalletCodes( |
| | | stockSelectViews.Select(x => x.PalletCode).ToList()); |
| | | |
| | | var sortedUserSelectedStocks = userSelectedStocks |
| | | .OrderBy(x => x.CreateDate) |
| | | .ToList(); |
| | | |
| | | // åé
ç¨æ·éæ©çåºå |
| | | foreach (var stock in sortedUserSelectedStocks) |
| | | { |
| | | x.Details.ForEach(v => |
| | | if (remainingNeedQuantity <= 0) break; |
| | | |
| | | // è·åç¨æ·å¯¹è¯¥æççéæ©æ°é |
| | | var userSelection = stockSelectViews.FirstOrDefault(x => x.PalletCode == stock.PalletCode); |
| | | if (userSelection == null) continue; |
| | | |
| | | // 计ç®è¯¥æçå®é
å¯ç¨æ°é |
| | | var availableQuantity = CalculateAvailableQuantity(stock, outboundOrderDetail.MaterielCode, |
| | | outboundOrderDetail.BatchNo, outboundOrderDetail.SupplyCode); |
| | | |
| | | // ç¡®å®åé
æ°éï¼åç¨æ·éæ©æ°éãå¯ç¨æ°éåå©ä½éæ±çæå°å¼ |
| | | var assignQuantity = Math.Min( |
| | | Math.Min(userSelection.UseableQuantity, availableQuantity), |
| | | remainingNeedQuantity); |
| | | |
| | | if (assignQuantity <= 0) continue; |
| | | |
| | | // æ§è¡åé
|
| | | var actualAssigned = AssignStockQuantity(stock, outboundOrderDetail, assignQuantity); |
| | | if (actualAssigned > 0) |
| | | { |
| | | v.OutboundQuantity = v.StockQuantity; |
| | | }); |
| | | }); |
| | | needQuantity -= assignQuantity; |
| | | if (outboundOrderDetail.OrderQuantity > outboundOrderDetail.LockQuantity) |
| | | outStocks.Add(stock); |
| | | totalAssignedFromUserSelection += actualAssigned; |
| | | remainingNeedQuantity -= actualAssigned; |
| | | |
| | | // å建éå®è®°å½ |
| | | var lockInfo = CreateOutStockLockInfo(outboundOrder, outboundOrderDetail, stock, actualAssigned); |
| | | outStockLockInfos.Add(lockInfo); |
| | | } |
| | | } |
| | | |
| | | // æ£æ¥ç¨æ·éæ©æ¯å¦è³å°åé
äºä¸é¨å |
| | | if (totalAssignedFromUserSelection <= 0) |
| | | { |
| | | List<Dt_StockInfo> stockInfos = _stockService.StockInfoService.GetUseableStocks(outboundOrderDetail.MaterielCode, outboundOrderDetail.BatchNo, ""); |
| | | stockInfos = stockInfos.Where(x => !stockSelectViews.Select(v => v.PalletCode).Contains(x.PalletCode)).ToList(); |
| | | var (autoAssignStocks, stockAllocations) = _stockService.StockInfoService.GetOutboundStocks(stockInfos, outboundOrderDetail.MaterielCode, needQuantity, out decimal residueQuantity); |
| | | outboundOrderDetail.LockQuantity += needQuantity - residueQuantity; |
| | | throw new Exception("ç¨æ·éæ©çåºåæ æ³åé
任使°é"); |
| | | } |
| | | |
| | | // å¦æç¨æ·éæ©çåºåä¸å¤ï¼èªå¨åé
å©ä½é¨å |
| | | 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); |
| | | outboundOrderDetail.OrderDetailStatus = OrderDetailStatusEnum.AssignOver.ObjToInt(); |
| | | if (residueQuantity > 0) |
| | | { |
| | | outboundOrderDetail.OrderDetailStatus = OrderDetailStatusEnum.AssignOverPartial.ObjToInt(); |
| | | |
| | | // 为èªå¨åé
çåºåå建éå®è®°å½ |
| | | var autoLockInfos = CreateLockInfosForAutoAssign(outboundOrder, outboundOrderDetail, autoAssignStocks, stockAllocations); |
| | | outStockLockInfos.AddRange(autoLockInfos); |
| | | } |
| | | } |
| | | |
| | | List<Dt_OutStockLockInfo> outStockLockInfos = _outStockLockInfoService.GetOutStockLockInfos(outboundOrder, outboundOrderDetail, outStocks); |
| | | // æ´æ°é宿°é |
| | | outboundOrderDetail.LockQuantity += totalAssignedFromUserSelection + autoAssignedQuantity; |
| | | |
| | | // æ´æ°ç¶æ |
| | | UpdateOrderDetailStatus(outboundOrderDetail, remainingNeedQuantity - autoAssignedQuantity); |
| | | |
| | | List<Dt_LocationInfo> locationInfos = _locationInfoService.GetLocationInfos(outStocks.Select(x => x.LocationCode).ToList()); |
| | | |
| | | return (outStocks, outboundOrderDetail, outStockLockInfos, locationInfos); |
| | | } |
| | | private (bool, string) CheckSelectStockDeital(Dt_OutboundOrderDetail outboundOrderDetail, List<StockSelectViewDTO> stockSelectViews) |
| | | |
| | | // è¾
婿¹æ³ |
| | | private decimal CalculateAvailableQuantity(Dt_StockInfo stock, string materielCode, string batchNo, string supplyCode) |
| | | { |
| | | if (outboundOrderDetail == null) |
| | | { |
| | | return (false, "æªæ¾å°åºåºåæç»ä¿¡æ¯"); |
| | | } |
| | | if (outboundOrderDetail.OrderDetailStatus != OrderDetailStatusEnum.New.ObjToInt() && outboundOrderDetail.OrderDetailStatus != OrderDetailStatusEnum.AssignOverPartial.ObjToInt()) |
| | | { |
| | | return (false, "该æç»ä¸å¯æä½"); |
| | | } |
| | | //if (stockSelectViews.Sum(x => x.UseableQuantity) > outboundOrderDetail.OrderQuantity - outboundOrderDetail.LockQuantity - outboundOrderDetail.MoveQty) |
| | | //{ |
| | | // return (false, "éæ©æ°éè¶
åºåæ®æ°é"); |
| | | //} |
| | | return (true, "æå"); |
| | | var relevantDetails = stock.Details |
| | | .Where(d => d.MaterielCode == materielCode && |
| | | (string.IsNullOrEmpty(batchNo) || d.BatchNo == batchNo) && |
| | | (string.IsNullOrEmpty(supplyCode) || d.SupplyCode == supplyCode)) |
| | | .ToList(); |
| | | |
| | | return relevantDetails.Sum(d => d.StockQuantity - d.OutboundQuantity); |
| | | } |
| | | |
| | | private decimal AssignStockQuantity(Dt_StockInfo stock, Dt_OutboundOrderDetail detail, decimal assignQuantity) |
| | | { |
| | | decimal remainingAssign = assignQuantity; |
| | | |
| | | // æå
è¿å
åºåé
åºåæç» |
| | | var sortedDetails = stock.Details |
| | | .Where(d => d.MaterielCode == detail.MaterielCode && |
| | | d.BatchNo == detail.BatchNo && |
| | | (d.StockQuantity - d.OutboundQuantity) > 0) |
| | | .OrderBy(d => d.CreateDate) |
| | | .ToList(); |
| | | |
| | | 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; |
| | | } |
| | | |
| | | return assignQuantity - remainingAssign; // è¿åå®é
åé
æ°é |
| | | } |
| | | |
| | | private Dt_OutStockLockInfo CreateOutStockLockInfo(Dt_OutboundOrder outboundOrder, Dt_OutboundOrderDetail detail, |
| | | Dt_StockInfo stock, decimal quantity) |
| | | { |
| | | var barcode = stock.Details |
| | | .Where(d => !string.IsNullOrEmpty(d.Barcode)) |
| | | .Select(d => d.Barcode) |
| | | .FirstOrDefault(); |
| | | |
| | | return _outStockLockInfoService.GetOutStockLockInfo(outboundOrder, detail, stock, quantity, barcode); |
| | | } |
| | | |
| | | private List<Dt_OutStockLockInfo> CreateLockInfosForAutoAssign(Dt_OutboundOrder outboundOrder, |
| | | Dt_OutboundOrderDetail detail, List<Dt_StockInfo> stocks, Dictionary<int, decimal> allocations) |
| | | { |
| | | var lockInfos = new List<Dt_OutStockLockInfo>(); |
| | | |
| | | foreach (var stock in stocks.OrderBy(x => x.CreateDate)) |
| | | { |
| | | if (allocations.TryGetValue(stock.Id, out decimal quantity) && quantity > 0) |
| | | { |
| | | var lockInfo = CreateOutStockLockInfo(outboundOrder, detail, stock, quantity); |
| | | lockInfos.Add(lockInfo); |
| | | } |
| | | } |
| | | |
| | | return lockInfos; |
| | | } |
| | | |
| | | private void UpdateOrderDetailStatus(Dt_OutboundOrderDetail detail, decimal remainingQuantity) |
| | | { |
| | | if (remainingQuantity <= 0) |
| | | { |
| | | detail.OrderDetailStatus = OrderDetailStatusEnum.AssignOver.ObjToInt(); |
| | | } |
| | | else |
| | | { |
| | | detail.OrderDetailStatus = OrderDetailStatusEnum.AssignOverPartial.ObjToInt(); |
| | | } |
| | | } |
| | | |
| | | private (bool, string) CheckSelectStockDeital(Dt_OutboundOrderDetail outboundOrderDetail, List<StockSelectViewDTO> stockSelectViews) |
| | | { |
| | | var needQuantity = outboundOrderDetail.OrderQuantity - outboundOrderDetail.LockQuantity - outboundOrderDetail.MoveQty; |
| | | |
| | | if (needQuantity <= 0) |
| | | { |
| | | return (false, "åºåºåæç»æ éåé
åºå"); |
| | | } |
| | | |
| | | // æ£æ¥æ»éæ©æ°éæ¯å¦å¤§äº0 |
| | | var totalSelected = stockSelectViews.Sum(x => x.UseableQuantity); |
| | | if (totalSelected <= 0) |
| | | { |
| | | return (false, "ç¨æ·éæ©çåºåæ°éå¿
须大äº0"); |
| | | } |
| | | |
| | | // æ£æ¥æ¯ä¸ªæççå¯ç¨æ°é |
| | | foreach (var selection in stockSelectViews) |
| | | { |
| | | if (selection.UseableQuantity <= 0) |
| | | { |
| | | return (false, $"æç[{selection.PalletCode}]çéæ©æ°éå¿
须大äº0"); |
| | | } |
| | | |
| | | var stock = _stockService.StockInfoService.GetStockInfoByPalletCode(selection.PalletCode); |
| | | if (stock == null) |
| | | { |
| | | return (false, $"æç[{selection.PalletCode}]ä¸åå¨"); |
| | | } |
| | | |
| | | var available = CalculateAvailableQuantity(stock, outboundOrderDetail.MaterielCode, |
| | | outboundOrderDetail.BatchNo, outboundOrderDetail.SupplyCode); |
| | | |
| | | if (available <= 0) |
| | | { |
| | | return (false, $"æç[{selection.PalletCode}]没æå¯ç¨åºå"); |
| | | } |
| | | } |
| | | |
| | | return (true, "éªè¯éè¿"); |
| | | } |
| | | } |
| | | } |