Как запустить процедуру модуля менеджера в фоновом задании и передать в нее документ объект?

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

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

В этой статье мы подробно разберем природу этих ограничений платформы 1С и рассмотрим пошаговые сценарии их обхода с конкретными примерами кода.

---

Часть 1. Вызов процедур модуля менеджера в фоновых заданиях

Выясним причину, почему при попытке вызвать процедуру менеджера документа напрямую через платформенный метод ФоновыеЗадания.Выполнить("Документы.МойДокумент.АсинхроннаяПроцедура") система возвращает ошибку «Метод задания не найден», даже если процедура объявлена с ключевым словом Экспорт.

Согласно синтакс-помощнику платформы 1С, первый параметр метода ФоновыеЗадания.Выполнить() (имя метода) принимает строго ограниченный формат данных. Это должна быть строка, содержащая имя экспортного метода из неглобального общего модуля в формате ИмяОбщегоМодуля.ИмяПроцедуры. Вызывать напрямую методы модулей менеджеров документов, справочников, обработок или регистров платформа на уровне ядра не умеет.

Рассмотрим два основных способа решения этой задачи: стандартный (с использованием общего модуля-обертки) и современный (при помощи механизмов Библиотеки стандартных подсистем).

Способ А. Создание общего модуля-обертки (без БСП)

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

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

  1. Создайте новый общий модуль. Назовите его, например, ФоновыеВызовыСервер. В свойствах модуля обязательно установите флаги Сервер и Вызов сервера (флаг Глобальный должен быть выключен).
  2. Внутри этого модуля напишите экспортную процедуру-шаблон, которая будет принимать имя вызываемого метода и массив параметров, а затем выполнять его динамически.

Посмотрим на пример реализации такого общего модуля:


// В общем модуле ФоновыеВызовыСервер

Процедура ВыполнитьМетодОбъекта(ИмяМетода, ПараметрыВызова) Экспорт
    
    // ПараметрыВызова — это Массив, содержащий аргументы для нашей целевой процедуры.
    // Используем конструкцию Выполнить для динамического вызова метода менеджера.
    
    СтрокаВызова = ИмяМетода + "(ПараметрыВызова[0]";
    
    Для Индекс = 1 По ПараметрыВызова.В Граница() Цикл
        СтрокаВызова = СтрокаВызова + ", ПараметрыВызова[" + Формат(Индекс, "ЧГ=0") + "]";
    КонецЦикла;
    
    СтрокаВызова = СтрокаВызова + ");";
    
    Выполнить(СтрокаВызова);
    
КонецПроцедуры

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


// Пример запуска фонового задания из формы или модуля
ПараметрыПроцедуры = Новый Массив;
ПараметрыПроцедуры.Добавить(СсылкаНаДокумент); // Передаем ссылку на документ в качестве параметра

ПараметрыФЗ = Новый Массив;
ПараметрыФЗ.Добавить("Документы.МойДокумент.АсинхроннаяПроцедура"); // Имя реальной процедуры
ПараметрыФЗ.Добавить(ПараметрыПроцедуры); // Массив ее параметров

ФоновыеЗадания.Выполнить("ФоновыеВызовыСервер.ВыполнитьМетодОбъекта", ПараметрыФЗ);

Способ Б. Использование БСП (Библиотека стандартных подсистем)

Если ваша конфигурация построена на базе БСП (например, 1С:ERP, 1С:КА, 1С:УТ, 1С:БП), то создавать собственные модули-обертки вручную не нужно. В БСП уже встроен мощный и гибкий механизм для работы с длительными операциями.

Для запуска процедуры из модуля менеджера любого объекта метаданных в фоне используется функция ВыполнитьПроцедуру общего модуля ДлительныеОперации.

Проанализируем пример запуска фонового задания через БСП:


