Как исправить ошибку «Поле объекта не обнаружено (Стоимость)» в упражнении 14 практического пособия М. Радченко

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

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

Разбор причины возникновения ошибки

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

Рассмотрим фрагмент кода, где происходит ошибка:


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

Проанализируем ситуацию: переменная РезультатЗапроса (использованная во второй строке) была инициализирована гораздо выше по коду — она хранит результат выполнения первого запроса. Первый запрос в данном упражнении предназначен лишь для помещения данных во временную таблицу НоменклатураДокумента и он не содержит поля Стоимость.

Второй запрос (Запрос2) как раз таки соединяет временную таблицу с регистрами остатков и выбирает поле Стоимость. Но, вызывая метод Выбрать() у первого результата (РезультатЗапроса), мы получаем выборку, в которой нужной колонки просто не существует. Отсюда и закономерная реакция платформы: Поле объекта не обнаружено (Стоимость).

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

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

  1. Найдем в коде строку, где выполняется второй запрос: Результат = Запрос2.Выполнить();.
  2. Обратим внимание на следующую за ней строку создания выборки.
  3. Заменим объект, у которого вызывается метод Выбрать(). Вместо РезультатЗапроса (старый результат) напишем Результат (актуальный результат).

Правильный фрагмент кода должен выглядеть так:


// Выполняем второй запрос, который реально возвращает данные с остатками и стоимостью
Результат = Запрос2.Выполнить();

// Создаем выборку именно из ЭТОГО результата
ВыборкаДетальныеЗаписи = Результат.Выбрать();

Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
    // Теперь поле Стоимость будет доступно в выборке
    СтоимостьМатериала = ВыборкаДетальныеЗаписи.Стоимость / ВыборкаДетальныеЗаписи.Количество;
    // ... дальнейшая логика
КонецЦикла;

Скрытые логические ошибки в тексте запроса

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

Ошибка в соединении по стоимости:
У автора: ПО НоменклатураДокумента.Номенклатура = СтоимостьМатериаловОстатки.СтоимостьОстаток.
Это в корне неверно. Мы должны сопоставлять ссылку на номенклатуру с измерением регистра, а не со значением остатка суммы.
Как правильно: ПО НоменклатураДокумента.Номенклатура = СтоимостьМатериаловОстатки.Материал.

Ошибка в соединении по количеству:
У автора: ПО НоменклатураДокумента.Номенклатура = ОстаткиМатериаловОстатки.КоличествоОстаток.
Ситуация аналогичная — нельзя приравнивать объект (Номенклатуру) к числу (Количеству).
Как правильно: ПО НоменклатураДокумента.Номенклатура = ОстаткиМатериаловОстатки.Материал.

Оптимизация кода: переход к пакетным запросам

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

Преимущества пакетного запроса:

Рассмотрим, как мог бы выглядеть упрощенный пакетный запрос для данной задачи:


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

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

РезультатПакета = Запрос.ВыполнитьПакет();
// Результат последнего запроса в пакете будет в последнем элементе массива
Выборка = РезультатПакета[РезультатПакета.Количество() - 1].Выбрать();

Важные рекомендации по методике проведения

В коде из упражнения используется так называемая «старая методика проведения». Мы сначала принудительно записываем пустые наборы записей (Движения.СтоимостьМатериалов.Записать();), чтобы текущие движения документа не влияли на остатки при запросе.

Выясним несколько важных моментов, которые помогут в будущем:

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

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

← На главную