Как в СКД программно включить или выключить выбранные поля отчета и итогов?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При программной настройке отчетов на базе системы компоновки данных часто возникает задача динамического управления составом выводимых полей — например, включения или скрытия определенных колонок по значениям параметров. Однако разработчики нередко сталкиваются с ситуацией, когда изменение флага видимости в цикле по коллекции Настройки.Структура отрабатывает только для детальных записей, но не затрагивает поля самого отчета, группировки колонок таблицы или общие вертикальные итоги.

Разберем причины такого поведения платформы 1С и подробно рассмотрим практические способы решения задачи: от итеративного обхода структуры таблицы до универсальной рекурсивной процедуры.

Выясним причину: почему простой цикл не видит все поля

Проанализируем структуру настроек схемы компоновки данных. Коллекция выбранных полей присутствует на нескольких уровнях схемы:

  1. Корневой уровень отчета: свойство Настройки.Выбор.Элементы. Именно оно определяет состав полей по умолчанию для всего отчета, а также для верхнеуровневых итоговых строк. Если обходить только коллекцию Настройки.Структура, корневые выбранные поля вообще не попадут в обработку.
  2. Элементы структуры: группировки, таблицы и диаграммы. В объекте ТаблицаКомпоновкиДанных состав полей разделен на строки и колонки (коллекции Строки и Колонки), каждая из которых может содержать собственную вложенную структуру и свой набор выбранных полей.
  3. Автополя: если в настройках группировки или колонки указан элемент АвтоВыбранноеПолеКомпоновкиДанных, реальный список колонок наследуется с вышестоящего уровня. Поэтому отключение полей на уровне вложенного узла ничего не даст — управлять доступностью необходимо в родительском узле или в корне отчета.
  4. Группы полей: поля могут быть сгруппированы в объекты типа ГруппаВыбранныхПолейКомпоновкиДанных, содержащие собственную вложенную коллекцию Элементы.

Способ 1. Явный обход колонок таблицы компоновки данных

Если структура отчета фиксирована и представляет собой таблицу СКД, достаточно расширить обход, обратившись к коллекции колонок таблицы. Посмотрим на пример кода в процедуре ПриКомпоновкеРезультата модуля отчета:


Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)

	ВключатьПоказательРуб = КомпоновкаДанныхКлиентСервер.ПолучитьПараметр(КомпоновщикНастроек, "ВключатьПоказательРуб").Значение;
	ВключатьПоказательEUR = КомпоновкаДанныхКлиентСервер.ПолучитьПараметр(КомпоновщикНастроек, "ВключатьПоказательEUR").Значение;

	МассивПолейРуб = Новый Массив;
	МассивПолейРуб.Добавить("СуммаПоказателяРуб");
	МассивПолейРуб.Добавить("СуммаПоказателяСреднееРуб");

	МассивПолейEUR = Новый Массив;
	МассивПолейEUR.Добавить("СуммаПоказателяEUR");
	МассивПолейEUR.Добавить("СуммаПоказателяСреднееEUR");

	Настройки = КомпоновщикНастроек.ПолучитьНастройки();

	// 1. Проверяем корневой выбор отчета
	Для Каждого ВыбранноеПоле Из Настройки.Выбор.Элементы Цикл
		Если ТипЗнч(ВыбранноеПоле) = Тип("ВыбранноеПолеКомпоновкиДанных") Тогда
			ИмяПоля = Строка(ВыбранноеПоле.Поле);
			Если МассивПолейРуб.Найти(ИмяПоля) <> Неопределено Тогда
				ВыбранноеПоле.Использование = ВключатьПоказательРуб;
			КонецЕсли;
			Если МассивПолейEUR.Найти(ИмяПоля) <> Неопределено Тогда
				ВыбранноеПоле.Использование = ВключатьПоказательEUR;
			КонецЕсли;
		КонецЕсли;
	КонецЦикла;

	// 2. Обходим структуру и колонки таблицы
	Для Каждого ЭлементСтруктуры Из Настройки.Структура Цикл

		Для Каждого ВыбранноеПоле Из ЭлементСтруктуры.Выбор.Элементы Цикл
			Если ТипЗнч(ВыбранноеПоле) = Тип("ВыбранноеПолеКомпоновкиДанных") Тогда
				ИмяПоля = Строка(ВыбранноеПоле.Поле);
				Если МассивПолейРуб.Найти(ИмяПоля) <> Неопределено Тогда
					ВыбранноеПоле.Использование = ВключатьПоказательРуб;
				КонецЕсли;
				Если МассивПолейEUR.Найти(ИмяПоля) <> Неопределено Тогда
					ВыбранноеПоле.Использование = ВключатьПоказательEUR;
				КонецЕсли;
			КонецЕсли;
		КонецЦикла;

		Если ТипЗнч(ЭлементСтруктуры) = Тип("ТаблицаКомпоновкиДанных") Тогда
			Для Каждого Колонка Из ЭлементСтруктуры.Колонки Цикл
				Для Каждого ВыбранноеПоле Из Колонка.Выбор.Элементы Цикл
					Если ТипЗнч(ВыбранноеПоле) = Тип("ВыбранноеПолеКомпоновкиДанных") Тогда
						ИмяПоля = Строка(ВыбранноеПоле.Поле);
						Если МассивПолейРуб.Найти(ИмяПоля) <> Неопределено Тогда
							ВыбранноеПоле.Использование = ВключатьПоказательРуб;
						КонецЕсли;
						Если МассивПолейEUR.Найти(ИмяПоля) <> Неопределено Тогда
							ВыбранноеПоле.Использование = ВключатьПоказательEUR;
						КонецЕсли;
					КонецЕсли;
				КонецЦикла;
			КонецЦикла;
		КонецЕсли;

	КонецЦикла;

	КомпоновщикНастроек.ЗагрузитьНастройки(Настройки);

