Как проверить вхождение товара в иерархию групп из табличной части справочника

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

В процессе разработки в среде 1С:Предприятие 8 часто возникает задача сопоставления данных. Например, когда у нас есть специальный справочник с настройками, содержащий список "исключений" в виде групп номенклатуры, и нам нужно быстро проверить, относится ли конкретный товар к одной из этих групп или их дочерним элементам. Разберем, как реализовать такую проверку эффективно, избегая избыточных циклов и перегрузки системы.

Постановка задачи

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

Метод 1: Использование метода НайтиСтроки (для простого поиска)

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

Метод НайтиСтроки работает с объектом или ссылкой на таблицу и принимает в качестве аргумента Структура, где ключи — это имена колонок, а значения — искомые данные. Проанализируем пример кода:


// Предположим, у нас есть ссылка на справочник настроек
ПараметрыОтбора = Новый Структура;
ПараметрыОтбора.Вставить("ГруппаИсключения", ТекТовар);

// Обращаемся к табличной части через ссылку
НайденныеСтроки = УзелОбмена.ГруппыИсключения.НайтиСтроки(ПараметрыОтбора);

Если НайденныеСтроки.Количество() > 0 Тогда
    // Товар найден в списке напрямую
КонецЕсли;

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

Метод 2: Проверка иерархии через запрос (оптимальный вариант)

Для проверки вхождения в группу со всеми вложенными элементами в 1С используется оператор запроса В ИЕРАРХИИ. Чтобы не писать запрос в цикле (что является грубой ошибкой производительности), мы подготовим данные заранее.

Разберем алгоритм по шагам:

  1. Выгрузим все группы из табличной части в массив.
  2. Передадим этот массив в параметр запроса.
  3. Выполним запрос к таблице номенклатуры с фильтром по конкретному товару и условием вхождения в иерархию массива групп.

Посмотрим на реализацию функции:


Функция ОтвечаетТребованиям(УзелОбмена, Товар)
    Отвечает = Истина;
    
    // 1. Проверяем, есть ли вообще группы в исключениях
    Если УзелОбмена.ГруппыИсключения.Количество() > 0 Тогда
        
        // 2. Выгружаем колонку ссылок в обычный массив
        МассивГруппИсключений = УзелОбмена.ГруппыИсключения.ВыгрузитьКолонку("ГруппаИсключения");
        
        // 3. Создаем запрос
        Запрос = Новый Запрос;
        Запрос.Текст = 
        "ВЫБРАТЬ
        |    Номенклатура.Ссылка КАК Ссылка
        |ИЗ
        |    Справочник.Номенклатура КАК Номенклатура
        |ГДЕ
        |    Номенклатура.Ссылка = &ВыбТовар
        |    И Номенклатура.Ссылка В ИЕРАРХИИ(&МассивГрупп)";
        
        Запрос.УстановитьПараметр("ВыбТовар", Товар);
        Запрос.УстановитьПараметр("МассивГрупп", МассивГруппИсключений);
        
        РезультатЗапроса = Запрос.Выполнить();
        
        // 4. Если запрос не пустой, значит товар входит в иерархию одной из групп
        Если НЕ РезультатЗапроса.Пустой() Тогда
            Отвечает = Ложь;
        КонецЕсли;
        
    КонецЕсли;
    
    Возврат Отвечает;
КонецФункции

Расширение логики: дополнительные проверки

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

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


Запрос = Новый Запрос;
Запрос.Текст = 
"ВЫБРАТЬ
|    Номенклатура.Ссылка
|ИЗ
|    Справочник.Номенклатура КАК Номенклатура
|ГДЕ
|    Номенклатура.Ссылка = &Товар
|    И (Номенклатура.Ссылка В ИЕРАРХИИ(&Группы)
|        ИЛИ Номенклатура.Родитель = ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)
|        ИЛИ Номенклатура.ФайлКартинки = ЗНАЧЕНИЕ(Справочник.НоменклатураПрисоединенныеФайлы.ПустаяСсылка)
|        ИЛИ Номенклатура.ФайлКартинки.ПометкаУдаления = ИСТИНА)";

Запрос.УстановитьПараметр("Товар", ПроверяемыйТовар);
Запрос.УстановитьПараметр("Группы", МассивГрупп);

Результат = Запрос.Выполнить();
Если НЕ Результат.Пустой() Тогда
    // Товар не прошел проверку (либо в исключениях, либо не заполнены реквизиты)
КонецЕсли;

В данном примере мы использовали конструкцию ЗНАЧЕНИЕ(...). Это позволяет нам обращаться к пустым ссылкам типов прямо в тексте запроса, не передавая их через параметры &, что делает код чище и быстрее.

Профессиональные советы по оптимизации

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

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

Использование запросов — это стандарт "хорошего тона" в разработке на платформе 8.3. Даже если поначалу кажется проще использовать циклы, освоение конструктора запросов и консоли запросов окупится скоростью работы ваших конфигураций и отсутствием блокировок базы данных. Для интерактивной проверки таких условий подойдёт консоль запросов с возможностью выбора групп в параметрах.

← На главную