Как программно вызвать внешний отчет из документа и передать в него данные в 1С?

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

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

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

Сценарий 1. Программный вызов внешнего отчета с передачей ссылки на документ

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

Рассмотрим по шагам реализацию этого алгоритма в модуле управляемой формы документа.

Шаг 1. Подключение внешнего отчета на сервере

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

Добавим в модуль формы документа серверную функцию:


&НаСервереБезКонтекста
Функция ПодключитьВнешнийОтчетНаСервере(СсылкаНаОтчет)
    Возврат ДополнительныеОтчетыИОбработкиВызовСервера.ПодключитьВнешнююОбработку(СсылкаНаОтчет);
КонецФункции

Эта функция возвращает строку с уникальным служебным именем подключенного отчета (например, "ВнешнийОтчет.ИмяПодключенногоОтчета"), которое мы будем использовать для вызова формы.

Шаг 2. Открытие формы отчета на клиенте и передача параметров

Теперь напишем клиентскую процедуру, которая будет вызываться, например, при нажатии кнопки на форме документа. Мы передадим в параметры открываемой формы отбор по текущему документу.


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

Этот способ идеален, если отчет работает по классической схеме: берет ссылку, делает запрос к базе данных и выводит результат. Но что делать, если данные на форме еще не записаны или перед выводом требуется сложная предварительная обработка данных в самом документе?

Сценарий 2. Передача таблицы значений во внешний отчет СКД

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

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

  1. Временное хранилище: поместим таблицу значений в адресное пространство сервера на клиенте и передадим только строковый адрес.
  2. Набор данных - объект в СКД: настроим схему компоновки данных внешнего отчета на прием внешней таблицы данных.
  3. Переопределение компоновки: в модуле объекта внешнего отчета программно свяжем адрес хранилища с процессором компоновки.

Выясним подробнее, как реализовать этот алгоритм по шагам.

Шаг 1. Формирование ТЗ и помещение во временное хранилище (в форме документа)

Нам необходимо собрать данные на сервере (например, взять текущую несохраненную табличную часть документа), сформировать из них классическую таблицу значений и положить ее во временное хранилище, привязав к уникальному идентификатору нашей формы.


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

Шаг 2. Вызов отчета с передачей адреса временного хранилища

Теперь на клиенте получаем адрес хранилища и передаем его как произвольный параметр при открытии формы отчета:


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

Шаг 3. Настройка СКД во внешнем отчете

Откройте ваш внешний отчет в конфигураторе. В схеме компоновки данных (СКД) выполните следующие настройки:

  1. Удалите стандартный набор данных - запрос (если он был).
  2. Добавьте новый Набор данных - объект.
  3. В поле Имя объекта, содержащего данные укажите строго определенное имя, например: ВнешняяТаблицаДанных.
  4. Вручную добавьте поля набора данных. Имена полей и их типы должны строго совпадать с колонками таблицы значений, которую мы сформировали на первом шаге (Номенклатура, Количество, Цена, Сумма).
  5. Настройте структуру вывода отчета на вкладке "Настройки" (например, детальные записи).

Шаг 4. Обработка внешней таблицы в модуле объекта внешнего отчета

Теперь перейдем в модуль объекта внешнего отчета. Нам нужно переопределить стандартное событие компоновки результата ПриКомпоновкеРезультата. В этой процедуре мы перехватим адрес временного хранилища, извлечем таблицу значений и передадим ее в СКД как внешний набор данных.


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

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

Важные нюансы и особенности эксплуатации

При использовании данных механизмов на практике обязательно учитывайте следующие важные аспекты:

Мы подробно проанализировали процесс программного вызова внешних отчетов из БСП и разработали универсальный механизм передачи сложных наборов данных. Применение этих методик сделает ваши прикладные решения быстрыми, гибкими и удобными для конечных пользователей.

← На главную