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