Example #1
0
        public override bool Save()
        {
            bool hasDuplicate = CheckDuplicate();
            bool userAnswer   = true;

            if (hasDuplicate)
            {
                userAnswer = MessageDialogWorks.RunQuestionDialog(
                    "Контрагент с данным ИНН уже существует. Сохранить?");
            }

            if (userAnswer)
            {
                Entity.UoW = UoW;
                var valid = new QSValidator <Counterparty>(UoWGeneric.Root);
                if (valid.RunDlgIfNotValid((Gtk.Window) this.Toplevel))
                {
                    return(false);
                }

                logger.Info("Сохраняем контрагента...");
                phonesView.SaveChanges();
                emailsView.SaveChanges();
                UoWGeneric.Save();
                logger.Info("Ok.");
                return(true);
            }
            return(false);
        }
Example #2
0
        public static void CheckBanksUpdate(bool forceUpdate)
        {
            if (!forceUpdate)
            {
                DateTime lastModified = new DateTime();
                if (MainSupport.BaseParameters.All.ContainsKey("last_banks_update"))
                {
                    lastModified = DateTime.Parse(MainSupport.BaseParameters.All["last_banks_update"]);
                }

                int withoutUpdate = (int)DateTime.Now.Subtract(lastModified).TotalDays;
                if (withoutUpdate < UpdatePeriod)
                {
                    return;
                }
                var runUpdate = MessageDialogWorks.RunQuestionDialog(
                    lastModified == default(DateTime) ? "Справочник банков никогда не обновлялся. Обновить?" :
                    NumberToTextRus.FormatCase(withoutUpdate, "Cправочник банков обновлялся\n{0} день назад. Обновить?",
                                               "Cправочник банков обновлялся\n{0} дня назад. Обновить?",
                                               "Cправочник банков обновлялся\n{0} дней назад. Обновить?"));
                if (!runUpdate)
                {
                    return;
                }
            }
            BanksUpdateWindow updateWindow = new BanksUpdateWindow();

            updateWindow.Show();
        }
        protected void OnButtonCreateBorderClicked(object sender, EventArgs e)
        {
            if (!creatingNewBorder)
            {
                creatingNewBorder = true;
                newBorderVertice  = new List <PointLatLng>();
            }
            else
            {
                if (MessageDialogWorks.RunQuestionDialog("Завершить задание границ района?"))
                {
                    if (MessageDialogWorks.RunQuestionDialog("Сохранить новые границы района?"))
                    {
                        var closingPoint = newBorderVertice[0];
                        newBorderVertice.Add(closingPoint);
                        currentBorderVertice           = newBorderVertice;
                        currentDistrict.DistrictBorder = gf.CreatePolygon(GetCoordinatesFromPoints());
                    }
                    creatingNewBorder = false;
                    ShowBorders();
                    ShowBorderVertice(currentBorderVertice);
                }
            }

            ControlsAccessibility();
        }
Example #4
0
 protected void OnButtonCancelClicked(object sender, EventArgs e)
 {
     if (MessageDialogWorks.RunQuestionDialog("Выйти без сохранения?"))
     {
         this.Destroy();
     }
 }
Example #5
0
        void EntryBuilding_Changed(object sender, EventArgs e)
        {
            if (entryBuilding.OsmCompletion.HasValue)
            {
                Entity.FoundOnOsm = entryBuilding.OsmCompletion.Value;
                decimal?latitude, longitude;
                entryBuilding.GetCoordinates(out longitude, out latitude);

                if (!Entity.ManualCoordinates || (Entity.ManualCoordinates && MessageDialogWorks.RunQuestionDialog("Координаты были установлены вручную, заменить их на коордитаты адреса?")))
                {
                    Entity.Latitude          = latitude;
                    Entity.Longitude         = longitude;
                    Entity.ManualCoordinates = false;
                }
            }
            if (entryBuilding.OsmHouse != null && !String.IsNullOrWhiteSpace(entryBuilding.OsmHouse.Name))
            {
                labelHouseName.Visible   = true;
                labelHouseName.LabelProp = entryBuilding.OsmHouse.Name;
            }
            else
            {
                labelHouseName.Visible = false;
            }
        }
