Шаблоны, поля и форматирование

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

Используйте класс Bitrix\DocumentGenerator\Template, чтобы загрузить шаблон и получить его тело, поля и связанные провайдеры. Для создания документа передайте подготовленный объект шаблона в метод Document::createByTemplate(), как показано в статье Документы.

Модель шаблона

Объект Template хранит название, код, регион, идентификаторы модуля и исходного файла, класс тела и настройки полей. Основные свойства шаблона перечислены в таблице. Исходный файл хранится отдельно от остальных данных шаблона.

Обязательные свойства обозначены *.

Свойство

Тип

Назначение

ID

int

Идентификатор шаблона

NAME*

string

Название шаблона длиной от 1 до 100 символов

CODE

string

Символьный код шаблона

MODULE_ID*

string

Идентификатор модуля, который зарегистрировал шаблон

FILE_ID*

int

Идентификатор исходного файла в хранилище модуля

BODY_TYPE*

class-string<Body>

Класс тела документа. Если загруженное значение отсутствует или не наследует Body, объект Template использует Body\Docx

REGION

string

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

WITH_STAMPS

string

Управляет обработкой полей STAMP.

  • Y — включить подписи и печати,
  • N — не добавлять подписи и печати.

Значение по умолчанию — N

PRODUCTS_TABLE_VARIANT

string

Вариант табличной части: все позиции, только товары или только услуги. Значение по умолчанию — пустая строка

Класс Body разбирает содержимое конкретного формата. Класс Body\Docx выполняет несколько действий:

  • обрабатывает основной документ, колонтитулы и другие части DOCX,

  • извлекает плейсхолдеры,

  • заменяет значения и изображения.

Получить сохраненный шаблон и его поля

Чтобы получить объект шаблона и его поля, сначала сохраните DOCX-шаблон в модуле генератора документов. Например, для CRM-шаблона откройте раздел CRM > Настройки > Настройки CRM > Другое > Шаблоны документов или перейдите по адресу /crm/documents/templates/. Нажмите Добавить шаблон, загрузите DOCX и выберите разделы CRM, в которых он будет доступен. После сохранения идентификатор шаблона появится в столбце ID списка шаблонов.

Шаблон также можно добавить через REST API методом documentgenerator.template.add. Такой шаблон относится к REST-приложению и использует REST-провайдер.

Чтобы получить сохраненный шаблон по идентификатору в коде, подключите модуль documentgenerator и вызовите метод Template::loadById(). Метод возвращает объект Template или null, если запись не найдена или идентификатор некорректен. Повторно загружать DOCX-файл не нужно.

Если поля зависят от основного провайдера, передайте его класс в setSourceType() до первого вызова getFields(). Объект шаблона кеширует рассчитанный список полей в памяти.

<?php

use Bitrix\DocumentGenerator\DataProvider;
use Bitrix\DocumentGenerator\Template;
use Bitrix\Main\Loader;

if (!Loader::includeModule('documentgenerator'))
{
    throw new \RuntimeException('Модуль documentgenerator не установлен');
}

$templateId = 17;
/** @var class-string<DataProvider> $providerClass */
// Замените классом провайдера из вашего модуля
$providerClass = Acme\Documents\OrderDataProvider::class;

$template = Template::loadById($templateId);
if ($template === null)
{
    throw new \RuntimeException('Шаблон не найден');
}

$template->setSourceType($providerClass);
if ($template->getSourceType() === null)
{
    throw new \RuntimeException('Класс провайдера недоступен для шаблона');
}

$body = $template->getBody();
if ($body === null)
{
    throw new \RuntimeException('Не удалось получить тело шаблона');
}

$placeholders = $body->getPlaceholders();
$fields = $template->getFields();

После выполнения кода переменные содержат:

  • $template — сохраненный шаблон по его идентификатору.

  • $body — объект, который представляет содержимое DOCX.

  • $placeholders — коды плейсхолдеров, найденные в теле DOCX.

  • $fields — описания доступных полей основного и служебного провайдеров.