// Описываем параметры выполнения фонового задания
ПараметрыВыполнения = ДлительныеОперации.ПараметрыВыполненияПроцедуры(УникальныйИдентификатор);
ПараметрыВыполнения.НаименованиеФоновогоЗадания = "Асинхронная обработка документа";

// Задаем параметры, которые ожидает сама процедура в модуле менеджера
ПараметрыПроцедуры = Новый Структура;
ПараметрыПроцедуры.Вставить("СсылкаНаДокумент", СсылкаНаДокумент);

// Формируем имя вызываемой процедуры. 
// БСП поддерживает специальный синтаксис для обращения к менеджерам объектов!
ИмяПроцедуры = "Документ.МойДокумент.АсинхроннаяПроцедура";

// Запуск фонового задания
Результат = ДлительныеОперации.ВыполнитьПроцедуру(ПараметрыВыполнения, ИмяПроцедуры, ПараметрыПроцедуры);

Если Результат.Статус = "Выполняется" Тогда
    // Фоновое задание успешно запущено и выполняется в данный момент
    ИдентификаторЗадания = Результат.ИдентификаторЗадания;
КонецЕсли;

Этот способ является наиболее предпочтительным и стандартным для современной разработки на платформе 1С:Предприятие 8.3.

---

Часть 2. Временное хранилище и изоляция сеансов в фоновых заданиях

Вторая частая проблема, с которой сталкиваются разработчики при работе с фоновыми заданиями — попытка передать тяжелые данные через Временное хранилище (ВХ).

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

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

Как правильно передавать данные ИЗ фонового задания обратно в родительский сеанс:

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

  1. В родительском сеансе создается пустая запись во временном хранилище с обязательной привязкой к УникальныйИдентификаторФормы:

// В модуле управляемой формы родительского сеанса
АдресРезультата = ПоместитьВоВременноеХранилище(Неопределено, УникальныйИдентификатор);
  1. Этот полученный АдресРезультата передается в фоновое задание как один из обычных параметров.
  2. Внутри процедуры фонового задания выполняются расчеты, и готовый результат записывается по переданному адресу:

// Внутри процедуры, выполняемой в фоновом задании
РезультатРасчетов = Новый Структура("Статус, Данные", "Успешно", ТаблицаРезультатов);
ПоместитьВоВременноеХранилище(РезультатРасчетов, АдресРезультата);
  1. После завершения фонового задания родительский сеанс может спокойно прочитать данные из временного хранилища по тому же самому адресу. Платформа 1С самостоятельно перенесет данные между сеансами при завершении работы фонового задания.

---

Часть 3. Как передать ДокументОбъект в фоновое задание?

Попытка передать объект типа ДокументОбъект или СправочникОбъект напрямую в параметры фонового задания гарантированно вызовет ошибку: «Ошибка передачи параметра фонового задания: параметр не может быть сериализован».

Объекты баз данных (такие как документы и справочники) не поддерживают сериализацию в XML/JSON «из коробки» для передачи между сеансами в неизменном виде, поскольку они содержат множество служебных свойств, транзакционных механизмов и привязаны к конкретному контексту соединения.

Рассмотрим три эффективных способа передачи данных документов в фоновые задания.

Способ 1. Передача по ссылке (для записанных объектов)

Если документ уже существует в базе данных и сохранен (записан), то самым простым, надежным и быстрым способом будет передача его ссылки ДокументСсылка.

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

  1. В параметры фонового задания передается ссылка на документ (ссылки отлично сериализуются платформой).
  2. Первой строкой внутри фонового задания мы получаем полноценный объект из базы данных для дальнейшей работы или модификации.

// Вызов фонового задания
ПараметрыЗапуска = Новый Массив;
ПараметрыЗапуска.Добавить(Объект.Ссылка); // Передаем ссылку
ФоновыеЗадания.Выполнить("МойОбщийМодуль.ОбработатьДокументВФоне", ПараметрыЗапуска);

