Нумератор

Нумератор генерирует уникальные номера по заданному шаблону. В шаблоне используются служебные слова: {NUMBER}, {YEAR}, {RANDOM} и другие.

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

Логика работы

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

Каждый генератор работает только с определенными типами нумераторов. Список доступных слов зависит от типа. Получить список можно с помощью метода getTemplateWordsForType.

use Bitrix\Main\Numerator\Numerator;

$templateWords = Numerator::getTemplateWordsForType('DOCUMENT');
// Или 'ALL', чтобы получить слова для всех типов

Результат с базовым набором:

[
    'Bitrix_Main_Numerator_Generator_SequentNumberGenerator' => [
        '{NUMBER}',
    ],
    'Bitrix_Main_Numerator_Generator_DateNumberGenerator' => [
        '{DAY}',
        '{MONTH}',
        '{YEAR}',
    ],
    'Bitrix_Main_Numerator_Generator_RandomNumberGenerator' => [
        '{RANDOM}',
    ],
    'Bitrix_Main_Numerator_Generator_PrefixNumberGenerator' => [
        '{PREFIX}',
    ],
];

Базовые генераторы

Для всех типов нумераторов доступно четыре базовых генератора.

Генератор

Служебные слова

RandomNumberGenerator

{RANDOM} — случайная строка из заглавных латинских букв и цифр

DateNumberGenerator

{DAY} — день месяца в момент генерации номера с ведущим нулем: 01, 23
{MONTH} — месяц в момент генерации номера с ведущим нулем: 03, 11
{YEAR} — текущий год на момент генерации кода

SequentNumberGenerator

{NUMBER} — уникальный последовательный номер

PrefixNumberGenerator

{PREFIX} — фиксированный текст, который задан в настройках

Пример преобразования:

Шаблон:      {PREFIX}__{YEAR}/{NUMBER}—{RANDOM}
               ↓          ↓       ↓         ↓
Генераторы:  Prefix     Date   Sequent   Random
               ↓          ↓       ↓         ↓
Значения:    'DOC'     '2025'   '007'   'A3F9K2'

Готовый номер:  DOC__2025/007—A3F9K2

Дополнительные генераторы

Тип нумератора

Служебные слова

DOCUMENT

{CLIENT_ID} — идентификатор клиента
{SELF_ID} — идентификатор объекта с данными
{SELF_COMPANY_ID} — идентификатор компании

CRM_QUOTE

{QUOTE_ID} — номер предложения
{USER_ID_QUOTES_COUNT} — идентификатор пользователя и число его предложений

CRM_INVOICE

{INVOICE_ID} — номер счета
{USER_ID_INVOICES_COUNT} — идентификатор пользователя и число его счетов

ORDER

{USER_ID_ORDERS_COUNT} — идентификатор пользователя и число его заказов
{ORDER_ID} — номер заказа

Как получить настройки нумератора

Чтобы получить настройки нумератора, используйте метод getSettingsFields.

use Bitrix\Main\Numerator\Numerator;

$settings = Numerator::getSettingsFields('DOCUMENT');

Метод возвращает два массива:

  • settingsFields — описывает интерфейс настроек: типы полей, заголовки, значения по умолчанию,

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

Пример результата:

[
    'settingsFields' => [
        'Bitrix_Main_Numerator_Numerator' => [
            ['settingName' => 'name', 'type' => 'string', 'default' => 'Нумератор 1', 'title' => 'Название нумератора', ],
            ['settingName' => 'template', 'type' => 'string', 'title' => 'Шаблон номера',],
        ],
        'Bitrix_Main_Numerator_Generator_SequentNumberGenerator' => [
            ['settingName' => 'start', 'type' => 'int', 'default' => 1, 'title' => 'Начинать последовательный номер с',],
            ['settingName' => 'step', 'type' => 'int', 'default' => 1, 'title' => 'Увеличивать последовательный номер на',],
            ['settingName' => 'periodicBy', 'type' => 'array', 'title' => 'Период работы нумератора', 'values' => [
                ['settingName' => 'default', 'value' => '', 'title' => 'Постоянно',],
                ['settingName' => 'day', 'value' => 'day', 'title' => 'В пределах дня',],
                ['settingName' => 'month', 'value' => 'month', 'title' => 'В пределах месяца',],
                ['settingName' => 'year', 'value' => 'year', 'title' => 'В пределах года',],],
            ],
            ['settingName' => 'timezone', 'type' => 'array', 'values' => [...]],
            // ...,
        ],
        // ...,
    ],
    'settingsWords' => [
        'Bitrix_Main_Numerator_Generator_SequentNumberGenerator' => ['{NUMBER}' => 'Последовательный номер',],
        'Bitrix_Main_Numerator_Generator_DateNumberGenerator' => ['{DAY}' => 'Текущий день', '{MONTH}' => 'Текущий месяц', '{YEAR}' => 'Текущий год',],
        'Bitrix_Main_Numerator_Generator_RandomNumberGenerator' => ['{RANDOM}' => 'Случайный номер',],
        'Bitrix_Main_Numerator_Generator_PrefixNumberGenerator' => ['{PREFIX}' => 'Префикс',],
    ]
];