В $fields входят не только поля, которые использованы в DOCX, но и все поля, доступные для выбранного провайдера. Массив также содержит служебные поля DOCUMENT и SOURCE.

Выбрать основной провайдер

У шаблона может быть несколько доступных провайдеров. Метод getDataProviders() возвращает их классы:

$providerClasses = $template->getDataProviders(true);

При первом вызове передайте true, чтобы метод преобразовал сохраненные фильтры в имена классов. Метод сохраняет результат в объекте Template. Поэтому вызовите getDataProviders(true) раньше getDataProviders() без аргумента.

Выберите из полученного списка класс, который соответствует исходному объекту документа. Затем выполните два действия:

  1. Передайте класс в метод setSourceType() до вызова getFields().

  2. Проверьте результат через getSourceType(). Если класс не зарегистрирован в DataProviderManager, метод вернет null.

Выбранный класс становится основным провайдером текущего документа. Через поле SOURCE документ получает данные исходного объекта. Служебный провайдер DOCUMENT использует тот же источник.

Связанные провайдеры хранятся в настройках шаблона. Не добавляйте эту связь напрямую через Model\TemplateProviderTable. Настраивайте область применения шаблона через штатный интерфейс модуля, а структуру собственного провайдера формируйте по правилам статьи Провайдеры данных.

Устройство полей шаблона

Ключ массива, который возвращает Template::getFields(), совпадает с кодом плейсхолдера. Описание поля определяет источник, тип представления и поведение при пустом значении.

Ключ

Тип

Назначение

TITLE

string

Понятное пользователю название поля

VALUE

mixed

Путь к значению в дереве провайдеров, скалярное значение или обработчик, который возвращает значение

TYPE

string или class-string<Value>

Один из типов IMAGE, STAMP, DATE, TEXT, NAME, PHONE или класс-наследник Value, который подготовит значение

PROVIDER

class-string<DataProvider>

Класс вложенного провайдера

PROVIDER_NAME

string

Имя класса провайдера, которое модуль проверяет перед использованием

REQUIRED

string

Признак обязательного значения: Y или N. Для сохраненной настройки значение по умолчанию — N

HIDE_ROW

string

Признак удаления строки таблицы или абзаца: Y или N. Для сохраненной настройки значение по умолчанию — N

OPTIONS

array

Дополнительные настройки поля и провайдера. Состав зависит от типа поля

Шаблон выбирает описание каждого плейсхолдера по приоритету:

  1. Настройка для конкретного шаблона.

  2. Настройка для основного провайдера шаблона.

  3. Общая настройка без шаблона и провайдера.

  4. Описание поля, которое DataProviderManager получил из основного или служебного провайдера.

Метод getFields() всегда добавляет два служебных корневых поля: SOURCE и DOCUMENT.

  • SOURCE открывает данные исходного объекта.

  • DOCUMENT открывает дату создания, название и номер документа, а также исходный объект через вложенное поле SOURCE.

Не сохраняйте настройки полей напрямую через Model\FieldTable. Этот ORM-класс обслуживает внутреннее хранение. Формируйте структуру полей в провайдере или используйте интерфейс настройки шаблона.

Добавить плейсхолдеры в DOCX

Добавьте текстовые плейсхолдеры в DOCX самостоятельно и заключите каждый код в фигурные скобки. Код может содержать латинские буквы, цифры, точки, символы _ и -.

{DOCUMENT.DOCUMENT_NUMBER}
{DOCUMENT.DOCUMENT_CREATE_TIME}
{SOURCE.CLIENT.NAME}

Точка разделяет уровни провайдеров. Например, путь SOURCE.CLIENT.NAME последовательно обращается к полю CLIENT основного провайдера и полю NAME вложенного провайдера.

