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