Пример #1
0
        /// <summary>
        /// Выводит вопрос "Перед печатью {0}, необходимо сохранить изменения в {1}. Сохранить?"
        /// Диалог использует название сущьности в предложном падеже (Prepositional)
        /// </summary>
        public bool SaveBeforePrint(Type savingEntity, string whatPrint)
        {
            string savingName = "НЕ УКАЗАНО";

            var att = savingEntity.GetCustomAttributes(typeof(AppellativeAttribute), true);

            if (att.Length > 0)
            {
                if (!String.IsNullOrWhiteSpace((att[0] as AppellativeAttribute).Prepositional))
                {
                    savingName = (att[0] as AppellativeAttribute).Prepositional;
                }
                else
                {
                    savingName = (att[0] as AppellativeAttribute).Nominative;
                }
            }

            string message = String.Format("Перед печатью {0}, необходимо сохранить изменения в {1}. Сохранить?",
                                           whatPrint,
                                           savingName
                                           );

            return(interactive.Question(message));
        }
Пример #2
0
        void ExistingCounterparty_PageClosed(object sender, QS.Project.Journal.JournalSelectedNodesEventArgs e)
        {
            var          counterpartyNode = e.SelectedNodes.First() as CounterpartyJournalNode;
            Counterparty client           = _uow.GetById <Counterparty>(counterpartyNode.Id);

            if (!CounterpartyOrdersViewModels.Any(c => c.Client.Id == client.Id))
            {
                if (_interactiveService.Question($"Добавить телефон к контрагенту {client.Name} ?", "Телефон контрагента"))
                {
                    client.Phones.Add(ActiveCall.Phone);
                    _uow.Save <Counterparty>(client);
                    _uow.Commit();
                }

                CounterpartyOrderViewModel model =
                    new CounterpartyOrderViewModel(
                        client, UnitOfWorkFactory.GetDefaultFactory, _tdiNavigation, _routedListRepository, MangoManager,
                        _orderParametersProvider, _employeeJournalFactory, _counterpartyJournalFactory, _nomenclatureRepository,
                        _parametersProvider, _deliveryRulesParametersProvider);

                CounterpartyOrdersViewModels.Add(model);
                currentCounterparty = client;
                MangoManager.AddCounterpartyToCall(client.Id);
                CounterpartyOrdersModelsUpdateEvent();
            }
        }
