The article contains instructions and recommendations for writing plugins for BILLmanager. A plugin is a software component that adds specific functionality or modifies the behavior of the main system.
BILLmanager modifications
BILLmanager allows you to add the following modifications:
- modules are large and self-contained components of the system that perform a specific function or a set of functions. For example, they extend functionality through integration with a third-party system. Modules:
- usually have their own dependencies and can include several plugins;
- are not intended for dynamic real-time system behavior modification;
- may require changes to the core system architecture;
-
by functionality, they are divided into:
- service handlers;
- payment methods;
- message gateways;
- online cash registers;
- document management modules;
- modules for coordination with third-party systems — LDAP, Omni, amoCRM, etc.
For information about existing system modules, see the Integration article.
-
plugins are usually simpler software components that add specific functionality or modify the behavior of the main system. Plugins allow you to:
- integrate new functions without changing the source code;
- intercept internal events;
- add new interface elements;
- execute background tasks via triggers on specific events;
- adapt the system to specific needs.
Types of plugins
Plugins are divided by functionality into:
- plugins that modify platform behavior;
- plugins that extend platform functionality;
- plugins that perform background tasks.
Behavior-modifying plugins allow you to catch internal events and modify them. For example, to add additional fields to a client card and fill them with the required values. Or to add new logic for existing actions. For more details, see the COREmanager documentation article Changing system behavior through event handlers.
Plugins that extend functionality add:
- new elements — custom tables and forms necessary for solving specific tasks;
- new actions and scenarios for working with the platform.
Plugins that perform background tasks:
- add triggers for specific events in the platform;
- allow running "heavy" tasks in the background that should not affect the work with the platform in the interface. For example, executing background tasks based on events.
For more details, see the COREmanager documentation article Background plugins.
To write your own plugin for BILLmanager:
- select a programming language;
- prepare the environment;
- decide on the plugin structure.
Choosing programming language
For developing plugins for BILLmanager, you can use any programming language, both scripting and compiled. We recommend:
- Python 3 is preferred choice. ISPsystem provides an SDK (Software Development Kit) that simplifies working with:
- XML data;
- sessions and request parameters;
- database;
- error handling;
-
C++ should be chosen for tasks requiring:
- integration with the billing core;
- working within a single database transaction;
- using the billing API;
- high performance.
When writing a plugin in C++, you can use the platform's public headers, which expands the functionality possibilities.
Preparing environment
To prepare the environment for creating a plugin:
- Check that the server meets the BILLmanager installation requirements. For more details, see the article Server requirements.
- Install the BILLmanager platform. For more details, see the article Installation process.
- Install the dev packages depending on the OS used: Server with AlmaLinux 9
sudo dnf install coremanager-devel billmanager-corporate-devel clangServer with Ubuntu or Astra Linuxsudo apt install coremanager-dev billmanager-corporate-devapt clang
For comfortable work with the code, we recommend setting up an IDE. For example, for setting up Visual Studio Code (VS Code):
- Install the Remote - SSH plugin for remote development.
- For Python: install the Python and Pylance extensions. For more information on extension settings, see the VS Code documentation.
- For C++: install the extensions:
- C/C++. For more information on extension settings, see the VS Code documentation;
- clangd. For more details, see the clangd documentation.
- Set up a connection to the server with BILLmanager.
Plugin structure. Source code
All plugins are developed and stored in the /usr/local/mgr5/src/ directory. A build system is based on
isp.mk, which provides:
- automatic file installation via
make install; - cleaning of temporary files via
make clean; - automatic restart of BILLmanager.
/usr/local/mgr5/src/<my_plugin>/
├── Makefile
├── README.md
├── dist/ (optional, for SQL scripts and configuration files)
├── xml/
│ └── <my_plugin>.xml
└── addon/ (for Python) or src/ (for C++)
└── <main_script_file>.py or <main_script_file>.cppWhere:
Makefile— a mandatory file that allows installing the plugin using the command:make install
The make install command automatically copies files to the required directories:
-
- /usr/local/mgr5/addon/
- /usr/local/mgr5/lib/
- /usr/local/mgr5/xml/
and restarts BILLmanager;
dist— a directory containing the plugin files. For example, SQL scripts or configuration files;- <
my_plugin>.xml— the name of the XML file containing the plugin description. The XML file describes the plugin's metadata and its interaction with BILLmanager; - <
main_script_file> — the name of the plugin file. For example, myplugin.py or myplugin.cpp
The source code directory structure of the plugin is further illustrated with examples for Python 3 and C++ languages.
Makefile for Python plugin
MGR=billmgr
PLUGIN=myplugin
dist-prepare: $(DISTDIR)/addon/myplugin.py
$(DISTDIR)/addon/myplugin.py: $(shell pwd)/myplugin.py
@echo "myplugin: copy script"
@mkdir -p $(DISTDIR)/addon/ && \
cp -rf $(shell pwd)/myplugin.py $(DISTDIR)/addon/myplugin.py && chmod 744 $(DISTDIR)/addon/myplugin.py
BASE ?= /usr/local/mgr5
include $(BASE)/src/isp.mkMakefile for C++ plugin
MGR=billmgr
PLUGIN=myplugin
LIB += libmyplugin
libmyplugin_LDADD = -lmgrdb -lispapi
libmyplugin_SOURCES = src/myplugin.cpp
BASE ?= /usr/local/mgr5
include $(BASE)/src/isp.mkPlugin description in XML
The plugin description is stored in the XML file /usr/local/mgr5/xml/billmgr_mod_mypluginname.xml, where billmgr_mod_mypluginname.xml is the name of the XML file containing the plugin description.
<mgrdata>
<handler name="<myplugin>.py">
<event name="employee.edit" after="yes" type="xml" ignoreerrors="yes"/>
</handler>
<messages name="employee.edit">
<msg name="mood">Mood</msg>
<msg name="mood_good">Good</msg>
<!-- ... other messages ... -->
</messages>
<form>
<field name="mood" type="select" after="name">
<option value="good">mood_good</option>
<!-- ... -->
</field>
</form>
</mgrdata>?xml version="1.0" encoding="UTF-8"?>
<mgrdata>
<library name="libmyplugin"/>
<metadata name="employee.edit" type="form">
<form title="name">
<page name="basic">
<field name="mood" before="name">
<select name="mood"/>
</field>
<field name="msg_to_boss" remove_if="new">
<textarea name="msg_to_boss" rows="5" wrap="on"/>
</field>
<field name="employee_active" remove_if="new">
<input name="employee_active" type="checkbox"/>
</field>
</page>
</form>
</metadata>
<lang name="en">
<messages name="employee.edit">
<msg name="mood">Mood</msg>
<msg name="mood_good">Good</msg>
<!-- ... other messages ... -->
</messages>
</lang>
</mgrdata>Features of Python 3 plugins
Requirements
Requirements for Python 3 plugins:
- the plugin must be executable;
- in response to a call from BILLmanager, the plugin must return XML. To output XML to stdout, execute:
result = ET.tostring(xml, encoding="unicode") print(result)
Type field value
For the Python SDK in BILLmanager, it does not matter what is specified in the type field, as parameter retrieval will always occur the same way. However, depending on your goals, you can choose:
- if you need to work with action metadata —
type="xml"; - if you need to modify data —
type="cgi".
Regardless of type, request parameters are available via:
func = session.get_query_param("func")
elid = session.get_query_param("elid")
sok = session.get_query_param("sok")Correctness of links
Use a shebang to ensure the script runs correctly regardless of the Python interpreter's location in the system
#!/usr/bin/env python3 Import order
Use the following import order:
- Modules included in the standard Python library or installed globally in the system (for example, via pip): Standard module import example
import xml.etree.ElementTree as ET import requests - The
sysmodule and adding the path to the SDK:Where /usr/local/mgr5/lib/python — is the path to the local billing SDK. Needed for subsequent imports from billmgr to be available.import sys sys.path.insert(0, "/usr/local/mgr5/lib/python") - Imports from the billing SDK (billmgr.*) Example of module imports from BILLmanager SDK
import billmgr.logger as logging import billmgr.exception as exception import billmgr.session as session import billmgr.db as db import billmgr.misc as misc
The module libraries from the SDK provide functionality for logging, error handling, working with XML sessions, the database, and auxiliary utilities (for example, mgrctl).
Initializing logging
Perform logging initialization:
logging.init_logging("myplugin")
logger = logging.get_logger("main")The log will be available in /usr/local/mg5/var/myplugin.log.
To log all request parameters (func, elid, sok etc), execute the command:
session.debug_session(xml)XML validation
To ensure BILLmanager always receives valid XML, execute the main logic within a try/except block:
if __name__ == "__main__":
try:
result = process_event()
print(result)
except exception.XmlException as exc:
logger.backtrace()
print(exc.as_xml())
except Exception as exc:
logger.backtrace()
print(exception.XmlException("unknown", "what", str(exc)).as_xml())BILLmanager forms an XML document describing the form, data, metadata, etc. The document is passed to the plugin via standard input stdin. To read and parse it, use:
xml = session.get_input_xml(True)To log all request parameters (func, elid, sok etc.), execute the command:
session.debug_session(xml)Features of C++ plugins
Import order
Keep to the following import order:
- C++ standard library headers:
Example of standard headers import#include <sstream> - Internal platform or framework headers: common api, mgr modules: Where:
#include <api/action.h> #include <mgr/mgrlog.h> #include <mgr/mgrdb_struct.h> #include <mgr/mgrrpc.h>api/action.h— defines base classes for handling events in the plugin, includingEventandSession;mgr/mgrlog.h— provides logging facilities via macros. For example,Debug,WarningandSTrace;mgr/mgrdb_struct.h— contains tools that describe the structure of database tables. Entities such asIdTable,StringField,ReferenceFieldallow linking C++ classes with database tables and fields without writing SQL queries;mgr/mgrrpc.h— includes theHttpQueryclass, designed for making HTTP requests to external services.
- Billing module headers (billmgr): Example of module headers import
#include <billmgr/db.h> #include <billmgr/util/http/status.h>
In the example above, the billing module headers provide access to the database, utilities for checking HTTP statuses, etc.
Initializing plugin
Perform plugin initialization to set up logging:
MODULE("myplugin");The log will be saved in /usr/local/mgr5/var/billmgr.log. The logging level for the module can be configured in the file /usr/local/mgr5/etc/debug.conf or via the platform interface. For more information on configuring logging, see the article Logging in BILLmanager.
If the plugin needs to store data, describe the table as a C++ class inherited from mgr_db::IdTable:
class EmployeeMoodTable : public mgr_db::IdTable {
public:
mgr_db::ReferenceField User;
mgr_db::StringField Mood;
EmployeeMoodTable()
: mgr_db::IdTable("employee_mood")
, User(this, "user", mgr_db::rtCascade)
, Mood(this, "mood")
{
AddIndex(mgr_db::itIndex, Mood);
}
};In the MODULE_INIT section, register everything needed at startup:
MODULE_INIT(myplugincpp, "mgr:last") {
db->Register<plugin_table::EmployeeMoodTable>(); // Table registration
isp_api::RegisterHandler<eEmployeeEdit>(); // Event registration
}Logging
We recommend using logging at all stages of development. To get the full log from the plugin:
- Add the following line to the /usr/local/mgr5/etc/debug.conf :
- when writing a plugin in Python — billmgr.pluginname.* 9
- when writing a plugin in C++ — billmgr. pluginname 9,
Where:
-
- pluginname — the plugin name. For example,
pluginname.pyorpluginname.cpp; - 9 or *9 — the maximum logging level. The higher the logging level, the more detailed information is written to the log. For more information on configuring logging, see the article Logging in BILLmanager.
- pluginname — the plugin name. For example,
- Add the logging library to the plugin using one of the commands: Python
import billmgr.logger as loggingС++#include <mgr/mgrlog.h>
Related topics:
BILLmanager documentation
COREmanager documentation
- Description of forms
- Description of lists
- Errors
- How to add additional table fields
- Lower-level interaction, C++ plug-ins
En
Es