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