Расширение функционала

ERP можно расширять конфигурацией, JEXL-выражениями, динамическим Java-кодом, серверными сервисами, обработчиками событий и плагинами. Этот раздел помогает выбрать подходящий механизм и не обойти транзакции, события и проверки платформы.

Как выбрать механизм

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

Настроить тип процесса, форму или правило

Конфигурация типа процесса и конструктор правил

Если готового условия или действия недостаточно

Вычислить условие или короткое значение

JEXL

Если выражение разрастается, требует повторного использования или сложного тестирования

Изменить процесс, параметры или связанные объекты

Сервисы из ServerContext

Прямой DAO допустим только внутри repository/DAO-слоя или в явно обоснованном исключении

Обработать доменное событие

AnnotatedEventListener либо штатный listener

Динамический listener нужен для инсталляционной логики, которую нельзя разместить в поставке

Добавить крупную изолированную возможность

Плагин или отдельный Gradle-модуль

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

Выполнять работу по расписанию

планировщик или модуль Task

Не создавайте собственные потоки и ExecutorService

Сервисы в ServerContext

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

Основные страницы:

В Kotlin сервис получают типобезопасно:

import ru.bgcrm.context.core.ServerContext
import ru.bgcrm.context.core.getService
import ru.bgcrm.model.process.service.ProcessService

val processService = ServerContext.get().getService<ProcessService>()
val process = processService.getProcess(processId)

В Java используется интерфейс класса:

ProcessService processService = ServerContext.lookup(ProcessService.class);
Process process = processService.getProcess(processId);
ServerContext должен быть инициализирован для текущего потока. В HTTP-запросе, обработчике события и задаче платформы это уже сделано. Для самостоятельного запуска используйте withContext/runWithContext и явно определите пользователя и транзакционный режим.

JEXL

JEXL предназначен для коротких условий, вычислений и шаблонных фрагментов. ERP использует Apache Commons JEXL 3 в строгом режиме: неизвестная переменная или некорректный вызов завершают выполнение ошибкой.

Метод объекта вызывается через точку:

process.statusId == 39 && 1 =~ processParam.addressCityIds(90)

Статические вспомогательные методы доступны через стандартные пространства имён:

Имя Объект

u

ru.bgcrm.util.Utils

tu

ru.bgcrm.util.TimeUtils

su

org.apache.commons.lang.StringUtils

cu

org.apache.commons.collections.CollectionUtils

contextProcessUtils

ru.bgcrm.util.ProcessUtils

NEW_LINE, NEW_LINE2

Один или два перевода строки

log

Буфер диагностических сообщений выражения

Контекст зависит от точки запуска. Для событий процесса доступны process, user, event, form, processParam, processLink, processService, parameterService, processTaskService и другие объекты. Полный список приведён на странице Поддерживаемые объекты JEXL.

Для изменения данных используйте сервисы:

parameterService.updateText(process.id, 63, "Новое значение");
processService.addDescription(process.id, "Параметр обновлён");

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

Многострочный скрипт задаётся синтаксисом конфигурации <<END. Если вызывающая настройка ожидает результат, верните его оператором return:

someExpression=<<END
value = parameterService.getText(process.id, 63);
return !u.isBlankString(value);
END

Документация синтаксиса: Apache Commons JEXL.

Расширение JEXL-контекста

Дополнительные значения для процесса добавляются обработчиком BuildJexlContextEvent. Пользовательские значения объединяются со штатным контекстом, но штатные ключи имеют приоритет. Не переопределяйте имена process, user, context, processService, parameterService и другие системные ключи.

Динамический код

Динамический код — Java-исходники из настроенного каталога dyn, которые компилируются и загружаются без полной сборки ERP. Классы размещаются в пакете ru.bgcrm.dyn…​.

Через раздел администратора Динамический код можно:

  • открыть и изменить исходник;

  • скомпилировать выбранные классы или весь набор;

  • увидеть ошибки и предупреждения компиляции;

  • запустить класс, реализующий Runnable.

Раздел "Динамический код"

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

Динамический код пишется только на Java. Он компилируется против публичного API текущей версии ERP, поэтому после обновления системы его необходимо перекомпилировать и проверить.

Конфигурация, lifecycle компиляции, classloader, административные операции и диагностика описаны на странице Динамический Java-код.

Обработчики событий

Класс динамического обработчика наследуется от ru.bgcrm.event.listener.DynamicEventListener. Имя обработчика задаётся в конфигурации соответствующего типа процесса или параметра. Для новых обработчиков со статически известным составом событий предпочтителен AnnotatedEventListener.

Из обработчика получайте бизнес-сервисы через активный ServerContext. Не создавайте JDBC-соединения самостоятельно и не вызывайте DAO для операций, уже представленных сервисом.

Запуск при старте

Настройка createOnStart создаёт перечисленные классы при старте сервера и тем самым позволяет им зарегистрировать обработчики. Для фоновой периодической работы используйте штатный планировщик или модуль Task, а не runOnStart и бесконечный цикл.

HTTP-вызов динамического класса

/admin/dynamic.do?action=runDynamicClass является внутренним административным действием. Оно требует обычной авторизации и прав ERP. Не публикуйте его как интеграционный API и не передавайте логин с паролем в URL. Для внешней интеграции используйте справочник интеграций, отдельное Struts-действие или HTTP-клиент платформы.

JSP и клиентские расширения

JSP остаётся частью серверного интерфейса и печатных форм. Пользовательские JSP-фрагменты размещайте в каталогах WEB-INF/jspf/…​/custom/, чтобы отделить их от штатных шаблонов.

Новый интерактивный интерфейс следует реализовывать во frontend-исходниках frontend/src/main/resources/webapps/. Каталоги app/webapps/ и build/ содержат результаты сборки и не являются источником.

JSP не должен содержать SQL или сложную бизнес-логику. Подготовьте данные в action/service и передайте их в request; в шаблоне оставьте отображение.

Схема обработки запроса: фильтр SetRequestParamsFilter добавляет в request контекст, action дополняет его данными, JSP формирует результат.

Прохождение request через фильтр и action к JSP

Логирование

ERP использует Log4j 2, конфигурация находится в app/log4j.properties. В коде используйте фасад ru.bgcrm.logging.Logger, а не System.out, java.util.logging, SLF4J или Log4j напрямую.

private val log = Logger.of(MyService::class.java)

log.info { "Обработан процесс $processId" }

Основные файлы — log/all.log и log/errors.log; отдельные подсистемы могут иметь собственные appenders. Не записывайте в лог пароли, токены, содержимое файлов и персональные данные.

Проверка расширения

Минимальная проверка перед поставкой:

  • код компилируется на JDK 21;

  • операции записи выполняются через сервисы и в ожидаемой транзакции;

  • проверены события до и после изменения;

  • повторный вызов не создаёт неожиданные дубли;

  • ошибки содержат безопасное и понятное сообщение;

  • для новой логики есть unit-тесты, а для JEXL — примеры входных данных и граничных значений;

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