Как в 1С сформировать отчет с помощью построителя отчета по нескольким группам на отдельные вкладки?

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

При разработке и доработке аналитических отчетов на базе объекта ПостроительОтчета часто возникает задача пакетного формирования данных. Например, требуется разбить единый результат на несколько независимых табличных документов по группам проектов или подразделений и отобразить каждый из них на отдельной вкладке панели. Разберем особенности реализации такой задачи, проанализируем возможные ошибки и рассмотрим оптимальные варианты программного кода.

Постановка задачи и анализ исходного подхода

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

Частая ошибка при реализации такой логики — выполнение лишнего запроса для получения подчиненных элементов группы:


Для каждого Сп Из СписокПроектовОтбор Цикл 
    СписокГруппПроектов = Новый СписокЗначений;
    ГруппаПроектов = Сп.Значение; 
    
    ЗапросПроектыГРуппы = Новый Запрос;
    ЗапросПроектыГРуппы.Текст =
    "ВЫБРАТЬ РАЗРЕШЕННЫЕ РАЗЛИЧНЫЕ
    |    Проекты.Ссылка КАК Пр
    |ИЗ
    |    Справочник.Проекты КАК Проекты
    |ГДЕ
    |    Проекты.Родитель = &Группа";
    ЗапросПроектыГРуппы.УстановитьПараметр("Группа", ГруппаПроектов);
    ВыборкаЭлПроектов = ЗапросПроектыГРуппы.Выполнить().Выбрать();
    Пока ВыборкаЭлПроектов.Следующий() Цикл
        СписокГруппПроектов.Добавить(ВыборкаЭлПроектов.Пр);
    КонецЦикла;                
    
    СформироватьОтчет(НовПоле, СписокГруппПроектов);            
КонецЦикла;

Выясним причину, почему такой подход неэффективен:

  1. Потеря вложенной иерархии: Условие Проекты.Родитель = &Группа выбирает только непосредственных потомков первого уровня и пропускает элементы из более глубоких подгрупп.
  2. Избыточные обращения к СУБД: Механизм отборов построителя отчета на уровне платформы поддерживает работу с иерархией через виды сравнения ВидСравнения.ВИерархии и ВидСравнения.ВСпискеПоИерархии. Нет необходимости предварительно выбирать подчиненные элементы запросом.

Вариант 1. Программное управление отбором построителя в цикле

Если мы не хотим модифицировать типовой текст запроса и переписывать процедуры формирования отчета, наиболее безопасным и быстрым способом является динамическая установка отбора в объекте ПостроительОтчета перед каждым вызовом формирования.

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

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

Посмотрим на пример практической реализации в коде обработки:


Процедура СформироватьОтчетПоГруппам()
    
    // Сохраняем исходные настройки построителя
    ИсходныеНастройки = ПостроительОтчета.ПолучитьНастройки();
    
    Для Каждого ЭлементСписка Из СписокГрупп Цикл
        
        ТекущаяГруппа = ЭлементСписка.Значение;
        
        // 1. Создаем страницу панели и поле табличного документа
        ИмяВкладки = "Стр_" + СокрЛП(ТекущаяГруппа.УникальныйИдентификатор());
        НоваяСтраница = ЭлементыФормы.ОсновнаяПанель.Страницы.Добавить(ИмяВкладки, СокрЛП(ТекущаяГруппа));
        
        ИмяПоля = "ПолеТабДок_" + СокрЛП(ТекущаяГруппа.УникальныйИдентификатор());
        НовоеПолеТабДок = ЭлементыФормы.Добавить(Тип("ПолеТабличногоДокумента"), ИмяПоля, Истина, ЭлементыФормы.ОсновнаяПанель);
        НовоеПолеТабДок.Страница = НоваяСтраница;
        
        // 2. Настраиваем отбор построителя отчета
        ЭлементОтбора = ПостроительОтчета.Отбор.Найти("Проект");
        Если ЭлементОтбора = Неопределено Тогда
            ЭлементОтбора = ПостроительОтчета.Отбор.Добавить("Проект");
        КонецЕсли;
        
        ЭлементОтбора.Использование = Истина;
        ЭлементОтбора.ВидСравнения  = ВидСравнения.ВИерархии;
        ЭлементОтбора.Значение      = ТекущаяГруппа;
        
        // 3. Выполняем построитель и выводим результат в созданный табличный документ
        ПостроительОтчета.Выполнить();
        НовоеПолеТабДок.Очистить();
        ПостроительОтчета.Вывести(НовоеПолеТабДок);
        
    КонецЦикла;
    
    // Восстанавливаем первоначальные настройки отчета
    ПостроительОтчета.УстановитьНастройки(ИсходныеНастройки);
    
КонецПроцедуры

Важный момент: динамическое добавление поля в ПостроительОтчета.Отбор сработает без ошибок только тогда, когда в тексте запроса построителя отчета описано соответствующее поле (например, в секции {ГДЕ Проекты.Ссылка.* КАК Проект} или в полях выборки).

Вариант 2. Единый запрос с последующим разделением по вкладкам

Проанализируем ситуацию, при которой групп проектов достаточно много. Многократное выполнение ПостроительОтчета.Выполнить() в цикле создает высокую нагрузку на базу данных из-за повторных запросов.

В этом случае эффективнее выполнить запрос один раз со списком всех групп, сгруппировать данные по родительским проектам и разделить результат при обходе группировок:

  1. В отбор построителя передаем список всех выбранных групп с видом сравнения ВидСравнения.ВСпискеПоИерархии.
  2. Добавляем в группировки строк построителя поле родителя или группы верхнего уровня.
  3. Выполняем запрос один раз: ПостроительОтчета.Выполнить().
  4. Полученный объект РезультатЗапроса обходим по группировкам верхнего уровня через Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам) и программно выводим секции макета в соответствующие поля табличных документов для каждой группы.

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

← На главную