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

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

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

Порядок выполнения нескольких подписок на одно событие

Официальная позиция фирмы «1С» заключается в том, что последовательность выполнения подписок ничем не регламентирована. Разработчик не должен закладывать в логику программы зависимость от того, какая подписка отработает первой. Тем не менее, проанализируем результаты эмпирических тестов, чтобы понимать внутреннюю механику платформы.

Рассмотрим, от чего зависит порядок срабатывания, если у нас есть несколько подписок:

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

Посмотрим на наглядный пример. Допустим, мы создали три подписки на событие ПриЗаписи документа:

При записи документа типа ДокументТип1, даже если в конфигураторе они расположены в обратном порядке (В, Б, А), платформа сначала выполнит обработчик «А» и «Б» (в зависимости от их взаимного расположения), и только в самом конце — подписку «В» общего типа.

Влияние расширений конфигурации на подписки

В современных реалиях 1С доработки чаще всего ведутся через механизм расширений. Выясним, как ведут себя подписки в этом случае:

  1. Код обработчиков из расширений всегда выполняется после того, как отработают все типовые подписки из основной конфигурации.
  2. Если к базе подключено несколько расширений, влияющих на один объект, порядок их выполнения зависит от назначения самого расширения: сначала выполняются расширения с типом «Исправление», затем «Адаптация», и только потом — «Дополнение». Также влияет очередность их подключения в режиме «Предприятие».

Контекст исполнения и транзакции

Проанализируем контекст, в котором работает код подписок:

Обработчики подписок всегда выполняются строго на сервере (или в толстом клиенте), так как они оперируют серверными объектами данных. Ключевой особенностью является то, что подписка выполняется в рамках той же транзакции базы данных, что и основное событие объекта.

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

Практические примеры и архитектурные решения

Рассмотрим несколько типовых задач и способов их решения с использованием подписок на события.

Пример 1. Выполнение нашей подписки до или после типовой

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

Мы должны создать свою подписку также на общий тип ДокументОбъект, разместить её ниже типовой в дереве метаданных (или в расширении), а внутри обработчика отсекать лишние документы проверкой типа:


Процедура НашаПодпискаПриЗаписи(Источник, Отказ)
    
    // Отсекаем все документы, кроме нужного нам
    Если ТипЗнч(Источник) <> Тип("ДокументОбъект.МойДокумент") Тогда
        Возврат;
    КонецЕсли;

    // Далее идет логика нашего обработчика
    
КонецПроцедуры

Пример 2. Тяжелые операции при записи (отправка SMS/E-mail)

Частая ошибка новичков — вставлять долгие интеграционные операции (отправку писем, SMS, запросы к внешним API) прямо в подписку ПриЗаписи или ОбработкаПроведения. Поскольку подписка находится в транзакции, это приведет к блокировке таблиц в базе на время ожидания ответа от внешнего сервера. Выясним правильный архитектурный подход:

  1. Создаем дополнительный непериодический регистр сведений (например, ОчередьОтправкиУведомлений).
  2. В подписке на событие мы лишь формируем запись в этот регистр. Это происходит мгновенно и в рамках той же транзакции (если документ не запишется, то и уведомление в очередь не попадет).
  3. Создаем регламентное задание (например, с запуском каждую минуту), которое читает записи из регистра, отправляет SMS и удаляет успешно обработанные строки.

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

Пример 3. Движения по регистрам в подписке ОбработкаПроведения

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


Процедура НашОбработчикПроведенияДокумента(Источник, Отказ, РежимПроведения)
    
    // Получаем набор записей регистра, привязанного к документу
    НаборЗаписей = Источник.Движения.ОтложенныеВознаграждения;
    НаборЗаписей.Отбор.Регистратор.Установить(Источник.Ссылка);
    
    // Добавляем новую запись
    Запись = НаборЗаписей.ДобавитьПриход();
    Запись.Сотрудник = Источник.Сотрудник; // берем данные из документа
    Запись.Период = Источник.Дата;
    Запись.Регистратор = Источник.Ссылка;
    // ... заполнение остальных измерений и ресурсов
    
    // Явно записываем набор
    НаборЗаписей.Записать(Истина);
    
КонецПроцедуры

Типовые паттерны использования механизма

Механизм подписок — это основа архитектуры современных типовых конфигураций (БСП, ERP, ЗУП). Выделим основные сценарии, где они применяются массово:

Рекомендации для разработчиков

Подводя итог, выделим главные правила безопасной работы с подписками:

Во-первых, никогда не стройте бизнес-логику на ожидании, что подписка «А» точно выполнится до подписки «Б». Если порядок критичен, используйте единую подписку-диспетчер, которая вызывает процедуры из общего модуля в строгой последовательности, либо контролируйте выполнение кода через дополнительные свойства объекта (например, записывая флаг в Источник.ДополнительныеСвойства).

Во-вторых, при анализе сложных баз (особенно ERP-решений, где счет подписок идет на сотни), используйте современные инструменты. Например, в 1C:Enterprise Development Tools (EDT) есть встроенный функционал «Все подписки на события», который позволяет в одном окне увидеть все перехватчики, связанные с конкретным объектом метаданных. Для быстрого исследования связей объектов и влияния расширений также подойдёт веб-инструмент для анализа зависимостей конфигураций 1С.

← На главную