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