Как в коде 1С определить, что код выполняется регламентным заданием, а не пользователем?

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

При разработке и сопровождении систем на платформе 1С:Предприятие часто возникает классическая задача: необходимо определить контекст выполнения кода. Например, один и тот же документ может проводиться интерактивно бухгалтером из-под Windows (где выгрузка файлов должна идти по пути D:\Папка) или автоматически регламентным заданием на сервере под управлением Linux (где путь к этой же сетевой папке выглядит как /y/папка).

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

Почему директивы компиляции не помогут?

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


#Если Клиент Тогда
    // Код для клиента
#Иначе
    // Код для сервера
#КонецЕсли

Выясним причину, почему в данном случае это работать не будет. Подписки на события (например, ПриЗаписи или ОбработкаПроведения) при работе в тонком или веб-клиенте всегда выполняются на стороне сервера. Независимо от того, нажал ли пользователь кнопку проведения в интерфейсе или документ проводится регламентным заданием в фоновом режиме, весь код подписки отработает на сервере 1С. Следовательно, директива #Если Клиент в модуле подписки (или в серверном общем модуле) всегда будет возвращать Ложь.

Рассмотрим рабочие программные способы решения этой проблемы.

Способ 1. Использование глобальной функции ИмяПриложения()

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

Проанализируем возможные возвращаемые значения функции:

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


ТекущееПриложение = ИмяПриложения();

Если ТекущееПриложение = "BackgroundJob" Тогда
    // Код выполняется регламентным или фоновым заданием
    ПутьККаталогу = "/y/папка/"; // Путь для Linux-сервера
Иначе
    // Код выполняется обычным пользователем через клиентское приложение
    ПутьККаталогу = "D:\папка\"; // Путь для Windows-клиента
КонецЕсли;

Способ 2. Анализ текущего сеанса информационной базы

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

У объекта сеанса есть свойство ИмяПриложения, а также метод ПолучитьФоновоеЗадание(), который возвращает объект фонового задания или значение Неопределено, если текущий сеанс не является фоновым процессом.

Разберем пример кода:


ТекущийСеанс = ПолучитьТекущийСеансИнформационнойБазы();

// Вариант А: проверка через ИмяПриложения сеанса
Если ТекущийСеанс.ИмяПриложения = "BackgroundJob" Тогда
    // Это фоновое задание
КонецЕсли;

// Вариант Б: проверка через ПолучитьФоновоеЗадание()
Если ТекущийСеанс.ПолучитьФоновоеЗадание() <> Неопределено Тогда
    // Код однозначно запущен в контексте фонового/регламентного задания
КонецЕсли;

Способ 3. Передача контекста через ДополнительныеСвойства

Иногда логика приложения требует более гибкого управления. Например, если проведение инициировано из определенной формы документа интерактивно, мы можем передать специальный флаг прямо в объект документа, используя свойство ДополнительныеСвойства. Это структура, которая живет вместе с объектом в памяти на протяжении всей транзакции.

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

  1. В модуле формы документа перед записью или проведением устанавливаем флаг:

&НаКлиенте
Процедура ПередЗаписью(Отказ, ПараметрыЗаписи)
    // Передаем признак интерактивного проведения на сервер
    ЭтотОбъект.ДополнительныеСвойства.Вставить("ИнтерактивноеПроведение", Истина);
КонецПроцедуры
  1. В обработчике подписки на событие на сервере проверяем наличие и значение этого флага:

Процедура ОбработкаПроведенияДокумента(Источник, Отказ, РежимПроведения) Экспорт
    
    Интерактивно = Ложь;
    Если Источник.ДополнительныеСвойства.Свойство("ИнтерактивноеПроведение") Тогда
        Интерактивно = Источник.ДополнительныеСвойства.ИнтерактивноеПроведение;
    КонецЕсли;
    
    Если Интерактивно Тогда
        // Файл записывается на D:\папка
    Иначе
        // Проводит регламентное задание, пишем в /y/папка
    КонецЕсли;
    
КонецПроцедуры

Способ 4. Проверка операционной системы (Рекомендуемый)

Вернемся к первоначальной проблеме автора темы: выбор пути к каталогу (Windows-путь или Linux-путь). Зачастую пытаться понять «кто» выполняет код — это избыточно, если на самом деле нужно понять «где» выполняется код. Поскольку запись файла всегда происходит на стороне сервера 1С (в контексте подписки), нам достаточно определить операционную систему самого сервера приложений.

Для определения ОС сервера воспользуемся объектом СистемнаяИнформация:


Инфо = Новый СистемнаяИнформация;
ТипПлатформыСервера = Инфо.ТипПлатформы;

Если ТипПлатформыСервера = ТипПлатформы.Linux_x86 
 ИЛИ ТипПлатформыСервера = ТипПлатформы.Linux_x86_64 Тогда
    // Код выполняется на сервере Linux
    ПутьККаталогу = "/y/папка/";
Иначе
    // Код выполняется на сервере Windows
    ПутьККаталогу = "D:\папка\";
КонецЕсли;

Этот подход более универсален, так как он защищает от ошибок, если регламентное задание вдруг будет запущено на Windows-платформе или интерактивный сеанс будет запущен на Linux-клиенте.

Рекомендации по проектированию файлового обмена

Выполнение длительных операций (таких как запись файлов на сетевые диски, особенно монтированные в Linux) непосредственно в транзакции проведения документа — это плохая практика разработки. Это может приводить к взаимным блокировкам базы данных и замедлению работы пользователей.

Рекомендуется использовать архитектурный шаблон «Очередь задач»:

← На главную