Почему веб-сервис 1С не видит «Метод объекта» при формировании отчета и как это исправить?

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

При интеграции 1С с внешними системами разработчики часто используют веб-сервисы (Web Services) для оперативного получения данных. Типичная задача — сформировать существующий в конфигурации отчет (например, «Расчетный листок») на стороне 1С, сохранить его в файл и передать внешнему потребителю в формате Base64. Однако при вызове проверенного кода, который отлично работает во внешней обработке, внутри веб-сервиса часто возникает ошибка: «Метод объекта не обнаружен (СформироватьОтчет)». В этой статье мы подробно разберем, почему возникает эта и сопутствующие ей ошибки, а также рассмотрим пошаговые методы их решения.

Причина 1. Разница контекстов исполнения (Клиент и Сервер)

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

Рассмотрим пример ошибочного объявления метода в модуле отчета:


#Если Клиент Тогда
Процедура СформироватьОтчет(ТабличныйДокумент) Экспорт
    // Код формирования отчета
КонецПроцедуры
#КонецЕсли

При вызове этого метода из веб-сервиса платформа выдаст ошибку, так как для сервера данный метод не существует. Чтобы решить эту проблему, проанализируем код модуля объекта отчета и выполним следующие действия:

  1. Убедимся, что ключевое слово Экспорт указано в объявлении метода.
  2. Уберем ограничивающие директивы компиляции (такие как #Если Клиент...) или заменим их на доступные на сервере (например, #Если Сервер ИЛИ ТолстыйКлиентОбычноеПриложение...).
  3. Убедимся, что внутри метода СформироватьОтчет отсутствуют вызовы интерактивных клиентских функций (таких как Предупреждение(), Вопрос() или открытие форм). Если они есть, их необходимо обойти с помощью проверки системной переменной или вынести в отдельный клиентский модуль.

Причина 2. Попытка вызова интерактивных настроек (ВосстановитьЗначение)

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


СтруктураПараметров = ВосстановитьЗначение("ПАРАМЕТРЫ_ПЕЧАТИ_РасчетногоЛисткаОрганизации");

Методы ВосстановитьЗначение() и СохранитьЗначение() предназначены исключительно для интерактивной работы пользователя в рамках его текущего сеанса. Они считывают настройки из локального кэша или системного хранилища конкретного пользователя ОС.

Сеанс веб-сервиса запускается под служебным пользователем (например, usr1cv8 на веб-сервере IIS или Apache) в фоновом режиме. У него нет интерактивного контекста и сохраненных настроек формы. В результате вызов ВосстановитьЗначение() возвращает Неопределено, что приводит к краху последующих алгоритмов, ожидающих структуру параметров.

Для обхода этой проблемы выполним следующие рекомендации:

Причина 3. Ошибка несоответствия типов при установке отбора

Следующий «подводный камень» — это типизация параметров отбора. При вызове метода веб-сервиса параметры (например, имя сотрудника или логин) передаются в виде простых типов данных: Строка, Число, Дата. Однако построитель отчетов или система компоновки данных (СКД) в полях отбора (например, «ФизЛицо» или «Организация») ожидают ссылочные типы данных.

Попытка напрямую присвоить строку в отбор:


ПостроительОтчетов.Отбор["ФизЛицо"].Значение = username; // Ошибка! username - строка.

Это неизбежно вызовет исключение «Ошибка при установке значения атрибута контекста (Значение)», так как платформа не может автоматически преобразовать строку в ссылку справочника.

Чтобы избежать этой ошибки, выполним предварительный поиск ссылочного объекта в базе данных 1С перед установкой отбора. Рассмотрим правильный пример реализации:


// Выполняем поиск физического лица по переданной строке (имени)
НайденноеФизЛицо = Справочники.ФизическиеЛица.НайтиПоНаименованию(username);

Если НайденноеФизЛицо.Пустая() Тогда
    // Записываем ошибку в журнал регистрации и вызываем исключение
    ЗаписьЖурналаРегистрации("ВебСервисы.ПолучениеРасчетногоЛистка",
        УровеньЖурналаРегистрации.Ошибка,,,
        "Не найдено физлицо с наименованием: " + username);
    ВызватьИсключение "Пользователь с указанным именем не найден в базе данных!";
КонецЕсли;

// Устанавливаем отбор, передавая корректную ссылку
ТиповыеОтчеты.ДобавитьОтбор(ОтчетРасчетныеЛисткиОрганизаций.КомпоновщикНастроек, "ФизЛицо", НайденноеФизЛицо);

Важное примечание: Поиск по наименованию может быть неточным при наличии однофамильцев. Безопаснее и надежнее передавать в веб-сервис уникальный идентификатор (UID), код сотрудника или СНИЛС и осуществлять поиск по ним (например, с помощью метода НайтиПоКоду()).

Причина 4. Работа с временными файлами на стороне веб-сервера

В коде формирования часто используется сохранение табличного документа на диск для последующего преобразования в Base64:


ТекИмяФайла = КаталогВременныхФайлов() + ИмяФайла + ".xls";
ТабличныйДокументРасчетныйЛисток.Записать(ТекИмяФайла, ТипФайлаТабличногоДокумента.XLS);

При переносе этого алгоритма на веб-сервис важно помнить о двух правилах:

  1. Права доступа: Системный пользователь операционной системы, под которым запущен пул приложений веб-сервера (например, IUSR, NetworkService или usr1cv8), должен иметь полные права на чтение и запись в папку, возвращаемую функцией КаталогВременныхФайлов() на сервере приложений 1С.
  2. Очистка ресурсов: Все созданные временные файлы должны быть гарантированно удалены после прочтения в двоичные данные, чтобы избежать переполнения дискового пространства сервера. Для надежности операции удаления рекомендуется оборачивать в блок Попытка — Исключение.

Готовый шаблон безопасной функции веб-сервиса

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


Функция ПолучитьРасчетныйЛистокВBase64(Username, ПериодОтчета) Экспорт
    
    // 1. Поиск ссылочного объекта для отбора
    СсылкаФизЛицо = Справочники.ФизическиеЛица.НайтиПоНаименованию(Username);
    Если СсылкаФизЛицо.Пустая() Тогда
        ВызватьИсключение "Ошибка: Физическое лицо '" + Username + "' не найдено в базе.";
    КонецЕсли;
    
    // 2. Создание объекта отчета на сервере
    ОтчетОбъект = Отчеты.РасчетныеЛисткиОрганизаций.Создать();
    
    // 3. Установка параметров отчета напрямую без использования ВосстановитьЗначение()
    ТиповыеОтчеты.УстановитьПараметр(ОтчетОбъект.КомпоновщикНастроек, "НачалоПериода", НачалоМесяца(ПериодОтчета));
    ТиповыеОтчеты.УстановитьПараметр(ОтчетОбъект.КомпоновщикНастроек, "КонецПериода", КонецМесяца(ПериодОтчета));
    ТиповыеОтчеты.УстановитьПараметр(ОтчетОбъект.КомпоновщикНастроек, "Группировать", Ложь);
    ТиповыеОтчеты.УстановитьПараметр(ОтчетОбъект.КомпоновщикНастроек, "ВидРасчетногоЛистка", "Для сотрудника");
    
    // Установка типизированного отбора
    ТиповыеОтчеты.ДобавитьОтбор(ОтчетОбъект.КомпоновщикНастроек, "ФизЛицо", СсылкаФизЛицо);
    
    // 4. Формирование табличного документа
    ТабДок = Новый ТабличныйДокумент;
    ОтчетОбъект.СформироватьОтчет(ТабДок);
    
    // 5. Сохранение во временный файл и преобразование в Base64
    ИмяФайла = "pay_slip_" + Строка(Новый УникальныйИдентификатор) + ".xls";
    ПолныйПуть = КаталогВременныхФайлов() + ИмяФайла;
    
    Попытка
        ТабДок.Записать(ПолныйПуть, ТипФайлаТабличногоДокумента.XLS);
        
        ДвоичныеДанныеФайла = Новый ДвоичныеДанные(ПолныйПуть);
        СтрокаBase64 = Base64Строка(ДвоичныеДанныеФайла);
    Исключение
        // Гарантируем запись детальной ошибки в Журнал регистрации
        ЗаписьЖурналаРегистрации("ВебСервисы.ПолучениеРасчетногоЛистка",
            УровеньЖурналаРегистрации.Ошибка,,,
            ОписаниеОшибки());
        ВызватьИсключение "Не удалось сформировать или записать файл отчета на сервере.";
    КонецПопытки;
    
    // Гарантированное удаление временного файла
    Попытка
        УдалитьФайлы(ПолныйПуть);
    Исключение
        // Ошибка удаления не должна прерывать работу веб-сервиса, просто фиксируем её
        ЗаписьЖурналаРегистрации("ВебСервисы.ОчисткаФайлов",
            УровеньЖурналаРегистрации.Предупреждение,,,
            "Не удалось удалить временный файл: " + ПолныйПуть);
    КонецПопытки;
    
    Возврат СтрокаBase64;
    
КонецФункции

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

← На главную