// Внутри фонового задания в общем модуле
Процедура ОбработатьДокументВФоне(СсылкаНаДокумент) Экспорт
    
    // Получаем полноценный ДокументОбъект из базы данных
    ДокументОбъект = СсылкаНаДокумент.ПолучитьОбъект();
    
    // Производим необходимые расчеты и изменения
    ДокументОбъект.Комментарий = "Обработано в фоне в " + ТекущаяДата();
    
    // Записываем изменения обратно в базу данных
    ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);
    
КонецПроцедуры

Способ 2. Использование структуры-имитатора (для новых или измененных документов)

Что делать, если документ еще не сохранен в базу данных (например, пользователь только что создал его на клиенте, заполнил реквизиты, табличные части и хочет запустить предварительный расчет в фоне до момента записи)?

В этом случае нам поможет преобразование объекта документа в структуру-имитатор, состоящую из примитивных сериализуемых типов данных.

Рассмотрим пример подготовки такой структуры на клиенте и ее восстановление в фоновом задании:


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

После подготовки этой структуры мы передаем ее в фоновое задание. На стороне фонового задания мы воссоздаем объект документа и заполняем его:


// Внутри процедуры фонового задания
Процедура РассчитатьНесохраненныйДокумент(ДанныеДокумента) Экспорт
    
    // Создаем новый объект документа нужного типа в памяти сервера
    НовыйДокумент = Документы.МойДокумент.СоздатьДокумент();
    
    // Заполняем основные реквизиты
    ЗаполнитьЗначенияСвойств(НовыйДокумент, ДанныеДокумента);
    
    // Заполняем табличную часть товары
    Для Каждого СтрокаДанных Из ДанныеДокумента.Товары Цикл
        НоваяСтрока = НовыйДокумент.Товары.Добавить();
        ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаДанных);
    КонецЦикла;
    
    // Теперь у нас в памяти фонового задания есть полноценный ДокументОбъект,
    // с которым мы можем проводить любые вычисления и проверки!
    
КонецПроцедуры

Способ 3. Универсальная сериализация в XML через XDTO (для сложных документов)

Если документ имеет сложную структуру, десятки табличных частей и множество реквизитов, то ручное создание структур-имитаторов (как в Способе 2) становится крайне трудоемким и приводит к быстрому устареванию кода при изменении метаданных.

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

Посмотрим на пример изящной реализации этого механизма.

Шаг 1. Сериализуем объект документа в строку XML перед запуском фонового задания:


// В модуле формы или серверном модуле перед запуском задания
&НаСервере
Функция ПолучитьСериализованныйДокумент(ДокументОбъект)
    
    ЗаписьXML = Новый ЗаписьXML;
    ЗаписьXML.УстановитьСтроку();
    
    // СериализаторXDTO автоматически преобразует весь ДокументОбъект в XML
    СериализаторXDTO.ЗаписатьXML(ЗаписьXML, ДокументОбъект);
    
    Возврат ЗаписьXML.Закрыть(); // Возвращает строку XML
    
КонецФункции

Шаг 2. Передаем полученную строку XML в качестве строкового параметра в фоновое задание. Внутри фонового задания восстанавливаем полноценный объект одной строчкой кода:


// В процедуре фонового задания
Процедура ОбработатьСложныйДокументИзXML(СтрокаXML) Экспорт
    
    ЧтениеXML = Новый ЧтениеXML;
    ЧтениеXML.УстановитьСтроку(СтрокаXML);
    
    // Восстанавливаем полноценный ДокументОбъект со всеми реквизитами и ТЧ
    ДокументОбъект = СериализаторXDTO.ПрочитатьXML(ЧтениеXML);
    
    // Объект восстановлен в памяти фонового задания! 
    // Мы можем вызывать его методы, заполнять реквизиты или проводить расчеты.
    
КонецПроцедуры

Этот способ является максимально универсальным, надежным и не требует поддержки кода при добавлении новых реквизитов или табличных частей в метаданные документа.

---

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

Подведем итоги нашего архитектурного разбора:

← На главную