documentgenerator 26.400.0 Ломающее

Documentgenerator 26.400.0: часовой пояс и сдвиг дат в шаблонах, очередь актуализации удалена

2 мин чтения Устаревших API: 5
Узнавайте о ломающих изменениях Битрикса раньше, чем они доедут до прода: свежие разборы — в канале @bxmax_updates

Ломающее обновление

Удалены или изменены публичные API — прикладной код может перестать работать.

В documentgenerator 26.400.0 у документов появился часовой пояс: его задают порталу и отдельному шаблону, а дату в плейсхолдере можно сдвинуть на N дней или вывести в другом поясе прямо в docx. Заодно из модуля вырезали очередь актуализации и Document::actualize(). CRM 26.1750.0 на нашем эталоне коробочного Битрикс24 по-прежнему пытается ставить документы в эту очередь, и документы смарт-счетов после правки счёта больше не перегенерируются. Если вы сами ставили документы в очередь или наследуете классы модуля, код придётся поправить.

Новое

Задайте документам часовой пояс

Дата и время в документе форматируются в поясе, который берётся по цепочке: поле TIMEZONE шаблона, затем опция модуля document_timezone, затем пояс сервера. Опцию выставляет новая настройка «Часовой пояс» в настройках генератора документов (компонент bitrix:documentgenerator.config). Пояс работает за фича-флагом Feature\Timezone, он включён по умолчанию. Пока ни опция, ни поле шаблона не заданы, документы выглядят как раньше, сдвиг от пояса сервера нулевой.

Пояс отдельного шаблона выбирают в форме шаблона в поле «Часовой пояс», пункт «Как в настройках модуля» его сбрасывает. Из кода пояс задают через контроллер или REST: documentgenerator.template.add и update (и crm.documentgenerator.template.* через прокси) принимают timezone, а get и list его возвращают. Нужен канонический идентификатор IANA вроде Asia/Yekaterinburg, на остальное придёт ошибка TIMEZONE_INVALID. Пустая строка сбрасывает поле в null, и шаблон снова берёт пояс из опции.

Из PHP можно писать в ORM напрямую, проверив пояс тем же Timezone::isValid():

        <?php

declare(strict_types=1);

use Bitrix\DocumentGenerator\Model\TemplateTable;
use Bitrix\DocumentGenerator\Timezone;
use Bitrix\Main\Config\Option;
use Bitrix\Main\Error;
use Bitrix\Main\Loader;
use Bitrix\Main\Result;

Loader::requireModule('documentgenerator');

// пояс по умолчанию для всех шаблонов, то же, что настройка «Часовой пояс»
Option::set('documentgenerator', Timezone::OPTION_NAME, 'Europe/Moscow');

function setTemplateTimezone(int $templateId, ?string $timezone): Result
{
    // null вернёт шаблон к поясу из опции
    if ($timezone !== null && !Timezone::isValid($timezone)) {
        return (new Result())->addError(new Error("Неизвестный пояс {$timezone}", 'TIMEZONE_INVALID'));
    }

    return TemplateTable::update($templateId, ['TIMEZONE' => $timezone]);
}

$result = setTemplateTimezone(12, 'Asia/Yekaterinburg');

    

Для своего выпадающего списка возьмите Timezone::getItems(). Он отдаёт пункты вида «(UTC+03:00) Europe/Moscow», отсортированные по смещению.

Считайте срок оплаты прямо в шаблоне

У плейсхолдеров дат два новых модификатора. add= сдвигает дату, timezone= выводит её в указанном поясе:

        {DocumentCreateTime~d.m.Y,add=+10 days}
{DocumentCreateTime~d.m.Y H:i,timezone=Asia/Novosibirsk}
{DocumentCreateTime~d.m.Y,add=+1 month,timezone=Asia/Vladivostok}

    

Знак у add= обязателен, а между числом и единицей нужен пробел: add=10 days и add=+10days не сработают. Число до шести цифр, единицы year, month, day, hour, minute и second, можно с окончанием s. Часы, минуты и секунды к полю с чистой датой без времени не применяются, а два add= в одном плейсхолдере отменяют сдвиг совсем. timezone= ставьте последним среди остальных сегментов, add= может стоять где угодно. Сдвиг работает, только пока включён флаг Feature\Timezone.

Ошибка в модификаторе генерацию не ломает. Дата выйдет без сдвига или в унаследованном поясе, а в логгер documentgenerator.Default уйдёт предупреждение, не чаще раза на вид ошибки за генерацию. Как вывести этот логгер в отдельный файл, мы показывали в разборе 26.300.0.

