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