Каталог решений - Умная маршрутизация: о сложностях интеграции с 1С (часть 2)

Умная маршрутизация: о сложностях интеграции с 1С (часть 2)

Умная маршрутизация: о сложностях интеграции с 1С (часть 2)

В наличии

В статье будет много примеров кода с комментариями работы расширения для обмена данными УТ11.4 с Яндекс.Маршрутизацией через выгрузку/загрузку EXCEL файла.

Категория:

Описание

Принцип работы, какие задачи решает и кому подходит — описано в первой части статьи: Умная маршрутизация: кейс интеграции с 1С (часть 1)

Во второй части описана доработка "Рабочего места менеджера по доставке" в УТ 11.4 для обмена данными с Яндекс.Маршрутизацией.

Идея. Нам нужны 2 кнопки: “Загрузить” и “Выгрузить”

Создаём расширение, в нем обработку с тремя кнопками кода и – вуаля:

 

 

  1. Есть нажать на первую кнопку (левую зеленую) формируется готовый Excel. Его нужно подгрузить в Яндекс.Маршрутизацию.
  2. Яндекс.Маршрутизация оптимизирует маршруты водителей и возвращает Excel – Маршрутный лист с параметрами поездки. Если нажать кнопку 2, Маршрутный лист загрузится в 1С и раскидает заказы по маршрутным листам.
  3. Позже появилась кнопка 3 (верхняя длинная) «Стоимость работы водителей». Она открывает отчёт с итогами работы за день/неделю/месяц и авторасчёт зарплаты водителей.

Стоп, нужно же где-то взять список заказов, которые необходимо сегодня доставить. Вспоминаем, что в УТ это Обработки.РабочееМестоМенеджераПоДоставке.

Переносим кнопки туда.

 

Формируем Excel с заказами

 

Чтобы сформировать Excel, нужно решить 3 проблемы (по факту больше, конечно, но кто бы научил точно оценивать все проблемы до начала разработки):

 

Шаг 1. У нас база клиент-серверная, значит Excel сформируется на сервере, а сохранить его надо будет на клиенте. Тут всё стандартно:

  &НаКлиенте
  Процедура ЯМ_ЯМ_СохранитьВЭксельПосле(Команда)
      //Сохраняем в Эксель на клиенте
      СохранитьПрочитатьЭксельЯндексМаршрутизации("Сохранение");
  КонецПроцедуры
  
  &НаКлиенте
  Процедура СохранитьПрочитатьЭксельЯндексМаршрутизации(ТипКоманды)
      //Прочитать Эксель на клиенте
      Режим = РежимДиалогаВыбораФайла[ТипКоманды]; 
      ДиалогФайла = Новый ДиалогВыбораФайла(Режим); 
      Если ТипКоманды = "Сохранение" Тогда
          ДиалогФайла.ПолноеИмяФайла = "YaRouting_" + Формат(ТекущаяДата(),"ДФ=yyyyMMdd_HHMMss"); 
      КонецЕсли;
      Фильтр = "Документ Excel (*.xlsx)|*.xlsx|";                 
      ДиалогФайла.Фильтр = Фильтр; 
      ДиалогФайла.МножественныйВыбор = Ложь; 
      ДиалогФайла.Заголовок = "Выберите файл"; 
      Если ДиалогФайла.Выбрать() Тогда 
          Если ТипКоманды = "Сохранение" Тогда
              ОднаКнига = Новый ПакетОтображаемыхДокументов;
              //Создав объект "ОднаКнига", мы можем в его состав поместить несколько табличных документов каким-то таким образом:
              ДобавитьСтраницыВКнигу(ОднаКнига);
              //А сохранить в одну книгу все это хозяйство можно так:
              ОднаКнига.Записать(ДиалогФайла.ПолноеИмяФайла, ТипФайлаПакетаОтображаемыхДокументов.XLSX);
          Иначе
              //Открытие файла
              Адрес = ПоместитьВоВременноеХранилище(Новый ДвоичныеДанные(ДиалогФайла.ПолноеИмяФайла));
              ПрочитатьИОбработатьФайлЯндексМаршрутизации(Адрес, ДиалогФайла.ПолноеИмяФайла);
          КонецЕсли;
      КонецЕсли;
  КонецПроцедуры

 

Шаг 2. Если вы посмотрели шаблон Excel Яндекс.Маршрутизации https://yandex.ru/routing/doc/vrp/concepts/example.html, то увидели, что в нём несколько страниц. Т.е. нужно сохранить не просто Excel, а сохранить в 1С многостраничный Excel.

Для этого мы на клиенте создаём: 

  ОднаКнига = Новый ПакетОтображаемыхДокументов;

и затем серверной процедурой ДобавитьСтраницыВКнигу(ОднаКнига) создаём 4 отдельных Табличных документа, сохраняем их как листы книги ОднаКнига, а на клиенте — многостраничный Excel

  ОднаКнига.Записать(ДиалогФайла.ПолноеИмяФайла, ТипФайлаПакетаОтображаемыхДокументов.XLSX).

Для красоты вынес код в общий модуль: и читать удобнее, и использовать повторно.

  &НаСервере
  Процедура ДобавитьСтраницыВКнигу(ОднаКнига)
      ЯМ_ИнтеграцияСЯндексМаршрутизацией.ДобавитьСтраницыЭкселяДляЯндексМаршрутизации(ОднаКнига, ПолучитьЗаказыНаДоставку(), УникальныйИдентификатор);
  КонецПроцедуры