Example #6
0
        void RunAgreementCreateDialog(CounterpartyContract contract)
        {
            ITdiTab dlg;
            string  paymentTypeString = "";

            switch (UoWGeneric.Root.Payment)
            {
            case PaymentType.cash:
                paymentTypeString = "наличной";
                break;

            case PaymentType.cashless:
                paymentTypeString = "безналичной";
                break;

            case PaymentType.barter:
                paymentTypeString = "бартерной";
                break;
            }
            string question = "Отсутствует доп. соглашение сервиса с клиентом в договоре для " +
                              paymentTypeString +
                              " формы оплаты. Создать?";

            if (MessageDialogWorks.RunQuestionDialog(question))
            {
                dlg = new RepairAgreementDlg(contract);
                (dlg as IAgreementSaved).AgreementSaved += (sender, e) => {
                    UoWGeneric.Root.InitialOrder?.CreateOrderAgreementDocument(e.Agreement);
                };
                TabParent.AddSlaveTab(this, dlg);
            }
        }
Example #7
0
        void SelectTemplate_ObjectSelected(object sender, OrmReferenceObjectSectedEventArgs e)
        {
            if (DocumentUoW.Root.Items.Count > 0)
            {
                if (MessageDialogWorks.RunQuestionDialog("Текущий список будет очищен. Продолжить?"))
                {
                    DocumentUoW.Root.ObservableItems.Clear();
                }
                else
                {
                    return;
                }
            }

            var template = DocumentUoW.GetById <RegradingOfGoodsTemplate>((e.Subject as RegradingOfGoodsTemplate).Id);

            foreach (var item in template.Items)
            {
                DocumentUoW.Root.AddItem(new RegradingOfGoodsDocumentItem()
                {
                    NomenclatureNew = item.NomenclatureNew,
                    NomenclatureOld = item.NomenclatureOld
                });
            }
            LoadStock();
        }
Example #8
0
        protected void OnButtonFillWarehouseItemsClicked(object sender, EventArgs e)
        {
            if (DocumentUoW.Root.Items.Count > 0)
            {
                if (!MessageDialogWorks.RunQuestionDialog("Список будет очищен. Продолжить?"))
                {
                    return;
                }
            }

            DocumentUoW.Root.FillFromRouteList(DocumentUoW, false);
            if (DocumentUoW.Root.Items.Any(i => i.Nomenclature.Warehouse == null))
            {
                string str = "";
                foreach (var nomenclarure in DocumentUoW.Root.Items.Where(i => i.Nomenclature.Warehouse == null))
                {
                    str = string.Join("\n", nomenclarure.Nomenclature.Name);
                }
                MessageDialogWorks.RunErrorWithSecondaryTextDialog("В МЛ есть номенклатура не привязанная к складу.", str);
            }

            DocumentUoW.Root.FillFromRouteList(DocumentUoW, true);
            DocumentUoW.Root.UpdateAlreadyLoaded(DocumentUoW);
            if (DocumentUoW.Root.Warehouse != null)
            {
                DocumentUoW.Root.UpdateStockAmount(DocumentUoW);
                UpdateAmounts();
            }
        }
Example #9
0
        public static void CheckBanksUpdate(bool forceUpdate)
        {
            if (!forceUpdate)
            {
                dynamic  parameters   = new ParametersService(QSMain.ConnectionDB);
                DateTime lastModified = parameters.last_banks_update ?? default(DateTime);

                int withoutUpdate = (int)DateTime.Now.Subtract(lastModified).TotalDays;
                if (withoutUpdate < UpdatePeriod)
                {
                    return;
                }
                var runUpdate = MessageDialogWorks.RunQuestionDialog(
                    lastModified == default(DateTime) ? "Справочник банков никогда не обновлялся. Обновить?" :
                    NumberToTextRus.FormatCase(withoutUpdate, "Cправочник банков обновлялся\n{0} день назад. Обновить?",
                                               "Cправочник банков обновлялся\n{0} дня назад. Обновить?",
                                               "Cправочник банков обновлялся\n{0} дней назад. Обновить?"));
                if (!runUpdate)
                {
                    return;
                }
            }
            BanksUpdateWindow updateWindow = new BanksUpdateWindow();

            updateWindow.Show();
        }
