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

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

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

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

Почему стандартные механизмы СКД не работают напрямую?

Когда в качестве источника данных в СКД выбран НаборДанных-Объект, схема компоновки ожидает готовую таблицу значений. Сама СКД "не знает", откуда берутся эти данные — из текстового файла, веб-сервиса или другой базы 1С. Следовательно, СКД не может автоматически внедрить параметры &НачалоПериода и &КонецПериода во внешний запрос.

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

Рассмотрим пошагово, как реализовать этот механизм.

Шаг 1. Настройка параметров периода в СКД (Интерфейс пользователя)

Чтобы предоставить пользователю удобный выбор периода (например, «Этот месяц», «Прошлый квартал» и т. д.), настроим параметры в схеме компоновки данных:

  1. Откроем конструктор схемы компоновки данных и перейдем на вкладку Параметры.
  2. Создадим новый параметр с именем Период и типом данных СтандартныйПериод. Включим его в быстрые пользовательские настройки, чтобы он отображался на форме отчета.
  3. Для параметров НачалоПериода и КонецПериода (которые мы будем использовать в коде для фильтрации внешнего запроса) установим флаг Ограничение доступности. Это скроет их от пользователя, чтобы не загромождать интерфейс.
  4. В колонке Выражение для параметра НачалоПериода укажем выражение: &Период.ДатаНачала.
  5. В колонке Выражение для параметра КонецПериода укажем выражение: &Период.ДатаОкончания.

Благодаря этой настройке, когда пользователь выбирает, например, «Вчера», СКД автоматически запишет границы этого интервала в скрытые параметры НачалоПериода и КонецПериода.

Шаг 2. Программный перехват генерации отчета

Для того чтобы внедрить наш алгоритм получения данных, нам необходимо отключить стандартную обработку формирования отчета и написать собственный код. Перейдем в Модуль объекта нашего отчета и создадим обработчик события ПриКомпоновкеРезультата.

Рассмотрим код, который извлекает выбранные пользователем даты из компоновщика настроек:


Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
    
    // 1. Отключаем автоматический вывод отчета платформой
    СтандартнаяОбработка = Ложь; 
    
    // 2. Получаем текущие настройки компоновщика (с учетом пользовательских изменений на форме)
    Настройки = КомпоновщикНастроек.ПолучитьНастройки();
    
    // Инициализируем переменные дат по умолчанию
    ДатаНачала = '00010101';
    ДатаОкончания = '00010101';
    
    // 3. Извлекаем значения параметров НачалоПериода и КонецПериода из настроек компоновки
    ПараметрНачало = Настройки.ПараметрыДанных.НайтиЗначениеПараметра(Новый ПараметрКомпоновкиДанных("НачалоПериода"));
    Если ПараметрНачало <> Неопределено И ПараметрНачало.Использование Тогда
        ДатаНачала = ПараметрНачало.Значение;
    КонецЕсли;
    
    ПараметрКонец = Настройки.ПараметрыДанных.НайтиЗначениеПараметра(Новый ПараметрКомпоновкиДанных("КонецПериода"));
    Если ПараметрКонец <> Неопределено И ПараметрКонец.Использование Тогда
        ДатаОкончания = ПараметрКонец.Значение;
    КонецЕсли;
    
    // Если период не задан пользователем, можно установить дефолтные значения или выдать предупреждение
    Если Не ЗначениеЗаполнено(ДатаОкончания) Тогда
        ДатаОкончания = ТекущаяДата();
    КонецЕсли;

    // 4. Вызываем функцию получения данных через COM, передавая туда даты
    ТаблицаДанных = ПолучитьДанныеИзВнешнейБазы(ДатаНачала, ДатаОкончания);
    
    // 5. Связываем полученную ТЗ с внешним набором данных СКД
    ВнешниеНаборыДанных = Новый Структура;
    // "ВнешнийНаборПутевыеЛисты" должно строго совпадать с именем набора данных типа "Объект" в СКД
    ВнешниеНаборыДанных.Вставить("ВнешнийНаборПутевыеЛисты", ТаблицаДанных); 
    
    // 6. Запускаем ручной вывод отчета с нашими данными
    КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
    МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки, ДанныеРасшифровки);
    
    ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
    ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, ВнешниеНаборыДанных, ДанныеРасшифровки);
    
    ДокументРезультат.Очистить();
    ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
    ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
    ПроцессорВывода.Вывести(ПроцессорКомпоновки);
    
КонецПроцедуры

Шаг 3. Запрос к внешней базе данных через COM с фильтрацией по периоду

Теперь реализуем функцию ПолучитьДанныеИзВнешнейБазы. В ней мы установим COM-соединение, создадим объект запроса внутри внешней базы и передадим в параметры полученные даты. Это позволит отсечь лишние записи еще на уровне СУБД внешней базы.


