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