При разработке внешних отчетов и обработок в среде 1С:Предприятие 8.3 (особенно в типовых конфигурациях вроде «Бухгалтерия предприятия 3.0») часто возникает задача подменить стандартную логику заполнения документа. Например, в документе Акт сверки взаиморасчетов используется типовой механизм формирования данных, который запускается в фоновом задании. Попытка просто скопировать вызов функции ДлительныеОперации.ВыполнитьВФоне во внешний отчет приводит к ошибкам, так как система не может найти процедуру внешнего файла по строковому пути.
В этой статье мы подробно разберем, как правильно организовать вызов собственной процедуры из модуля внешнего отчета, проанализируем ограничения фоновых заданий для внешних объектов и рассмотрим рабочие программные решения.
Разберем ситуацию: в типовом решении вы видите код, который обращается к процедуре по ее полному имени в конфигурации, например: "Документы.АктСверкиВзаиморасчетов.ПодготовитьДанныеДляЗаполнения". Когда вы создаете внешний отчет (файл .erf), этот объект не является частью метаданных основной конфигурации. Метод ВыполнитьВФоне создает новый сеанс на сервере, в котором «область видимости» ограничена только тем, что прописано в дереве метаданных.
Поскольку ваш внешний отчет существует только в памяти текущего сеанса или в виде файла на диске, фоновое задание «не знает» о его существовании. Чтобы вызвать процедуру в фоне, пришлось бы сначала программно подключать внешний отчет внутри самого фонового задания, что значительно усложняет код.
Если объемы данных позволяют выполнить расчет без риска прерывания сеанса по таймауту, самым надежным и простым способом будет отказ от фонового задания в пользу прямого вызова процедуры модуля объекта. Рассмотрим, как это реализовать по шагам.
Для начала нам необходимо получить объект нашего отчета из данных формы. На управляемых формах мы работаем с данными формы (тип ОтчетОбъект.ИмяВашегоОтчета), а методы модуля объекта доступны только у «настоящего» объекта. Для этого используем метод РеквизитФормыВЗначение.
Проанализируем пример кода, который необходимо разместить в модуле формы вашего внешнего отчета:
&НаСервере
Процедура ВыполнитьЗаполнениеНаСервере()
// 1. Преобразуем данные формы в объект внешнего отчета
// Это позволяет нам обращаться к экспортным процедурам модуля объекта
ОтчетОбъект = РеквизитФормыВЗначение("Отчет");
// 2. Подготавливаем структуру параметров (аналогично типовому акту сверки)
СтруктураПараметров = Новый Структура;
СтруктураПараметров.Вставить("Организация", Отчет.Организация);
СтруктураПараметров.Вставить("Контрагент", Отчет.Контрагент);
СтруктураПараметров.Вставить("НачалоПериода", Отчет.НачалоПериода);
СтруктураПараметров.Вставить("КонецПериода", Отчет.КонецПериода);
// 3. Вызываем вашу процедуру из модуля объекта
// Убедитесь, что в модуле объекта процедура объявлена с ключевым словом Экспорт
РезультатЗаполнения = ОтчетОбъект.ПодготовитьДанныеДляЗаполнения(СтруктураПараметров);
// 4. Обрабатываем полученный результат
// Если нужно вернуть данные в форму, используем ЗначениеВРеквизитФормы
ЗначениеВРеквизитФормы(ОтчетОбъект, "Отчет");
КонецПроцедуры
Обратите внимание: в модуле объекта ваша процедура должна выглядеть следующим образом:
// В модуле ОБЪЕКТА внешнего отчета
Процедура ПодготовитьДанныеДляЗаполнения(Параметры) Экспорт
// Здесь ваш кастомный запрос
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ ..."; // Ваш измененный текст запроса
// Логика обработки данных
КонецПроцедуры
Если вам не требуется вызывать код из других мест и вы хотите максимально упростить структуру, можно перенести процедуру ПодготовитьДанныеДляЗаполнения непосредственно в модуль формы. В этом случае нам не потребуется использовать РеквизитФормыВЗначение, так как мы будем работать в контексте формы.
Рассмотрим этот вариант:
&НаСервере перед процедурой.Важный нюанс: если типовой код ожидает на выходе структуру, имитирующую результат работы фонового задания, вам придется создать ее вручную, чтобы не переписывать последующие блоки кода, которые проверяют статус выполнения. Проанализируем пример формирования такой «заглушки»:
&НаСервере
Функция ВыполнитьПодготовкуДанных()
// Имитируем структуру, которую возвращает ДлительныеОперации.ВыполнитьВФоне
Результат = Новый Структура;
Результат.Вставить("Статус", "Выполнено");
Результат.Вставить("ИдентификаторЗадания", Неопределено);
// Вызываем нашу локальную процедуру подготовки
ПодготовитьДанныеДляЗаполненияЛокально();
Возврат Результат;
КонецФункции
Если вы все же хотите сохранить возможность фонового выполнения (например, если запрос очень тяжелый и вы не хотите «вешать» интерфейс пользователя), правильным путем в рамках БСП (Библиотеки стандартных подсистем) будет регистрация вашего отчета в справочнике Дополнительные отчеты и обработки.
Рассмотрим, как это работает программно:
ДополнительныеОтчетыИОбработки.ВыполнитьКомандуИзФормыВФоне (наименование метода может варьироваться в зависимости от версии БСП).Когда мы заменяем стандартный вызов на свой, мы должны быть очень внимательны к контексту. Выясним основные причины возможных ошибок:
ЭтотОбъект. Во внешнем отчете у вас нет этого документа в базе. Все данные, которые нужны для запроса (организация, контрагент), должны передаваться через структуру Параметры.Экспорт. Без него метод будет невидим для контекста формы, даже после применения РеквизитФормыВЗначение.Подводя итог, можно сказать следующее: для внешнего отчета в 90% случаев оптимальным решением является синхронный вызов через ОтчетОбъект = РеквизитФормыВЗначение("Отчет"). Это дает полный контроль над логикой запроса и позволяет избежать сложностей с инициализацией внешних файлов в сеансах фоновых заданий.