Как управлять последовательностью выполнения подписок на события в 1С?

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

В процессе разработки на платформе 1С:Предприятие 8.3 программисты часто сталкиваются с ситуацией, когда для одного и того же события (например, ПередЗаписью) создается несколько подписок. Возникает закономерный вопрос: как заставить систему выполнять их в строго определенном порядке? В этой статье мы разберем, почему стандартное перемещение подписок в дереве метаданных не работает, и какие архитектурные приемы позволяют гарантированно управлять последовательностью вызовов.

Почему порядок в дереве конфигурации — это иллюзия?

Многие начинающие разработчики полагают, что если переместить одну подписку выше другой в дереве конфигурации, то она выполнится первой. Однако, проанализируем техническую сторону вопроса: платформа 1С официально заявляет, что порядок выполнения подписок на одно и то же событие не определен. На практике это означает, что вызовы могут происходить в порядке внутренних идентификаторов (UUID) объектов метаданных или в порядке их создания. Визуальное положение в списке — это лишь удобство для разработчика, которое никак не влияет на исполняемый код.

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

Решение №1: Объединение в одну «тяжелую» подписку (Паттерн Диспетчер)

Рассмотрим наиболее надежный и рекомендуемый способ. Вместо создания десяти мелких подписок на разные объекты, мы создаем одну общую подписку на группу объектов (например, на тип ДокументОбъект) и делегируем управление коду. Этот подход активно используется в Библиотеке стандартных подсистем (БСП).

Разберем реализацию по шагам:

  1. Создаем одну подписку на событие ПередЗаписью для всех необходимых типов документов.
  2. В качестве обработчика указываем процедуру в общем модуле.
  3. Внутри этой процедуры жестко прописываем последовательность вызовов.

Пример реализации такого диспетчера в общем модуле:


Процедура ПередЗаписьюОбъектаДляВсех(Источник, Отказ, РежимЗаписи, РежимПроведения) Экспорт
    
    // 1. Сначала выполняем общую проверку прав
    ПроверитьПраваПользователя(Источник, Отказ);
    Если Отказ Тогда Возврат; КонецЕсли;
    
    // 2. Затем выполняем специфические расчеты, которые важны для других функций
    РассчитатьТехническиеПоля(Источник);
    
    // 3. Вызываем алгоритмы в зависимости от типа объекта
    ТипОбъекта = ТипЗнч(Источник);
    Если ТипОбъекта = Тип("ДокументОбъект.РеализацияТоваровУслуг") Тогда
        ОбработатьРеализацию(Источник, Отказ);
    ИначеЕсли ТипОбъекта = Тип("ДокументОбъект.ПоступлениеТоваровУслуг") Тогда
        ОбработатьПоступление(Источник, Отказ);
    КонецЕсли;

КонецПроцедуры

Этот метод позволяет нам полностью контролировать цепочку событий. Мы точно знаем, что РассчитатьТехническиеПоля отработает раньше, чем ОбработатьРеализацию.

Решение №2: Использование структуры ДополнительныеСвойства

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

Каждый объект в 1С (документ, справочник) обладает свойством ДополнительныеСвойства, которое представляет собой структуру. Эта структура живет на протяжении всего цикла записи объекта. Мы можем использовать её как «флагшток»:

  1. Подписка №1: Выполняет свою работу и записывает флаг: Источник.ДополнительныеСвойства.Вставить("ПерваяПодпискаВыполнена", Истина);.
  2. Подписка №2: Проверяет наличие этого флага. Если флага нет — значит, первая подписка еще не запускалась.

Посмотрим на пример кода для второй подписки:


Процедура ОбработкаВторойПодписки(Источник, Отказ) Экспорт
    
    Если Не Источник.ДополнительныеСвойства.Свойство("ПерваяПодпискаВыполнена") Тогда
        // Ой! Первая подписка еще не отработала. 
        // Мы можем либо вызвать её принудительно, либо выдать ошибку,
        // либо пропустить выполнение, если это допустимо.
        ВыполнитьЛогикуПервойПодписки(Источник);
    КонецЕсли;
    
    // Теперь выполняем основную логику второй подписки
    ПродолжитьОбработку(Источник);
    
КонецПроцедуры

Решение №3: Перенос логики в модуль менеджера или объекта

Выясним причину, по которой вы решили использовать именно подписки. Если алгоритм уникален для конкретного документа, возможно, стоит вернуть его в модуль объекта в процедуру ПередЗаписью. Помните о важном правиле платформы: обработчик в модуле объекта всегда срабатывает ПЕРЕД всеми подписками.

Если же вы хотите унифицировать код, рассмотрите следующий паттерн:

  1. В модуле менеджера объекта создаем экспортную процедуру ВыполнитьПодготовкуПередЗаписью(Объект).
  2. В модуле самого объекта вызываем её.

Это обеспечит выполнение вашего кода в самую первую очередь, еще до того, как платформа начнет перебирать массив подписок.

Влияние на производительность и транзакции

Проанализируем ситуацию с точки зрения быстродействия. Каждая отдельная подписка — это дополнительный вызов обработчика платформой. Если у вас 20 подписок на один объект, накладные расходы на инициализацию контекста могут стать заметными при массовой загрузке данных. Объединение их в одну точку входа (как в Решении №1) работает быстрее.

Важный момент по транзакциям: События ПередЗаписью и ПриЗаписи выполняются внутри одной транзакции. Если в любой подписке (даже если она стоит в очереди последней) вы установите Отказ = Истина, вся транзакция будет отменена, и данные в базу не попадут. Однако промежуточные изменения, внесенные в объект Источник в памяти первой подпиской, будут видны второй подписке, даже если они еще не записаны в БД.

Итоги и рекомендации

Подведем итог нашему исследованию порядка выполнения:

Следуя этим архитектурным принципам, вы сделаете свою систему предсказуемой и легкой в отладке.

← На главную