Основная процедура общего модуля заполняет начальные параметры и добавляет 4 страницы в книгу Excel:

  Процедура ДобавитьСтраницыЭкселяДляЯндексМаршрутизации(ОднаКнига, ЗаказыНаДоставку, УникальныйИдентификатор) Экспорт
      //Определяем режим времени суток
      РежимВремени = Новый Структура;
      РежимВремени.Вставить("ТекущаяДата",    ТекущаяДата());
      РежимВремени.Вставить("СейчасВечер",     ?(Час(РежимВремени.ТекущаяДата)>= 15, Истина, Ложь)        );
      РежимВремени.Вставить("СейчасВыходной", ?(ДеньНедели(РежимВремени.ТекущаяДата)>=6, Истина, Ложь));
      РежимВремени.Вставить("Суффикс",         ?(РежимВремени.СейчасВечер, "_НОЧЬ", "_ДЕНЬ"));
      РежимВремени.Вставить("СуффиксОбщий",   ?(РежимВремени.СейчасВыходной, "_ВЫХОДНОЙ", ?(РежимВремени.СейчасВечер, "_НОЧЬ", "_ДЕНЬ")));
     
      //Добавляем страницу Опций   
      ДобавитьСтраницуВЭксель(ОднаКнига, "Orders", УникальныйИдентификатор, ЗаказыНаДоставку, РежимВремени);
      ДобавитьСтраницуВЭксель(ОднаКнига, "Vehicles", УникальныйИдентификатор,,РежимВремени);
      ДобавитьСтраницуВЭксель(ОднаКнига, "Depot", УникальныйИдентификатор,,РежимВремени);
      ДобавитьСтраницуВЭксель(ОднаКнига, "Options", УникальныйИдентификатор,,РежимВремени);
     
  КонецПроцедуры

И, наконец, код добавления страниц:

  &НаСервере
  Процедура ДобавитьСтраницуВЭксель(ОднаКнига, НазваниеСтраницыЭксель, УникальныйИдентификатор, ЗаказыНаДоставку = Неопределено, РежимВремени)
      Элт = ОднаКнига.Состав.Добавить();
      Элт.Наименование = НазваниеСтраницыЭксель;
      Элт.Данные = ВыгрузитьТабДокВовременноеХранилищеНаСервере(НазваниеСтраницыЭксель, УникальныйИдентификатор, ЗаказыНаДоставку, РежимВремени);
  КонецПроцедуры

Как вы заметили, всё довольно просто. Нужно указать название нового листа Элт.Наименование  и добавить данные  Элт.Данные.

  &НаСервере
  Функция ВыгрузитьТабДокВовременноеХранилищеНаСервере(НазваниеСтраницыЭксель, УникальныйИдентификатор, ЗаказыНаДоставку = Неопределено, РежимВремени)
     
      Макет = Обработки.РабочееМестоМенеджераПоДоставке.ПолучитьМакет("" + НазваниеСтраницыЭксель);
      ОбластьШапка = Макет.ПолучитьОбласть("Шапка");
      ОбластьСтрока = Макет.ПолучитьОбласть("Строка");
     
      ТабДок = Новый ТабличныйДокумент;
      ТабДок.Вывести(ОбластьШапка);
      ЗаполнитьСтрокиДокумента(Макет, ТабДок, НазваниеСтраницыЭксель, ЗаказыНаДоставку, РежимВремени);
     
      АдресВременногоХранилища = ПоместитьВоВременноеХранилище(ТабДок, УникальныйИдентификатор);
     
      Возврат АдресВременногоХранилища;
     
  КонецФункции

 

Шаг 3. Шапки страниц с названием колонок (именно по ним Яндекс.Маршрутизация считывает значения параметров) мы храним в виде макетов и называем их как страницы в формируемом Excel. Это позволяет использовать парадигму, так называемого, самопишушегося кода

  Макет = Обработки.РабочееМестоМенеджераПоДоставке.ПолучитьМакет("" + НазваниеСтраницыЭксель);

 

 

Orders

 

Vehicles

Depot

 

Options

 

