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