Example #10
0
 protected void OnButtonAddClicked(object sender, EventArgs e)
 {
     if (!String.IsNullOrWhiteSpace(textComment.Buffer.Text) || MessageDialogWorks.RunQuestionDialog("Вы не заполнили комментарий. Продолжить?"))
     {
         ServiceClaimStatus newStatus = (ServiceClaimStatus)(enumStatusEditable.SelectedItem ?? UoWGeneric.Root.Status);
         UoWGeneric.Root.AddHistoryRecord(newStatus, textComment.Buffer.Text, _employeeRepository);
     }
 }
Example #11
0
        protected void OnButtonRefreshClicked(object sender, EventArgs e)
        {
            bool hasChanges = items.Count(item => item.HasChanged) > 0;

            if (!hasChanges || MessageDialogWorks.RunQuestionDialog("Вы действительно хотите обновить список заказов? Внесенные изменения будут утрачены."))
            {
                UoWGeneric.Session.Refresh(Entity);
                UpdateNodes();
            }
        }
 protected void OnBtnEditDistrictClicked(object sender, EventArgs e)
 {
     if (currentDistrict.Id == 0 &&
         MessageDialogWorks.RunQuestionDialog("Для продолжения необходимо сохранить район, сохранить и продолжить?"))
     {
         uow.Save(currentDistrict);
         uow.Commit();
     }
     TabParent.OpenTab(
         DialogHelper.GenerateDialogHashName <ScheduleRestrictedDistrict>(currentDistrict.Id),
         () => new ScheduleRestrictedDistrictDlg(currentDistrict)
         );
 }
 protected void OnYentryrefWarehouseBeforeChangeByUser(object sender, EntryReferenceBeforeChangeEventArgs e)
 {
     if (Entity.Warehouse != null && Entity.Items.Count > 0)
     {
         if (MessageDialogWorks.RunQuestionDialog("При изменении склада табличная часть документа будет очищена. Продолжить?"))
         {
             Entity.ObservableItems.Clear();
         }
         else
         {
             e.CanChange = false;
         }
     }
 }
Example #14
0
 void Templatewidget_BeforeOpen(object sender, EventArgs e)
 {
     if (UoW.HasChanges)
     {
         if (MessageDialogWorks.RunQuestionDialog("Необходимо сохранить документ перед открытием печатной формы, сохранить?"))
         {
             UoWGeneric.Save();
             RefreshParserRootObject();
         }
         else
         {
             templatewidget.CanOpenDocument = false;
         }
     }
 }
Example #15
0
        public override bool Save()
        {
            if (Entity.Status == RouteListStatus.EnRoute && items.All(x => x.Status != RouteListItemStatus.EnRoute))
            {
                if (MessageDialogWorks.RunQuestionDialog("В маршрутном листе не осталось адресов со статусом в 'В пути'. Завершить маршрут?"))
                {
                    Entity.CompleteRoute();
                }
            }

            UoWGeneric.Save();

            var changedList = items.Where(item => item.ChangedDeliverySchedule || item.HasChanged).ToList();

            if (changedList.Count == 0)
            {
                return(true);
            }

            var currentEmployee = EmployeeRepository.GetEmployeeForCurrentUser(UoWGeneric);

            if (currentEmployee == null)
            {
                MessageDialogWorks.RunInfoDialog("Ваш пользователь не привязан к сотруднику, уведомления об изменениях в маршрутном листе не будут отправлены водителю.");
                return(true);
            }

            foreach (var item in changedList)
            {
                if (item.HasChanged)
                {
                    getChatService()
                    .SendOrderStatusNotificationToDriver(
                        currentEmployee.Id,
                        item.RouteListItem.Id
                        );
                }
                if (item.ChangedDeliverySchedule)
                {
                    getChatService().SendDeliveryScheduleNotificationToDriver(
                        currentEmployee.Id,
                        item.RouteListItem.Id
                        );
                }
            }
            return(true);
        }
 void dlg_EntitySaved(object sender, EntitySavedEventArgs e)
 {
     if (e.Entity != null && mode == OrmReferenceMode.Select)
     {
         if (!MessageDialogWorks.RunQuestionDialog("Выбрать созданный объект и вернуться к предыдущему диалогу?"))
         {
             return;
         }
         RepresentationSelectResult   res      = new RepresentationSelectResult(DomainHelper.GetId(e.Entity), e.Entity);
         RepresentationSelectResult[] selected = { res };
         logger.Debug("Выбрано {0} id:({1})", objectType, String.Join(",", selected.Select(x => x.EntityId)));
         ObjectSelected(this, new ReferenceRepresentationSelectedEventArgs(selected));
         Application.Invoke(delegate {
             OnCloseTab(CloseSource.Self);
         });
     }
 }
