Internal API

Материал из WiKi - UserSide

Custom Events v2 - это локальная система внутренних событий ERP. Она выполняет клиентский PHP-код внутри текущего PHP-процесса ERP без HTTP-запросов, веб-хуков и внешних очередей.

Старый механизм legacy/Config/custom_api.txt с функцией api_function(...) остается legacy-контрактом. В v2 старые названия используются только для карты совместимости. Новый клиентский код должен использовать константы CustomEvent::....

Файлы

Рабочий клиентский файл один:

evolution/CustomEvents/v2/handlers.php

Пример:

evolution/CustomEvents/v2/handlers.example.php

Список всех констант:

evolution/CustomEvents/Runtime/v2/CustomEvent.php

Карта старых имен в новые события:

evolution/CustomEvents/Runtime/v2/CustomEventLegacyEventMap.php

Кеш:

var/cache/CustomEvents/handlers.v2.cache.php

Лог:

var/log/custom-events-v2.log

Как писать обработчики

Используется одна функция регистрации: internal_event(...).

В одном файле можно зарегистрировать несколько обработчиков подряд:

internal_event(
    CustomEvent::TASK_CREATED,
    static function ($event): bool {
        $taskId = $event->get('task_id');

        return true;
    },
);

internal_event(
    CustomEvent::CUSTOMER_STATUS_CHANGE_BEFORE,
    static function ($event): bool|string {
        if ((int)$event->get('new_status_id') === 0) {
            return 'Запрещено переводить абонента в этот статус';
        }

        return true;
    },
);

internal_event(
    CustomEvent::CUSTOMER_CARD_PRIMARY_CONTENT_RENDER,
    static function ($event): string {
        $customerId = (int)$event->get('customer_id');

        return '<div>Дополнительная информация по абоненту #' . $customerId . '</div>';
    },
);

В обработчик всегда приходит один параметр $event.

Данные читаются без типовых оберток:

$taskId = $event->get('task_id');
$customerId = $event->get('customer_id');
$data = $event->all();

Если нужен конкретный тип, приводите значение в своем коде:

$taskId = (int)$event->get('task_id');
$comment = (string)$event->get('comment');

Результат обработчика

Обработчик возвращает простое значение:

Возврат Значение
true Обработчик выполнен успешно
false Для события *.before запретить штатную операцию
null Ничего не менять
строка Для *.before запретить операцию с текстом причины; для *.render вывести строку/HTML

События *.before могут отменять штатную операцию. Остальные события не должны отменять уже выполненное действие.

Пример запрета операции

internal_event(
    CustomEvent::CUSTOMER_STATUS_CHANGE_BEFORE,
    static function ($event): bool|string {
        if ((int)$event->get('new_status_id') === 0) {
            return 'Запрещено переводить абонента в этот статус';
        }

        return true;
    },
);

Пример вывода HTML

internal_event(
    CustomEvent::CUSTOMER_CARD_PRIMARY_CONTENT_RENDER,
    static function ($event): string {
        $customerId = (int)$event->get('customer_id');

        return '<div>Дополнительная информация по абоненту #' . $customerId . '</div>';
    },
);

Проверка, кеш и лог

При изменении evolution/CustomEvents/v2/handlers.php ERP проверяет синтаксис через php -l. Если синтаксис корректный, файл копируется в var/cache/CustomEvents/handlers.v2.cache.php.

Runtime-лог пишется в JSON Lines в var/log/custom-events-v2.log. Одна строка - один вызов обработчика, ошибка или факт отсутствия обработчиков.

Пример строки:

{"ts":"2026-05-10T12:01:03+03:00","event":"task.created","has_handlers":true,"handlers":1,"handler_index":0,"duration_ms":2.341,"memory_kb":18,"result":"allow","message":null}

Лог ротируется в:

var/log/custom-events-v2.log.1

Ошибки

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

Обработчик выполняется внутри того же PHP-процесса, что и ядро ERP. Через ядро проходят тысячи операций, и медленный или ошибочный клиентский код может остановить или дестабилизировать работу всей системы. Не используйте exit, die, бесконечные циклы, тяжелые SQL-запросы без ограничений и долгие внешние операции. Держите обработчики короткими и предсказуемыми.

