Как исправить ошибку «Поле объекта не обнаружено» при использовании временных таблиц в 1С

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

При изучении разработки на платформе 1С:Предприятие 8.3 по методикам М.Г. Радченко многие начинающие программисты сталкиваются с трудностями на 14-м занятии. Этот урок посвящен оптимизации проведения документа «Оказание услуги» с использованием запросов и временных таблиц. Ошибка «Поле объекта не обнаружено (Сумма)» является крайне распространенной и сигнализирует о нарушении логической связи между частями сложного запроса.

В этой статье мы подробно разберем, почему возникает эта ошибка, как правильно структурировать запросы с временными таблицами и на что обратить внимание при обращении к результатам выборки в коде 1С.

Анализ механизма работы с временными таблицами

Для начала разберем, как работает механизм, описанный в листинге. Процесс разбит на несколько этапов:

  1. Создается объект МенеджерВременныхТаблиц.
  2. Выполняется первый запрос, который выбирает данные из табличной части документа и помещает их во временную таблицу (ВТ).
  3. Выполняется второй запрос, который соединяет данные из этой ВТ с остатками из регистров накопления.
  4. Результат обходится в цикле для формирования движений по регистрам.

Проанализируем проблемный участок кода. Ошибка «поле не найдено» возникает тогда, когда во втором запросе или в коде обхода выборки мы пытаемся обратиться к колонке, которая не была определена на этапе создания временной таблицы.

Выявление причины ошибки: несоответствие структуры таблиц

Рассмотрим текст первого запроса, представленный в вопросе автора:


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

Здесь мы видим, что во временную таблицу НоменклатураВДокументе попадают только три поля: Номенклатура, ВидНоменклатуры и КоличествоВДокументе.

Теперь посмотрим на фрагмент второго запроса:


Запрос2.Текст = "ВЫБРАТЬ
    |    ...
    |    НоменклатураВДокументе.Сумма КАК Сумма
    |ИЗ
    |    НоменклатураВДокументе КАК НоменклатураВДокументе
    |    ...";

Выясним причину: во втором запросе происходит обращение к полю НоменклатураВДокументе.Сумма. Однако в первом запросе это поле не было включено в список выбора. Система справедливо сообщает, что во временной таблице такого поля просто не существует. Это критическая ошибка проектирования запроса.

Пошаговое решение проблемы

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

Шаг 1. Модификация первого запроса. Нам нужно добавить поле Сумма из табличной части документа в список выбора первого запроса. Поскольку мы используем группировку (СГРУППИРОВАТЬ ПО), поле суммы должно быть агрегировано.


Запрос.Текст = 
    "ВЫБРАТЬ
    |    ОказаниеУслугиПереченьНомеклатуры.Номенклатура КАК Номенклатура,
    |    ОказаниеУслугиПереченьНомеклатуры.Номенклатура.ВидНоменклатуры КАК ВидНоменклатуры,
    |    СУММА(ОказаниеУслугиПереченьНомеклатуры.Количество) КАК КоличествоВДокументе,
    |    СУММА(ОказаниеУслугиПереченьНомеклатуры.Сумма) КАК СуммаВДокументе
    |ПОМЕСТИТЬ НоменклатураВДокументе
    |...

Шаг 2. Контроль имен полей. Обратите внимание, что автор в одном месте использует имя Сумма, а в другом СуммаВДокументе. Рекомендуется придерживаться единого именования. Если в первом запросе мы назвали поле СуммаВДокументе, то и во втором запросе, и в коде 1С при обращении к выборке нужно использовать именно это имя.

Шаг 3. Исправление цикла обхода. Проанализируем строку в цикле:

Движение.Выручка = ВыборкаДетальныеЗаписи.СуммаВДокументе;

Если во втором запросе поле было описано как НоменклатураВДокументе.Сумма КАК Сумма, то в коде нужно писать ВыборкаДетальныеЗаписи.Сумма. Если же вы изменили запрос на КАК СуммаВДокументе, тогда код заработает без изменений.

Оптимизация: Использование пакетных запросов

Хотя в книге Радченко на начальных этапах могут использоваться разные объекты Запрос, связанных через МенеджерВременныхТаблиц, в реальной практике 1С эффективнее использовать пакетные запросы. Это позволяет выполнить всю цепочку действий за один вызов сервера базы данных.

Рассмотрим пример правильной структуры пакетного запроса:


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

Запрос.УстановитьПараметр("Ссылка", Ссылка);
Запрос.УстановитьПараметр("МоментВремени", МоментВремени());

В данном примере точка с запятой ; разделяет первый запрос (помещение во временную таблицу) и второй запрос (выборка данных). Весь пакет выполняется методом Запрос.Выполнить(), что гораздо быстрее работы с несколькими объектами запросов.

Важные нюансы проведения и расчета себестоимости

Разберем дополнительные моменты, которые помогут избежать ошибок в будущем:

  1. Запись пустых наборов. Перед выполнением запроса остатков в процедуре ОбработкаПроведения важно очистить старые движения документа, если он проводится повторно. Код Движения.ОстаткиМатериалов.Записать(); именно это и делает. Без этой строки запрос может посчитать остатки с учетом самого себя, что приведет к неверному результату.
  2. Проверка на NULL. При использовании ЛЕВОЕ СОЕДИНЕНИЕ с виртуальными таблицами остатков, если товара нет на складе, база данных вернет NULL. Чтобы избежать ошибок при расчетах (например, деления на 0), всегда используйте функцию ЕСТЬNULL(Поле, 0) прямо в тексте запроса.
  3. Деление на ноль в коде. В цикле обхода обязательно должна быть проверка:
    
    Если Выборка.Количество = 0 Тогда
        СтоимостьМатериала = 0;
    Иначе
        СтоимостьМатериала = Выборка.Стоимость / Выборка.Количество;
    КонецЕсли;
    
    Это защитит программу от аварийного завершения, если по какой-то причине на складе числится стоимость, но нет количества.

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

← На главную