КонецПроцедуры

Рассмотренный подход решает задачу для простой таблицы, однако при добавлении вложенных группировок или групп полей ручной обход каждого уровня становится громоздким и ненадежным.

Способ 2. Универсальный рекурсивный обход всех настроек СКД

Разберем более надежный метод: построение универсальной процедуры, которая способна рекурсивно обойти произвольное дерево настроек компоновки данных. Процедура анализирует тип переданного объекта и обращается к соответствующим коллекциям.

Создадим процедуру рекурсивного переключения видимости:


Процедура УстановитьВидимостьПолейРекурсивно(Знач Узел, МассивПолей, Знач Использование)

	Если ТипЗнч(Узел) = Тип("ВыбранноеПолеКомпоновкиДанных") Тогда
		Для Каждого ИмяПоля Из МассивПолей Цикл
			Если Узел.Поле = Новый ПолеКомпоновкиДанных(ИмяПоля) Тогда
				Узел.Использование = Использование;
			КонецЕсли;
		КонецЦикла;
		Возврат;
	КонецЕсли;

	// Обход группы выбранных полей
	Если ТипЗнч(Узел) = Тип("ГруппаВыбранныхПолейКомпоновкиДанных") Тогда
		Для Каждого ВложенныйЭлемент Из Узел.Элементы Цикл
			УстановитьВидимостьПолейРекурсивно(ВложенныйЭлемент, МассивПолей, Использование);
		КонецЦикла;
		Возврат;
	КонецЕсли;

	// Обход структуры таблицы
	Если ТипЗнч(Узел) = Тип("ТаблицаКомпоновкиДанных") Тогда
		Для Каждого СтрТаблицы Из Узел.Строки Цикл
			УстановитьВидимостьПолейРекурсивно(СтрТаблицы, МассивПолей, Использование);
		КонецЦикла;
		Для Каждого КолТаблицы Из Узел.Колонки Цикл
			УстановитьВидимостьПолейРекурсивно(КолТаблицы, МассивПолей, Использование);
		КонецЦикла;
		Возврат;
	КонецЕсли;

	// Обработка объектов, содержащих свойство Выбор
	Если ТипЗнч(Узел) = Тип("НастройкиКомпоновкиДанных")
		ИЛИ ТипЗнч(Узел) = Тип("ГруппировкаКомпоновкиДанных")
		ИЛИ ТипЗнч(Узел) = Тип("ГруппировкаТаблицыКомпоновкиДанных") Тогда

		Для Каждого ПолеВыбора Из Узел.Выбор.Элементы Цикл
			УстановитьВидимостьПолейРекурсивно(ПолеВыбора, МассивПолей, Использование);
		КонецЦикла;

		Для Каждого ЭлементСтруктуры Из Узел.Структура Цикл
			УстановитьВидимостьПолейРекурсивно(ЭлементСтруктуры, МассивПолей, Использование);
		КонецЦикла;

	КонецЕсли;

КонецПроцедуры

Теперь вызовем эту процедуру для корневого узла настроек. Метод отработает как для общего отчета, так и для всех подчиненных группировок:


Настройки = КомпоновщикНастроек.ПолучитьНастройки();

УстановитьВидимостьПолейРекурсивно(Настройки, МассивПолейРуб, ВключатьПоказательРуб);
УстановитьВидимостьПолейРекурсивно(Настройки, МассивПолейEUR, ВключатьПоказательEUR);

КомпоновщикНастроек.ЗагрузитьНастройки(Настройки);

Важные практические нюансы

При программной работе со структурой СКД обратим внимание на следующие тонкости:

  1. Пользовательские настройки: если пользователь изменил состав или видимость колонок через форму настроек отчета, вызов КомпоновщикНастроек.ЗагрузитьНастройки() может быть перекрыт наложением пользовательских настроек. Чтобы избежать конфликта, при полном программном формировании отключают стандартную обработку (СтандартнаяОбработка = Ложь) и передают сформированные Настройки напрямую в КомпоновщикМакета.Выполнить().
  2. Корректное сравнение полей: сравнение через Строка(ВыбранноеПоле.Поле) может приводить к ошибкам из-за синонимов и заголовков. Рекомендуется выполнять сравнение с объектом типа ПолеКомпоновкиДанных: ВыбранноеПоле.Поле = Новый ПолеКомпоновкиДанных("ИмяПоля").
  3. Безопасность типов: в коллекциях выбора могут присутствовать автополя (АвтоВыбранноеПолеКомпоновкиДанных), у которых нет свойства Поле. Всегда выполняйте проверку типа элемента перед обращением к его реквизитам.
← На главную