Документация для разработчиков
Темная тема

Кастомизация

Добавление провайдера и данных

Про создание своего провайдера можно почитать тут.

Регистрируем обработчик получения списка провайдеров:

//Для модуля
\Bitrix\Main\EventManager::getInstance(
)->registerEventHandler('documentgenerator', 'onGetDataProviderList', 'crm', '\Bitrix\Crm\Integration\DocumentGeneratorManager', 'getDataProviders');
//рантайм
\Bitrix\Main\EventManager::getInstance(
)->addEventHandler('documentgenerator', 'onGetDataProviderList', ['myClass', 'addDocumentProviders']);

Обработчик должен возвращать массив вида:

$result[$className] = [
	'NAME' => 'Языковое название',
	'CLASS' => $className,
	'MODULE' => 'myModule',
];

где $className - полное название класса с неймспейсом.

Это должен быть наследник класса \Bitrix\DocumentGenerator\DataProvider.

Добавление дополнительных данных в результат \Bitrix\DocumentGenerator\Document::getFile()

В своём наследнике класса \Bitrix\DocumentGenerator\DataProvider определяем метод getAdditionalDocumentInfo().

Этот метод должен вернуть массив, который будет подмешан в результат \Bitrix\DocumentGenerator\Document::getFile().

Изменение значений полей

Изменение значений полей документа перед генерацией (с documentgenerator 18.0.6)

Подписываемся на событие:

\Bitrix\Main\EventManager::getInstance()->addEventHandler('documentgenerator', 'onBeforeProcessDocument', 'onBeforeProcessDocument');

function onBeforeProcessDocument($event)
{
	$document = $event->getParameter('document');
	/** @var \Bitrix\DocumentGenerator\Document $document */
	// добавить дополнительные описания полей
	// $document->setFields($newFields);
	// добавить значения полей
	//$document->setValues(['someField' => 'myCustomValue']);
	// получить список полей и их текущих значений
	//$fields = $document->getFields();
}

Обработчик события может ничего не возвращать, все действия производятся над объектом документа.

С объектом следует быть осторожнее. Если в обработчике вызвать $document->getFile() может возникнуть бесконечная рекурсия.

Изменение списков

Изменение содержимого списков (с версии documentgenerator 20.0.0)

Раньше для изменения полей списков в событии onBeforeProcessDocument приходилось пользоваться рефлексией.

Теперь добавлены несколько новых полезных методов:

Метод Описание
ArrayDataProvider::getItemByIndex(int $index)
Метод вернет элемент списка по индексу (если он есть).
ArrayDataProvider::replaceItem(int $index, $item)
Метод заменит элемент списка с индексом $index на $item.

Если элемента списка по этому индексу нет, то будет выброшено исключение OutOfRangeException.

ArrayDataProvider::addItem($item): int
Метод добавит новый элемент в конец списка. Вернет индекс нового элемента.
ArrayDataProvider::deleteItemByIndex(int $index)
Метод удалит элемент по индексу $index, после чего все элементы будут сдвинуты.
HashDataProvider::getData()x)
Метод вернет значения всех полей провайдера.
HashDataProvider::setData(array $data)
Метод перезапишет значения полей полностью.

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

\Bitrix\Main\EventManager::getInstance()->addEventHandler(
	\Bitrix\DocumentGenerator\Driver::MODULE_ID,
	'onBeforeProcessDocument',
	function(\Bitrix\Main\Event $event) {
		$productsPlaceholder = 'PRODUCTS';
		/** @var \Bitrix\DocumentGenerator\Document $document */
		$document = $event->getParameter('document');
		$provider = $document->getProvider();
		$productsProvider = $provider->getValue($productsPlaceholder);
		if($productsProvider instanceof \Bitrix\DocumentGenerator\DataProvider\ArrayDataProvider)
		{
			foreach($productsProvider as $index => $product)
			{
				if($index > 0)
				{
					$productsProvider->deleteItemByIndex($index);
				}
				else
				{
					$data = $product->getData();
					$data['NAME'] = 'New Product Name';
					$product->setData($data);
				}
			}
		}
	}
);