Шаг 4. Самая неаккуратная процедура заполнения строки:

  &НаСервере
  Процедура ЗаполнитьСтрокиДокумента(Макет, ТабДок, НазваниеСтраницыЭксель, ЗаказыНаДоставку = Неопределено, РежимВремени)
      ////////////////////  "Depot"
      Если НазваниеСтраницыЭксель = "Depot" Тогда
          ПараметрыСклада = ПолучитьЗаполненныеПараметрыСтрокиСклада();
          //Ставим на вечер другой склад
          Если РежимВремени.СейчасВечер Тогда
              ПараметрыСклада.НомерСтроки = "2";
              ПараметрыСклада.НазваниеСклада = "МСК- Опт - ДОПЛАНИРОВАНИЕ";
          КонецЕсли;
  
          ЗаполнитьСтрокуИДобавитьТабДок(Макет, ТабДок, ПараметрыСклада);
  
      ///////////////////  "Options"
      ИначеЕсли НазваниеСтраницыЭксель = "Options" Тогда
          ЗаполнитьСтрокуИДобавитьТабДок(Макет, ТабДок, ПолучитьЗаполненныеПараметрыСтрокиОпций());
      ///////////////////  "Vehicles"   
      ИначеЕсли НазваниеСтраницыЭксель = "Vehicles" Тогда
          Машины = ПолучитьСписокМашин();
          Для Каждого Автомобиль ИЗ Машины Цикл
              ПараметрыМашины = ПолучитьЗаполненныеПараметрыСтрокиМашины();           
  
              ЗаполнитьЗначенияСвойств(ПараметрыМашины,Автомобиль.ТранспортноеСредство);
             
              ПараметрыМашины.ЯМ_НазваниеМашины = "" + Автомобиль.ТранспортноеСредство + " - [" + Автомобиль.Водитель + "]"; 
              ПараметрыМашины.ЯМ_ЛогинКурьера = Автомобиль.ТранспортноеСредство["ЯМ_ЛогинКурьера" + РежимВремени.Суффикс];
              ПараметрыМашины.ЯМ_ВремяНачалаИКонцаСмены = Автомобиль.ТранспортноеСредство["ЯМ_ВремяНачалаИКонцаСмены" + РежимВремени.Суффикс];
             
              ЗаполнитьСтрокуИДобавитьТабДок(Макет, ТабДок, ПараметрыМашины);
  
          КонецЦикла;
      ///////////////////  "Orders"   
      ИначеЕсли НазваниеСтраницыЭксель = "Orders" Тогда
          Для Каждого СтрЗаказа ИЗ ЗаказыНаДоставку Цикл
              ПараметрыЗаказа = ПолучитьЗаполненныеПараметрыСтрокиЗаказов();
              УточненныеПараметрыЗаказа = ПолучитьУточненныеПараметрыЗаказа(СтрЗаказа);           
  
              ЗаполнитьЗначенияСвойств(ПараметрыЗаказа, УточненныеПараметрыЗаказа);           
  
              //Подбираем наиболее подходящее временное окно
              Если УточненныеПараметрыЗаказа.Свойство("ВременноеОкно" + РежимВремени.СуффиксОбщий) Тогда
                  ПараметрыЗаказа.ВременноеОкно = УточненныеПараметрыЗаказа["ВременноеОкно" + РежимВремени.СуффиксОбщий];
              ИначеЕсли УточненныеПараметрыЗаказа.Свойство("ВременноеОкно" + РежимВремени.Суффикс) Тогда
                  ПараметрыЗаказа.ВременноеОкно = УточненныеПараметрыЗаказа["ВременноеОкно" + РежимВремени.Суффикс];
              КонецЕсли;
             
              //ПараметрыМашины.ЯМ_НазваниеМашины = "" + Автомобиль.ТранспортноеСредство + " - [" + Автомобиль.Водитель + "]"; 
              //ПараметрыМашины.ЯМ_ЛогинКурьера = Автомобиль.ТранспортноеСредство.ЯМ_ЛогинКурьера_ДЕНЬ;
              //ПараметрыМашины.ЯМ_ВремяНачалаИКонцаСмены = Автомобиль.ТранспортноеСредство.ЯМ_ВремяНачалаИКонцаСмены_ДЕНЬ;
             
              ЗаполнитьСтрокуИДобавитьТабДок(Макет, ТабДок, ПараметрыЗаказа);
  
          КонецЦикла;
      КонецЕсли;   
  КонецПроцедуры

Тут мы себе помогли, сделав предзаполнение параметров процедурами:

  ПолучитьЗаполненныеПараметрыСтрокиСклада()
  ПолучитьЗаполненныеПараметрыСтрокиОпций()
  ПолучитьЗаполненныеПараметрыСтрокиМашины()
  ПолучитьЗаполненныеПараметрыСтрокиЗаказов()

Функции облегчают чтение кода, а по сути являются сохранённым результатом нашего 2-х месячного подбора параметров. Значения по умолчанию для нашей компании. Приведу их в спойлере, чтобы не загромождать статью:

  &НаСервере
  Процедура ЗаполнитьСтрокиДокумента(Макет, ТабДок, НазваниеСтраницыЭксель, ЗаказыНаДоставку = Неопределено, РежимВремени)
      ////////////////////  "Depot"
      Если НазваниеСтраницыЭксель = "Depot" Тогда
          ПараметрыСклада = ПолучитьЗаполненныеПараметрыСтрокиСклада();
          //Ставим на вечер другой склад
          Если РежимВремени.СейчасВечер Тогда
              ПараметрыСклада.НомерСтроки = "2";
              ПараметрыСклада.НазваниеСклада = "МСК- Опт - ДОПЛАНИРОВАНИЕ";
          КонецЕсли;
  
          ЗаполнитьСтрокуИДобавитьТабДок(Макет, ТабДок, ПараметрыСклада);
         
      ///////////////////  "Options"
      ИначеЕсли НазваниеСтраницыЭксель = "Options" Тогда
          ЗаполнитьСтрокуИДобавитьТабДок(Макет, ТабДок, ПолучитьЗаполненныеПараметрыСтрокиОпций());
      ///////////////////  "Vehicles"   
      ИначеЕсли НазваниеСтраницыЭксель = "Vehicles" Тогда
          Машины = ПолучитьСписокМашин();
          Для Каждого Автомобиль ИЗ Машины Цикл
              ПараметрыМашины = ПолучитьЗаполненныеПараметрыСтрокиМашины();
             
              ЗаполнитьЗначенияСвойств(ПараметрыМашины,Автомобиль.ТранспортноеСредство);
             
              ПараметрыМашины.ЯМ_НазваниеМашины = "" + Автомобиль.ТранспортноеСредство + " - [" + Автомобиль.Водитель + "]"; 
              ПараметрыМашины.ЯМ_ЛогинКурьера = Автомобиль.ТранспортноеСредство["ЯМ_ЛогинКурьера" + РежимВремени.Суффикс];
              ПараметрыМашины.ЯМ_ВремяНачалаИКонцаСмены = Автомобиль.ТранспортноеСредство["ЯМ_ВремяНачалаИКонцаСмены" + РежимВремени.Суффикс];
             
              ЗаполнитьСтрокуИДобавитьТабДок(Макет, ТабДок, ПараметрыМашины);
  
          КонецЦикла;
      ///////////////////  "Orders"   
      ИначеЕсли НазваниеСтраницыЭксель = "Orders" Тогда
          Для Каждого СтрЗаказа ИЗ ЗаказыНаДоставку Цикл
              ПараметрыЗаказа = ПолучитьЗаполненныеПараметрыСтрокиЗаказов();
              УточненныеПараметрыЗаказа = ПолучитьУточненныеПараметрыЗаказа(СтрЗаказа);
             
              ЗаполнитьЗначенияСвойств(ПараметрыЗаказа, УточненныеПараметрыЗаказа);
             
              //Подбираем наиболее подходящее временное окно
              Если УточненныеПараметрыЗаказа.Свойство("ВременноеОкно" + РежимВремени.СуффиксОбщий) Тогда
                  ПараметрыЗаказа.ВременноеОкно = УточненныеПараметрыЗаказа["ВременноеОкно" + РежимВремени.СуффиксОбщий];
              ИначеЕсли УточненныеПараметрыЗаказа.Свойство("ВременноеОкно" + РежимВремени.Суффикс) Тогда
                  ПараметрыЗаказа.ВременноеОкно = УточненныеПараметрыЗаказа["ВременноеОкно" + РежимВремени.Суффикс];
              КонецЕсли;
             
              //ПараметрыМашины.ЯМ_НазваниеМашины = "" + Автомобиль.ТранспортноеСредство + " - [" + Автомобиль.Водитель + "]"; 
              //ПараметрыМашины.ЯМ_ЛогинКурьера = Автомобиль.ТранспортноеСредство.ЯМ_ЛогинКурьера_ДЕНЬ
              //ПараметрыМашины.ЯМ_ВремяНачалаИКонцаСмены = Автомобиль.ТранспортноеСредство.ЯМ_ВремяНачалаИКонцаСмены_ДЕНЬ;
             
              ЗаполнитьСтрокуИДобавитьТабДок(Макет, ТабДок, ПараметрыЗаказа);
  
          КонецЦикла;
         
      КонецЕсли;
  
  КонецПроцедуры

