При разработке сложных отчетов в системе компоновки данных (СКД) разработчики часто сталкиваются с задачей интеграции данных из разных информационных баз. Типичный сценарий: получение фактического пробега автомобилей или водителей из внешней базы (например, «Управление автотранспортом» или УАТ) через COM-соединение и последующее сопоставление этих данных с документами маршрутных заданий в текущей базе.
Однако в процессе реализации такого сопоставления возникает распространенная проблема: в сформированном отчете пройденное расстояние по всем водителям дублируется или показывает одинаковое, сильно завышенное число. При этом при пошаговой отладке кода во внешней таблице значений показатели по каждому водителю рассчитываются абсолютно верно. Разберем детально, почему так происходит, и пошагово исправим эту ошибку.
Данная проблема носит комплексный характер и обычно вызывается следующими факторами:
СправочникСсылка.ФизическиеЛица) со строковым представлением или COM-объектом из внешней базы приводит к потере связи или дублированию записей.Проанализируем ситуацию и пошагово исправим все три слабых места нашей архитектуры.
Для начала исправим формулу расчета пройденного расстояния. Разность показаний спидометра должна рассчитываться как вычитание показания при выезде из показания при возвращении (а не наоборот). Кроме того, нам необходимо жестко сгруппировать результат по водителю, чтобы исключить дублирование записей на уровне самого COM-запроса.
Рассмотрим правильный вариант построения внешнего запроса:
// Создаем объект запроса в контексте COM-соединения
ЗапросВнешний = Соединение.NewObject("Запрос");
ЗапросВнешний.Текст =
"ВЫБРАТЬ
| уатПутевойЛист.Водитель1 КАК Водитель,
| уатПутевойЛист.Водитель1.Наименование КАК ВодительНаименование,
| СУММА(уатПутевойЛист.SпидометрВозвращения - уатПутевойЛист.СпидометрВыезда) КАК ПройденноеРасстояние
|ИЗ
| Документ.уатПутевойЛист КАК уатПутевойЛист
|ГДЕ
| уатПутевойЛист.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания
|СГРУППИРОВАТЬ ПО
| уатПутевойЛист.Водитель1,
| уатПутевойЛист.Водитель1.Наименование";
ЗапросВнешний.УстановитьПараметр("ДатаНачала", НачалоДня(ДатаНачалаОтчета));
ЗапросВнешний.УстановитьПараметр("ДатаОкончания", КонецДня(ДатаОкончанияОтчета));
РезультатЗапроса = ЗапросВнешний.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Использование агрегирующей функции СУММА и группировки по водителю гарантирует, что внешняя база вернет строго одну результирующую строку для каждого уникального водителя с его суммарным пробегом за выбранный период.
При обходе выборки COM-объектов часто возникает ошибка «Метод объекта не обнаружен». Это связано с тем, что уникальный идентификатор ссылки в 1С является методом, а не свойством, и при работе через COM-соединение его необходимо вызывать со скобками: УникальныйИдентификатор(). Кроме того, все типы данных внешней базы обязательно нужно конвертировать в примитивные типы (строки, числа) нашей текущей базы с помощью метода String().
Разберем, как правильно наполнить локальную Таблицу Значений данными из COM-соединения:
// Создаем локальную таблицу значений для последующей передачи в СКД
ТЗ_ВнешниеДанные = Новый ТаблицаЗначений;
ТЗ_ВнешниеДанные.Колонки.Добавить("ВодительНаименование", Новый ОписаниеТипов("Строка"));
ТЗ_ВнешниеДанные.Колонки.Добавить("ВодительGUID", Новый ОписаниеТипов("Строка"));
ТЗ_ВнешниеДанные.Колонки.Добавить("ПройденноеРасстояние", Новый ОписаниеТипов("Число"));
Пока Выборка.Следующий() Цикл
СтрокаТЗ = ТЗ_ВнешниеДанные.Добавить();
СтрокаТЗ.ВодительНаименование = Соединение.String(Выборка.ВодительНаименование);
СтрокаТЗ.ПройденноеРасстояние = Выборка.ПройденноеРасстояние;
// Безопасно получаем GUID внешней ссылки в виде строки
Попытка
СтрокаТЗ.ВодительGUID = Соединение.String(Выборка.Водитель.УникальныйИдентификатор());
Исключение
СтрокаТЗ.ВодительGUID = "";
КонецПопытки;
КонецЦикла;
Если мы попытаемся связать в схеме СКД поле локальной базы (которое имеет тип ссылки, например, СправочникСсылка.ФизическиеЛица) со строковым идентификатором GUID из внешней таблицы, СКД не сможет выполнить сопоставление, и отчет выведет пустой результат или некорректно продублирует строки. Чтобы этого не произошло, мы должны предварительно сопоставить строковые GUID с локальными ссылками прямо в коде нашей базы перед вызовом компоновщика.
Проанализируем алгоритм сопоставления ссылок:
// Добавляем колонку для хранения сопоставленной локальной ссылки
ТЗ_ВнешниеДанные.Колонки.Добавить("ЛокальныйВодитель", Новый ОписаниеТипов("СправочникСсылка.ФизическиеЛица"));
Для Каждого СтрокаДанных Из ТЗ_ВнешниеДанные Цикл
Если ЗначениеЗаполнено(СтрокаДанных.ВодительGUID) Тогда
Попытка
// Преобразуем строковый GUID обратно в объект уникального идентификатора нашей базы
UID = Новый УникальныйИдентификатор(СтрокаДанных.ВодительGUID);
// Получаем ссылку на элемент справочника в текущей базе по GUID
СтрокаДанных.ЛокальныйВодитель = Справочники.ФизическиеЛица.ПолучитьСсылку(UID);
Исключение
СтрокаДанных.ЛокальныйВодитель = Справочники.ФизическиеЛица.ПустаяСсылка();
КонецПопытки;
КонецЕсли;
// Альтернативный поиск по наименованию на случай расхождения GUID баз
Если (СтрокаДанных.ЛокальныйВодитель.Пустая() Или СтрокаДанных.ЛокальныйВодитель = Неопределено)
И ЗначениеЗаполнено(СтрокаДанных.ВодительНаименование) Тогда
СтрокаДанных.ЛокальныйВодитель = Справочники.ФизическиеЛица.НайтиПоНаименованию(СтрокаДанных.ВодительНаименование, Истина);
КонецЕсли;
КонецЦикла;
Теперь в нашей таблице значений заполнено поле ЛокальныйВодитель, которое имеет правильный тип ссылки текущей базы данных. Это позволит СКД корректно сопоставить данные двух наборов.
Последний критически важный шаг выполняется непосредственно в схеме компоновки данных (СКД). Именно здесь чаще всего совершается ошибка, приводящая к дублированию.
Выполним правильную настройку СКД по шагам:
МаршрутноеЗадание.Водитель), а в качестве «Выражения приемника» — наше сопоставленное поле ЛокальныйВодитель.ПройденноеРасстояние в список ресурсов отчета и задать выражение агрегации: Сумма(ПройденноеРасстояние).Важное правило разработки в СКД: Если связываемое числовое поле из внешнего набора данных не объявлено ресурсом, СКД считает его обычной текстовой строкой или реквизитом. При выводе детальных строк отчета система будет дублировать значение этого реквизита для каждой связанной записи основного документа. Как только вы переносите числовое поле на вкладку «Ресурсы», СКД начинает правильно рассчитывать его математические итоги на всех уровнях группировки, полностью исключая дублирование.
Применяя этот комплексный подход — корректную математику и группировку на уровне внешнего запроса, приведение COM-типов к локальным ссылкам по GUID и объявление связываемых показателей ресурсами СКД — мы получим точный, оптимизированный и корректно работающий отчет.