Calculations modules

Calculations modules

Basic information

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 ruleDetails dictionaries 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:

  1. Emits a signal to request parameter definitions from each rule.

  2. Each rule:

    • Checks if it impacts the given class.

    • Runs check_calculation(instance) to ensure it's valid for the specific object.

  3. Valid responses are aggregated.

Returns:

  • A list of ruleDetails with parameters relevant 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_classnames is 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

Action

Right Code

Search Calculation Rules

153001

Update Rule Status (e.g., activate/deactivate)

153003

Calculation rule module Structure

  • config.py

    • Defines constants for use in description and impacted_class_parameter.

  • app.py

    • Loads configuration and registers calculation rules.

  • calculation_rule.py

    • Hosts classes that implement AbsCalculationRule.

Static Fields

Field

Type

Description

Field

Type

Description

version

str

Tracks the version of the calculation rule

status

int

Status of the rule: inactive, future, active, archived

UUID

str

Unique identifier for rule version tracking

calculation_rule_name

str

Short identifier for the rule

description

str

Description of the rule

impacted_class_parameter

List[Dict]

List of impacted class parameters (see below)

date_valid_from

datetime

Default: datetime.datetime(2000, 1, 1)

date_valid_to

datetime or None

Optional expiration date

type

str

Calculation type (default: account_receivable)

sub_type

str

Detailed subtype based on type

from-to

List[Dict]

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_receivable

  • account_payable

  • tax

sub_type

For account_receivable:

  • contribution

  • reinsurance_payment

  • funding

For account_payable:

  • third_party_payment (toward HF)

  • reimbursement (toward Insuree)

  • commissions (toward User)

  • fees (toward Other)

  • reinsurance_contribution

For tax:

  • local_tax

  • state_tax

  • country_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.

  1. Extract instance class

  2. 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-to definition

  • User rights

  • Calls conversion function with passed parameters

Can be defined at abstract level for reusability


Signals Listened

  • signal_get_rule_name

  • signal_get_rule_details

  • signal_get_param

  • signal_get_linked_class

  • signal_calculate_event

Configuration

Configurable via core.ModuleConfiguration.

Important Variable:

  • CALCULATION_RULE: Global list of loaded rules from calculation_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:

  1. A Django signal is emitted (e.g., signal_calculate_event).

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

  3. The system invokes the rule’s calculate() method with relevant parameters.

  4. The rule performs the logic and returns a structured result, such as:

    { "amount": 1200, "currency": "USD", "details": { ... } }

Benefits of the Pattern

Feature

Description

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 (create, submit, validate, etc.) and instance values.

Decoupled

Keeps calculation logic independent from business workflows, GraphQL endpoints, and UI.

Extensible

Allows integration of converters (from-to) and frontend parameter configurations.

 

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 relevance field 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 relevance is not defined, the input defaults to visible.

⚠️ Inputs with relevance = False are not evaluated for validity, even if a condition is defined.

condition: Input Validation Logic

  • The condition field 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.subProperty

Examples:

  • OBJECT.isDeleted → accesses isDeleted field on the current object.

  • OBJECT.insuree.id → accesses the nested id property from the insuree property.

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_ID
  • These 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 field

  • The system blocks submission if the setJsonExtValid(boolean) callback is provided in the frontend and passed false

Validation Logic Summary

Scenario

Relevance

Condition

Input Displayed

Input Validated

Form Submission

Scenario

Relevance

Condition

Input Displayed

Input Validated

Form Submission

Visible & Validated

True

Formula returns True

Visible & Invalid

True

Formula returns False

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/