Расширение функционала
Система ERP является в первую очередь платформой, поэтому значительный эффект от её применения достигается путём реализации различного рода расширений под специфику пользователя. Вы можете пропустить данный раздел целиком при первичном знакомстве с системой.
JEXL, REGEXP, Log4j
JEXL
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.lang.StringUtils - возможность вызова статических функций;
-
сu - статический контекст объекта org.apache.commons.collections.CollectionUtils возможность вызова статических функций;
-
NEW_LINE - перенос строки;
-
NEW_LINE2 - два переноса строки.
Дополнительные объекты передаются в зависимости от места использования. При необходимости выражения могут быть многострочными, при этом результат (если он есть) возвращается оператором 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 ) ) + ")"
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
Log4j - библиотека логирования для Java. Настройка логирования производится в файле log4j.properties, изменение файла можно производить при работающем приложении. Вид файла при установке системы:
log4j.rootLogger=INFO, file
log4j.logger.ru.bgcrm=INFO, file
log4j.additivity.ru.bgcrm=false
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{MM-dd/HH:mm:ss} %5p [%t] %c{1} -
%m%n
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.File=./log/bgcrm.log
log4j.appender.file.Append=true
log4j.appender.file.BufferedIO=false
log4j.appender.file.BufferSize=1024
log4j.appender.file.MaxBackupIndex=5
log4j.appender.file.MaxFileSize=10MB
Сообщения в логе разделяются на уровни (в порядке возрастания): DEBUG, INFO, WARN, ERROR, FATAL. Поумолчанию настроен уровень INFO, т.е. выводятся информационные и ошибочные сообщения (INFO, FATAL, ERROR), отладка не выводится. Вывод осуществляется в файл log/bgcrm.log, который обрезается на размере 10МБ с созданием отдельных файлов.
Для включения вывода отладочной информации необходимо установить:
log4j.logger.ru.bgcrm=DEBUG, file
В конфигурационном файле возможно изменять формат информации в файле, фильтр по классам и другие параметры логирования.
XSLT 2.0
Устаревшая технология. Использование XSLT более не развивается в продукте. Первоначально использовалась для генерации документов в плагине Document, но в новых версиях вместо XSLT могут использоваться JSP шаблоны. |
XSLT - язык, основанный на формате XML. Его назначение - трансформация XML дерева с данными в какой-либо результирующий формат. Например: TXT, XHTML (HTML документ, соответсвующий правилам формата XML). Трансформация производится XSLT процессором.
Версия 2.0 является существенным расширением версии 1.0, ключевые изменения можно посмотреть здесь: http://www.xmlhack.ru/texts/02/xslt20/xslt20.html