Пример #3
0
        public bool CreateLogin(string login, string password)
        {
            logger.Info("Создание учетной записи на сервере...");

            try {
                //Проверка существует ли логин
                string sql = "SELECT COUNT(*) FROM users WHERE login = @login";
                mysqlProvider.CheckConnectionAlive();
                MySqlCommand cmd = new MySqlCommand(sql, mysqlProvider.DbConnection);
                cmd.Parameters.AddWithValue("@login", login);
                if (Convert.ToInt32(cmd.ExecuteScalar()) > 0)
                {
                    string message = "Пользователь с логином " + login + " уже существует в базе. " +
                                     "Создание второго пользователя с таким же логином невозможно.";
                    interactiveService.ShowMessage(Dialog.ImportanceLevel.Error, message, "Создание учетной записи на сервере");
                    return(false);
                }

                sql = "SELECT COUNT(*) from mysql.user WHERE USER = @login";
                cmd = new MySqlCommand(sql, mysqlProvider.DbConnection);
                cmd.Parameters.AddWithValue("@login", login);
                try {
                    if (Convert.ToInt32(cmd.ExecuteScalar()) > 0)
                    {
                        string message = "Пользователь с логином " + login + " уже существует на сервере. " +
                                         "Если он использовался для доступа к другим базам, может возникнуть путаница. " +
                                         "Использовать этот логин?";
                        bool result = interactiveService.Question(message, "Создание учетной записи на сервере");
                        return(result);
                    }
                } catch (MySqlException ex) {
                    if (ex.Number == 1045)
                    {
                        logger.Warn(ex, "Нет доступа к таблице пользователей, пробую создать пользователя в слепую.");
                    }
                    else
                    {
                        return(false);
                    }
                }
                //Создание пользователя.
                sql = "CREATE USER @login IDENTIFIED BY @password";
                cmd = new MySqlCommand(sql, mysqlProvider.DbConnection);
                cmd.Parameters.AddWithValue("@login", login);
                cmd.Parameters.AddWithValue("@password", password);
                cmd.ExecuteNonQuery();
                cmd.CommandText = "CREATE USER @login @'localhost' IDENTIFIED BY @password";
                cmd.ExecuteNonQuery();

                logger.Info("Ok");
                return(true);
            } catch (Exception ex) {
                logger.Error(ex, "Ошибка создания пользователя!");
                throw ex;
            }
        }
        void ExistingCounterparty_PageClosed(object sender, QS.Project.Journal.JournalSelectedNodesEventArgs e)
        {
            var          counterpartyNode = e.SelectedNodes.First() as CounterpartyJournalNode;
            Counterparty client           = UoW.GetById <Counterparty>(counterpartyNode.Id);

            if (!CounterpartyOrdersModels.Any(c => c.Client.Id == client.Id))
            {
                if (interactiveService.Question($"Добавить телефон к контрагенту {client.Name} ?", "Телефон контрагента"))
                {
                    client.Phones.Add(ActiveCall.Phone);
                    UoW.Save <Counterparty>(client);
                    UoW.Commit();
                }
                CounterpartyOrderViewModel model = new CounterpartyOrderViewModel(client, UnitOfWorkFactory.GetDefaultFactory, tdiNavigation, routedListRepository, this.MangoManager);
                CounterpartyOrdersModels.Add(model);
                currentCounterparty = client;
                MangoManager.AddCounterpartyToCall(client.Id);
                CounterpartyOrdersModelsUpdateEvent();
            }
        }
Пример #5
0
        public void SetIssueDateManual(EmployeeCardItem row)
        {
            var operations = employeeIssueRepository.GetOperationsForEmployee(UoW, Entity, row.ProtectionTools).OrderByDescending(x => x.OperationTime).ToList();
            IPage <ManualEmployeeIssueOperationViewModel> page;

            if (!operations.Any() || operations.First().ExpiryByNorm < DateTime.Today)
            {
                page = navigation.OpenViewModel <ManualEmployeeIssueOperationViewModel, IEntityUoWBuilder, EmployeeCardItem>(employeeViewModel, EntityUoWBuilder.ForCreate(), row, OpenPageOptions.AsSlave);
            }
            else if (operations.First().OverrideBefore)
            {
                page = navigation.OpenViewModel <ManualEmployeeIssueOperationViewModel, IEntityUoWBuilder, EmployeeCardItem>(employeeViewModel, EntityUoWBuilder.ForOpen(operations.First().Id), row, OpenPageOptions.AsSlave);
            }
            else if (interactive.Question($"Для «{row.ProtectionTools.Name}» уже выполнялись полноценные выдачи, внесение ручных изменений может привести к нежелательным результатам. Продолжить?"))
            {
                page = navigation.OpenViewModel <ManualEmployeeIssueOperationViewModel, IEntityUoWBuilder, EmployeeCardItem>(employeeViewModel, EntityUoWBuilder.ForCreate(), row, OpenPageOptions.AsSlave);
            }
            else
            {
                return;
            }
            page.PageClosed += SetIssueDateManual_PageClosed;
        }