Далее мы уточняем значение параметров строки и запускаем лаконичную  ЗаполнитьСтрокуИДобавитьТабДок(Макет, ТабДок, ЗначенияПараметров).

  &НаСервере
  Процедура ЗаполнитьСтрокуИДобавитьТабДок(Макет, ТабДок, ЗначенияПараметров)
     
      ОбластьСтрока = Макет.ПолучитьОбласть("Строка");
      ЗаполнитьЗначенияСвойств(ОбластьСтрока.Параметры, ЗначенияПараметров);
      ТабДок.Вывести(ОбластьСтрока);
     
  КонецПроцедуры

Такой минималистичный код получился благодаря нашим ПолучитьЗаполненныеПараметры…() и тому, что мы идентично назвали параметры ячеек в макетах страниц (Проблемы шаг 3)

 

Шаг 5. Как хранить дополнительные параметры? Чтобы ускорить разработку, мы решили хранить все параметры в реквизитах Справочников. Сейчас, я бы хранил все параметры, используя стандартный механизм 1С Дополнительных Значений. Это обезопасит пользователей от потери данных, введенных в эти доп. реквизиты, например, если кто-то из сотрудников (админ, конечно, кто же ещё) снесёт расширение, а потом его опять запустит. Но было решено так:

 

 

Внешний вид карточек ТС, клиентов, склада можно посмотреть в первой части статьи — Умная маршрутизация: кейс интеграции с 1С (часть 1) 

 

Шаг 6. Но ведь нам нужны данные — список заказов. А в обработке РабочееМестоМенеджераПоДоставке этот самый список не Список, а деревоЗначений. Что ж, берём поисковик и рисуем рекурсию:

  &НаСервере
  Функция ПолучитьЗаказыНаДоставку()
     
      ДеревоЗнач = РеквизитФормыВЗначение("РаспоряженияНаДоставку");
      ЗаказыНаДоставку = ПолучитьПустуюТЗ();
      ОбходДереваДетально(ДеревоЗнач, ЗаказыНаДоставку);
     
      Возврат ЗаказыНаДоставку;
  
  КонецФункции
  
  &НаСервере
  Функция ПолучитьПустуюТЗ()
     
      ТЗ = Новый ТаблицаЗначений;
      ТЗ.Колонки.Добавить("Распоряжение");
      ТЗ.Колонки.Добавить("Перевозчик");
      ТЗ.Колонки.Добавить("Адрес");
      ТЗ.Колонки.Добавить("Номер");
      ТЗ.Колонки.Добавить("ПолучательОтправитель");
      ТЗ.Колонки.Добавить("Вес");
  
      Возврат ТЗ;
     
  КонецФункции
  
  //Рекурсивная процедура
  &НаСервере
  Процедура ОбходДереваДетально(ПереданноеДер, ТЗ)
     
      Для Каждого СтрПолученногоДерева Из ПереданноеДер.Строки Цикл
         
          //Сообщить(СтрПолученногоДерева.Номенклатура);
          Если ЗначениеЗаполнено(СтрПолученногоДерева.Распоряжение) Тогда
              НовСтр = ТЗ.Добавить();
              ЗаполнитьЗначенияСвойств(НовСтр, СтрПолученногоДерева);
          КонецЕсли; 
         
          Если СтрПолученногоДерева.Строки.Количество()>0 Тогда           
              ОбходДереваДетально(СтрПолученногоДерева, ТЗ);           
          КонецЕсли;
         
      КонецЦикла;
     
  КонецПроцедуры

