Почему в расширении 1С обработчик &После срабатывает раньше основного кода или вызывается дважды?

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

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

В этой статье мы подробно разберем, почему возникают эти эффекты, проанализируем внутренние механизмы платформы и БСП, а также рассмотрим несколько надежных способов решения задачи.

---

Причина 1: Асинхронность клиентских процедур (Почему код срабатывает раньше времени)

Выясним причину, по которой процедура с директивой &После("ЗаполнитьПоИНН") на клиенте отрабатывает раньше, чем на форме фактически появляются заполненные реквизиты контрагента.

Современные конфигурации 1С работают в парадигме асинхронного вызова интерфейсных методов. Когда пользователь нажимает кнопку «Заполнить по ИНН», запускается стандартный обработчик. Внутри него платформа выполняет следующие действия:

  1. Запускается клиентская процедура ЗаполнитьПоИНН.
  2. Система проверяет необходимость перезаполнения и выводит диалоговое окно с вопросом (например, «Перезаполнить реквизиты?»).
  3. Для вывода вопроса используется асинхронный метод (например, ПоказатьВопрос), которому передается описание оповещения (колбэк).
  4. Критический момент: асинхронный вызов не блокирует выполнение кода! Как только окно вопроса отобразилось на экране, исходная процедура ЗаполнитьПоИНН завершает свою работу, возвращая управление платформе.
  5. Платформа видит, что процедура ЗаполнитьПоИНН формально завершена, и тут же вызывает ваш расширяющий метод &После("ЗаполнитьПоИНН").
  6. Только после того, как пользователь нажмет кнопку «Да» в окне вопроса, сработает процедура-колбэк, которая отправит запрос на сервер, получит данные из веб-сервиса и заполнит реквизиты на форме.

Таким образом, ваш код в расширении выполняется в момент, когда диалоговое окно только открылось, а реальное получение данных с сервера даже не начиналось. Именно поэтому попытка прочитать заполненные реквизиты в этот момент возвращает пустые значения.

---

Причина 2: Конфликт аннотаций и свойств формы (Почему код вызывается дважды)

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

Если применить оба метода одновременно — указать обработчик «После» в свойствах элемента формы и одновременно написать аннотацию &После в модуле, — платформа зарегистрирует два независимых вызова для одного события. В результате ваш код выполнится дважды.

---

Варианты корректного решения задачи

Рассмотрим по шагам несколько способов реализации задачи, при которой нам необходимо скопировать НаименованиеПолное в Наименование сразу после успешного заполнения данных по ИНН.

Способ 1. Внедрение в серверный метод заполнения (Рекомендуемый)

Самый надежный и архитектурно правильный способ — перехватить процедуру, которая выполняет непосредственное заполнение данных объекта на сервере. В механизмах БСП серверные вызовы всегда происходят синхронно. Нам не нужно обрабатывать асинхронные клиентские диалоги, так как к моменту серверной обработки все решения пользователем уже приняты.

Найдем в модуле формы контрагента серверную процедуру заполнения реквизитов (например, ЗаполнитьРеквизитыПоИНННаСервере) и расширим её:


&НаСервере
&После("ЗаполнитьРеквизитыПоИНННаСервере")
Процедура АБД_ЗаполнитьРеквизитыПоИНННаСервереПосле(РеквизитыКонтрагента)
    
    // На сервере Объект уже заполнен новыми данными из сервиса ЕГРЮЛ/ЕГРИП
    Объект.Наименование = Объект.НаименованиеПолное;
    
КонецПроцедуры

Этот вариант гарантирует, что код отработает строго один раз, на сервере, и в тот момент, когда данные гарантированно получены и записаны в реквизиты объекта.

Способ 2. Перехват клиентской процедуры завершения асинхронного вызова

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

Обычно они называются ЗаполнитьПоДаннымЕдиныхГосРеестровЗавершение или ЗаполнитьПоИННЗавершение. Проанализируем исходный код формы, найдем процедуру завершения и добавим обработчик в расширение:


&НаКлиенте
&После("ЗаполнитьПоДаннымЕдиныхГосРеестровЗавершение")
Процедура АБД_ЗаполнитьПоДаннымЕдиныхГосРеестровЗавершениеПосле(Результат, ДополнительныеПараметры)
    
    // Этот код сработает, когда все асинхронные цепочки БСП завершатся
    Объект.Наименование = Объект.НаименованиеПолное;
    ЭтаФорма.ОбновитьОтображениеДанных();
    
КонецПроцедуры

При таком подходе данные на форме уже будут обновлены стандартным кодом БСП, и мы сможем корректно выполнить постобработку на клиенте.

Способ 3. Исправление двойного вызова (если пишем собственный обработчик команды)

Если вы хотите перехватить именно нажатие кнопки на клиенте (без привязки к асинхронному заполнению), выберите строго один способ связи события с кодом:

Вариант А (только через свойства формы):

  1. В форме расширения найдите команду, нажмите кнопку поиска (лупу) у события.
  2. Создайте обработчик с типом вызова «После».
  3. В модуле формы расширения оставьте только директиву компиляции &НаКлиенте:

&НаКлиенте
Процедура АБД_ЗаполнитьПоИННПосле(Команда)
    // Ваш код без аннотации &После
КонецПроцедуры

Вариант Б (только через аннотации кода):

  1. В свойствах команды на форме расширения очистите поле обработчика события (верните стандартное поведение).
  2. В модуле формы расширения свяжите методы программно с помощью аннотаций:

&НаКлиенте
&После("ЗаполнитьПоИНН")
Процедура АБД_ЗаполнитьПоИНН(Команда)
    // Ваш код связывается автоматически платформой
КонецПроцедуры

Использование любого из этих вариантов по отдельности исключит повторный запуск процедуры и избавит от двойных сообщений.

---

Резюме и правила хорошего тона при работе с расширениями

Для того чтобы избежать трудноуловимых ошибок при кастомизации типовых конфигураций через расширения, рекомендуем придерживаться следующих правил:

← На главную