Как исправить ошибку Поле объекта не обнаружено при работе с запросами и выгрузке данных в отчет

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

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

Разбор первой причины: Ошибки в именовании объектов

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


МенеджерВТ = Новый МенеджерВременныхТаблиц; 
Запрос2 = Новый Запрос; 
Запрос2.МенеджерВременныхТаблиц = МенеджерВТ; 
Запрос2.Текст = "ВЫБРАТЬ СУММА(ТЧ.Расстояние) КАК Пробег ...";

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

Выясним причину: в коде создается объект Запрос2, заполняется его текст и параметры, однако команда Выполнить() вызывается у объекта с именем Запрос. Если переменная Запрос была определена выше с другим текстом (или не определена вовсе), то в ее результате просто не будет поля Пробег. Система закономерно выдаст ошибку Поле объекта не обнаружено (Пробег).

Решение: Всегда проверяйте соответствие имен переменных. Если вы используете несколько объектов типа Запрос, давайте им осмысленные имена, например ЗапросПоДокументам и ЗапросПоИтогам, чтобы избежать путаницы.

Разбор второй причины: Отсутствие псевдонимов в тексте запроса

Рассмотрим еще одну ситуацию. Программа может не видеть поле, если в тексте запроса вы используете агрегатные функции (СУММА, МАКСИМУМ и др.), но не назначаете им явное имя (псевдоним). Проанализируем пример:


Запрос.Текст = "ВЫБРАТЬ СУММА(Маршрут.Расстояние) ИЗ Документ.ПутевойЛист.Маршрут КАК Маршрут";
Результат = Запрос.Выполнить().Выбрать();
Пока Результат.Следующий() Цикл
    Сообщить(Результат.Расстояние); // ОШИБКА!
КонецЦикла;

В данном случае поле в выборке будет называться автоматически (например, Поле1). Чтобы код работал корректно, мы должны использовать ключевое слово КАК:


Запрос.Текст = "ВЫБРАТЬ СУММА(Маршрут.Расстояние) КАК Пробег ИЗ ...";
// Теперь обращение Результат.Пробег будет работать.

Проблема вывода данных: Почему во всех строках одинаковые значения?

Часто после исправления ошибки «Поле не обнаружено» разработчик сталкивается с другой проблемой: во всех строках отчета выводятся данные последнего обработанного документа. Посмотрим, почему так происходит. Разберем логику типичного ошибочного алгоритма:

  1. Выполняется запрос по всем документам за период.
  2. Выполняется второй запрос, который считает общий пробег по всем документам сразу.
  3. Запускается цикл обхода документов.
  4. Внутри цикла каждой строке отчета присваиваются значения из результата второго запроса.

Так как второй запрос не «знает», к какому именно документу относится пробег, он выдает общую сумму или значение последней записи. Чтобы исправить это, нам нужно связать расчеты с конкретным документом.

Вариант решения №1: Запрос внутри цикла (простой, но медленный)

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


ВыборкаДокументов = ЗапросПоДокументам.Выполнить().Выбрать();

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

Важное замечание: Несмотря на то что этот метод решает проблему, он является «антипаттерном» в 1С. Если документов будет 1000, система выполнит 1001 запрос к базе данных, что значительно замедлит работу.

Вариант решения №2: Оптимальный (один запрос с соединениями)

Рассмотрим, как профессионально решить задачу, избежав запросов в цикле и ошибок с полями объектов. Мы объединим получение данных документа и расчет итогов по табличным частям в одном запросе, используя ЛЕВОЕ СОЕДИНЕНИЕ.

Проанализируем шаги:

  1. Подготовим временную таблицу с итогами по маршрутам.
  2. Подготовим временную таблицу с итогами по топливу.
  3. Выберем основные данные документов и присоединим к ним наши итоги по ссылке.

Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ
|   Маршруты.Ссылка КАК Ссылка,
|   СУММА(Маршруты.Расстояние) КАК ПробегОбщий
|ПОМЕСТИТЬ ВТ_Пробег
|ИЗ
|   Документ.ПутевойЛист.Маршрут КАК Маршруты
|СГРУППИРОВАТЬ ПО
|   Маршруты.Ссылка
|;
|
|ВЫБРАТЬ
|   ПЛ.Ссылка,
|   ПЛ.Номер,
|   ПЛ.Дата,
|   ЕСТЬNULL(ВТ_Пробег.ПробегОбщий, 0) КАК Пробег
|ИЗ
|   Документ.ПутевойЛист КАК ПЛ
|   ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Пробег КАК ВТ_Пробег
|   ПО ПЛ.Ссылка = ВТ_Пробег.Ссылка
|ГДЕ
|   ПЛ.Дата МЕЖДУ &НачПериода И &КонПериода";

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

В этом примере мы используем функцию ЕСТЬNULL. Это критически важно, так как если у документа нет записей в табличной части «Маршрут», соединение вернет NULL, что может вызвать ошибки при дальнейших математических вычислениях в коде. Мы заменяем NULL на 0 сразу на уровне СУБД.

Особенности работы с остатками

В процессе формирования отчетов по путевым листам часто требуется расчет остатка топлива. В исходном коде темы использовался вызов функции ОстатокТопливаВБаке из модуля справочника внутри цикла. Посмотрим, как это оптимизировать.

Если вы вызываете расчет остатка в цикле, система каждый раз обращается к регистру накопления. Для ускорения работы лучше получить остатки на начало периода для всех транспортных средств одним запросом, сохранить их в Соответствие или ТаблицуЗначений, а затем внутри цикла просто выполнять арифметические действия: ОстатокНаНачало + Получено - Расход = ОстатокНаКонец.

Подведем итоги

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

Проанализировав эти подходы, мы видим, что большинство ошибок возникает из-за нарушения структуры связей между данными. Использование пакетных запросов и правильная группировка по полю Ссылка — это ключ к созданию надежных и быстрых отчетов в 1С.

← На главную