Как добавить генератор

Генератор регистрируют в событии onNumberGeneratorsClassesCollect, потому что нумератор формирует динамически список доступных генераторов при инициализации.

Например, добавьте генератор TimeNumberGenerator и служебное слово {TIME}.

Зарегистрировать класс в событии

Зарегистрируйте класс генератора в событии onNumberGeneratorsClassesCollect. Достаточно указать имя класса — TimeNumberGenerator. Метод onGeneratorClassesCollect реализован в базовом классе \Bitrix\Main\Numerator\Generator\NumberGenerator.

RegisterModuleDependences(
    'main',
    'onNumberGeneratorsClassesCollect',
    'mymodule',
    'TimeNumberGenerator',
    'onGeneratorClassesCollect'
);

Создать класс генератора

  1. Унаследуйте класс TimeNumberGenerator от NumberGenerator.

  2. В класс добавьте методы:

    • getConfig — возвращает текущие настройки. Например, формат времени.

    • setConfig — устанавливает настройки при инициализации из базы.

    • getSettingsFields — описывает поля настроек для интерфейса.

    • getTemplateWordsForParse — перечисляет служебные слова, которые генератор может обработать. В примере — {TIME}.

    • getTemplateWordsSettings — сопоставляет служебные слова с названиями. Используется для подписей на кнопках в интерфейсе. В примере — кнопка time.

    • getAvailableForType — возвращает типы нумераторов, с которыми работает генератор. В примере — все типы NUMERATOR_DEFAULT_TYPE.

    • parseTemplate — подставляет значение вместо служебного слова. Остальные слова оставляет без изменений, их обработают другие генераторы.

    • validateConfig — проверяет корректность настроек.

<?php
use Bitrix\Main\Localization\Loc;
use Bitrix\Main\Numerator\Generator\NumberGenerator;
use Bitrix\Main\Numerator\Generator\Contract\UserConfigurable;
use Bitrix\Main\Numerator\Numerator;

class TimeNumberGenerator extends NumberGenerator implements UserConfigurable
{
    const TEMPLATE_WORD_TIME = "TIME";
    protected $format;

    /** @inheritdoc */
    public function getConfig()
    {
        return ['format' => $this->format];
    }

    /** @inheritdoc */
    public function setConfig($config)
    {
        $this->setFromArrayOrDefault('format', $config, 'H:i:s', 'string');
    }

    /** @inheritdoc */
    public static function getSettingsFields()
    {
        return [
            [
                'settingName' => 'format',
                'type'        => 'string',
                'default'     => 'H:i:s',
                'title'       => 'Формат времени',
            ],
        ];
    }

    /** @inheritdoc */
    public static function getTemplateWordsForParse()
    {
        return [static::getPatternFor(static::TEMPLATE_WORD_TIME)];
    }

    /** @inheritdoc */
    public static function getTemplateWordsSettings()
    {
        return [static::getTemplateFor(static::TEMPLATE_WORD_TIME) => 'time'];
    }

    /** @inheritdoc */
    public static function getAvailableForType()
    {
        return Numerator::NUMERATOR_DEFAULT_TYPE;
    }

    /** @inheritdoc */
    public function parseTemplate($template)
    {
        return str_replace(
            static::getPatternFor(static::TEMPLATE_WORD_TIME),
            date($this->format, time()),
            $template
        );
    }

    /** @inheritdoc */
    public function validateConfig($config)
    {
        $result = new \Bitrix\Main\Result();
        return $result;
    }
}

После подключения класса в интерфейсе отображается кнопка time. Клик по кнопке вставляет слово {TIME} в шаблон.

Как создать нумератор

Чтобы создать нумератор, используйте класс \Bitrix\Main\Numerator\Numerator.

Получить экземпляр

Вызовите метод create. Он возвращает пустой объект нумератора без имени и шаблона.

use Bitrix\Main\Numerator\Numerator;

$numerator = Numerator::create();

Передать конфигурацию

Конфигурация нумератора — ассоциативный массив. Ключ — тип класса генератора, значение — параметры.

