Как вывести подвал печатной формы внизу каждой страницы в 1С:Предприятие

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

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

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

Почему стандартный метод ПовторятьПриПечатиСтроки не подходит для подвала?

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

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

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

Для реализации этой логики нам придется прибегнуть к программным методам контроля вывода областей.


Вариант 1. Использование встроенных колонтитулов (простой текстовый подвал)

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

Колонтитулы выводятся автоматически силами самой платформы и гарантированно прижимаются к нижнему краю листа с учетом заданных полей принтера. Посмотрим на пример настройки нижнего колонтитула:


ТабДокумент.НижнийКолонтитул.Выводить = Истина;
ТабДокумент.НижнийКолонтитул.НачальнаяСтраница = 1;
ТабДокумент.НижнийКолонтитул.ТекстСлева = "Акт сдачи-приемки услуг";
ТабДокумент.НижнийКолонтитул.ТекстВЦентре = "Страница [&НомерСтраницы] из [&СтраницВсего]";
ТабДокумент.НижнийКолонтитул.ТекстСправа = "ООО ""Наша Организация""";

Преимущества метода:

  1. Высокая скорость работы — платформа сама рассчитывает границы страниц и распределяет текст.
  2. Минимум программного кода.
  3. Гарантированное отсутствие наложений текста и «пустых» листов при печати.

Недостатки метода:

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


Вариант 2. Программная эмуляция сквозного подвала методом «Пропуска»

Рассмотрим подробнее идею этого метода. Чтобы вывести полноценную область макета в самом низу страницы, нам нужно искусственно заполнить свободное пространство между концом содержательной части (строками таблицы) и началом подвала невидимыми пустыми строками. Этот процесс визуально «прижмет» область подвала к нижней границе печатной области листа.

Для реализации этого метода нам потребуются три составляющие:

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

Разберем вспомогательную функцию дополнения страницы по шагам

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

Создадим функцию ДополнитьСтраницуТабличногоДокумента:


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

Выясним, как работает данный алгоритм. Метод ПроверитьВывод() имитирует вывод переданного массива областей (в нашем случае это микро-строка и подвал) на текущую страницу табличного документа. Если они помещаются, мы реально выводим в документ одну пустую микро-строку. На следующей итерации цикла проверка повторяется уже с учетом только что выведенной строки. Как только метод возвращает Ложь, это означает, что свободного места на листе осталось ровно столько, чтобы поместить туда наш подвал. Цикл завершается, и мы готовы вывести сам подвал.


Интеграция алгоритма в цикл вывода строк печатной формы

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

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

Рассмотрим детальный пошаговый алгоритм:


Процедура КнопкаСформироватьНажатие(Кнопка)
    
    ТабДокумент = Новый ТабличныйДокумент;
    
    // Настройки страницы КРИТИЧЕСКИ ВАЖНО задать ДО вызова ПроверитьВывод()
    ТабДокумент.Автомасштаб = Истина;
    ТабДокумент.ОриентацияСтраницы = ОриентацияСтраницы.Портрет;
    ТабДокумент.ПолеСверху = 10;
    ТабДокумент.ПолеСнижу = 10;
    
    Макет = ЭтотОбъект.ПолучитьМакет("Акт");
    
    // Получаем области макета
    ОбластьНазвание     = Макет.ПолучитьОбласть("Название");
    ОбластьРеквизиты    = Макет.ПолучитьОбласть("Реквизиты");
    ОбластьЗаголовок    = Макет.ПолучитьОбласть("Заголовок");
    ОбластьШапка        = Макет.ПолучитьОбласть("Шапка");
    ОбластьОснова       = Макет.ПолучитьОбласть("Основа");
    ОбластьПодвал       = Макет.ПолучитьОбласть("Подвал");
    ОбластьДир          = Макет.ПолучитьОбласть("Дир");
    ОбластьИсполнитель  = Макет.ПолучитьОбласть("Исполнитель");
    
    // Специальная область-микрострока для заполнения пустот
    ОбластьПустаяСтрока = Макет.ПолучитьОбласть("ПустаяСтрока");
    
    // Выводим статичную шапку документа
    ТабДокумент.Вывести(ОбластьНазвание);
    ТабДокумент.Вывести(ОбластьРеквизиты);
    ОбластьЗаголовок.Параметры.Контрагент = Контрагент;
    ТабДокумент.Вывести(ОбластьЗаголовок);
    ТабДокумент.Вывести(ОбластьШапка);
    
    // Подготовим массив для проверки вывода строк данных вместе с подвалом.
    // Нам нужно знать, поместится ли очередная строка данных И подвал на текущий лист.
    МассивСтрокаИПодвал = Новый Массив;
    МассивСтрокаИПодвал.Добавить(ОбластьОснова);
    МассивСтрокаИПодвал.Добавить(ОбластьПодвал);
    
    // Запрос выборки данных
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   РеализацияТоваровУслуг.Номер КАК Номер,
        |   РеализацияТоваровУслуг.Дата КАК Дата,
        |   РеализацияТоваровУслуг.Контрагент КАК Контрагент
        |ИЗ
        |   Документ.RealizationТоваровУслуг КАК РеализацияТоваровУслуг
        |ГДЕ
        |   РеализацияТоваровУслуг.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания
        |   И РеализацияТоваровУслуг.Контрагент = &Контрагент";
        
    Запрос.УстановитьПараметр("ДатаНачала", НачалоПериода);
    Запрос.УстановитьПараметр("ДатаОкончания", КонецДня(КонецПериода));
    Запрос.УстановитьПараметр("Контрагент", Контрагент);
    
    Выборка = Запрос.Выполнить().Выбрать();
    
    Пока Выборка.Следующий() Цикл
        
        ОбластьОснова.Параметры.Заполнить(Выборка);
        ОбластьОснова.Параметры.ДатаДокумент = Формат(Выборка.Дата, "ДЛФ=DD");
        ОбластьОснова.Параметры.Номер = Выборка.Номер;
        
        // Проверяем: если мы выведем текущую строку таблицы, поместится ли после нее подвал на этой же странице?
        Если Не ТабДокумент.ПроверитьВывод(МассивСтрокаИПодвал) Тогда
            
            // Если НЕ помещается, значит текущий лист заполнен. 
            // Сначала красиво прижимаем подвал к самому низу текущей страницы:
            ДополнитьСтраницуТабличногоДокумента(ТабДокумент, ОбластьПустаяСтрока, ОбластьПодвал);
            
            // Выводим подвал для текущей страницы
            ТабДокумент.Вывести(ОбластьПодвал);
            
            // Осуществляем переход на новую страницу
            ТабДокумент.ВывестиГоризонтальныйРазделительСтраниц();
            
            // На новой странице дублируем шапку таблицы
            ТабДокумент.Вывести(ОбластьШапка);
            
        КонецЕсли;
        
        // Выводим саму строку данных
        ТабДокумент.Вывести(ОбластьОснова);
        
    КонецЦикла;
    
    // После окончания вывода всех строк данных нам нужно оформить последнюю страницу.
    // Прижимаем подвал к низу последнего листа:
    ДополнитьСтраницуТабличногоДокумента(TabДокумент, ОбластьПустаяСтрока, ОбластьПодвал);
    
    // Выводим финальный подвал
    ТабДокумент.Вывести(ОбластьПодвал);
    
    // Выводим сопутствующие подписи (например, Директор и Исполнитель)
    ТабДокумент.Вывести(ОбластьДир);
    ТабДокумент.Вывести(ОбластьИсполнитель);
    
    // Настройки отображения документа
    ТабДокумент.ОтображатьСетку = Ложь;
    ТабДокумент.Защита = Ложь;
    ТабДокумент.ТолькоПросмотр = Истина;
    
    ТабДокумент.Показать();
    
КонецПроцедуры

Важные нюансы, оптимизация и подводные камни

Реализуя программный контроль вывода страниц, важно помнить о нескольких тонкостях разработки в 1С, которые могут существенно повлиять на результат и производительность системы.

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

    Для оптимизации скорости рекомендуется делать высоту пустой строки-заполнителя в макете сопоставимой с высотой обычной информационной строки (примерно 12–15 пунктов). Это сократит количество итераций цикла заполнения в несколько раз без видимой потери качества верстки.

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

    Чтобы избежать этой проблемы, старайтесь делать область подвала компактной, а высоту строки-заполнителя подбирайте экспериментально (начните с высоты в 5–8 пунктов).

  4. Кэширование принтера. На некоторых серверах под управлением ОС Linux без установленного графического окружения или принтера по умолчанию метод ПроверитьВывод() может возвращать непредсказуемые результаты (или всегда возвращать Истина). Убедитесь, что на сервере 1С настроен виртуальный принтер по умолчанию — это гарантирует корректную работу математических алгоритмов расчета высоты страниц.

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

← На главную