Функция ПолучитьДанныеИзВнешнейБазы(ДатаНачала, ДатаОкончания)
    
    СтрокаПодключения = "Srvr=""ServerName"";Ref=""BaseName"";Usr=""Login"";Pwd=""Password"";";
    
    Попытка
        // Создаем COM-соединение с внешней базой
        COMConnector = Новый COMОбъект("V83.COMConnector");
        COMСоединение = COMConnector.Connect(СтрокаПодключения);
    Исключение
        ЗаписьЖурналаРегистрации("Подключение по COM", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
        ВызватьИсключение "Не удалось установить соединение с внешней базой данных!";
    КонецПопытки;
    
    // Создаем запрос внутри контекста COM-соединения
    ЗапросCOM = COMСоединение.NewObject("Запрос");
    ЗапросCOM.Текст = 
        "ВЫБРАТЬ
        |   уатПутевойЛист.Ссылка КАК Ссылка,
        |   уатПутевойЛист.Дата КАК Дата,
        |   уатПутевойЛист.Номер КАК Номер,
        |   уатПутевойЛист.ТранспортноеСредство.ГосударственныйНомер КАК ГосНомер
        |ИЗ
        |   Документ.уатПутевойЛист КАК уатПутевойЛист
        |ГДЕ
        |   уатПутевойЛист.Дата МЕЖДУ &НачалоПериода И &КонецПериода";
        
    // Передаем локальные параметры даты в COM-объект запроса
    ЗапросCOM.УстановитьПараметр("НачалоПериода", ДатаНачала);
    ЗапросCOM.УстановитьПараметр("КонецПериода", ДатаОкончания);
    
    // Выполняем и выгружаем результат во внешнюю таблицу значений
    ТЗ_COM = ЗапросCOM.Выполнить().Выгрузить();
    
    // Важно! Закрываем соединение или передаем данные локальному обработчику
    ЛокальнаяТЗ = СкопироватьТЗИзCOM(ТЗ_COM, COMСоединение);
    
    Возврат ЛокальнаяТЗ;
    
КонецФункции

Шаг 4. Преобразование типов данных (Избавление от COM-объектов)

Разберем важнейший нюанс интеграции через COM-соединение. Если мы попытаемся напрямую передать таблицу значений ТЗ_COM в СКД, отчет либо упадет с системной ошибкой, либо выведет пустые строки.

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

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


Функция СкопироватьТЗИзCOM(ТЗ_COM, COMСоединение)
    
    ЛокальнаяТЗ = Новый ТаблицаЗначений;
    
    // Явно описываем типы колонок локальной таблицы значений
    ЛокальнаяТЗ.Колонки.Добавить("СсылкаСтрокой", Новый ОписаниеТипов("Строка"));
    ЛокальнаяТЗ.Колонки.Добавить("Дата", Новый ОписаниеТипов("Дата"));
    ЛокальнаяТЗ.Колонки.Добавить("Номер", Новый ОписаниеТипов("Строка"));
    ЛокальнаяТЗ.Колонки.Добавить("ГосНомер", Новый ОписаниеТипов("Строка"));
    
    // Переносим данные построчно с конвертацией типов
    Для Каждого СтрокаCOM Из ТЗ_COM Цикл
        
        НоваяСтрока = ЛокальнаяТЗ.Добавить();
        
        // Преобразуем уникальный идентификатор COM-ссылки в локальную строку
        UID_Строкой = COMСоединение.String(СтрокаCOM.Ссылка.УникальныйИдентификатор());
        НоваяСтрока.СсылкаСтрокой = UID_Строкой;
        
        // Простые типы (Дата, Число, Строка) переносятся без проблем напрямую
        НоваяСтрока.Дата = СтрокаCOM.Дата;
        НоваяСтрока.Номер = СтрокаCOM.Номер;
        НоваяСтрока.ГосНомер = СтрокаCOM.ГосНомер;
        
    КонецЦикла;
    
    Возврат ЛокальнаяТЗ;
    
КонецФункции

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

Альтернативный вариант: Использование стандартных отборов СКД (Плюсы и минусы)

Существует и альтернативный, "бескодовый" вариант фильтрации, когда разработчик не перехватывает процедуру компоновки.

Суть метода: выгрузить абсолютно все путевые листы из внешней базы в ТЗ, передать ее в СКД, а на вкладке Настройки -> Отбор позволить пользователю установить отбор по колонке Дата (например, Дата Больше или равно...). В этом случае фильтрацию выполнит сама СКД перед выводом табличного документа.

Преимущества:

Недостатки:

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

Резюме

Мы проанализировали специфику работы с внешними источниками данных в СКД. При получении данных по COM-соединению лучшей практикой является сочетание гибких параметров периода на форме отчета с программной передачей этих границ в SQL/1С запрос к внешней базе. Это минимизирует объем передаваемых по сети данных и обеспечивает мгновенную скорость построения отчетов.

← На главную