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