Как программно вызвать внешний отчет из расширения и получить результат выполнения

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

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

Постановка задачи и анализ ситуации

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

Рассмотрим по шагам, как правильно организовать это взаимодействие.

Шаг 1. Перенос кода в модуль объекта

Первое и самое важное отличие, которое мы должны учитывать при выносе отчета во внешний файл: у внешних отчетов отсутствует модуль менеджера в том понимании, в котором он есть у встроенных объектов. Если во внутреннем отчете процедура СформироватьОтчет находилась в модуле менеджера, то во внешнем отчете ее необходимо перенести в модуль объекта и обязательно добавить экспортное слово Экспорт.

Проанализируем пример структуры модуля объекта внешнего отчета:


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

Шаг 2. Получение ссылки на внешний отчет

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


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

Шаг 3. Подключение внешнего отчета программно

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

Разберем процедуру подключения на сервере:


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

Шаг 4. Использование фоновых заданий (Длительные операции)

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

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

Схема работы:

  1. Клиент вызывает серверную процедуру запуска.
  2. Сервер запускает фоновое задание, передавая ему ссылку на элемент справочника дополнительных отчетов.
  3. Внутри фонового задания отчет подключается заново, формируется и результат помещается в хранилище.

Пример кода для запуска через типовой механизм ДлительныеОперации:


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

А в общем модуле МойОбщийМодульСервер мы напишем код, который умеет работать с внешним объектом:


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

Шаг 5. Обработка команды в расширении

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


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

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

Важные нюансы и типичные ошибки

Проанализируем основные подводные камни, с которыми сталкиваются разработчики:

  1. Безопасный режим: Внешние отчеты часто подключаются в безопасном режиме. Это накладывает ограничения на использование некоторых методов (например, работу с файловой системой или COM-объектами). Если вашему отчету нужны расширенные права, убедитесь, что в справочнике доп. отчетов установлены соответствующие разрешения или используется профиль безопасности.
  2. Передача параметров: При вызове методов внешнего отчета через ОбъектОтчета.ИмяМетода() всегда старайтесь передавать параметры через одну структуру. Это делает код более гибким и защищает от ошибок "Несоответствие типов" при передаче данных между клиентом и сервером.
  3. Очистка временных файлов: При использовании ПодключитьВнешнююОбработку система сама управляет временными именами, но если вы все же используете старый метод с ПолучитьИмяВременногоФайла, не забывайте удалять файл после создания объекта (УдалитьФайлы(ИмяФайла)).
  4. Контекст формы: Помните, что ОбъектОтчета на сервере не имеет доступа к элементам формы, из которой он вызван. Все необходимые данные (значения реквизитов, настройки) должны передаваться в него явно через параметры.

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

← На главную