# ExtensionS

| This chapter may be skipped at first time of using the system.

Система BGERP является в первую очередь платформой, поэтому значительный эффект от её применения достигается путём реализации различного рода расширений под специфику пользователя.

## Choose technology CCC

BGERP предоставляет сразу несколько уровней для расширения либо модификации стандартной логики. Способы перечислены в порядке возрастания сложности и функциональности. Рекомендуемая практика - переход от простого к более сложному.

### Config

Very many things in our product can be changed using configurations, which are well-documented and stable after version updates. Use JEXL scripts for more flexibility.

### Custom

Develop own Custom plugins using Java, JSP and full-featured IDE.

### Contribute

Обобщение функциональности, есть требования к оформлению передаваемого кода и документации. Переданный код постоянно находится "под присмотром", учитывается при изменениях продукта, равно как и используемые библиотеки.

## Config

### Pattern

Conditional pattern allows substitutions text before ${key} text after of ${key} with different values, like in many other different template engines. Specifically for our product the pattern supports conditional substitutions like text before (optional text before ${key} optional text after) text after, where the whole optional text before ${key} optional text after part has been added only when value of ${key} is not blank.

### JEXL

JEXL - short expression’s language.

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

Cheat Sheets:

- operator [] - creates List, {} - Set;

- function new - creates object of class, the name of it passed as the first parameter;

- operators for checking presence values in collections: =~ , !~;

- expression can be multiline, the result if exists, passed with operator return.

References:

- http://commons.apache.org/jexl/reference/syntax.html#Functions

- https://commons.apache.org/proper/commons-jexl/apidocs/org/apache/commons/jexl3/package-summary.html

In JEXL processor always passed the variables:

- u - static context of class ru.bgcrm.util.Utils - for static methods calls;

- tu - static context of class ru.bgcrm.util.TimeUtils - for static methods calls;

- tc - static context of class org.bgerp.util.TimeConvert - for static methods calls;

- su - static context of class org.apache.commons.lang3.StringUtils - for static methods calls;

- сu - static context of class org.apache.commons.collections.CollectionUtils - for static methods calls;

- fu - object of class org.apache.commons.io.FileUtils for calling static methods;

- log - object of class org.bgerp.util.Log, allows debugging using log.debug and other calls;

- NEW_LINE - line break;

- NEW_LINE2 - two line breaks.

Variables for process handling:

- user - object of class ru.bgcrm.model.user.User - текущий пользователь;

- up or userParam - object of class org.bgerp.dao.expression.ParamExpressionObject - параметры текущего пользователя;

- p or process - object of class ru.bgcrm.model.process.Process - изменяющийся процесс;

- pp or processParam - object of class org.bgerp.dao.expression.ParamExpressionObject - параметры изменяющегося процесса;

- pl or processLink - object of class org.bgerp.dao.expression.ProcessLinkExpressionObject - привязки изменяющегося процесса;

- ui - object of class org.bgerp.dao.expression.UiExpressionObject, UI features;

- conSet - object of class ru.bgcrm.util.sql.ConnectionSet - соединения к БД;

- form - object of class ru.bgcrm.struts.form.DynActionForm - данные по запросу к серверу;

- все переменные контекста из ru.bgcrm.servlet.filter.SetRequestParamsFilter;

- object of class org.bgerp.dao.expression.ProcessChangeExpressionObject передаётся как контекст функций по-умолчанию, т.е. все его функции можно вызывать в скрипте без префикса параменной с точкой.

### Java REGEXP

Регулярные выражения позволяют гибко описывать шаблоны строк.

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

Например:

- (342) - это символы 342 следующие один за другим;

- 3\d2 - это 3 затем любая цифра и 2;

