Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 29 Next »

Concepts

The rules for the Formal Sector can be complex and evolving. Therefore, the "Formal Sector" solution should bring flexibility, to avoid complete redesign and databases change when a parameter to define the contribution value is changed.
To bring that flexibility, the solution described here uses a Calculation Rule (the Calculation Rules are defined in additional openIMIS modules extending the Calculation Module). This module is defining a framework to design calculations and to manage their activation and versioning in openIMIS. This solution will deliver a Calculation Module that takes the income (on the level of the Contract’s insuree) and a rate (on the level of the Contribution Plan) as parameters, make the calculation and updates the payment value of the Contract’s insuree.


The value of the parameters will be saved as a JSON string in the json_ext field.

Later, this module could be used as a framework to create event-based actions

Business process

This module will use a "generic contribution" to get the required parameters and the json_ext database field will be used to save the parameters (generic backend feature). Finally, a signal will trigger the calculation action. 

The left part, if there is no reachable technical solution will be hardcoded by having the contribution “knowing“ which are the related class (e.g CPB for PH insuree and contract detail and calculation for Contribution)

Solution

This module will rely heavily on the django signals

In order to have a generic approach that will make the code more readable, one argument will be expected in the signal, this argument will be called obj and should contain the instance of the class sending his signal:

my_signal= dispatch.Signal(providing_args=["instance","user"])

Use cases

UC14-1: add a new Calculation rule (needs to change the openimis.json).
UC14-2: use the contribution_fe to select a calculation rule.
UC14-3: use the contribution_fe to display the parameters required by multiple calculation for an object.
UC14-4: replace a calculation rule.
UC14-5: remove a calculation rule.

Authorities

This module won't have ad-hoc authorities.

Entities

class CalculationRules

  • CalculationClassName: str

  • Status: int

  • Description: str

  • Priority : int

  • dateValidFrom : date

  • dateValidTo : date

class CalculationRulesDetails (HistoryModel)

  • CalculationRulesUUID: uuid

  • Status: int

  • ClassName: str

  • Main: bool

  • Params: Dict

  • ClassParams: List[CalculationParam]

class CalculationParam

  • Type: str

  • rights: Dict

  • Relevance: str

  • conditions: str

  • optionSet: Dict

Detailed design

Calculations Backend Module

Backend management code

The backend management code will be used to register the calculation sub-module parameters, the sub-module status and the ReadUpdate right management on the database.
This code should have a function to clean the table from the remove calculation
This code should manage the uniqueness of the active version for a given UUID

A Custom signal class be created to add a priority of calculation

from django.dispatch import Signal as BaseSignal
from django.dispatch.dispatcher import _make_id


class Signal(BaseSignal):

    def connect(self, receiver, sender=None, weak=True, dispatch_uid=None, priority=50):
        if dispatch_uid is None:
            dispatch_uid = _make_id(receiver)

        inner_uid = '{0}{1}'.format(priority, dispatch_uid)
        super(Signal, self).connect(receiver, sender=sender, weak=weak, dispatch_uid=inner_uid)
        self.receivers.sort()

Source of the sub Signal class: https://stackoverflow.com/questions/10017567/possible-to-change-order-of-django-signals

absCalculationRule class

The calculation module will be composed of an abstract class "absCalculation" that will have those methods and members.
This class should be imported as a module in the subclass definition (inside the "hat" modules)

Members

static version
The version is used to keep track of the changes in the version of the calculation rule,
status
integer inactive, future, active, archived
static UUID
UUID to follow the version of the calculation

static CalculationRuleName

short name of the calculation rule

static description

description text of the calculation

list of impacted class and parameters

