Как правильно вывести сгруппированные строки таблицы в макет табличного документа 1С

Программист 1С v8.3 (Обычные формы) Промышленность, строительство и АПК
← На главную

При формировании печатных форм и отчетов разработчики часто сталкиваются с задачей вывода связанных данных типа «один ко многим». Типичный пример — список сотрудников или должностей, для каждой из которых предусмотрен перечень норм выдачи спецодежды (СИЗ), оборудования или материалов. Если попытаться выводить наименование должности методом ТабличныйДокумент.Вывести(), а подчиненные строки присоединять через ТабличныйДокумент.Присоединить() в цикле, каждая следующая строка будет непрерывно смещаться вправо по горизонтали.

Выясним причину этой проблемы и подробно разберем несколько надежных способов вывода иерархических данных в макет 1С.

Причина ошибки при использовании метода Присоединить()

Метод Присоединить() предназначен исключительно для вывода областей по горизонтали (вправо от уже выведенной строки на том же уровне). Когда в цикле по подчиненным строкам вызывается Присоединить(), платформа добавляет новую секцию справа от предыдущей, формируя длинную диагональную «лесенку» из ячеек вместо аккуратной таблицы со строками вниз.

Для добавления строк строго вниз необходимо использовать метод Вывести(). Чтобы при этом красиво отобразить группирующую колонку (например, должность), рассмотрим основные подходы к оформлению макета и логике программного кода.

Способ 1. Вывод через две секции макета (без использования метода Объединить)

Этот способ является самым простым, производительным и не требует сложных расчетов координат ячеек. Идея заключается в подготовке двух строк в макете:

  1. ОбластьСтрокаШапка (первая строка группы): содержит параметр для вывода должности, а также параметры для первой позиции СИЗ. В оформлении ячейки должности задаются границы сверху, слева и справа.
  2. ОбластьСтрокаДетали (последующие строки группы): ячейка должности оставляется пустой (без параметра), в ней настроены только границы слева и справа (без горизонтальных линий). Остальные колонки содержат параметры для следующих позиций СИЗ.

Проанализируем алгоритм обхода данных с запоминанием текущего значения группировки в переменную:


ТабДок = Новый ТабличныйДокумент;
Макет = ПолучитьМакет("Макет");

ОбластьПервая = Макет.ПолучитьОбласть("СтрокаГруппировка");
ОбластьВторая = Макет.ПолучитьОбласть("СтрокаДетали");

ТекущаяДолжность = "";
НомерПП = 0;

// Исходная таблица должна быть обязательно отсортирована по полю группировки
ТаблицаДанных.Сортировать("НаименованиеПрофессии, НаименованиеСИЗ");

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

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

Способ 2. Программное объединение ячеек методом Объединить()

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

Разберем по шагам, как динамически определять координаты строк с помощью метода ТабличныйДокумент.ВысотаТаблицы():


ТабДок = Новый ТабличныйДокумент;
Макет = ПолучитьМакет("Макет");
ОбластьСтроки = Макет.ПолучитьОбласть("СтрокаТаблицы");

ТекущаяДолжность = "";
НачалоГруппы = 0;
НомерПП = 0;

ТаблицаДанных.Сортировать("НаименованиеПрофессии, НаименованиеСИЗ");

Для Каждого Стр Из ТаблицаДанных Цикл
    Если Стр.НаименованиеПрофессии <> ТекущаяДолжность Тогда
        // Объединяем ячейки предыдущей группы, если в ней было более 1 строки
        Если НачалоГруппы > 0 И ТабДок.ВысотаТаблицы() > НачалоГруппы Тогда
            ТабДок.Область(НачалоГруппы, 1, ТабДок.ВысотаТаблицы(), 1).Объединить();
            ТабДок.Область(НачалоГруппы, 2, ТабДок.ВысотаТаблицы(), 2).Объединить();
        КонецЕсли;
        
        ТекущаяДолжность = Стр.НаименованиеПрофессии;
        НомерПП = НомерПП + 1;
        
        ОбластьСтроки.Параметры.НомерПП = НомерПП;
        ОбластьСтроки.Параметры.НаименованиеПрофессии = Стр.НаименованиеПрофессии;
        
        ТабДок.Вывести(ОбластьСтроки);
        НачалоГруппы = ТабДок.ВысотаТаблицы();
    Иначе
        // Очищаем параметры группировки перед выводом подчиненных строк
        ОбластьСтроки.Параметры.НомерПП = "";
        ОбластьСтроки.Параметры.НаименованиеПрофессии = "";
        
        ТабДок.Вывести(ОбластьСтроки);
    КонецЕсли;
    
    // Заполняем детальные данные текущей строки
    ОбластьСтроки.Параметры.НаименованиеСИЗ = Стр.НаименованиеСИЗ;
    ОбластьСтроки.Параметры.Количество = Стр.Количество;
    ОбластьСтроки.Параметры.ЕдИзм = Стр.ЕдИзм;
