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