При разработке внешних отчетов и обработок на управляемых формах без использования СКД разработчики часто сталкиваются с ситуацией: на форме пользователь заполняет отбор в виде списка значений, но при попытке использовать этот список в запросе внутри модуля объекта возникают ошибки Метод объекта не обнаружен (Пустая) или Не задано значение параметра. Разберем по шагам причины такого поведения платформы 1С и рассмотрим правильные варианты передачи данных и построения запроса.
Проанализируем основные причины, из-за которых возникают сбои при передаче списочных данных:
РеквизитФормыВЗначение данные этого реквизита не попадают в контекст объекта автоматически.СписокЗначений и Массив отсутствует метод Пустая(). Этот метод применим только к ссылочным типам данных (например, СправочникСсылка или ДокументСсылка). При вызове Регистратор.Пустая() для списка значений платформа вызывает исключение времени выполнения.В (&Параметр): в языке запросов 1С при сравнении с единичным значением используется знак равенства =, а при сравнении со списком или массивом — оператор В. Если в условие передается пустой список или массив, запрос не падает с ошибкой, но условие возвращает Ложь (в выборку ничего не попадет). Если же параметр вовсе не установлен и не вырезан из текста запроса, платформа выдает ошибку о незаданном параметре.СтрЗаменить: функция СтрЗаменить чувствительна к регистру символов и пробелам. Если в тексте запроса написано В (&Регистратор), а при удалении условия указано в (&Регистратор) (со строчной буквой), строка не будет заменена, фрагмент останется в тексте, и запрос выдаст ошибку о незаданном параметре.Рассмотрим вариант, когда логика формирования отчета вынесена в модуль объекта, а реквизит отбора расположен на управляемой форме. Для надежной передачи списка значений преобразуем его в массив или передадим напрямую в процедуру формирования отчета.
Посмотрим на реализацию серверной процедуры в модуле управляемой формы. Выгрузим список значений в массив перед передачей:
&НаСервере
Процедура ОбновитьОтчетСервер()
// Получаем массив значений из реквизита формы типа СписокЗначений
МассивРегистраторов = Отчет.Регистратор.ВыгрузитьЗначения();
// Преобразуем контекст формы в объект отчета
ОтчетОбъект = РеквизитФормыВЗначение("Отчет");
// Вызываем процедуру формирования, передавая массив в качестве дополнительного параметра
ОтчетОбъект.СформироватьОтчет(ТабДокумент, МассивРегистраторов);
КонецПроцедуры
Теперь разберем, как принять этот параметр в модуле объекта и корректно установить условия в тексте запроса. Проверим массив на заполненность с помощью метода Количество() или функции ЗначениеЗаполнено():
Процедура СформироватьОтчет(ТабДокумент, МассивРегистраторов = Неопределено) Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Планы.Ресурс КАК Номенклатура,
| Планы.КоличествоИзвнеОборот КАК Количество
|ИЗ
| РегистрНакопления.оупПланыПотребностейИСопутствующихИзделий.Обороты(
| &ДатаНач,
| &ДатаКон,
| День,
| ПроизводственнаяПрограмма В (&Регистратор)) КАК Планы";
Запрос.УстановитьПараметр("ДатаНач", ДатаНач);
Запрос.УстановитьПараметр("ДатаКон", ДатаКон);
// Проверяем, передан ли отбор и есть ли в нем элементы
Если МассивРегистраторов <> Неопределено И МассивРегистраторов.Количество() > 0 Тогда
Запрос.УстановитьПараметр("Регистратор", МассивРегистраторов);
Иначе
// Точно совпадает по регистру и пробелам с текстом в запросе
Запрос.Текст = СтрЗаменить(Запрос.Текст, "ПроизводственнаяПрограмма В (&Регистратор)", "ИСТИНА");
КонецЕсли;
Результат = Запрос.Выполнить();
// Дальнейшая обработка результата и вывод в табличный документ...
КонецПроцедуры
Выясним причину, почему замена текста запроса через СтрЗаменить считается уязвимым подходом: при любом изменении форматирования запроса (переносы строк, изменение регистра операторов) замена перестает работать. Рассмотрим более надежный способ — внедрение флага отключения отбора прямо в текст запроса.
Посмотрим на пример текста запроса и установки параметров:
// В тексте запроса добавляем условие с проверкой флага
Запрос.Текст =
"ВЫБРАТЬ
| Планы.Ресурс КАК Номенклатура,
| Планы.КоличествоИзвнеОборот КАК Количество
|ИЗ
| РегистрНакопления.оупПланыПотребностейИСопутствующихИзделий.Обороты(
| &ДатаНач,
| &ДатаКон,
| День,
| (&ОтключитьОтборПоРегистраторам ИЛИ ПроизводственнаяПрограмма В (&Регистратор))) КАК Планы";
ОтборЗаполнен = (МассивРегистраторов <> Неопределено И МассивРегистраторов.Количество() > 0);
Запрос.УстановитьПараметр("ДатаНач", ДатаНач);
Запрос.УстановитьПараметр("ДатаКон", ДатаКон);
Запрос.УстановитьПараметр("ОтключитьОтборПоРегистраторам", НЕ ОтборЗаполнен);
Запрос.УстановитьПараметр("Регистратор", МассивРегистраторов);
Результат = Запрос.Выполнить();
Преимущества данного подхода: текст запроса остается неизменным и легко читается, исключаются ошибки о незаданных параметрах, а отбор срабатывает только тогда, когда в массив переданы реальные ссылки.
Если в отчете используется множество необязательных фильтров (по номенклатуре, менеджеру, регистраторам), разберем классический подход с динамической сборкой секции условий через СтрСоединить.
Рассмотрим пример реализации:
МассивУсловий = Новый Массив;
// Добавляем условие по номенклатуре
Если ЗначениеЗаполнено(Номенклатура) Тогда
МассивУсловий.Добавить("Ресурс В ИЕРАРХИИ (&Номенклатура)");
Запрос.УстановитьПараметр("Номенклатура", Номенклатура);
КонецЕсли;
// Добавляем условие по списку документов
Если МассивРегистраторов <> Неопределено И МассивРегистраторов.Количество() > 0 Тогда
МассивУсловий.Добавить("ПроизводственнаяПрограмма В (&Регистратор)");
Запрос.УстановитьПараметр("Регистратор", МассивРегистраторов);
КонецЕсли;
// Формируем итоговую строку условий для виртуальной таблицы
Если МассивУсловий.Количество() > 0 Тогда
ТекстУсловий = СтрСоединить(МассивУсловий, " И ");
Запрос.Текст = СтрЗаменить(Запрос.Текст, "&УсловияВиртуальнойТаблицы", ТекстУсловий);
Иначе
Запрос.Текст = СтрЗаменить(Запрос.Текст, "&УсловияВиртуальнойТаблицы", "ИСТИНА");
КонецЕсли;
СписокЗначений и Массив используем конструкцию Коллекция.Количество() > 0 либо функцию платформы ЗначениеЗаполнено(Список).Список.ВыгрузитьЗначения() перед отправкой на сервер. Массив является более легковесной структурой для сериализации при клиент-серверных вызовах.ЗаписьЖурналаРегистрации или тестируем отчет в файловой копии информационной базы.