Как добавить свою команду в стандартную форму отчета БСП без изменения конфигурации?

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

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

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

Выясним причину этой проблемы и рассмотрим по шагам варианты ее решения.

---

В чем сложность: Сервер vs Клиент в контексте БСП

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

  1. Код модуля объекта внешнего отчета выполняется строго на сервере.
  2. Нажатие кнопки на форме — это интерактивное событие, которое должно начинаться на клиенте (чтобы можно было открыть диалог, задать вопрос пользователю, показать предупреждение или открыть другую форму).
  3. БСП при добавлении динамической команды ожидает, что в качестве обработчика действия будет указана процедура Подключаемый_Команда, которая перенаправляет вызов в общий модуль конфигурации ОтчетыКлиентПереопределяемый.ОбработчикКоманды.

Если мы не меняем конфигурацию, то мы не можем прописать код обработки клика в модуле ОтчетыКлиентПереопределяемый. Напрямую назначить процедуру из модуля объекта внешнего отчета в качестве клиентского обработчика невозможно — у объекта нет клиентского контекста.

Проанализируем два практических пути решения этой проблемы: правильный (с использованием минимального расширения) и альтернативный (без использования расширений).

---

Решение 1. Шаблон «Универсальный мост» через расширение (Рекомендуемый способ)

Если в вашей системе разрешено использование расширений (а в современных реалиях это стандарт де-факто), можно создать одно простое расширение. Оно будет выступать "транзитом" для вызовов и позволит писать клиентский код обработчиков прямо внутри ваших внешних отчетов.

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

Шаг 1. Создание вспомогательной формы во внешнем отчете

В вашем внешнем отчете создайте новую управляемую форму и назовите её, например, ФормаОбработчикКоманд. Сама форма на экран выводиться не будет, она нужна нам исключительно как контейнер для клиентского кода. В модуле этой формы пишем клиентский метод с ключевым словом Экспорт:


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

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

Шаг 2. Регистрация команды в модуле объекта внешнего отчета

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


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

Процедура ПриСозданииНаСервере(Форма, Отказ, СтандартнаяОбработка) Экспорт
    
    // Передаем имя нашей вспомогательной формы-обработчика в параметры формы отчета
    Форма.ФормаПараметры.Вставить("ИмяФормыОбработчикаКоманд", "ФормаОбработчикКоманд");
    
    // Создаем команду программно на форме стандартного отчета
    НоваяКоманда = Форма.Команды.Добавить("МояСуперКоманда");
    НоваяКоманда.Заголовок = "Доп. действие";
    НоваяКоманда.Действие = "Подключаемый_Команда"; // Стандартный обработчик БСП
    
    // Выводим кнопку в командную панель (например, в группу "Главное" рядом с кнопкой Сформировать)
    ОтчетыСервер.ВывестиКоманду(Форма, НоваяКоманда, "Главное");
    
КонецПроцедуры

Шаг 3. Создание расширения для перенаправления вызова

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


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

В чем преимущество данного подхода? Мы написали расширение всего один раз. Оно абсолютно универсально. Любой другой внешний отчет, который вы добавите в систему позже, сможет использовать этот же механизм — достаточно будет создать внутри отчета форму с именем ФормаОбработчикКоманд и реализовать в ней экспортный метод.

---

Решение 2. Обходной путь без расширений (Интерактивная ячейка)

Разберем ситуацию, когда использовать расширения запрещено политикой безопасности компании. В этом случае мы лишены возможности вмешиваться в клиентские модули конфигурации. Как дать пользователю возможность запустить код внешнего отчета по кнопке?

Мы можем пойти на хитрость и отказаться от кнопки в верхней командной панели. Вместо этого мы выведем "кнопку" прямо в результирующее поле отчета (в Табличный документ) с помощью механизма расшифровки.

Реализация через расшифровку в СКД

Рассмотрим этот алгоритм подробнее:

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

Однако этот метод имеет существенный минус: пользователю придется делать двойной клик по ячейке таблицы, что не всегда интуитивно понятно.

---

Важная деталь: Регистрация внешнего отчета в БСП

Для того чтобы БСП корректно обрабатывала ваши подписки на события формы (такие как ПриСозданииНаСервере) во внешнем отчете, сам отчет должен быть правильно зарегистрирован в справочнике «Дополнительные отчеты и обработки».

Обязательно проверьте, чтобы в модуле объекта внешнего отчета была определена экспортная функция СведенияОВнешнейОбработке(), и в возвращаемой структуре параметров регистрации свойство ОпределитьНастройкиФормы было установлено в значение Истина:


Функция СведенияОВнешнейОбработке() Экспорт
    
    ПараметрыРегистрации = ДополнительныеОтчетыИОбработки.СведенияОВнешнейОбработке("2.2.2.3");
    
    ПараметрыРегистрации.Вид = ДополнительныеОтчетыИОбработкиКлиентСервер.ВидОбработкиДополнительныйОтчет();
    ПараметрыРегистрации.Наименование = "Мой внешний отчет с командами";
    ПараметрыРегистрации.БезопасныйРежим = Ложь;
    
    // КРИТИЧЕСКИ ВАЖНЫЙ ФЛАГ для работы событий формы!
    ПараметрыРегистрации.ОпределитьНастройкиФормы = Истина;
    
    Возврат ПараметрыРегистрации;
    
КонецФункции

Если этот флаг не установлен в значение Истина, БСП при открытии отчета проигнорирует процедуру ОпределитьНастройкиФормы в модуле объекта вашего отчета, и динамическая кнопка на форме просто не появится.

---

Резюме

Мы проанализировали ограничения платформы при работе с внешними отчетами и нашли два способа решения проблемы:

Используйте предложенные шаблоны кодов в своих проектах, чтобы создавать удобный и функциональный интерфейс для пользователей, не ломая при этом типовую конфигурацию!

← На главную