Ну вот и всё! Многостраничный EXCEL с заполненными строками для Яндекс.Маршрутизации сформирован, сохранён, казалось бы, можно идти пить чай, но нет… нужно теперь загрузить ответ от Яндекс.Маршрутизации. Ну его этот чай, поехали!

 

Загружаем данные в 1С

 

Шаг 7. Теперь файл — на клиенте, а обработка — на сервере.

  &НаКлиенте
  Процедура ЯМ_ЯМ_ЗагрузитьМаршрутныеЛистыИзЭксельПосле(Команда)   
      СохранитьПрочитатьЭксельЯндексМаршрутизации("Открытие");
  КонецПроцедуры

Процедура получилась универсальной (см. шаг 1), несколько строчек помещаем во временное хранилище и отправляем на сервер:

  Адрес = ПоместитьВоВременноеХранилище(Новый ДвоичныеДанные(ДиалогФайла.ПолноеИмяФайла));
              ПрочитатьИОбработатьФайлЯндексМаршрутизации(Адрес, ДиалогФайла.ПолноеИмяФайла);
  
  &НаСервере
  Процедура ПрочитатьИОбработатьФайлЯндексМаршрутизации(Адрес, ИмяФайла)
         //вывод в таблицу значений
         ЯМ_ИнтеграцияСЯндексМаршрутизацией.ПрочитатьИОбработатьФайлЯндексМаршрутизации(Адрес, ИмяФайла);  
  КонецПроцедуры

 

Шаг 8. Используем современные методы чтения Excel:

  Процедура ПрочитатьИОбработатьФайлЯндексМаршрутизации(Адрес, ИмяФайла) Экспорт
      //Необходимо чтение файлов XLS или XLSX
      Расширение = Прав(ИмяФайла, 4);
      Расширение = СтрЗаменить(Расширение, ".", "");
     
      ФайлПриемник = ПолучитьИмяВременногоФайла(Расширение);
      ДанныеХранилища = ПолучитьИзВременногоХранилища(Адрес);
      ДанныеХранилища.Записать(ФайлПриемник);
    
      ТабДок = Новый ТабличныйДокумент;
      ТабДок.Прочитать(ФайлПриемник, СпособЧтенияЗначенийТабличногоДокумента.Значение); 
    
      //вывод в таблицу значений   
      ПЗ = Новый ПостроительЗапроса;
      //ПЗ.ИсточникДанных = Новый ОписаниеИсточникаДанных(ТабДок.Область());
      //Загружаем не весь Эксель, а только лист "Маршруты"
      ПЗ.ИсточникДанных = Новый ОписаниеИсточникаДанных(ТабДок.ПолучитьОбласть("Маршруты").Область());
      ПЗ.ДобавлениеПредставлений = ТипДобавленияПредставлений.НеДобавлять;
      ПЗ.ЗаполнитьНастройки();
      ПЗ.Выполнить();
     
      ТаблицаЗначений = ПЗ.Результат.Выгрузить();
      ДозаполнитьТЗДаннымиДляЗагрузки(ТаблицаЗначений);  
     
      ЗагрузитьТочкиВМаршрутныеЛисты(ТаблицаЗначений);  
  
  Конецпроцедуры

Вот этот кусок кода мой любимый:

      //вывод в таблицу значений   
      ПЗ = Новый ПостроительЗапроса;
      //ПЗ.ИсточникДанных = Новый ОписаниеИсточникаДанных(ТабДок.Область());
      //Загружаем не весь Эксель, а только лист "Маршруты"
      ПЗ.ИсточникДанных = Новый ОписаниеИсточникаДанных(ТабДок.ПолучитьОбласть("Маршруты").Область());
      ПЗ.ДобавлениеПредставлений = ТипДобавленияПредставлений.НеДобавлять;
      ПЗ.ЗаполнитьНастройки();
      ПЗ.Выполнить();
      ТаблицаЗначений = ПЗ.Результат.Выгрузить();

Он подходит для любого Excel, нужно лишь подставить верное название листа. Если Excel одностраничный, то пишем так:

      ПЗ = Новый ПостроительЗапроса;
      ПЗ.ИсточникДанных = Новый ОписаниеИсточникаДанных(ТабличныйДокумент.Область());
      ПЗ.ДобавлениеПредставлений = ТипДобавленияПредставлений.НеДобавлять;
      ПЗ.ЗаполнитьНастройки();
      ПЗ.Выполнить();
      ТаблицаЗначений = ПЗ.Результат.Выгрузить();

Т.е. на входе любой Excel, а на выходе — удобная ТаблицаЗначений, в которой колонки названы как в исходном Excel. И не нужно сопоставлять колонки по ячейкам.

 

