При работе со справочниками в платформе 1С:Предприятие 7.7 начинающие разработчики часто сталкиваются со ситуацией, когда стандартный метод НайтиПоКоду() перестает находить элементы. Это происходит в тех случаях, когда справочник является многоуровневым (иерархическим), а в Конфигураторе для него задано свойство нумерации «Серии кодов: В пределах группы» (или «В пределах подчинения»). В этой статье мы подробно проанализируем, почему возникает эта проблема, разберем способы ее решения с помощью встроенного языка 1С 7.7 и рассмотрим, как правильно обращаться к периодическим реквизитам, чтобы избежать распространенной ошибки «Неизвестный объект».
Разберем механику работы поиска. В 1С 7.7 у справочников может быть настроена различная уникальность кодов:
"00000000001" может быть и в первой группе, и во второй).Когда серии кодов установлены в пределах группы, стандартный вызов СПР.НайтиПоКоду("00000000093") с флагом по умолчанию не может определить, в какой именно группе искать элемент, и возвращает 0 (элемент не найден) или позиционируется на первой попавшейся группе/элементе.
Обратимся к синтаксису метода НайтиПоКоду():
НайтиПоКоду(<Код>, <ФлагПоиска>)
Параметр <ФлагПоиска> принимает следующие значения:
0 — поиск во всем справочнике вне зависимости от родителя;1 — поиск внутри предварительно установленной группы (родителя);2 — поиск по полному (составному) коду через разделитель.Если нам известна иерархия групп, в которых находится нужный элемент, мы можем выполнять поиск последовательно, переходя с уровня на уровень. Для этого используется метод ИспользоватьРодителя().
Рассмотрим пошаговый алгоритм решения задачи:
СоздатьОбъект("Справочник.ИмяСправочника").ИспользоватьРодителя().1.Проанализируем пример кода на языке 1С 7.7:
СПР1 = СоздатьОбъект("Справочник.ЦеныДляСубАбонентов");
СПР1.ИспользоватьДату(ДатаДок);
// Шаг 1: Находим родительскую группу на верхнем уровне
Если СПР1.НайтиПоКоду("00000000053", 0) = 1 Тогда
Группа = СПР1.ТекущийЭлемент();
// Проверяем, что найденный элемент действительно является группой
Если Группа.ЭтоГруппа() = 1 Тогда
// Шаг 2: Устанавливаем родителя для последующего поиска
СПР1.ИспользоватьРодителя(Группа);
// Шаг 3: Ищем элемент внутри этой группы (флаг поиска = 1)
Если СПР1.НайтиПоКоду("00000000093", 1) = 1 Тогда
Тек = СПР1.ТекущийЭлемент();
Сообщить("Найден элемент: " + Тек.Наименование);
Сообщить("Ед. измерения: " + Тек.ЕдИзмер);
КонецЕсли;
КонецЕсли;
КонецЕсли;
Обратите внимание: Для выполнения поиска внутри группы нет необходимости создавать отдельную переменную (например, СПР2). Одной переменной объекта СПР1 вполне достаточно — после вызова СПР1.ИспользоватьРодителя(Группа) область поиска для этого объекта переключается внутрь указанной группы.
Если вам известны коды всех вышестоящих групп, составляющих цепочку иерархии, поиск можно выполнить в одну строчку без использования метода ИспользоватьРодителя(). Для этого применяется флаг 2.
Полный код представляет собой последовательность кодов всех групп от корня справочника до искомого элемента, разделенных символом косой черты /.
Рассмотрим пример использования:
СПР = СоздатьОбъект("Справочник.ЦеныДляСубАбонентов");
СПР.ИспользоватьДату(ДатаДок);
// Составной полный код: "КодГруппы1/КодПодгруппы2/КодЭлемента"
ПолныйКодИскомого = "00000000053/00000000093";
Если СПР.НайтиПоКоду(ПолныйКодИскомого, 2) = 1 Тогда
Сообщить("Элемент успешно найден: " + СПР.Наименование);
Иначе
Сообщить("Элемент с полным кодом " + ПолныйКодИскомого + " не найден.");
КонецЕсли;
Этот способ существенно сокращает объем кода и работает быстрее, так как платформа самостоятельно разбирает цепочку родительских групп.
Часто после успешного позиционирования на элементе разработчики пытаются вывести значение реквизита (например, Сообщить(Тек.Цена)) и получают текст «Неизвестный объект» вместо числового значения. При этом обычные реквизиты (такие как Наименование или ЕдИзмер) выводятся корректно.
Выясним причину этой проблемы. В 1С 7.7 реквизит справочника может быть периодическим (его значение меняется во времени — например, цена, оклад, курсы валют). Обращение к периодическому реквизиту напрямую возвращает не конкретное число, а специальный агрегатный объект платформы Периодический.
Существует два правильных способа считывания значения периодического реквизита:
У периодического реквизита есть встроенный метод Получить(<Дата>), который возвращает значение реквизита на конкретную дату:
Если СПР1.НайтиПоКоду("00000000093", 1) = 1 Тогда
// Получаем цену строго на дату документа
ЦенаНаДату = СПР1.Цена.Получить(ДатаДок);
Сообщить("Цена на " + ДатаДок + ": " + ЦенаНаДату);
КонецЕсли;
Если перед выполнением операций поиска вызвать метод ИспользоватьДату(Дата), платформа автоматически зафиксирует дату для всех периодических реквизитов этого объекта справочника:
СПР1 = СоздатьОбъект("Справочник.ЦеныДляСубАбонентов");
// Устанавливаем дату СРАЗУ после создания объекта
СПР1.ИспользоватьДату(ДатаДок);
Если СПР1.НайтиПоКоду(...) = 1 Тогда
// Теперь прямое обращение вернет числовое значение
Сообщить("Цена: " + СПР1.Цена);
КонецЕсли;
Важное замечание: Вызывать метод ИспользоватьДату() рекомендуется сразу после создания объекта СоздатьОбъект(), до вызовов методов НайтиПоКоду() или НайтиПоНаименованию(). Если вызвать ИспользоватьДату() уже после того, как элемент найден, платформа может не успеть обновить контекст периодических реквизитов.
Если вам известен только конечный код элемента, но неизвестно, в какой именно группе он находится, каскадный поиск через ИспользоватьРодителя() выполнить невозможно. В такой ситуации идеальным решением станет использование объекта Запрос.
Рассмотрим пример написания запроса для поиска элемента по коду во всех уровнях иерархии:
Запрос = СоздатьОбъект("Запрос");
ТекстЗапроса =
"//{{ЗАПРОС(ПоискЭлемента)
|ТекущийЭлемент = Справочник.ЦеныДляСубАбонентов.ТекущийЭлемент;
|Код = Справочник.ЦеныДляСубАбонентов.Код;
|Группировка ТекущийЭлемент без групп;
|Условие(Код = ""00000000093"");
|";
Если Запрос.Выполнить(ТекстЗапроса) = 1 Тогда
Пока Запрос.Группировка() = 1 Цикл
НайденныйЭлемент = Запрос.ТекущийЭлемент;
Сообщить("Найден элемент: " + НайденныйЭлемент.Наименование);
Сообщить("Полный код элемента: " + НайденныйЭлемент.ПолныйКод());
Сообщить("Цена: " + НайденныйЭлемент.Цена.Получить(ДатаДок));
КонецЦикла;
КонецЕсли;
Язык запросов 1С 7.7 обходит весь справочник и находит все элементы с указанным кодом независимо от того, в каких родительских группах они расположены.
Разберем основные правила работы с многоуровневыми справочниками в 1С 7.7:
ИспользоватьРодителя() и флаг 1 для пошагового поиска./ с флагом 2.ИспользоватьДату() или используйте конструкцию Реквизит.Получить(Дата), чтобы избежать ошибки «Неизвестный объект».