При разработке сложных систем на платформе 1С:Предприятие 8 часто возникает ситуация, когда на одно и то же событие одного и того же объекта (например, ПередЗаписью документа РеализацияТоваровУслуг) назначено несколько обработчиков в виде подписок на события. Вопрос о том, в какой последовательности они будут выполняться, является критически важным для обеспечения целостности данных и правильной работы бизнес-логики. В этой статье мы подробно разберем внутренние механизмы платформы, которые управляют этой очередностью, и выясним, на что можно полагаться, а что считается плохой практикой.
Прежде чем анализировать порядок самих подписок, нам необходимо вспомнить фундаментальное правило платформы: сначала всегда выполняется код в модуле самого объекта. Если мы рассматриваем событие ПередЗаписью, то последовательность будет следующей:
ПередЗаписью в модуле документа или справочника.Отказ не был установлен в значение Истина, платформа начинает вызывать назначенные подписки на события.Рассмотрим пример кода в модуле объекта, который может повлиять на запуск всей цепочки подписок:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
// Проверка условий перед записью
Если НЕ ЗначениеЗаполнено(Организация) Тогда
Отказ = Истина;
Сообщить("Не заполнена организация!");
Возврат;
КонецЕсли;
// Если код дошел до конца и Отказ = Ложь,
// далее платформа вызовет все подписки на это событие.
КонецПроцедуры
Проанализируем ситуацию, когда у нас есть несколько подписок, настроенных на разные типы источников. Платформа 1С применяет интеллектуальную сортировку на основе "узкости" типа. Это один из самых надежных механизмов определения очередности. Правило звучит так: подписки с конкретным типом выполняются раньше подписок с обобщенным типом.
Представим, что у нас есть две подписки на событие ПередЗаписью:
ДокументОбъект.РеализацияТоваровУслуг.ДокументОбъект (общий тип для всех документов).В данном случае, при записи документа "Реализация товаров и услуг", платформа сначала выполнит Подписку А, так как она более специфична, и только потом Подписку Б. Это позволяет системе сначала отработать специализированную логику документа, а затем перейти к общим механизмам (например, к универсальному версионированию объектов или регистрации изменений для обменов данными).
Если типы источников у подписок одинаковы, в игру вступает порядок их расположения в дереве конфигурации. Рассмотрим этот момент подробнее. Многие разработчики полагают, что подписки выполняются строго сверху вниз, как они расположены в списке "Подписки на события" в Конфигураторе.
В большинстве случаев это действительно так: платформа обходит коллекцию метаданных в том порядке, в котором они были созданы или перемещены разработчиком. Однако здесь есть технический нюанс, связанный с внутренними идентификаторами (UUID). В редких случаях, особенно при некорректном кешировании метаданных на сервере, порядок может нарушаться. Выясним причину: платформа может ориентироваться на порядок создания объектов, если дерево метаданных не было принудительно перестроено. Если для вас критически важен порядок внутри одного уровня, иногда помогает физическое пересоздание подписок в нужном порядке, хотя это и считается экстремальной мерой.
С появлением механизма расширений картина значительно усложнилась. Разберем, как расширения вклиниваются в общую очередь вызовов. Очередность здесь строго регламентирована платформой:
Важно понимать порядок внутри самих расширений. Если у вас подключено несколько расширений ("Расширение1", "Расширение2"), их подписки будут вызываться в том порядке, в котором расширения расположены в списке "Расширения конфигурации" в базе данных. Вы можете менять этот порядок, перемещая расширения в списке стрелками, что напрямую повлияет на последовательность выполнения кода.
Проанализируем интересную особенность параметра Отказ. Многие начинающие разработчики думают, что если в первой подписке установить Отказ = Истина, то остальные подписки не выполнятся. Это заблуждение.
Если в одной из подписок установлен флаг отказа, платформа все равно продолжит вызывать все последующие подписки из очереди. Однако в параметр Отказ им придет уже значение Истина. Посмотрим на пример правильного написания обработчика подписки, учитывающего действия предшественников:
Процедура ОбработкаСобытияПередЗаписью(Источник, Отказ, РежимЗаписи, РежимПроведения) Экспорт
// Если кто-то до нас уже установил Отказ в Истина,
// возможно, нам не стоит выполнять тяжелые проверки.
Если Отказ Тогда
Возврат;
КонецЕсли;
// Ваша логика здесь
Если Источник.СуммаДокумента < 0 Тогда
Отказ = Истина;
КонецЕсли;
КонецПроцедуры
Важное предостережение: никогда не устанавливайте в подписках Отказ = Ложь. Если предыдущий обработчик (или модуль объекта) отменил запись по веской причине, а ваша подписка принудительно вернет Ложь, это может привести к записи логически некорректных данных и нарушению целостности БД. Это считается "хаком" и крайне плохой практикой разработки.
Официальные стандарты разработки 1С рекомендуют проектировать подписки таким образом, чтобы они не зависели от порядка их выполнения. Каждая подписка должна быть атомарной и выполнять свою независимую задачу.
Если вы обнаружили, что ваша логика в Подписке Б обязательно должна выполняться после логики в Подписке А, рассмотрите следующие варианты решения:
Поскольку визуального диспетчера событий в 1С не существует, выяснить реальный порядок в сложной системе (например, в 1С:ERP, где на одно событие могут быть десятки подписок) можно только экспериментальным путем. Рассмотрим два способа:
Подводя итог, можно сказать: хотя правила очередности существуют (типы, дерево, расширения), полагаться на них как на единственный гарант стабильности системы не стоит. Старайтесь делать ваш код независимым, и тогда порядок выполнения подписок перестанет быть для вас критическим фактором. Для практического анализа последовательности вызова обработчиков можно использовать инструмент для изучения событий платформы 1С.