Как исправить ошибку "Поле объекта не обнаружено (Номенклатура)" при заполнении табличной части документа в 1С?

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

В процессе разработки или доработки функционала в 1С, особенно при работе с данными через запросы, мы часто сталкиваемся с ошибками типа "Поле объекта не обнаружено". Эта ошибка указывает на то, что программа пытается обратиться к полю объекта (в нашем случае, к полю Номенклатура), которое отсутствует в текущем контексте или в результате запроса. Давайте разберем подробнее ситуацию, с которой столкнулся наш коллега, и выясним, как ее эффективно решить.

Анализ исходной проблемы и кода

Наш коллега столкнулся с проблемой при попытке создать новый документ "РасходнаяНакладная" и заполнить его табличную часть "Запасы" данными из существующего документа. Основная логика заключалась в копировании полей из исходного документа в новый через запрос и последующее программное заполнение табличной части. Вот фрагмент кода, который вызвал ошибку:


НовыйДок = Документы.РасходнаяНакладная.СоздатьДокумент;
НовыйДок.Дата = Док.Дата;
НовыйДок.ВидОперации = Док.ВидОперации;
НовыйДок.Контрагент = Док.Контрагент;
НовыйДок.Договор = Док.Договор;
НовыйДок.Номер = "Н"+Док.Номер;

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

НовыйДок.Записать;
Показать

Ошибка "Поле объекта не обнаружено (Номенклатура)" возникла на строке, где происходит попытка присвоить значение полю СтрокаТЧ.Номенклатура. Изначально, автор предполагал, что проблема может быть в том, что Выборка.Номенклатура содержит наименование в виде строки, а не ссылку на элемент справочника. Поэтому он попытался использовать метод Справочники.Номенклатура.НайтиПоНаименованию() для получения ссылки. Однако, это не решило проблему, а лишь подтвердило, что поле Номенклатура вообще не существует в объекте Выборка на момент выполнения этого кода.

Важность отладки в 1С

Когда мы сталкиваемся с подобными ошибками, первым и наиболее эффективным шагом всегда должна быть отладка. Отладчик 1С позволяет нам пошагово отслеживать выполнение кода и проверять значения всех переменных в любой точке программы. Давайте рассмотрим, как мы могли бы использовать отладку в данной ситуации:

  1. Установите точку останова: Поставьте точку останова на строке СтрокаТЧ.Номенклатура = Справочники.Номенклатура.НайтиПоНаименованию(Выборка.Номенклатура);.
  2. Запустите отладку: Запустите выполнение кода в режиме отладки.
  3. Проверьте содержимое переменной Выборка: Как только выполнение остановится на точке останова, откройте окно "Табло" или наведите курсор на переменную Выборка. Мы увидим, какие поля фактически доступны в результате запроса.
  4. Проверьте тип и содержимое Выборка.Номенклатура: Если поле Номенклатура отсутствует в списке полей Выборки, это сразу укажет нам на причину ошибки. Если оно присутствует, но имеет неожиданный тип (например, Неопределено или строка вместо ссылки), это также будет полезной информацией.
  5. Проверьте объект СтрокаТЧ: Параллельно мы можем убедиться, что поле Номенклатура существует в табличной части СтрокаТЧ и имеет ожидаемый тип данных (СправочникСсылка.Номенклатура).

В нашем случае отладка быстро бы показала, что в объекте Выборка просто нет поля с именем Номенклатура, что сразу нацелило бы нас на анализ запроса.

Детальный анализ запроса — корень проблемы

После проверки кода и подтверждения ошибки, мы попросили автора предоставить текст запроса, который формирует переменную Выборка. Автор предоставил следующий запрос:


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

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

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

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

Также обратим внимание на условие ГДЕ ... И ВТЗапасы.Ссылка = &Док во второй части запроса. Если мы хотим получить данные только для конкретного документа, логичнее фильтровать по &Док уже при формировании временной таблицы ВТЗапасы.

Исправление запроса для получения всех необходимых данных

