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

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

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

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

---

Архитектура программного формирования отчета СКД

Для того чтобы свободно управлять процессом вывода отчета, нам необходимо четко понимать, из каких программных объектов состоит конвейер СКД. Процесс генерации отчета представляет собой последовательную цепочку передачи данных от схемы к финальному документу:

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

Давайте пошагово реализуем этот алгоритм в коде.

---

Реализация базового алгоритма вывода отчета в Табличный Документ

Рассмотрим канонический вариант программного формирования отчета СКД с выводом результата в табличный документ на управляемой форме. Этот код должен выполняться в серверном контексте, так как большинство объектов СКД недоступны на тонком клиенте.


&НаСервере
Процедура СформироватьОтчетНаСервере(РезультатТабДок)

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

КонецПроцедуры

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

---

Критический нюанс: разница между ПолучитьНастройки() и свойством Настройки

Обратите внимание на вторую строку кода в примере выше:

Настройки = Отчет.КомпоновщикНастроек.ПолучитьНастройки();

Начинающие разработчики часто совершают ошибку, обращаясь напрямую к свойству: Настройки = Отчет.КомпоновщикНастроек.Настройки;. Разберем, почему это приводит к некорректному поведению формы отчета.

Правило: При программном формировании отчета из формы всегда используйте метод ПолучитьНастройки() для корректного слияния пользовательских параметров.

---

Использование функций общих модулей в вычисляемых полях

Очень часто в вычисляемых полях или выражениях ресурсов СКД разработчики вызывают функции из общих модулей конфигурации (например, ОбщегоНазначения.ПолучитьПредставлениеСубъекта(Договор)). При интерактивном формировании платформой этот код выполняется без проблем. Однако при попытке программной инициализации процессора компоновки вы можете столкнуться с ошибкой вызова метода Инициализировать.

Для безопасности выполнения кода платформа 1С по умолчанию блокирует вызовы произвольного внешнего программного кода из СКД при ручной сборке. Выясним причину этой проблемы и разберем ее решение.

За возможность вычисления внешних функций в СКД отвечает четвертый параметр метода Инициализировать() объекта ПроцессорКомпоновкиДанных. Он называется ВозможностьИспользованияВнешнихФункций и по умолчанию имеет значение Ложь.

Чтобы предоставить СКД доступ к функциям общих модулей, необходимо явно передать Истина в этот параметр:


ПроцессорКомпоновкиДанных = Новый ПроцессорКомпоновкиДанных;
// Четвертый параметр разрешает использование внешних функций общих модулей
ПроцессорКомпоновкиДанных.Инициализировать(МакетКомпоновкиДанных, , , Истина);

Важные требования к вызываемому общему модулю:

  1. Функция общего модуля должна быть объявлена с ключевым словом Экспорт.
  2. Общий модуль должен иметь установленный флаг «Сервер» (так как СКД выполняется на стороне сервера). Если формирование инициируется из файловой базы на клиенте (например, в толстом клиенте), модуль должен иметь флаг «Клиент».
  3. Если функция вызывается из неглобального общего модуля, в выражении вычисляемого поля СКД обязательно нужно указывать полное имя функции: ИмяОбщегоМодуля.ИмяФункции().

---

Устранение ошибки вывода в универсальную коллекцию значений

В процессе обсуждения на форумах часто возникает вопрос: можно ли выгрузить результат работы СКД напрямую в ТаблицуЗначений или ДеревоЗначений без формирования графической таблицы на экране?

Для этого используется специальный процессор вывода: ПроцессорВыводаРезультатаКомпоновкиДанныхВКоллекциюЗначений. Однако при его вызове разработчики часто получают следующую ошибку:

«Не поддерживается вывод таблиц, диаграмм и вложенных отчетов в универсальную коллекцию значений»

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

Способы решения:

---

Фоновое формирование отчета и постобработка через механизмы БСП

Когда отчет обрабатывает большие массивы информации, монопольное формирование на сервере приводит к «зависанию» интерфейса пользователя. Это плохой тон разработки. В современных конфигурациях на базе БСП (Библиотека стандартных подсистем) формирование отчетов выполняется в фоновом режиме (асинхронно). При этом мы можем заблокировать повторные клики по кнопке «Сформировать» и перехватить событие завершения для постобработки документа.

Рассмотрим, как организовать асинхронный запуск программного формирования СКД по стандартам БСП.

Шаг 1. Клиентский вызов и блокировка формы

При нажатии нашей кастомной кнопки формирования мы блокируем элементы формы и отправляем запрос в фоновое задание:


&НаКлиенте
Процедура СформироватьСПостобработкой(Команда)

    // Предотвращаем повторный запуск
    Элементы.ФормаСформировать.Доступность = Ложь;
    
    // Подготавливаем параметры для фонового задания
    ПараметрыВыполнения = Новый Структура;
    ПараметрыВыполнения.Вставить("НастройкиОтчета", Отчет.КомпоновщикНастроек.ПолучитьНастройки());
    ПараметрыВыполнения.Вставить("АдресХранилища", ПоместитьВоВременноеХранилище(Неопределено, УникальныйИдентификатор));
    
    // Запускаем фоновое задание через БСП
    РезультатЗапуска = ЗапуститьФормированиеВФоне(ПараметрыВыполнения);
    
    Если РезультатЗапуска.Статус = "Выполняется" Тогда
        // Ожидаем завершения фонового процесса
        ПараметрыОжидания = ДлительныеОперацииКлиент.ПараметрыОжидания(ЭтаФорма);
        ПараметрыОжидания.ВыводитьПрогрессВыполнения = Истина;
        ПараметрыОжидания.ТекстСообщения = "Выполняется построение отчета...";
        
        ОповещениеОЗавершении = Новый ОписаниеОповещения("ПослеЗавершенияФоновогоЗадания", ЭтаФорма, ПараметрыВыполнения.АдресХранилища);
        ДлительныеОперацииКлиент.ОжидатьЗавершение(РезультатЗапуска.ИдентификаторЗадания, ОповещениеОЗавершении, ПараметрыОжидания);
    Иначе
        // Если выполнилось мгновенно
        ОбработатьРезультатВыполнения(ПараметрыВыполнения.АдресХранилища);
    EndIf;

КонецПроцедуры

Шаг 2. Выполнение серверной генерации в фоновом задании

В общем модуле (который имеет доступ к фоновым заданиям) размещаем экспортную процедуру, которая будет вызвана платформой асинхронно:


// Процедура должна находиться в серверном общем модуле с поддержкой вызова фоновых заданий
Процедура СформироватьОтчетВФоне(ПараметрыЗадачи, АдресРезультата) Экспорт

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

КонецПроцедуры

Шаг 3. Завершение фонового задания и постобработка табличного документа

После завершения фонового задания управление возвращается на клиентскую сторону в процедуру обратного вызова (колбэк). Здесь мы забираем готовый документ, производим его программную модификацию и выводим на форму:


&НаКлиенте
Процедура ПослеЗавершенияФоновогоЗадания(РезультатЗадания, АдресХранилища) Экспорт

    // Разблокируем интерфейс
    Элементы.ФормаСформировать.Доступность = Истина;
    
    Если РезультатЗадания.Статус = "Выполнено" Тогда
        ОбработатьРезультатВыполнения(АдресХранилища);
    Иначе
        ВызватьИсключение "Ошибка формирования отчета: " + РезультатЗадания.КраткоеПредставлениеОшибки;
    КонецЕсли;

КонецПроцедуры

&НаСервере
Процедура ОбработатьРезультатВыполнения(Знач АдресХранилища)

    // Извлекаем табличный документ из хранилища
    ТабДокумент = ПолучитьИзВременногоХранилища(АдресХранилища);
    
    Если ТабДокумент <> Неопределено Тогда
        
        // --- ЗДЕСЬ НАЧИНАЕТСЯ ПОСТОБРАБОТКА ---
        // Например, установим защиту листа и запретим редактирование ячеек
        ТабДокумент.Защита = Истина;
        ТабДокумент.ТолькоПросмотр = Истина;
        
        // Можем принудительно установить ширину первой колонки
        Если ТабДокумент.ШиринаТаблицы > 0 Тогда
            ТабДокумент.Область("К1").ШиринаКолонки = 35;
        КонецЕсли;
        
        // Можем раскрасить итоговые строки или добавить водяной знак
        // ... (любые манипуляции с ячейками ТабличногоДокумента)
        
        // Выводим результат в реквизит нашей управляемой формы
        ЗначениеВРеквизитФормы(ТабДокумент, "Результат");
        
    КонецЕсли;

КонецПроцедуры

Использование этого подхода позволяет решить сразу три задачи: интерфейс программы не зависает при формировании тяжелых отчетов, пользователь физически не может нажать кнопку запуска повторно во время расчета, а разработчик получает полный контроль над готовым объектом ТабличныйДокумент в точке его возврата на сервер, получая возможность провести любые манипуляции форматирования перед финальным показом на экране.

← На главную