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