Как в печатной форме 1С вставить разделитель страниц при смене значения в строке табличной части?

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

При формировании печатных форм документов часто возникает задача группировки данных: если в строках табличной части изменяется определенный реквизит (например, Собственник, Склад или Поставщик), необходимо закрыть текущую таблицу подвалом, вставить разрыв страницы и заново вывести шапку для следующего значения. Разберем основные способы реализации такого алгоритма в платформе 1С:Предприятие, а также проанализируем типичные ошибки, возникающие при обходе строк.

Типичные ошибки при сравнении значений в цикле

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

  1. Несоответствие типов при инициализации: если реквизит имеет ссылочный тип (например, СправочникСсылка.Контрагенты), инициализация переменной сравнения пустой строкой вида ПредыдущееЗначение = "" приведет к ошибке логики: ссылка никогда не равна строке, и условие сработает уже на первой строке. Для ссылочных типов следует использовать Справочники.ИмяСправочника.ПустаяСсылка() или проверять значение через функцию ЗначениеЗаполнено().
  2. Отсутствие предварительной сортировки: если строки табличной части не отсортированы по группировочному полю, одинаковые значения могут встречаться вразнобой. В этом случае смена значения будет происходить многократно, порождая лишние разрывы страниц.
  3. Неправильный момент перезаписи переменной: если перезаписывать сохраненное значение не в конце итерации цикла или перезаписывать его внутри условия некорректно, условие перестанет определять факт смены данных.
  4. Использование индексатора для пустых значений: обращение вида ТабличнаяЧасть[0] вызовет исключение, если табличная часть не содержит строк.

Способ 1. Использование запроса с группировкой по итогам (рекомендуемый)

Рассмотрим наиболее надежный и производительный способ. Вместо ручного отслеживания смены значений в плоском цикле перенесем группировку на уровень запроса с помощью конструкции ИТОГИ ПО. Это гарантирует правильный порядок данных и исключает ошибки сравнения типов.

Разберем по шагам построение запроса и двухуровневого обхода:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ДокТЧ.Ссылка КАК Ссылка,
    |	ДокТЧ.Собственник КАК Собственник,
    |	ДокТЧ.Номенклатура КАК Номенклатура,
    |	ДокТЧ.Количество КАК Количество
    |ИЗ
    |	Документ.РеализацияТоваровУслуг.Товары КАК ДокТЧ
    |ГДЕ
    |	ДокТЧ.Ссылка = &ТекущийДокумент
    |УПОРЯДОЧИТЬ ПО
    |	Собственник,
    |	ДокТЧ.НомерСтроки
    |ИТОГИ
    |	СУММА(Количество)
    |ПО
    |	Собственник";

Запрос.УстановитьПараметр("ТекущийДокумент", СсылкаНаДокумент);
РезультатЗапроса = Запрос.Выполнить();

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

ЭтоПерваяСтраница = Истина;

Пока ВыборкаГруппы.Следующий() Цикл
    
    // Если это не первая страница, разделяем документ
    Если НЕ ЭтоПерваяСтраница Тогда
        ТабДок.ВывестиГоризонтальныйРазделительСтраниц();
    Иначе
        ЭтоПерваяСтраница = Ложь;
    КонецЕсли;
    
    // Вывод шапки документа/группы
    ОбластьШапка.Параметры.Заполнить(ВыборкаГруппы);
    ТабДок.Вывести(ОбластьШапка);
    
    // Обход детальных записей группы
    ВыборкаДетали = ВыборкаГруппы.Выбрать();
    Пока ВыборкаДетали.Следующий() Цикл
        ОбластьСтрока.Параметры.Заполнить(ВыборкаДетали);
        ТабДок.Вывести(ОбластьСтрока);
    КонецЦикла;
    
    // Вывод подвала группы с итогами
    ОбластьПодвал.Параметры.Заполнить(ВыборкаГруппы);
    ТабДок.Вывести(ОбластьПодвал);
    
КонецЦикла;

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

Способ 2. Последовательный обход строк с булевым флагом

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

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


ТаблицаДанных = ДокументОбъект.ТабличнаяЧасть1.Выгрузить();
// Обязательно сортируем данные перед обходом
ТаблицаДанных.Сортировать("Собственник Возр");

ПредыдущийСобственник = Неопределено;
ЭтоПерваяСтрока = Истина;

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

// Выводим подвал для последней группы, если были данные
Если НЕ ЭтоПерваяСтрока Тогда
    ТабДок.Вывести(ОбластьПодвал);
КонецЕсли;

Способ 3. Группировка через выгрузку в таблицу значений

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

  1. Выгружаем табличную часть в объект ТаблицаЗначений методом Выгрузить().
  2. Создаем копию таблицы и сворачиваем её методом Свернуть("Собственник"), получая список всех уникальных владельцев.
  3. Во внешнем цикле обходим уникальных владельцев, отбираем строки методом НайтиСтроки() и единым блоком выводим шапку, детальные строки и подвал.

Рассмотрим пример кода:


ТаблицаСтрок = СсылкаНаДокумент.ТабличнаяЧасть1.Выгрузить();

ТаблицаГрупп = ТаблицаСтрок.Скопировать();
ТаблицаГрупп.Свернуть("Собственник");

НомерГруппы = 0;

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

Рекомендации по управлению выводом табличного документа

При формировании печатных форм обратите внимание на следующие встроенные механизмы платформы 1С:

← На главную