Example #17
0
        public override bool Save()
        {
            var valid = new QSValidator <Employee> (UoWGeneric.Root);

            if (valid.RunDlgIfNotValid((Gtk.Window) this.Toplevel))
            {
                return(false);
            }

            if (Entity.User != null)
            {
                var associatedEmployees = Repository.Company.EmployeeRepository.GetEmployeesForUser(UoW, Entity.User.Id);
                if (associatedEmployees.Any(e => e.Id != Entity.Id))
                {
                    string mes = String.Format("Пользователь {0} уже связан с сотрудником {1}, при привязке этого сотрудника к пользователю, старая связь будет удалена. Продолжить?",
                                               Entity.User.Name,
                                               String.Join(", ", associatedEmployees.Select(e => e.ShortName))
                                               );
                    if (MessageDialogWorks.RunQuestionDialog(mes))
                    {
                        foreach (var ae in associatedEmployees.Where(e => e.Id != Entity.Id))
                        {
                            ae.User = null;
                            UoWGeneric.Save(ae);
                        }
                    }
                    else
                    {
                        return(false);
                    }
                }
            }

            phonesView.SaveChanges();
            logger.Info("Сохраняем сотрудника...");
            try {
                UoWGeneric.Save();
            } catch (Exception ex) {
                logger.Error(ex, "Не удалось записать сотрудника.");
                QSProjectsLib.QSMain.ErrorMessage((Gtk.Window) this.Toplevel, ex);
                return(false);
            }
            logger.Info("Ok");
            return(true);
        }
        public override bool Save()
        {
            if (Entity.Status > RouteListStatus.OnClosing)
            {
                if (Entity.FuelOperationHaveDiscrepancy())
                {
                    if (!MessageDialogWorks.RunQuestionDialog("Был изменен водитель или автомобиль, при сохранении МЛ баланс по топливу изменится с учетом этих изменений. Продолжить сохранение?"))
                    {
                        return(false);
                    }
                }
                Entity.UpdateFuelOperation();
            }

            UoWGeneric.Save();

            return(true);
        }
Example #19
0
        void SelectDrivers_ObjectSelected(object sender, OrmReferenceObjectSectedEventArgs e)
        {
            var addDrivers = e.GetEntities <Employee>().ToList();

            logger.Info("Получаем авто для водителей...");
            MainClass.progressBarWin.ProgressStart(2);
            var onlyNew = addDrivers.Where(x => driversAtDay.All(y => y.Employee.Id != x.Id)).ToList();
            var allCars = CarRepository.GetCarsbyDrivers(uow, onlyNew.Select(x => x.Id).ToArray());

            MainClass.progressBarWin.ProgressAdd();

            foreach (var driver in addDrivers)
            {
                if (driversAtDay.Any(x => x.Employee.Id == driver.Id))
                {
                    logger.Warn("Водитель {0} уже добавлен. Пропускаем...", driver.ShortName);
                    continue;
                }
                var atwork = new AtWorkDriver(driver, DialogAtDate,
                                              allCars.FirstOrDefault(x => x.Driver.Id == driver.Id)
                                              );
                if (driver.DefaultForwarder != null)
                {
                    var forwarder = ForwardersAtDay.FirstOrDefault(x => x.Employee.Id == driver.DefaultForwarder.Id);
                    if (forwarder == null)
                    {
                        if (MessageDialogWorks.RunQuestionDialog($"Водитель {driver.ShortName} обычно ездить с экспедитором {driver.DefaultForwarder.ShortName}. Он отсутствует в списке экспедиторов. Добавить его в список?"))
                        {
                            forwarder = new AtWorkForwarder(driver.DefaultForwarder, DialogAtDate);
                            observableForwardersAtDay.Add(forwarder);
                        }
                    }
                    if (forwarder != null && DriversAtDay.All(x => x.WithForwarder != forwarder))
                    {
                        atwork.WithForwarder = forwarder;
                    }
                }
                driversAtDay.Add(atwork);
            }
            MainClass.progressBarWin.ProgressAdd();
            DriversAtDay = driversAtDay.OrderBy(x => x.Employee.ShortName).ToList();
            logger.Info("Ок");
            MainClass.progressBarWin.ProgressClose();
        }