Шаг 9. Привязываем данные из Excel к своим данным в 1С. Поэтому привожу код без пояснений, он очень персональный, и вам точно не подойдёт, но для целостной картины нужен:

  Процедура ДозаполнитьТЗДаннымиДляЗагрузки(ТЗ)
  
      ТЗ.Колонки.Добавить("Вес");
      ТЗ.Колонки.Добавить("КлючСвязи");
      ТЗ.Колонки.Добавить("Распоряжение");
      ТЗ.Колонки.Добавить("Перевозчик");
      ТЗ.Колонки.Добавить("ПолучательОтправитель");
      ТЗ.Колонки.Добавить("Склад");
      ТЗ.Колонки.Добавить("Доставлено");
      ТЗ.Колонки.Добавить("ДоставляетсяПолностью");
      ТЗ.Колонки.Добавить("ВремяС");
      ТЗ.Колонки.Добавить("ВремяПо");
      ТЗ.Колонки.Добавить("АдресЗначенияПолей");
      ТЗ.Колонки.Добавить("ДополнительнаяИнформация");
      ТЗ.Колонки.Добавить("ИдентификаторМашиныДля1С");
     
      ПрефиксУИД = "" + Формат(ТекущаяДата(),"ДФ=ddMMyyhhmmss") + "_";
     
      Для Каждого Стр ИЗ ТЗ Цикл
          Стр.Вес = Стр.ВесЗаказа_Кг;
          Если ЗначениеЗаполнено(Стр.ИдентификаторМашины) Тогда
              Стр.ИдентификаторМашиныДля1С = СформироватьПравильныйКлюч(Стр.ИдентификаторМашины);
              Если ЗначениеЗаполнено(Стр.НомерЗаказа) И НЕ Стр.ТипТочки = "Склад" Тогда
                  ГУИДНачало = СтрНайти(Стр.НомерЗаказа,"[GUID ");
                  ГУИД = Сред(Стр.НомерЗаказа,ГУИДНачало + 6,36);
                  НовыйGUID = Новый УникальныйИдентификатор(ГУИД);               
  
                  //Если Стр.НомерЗаказа = "ЭПУТ-003737 - 02.10.2020 11:34:19 [GUID 8dcf91c6-03cc-11eb-9aff-c86000c31ebb]"
                  //    ИЛИ Стр.НомерЗаказа = "ЭПУТ-003716 - 02.10.2020 11:34:19 [GUID 07cd4366-02f6-11eb-9aff-c86000c31ebb]" Тогда
                  //    а=1;
                  //КонецЕсли;
                
                  ЗаказКлиента = Документы.ЗаказКлиента.ПолучитьСсылку(НовыйGUID);              
  
                  Если СтрНайти("" + ЗаказКлиента,"Объект не найден") > 0 Тогда
                      ЗаказКлиента = Документы.ПоручениеЭкспедитору.ПолучитьСсылку(НовыйGUID);
                  КонецЕсли;
                 
                  Если СтрНайти("" + ЗаказКлиента,"Объект не найден") > 0 Тогда
                      ЗаказКлиента = Документы.ЗаказНаПеремещение.ПолучитьСсылку(НовыйGUID);
                  КонецЕсли;
                 
                  Если ЗначениеЗаполнено(ЗаказКлиента) Тогда
                      Стр.Распоряжение = ЗаказКлиента;
                      //Если ТипЗнч(ЗаказКлиента) = Тип("ДокументСсылка.ЗаказНаПеремещение") Тогда
                      //    Стр.Склад = ЗаказКлиента.СкладОтправитель;
                      //Иначе
                      //    Стр.Склад = ЗаказКлиента.Склад;
                      //КонецЕсли;
                      //Пока делаем для всех склад МСК-Опт
                      Стр.Склад = Справочники.Склады.НайтиПоНаименованию("МСК- Опт");
                      Стр.ПолучательОтправитель = Справочники.Партнеры.НайтиПоНаименованию(Стр.ТорговаяСеть);
                      Стр.Доставлено = Ложь;
                      Стр.ДоставляетсяПолностью = Истина;
                      Стр.Адрес = ЗаказКлиента.АдресДоставки;
                      Стр.АдресЗначенияПолей = ЗаказКлиента.АдресДоставкиЗначенияПолей;
                      Стр.ДополнительнаяИнформация = ЗаказКлиента.ДополнительнаяИнформацияПоДоставке;
                      Если ТипЗнч(ЗаказКлиента) = Тип("ДокументСсылка.ЗаказКлиента")
                          ИЛИ ТипЗнч(ЗаказКлиента) = Тип("ДокументСсылка.ЗаказНаПеремещение") Тогда
                          Стр.Перевозчик = ЗаказКлиента.ПеревозчикПартнер;
                          Если ЗначениеЗаполнено(Стр.Перевозчик) Тогда
                              Стр.Адрес = ЗаказКлиента.АдресДоставкиПеревозчика;
                              Стр.АдресЗначенияПолей = ЗаказКлиента.АдресДоставкиПеревозчикаЗначенияПолей;
                          КонецЕсли;
                      КонецЕсли;
                      //Уникальный номер связи строк
                      АдресУИД = Стр.Адрес;
                      АдресУИД = СтрЗаменить(АдресУИД," ","");
                      АдресУИД = СтрЗаменить(АдресУИД,",","");
                      АдресУИД = СтрЗаменить(АдресУИД,".","");
                      Стр.КлючСвязи = "" + ПрефиксУИД + Прав(АдресУИД, 12) + Лев(АдресУИД, 12) + Прав(АдресУИД, 12);
                  КонецЕсли;
              КонецЕсли;
          КонецЕсли;
      КонецЦикла;
  КонецПроцедуры

 