Шаблон названия документа за выключенным флагом

Флаг Feature\DocumentTitlePattern по умолчанию выключен. С включённым флагом в форме шаблона появляется поле для шаблона названия (расширение documentgenerator.title-pattern), а template.add и update принимают titlePattern до 255 символов: {DocumentNumber}, {DocumentCreateTime} и скалярные поля, которые есть и в теле шаблона, и у всех провайдеров. Шаблон применяется только к новым документам без явного DocumentTitle. Если шаблон названия не отрендерился, документ получит прежнее название «имя шаблона + номер».

Что сломается

Замените Document::actualize()

Удалены Service\ActualizeQueue, Service\ActualizeQueue\Task, ORM-сущность Model\ActualizeQueueTable и сервис documentgenerator.service.actualizeQueue. ServiceLocator по коду этого сервиса теперь бросает ServiceNotFoundException. Вместе с очередью ушёл публичный Document::actualize().

Внутри actualize() были два вызова: setUserId() и update($this->getExternalValues(true)). Второй вызов снаружи не повторить, getExternalValues() объявлен protected. Но loadById() уже кладёт в документ сохранённые ручные значения, а setValues() внутри update() их только дополняет. Поэтому update([]) на загруженном документе делает то же самое: провайдеры отдают свежие данные, ручные правки остаются, файл пересобирается.

        <?php

declare(strict_types=1);

namespace Vendor\Docs;

use Bitrix\Crm\Activity\Provider\Document as DocumentActivity;
use Bitrix\DocumentGenerator\Document;
use Bitrix\Main\Error;
use Bitrix\Main\Loader;
use Bitrix\Main\Result;

final class DocumentActualizer
{
    public function actualize(int $documentId, ?int $userId = null): Result
    {
        Loader::requireModule('documentgenerator');

        $document = Document::loadById($documentId);
        if ($document === null) {
            return (new Result())->addError(new Error("Документ {$documentId} не найден"));
        }
        if ($userId !== null) {
            $document->setUserId($userId);
        }

        return $document->update([]);
    }

    /** Документы незавершённых дел смарт-счёта, как их раньше брал CRM */
    public function actualizeSmartInvoice(int $invoiceId, int $userId): Result
    {
        Loader::requireModule('crm');

        $result = new Result();
        $activities = \CCrmActivity::GetList([], [
            'OWNER_TYPE_ID' => \CCrmOwnerType::SmartInvoice,
            'OWNER_ID' => $invoiceId,
            'COMPLETED' => 'N',
            'STATUS' => \CCrmActivityStatus::Waiting,
            'TYPE_ID' => \CCrmActivityType::Provider,
            'PROVIDER_ID' => DocumentActivity::getId(),
            'PROVIDER_TYPE_ID' => DocumentActivity::PROVIDER_TYPE_ID_DOCUMENT,
        ], false, false, ['ID', 'ASSOCIATED_ENTITY_ID']);

        while ($activity = $activities->Fetch()) {
            $update = $this->actualize((int)$activity['ASSOCIATED_ENTITY_ID'], $userId);
            if (!$update->isSuccess()) {
                $result->addErrors($update->getErrors());
            }
        }

        return $result;
    }
}

    

Документы смарт-счетов перестали обновляться сами

Очередью пользовался CRM. В CRM 26.1750.0, который стоит на эталоне вместе с этой версией, код постановки в очередь остался, но DocumentGeneratorManager::getActualizeQueue() ловит ObjectNotFoundException и возвращает null. В итоге молча ничего не делают действие ActualizeDocuments после сохранения смарт-счёта и saveAction карточки crm.invoice.details. Актуализацию при открытии документа вырезали из ViewComponent самого documentgenerator.

Пользователь поправил счёт, а документ из запланированного дела остался со старыми данными. Если документы должны обновляться, вызывайте actualizeSmartInvoice() из примера выше там, где вы уже обрабатываете сохранение счёта. ID документа CRM хранит в ASSOCIATED_ENTITY_ID дела.

Проверьте наследников

Три protected-метода сменили сигнатуру:

  • Controller\Template::add() получил пятый параметр ?\CRestServer $restServer = null;
  • Document::getValues() получил второй параметр по ссылке ?array &$rawValueKinds = null;
  • в DataProviderManager::getValueByType() параметр $format переименован в $options.

Наследник, который переопределяет первые два метода со старой сигнатурой, упадёт с fatal error о несовместимом объявлении. Переименование фатала не даёт и мешает только вызову с именованными аргументами.

Deprecated