Example #20
0
        void RunContractCreateDialog()
        {
            ITdiTab dlg;
            string  paymentTypeString = "";

            switch (UoWGeneric.Root.Payment)
            {
            case PaymentType.cash:
                paymentTypeString = "наличной";
                break;

            case PaymentType.cashless:
                paymentTypeString = "безналичной";
                break;

            case PaymentType.barter:
                paymentTypeString = "бартерной";
                break;
            }
            string question = "Отсутствует договор с клиентом для " +
                              paymentTypeString +
                              " формы оплаты. Создать?";

            if (MessageDialogWorks.RunQuestionDialog(question))
            {
                Organization organization = null;
                throw  new NotImplementedException();

                dlg = new CounterpartyContractDlg(UoWGeneric.Root.Counterparty, organization);
                (dlg as IContractSaved).ContractSaved += (sender, e) => {
                    if (UoWGeneric.Root.InitialOrder != null)
                    {
                        UoWGeneric.Root.InitialOrder.ObservableOrderDocuments.Add(new OrderContract {
                            Order           = UoWGeneric.Root.InitialOrder,
                            AttachedToOrder = UoWGeneric.Root.InitialOrder,
                            Contract        = e.Contract
                        });
                    }
                };
                TabParent.AddSlaveTab(this, dlg);
            }
        }
Example #21
0
        void MapWidget_ButtonReleaseEvent(object o, Gtk.ButtonReleaseEventArgs args)
        {
            if (args.Event.Button == 1)
            {
                addressMoving = false;
                var newPoint = MapWidget.FromLocalToLatLng((int)args.Event.X, (int)args.Event.Y);
                if (!Entity.ManualCoordinates && Entity.FoundOnOsm)
                {
                    if (!MessageDialogWorks.RunQuestionDialog("Координаты сейчас установлены по адресу. Вы уверены что хотите установить новые координаты?"))
                    {
                        UpdateAddressOnMap();
                        return;
                    }
                }

                Entity.ManualCoordinates = true;
                Entity.Latitude          = (decimal)newPoint.Lat;
                Entity.Longitude         = (decimal)newPoint.Lng;
            }
        }
Example #22
0
        public override bool Save()
        {
            if (!Entity.СoordinatesExist)
            {
                if (!MessageDialogWorks.RunQuestionDialog("Адрес не найден на карте, вы точно хотите его сохранить?"))
                {
                    return(false);
                }
            }

            var valid = new QSValidator <DeliveryPoint> (UoWGeneric.Root);

            if (valid.RunDlgIfNotValid((Gtk.Window) this.Toplevel))
            {
                return(false);
            }

            UoWGeneric.Save();
            return(true);
        }