Word может сохранить один плейсхолдер несколькими фрагментами внутри DOCX. При загрузке шаблона модуль объединяет такие фрагменты. Если поле не определяется, заново введите весь плейсхолдер одним стилем и повторно загрузите файл.

Во время обработки модуль заменяет неизвестный плейсхолдер пустой строкой. До генерации проверьте, что для каждого кода из $body->getPlaceholders() есть ключ с таким же именем в $template->getFields(). Отсутствующий ключ указывает на опечатку в плейсхолдере или на недоступное поле провайдера.

Форматировать значения

Тип поля определяет класс, который превращает исходное значение в строку или специальный объект для тела документа. Модификатор после символа ~ изменяет формат только в текущем месте шаблона.

{DOCUMENT.DOCUMENT_CREATE_TIME~format=DD.MM.YYYY}
{SOURCE.CLIENT.NAME~letterCase=upper}

Правила записи параметров:

  • Разделяйте несколько параметров запятыми.

  • Используйте Y и N для логических значений.

  • Учитывайте, что доступный набор параметров зависит от класса Value.

  • Сохраняйте регистр в именах параметров. Например, используйте CurId, а не curid.

Регистр итоговой строки

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

Модификатор letterCase поддерживает три значения:

Значение

Результат

lower

Нижний регистр

upper

Верхний регистр

title

Верхний регистр для первых букв слов

Массив или объект без класса Value и без строкового представления нельзя вставить как обычный текст. Опишите для такого поля подходящий тип или вложенный провайдер.

Дата и время

Тип DATE использует Value\DateTime. По умолчанию класс берет формат даты из региональных настроек текущего контекста генерации. Параметр format переопределяет его для одного плейсхолдера.

{DOCUMENT.DOCUMENT_CREATE_TIME~format=DD.MM.YYYY}

Класс принимает объект Bitrix\Main\Type\Date, дату и время или строку, которую может разобрать Framework. Если строку преобразовать не удалось, результат будет пустым.

Имя

Тип NAME использует Value\Name. Исходное значение должно быть массивом с частями имени: TITLE, NAME, SECOND_NAME, LAST_NAME и при необходимости GENDER.

Параметр format задает порядок частей имени. Без него модуль использует формат имен из региональных настроек. В строке формата доступны подстановки:

  • #TITLE# — обращение,

  • #NAME# — имя,

  • #LAST_NAME# — фамилия,

  • #SECOND_NAME# — отчество,

  • #NAME_SHORT# — первая буква имени и точка,

  • #LAST_NAME_SHORT# — первая буква фамилии и точка,

  • #SECOND_NAME_SHORT# — первая буква отчества и точка.

Параметр case изменяет падеж русскоязычного имени:

Значение

Падеж

-1

Именительный

0

Родительный

1

Дательный

2

Винительный

3

Творительный

4

Предложный

Класс изменяет падеж, если непустые части имени содержат только русские буквы, пробелы и дефисы. Пол задает элемент GENDER со значением F или M. Если элемент не задан, класс пытается определить пол по отчеству. Когда пол определить нельзя, части имени остаются в исходном падеже.

