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