Механизм подписок на события — это один из важнейших инструментов разработчика 1С, позволяющий вмешиваться в логику записи, проведения или удаления объектов без изменения их модулей. Однако официальная документация оставляет открытым множество вопросов, главным из которых является порядок выполнения обработчиков, если на одно и то же событие (например, ПриЗаписи документа) назначено несколько разных подписок. Разберем детально, как платформа определяет очередность, проанализируем неочевидные нюансы механизма и посмотрим на практические примеры использования.
Официальная позиция фирмы «1С» заключается в том, что последовательность выполнения подписок ничем не регламентирована. Разработчик не должен закладывать в логику программы зависимость от того, какая подписка отработает первой. Тем не менее, проанализируем результаты эмпирических тестов, чтобы понимать внутреннюю механику платформы.
Рассмотрим, от чего зависит порядок срабатывания, если у нас есть несколько подписок:
Источник = ДокументОбъект.РеализацияТоваровУслуг), всегда отрабатывают раньше, чем подписки, привязанные к общему типу (например, Источник = ДокументОбъект). Это происходит независимо от их расположения в конфигураторе.Источник = ДокументОбъект.Тип1, ДокументОбъект.Тип2), она подчиняется правилу расположения в конфигураторе наравне с другими аналогичными подписками.Посмотрим на наглядный пример. Допустим, мы создали три подписки на событие ПриЗаписи документа:
ДокументОбъект.ДокументТип1 — конкретный тип).ДокументОбъект — общий тип).При записи документа типа ДокументТип1, даже если в конфигураторе они расположены в обратном порядке (В, Б, А), платформа сначала выполнит обработчик «А» и «Б» (в зависимости от их взаимного расположения), и только в самом конце — подписку «В» общего типа.
В современных реалиях 1С доработки чаще всего ведутся через механизм расширений. Выясним, как ведут себя подписки в этом случае:
Проанализируем контекст, в котором работает код подписок:
Обработчики подписок всегда выполняются строго на сервере (или в толстом клиенте), так как они оперируют серверными объектами данных. Ключевой особенностью является то, что подписка выполняется в рамках той же транзакции базы данных, что и основное событие объекта.
Это означает, что если в обработчике подписки (например, ПередЗаписью) мы установим параметр Отказ = Истина или произойдет программная ошибка, вся транзакция записи документа будет отменена.
Рассмотрим несколько типовых задач и способов их решения с использованием подписок на события.
Представим ситуацию: в типовой конфигурации есть подписка на источник ДокументОбъект, а нам нужно выполнить свой код для конкретного документа строго после типового обработчика. Если мы укажем в своей подписке конкретный тип документа, она отработает первой. Разберем решение:
Мы должны создать свою подписку также на общий тип ДокументОбъект, разместить её ниже типовой в дереве метаданных (или в расширении), а внутри обработчика отсекать лишние документы проверкой типа:
Процедура НашаПодпискаПриЗаписи(Источник, Отказ)
// Отсекаем все документы, кроме нужного нам
Если ТипЗнч(Источник) <> Тип("ДокументОбъект.МойДокумент") Тогда
Возврат;
КонецЕсли;
// Далее идет логика нашего обработчика
КонецПроцедуры
Частая ошибка новичков — вставлять долгие интеграционные операции (отправку писем, SMS, запросы к внешним API) прямо в подписку ПриЗаписи или ОбработкаПроведения. Поскольку подписка находится в транзакции, это приведет к блокировке таблиц в базе на время ожидания ответа от внешнего сервера. Выясним правильный архитектурный подход:
ОчередьОтправкиУведомлений).Этот подход гарантирует стабильную работу системы и исключает конфликты блокировок. Для организации очередей и выноса длительных операций из транзакций можно использовать библиотеку для создания конвейеров фоновых задач.
Иногда нужно добавить движения по новому регистру накопления или сведений для типового документа, не снимая его модуль с поддержки. Посмотрим на пример кода, как правильно сформировать движения в подписке на событие ОбработкаПроведения. Важно помнить, что с набором записей придется работать вручную, так как автоматическое формирование движений для этого регистра в документе отключено.
Процедура НашОбработчикПроведенияДокумента(Источник, Отказ, РежимПроведения)
// Получаем набор записей регистра, привязанного к документу
НаборЗаписей = Источник.Движения.ОтложенныеВознаграждения;
НаборЗаписей.Отбор.Регистратор.Установить(Источник.Ссылка);
// Добавляем новую запись
Запись = НаборЗаписей.ДобавитьПриход();
Запись.Сотрудник = Источник.Сотрудник; // берем данные из документа
Запись.Период = Источник.Дата;
Запись.Регистратор = Источник.Ссылка;
// ... заполнение остальных измерений и ресурсов
// Явно записываем набор
НаборЗаписей.Записать(Истина);
КонецПроцедуры
Механизм подписок — это основа архитектуры современных типовых конфигураций (БСП, ERP, ЗУП). Выделим основные сценарии, где они применяются массово:
ПередЗаписью и ПередУдалением) для гибкого определения того, нужно ли регистрировать изменение объекта на узлах планов обмена.Дата документа или записях независимых регистров. Например, чтобы увольнение всегда регистрировалось концом дня, а кадровый перевод — началом, выстраивая правильную хронологию.Подводя итог, выделим главные правила безопасной работы с подписками:
Во-первых, никогда не стройте бизнес-логику на ожидании, что подписка «А» точно выполнится до подписки «Б». Если порядок критичен, используйте единую подписку-диспетчер, которая вызывает процедуры из общего модуля в строгой последовательности, либо контролируйте выполнение кода через дополнительные свойства объекта (например, записывая флаг в Источник.ДополнительныеСвойства).
Во-вторых, при анализе сложных баз (особенно ERP-решений, где счет подписок идет на сотни), используйте современные инструменты. Например, в 1C:Enterprise Development Tools (EDT) есть встроенный функционал «Все подписки на события», который позволяет в одном окне увидеть все перехватчики, связанные с конкретным объектом метаданных. Для быстрого исследования связей объектов и влияния расширений также подойдёт веб-инструмент для анализа зависимостей конфигураций 1С.