Skip to content

Commit e420fd8

Browse files
authored
Merge pull request #402 from openimis/feature/OP-2585
Feature/op 2585
2 parents c326d8c + f593557 commit e420fd8

119 files changed

Lines changed: 4956 additions & 2695 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ It is a required module of [openimis-be_py](https://github.com/openimis/openimis
1616
## ORM mapping:
1717
* UUIDModel: abstract model for new entities (and later on migrated entities), enforcing the use of UUID is identifier
1818
* VersionedModel: abstract model implementing the legacy 'in table archiving' mechanism
19-
* HistoryModel: abstract model implementing the django-simple-history archiving mechanism with standard mutaitons
19+
* HistoryModel: abstract model implementing the django-simple-history archiving mechanism with standard mutations
2020
* HistoryBusinessModel: abstract model implementing the django-simple-history archiving mechanism with ValidFrom and ValidTo date and with standard and replace mutations
2121
* core_ModuleConfiguration > ModuleConfiguration: a generic entity each module should use to let (admin)users provide the expected configuration (via a central management console).
2222
* core_FieldControl > FieldControl: allow to hide or mark readonly fields in UI (tables, forms,...)
@@ -67,12 +67,12 @@ from core.fields import DateField, DateTimeField
6767
```
6868

6969
### UserManager
70-
openIMIS backend is configured for SSO, receiving the user (login) in the REMOTE_USER http header. Since django security is defined uppon User (core_User table), the UserManager auto-provision the received login (REMOTE_USER) as User, binding it (i_user) to the corresponding InteractiveUser record.
70+
openIMIS backend is configured for SSO, receiving the user (login) in the REMOTE_USER http header. Since django security is defined upon User (core_User table), the UserManager auto-provision the received login (REMOTE_USER) as User, binding it (i_user) to the corresponding InteractiveUser record.
7171
The auto-provisioning assigns a default Group (name can be parameterized) from which django permissions are calculated (with UserRole - Role - RoleRight contributed from InteractiveUser).
7272
Note: if not existing, the default group is created at startup.
7373

7474
### Language Support for Role Labels
75-
If the user has a language different from the system default and a translation is available in alt_language, the role name is returned in that language. Otherwise, it falls back to the default name. This ensures localized role labels are shown based on the user's language preferences.
75+
Role names are displayed in the user's selected language using Django's translation system with .po and .mo files. If a translation is available for the user's language (e.g., fr), the translated role name is shown. Otherwise, it falls back to the default role name. This ensures localized role labels are displayed based on the user's language preferences, managed through translation files rather than a database column.
7676

7777
### Mutations & Signals
7878
The OpenIMISMutation class of this module provides the template code for
@@ -114,7 +114,7 @@ If the callback returns an array of error message:
114114
```
115115
If the callback returns None (or an empty array), the mutation is marked as successful.
116116

117-
__Important Note__: by default the callback is executed __in transaction__ and, as a consequence, will (in case of exception/errors) cancel the complete mutation. If this is not the desired behaviour, the callback must explicitely detach to separate transaction (process).
117+
__Important Note__: by default the callback is executed __in transaction__ and, as a consequence, will (in case of exception/errors) cancel the complete mutation. If this is not the desired behaviour, the callback must explicitly detach to separate transaction (process).
118118

119119
#### Extending mutations with signals
120120
Signal callbacks could use mutationExtensions JSON field to receive additional data from mutation payload. This
@@ -139,15 +139,15 @@ function to connect new signals. Receivers can be registered also in other place
139139

140140
#### Modules Scheduled Tasks
141141
To add a scheduled task directly from within a module, add the file `scheduled_tasks.py`
142-
in the module package. From there, the function `schedule_tasks` accepting `BackgroundScheudler`
142+
in the module package. From there, the function `schedule_tasks` accepting `BackgroundScheduler`
143143
as argument must be accessible.
144144

145145
**Example content of scheduled_tasks.py:**
146146
```python
147147
def module_task():
148148
...
149149

150-
def schedule_tasks(scheduler: BackgroundScheduler): # Has to accept BackgroundScheudler as input
150+
def schedule_tasks(scheduler: BackgroundScheduler): # Has to accept BackgroundScheduler as input
151151
scheduler.add_job(
152152
module_task,
153153
trigger=CronTrigger(hour=8), # Daily at 8 AM
@@ -161,7 +161,7 @@ set to `True`.
161161
### Graphene Custom Types & Helper Classes/Methods
162162
* schema.SmallInt: Integer, with values ranging from -32768 to +32767
163163
* schema.TinyInt: Integer (8 bit), with values ranging from 0 to 255
164-
* utils.filter_validity: many openIMIS entities have a validity_from/validity_to, this filters provides a helper implementing the vality logic based on date (today if None)
164+
* utils.filter_validity: many openIMIS entities have a validity_from/validity_to, this filters provides a helper implementing the validity logic based on date (today if None)
165165
Sample usage:
166166
```
167167
Insuree.objects.get(
@@ -235,14 +235,14 @@ TechnicalUserForm (ability to add technical users from the console)
235235
which allows filtering by json field attributes in the SQL Server database. Filtering by simple data types and nested
236236
arguments. The use is as follows:
237237
```
238-
claim.objects.filter(json_ext__jsoncontains={'amount': 10.00, 'adress': { 'country': 'X', 'city': 'Y'}})
238+
claim.objects.filter(json_ext__jsoncontains={'amount': 10.00, 'address': { 'country': 'X', 'city': 'Y'}})
239239
```
240240
* jsoncontainskey - another custom filtering parameter for json fields. Equivalent to `__contains` search on
241241
underlying serialized json string. It allows queries as:
242242
```
243243
claim.objects.filter(json_ext__jsoncontainskey='amount')
244244
```
245-
This query searches for `"amouunt":` in json string, so it will match keys in nested json objects
245+
This query searches for `"amount":` in json string, so it will match keys in nested json objects
246246

247247
### WebSocket client
248248
The module gives access to WebSocket clients allowing external socket communication.
@@ -270,7 +270,7 @@ with websocket_instance.connect() as connection: # keeps connection open
270270
## Abstract calculation rule class
271271
* core/abs_calculation_rule: here is defined the abstract calculation rule class that might be used
272272
for defining some calculation rules.
273-
* class is a representation of calculation rule. Here are defined some informations about rule and how some actions
273+
* class is a representation of calculation rule. Here are defined some information about rule and how some actions
274274
are implemented.
275275
* members
276276
- version (static) - the version is used to keep track of the changes in the version of the calculation rule,
@@ -347,7 +347,7 @@ CLASS_RULE_PARAM_VALIDATION = [
347347
- get_linked_class(List[classname]) - that function will return the possible instance that can have a link to the calculation
348348
- convert(instance, convert_to, **argv) - Convert on or several object toward another type, especially to invoice or bill . It will check the from-to, and the rights then will call the Function Name with agrv as parameters
349349
* generic methods defined on abstract class level
350-
- get_rule_name(classname) - return an object which is representation of calculaton rule
350+
- get_rule_name(classname) - return an object which is representation of calculation rule
351351
- get_rule_details(classname) - return the data about class and parameters
352352
- get_parameters(class_name, instance) - Function to obtain the required parameter and its properties for an instance of certain model.
353353
This function is registered to the module signal via the ready function if the rule is active

core/__init__.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
from core.utils import *
2-
from core.datetimes.shared import *
3-
from core.custom_lookups import *
1+
from core.utils import * # noqa: F401,F403
2+
from core.datetimes.shared import * # noqa: F401,F403
3+
from core.custom_lookups import * # noqa: F401,F403
44

5-
default_app_config = 'core.apps.CoreConfig'
5+
default_app_config = "core.apps.CoreConfig"
66

77
# For IDE support, filled at runtime
88
datetime = None

core/abs_calculation_rule.py

Lines changed: 44 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ def get_calculation_rule_name(cls):
3232
def set_calculation_rule_name(cls, val):
3333
type(cls)._calculation_rule_name = val
3434

35-
calculation_rule_name = property(get_calculation_rule_name, set_calculation_rule_name)
35+
calculation_rule_name = property(
36+
get_calculation_rule_name, set_calculation_rule_name
37+
)
3638

3739
@classmethod
3840
def get_description(cls):
@@ -52,7 +54,9 @@ def get_impacted_class_parameter(cls):
5254
def set_impacted_class_parameter(cls, val):
5355
type(cls)._impacted_class_parameter = val
5456

55-
impacted_class_parameter = property(get_impacted_class_parameter, set_impacted_class_parameter)
57+
impacted_class_parameter = property(
58+
get_impacted_class_parameter, set_impacted_class_parameter
59+
)
5660

5761
@classmethod
5862
def get_type(cls):
@@ -87,8 +91,11 @@ def set_from_to(cls, val):
8791
@classmethod
8892
def ready(cls):
8993
now = datetime.datetime.now()
90-
condition_is_valid = (now >= cls.date_valid_from and now <= cls.date_valid_to) \
91-
if cls.date_valid_to else (now >= cls.date_valid_from and cls.date_valid_to is None)
94+
condition_is_valid = (
95+
(now >= cls.date_valid_from and now <= cls.date_valid_to)
96+
if cls.date_valid_to
97+
else (now >= cls.date_valid_from and cls.date_valid_to is None)
98+
)
9299
if not condition_is_valid:
93100
cls.status = "inactive"
94101

@@ -111,14 +118,18 @@ def calculate(cls, instance, *args, **kwargs):
111118
def get_linked_class(cls, sender, class_name, **kwargs):
112119
# calculation are loaded on the side, therefore contentType have to be loaded on execution
113120
from django.contrib.contenttypes.models import ContentType
121+
114122
list_class = []
115123
if class_name is not None:
116124
model_class = ContentType.objects.filter(model__iexact=class_name).first()
117125
if model_class:
118126
model_class = model_class.model_class()
119-
list_class = list_class + \
120-
[f.remote_field.model.__name__ for f in model_class._meta.fields
121-
if f.get_internal_type() == 'ForeignKey' and f.remote_field.model.__name__ != "User"]
127+
list_class = list_class + [
128+
f.remote_field.model.__name__
129+
for f in model_class._meta.fields
130+
if f.get_internal_type() == "ForeignKey"
131+
and f.remote_field.model.__name__ != "User"
132+
]
122133
else:
123134
list_class.append("Calculation")
124135
return list_class
@@ -143,29 +154,33 @@ def get_rule_details(cls, sender, class_name, **kwargs):
143154
@classmethod
144155
def get_parameters(cls, sender, class_name, instance, **kwargs):
145156
"""
146-
class_name is the class name of the object where the calculation param need to be added
147-
instance is where the link with a calculation need to be found,
148-
like the CPB in case of PH insuree or Contract Details
149-
return a list only with rule details that matches step 1 and 2
157+
class_name is the class name of the object where the calculation param need to be added
158+
instance is where the link with a calculation need to be found,
159+
like the CPB in case of PH insuree or Contract Details
160+
return a list only with rule details that matches step 1 and 2
150161
"""
151162
rule_details = cls.get_rule_details(sender=sender, class_name=class_name)
152163
if rule_details:
153164
if cls.check_calculation(instance=instance):
154-
return rule_details["parameters"] if "parameters" in rule_details else []
165+
return (
166+
rule_details["parameters"] if "parameters" in rule_details else []
167+
)
155168

156169
@classmethod
157170
def run_calculation_rules(cls, sender, instance, user, context, **kwargs):
158171
"""
159-
this function will send a signal and the rules will
160-
reply if they have object matching the classname in their list of object
172+
this function will send a signal and the rules will
173+
reply if they have object matching the classname in their list of object
161174
"""
162175
list_class = cls.get_linked_class(sender, instance.__class__.__name__)
163176
# if the class have a calculation param, (like contribution or payment plan) add class name
164-
if hasattr(instance, 'calculation'):
177+
if hasattr(instance, "calculation"):
165178
list_class.append(instance.__class__.__name__)
166179
if list_class:
167180
for class_name in list_class:
168-
rule_details = cls.get_rule_details(class_name=class_name, sender=sender)
181+
rule_details = cls.get_rule_details(
182+
class_name=class_name, sender=sender
183+
)
169184
if rule_details or len(cls.impacted_class_parameter) == 0:
170185
# add context to kwargs
171186
kwargs["context"] = context
@@ -174,32 +189,39 @@ def run_calculation_rules(cls, sender, instance, user, context, **kwargs):
174189

175190
@classmethod
176191
def calculate_if_active_for_object(cls, instance, **kwargs):
177-
if cls.active_for_object(instance=instance, context=kwargs['context']):
192+
if cls.active_for_object(instance=instance, context=kwargs["context"]):
178193
return cls.calculate(instance, **kwargs)
179194

180195
@classmethod
181196
def run_convert(cls, instance, convert_to, **kwargs):
182197
"""
183-
execute the conversion for the instance with the first
184-
rule that provide the conversion (see get_convert_from_to)
198+
execute the conversion for the instance with the first
199+
rule that provide the conversion (see get_convert_from_to)
185200
"""
186201
convert_from = instance.__class__.__name__
187202
if convert_from == "Contract":
188203
convert_from = "ContractContributionPlanDetails"
189204
list_possible_conversion = cls.get_convert_from_to()
190205
for possible_conversion in list_possible_conversion:
191-
if convert_from == possible_conversion['from'] and convert_to == possible_conversion['to']:
206+
if (
207+
convert_from == possible_conversion["from"]
208+
and convert_to == possible_conversion["to"]
209+
):
192210
result = cls.convert(instance=instance, convert_to=convert_to, **kwargs)
193211
return result
194212

195213
@classmethod
196214
def get_convert_from_to(cls):
197215
"""
198-
get the possible conversion, return [calc UUID, from, to]
216+
get the possible conversion, return [calc UUID, from, to]
199217
"""
200218
list_possible_conversion = []
201219
for ft in cls.from_to:
202-
convert_from_to = {'calc_uuid': cls.uuid, 'from': ft['from'], 'to': ft['to']}
220+
convert_from_to = {
221+
"calc_uuid": cls.uuid,
222+
"from": ft["from"],
223+
"to": ft["to"],
224+
}
203225
list_possible_conversion.append(convert_from_to)
204226
return list_possible_conversion
205227

0 commit comments

Comments
 (0)