В XSLT шаблоне различаются просто теги, которые без изменений перейдут в результирующий документ и управляющие теги для процессора с префиксом xslt. Пример фрагмента XSLT документа:
<tbody>
<xsl:for-each select="bills/bill">
<xsl:variable name="uid" select="@uid"/>
<tr>
<td nowrap="nowrap"><xsl:value-of select="@number"/></td>
<td><xsl:value-of select="@create_dt"/></td>
<td><xsl:value-of select="@pay_dt"/></td>
<td nowrap="nowrap"><xsl:value-of select="@summ"/></td>
<td nowrap="nowrap">
<xsl:choose>
<xsl:when test="@status=0">не оплачен</xsl:when>
<xsl:otherwise>оплачен</xsl:otherwise>
</xsl:choose>
</td>
<td>
<xsl:choose>
<xsl:when test="$uid=-1">создан Вами</xsl:when>
<xsl:otherwise>создан администратором</xsl:otherwise>
</xsl:choose>
< /td>
Здесь форматируется XHTML документ, при этом используются стандартые HTML теги (tr, td) и управляющие теги процессора (xsl:choose, xsl:value-of). Рассмотрим несколько XSLT директив, встречающихся в приведенном фрагменте:
-
<xsl:for-each select="bills/bill"> - для каждого узла bills/bill исходного дерева XML данных выполнить то что указано до </xsl:for-each>
-
<xsl:variable name="uid" select="@uid"/> - создать переменную uid и присвоить ей значение из атрибута uid текущего узла bill
-
<xsl:value-of select="@number"/> - вставить значение атрибута number текущего элемента bill
-
<xsl:choose> - условный оператор, аналог case либо if-else, внутри могу быть несколько <xsl:when> условий и действие по умолчанию <xsl:otherwise> Ниже приведены ссылки на руководства по XSLT.
Язык разметки XSLT тесно завязан с языком XPath - языком выборки данных в XML деревьях. XSLT процессор "Saxon HE" используемый в ERP поддерживает спецификации XSLT и XPath версий 2.0 и 2.0.
-
http://ru.wikipedia.org/wiki/XSLT - статья в Wikipedia
-
http://www.xmlhack.ru/texts/02/xslt20/xslt20.html - отличия XSLT 2.0 от 1.0 версии
-
http://www.xmlhack.ru/texts/02/xpath20/xpath20.html - отличия XPath 2.0 от 1.0 вервсии
-
http://www.shttps://www.saxonica.com/documentation11/documentation.xml - реализованные в процессере Saxon функции XSLT
-
http://www.w3.org/TR/xslt20/ - XSLT 2.0 спецификация
-
http://www.w3.org/TR/xpath20/ - XPath 2.0 спецификация
-
http://www.w3.org/TR/xpath-datamodel/ - модель данных XPath
Динамический код
Динамический код - это Java файлы, которые можно изменять и подгружать без перезапуска приложения. С его помощью можно обрабатывать различные события в системе. Файлы с классами динамического кода располагаются в по-умолчанию каталоге dyn.
Динамические классы необходимо создавать в пакете ru.bgcrm.dyn.<дальнейшая иерархия пакетов> и в соответствующем каталоге.
Параметры динамического кода могут быть настроены в конфигурации. |
Для написания динамического кода возможно использование как простого текстового редактора, так и полноценные IDE для Java разработки. Методология при этом аналогична применяемой для разработки в ABilling.
Компиляция динамического кода осуществляется на вкладке Динамический код интерфейса администратора. Можно скомпилировать только все классы сразу. При успешной компиляции динамический код применяется также целиком.

Как видно из снимка экрана, помимо компиляции в оснастке возможно создание и запуск объекта класса реализующего интерфейс java.lang.Runnable. Кроме данного способа возможны перечисленные в последующих разделах способы создания и применения объектов динамических классов.
Все нижеперечисленные методы подходят и для работы с обычными классами загружаемыми из JAR файлов каталога lib.
Обработчики событий процессов
Имя класса-обработчика может быть указано в свойствах типа процесса. Создаваемые динамические классы - обработчики событий должны расширять абстрактный класс ru.bgcrm.event.listener.DynamicEventListener. Информацию по типам событий можно получить из API документации к системе в формате JavaDoc.
Запуск и создание объектов при старте сервера
Параметры runOnStart и createOnStart в конфигурации сервера. Указанные в них объекты классов создаются и запускаются для runOnStart при старте сервера.
Вызов динамического класса HTTP запросом
<crmUrl>/admin/dynamic.do?action=runDynamicClass&iface=<iface>&class=<className>&j_
username=<user>&j_password=<pswd>¶m1=value¶m2=..
Где:
-
<crmUrl> - URL и порт ERP;
-
<className> - имя динамического класса;
-
<user> и <pswd> - логин и пароль пользователя ERP;
-
<iface> - тип класса-обработчика, подробнее ниже.
При параметре <iface> равным event класс должен расширять абстрактный класс ru.bgcrm.event.listener.DynamicEventListener в который передаётся событие ru.bgcrm.event.RunClassRequestEvent. В противном случае класс может реализовать интерфейс java.lang.Runnable, который просто будет запущен.
JSP
JSP шаблоны используются в ERP для генерации всего HTML интерфейса, а также как шаблоны печатных форм в плагине Document. В сочетании с библиотекой тегов и возможностью лёгкого вызова Java кода, JSP предоставляет обширные возможности по адаптации интерфейса.
Пользовательские JSP страницы располагайте в каталогах WEB-INF/jspf/…/custom/.., это гарантирует вас от перетирания названия каталога штатными шаблонами. Например: "/WEB-INF/jspf/user/process/process /custom/process_jur/zayavka.jsp". |
Вкратце схема работы JSP шаблона представлена на схеме ниже.
На вход шаблона передаётся объект Request. Изначально это чаще всего HTTP запрос с параметрами, но в общем и целом это просто некий Map c Java объектами. По мере обработки запроса в этот Map помещаются:

