Как передать список значений из управляемой формы в запрос модуля объекта в 1С?

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

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

Анализ типовых ошибок при работе со списком значений в запросе

Проанализируем основные причины, из-за которых возникают сбои при передаче списочных данных:

  1. Разделение контекстов формы и объекта: реквизиты управляемой формы не видны в модуле объекта напрямую. Если реквизит создан только на форме, при вызове метода объекта через РеквизитФормыВЗначение данные этого реквизита не попадают в контекст объекта автоматически.
  2. Некорректная проверка на пустое значение: у объектов типа СписокЗначений и Массив отсутствует метод Пустая(). Этот метод применим только к ссылочным типам данных (например, СправочникСсылка или ДокументСсылка). При вызове Регистратор.Пустая() для списка значений платформа вызывает исключение времени выполнения.
  3. Особенности работы оператора В (&Параметр): в языке запросов 1С при сравнении с единичным значением используется знак равенства =, а при сравнении со списком или массивом — оператор В. Если в условие передается пустой список или массив, запрос не падает с ошибкой, но условие возвращает Ложь (в выборку ничего не попадет). Если же параметр вовсе не установлен и не вырезан из текста запроса, платформа выдает ошибку о незаданном параметре.
  4. Регистрозависимость функции СтрЗаменить: функция СтрЗаменить чувствительна к регистру символов и пробелам. Если в тексте запроса написано В (&Регистратор), а при удалении условия указано в (&Регистратор) (со строчной буквой), строка не будет заменена, фрагмент останется в тексте, и запрос выдаст ошибку о незаданном параметре.

Способ 1. Передача списка значений параметром в метод модуля объекта

Рассмотрим вариант, когда логика формирования отчета вынесена в модуль объекта, а реквизит отбора расположен на управляемой форме. Для надежной передачи списка значений преобразуем его в массив или передадим напрямую в процедуру формирования отчета.

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


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

Теперь разберем, как принять этот параметр в модуле объекта и корректно установить условия в тексте запроса. Проверим массив на заполненность с помощью метода Количество() или функции ЗначениеЗаполнено():


Процедура СформироватьОтчет(ТабДокумент, МассивРегистраторов = Неопределено) Экспорт

    Запрос = Новый Запрос;
    Запрос.Текст = 
    "ВЫБРАТЬ
    |   Планы.Ресурс КАК Номенклатура,
    |   Планы.КоличествоИзвнеОборот КАК Количество
    |ИЗ
    |   РегистрНакопления.оупПланыПотребностейИСопутствующихИзделий.Обороты(
    |       &ДатаНач,
    |       &ДатаКон,
    |       День,
    |       ПроизводственнаяПрограмма В (&Регистратор)) КАК Планы";

    Запрос.УстановитьПараметр("ДатаНач", ДатаНач);
    Запрос.УстановитьПараметр("ДатаКон", ДатаКон);

    // Проверяем, передан ли отбор и есть ли в нем элементы
    Если МассивРегистраторов <> Неопределено И МассивРегистраторов.Количество() > 0 Тогда
        Запрос.УстановитьПараметр("Регистратор", МассивРегистраторов);
    Иначе
        // Точно совпадает по регистру и пробелам с текстом в запросе
        Запрос.Текст = СтрЗаменить(Запрос.Текст, "ПроизводственнаяПрограмма В (&Регистратор)", "ИСТИНА");
    КонецЕсли;

    Результат = Запрос.Выполнить();
    // Дальнейшая обработка результата и вывод в табличный документ...

КонецПроцедуры

Способ 2. Использование булева флага без модификации текста запроса

Выясним причину, почему замена текста запроса через СтрЗаменить считается уязвимым подходом: при любом изменении форматирования запроса (переносы строк, изменение регистра операторов) замена перестает работать. Рассмотрим более надежный способ — внедрение флага отключения отбора прямо в текст запроса.

Посмотрим на пример текста запроса и установки параметров:


// В тексте запроса добавляем условие с проверкой флага
Запрос.Текст = 
"ВЫБРАТЬ
|   Планы.Ресурс КАК Номенклатура,
|   Планы.КоличествоИзвнеОборот КАК Количество
|ИЗ
|   РегистрНакопления.оупПланыПотребностейИСопутствующихИзделий.Обороты(
|       &ДатаНач,
|       &ДатаКон,
|       День,
|       (&ОтключитьОтборПоРегистраторам ИЛИ ПроизводственнаяПрограмма В (&Регистратор))) КАК Планы";

ОтборЗаполнен = (МассивРегистраторов <> Неопределено И МассивРегистраторов.Количество() > 0);

Запрос.УстановитьПараметр("ДатаНач", ДатаНач);
Запрос.УстановитьПараметр("ДатаКон", ДатаКон);
Запрос.УстановитьПараметр("ОтключитьОтборПоРегистраторам", НЕ ОтборЗаполнен);
Запрос.УстановитьПараметр("Регистратор", МассивРегистраторов);

Результат = Запрос.Выполнить();

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

Способ 3. Динамическая сборка условий запроса через массив

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

Рассмотрим пример реализации:


МассивУсловий = Новый Массив;

// Добавляем условие по номенклатуре
Если ЗначениеЗаполнено(Номенклатура) Тогда
    МассивУсловий.Добавить("Ресурс В ИЕРАРХИИ (&Номенклатура)");
    Запрос.УстановитьПараметр("Номенклатура", Номенклатура);
КонецЕсли;

// Добавляем условие по списку документов
Если МассивРегистраторов <> Неопределено И МассивРегистраторов.Количество() > 0 Тогда
    МассивУсловий.Добавить("ПроизводственнаяПрограмма В (&Регистратор)");
    Запрос.УстановитьПараметр("Регистратор", МассивРегистраторов);
КонецЕсли;

// Формируем итоговую строку условий для виртуальной таблицы
Если МассивУсловий.Количество() > 0 Тогда
    ТекстУсловий = СтрСоединить(МассивУсловий, " И ");
    Запрос.Текст = СтрЗаменить(Запрос.Текст, "&УсловияВиртуальнойТаблицы", ТекстУсловий);
Иначе
    Запрос.Текст = СтрЗаменить(Запрос.Текст, "&УсловияВиртуальнойТаблицы", "ИСТИНА");
КонецЕсли;

Рекомендации по отладке и проверке кода

  1. Для проверки наличия элементов в объектах типа СписокЗначений и Массив используем конструкцию Коллекция.Количество() > 0 либо функцию платформы ЗначениеЗаполнено(Список).
  2. Если список формируется на клиенте, выгружаем значения в массив через метод Список.ВыгрузитьЗначения() перед отправкой на сервер. Массив является более легковесной структурой для сериализации при клиент-серверных вызовах.
  3. Если на рабочем сервере отключена отладка, для локализации ошибок выводим промежуточные значения в журнал регистрации с помощью метода ЗаписьЖурналаРегистрации или тестируем отчет в файловой копии информационной базы.
← На главную