Шаг 10. И, наконец, привязываем наши Заказы к машинам (Маршрутным листам, сформированным Яндекс.Маршрутизацией):

  Процедура ЗагрузитьТочкиВМаршрутныеЛисты(ТЗ) Экспорт
     
      //Обрабатываем ТЗ
      ЗаданияНаПеревозку = НайтиВсеЗаданияНаПеревозку(ТЗ);
     
      Для Каждого ТекЗадание Из ЗаданияНаПеревозку Цикл
          Если ЗначениеЗаполнено(ТекЗадание.Значение) Тогда
             
              П = Новый Структура;
              П.Вставить("ИдентификаторМашиныДля1С",ТекЗадание.Ключ);
              НайденныеСтроки = ТЗ.НайтиСтроки(П);
             
              Если НайденныеСтроки.Количество() > 2 Тогда
                  Попытка
                      //Очищаем зание перед загрузкой
                      ОбЗадание = ТекЗадание.Значение.ПолучитьОбъект();
                      ОбЗадание.Маршрут.Очистить();
                      ОбЗадание.Распоряжения.Очистить();
                      ОбЗадание.ЯМ_Км_План = 0;        
                     
                      ТЗНайденныеСтроки = ТЗ.СкопироватьКолонки();
                     
                      Для Каждого Стр Из НайденныеСтроки Цикл
                                                                         
                          //Считаем километры ЯМ_Км_План
                          Если ЗначениеЗаполнено(Стр.ДлинаПутиДоТочки_Км) Тогда
                              ОбЗадание.ЯМ_Км_План = ОбЗадание.ЯМ_Км_План + Стр.ДлинаПутиДоТочки_Км;
                              //ОбЗадание.ЯМ_Км_Факт = ОбЗадание.ЯМ_Км_Факт + Стр.ДлинаПутиДоТочки_Км;
                          КонецЕсли;
                         
                          Если Стр.ТипТочки = "Склад" Тогда
                              //Парсим время
                              Попытка
                                  Если СтрНайти(Стр.ВремяПрибытияНаЗаказ_Склад,"1.") > 0 Тогда
                                      ТекДатаСтрока = "" + Формат(ОбЗадание.Дата + 24*60*60,"ДФ=yyyyMMdd");
                                      СтрВремя = СтрЗаменить(Стр.ВремяПрибытияНаЗаказ_Склад,"1.","");
                                  Иначе
                                      ТекДатаСтрока = "" + Формат(ОбЗадание.Дата,"ДФ=yyyyMMdd");
                                      СтрВремя = Стр.ВремяПрибытияНаЗаказ_Склад;
                                  КонецЕсли;
                                  СтрВремя = СтрЗаменить(СтрВремя,":","");
     
                                  ТекДата = Дата(ТекДатаСтрока + СтрВремя);
                                 
                                  Если Не ЗначениеЗаполнено(ОбЗадание.ДатаВремяРейсаПланС) Тогда
                                      ОбЗадание.ДатаВремяРейсаПланС      = ТекДата;
                                  Иначе
                                      Если ТекДата > ОбЗадание.ДатаВремяРейсаПланС Тогда
                                          ОбЗадание.ДатаВремяРейсаПланПо = ТекДата;
                                      Иначе
                                          ОбЗадание.ДатаВремяРейсаПланПо = ОбЗадание.ДатаВремяРейсаПланС; 
                                          ОбЗадание.ДатаВремяРейсаПланС  = ТекДата;
                                      КонецЕсли;
                                  КонецЕсли;
                              Исключение
                              КонецПопытки;
                          Иначе
                              //Копируем строки массива в ТЗ,чтобы потом загрузить в документ
                              НовСтр_ТЗНайденныеСтроки = ТЗНайденныеСтроки.Добавить();
                              ЗаполнитьЗначенияСвойств(ТЗНайденныеСтроки,Стр);
                              НовСтр_Маршрут         = ОбЗадание.Маршрут.Добавить();
                              ЗаполнитьЗначенияСвойств(НовСтр_Маршрут,Стр);
                             
                              НовСтр_Распоряжения = ОбЗадание.Распоряжения.Добавить();
                              ЗаполнитьЗначенияСвойств(НовСтр_Распоряжения,Стр);
                          КонецЕсли;
                      КонецЦикла;
                     
                      //ТЗНайденныеСтроки.Свернуть("Адрес,ВремяС,ВремяПо,КлючСвязи,Доставлено,АдресЗначенияПолей,ДополнительнаяИнформация","Вес");
                      //ОбЗадание.Маршрут.Загрузить(ТЗНайденныеСтроки);
                     
                      ТЗНов = ОбЗадание.Маршрут.Выгрузить();
                      ТЗНов.Свернуть("Адрес,Зона,ВремяС,ВремяПо,КлючСвязи,Доставлено,АдресЗначенияПолей,ДополнительнаяИнформация","Вес,Объем");
                      ОбЗадание.Маршрут.Загрузить(ТЗНов);
                     
                      //ОбЗадание.Статус = Перечисления.СтатусыЗаданийНаПеревозку.КПогрузке;
                      //ОбЗадание.ЯМ_ПлатимЗаЧасРаботыПередРейсом = Истина;
                      ОбЗадание.ЯМ_Час_План = (ОбЗадание.ДатаВремяРейсаПланПо - ОбЗадание.ДатаВремяРейсаПланС)/3600;
                      ОбЗадание.Записать();
                  Исключение
                  КонецПопытки;
              КонецЕсли
  
              //Заполняем документ Задание на перевозку
  
          КонецЕсли;
      КонецЦикла;
  КонецПроцедуры

Ура. Теперь можно допить чай. Осталось нарисовать для начальника волшебную кнопку “Стоимость Работ Водителей”:

  &НаКлиенте
  Процедура ЯМ_ЯМ_СтоимостьРаботВодителейПосле(Команда)
      //
      ОткрытьФорму("Отчет.ЯМ_СтоимостьРаботВодителей.Форма");
  КонецПроцедуры

 

Делаем отчёт о работе водителя

 

