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

Система 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, () - группа символов.

Ссылки:

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

extension 1

В 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.

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

Динамический код - это Java файлы, которые можно изменять и подгружать без перезапуска приложения. С его помощью можно обрабатывать различные события в системе. Файлы с классами динамического кода располагаются в по-умолчанию каталоге dyn.

Динамические классы необходимо создавать в пакете ru.bgcrm.dyn.<дальнейшая иерархия пакетов> и в соответствующем каталоге.

Параметры динамического кода могут быть настроены в конфигурации.

Для написания динамического кода возможно использование как простого текстового редактора, так и полноценные IDE для Java разработки. Методология при этом аналогична применяемой для разработки в ABilling.

Компиляция динамического кода осуществляется на вкладке Динамический код интерфейса администратора. Можно скомпилировать только все классы сразу. При успешной компиляции динамический код применяется также целиком.

extension 2

Как видно из снимка экрана, помимо компиляции в оснастке возможно создание и запуск объекта класса реализующего интерфейс 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>&param1=value&param2=..

Где:

  • <crmUrl> - URL и порт ERP;

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

  • <user> и <pswd> - логин и пароль пользователя ERP;

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

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

Примеры

В составе поставки доступны примеры динамических классов в dyn/ru/bgcrm/dyn поименованные как Example* с описанием в комментариях.

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 помещаются:

extension 3
  • ссылки на объекты контекста, чаще всего различные справочники в фильтре 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&currentUserMode=&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
      }
   ]
}