Почему не заполняются данные в отчете СКД при COM-соединении и как исправить одинаковые значения в строках?

Программист 1С v8.3 (Управляемые формы) Управленческий учет IT и автоматизация бизнеса
← На главную

При разработке сложных отчетов в системе компоновки данных (СКД) программисты часто сталкиваются с необходимостью объединения локальных данных информационной базы с данными из внешних источников. Одним из наиболее популярных способов получения таких внешних данных является COM-соединение (COMConnector). Однако при попытке связать полученную таблицу значений с основным набором данных СКД нередко возникают две критические проблемы: либо колонки из внешнего источника остаются абсолютно пустыми, либо во всех строках отчета выводится одно и то же неверное числовое значение.

В этой статье мы подробно разберем причины возникновения этих ошибок, проанализируем внутренние механизмы работы COM-соединений и СКД, а также пошагово реализуем правильные алгоритмы сопоставления данных.

Часть 1. Почему не заполняются колонки: проблема COMObject и ссылочных типов

Рассмотрим классическую ситуацию. Разработчик подключается к внешней базе через COM-соединение, выбирает данные путевых листов (включая водителя и показатели спидометра) во внешнюю таблицу значений, а затем пытается сопоставить водителя из внешней базы с локальным справочником ФизическиеЛица. На первый взгляд код выглядит логично:


Пока Выборка.Следующий Цикл
    Строка = ТЗ.Добавить();
    Строка.СпидометрВыезда = Выборка.СпидометрВыезда;
    Строка.СпидометрВозвращения = Выборка.СпидометрВозвращения;
    // Попытка найти водителя по наименованию
    Строка.Водитель1 = Справочники.ФизическиеЛица.НайтиПоНаименованию(String(Выборка.Водитель1Ref), Истина);
КонецЦикла;

При выполнении этого кода колонки спидометра в отчете остаются пустыми, а локальный водитель не находится. Выясним причину такого поведения.

Дело в том, что при работе через COM-соединение все ссылочные объекты внешней базы (такие как СправочникСсылка.ФизическиеЛица или ДокументСсылка) передаются в вызывающую базу не в виде строк или уникальных идентификаторов, а в виде системных объектов типа COMObject.

Когда мы применяем функцию String() к переменной типа COMObject, платформа 1С не может получить реальное текстовое представление (наименование) объекта из внешней базы. Вместо этого функция возвращает стандартное строковое описание типа вроде "COM-объект". В результате метод НайтиПоНаименованию("COM-объект", Истина) возвращает пустую ссылку, сопоставление наборов данных в СКД нарушается, и все связанные показатели (спидометры) пропадают из отчета.

Для решения этой проблемы мы должны передавать из внешней базы только примитивные типы данных (строки, числа, даты). Рассмотрим два надежных способа сопоставления.

Способ А. Сопоставление по наименованию (простой вариант)

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


// В запросе внешней базы обращаемся к реквизиту Наименование
Запрос.Текст = 
    "ВЫБРАТЬ
    |   уатПутевойЛист.Водитель1.Наименование КАК ВодительНаименование,
    |   уатПутевойЛист.СпидометрВыезда КАК СпидометрВыезда,
    |   уатПутевойЛист.СпидометрВозвращения КАК СпидометрВозвращения
    |ИЗ
    |   Документ.уатПутевойЛист КАК уатПутевойЛист";

Теперь в цикле обработки выборки мы будем работать с обычной строкой, что позволит корректно найти локальный элемент справочника:


Пока Выборка.Следующий Цикл
    Строка = ТЗ.Добавить();
    Строка.СпидометрВыезда = Выборка.S_Out; // Числовой тип передается без изменений
    Строка.СпидометрВозвращения = Выборка.S_In;
    
    // Выборка.ВодительНаименование — это уже обычная строка 1С
    Строка.Водитель1 = Справочники.ФизическиеЛица.OnName(Выборка.ВодительНаименование, Истина);
КонецЦикла;

Способ Б. Сопоставление по уникальному идентификатору (GUID)

Если наименования водителей могут различаться, но базы данных были синхронизированы, наиболее надежным решением будет сопоставление по уникальному идентификатору (GUID). Мы можем получить строковое представление GUID через COM-соединение:


Пока Выборка.Следующий Цикл
    Строка = ТЗ.Добавить();
    Строка.СпидометрВыезда = Выборка.СпидометрВыезда;
    Строка.СпидометрВозвращения = Выборка.СпидометрВозвращения;
    
    // Получаем строковый GUID из COM-объекта ссылки
    СтрокаGUID = Соединение.String(Выборка.Водитель1.УникальныйИдентификатор());
    
    // Воссоздаем GUID в текущей базе и получаем локальную ссылку
    UID = Новый УникальныйИдентификатор(СтрокаGUID);
    Строка.Водитель1 = Справочники.ФизическиеЛица.ПолучитьСсылку(UID);
КонецЦикла;

Часть 2. Почему во всех строках отчета выводятся одинаковые неверные значения?

