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