Как организовать подписку на события для всех документов в расширении 1С

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

При разработке расширений для крупных конфигураций (таких как 1С:Комплексная автоматизация 2, ERP 2.2/2.5 или Управление торговлей 11) программисты часто сталкиваются с задачей: нужно выполнить определенное действие при записи абсолютно любого документа в системе. Это может быть логирование, проверка прав, интеграция с внешними системами или специфический контроль данных.

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

Способ 1. Использование существующих «массовых» подписок конфигурации

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

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

  1. Найдем общий модуль, который является обработчиком этой подписки (обычно это ОбменДаннымиСобытия).
  2. В нашем расширении создадим процедуру-перехватчик с аннотацией &После или &Перед.

Разберем пример кода для перехвата события ПриЗаписи:


&После("ПолныйРегистрацияДокументаПриЗаписи")
Процедура Мой_ПолныйРегистрацияДокументаПриЗаписи(Источник, Отказ, Замещение)
    // Проверяем, что это именно документ
    Если Документы.ТипВсеСсылки().СодержитТип(ТипЗнч(Источник.Ссылка)) Тогда
        // Наша логика для всех документов
        ЗафиксироватьИзменениеДокумента(Источник);
    КонецЕсли;
КонецПроцедуры

Также стоит обратить внимание на подписку ЗапретИзмененияДанныхПередЗаписью. Она охватывает все документы, которые подпадают под механизм даты запрета редактирования, что в финансово-учетных системах (КА, ERP) составляет 99% всех документов.

Способ 2. Переопределяемые модули БСП

Современные версии БСП предоставляют механизмы, специально предназначенные для встраивания кода. Одним из самых удобных является модуль МодификацияКонфигурацииПереопределяемый. Посмотрим на метод ПослеЗаписиНаСервере.

Этот метод вызывается платформой или программными механизмами БСП для широкого спектра объектов. Преимущество этого метода в том, что он работает на уровне логики конфигурации, и нам не нужно заботиться о составе подписок. Проанализируем, как реализовать расширение в этом модуле:


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

Способ 3. Лайфхак с программным формированием состава подписки (XML)

Если нам всё же необходимо создать собственную подписку в расширении, но мы не хотим вручную проставлять галочки у 300-500 документов (и боимся пропустить новые при обновлении), можно прибегнуть к «хакерскому» методу через выгрузку расширения в файлы.

Разберем этот алгоритм по шагам:

  1. Создаем в расширении новую подписку на событие.
  2. Выбираем в качестве источника один любой документ (например, ДокументОбъект.ЗаказКлиента).
  3. Сохраняем расширение и выгружаем его в файлы (Конфигурация -> Выгрузить конфигурацию в файлы).
  4. Находим XML-файл описания нашей подписки. В секции <Source> мы увидим список типов.
  5. Программным скриптом (или простым поиском и заменой) добавляем в этот файл все типы ДокументОбъект.*, которые есть в основной конфигурации.
  6. Загружаем расширение обратно из файлов.

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

Способ 4. Перехват через общие процедуры записи БСП

Большинство типовых документов используют стандартизированные механизмы записи. Проанализируем общий модуль ОбщегоНазначенияБСП. В нем часто вызываются процедуры контроля, такие как ПередЗаписьюОбъекта.

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


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

Способ 5. Подписка на события формы (UI-уровень)

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

В расширении мы можем создать подписку на событие ПриСозданииНаСервере и выбрать тип ДокументФорма. В отличие от ДокументОбъект, тип ДокументФорма является обобщенным и включает в себя формы всех документов системы.

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


Процедура Мой_ОбщаяПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    // Этот код сработает для любой формы документа
    Если ЭтаФорма.Элементы.Найти("Дата") <> Неопределено Тогда
        // Например, подсветим дату документа
        ЭтаФорма.Элементы.Дата.ЦветТекста = ЦветаСтиля.ПоясняющийТекст;
    КонецЕсли;
КонецПроцедуры

Резюме и рекомендации

Выясним, какой из методов лучше выбрать в зависимости от ситуации:

Важно: При массовом перехвате событий всегда следите за производительностью. Любая задержка в 0.1 секунды в общем обработчике превратится в ощутимое замедление работы всей системы, так как документы записываются пользователями постоянно.

← На главную