<list>JSONObject objects {class:"className"(to be compare with property name of the FE details object which is pulled from BE), 'parameters':['type':"int",'name':"example",'label':{'en':'DiplayName'},'rights':{'read':"readAuthority", ‘write':"writeAuthority"},relevance:"frontendjsdisplaylogic",condition:"frontendjsvalidationlogic",'optionSet’:[]]} // list of the parameters required on other object,

relevance and condition are Nice to have.

Methods

getRuleName(classname)

NEED TO BE DEFINED ON ABSTRACT CLASS LEVEL

return an object “CalculationRules”

getRuleDetails(classname)

NEED TO BE DEFINED ON ABSTRACT CLASS LEVEL

For object in listObject

if object.class == classname

return object

getParamteres(ClassName, instance)

NEED TO BE DEFINED ON ABSTRACT CLASS LEVEL

#className is the class name of the object where the calculation param need to be added

#instance is where the link with a calculation need to be found, like the CPB in case of PH insuree or Contract Details

This function has two steps

1- getRuleDetails(className)

2- define if the calculation should be used

for each “eligible“ ruleDetails,

checkCalculation(instance) #check if the calculation is really concerning the class for that context

return a list only with rule details that matches step 1 and 2

runCalculationRules(instance, context)

NEED TO BE DEFINED ON ABSTRACT CLASS LEVEL

this function will be register to the module signal via the ready function if the rule is active

1- getRuleDetails(instance.__class__)

2 if activeForObject(instance)

calculate(instance)
ready()

NEED TO BE DEFINED ON RULE LEVEL

This method makes sure the calculation is registered in the calculation table (if not the line should be added with "inactive status") and register the signals only if it is active

all rules, if active will have to register to the signal sent by “getRuleDetails”

activeForObject(object, context)

NEED TO BE DEFINED ON RULE LEVEL

this method will contains the checks if the calculation need to be executed for the object on that context. the default context will be:

  • create

  • update

  • delete

  • submit

  • amend

  • replace

  • check

  • validate

This function is required because the same class can have different calculation based on the object members values (like product ….)

(TDC)calculateEvent(sender, instance, user, **kwargs)

NEED TO BE DEFINED ON RULE LEVEL

  • This method runs the calculation based on the object sending the signal, this means that the relationship with the other item required for the calculation could be found from the object sending the signal. (e.g., link to the product can be found in the policy as foreign key.)

  • This function should call first the activeForObject method, the context will depend on the calling event, if the fuction return true the calcuation needs to be run, in other case the even handling will stop

  • the calculate(*args) function

  • Signal need to be set up on the ready function to call it

checkCalculation(instance)

NEED TO BE DEFINED ON RULE LEVEL

this function will get the calculation relative the instance

1- get instance class

2- if class known then return check that the linked calculation is the current one

calculate(instance, *args)

NEED TO BE DEFINED ON RULE LEVEL

Function that will do the calculation based on the parameters

getLinkedClass(List[classname])

NEED TO BE DEFINED ON RULE LEVEL

that function will return the possible instance that can have a link to the calculation

Module function/Service

NEED TO BE DEFINED ON MODULE LEVEL

getRuleDetails(classname)

this function will send a signal and the rules will reply if they have object matching the classname in their list of object

listruledetails = []

for all calculation rules (send signal):

if result_signal != []

merge listruledetails and result_signal

return listruledetails

runCalculationRules(instance, context)

for all calculation rules (send signal):

runCalculationRules(instance, context)

no return

getParamteres(ClassName, instance)

#className is the class name of the object where the calculation param need to be added

#instance is where the link with a calculation need to be found, like the CPB in case of PH insuree or Contract Details

listparameters = []

for all calculation rules (send signal):

if result_signal != []

merge listparameters and result_signal

return the ruleDetails that are valid to classname and related to instance

getLinkedClass(List[ClassName] = None)

#List[ClassName] is send from FE, by checking the class used in page where the user ave access if None (in case too difficult to do on the FE) all liked class will be retrieve

for all calculation rules (send signal):

returnListClass = []

if listClass == None

sendsignal getLinkedClass(none)

if result_signal != []

merge returnListClass and result_signal

else

for class in listclasss

sendsignal getLinkedClass(class)

if result_signal != []

merge returnListClass and result_signal

return all the returnListClass

Authorities

Calculation (prefix 153)

  • search → 153 001

  • update → 153 003 (to activate or not)

Calculations Frontend Module

(NICE TO HAVE, or via Django Admin)Calculation management page

This page will show the calculation page and change the status/activate a given version
This page should group the calculation by UUID

Contributions

Standard contributions should be available for most of the entities (claims, contribution, contribution collection, policies, insuree ....)

  • PH insuree

  • Contract details

  • ContributionPlan

  • All the other are NICE TO HaVE


Those contributions should get the parameters template from the backend and display the relevant ones. The values should be saved in the JsonExt fields

Ideally the contribution from the main calculation module should manage the contribution form the rules in order to avoid having the same parameters twice. For example , a single insuree can have 2 contributions plan using those two rule different rules based on the income

I see two possible ways (one must be selected)

  • either by wrapping the fe contribution from the rules in a “overall calculation fe contribution“ (this will require rebuilding the front end when adding a rule)

  • by managing all the rule fe contributions directly within the calculation fe contributions using the parameters defined in the rules (preferred solution)

    • only simple type should be supported:

      • Number (int. float …)

      • Select ( choice list key(int)-> value([lang-strings]))

      • checkbox

    • refresh logic should be based on the link between the entity having the contribution and the entity that hold the calculation

    • Appearance/logic of the data should be defined based:

      • on the entity

      • on the rules related to this entity (directly or not)

    • (nice to have) format should be based on the rule fields constraint (regex)

    • (nice to have) constraint/ validation should be based on the rule fileds constraint (js test function)

    • Overall the logic use in enketo could be reused https://github.com/enketo/enketo-core/blob/e7159f95c5f459efeb83314fdd0aecac78e47400/src/js/input.js

Nice to have

Formula parser

https://formulas.readthedocs.io/en/stable/

  • No labels