Константа Body::DO_NOT_INSERT_VALUE_MODIFIER ('__SystemDeletePlaceholder') помечена @deprecated, а её обработку удалили. Если в docx-шаблоне стоял ~__SystemDeletePlaceholder, значение теперь печатается, и строки таблиц с этим модификатором больше не исключаются при размножении. Замены нет. В модулях эталона модификатор не используется, так что проверять стоит только свои шаблоны.

БД

  • В b_documentgenerator_template добавлены колонки TIMEZONE varchar(255) NULL и TITLE_PATTERN varchar(255) NULL, в TemplateTable появились одноимённые поля. Пустой TIMEZONE ORM сохраняет как null.
  • Таблица b_documentgenerator_actualize_queue осталась в схеме с пометкой @deprecated. ORM-класса у неё больше нет, и писать в неё некому.
  • Новая опция documentgenerator / document_timezone.
  • Агент \Bitrix\DocumentGenerator\Service\ActualizeQueue::process(5); убран из install/migrations/agents.php. Удаляет ли обновление уже зарегистрированную запись, по диффу не видно. Если запись осталась, сама она не удалится. Агент будет падать на отсутствующем классе примерно раз в 10 минут и каждый раз писать ошибку в лог исключений. Проверьте:
        SELECT ID, NAME, NEXT_EXEC FROM b_agent WHERE NAME LIKE '%ActualizeQueue::process%';

    

Найденную запись удалите в списке агентов (/bitrix/admin/agent_list.php).

Мелочи и находки

  • Из DocxXml::processMultiplyingBlock() убрали отладочный die. Блок {<список>.BLOCK_START~index=2} с ненулевым индексом обрывал PHP-процесс посреди генерации, если элемент с таким индексом в списке был. В истории эталона этот die живёт с самого первого снапшота.
  • Если на странице несколько превью документов, картинку по push теперь получает каждое. Раньше глобальный флаг привязывал обработчик только к первому.

Устаревшие и удалённые API в этой версии

Символ Статус Чем заменять
Bitrix\DocumentGenerator\Body::DO_NOT_INSERT_VALUE_MODIFIER Deprecated —
Bitrix\DocumentGenerator\Service\ActualizeQueue Удалено —
Bitrix\DocumentGenerator\Service\ActualizeQueue\Task Удалено —
Bitrix\DocumentGenerator\Model\ActualizeQueueTable Удалено —
Bitrix\DocumentGenerator\Document::actualize() Удалено Bitrix\DocumentGenerator\Document::update()

Новая версия вышла — разбор уже в Telegram

В канале @bxmax_updates публикуем каждый свежий разбор: что сломается, что устарело и что появилось в API. Узнаете до того, как обновите прод.

Подписаться

Читайте дальше

documentgenerator 26.300.0 Безопасность

Documentgenerator 26.300.0: file.delete требует права на шаблоны и закрыт для REST

До 26.300.0 любой авторизованный сотрудник портала мог удалить файл генератора документов по ID через `documentgenerator.api.file.delete`, в том числе PDF и превью документов. Теперь экшен требует права на изменение шаблонов и принимается только из AJAX, `FileTable::delete()` не удаляет превью и PDF…

6 мин
documentgenerator 26.200.0 Ломающее

Documentgenerator 26.200.0: плейсхолдеры в атрибутах DOCX больше не вычищаются, у DocxXml новые сигнатуры

Генератор документов 26.200.0 заполняет DOCX обходом DOM вместо регулярки по всему XML, и плейсхолдеры в атрибутах разметки, кроме `descr` и `name` у картинок, теперь доходят до готового документа как текст. У `Bitrix\DocumentGenerator\Body\DocxXml` удалён protected-метод и сменились сигнатуры ещё д…

3 мин
documentgenerator 26.200.100 Ломающее

Documentgenerator 26.200.100: рисунки без файла больше не остаются в размноженных строках DOCX

Documentgenerator 26.200.100 перестал оставлять в разметке DOCX рисунки из размноженных строк, у которых файл картинки не существует или не читается. Если вы наследуете `Docx` и переопределяете protected `replaceImages()`, смените `: void` на `: array` и возвращайте ID связей на удаление, иначе полу…

1 мин
voximplant 26.900.0 Заметное Свежее

Voximplant 26.900.0: подписанные параметры экспорта статистики

Voximplant 26.900.0 подписывает имя компонента при экспорте детализации статистики. Обновление также исправляет REST-сценарии, отметку пропущенного переадресованного звонка и чтение настроек прокси.

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

AI Домовой История

на связи

пишет…

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

Нет истории чатов
AI Домовой

Нужна авторизация

Войдите, чтобы задавать вопросы AI Домовому.

Войти