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

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

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

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

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


ИскомыеСтроки = Объект.Комплектующие.НайтиСтроки(мОтбор);
Если ИскомыеСтроки.Количество() = 1 Тогда
    ИскомыеСтроки.Цена = СтрокаЗапроса.Цена; // ОШИБКА ЗДЕСЬ
КонецЕсли;

Давайте выясним причину ошибки. Метод НайтиСтроки() предназначен для поиска всех строк, соответствующих заданному отбору (структуре). Даже если в вашей таблице всего одна подходящая строка, этот метод всегда возвращает Массив. У объекта типа Массив в 1С нет свойства Цена — оно есть только у элементов этого массива (самих строк табличной части). Именно поэтому система выдает исключение, сообщая, что поле не обнаружено.

Решение №1: Обращение к элементу массива по индексу

Если мы точно уверены, что по нашему условию может быть найдена только одна строка (или нам нужна только первая найденная), мы можем обратиться к ней по индексу [0]. Разберем исправленный вариант кода:


ИскомыеСтроки = Объект.Комплектующие.НайтиСтроки(мОтбор);
Если ИскомыеСтроки.Количество() > 0 Тогда
    // Обращаемся к ПЕРВОМУ элементу массива найденных строк
    ИскомыеСтроки[0].Цена = СтрокаЗапроса.Цена;
КонецЕсли;

Этот способ прост и эффективен, когда уникальность данных в табличной части гарантирована программно. Однако стоит помнить, что использование ИскомыеСтроки[0] без предварительной проверки Количество() > 0 может привести к другой критической ошибке: «Индекс находится за границами массива».

Решение №2: Обход всех найденных строк (Безопасный метод)

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


ИскомыеСтроки = Объект.Комплектующие.НайтиСтроки(мОтбор);
Для каждого СтрокаТЧ Из ИскомыеСтроки Цикл
    СтрокаТЧ.Цена = СтрокаЗапроса.Цена;
КонецЦикла;

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

  1. Код не вызовет ошибку, если массив пуст (цикл просто не выполнится).
  2. Если найдено несколько строк, цена обновится в каждой из них.
  3. Код остается читаемым и легко поддается отладке.

Альтернативный метод: Использование метода Найти()

Если поиск производится только по одному полю (например, по Номенклатуре) и нам достаточно найти только первое вхождение, мы рекомендуем использовать метод Найти() вместо НайтиСтроки(). Рассмотрим пример:


НайденнаяСтрока = Объект.Комплектующие.Найти(СтрокаЗапроса.Номенклатура, "Номенклатура");
Если НайденнаяСтрока <> Неопределено Тогда
    НайденнаяСтрока.Цена = СтрокаЗапроса.Цена;
КонецЕсли;

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

Оптимизация производительности: Использование Соответствия (Map)

Когда мы выполняем поиск внутри цикла по результату запроса (как в исходном примере), мы создаем алгоритм со сложностью $O(N \times M)$. Если в табличной части 1000 строк и в результате запроса 1000 строк, системе придется выполнить до 1 000 000 операций сравнения. Это может привести к заметным задержкам.

Проанализируем более профессиональный подход с использованием объекта Соответствие. Мы сначала соберем все цены в «словарь», а затем один раз пройдем по табличной части:


// 1. Выгружаем цены в Соответствие для быстрого поиска
ЦеныСоответствие = Новый Соответствие;
РезультатЗапроса = Запрос.Выполнить().Выбрать();

Пока РезультатЗапроса.Следующий() Цикл
    ЦеныСоответствие.Вставить(РезультатЗапроса.Номенклатура, РезультатЗапроса.Цена);
КонецЦикла;

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

Такой метод работает в десятки раз быстрее на больших объемах данных, так как поиск в Соответствии происходит практически мгновенно.

Важное замечание по передаче параметров в запрос

Посмотрим на строку кода автора: Запрос.УстановитьПараметр("СпКомплектующие", Объект.Комплектующие.Выгрузить(,"Номенклатура"));. Метод Выгрузить() возвращает ТаблицуЗначений. Хотя 1С позволяет использовать таблицу из одной колонки в условии запроса В (&Параметр), это считается менее «чистым» подходом.

Мы рекомендуем использовать метод ВыгрузитьКолонку(), который возвращает Массив:


СписокНоменклатуры = Объект.Комплектующие.ВыгрузитьКолонку("Номенклатура");
Запрос.УстановитьПараметр("СпКомплектующие", СписокНоменклатуры);

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

Заключение и выводы

Подводя итог, повторим основные правила работы с поиском в коллекциях 1С:

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

← На главную