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