-
ссылки на объекты контекста, чаще всего различные справочники в фильтре ru.bgcrm.servlet.filter. SetRequestParamsFilter;
-
объект с параметрами запроса ru.bgcrm.struts.form.DynActionForm;
-
различные данные после отработки серверных Action ов.
Любой объект в Request доступен в JSP шаблоне как ${object}. Для вызова метода ${object.method()}. На выход шаблона образуется по сути текст, который указан в шаблоне, за исключением специальных инструкций. Это может быть HTML, передаваемый на клиента, либо простой текст. Специальные инструкции, оперируя с объектами из Request, добавляют в этот текст дополнительные данные.
JSP технология предоставляет возможность расширения функциональности с использованием библиотек тегов и функций. ERP также предоставляет собственный набор тегов, который особенно активно используются, например, для формирования интерфейса пользователя.
Обзор использования тегов с элементами управления пользователя вы можете посмотреть в файле webapps/test.jsp, для выполнения шаблона наберите в браузере -http://<host>:<port>/test.jsp. Полный обзор библиотеки ERP вы можете получить в файлах объявлениях webapps/WEB-INF/tld/…
Создание произвольных Java объектов
Хотелось бы особо отметить следующую возможность. Создание произвольного Java объекта, использование параметризованного конструктора, что не позволяет стандартный тег . Данный подход позволяет существенно уменьшить использование скриплетов (вставок Java кода).
<u:newInstance var="paramDao" clazz="ru.bgcrm.dao.ParamValueDAO">
<u:param value="${conSlave}"/>
</u:newInstance>
Изменение данных в Request
Шаблон может менять объекты в Request. Этот немного нестандартный метод используется в плагине ink:/bgcrm/docs/basic_documentation/plugin/Document/document[Document] для подготовки шаблоном структурированных данных, вставляемых в дальнейшем в шаблоны документов PDF либо DOCX/ODT.
Изменение файлов системы
При корректировке штатных файлов системы: JSP страниц за исключением custom, конфигурационных файлов, библиотек и т.п. предварительно необходимо создать файл с аналогичным именем но дополненным .orig в конце. Например: process_color.jsp → process_color.jsp.orig. В противном случае изменённый файл будет перетёрт при следующем обновлении. При наличии .orig файла программа сверит файл из обновления с ним и перетрёт файл в дистрибутиве только в случае несовпадения, сохранив резервную копию перетираемого файла. .orig файл обозначает версию оригинального файла, относительно которого был создан изменённый.
Интеграция с внешними системами
Все запросы на изменение данных в возвращают результат в JSON формате. Запросы выборки данных возвращают результат в HTML формате, однако возможно получение данных и в JSON формате, путём добавления в запрос параметра responseType=json.
Для прозрачной авторизации запроса сторонней системы логин и пароль пользователя могут быть переданы в запросе в HTTP параметрах запроса j_username и j_password соответственно. Параметр authToSession=0 в запросе указывает на хранение отсутствие необходимости в HTTP сессии. Настоятельно рекомендуется использовать его при запросах внешних систем, т.к. предотвращение создания HTTP сессий экономит память ERP.
Пример запроса на получение данных во внешнюю систему в JSON формате (выборка по очереди процессов):
http://ncrm.core.ufanet.ru/user/process.do?action=queueShow&id=4&dateStatusStatus=1
0&status=10&status=9&status=13¤tUserMode=&group=7&sort=0&j_username=shamil&j_
password=*****&responseType=json&authToSession=0
При изучении формата запросов и ответов возможно использование FireBug или иного инструмент разработчика в браузере с возможность просмотра запросов, в данный момент описания формата запросов нет. Т.е. получение протокола взаимодействия доступно только путём мониторинга вида запросов, отправляемых браузером при работе пользователя в ERP.
Запуск классов в контексте сервера
Для запуска любого класса, статического или динамического в контексте сервера ERP вызовите:
./crm.sh "runclass <class_name>"
Где <class_name> - полное имя класса с пакетом. Класс должен реализовывать интерфейс java.lang.Runnable.
Запуск в контексте сервера обозначает, что класс будет выполнен в рамках отдельного потока процесса сервера, получив доступ к соединению с БД, конфигурациям и другим объектам контекста. Результаты работы можно выводить в логи.
Для периодического выполнения класса необходимо использовать планировщик.
API для получения адресного справочника
Запрос на получение адресного справочника возвращает результат в форматах XML и JSON в зависимости от параметра responseType. Если же параметр responseType не указан, результат будет в формате XML.
http://erp.core.ufanet.ru/getUpdatedAddressObjects
Параметр |
Тип |
Обязательно |
Описание |
time |
int |
Нет. Имеется конфигурация |
Время обновления |
city |
int[] |
Нет |
Id выбранных городов |
houseParams |
int[] |
Нет |
Id дополнительных выгружаемых параметров для домов |
cityParams |
int[] |
Нет |
Id дополнительных выгружаемых параметров для городов |
responseType |
string |
Нет |
Формат выгружаемого файла xml либо json |
NOTE:
Для параметра time
имеются настройки в основном конфигурационном файле №6.
Пример конфигурации:
# Дефолтное значение времени обновления
api.updated.address.defaultTime.value=15
# Дефолтное значение единицы измерения (Поддерживается MINUTES, HOURS, MONTHS, YEARS)
api.updated.address.defaultTime.unit=MINUTES
# Минимальное значение разрешенного времени обновления
api.updated.address.minTime.value=3
# Минимальное значение единицы измерения (Поддерживается MINUTES, HOURS, MONTHS, YEARS)
api.updated.address.minTime.unit=MONTH
Пример (XML):
http://erp.core.ufanet.ru/getUpdatedAddressObjects?time=0&city=102,295&houseParams=1941,1367,1096&cityParams=2286
Вывод
<address time="...">
<address_area>
<!-- Список областей котором принадлежат указанные города -->
<record cityId="102" id="..." title="..."/>
...
</address_area>
<address_city>
<!-- Список указанных городов -->
<record countryId="..." id="..." title="...">
<!-- Список указанных для вывода параметров города -->
<param id="2286" value="..."/>
...
</record>
...
</address_city>
<address_country>
<!-- Список стран которым принадлежат указанные города -->
<record id="..." title="..."/>
...
</address_country>
<address_quarter>
<!-- Список кварталов в указанных городах -->
<record cityId="102" id="..." title="..."/>
...
</address_quarter>
<address_street>
<!-- Список улиц в указанных городах -->
<record cityId="102" id="..." title="..."/>
...
</address_street>
<address_house>
<!-- Список домов в указанных городах -->
<record areaId="..." comment="..." frac="..." house="..." id="..." postIndex="..." quarterId="..." streetId="...">
<!-- Список указанных для вывода параметров дома-->
<param id="1367" value="..."/>
...
<!-- Список конфигураций -->
<config name="..." value="..."/>
...
</record>
...
</address_house>
</address>
Пример (JSON):
http://erp.core.ufanet.ru/getUpdatedAddressObjects?time=0&city=102,295&houseParams=1941,1367,1096&cityParams=2286responseType=json
Вывод
{
"address_area":[
{
"id":0,
"cityId":102,
"title":"...",
"config":[
]
}
],
"address_house":[
{
"quarterId":0,
"areaId":0,
"param":[
{
"id":1367,
"value":"{}"
}
],
"postIndex":"",
"comment":"",
"id":0,
"house":0,
"config":[
{
"name":".x.xxx.xxxx",
"value":"2"
}
],
"frac":"",
"streetId":0
},
],
"address_country":[
{
"id":1,
"title":"Россия",
"config":[
]
}
],
"address_street":[
{
"id":0,
"cityId":102,
"title":"...",
"config":[
]
}
],
"address_quarter":[
{
"id":0,
"cityId":102,
"title":"...",
"config":[
]
}
],
"time":0,
"address_city":[
{
"param":[
{
"id":2286,
"value":"..."
}
],
"id":102,
"title":"...",
"config":[
],
"countryId":1
}
]
}