Генератор

Параметры

Numerator

name — название нумератора, обязательный параметр
template — шаблон номера, обязательный параметр

SequentNumberGenerator

start — с какого числа начинать
step — шаг для увеличения номера
periodicBy — сбрасывать счетчик в start при наступлении нового периода
timezone — по какому часовому поясу определять наступление нового периода
isDirectNumeration — использовать сквозную нумерацию или иметь независимые счетчики

RandomNumberGenerator

length — длина номера

PrefixNumberGenerator

prefix — строка символов

DateNumberGenerator

Без параметров

use Bitrix\Main\Numerator\Generator;

$config = [
    Numerator::getType() => [
        'name' => 'Мой нумератор',
        'template' => '{PREFIX}__{YEAR}/{NUMBER}—{RANDOM}',
    ],
    Generator\RandomNumberGenerator::getType() => [
        'length' => 6,
    ],
    Generator\SequentNumberGenerator::getType() => [
        'start' => 3,
        'step' => 2,
    ],
    Generator\PrefixNumberGenerator::getType() => [
        'prefix' => 'DOC',
    ],
];

$numerator->setConfig($config);

Сохранить нумератор

Метод save возвращает объект результата. При успехе $result->getId() содержит идентификатор нового нумератора.

$result = $numerator->save(); // \Bitrix\Main\Entity\AddResult

Как получить нумератор

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

Получить по идентификатору

use Bitrix\Main\Numerator\Numerator;

$numerator = Numerator::load($numeratorId);

Получить по типу

Первый нумератор указанного типа:

use Bitrix\Main\Numerator\Numerator;

$numerator = Numerator::getOneByType('ORDER');

Список всех нумераторов указанного типа:

use Bitrix\Main\Numerator\Numerator;

$numerators = Numerator::getListByType('CRM_INVOICE');

Получить следующий номер

$number = $numerator->getNext();

Изменить настройки нумератора

Измените конфигурацию и сохраните с помощью метода update:

use Bitrix\Main\Numerator\Numerator;

$numerator = Numerator::load($numeratorId);
$config = $numerator->getConfig();
$config[Numerator::getType()]['name'] = 'Обновленный нумератор';

$result = Numerator::update($id, $config);

Для существующих нумераторов не вызывайте метод save. Используйте update, чтобы избежать конфликтов при параллельных запросах.

Передать контекст

В нумератор передают дополнительные данные, если генератор реализует интерфейс DynamicConfigurable.

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

  1. При загрузке нумератора методом load.

    $numerator = Numerator::load($id, $source);
    
  2. Через метод setDynamicConfig.

    $numerator->setDynamicConfig($config);
    

Как использовать хеш

Нумератор с {NUMBER} может отвечать за несколько последовательных номеров и иметь несколько независимых счетчиков.

Если запросить следующий номер у нумератора, получите последовательно увеличивающееся число.

use Bitrix\Main\Numerator\Numerator;

$numerator = Numerator::create();
$numerator->setConfig([
    Numerator::getType() => [
        'name' => 'test sequence',
        'template' => '{NUMBER}',
    ],
]);
$result = $numerator->save();
$numerator = Numerator::load($result->getId());
echo $numerator->getNext() . PHP_EOL;
$numerator = Numerator::load($result->getId());
echo $numerator->getNext() . PHP_EOL;
$numerator = Numerator::load($result->getId());
echo $numerator->getNext() . PHP_EOL;

// Получим
// 1
// 2
// 3

Если задать хеш и запросить номер для разного хеша, получите независимые счетчики.

use Bitrix\Main\Numerator\Numerator;

$numerator = Numerator::create();
$numerator->setConfig([
    Numerator::getType() => [
        'name' => 'sequence',
        'template' => '{NUMBER}',
    ],
]);
$result = $numerator->save();
$numerator = Numerator::load($result->getId());
echo $numerator->getNext('A') . PHP_EOL;
$numerator = Numerator::load($result->getId());
echo $numerator->getNext('B') . PHP_EOL;
$numerator = Numerator::load($result->getId());
echo $numerator->getNext('A') . PHP_EOL;

// Получим
// 1
// 1
// 2

Хеш можно установить тремя способами.

  1. В методе load вторым параметром в виде объекта, который реализует интерфейс Hashable.

    use Bitrix\Main\Numerator\Numerator;
    
    Numerator::load($numeratorId, $hashable);
    
  2. В setHash через сеттер в виде объекта Hashable.

    $numerator->setHash($hashable);
    
  3. В методе getNext в виде строки.

    $hash = 'MANAGER_42';
    $numerator->getNext($hash);