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