Отслеживание изменений документов

Подписываемся на события

// создание документа (уже после сохранения записи в БД)
\Bitrix\Main\EventManager::getInstance()->addEventHandler('documentgenerator', 'onCreateDocument', ['myClass', 'onCreateDocument']);
// изменение документа
\Bitrix\Main\EventManager::getInstance()->addEventHandler('documentgenerator', 'onUpdateDocument', ['myClass', 'onUpdateDocument']);

В обоих случаях, аналогично onBeforeProcessDocument, в событие придёт параметр $document с объектом документа.

// окончание конвертации документа
\Bitrix\Main\EventManager::getInstance()->addEventHandler('documentgenerator', 'onDocumentTransformationComplete', ['myClass', 'onDocumentTransformationComplete']);

В обработчик этого события придут два параметра - $documentId и $data.

$documentId - ID документа, по нему документ можно поднять через \Bitrix\DocumentGenerator\Document::loadById($documentId)

$data - результат \Bitrix\DocumentGenerator\Document::getFile().

Свои модификатор и хранилища

Чтобы сделать обработку своих провайдеров необходимо написать наследника \Bitrix\DocumentGenerator\Value.

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

Лучше всего делать по образцу (например \Bitrix\DocumentGenerator\Value\DateTime).

Использование своего хранилища документов

Сначала необходимо реализовать класс, который будет воспроизводить интерфейс \Bitrix\DocumentGenerator\Storage. Дальше можно либо через событие onBeforeProcessDocument менять хранилище документа перед его обработкой:

$storage = new MyStorage();
$document->setStorage($storage);

либо сохранить значение хранилища по умолчанию через опцию

\Bitrix\Main\Config\Option::set('documentgenerator', 'default_storage_type', MyStorage::class);

Перед реализацией рекомендется посмотреть существующие классы \Bitrix\DocumentGenerator\Storage\File, \Bitrix\DocumentGenerator\Storage\BFile, \Bitrix\DocumentGenerator\Storage\Disk.

Отслеживание просмотра

Отслеживание просмотра публичной ссылки на документ (с documentgenerator 18.6.0)

При просмотре публичной ссылки отправляется php-событие

\Bitrix\Main\EventManager::getInstance()->send(new \Bitrix\Main\Event('documentgenerator', 'onPublicView', [
	'document' => $document,
	'isPublicView' => false,
]));

где:

  • $document - объект \Bitrix\DocumentGenerator\Document;
  • isPublicView - Параметр доступен с версии documentgenerator 20.100.0 и принимает значение True или False. Параметр равен true, если публичная ссылка просмотрена не сотрудником портала в первый раз.

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

$template = $document->getTemplate();
if($template && $template->MODULE_ID == 'crm')
{
	// your code for crm here
}

Если надо отследить документа для CRM и получить данные об источнике документа, можно воспользоваться следующим кодом:

$provider = $document->getProvider();
if($provider && $provider instanceof \Bitrix\Crm\Integration\DocumentGenerator\DataProvider\CrmEntityDataProvider)
{
	$ownerId = $provider->getSource();
	$ownerType = $provider->getCrmOwnerType();
}

Новое с версии 20.0.0

Переопределение логики

Модуль позволяет использовать свои классы вместо предустановленных. Таким образом можно изменить поведение модуля.

Чтобы это сделать, необходимо подписаться на событие onDriverCollectClasses модуля documentgenerator.

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

Например, вы хотите использовать свою реализацию Document, чтобы использовать свой алгоритм генерации номеров вместо встроенного нумератора.

Для этого создаете своего наследника:

class MyDocument extends Bitrix\DocumentGenerator\Document 
{
	public function getNumber(bool $preview = true): string
	{
		return 'my_super_smart_number';
	}
}

После этого в init.php подписываетесь на событие

Bitrix\Main\EventManager::getInstance()->addEventHandler(
	'documentgenerator',
	'onDriverCollectClasses', 
	static function(\Bitrix\Main\Event $event) {
		return new \Bitrix\Main\EventResult(\Bitrix\Main\EventResult::SUCCESS, [
			'documentClassName' => MyDocument::class,
		]);
	}
);

