Calculations modules
Basic information
Main base module: https://github.com/openimis/openimis-be-calculation_py , frontend: https://github.com/openimis/openimis-fe-calculation_js
Example of basic calculation rule responsible for calculating contribution value: https://github.com/openimis/openimis-be-calculation_py/blob/main/calculation/calculation_rule.py
Module-Level Services (Signal-Based)
1. get_rule_details(classname)
Purpose: Retrieves all rule definitions that declare an impacted class matching the given classname.
How it works:
Sends a signal to all registered rules.
Each rule checks if it has an impacted class matching
classname.Collects matching responses from all rules.
Returns:
A list of
ruleDetailsdictionaries with parameters and metadata.
2. calculate(instance, context)
Purpose: Executes all applicable calculation rules for a given instance in a specific context (e.g., create, submit, validate).
How it works:
Emits signal to trigger
run_calculation_rules(instance, context)in each registered rule.Rules that are active for the object and context will run
calculate().
Returns:
No return value. Calculation results are expected to update state or be stored in-place.
3. get_parameters(classname, instance)
Purpose: Retrieves all applicable parameters from rules that apply to the given object class and linked instance.
How it works:
Emits a signal to request parameter definitions from each rule.
Each rule:
Checks if it impacts the given class.
Runs
check_calculation(instance)to ensure it's valid for the specific object.
Valid responses are aggregated.
Returns:
A list of
ruleDetailswithparametersrelevant to the instance.
4. get_linked_class(list_classnames=None)
Purpose: Identifies all business objects (classes) linked to one or more calculation rules.
Usage: Used by frontend to render calculation-aware pages and prefetch input structures.
How it works:
If
list_classnamesis provided:Sends signal for each class.
If not:
Broadcasts a signal to get all available linked classes.
Aggregates matching responses.
Returns:
List of object class names or structures representing linkable classes.
Authorities
Action | Right Code |
|---|---|
Search Calculation Rules |
|
Update Rule Status (e.g., activate/deactivate) |
|
Calculation rule module Structure
config.pyDefines constants for use in
descriptionandimpacted_class_parameter.
app.pyLoads configuration and registers calculation rules.
calculation_rule.pyHosts classes that implement
AbsCalculationRule.
Static Fields
Field | Type | Description |
|---|---|---|
|
| Tracks the version of the calculation rule |
|
| Status of the rule: |
|
| Unique identifier for rule version tracking |
|
| Short identifier for the rule |
|
| Description of the rule |
|
| List of impacted class parameters (see below) |
|
| Default: |
|
| Optional expiration date |
|
| Calculation type (default: |
|
| Detailed subtype based on |
|
| Converter functions between classes |
Impacted Class Parameter Format
Each parameter follows this structure:
{
"class": "ClassName",
"parameters": [
{
"type": "int",
"name": "example",
"label": { "en": "DisplayName" },
"rights": {
"read": "readAuthority",
"write": "writeAuthority"
},
"relevance": "frontendJsDisplayLogic",
"condition": "frontendJsValidationLogic",
"optionSet": []
}
]
}Configuration of calculation rule format:
Example of config.py
CLASS_RULE_PARAM_VALIDATION = [
{
"class": "ContributionPlan",
"parameters": [
{
"type": "select",
"name": "rate",
"label": {
"en": "Percentage of income",
"fr": "Pourcentage du salaire"
},
"rights": {
"read": "151201",
"write": "151202",
"update": "151203",
"replace": "151206",
},
'relevance': "True",
'condition': "INPUT>1",
'optionSet': [
{
"value": "5",
"label": {
"en": "5%",
"fr": "5%"
}
},
{
"value": "10",
"label": {
"en": "10%",
"fr": "10%"
}
},
{
"value": "15",
"label": {
"en": "15%",
"fr": "15%"
}
},
],
"default": "5"
},
{
"type": "checkbox",
"name": "includeFamily",
"label": {
"en": "include family members",
"fr": "Inclure les membres de la familles"
},
"rights": {
"read": "151201",
"write": "151202",
"update": "151203",
"replace": "151206",
},
"relevance": "True",
"default": "False"
},
],
},
{
"class": "ContractDetails",
"parameters": [
{
"type": "number",
"name": "income",
"label": {
"en": "Income",
"fr": "Salaire"
},
"rights": {
"read": "152101",
"write": "152102",
"update": "152103",
"replace": "152103",
},
"relevance": "True",
"condition": "INPUT>100",
"default": ""
}
],
},
{
"class": "PolicyHolderInsuree",
"parameters": [
{
"type": "number",
"name": "income",
"label": {
"en": "Income",
"fr": "Salaire"
},
"rights": {
"read": "150201",
"write": "150202",
"update": "150203",
"replace": "150206",
},
"relevance": "True",
"condition": "INPUT>100",
"default": ""
}
],
},
]
DESCRIPTION_CONTRIBUTION_VALUATION = F"" \
F"This calcutation will add the income in the contract details " \
F"and PHinsuree and the percentage in the Contribution plan" \
F" so when a contract valuation is requested then the calculation will" \
F" determine the value based on the contract details income and CP percentage"
FROM_TO = []Type & Subtype Reference
type (default: account_receivable)
account_receivableaccount_payabletax
sub_type
For account_receivable:
contributionreinsurance_paymentfunding
For account_payable:
third_party_payment(toward HF)reimbursement(toward Insuree)commissions(toward User)fees(toward Other)reinsurance_contribution
For tax:
local_taxstate_taxcountry_tax
Converter Structure (from-to)
Defines transformation from one business object to another.
[
{
"name": "FunctionName",
"from": {
"class": "BusinessObject1",
"right": "rightObject1"
},
"to": {
"class": "BusinessObject2",
"right": "rightObject2"
},
"parameters": [ ... ] // Same format as impacted_class_parameter
}
]Core Functions
ready()
Initializes module:
Registers rules (adds entry to calculation table if not found with "inactive" status)
Registers active rules to signals
active_for_object(cls, instance, context, type='account_receivable', sub_type='contribution')
Determines if rule should apply to a given object within a specific context.
Contexts:
create,update,delete,submit,amend,replace,check,validate
Used for filtering rules dynamically based on object properties (e.g., product, type, etc.)
check_calculation(cls, instance)
Verifies if the calculation can be used for the given instance.
Extract instance class
If class is known, verify link to this calculation rule
calculate(cls, instance, *args)
Performs the actual calculation based on provided parameters and instance.
get_linked_class(cls, sender, class_name, **kwargs)
Returns potential objects that can be linked to a calculation rule.
convert(from, to, **kwargs)
Handles conversion between different business object types. Checks:
Matching
from-todefinitionUser rights
Calls conversion function with passed parameters
Can be defined at abstract level for reusability
Signals Listened
signal_get_rule_namesignal_get_rule_detailssignal_get_paramsignal_get_linked_classsignal_calculate_event
Configuration
Configurable via core.ModuleConfiguration.
Important Variable:
CALCULATION_RULE: Global list of loaded rules fromcalculation_rule.py
Custom Methods
Calculation rules can define and register custom methods that respond to specific signals or events
📄 calculation_rule.py – Example of a Registered Calculation Rule
The calculation_rule.py file defines concrete implementations of calculation rules for the openimis-be-calculation_py module. These implementations inherit from AbstractCalculation (or AbsCalculationRule), allowing you to encapsulate custom calculation logic that can be executed dynamically based on business context.
Purpose
This module serves as the plug-in location for registering custom rule classes used to perform calculations such as premiums, contributions, reinsurance payments, reimbursements, or taxes, depending on the product configuration, object type, and operation context (e.g., create, validate, submit).
How It Works
Inheritance from AbsCalculationRule (or AbstractCalculation)
Each rule is implemented as a Python class that inherits from the base AbsCalculationRule, which defines the core interface and life-cycle methods, including:
calculate()– Main method that executes the rule’s logic on a given object instance.active_for_object()– Determines whether the rule should be triggered based on the instance, type, subtype, and context.check_calculation()– Verifies whether the rule is applicable for a given instance.Optional:
convert(),get_linked_class()and other custom signal handlers.
Registration & Discovery
Calculation rule classes are discovered and registered automatically during app startup via:
Being imported in
calculation_rule.py.Optionally registered explicitly in the module’s
__init__.py.
Once registered, the rules are:
Added to the global registry (
CALCULATION_RULE)Made available to Django signals, which invoke them based on matching criteria.
Example Use Case
When a user submits a policy for activation:
A Django signal is emitted (e.g.,
signal_calculate_event).The system determines the applicable calculation rule(s) based on:
The instance's type/subtype (e.g.,
account_receivable/contribution)Context (e.g.,
submit)Linked product or configuration.
The system invokes the rule’s
calculate()method with relevant parameters.The rule performs the logic and returns a structured result, such as:
{ "amount": 1200, "currency": "USD", "details": { ... } }
Benefits of the Pattern
Feature | Description |
|---|---|
Modular | Add or modify calculation rules without altering core logic. |
Flexible | Supports multiple types, subtypes, and custom parameters. |
Context-Aware | Can execute differently based on action ( |
Decoupled | Keeps calculation logic independent from business workflows, GraphQL endpoints, and UI. |
Extensible | Allows integration of converters ( |
Relevance & Condition Logic for Parameter Inputs
In calculation rules, each parameter defined under impacted_class_parameter includes optional relevance and condition fields that govern both the visibility of the input in the frontend and the validation logic applied before saving.
These fields are critical for dynamically rendering and validating parameter inputs based on runtime data (e.g., from product configuration or related business objects).
relevance: Input Visibility Control
The
relevancefield accepts a boolean expression (evaluated as a string).Evaluation logic:
True→ Show the input in the frontend.False→ Hide the input and automatically treat its value as valid.
If
relevanceis not defined, the input defaults to visible.
⚠️ Inputs with
relevance = Falseare not evaluated for validity, even if aconditionis defined.
condition: Input Validation Logic
The
conditionfield contains a validation formula to be parsed and evaluated at runtime using:👉 Handsontable/formula-parser (JavaScript library that supports Excel-like formula parsing)
Expected Output: Formula must return a boolean:
true→ Input value is valid.false→ Input value is invalid; a"Value validation failed"message is shown.
Formula Structure
Formulas support dynamic value injection through reserved keywords:
Accessing Parameter Inputs
To use a parameter’s value in a formula:
INPUT✅ Will be replaced with the actual user-entered value during evaluation.
Accessing Object Properties
To reference values from the current business object (e.g., PHInsuree, Claim, Policy, etc.):
OBJECT.property.subPropertyExamples:
OBJECT.isDeleted→ accessesisDeletedfield on the current object.OBJECT.insuree.id→ accesses the nestedidproperty from theinsureeproperty.
Property names must exactly match the GraphQL schema's field names (case-sensitive).
Runtime Formula Transformation
Since formula-parser does not support dot-notation, formulas using OBJECT.some.property are converted internally by:
Replacing dot notation with uppercase, underscore-separated variable names:
OBJECT.insuree.id → INSUREE_IDThese variables are resolved to actual values and injected into the parser at runtime.
Validation Failure
When a condition evaluates to false:
The input is marked invalid
A
"Value validation failed"message appears under the input fieldThe system blocks submission if the
setJsonExtValid(boolean)callback is provided in the frontend and passedfalse
Validation Logic Summary
Scenario | Relevance | Condition | Input Displayed | Input Validated | Form Submission |
|---|---|---|---|---|---|
Visible & Validated |
| Formula returns | ✅ | ✅ | ✅ |
Visible & Invalid |
| Formula returns | ✅ | ❌ |
Did you encounter a problem or do you have a suggestion?
Please contact our Service Desk
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. https://creativecommons.org/licenses/by-sa/4.0/