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