Теперь Bitrix\DocumentGenerator\Driver::getInstance()->getDocumentClassName() вернет имя вашего класса.

Статические методы Bitrix\DocumentGenerator\Document::createByTemplate() и Bitrix\DocumentGenerator\Document::loadById() вернут объекты именно этого класса.

Классы, которые можно подменить, их имена и классы, которые надо наследовать

[
	'documentClassName' => Bitrix\DocumentGenerator\Document::class,
	'templateClassName' => Bitrix\DocumentGenerator\Template::class,
	'userPermissionsClassName' => Bitrix\DocumentGenerator\UserPermissions::class,
	'dataProviderManager' => Bitrix\DocumentGenerator\DataProviderManager::class,
];

Использование своих парсеров вместо стандартных Docx/DocxXml

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

Надо воспользоваться предыдущим примером, но заменить своего наследника Bitrix\DocumentGenerator\Template

class MyDocxXml extends Bitrix\DocumentGenerator\Body\DocxXml
{
	protected function getValuesForMultiplyingBlock(string $placeholder, Bitrix\DocumentGenerator\DataProvider\ArrayDataProvider $list, $data, array $fieldNames): array
	{
		// some custom logic
		return [];
	}
}
class MyDocx extends Bitrix\DocumentGenerator\Body\Docx
{
	protected function getXmlClassName(): string
	{
		return MyDocxXml::class;
	}
}
class MyTemplate extends Bitrix\DocumentGenerator\Template
{
	public function getBodyClassName(): string
	{
		return MyDocx::class;    
	} 
}

Кроме подмены класса в явном виде, можно подписаться на ORM-событие создания записи в таблице b_documentgenerator_template и менять в нем содержимое колонки BODY_TYPE. Класс парсера теперь берется из этой колонки.

Переопределение провайдеров (добавление своих полей)

Изменить значения полей можно с помощью события onBeforeProcessDocument. А вот с добавлением полей раньше была проблема.

Теперь можно использовать свои провайдеры вместо штатных. Есть два способа:

  1. Можно переопределить DataProviderManager и в нем крутить-вертеть классы-провайдеры и их поля как угодно
  2. Упрощенный вариант (когда нужно все провайдеры одного класса заменить его наследником) - через событие

Задача: добавить в сделку какое-то своё уникальное поле.

Решение: наследуем стандартный провайдер, в наследнике добавляем нужное поле, указываем наследник в качестве замены стандартному провайдеру.

// содержимое файла documentgenerator_classes.php
if(!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true)
{
	die();
}

if(\Bitrix\Main\Loader::includeModule('crm'))
{
	class MyDeal extends \Bitrix\Crm\Integration\DocumentGenerator\DataProvider\Deal
	{
		public function getFields(): array
		{
			if($this->fields === null)
			{
				parent::getFields();
				$this->fields['MY_SUPER_UNIQUE_FIELD'] = [
					'TITLE' => 'Cool Fields',
					'VALUE' => [
						$this, 'getUniqueFieldValue',
					],
				];
			}

			return $this->fields;
		}

		public function getUniqueFieldValue()
		{
			return 'unique_value';
		}
	}
}

// обработчик события в init.php
Bitrix\Main\EventManager::getInstance()->addEventHandler(
	'documentgenerator',
	'onDataProviderManagerFillSubstitutionProviders', 
	static function(\Bitrix\Main\Event $event) {
		require_once('documentgenerator_classes.php');

		$result = [];

		if(\Bitrix\Main\Loader::includeModule('crm'))
		{
			$result = [
				Bitrix\Crm\Integration\DocumentGenerator\DataProvider\Deal::class => MyDeal::class,
			];
		}
		return new \Bitrix\Main\EventResult(\Bitrix\Main\EventResult::SUCCESS, $result);
	}
);

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

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

У этого метода есть ограничения - заменяемый провайдер должен был наследником оригинального.



© «Битрикс», 2001-2024, «1С-Битрикс», 2024