При интеграции 1С с внешними системами разработчики часто используют веб-сервисы (Web Services) для оперативного получения данных. Типичная задача — сформировать существующий в конфигурации отчет (например, «Расчетный листок») на стороне 1С, сохранить его в файл и передать внешнему потребителю в формате Base64. Однако при вызове проверенного кода, который отлично работает во внешней обработке, внутри веб-сервиса часто возникает ошибка: «Метод объекта не обнаружен (СформироватьОтчет)». В этой статье мы подробно разберем, почему возникает эта и сопутствующие ей ошибки, а также рассмотрим пошаговые методы их решения.
Основная причина, по которой веб-сервис не видит метод СформироватьОтчет, заключается в разнице контекстов компиляции. Веб-сервисы 1С всегда выполняются исключительно на стороне сервера (в контексте специального сеанса веб-сервиса). Если процедура формирования отчета в модуле объекта обернута в инструкции препроцессора, ограничивающие выполнение кодом клиента, серверная часть веб-сервиса этот метод просто не скомпилирует.
Рассмотрим пример ошибочного объявления метода в модуле отчета:
#Если Клиент Тогда
Процедура СформироватьОтчет(ТабличныйДокумент) Экспорт
// Код формирования отчета
КонецПроцедуры
#КонецЕсли
При вызове этого метода из веб-сервиса платформа выдаст ошибку, так как для сервера данный метод не существует. Чтобы решить эту проблему, проанализируем код модуля объекта отчета и выполним следующие действия:
Экспорт указано в объявлении метода.#Если Клиент...) или заменим их на доступные на сервере (например, #Если Сервер ИЛИ ТолстыйКлиентОбычноеПриложение...).СформироватьОтчет отсутствуют вызовы интерактивных клиентских функций (таких как Предупреждение(), Вопрос() или открытие форм). Если они есть, их необходимо обойти с помощью проверки системной переменной или вынести в отдельный клиентский модуль.Когда разработчик адаптирует код для работы на сервере, он может столкнуться со следующей ошибкой в процедурах инициализации параметров отчета:
СтруктураПараметров = ВосстановитьЗначение("ПАРАМЕТРЫ_ПЕЧАТИ_РасчетногоЛисткаОрганизации");
Методы ВосстановитьЗначение() и СохранитьЗначение() предназначены исключительно для интерактивной работы пользователя в рамках его текущего сеанса. Они считывают настройки из локального кэша или системного хранилища конкретного пользователя ОС.
Сеанс веб-сервиса запускается под служебным пользователем (например, usr1cv8 на веб-сервере IIS или Apache) в фоновом режиме. У него нет интерактивного контекста и сохраненных настроек формы. В результате вызов ВосстановитьЗначение() возвращает Неопределено, что приводит к краху последующих алгоритмов, ожидающих структуру параметров.
Для обхода этой проблемы выполним следующие рекомендации:
Следующий «подводный камень» — это типизация параметров отбора. При вызове метода веб-сервиса параметры (например, имя сотрудника или логин) передаются в виде простых типов данных: Строка, Число, Дата. Однако построитель отчетов или система компоновки данных (СКД) в полях отбора (например, «ФизЛицо» или «Организация») ожидают ссылочные типы данных.
Попытка напрямую присвоить строку в отбор:
ПостроительОтчетов.Отбор["ФизЛицо"].Значение = username; // Ошибка! username - строка.
Это неизбежно вызовет исключение «Ошибка при установке значения атрибута контекста (Значение)», так как платформа не может автоматически преобразовать строку в ссылку справочника.
Чтобы избежать этой ошибки, выполним предварительный поиск ссылочного объекта в базе данных 1С перед установкой отбора. Рассмотрим правильный пример реализации:
// Выполняем поиск физического лица по переданной строке (имени)
НайденноеФизЛицо = Справочники.ФизическиеЛица.НайтиПоНаименованию(username);
Если НайденноеФизЛицо.Пустая() Тогда
// Записываем ошибку в журнал регистрации и вызываем исключение
ЗаписьЖурналаРегистрации("ВебСервисы.ПолучениеРасчетногоЛистка",
УровеньЖурналаРегистрации.Ошибка,,,
"Не найдено физлицо с наименованием: " + username);
ВызватьИсключение "Пользователь с указанным именем не найден в базе данных!";
КонецЕсли;
// Устанавливаем отбор, передавая корректную ссылку
ТиповыеОтчеты.ДобавитьОтбор(ОтчетРасчетныеЛисткиОрганизаций.КомпоновщикНастроек, "ФизЛицо", НайденноеФизЛицо);
Важное примечание: Поиск по наименованию может быть неточным при наличии однофамильцев. Безопаснее и надежнее передавать в веб-сервис уникальный идентификатор (UID), код сотрудника или СНИЛС и осуществлять поиск по ним (например, с помощью метода НайтиПоКоду()).
В коде формирования часто используется сохранение табличного документа на диск для последующего преобразования в Base64:
ТекИмяФайла = КаталогВременныхФайлов() + ИмяФайла + ".xls";
ТабличныйДокументРасчетныйЛисток.Записать(ТекИмяФайла, ТипФайлаТабличногоДокумента.XLS);
При переносе этого алгоритма на веб-сервис важно помнить о двух правилах:
IUSR, NetworkService или usr1cv8), должен иметь полные права на чтение и запись в папку, возвращаемую функцией КаталогВременныхФайлов() на сервере приложений 1С.Попытка — Исключение.Давайте объединим все исправления и спроектируем надежную функцию для модуля веб-сервиса, которая безопасно находит объект, устанавливает параметры отчета на сервере, формирует табличный документ, конвертирует его в Base64 и очищает за собой временные файлы:
Функция ПолучитьРасчетныйЛистокВBase64(Username, ПериодОтчета) Экспорт
// 1. Поиск ссылочного объекта для отбора
СсылкаФизЛицо = Справочники.ФизическиеЛица.НайтиПоНаименованию(Username);
Если СсылкаФизЛицо.Пустая() Тогда
ВызватьИсключение "Ошибка: Физическое лицо '" + Username + "' не найдено в базе.";
КонецЕсли;
// 2. Создание объекта отчета на сервере
ОтчетОбъект = Отчеты.РасчетныеЛисткиОрганизаций.Создать();
// 3. Установка параметров отчета напрямую без использования ВосстановитьЗначение()
ТиповыеОтчеты.УстановитьПараметр(ОтчетОбъект.КомпоновщикНастроек, "НачалоПериода", НачалоМесяца(ПериодОтчета));
ТиповыеОтчеты.УстановитьПараметр(ОтчетОбъект.КомпоновщикНастроек, "КонецПериода", КонецМесяца(ПериодОтчета));
ТиповыеОтчеты.УстановитьПараметр(ОтчетОбъект.КомпоновщикНастроек, "Группировать", Ложь);
ТиповыеОтчеты.УстановитьПараметр(ОтчетОбъект.КомпоновщикНастроек, "ВидРасчетногоЛистка", "Для сотрудника");
// Установка типизированного отбора
ТиповыеОтчеты.ДобавитьОтбор(ОтчетОбъект.КомпоновщикНастроек, "ФизЛицо", СсылкаФизЛицо);
// 4. Формирование табличного документа
ТабДок = Новый ТабличныйДокумент;
ОтчетОбъект.СформироватьОтчет(ТабДок);
// 5. Сохранение во временный файл и преобразование в Base64
ИмяФайла = "pay_slip_" + Строка(Новый УникальныйИдентификатор) + ".xls";
ПолныйПуть = КаталогВременныхФайлов() + ИмяФайла;
Попытка
ТабДок.Записать(ПолныйПуть, ТипФайлаТабличногоДокумента.XLS);
ДвоичныеДанныеФайла = Новый ДвоичныеДанные(ПолныйПуть);
СтрокаBase64 = Base64Строка(ДвоичныеДанныеФайла);
Исключение
// Гарантируем запись детальной ошибки в Журнал регистрации
ЗаписьЖурналаРегистрации("ВебСервисы.ПолучениеРасчетногоЛистка",
УровеньЖурналаРегистрации.Ошибка,,,
ОписаниеОшибки());
ВызватьИсключение "Не удалось сформировать или записать файл отчета на сервере.";
КонецПопытки;
// Гарантированное удаление временного файла
Попытка
УдалитьФайлы(ПолныйПуть);
Исключение
// Ошибка удаления не должна прерывать работу веб-сервиса, просто фиксируем её
ЗаписьЖурналаРегистрации("ВебСервисы.ОчисткаФайлов",
УровеньЖурналаРегистрации.Предупреждение,,,
"Не удалось удалить временный файл: " + ПолныйПуть);
КонецПопытки;
Возврат СтрокаBase64;
КонецФункции
Используя данный подход, мы полностью исключаем влияние клиентского контекста, избегаем ошибок отсутствия интерактивных настроек и гарантируем точную типизацию данных при установке отборов компоновщика настроек или построителя отчета.