Как объединить заголовки горизонтальных группировок в СКД

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

При разработке отчетов на Системе компоновки данных (СКД) программисты часто сталкиваются с требованием пользователей (особенно бухгалтеров) сделать шапку отчета более компактной и читаемой — есть инструментарий для отладки кода и запросов СКД. Типичная ситуация: у нас есть горизонтальная группировка, например, по приемам пищи (Завтрак, Обед), а внутри нее — группировка по блюдам (Каша, Суп, Чай). По умолчанию СКД будет дублировать заголовок родительской группировки над каждой колонкой вложенной. Наша задача — сделать так, чтобы заголовок "Завтрак" был написан один раз и объединял все относящиеся к нему колонки блюд.

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

В чем сложность стандартного механизма

В СКД нет прямой галочки "Объединять заголовки по горизонтали". Если колонки формируются динамически (их количество заранее неизвестно), статические макеты оформления часто "разваливаются" или не подходят для гибких отчетов. Мы проанализируем метод, который основан на постобработке уже сформированного ТабличногоДокумента.

Шаг 1. Подготовка данных и использование маркеров объединения

Для того чтобы наша программа поняла, какие именно ячейки в итоговом документе нужно объединить, мы должны пометить их специальным маркером. В качестве маркера выберем любую редкую текстовую комбинацию, например, {ОБЪЕДИНИТЬ}.

Рассмотрим наиболее правильный способ добавления маркера — через Выражение представления в наборе данных СКД. Это позволит нам сохранить оригинальное значение поля (ссылку) для работы расшифровки, изменив только визуальный текст.

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

Строка(ПриемПищи) + "{ОБЪЕДИНИТЬ}"

Важный нюанс: если поле имеет тип, отличный от строки (например, СправочникСсылка или Дата), обязательно используем функцию Строка(), иначе система выдаст ошибку "Неверные параметры +".

Шаг 2. Перехват формирования отчета

Теперь нам нужно программно сформировать отчет, чтобы получить доступ к табличному документу до того, как он будет показан пользователю. Для этого в модуле объекта отчета найдем (или создадим) обработчик события (поможет инструмент для отладки кода 1С в режиме «Предприятие») ПриКомпоновкеРезультата.

Разберем код, который отключает стандартный вывод и инициализирует ручной процесс:


Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)
    
    СтандартнаяОбработка = Ложь; // Отключаем автоматический вывод
    ДокументРезультат.Очистить();
    
    // Инициализируем настройки и процессор вывода
    КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
    МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, КомпоновщикНастроек.ПолучитьНастройки(), ДанныеРасшифровки);
    
    ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
    ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки, Истина);
    
    ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
    ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
    ПроцессорВывода.Вывести(ПроцессорКомпоновки);
    
    // Здесь будет вызываться наш механизм объединения
    МаркерОбъединения = "{ОБЪЕДИНИТЬ}";
    ОбъединитьЯчейкиВТабличномДокументе(ДокументРезультат, МаркерОбъединения);
    ЗаменитьТекстВТабличномДокументе(ДокументРезультат, МаркерОбъединения, "");
    
КонецПроцедуры

Шаг 3. Реализация алгоритма объединения

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

Рассмотрим пример реализации функции поиска и объединения:


Процедура ОбъединитьЯчейкиВТабличномДокументе(ТабДок, Маркер)
    
    КоличествоСтрок = ТабДок.ВысотаТаблицы;
    КоличествоКолонок = ТабДок.ШиринаТаблицы;
    
    // Проходим циклом по шапке (обычно это первые 10-20 строк)
    // Для оптимизации можно ограничить диапазон поиска
    Для НомерСтроки = 1 По КоличествоСтрок Цикл
        Для НомерКолонки = 1 По КоличествоКолонок Цикл
            
            ТекущаяОбласть = ТабДок.Область(НомерСтроки, НомерКолонки);
            ТекстЯчейки = ТекущаяОбласть.Текст;
            
            Если Найти(ТекстЯчейки, Маркер) > 0 Тогда
                // Нашли начало потенциального объединения
                НачалоОбъединения = НомерКолонки;
                КонецОбъединения = НомерКолонки;
                
                // Проверяем следующие ячейки справа
                Для К = НомерКолонки + 1 По КоличествоКолонок Цикл
                    СледующаяОбласть = ТабДок.Область(НомерСтроки, К);
                    Если СледующаяОбласть.Текст = ТекстЯчейки Тогда
                        КонецОбъединения = К;
                    Иначе
                        Прервать;
                    КонецЕсли;
                КонецЦикла;
                
                Если КонецОбъединения > НачалоОбъединения Тогда
                    ТабДок.Область(НомерСтроки, НачалоОбъединения, НомерСтроки, КонецОбъединения).Объединить();
                    // Сдвигаем счетчик колонок, чтобы не обрабатывать уже объединенные
                    НомерКолонки = КонецОбъединения;
                КонецЕсли;
            КонецЕсли;
            
        КонецЦикла;
    КонецДля;
    
КонецПроцедуры

Шаг 4. Очистка документа от технических маркеров

После того как ячейки объединены, текст {ОБЪЕДИНИТЬ} больше не нужен — он будет только мешать пользователю. Нам необходимо выполнить глобальную замену этого текста на пустую строку во всем документе.


Процедура ЗаменитьТекстВТабличномДокументе(ТабДок, ЧтоИскать, НаЧтоЗаменить)
    // Используем метод НайтиТекст для быстрого поиска и замены
    НайденнаяОбласть = ТабДок.НайтиТекст(ЧтоИскать);
    Пока НайденнаяОбласть <> Неопределено Цикл
        НайденнаяОбласть.Текст = СтрЗаменить(НайденнаяОбласть.Текст, ЧтоИскать, НаЧтоЗаменить);
        НайденнаяОбласть = ТабДок.НайтиТекст(ЧтоИскать);
    КонецЦикла;
КонецПроцедуры

Как сохранить работоспособность расшифровки

Частая проблема при программном выводе СКД — "пропадание" возможности кликнуть на поле и открыть связанный объект. Выясним причину: расшифровка отключается, если мы выводим в ячейку просто текст вместо значения. Однако, используя предложенный метод с Выражением представления, мы решаем эту проблему:

Важно убедиться, что при вызове ПроцессорКомпоновки.Инициализировать вы передаете переменную ДанныеРасшифровки, полученную из параметров процедуры ПриКомпоновкеРезультата. Без этого интерактивность отчета будет потеряна.

Альтернативный метод: Условное оформление

Если задача стоит не в физическом объединении ячеек, а только в визуальном скрытии дублей, можно применить более простой, но менее эстетичный способ. В Условном оформлении для повторяющихся полей группировки можно задать условие: если значение в текущей колонке совпадает с предыдущим, установить цвет текста, равный цвету фона (белый). Также можно убрать правую или левую границу ячейки. Это создаст иллюзию единого пространства, хотя технически ячейки останутся раздельными.

Заключение

Мы рассмотрели наиболее надежный способ объединения заголовков горизонтальных группировок в СКД. Использование маркеров и программная постобработка ТабличногоДокумента позволяют создавать отчеты любой сложности, которые полностью удовлетворяют требования пользователей к оформлению. Помните о необходимости приведения данных к строковому типу при использовании маркеров и всегда проверяйте работоспособность расшифровки после программного вмешательства в результат компоновки.

← На главную