Сгенерировать документ со сложными таблицами
Выберите инструмент для разработки с AI-агентом:
- используйте Битрикс24 Вайбкод, чтобы создать приложение для Битрикс24 по описанию задачи без знания языков программирования. Агент напишет код и разместит приложение на сервере без ручной настройки хостинга
- используйте MCP-сервер, чтобы разрабатывать интеграцию через REST API в своем проекте. Агент будет обращаться к официальной REST-документации
Scope:
documentgeneratorКто может выполнять метод: пользователь с правом на создание документов
В REST генератора документов можно заполнять плейсхолдеры шаблона вложенными списками, когда одно из значений списка первого уровня содержит еще один список. Для создания документа используется метод documentgenerator.document.add.
Когда использовать
- Нужно вставить несколько таблиц одинаковой структуры, но с разным числом строк
- Нужно построить таблицу внутри повторяющегося блока
В таких сценариях внешний список задает структуру, а внутренние списки передаются отдельными полями и связываются через цепочки вида Events.Event.Title и Event1Speakers.Speaker.Name.
Что передавать в запросе
- Обязательные параметры запроса —
templateIdиvalue: идентификатор шаблона и внешний идентификатор объекта, для которого создается документ - В
valuesпередайте внешний список, напримерEvents - В элементах внешнего списка передайте плейсхолдеры для связанных внутренних списков, например
{Event1SpeakersSpeakerName} - Для полей внешнего списка передайте цепочки доступа вида
Events.Event.Title - Для каждого внутреннего списка передайте отдельный массив в
values, напримерEvent1Speakers - Для полей внутренних списков передайте цепочки доступа вида
Event1Speakers.Speaker.Name - В
fieldsопишите внешний и внутренние списки черезArrayDataProvider - В
OPTIONSкаждого списка укажитеITEM_NAMEиITEM_PROVIDER=Bitrix\DocumentGenerator\DataProvider\HashDataProvider
На что обратить внимание
- В
fieldsвнешний список должен идти раньше связанных внутренних списков, чтобы провайдеры разрешались в нужном порядке - Коды внутренних списков должны совпадать с плейсхолдерами, которые передаются во внешнем списке
Пример
В примере создается документ с блоком мероприятий, внутри каждого мероприятия — своя таблица спикеров.
Как использовать примеры в документации
curl -X POST \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"templateId":203,"value":"EVENTS_2026","values":{"Title":"Welcome to my template","Description":"<b>Description is here</b>","Events":[{"Title":"Automation","Description":"Some description of the automation event","SpeakerName":"{Event1SpeakersSpeakerName}","SpeakerCompany":"{Event1SpeakersSpeakerCompany}","SpeakerPosition":"{Event1SpeakersSpeakerPosition}"},{"Title":"Documents","Description":"This event is about document processing","SpeakerName":"{Event2SpeakersSpeakerName}","SpeakerCompany":"{Event2SpeakersSpeakerCompany}","SpeakerPosition":"{Event2SpeakersSpeakerPosition}"}],"EventsEventTitle":"Events.Event.Title","EventsEventDescription":"Events.Event.Description","EventsEventSpeakerName":"Events.Event.SpeakerName","EventsEventSpeakerCompany":"Events.Event.SpeakerCompany","EventsEventSpeakerPosition":"Events.Event.SpeakerPosition","Event1Speakers":[{"Name":"Ivan Petrov","Company":"Cool Ltd.","Position":"Core developer"},{"Name":"Igor Milov","Company":"Cool Ltd.","Position":"Product Manager"}],"Event1SpeakersSpeakerName":"Event1Speakers.Speaker.Name","Event1SpeakersSpeakerCompany":"Event1Speakers.Speaker.Company","Event1SpeakersSpeakerPosition":"Event1Speakers.Speaker.Position","Event2Speakers":[{"Name":"Sergey Ivanov","Company":"Devils corp.","Position":"Chief"}],"Event2SpeakersSpeakerName":"Event2Speakers.Speaker.Name","Event2SpeakersSpeakerCompany":"Event2Speakers.Speaker.Company","Event2SpeakersSpeakerPosition":"Event2Speakers.Speaker.Position"},"fields":{"Events":{"PROVIDER":"Bitrix\\DocumentGenerator\\DataProvider\\ArrayDataProvider","OPTIONS":{"ITEM_NAME":"Event","ITEM_PROVIDER":"Bitrix\\DocumentGenerator\\DataProvider\\HashDataProvider"}},"Event1Speakers":{"PROVIDER":"Bitrix\\DocumentGenerator\\DataProvider\\ArrayDataProvider","OPTIONS":{"ITEM_NAME":"Speaker","ITEM_PROVIDER":"Bitrix\\DocumentGenerator\\DataProvider\\HashDataProvider"}},"Event2Speakers":{"PROVIDER":"Bitrix\\DocumentGenerator\\DataProvider\\ArrayDataProvider","OPTIONS":{"ITEM_NAME":"Speaker","ITEM_PROVIDER":"Bitrix\\DocumentGenerator\\DataProvider\\HashDataProvider"}}}}' \
https://**put_your_bitrix24_address**/rest/**put_your_user_id_here**/**put_your_webhook_here**/documentgenerator.document.add
// This snippet is an ES module: top-level await requires type="module" or a bundler.
// $b24 is an already-initialized SDK instance (see the SDK "Get started" guide).
import { Text } from '@bitrix24/b24jssdk'
import type { B24Frame } from '@bitrix24/b24jssdk'
declare const $b24: B24Frame
type DocumentAddResult = {
document: {
id: number
downloadUrl: string
}
}
const arrayProvider = 'Bitrix\\DocumentGenerator\\DataProvider\\ArrayDataProvider'
const hashProvider = 'Bitrix\\DocumentGenerator\\DataProvider\\HashDataProvider'
const response = await $b24.actions.v2.call.make<DocumentAddResult>({
method: 'documentgenerator.document.add',
params: {
templateId: 203,
value: 'EVENTS_2026',
values: {
Title: 'Welcome to my template',
Description: '<b>Description is here</b>',
// the outer list: each item carries placeholders of its own speakers table
Events: [
{
Title: 'Automation',
Description: 'Some description of the automation event',
SpeakerName: '{Event1SpeakersSpeakerName}',
SpeakerCompany: '{Event1SpeakersSpeakerCompany}',
SpeakerPosition: '{Event1SpeakersSpeakerPosition}',
},
{
Title: 'Documents',
Description: 'This event is about document processing',
SpeakerName: '{Event2SpeakersSpeakerName}',
SpeakerCompany: '{Event2SpeakersSpeakerCompany}',
SpeakerPosition: '{Event2SpeakersSpeakerPosition}',
},
],
EventsEventTitle: 'Events.Event.Title',
EventsEventDescription: 'Events.Event.Description',
EventsEventSpeakerName: 'Events.Event.SpeakerName',
EventsEventSpeakerCompany: 'Events.Event.SpeakerCompany',
EventsEventSpeakerPosition: 'Events.Event.SpeakerPosition',
// the inner lists are passed as separate fields
Event1Speakers: [
{ Name: 'Ivan Petrov', Company: 'Cool Ltd.', Position: 'Core developer' },
{ Name: 'Igor Milov', Company: 'Cool Ltd.', Position: 'Product Manager' },
],
Event1SpeakersSpeakerName: 'Event1Speakers.Speaker.Name',
Event1SpeakersSpeakerCompany: 'Event1Speakers.Speaker.Company',
Event1SpeakersSpeakerPosition: 'Event1Speakers.Speaker.Position',
Event2Speakers: [
{ Name: 'Sergey Ivanov', Company: 'Devils corp.', Position: 'Chief' },
],
Event2SpeakersSpeakerName: 'Event2Speakers.Speaker.Name',
Event2SpeakersSpeakerCompany: 'Event2Speakers.Speaker.Company',
Event2SpeakersSpeakerPosition: 'Event2Speakers.Speaker.Position',
},
fields: {
Events: {
PROVIDER: arrayProvider,
OPTIONS: { ITEM_NAME: 'Event', ITEM_PROVIDER: hashProvider },
},
Event1Speakers: {
PROVIDER: arrayProvider,
OPTIONS: { ITEM_NAME: 'Speaker', ITEM_PROVIDER: hashProvider },
},
Event2Speakers: {
PROVIDER: arrayProvider,
OPTIONS: { ITEM_NAME: 'Speaker', ITEM_PROVIDER: hashProvider },
},
},
},
requestId: Text.getUuidRfc4122()
})
if (!response.isSuccess) {
console.error(response.getErrorMessages().join('; '))
} else {
console.info('Created document id:', response.getData()!.result.document.id)
}
try {
$arrayProvider = 'Bitrix\\DocumentGenerator\\DataProvider\\ArrayDataProvider';
$hashProvider = 'Bitrix\\DocumentGenerator\\DataProvider\\HashDataProvider';
$response = $b24Service->core->call(
'documentgenerator.document.add',
[
'templateId' => 203,
'value' => 'EVENTS_2026',
'values' => [
'Title' => 'Welcome to my template',
'Description' => '<b>Description is here</b>',
// внешний список: каждый элемент несет плейсхолдеры своей таблицы спикеров
'Events' => [
[
'Title' => 'Automation',
'Description' => 'Some description of the automation event',
'SpeakerName' => '{Event1SpeakersSpeakerName}',
'SpeakerCompany' => '{Event1SpeakersSpeakerCompany}',
'SpeakerPosition' => '{Event1SpeakersSpeakerPosition}',
],
[
'Title' => 'Documents',
'Description' => 'This event is about document processing',
'SpeakerName' => '{Event2SpeakersSpeakerName}',
'SpeakerCompany' => '{Event2SpeakersSpeakerCompany}',
'SpeakerPosition' => '{Event2SpeakersSpeakerPosition}',
],
],
'EventsEventTitle' => 'Events.Event.Title',
'EventsEventDescription' => 'Events.Event.Description',
'EventsEventSpeakerName' => 'Events.Event.SpeakerName',
'EventsEventSpeakerCompany' => 'Events.Event.SpeakerCompany',
'EventsEventSpeakerPosition' => 'Events.Event.SpeakerPosition',
// внутренние списки передаются отдельными полями
'Event1Speakers' => [
[
'Name' => 'Ivan Petrov',
'Company' => 'Cool Ltd.',
'Position' => 'Core developer',
],
[
'Name' => 'Igor Milov',
'Company' => 'Cool Ltd.',
'Position' => 'Product Manager',
],
],
'Event1SpeakersSpeakerName' => 'Event1Speakers.Speaker.Name',
'Event1SpeakersSpeakerCompany' => 'Event1Speakers.Speaker.Company',
'Event1SpeakersSpeakerPosition' => 'Event1Speakers.Speaker.Position',
'Event2Speakers' => [
[
'Name' => 'Sergey Ivanov',
'Company' => 'Devils corp.',
'Position' => 'Chief',
],
],
'Event2SpeakersSpeakerName' => 'Event2Speakers.Speaker.Name',
'Event2SpeakersSpeakerCompany' => 'Event2Speakers.Speaker.Company',
'Event2SpeakersSpeakerPosition' => 'Event2Speakers.Speaker.Position',
],
'fields' => [
'Events' => [
'PROVIDER' => $arrayProvider,
'OPTIONS' => [
'ITEM_NAME' => 'Event',
'ITEM_PROVIDER' => $hashProvider,
],
],
'Event1Speakers' => [
'PROVIDER' => $arrayProvider,
'OPTIONS' => [
'ITEM_NAME' => 'Speaker',
'ITEM_PROVIDER' => $hashProvider,
],
],
'Event2Speakers' => [
'PROVIDER' => $arrayProvider,
'OPTIONS' => [
'ITEM_NAME' => 'Speaker',
'ITEM_PROVIDER' => $hashProvider,
],
],
],
]
);
$result = $response->getResponseData()->getResult();
print_r($result);
} catch (Throwable $e) {
echo $e->getMessage();
}
Что вернется
Метод возвращает данные созданного документа. Пример ответа сокращен, полное описание полей — на странице метода documentgenerator.document.add.
{
"result": {
"document": {
"id": 51,
"title": "EVENTS Template 51",
"templateId": "203",
"value": "EVENTS_2026",
"isTransformationError": false,
"downloadUrl": "/bitrix/services/main/ajax.php?action=documentgenerator.api.document.getfile&SITE_ID=s1&id=51&ts=1773844068"
}
}
}
Ответ подтверждает только создание документа. Развернутые вложенные списки в нем не возвращаются, поэтому результат подстановки проверяйте в самом файле.
Проверим результат
- Скачайте файл по
downloadUrlиз ответа - Убедитесь, что повторяющийся блок построен по числу элементов внешнего списка
Events - Проверьте, что внутри каждого блока таблица спикеров содержит строки своего внутреннего списка, а не общий набор
Если метод вернул ошибку
Empty required parameter "value"— не передан обязательный параметрvalueШаблон не найден— шаблона с указаннымtemplateIdне существует
Во всех блоках выводится один и тот же список спикеров — коды внутренних списков не совпали с плейсхолдерами, которые переданы в элементах внешнего списка.
Внутренние таблицы остались пустыми — в fields внутренний список описан раньше внешнего или для него не указан ArrayDataProvider.
Полный список ошибок — в разделе «Обработка ошибок» на странице метода documentgenerator.document.add.