КонецЦикла;

// Объединяем последнюю группу строк в документе
Если НачалоГруппы > 0 И ТабДок.ВысотаТаблицы() > НачалоГруппы Тогда
    ТабДок.Область(НачалоГруппы, 1, ТабДок.ВысотаТаблицы(), 1).Объединить();
    ТабДок.Область(НачалоГруппы, 2, ТабДок.ВысотаТаблицы(), 2).Объединить();
КонецЕсли;

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

Способ 3. Иерархический обход через запрос с конструкцией ИТОГИ ПО

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

Посмотрим на пример реализации:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   Т.НаименованиеПрофессии КАК НаименованиеПрофессии,
    |   Т.НаименованиеСИЗ КАК НаименованиеСИЗ,
    |   Т.Количество КАК Количество,
    |   Т.ЕдИзм КАК ЕдИзм
    |ПОМЕСТИТЬ ВТ_СИЗ
    |ИЗ
    |   &ТаблицаСИЗ КАК Т
    |;
    |
    |ВЫБРАТЬ
    |   ВТ_СИЗ.НаименованиеПрофессии КАК НаименованиеПрофессии,
    |   ВТ_СИЗ.НаименованиеСИЗ КАК НаименованиеСИЗ,
    |   ВТ_СИЗ.Количество КАК Количество,
    |   ВТ_СИЗ.ЕдИзм КАК ЕдИзм
    |ИЗ
    |   ВТ_СИЗ КАК ВТ_СИЗ
    |ИТОГИ ПО
    |   НаименованиеПрофессии";

Запрос.УстановитьПараметр("ТаблицаСИЗ", ПереченьСИЗ.Выгрузить());
РезультатЗапроса = Запрос.Выполнить();

ВыборкаГруппировка = РезультатЗапроса.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);
НомерПП = 0;

Пока ВыборкаГруппировка.Следующий() Цикл
    НомерПП = НомерПП + 1;
    ВыборкаДетали = ВыборкаГруппировка.Выбрать();
    
    ПерваяСтрока = Истина;
    Пока ВыборкаДетали.Следующий() Цикл
        Если ПерваяСтрока Тогда
            ОбластьПервая.Параметры.НомерПП = НомерПП;
            ОбластьПервая.Параметры.НаименованиеПрофессии = ВыборкаГруппировка.НаименованиеПрофессии;
            ОбластьПервая.Параметры.Заполнить(ВыборкаДетали);
            ТабДок.Вывести(ОбластьПервая);
            ПерваяСтрока = Ложь;
        Иначе
            ОбластьВторая.Параметры.Заполнить(ВыборкаДетали);
            ТабДок.Вывести(ОбластьВторая);
        КонецЕсли;
    КонецЦикла;
КонецЦикла;

Полезные механизмы платформы для оформления отчетов

Для повышения качества печатных форм рекомендуется применять встроенные возможности платформы 1С:

  1. Интерактивные группировки строк: методы ТабличныйДокумент.НачатьГруппуСтрок() и ТабличныйДокумент.ЗакончитьГруппуСтрок() позволяют создавать иерархические уровни со значками разворачивания «+» и «−» без усложнения разметки ячеек.
  2. Контроль разрыва страниц: чтобы блок строк одной должности не разрывался при переходе на новую страницу принтера, перед выводом группы областей вызывается метод ТабличныйДокумент.ПроверитьВывод(МассивОбластей). Если массив не помещается на страницу, принудительно вставляется ТабличныйДокумент.ВывестиГоризонтальныйРазделительСтраниц().
← На главную