Рекомендации

  • Используйте только константы CustomEvent::....
  • Не пишите строку события вручную.
  • Делайте обработчики короткими.
  • Не вызывайте HTTP/webhook из высокочастотных событий.
  • Не используйте exit, die, бесконечные циклы и тяжелые выборки без ограничений.
  • Возвращайте true, false, null или строку.

Все события

Ниже перечислены все события v2. Полный технический список констант находится в evolution/CustomEvents/Runtime/v2/CustomEvent.php.

Поля event указаны как текущий контракт данных.

Здания

Константа Когда вызывается Данные $event
CustomEvent::BUILDING_CREATED После создания здания building_id, data
CustomEvent::BUILDING_CHANGED После редактирования здания building_id, data

Коммутация

Константа Когда вызывается Данные $event
CustomEvent::COMMUTATION_CREATE_BEFORE Перед созданием коммутации между объектами source_type, source_id, target_type, target_id, data
CustomEvent::COMMUTATION_DELETE_BEFORE Перед удалением коммутации между объектами source_type, source_id, target_type, target_id, data

Главная страница

Константа Когда вызывается Данные $event
CustomEvent::DASHBOARD_TOP_CONTENT_RENDER При выводе текста в начальной части главной страницы employee_id, data

Оборудование

Константа Когда вызывается Данные $event
CustomEvent::DEVICE_CHANGED После редактирования оборудования device_id, device_type, data
CustomEvent::DEVICE_NOTIFICATION_DOWN При фиксации недоступности оборудования device_id, device_type, ip, data
CustomEvent::DEVICE_NOTIFICATION_UP При восстановлении доступности оборудования device_id, device_type, ip, data
CustomEvent::DEVICE_INTERFACE_PORT_NUMBER_RENDER При выводе номера порта в таблице интерфейсов device_id, interface_id, port_number, data
CustomEvent::DEVICE_INTERFACE_ADDITIONAL_DATA_RENDER При выводе дополнительного содержимого в карточке оборудования device_id, data

Абоненты

Константа Когда вызывается Данные $event
CustomEvent::CUSTOMER_CREATED После создания абонента customer_id, data
CustomEvent::CUSTOMER_CHANGE_BEFORE Перед редактированием абонента customer_id, data
CustomEvent::CUSTOMER_CHANGED После редактирования абонента customer_id, data
CustomEvent::CUSTOMER_MERGED После объединения абонентов customer_id, target_customer_id, data
CustomEvent::CUSTOMER_STATUS_CHANGE_BEFORE Перед изменением статуса абонента customer_id, new_status_id, old_status_id, data
CustomEvent::CUSTOMER_STATUS_CHANGED После изменения статуса абонента customer_id, new_status_id, old_status_id, data
CustomEvent::CUSTOMER_TARIFF_CHANGE_BEFORE Перед изменением тарифа абонента customer_id, billing_id, new_tariff_id, old_tariff_id, data
CustomEvent::CUSTOMER_TARIFF_CHANGED После изменения тарифа абонента customer_id, billing_id, new_tariff_id, old_tariff_id, data
CustomEvent::CUSTOMER_BALANCE_CHANGED При изменении баланса абонента customer_id, amount, data
CustomEvent::CUSTOMER_FORMER_TRANSFER_BEFORE Перед переводом абонента в бывшие абоненты customer_id, data
CustomEvent::CUSTOMER_FORMER_TRANSFERRED После перевода абонента в бывшие абоненты customer_id, data
CustomEvent::CUSTOMER_FORMER_RESTORE_BEFORE Перед восстановлением абонента из бывших абонентов customer_id, data
CustomEvent::CUSTOMER_FORMER_RESTORED После восстановления абонента из бывших абонентов customer_id, data
CustomEvent::CUSTOMER_DISCONNECT_PLAN_BEFORE Перед постановкой абонента на отключение customer_id, data
CustomEvent::CUSTOMER_DISCONNECT_PLANNED После постановки абонента на отключение customer_id, data
CustomEvent::CUSTOMER_DISCONNECT_BEFORE Перед отключением абонента customer_id, data
CustomEvent::CUSTOMER_DISCONNECTED После отключения абонента customer_id, data
CustomEvent::CUSTOMER_DISCONNECT_CANCEL_BEFORE Перед отменой отключения абонента customer_id, data
CustomEvent::CUSTOMER_DISCONNECT_CANCELLED После отмены отключения абонента customer_id, data
CustomEvent::CUSTOMER_SERVICE_ENABLE_BEFORE Перед подключением услуги абоненту customer_id, service_id, data
CustomEvent::CUSTOMER_SERVICE_ENABLED После подключения услуги абоненту customer_id, service_id, data
CustomEvent::CUSTOMER_SERVICE_DISABLE_BEFORE Перед отключением услуги абоненту customer_id, service_id, data
CustomEvent::CUSTOMER_SERVICE_DISABLED После отключения услуги абоненту customer_id, service_id, data

