При разработке подсистем автоматической рассылки отчетов по расписанию разработчики часто сталкиваются с ситуацией: в интерактивном режиме (при запуске пользователем вручную) отчет формируется без ошибок, однако при попытке запустить тот же код в регламентном или фоновом задании система выдает ошибку «Метод объекта не обнаружен (СформироватьОтчет)» либо при попытке получить форму возвращает Неопределено.
Разберем детально архитектурные причины такого поведения платформы 1С:Предприятие и рассмотрим практические способы решения этой задачи.
Проанализируем, почему код ведет себя по-разному в пользовательском сеансе и в регламентном задании:
#Если Клиент Тогда, #Если ТолстыйКлиентОбычноеПриложение Тогда. На сервере этот код физически не компилируется. Экспортная процедура, находящаяся внутри такой директивы, на сервере просто не существует, что и приводит к ошибке «Метод объекта не обнаружен».ПолучитьФорму() и ОткрытьФорму() в серверном неинтерактивном сеансе не имеют экранного контекста. Вызов ПолучитьФорму() вернет либо Неопределено, либо завершится аварийно, так как сервер приложений не может инициализировать элементы пользовательского интерфейса.Рассмотрим рабочие способы обхода этой проблемы в зависимости от архитектуры отчетов.
Если типовые или пользовательские отчеты построены на основе Схемы компоновки данных (СКД), нам не требуется открывать форму или вызывать процедуры модуля объекта. Платформа 1С позволяет программно инициализировать схему, применить сохраненные настройки и вывести результат в табличный документ на чистом сервере.
Разберем по шагам универсальный алгоритм формирования отчета через СКД:
// Функция формирует отчет по имени метаданных и возвращает готовый ТабличныйДокумент
Функция СформироватьОтчетНаСервере(ИмяОтчета, ПользовательскиеНастройки = Неопределено) Экспорт
ТабличныйДокумент = Новый ТабличныйДокумент;
// Получаем объект метаданных отчета и создаем его экземпляр
ОбъектОтчета = Отчеты[ИмяОтчета].Создать();
// Получаем основную схему компоновки данных
СхемаКомпоновкиДанных = ОбъектОтчета.ПолучитьМакет("ОсновнаяСхемаКомпоновкиДанных");
Если СхемаКомпоновкиДанных = Неопределено Тогда
ВызватьИсключение "У отчета " + ИмяОтчета + " отсутствует ОсновнаяСхемаКомпоновкиДанных";
КонецЕсли;
// Инициализируем настройки: берем настройки по умолчанию или переданные сохраненные
Если ПользовательскиеНастройки <> Неопределено Тогда
Настройки = ПользовательскиеНастройки;
Иначе
Настройки = СхемаКомпоновкиДанных.НастройкиПоУмолчанию;
КонецЕсли;
// Инициализируем компоновщик макета
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(
СхемаКомпоновкиДанных,
Настройки,
,,,
Тип("ГенераторМакетаКомпоновкиДанныхДляКоллекцииЗначений")
);
// Если требуется стандартный табличный вывод:
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, Настройки);
// Инициализируем процессор компоновки
ПроцессорКомпоновкиДанных = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновкиДанных.Инициализировать(МакетКомпоновки);
// Выводим результат в табличный документ
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(ТабличныйДокумент);
ПроцессорВывода.Вывести(ПроцессорКомпоновкиДанных);
Возврат ТабличныйДокумент;
КонецФункции
Сформированный табличный документ можно сразу сохранить во временный файл (PDF, XLSX) и прикрепить к письму электронной почты:
ИмяФайла = КаталогВременныхФайлов() + "Отчет_" + Строка(ТекущаяДата()) + ".pdf";
ТабличныйДокумент.Записать(ИмяФайла, ТипФайлаТабличногоДокумента.PDF);
Если отчет не использует СКД (например, написан с использованием построителя отчетов или прямых запросов с ручным заполнением секций макета), проанализируем, как адаптировать его программный код:
#Если Клиент Тогда или заменяем их на директивы компиляции сервера.Предупреждение(), Вопрос(), Сообщить(), ОткрытьФорму().Посмотрим на пример правильного оформления экспортного метода в модуле объекта отчета:
// В модуле объекта отчета (доступен и на сервере, и на клиенте)
Процедура СформироватьОтчетВТабличныйДокумент(ТабличныйДокумент) Экспорт
Макет = ПолучитьМакет("ОсновнойМакет");
Шапка = Макет.ПолучитьОбласть("Шапка");
СтрокаТаблицы = Макет.ПолучитьОбласть("Строка");
ТабличныйДокумент.Очистить();
ТабличныйДокумент.Вывести(Шапка);
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Наименование КАК Наименование,
| ТоварыНаСкладахОстатки.КоличествоОстаток КАК Остаток
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки(&Период, ) КАК ТоварыНаСкладахОстатки
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Номенклатура
| ПО ТоварыНаСкладахОстатки.Номенклатура = Номенклатура.Ссылка";
Запрос.УстановитьПараметр("Период", ТекущаяДата());
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
СтрокаТаблицы.Параметры.Заполнить(Выборка);
ТабличныйДокумент.Вывести(СтрокаТаблицы);
КонецЦикла;
КонецПроцедуры
Вызов из модуля регламентного задания или справочника теперь будет работать корректно:
ОтчетОбъект = Отчеты[ИмяОтчета].Создать();
ТабДок = Новый ТабличныйДокумент;
ОтчетОбъект.СформироватьОтчетВТабличныйДокумент(ТабДок);
Бывают ситуации, когда конфигурация находится на полной поддержке вендора, изменений вносить нельзя, а типовые отчеты полностью завязаны на логику форм толстого клиента. В таком случае регламентное задание сервера выполнить их не сможет.
Для решения задачи разберем вариант с запуском отдельного служебного сеанса 1С:
1cv8.exe с параметрами командной строки:
"C:\Program Files\1cv8\8.3.XX.XXXX\bin\1cv8.exe" ENTERPRISE /F "C:\Bases\MyBase" /N "РоботРассылки" /P "Пароль" /C "ВыполнитьРассылку"
ПриНачалеРаботыСистемы() считываем переданный параметр запуска через функцию ПараметрЗапуска:
Процедура ПриНачалеРаботыСистемы()
Если ПараметрЗапуска = "ВыполнитьРассылку" Тогда
// Здесь доступен полноценный клиентский контекст:
// можно открывать формы, вызывать клиентские процедуры отчетов
ОбработатьРассылкуОтчетов();
// Завершаем работу сеанса после выполнения задачи
ЗавершитьРаботуСистемы(Ложь);
КонецЕсли;
КонецПроцедуры
Таким образом, для отчетов на СКД всегда следует предпочитать Способ 1 (формирование через процессор компоновки данных), а для нестандартных отчетов — переносить расчетные процедуры в контекст сервера без привязки к формам.