-
Kernel
-
Plugins
-
Решения
-
CHANGES
Kernel
Plugins
Решения
CHANGES
This chapter may be skipped at first time of using the system. |
Система BGERP является в первую очередь платформой, поэтому значительный эффект от её применения достигается путём реализации различного рода расширений под специфику пользователя.
BGERP предоставляет сразу несколько уровней для расширения либо модификации стандартной логики. Способы перечислены в порядке возрастания сложности и функциональности. Рекомендуемая практика - переход от простого к более сложному.
Изменение конфигураций продукта согласно документации. Полная обратная совместимость, поведение описано документацией. Using JEXL scripts for more flexibility. No error check, no full-functional IDE>
Develop own Custom plugins using Java, JSP and full-featured IDE.
JEXL - язык коротких выражений.
Используется для написания в конфигурации макросов условных выражений, гибкого вычисления небольших строк. Помимо операторов, описание которых доступно по ссылке в конце раздела, язык поддерживает обращение к функциям Java - объектов, переданных на вход обработчика в зависимости от условий.
Для вызова функции Java объекта необходимо ввести название объекта в контексте, точку и непосредственно вызов функции. Для вызова статического метода класса указывается имя класса с точкой.
Пример использования выражения:
processCreateLink.1.title=Проект (Уфа)
processCreateLink.1.processTypeId=9260
processCreateLink.1.linkType=processDepend
processCreateLink.1.checkExpression=1 =~ processParam.addressCityIds( 90 ) and process.getStatusId() == 39
processCreateLink.1.copyParams=90,89,238
processCreateLink.1.copyLinks=1
В данном случае создание связанного процесса будет доступно только для процессов в статусе с кодом 39 и с наличием адреса в параметре с кодом 90 с городом 1.
В JEXL процессор всегда передаются объекты:
u - статический контекст объекта ru.bgcrm.util.Utils - возможность вызова статических функций;
tu - статический контекст объекта ru.bgcrm.util.TimeUtils - возможность вызова статических функций;
su - статический контекст объекта org.apache.commons.lang3.StringUtils - возможность вызова статических функций;
сu - статический контекст объекта org.apache.commons.collections.CollectionUtils - возможность вызова статических функций;
fu - object of class org.apache.commons.io.FileUtils for calling static methods;
log - объект класса org.apache.log4j.Logger, позволяет вести отладку с помощью log.debug и других вызовов;
NEW_LINE - перенос строки;
NEW_LINE2 - два переноса строки.
Дополнительные объекты передаются в зависимости от места использования.
Типовой дополнительный набор объектов для обработчика процесса:
user - объект класса ru.bgcrm.model.user.User - текущий пользователь;
userParam - объект класса ru.bgcrm.dao.expression.ParamValueFunction - параметры текущего пользователя;
process - объект класс ru.bgcrm.model.process.Process - изменяющийся процесс;
processParam - объект класса ru.bgcrm.dao.expression.ParamValueFunction - параметры изменяющегося процесса;
processLink - объект класса ru.bgcrm.dao.expression.ProcessLinkFunction - привязки изменяющегося процесса;
conSet - объект класса ru.bgcrm.util.sql.ConnectionSet - соединения к БД;
form - объект класса ru.bgcrm.struts.form.DynActionForm - данные по запросу к серверу;
все переменные контекста из ru.bgcrm.servlet.filter.SetRequestParamsFilter;
объект класса ru.bgcrm.dao.expression.ProcessChangeFunctions передаётся как контекст функций по-умолчанию, т.е. все его функции можно вызывать непосредственно в скрипте.
При необходимости выражения могут быть многострочными, при этом результат (если он есть) возвращается оператором return. Пример многострочного скрипта для простого обработчика событий процесса:
onProcessEvent.2.doExpression=<<END
dao = new("ru.bgcrm.dao.ParamValueDAO", conSet.getConnection());
dao.updateParamText(process.getId(), 63, "тест");
END
Часто необходимая информация (детальное описание - по ссылкам далее):
оператор [] - создание массива, {} - HashSet, подойдёт на место Collection;
функция new (см. пример выше) - создание объекта класса, конструктор может быть с параметрами;
операторы проверки наличия объектов в коллекциях: =~ , !~
Методы вызываются у объектов классов с помощью точки, для вызова статического метода используется объект типа java.lang.Class нужного класса, который может быть создан просто записью полного имени класса. Небольшой пример, как вызывать статические методы ru.bgcrm.util.Utils.
u = ru.bgcrm.util.Utils;
v = u.parseInt(3);
Подробная спецификация по языку:
Обратите внимание на вызов функции u.escapeXml - она преобразует все символы HTML разметки в спецпоследовательности. Если не использовать эту функцию для генерации HTML, возможны проблемы, в случае появления в описании процесса символов <,> либо кавычек. Пример ниже. |
processReference.1.stringExpression=u.escapeXml( u.maskNull( u.getFirst( processParam.addressValues( 345, 'fromStreet' ) ) ) ) + " (" + size( processParam.addressValues( 345 ) ) + ")"
Регулярные выражения позволяют гибко описывать шаблоны строк.
Описание строк осуществляется путём подстановки определённых макросов, обозначающих части строки либо символы определённого типа.
Например:
(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 - 75
# 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
log4j.additivity.org.apache.tomcat.util.http.Parameters=false
# 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
log4j.additivity.ru.bgcrm=false
log4j.additivity.org.bgerp=false
# messages starting from WARN
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=true
log4j.appender.filew.MaxBackupIndex=5
log4j.appender.filew.MaxFileSize=10MB
log4j.appender.filew.Threshold=WARN
# messages starting from INFO
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
# messages starting from DEBUG
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=ru.bgcrm.util.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 'ALL, file, session'
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
Сообщения в логе разделяются на уровни (в порядке возрастания): 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:
В конфигурационном файле возможно изменять формат информации в файле, фильтр по классам и другие параметры логирования.
Ссылки:
http://artamonov.ru/2007/04/06/vvedenie-v-log4j/ - вводная статья на русском.
Для более удобной отладки JEXL скриптов, конфигураций либо динамического кода возможно получение логов только текущей сессии с помощью оснастки Пуск / Логирование.
Логирование позволяет отследить вызванную действиями пользователя активность на стороне сервера. Количество строк лога ограничено, отображаются только последние 1000 строк. Каждый раз при переходе в оснастку поле Текущий лог обновляется, то же действие производит повторное нажатие кнопки Включить.
Custom application code has to be placed custom
directory in the project root.
In order to store your custom code you have to create a custom GIT repository and add there permissions of developers, who do you trust. We kindly ask you to use open forks of the Custom GIT template repo: https://github.com/Pingvin235/bgerp-custom , hosted on GitHub. With that you share your experience with other customers.
Be sure that you are not hardcoded any confidential data in your Custom GIT. |
For creating your Custom GIT you have make the following steps.
Make a GitHub account if it you don’t have it and log in with it.
Open the template repo https://github.com/Pingvin235/bgerp-custom and press Fork button.
Rename the fork on Settings tab to bgerp-custom-<MyCompany>, using instead of <MyCompany> a wanted name. For example bgerp-custom-bitel as on the screen below.
Go to Settings / Collaborators and add your trusted developers using their GitHub accounts or e-mails.
Content of the directory may be stored using GIT and developed in full-featured IDE.
The custom
directory is ignored in the root directory of the project, and has to be checked out independently, e.g.:
git clone https://github.com/Company/bgerp-custom-company.git custom
A GIT URL can be taken from GitHub UI.
Once you did changes, run the commands for pushing them in custom
directory.
git pull --rebase && git add . && git commit -m "My changes" && git push
The same clone command has to be run in application directory, e.g.
git clone https://github.com/Company/bgerp-custom-company.git /opt/bgerp/custom
For checking out changes out of CUSTOM GIT may be used approach with DETACHED HEAD:
git fetch && git checkout origin/master
With CUSTOM GIT also can be used the same GIT workflow as for the main project’s code. Any change has to be placed in a separated branch.
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 files have to be stored under paths: custom/org/bgerp/plugin/custom/<some-name> .
That code has equal possibilities as the native application’s, can use API and connected libraries. After compilation Administration - Custom this code is persisted to lib/app/custom.jar
.
The application must be restarted to reload the custom.jar . There is Restart button available after successful compilation. |
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. |
In file custom/l10n.xml
has a special meaning for localization system, it allows to re-define each localized string in the system.
Пользовательские Java библиотеки, используемые в динамическом коде, JEXL выражениях либо JSP страницах должны быть размещены в каталоге lib/custom JAR файлы из lib/ext перетираются при получении обновления библиотек.
This approach is deprecated, use Custom instead. |
This approach is deprecated, use Custom instead. |
With custom JAR in the application might be added any wanted Java classes.
This sample describes deploing own annotated servlets in
Links: |
For making own servlet write annotated class extending javax.servlet.http.HttpServlet.
@WebServlet(name="RestApi", urlPatterns = "/restapi")
Compile to *.class, assemple to a JAR file, place it in previously created directory webapps/WEB-INF/lib
To be loaded the JAR file has to be named with custom in name, or you may change the marker string in configuration parameter custom.jar.marker.
В составе поставки доступны примеры динамических классов в dyn/ru/bgcrm/dyn поименованные как Example.. с описанием в комментариях. Помимо этого вы можете найти примеры динамического кода здесь.
Во всех данных примерах могут использоватся как классы из библиотек системы, так и custom.
Имя класса-обработчика может быть указано в свойствах типа процесса. Создаваемые динамические классы - обработчики событий должны расширять абстрактный класс ru.bgcrm.event.listener.DynamicEventListener. Информацию по типам событий можно получить из API документации к системе в формате JavaDoc.
Параметры runOnStart и createOnStart в конфигурации сервера. Указанные в них объекты классов создаются и запускаются для runOnStart при старте сервера.
<server>/admin/dynamic.do?action=runDynamicClass&iface=<iface>&class=<className>&j_username=<user>&j_password=<pswd>¶m1=value¶m2=..
Где:
<server> - host and port of the server;
<className> - имя динамического класса;
<user> и <pswd> - логин и пароль пользователя BGERP, подробнее о запросах внешних систем;
<iface> - тип класса-обработчика, подробнее ниже.
При параметре <iface> равным event класс должен расширять абстрактный класс ru.bgcrm.event.listener.DynamicEventListener в который передаётся событие ru.bgcrm.event.RunClassRequestEvent. В противном случае класс может реализовать интерфейс java.lang.Runnable, который просто будет запущен.
Для запуска любого класса, статического или динамического в контексте сервера BGERP вызовите:
./crm.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¤tUserMode=&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