Example #23
0
        public override bool Save()
        {
            var valid = new QSValidation.QSValidator <CarLoadDocument> (UoWGeneric.Root);

            if (valid.RunDlgIfNotValid((Gtk.Window) this.Toplevel))
            {
                return(false);
            }

            Entity.LastEditor     = Repository.EmployeeRepository.GetEmployeeForCurrentUser(UoW);
            Entity.LastEditedTime = DateTime.Now;
            if (Entity.LastEditor == null)
            {
                MessageDialogWorks.RunErrorDialog("Ваш пользователь не привязан к действующему сотруднику, вы не можете изменять складские документы, так как некого указывать в качестве кладовщика.");
                return(false);
            }

            if (Entity.Items.Any(x => x.Amount == 0))
            {
                if (MessageDialogWorks.RunQuestionDialog("В списке есть нулевые позиции. Убрать нулевые позиции перед сохранением?"))
                {
                    Entity.ClearItemsFromZero();
                }
            }

            Entity.UpdateOperations(UoW);

            logger.Info("Сохраняем погрузочный талон...");
            UoWGeneric.Save();

            logger.Info("Меняем статус маршрутного листа...");
            if (Entity.RouteList.ShipIfCan(UoW))
            {
                MessageDialogWorks.RunInfoDialog("Маршрутный лист отгружен полностью.");
            }
            UoW.Save(Entity.RouteList);
            UoW.Commit();

            logger.Info("Ok.");
            return(true);
        }
Example #24
0
        protected void OnButtonFillAllItemsClicked(object sender, EventArgs e)
        {
            if (DocumentUoW.Root.Items.Count > 0)
            {
                if (!MessageDialogWorks.RunQuestionDialog("Список будет очищен. Продолжить?"))
                {
                    return;
                }
            }
            DocumentUoW.Root.FillFromRouteList(DocumentUoW, false);

            var    items = DocumentUoW.Root.Items;
            string errorNomenclatures = string.Empty;

            foreach (var item in items)
            {
                if (item.Nomenclature.Unit == null)
                {
                    errorNomenclatures += string.Format("{0} код {1}{2}",
                                                        item.Nomenclature.Name, item.Nomenclature.Id, Environment.NewLine);
                }
            }
            if (!string.IsNullOrEmpty(errorNomenclatures))
            {
                errorNomenclatures = "Не указаны единицы измерения для следующих номенклатур:"
                                     + Environment.NewLine + errorNomenclatures;
                MessageDialogWorks.RunErrorDialog(errorNomenclatures);
                DocumentUoW.Root.Items.Clear();
                return;
            }

            DocumentUoW.Root.UpdateAlreadyLoaded(DocumentUoW);
            if (DocumentUoW.Root.Warehouse != null)
            {
                DocumentUoW.Root.UpdateStockAmount(DocumentUoW);
                UpdateAmounts();
            }
        }
Example #25
0
        protected void OnButtonChangeToEmployeeClicked(object sender, EventArgs e)
        {
            if (UoW.HasChanges || Entity.Id == 0)
            {
                if (!MessageDialogWorks.RunQuestionDialog("Для продолжения необходимо сохранить изменения, сохранить и продолжить?"))
                {
                    return;
                }
                if (Save())
                {
                    OnEntitySaved(true);
                }
                else
                {
                    return;
                }
            }
            var employeeUow = UnitOfWorkFactory.CreateWithNewRoot <Employee>();

            Personnel.ChangeTraineeToEmployee(employeeUow, Entity);
            TabParent.OpenTab(OrmMain.GenerateDialogHashName <Employee>(Entity.Id),
                              () => new EmployeeDlg(employeeUow));
            this.OnCloseTab(false);
        }
