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