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