Example #26
0
        /// <summary>
        /// Метод создаем маршруты на день основываясь на данных всесенных в поля <c>Routes</c>, <c>Orders</c>,
        /// <c>Drivers</c> и <c>Forwarders</c>.
        /// </summary>
        public void CreateRoutes()
        {
            WarningMessages.Clear();
            ProposedRoutes.Clear();             //Очищаем сразу, так как можем выйти из метода ранее.

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

            /// Создаем список поездок всех водителей. Тут перебираем всех водителей с машинами
            /// и создаем поездки для них, в зависимости от выбранного режима работы.
            var trips = Drivers.Where(x => x.Car != null)
                        .OrderBy(x => x.PriorityAtDay)
                        .SelectMany(x => x.DaySchedule != null
                                                                                                ? x.DaySchedule.Shifts.Select(s => new PossibleTrip(x, s))
                                                                                                : new[] { new PossibleTrip(x, 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.Any(x => x.Id == address.Order.Id))
                    {
                        Orders.Add(address.Order);
                    }
                }
            }

            var possibleRoutes = trips.ToArray();

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

            TestCars(possibleRoutes);

            var areas = UoW.GetAll <LogisticsArea>().ToList();
            List <LogisticsArea>   unusedDistricts  = new List <LogisticsArea>();
            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 aria  = areas.Find(x => x.Geometry.Contains(point));
                if (aria != null)
                {
                    var oldRoute = Routes.FirstOrDefault(r => r.Addresses.Any(a => a.Order.Id == order.Id));
                    if (oldRoute != null)
                    {
                        calculatedOrders.Add(new CalculatedOrder(order, aria, false, oldRoute));
                    }
                    else if (possibleRoutes.SelectMany(x => x.Districts).Any(x => x.District.Id == aria.Id))
                    {
                        calculatedOrders.Add(new CalculatedOrder(order, aria));
                    }
                    else if (!unusedDistricts.Contains(aria))
                    {
                        unusedDistricts.Add(aria);
                    }
                }
            }
            Nodes = calculatedOrders.ToArray();
            if (unusedDistricts.Count > 0)
            {
                AddWarning("Районы без водителей: {0}", String.Join(", ", unusedDistricts.Select(x => x.Name)));
            }

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

            MainClass.progressBarWin.ProgressAdd();
            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> - Максимальное время ожидания водителя. То есть водитель закончил разгрузку следующий
            /// адрес в маршруте у него не должен быть позже чем на 3 часа ожидания.
            int horizon        = 24 * 3600;
            int maxWaitTime    = 3 * 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.Car.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);
                /// Добавляем фиксированный штраф за приоритет водителя.
                routing.SetFixedCostOfVehicle((possibleRoutes[ix].DriverPriority - 1) * DriverPriorityPenalty, 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].Car.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.OptimizationStep = 100;

            var solver = routing.solver();

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

            if (WarningMessages.Count > 0 &&
                !MessageDialogWorks.RunQuestionDialog("При построении транспортной модели обнаружены следующие проблемы:\n{0}\nПродолжить?",
                                                      String.Join("\n", WarningMessages.Select(x => "⚠ " + x))))
            {
                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, OrdersProgress, DebugBuffer, lastSolution));

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

            Assignment solution = routing.SolveWithParameters(search_parameters);
            PerformanceHelper.AddTimePoint(logger, $"Получили решение.");
            logger.Info("Готово. Заполняем.");
            MainClass.progressBarWin.ProgressAdd();
#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

            MainClass.progressBarWin.ProgressAdd();

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

            if (distanceCalculator.ErrorWays.Count > 0)
            {
                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)))
                             );
            }
        }
