public IList <FastDeliveryAvailabilityHistoryItem> ConvertVerificationDetailsNodesToAvailabilityHistoryItems( IEnumerable <FastDeliveryVerificationDetailsNode> nodes, FastDeliveryAvailabilityHistory fastDeliveryAvailabilityHistory) { var items = new List <FastDeliveryAvailabilityHistoryItem>(); foreach (var node in nodes) { var item = new FastDeliveryAvailabilityHistoryItem { DistanceByLineToClient = node.DistanceByLineToClient.ParameterValue, IsValidDistanceByLineToClient = node.DistanceByLineToClient.IsValidParameter, DistanceByRoadToClient = node.DistanceByRoadToClient.ParameterValue, IsValidDistanceByRoadToClient = node.DistanceByRoadToClient.IsValidParameter, Driver = node.RouteList.Driver, IsGoodsEnough = node.IsGoodsEnough.ParameterValue, IsValidIsGoodsEnough = node.IsGoodsEnough.IsValidParameter, LastCoordinateTimeElapsed = node.LastCoordinateTime.ParameterValue, IsValidLastCoordinateTime = node.LastCoordinateTime.IsValidParameter, RemainingTimeForShipmentNewOrder = node.RemainingTimeForShipmentNewOrder.ParameterValue, IsValidRemainingTimeForShipmentNewOrder = node.RemainingTimeForShipmentNewOrder.IsValidParameter, RouteList = node.RouteList, UnclosedFastDeliveries = node.UnClosedFastDeliveries.ParameterValue, IsValidUnclosedFastDeliveries = node.UnClosedFastDeliveries.IsValidParameter, IsValidToFastDelivery = node.IsValidRLToFastDelivery, FastDeliveryAvailabilityHistory = fastDeliveryAvailabilityHistory }; items.Add(item); } return(items); }
public IList <FastDeliveryNomenclatureDistributionHistory> ConvertNomenclatureDistributionToDistributionHistory( IEnumerable <AdditionalLoadingNomenclatureDistribution> distributions, FastDeliveryAvailabilityHistory fastDeliveryAvailabilityHistory) { return(distributions.Select(x => new FastDeliveryNomenclatureDistributionHistory { Nomenclature = x.Nomenclature, Percent = x.Percent, FastDeliveryAvailabilityHistory = fastDeliveryAvailabilityHistory }) .ToList()); }
public IList <FastDeliveryOrderItemHistory> ConvertNomenclatureAmountNodesToOrderItemsHistory( IEnumerable <NomenclatureAmountNode> nomenclatureNodes, FastDeliveryAvailabilityHistory fastDeliveryAvailabilityHistory) { return(nomenclatureNodes .Select(x => new FastDeliveryOrderItemHistory { Nomenclature = x.Nomenclature ?? new Nomenclature { Id = x.NomenclatureId }, Count = x.Amount, FastDeliveryAvailabilityHistory = fastDeliveryAvailabilityHistory }) .ToList()); }
public void SaveFastDeliveryAvailabilityHistory(FastDeliveryAvailabilityHistory fastDeliveryAvailabilityHistory) { using (var uow = _unitOfWorkFactory.CreateWithoutRoot("SaveFastDeliveryAvailabilityHistory")) { try { uow.Save(fastDeliveryAvailabilityHistory); uow.Commit(); } catch (Exception e) { _logger.Error(e, "Не удалось сохранить историю проверки экспресс-доставки."); } } }
public FastDeliveryVerificationViewModel(FastDeliveryAvailabilityHistory fastDeliveryAvailabilityHistory) { var order = fastDeliveryAvailabilityHistory.Order; var deliveryPoint = fastDeliveryAvailabilityHistory.DeliveryPoint; DetailsTitle = $"Детализация по заказу №{order?.Id ?? 0}, адрес: {deliveryPoint?.ShortAddress}"; FastDeliveryAvailabilityHistory = fastDeliveryAvailabilityHistory ?? throw new ArgumentNullException(nameof(fastDeliveryAvailabilityHistory));; var fastDeliveryHistoryConverter = new FastDeliveryHistoryConverter(); Nodes = fastDeliveryHistoryConverter.ConvertAvailabilityHistoryItemsToVerificationDetailsNodes(fastDeliveryAvailabilityHistory.Items); Message = Nodes.Any(x => x.IsValidRLToFastDelivery) ? "Есть доступные водители для быстрой доставки" : "Нет доступных водителей для быстрой доставки"; if (fastDeliveryAvailabilityHistory.AdditionalInformation != null) { Message += string.Join("\n", fastDeliveryAvailabilityHistory.AdditionalInformation); } }
public FastDeliveryAvailabilityHistory GetRouteListsForFastDelivery( IUnitOfWork uow, double latitude, double longitude, bool isGetClosestByRoute, IDeliveryRulesParametersProvider deliveryRulesParametersProvider, IEnumerable <NomenclatureAmountNode> nomenclatureNodes, Order fastDeliveryOrder = null) { var maxDistanceToTrackPoint = deliveryRulesParametersProvider.MaxDistanceToLatestTrackPointKm; var driverGoodWeightLiftPerHand = deliveryRulesParametersProvider.DriverGoodWeightLiftPerHandInKg; var maxFastOrdersPerSpecificTime = deliveryRulesParametersProvider.MaxFastOrdersPerSpecificTime; var maxTimeForFastDeliveryTimespan = deliveryRulesParametersProvider.MaxTimeForFastDelivery; //Переводим всё в минуты var trackPointTimeOffset = (int)deliveryRulesParametersProvider.MaxTimeOffsetForLatestTrackPoint.TotalMinutes; var maxTimeForFastDelivery = (int)maxTimeForFastDeliveryTimespan.TotalMinutes; var minTimeForNewOrder = (int)deliveryRulesParametersProvider.MinTimeForNewFastDeliveryOrder.TotalMinutes; var driverUnloadTime = (int)deliveryRulesParametersProvider.DriverUnloadTime.TotalMinutes; var specificTimeForFastOrdersCount = (int)deliveryRulesParametersProvider.SpecificTimeForMaxFastOrdersCount.TotalMinutes; var fastDeliveryAvailabilityHistory = new FastDeliveryAvailabilityHistory { IsGetClosestByRoute = isGetClosestByRoute, Order = fastDeliveryOrder, MaxDistanceToLatestTrackPointKm = maxDistanceToTrackPoint, DriverGoodWeightLiftPerHandInKg = driverGoodWeightLiftPerHand, MaxFastOrdersPerSpecificTime = maxFastOrdersPerSpecificTime, MaxTimeForFastDelivery = maxTimeForFastDeliveryTimespan, MinTimeForNewFastDeliveryOrder = deliveryRulesParametersProvider.MinTimeForNewFastDeliveryOrder, DriverUnloadTime = deliveryRulesParametersProvider.DriverUnloadTime, SpecificTimeForMaxFastOrdersCount = deliveryRulesParametersProvider.SpecificTimeForMaxFastOrdersCount, }; var order = fastDeliveryAvailabilityHistory.Order; if (order != null) { fastDeliveryAvailabilityHistory.Order = order.Id == 0 ? null : order; fastDeliveryAvailabilityHistory.Author = order.Author; fastDeliveryAvailabilityHistory.DeliveryPoint = order.DeliveryPoint; fastDeliveryAvailabilityHistory.District = order.DeliveryPoint.District; fastDeliveryAvailabilityHistory.Counterparty = order.Client; } var fastDeliveryHistoryConverter = new FastDeliveryHistoryConverter(); if (nomenclatureNodes != null) { fastDeliveryAvailabilityHistory.OrderItemsHistory = fastDeliveryHistoryConverter.ConvertNomenclatureAmountNodesToOrderItemsHistory(nomenclatureNodes, fastDeliveryAvailabilityHistory); } var distributions = uow.GetAll <AdditionalLoadingNomenclatureDistribution>(); fastDeliveryAvailabilityHistory.NomenclatureDistributionHistoryItems = fastDeliveryHistoryConverter.ConvertNomenclatureDistributionToDistributionHistory(distributions, fastDeliveryAvailabilityHistory); var district = GetDistrict(uow, (decimal)latitude, (decimal)longitude); if (district?.TariffZone == null || !district.TariffZone.IsFastDeliveryAvailableAtCurrentTime) { fastDeliveryAvailabilityHistory.AdditionalInformation = new List <string> { "Не найден район, у района отсутствует тарифная зона, либо недоступна экспресс-доставка в текущее время." }; return(fastDeliveryAvailabilityHistory); } var neededNomenclatures = nomenclatureNodes.ToDictionary(x => x.NomenclatureId, x => x.Amount); Track t = null; TrackPoint tp = null; RouteList rl = null; TrackPoint tpInner = null; FastDeliveryVerificationDetailsNode result = null; Employee e = null; RouteListItem rla = null; RouteListItem rlaTransfered = null; Order o = null; OrderItem oi = null; OrderEquipment oe = null; CarLoadDocument scld = null; CarLoadDocumentItem scldi = null; CountUnclosedFastDeliveryAddressesNode countUnclosedFastDeliveryAddressesAlias = null; RouteListNomenclatureAmount ordersAmountAlias = null; RouteListNomenclatureAmount loadDocumentsAmountAlias = null; var lastTimeTrackQuery = QueryOver.Of(() => tpInner) .Where(() => tpInner.Track.Id == t.Id) .Select(Projections.Max(() => tpInner.TimeStamp)); //МЛ только в пути и с погруженным запасом var routeListNodes = uow.Session.QueryOver(() => rl) .JoinEntityAlias(() => t, () => t.RouteList.Id == rl.Id) .Inner.JoinAlias(() => t.TrackPoints, () => tp) .Inner.JoinAlias(() => rl.Driver, () => e) .WithSubquery.WhereProperty(() => tp.TimeStamp).Eq(lastTimeTrackQuery) .And(() => rl.Status == RouteListStatus.EnRoute) .And(() => rl.AdditionalLoadingDocument.Id != null) // только с погруженным запасом .SelectList(list => list .Select(() => tp.TimeStamp).WithAlias(() => result.TimeStamp) .Select(() => tp.Latitude).WithAlias(() => result.Latitude) .Select(() => tp.Longitude).WithAlias(() => result.Longitude) .Select(Projections.Entity(() => rl)).WithAlias(() => result.RouteList)) .TransformUsing(Transformers.AliasToBean <FastDeliveryVerificationDetailsNode>()) .List <FastDeliveryVerificationDetailsNode>(); //Последняя координата в указанном радиусе foreach (var node in routeListNodes) { var distance = DistanceHelper.GetDistanceKm(node.Latitude, node.Longitude, latitude, longitude); var deliveryPoint = new PointOnEarth(latitude, longitude); var proposedRoute = OsrmClientFactory.Instance .GetRoute(new List <PointOnEarth> { new PointOnEarth(node.Latitude, node.Longitude), deliveryPoint }, false, GeometryOverview.False, _globalSettings.ExcludeToll)?.Routes? .FirstOrDefault(); node.DistanceByLineToClient.ParameterValue = (decimal)distance; node.DistanceByRoadToClient.ParameterValue = decimal.Round((decimal)(proposedRoute?.TotalDistance ?? int.MaxValue) / 1000, 2); if (distance < maxDistanceToTrackPoint) { node.DistanceByLineToClient.IsValidParameter = node.DistanceByRoadToClient.IsValidParameter = true; } else { node.DistanceByLineToClient.IsValidParameter = node.DistanceByRoadToClient.IsValidParameter = false; node.IsValidRLToFastDelivery = false; } //Выставляем время последней координаты var timeSpan = DateTime.Now - node.TimeStamp; node.LastCoordinateTime.ParameterValue = timeSpan.TotalHours > 838 ? new TimeSpan(838, 0, 0) : timeSpan; if (node.LastCoordinateTime.ParameterValue.TotalMinutes <= trackPointTimeOffset) { node.LastCoordinateTime.IsValidParameter = true; } else { node.LastCoordinateTime.IsValidParameter = false; node.IsValidRLToFastDelivery = false; } } routeListNodes = routeListNodes .OrderBy(x => isGetClosestByRoute ? x.DistanceByRoadToClient.ParameterValue : x.DistanceByLineToClient.ParameterValue) .ToList(); //Не более определённого кол-ва заказов с быстрой доставкой в определённый промежуток времени var addressCountSubquery = QueryOver.Of(() => rla) .Inner.JoinAlias(() => rla.Order, () => o) .Where(() => rla.RouteList.Id == rl.Id) .And(() => rla.Status == RouteListItemStatus.EnRoute) .And(() => o.IsFastDelivery) .And(Restrictions.GtProperty( Projections.Property(() => rla.CreationDate), Projections.SqlFunction( new SQLFunctionTemplate(NHibernateUtil.DateTime, $"TIMESTAMPADD(MINUTE, -{specificTimeForFastOrdersCount}, CURRENT_TIMESTAMP)"), NHibernateUtil.DateTime))) .Select(Projections.Count(() => rla.Id)); var routeListsWithCountUnclosedFastDeliveries = uow.Session.QueryOver(() => rl) .WhereRestrictionOn(() => rl.Id).IsInG(routeListNodes.Select(x => x.RouteList.Id)) .SelectList(list => list .Select(() => rl.Id).WithAlias(() => countUnclosedFastDeliveryAddressesAlias.RouteListId) .SelectSubQuery(addressCountSubquery).WithAlias(() => countUnclosedFastDeliveryAddressesAlias.UnclosedFastDeliveryAddresses)) .TransformUsing(Transformers.AliasToBean <CountUnclosedFastDeliveryAddressesNode>()) .List <CountUnclosedFastDeliveryAddressesNode>(); var rlsWithCountUnclosedFastDeliveries = routeListsWithCountUnclosedFastDeliveries.ToDictionary(x => x.RouteListId, x => x.UnclosedFastDeliveryAddresses); foreach (var node in routeListNodes) { var countUnclosedFastDeliveryAddresses = rlsWithCountUnclosedFastDeliveries[node.RouteList.Id]; node.UnClosedFastDeliveries.ParameterValue = countUnclosedFastDeliveryAddresses; if (countUnclosedFastDeliveryAddresses < maxFastOrdersPerSpecificTime) { node.UnClosedFastDeliveries.IsValidParameter = true; } else { node.UnClosedFastDeliveries.IsValidParameter = false; node.IsValidRLToFastDelivery = false; } } //Время доставки следующего (текущего) заказа позволяет взять быструю доставку foreach (var routeListNode in routeListNodes) { RouteListItem latestAddress = null; var orderedEnRouteAddresses = routeListNode.RouteList.Addresses .Where(x => x.Status == RouteListItemStatus.EnRoute).OrderBy(x => x.IndexInRoute).ToList(); var orderedCompletedAddresses = routeListNode.RouteList.Addresses .Where(x => x.Status == RouteListItemStatus.Completed).OrderBy(x => x.IndexInRoute).ToList(); var latestCompletedAddress = orderedCompletedAddresses.OrderByDescending(x => x.StatusLastUpdate).FirstOrDefault(); if (latestCompletedAddress != null) { latestAddress = orderedEnRouteAddresses.FirstOrDefault(x => x.IndexInRoute > latestCompletedAddress.IndexInRoute); } if (latestAddress == null) { latestAddress = orderedEnRouteAddresses.FirstOrDefault(); } if (latestAddress != null) { var neededTime1 = maxTimeForFastDelivery - latestAddress.Order.DeliveryPoint.MinutesToUnload; if (neededTime1 < minTimeForNewOrder) { routeListNode.RemainingTimeForShipmentNewOrder.ParameterValue = new TimeSpan(0, neededTime1, 0); routeListNode.RemainingTimeForShipmentNewOrder.IsValidParameter = false; routeListNode.IsValidRLToFastDelivery = false; continue; } var water19Count = latestAddress.Order.OrderItems .Where(x => x.Nomenclature.TareVolume == TareVolume.Vol19L && x.Nomenclature.Category == NomenclatureCategory.water) .Sum(x => x.Count); var orderItemsSummaryWeight = latestAddress.Order.OrderItems .Where(x => x.Nomenclature.TareVolume != TareVolume.Vol19L || x.Nomenclature.Category != NomenclatureCategory.water) .Sum(x => x.Nomenclature.Weight * x.Count); var orderEquipmentsSummaryWeight = latestAddress.Order.OrderEquipments .Where(x => x.Direction == Direction.Deliver) .Sum(x => x.Nomenclature.Weight * x.Count); var goodsSummaryWeight = orderItemsSummaryWeight + orderEquipmentsSummaryWeight; //Время выгрузки след. заказа: //(Суммарный вес прочих товаров / кол-во кг, которое водитель может унести в одной руке + кол-во 19л) / 2 руки * время выгрузки в 2 руках 2 бутылей или товара var unloadTime = (goodsSummaryWeight / driverGoodWeightLiftPerHand + water19Count) / 2 * driverUnloadTime; var neededTime2 = maxTimeForFastDelivery - (int)unloadTime; if (neededTime2 < minTimeForNewOrder) { routeListNode.RemainingTimeForShipmentNewOrder.ParameterValue = new TimeSpan(0, neededTime2, 0); routeListNode.RemainingTimeForShipmentNewOrder.IsValidParameter = false; routeListNode.IsValidRLToFastDelivery = false; } else { routeListNode.RemainingTimeForShipmentNewOrder.ParameterValue = new TimeSpan(0, neededTime2, 0); routeListNode.RemainingTimeForShipmentNewOrder.IsValidParameter = true; } } else { routeListNode.RemainingTimeForShipmentNewOrder.ParameterValue = maxTimeForFastDeliveryTimespan; routeListNode.RemainingTimeForShipmentNewOrder.IsValidParameter = true; } } var rlIds = routeListNodes.Select(x => x.RouteList.Id).ToArray(); //OrderItems var orderItemsToDeliver = uow.Session.QueryOver <RouteListItem>(() => rla) .Inner.JoinAlias(() => rla.Order, () => o) .Inner.JoinAlias(() => o.OrderItems, () => oi) .Left.JoinAlias(() => rla.TransferedTo, () => rlaTransfered) .WhereRestrictionOn(() => rla.RouteList.Id).IsIn(rlIds) .WhereRestrictionOn(() => oi.Nomenclature.Id).IsIn(neededNomenclatures.Keys) .Where(() => //не отменённые и не недовозы rla.Status != RouteListItemStatus.Canceled && rla.Status != RouteListItemStatus.Overdue // и не перенесённые к водителю; либо перенесённые с погрузкой; либо перенесённые и это экспресс-доставка (всегда без погрузки) && (!rla.WasTransfered || rla.NeedToReload || o.IsFastDelivery) // и не перенесённые от водителя; либо перенесённые и не нужна погрузка и не экспресс-доставка (остатки по экспресс-доставке не переносятся) && (rla.Status != RouteListItemStatus.Transfered || (!rlaTransfered.NeedToReload && !o.IsFastDelivery))) .SelectList(list => list .SelectGroup(() => rla.RouteList.Id).WithAlias(() => ordersAmountAlias.RouteListId) .SelectGroup(() => oi.Nomenclature.Id).WithAlias(() => ordersAmountAlias.NomenclatureId) .SelectSum(() => oi.Count).WithAlias(() => ordersAmountAlias.Amount)) .TransformUsing(Transformers.AliasToBean <RouteListNomenclatureAmount>()) .Future <RouteListNomenclatureAmount>(); //OrderEquipments var orderEquipmentsToDeliver = uow.Session.QueryOver <RouteListItem>(() => rla) .Inner.JoinAlias(() => rla.Order, () => o) .Inner.JoinAlias(() => o.OrderEquipments, () => oe) .Left.JoinAlias(() => rla.TransferedTo, () => rlaTransfered) .WhereRestrictionOn(() => rla.RouteList.Id).IsIn(rlIds) .WhereRestrictionOn(() => oe.Nomenclature.Id).IsIn(neededNomenclatures.Keys) .Where(() => //не отменённые и не недовозы rla.Status != RouteListItemStatus.Canceled && rla.Status != RouteListItemStatus.Overdue // и не перенесённые к водителю; либо перенесённые с погрузкой; либо перенесённые и это экспресс-доставка (всегда без погрузки) && (!rla.WasTransfered || rla.NeedToReload || o.IsFastDelivery) // и не перенесённые от водителя; либо перенесённые и не нужна погрузка и не экспресс-доставка (остатки по экспресс-доставке не переносятся) && (rla.Status != RouteListItemStatus.Transfered || (!rlaTransfered.NeedToReload && !o.IsFastDelivery))) .And(() => oe.Direction == Direction.Deliver) .SelectList(list => list .SelectGroup(() => rla.RouteList.Id).WithAlias(() => ordersAmountAlias.RouteListId) .SelectGroup(() => oe.Nomenclature.Id).WithAlias(() => ordersAmountAlias.NomenclatureId) .Select(Projections.Sum(Projections.Cast(NHibernateUtil.Decimal, Projections.Property(() => oe.Count))) ).WithAlias(() => ordersAmountAlias.Amount)) .TransformUsing(Transformers.AliasToBean <RouteListNomenclatureAmount>()) .Future <RouteListNomenclatureAmount>(); //CarLoadDocuments var allLoaded = uow.Session.QueryOver <CarLoadDocument>(() => scld) .Inner.JoinAlias(() => scld.Items, () => scldi) .WhereRestrictionOn(() => scld.RouteList.Id).IsIn(rlIds) .WhereRestrictionOn(() => scldi.Nomenclature.Id).IsIn(neededNomenclatures.Keys) .SelectList(list => list .SelectGroup(() => scld.RouteList.Id).WithAlias(() => loadDocumentsAmountAlias.RouteListId) .SelectGroup(() => scldi.Nomenclature.Id).WithAlias(() => loadDocumentsAmountAlias.NomenclatureId) .SelectSum(() => scldi.Amount).WithAlias(() => loadDocumentsAmountAlias.Amount)) .TransformUsing(Transformers.AliasToBean <RouteListNomenclatureAmount>()) .Future <RouteListNomenclatureAmount>(); var allToDeliver = orderItemsToDeliver .Union(orderEquipmentsToDeliver) .GroupBy(x => new { x.RouteListId, x.NomenclatureId }) .Select(group => new RouteListNomenclatureAmount { RouteListId = group.Key.RouteListId, NomenclatureId = group.Key.NomenclatureId, Amount = group.Sum(x => x.Amount) }) .ToList(); //Выбираем МЛ, в котором хватает запаса номенклатур на поступивший быстрый заказ foreach (var routeListNode in routeListNodes) { var toDeliverForRL = allToDeliver.Where(x => x.RouteListId == routeListNode.RouteList.Id).ToList(); var loadedForRL = allLoaded.Where(x => x.RouteListId == routeListNode.RouteList.Id).ToList(); foreach (var need in neededNomenclatures) { var toDeliver = toDeliverForRL.FirstOrDefault(x => x.NomenclatureId == need.Key)?.Amount ?? 0; var loaded = loadedForRL.FirstOrDefault(x => x.NomenclatureId == need.Key)?.Amount ?? 0; var onBoard = loaded - toDeliver; if (onBoard < need.Value) { routeListNode.IsGoodsEnough.ParameterValue = false; routeListNode.IsGoodsEnough.IsValidParameter = false; routeListNode.IsValidRLToFastDelivery = false; break; } } } if (routeListNodes != null) { fastDeliveryAvailabilityHistory.Items = fastDeliveryHistoryConverter .ConvertVerificationDetailsNodesToAvailabilityHistoryItems(routeListNodes, fastDeliveryAvailabilityHistory); } return(fastDeliveryAvailabilityHistory); }
public FastDeliveryVerificationViewModel(FastDeliveryAvailabilityHistory fastDeliveryAvailabilityHistory, IUnitOfWork uow, Employee logistician) : this(fastDeliveryAvailabilityHistory) { _uow = uow ?? throw new ArgumentNullException(nameof(uow)); _logistician = logistician ?? throw new ArgumentNullException(nameof(logistician)); }