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