IP/MAC абонента

Константа Когда вызывается Данные $event
CustomEvent::CUSTOMER_IP_ADD_BEFORE Перед добавлением IP/MAC-адреса customer_id, ip, mac, subnet_property, data
CustomEvent::CUSTOMER_IP_ADDED После добавления IP/MAC-адреса customer_id, ip, mac, subnet_property, data
CustomEvent::CUSTOMER_IP_DELETE_BEFORE Перед удалением IP/MAC-адреса customer_id, ip, mac, subnet_property, data
CustomEvent::CUSTOMER_IP_DELETED После удаления IP/MAC-адреса customer_id, ip, mac, subnet_property, data

Метки абонента

Константа Когда вызывается Данные $event
CustomEvent::CUSTOMER_TAG_ADD_BEFORE Перед добавлением метки абоненту customer_id, tag_id, data
CustomEvent::CUSTOMER_TAG_ADDED После добавления метки абоненту customer_id, tag_id, data
CustomEvent::CUSTOMER_TAG_DELETE_BEFORE Перед удалением метки абонента customer_id, tag_id, data
CustomEvent::CUSTOMER_TAG_DELETED После удаления метки абонента customer_id, tag_id, data

Личный кабинет абонента

Константа Когда вызывается Данные $event
CustomEvent::CUSTOMER_PORTAL_REGISTRATION_BEFORE Перед регистрацией абонента в личном кабинете customer_id, data
CustomEvent::CUSTOMER_PORTAL_REGISTERED После регистрации абонента в личном кабинете customer_id, data
CustomEvent::CUSTOMER_PORTAL_LOGIN_BEFORE Перед входом абонента в личный кабинет customer_id, data
CustomEvent::CUSTOMER_PORTAL_DASHBOARD_RENDER При выводе содержимого на главной странице личного кабинета customer_id, data
CustomEvent::CUSTOMER_PORTAL_PAGE_RENDER При выводе содержимого на страницах личного кабинета customer_id, page_mode, page_id, data
CustomEvent::CUSTOMER_PORTAL_HEAD_RENDER При выводе содержимого после head в личном кабинете customer_id, data

Карточка абонента

Константа Когда вызывается Данные $event
CustomEvent::CUSTOMER_CARD_PRIMARY_CONTENT_RENDER При выводе основного содержимого в карточке абонента customer_id, mode, employee_id, data
CustomEvent::CUSTOMER_CARD_SECONDARY_CONTENT_RENDER При выводе дополнительного содержимого в карточке абонента customer_id, mode, employee_id, ip_mac, data
CustomEvent::CUSTOMER_CARD_IP_MAC_CONTENT_RENDER При выводе информации возле IP/MAC-адресов абонента customer_id, ip, mac, data
CustomEvent::CUSTOMER_CARD_SMS_CONTENT_RENDER При выводе содержимого отправки SMS в карточке абонента customer_id, data

Сотрудники