Пример #6
0
        // <summary>
        // Метод создаем маршруты на день основываясь на данных всесенных в поля <c>Routes</c>, <c>Orders</c>,
        // <c>Drivers</c> и <c>Forwarders</c>.
        // </summary>
        public void CreateRoutes(TimeSpan drvStartTime, TimeSpan drvEndTime)
        {
            WarningMessages.Clear();
            ProposedRoutes.Clear();             //Очищаем сразу, так как можем выйти из метода ранее.

            logger.Info("Подготавливаем заказы...");
            PerformanceHelper.StartMeasurement($"Строим оптимальные маршруты");

            // Создаем список поездок всех водителей. Тут перебираем всех водителей с машинами
            // и создаем поездки для них, в зависимости от выбранного режима работы.
            var trips = Drivers.Where(x => x.Car != null)
                        .OrderBy(x => x.PriorityAtDay)
                        .SelectMany(drv => drv.DaySchedule != null
                                                                                                ? drv.DaySchedule.Shifts.Where(s => s.StartTime >= drvStartTime && s.StartTime < drvEndTime)
                                    .Select(shift => new PossibleTrip(drv, shift))
                                                                                                                                                : new[] { new PossibleTrip(drv, null) }
                                    )
                        .ToList();

            // Стыкуем уже созданные маршрутные листы с возможными поездками, на основании водителя и смены.
            // Если уже созданный маршрут не найден в поездках, то создаем поездку для него.
            foreach (var existRoute in Routes)
            {
                var trip = trips.FirstOrDefault(x => x.Driver == existRoute.Driver && x.Shift == existRoute.Shift);
                if (trip != null)
                {
                    trip.OldRoute = existRoute;
                }
                else
                {
                    trips.Add(new PossibleTrip(existRoute));
                }
                //Проверяем все ли заказы из МЛ присутствуют в списке заказов. Если их нет. Добавляем.
                foreach (var address in existRoute.Addresses)
                {
                    if (Orders.All(x => x.Id != address.Order.Id))
                    {
                        Orders.Add(address.Order);
                    }
                }
            }

            var possibleRoutes = trips.ToArray();

            if (!possibleRoutes.Any())
            {
                AddWarning("Для построения маршрутов, нет водителей.");
                return;
            }

            TestCars(possibleRoutes);

            var                    areas            = UoW.GetAll <District>().Where(x => x.DistrictsSet.Status == DistrictsSetStatus.Active).ToList();
            List <District>        unusedDistricts  = new List <District>();
            List <CalculatedOrder> calculatedOrders = new List <CalculatedOrder>();

            // Перебираем все заказы, исключаем те которые без координат, определяем для каждого заказа район
            // на основании координат. И создавая экземпляр <c>CalculatedOrder</c>, происходит подсчет сумарной
            // информации о заказе. Всего бутылей, вес и прочее.
            foreach (var order in Orders)
            {
                if (order.DeliveryPoint.Longitude == null || order.DeliveryPoint.Latitude == null)
                {
                    continue;
                }
                var point = new Point((double)order.DeliveryPoint.Latitude.Value, (double)order.DeliveryPoint.Longitude.Value);
                var area  = areas.Find(x => x.DistrictBorder.Contains(point));
                if (area != null)
                {
                    var oldRoute = Routes.FirstOrDefault(r => r.Addresses.Any(a => a.Order.Id == order.Id));
                    if (oldRoute != null)
                    {
                        calculatedOrders.Add(new CalculatedOrder(order, area, false, oldRoute));
                    }
                    else if (possibleRoutes.SelectMany(x => x.Districts).Any(x => x.District.Id == area.Id))
                    {
                        var cOrder = new CalculatedOrder(order, area);
                        //if(possibleRoutes.Any(r => r.GeographicGroup.Id == cOrder.ShippingBase.Id))//убрать, если в автоформировании должны учавствовать заказы из всех частей города вне зависимости от того какие части города выбраны в диалоге
                        calculatedOrders.Add(cOrder);
                    }
                    else if (!unusedDistricts.Contains(area))
                    {
                        unusedDistricts.Add(area);
                    }
                }
            }
            Nodes = calculatedOrders.ToArray();
            if (unusedDistricts.Any())
            {
                AddWarning("Районы без водителей: {0}", string.Join(", ", unusedDistricts.Select(x => x.DistrictName)));
            }

            // Создаем калькулятор расчета расстояний. Он сразу запрашивает уже имеющиеся расстояния из кеша
            // и в фоновом режиме начинает считать недостающую матрицу.
            distanceCalculator = new ExtDistanceCalculator(DistanceProvider.Osrm, Nodes.Select(x => x.Order.DeliveryPoint).ToArray(), StatisticsTxtAction);

            logger.Info("Развозка по {0} районам.", calculatedOrders.Select(x => x.District).Distinct().Count());
            PerformanceHelper.AddTimePoint(logger, $"Подготовка заказов");

            // Пред запуском оптимизации мы должны создать модель и внести в нее все необходимые данные.
            logger.Info("Создаем модель...");
            RoutingModel routing = new RoutingModel(Nodes.Length + 1, possibleRoutes.Length, 0);

            // Создаем измерение со временем на маршруте.
            // <c>horizon</c> - ограничивает максимально допустимое значение диапазона, чтобы не уйти за границы суток;
            // <c>maxWaitTime</c> - Максимальное время ожидания водителя. То есть водитель закончил разгрузку следующий
            // адрес в маршруте у него не должен быть позже чем на 4 часа ожидания.
            int horizon        = 24 * 3600;
            int maxWaitTime    = 4 * 3600;
            var timeEvaluators = possibleRoutes.Select(x => new CallbackTime(Nodes, x, distanceCalculator)).ToArray();

            routing.AddDimensionWithVehicleTransits(timeEvaluators, maxWaitTime, horizon, false, "Time");
            var time_dimension = routing.GetDimensionOrDie("Time");

            // Ниже заполняем все измерения для учета бутылей, веса, адресов, объема.
            var bottlesCapacity = possibleRoutes.Select(x => (long)x.Car.MaxBottles).ToArray();

            routing.AddDimensionWithVehicleCapacity(new CallbackBottles(Nodes), 0, bottlesCapacity, true, "Bottles");

            var weightCapacity = possibleRoutes.Select(x => (long)x.Car.MaxWeight).ToArray();

            routing.AddDimensionWithVehicleCapacity(new CallbackWeight(Nodes), 0, weightCapacity, true, "Weight");

            var volumeCapacity = possibleRoutes.Select(x => (long)(x.Car.MaxVolume * 1000)).ToArray();

            routing.AddDimensionWithVehicleCapacity(new CallbackVolume(Nodes), 0, volumeCapacity, true, "Volume");

            var addressCapacity = possibleRoutes.Select(x => (long)(x.Driver.MaxRouteAddresses)).ToArray();

            routing.AddDimensionWithVehicleCapacity(new CallbackAddressCount(Nodes.Length), 0, addressCapacity, true, "AddressCount");

            var bottlesDimension = routing.GetDimensionOrDie("Bottles");
            var addressDimension = routing.GetDimensionOrDie("AddressCount");

            for (int ix = 0; ix < possibleRoutes.Length; ix++)
            {
                // Устанавливаем функцию получения стоимости маршрута.
                routing.SetArcCostEvaluatorOfVehicle(new CallbackDistanceDistrict(Nodes, possibleRoutes[ix], distanceCalculator), ix);

                // Добавляем фиксированный штраф за принадлежность водителя.
                if (possibleRoutes[ix].Driver.DriverType.HasValue)
                {
                    routing.SetFixedCostOfVehicle(((int)possibleRoutes[ix].Driver.DriverType) * DriverPriorityPenalty, ix);
                }
                else
                {
                    routing.SetFixedCostOfVehicle(DriverPriorityPenalty * 3, ix);
                }

                var cumulTimeOnEnd   = routing.CumulVar(routing.End(ix), "Time");
                var cumulTimeOnBegin = routing.CumulVar(routing.Start(ix), "Time");

                // Устанавливаем минимальные(мягкие) границы для измерений. При значениях меньше минимальных, маршрут все таки принимается,
                // но вносятся некоторые штрафные очки на последнюю точку маршрута.
                //bottlesDimension.SetEndCumulVarSoftLowerBound(ix, possibleRoutes[ix].Car.MinBottles, MinBottlesInRoutePenalty);
                //addressDimension.SetEndCumulVarSoftLowerBound(ix, possibleRoutes[ix].Driver.MinRouteAddresses, MinAddressesInRoutePenalty);

                // Устанавливаем диапазон времени для движения по маршруту в зависимости от выбраной смены,
                // день, вечер и с учетом досрочного завершения водителем работы.
                if (possibleRoutes[ix].Shift != null)
                {
                    var shift   = possibleRoutes[ix].Shift;
                    var endTime = possibleRoutes[ix].EarlyEnd.HasValue
                                                                                                        ? Math.Min(shift.EndTime.TotalSeconds, possibleRoutes[ix].EarlyEnd.Value.TotalSeconds)
                                                                                                        : shift.EndTime.TotalSeconds;
                    cumulTimeOnEnd.SetMax((long)endTime);
                    cumulTimeOnBegin.SetMin((long)shift.StartTime.TotalSeconds);
                }
                else if (possibleRoutes[ix].EarlyEnd.HasValue)                //Устанавливаем время окончания рабочего дня у водителя.
                {
                    cumulTimeOnEnd.SetMax((long)possibleRoutes[ix].EarlyEnd.Value.TotalSeconds);
                }
            }

            for (int ix = 0; ix < Nodes.Length; ix++)
            {
                // Проставляем на каждый адрес окно времени приезда.
                var startWindow = Nodes[ix].Order.DeliverySchedule.From.TotalSeconds;
                var endWindow   = Nodes[ix].Order.DeliverySchedule.To.TotalSeconds - Nodes[ix].Order.CalculateTimeOnPoint(false);               //FIXME Внимание здесь задаем время без экспедитора и без учета скорости водителя. Это не правильно, но другого варианта я придумать не смог.
                if (endWindow < startWindow)
                {
                    AddWarning("Время разгрузки на {2}, не помещается в диапазон времени доставки. {0}-{1}", Nodes[ix].Order.DeliverySchedule.From, Nodes[ix].Order.DeliverySchedule.To, Nodes[ix].Order.DeliveryPoint.ShortAddress);
                    endWindow = startWindow;
                }
                time_dimension.CumulVar(ix + 1).SetRange((long)startWindow, (long)endWindow);
                // Добавляем абсолютно все заказы в дизюкцию. Если бы заказы небыли вдобавлены в отдельные дизьюкции
                // то при не возможность доставить хоть один заказ. Все решение бы считаль не верным. Добавление каждого заказа
                // в отдельную дизьюкцию, позволяет механизму не вести какой то и заказов, и все таки формировать решение с недовезенными
                // заказами. Дизьюкция работает так. Он говорит, если хотя бы один заказ в этой группе(дизьюкции) доставлен,
                // то все хорошо, иначе штраф. Так как у нас в кадой дизьюкции по одному заказу. Мы получаем опциональную доставку каждого заказа.
                routing.AddDisjunction(new int[] { ix + 1 }, MaxDistanceAddressPenalty);
            }

            logger.Debug("Nodes.Length = {0}", Nodes.Length);
            logger.Debug("routing.Nodes() = {0}", routing.Nodes());
            logger.Debug("GetNumberOfDisjunctions = {0}", routing.GetNumberOfDisjunctions());

            RoutingSearchParameters search_parameters = RoutingModel.DefaultSearchParameters();

            // Setting first solution heuristic (cheapest addition).
            // Указывается стратегия первоначального заполнения. Опытным путем было вычислено, что именно при
            // стратегиях вставки маршруты получаются с набором точек более близких к друг другу. То есть в большей
            // степени облачком. Что воспринималось человеком как более отпимальное. В отличии от большенства других
            // стратегий в которых маршруты, формируюся скорее по лентами ведущими через все обезжаемые раоны. То есть водители
            // чаще имели пересечения маршутов.
            search_parameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.ParallelCheapestInsertion;

            search_parameters.TimeLimitMs = MaxTimeSeconds * 1000;
            // Отключаем внутреннего кеширования расчитанных значений. Опытным путем было проверено, что включение этого значения.
            // Значительно(на несколько секунд) увеличивает время закрытия модели и сокращает иногда не значительно время расчета оптимизаций.
            // И в принцепе становится целесообразно только на количествах заказов 300-400. При количестве заказов менее 200
            // влючение отпечатков значений. Не уменьшало, а увеличивало общее время расчета. А при большом количестве заказов
            // время расчета уменьшалось не значительно.
            //search_parameters.FingerprintArcCostEvaluators = false;

            search_parameters.FingerprintArcCostEvaluators = true;

            //search_parameters.OptimizationStep = 100;

            var solver = routing.solver();

            PerformanceHelper.AddTimePoint(logger, $"Настроили оптимизацию");
            logger.Info("Закрываем модель...");

            if (
                WarningMessages.Any() && !interactiveService.Question(
                    string.Join("\n", WarningMessages.Select(x => "⚠ " + x)),
                    "При построении транспортной модели обнаружены следующие проблемы:\n{0}\nПродолжить?"
                    )
                )
            {
                distanceCalculator.Canceled = true;
                distanceCalculator.Dispose();
                return;
            }

            logger.Info("Рассчет расстояний между точками...");
            routing.CloseModelWithParameters(search_parameters);
#if DEBUG
            PrintMatrixCount(distanceCalculator.matrixcount);
#endif
            //Записывем возможно не схраненый кеш в базу.
            distanceCalculator.FlushCache();
            //Попытка хоть как то ослеживать что происходит в момент построения. Возможно не очень правильная.
            //Пришлось создавать 2 монитора.
            var lastSolution = solver.MakeLastSolutionCollector();
            lastSolution.AddObjective(routing.CostVar());
            routing.AddSearchMonitor(lastSolution);
            routing.AddSearchMonitor(new CallbackMonitor(solver, StatisticsTxtAction, lastSolution));

            PerformanceHelper.AddTimePoint(logger, $"Закрыли модель");
            logger.Info("Поиск решения...");

            Assignment solution = routing.SolveWithParameters(search_parameters);
            PerformanceHelper.AddTimePoint(logger, $"Получили решение.");
            logger.Info("Готово. Заполняем.");
#if DEBUG
            PrintMatrixCount(distanceCalculator.matrixcount);
#endif
            Console.WriteLine("Status = {0}", routing.Status());
            if (solution != null)
            {
                // Solution cost.
                Console.WriteLine("Cost = {0}", solution.ObjectiveValue());
                time_dimension = routing.GetDimensionOrDie("Time");

                //Читаем полученные маршруты.
                for (int route_number = 0; route_number < routing.Vehicles(); route_number++)
                {
                    var  route       = new ProposedRoute(possibleRoutes[route_number]);
                    long first_node  = routing.Start(route_number);
                    long second_node = solution.Value(routing.NextVar(first_node));                     // Пропускаем первый узел, так как это наша база.
                    route.RouteCost = routing.GetCost(first_node, second_node, route_number);

                    while (!routing.IsEnd(second_node))
                    {
                        var time_var = time_dimension.CumulVar(second_node);
                        var rPoint   = new ProposedRoutePoint(
                            TimeSpan.FromSeconds(solution.Min(time_var)),
                            TimeSpan.FromSeconds(solution.Max(time_var)),
                            Nodes[second_node - 1].Order
                            );
                        rPoint.DebugMaxMin = string.Format("\n({0},{1})[{3}-{4}]-{2} Cost:{5}",
                                                           new DateTime().AddSeconds(solution.Min(time_var)).ToShortTimeString(),
                                                           new DateTime().AddSeconds(solution.Max(time_var)).ToShortTimeString(),
                                                           second_node,
                                                           rPoint.Order.DeliverySchedule.From.ToString("hh\\:mm"),
                                                           rPoint.Order.DeliverySchedule.To.ToString("hh\\:mm"),
                                                           routing.GetCost(first_node, second_node, route_number)
                                                           );
                        route.Orders.Add(rPoint);

                        first_node       = second_node;
                        second_node      = solution.Value(routing.NextVar(first_node));
                        route.RouteCost += routing.GetCost(first_node, second_node, route_number);
                    }

                    if (route.Orders.Count > 0)
                    {
                        ProposedRoutes.Add(route);
                        logger.Debug("Маршрут {0}: {1}",
                                     route.Trip.Driver.ShortName,
                                     string.Join(" -> ", route.Orders.Select(x => x.DebugMaxMin))
                                     );
                    }
                    else
                    {
                        logger.Debug("Маршрут {0}: пустой", route.Trip.Driver.ShortName);
                    }
                }
            }

#if DEBUG
            logger.Debug("SGoToBase:{0}", string.Join(", ", CallbackDistanceDistrict.SGoToBase.Select(x => $"{x.Key.Driver.ShortName}={x.Value}")));
            logger.Debug("SFromExistPenality:{0}", string.Join(", ", CallbackDistanceDistrict.SFromExistPenality.Select(x => $"{x.Key.Driver.ShortName}={x.Value}")));
            logger.Debug("SUnlikeDistrictPenality:{0}", string.Join(", ", CallbackDistanceDistrict.SUnlikeDistrictPenality.Select(x => $"{x.Key.Driver.ShortName}={x.Value}")));
            logger.Debug("SLargusPenality:{0}", string.Join(", ", CallbackDistanceDistrict.SLargusPenality.Select(x => $"{x.Key.Driver.ShortName}={x.Value}")));
#endif

            if (ProposedRoutes.Count > 0)
            {
                logger.Info($"Предложено {ProposedRoutes.Count} маршрутов.");
            }
            PerformanceHelper.Main.PrintAllPoints(logger);

            if (distanceCalculator.ErrorWays.Any())
            {
                logger.Debug("Ошибок получения расстояний {0}", distanceCalculator.ErrorWays.Count);
                var uniqueFrom = distanceCalculator.ErrorWays.Select(x => x.FromHash).Distinct().ToList();
                var uniqueTo   = distanceCalculator.ErrorWays.Select(x => x.ToHash).Distinct().ToList();
                logger.Debug("Уникальных точек: отправки = {0}, прибытия = {1}", uniqueFrom.Count, uniqueTo.Count);
                logger.Debug("Проблемные точки отправки:\n{0}",
                             string.Join("; ", distanceCalculator.ErrorWays
                                         .GroupBy(x => x.FromHash)
                                         .Where(x => x.Count() > (uniqueTo.Count / 2))
                                         .Select(x => CachedDistance.GetText(x.Key)))
                             );
                logger.Debug("Проблемные точки прибытия:\n{0}",
                             string.Join("; ", distanceCalculator.ErrorWays
                                         .GroupBy(x => x.ToHash)
                                         .Where(x => x.Count() > (uniqueFrom.Count / 2))
                                         .Select(x => CachedDistance.GetText(x.Key)))
                             );
            }
        }
