При интеграции систем часто возникает задача: необходимо, чтобы определенная логика (например, сложные проверки, записи в регистры или отправка уведомлений), реализованная в подписках на события, не выполнялась, если документ или справочник изменяется программно через COM-соединение. Начинающие разработчики часто совершают ошибку, пытаясь решить это простым отключением свойства общего модуля, однако это приводит к аварийному завершению работы программы. В данной статье мы разберем, почему так происходит, и рассмотрим правильные архитектурные подходы к решению этой задачи.
Разберем ситуацию: у вас есть общий модуль, в котором прописаны процедуры-обработчики для подписок на события. Вы решаете, что раз этот код не должен работать для COM, то достаточно зайти в свойства модуля и снять галочку «Внешнее соединение». Это ошибочный путь.
Проанализируем механику работы платформы 1С:Предприятие. Когда внешнее приложение подключается к вашей базе через COMConnector и пытается записать документ, платформа на сервере начинает собирать все обработчики событий, зарегистрированные для этого объекта. Если в метаданных указано, что обработчик находится в модуле, который «не виден» для внешнего соединения (флаг снят), платформа просто не сможет скомпилировать этот модуль в контексте данного сеанса. Результат — критическая ошибка: «Метод обработчика события не обнаружен». Программа «развалится», и запись объекта станет невозможной.
Вывод: флаги клиентского контекста в свойствах общего модуля должны позволять модулю компилироваться во всех средах, где инициируется событие. Фильтрацию логики нужно делать не на уровне настроек модуля, а внутри самого кода.
Наиболее надежный и производительный способ определить, откуда пришел вызов на сервер — это анализ типа текущего сеанса. Рассмотрим, как это реализовать правильно, чтобы не нагружать систему постоянными вызовами тяжелых функций платформы.
Для начала нам нужно определить тип соединения при старте сеанса. Это лучше всего сделать в Модуле сеанса. Проанализируем пример кода:
Процедура УстановкаПараметровСеанса(ТребуемыеПараметры)
// Определяем, является ли текущий сеанс внешним соединением
ТекущийСеанс = ПолучитьТекущийСеансИнформационнойБазы();
ИмяПриложения = ТекущийСеанс.ИмяПриложения;
Если ИмяПриложения = "COMConnection" Или ИмяПриложения = "COMServer" Тогда
ПараметрыСеанса.ЭтоВнешнееСоединение = Истина;
Иначе
ПараметрыСеанса.ЭтоВнешнееСоединение = Ложь;
КонецЕсли;
КонецПроцедуры
Обратите внимание, что мы проверяем два варианта: COMConnection (использование V83.COMConnector) и COMServer (полноценный запуск через V83.Application). Записав этот признак в параметр сеанса ЭтоВнешнееСоединение (его нужно предварительно создать в метаданных), мы получаем мгновенный доступ к этой информации в любой точке кода.
Теперь рассмотрим, как будет выглядеть обработчик подписки на событие в вашем общем модуле:
Процедура ОбработкаПроверкиЗаполненияПриЗаписи(Источник, Отказ) Экспорт
// Проверяем параметр сеанса
Если ПараметрыСеанса.ЭтоВнешнееСоединение Тогда
Возврат; // Выходим из процедуры, не выполняя логику
КонецЕсли;
// Далее идет основной код подписки
// ...
КонецПроцедуры
Иногда требуется более гибкий подход: нужно игнорировать не все действия через COM, а только конкретные операции загрузки данных. В этом случае использование глобального флага сеанса может быть избыточным. Проанализируем ситуацию, когда мы хотим управлять поведением подписки прямо из вызывающего кода.
У каждого объекта 1С (документа, справочника) есть коллекция ДополнительныеСвойства. Это структура, которая живет в памяти вместе с объектом и не записывается в базу данных. Посмотрим, как это использовать.
Со стороны внешнего соединения (код на языке вызывающей системы или скрипта):
// Пример на языке 1С, вызываемом через COM
НовыйДокумент = Соединение.Документы.РеализацияТоваровУслуг.СоздатьДокумент();
// ... заполнение реквизитов ...
НовыйДокумент.ДополнительныеСвойства.Вставить("ПропуститьОбработкуСобытий", Истина);
НовыйДокумент.Записать();
Теперь настроим обработчик в общем модуле 1С так, чтобы он реагировал на этот флаг:
Процедура ПередЗаписьюДокумента(Источник, Отказ, РежимЗаписи, РежимПроведения) Экспорт
// Проверяем наличие специального свойства в объекте
Если Источник.ДополнительныеСвойства.Свойство("ПропуститьОбработкуСобытий")
И Источник.ДополнительныеСвойства.ПропуститьОбработкуСобытий = Истина Тогда
Возврат;
КонецЕсли;
// Выполнение логики для обычных случаев
// ...
КонецПроцедуры
Этот метод считается более профессиональным в контексте разработки сложных интеграций, так как он позволяет точечно управлять поведением системы, не полагаясь только на технический тип соединения.
Если ваша конфигурация создана на базе современных типовых решений от 1С (УТ 11, ERP, БП 3.0), то в ней, скорее всего, уже интегрирована БСП. Разберем, как упростить себе задачу, используя готовые инструменты библиотеки.
В БСП существует общий модуль ОбщегоНазначения, который содержит функции для определения контекста. Посмотрим на пример их использования:
ОбщегоНазначения.ЭтоВнешнееСоединение() — возвращает Истина, если код выполняется в сеансе COM-соединения.ОбщегоНазначения.ЭтоСеансИнформационнойБазы() — позволяет проверить, является ли текущий контекст обычным сеансом пользователя.Использование типовых функций предпочтительнее, так как разработчики 1С уже учли в них нюансы разных версий платформы и специфику работы в облачных сервисах (Fresh).
Рассмотрим ситуацию, которую часто упускают из виду. Допустим, внешнее соединение инициирует на сервере выполнение какого-то длительного процесса через Фоновое задание. Внутри этого фонового задания проверка ПараметрыСеанса.ЭтоВнешнееСоединение или ОбщегоНазначения.ЭтоВнешнееСоединение() вернет Ложь.
Это происходит потому, что фоновое задание — это отдельный сеанс с типом приложения BackgroundJob. Если ваша логика в подписках должна «знать», что фоновое задание было порождено именно внешним соединением, вам придется транслировать этот признак вручную через параметры фонового задания или использовать временные таблицы для передачи флагов состояния.
Подведем итог нашему анализу и сформулируем пошаговый алгоритм действий для настройки общего модуля:
ОбщегоНазначения.ЭтоВнешнееСоединение() в начале каждого обработчика.ДополнительныеСвойства объекта, если нужно управлять логикой выборочно, а не для всех вызовов через COM.#Если ВнешнееСоединение работает только на этапе компиляции модуля. Если ваш обработчик выполняется на сервере (флаг «Сервер» в модуле), то для сервера тип клиента не важен, и эта директива может не сработать так, как вы ожидаете. Используйте именно программные проверки (Если ... Тогда) внутри процедур.Применяя эти методы, вы обеспечите стабильную работу системы и исключите возникновение трудноуловимых ошибок при интеграции вашей базы 1С с внешними приложениями.