Как в СКД вывести данные из функции, возвращающей таблицу значений?

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

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

Метод 1. Использование набора данных «Объект»

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

Шаг 1. Настройка схемы компоновки (СКД).

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

Шаг 2. Программный вывод отчета.

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


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

Метод 2. Передача таблицы значений как параметра запроса (Платформа 8.3.20+)

Если ваша платформа версии 8.3.20 или выше, мы можем воспользоваться более современным механизмом. Рассмотрим возможность передачи таблицы значений напрямую в запрос в качестве параметра. Проанализируем, как это упрощает работу.

В тексте запроса СКД теперь можно написать:


ВЫБРАТЬ
    ТЗ.Договор КАК Договор,
    ТЗ.Значение1 КАК Значение1
ПОМЕСТИТЬ ВТ_ДанныеИзФункции
ИЗ
    &ВнешняяТЗ КАК ТЗ
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ОсновнойЗапрос.Сотрудник,
    ВТ_ДанныеИзФункции.Значение1
ИЗ
    ОсновнойЗапрос КАК ОсновнойЗапрос
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ДанныеИзФункции КАК ВТ_ДанныеИзФункции
    ПО ОсновнойЗапрос.Договор = ВТ_ДанныеИзФункции.Договор

Чтобы этот код заработал, выясним правила оформления параметра. В списке параметров СКД для &ВнешняяТЗ необходимо указать тип ТаблицаЗначений. При вызове отчета программно, мы просто устанавливаем этот параметр:

КомпоновщикНастроек.Настройки.ПараметрыДанных.УстановитьЗначениеПараметра("ВнешняяТЗ", ТаблицаИзФункции);

Важный нюанс: Таблица значений должна быть строго типизирована. Это означает, что при её создании в коде функции колонки должны создаваться с указанием типов:

ТЗ.Колонки.Добавить("Договор", Новый ОписаниеТипов("СправочникСсылка.ДоговорыКонтрагентов"));

Анализ типичных ошибок: «Итератор не определен»

Разберем ситуацию, упомянутую в обсуждении, когда при программном выводе возникает ошибка Итератор не определен. Это часто происходит в методе ПроцессорКомпоновки.Инициализировать(). Проанализируем основные причины:

Рекомендации по производительности

Посмотрим на ситуацию со стороны оптимизации. Если мы вызываем функцию в цикле для каждого договора, это создаст огромную нагрузку на сервер. Рассмотрим лучший алгоритм действий:

  1. Сначала одним запросом получаем список всех договоров, которые потенциально попадут в отчет.
  2. Передаем этот массив (или список значений) в вашу функцию.
  3. Функция выполняет один запрос к базе данных с условием ГДЕ Договор В (&МассивДоговоров) и возвращает общую таблицу.
  4. Эту таблицу мы один раз передаем в СКД.

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

Заключение

Мы рассмотрели два основных способа вывода данных из функции в СКД. Использование набора данных «Объект» является классическим и универсальным методом для любой версии платформы 8.3. Использование ТЗ как параметра запроса — это современный и более лаконичный способ, доступный в последних версиях. Выбор зависит от ваших задач и требований к поддержке различных версий платформы 1С.

← На главную