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