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

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

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

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

Причина проблемы: почему стандартный вывод разделяет таблицу и подвал?

По умолчанию движок компоновки и вывода табличных документов 1С заполняет страницу «до упора». Если до физического конца листа остается условно 20 миллиметров, платформа без проблем выведет туда очередную строку таблицы. Но когда очередь дойдет до области подвала, высота которой составляет 50 миллиметров, алгоритм поймет, что она не помещается, и автоматически перенесет ее на новую страницу. В результате мы получаем сиротливый подвал на отдельном листе.

Для решения этой проблемы мы можем использовать два принципиально разных подхода:

  1. Декларативный метод — использование платформенного свойства ВместеСоСледующим.
  2. Императивный (программный) метод — превентивная проверка вывода группы областей с помощью метода ПроверитьВывод().

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

---

Способ 1. Использование свойства «ВместеСоСледующим» (Рекомендуемый)

Это самый простой, лаконичный и быстродействующий способ, который практически не требует сложных математических расчетов в коде. У объекта ОбластьЯчеекТабличногоДокумента существует замечательное свойство — ВместеСоСледующим (в английском варианте KeepWithNext).

Если для строки (или группы строк) установлено это свойство в значение Истина, то при формировании страниц для печати платформа будет анализировать: помещается ли текущая строка вместе со следующей за ней строкой на текущем листе. Если следующая строка не влезает, то и текущая строка будет принудительно перенесена на новый лист.

Выясним, как применить это свойство программно при выводе строк нашей таблицы:


// Предположим, что ТабДокумент — это наш результирующий ТабличныйДокумент
// В цикле выводим строки таблицы
Ном = 0;
ВсегоСтрок = ВремТаб.Количество();

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

    // Выводим строку в результирующий документ
    ТабДокумент.Вывести(ОбластьМакета);
    
    // Определяем номер только что выведенной строки в ТабличномДокументе
    НомерТекущейСтрокиТабДока = ТабДокумент.ВысотаТаблицы;
    
    // Если это предпоследняя или последняя строка таблицы, 
    // связываем их «со следующими» объектами
    Если Ном >= (ВсегоСтрок - 1) Тогда
        ТабДокумент.Область(НомерТекущейСтрокиТабДока, , НомерТекущейСтрокиТабДока).ВместеСоСледующим = Истина;
    КонецЕсли;
КонецЦикла;

// Выводим области Итогов и Подвала
ОбластьИтого = Макет.ПолучитьОбласть("Итого");
// ... заполнение параметров итога ...
ТабДокумент.Вывести(ОбластьИтого);

// Для строк Итогов также ставим свойство ВместеСоСледующим, 
// чтобы связать их с Подвалом
НомерИтогоСтроки = ТабДокумент.ВысотаТаблицы;
ТабДокумент.Область(НомерИтогоСтроки, , НомерИтогоСтроки).ВместеСоСледующим = Истина;

ОбластьПодвал = Макет.ПолучитьОбласть("Подвал");
ТабДокумент.Вывести(ОбластьПодвал);

Важный нюанс при работе с ВместеСоСледующим: Никогда не устанавливайте это свойство для самой последней строки вашего подвала! Если связать последнюю строчку документа «со следующей» (которой физически не существует), алгоритм визуализации платформы 1С может повести себя непредсказуемо, например, добавив в конец печатной формы лишний пустой лист.

Также помните, что это свойство можно установить интерактивно непосредственно в макете печатной формы в конфигураторе. Для этого достаточно выделить строки макета, открыть их палитру свойств и установить флажок «Вместе со следующим». Однако для динамически генерируемых строк таблицы программная установка (как в примере выше) является более надежной.

---

Способ 2. Проверка вывода группы областей через массив

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

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

Разберем этот алгоритм по шагам:

  1. Выводим строки таблицы в обычном режиме до тех пор, пока индекс строки меньше, чем КоличествоСтрок - 1 (индексация с 1).
  2. Когда мы подходим к выводу предпоследней строки, мы не выводим ее сразу. Вместо этого мы собираем в специальный массив (коллекцию) все оставшиеся элементы: предпоследнюю строку, последнюю строку, область итогов и область подписи/подвала.
  3. Передаем этот массив в метод ПроверитьВывод().
  4. Если метод возвращает Ложь, это означает, что «хвост» таблицы вместе с подвалом целиком не влезает на текущий лист. В таком случае мы принудительно вставляем разделитель страниц с помощью метода ВывестиГоризонтальныйРазделительСтраниц(), при необходимости повторно печатаем шапку таблицы на новом листе, а затем выводим оставшиеся строки и подвал.

Посмотрим на практическую реализацию этого сценария в коде:


МассивОбластейПроверки = Новый Массив;
ВсегоСтрок = ВремТаб.Количество();
Ном = 0;