- 342)|(559 - последовательность симоволов 342 либо 559;

- 44[2-8] - строки 442, 443, 444, 445, 446, 447, 448.

Расшифровка некоторых макросов:

- а-b - на этом месте может располагаться симовол от a до b (в таблице символов);

- [abc] - на этом месте может располагаться любой из символов a, b либо c;

- abc - последовательное расположение символов a, b, c;

- abc)|(def - на этом месте последовательно располагаются abc либо def, () - группа символов.

Ссылки:

- http://www.opennet.ru/docs/RUS/perlre_man/ - регулярные выражения Perl, практически идентичны Java.

- http://j2w.blogspot.com/2008/01/java.html - регулярные выражения Java.

- http://docs.oracle.com/javase/1.5.0/docs/api/java/util/regex/Pattern.html - спецификация на английском.

### Логирование

Log4j - библиотека логирования для Java. Настройка логирования производится в файле log4j.properties, изменение файла можно производить при работающем приложении. Вид файла при установке системы:

    // PzdcDoc snippet of: 'log4j.properties', lines: 1 - 92

    # factory for making loggers out of the configuration with additivity=false
    log4j.loggerFactory=org.bgerp.util.log.LoggerFactory

    # libraries logging
    log4j.rootLogger=WARN, file, session

    # prevent messages "Invalid chunk starting at byte [0] and ending at byte [0] with a value of [null] ignored"
    log4j.logger.org.apache.tomcat.util.http.Parameters=ERROR, file

    # the application logging
    # add ending ', out' appender when running inside IDE to see INFO output also in STDOUT
    # or ', outa' to see all output in STDOUT
    log4j.logger.ru.bgcrm=ALL, filew, file, filed, session
    log4j.logger.org.bgerp=ALL, filew, file, filed, session

    # only WARN messages
    log4j.appender.filew=org.apache.log4j.RollingFileAppender
    log4j.appender.filew.layout=org.apache.log4j.PatternLayout
    log4j.appender.filew.layout.ConversionPattern=%d{MM-dd/HH:mm:ss} %5p [%t] %c{1} - %m%n
    log4j.appender.filew.encoding=UTF-8
    log4j.appender.filew.File=./log/bgerp.warn.log
    log4j.appender.filew.Append=false
    log4j.appender.filew.MaxBackupIndex=0
    log4j.appender.filew.MaxFileSize=10MB
    log4j.appender.filew.Threshold=WARN
    log4j.appender.filew.filter.a=org.apache.log4j.varia.LevelMatchFilter
    log4j.appender.filew.filter.a.LevelToMatch=WARN
    log4j.appender.filew.filter.a.AcceptOnMatch=true
    log4j.appender.filew.filter.b=org.apache.log4j.varia.LevelMatchFilter
    log4j.appender.filew.filter.b.LevelToMatch=ERROR
    log4j.appender.filew.filter.b.AcceptOnMatch=false

    # INFO, WARN, ERROR messages
    log4j.appender.file=org.apache.log4j.RollingFileAppender
    log4j.appender.file.layout=org.apache.log4j.PatternLayout
    log4j.appender.file.layout.ConversionPattern=%d{MM-dd/HH:mm:ss} %5p [%t] %c{1} - %m%n
    log4j.appender.file.encoding=UTF-8
    log4j.appender.file.File=./log/bgerp.log
    log4j.appender.file.Append=true
    log4j.appender.file.MaxBackupIndex=5
    log4j.appender.file.MaxFileSize=10MB
    log4j.appender.file.Threshold=INFO

    # DEBUG, INFO, WARN, ERROR messages
    log4j.appender.filed=org.apache.log4j.RollingFileAppender
    log4j.appender.filed.layout=org.apache.log4j.PatternLayout
    log4j.appender.filed.layout.ConversionPattern=%d{MM-dd/HH:mm:ss} %5p [%t] %c{1} - %m%n
    log4j.appender.filed.encoding=UTF-8
    log4j.appender.filed.File=./log/bgerp.debug.log
    log4j.appender.filed.Append=true
    log4j.appender.filed.MaxBackupIndex=5
    log4j.appender.filed.MaxFileSize=10MB
    log4j.appender.filed.Threshold=DEBUG

    # all messages
    log4j.appender.filea=org.apache.log4j.RollingFileAppender
    log4j.appender.filea.layout=org.apache.log4j.PatternLayout
    log4j.appender.filea.layout.ConversionPattern=%d{MM-dd/HH:mm:ss} %5p [%t] %c{1} - %m%n
    log4j.appender.filea.encoding=UTF-8
    log4j.appender.filea.File=./log/bgerp.all.log
    log4j.appender.filea.Append=true
    log4j.appender.filea.MaxBackupIndex=5
    log4j.appender.filea.MaxFileSize=10MB

    log4j.appender.session=org.bgerp.util.log.SessionLogAppender
    log4j.appender.session.layout=org.apache.log4j.PatternLayout
    log4j.appender.session.layout.ConversionPattern=%d{MM-dd/HH:mm:ss} %5p [%t] %c{1} - %m%n
    log4j.appender.session.Threshold=DEBUG

    # info out, for running in IDE add it after comma at the end of 13 and 14 lines
    log4j.appender.out=org.apache.log4j.ConsoleAppender
    log4j.appender.out.Target=System.out
    log4j.appender.out.layout=org.apache.log4j.PatternLayout
    log4j.appender.out.layout.ConversionPattern=%d{MM-dd/HH:mm:ss} %5p [%t] %c{1} - %m%n
    log4j.appender.out.Threshold=INFO

    # all stdout, for debuging in IDE connect it to newly added loggers
    log4j.appender.outa=org.apache.log4j.ConsoleAppender
    log4j.appender.outa.Target=System.out
    log4j.appender.outa.layout=org.apache.log4j.PatternLayout
    log4j.appender.outa.layout.ConversionPattern=%d{MM-dd/HH:mm:ss} %5p [%t] %c{1} - %m%n

    # sent mails
    log4j.logger.org.bgerp.util.mail.MailMsg=INFO, mail
    log4j.appender.mail=org.apache.log4j.RollingFileAppender
    log4j.appender.mail.layout=org.apache.log4j.PatternLayout
    log4j.appender.mail.layout.ConversionPattern=%d{MM-dd/HH:mm:ss} %5p [%t] %c{1} - %m%n
    log4j.appender.mail.encoding=UTF-8
    log4j.appender.mail.File=./log/mail.log
    log4j.appender.mail.Append=true
    log4j.appender.mail.MaxBackupIndex=5
    log4j.appender.mail.MaxFileSize=10MB

Сообщения в логе разделяются на уровни (в порядке возрастания): DEBUG, INFO, WARN, ERROR, FATAL. По-умолчанию настроен уровень INFO, т.е. выводятся информационные и ошибочные сообщения (INFO, FATAL, ERROR), отладка не выводится. Вывод осуществляется в файл log/bgerp.log, который обрезается на размере 10МБ с созданием отдельных файлов.

Samples, how to enable loggers wanted package or classes to log/bgerp.all.log in:

- Plugin Asterisk

- Plugin Auth

В конфигурационном файле возможно изменять формат информации в файле, фильтр по классам и другие параметры логирования.

To observe only the logs of the current user session, use User Session Log.

Ссылки:

- http://artamonov.ru/2007/04/06/vvedenie-v-log4j/ - вводная статья на русском.

## Custom

Custom application code has to be placed `custom` directory in the project root.

### Source

#### Java

Inside `custom/src` placed regular Java code, including plugins plugins. PLUGIN_ID for those has to be prefixed by custom., e.g. custom.bitel. Respectively plugin Java classes have to be stored under paths: `custom/src/org/bgerp/plugin/custom/<PLUGIN_ID_AFTER_DOT>`, e.g. `custom/src/org/bgerp/plugin/custom/bitel`.

That code has equal possibilities as the native application’s, can use API and connected libraries. After compilation Administration / Custom the bytecode is persisted to `lib/app/custom.jar`.

The custom plugins can named without custom. prefix and their Java code may be placed to other then org.bgerp.plugin.custom packages, but in this case the custmo classes are not dynamically reloaded after each successfull compilation. They are taken together with built-in Java classes only during the app’s startup, so there is a Restart button in Custom tool available for that.

#### WebApp

Subdirectory `custom/webapps` is searched before `webapps` from root directory and should be used for placing custom JSP and JS files. Both types are applied immediately after change.

| Each file from the original `webapps` may be "replaced" for Web server. That can brake built-in functionality.

#### IDE

For comfortable development and reducing mistakes is strongly recommended editing your Custom in full-futured IDE. Check the BGERP project out of GIT repository and make `custom` directory inside.

After modification the changed files have to be copied to `custom` directory of the running server. For version control of a Custom, can be used GIT system.

| Not currently used custom directories in project can be renamed with underscore prefix, like `_custom_atel` on the screen above

### Localization

In file `custom/l10n.xml` has a special meaning for localization system, it allows to re-define each localized string in the system.

### Java Libraries

Additional third-party Java libraries, used in Custom solutions, must be stored in `lib/custom` directory, as JAR files in `lib/ext` are overwritten during libraries update.

### GIT

Storing custom sources in a GIT repository allows you to track all made changes and always have backup copy of your work.

#### Repository

In order to store your custom code you have to create a GIT repository and add there permissions of developers, who do you trust. We offer for our Subscribers free hosting of their Custom GIT on our GitLab server. Please, contact your Consultant for creating your GitLab accounts and making such a fork of https://git.bgerp.org/bgerp/custom/bgerp-custom

#### Workflow

Use bgerp-custom-<YOUR_COMPANY_NAME> instead of bgerp-custom-company.

##### IDE

Content of the `custom` directory may be stored using GIT and developed in full-featured IDE. First you have to check the BGERP project out from GitHub. After make it recognized and compiled in one of supported IDE.

The `custom` directory has to be checked out inside of the project directory independently.

    git clone https://git.bgerp.org/bgerp/custom/bgerp-custom-company.git BGEPR/custom

The HTTPS GIT URL can be taken from GitLab UI.

Once you did changes, run the commands for pushing them in `custom` directory.

    git add . &&  git commit -m "My changes in IDE Custom" && git push

After pull the changes in `custom` directory of app.

    git pull --rebase

The easiest workflow, described above, uses only a singe master branch. But you can also use the same GIT workflow as for the main project’s code. Any change there is placed to a separated branch.

| Be sure that the latest changes from your `bgerp/custom` directory are pushed to the GIT repository.

##### App

You have to upload your Public key here. Typically you can find the Linux user’s SSH key in file `~/.ssh/id_(rsa|ed25519|ecdsa).pub` Case there is none exists yet, generate it.

The SSH GIT URL, allowing to use key authentication in console can be taken from GitLab UI.

Here is the example of clone command with SSH GIT URL has to be run in application directory.

    git clone ssh://git@git.bgerp.org:822/bgerp/custom/bgerp-custom-company.git /opt/bgerp/custom

Use plugin GIT for updating the Custom code, changed in IDE.

#### Update Fork

Use GitLab UI for updating your Custom fork to the actual state of the Custom Base repo.

## Запуск кода

Во всех данных примерах могут использоватся как классы из библиотек системы, так и custom.

### При старте сервера

Параметры runOnStart и createOnStart в конфигурации сервера. Указанные в них объекты классов создаются и запускаются для runOnStart при старте сервера.

### HTTP запросом

    <server>/admin/run.do?method=runClass&iface=<iface>&class=<className>&j_username=<user>&j_password=<pswd>&param1=value&param2=..

Где:

- <server> - host and port of the server;

- <className> - имя динамического класса;

- <user> и <pswd> - логин и пароль пользователя BGERP, подробнее о запросах внешних систем;

- <iface> - тип класса-обработчика, подробнее ниже.

При параметре <iface> равным event класс должен реализовывать интерфейс org.bgerp.app.event.iface.EventListener в который передаётся событие ru.bgcrm.event.RunClassRequestEvent. В противном случае класс может реализовать интерфейс java.lang.Runnable, который просто будет запущен.

### Из командной строки

Для запуска любого класса, статического или динамического в контексте сервера BGERP вызовите:

    ./erp.sh "runclass <class_name>"

Где <class_name> - полное имя класса с пакетом. Класс должен реализовывать интерфейс java.lang.Runnable.

Запуск в контексте сервера обозначает, что класс будет выполнен в рамках отдельного потока процесса сервера, получив доступ к соединению с БД, конфигурациям и другим объектам контекста. Результаты работы можно выводить в логи.

### Планировщик

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

## Интеграция с внешними системами

Все запросы на изменение данных в возвращают результат в JSON формате. Запросы выборки данных возвращают результат в HTML формате, однако возможно получение данных и в JSON формате, путём добавления в запрос параметра responseType=json.

Для прозрачной авторизации запроса сторонней системы логин и пароль пользователя могут быть переданы в запросе в HTTP параметрах запроса j_username и j_password соответственно. Параметр authToSession=0 в запросе указывает на хранение отсутствие необходимости в HTTP сессии. Настоятельно рекомендуется использовать его при запросах внешних систем, т.к. предотвращение создания HTTP сессий экономит память BGERP.

Пример запроса на получение данных во внешнюю систему в JSON формате (выборка по очереди процессов):

    https://bgerp.company.com/user/process.do?action=queueShow&id=4&dateStatusStatus=10&status=10&status=9&status=13&currentUserMode=&group=7&sort=0&j_username=shamil&j_password=*****&responseType=json&authToSession=0

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

Another sample for retrieving user list. Notice the request parameter page.pageIndex=-1 for disabling pagination.

    https://demo.bgerp.org/admin/user.do?action=userList&j_username=admin&j_password=admin&responseType=json&authToSession=0&page.pageIndex=-1

For complex data reading Plugin DBA with SQL queries is recommend you to use, an example:

    https://demo.bgerp.org/admin/plugin/dba/query.do?query=SELECT%20id,%20title%20FROM%20user&j_username=admin&j_password=admin&responseType=json&authToSession=0&page.pageIndex=-1