{SOURCE.CONTACT.NAME~format=#LAST_NAME# #NAME_SHORT# #SECOND_NAME_SHORT#,case=1}

Имена параметров Format и Case с заглавной буквы работают как псевдонимы для format и case.

Телефон

Тип PHONE использует Value\PhoneNumber. Класс разбирает исходную строку через Bitrix\Main\PhoneNumber\Parser и по умолчанию возвращает номер в международном формате.

Параметр format принимает одно из значений класса Bitrix\Main\PhoneNumber\Format:

  • E.164 — формат E.164, например +74951234567.

  • International — международный формат с разделителями.

  • National — национальный формат.

Значение по умолчанию — International.

Множественные значения

Для списка простых значений DataProviderManager создает Value\Multiple. По умолчанию класс объединяет непустые элементы через запятую и пробел.

Модификатор

Значение

Результат

mseparator

1

Разделяет элементы запятой и пробелом

mseparator

2

Разделяет элементы переводом строки

mfirst

Y

Выводит только первый непустой элемент

{SOURCE.CONTACT.PHONES~mseparator=2}
{SOURCE.CONTACT.EMAILS~mfirst=Y}

Остальные модификаторы применяются к каждому элементу списка. Массивы и объекты без подходящего типа класс пропускает.

Элемент списка провайдеров

Если часть пути возвращает ArrayDataProvider, модуль по умолчанию берет поле первого элемента. Параметр index выбирает элемент по индексу. Нумерация начинается с нуля.

{SOURCE.PRODUCTS.NAME~index=0}
{SOURCE.PRODUCTS.NAME~index=1}

Первый плейсхолдер выведет название первого товара, второй — второго. Если элемента с заданным индексом нет, модуль подставит пустую строку.

Адрес CRM

Поля класса Bitrix\Crm\Integration\DocumentGenerator\Value\Address поддерживают модификаторы формата, разделителя и типа адреса. Модификаторы влияют на значение в виде массива. Строковый адрес класс возвращает без изменений.

Модификатор

Значение

Результат

Format

1

Европейский формат EU

Format

2

Формат Великобритании UK

Format

3

Формат США USA

Format

4

Российский формат RUS

Format

5

Российский формат RUS2

Format

6

Формат Узбекистана UZ

Separator

1

Запятая и пробел

Separator

2

Перевод строки

Separator

3

HTML-разделитель <br />

ShowType

Y

Добавляет тип адреса в скобках, если он заполнен

Без модификатора Format класс выбирает формат по региональным настройкам контекста генерации. Разделитель по умолчанию — запятая и пробел.

{SOURCE.CLIENT.ADDRESS~Format=4,Separator=2,ShowType=Y}

Деньги CRM

Поля класса Bitrix\Crm\Integration\DocumentGenerator\Value\Money по умолчанию используют базовую валюту CRM.

Модификатор

Значение

Результат

CurId

Код валюты

Форматирует сумму в выбранной валюте

WZ

Y

Сохраняет незначащие нули дробной части

NS

Y

Не добавляет обозначение валюты

W

Y

Преобразует сумму в слова, если доступна функция Number2Word_Rus

Если преобразование в слова недоступно или вернуло пустой результат, класс форматирует сумму числом.

{SOURCE.OPPORTUNITY~WZ=Y,NS=Y}
{SOURCE.OPPORTUNITY~CurId=RUB,W=Y}

Обработать пустые и обязательные поля

Результат для пустого значения зависит от настройки и типа поля.

Условие

Результат

Плейсхолдер отсутствует в рассчитанных значениях

Body заменяет его пустой строкой

Значение текстового плейсхолдера равно null или пустой строке

В документ попадает пустая строка

Поле имеет REQUIRED=Y и возвращает пустое значение

Document::checkFields() включает поле в результат проверки, а обработка документа добавляет ошибку No value for required placeholder ...

Множественное поле не содержит подходящих элементов

Value\Multiple возвращает пустую строку

Поле типа IMAGE или STAMP содержит пустое значение либо один пробел

Body\Docx удаляет изображение-заглушку

Изображение, печать или множественное поле содержит null либо пустую строку и имеет HIDE_ROW=Y

Body\Docx удаляет строку таблицы или ближайший абзац

Проверьте обязательные значения без сохранения файла. Передайте в Document::createByTemplate() идентификатор или объект того типа, который ожидает основной провайдер.

use Bitrix\DocumentGenerator\Document;

$sourceId = 25;
// Замените идентификатором или объектом того типа, который ожидает провайдер
$document = Document::createByTemplate($template, $sourceId);
if ($document === null)
{
    throw new \RuntimeException('Не удалось создать объект документа');
}

$emptyRequiredFields = $document->checkFields();
foreach ($emptyRequiredFields as $placeholder => $field)
{
    $title = $field['TITLE'] ?? $placeholder;
    echo $placeholder . ': ' . $title . PHP_EOL;
}

Пустой массив $emptyRequiredFields означает, что метод не нашел пропущенных обязательных значений. После проверки выполните пробную генерацию. Порядок сохранения файла и обработки объекта Result описан в разделе «Создать и сохранить документ» статьи Документы.

Повторить блок для списка объектов

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

{SOURCE.PRODUCTS.BLOCK_START}
{SOURCE.PRODUCTS.NAME} — {SOURCE.PRODUCTS.PRICE}
{SOURCE.PRODUCTS.BLOCK_END}

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

Размещайте начало и конец блока не дальше 20 соседних абзацев или других XML-узлов друг от друга. Если конец не найден в этом диапазоне, модуль не создает повторяемый блок.

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

Не вкладывайте блок одного множественного провайдера в другой без отдельной проверки результата. Порядок обхода и структура XML могут дать лишние или потерянные строки.

Вставить изображение или печать

Для типов IMAGE и STAMP одного текстового плейсхолдера недостаточно. Добавьте в DOCX изображение-заглушку и запишите плейсхолдер в альтернативный текст или имя этого изображения. Класс Body\Docx найдет такую фигуру и заменит связанный файл, сохранив размещение изображения в документе.

Если значение изображения пустое, модуль удаляет фигуру из результата. Настройка поля HIDE_ROW=Y дополнительно удаляет строку таблицы, а если строки нет — ближайший абзац.

Поле STAMP обрабатывается как изображение, но зависит от настройки шаблона WITH_STAMPS. При значении N документ передает для всех полей STAMP внутренний маркер — строку из одного пробела. Класс Body\Docx удаляет изображение с таким значением, но HIDE_ROW не удаляет содержащую его строку или абзац. При значении Y модуль использует данные провайдера.

Учесть региональные настройки

Свойство шаблона REGION участвует в создании контекста генерации. Класс DataProviderManager выбирает по нему язык региональных фраз и настройки форматов дат и имен.

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

Перед генерацией задайте регион в настройках шаблона. Добавляйте модификатор поля только тогда, когда его формат должен отличаться от регионального.

Выбрать вариант таблицы товаров

Настройка PRODUCTS_TABLE_VARIANT передается из шаблона в создаваемый документ. Она принимает одно из трех значений:

Значение

Результат

Пустая строка

Все позиции

Dictionary\ProductVariant::GOODS

Только товары

Dictionary\ProductVariant::SERVICE

Только услуги

Настройка влияет на данные, которые предоставляет интеграционный провайдер товаров. Сам Body\Docx не определяет, является ли позиция товаром или услугой. Если собственный провайдер не учитывает вариант, фильтрацию нужно реализовать в нем.

Проверить шаблон перед генерацией

Проверьте шаблон перед созданием документа с помощью Document::createByTemplate().

  1. Проверьте права пользователя на настройку шаблона и чтение исходного объекта: метод Template::loadById() эти права не проверяет. Подробнее читайте в статье Права доступа.

  2. Убедитесь, что Template::loadById() вернул объект.

  3. Проверьте, что getSourceType() возвращает ожидаемый класс основного провайдера.

  4. Убедитесь, что getBody() вернул объект, а тело поддерживает обработку файла.

  5. Проверьте, что каждый код из getPlaceholders() имеет описание в getFields().

  6. Убедитесь, что обязательные поля имеют источник данных.

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

  8. Убедитесь, что маркеры повторяемого блока имеют одинаковый префикс и расположены рядом.

  9. Проверьте, что регион и вариант таблицы товаров соответствуют сценарию документа.

  10. Создайте пробный документ и обработайте ошибки объекта Result, которые могут возникнуть при проверке обязательных значений и обработке DOCX. Подробнее об этом читайте в статье Документы.

Связанные материалы