Шаг 11. Делаем запрос на СКД:

  ВЫБРАТЬ
      СУММА(1) КАК ВсегоТочек,
      СУММА(ВЫБОР
              КОГДА ЗаданиеНаПеревозкуМаршрут.Доставлено
                  ТОГДА 1
              ИНАЧЕ 0
          КОНЕЦ) КАК ДоставленоТочек,
      СУММА(ВЫБОР
              КОГДА ЗаданиеНаПеревозкуМаршрут.Доставлено
                  ТОГДА 0
              ИНАЧЕ 1
          КОНЕЦ) КАК НеДоставленоТочек,
      МАКСИМУМ(ЗаданиеНаПеревозкуМаршрут.Ссылка.ЯМ_ТарифЗаКм) КАК ЯМ_ТарифЗаКм,
      МАКСИМУМ(ЗаданиеНаПеревозкуМаршрут.Ссылка.ЯМ_ТарифЗаТочку) КАК ЯМ_ТарифЗаТочку,
      МАКСИМУМ(ЕСТЬNULL(ЗаданиеНаПеревозкуМаршрут.Ссылка.ЯМ_Км_План, 0)) КАК ЯМ_Км_План,
      МАКСИМУМ(ЕСТЬNULL(ЗаданиеНаПеревозкуМаршрут.Ссылка.ЯМ_Км_Факт, 0)) КАК ЯМ_Км_Факт,
      МАКСИМУМ(ЗаданиеНаПеревозкуМаршрут.Ссылка.ЯМ_СтоимостьРейса) КАК ЯМ_СтоимостьРейса,
      ЗаданиеНаПеревозкуМаршрут.Ссылка.ТранспортноеСредство КАК ТранспортноеСредство,
      ЗаданиеНаПеревозкуМаршрут.Ссылка.Водитель КАК Водитель,
      ЗаданиеНаПеревозкуМаршрут.Ссылка КАК ЗаданиеНаПеревозку,
      ЕСТЬNULL(ЗаданиеНаПеревозкуМаршрут.Ссылка.ЯМ_Час_План, 0) КАК ЯМ_Час_План,
      ЕСТЬNULL(ЗаданиеНаПеревозкуМаршрут.Ссылка.ЯМ_Час_Факт, 0) КАК ЯМ_Час_Факт
  ПОМЕСТИТЬ ВТТочки
  ИЗ
      Документ.ЗаданиеНаПеревозку.Маршрут КАК ЗаданиеНаПеревозкуМаршрут
  ГДЕ
      ЗаданиеНаПеревозкуМаршрут.Ссылка.Дата >= &ДатаНачала
      И ЗаданиеНаПеревозкуМаршрут.Ссылка.Дата <= &ДатаОкончания
      И ЗаданиеНаПеревозкуМаршрут.Ссылка.Проведен = ИСТИНА
  СГРУППИРОВАТЬ ПО
      ЗаданиеНаПеревозкуМаршрут.Ссылка,
      ЗаданиеНаПеревозкуМаршрут.Ссылка.ТранспортноеСредство,
      ЗаданиеНаПеревозкуМаршрут.Ссылка.Водитель,
      ЕСТЬNULL(ЗаданиеНаПеревозкуМаршрут.Ссылка.ЯМ_Час_Факт, 0),
      ЕСТЬNULL(ЗаданиеНаПеревозкуМаршрут.Ссылка.ЯМ_Час_План, 0)
  ;
  ////////////////////////////////////////////////////////////////////////////////
  ВЫБРАТЬ
      ВТТочки.ВсегоТочек КАК ВсегоТочек,
      ВТТочки.ДоставленоТочек КАК ДоставленоТочек,
      ВТТочки.НеДоставленоТочек КАК НеДоставленоТочек,
      ВТТочки.ЯМ_ТарифЗаКм КАК ЯМ_ТарифЗаКм,
      ВТТочки.ЯМ_ТарифЗаТочку КАК ЯМ_ТарифЗаТочку,
      ВТТочки.ЯМ_Км_План КАК ЯМ_Км_План,
      ВТТочки.ЯМ_Км_Факт КАК ЯМ_Км_Факт,
      ВТТочки.ЯМ_СтоимостьРейса КАК ЯМ_СтоимостьРейса,
      ВТТочки.ТранспортноеСредство КАК ТранспортноеСредство,
      ВТТочки.Водитель КАК Водитель,
      ВТТочки.ЗаданиеНаПеревозку КАК ЗаданиеНаПеревозку,
      ВТТочки.ЯМ_Час_План КАК ЯМ_Час_План,
      ВТТочки.ЯМ_Час_Факт КАК ЯМ_Час_Факт,
      ВТТочки.ЯМ_Км_План - ВТТочки.ЯМ_Км_Факт КАК Км_Разница,
      ВТТочки.ЯМ_Час_План - ВТТочки.ЯМ_Час_Факт КАК Час_Разница
  ИЗ
      ВТТочки КАК ВТТочки

 

Шаг 12. Проставляем Ресурсы и Настройки:

 

 

 

Шаг 13. Отчёт готов!

 

 

Ну да, не 3 проблемы, а 13. Обычно при оценке проекта первое впечатление не врёт, поэтому… его нужно смело умножать на 3 и прибавлять ещё 2, на всякий случай. Но кто бы научил правильно считать эти эстимэйты…

 

Благодарю за ваше бесценное время!

 

 

Само расширение можно скачать из вложения к статье. Там же можно увидеть полный листинг кода.

Шаблон Excel можно взять на сайте Яндекс.Маршрутизации: https://yandex.ru/routing/doc/vrp/concepts/example.html

Там же есть описание всех параметров: https://yandex.ru/routing/doc/vrp/concepts/excel-fill-guide.html

Также во вложении к публикации шаблон с примером заполнения EXCEL, который мы после 2-х месяцев подбора выработали под потребности нашей компании (напомню, 3-5 машин, около 40 заказов в день, доставка в две смены, без выходных). 

has been added to your cart:
Оформление заказа