Как исправить ошибку «Значение не является значением объектного типа (Выбрать)» при формировании печатной формы в 1С?

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

При разработке алгоритмов печати документов начинающие разработчики часто сталкиваются с исключением «Значение не является значением объектного типа (Выбрать)». Ошибка возникает в момент, когда в документе выбираются реквизиты, не подпадающие под условия запроса, либо когда нарушен порядок обхода выборки результата запроса. Разберем по шагам причины такого поведения платформы и рассмотрим правильные варианты построения запросов и вывода областей табличного документа.

Причины возникновения ошибки

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


ВЫБРАТЬ
    Выхода_на_практику.Ссылка,
    Выхода_на_практику.Дата,
    Выхода_на_практику.Студенты.(
        НомерСтроки,
        ФИО_студента,
        База,
        Основа_обучения
    )
ИЗ
    Документ.Выхода_на_практику КАК Выхода_на_практику
ГДЕ
    Выхода_на_практику.Ссылка В(&Ссылка)
    И (Выхода_на_практику.Студенты.База = Значение(Перечисление.База_классов.классов_9)
        ИЛИ Выхода_на_практику.Студенты.База = Значение(Перечисление.База_классов.ПустаяСсылка))
    И Выхода_на_практику.Студенты.Основа_обучения = Значение(Перечисление.Основа_обучения.бюджетник)

К возникновению исключительной ситуации приводят два ключевых фактора:

  1. Особенность поведения условия ГДЕ на вложенные таблицы: Когда условие накладывается на поле табличной части (Студенты.База) в запросе к шапке документа, платформа 1С проверяет факт существования подходящих строк (конструкция EXISTS). Если в документе ни одна строка не соответствует фильтру, из выборки исключается сам документ целиком. В результате запрос возвращает абсолютно пустой результат.
  2. Некорректная работа с курсором выборки: После вызова Запрос.Выполнить().Выбрать() указатель стоит перед первой строкой. Если результат запроса пуст, вызов Выборка.Следующий() возвращает Ложь. Если этот результат не проверяется через условие Если Выборка.Следующий() Тогда, дальнейшее обращение к вложенным полям (например, Выборка.Студенты) возвращает значение Неопределено. Попытка вызвать метод .Выбрать() у значения Неопределено немедленно приводит к ошибке «Значение не является значением объектного типа».

Вариант 1. Запрос к реальной таблице табличной части с группировкой по документу (Рекомендуемый)

Рассмотрим наиболее производительный и надежный подход. Вместо выборки через точку от документа перепишем запрос напрямую к табличной части Документ.Выхода_на_практику.Студенты и применим иерархическую группировку через секцию ИТОГИ ПО.

Посмотрим на пример правильного кода процедуры печати:


Макет = Документы.Выхода_на_практику.ПолучитьМакет("Печать");
ОбластьЗаголовок = Макет.ПолучитьОбласть("Заголовок");
ОбластьШапка = Макет.ПолучитьОбласть("Шапка");
ОбластьСтудентыШапка = Макет.ПолучитьОбласть("СтудентыШапка");
ОбластьСтуденты = Макет.ПолучитьОбласть("Студенты");

ТабДок.Очистить();

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

Запрос.УстановитьПараметр("Ссылка", Ссылка);
Запрос.УстановитьПараметр("База9", Перечисления.База_классов.классов_9);
Запрос.УстановитьПараметр("ПустаяБаза", Перечисления.База_классов.ПустаяСсылка());
Запрос.УстановитьПараметр("ОсноваБюджет", Перечисления.Основа_обучения.бюджетник);

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

Если Не РезультатЗапроса.Пустой() Тогда
    ТабДок.Вывести(ОбластьЗаголовок);
    
    ВыборкаДокумент = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
    Пока ВыборкаДокумент.Следующий() Цикл
        
        ОбластьШапка.Параметры.Заполнить(ВыборкаДокумент);
        ОбластьШапка.Параметры.Дата_начала_практики = Формат(ВыборкаДокумент.Дата_начала_практики, "ДЛФ=D");
        ОбластьШапка.Параметры.Дата_конца_практики = Формат(ВыборкаДокумент.Дата_конца_практики, "ДЛФ=D");
        ТабДок.Вывести(ОбластьШапка);
        
        ТабДок.Вывести(ОбластьСтудентыШапка);
        
        ВыборкаСтроки = ВыборкаДокумент.Выбрать();
        Пока ВыборкаСтроки.Следующий() Цикл
            ОбластьСтуденты.Параметры.Заполнить(ВыборкаСтроки);
            ТабДок.Вывести(ОбластьСтуденты);
        КонецЦикла;
        
    КонецЦикла;
КонецЕсли;

Разберем преимущества этого решения:

  1. Метод РезультатЗапроса.Пустой() заранее определяет, есть ли данные для печати. Если подходящих студентов в документе нет, табличный документ просто останется пустым без падений программы.
  2. Использование конструкции ИТОГИ ПО Ссылка позволяет корректно обойти документ и только те строки табличной части, которые точно удовлетворили условию отбора.

Вариант 2. Использование пакета запросов (Вывод шапки при пустых строках)

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

Выполним разделение на два независимых запроса в одном пакете:


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

Запрос.УстановитьПараметр("Ссылка", Ссылка);
Запрос.УстановитьПараметр("База9", Перечисления.База_классов.классов_9);
Запрос.УстановитьПараметр("ПустаяБаза", Перечисления.База_классов.ПустаяСсылка());
Запрос.УстановитьПараметр("ОсноваБюджет", Перечисления.Основа_обучения.бюджетник);

ПакетРезультатов = Запрос.ВыполнитьПакет();
РезультатШапка = ПакетРезультатов[0];
РезультатСтроки = ПакетРезультатов[1];

Если Не РезультатШапка.Пустой() Тогда
    ТабДок.Вывести(ОбластьЗаголовок);
    
    ВыборкаШапка = РезультатШапка.Выбрать();
    Пока ВыборкаШапка.Следующий() Цикл
        ОбластьШапка.Параметры.Заполнить(ВыборкаШапка);
        ТабДок.Вывести(ОбластьШапка);
        
        Если Не РезультатСтроки.Пустой() Тогда
            ТабДок.Вывести(ОбластьСтудентыШапка);
            
            ВыборкаСтроки = РезультатСтроки.Выбрать();
            Пока ВыборкаСтроки.Следующий() Цикл
                ОбластьСтуденты.Параметры.Заполнить(ВыборкаСтроки);
                ТабДок.Вывести(ОбластьСтуденты);
            КонецЦикла;
        КонецЕсли;
    КонецЦикла;
КонецЕсли;

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

← На главную