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