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

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

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

Исходная проблема: непредсказуемое поведение метода ТабДокумент.ПроверитьВывод()

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


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

Здесь ОбластьОтступ — это пустая строка заданной высоты, а ОбластьНомера — первая колонка строки таблицы, к которой затем присоединяются другие колонки. Перед этой проверкой и выводом ОбластиНомера также мог выводиться условный разделитель (область из пустой строки небольшой высоты с границами сверху и снизу).

В теории этот подход казался идеальным, но на практике столкнулись с его нестабильной работой. Отступы появлялись нерегулярно: то со второй страницы, то с третьей, а иногда и вовсе пропускались на какой-то из страниц (например, на четвертой). При этом, использование этого метода в некоторых случаях приводило к значительному увеличению количества страниц. Документ, который без доработок занимал 4 листа, мог "распираться" до 19 листов, с неполным заполнением последних страниц: листы заполнялись на 2/3, потом наполовину, а на самых последних — всего по 2-3 строчки, после чего следовал горизонтальный разрыв.

Мы выяснили, что ТабДокумент.ПроверитьВывод() может вести себя непредсказуемо по нескольким причинам:

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

Альтернативный подход 1: Глобальное изменение поля сверху

Один из самых простых альтернативных вариантов — это установить свойство ТабДокумент.ПолеСверху на желаемую величину, например, 35 миллиметров:

ТабДокумент.ПолеСверху = 35; // Устанавливаем отступ в 35 мм

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

Альтернативный подход 2: Динамическая проверка количества страниц и принудительный разрыв

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


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

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

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

Наиболее стабильное решение: Использование нескольких макетов

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

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

Давайте рассмотрим шаги по реализации этого подхода:

  1. Создайте макеты или области: Откройте форму объекта, для которого вы формируете печатную форму. В разделе "Макеты" можно создать два новых макета (например, "МакетДокументаПерваяСтраница" и "МакетДокументаПоследующиеСтраницы") или, что чаще, определить в одном макете две группы областей с разными именами (например, "Заголовок_Первая", "Шапка_Первая", "Шапка_Последующая_С_Отступом", "Строка_Таблицы").
  2. Настройте макеты/области:
    • В области(ях) для первой страницы разместите все элементы, которые должны быть только на первой странице (полная шапка документа, общие реквизиты).
    • В области "Шапка_Последующая_С_Отступом" (из второго макета или из того же, но с другим именем) создайте в самой верхней части макета пустую область (строку) необходимой высоты, которая будет служить вашим отступом. Затем под этой областью разместите повторяющуюся шапку таблицы, которая должна отображаться на всех последующих страницах. Таким образом, отступ станет неотъемлемой частью шапки последующих страниц.
  3. Реализуйте логику вывода в коде: В процедуре формирования печатной формы вам потребуется отслеживать, какая страница сейчас выводится, и динамически выбирать нужные области.

Пример кода, демонстрирующий эту логику:


Процедура ПечатьДокумента()

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

    // --- Определение областей из макета ---
    // Области для первой страницы
    ОбластьЗаголовокПерваяСтраница = Макет.ПолучитьОбласть("Заголовок_ПерваяСтраница"); // Например, полные реквизиты документа
    ОбластьШапкаТаблицыПерваяСтраница = Макет.ПолучитьОбласть("ШапкаТаблицы_ПерваяСтраница"); // Шапка таблицы без отступа

    // Область для последующих страниц, которая уже включает в себя верхний отступ
    ОбластьШапкаПоследующихСОтступом = Макет.ПолучитьОбласть("ШапкаТаблицы_ПоследующихСОтступом");

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

    // --- Выводим начальные данные и шапку первой страницы ---
    // Здесь заполняем параметры ОбластьЗаголовокПерваяСтраница и ОбластьШапкаТаблицыПерваяСтраница
    // ...
    ТабДокумент.Вывести(ОбластьЗаголовокПерваяСтраница);
    ТабДокумент.Вывести(ОбластьШапкаТаблицыПерваяСтраница);

    // --- Цикл вывода строк табличной части ---
    Для Каждого СтрокаТЧ Из Объект.ТабличнаяЧасть Цикл

        // Перед выводом каждой строки заполняем ее данными
        // ... Заполнение ОбластьСтрокаТаблицы параметрами из СтрокаТЧ ...

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

            // После разрыва страницы, мы всегда выводим шапку,
            // предназначенную для последующих страниц, которая УЖЕ содержит нужный отступ сверху.
            ТабДокумент.Вывести(ОбластьШапкаПоследующихСОтступом);
        КонецЕсли;

        // Выводим саму строку табличной части
        ТабДокумент.Вывести(ОбластьСтрокаТаблицы);

    КонецЦикла;

    ТабДокумент.Показать();

КонецПроцедуры

Важные моменты при использовании нескольких макетов или областей:

  1. Встроенный отступ: Ключевая идея заключается в том, что отступ для последующих страниц должен быть встроен непосредственно в область шапки для этих страниц. Это исключает необходимость динамического добавления отдельной ОбластьОтступ и повышает стабильность.
  2. ТабДокумент.ПроверитьВывод() для строк: Мы все равно используем ТабДокумент.ПроверитьВывод() для определения, поместится ли очередная строка. Несмотря на его потенциальную нестабильность, в сочетании с жестко определенными областями шапок это дает более предсказуемый результат, чем попытки динамического управления отступами.
  3. ТабДокумент.ВывестиГоризонтальныйРазделительСтраниц(): Этот метод принудительно создает разрыв страницы. Сразу после него мы выводим нужную шапку.

Дополнительные рекомендации и советы

Для повышения стабильности работы с табличными документами и их печатными формами мы рекомендуем следующее:

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

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

← На главную