Для того чтобы решить проблему, нам необходимо модифицировать предложение ВЫБРАТЬ во втором запросе так, чтобы оно включало все поля из временной таблицы ВТЗапасы, которые мы собираемся использовать для заполнения документа, а также поле Остаток. Также мы учтем рекомендацию по оптимизации условия соединения для ссылочных полей, которая была дана в ходе обсуждения.


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

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

  1. Фильтрация временной таблицы: Мы переместили условие ГДЕ РасходнаяНакладнаяЗапасы.Ссылка = &Док в первую часть запроса, при формировании временной таблицы ВТИсходныеЗапасы. Это гарантирует, что мы работаем только с данными конкретного исходного документа с самого начала.
  2. Явное перечисление полей в финальном ВЫБРАТЬ: Во второй (основной) части запроса мы теперь явно перечисляем все необходимые поля: ВТИсходныеЗапасы.Номенклатура, ВТИсходныеЗапасы.ЕдиницаИзмерения, ВТИсходныеЗапасы.Количество, ВТИсходныеЗапасы.СтавкаНДС, ВТИсходныеЗапасы.Цена, а также вычисляемое поле Остаток. Теперь объект Выборка будет содержать все эти поля, и мы сможем к ним корректно обращаться.
  3. Оптимизированное условие соединения: Мы изменили условие соединения с ПО ЗапасыНаСкладахОстатки.Номенклатура.Ссылка = ВТЗапасы.Номенклатура.Ссылка на ПО ВТИсходныеЗапасы.Номенклатура = ЗапасыНаСкладахОстатки.Номенклатура. При соединении по полям ссылочного типа, как Номенклатура, обычно достаточно прямого сравнения самих ссылок, без явного обращения к свойству .Ссылка, что может быть немного более эффективным и лаконичным.

После внесения этих изменений в запрос, переменная Выборка, полученная в результате выполнения запроса, будет содержать все необходимые поля. Важно, что поле Выборка.Номенклатура будет уже являться ссылкой на элемент справочника Номенклатура (типа СправочникСсылка.Номенклатура), а не строкой.

Корректное заполнение табличной части

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


Пока Выборка.Следующий Цикл
    СтрокаТЧ = НовыйДок.Запасы.Добавить;
    СтрокаТЧ.Номенклатура = Выборка.Номенклатура; // Прямое присвоение ссылки
    СтрокаТч.ЕдиницаИзмерения = Выборка.ЕдиницаИзмерения;
    СтрокаТЧ.Количество = Выборка.Количество;
    СтрокаТЧ.СтавкаНДС = Выборка.СтавкаНДС;
    СтрокаТЧ.Цена = Выборка.Цена;
КонецЦикла;

Это прямое присвоение ссылки является наиболее правильным и эффективным способом заполнения ссылочных полей в табличных частях. Метод НайтиПоНаименованию() был бы необходим только в том случае, если бы в Выборке содержалось исключительно строковое наименование номенклатуры, и нам потребовалось бы найти соответствующий элемент справочника. Однако, наш исправленный запрос уже возвращает ссылку, что упрощает и ускоряет процесс.

Как мы видим из последнего сообщения автора, проблема была успешно решена после внесенных изменений. Это подтверждает, что основной причиной ошибки было отсутствие поля Номенклатура в финальном результате запроса.

Общие рекомендации по работе с запросами и отладке в 1С

Чтобы избежать подобных проблем в будущем и повысить эффективность нашей работы, давайте обобщим несколько важных рекомендаций:

  1. Всегда используйте конструктор запросов: Конструктор запросов (доступный в конфигураторе 1С) является мощным инструментом. Он позволяет визуально строить запросы, выбирать поля, устанавливать связи и условия, а также сразу проверять результат выполнения. Это помогает избежать синтаксических ошибок и логических неточностей, подобных той, что мы рассмотрели. Даже если вы пишете запрос руками, прогоните его через конструктор для проверки.
  2. Проверяйте типы данных: Если вы сомневаетесь в типе данных переменной или поля в выборке, используйте функцию ТИПЗНАЧЕНИЯ() в отладчике. Например, ТИПЗНАЧЕНИЯ(Выборка.Номенклатура) поможет вам точно определить, что содержит переменная.
  3. Внимательно относитесь к ПОМЕСТИТЬ и итоговым ВЫБРАТЬ: При использовании временных таблиц (оператор ПОМЕСТИТЬ) всегда убедитесь, что ваш финальный оператор ВЫБРАТЬ включает все необходимые поля из временных таблиц или присоединенных источников данных. Очень часто ошибка кроется именно в том, что в конечном итоге выбираются не все нужные столбцы.
  4. Очистка кэша: Хотя в данном случае проблема была в коде, иногда "странные" ошибки могут быть связаны с устаревшим кэшем 1С. При возникновении необъяснимых проблем, очистка кэша (через меню запуска 1С:Предприятия или вручную удаляя папки кэша) может помочь.
  5. Сравнение ссылок: При соединении таблиц по ссылочным полям (например, по Номенклатуре или Контрагенту), как правило, достаточно прямого сравнения полей (Источник.Поле = Цель.Поле). Явное использование .Ссылка (Источник.Поле.Ссылка = Цель.Поле.Ссылка) обычно не требуется и иногда может быть менее производительным.

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

← На главную