Пример #7
0
 public bool NeedDropDatabaseIfExists(string dbname)
 {
     return(interactive.Question($"База с именем `{dbname}` уже существует на сервере. Удалить существующую базу перед соданием новой?"));
 }
        private void OnSendSmsButtonPressed(object btn, EventArgs args)
        {
            if (_order.Id == 0)
            {
                _interactiveService.ShowMessage(ImportanceLevel.Error, "Перед отправкой SMS необходимо сохранить заказ",
                                                "Не удалось отправить SMS");
                return;
            }
            if (string.IsNullOrWhiteSpace(validatedPhoneEntry.Text))
            {
                _interactiveService.ShowMessage(ImportanceLevel.Error, "Вы забыли выбрать номер.", "Не удалось отправить SMS");
                return;
            }

            var alreadySentSms = _smsPaymentRepository.GetSmsPaymentsForOrder(
                InfoProvider.UoW,
                _order.Id,
                _excludeSmsPaymentStatuses
                );

            var paidSmsPayments    = alreadySentSms.Where(x => x.SmsPaymentStatus == SmsPaymentStatus.Paid).ToList();
            var waitingSmsPayments = alreadySentSms.Where(x =>
                                                          x.SmsPaymentStatus == SmsPaymentStatus.WaitingForPayment &&
                                                          DateTime.Now.Subtract(x.CreationDate).TotalMinutes < 60
                                                          ).ToList();

            if (paidSmsPayments.Any())
            {
                var paidStringBuilder = new StringBuilder();

                foreach (var payment in paidSmsPayments)
                {
                    paidStringBuilder.AppendLine($"\tКод платежа:     {payment.Id}");
                    paidStringBuilder.AppendLine($"\tТелефон:            +7 {_phoneFormatter.FormatString(payment.PhoneNumber)}");
                    paidStringBuilder.AppendLine($"\tДата создания:  {payment.CreationDate}");
                    paidStringBuilder.AppendLine($"\tДата оплаты:     {payment.PaidDate}");
                    paidStringBuilder.AppendLine($"\tНомер оплаты:  {payment.ExternalId}");
                    paidStringBuilder.AppendLine();
                }

                var sendPayment = _interactiveService.Question("Для заказа уже есть ранее оплаченные платежи по SMS:\n\n" +
                                                               $"{paidStringBuilder}" +
                                                               "Вы уверены что хотите отправить ещё одну SMS?");

                if (!sendPayment)
                {
                    return;
                }
            }
            else if (waitingSmsPayments.Any())
            {
                var waitingStringBuilder = new StringBuilder();

                foreach (var payment in waitingSmsPayments)
                {
                    waitingStringBuilder.AppendLine($"\tКод платежа:     {payment.Id}");
                    waitingStringBuilder.AppendLine($"\tТелефон:            +7 {_phoneFormatter.FormatString(payment.PhoneNumber)}");
                    waitingStringBuilder.AppendLine($"\tДата создания:  {payment.CreationDate}");
                    waitingStringBuilder.AppendLine();
                }

                var sendPayment = _interactiveService.Question("Для заказа найдены SMS, ожидающие оплату клиента:\n\n" +
                                                               $"{waitingStringBuilder}" +
                                                               "Вы уверены что хотите отправить ещё одну SMS?");

                if (!sendPayment)
                {
                    return;
                }
            }

            btnSendFastPaymentPayByQrUrlBySms.Sensitive = btnSendFastPaymentPayByCardUrlBySms.Sensitive = ySendSmsButton.Sensitive = false;
            GLib.Timeout.Add(10000, () =>
            {
                btnSendFastPaymentPayByQrUrlBySms.Sensitive   = true;
                btnSendFastPaymentPayByCardUrlBySms.Sensitive = _canSendSmsForPayFromSbpByCard;
                ySendSmsButton.Sensitive = _canSendSmsForPayFromYookassa;

                return(false);
            });

            var           smsSender = new SmsPaymentSender();
            PaymentResult result;

            try
            {
                result = smsSender.SendSmsPaymentToNumber(_order.Id, validatedPhoneEntry.Text);
            }
            catch (TimeoutException)
            {
                _interactiveService.ShowMessage(ImportanceLevel.Warning,
                                                "Превышено время ожидания ответа от сервиса оплаты по SMS.\nЕсли SMS не была отправлена, произведите повторную отправку");
                return;
            }
            switch (result.Status)
            {
            case PaymentResult.MessageStatus.Ok:
                _interactiveService.ShowMessage(ImportanceLevel.Info, "SMS отправлена успешно");
                break;

            case PaymentResult.MessageStatus.Error:
                _interactiveService.ShowMessage(ImportanceLevel.Error, result.ErrorDescription, "Не удалось отправить SMS");
                break;

            default:
                throw new ArgumentOutOfRangeException();
            }
        }