При разработке расширений для крупных конфигураций (таких как 1С:Комплексная автоматизация 2, ERP 2.2/2.5 или Управление торговлей 11) программисты часто сталкиваются с задачей: нужно выполнить определенное действие при записи абсолютно любого документа в системе. Это может быть логирование, проверка прав, интеграция с внешними системами или специфический контроль данных.
Сложность заключается в том, что платформа 1С в режиме расширения не позволяет создать подписку на событие, выбрав в качестве источника обобщенный тип ДокументОбъект (как это можно сделать в основной конфигурации). Мы вынуждены либо перечислять все документы вручную, либо искать обходные пути. Рассмотрим подробнее, как решить эту задачу эффективно и элегантно.
В типовых решениях от 1С, построенных на базе БСП (Библиотеки стандартных подсистем), уже существуют подписки, в состав которых включено подавляющее большинство документов. Вместо создания своей подписки, мы можем «пристроиться» к существующей через механизм расширения общих модулей.
Проанализируем состав подписки ПолныйРегистрацияДокумента. Она предназначена для регистрации изменений данных во всех узлах планов обмена. Поскольку почти каждый документ в системе должен участвовать в обмене данными, список объектов в этой подписке максимально полный. Чтобы использовать её, выполним следующие шаги:
ОбменДаннымиСобытия).&После или &Перед.Разберем пример кода для перехвата события ПриЗаписи:
&После("ПолныйРегистрацияДокументаПриЗаписи")
Процедура Мой_ПолныйРегистрацияДокументаПриЗаписи(Источник, Отказ, Замещение)
// Проверяем, что это именно документ
Если Документы.ТипВсеСсылки().СодержитТип(ТипЗнч(Источник.Ссылка)) Тогда
// Наша логика для всех документов
ЗафиксироватьИзменениеДокумента(Источник);
КонецЕсли;
КонецПроцедуры
Также стоит обратить внимание на подписку ЗапретИзмененияДанныхПередЗаписью. Она охватывает все документы, которые подпадают под механизм даты запрета редактирования, что в финансово-учетных системах (КА, ERP) составляет 99% всех документов.
Современные версии БСП предоставляют механизмы, специально предназначенные для встраивания кода. Одним из самых удобных является модуль МодификацияКонфигурацииПереопределяемый. Посмотрим на метод ПослеЗаписиНаСервере.
Этот метод вызывается платформой или программными механизмами БСП для широкого спектра объектов. Преимущество этого метода в том, что он работает на уровне логики конфигурации, и нам не нужно заботиться о составе подписок. Проанализируем, как реализовать расширение в этом модуле:
&После("ПослеЗаписиНаСервере")
Процедура Мой_ПослеЗаписиНаСервере(ТекущийОбъект, ТекущиеПараметры)
// Метод вызывается для справочников и документов.
// Делаем проверку на тип объекта.
Если Метаданные.Документы.Содержит(ТекущийОбъект.Метаданные()) Тогда
// Выполняем общую логику
Сообщить("Документ " + ТекущийОбъект + " успешно обработан.");
КонецЕсли;
КонецПроцедуры
Если нам всё же необходимо создать собственную подписку в расширении, но мы не хотим вручную проставлять галочки у 300-500 документов (и боимся пропустить новые при обновлении), можно прибегнуть к «хакерскому» методу через выгрузку расширения в файлы.
Разберем этот алгоритм по шагам:
ДокументОбъект.ЗаказКлиента).<Source> мы увидим список типов.ДокументОбъект.*, которые есть в основной конфигурации.Важный момент: Платформа «проглотит» такой список, и подписка будет работать для всех указанных объектов. Однако помните, что при добавлении новых документов в основную конфигурацию в процессе обновления, их придется добавлять в этот список повторно.
Большинство типовых документов используют стандартизированные механизмы записи. Проанализируем общий модуль ОбщегоНазначенияБСП. В нем часто вызываются процедуры контроля, такие как ПередЗаписьюОбъекта.
Если мы расширим метод, который вызывается внутри ПередЗаписью или ПриЗаписи модуля объекта, мы сможем внедрить свою логику. Рассмотрим пример использования аннотации &Вместо (с обязательным вызовом ПродолжитьВызов, чтобы не нарушить типовой функционал):
&Вместо("ПроверитьВозможностьИзмененияОбъекта")
Процедура Мой_ПроверитьВозможностьИзмененияОбъекта(Источник, Отказ)
// Сначала даем отработать типовой логике
ПродолжитьВызов(Источник, Отказ);
// Если типовая логика не установила Отказ = Истина, добавляем свою проверку
Если Не Отказ И Метаданные.Документы.Содержит(Источник.Метаданные()) Тогда
// Наша логика
КонецЕсли;
КонецПроцедуры
Иногда задача стоит не в перехвате записи в базу данных, а в изменении поведения интерфейса (например, сделать поле доступным во всех документах). В этом случае нам поможет подписка на событие формы.
В расширении мы можем создать подписку на событие ПриСозданииНаСервере и выбрать тип ДокументФорма. В отличие от ДокументОбъект, тип ДокументФорма является обобщенным и включает в себя формы всех документов системы.
Проанализируем ситуацию: это позволяет нам один раз написать код, который будет выполняться при открытии любого документа:
Процедура Мой_ОбщаяПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// Этот код сработает для любой формы документа
Если ЭтаФорма.Элементы.Найти("Дата") <> Неопределено Тогда
// Например, подсветим дату документа
ЭтаФорма.Элементы.Дата.ЦветТекста = ЦветаСтиля.ПоясняющийТекст;
КонецЕсли;
КонецПроцедуры
Выясним, какой из методов лучше выбрать в зависимости от ситуации:
ПолныйРегистрацияДокумента или ЗапретИзмененияДанныхПередЗаписью.МодификацияКонфигурацииПереопределяемый), это наиболее «чистый» с точки зрения архитектуры 1С путь.ДокументФорма.Важно: При массовом перехвате событий всегда следите за производительностью. Любая задержка в 0.1 секунды в общем обработчике превратится в ощутимое замедление работы всей системы, так как документы записываются пользователями постоянно.