При разработке на платформе 1С:Предприятие 8.3 с использованием Библиотеки стандартных подсистем (БСП) часто возникает задача: добавить новую кнопку (команду) на стандартную панель отчета. Если отчет встроен в конфигурацию, это делается легко. Но что делать, если перед нами стоит задача разработать внешний отчет (файлы .erf), который будет подключаться через механизм дополнительных отчетов и обработок, при этом вносить изменения в основную конфигурацию категорически запрещено?
В этой статье мы подробно разберем, почему стандартный механизм добавления команд натыкается на ограничения архитектуры 1С, как устроена работа команд в БСП и как элегантно решить эту задачу с помощью универсального шаблона-перехватчика в расширении.
Выясним причину этой проблемы и рассмотрим по шагам варианты ее решения.
---
Начнем с анализа ситуации. Разработчик может легко прописать инициализацию кнопки в модуле объекта внешнего отчета. БСП предоставляет для этого удобные события формы, которые можно переопределить на сервере. Но когда дело доходит до описания действия (обработчика нажатия кнопки), мы сталкиваемся с жестким ограничением платформы:
Подключаемый_Команда, которая перенаправляет вызов в общий модуль конфигурации ОтчетыКлиентПереопределяемый.ОбработчикКоманды.Если мы не меняем конфигурацию, то мы не можем прописать код обработки клика в модуле ОтчетыКлиентПереопределяемый. Напрямую назначить процедуру из модуля объекта внешнего отчета в качестве клиентского обработчика невозможно — у объекта нет клиентского контекста.
Проанализируем два практических пути решения этой проблемы: правильный (с использованием минимального расширения) и альтернативный (без использования расширений).
---
Если в вашей системе разрешено использование расширений (а в современных реалиях это стандарт де-факто), можно создать одно простое расширение. Оно будет выступать "транзитом" для вызовов и позволит писать клиентский код обработчиков прямо внутри ваших внешних отчетов.
Идея метода заключается в том, чтобы перехватить выполнение процедуры ОбработчикКоманды в общем модуле БСП и перенаправить его во вспомогательную (неосновную) форму самого внешнего отчета.
В вашем внешнем отчете создайте новую управляемую форму и назовите её, например, ФормаОбработчикКоманд. Сама форма на экран выводиться не будет, она нужна нам исключительно как контейнер для клиентского кода. В модуле этой формы пишем клиентский метод с ключевым словом Экспорт:
&НаКлиенте
Процедура ВыполнитьКлиентскийМетодКоманды(ФормаОтчета, Команда, Результат) Экспорт
Если Команда.Имя = "МояСуперКоманда" Тогда
// Здесь мы находимся в полноценном клиентском контексте!
// Можем открывать вопросы, предупреждения, формы выбора и т.д.
ПараметрыОповещения = Новый ОписаниеОповещения("ПослеВопроса", ЭтотОбъект, ФормаОтчета);
ПоказатьВопрос(ПараметрыОповещения, "Выполнить дополнительное действие по отчету?", РежимДиалогаВопрос.ДаНет);
Результат = Истина; // Сообщаем системе, что команда успешно перехвачена
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ПослеВопроса(РезультатВыбора, ФормаОтчета) Экспорт
Если РезультатВыбора = КодВозвратаДиалога.Да Тогда
ПоказатьПредупреждение(, "Команда успешно выполнена на клиенте из внешнего отчета!");
// При необходимости можно принудительно обновить отчет:
// ФормаОтчета.СформироватьОтчет();
КонецЕсли;
КонецПроцедуры
В модуле объекта внешнего отчета пропишем инициализацию нашей команды при создании формы на сервере. Для этого используем штатный механизм БСП ОпределитьНастройкиФормы:
Процедура ОпределитьНастройкиФормы(Форма, КлючВарианта, Настройки) Экспорт
// Разрешаем перехват события ПриСозданииНаСервере формы отчета
Настройки.События.ПриСозданииНаСервере = Истина;
КонецПроцедуры
Процедура ПриСозданииНаСервере(Форма, Отказ, СтандартнаяОбработка) Экспорт
// Передаем имя нашей вспомогательной формы-обработчика в параметры формы отчета
Форма.ФормаПараметры.Вставить("ИмяФормыОбработчикаКоманд", "ФормаОбработчикКоманд");
// Создаем команду программно на форме стандартного отчета
НоваяКоманда = Форма.Команды.Добавить("МояСуперКоманда");
НоваяКоманда.Заголовок = "Доп. действие";
НоваяКоманда.Действие = "Подключаемый_Команда"; // Стандартный обработчик БСП
// Выводим кнопку в командную панель (например, в группу "Главное" рядом с кнопкой Сформировать)
ОтчетыСервер.ВывестиКоманду(Форма, НоваяКоманда, "Главное");
КонецПроцедуры
Теперь создаем новое расширение конфигурации. Нам необходимо заимствовать общий модуль ОтчетыКлиентПереопределяемый и добавить к процедуре ОбработчикКоманды аннотацию &После:
&После("ОбработчикКоманды")
Процедура ИмяПрефикса_ОбработчикКоманды(ФормаОтчета, Команда, Результат)
// Проверяем, передал ли наш внешний отчет имя своей формы-обработчика
Если ОбщегоНазначенияКлиентСервер.ЕстьРеквизитИлиСвойствоОбъекта(ФормаОтчета, "ФормаПараметры") Тогда
ИмяФормыОбработчика = "";
Если ФормаОтчета.ФормаПараметры.Свойство("ИмяФормыОбработчикаКоманд", ИмяФормыОбработчика) Тогда
Если ЗначениеЗаполнено(ИмяФормыОбработчика) Тогда
// Конструируем полный путь к форме внешнего отчета.
// ИмяФормы у внешнего отчета будет иметь вид "ВнешнийОтчет.ИмяИдентификатора"
ПолныйПутьКФорме = ФормаОтчета.ИмяФормы + "." + ИмяФормыОбработчика;
Попытка
// Получаем контекст формы без вывода её на экран (не вызывая метод Открыть)
ФормаКонтекст = ПолучитьФорму(ПолныйПутьКФорме, , ФормаОтчета);
// Вызываем клиентский метод во внешней обработке
ФормаКонтекст.ВыполнитьКлиентскийМетодКоманды(ФормаОтчета, Команда, Результат);
Исключение
// Запишем ошибку, если метод не найден или форма не создалась
ЗаписьЖурналаРегистрации("ВнешниеОтчеты.Команды", УровеньЖурналаРегистрации.Ошибка,,,
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
В чем преимущество данного подхода? Мы написали расширение всего один раз. Оно абсолютно универсально. Любой другой внешний отчет, который вы добавите в систему позже, сможет использовать этот же механизм — достаточно будет создать внутри отчета форму с именем ФормаОбработчикКоманд и реализовать в ней экспортный метод.
---
Разберем ситуацию, когда использовать расширения запрещено политикой безопасности компании. В этом случае мы лишены возможности вмешиваться в клиентские модули конфигурации. Как дать пользователю возможность запустить код внешнего отчета по кнопке?
Мы можем пойти на хитрость и отказаться от кнопки в верхней командной панели. Вместо этого мы выведем "кнопку" прямо в результирующее поле отчета (в Табличный документ) с помощью механизма расшифровки.
Рассмотрим этот алгоритм подробнее:
ПараметрыДействия, содержащую имя нашей команды.Однако этот метод имеет существенный минус: пользователю придется делать двойной клик по ячейке таблицы, что не всегда интуитивно понятно.
---
Для того чтобы БСП корректно обрабатывала ваши подписки на события формы (такие как ПриСозданииНаСервере) во внешнем отчете, сам отчет должен быть правильно зарегистрирован в справочнике «Дополнительные отчеты и обработки».
Обязательно проверьте, чтобы в модуле объекта внешнего отчета была определена экспортная функция СведенияОВнешнейОбработке(), и в возвращаемой структуре параметров регистрации свойство ОпределитьНастройкиФормы было установлено в значение Истина:
Функция СведенияОВнешнейОбработке() Экспорт
ПараметрыРегистрации = ДополнительныеОтчетыИОбработки.СведенияОВнешнейОбработке("2.2.2.3");
ПараметрыРегистрации.Вид = ДополнительныеОтчетыИОбработкиКлиентСервер.ВидОбработкиДополнительныйОтчет();
ПараметрыРегистрации.Наименование = "Мой внешний отчет с командами";
ПараметрыРегистрации.БезопасныйРежим = Ложь;
// КРИТИЧЕСКИ ВАЖНЫЙ ФЛАГ для работы событий формы!
ПараметрыРегистрации.ОпределитьНастройкиФормы = Истина;
Возврат ПараметрыРегистрации;
КонецФункции
Если этот флаг не установлен в значение Истина, БСП при открытии отчета проигнорирует процедуру ОпределитьНастройкиФормы в модуле объекта вашего отчета, и динамическая кнопка на форме просто не появится.
---
Мы проанализировали ограничения платформы при работе с внешними отчетами и нашли два способа решения проблемы:
Используйте предложенные шаблоны кодов в своих проектах, чтобы создавать удобный и функциональный интерфейс для пользователей, не ломая при этом типовую конфигурацию!