Константа Когда вызывается Данные $event
CustomEvent::EMPLOYEE_MESSAGE_CREATED После создания сообщения сотруднику receiver_employee_id, message_id, data
CustomEvent::EMPLOYEE_CHANGED После редактирования сотрудника employee_id, data
CustomEvent::EMPLOYEE_PERSONAL_SECTION_CONTENT_RENDER При выводе информации в персональном разделе сотрудника employee_id, data

Табель работ

Константа Когда вызывается Данные $event
CustomEvent::EMPLOYEE_TIMESHEET_PRINT_HEADER_RENDER При выводе шапки печатного табеля employee_id, period, data
CustomEvent::EMPLOYEE_TIMESHEET_PRINT_FOOTER_RENDER При выводе подвала печатного табеля employee_id, period, data

Склад

Константа Когда вызывается Данные $event
CustomEvent::INVENTORY_TRANSFER_BEFORE Перед перемещением ТМЦ inventory_id, operation_id, data
CustomEvent::INVENTORY_TRANSFERRED После перемещения ТМЦ inventory_id, operation_id, data

Задания

Константа Когда вызывается Данные $event
CustomEvent::TASK_CARD_RENDER При выводе содержимого в карточке задания task_type_id, task_id, employee_id, data
CustomEvent::TASK_WORK_DATE_RENDER При выводе содержимого возле даты работ task_type_id, task_id, employee_id, data
CustomEvent::TASK_CREATE_BEFORE Перед созданием задания task_type_id, author_employee_id, data
CustomEvent::TASK_CREATED После создания задания task_id, task_type_id, author_employee_id, data
CustomEvent::TASK_DELETE_BEFORE Перед удалением задания task_id, data
CustomEvent::TASK_CHANGE_BEFORE Перед редактированием задания task_id, data
CustomEvent::TASK_CHANGED После редактирования задания task_id, data
CustomEvent::TASK_COMMENT_CREATE_BEFORE Перед добавлением комментария к заданию task_id, comment, data
CustomEvent::TASK_COMMENT_CREATED После добавления комментария к заданию task_id, comment_id, comment, data
CustomEvent::TASK_COMMENT_CHANGED После редактирования комментария к заданию task_id, comment_id, comment, data
CustomEvent::TASK_COMMENT_TEXT_RENDER При выводе текста комментария задания task_id, comment_id, comment, data
CustomEvent::TASK_DIVISION_ASSIGN_BEFORE Перед добавлением подразделения к заданию task_id, division_id, data
CustomEvent::TASK_DIVISION_REMOVE_BEFORE Перед исключением подразделения из задания task_id, division_id, data
CustomEvent::TASK_EMPLOYEE_ASSIGN_BEFORE Перед добавлением исполнителя к заданию task_id, employee_id, data
CustomEvent::TASK_EMPLOYEE_REMOVE_BEFORE Перед исключением исполнителя из задания task_id, employee_id, data
CustomEvent::TASK_WATCHER_DIVISION_ASSIGN_BEFORE Перед добавлением подразделения наблюдателем task_id, division_id, data
CustomEvent::TASK_WATCHER_EMPLOYEE_ASSIGN_BEFORE Перед добавлением сотрудника наблюдателем task_id, employee_id, data
CustomEvent::TASK_OBJECT_ATTACHED После добавления объекта к заданию task_id, object_type, object_id, data
CustomEvent::TASK_OBJECT_DETACHED После исключения объекта из задания task_id, object_type, object_id, data
CustomEvent::TASK_RETURNED_TO_AUTHOR После возврата задания автору task_id, data
CustomEvent::TASK_STATE_CHANGE_BEFORE Перед изменением статуса задания task_id, old_state_id, new_state_id, data
CustomEvent::TASK_STATE_CHANGED После изменения статуса задания task_id, old_state_id, new_state_id, data

Сооружения связи

Константа Когда вызывается Данные $event
CustomEvent::NODE_CARD_ADDITIONAL_DATA_RENDER При выводе дополнительного содержимого в карточке сооружения связи node_id, data
CustomEvent::NODE_CARD_PRIMARY_CONTENT_RENDER При выводе основного содержимого в карточке сооружения связи node_id, data