Разработка и Конфигурация · 12 мин
Как проверить, что элемент справочника входит в группу, в 1С
Как проверить вхождение элемента справочника в группу в 1С: метод ПринадлежитЭлементу, сравнение с Родителем, запросы В ИЕРАРХИИ и типичные ошибки разработчиков.
Иерархические справочники — один из базовых механизмов 1С: товары группируются по видам, контрагенты — по категориям, номенклатура — по папкам. Практически в каждой доработке возникает задача: определить, находится ли конкретный элемент внутри заданной группы, причём с учётом вложенных подгрупп любой глубины.
Ошибка в такой проверке приводит к реальным последствиям: скидка применяется не к тем товарам, отчёт собирается по лишней номенклатуре, обмен отбирает не те данные. При этом у задачи есть несколько решений — от простого сравнения родителя до запроса с оператором В ИЕРАРХИИ, и выбор зависит от контекста: где выполняется код, насколько глубока иерархия и насколько критична производительность.
Ниже разобраны рабочие способы проверки на встроенном языке 1С и в запросах, их ограничения, а также типичные ошибки, из-за которых проверка «вроде работает», но на реальных данных даёт неверный результат.
Как устроена иерархия справочников и почему это важно
Справочник в 1С может быть иерархическим двух видов: иерархия групп и элементов (группа не может быть «обычным» элементом) либо иерархия элементов (любой элемент может иметь подчинённые). В обоих случаях связь реализована через стандартный реквизит Родитель — ссылку на элемент-родителя.
Ключевой момент: Родитель хранит только ближайшую группу. Если товар «Молоко 3,2%» лежит в группе «Молочные продукты», которая вложена в «Продукты питания», то Родитель вернёт только «Молочные продукты». Проверка Элемент.Родитель = ГруппаПродукты даст Ложь, хотя логически товар в эту группу входит.
Поэтому первый вопрос при проектировании проверки — нужна ли проверка с учётом вложенности. Если группа гарантированно не имеет подгрупп, достаточно сравнения родителя. Если вложенность возможна (а в реальных базах она почти всегда возможна), нужен обход иерархии или запрос.
Способ 1. Сравнение с реквизитом Родитель
Самый простой вариант — прямое сравнение родителя элемента с нужной группой:
Если ЭлементСправочника.Родитель = ГруппаПроверки Тогда
// элемент лежит непосредственно в группе
КонецЕсли;
Способ работает быстро и не требует запросов, но проверяет только первый уровень вложенности. Он уместен, когда структура справочника жёстко зафиксирована и подгруппы исключены — например, справочник из двух десятков элементов, разложенных по плоским папкам.
Частая ошибка — использовать этот способ «на вырост». Пока группа одна, проверка работает; как только пользователь создаёт подгруппу и переносит туда элементы, логика молча перестаёт срабатывать. Такие дефекты всплывают через месяцы и диагностируются тяжело.
Способ 2. Запрос с оператором В ИЕРАРХИИ
Основной и рекомендуемый способ — запрос с оператором В ИЕРАРХИИ. Он учитывает все уровни вложенности и выполняется на стороне СУБД, что критично для больших справочников:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Ссылка В ИЕРАРХИИ(&Группа)
| И Номенклатура.Ссылка = &Элемент";
Запрос.УстановитьПараметр("Группа", ГруппаПроверки);
Запрос.УстановитьПараметр("Элемент", ЭлементСсылка);
ВходитВГруппу = Не Запрос.Выполнить().Пустой();
Оператор В ИЕРАРХИИ возвращает Истина, если элемент находится в указанной группе на любом уровне вложенности, включая случай, когда элемент совпадает с самой группой (если типы позволяют). Это стандартное поведение, о котором иногда забывают: если передать в параметр группу, а в проверке — ту же группу, результат будет Истина.
Запрос особенно эффективен, когда проверить нужно не один элемент, а выбрать все элементы группы — тогда условие В ИЕРАРХИИ применяется без дополнительных циклов и обходов.
// Быстрая проверка одного элемента
Функция ЭлементВходитВГруппу(Элемент, Группа)
Запрос = Новый Запрос(
"ВЫБРАТЬ ПЕРВЫЕ 1 Ссылка
|ИЗ Справочник.Номенклатура
|ГДЕ Ссылка = &Элемент И Ссылка В ИЕРАРХИИ(&Группа)");
Запрос.УстановитьПараметр("Элемент", Элемент);
Запрос.УстановитьПараметр("Группа", Группа);
Возврат Не Запрос.Выполнить().Пустой();
КонецФункцииСпособ 3. Обход иерархии в коде
Если запрос по какой-то причине недоступен или нежелателен, иерархию можно обойти в коде, поднимаясь от элемента вверх по родителям:
Функция ВходитВГруппу(Элемент, Группа)
ТекущийРодитель = Элемент.Родитель;
Пока ЗначениеЗаполнено(ТекущийРодитель) Цикл
Если ТекущийРодитель = Группа Тогда
Возврат Истина;
КонецЕсли;
ТекущийРодитель = ТекущийРодитель.Родитель;
КонецЦикла;
Возврат Ложь;
КонецФункции;
Способ понятен и не требует построения запроса, но у него есть цена: каждое обращение к .Родитель по ссылке — это обращение к базе (в клиент-серверном варианте — к серверу). На глубокой иерархии и массовых проверках в цикле это заметно медленнее одного запроса.
Альтернатива — метод ВыборкаПоИерархии менеджера справочника: Справочники.Номенклатура.ВыбратьИерархически(ГруппаПроверки) вернёт выборку всех элементов группы с подгруппами. Он удобен, когда нужно перебрать содержимое группы, но для проверки одного элемента избыточен.
Сравнение способов проверки
Быстро и просто, но видит только первый уровень. Подходит только для гарантированно плоской структуры.
Учитывает всю вложенность, быстро работает на СУБД, подходит для массовых выборок. Требует написания запроса.
Гибко и наглядно, но порождает обращение к базе на каждый уровень. Не подходит для массовых проверок.
Удобен для перебора содержимого группы, избыточен для проверки одного элемента.
Выбор способа под задачу
Практическое правило простое: если проверяется один элемент и глубина иерархии мала и фиксирована — допустим обход в коде или сравнение родителя. Если проверка выполняется в цикле по сотням элементов, в отчёте или в обмене данными — только запрос с В ИЕРАРХИИ.
Отдельный случай — отбор в динамических списках и СКД. Там проверку вхождения в группу правильнее задавать через отбор с видом сравнения «В иерархии» по полю Ссылка, а не программной проверкой каждой строки. Это отдаёт работу СУБД и не ломает постраничную выборку списка.
Как выбрать способ
Типичные ошибки и подводные камни
Первая ошибка — поиск несуществующего метода. Во многих языках и платформах есть метод вида «принадлежит группе», и разработчики ищут у ссылки справочника метод ПринадлежитЭлементу. У справочников его нет: такой метод существует у планов счетов (ПланСчетовСсылка.ПринадлежитЭлементу) и у планов видов характеристик в отдельных механизмах. Для справочников — запрос или обход.
Вторая ошибка — сравнение ссылок разных типов или с незаполненной группой. Если параметр Группа пустой, оператор В ИЕРАРХИИ поведёт себя не так, как ожидает логика: пустая ссылка — корень иерархии, и проверка может вернуть неожиданные результаты. Всегда проверяйте ЗначениеЗаполнено(Группа) перед запросом.
Третья ошибка — игнорирование пометки удаления и предопределённости. Запрос с В ИЕРАРХИИ вернёт и помеченные на удаление элементы, если не добавить условие НЕ ПометкаУдаления. В отборах для бизнес-логики это часто критично.
Четвёртая ошибка — проверка по наименованию группы вместо ссылки. Наименования не уникальны: две группы «Прочее» в разных ветках сломают логику. Проверка всегда должна идти по ссылке.
Проверка результата и тестирование
После внедрения проверки её стоит протестировать на минимальном наборе случаев: элемент в самой группе, элемент во вложенной подгруппе второго-третьего уровня, элемент вне группы, элемент в соседней группе с похожим наименованием, пустая группа-параметр. Только прохождение всех пяти случаев подтверждает корректность.
Для массовых проверок дополнительно замерьте время выполнения на реальном объёме данных. Если запрос с В ИЕРАРХИИ выполняется внутри цикла по строкам документа, вынесите его наружу: один запрос с условием по списку ссылок всегда быстрее сотни отдельных вызовов.
Как проверить результат
Проверка возвращает Истина
Возвращает Ложь — ошибка в параметрах запроса
Возвращает Истина
Ложь — используется сравнение только с ближайшим Родителем
Возвращает Ложь
Истина — вероятно, сравнение по наименованию или пустая группа
Проверка отклоняется до запроса
Запрос выполняется с пустой ссылкой и даёт неожиданный результат
Исключён условием НЕ ПометкаУдаления
Попадает в выборку — условие забыто
Ограничения и особые случаи
Оператор В ИЕРАРХИИ работает только с иерархическими справочниками. Если справочник не имеет иерархии, запрос с этим оператором вызовет ошибку — проверьте свойство «Вид иерархии» в конфигураторе перед написанием кода.
В справочниках с иерархией элементов (без деления на группы и элементы) оператор работает так же, но помните: «группой» там может быть любой элемент, и проверка вхождения элемента «сам в себя» вернёт Истина.
На очень больших справочниках (сотни тысяч элементов, глубокая иерархия) производительность В ИЕРАРХИИ зависит от СУБД и индексов. Если запрос стал узким местом, рассмотрите кэширование результата проверки в соответствии или использование служебного регистра сведений с предвычисленной принадлежностью к группам верхнего уровня.
Что важно запомнить
Для проверки вхождения элемента справочника в группу с учётом вложенных подгрупп используйте запрос с оператором В ИЕРАРХИИ. Сравнение с Родителем проверяет только первый уровень. Метода ПринадлежитЭлементу у справочников нет. Всегда проверяйте заполненность группы и исключайте помеченные на удаление элементы.
Частые вопросы
Есть ли в 1С готовый метод «ПринадлежитЭлементу» для справочников?
Нет. Метод ПринадлежитЭлементу существует у планов счетов и в отдельных механизмах планов видов характеристик, но не у справочников. Для справочников вхождение в группу проверяется запросом с оператором В ИЕРАРХИИ или обходом родителей в коде.
Почему сравнение Элемент.Родитель = Группа иногда не срабатывает?
Потому что Родитель хранит только ближайшую группу. Если элемент лежит во вложенной подгруппе, его родитель — подгруппа, а не проверяемая группа верхнего уровня. Для учёта всех уровней нужен запрос В ИЕРАРХИИ или цикл по родителям.
Как проверить вхождение в группу в динамическом списке?
Через отбор: добавьте элемент отбора по полю Ссылка с видом сравнения «В иерархии» и значением — нужной группой. Это выполняется на уровне запроса списка и не замедляет его работу, в отличие от программной проверки каждой строки.
Что вернёт В ИЕРАРХИИ, если группа не заполнена?
Поведение зависит от контекста, но пустая ссылка трактуется как корень иерархии, и результат может не совпасть с ожиданиями логики. Надёжное решение — перед выполнением запроса проверить ЗначениеЗаполнено(Группа) и при пустом значении сразу возвращать Ложь или обрабатывать случай отдельно.
Как проверить сразу много элементов на вхождение в группу?
Одним запросом: выберите из справочника ссылки, удовлетворяющие условию Ссылка В ИЕРАРХИИ(&Группа) И Ссылка В (&СписокЭлементов), передав список массивом в параметр. Это в разы быстрее, чем вызывать проверку для каждого элемента в цикле.
Работает ли В ИЕРАРХИИ, если у справочника иерархия элементов, а не групп?
Да, оператор работает с обоими видами иерархии. Отличие лишь в том, что в иерархии элементов роль «группы» может выполнять любой элемент, и проверка элемента на вхождение «самого в себя» вернёт Истина — учитывайте это в логике.