Как вызвать событие «ПриИзменении» одного реквизита при программном изменении другого в расширении 1С?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

При разработке расширений для 1С программисты часто сталкиваются с ситуацией, когда автоматизация одного действия должна запускать цепочку других. Классический пример: при изменении номенклатуры в документе реализации должна автоматически пересчитаться цена. Однако стандартное поведение платформы 1С таково, что событие ПриИзменении (OnChange) генерируется исключительно при интерактивном действии пользователя. Если мы программно присваиваем значение реквизиту внутри кода расширения, связанные с ним обработчики событий не срабатывают автоматически. В этой статье мы подробно разберем, как правильно решить эту задачу, избегая простого копирования кода и сохраняя чистоту архитектуры.

Проблема программного изменения реквизитов

Рассмотрим ситуацию подробнее. Допустим, мы используем аннотацию &После для процедуры НоменклатураПриИзменении. Внутри этой процедуры мы меняем значение реквизита Цена. Проблема заключается в том, что логика, заложенная разработчиками конфигурации в процедуру ЦенаПриИзменении, не выполнится. Платформа ожидает, что пользователь физически изменит значение в поле ввода. Чтобы обойти это ограничение, нам нужно инициировать вызов нужных процедур вручную.

Решение 1: Прямой вызов обработчика события

Самый простой и очевидный способ — вызвать процедуру-обработчик как обычную программную процедуру. Поскольку расширение формы работает в контексте самой формы, нам доступны все её методы, включая обработчики событий элементов.

Проанализируем пример кода, который можно разместить в расширении:


&После("ТоварыНоменклатураПриИзменении")
Процедура Расш1_ТоварыНоменклатураПриИзменении(Элемент)
    
    СтрокаТабличнойЧасти = Элементы.Товары.ТекущиеДанные;
    Если СтрокаТабличнойЧасти <> Неопределено Тогда
        // Программно меняем цену
        СтрокаТабличнойЧасти.Цена = 100; 
        
        // Вызываем обработчик изменения цены напрямую
        ТоварыЦенаПриИзменении(Элементы.ТоварыЦена);
    КонецЕсли;
    
КонецПроцедуры

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

Решение 2: Динамический вызов через ПолучитьДействие

Иногда имя процедуры-обработчика может быть неизвестно заранее или оно может измениться в будущих обновлениях конфигурации. В таких случаях мы можем воспользоваться методом ПолучитьДействие. Этот метод позволяет узнать имя функции, назначенной событию в свойствах элемента управления.

Разберем этот подход по шагам:

  1. Получаем имя процедуры, привязанной к событию ПриИзменении нужного реквизита.
  2. Используем команду Выполнить для запуска этой процедуры.

ИмяПроцедуры = Элементы.ТоварыЦена.ПолучитьДействие("ПриИзменении");
Если Не ПустаяСтрока(ИмяПроцедуры) Тогда
    Попытка
        Выполнить(ИмяПроцедуры + "(Элементы.ТоварыЦена)");
    Исключение
        ЗаписьЖурналаРегистрации("ОшибкаВызоваСобытия", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
    КонецПопытки;
КонецЕсли;

Этот метод делает ваше расширение более гибким и устойчивым к изменениям в основной конфигурации, так как вы не «привязываетесь» к жесткому имени процедуры в коде.

Решение 3: Архитектурно правильный подход (Вызов бизнес-логики)

В современных конфигурациях, таких как УТ 11, ERP 2 или КА 2, разработчики редко пишут сложную логику расчетов непосредственно внутри событий ПриИзменении. Обычно эти события являются лишь «триггерами», которые вызывают специализированные процедуры пересчета, расположенные в модуле формы или в общих модулях (например, ОбработкаТабличнойЧастиКлиент).

Проанализируем ситуацию: вместо того чтобы имитировать нажатие пользователем на поле «Цена», правильнее будет найти ту процедуру, которая отвечает за расчеты. Например, это может быть процедура РассчитатьСуммуСтроки или ЗаполнитьЦенуПоВидуЦен. Вызов этих процедур напрямую — более надежный способ, так как он гарантирует выполнение всей бизнес-логики, а не только той части, что привязана к экранному элементу.

Использование аннотации &Вместо и метода ПродолжитьВызов

Если вам нужно полностью контролировать процесс изменения реквизита и последующих событий, рассмотрим использование аннотации &Вместо. Это позволяет перехватить управление и решить, в какой момент должен выполниться стандартный код, а в какой — ваш дополнительный.

Рассмотрим пример использования метода ПродолжитьВызов():


&Вместо("ТоварыНоменклатураПриИзменении")
Процедура Расш1_ТоварыНоменклатураПриИзменении(Элемент)
    
    // Выполняем стандартную логику платформы
    ПродолжитьВызов(Элемент);
    
    // Сразу после стандартной логики выполняем свои действия
    // Например, вызываем логику изменения цены
    ТоварыЦенаПриИзменении(Элементы.ТоварыЦена);
    
КонецПроцедуры

Этот подход удобен тем, что ваш код «До» и код «После» выполняются в одном контексте, что позволяет гибко управлять переменными и состоянием формы.

Важные предостережения при работе с событиями

При реализации цепочек вызовов событий в расширениях необходимо учитывать несколько критических факторов:

Подводя итог, можно сказать, что наиболее правильным способом вызова связанных событий в расширении является либо прямой вызов процедуры-обработчика с передачей корректного элемента управления, либо (что еще лучше) вызов процедур бизнес-логики, которые стоят за этим событием. Это обеспечит стабильность работы приложения и упростит дальнейшую поддержку кода.

← На главную