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