Internal API: различия между версиями
Admin (обсуждение | вклад) (Новая страница: «Событийная система») |
IronBot (обсуждение | вклад) ([IronBot] Убрана ручная нумерация заголовков событий) |
||
| (не показана 1 промежуточная версия этого же участника) | |||
| Строка 1: | Строка 1: | ||
Custom Events v2 - это локальная система внутренних событий ERP. Она выполняет клиентский PHP-код внутри текущего PHP-процесса ERP без HTTP-запросов, веб-хуков и внешних очередей. | |||
Старый механизм <code>legacy/Config/custom_api.txt</code> с функцией <code>api_function(...)</code> остается legacy-контрактом. В v2 старые названия используются только для карты совместимости. Новый клиентский код должен использовать константы <code>CustomEvent::...</code>. | |||
== Файлы == | |||
Рабочий клиентский файл один: | |||
<pre>evolution/CustomEvents/v2/handlers.php</pre> | |||
Пример: | |||
<pre>evolution/CustomEvents/v2/handlers.example.php</pre> | |||
Список всех констант: | |||
<pre>evolution/CustomEvents/Runtime/v2/CustomEvent.php</pre> | |||
Карта старых имен в новые события: | |||
<pre>evolution/CustomEvents/Runtime/v2/CustomEventLegacyEventMap.php</pre> | |||
Кеш: | |||
<pre>var/cache/CustomEvents/handlers.v2.cache.php</pre> | |||
Лог: | |||
<pre>var/log/custom-events-v2.log</pre> | |||
== Как писать обработчики == | |||
Используется одна функция регистрации: <code>internal_event(...)</code>. | |||
В одном файле можно зарегистрировать несколько обработчиков подряд: | |||
<syntaxhighlight lang="php"> | |||
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>'; | |||
}, | |||
); | |||
</syntaxhighlight> | |||
В обработчик всегда приходит один параметр <code>$event</code>. | |||
Данные читаются без типовых оберток: | |||
<syntaxhighlight lang="php"> | |||
$taskId = $event->get('task_id'); | |||
$customerId = $event->get('customer_id'); | |||
$data = $event->all(); | |||
</syntaxhighlight> | |||
Если нужен конкретный тип, приводите значение в своем коде: | |||
<syntaxhighlight lang="php"> | |||
$taskId = (int)$event->get('task_id'); | |||
$comment = (string)$event->get('comment'); | |||
</syntaxhighlight> | |||
== Результат обработчика == | |||
Обработчик возвращает простое значение: | |||
{| class="wikitable" | |||
! Возврат | |||
! Значение | |||
|- | |||
| <code>true</code> | |||
| Обработчик выполнен успешно | |||
|- | |||
| <code>false</code> | |||
| Для события <code>*.before</code> запретить штатную операцию | |||
|- | |||
| <code>null</code> | |||
| Ничего не менять | |||
|- | |||
| строка | |||
| Для <code>*.before</code> запретить операцию с текстом причины; для <code>*.render</code> вывести строку/HTML | |||
|} | |||
События <code>*.before</code> могут отменять штатную операцию. Остальные события не должны отменять уже выполненное действие. | |||
== Пример запрета операции == | |||
<syntaxhighlight lang="php"> | |||
internal_event( | |||
CustomEvent::CUSTOMER_STATUS_CHANGE_BEFORE, | |||
static function ($event): bool|string { | |||
if ((int)$event->get('new_status_id') === 0) { | |||
return 'Запрещено переводить абонента в этот статус'; | |||
} | |||
return true; | |||
}, | |||
); | |||
</syntaxhighlight> | |||
== Пример вывода HTML == | |||
<syntaxhighlight lang="php"> | |||
internal_event( | |||
CustomEvent::CUSTOMER_CARD_PRIMARY_CONTENT_RENDER, | |||
static function ($event): string { | |||
$customerId = (int)$event->get('customer_id'); | |||
return '<div>Дополнительная информация по абоненту #' . $customerId . '</div>'; | |||
}, | |||
); | |||
</syntaxhighlight> | |||
== Проверка, кеш и лог == | |||
При изменении <code>evolution/CustomEvents/v2/handlers.php</code> ERP проверяет синтаксис через <code>php -l</code>. Если синтаксис корректный, файл копируется в <code>var/cache/CustomEvents/handlers.v2.cache.php</code>. | |||
Runtime-лог пишется в JSON Lines в <code>var/log/custom-events-v2.log</code>. Одна строка - один вызов обработчика, ошибка или факт отсутствия обработчиков. | |||
Пример строки: | |||
<syntaxhighlight lang="json"> | |||
{"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} | |||
</syntaxhighlight> | |||
Лог ротируется в: | |||
<pre>var/log/custom-events-v2.log.1</pre> | |||
== Ошибки == | |||
Если в обработчике возникнет ошибка, ERP запишет ее в лог и продолжит работу по правилам конкретного события. | |||
Обработчик выполняется внутри того же PHP-процесса, что и ядро ERP. Через ядро проходят тысячи операций, и медленный или ошибочный клиентский код может остановить или дестабилизировать работу всей системы. Не используйте <code>exit</code>, <code>die</code>, бесконечные циклы, тяжелые SQL-запросы без ограничений и долгие внешние операции. Держите обработчики короткими и предсказуемыми. | |||
== Рекомендации == | |||
* Используйте только константы <code>CustomEvent::...</code>. | |||
* Не пишите строку события вручную. | |||
* Делайте обработчики короткими. | |||
* Не вызывайте HTTP/webhook из высокочастотных событий. | |||
* Не используйте <code>exit</code>, <code>die</code>, бесконечные циклы и тяжелые выборки без ограничений. | |||
* Возвращайте <code>true</code>, <code>false</code>, <code>null</code> или строку. | |||
== Все события == | |||
Ниже перечислены все события v2. Полный технический список констант находится в <code>evolution/CustomEvents/Runtime/v2/CustomEvent.php</code>. | |||
Поля <code>event</code> указаны как текущий контракт данных. | |||
=== Здания === | |||
{| class="wikitable" | |||
! Константа | |||
! Когда вызывается | |||
! Данные <code>$event</code> | |||
|- | |||
| <code>CustomEvent::BUILDING_CREATED</code> | |||
| После создания здания | |||
| <code>building_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::BUILDING_CHANGED</code> | |||
| После редактирования здания | |||
| <code>building_id</code>, <code>data</code> | |||
|} | |||
=== Коммутация === | |||
{| class="wikitable" | |||
! Константа | |||
! Когда вызывается | |||
! Данные <code>$event</code> | |||
|- | |||
| <code>CustomEvent::COMMUTATION_CREATE_BEFORE</code> | |||
| Перед созданием коммутации между объектами | |||
| <code>source_type</code>, <code>source_id</code>, <code>target_type</code>, <code>target_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::COMMUTATION_DELETE_BEFORE</code> | |||
| Перед удалением коммутации между объектами | |||
| <code>source_type</code>, <code>source_id</code>, <code>target_type</code>, <code>target_id</code>, <code>data</code> | |||
|} | |||
=== Главная страница === | |||
{| class="wikitable" | |||
! Константа | |||
! Когда вызывается | |||
! Данные <code>$event</code> | |||
|- | |||
| <code>CustomEvent::DASHBOARD_TOP_CONTENT_RENDER</code> | |||
| При выводе текста в начальной части главной страницы | |||
| <code>employee_id</code>, <code>data</code> | |||
|} | |||
=== Оборудование === | |||
{| class="wikitable" | |||
! Константа | |||
! Когда вызывается | |||
! Данные <code>$event</code> | |||
|- | |||
| <code>CustomEvent::DEVICE_CHANGED</code> | |||
| После редактирования оборудования | |||
| <code>device_id</code>, <code>device_type</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::DEVICE_NOTIFICATION_DOWN</code> | |||
| При фиксации недоступности оборудования | |||
| <code>device_id</code>, <code>device_type</code>, <code>ip</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::DEVICE_NOTIFICATION_UP</code> | |||
| При восстановлении доступности оборудования | |||
| <code>device_id</code>, <code>device_type</code>, <code>ip</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::DEVICE_INTERFACE_PORT_NUMBER_RENDER</code> | |||
| При выводе номера порта в таблице интерфейсов | |||
| <code>device_id</code>, <code>interface_id</code>, <code>port_number</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::DEVICE_INTERFACE_ADDITIONAL_DATA_RENDER</code> | |||
| При выводе дополнительного содержимого в карточке оборудования | |||
| <code>device_id</code>, <code>data</code> | |||
|} | |||
=== Абоненты === | |||
{| class="wikitable" | |||
! Константа | |||
! Когда вызывается | |||
! Данные <code>$event</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_CREATED</code> | |||
| После создания абонента | |||
| <code>customer_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_CHANGE_BEFORE</code> | |||
| Перед редактированием абонента | |||
| <code>customer_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_CHANGED</code> | |||
| После редактирования абонента | |||
| <code>customer_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_MERGED</code> | |||
| После объединения абонентов | |||
| <code>customer_id</code>, <code>target_customer_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_STATUS_CHANGE_BEFORE</code> | |||
| Перед изменением статуса абонента | |||
| <code>customer_id</code>, <code>new_status_id</code>, <code>old_status_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_STATUS_CHANGED</code> | |||
| После изменения статуса абонента | |||
| <code>customer_id</code>, <code>new_status_id</code>, <code>old_status_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_TARIFF_CHANGE_BEFORE</code> | |||
| Перед изменением тарифа абонента | |||
| <code>customer_id</code>, <code>billing_id</code>, <code>new_tariff_id</code>, <code>old_tariff_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_TARIFF_CHANGED</code> | |||
| После изменения тарифа абонента | |||
| <code>customer_id</code>, <code>billing_id</code>, <code>new_tariff_id</code>, <code>old_tariff_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_BALANCE_CHANGED</code> | |||
| При изменении баланса абонента | |||
| <code>customer_id</code>, <code>amount</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_FORMER_TRANSFER_BEFORE</code> | |||
| Перед переводом абонента в бывшие абоненты | |||
| <code>customer_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_FORMER_TRANSFERRED</code> | |||
| После перевода абонента в бывшие абоненты | |||
| <code>customer_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_FORMER_RESTORE_BEFORE</code> | |||
| Перед восстановлением абонента из бывших абонентов | |||
| <code>customer_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_FORMER_RESTORED</code> | |||
| После восстановления абонента из бывших абонентов | |||
| <code>customer_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_DISCONNECT_PLAN_BEFORE</code> | |||
| Перед постановкой абонента на отключение | |||
| <code>customer_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_DISCONNECT_PLANNED</code> | |||
| После постановки абонента на отключение | |||
| <code>customer_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_DISCONNECT_BEFORE</code> | |||
| Перед отключением абонента | |||
| <code>customer_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_DISCONNECTED</code> | |||
| После отключения абонента | |||
| <code>customer_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_DISCONNECT_CANCEL_BEFORE</code> | |||
| Перед отменой отключения абонента | |||
| <code>customer_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_DISCONNECT_CANCELLED</code> | |||
| После отмены отключения абонента | |||
| <code>customer_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_SERVICE_ENABLE_BEFORE</code> | |||
| Перед подключением услуги абоненту | |||
| <code>customer_id</code>, <code>service_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_SERVICE_ENABLED</code> | |||
| После подключения услуги абоненту | |||
| <code>customer_id</code>, <code>service_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_SERVICE_DISABLE_BEFORE</code> | |||
| Перед отключением услуги абоненту | |||
| <code>customer_id</code>, <code>service_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_SERVICE_DISABLED</code> | |||
| После отключения услуги абоненту | |||
| <code>customer_id</code>, <code>service_id</code>, <code>data</code> | |||
|} | |||
==== IP/MAC абонента ==== | |||
{| class="wikitable" | |||
! Константа | |||
! Когда вызывается | |||
! Данные <code>$event</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_IP_ADD_BEFORE</code> | |||
| Перед добавлением IP/MAC-адреса | |||
| <code>customer_id</code>, <code>ip</code>, <code>mac</code>, <code>subnet_property</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_IP_ADDED</code> | |||
| После добавления IP/MAC-адреса | |||
| <code>customer_id</code>, <code>ip</code>, <code>mac</code>, <code>subnet_property</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_IP_DELETE_BEFORE</code> | |||
| Перед удалением IP/MAC-адреса | |||
| <code>customer_id</code>, <code>ip</code>, <code>mac</code>, <code>subnet_property</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_IP_DELETED</code> | |||
| После удаления IP/MAC-адреса | |||
| <code>customer_id</code>, <code>ip</code>, <code>mac</code>, <code>subnet_property</code>, <code>data</code> | |||
|} | |||
==== Метки абонента ==== | |||
{| class="wikitable" | |||
! Константа | |||
! Когда вызывается | |||
! Данные <code>$event</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_TAG_ADD_BEFORE</code> | |||
| Перед добавлением метки абоненту | |||
| <code>customer_id</code>, <code>tag_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_TAG_ADDED</code> | |||
| После добавления метки абоненту | |||
| <code>customer_id</code>, <code>tag_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_TAG_DELETE_BEFORE</code> | |||
| Перед удалением метки абонента | |||
| <code>customer_id</code>, <code>tag_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_TAG_DELETED</code> | |||
| После удаления метки абонента | |||
| <code>customer_id</code>, <code>tag_id</code>, <code>data</code> | |||
|} | |||
==== Личный кабинет абонента ==== | |||
{| class="wikitable" | |||
! Константа | |||
! Когда вызывается | |||
! Данные <code>$event</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_PORTAL_REGISTRATION_BEFORE</code> | |||
| Перед регистрацией абонента в личном кабинете | |||
| <code>customer_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_PORTAL_REGISTERED</code> | |||
| После регистрации абонента в личном кабинете | |||
| <code>customer_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_PORTAL_LOGIN_BEFORE</code> | |||
| Перед входом абонента в личный кабинет | |||
| <code>customer_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_PORTAL_DASHBOARD_RENDER</code> | |||
| При выводе содержимого на главной странице личного кабинета | |||
| <code>customer_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_PORTAL_PAGE_RENDER</code> | |||
| При выводе содержимого на страницах личного кабинета | |||
| <code>customer_id</code>, <code>page_mode</code>, <code>page_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_PORTAL_HEAD_RENDER</code> | |||
| При выводе содержимого после <code>head</code> в личном кабинете | |||
| <code>customer_id</code>, <code>data</code> | |||
|} | |||
==== Карточка абонента ==== | |||
{| class="wikitable" | |||
! Константа | |||
! Когда вызывается | |||
! Данные <code>$event</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_CARD_PRIMARY_CONTENT_RENDER</code> | |||
| При выводе основного содержимого в карточке абонента | |||
| <code>customer_id</code>, <code>mode</code>, <code>employee_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_CARD_SECONDARY_CONTENT_RENDER</code> | |||
| При выводе дополнительного содержимого в карточке абонента | |||
| <code>customer_id</code>, <code>mode</code>, <code>employee_id</code>, <code>ip_mac</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_CARD_IP_MAC_CONTENT_RENDER</code> | |||
| При выводе информации возле IP/MAC-адресов абонента | |||
| <code>customer_id</code>, <code>ip</code>, <code>mac</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::CUSTOMER_CARD_SMS_CONTENT_RENDER</code> | |||
| При выводе содержимого отправки SMS в карточке абонента | |||
| <code>customer_id</code>, <code>data</code> | |||
|} | |||
=== Сотрудники === | |||
{| class="wikitable" | |||
! Константа | |||
! Когда вызывается | |||
! Данные <code>$event</code> | |||
|- | |||
| <code>CustomEvent::EMPLOYEE_MESSAGE_CREATED</code> | |||
| После создания сообщения сотруднику | |||
| <code>receiver_employee_id</code>, <code>message_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::EMPLOYEE_CHANGED</code> | |||
| После редактирования сотрудника | |||
| <code>employee_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::EMPLOYEE_PERSONAL_SECTION_CONTENT_RENDER</code> | |||
| При выводе информации в персональном разделе сотрудника | |||
| <code>employee_id</code>, <code>data</code> | |||
|} | |||
==== Табель работ ==== | |||
{| class="wikitable" | |||
! Константа | |||
! Когда вызывается | |||
! Данные <code>$event</code> | |||
|- | |||
| <code>CustomEvent::EMPLOYEE_TIMESHEET_PRINT_HEADER_RENDER</code> | |||
| При выводе шапки печатного табеля | |||
| <code>employee_id</code>, <code>period</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::EMPLOYEE_TIMESHEET_PRINT_FOOTER_RENDER</code> | |||
| При выводе подвала печатного табеля | |||
| <code>employee_id</code>, <code>period</code>, <code>data</code> | |||
|} | |||
=== Склад === | |||
{| class="wikitable" | |||
! Константа | |||
! Когда вызывается | |||
! Данные <code>$event</code> | |||
|- | |||
| <code>CustomEvent::INVENTORY_TRANSFER_BEFORE</code> | |||
| Перед перемещением ТМЦ | |||
| <code>inventory_id</code>, <code>operation_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::INVENTORY_TRANSFERRED</code> | |||
| После перемещения ТМЦ | |||
| <code>inventory_id</code>, <code>operation_id</code>, <code>data</code> | |||
|} | |||
=== Задания === | |||
{| class="wikitable" | |||
! Константа | |||
! Когда вызывается | |||
! Данные <code>$event</code> | |||
|- | |||
| <code>CustomEvent::TASK_CARD_RENDER</code> | |||
| При выводе содержимого в карточке задания | |||
| <code>task_type_id</code>, <code>task_id</code>, <code>employee_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::TASK_WORK_DATE_RENDER</code> | |||
| При выводе содержимого возле даты работ | |||
| <code>task_type_id</code>, <code>task_id</code>, <code>employee_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::TASK_CREATE_BEFORE</code> | |||
| Перед созданием задания | |||
| <code>task_type_id</code>, <code>author_employee_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::TASK_CREATED</code> | |||
| После создания задания | |||
| <code>task_id</code>, <code>task_type_id</code>, <code>author_employee_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::TASK_DELETE_BEFORE</code> | |||
| Перед удалением задания | |||
| <code>task_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::TASK_CHANGE_BEFORE</code> | |||
| Перед редактированием задания | |||
| <code>task_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::TASK_CHANGED</code> | |||
| После редактирования задания | |||
| <code>task_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::TASK_COMMENT_CREATE_BEFORE</code> | |||
| Перед добавлением комментария к заданию | |||
| <code>task_id</code>, <code>comment</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::TASK_COMMENT_CREATED</code> | |||
| После добавления комментария к заданию | |||
| <code>task_id</code>, <code>comment_id</code>, <code>comment</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::TASK_COMMENT_CHANGED</code> | |||
| После редактирования комментария к заданию | |||
| <code>task_id</code>, <code>comment_id</code>, <code>comment</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::TASK_COMMENT_TEXT_RENDER</code> | |||
| При выводе текста комментария задания | |||
| <code>task_id</code>, <code>comment_id</code>, <code>comment</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::TASK_DIVISION_ASSIGN_BEFORE</code> | |||
| Перед добавлением подразделения к заданию | |||
| <code>task_id</code>, <code>division_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::TASK_DIVISION_REMOVE_BEFORE</code> | |||
| Перед исключением подразделения из задания | |||
| <code>task_id</code>, <code>division_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::TASK_EMPLOYEE_ASSIGN_BEFORE</code> | |||
| Перед добавлением исполнителя к заданию | |||
| <code>task_id</code>, <code>employee_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::TASK_EMPLOYEE_REMOVE_BEFORE</code> | |||
| Перед исключением исполнителя из задания | |||
| <code>task_id</code>, <code>employee_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::TASK_WATCHER_DIVISION_ASSIGN_BEFORE</code> | |||
| Перед добавлением подразделения наблюдателем | |||
| <code>task_id</code>, <code>division_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::TASK_WATCHER_EMPLOYEE_ASSIGN_BEFORE</code> | |||
| Перед добавлением сотрудника наблюдателем | |||
| <code>task_id</code>, <code>employee_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::TASK_OBJECT_ATTACHED</code> | |||
| После добавления объекта к заданию | |||
| <code>task_id</code>, <code>object_type</code>, <code>object_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::TASK_OBJECT_DETACHED</code> | |||
| После исключения объекта из задания | |||
| <code>task_id</code>, <code>object_type</code>, <code>object_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::TASK_RETURNED_TO_AUTHOR</code> | |||
| После возврата задания автору | |||
| <code>task_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::TASK_STATE_CHANGE_BEFORE</code> | |||
| Перед изменением статуса задания | |||
| <code>task_id</code>, <code>old_state_id</code>, <code>new_state_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::TASK_STATE_CHANGED</code> | |||
| После изменения статуса задания | |||
| <code>task_id</code>, <code>old_state_id</code>, <code>new_state_id</code>, <code>data</code> | |||
|} | |||
=== Сооружения связи === | |||
{| class="wikitable" | |||
! Константа | |||
! Когда вызывается | |||
! Данные <code>$event</code> | |||
|- | |||
| <code>CustomEvent::NODE_CARD_ADDITIONAL_DATA_RENDER</code> | |||
| При выводе дополнительного содержимого в карточке сооружения связи | |||
| <code>node_id</code>, <code>data</code> | |||
|- | |||
| <code>CustomEvent::NODE_CARD_PRIMARY_CONTENT_RENDER</code> | |||
| При выводе основного содержимого в карточке сооружения связи | |||
| <code>node_id</code>, <code>data</code> | |||
|} | |||
Текущая версия от 17:59, 21 мая 2026
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
|