Макет печатной формы 1С — табличный документ в конфигурации, на котором заранее нарисован бланк: шапка, строка таблицы, итоги, подписи. Код ничего не рисует сам: заполняет параметры областей и выводит области одну под другой. Ниже — как нарисовать макет в конфигураторе, чтобы он ровно ложился на A4, и как вывести его кодом: длинная таблица с повтором шапки и итогом по листу, пачка документов с разрывом страниц, колонтитулы, колонка по условию.
Основы разобраны отдельно: подключение к команде «Печать» — в статье о печатных формах 1С, получение макета из кода — в материале про ПолучитьМакет, свойства документа — в руководстве по табличному документу. Макет печатной формы в 1С 8.3 одинаков для обычных и управляемых форм, отличается только код команды.
Макет добавляют в ветке «Макеты» документа: «Добавить» → «Табличный документ». На БСП макеты печатных форм называют с префиксом ПФ_MXL_, в самописной конфигурации имя любое — у нас Накладная. Дальше:
АвтоМасштаб ужмёт его вместе со шрифтом; широкие бланки рисуют под альбомный лист.Код из статьи рассчитан на такой печатный макет:
| Область | Что внутри |
|---|---|
Шапка | шаблон «Расходная накладная № [Номер] от [ДатаДокумента]», параметры Организация, Контрагент |
ШапкаТаблицы | заголовки колонок, только текст |
Строка | НомерСтроки, Номенклатура, Количество, Цена, Сумма |
ИтогоПоЛисту | КоличествоНаЛисте, СуммаНаЛисте |
Продолжение | шаблон «Продолжение накладной № [Номер] от [ДатаДокумента], лист [НомерЛиста]» |
Подвал | ВсегоНаименований, СуммаВсего, строки для подписей |
Тип области зависит от того, что выделено при назначении имени:
Вывести();"Строка|Скидка" позволяет собрать строку из частей;Если выделить ячейки вместо строк, область станет прямоугольной, и при выводе колонки могут съехать.
| «Заполнение» | Что печатается | Пример |
|---|---|---|
| Текст | надпись как есть | Наименование |
| Параметр | значение параметра, имя которого записано в свойстве «Параметр»; в конфигураторе ячейка показывает его в угловых скобках | <Контрагент> |
| Шаблон | текст с именами параметров в квадратных скобках | Накладная № [Номер] |
Параметры ячеек и шаблонов попадают в коллекцию Параметры области и заполняются одинаково: Область.Параметры.Контрагент = … или Параметры.Заполнить(Выборка). Шаблон держит текст и данные одной фразой, и заголовок не разъедется при длинном номере. Дату в шаблон передавайте строкой через Формат(Дата, "ДЛФ=Д"), иначе напечатается «02.10.2026 0:00:00». Ещё два свойства ячейки-параметра в палитре:
Формат(): ЧДЦ=2 для сумм, ДЛФ=Д для дат, ЧГ=0 без пробелов между разрядами;РазмещениеТекста) = «Переносить», иначе длинное название обрежется.Повтор строк и колонтитулы можно задать и самому макету, но Вывести() переносит в новый документ только содержимое области — ячейки с оформлением и рисунки. Ориентация, поля, колонтитулы, повтор строк и область печати у результата свои. Шапку таблицы на каждом листе дают два способа:
ПовторятьПриПечатиСтрок — просто, но область повтора одна на весь документ, а итог по листу так не вывести;ПроверитьВывод() — для итогов по листу, надписи «Продолжение» и пачки документов, как в листинге ниже.Выберите один: при обоих шапка задвоится. Колонтитулы — объекты ВерхнийКолонтитул и НижнийКолонтитул табличного документа:
| Свойство | Назначение |
|---|---|
Выводить | включает колонтитул |
ТекстСлева, ТекстВЦентре, ТекстСправа | три части строки колонтитула |
НачальнаяСтраница | с какой страницы печатать |
Шрифт | шрифт колонтитула |
В текст вставляют служебные поля [&НомерСтраницы], [&СтраницВсего], [&Дата], [&Время]. Нумерация сквозная по всему табличному документу.
В самописной конфигурации без БСП заготовку даёт «Конструктор печати» из контекстного меню документа в дереве конфигурации. Он спрашивает, какие реквизиты шапки и колонки табличной части выводить, и по «ОК» создаёт макет печати с областями и параметрами, команду и процедуру в модуле менеджера. Так быстро получают макеты форм для простых документов, а перенос таблицы, итоги по листу и вёрстку под бланк дописывают руками. В типовых на БСП конструктор не нужен: команды печати строит подсистема «Печать».
Модуль менеджера документа РасходнаяНакладная без БСП. Функция получает массив ссылок, выводит каждую накладную с нового листа и сама переносит таблицу: если строка вместе с итогом не помещается на лист, закрывает его итогом, ставит разрыв и повторяет шапку таблицы с надписью «Продолжение». К последней строке в проверку добавляется подвал, чтобы подписи не ушли на отдельный лист.
// Модуль менеджера документа РасходнаяНакладная (конфигурация без БСП).
// Платформа 8.3.6 и выше (СтрРазделить).
// Макет «Накладная» - табличный документ с горизонтальными областями:
// Шапка - шаблон «Расходная накладная № [Номер] от [ДатаДокумента]»,
// параметры Организация, Контрагент;
// ШапкаТаблицы - заголовки колонок, только текст;
// Строка - НомерСтроки, Номенклатура (она же параметр расшифровки),
// Количество, Цена, Сумма;
// ИтогоПоЛисту - КоличествоНаЛисте, СуммаНаЛисте;
// Продолжение - шаблон «Продолжение накладной № [Номер] от [ДатаДокумента], лист [НомерЛиста]»;
// Подвал - ВсегоНаименований, СуммаВсего и строки для подписей.
#Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда
#Область ПрограммныйИнтерфейс
// Формирует накладные по массиву ссылок: каждая начинается с нового листа,
// длинная таблица переносится с итогом по листу и повтором шапки таблицы.
//
// Параметры:
// МассивСсылок - Массив из ДокументСсылка.РасходнаяНакладная
//
// Возвращаемое значение:
// ТабличныйДокумент
//
Функция ПечатьНакладных(МассивСсылок) Экспорт
ТабДок = Новый ТабличныйДокумент;
НастроитьЛист(ТабДок); // до вывода: от параметров листа зависит ПроверитьВывод
Макет = Документы.РасходнаяНакладная.ПолучитьМакет("Накладная");
Области = Новый Структура;
Для Каждого ИмяОбласти Из СтрРазделить("Шапка,ШапкаТаблицы,Строка,ИтогоПоЛисту,Продолжение,Подвал", ",") Цикл
Области.Вставить(ИмяОбласти, Макет.ПолучитьОбласть(ИмяОбласти));
КонецЦикла;
Запрос = Новый Запрос;
Запрос.УстановитьПараметр("МассивСсылок", МассивСсылок);
Запрос.Текст =
"ВЫБРАТЬ
| Док.Ссылка КАК Ссылка,
| Док.Номер КАК Номер,
| Док.Дата КАК Дата,
| Док.Организация КАК Организация,
| Док.Контрагент КАК Контрагент
|ИЗ
| Документ.РасходнаяНакладная КАК Док
|ГДЕ
| Док.Ссылка В (&МассивСсылок)
|
|УПОРЯДОЧИТЬ ПО
| Док.Дата,
| Док.Ссылка
|;
|
|ВЫБРАТЬ
| Товары.Ссылка КАК Ссылка,
| Товары.НомерСтроки КАК НомерСтроки,
| Товары.Номенклатура КАК Номенклатура,
| Товары.Количество КАК Количество,
| Товары.Цена КАК Цена,
| Товары.Сумма КАК Сумма
|ИЗ
| Документ.РасходнаяНакладная.Товары КАК Товары
|ГДЕ
| Товары.Ссылка В (&МассивСсылок)
|
|УПОРЯДОЧИТЬ ПО
| Товары.НомерСтроки";
Результаты = Запрос.ВыполнитьПакет();
ВыборкаШапок = Результаты[0].Выбрать();
ТаблицаТоваров = Результаты[1].Выгрузить();
ТаблицаТоваров.Индексы.Добавить("Ссылка");
Пока ВыборкаШапок.Следующий() Цикл
// Каждая следующая накладная - с нового листа.
Если ТабДок.ВысотаТаблицы > 0 Тогда
ТабДок.ВывестиГоризонтальныйРазделительСтраниц();
КонецЕсли;
СтрокиТоваров = ТаблицаТоваров.НайтиСтроки(Новый Структура("Ссылка", ВыборкаШапок.Ссылка));
ВывестиНакладную(ТабДок, Области, ВыборкаШапок, СтрокиТоваров);
КонецЦикла;
Возврат ТабДок;
КонецФункции
#КонецОбласти
#Область СлужебныеПроцедурыИФункции
// Выводит одну накладную. Перед каждой строкой проверяет, поместятся ли
// на текущий лист строка и итог по листу, а для последней строки - ещё и подвал.
//
Процедура ВывестиНакладную(ТабДок, Области, Шапка, СтрокиТоваров)
ДатаДокумента = Формат(Шапка.Дата, "ДЛФ=Д"); // в шаблон - строкой, без времени
Области.Шапка.Параметры.Заполнить(Шапка); // Номер, Организация, Контрагент
Области.Шапка.Параметры.ДатаДокумента = ДатаДокумента;
ТабДок.Вывести(Области.Шапка);
ТабДок.Вывести(Области.ШапкаТаблицы);
НомерЛиста = 1;
СтрокНаЛисте = 0;
КоличествоНаЛисте = 0;
СуммаНаЛисте = 0;
СуммаВсего = 0;
ПоследнийИндекс = СтрокиТоваров.ВГраница();
Для Индекс = 0 По ПоследнийИндекс Цикл
СтрокаТоваров = СтрокиТоваров[Индекс];
Области.Строка.Параметры.Заполнить(СтрокаТоваров); // до проверки: высота зависит от текста
Проверка = Новый Массив;
Проверка.Добавить(Области.Строка);
Проверка.Добавить(Области.ИтогоПоЛисту);
Если Индекс = ПоследнийИндекс Тогда
Проверка.Добавить(Области.Подвал); // подписи не останутся на листе одни
КонецЕсли;
Если СтрокНаЛисте > 0 И Не ТабДок.ПроверитьВывод(Проверка) Тогда
// Закрываем лист итогом и начинаем новый с повтором шапки таблицы.
ВывестиИтогоПоЛисту(ТабДок, Области.ИтогоПоЛисту, КоличествоНаЛисте, СуммаНаЛисте);
ТабДок.ВывестиГоризонтальныйРазделительСтраниц();
НомерЛиста = НомерЛиста + 1;
Области.Продолжение.Параметры.Номер = Шапка.Номер;
Области.Продолжение.Параметры.ДатаДокумента = ДатаДокумента;
Области.Продолжение.Параметры.НомерЛиста = НомерЛиста;
ТабДок.Вывести(Области.Продолжение);
ТабДок.Вывести(Области.ШапкаТаблицы);
СтрокНаЛисте = 0;
КоличествоНаЛисте = 0;
СуммаНаЛисте = 0;
КонецЕсли;
ТабДок.Вывести(Области.Строка);
СтрокНаЛисте = СтрокНаЛисте + 1;
КоличествоНаЛисте = КоличествоНаЛисте + СтрокаТоваров.Количество;
СуммаНаЛисте = СуммаНаЛисте + СтрокаТоваров.Сумма;
СуммаВсего = СуммаВсего + СтрокаТоваров.Сумма;
КонецЦикла;
ВывестиИтогоПоЛисту(ТабДок, Области.ИтогоПоЛисту, КоличествоНаЛисте, СуммаНаЛисте);
Области.Подвал.Параметры.ВсегоНаименований = СтрокиТоваров.Количество();
Области.Подвал.Параметры.СуммаВсего = СуммаВсего;
ТабДок.Вывести(Области.Подвал);
КонецПроцедуры
Процедура ВывестиИтогоПоЛисту(ТабДок, ОбластьИтого, Количество, Сумма)
ОбластьИтого.Параметры.КоличествоНаЛисте = Количество;
ОбластьИтого.Параметры.СуммаНаЛисте = Сумма;
ТабДок.Вывести(ОбластьИтого);
КонецПроцедуры
// Параметры листа и колонтитулы задаются результату:
// при выводе областей настройки листа самого макета не переносятся.
//
Процедура НастроитьЛист(ТабДок)
ТабДок.ОриентацияСтраницы = ОриентацияСтраницы.Портрет;
ТабДок.ПолеСлева = 10; // миллиметры
ТабДок.ПолеСправа = 10;
ТабДок.ПолеСверху = 10;
ТабДок.ПолеСнизу = 15;
ТабДок.АвтоМасштаб = Истина;
// Нумерация сквозная по всему табличному документу, то есть по всей пачке.
ТабДок.НижнийКолонтитул.Выводить = Истина;
ТабДок.НижнийКолонтитул.ТекстСправа = "Страница [&НомерСтраницы] из [&СтраницВсего]";
ТабДок.НижнийКолонтитул.Шрифт = Новый Шрифт("Arial", 8);
КонецПроцедуры
#КонецОбласти
#КонецЕсли
НастроитьЛист(ТабДок) — до первого Вывести(): ПроверитьВывод() считает место по ориентации, полям и масштабу на момент вызова. КлючПараметровПечати, если нужен, ставят последней строкой этой процедуры, чтобы в расчёт попали и сохранённые настройки пользователя.ТабДок.ВысотаТаблицы > 0 — разделитель только между накладными.ДатаДокумента присваивается строкой после Параметры.Заполнить(Шапка), который переносит номер, организацию и контрагента.Проверка — всё, что должно уместиться вместе: строка и итог по листу, для последней строки ещё и подвал.СтрокНаЛисте > 0 — защита от листа с одной шапкой таблицы и нулевым итогом.НомерЛиста > 1.На БСП меняются две вещи: макет берут через УправлениеПечатью.МакетПечатнойФормы(), а номер первой строки каждой накладной (ТабДок.ВысотаТаблицы + 1 перед выводом) передают в УправлениеПечатью.ЗадатьОбластьПечатиДокумента() — см. статью о печатной форме.
Колонки «Скидка» выделите по заголовкам и назначьте им имя Скидка, колонки левее — Товар, правее — Сумма. Строка собирается из пересечений с горизонтальной областью: первая часть выводится с новой строки, остальные присоединяются справа. Без скидок таблица станет уже на эту колонку.
// Модуль менеджера документа или общий серверный модуль.
// Колонка «Скидка» печатается, только если скидка есть хотя бы в одной строке.
// В макете: горизонтальные области ШапкаТаблицы и Строка,
// вертикальные области Товар, Скидка и Сумма - колонки слева направо.
//
// Параметры:
// ТабДок - ТабличныйДокумент - результат.
// Макет - ТабличныйДокумент - табличный макет формы.
// Товары - ТаблицаЗначений - колонки НомерСтроки, Номенклатура, Количество,
// Цена, СуммаСкидки, Сумма.
//
Процедура ВывестиТаблицуТоваров(ТабДок, Макет, Товары)
Колонки = Новый Массив;
Колонки.Добавить("Товар");
Если Товары.Итог("СуммаСкидки") <> 0 Тогда
Колонки.Добавить("Скидка");
КонецЕсли;
Колонки.Добавить("Сумма");
Кэш = Новый Соответствие; // каждую область получаем один раз, а не в каждой строке
ВывестиСтрокуИзЧастей(ТабДок, Макет, Кэш, "ШапкаТаблицы", Колонки, Неопределено);
Для Каждого СтрокаТоваров Из Товары Цикл
ВывестиСтрокуИзЧастей(ТабДок, Макет, Кэш, "Строка", Колонки, СтрокаТоваров);
КонецЦикла;
КонецПроцедуры
// Собирает строку из пересечений «горизонтальная|вертикальная»:
// первая часть выводится с новой строки, остальные присоединяются справа.
//
Процедура ВывестиСтрокуИзЧастей(ТабДок, Макет, Кэш, ИмяСтроки, Колонки, Данные)
Для Индекс = 0 По Колонки.ВГраница() Цикл
ИмяОбласти = ИмяСтроки + "|" + Колонки[Индекс];
Часть = Кэш[ИмяОбласти];
Если Часть = Неопределено Тогда
Часть = Макет.ПолучитьОбласть(ИмяОбласти);
Кэш.Вставить(ИмяОбласти, Часть);
КонецЕсли;
Если Данные <> Неопределено Тогда
Часть.Параметры.Заполнить(Данные);
КонецЕсли;
Если Индекс = 0 Тогда
ТабДок.Вывести(Часть);
Иначе
ТабДок.Присоединить(Часть);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Когда печать падает с «Поле объекта не обнаружено» или на бумаге выходит «[Номер]», посмотрите, что на самом деле размечено. Функция обходит именованные области, собирает имена параметров, включая шаблоны и расшифровку, и отмечает скобки в ячейках с заполнением «Текст».
// Общий модуль ПроверкаМакетов (флажок «Сервер») или модуль объекта внешней обработки.
// Описывает табличный макет: именованные области, их тип и имена параметров,
// включая параметры шаблонов и расшифровки, а также подозрительные ячейки.
// Пример вызова на сервере:
// Сообщить(ОписаниеМакета(Документы.РасходнаяНакладная.ПолучитьМакет("Накладная")));
// Платформа 8.3.6 и выше (СтрНайти, СтрШаблон, СтрСоединить).
//
// Параметры:
// Макет - ТабличныйДокумент - табличный макет.
//
// Возвращаемое значение:
// Строка - по строке на область, замечания - с отступом.
//
Функция ОписаниеМакета(Макет) Экспорт
Строки = Новый Массив;
Для Каждого ИменованнаяОбласть Из Макет.Области Цикл
Если ТипЗнч(ИменованнаяОбласть) <> Тип("ОбластьЯчеекТабличногоДокумента") Тогда
Продолжить; // именованные рисунки пропускаем
КонецЕсли;
Часть = Макет.ПолучитьОбласть(ИменованнаяОбласть.Имя);
ИменаПараметров = Новый Массив;
Замечания = Новый Массив;
Для НомерСтроки = 1 По Часть.ВысотаТаблицы Цикл
Для НомерКолонки = 1 По Часть.ШиринаТаблицы Цикл
Ячейка = Часть.Область(НомерСтроки, НомерКолонки, НомерСтроки, НомерКолонки);
Адрес = "R" + Формат(НомерСтроки, "ЧГ=0") + "C" + Формат(НомерКолонки, "ЧГ=0");
ПроверитьЯчейку(Ячейка, Адрес, ИменаПараметров, Замечания);
КонецЦикла;
КонецЦикла;
Строки.Добавить(СтрШаблон("%1 (%2): %3", ИменованнаяОбласть.Имя,
ИменованнаяОбласть.ТипОбласти, СтрСоединить(ИменаПараметров, ", ")));
Для Каждого Замечание Из Замечания Цикл
Строки.Добавить(" " + Замечание);
КонецЦикла;
КонецЦикла;
Возврат СтрСоединить(Строки, Символы.ПС);
КонецФункции
Процедура ПроверитьЯчейку(Ячейка, Адрес, ИменаПараметров, Замечания)
Если Ячейка.Заполнение = ТипЗаполненияОбластиТабличногоДокумента.Параметр Тогда
Если ПустаяСтрока(Ячейка.Параметр) Тогда
Замечания.Добавить(Адрес + ": заполнение «Параметр», но имя параметра не указано");
Иначе
ДобавитьИмя(ИменаПараметров, Ячейка.Параметр);
КонецЕсли;
ИначеЕсли Ячейка.Заполнение = ТипЗаполненияОбластиТабличногоДокумента.Шаблон Тогда
ДобавитьПараметрыШаблона(ИменаПараметров, Ячейка.Текст);
ИначеЕсли СтрНайти(Ячейка.Текст, "[") > 0 И СтрНайти(Ячейка.Текст, "]") > 0 Тогда
Замечания.Добавить(Адрес + ": скобки [ ] при заполнении «Текст» напечатаются как есть");
КонецЕсли;
Если Не ПустаяСтрока(Ячейка.ПараметрРасшифровки) Тогда
ДобавитьИмя(ИменаПараметров, Ячейка.ПараметрРасшифровки);
КонецЕсли;
КонецПроцедуры
// Добавляет имена параметров из шаблона вида «Счёт № [Номер] от [Дата]».
Процедура ДобавитьПараметрыШаблона(ИменаПараметров, ТекстШаблона)
Начало = СтрНайти(ТекстШаблона, "[");
Пока Начало > 0 Цикл
Конец = СтрНайти(ТекстШаблона, "]", , Начало);
Если Конец = 0 Тогда
Прервать;
КонецЕсли;
ДобавитьИмя(ИменаПараметров, Сред(ТекстШаблона, Начало + 1, Конец - Начало - 1));
Начало = СтрНайти(ТекстШаблона, "[", , Конец);
КонецЦикла;
КонецПроцедуры
Процедура ДобавитьИмя(ИменаПараметров, Знач Имя)
Имя = СокрЛП(Имя);
Если Не ПустаяСтрока(Имя) И ИменаПараметров.Найти(Имя) = Неопределено Тогда
ИменаПараметров.Добавить(Имя);
КонецЕсли;
КонецПроцедуры
Параметры.Заполнить() такие имена пропускает молча, прямое присваивание падает."Строка|Колонка".Формат().ПовторятьПриПечатиСтрок, и шапка выводится вручную после разрыва.ПроверитьВывод() перед последней строкой.Да, свойством ПовторятьПриПечатиСтрок. Но область повтора одна на документ: в пачке шапка таблицы первой накладной напечатается и над шапкой второй, а итог по листу так не вывести.
Задайте колонтитулу НачальнаяСтраница = 2. В пачке это сработает только для первого листа всего документа: колонтитул не знает, где кончается одна накладная.
Счётчиком в коде, как НомерЛиста в области «Продолжение»: колонтитул нумерует страницы всего документа. Число листов накладной заранее неизвестно, поэтому печатают «лист N» без «из M».
Разрывы из ВывестиГоризонтальныйРазделительСтраниц() рассчитаны на параметры листа при формировании и не двигаются. Шире поля — короче лист, и платформа добавит свои разрывы посреди «листа» из кода. Для другого формата форму формируют заново.
Перенос таблицы с итогами, пачка документов и колонки по условию — рутина для программиста, который регулярно делает печатные формы. Если своего специалиста нет или бланк нужно встроить в типовую без снятия с поддержки, пришлите образец бланка и опишите, откуда брать данные: задачу можно разместить бесплатно, оценка тоже бесплатная, ответ — в течение рабочего дня. Подробнее — на странице доработки печатных форм.
Станьте частью сообщества!
Войдите или зарегистрируйтесь, и вы сможете участвовать в обсуждениях.