Принцип работы, какие задачи решает и кому подходит - описано в первой части статьи: Умная маршрутизация: кейс интеграции с 1С (часть 1)
Во второй части описана доработка "Рабочего места менеджера по доставке" в УТ 11.4 для обмена данными с Яндекс.Маршрутизацией.
Идея. Нам нужны 2 кнопки: “Загрузить” и “Выгрузить”
Создаём расширение, в нем обработку с тремя кнопками кода и – вуаля:
- Есть нажать на первую кнопку (левую зеленую) формируется готовый Excel. Его нужно подгрузить в Яндекс.Маршрутизацию.
- Яндекс.Маршрутизация оптимизирует маршруты водителей и возвращает Excel – Маршрутный лист с параметрами поездки. Если нажать кнопку 2, Маршрутный лист загрузится в 1С и раскидает заказы по маршрутным листам.
- Позже появилась кнопка 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 заказов в день, доставка в две смены, без выходных).