Example #27
0
        protected void OnButtonFindGapClicked(object sender, EventArgs e)
        {
            trackOnGapOverlay.Clear();
            tracksDistance.RemoveAll(x => x.Id == "MissingTrack");
            string message          = "Найдены разрывы в треке:";
            double replacedDistance = 0;

            TrackPoint lastPoint = null;

            foreach (var point in track.TrackPoints)
            {
                if (lastPoint == null)
                {
                    lastPoint = point;
                    continue;
                }

                var distance = GMapProviders.EmptyProvider.Projection.GetDistance(
                    new PointLatLng(lastPoint.Latitude, lastPoint.Longitude),
                    new PointLatLng(point.Latitude, point.Longitude));

                if (distance > 0.5)
                {
                    logger.Info("Найден разрыв в треке расстоянием в {0}", distance);
                    message += String.Format("\n* разрыв c {1:t} по {2:t} — {0:N1} км.",
                                             distance,
                                             lastPoint.TimeStamp,
                                             point.TimeStamp
                                             );
                    replacedDistance += distance;

                    var addressesByCompletion = routeList.Addresses
                                                .Where(x => x.Status == RouteListItemStatus.Completed)
                                                .OrderBy(x => x.StatusLastUpdate)
                                                .ToList();

                    RouteListItem addressBeforeGap = addressesByCompletion.LastOrDefault(x => x.StatusLastUpdate < lastPoint.TimeStamp.AddMinutes(2));
                    RouteListItem addressAfterGap  = addressesByCompletion.FirstOrDefault(x => x.StatusLastUpdate > point.TimeStamp.AddMinutes(-2));

                    var beforeIndex = addressBeforeGap == null ? -1 : addressesByCompletion.IndexOf(addressBeforeGap);
                    var afterIndex  = addressAfterGap == null ? addressesByCompletion.Count : addressesByCompletion.IndexOf(addressAfterGap);
                    var routePoints = new List <PointOnEarth>();
                    routePoints.Add(new PointOnEarth(lastPoint.Latitude, lastPoint.Longitude));

                    if (afterIndex - beforeIndex > 1)
                    {
                        var throughAddress = addressesByCompletion.GetRange(beforeIndex + 1, afterIndex - beforeIndex - 1);
                        logger.Info("В разрыве найдены выполенные адреса порядковый(е) номер(а) {0}", String.Join(", ", throughAddress.Select(x => x.IndexInRoute)));
                        routePoints.AddRange(
                            throughAddress.Where(x => x.Order?.DeliveryPoint?.Latitude != null && x.Order?.DeliveryPoint?.Longitude != null)
                            .Select(x => new PointOnEarth(x.Order.DeliveryPoint.Latitude.Value, x.Order.DeliveryPoint.Longitude.Value)));

                        message += $" c выполненными адресами({beforeIndex + 2}-{afterIndex}) п/п в МЛ " + String.Join(", ", throughAddress.Select(x => x.IndexInRoute));
                    }
                    routePoints.Add(new PointOnEarth(point.Latitude, point.Longitude));

                    var missedTrack = SputnikMain.GetRoute(routePoints, false, true);
                    if (missedTrack == null)
                    {
                        MessageDialogWorks.RunErrorDialog("Не удалось получить ответ от сервиса \"Спутник\"");
                        return;
                    }
                    if (missedTrack.Status != 0)
                    {
                        MessageDialogWorks.RunErrorDialog("Cервис \"Спутник\" сообщил об ошибке {0}: {1}", missedTrack.Status, missedTrack.StatusMessageRus);
                        return;
                    }

                    var decodedPoints = Polyline.DecodePolyline(missedTrack.RouteGeometry);
                    var points        = decodedPoints.Select(p => new PointLatLng(p.Latitude * 0.1, p.Longitude * 0.1)).ToList();

                    var route = new GMapRoute(points, "MissedRoute");
                    route.Stroke           = new System.Drawing.Pen(System.Drawing.Color.DarkMagenta);
                    route.Stroke.Width     = 4;
                    route.Stroke.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;

                    trackOnGapOverlay.Routes.Add(route);
                }

                lastPoint = point;
            }

            if (trackOnGapOverlay.Routes.Count > 0)
            {
                var distanceLayout = MakeDistanceLayout(trackOnGapOverlay.Routes.ToArray());
                distanceLayout.Id = "MissingTrack";
                tracksDistance.Add(distanceLayout);
                var oldDistance         = track.DistanceEdited ? trackRoute.Distance : track.Distance;
                var missedRouteDistance = trackOnGapOverlay.Routes.Sum(x => x.Distance);
                var newDistance         = oldDistance - replacedDistance + missedRouteDistance;
                var diffDistance        = newDistance - oldDistance;

                message += $"\n Старая длинна трека:{oldDistance:N1} км." +
                           $"\n Новая длинна трека: {newDistance:N1} км.(+{diffDistance:N1})" +
                           "\n Сохранить изменения длинны трека?";

                if (MessageDialogWorks.RunQuestionDialog(message))
                {
                    track.Distance       = newDistance;
                    track.DistanceEdited = true;
                    UoW.Save(track);
                    UoW.Commit();
                    UpdateDistanceLabel();
                }
            }
            else
            {
                MessageDialogWorks.RunInfoDialog("Разрывов в треке не найдено.");
            }
        }