При разработке универсальных механизмов и сложных отчетов в 1С часто возникает потребность получить доступ к свойствам конфигурации непосредственно при выборке данных. Разберем типичную ситуацию: можно ли в тексте запроса обратиться к свойствам объектов, например, прочитать Метаданные.СвойстваОбъектов.ПериодичностьНомераДокумента? Проанализируем эту задачу и выясним, как правильно реализовать подобную логику.
Сразу ответим на главный вопрос: напрямую обратиться к дереву метаданных в тексте запроса невозможно ни в одной из версий платформы 1С:Предприятие. Выясним причину этого ограничения на уровне архитектуры.
Язык запросов 1С транслируется платформой в SQL-запросы, которые выполняются на стороне системы управления базами данных (СУБД). СУБД оперирует исключительно таблицами, физически хранящимися в базе данных (справочниками, документами, регистрами). Дерево метаданных (и такие свойства, как периодичность номера, длина кода, режим записи) является частью конфигурации серверного приложения 1С и не существует в виде таблиц с данными на уровне SQL. Поэтому попытка написать в запросе конструкцию вида Метаданные.Документы.РеализацияТоваровУслуг.ПериодичностьНомера приведет к синтаксической ошибке.
Тем не менее, если мы пишем универсальный запрос (например, обработку для проверки нумерации всех документов в базе), нам необходимо как-то передать эти свойства внутрь. Рассмотрим подробнее рабочие альтернативы.
Многие разработчики, столкнувшись с невозможностью получить метаданные в запросе, прибегают к динамической сборке текста запроса из кусков (конкатенации). Однако более технологичным, читаемым и аккуратным решением является использование объекта ТаблицаЗначений и временных таблиц в запросе.
Разберем по шагам, как это реализовать:
Метаданные.Документы).ТИПЗНАЧЕНИЯ().Посмотрим на пример кода:
// 1. Подготовка таблицы значений с данными метаданных
ТаблицаМетаданных = Новый ТаблицаЗначений;
ТаблицаМетаданных.Колонки.Добавить("ТипДокумента");
ТаблицаМетаданных.Колонки.Добавить("Периодичность");
// Обходим метаданные и заполняем свойства
Для Каждого МетаданныеДокумента Из Метаданные.Документы Цикл
НоваяСтрока = ТаблицаМетаданных.Добавить();
// Для сопоставления типов в запросе удобнее использовать пустую ссылку
НоваяСтрока.ТипДокумента = Документы[МетаданныеДокумента.Имя].ПустаяСсылка();
НоваяСтрока.Периодичность = МетаданныеДокумента.ПериодичностьНомера;
КонецЦикла;
// 2. Передача данных в запрос и использование Временной Таблицы
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТаблицаМД.ТипДокумента КАК ТипДокумента,
| ТаблицаМД.Периодичность КАК Периодичность
|ПОМЕСТИТЬ ВТ_СвойстваМетаданных
|ИЗ
| &ТаблицаМетаданных КАК ТаблицаМД
|;
|
|ВЫБРАТЬ
| Журнал.Ссылка КАК ДокументСсылка,
| ВТ_СвойстваМетаданных.Периодичность КАК ПериодичностьНомера
|ИЗ
| ЖурналДокументов.ВсеДокументы КАК Журнал
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_СвойстваМетаданных КАК ВТ_СвойстваМетаданных
| ПО (ВТ_СвойстваМетаданных.ТипДокумента = ТИПЗНАЧЕНИЯ(Журнал.Ссылка))";
Запрос.УстановитьПараметр("ТаблицаМетаданных", ТаблицаМетаданных);
РезультатЗапроса = Запрос.Выполнить();
Такой подход позволяет сохранить текст запроса неизменным и избежать громоздких операторов выбора ВЫБОР КОГДА ... ТОГДА ... ИНАЧЕ ... КОНЕЦ.
Если ваш запрос не является абсолютно универсальным для всех документов конфигурации, а работает лишь с несколькими конкретными типами, вы можете передавать значения свойств метаданных точечно. Системные перечисления метаданных, такие как ПериодичностьНомераДокумента.Год, ПериодичностьНомераДокумента.Месяц, ПериодичностьНомераДокумента.Непериодический, являются валидными значениями для платформы.
Вы можете передать их в параметры и использовать прямо в условиях запроса:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Док.Ссылка КАК Документ
|ИЗ
| Документ.ПоступлениеТоваровУслуг КАК Док
|ГДЕ
| &ПериодичностьПоступления = &ПериодичностьГод";
Запрос.УстановитьПараметр("ПериодичностьПоступления", Метаданные.Документы.ПоступлениеТоваровУслуг.ПериодичностьНомера);
Запрос.УстановитьПараметр("ПериодичностьГод", ПериодичностьНомераДокумента.Год);
Если по каким-то причинам использовать временные таблицы нецелесообразно (например, если логика запроса кардинально меняется в зависимости от свойств объекта), остается метод конкатенации строк. Мы формируем текст запроса программно перед его выполнением, вставляя нужные нам условия.
ТекстЗапроса = "ВЫБРАТЬ Ссылка ИЗ Документ.АвансовыйОтчет ГДЕ ";
Если Метаданные.Документы.АвансовыйОтчет.ПериодичностьНомера = ПериодичностьНомераДокумента.Год Тогда
ТекстЗапроса = ТекстЗапроса + "ДАТАВРЕМЯ(ГОД, Дата) = &ТекущийГод";
Иначе
ТекстЗапроса = ТекстЗапроса + "ИСТИНА";
КонецЕсли;
Этот метод работает быстро, но сильно снижает читаемость кода, если запрос большой и ветвлений много.
Если вы собираете данные о документах или справочниках до выполнения запроса в циклах (как в первом решении), обращайте внимание на производительность получения метаданных. Доступ к метаданным конкретного объекта через методы Метаданные.НайтиПоТипу(ТипЗначения) или через вызов Объект.Метаданные() (если объект уже получен) работает быстрее, чем поиск по имени через глобальную коллекцию Метаданные.Документы[ИмяДокумента]. Обязательно учитывайте этот нюанс при разработке универсальных механизмов массовой обработки данных.