Для Каждого Тек Из ВремТаб Цикл  
    Ном = Ном + 1;
    
    // Создаем область для текущей строки и заполняем ее параметрами
    ОбластьСтроки = Макет.ПолучитьОбласть("Строка");
    ОбластьСтроки.Параметры.Заполнить(Тек);
    ОбластьСтроки.Параметры.Номер = Ном;
    // ... (заполнение остальных параметров строки) ...
    
    // Нам нужно перехватить вывод на предпоследней строке
    Если Ном = (ВсегоСтрок - 1) Тогда
        
        // 1. Добавляем в массив текущую (предпоследнюю) строку
        МассивОбластейПроверки.Добавить(ОбластьСтроки);
        
        // 2. Создаем и подготавливаем последнюю строку
        СтрокаПоследняяДанные = ВремТаб[ВсегоСтрок - 1];
        ОбластьСтрокиПоследняя = Макет.ПолучитьОбласть("Строка");
        ОбластьСтрокиПоследняя.Параметры.Заполнить(СтрокаПоследняяДанные);
        ОбластьСтрокиПоследняя.Параметры.Номер = ВсегоСтрок;
        // ... (заполнение параметров последней строки) ...
        МассивОбластейПроверки.Добавить(ОбластьСтрокиПоследняя);
        
        // 3. Создаем и подготавливаем область Итогов
        ОбластьИтого = Макет.ПолучитьОбласть("Итого");
        ОбластьИтого.Параметры.ИтогоСумма = ИтогоСумма + СтрокаПоследняяДанные.СуммаПокупателя; 
        // Обратите внимание: к накопленным итогам нужно прибавить показатели последней строки,
        // так как они еще не были учтены в общем цикле!
        МассивОбластейПроверки.Добавить(ОбластьИтого);
        
        // 4. Добавляем область Подвала
        ОбластьПодвал = Макет.ПолучитьОбласть("Подвал");
        МассивОбластейПроверки.Добавить(ОбластьПодвал);
        
        // Выполняем проверку вывода всего «пакета» областей целиком
        Попытка
            Если Не ТабДокумент.ПроверитьВывод(МассивОбластейПроверки) Тогда
                // Если пакет не влезает на текущую страницу, переносим его на следующую
                ТабДокумент.ВывестиГоризонтальныйРазделительСтраниц();
                
                // Если в вашей печатной форме предусмотрен вывод шапки таблицы на каждой новой странице:
                // ШапкаТаблицы = Макет.ПолучитьОбласть("ШапкаТаблицы");
                // ТабДокумент.Вывести(ШапкаТаблицы);
            КонецЕсли;
        Исключение
            // На сервере без установленных принтеров ПроверитьВывод() может вызвать исключение.
            // Защищаем код от падения транзакции.
            ЗаписьЖурналаРегистрации("Печать", УровеньЖурналаРегистрации.Предупреждение, , , 
                "Не удалось проверить вывод табличного документа: " + ОписаниеОшибки());
        КонецПопытки;
        
    КонецЕсли;

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

---

Критически важные правила работы с методом ПроверитьВывод()

Если вы выбрали программный путь с использованием метода ПроверитьВывод(), обязательно следуйте трем «золотым» правилам платформы 1С. Пренебрежение ими гарантированно приведет к тому, что на предварительном просмотре или при печати подвал все равно оторвется.

  1. Настройка параметров страницы ДО проверки.

    Платформа 1С рассчитывает высоту областей и размер страницы на основе параметров табличного документа. Если вы установите поля, ориентацию (ландшафт/портрет) и автоклиентский масштаб в самом конце кода (как это часто делают начинающие разработчики), то метод ПроверитьВывод() выполнит проверку по дефолтным настройкам (страница А4, портрет, стандартные поля 10 мм). На печати масштаб сожмется, поля изменятся, и ваши расчеты окажутся неверными.

    Решение: Всегда задавайте настройки страницы в самом начале формирования документа:

    
    ТабДокумент.ПолеСверху = 5;
    ТабДокумент.ПолеСнизу  = 5;
    ТабДокумент.АвтоМасштаб = Истина;
    // ... и только после этого запускайте циклы вывода и проверки!
    
  2. Заполнение параметров областей перед проверкой.

    Нельзя передавать в метод ПроверитьВывод() пустые, только что полученные из макета области. Если в ячейках макета настроен автоматический перенос текста (свойство «Размещение» = «Переносить»), то незаполненная область будет иметь высоту в одну строку. Когда вы заполните ее реальным длинным наименованием номенклатуры, строка на печати расширится до 2–3 строк по высоте. Проверка по пустой области вернет некорректный результат.

    Решение: Сначала заполняйте все параметры ячеек в области (Параметры.Заполнить()) и только потом добавляйте ее в массив для проверки вывода.

  3. Обработка исключений (Защита от отсутствия принтера).

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

    Решение: Всегда оборачивайте вызов метода ПроверитьВывод() в конструкцию Попытка ... Исключение ... КонецПопытки.

Подведем итоги

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

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

← На главную