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