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