После того как данные из COM-базы начинают успешно попадать в таблицу значений, разработчики часто сталкиваются со следующей проблемой: во всех строках отчета СКД по разным водителям выводится одно и то же число (например, сумма всего пройденного расстояния по всем водителям сразу).

Проанализируем причины этой ошибки. При связывании двух наборов данных (Набора данных-Запроса и Набора данных-Объекта) СКД выполняет соединение, аналогичное левому соединению (LEFT JOIN). Ошибки дублирования значений возникают из-за двух основных причин.

Причина 1. Несовпадение типов данных в полях связи

Если в основном наборе данных (в локальном запросе) поле связи имеет тип СправочникСсылка.ФизическиеЛица, а во втором наборе (таблице значений из COM) поле связи заполнено строковыми наименованиями (тип Строка), СКД не сможет выполнить корректное сопоставление.

При несовпадении типов левое соединение внутри СКД не находит соответствий по ключу. В некоторых конфигурациях компоновщика это приводит к тому, что СКД «растягивает» общие агрегированные итоги второго набора данных на все строки первого набора, выводя везде одинаковое неверное число.

Решение: Убедитесь, что связываемые поля имеют строго одинаковый тип. Если вы связываете по ссылке — в таблице значений должна лежать реальная ссылка текущей базы (полученная методами НайтиПоНаименованию или по GUID). Если связываете по строке — в основном запросе СКД также выводите строковое поле (например, Водитель.Наименование) и связывайте его со строковым полем таблицы значений.

Причина 2. Числовые поля не добавлены в ресурсы СКД

Если числовые показатели (такие как километраж, расход топлива или пройденное расстояние) поступают из подчиненного набора данных (внешней таблицы значений) и выводятся в отчет как обычные детальные поля, СКД не понимает, как группировать и суммировать эти данные по строкам отчета.

Вместо корректного сложения или усреднения значений СКД начинает дублировать первую попавшуюся цифру из подчиненного набора во все строки группировок.

Решение: Перейдите на вкладку «Ресурсы» в схеме компоновки данных. Перенесите туда поля СпидометрВыезда, СпидометрВозвращения и ПройденноеРасстояние. Назначьте им выражение расчета (например, Сумма(СпидометрВыезда) или Максимум(СпидометрВыезда) в зависимости от логики отчета).

Часть 3. Оптимизация локального запроса СКД

Для демонстрации качественной настройки отчета обратим внимание на структуру исходного запроса. Часто разработчики делают запросы к шапке документа и вытаскивают реквизиты табличной части через точку, группируя огромное количество полей:


// Пример неоптимального запроса с обращением к ТЧ через точку шапки документа
ВЫБРАТЬ
    МаршрутноеЗадание.ДокументыРеализации.СуммаЗаявки,
    МаршрутноеЗадание.ДокументыРеализации.Заявка.МаршрутноеЗадание.Водитель
ИЗ
    Документ.МаршрутноеЗадание КАК МаршрутноеЗадание

Такой запрос заставляет СУБД выполнять тяжелые неявные левые соединения и сильно снижает скорость построения отчета. Перепишем запрос СКД правильно, обращаясь напрямую к физической таблице табличной части документа, и установим параметры отбора по датам:


ВЫБРАТЬ
    МаршрутноеЗаданиеРеализация.СуммаЗаявки КАК СуммаЗаявки,
    НАЧАЛОПЕРИОДА(МаршрутноеЗаданиеРеализация.Ссылка.Date, ДЕНЬ) КАК ДатаДоставки,
    МаршрутноеЗаданиеРеализация.Ссылка.Водитель КАК Водитель,
    МаршрутноеЗаданиеРеализация.Ссылка.Подразделение КАК Подразделение
ИЗ
    Документ.МаршрутноеЗадание.ДокументыРеализации КАК МаршрутноеЗаданиеРеализация
ГДЕ
    МаршрутноеЗаданиеРеализация.Ссылка.Проведен = ИСТИНА
    И МаршрутноеЗаданиеРеализация.Ссылка.Дата МЕЖДУ &НачалоПериода И &КонецПериода

Этот запрос отрабатывает в разы быстрее, возвращает чистые типизированные данные и легко связывается с внешней таблицей значений по полю Водитель.

Резюме по шагам для гарантированного результата

  1. В запросе к COM-базе выбирайте только примитивные типы (строки, числа, даты) во избежание проблем с типом COMObject.
  2. При заполнении внешней таблицы значений сопоставляйте COM-данные с локальной базой (по наименованию или GUID) и записывайте в ТЗ готовую локальную ссылку.
  3. В настройках связей наборов данных СКД связывайте поля одного и того же типа (например, локальную ссылку на водителя из основного запроса с локальной ссылкой из ТЗ).
  4. Все числовые показатели, пришедшие из внешнего набора данных, в обязательном порядке добавляйте на вкладку «Ресурсы» схемы компоновки данных.
← На главную