diff --git a/policy/__init__.py b/policy/__init__.py index c989d16..cbe669f 100644 --- a/policy/__init__.py +++ b/policy/__init__.py @@ -1 +1 @@ -default_app_config = 'policy.apps.PolicyConfig' +default_app_config = "policy.apps.PolicyConfig" diff --git a/policy/apps.py b/policy/apps.py index 9f90c69..274944a 100644 --- a/policy/apps.py +++ b/policy/apps.py @@ -6,16 +6,16 @@ MODULE_NAME = "policy" DEFAULT_CFG = { - "gql_query_policies_perms": ['101201'], + "gql_query_policies_perms": ["101201"], "gql_query_policy_officers_perms": [], - "gql_query_policies_by_insuree_perms": ['101201'], - "gql_query_policies_by_family_perms": ['101201'], - "gql_query_eligibilities_perms": ['101201'], - "gql_mutation_create_policies_perms": ['101202'], - "gql_mutation_renew_policies_perms": ['101205'], - "gql_mutation_edit_policies_perms": ['101203'], - "gql_mutation_suspend_policies_perms": ['101203'], - "gql_mutation_delete_policies_perms": ['101204'], + "gql_query_policies_by_insuree_perms": ["101201"], + "gql_query_policies_by_family_perms": ["101201"], + "gql_query_eligibilities_perms": ["101201"], + "gql_mutation_create_policies_perms": ["101202"], + "gql_mutation_renew_policies_perms": ["101205"], + "gql_mutation_edit_policies_perms": ["101203"], + "gql_mutation_suspend_policies_perms": ["101203"], + "gql_mutation_delete_policies_perms": ["101204"], "policy_renewal_interval": 14, # Notify renewal nb of days before expiry date "policy_location_via": "family", # ... or product "default_eligibility_disabled": False, @@ -26,6 +26,7 @@ "contribution_receipt_length": 5, } + class PolicyConfig(AppConfig): name = MODULE_NAME @@ -55,5 +56,6 @@ def __load_config(self, cfg): def ready(self): from core.models import ModuleConfiguration + cfg = ModuleConfiguration.get_or_default(MODULE_NAME, DEFAULT_CFG) - self.__load_config(cfg) \ No newline at end of file + self.__load_config(cfg) diff --git a/policy/gql_mutations.py b/policy/gql_mutations.py index 877bb39..560d1ff 100644 --- a/policy/gql_mutations.py +++ b/policy/gql_mutations.py @@ -38,19 +38,21 @@ class CreateRenewOrUpdatePolicyMutation(OpenIMISMutation): @classmethod def do_mutate(cls, perms, user, **data): if type(user) is AnonymousUser or not user.id: - raise ValidationError( - _("mutation.authentication_required")) + raise ValidationError(_("mutation.authentication_required")) if not user.has_perms(perms): raise PermissionDenied(_("unauthorized")) client_mutation_id = data.get("client_mutation_id") errors = validate_idle_policy(data) if len(errors): return errors - data['audit_user_id'] = user.id_for_audit + data["audit_user_id"] = user.id_for_audit from core.utils import TimeUtils - data['validity_from'] = TimeUtils.now() + + data["validity_from"] = TimeUtils.now() policy = PolicyService(user).update_or_create(data, user) - PolicyMutation.object_mutated(user, client_mutation_id=client_mutation_id, policy=policy) + PolicyMutation.object_mutated( + user, client_mutation_id=client_mutation_id, policy=policy + ) return None @@ -67,12 +69,17 @@ def async_mutate(cls, user, **data): with transaction.atomic(): data["status"] = Policy.STATUS_IDLE data["stage"] = Policy.STAGE_NEW - return cls.do_mutate(PolicyConfig.gql_mutation_create_policies_perms, user, **data) + return cls.do_mutate( + PolicyConfig.gql_mutation_create_policies_perms, user, **data + ) except Exception as exc: - return [{ - 'message': _("policy.mutation.failed_to_create_policy"), - 'detail': str(exc), - 'exc': exc}] + return [ + { + "message": _("policy.mutation.failed_to_create_policy"), + "detail": str(exc), + "exc": exc, + } + ] class UpdatePolicyMutation(CreateRenewOrUpdatePolicyMutation): @@ -86,12 +93,17 @@ class Input(PolicyInputType): def async_mutate(cls, user, **data): try: with transaction.atomic(): - return cls.do_mutate(PolicyConfig.gql_mutation_edit_policies_perms, user, **data) + return cls.do_mutate( + PolicyConfig.gql_mutation_edit_policies_perms, user, **data + ) except Exception as exc: - return [{ - 'message': _("policy.mutation.failed_to_update_policy"), - 'detail': str(exc), - 'exc': exc}] + return [ + { + "message": _("policy.mutation.failed_to_update_policy"), + "detail": str(exc), + "exc": exc, + } + ] class RenewPolicyMutation(CreateRenewOrUpdatePolicyMutation): @@ -106,16 +118,21 @@ def async_mutate(cls, user, **data): try: with transaction.atomic(): # ensure we don't update the existing one, but recreate a new one! - if 'policy_uuid' in data: - data['prev_policy'] = data.pop('policy_uuid') + if "policy_uuid" in data: + data["prev_policy"] = data.pop("policy_uuid") data["status"] = Policy.STATUS_IDLE data["stage"] = Policy.STAGE_RENEWED - return cls.do_mutate(PolicyConfig.gql_mutation_renew_policies_perms, user, **data) + return cls.do_mutate( + PolicyConfig.gql_mutation_renew_policies_perms, user, **data + ) except Exception as exc: - return [{ - 'message': _("policy.mutation.failed_to_renew_policy"), - 'detail': str(exc), - 'exc': exc}] + return [ + { + "message": _("policy.mutation.failed_to_renew_policy"), + "detail": str(exc), + "exc": exc, + } + ] class SuspendPoliciesMutation(OpenIMISMutation): @@ -130,8 +147,7 @@ def async_mutate(cls, user, **data): try: with transaction.atomic(): if type(user) is AnonymousUser or not user.id: - raise ValidationError( - _("mutation.authentication_required")) + raise ValidationError(_("mutation.authentication_required")) if not user.has_perms(PolicyConfig.gql_mutation_suspend_policies_perms): raise PermissionDenied(_("unauthorized")) errors = [] @@ -139,20 +155,27 @@ def async_mutate(cls, user, **data): policy = Policy.objects.filter(uuid=policy_uuid).first() if policy is None: errors += { - 'title': policy_uuid, - 'list': [{'message': _( - "policy.mutation.id_does_not_exist") % {'id': policy_uuid}}] + "title": policy_uuid, + "list": [ + { + "message": _("policy.mutation.id_does_not_exist") + % {"id": policy_uuid} + } + ], } continue errors += PolicyService(user).set_suspended(user, policy) if len(errors) == 1: - errors = errors[0]['list'] + errors = errors[0]["list"] return errors except Exception as exc: - return [{ - 'message': _("policy.mutation.failed_to_suspend_policy"), - 'detail': str(exc), - 'exc': exc}] + return [ + { + "message": _("policy.mutation.failed_to_suspend_policy"), + "detail": str(exc), + "exc": exc, + } + ] class DeletePoliciesMutation(OpenIMISMutation): @@ -170,22 +193,27 @@ def async_mutate(cls, user, **data): raise PermissionDenied(_("unauthorized")) errors = [] for policy_uuid in data["uuids"]: - policy = Policy.objects \ - .filter(uuid=policy_uuid) \ - .first() + policy = Policy.objects.filter(uuid=policy_uuid).first() if policy is None: errors += { - 'title': policy_uuid, - 'list': [{'message': _( - "policy.validation.id_does_not_exist") % {'id': policy_uuid}}] + "title": policy_uuid, + "list": [ + { + "message": _("policy.validation.id_does_not_exist") + % {"id": policy_uuid} + } + ], } continue errors += PolicyService(user).set_deleted(policy) if len(errors) == 1: - errors = errors[0]['list'] + errors = errors[0]["list"] return errors except Exception as exc: - return [{ - 'message': _("policy.mutation.failed_to_delete_policies"), - 'detail': str(exc), - 'exc': exc}] + return [ + { + "message": _("policy.mutation.failed_to_delete_policies"), + "detail": str(exc), + "exc": exc, + } + ] diff --git a/policy/gql_queries.py b/policy/gql_queries.py index e51986e..894633d 100644 --- a/policy/gql_queries.py +++ b/policy/gql_queries.py @@ -3,7 +3,11 @@ from django.utils.translation import gettext as _ from .apps import PolicyConfig from .models import Policy -from core import prefix_filterset, filter_validity, ExtendedConnection, ExtendedRelayConnection +from core import ( + prefix_filterset, + ExtendedConnection, + ExtendedRelayConnection, +) from core.schema import OfficerGQLType from product.schema import ProductGQLType from django.core.exceptions import PermissionDenied @@ -11,7 +15,7 @@ class PolicyGQLType(DjangoObjectType): sum_premiums = graphene.Float(source="sum_premiums") - + def resolve_family(self, info): if not info.context.user.has_perms(PolicyConfig.gql_query_policies_perms): raise PermissionDenied(_("unauthorized")) @@ -104,4 +108,3 @@ class EligibilityGQLType(graphene.ObjectType): item_left = graphene.Int() is_item_ok = graphene.Boolean() is_service_ok = graphene.Boolean() - diff --git a/policy/migrations/0001_initial.py b/policy/migrations/0001_initial.py index 8680f29..c93aeb4 100644 --- a/policy/migrations/0001_initial.py +++ b/policy/migrations/0001_initial.py @@ -8,31 +8,78 @@ class Migration(migrations.Migration): initial = True - dependencies = [ - ] + dependencies = [] operations = [ migrations.CreateModel( - name='Policy', + name="Policy", fields=[ - ('id', models.AutoField(db_column='PolicyID', primary_key=True, serialize=False)), - ('legacy_id', models.IntegerField(blank=True, db_column='LegacyID', null=True)), - ('stage', models.CharField(blank=True, db_column='PolicyStage', max_length=1, null=True)), - ('status', models.SmallIntegerField(blank=True, db_column='PolicyStatus', null=True)), - ('value', models.DecimalField(blank=True, db_column='PolicyValue', decimal_places=2, max_digits=18, null=True)), - ('enroll_date', core.fields.DateField(db_column='EnrollDate')), - ('start_date', core.fields.DateField(db_column='StartDate')), - ('effective_date', core.fields.DateField(blank=True, db_column='EffectiveDate', null=True)), - ('expiry_date', core.fields.DateField(blank=True, db_column='ExpiryDate', null=True)), - ('validity_from', core.fields.DateTimeField(db_column='ValidityFrom')), - ('validity_to', core.fields.DateTimeField(blank=True, db_column='ValidityTo', null=True)), - ('offline', models.BooleanField(blank=True, db_column='isOffline', null=True)), - ('audit_user_id', models.IntegerField(db_column='AuditUserID')), - ('row_id', models.BinaryField(blank=True, db_column='RowID', null=True)), + ( + "id", + models.AutoField( + db_column="PolicyID", primary_key=True, serialize=False + ), + ), + ( + "legacy_id", + models.IntegerField(blank=True, db_column="LegacyID", null=True), + ), + ( + "stage", + models.CharField( + blank=True, db_column="PolicyStage", max_length=1, null=True + ), + ), + ( + "status", + models.SmallIntegerField( + blank=True, db_column="PolicyStatus", null=True + ), + ), + ( + "value", + models.DecimalField( + blank=True, + db_column="PolicyValue", + decimal_places=2, + max_digits=18, + null=True, + ), + ), + ("enroll_date", core.fields.DateField(db_column="EnrollDate")), + ("start_date", core.fields.DateField(db_column="StartDate")), + ( + "effective_date", + core.fields.DateField( + blank=True, db_column="EffectiveDate", null=True + ), + ), + ( + "expiry_date", + core.fields.DateField( + blank=True, db_column="ExpiryDate", null=True + ), + ), + ("validity_from", core.fields.DateTimeField(db_column="ValidityFrom")), + ( + "validity_to", + core.fields.DateTimeField( + blank=True, db_column="ValidityTo", null=True + ), + ), + ( + "offline", + models.BooleanField(blank=True, db_column="isOffline", null=True), + ), + ("audit_user_id", models.IntegerField(db_column="AuditUserID")), + ( + "row_id", + models.BinaryField(blank=True, db_column="RowID", null=True), + ), ], options={ - 'db_table': 'tblPolicy', - 'managed': False, + "db_table": "tblPolicy", + "managed": False, }, ), ] diff --git a/policy/migrations/0002_policy_renewals.py b/policy/migrations/0002_policy_renewals.py index 58ffcef..688a526 100644 --- a/policy/migrations/0002_policy_renewals.py +++ b/policy/migrations/0002_policy_renewals.py @@ -9,44 +9,124 @@ class Migration(migrations.Migration): dependencies = [ - ('policy', '0001_initial'), + ("policy", "0001_initial"), ] operations = [ migrations.CreateModel( - name='PolicyRenewal', + name="PolicyRenewal", fields=[ - ('validity_from', core.fields.DateTimeField(db_column='ValidityFrom', default=datetime.datetime.now)), - ('validity_to', core.fields.DateTimeField(blank=True, db_column='ValidityTo', null=True)), - ('legacy_id', models.IntegerField(blank=True, db_column='LegacyID', null=True)), - ('id', models.AutoField(db_column='RenewalID', primary_key=True, serialize=False)), - ('uuid', models.CharField(db_column='RenewalUUID', default=uuid.uuid4, max_length=36, unique=True)), - ('renewal_prompt_date', core.fields.DateField(db_column='RenewalPromptDate')), - ('renewal_date', core.fields.DateField(db_column='RenewalDate')), - ('phone_number', models.CharField(blank=True, db_column='PhoneNumber', max_length=25, null=True)), - ('sms_status', models.SmallIntegerField(db_column='SMSStatus', default=0)), - ('renewal_warnings', models.SmallIntegerField(blank=True, db_column='RenewalWarnings', default=0, null=True)), - ('response_status', models.IntegerField(blank=True, db_column='ResponseStatus', default=0, null=True)), - ('response_date', core.fields.DateTimeField(blank=True, db_column='ResponseDate', null=True)), - ('audit_user_id', models.IntegerField(blank=True, db_column='AuditCreateUser', null=True)), + ( + "validity_from", + core.fields.DateTimeField( + db_column="ValidityFrom", default=datetime.datetime.now + ), + ), + ( + "validity_to", + core.fields.DateTimeField( + blank=True, db_column="ValidityTo", null=True + ), + ), + ( + "legacy_id", + models.IntegerField(blank=True, db_column="LegacyID", null=True), + ), + ( + "id", + models.AutoField( + db_column="RenewalID", primary_key=True, serialize=False + ), + ), + ( + "uuid", + models.CharField( + db_column="RenewalUUID", + default=uuid.uuid4, + max_length=36, + unique=True, + ), + ), + ( + "renewal_prompt_date", + core.fields.DateField(db_column="RenewalPromptDate"), + ), + ("renewal_date", core.fields.DateField(db_column="RenewalDate")), + ( + "phone_number", + models.CharField( + blank=True, db_column="PhoneNumber", max_length=25, null=True + ), + ), + ( + "sms_status", + models.SmallIntegerField(db_column="SMSStatus", default=0), + ), + ( + "renewal_warnings", + models.SmallIntegerField( + blank=True, db_column="RenewalWarnings", default=0, null=True + ), + ), + ( + "response_status", + models.IntegerField( + blank=True, db_column="ResponseStatus", default=0, null=True + ), + ), + ( + "response_date", + core.fields.DateTimeField( + blank=True, db_column="ResponseDate", null=True + ), + ), + ( + "audit_user_id", + models.IntegerField( + blank=True, db_column="AuditCreateUser", null=True + ), + ), ], options={ - 'db_table': 'tblPolicyRenewals', - 'managed': False, + "db_table": "tblPolicyRenewals", + "managed": False, }, ), migrations.CreateModel( - name='PolicyRenewalDetail', + name="PolicyRenewalDetail", fields=[ - ('validity_from', core.fields.DateTimeField(db_column='ValidityFrom', default=datetime.datetime.now)), - ('validity_to', core.fields.DateTimeField(blank=True, db_column='ValidityTo', null=True)), - ('legacy_id', models.IntegerField(blank=True, db_column='LegacyID', null=True)), - ('id', models.AutoField(db_column='RenewalDetailID', primary_key=True, serialize=False)), - ('audit_user_id', models.IntegerField(blank=True, db_column='AuditCreateUser', null=True)), + ( + "validity_from", + core.fields.DateTimeField( + db_column="ValidityFrom", default=datetime.datetime.now + ), + ), + ( + "validity_to", + core.fields.DateTimeField( + blank=True, db_column="ValidityTo", null=True + ), + ), + ( + "legacy_id", + models.IntegerField(blank=True, db_column="LegacyID", null=True), + ), + ( + "id", + models.AutoField( + db_column="RenewalDetailID", primary_key=True, serialize=False + ), + ), + ( + "audit_user_id", + models.IntegerField( + blank=True, db_column="AuditCreateUser", null=True + ), + ), ], options={ - 'db_table': 'tblPolicyRenewalDetails', - 'managed': False, + "db_table": "tblPolicyRenewalDetails", + "managed": False, }, ), ] diff --git a/policy/migrations/0003_auto_20201021_0811.py b/policy/migrations/0003_auto_20201021_0811.py index b46ac29..1888b0e 100644 --- a/policy/migrations/0003_auto_20201021_0811.py +++ b/policy/migrations/0003_auto_20201021_0811.py @@ -8,24 +8,46 @@ class Migration(migrations.Migration): dependencies = [ - ('core', '0009_mutationlog_client_mutation_details'), - ('policy', '0002_policy_renewals'), + ("core", "0009_mutationlog_client_mutation_details"), + ("policy", "0002_policy_renewals"), ] operations = [ migrations.CreateModel( - name='PolicyMutation', + name="PolicyMutation", fields=[ - ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), - ('mutation', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='policies', to='core.MutationLog')), - ('policy', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='mutations', to='policy.Policy')), + ( + "id", + models.UUIDField( + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + ), + ), + ( + "mutation", + models.ForeignKey( + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="policies", + to="core.MutationLog", + ), + ), + ( + "policy", + models.ForeignKey( + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="mutations", + to="policy.Policy", + ), + ), ], options={ - 'db_table': 'location_PolicyMutation', - 'managed': True, + "db_table": "location_PolicyMutation", + "managed": True, }, ), migrations.DeleteModel( - name='PolicyRenewalDetail', + name="PolicyRenewalDetail", ), ] diff --git a/policy/migrations/0004_add_medical_oficer_reading_rights.py b/policy/migrations/0004_add_medical_oficer_reading_rights.py index 4887d6e..952cb02 100644 --- a/policy/migrations/0004_add_medical_oficer_reading_rights.py +++ b/policy/migrations/0004_add_medical_oficer_reading_rights.py @@ -11,19 +11,16 @@ MEDICAL_OFFICER_SYSTEM_ROLE_ID = 16 # Medical officer - def create_role_right(apps, schema_editor): - if schema_editor.connection.alias != 'default': + if schema_editor.connection.alias != "default": return for right_id in ROLE_RIGHTS_ID: - insert_role_right_for_system(MEDICAL_OFFICER_SYSTEM_ROLE_ID, right_id, apps ) + insert_role_right_for_system(MEDICAL_OFFICER_SYSTEM_ROLE_ID, right_id, apps) class Migration(migrations.Migration): - dependencies = [ - ('policy', '0003_auto_20201021_0811') - ] + dependencies = [("policy", "0003_auto_20201021_0811")] operations = [ migrations.RunPython(create_role_right), diff --git a/policy/migrations/0005_add_foreign_keys.py b/policy/migrations/0005_add_foreign_keys.py index 3ec5b41..a03f231 100644 --- a/policy/migrations/0005_add_foreign_keys.py +++ b/policy/migrations/0005_add_foreign_keys.py @@ -10,60 +10,103 @@ class Migration(migrations.Migration): dependencies = [ - ('insuree', '0013_auto_20211103_1023'), - ('core', '0019_extended_field'), - ('product', '0006_insert_ceiling_type'), - ('policy', '0004_add_medical_oficer_reading_rights'), + ("insuree", "0013_auto_20211103_1023"), + ("core", "0019_extended_field"), + ("product", "0006_insert_ceiling_type"), + ("policy", "0004_add_medical_oficer_reading_rights"), ] operations = [ migrations.RemoveField( - model_name='policy', - name='row_id', + model_name="policy", + name="row_id", ), migrations.AddField( - model_name='policy', - name='family', - field=models.ForeignKey(db_column='FamilyID', on_delete=django.db.models.deletion.DO_NOTHING, related_name='policies', to='insuree.family'), + model_name="policy", + name="family", + field=models.ForeignKey( + db_column="FamilyID", + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="policies", + to="insuree.family", + ), ), migrations.AddField( - model_name='policy', - name='officer', - field=models.ForeignKey(blank=True, db_column='OfficerID', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='policies', to='core.officer'), + model_name="policy", + name="officer", + field=models.ForeignKey( + blank=True, + db_column="OfficerID", + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="policies", + to="core.officer", + ), ), migrations.AddField( - model_name='policy', - name='product', - field=models.ForeignKey(db_column='ProdID', on_delete=django.db.models.deletion.DO_NOTHING, related_name='policies', to='product.product'), + model_name="policy", + name="product", + field=models.ForeignKey( + db_column="ProdID", + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="policies", + to="product.product", + ), ), migrations.AddField( - model_name='policy', - name='uuid', - field=models.CharField(db_column='PolicyUUID', default=uuid.uuid4, max_length=36, unique=True), + model_name="policy", + name="uuid", + field=models.CharField( + db_column="PolicyUUID", default=uuid.uuid4, max_length=36, unique=True + ), ), migrations.AddField( - model_name='policyrenewal', - name='insuree', - field=models.ForeignKey(db_column='InsureeID', on_delete=django.db.models.deletion.DO_NOTHING, related_name='policy_renewals', to='insuree.insuree'), + model_name="policyrenewal", + name="insuree", + field=models.ForeignKey( + db_column="InsureeID", + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="policy_renewals", + to="insuree.insuree", + ), ), migrations.AddField( - model_name='policyrenewal', - name='new_officer', - field=models.ForeignKey(blank=True, db_column='NewOfficerID', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='policy_renewals', to='core.officer'), + model_name="policyrenewal", + name="new_officer", + field=models.ForeignKey( + blank=True, + db_column="NewOfficerID", + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="policy_renewals", + to="core.officer", + ), ), migrations.AddField( - model_name='policyrenewal', - name='new_product', - field=models.ForeignKey(db_column='NewProdID', on_delete=django.db.models.deletion.DO_NOTHING, related_name='policy_renewals', to='product.product'), + model_name="policyrenewal", + name="new_product", + field=models.ForeignKey( + db_column="NewProdID", + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="policy_renewals", + to="product.product", + ), ), migrations.AddField( - model_name='policyrenewal', - name='policy', - field=models.ForeignKey(db_column='PolicyID', on_delete=django.db.models.deletion.DO_NOTHING, related_name='policy_renewals', to='policy.policy'), + model_name="policyrenewal", + name="policy", + field=models.ForeignKey( + db_column="PolicyID", + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="policy_renewals", + to="policy.policy", + ), ), migrations.AlterField( - model_name='policy', - name='validity_from', - field=core.fields.DateTimeField(db_column='ValidityFrom', default=datetime.datetime.now), + model_name="policy", + name="validity_from", + field=core.fields.DateTimeField( + db_column="ValidityFrom", default=datetime.datetime.now + ), ), ] diff --git a/policy/migrations/0006_set_model_managed_to_true.py b/policy/migrations/0006_set_model_managed_to_true.py index eec4f69..9825f92 100644 --- a/policy/migrations/0006_set_model_managed_to_true.py +++ b/policy/migrations/0006_set_model_managed_to_true.py @@ -6,16 +6,16 @@ class Migration(migrations.Migration): dependencies = [ - ('policy', '0005_add_foreign_keys'), + ("policy", "0005_add_foreign_keys"), ] operations = [ migrations.AlterModelOptions( - name='policy', - options={'managed': True}, + name="policy", + options={"managed": True}, ), migrations.AlterModelOptions( - name='policyrenewal', - options={'managed': True}, + name="policyrenewal", + options={"managed": True}, ), - ] \ No newline at end of file + ] diff --git a/policy/migrations/0007_fix_policy_mutation_name.py b/policy/migrations/0007_fix_policy_mutation_name.py index 231b210..35e5dd6 100644 --- a/policy/migrations/0007_fix_policy_mutation_name.py +++ b/policy/migrations/0007_fix_policy_mutation_name.py @@ -6,12 +6,12 @@ class Migration(migrations.Migration): dependencies = [ - ('policy', '0006_set_model_managed_to_true'), + ("policy", "0006_set_model_managed_to_true"), ] operations = [ migrations.AlterModelTable( - name='policymutation', - table='policy_PolicyMutation', + name="policymutation", + table="policy_PolicyMutation", ), ] diff --git a/policy/migrations/0008_remove_policy_row_id_policy_contribution_plan.py b/policy/migrations/0008_remove_policy_row_id_policy_contribution_plan.py index 5e9f18e..968a0e9 100644 --- a/policy/migrations/0008_remove_policy_row_id_policy_contribution_plan.py +++ b/policy/migrations/0008_remove_policy_row_id_policy_contribution_plan.py @@ -7,14 +7,21 @@ class Migration(migrations.Migration): dependencies = [ - ('contribution_plan', '0013_alter_contributionplan_date_created_and_more'), - ('policy', '0007_fix_policy_mutation_name'), + ("contribution_plan", "0013_alter_contributionplan_date_created_and_more"), + ("policy", "0007_fix_policy_mutation_name"), ] operations = [ migrations.AddField( - model_name='policy', - name='contribution_plan', - field=models.ForeignKey(blank=True, db_column='ContributionPlanID', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='contribution_plans', to='contribution_plan.contributionplan'), + model_name="policy", + name="contribution_plan", + field=models.ForeignKey( + blank=True, + db_column="ContributionPlanID", + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="contribution_plans", + to="contribution_plan.contributionplan", + ), ), ] diff --git a/policy/migrations/0009_auto_20240716_1744.py b/policy/migrations/0009_auto_20240716_1744.py index b945c3c..ef1c374 100644 --- a/policy/migrations/0009_auto_20240716_1744.py +++ b/policy/migrations/0009_auto_20240716_1744.py @@ -7,13 +7,18 @@ class Migration(migrations.Migration): dependencies = [ - ('policy', '0008_remove_policy_row_id_policy_contribution_plan'), + ("policy", "0008_remove_policy_row_id_policy_contribution_plan"), ] operations = [ migrations.AddField( - model_name='policy', - name='creation_date', - field=models.DateField(blank=True, default=django.utils.timezone.now, db_column='creationDate', null=True), + model_name="policy", + name="creation_date", + field=models.DateField( + blank=True, + default=django.utils.timezone.now, + db_column="creationDate", + null=True, + ), ), - ] \ No newline at end of file + ] diff --git a/policy/models.py b/policy/models.py index 154b8f5..2e13faa 100644 --- a/policy/models.py +++ b/policy/models.py @@ -1,9 +1,10 @@ import uuid - +import sys from core import fields from core import models as core_models from core.utils import filter_validity from core.models import Officer +from django.core.cache import cache from django.conf import settings from django.db import models @@ -12,44 +13,75 @@ from product.models import Product from contribution_plan.models import ContributionPlan from django.utils import timezone as django_tz +from django.db.models.signals import post_save, post_delete +from django.dispatch import receiver + class Policy(core_models.VersionedModel): - id = models.AutoField(db_column='PolicyID', primary_key=True) - uuid = models.CharField(db_column='PolicyUUID', max_length=36, default=uuid.uuid4, unique=True) - - stage = models.CharField(db_column='PolicyStage', max_length=1, blank=True, null=True) - status = models.SmallIntegerField(db_column='PolicyStatus', blank=True, null=True) - value = models.DecimalField(db_column='PolicyValue', max_digits=18, decimal_places=2, blank=True, null=True) - - family = models.ForeignKey(Family, models.DO_NOTHING, db_column='FamilyID', related_name="policies") - enroll_date = fields.DateField(db_column='EnrollDate') - start_date = fields.DateField(db_column='StartDate') - effective_date = fields.DateField(db_column='EffectiveDate', blank=True, null=True) - expiry_date = fields.DateField(db_column='ExpiryDate', blank=True, null=True) - - product = models.ForeignKey(Product, models.DO_NOTHING, db_column='ProdID', related_name="policies") - officer = models.ForeignKey(Officer, models.DO_NOTHING, db_column='OfficerID', blank=True, null=True, - related_name="policies") - - offline = models.BooleanField(db_column='isOffline', blank=True, null=True) - audit_user_id = models.IntegerField(db_column='AuditUserID') - contribution_plan = models.ForeignKey(ContributionPlan, models.DO_NOTHING, - db_column='ContributionPlanID', related_name="contribution_plans", - blank=True, null=True,) - creation_date = models.DateField(db_column='creationDate', default=django_tz.now, blank=True, null=True) + id = models.AutoField(db_column="PolicyID", primary_key=True) + uuid = models.CharField( + db_column="PolicyUUID", max_length=36, default=uuid.uuid4, unique=True + ) + + stage = models.CharField( + db_column="PolicyStage", max_length=1, blank=True, null=True + ) + status = models.SmallIntegerField(db_column="PolicyStatus", blank=True, null=True) + value = models.DecimalField( + db_column="PolicyValue", max_digits=18, decimal_places=2, blank=True, null=True + ) + + family = models.ForeignKey( + Family, models.DO_NOTHING, db_column="FamilyID", related_name="policies" + ) + enroll_date = fields.DateField(db_column="EnrollDate") + start_date = fields.DateField(db_column="StartDate") + effective_date = fields.DateField(db_column="EffectiveDate", blank=True, null=True) + expiry_date = fields.DateField(db_column="ExpiryDate", blank=True, null=True) + + product = models.ForeignKey( + Product, models.DO_NOTHING, db_column="ProdID", related_name="policies" + ) + officer = models.ForeignKey( + Officer, + models.DO_NOTHING, + db_column="OfficerID", + blank=True, + null=True, + related_name="policies", + ) + + offline = models.BooleanField(db_column="isOffline", blank=True, null=True) + audit_user_id = models.IntegerField(db_column="AuditUserID") + contribution_plan = models.ForeignKey( + ContributionPlan, + models.DO_NOTHING, + db_column="ContributionPlanID", + related_name="contribution_plans", + blank=True, + null=True, + ) + creation_date = models.DateField( + db_column="creationDate", default=django_tz.now, blank=True, null=True + ) # row_id = models.BinaryField(db_column='RowID', blank=True, null=True) @staticmethod def get_query_sum_premium(photo=False): return models.Sum( - 'premiums__amount', - filter=models.Q(*filter_validity(prefix='premiums__'),premiums__is_photo_fee=photo) - ) - - def sum_premiums(self, photo = False): - return Policy.objects.filter(id=self.id).aggregate( + "premiums__amount", + filter=models.Q( + *filter_validity(prefix="premiums__"), premiums__is_photo_fee=photo + ), + ) + + def sum_premiums(self, photo=False): + return ( + Policy.objects.filter(id=self.id).aggregate( sum_premiums=Policy.get_query_sum_premium(photo) - )['sum_premiums'] or 0 + )["sum_premiums"] + or 0 + ) def claim_ded_rems(self): return self.claim_ded_rems @@ -58,10 +90,14 @@ def is_new(self): return not self.stage or self.stage == Policy.STAGE_NEW def can_add_insuree(self): - return self.family.members.filter(validity_to__isnull=True).count() < self.product.max_members + return ( + self.family.members.filter(validity_to__isnull=True).count() + < self.product.max_members + ) + class Meta: managed = True - db_table = 'tblPolicy' + db_table = "tblPolicy" STATUS_IDLE = 1 STATUS_ACTIVE = 2 @@ -69,8 +105,8 @@ class Meta: STATUS_EXPIRED = 8 STATUS_READY = 16 - STAGE_NEW = 'N' - STAGE_RENEWED = 'R' + STAGE_NEW = "N" + STAGE_RENEWED = "R" @classmethod def get_queryset(cls, queryset, user): @@ -87,44 +123,105 @@ def get_queryset(cls, queryset, user): # return queryset.filter( # health_facility__location_id__in=[l.location.id for l in dist] # ) - + return queryset class PolicyRenewal(core_models.VersionedModel): - id = models.AutoField(db_column='RenewalID', primary_key=True) - uuid = models.CharField(db_column='RenewalUUID', max_length=36, default=uuid.uuid4, unique=True) - - new_officer = models.ForeignKey(Officer, models.DO_NOTHING, db_column='NewOfficerID', - blank=True, null=True, related_name="policy_renewals") - insuree = models.ForeignKey('insuree.Insuree', models.DO_NOTHING, db_column='InsureeID', - related_name='policy_renewals') - policy = models.ForeignKey('policy.Policy', models.DO_NOTHING, db_column='PolicyID', - related_name='policy_renewals') - new_product = models.ForeignKey(Product, db_column='NewProdID', on_delete=models.DO_NOTHING, - related_name="policy_renewals") - - renewal_prompt_date = fields.DateField(db_column='RenewalPromptDate') - renewal_date = fields.DateField(db_column='RenewalDate') - phone_number = models.CharField(db_column='PhoneNumber', max_length=25, blank=True, null=True) - sms_status = models.SmallIntegerField(db_column='SMSStatus', default=0) # TODO choices - renewal_warnings = models.SmallIntegerField(db_column='RenewalWarnings', null=True, blank=True, default=0) # TODO choices - response_status = models.IntegerField(db_column='ResponseStatus', null=True, blank=True, default=0) # TODO Choices - response_date = fields.DateTimeField(db_column='ResponseDate', null=True, blank=True) - - audit_user_id = models.IntegerField(db_column='AuditCreateUser', null=True, blank=True) + id = models.AutoField(db_column="RenewalID", primary_key=True) + uuid = models.CharField( + db_column="RenewalUUID", max_length=36, default=uuid.uuid4, unique=True + ) + + new_officer = models.ForeignKey( + Officer, + models.DO_NOTHING, + db_column="NewOfficerID", + blank=True, + null=True, + related_name="policy_renewals", + ) + insuree = models.ForeignKey( + "insuree.Insuree", + models.DO_NOTHING, + db_column="InsureeID", + related_name="policy_renewals", + ) + policy = models.ForeignKey( + "policy.Policy", + models.DO_NOTHING, + db_column="PolicyID", + related_name="policy_renewals", + ) + new_product = models.ForeignKey( + Product, + db_column="NewProdID", + on_delete=models.DO_NOTHING, + related_name="policy_renewals", + ) + + renewal_prompt_date = fields.DateField(db_column="RenewalPromptDate") + renewal_date = fields.DateField(db_column="RenewalDate") + phone_number = models.CharField( + db_column="PhoneNumber", max_length=25, blank=True, null=True + ) + sms_status = models.SmallIntegerField( + db_column="SMSStatus", default=0 + ) # TODO choices + renewal_warnings = models.SmallIntegerField( + db_column="RenewalWarnings", null=True, blank=True, default=0 + ) # TODO choices + response_status = models.IntegerField( + db_column="ResponseStatus", null=True, blank=True, default=0 + ) # TODO Choices + response_date = fields.DateTimeField( + db_column="ResponseDate", null=True, blank=True + ) + + audit_user_id = models.IntegerField( + db_column="AuditCreateUser", null=True, blank=True + ) class Meta: managed = True - db_table = 'tblPolicyRenewals' + db_table = "tblPolicyRenewals" class PolicyMutation(core_models.UUIDModel, core_models.ObjectMutation): - policy = models.ForeignKey(Policy, models.DO_NOTHING, - related_name='mutations') + policy = models.ForeignKey(Policy, models.DO_NOTHING, related_name="mutations") mutation = models.ForeignKey( - core_models.MutationLog, models.DO_NOTHING, related_name='policies') + core_models.MutationLog, models.DO_NOTHING, related_name="policies" + ) class Meta: managed = True db_table = "policy_PolicyMutation" + + +if "claim" in sys.modules: + from claim.models import Claim + + @receiver(post_save, sender=Claim) + @receiver(post_delete, sender=Claim) + def clean_enquire_cache_claim(sender, instance, *args, **kwagrs): + cache.delete( + f"eligibility_{instance.insuree.family_id or instance.insuree.id}_*" + ) + + +@receiver(post_save, sender=Product) +@receiver(post_delete, sender=Product) +def clean_all_enquire_cache_product(sender, instance, *args, **kwagrs): + cache.delete("eligibility_*") + + +@receiver(post_save, sender=Policy) +@receiver(post_delete, sender=Policy) +def clean_all_enquire_cache_policy(sender, instance, *args, **kwagrs): + cache.delete(f"eligibility_{instance.family_id}_*") + + +@receiver(post_save, sender=Family) +@receiver(post_delete, sender=Family) +def clean_all_enquire_cache_family(sender, instance, *args, **kwagrs): + cache.delete(f"eligibility_{instance.id}_*") diff --git a/policy/report.py b/policy/report.py index 300d3a4..226bc22 100644 --- a/policy/report.py +++ b/policy/report.py @@ -1,7 +1,9 @@ from policy.reports import policy_renewals from policy.reports.policy_renewals import policy_renewals_query from policy.reports import primary_operational_indicators -from policy.reports.primary_operational_indicators import policies_primary_indicators_query +from policy.reports.primary_operational_indicators import ( + policies_primary_indicators_query, +) report_definitions = [ { diff --git a/policy/reports/policy_renewals.py b/policy/reports/policy_renewals.py index 295558c..b426523 100644 --- a/policy/reports/policy_renewals.py +++ b/policy/reports/policy_renewals.py @@ -1,6 +1,5 @@ -from django.db.models import Q, Sum, F, QuerySet +from django.db.models import Q, F, QuerySet -from contribution.models import Premium from core.models import Officer from location.models import Location from policy.models import Policy @@ -6167,11 +6166,7 @@ SORTING_BY_DATE = "D" SORTING_BY_RECEIPT = "R" SORTING_BY_OFFICER = "O" -AVAILABLE_SORTING_CHOICES = [ - SORTING_BY_DATE, - SORTING_BY_RECEIPT, - SORTING_BY_OFFICER -] +AVAILABLE_SORTING_CHOICES = [SORTING_BY_DATE, SORTING_BY_RECEIPT, SORTING_BY_OFFICER] SORTING_FIELD_NAME_MAPPING = { SORTING_BY_DATE: "enroll_date", @@ -6191,10 +6186,7 @@ def generate_subtotals(policies: QuerySet): amount = policy["amount"] if region_code not in subtotals: - subtotals[region_code] = { - district_code: amount, - "total": amount - } + subtotals[region_code] = {district_code: amount, "total": amount} elif district_code not in subtotals[region_code]: subtotals[region_code][district_code] = amount subtotals[region_code]["total"] += amount @@ -6210,28 +6202,32 @@ def format_final_data(policies: QuerySet, subtotals: dict): # Formats the data in order to match what is expected by the report data = [] for policy in policies: - region_code = policy['region_code'] - district_code = policy['district_code'] - insuree_id = str(policy["insuree_id"]) # explicitely cast the ID b/c the report doesn't understand when it's a number + region_code = policy["region_code"] + district_code = policy["district_code"] + insuree_id = str( + policy["insuree_id"] + ) # explicitely cast the ID b/c the report doesn't understand when it's a number new_data_line = { "region_total": subtotals[region_code]["total"], "district_total": subtotals[region_code][district_code], **policy, - "insuree_id": insuree_id + "insuree_id": insuree_id, } data.append(new_data_line) return data -def policy_renewals_query(user, - date_start="2019-01-01", - date_end="2099-12-31", - requested_region_id=ALL_REGIONS, - requested_district_id=ALL_DISTRICTS, - requested_product_id=ALL_PRODUCTS, - requested_officer_id=ALL_OFFICERS, - requested_sorting=SORTING_BY_DATE, - **kwargs): +def policy_renewals_query( + user, + date_start="2019-01-01", + date_end="2099-12-31", + requested_region_id=ALL_REGIONS, + requested_district_id=ALL_DISTRICTS, + requested_product_id=ALL_PRODUCTS, + requested_officer_id=ALL_OFFICERS, + requested_sorting=SORTING_BY_DATE, + **kwargs, +): # /!\ Product is never displayed in the original report, it's only a search criteria. # Checking the parameters received and returning an error if anything is wrong @@ -6251,43 +6247,65 @@ def policy_renewals_query(user, return {"error": "Error - the requested officer does not exist"} region_id = int(requested_region_id) if region_id != ALL_REGIONS: - region = Location.objects.filter(validity_to=None, type='R', id=region_id).first() + region = Location.objects.filter( + validity_to=None, type="R", id=region_id + ).first() if not region: return {"error": "Error - the requested region does not exist"} district_id = int(requested_district_id) if district_id != ALL_DISTRICTS: district_filters = Q(validity_to__isnull=True) & Q(type="D") & Q(id=district_id) - if region_id != ALL_REGIONS: # The FE pickers allow you to select a district without a region, so additional steps are required + if ( + region_id != ALL_REGIONS + ): # The FE pickers allow you to select a district without a region, so additional steps are required district_filters &= Q(parent_id=region_id) district = Location.objects.filter(district_filters).first() if not district: return {"error": "Error - the requested district does not exist"} - if region_id == ALL_REGIONS: # The FE pickers allow you to select a district without a region, so additional steps are required + if ( + region_id == ALL_REGIONS + ): # The FE pickers allow you to select a district without a region, so additional steps are required region = district.parent region_id = region.id # Preparing data for the header table header = { - "region": "All regions" if region_id == ALL_REGIONS else f"{region.code} - {region.name}", - "district": "All districts" if district_id == ALL_DISTRICTS else f"{district.code} - {district.name}", - "product": "All products" if product_id == ALL_PRODUCTS else f"{product.code} - {product.name}", - "officer": "All officers" if officer_id == ALL_OFFICERS else f"{officer.code} - {officer.other_names} {officer.last_name}", + "region": ( + "All regions" + if region_id == ALL_REGIONS + else f"{region.code} - {region.name}" + ), + "district": ( + "All districts" + if district_id == ALL_DISTRICTS + else f"{district.code} - {district.name}" + ), + "product": ( + "All products" + if product_id == ALL_PRODUCTS + else f"{product.code} - {product.name}" + ), + "officer": ( + "All officers" + if officer_id == ALL_OFFICERS + else f"{officer.code} - {officer.other_names} {officer.last_name}" + ), "sorting": requested_sorting, "date_from": date_start, "date_to": date_end, } - report_data = { - "header": [header] - } + report_data = {"header": [header]} # Prepare the search criteria based on the received parameters - search_filters = (Q(validity_to__isnull=True) - & Q(family__validity_to__isnull=True) - & Q(family__head_insuree__validity_to__isnull=True) - & Q(officer__validity_to__isnull=True) - & Q(premiums__validity_to__isnull=True) - & Q(stage="R") - & Q(enroll_date__range=[date_start, date_end])) + search_filters = ( + Q(validity_to__isnull=True) + & Q(family__validity_to__isnull=True) + & Q(family__head_insuree__validity_to__isnull=True) + & Q(officer__validity_to__isnull=True) + & Q(premiums__validity_to__isnull=True) + & Q(stage="R") + & Q(enroll_date__range=[date_start, date_end]) + ) if product_id != ALL_PRODUCTS: search_filters &= Q(product_id=product_id) if officer_id != ALL_OFFICERS: @@ -6298,27 +6316,28 @@ def policy_renewals_query(user, search_filters &= Q(family__location__parent__parent__parent_id=region_id) last_order_by_criterion = SORTING_FIELD_NAME_MAPPING[requested_sorting] - policies = Policy.objects.filter(search_filters) \ - .values("enroll_date", - region_code=F("family__location__parent__parent__parent__code"), - region_name=F("family__location__parent__parent__parent__name"), - district_code=F("family__location__parent__parent__code"), - district_name=F("family__location__parent__parent__name"), - ward_name=F("family__location__parent__name"), - village_name=F("family__location__name"), - insuree_id=F("family__head_insuree_id"), - insuree_other_names=F("family__head_insuree__other_names"), - insuree_last_name=F("family__head_insuree__last_name"), - officer_code=F("officer__code"), - officer_last_name=F("officer__last_name"), - officer_other_names=F("officer__other_names"), - receipt=F("premiums__receipt"), - amount=F("premiums__amount"), - payer=F("premiums__payer__name"), - ) \ - .order_by("region_code", - "district_code", - last_order_by_criterion) + policies = ( + Policy.objects.filter(search_filters) + .values( + "enroll_date", + region_code=F("family__location__parent__parent__parent__code"), + region_name=F("family__location__parent__parent__parent__name"), + district_code=F("family__location__parent__parent__code"), + district_name=F("family__location__parent__parent__name"), + ward_name=F("family__location__parent__name"), + village_name=F("family__location__name"), + insuree_id=F("family__head_insuree_id"), + insuree_other_names=F("family__head_insuree__other_names"), + insuree_last_name=F("family__head_insuree__last_name"), + officer_code=F("officer__code"), + officer_last_name=F("officer__last_name"), + officer_other_names=F("officer__other_names"), + receipt=F("premiums__receipt"), + amount=F("premiums__amount"), + payer=F("premiums__payer__name"), + ) + .order_by("region_code", "district_code", last_order_by_criterion) + ) subtotals = generate_subtotals(policies) report_data["total_amount"] = subtotals["total"] diff --git a/policy/reports/primary_operational_indicators.py b/policy/reports/primary_operational_indicators.py index 0e4733f..530ebdb 100644 --- a/policy/reports/primary_operational_indicators.py +++ b/policy/reports/primary_operational_indicators.py @@ -1,11 +1,11 @@ from datetime import datetime, timedelta from dateutil.relativedelta import relativedelta -from django.conf import settings from django.db import connection from tools.utils import dictfetchall import logging + logger = logging.getLogger(__name__) # If manually pasting from ReportBro and you have test data, search and replace \" with \\" @@ -4197,97 +4197,97 @@ policies_primary_indicators_sql = f""" SELECT prod."ProdID" as "ProdID", prod."ProductCode" as "ProductCode", COUNT(ins."InsureeID") FILTER ( - WHERE Ins."Gender" = 'M' + WHERE Ins."Gender" = 'M' AND pl."PolicyStatus" > 1 AND pl."EffectiveDate" <= %(LastDay)s AND pl."ExpiryDate" > %(LastDay)s ) AS "TotalMaleInsurees", COUNT(ins."InsureeID") FILTER ( - WHERE Ins."Gender" = 'F' + WHERE Ins."Gender" = 'F' AND pl."PolicyStatus" > 1 AND pl."EffectiveDate" <= %(LastDay)s AND pl."ExpiryDate" > %(LastDay)s ) AS "TotalFemaleInsurees", COUNT(ins."InsureeID") FILTER ( - WHERE Ins."Gender" = 'O' + WHERE Ins."Gender" = 'O' AND pl."PolicyStatus" > 1 AND pl."EffectiveDate" <= %(LastDay)s AND pl."ExpiryDate" > %(LastDay)s ) AS "TotalOtherInsurees", COUNT(ins."InsureeID") FILTER ( - WHERE Ins."Gender" = 'M' + WHERE Ins."Gender" = 'M' AND pl."PolicyStatus" > 1 AND pl."PolicyStage" = 'N' AND YEAR(pl."EffectiveDate") = YEAR(%(LastDay)s) AND MONTH(pl."EffectiveDate") = MONTH(%(LastDay)s) ) AS "NewMaleInsurees", COUNT(ins."InsureeID") FILTER ( - WHERE Ins."Gender" = 'F' + WHERE Ins."Gender" = 'F' AND pl."PolicyStatus" > 1 AND pl."PolicyStage" = 'N' AND YEAR(pl."EffectiveDate") = YEAR(%(LastDay)s) AND MONTH(pl."EffectiveDate") = MONTH(%(LastDay)s) ) AS "NewFemaleInsurees", COUNT(ins."InsureeID") FILTER ( - WHERE Ins."Gender" = 'O' + WHERE Ins."Gender" = 'O' AND pl."PolicyStatus" > 1 AND pl."PolicyStage" = 'N' AND YEAR(pl."EffectiveDate") = YEAR(%(LastDay)s) AND MONTH(pl."EffectiveDate") = MONTH(%(LastDay)s) ) AS "NewOtherInsurees", COUNT(ins."InsureeID") FILTER ( - WHERE Ins."Gender" = 'M' + WHERE Ins."Gender" = 'M' AND pl."PolicyStatus" > 1 AND pl."PolicyStage" = 'R' AND YEAR(pl."EnrollDate") = YEAR(%(LastDay)s) AND MONTH(pl."EnrollDate") = MONTH(%(LastDay)s) ) AS "RenewMaleInsurees", COUNT(ins."InsureeID") FILTER ( - WHERE Ins."Gender" = 'F' + WHERE Ins."Gender" = 'F' AND pl."PolicyStatus" > 1 AND pl."PolicyStage" = 'R' AND YEAR(pl."EnrollDate") = YEAR(%(LastDay)s) AND MONTH(pl."EnrollDate") = MONTH(%(LastDay)s) ) AS "RenewFemaleInsurees", COUNT(ins."InsureeID") FILTER ( - WHERE Ins."Gender" = 'O' + WHERE Ins."Gender" = 'O' AND pl."PolicyStatus" > 1 AND pl."PolicyStage" = 'R' AND YEAR(pl."EnrollDate") = YEAR(%(LastDay)s) AND MONTH(pl."EnrollDate") = MONTH(%(LastDay)s) ) AS "RenewOtherInsurees", COUNT(ins."InsureeID") FILTER ( - WHERE Ins."Gender" = 'M' + WHERE Ins."Gender" = 'M' AND pl."PolicyStatus" = 4 AND YEAR(pl."ValidityFrom") = YEAR(%(LastDay)s) AND MONTH(pl."ValidityFrom") = MONTH(%(LastDay)s) ) AS "SuspendedMaleInsurees", COUNT(ins."InsureeID") FILTER ( - WHERE Ins."Gender" = 'F' + WHERE Ins."Gender" = 'F' AND pl."PolicyStatus" = 4 AND YEAR(pl."ValidityFrom") = YEAR(%(LastDay)s) AND MONTH(pl."ValidityFrom") = MONTH(%(LastDay)s) ) AS "SuspendedFemaleInsurees", COUNT(ins."InsureeID") FILTER ( - WHERE Ins."Gender" = 'O' + WHERE Ins."Gender" = 'O' AND pl."PolicyStatus" = 4 AND YEAR(pl."ValidityFrom") = YEAR(%(LastDay)s) AND MONTH(pl."ValidityFrom") = MONTH(%(LastDay)s) ) AS "SuspendedOtherInsurees", COUNT(ins."InsureeID") FILTER ( - WHERE Ins."Gender" = 'M' + WHERE Ins."Gender" = 'M' AND pl."PolicyStatus" > 1 AND YEAR(pl."ExpiryDate") = YEAR(%(LastDay)s) AND MONTH(pl."ExpiryDate") = MONTH(%(LastDay)s) ) AS "ExpiredMaleInsurees", COUNT(ins."InsureeID") FILTER ( - WHERE Ins."Gender" = 'F' + WHERE Ins."Gender" = 'F' AND pl."PolicyStatus" > 1 AND YEAR(pl."ExpiryDate") = YEAR(%(LastDay)s) AND MONTH(pl."ExpiryDate") = MONTH(%(LastDay)s) ) AS "ExpiredFemaleInsurees", COUNT(ins."InsureeID") FILTER ( - WHERE Ins."Gender" = 'O' + WHERE Ins."Gender" = 'O' AND pl."PolicyStatus" > 1 AND YEAR(pl."ExpiryDate") = YEAR(%(LastDay)s) AND MONTH(pl."ExpiryDate") = MONTH(%(LastDay)s) @@ -4317,7 +4317,9 @@ """ -def policies_primary_indicators_query(user, yearMonth, locationId=0, prodId=0, **kwargs): +def policies_primary_indicators_query( + user, yearMonth, locationId=0, prodId=0, **kwargs +): first_day = datetime.strptime(yearMonth, "%Y-%m-%d").replace(day=1) last_day = first_day + relativedelta(months=1) - timedelta(days=1) days_in_month = last_day.day @@ -4333,9 +4335,7 @@ def policies_primary_indicators_query(user, yearMonth, locationId=0, prodId=0, * "DaysInMonth": days_in_month, }, ) - return { - "data": dictfetchall(cur) - } + return {"data": dictfetchall(cur)} except Exception as e: logger.exception("Error fetching policies primary indicators query") raise e diff --git a/policy/schema.py b/policy/schema.py index 5249c04..b7dd767 100644 --- a/policy/schema.py +++ b/policy/schema.py @@ -3,6 +3,7 @@ OrderedDjangoFilterConnectionField, signal_mutation_module_validate, ) +from core.utils import filter_validity import graphene from django.core.exceptions import PermissionDenied from django.db.models import Prefetch @@ -15,16 +16,28 @@ import graphene_django_optimizer as gql_optimizer from graphene_django.filter import DjangoFilterConnectionField from core.models import Officer -from .models import PolicyMutation +from .models import PolicyMutation, Policy from product.models import Product -from contribution.models import Premium from insuree.models import Family, Insuree, InsureePolicy -from django.db.models import OuterRef, Subquery, Sum, F, Count +from django.db.models import OuterRef, Subquery, F, Count from location.apps import LocationConfig # We do need all queries and mutations in the namespace here. -from .gql_queries import * # lgtm [py/polluting-import] -from .gql_mutations import * # lgtm [py/polluting-import] +from .gql_queries import ( + EligibilityGQLType, + PolicyByFamilyOrInsureeGQLType, + PolicyAndWarningsGQLType, + PolicyGQLType, + OfficerGQLType, + PolicyByFamilyOrInsureeConnection, +) # lgtm [py/polluting-import] +from .gql_mutations import ( + CreatePolicyMutation, + UpdatePolicyMutation, + DeletePoliciesMutation, + RenewPolicyMutation, + SuspendPoliciesMutation +) from .values import policy_values from contribution_plan.models import ContributionPlan @@ -38,7 +51,7 @@ class Query(graphene.ObjectType): enrollDate=graphene.DateTime(required=True), product_id=graphene.Int(required=False), family_id=graphene.Int(required=True), - contribution_plan_uuid=graphene.UUID(required=False) + contribution_plan_uuid=graphene.UUID(required=False), ) policies = OrderedDjangoFilterConnectionField( PolicyGQLType, @@ -76,13 +89,12 @@ class Query(graphene.ObjectType): # YET: family member may not be covered by the policy!! # This requires to refactor the EligibilityService policy_eligibility_by_insuree = graphene.Field( - EligibilityGQLType, - chfId=graphene.String(required=True) + EligibilityGQLType, chfId=graphene.String(required=True) ) policy_item_eligibility_by_insuree = graphene.Field( EligibilityGQLType, chfId=graphene.String(required=True), - itemCode=graphene.String(required=True) + itemCode=graphene.String(required=True), ) policy_service_eligibility_by_insuree = graphene.Field( EligibilityGQLType, @@ -101,48 +113,56 @@ def resolve_policy_values(self, info, **kwargs): raise PermissionDenied(_("unauthorized")) cp_uuid = None product_id = None - if 'contribution_plan_uuid' in kwargs: - cp_uuid = str(kwargs.get('contribution_plan_uuid')) + if "contribution_plan_uuid" in kwargs: + cp_uuid = str(kwargs.get("contribution_plan_uuid")) contribution_plan = ContributionPlan.objects.filter(uuid=cp_uuid).first() if not contribution_plan: raise ValueError(f"Contribution plan {cp_uuid} not found") - - if not contribution_plan.benefit_plan_type.name == 'product': - raise ValueError(f"Contribution plan {cp_uuid} is not attached to a product") + + if not contribution_plan.benefit_plan_type.name == "product": + raise ValueError( + f"Contribution plan {cp_uuid} is not attached to a product" + ) product_id = contribution_plan.benefit_plan - elif 'product_id' in kwargs: - product_id = kwargs.get('product_id') + elif "product_id" in kwargs: + product_id = kwargs.get("product_id") else: - raise ValueError("Product or contribution plan is mandatory") + raise ValueError("Product or contribution plan is mandatory") product = None if product_id: - product = Product.objects.filter( - Q(validity_to__isnull=True), - Q(id=product_id) | Q(legacy_id=product_id), - Q(validity_from__date__lte=kwargs.get('enrollDate')) | Q(date_from__lte=kwargs.get('enrollDate')), - ).order_by('-validity_from').first() - + product = ( + Product.objects.filter( + Q(validity_to__isnull=True), + Q(id=product_id) | Q(legacy_id=product_id), + Q(validity_from__date__lte=kwargs.get("enrollDate")) + | Q(date_from__lte=kwargs.get("enrollDate")), + ) + .order_by("-validity_from") + .first() + ) + if not product: raise ValueError(f"product {product_id} not found") policy = PolicyGQLType( - stage=kwargs.get('stage'), - enroll_date=kwargs.get('enrollDate'), - start_date=kwargs.get('enrollDate'), + stage=kwargs.get("stage"), + enroll_date=kwargs.get("enrollDate"), + start_date=kwargs.get("enrollDate"), product=product, - contribution_plan=cp_uuid + contribution_plan=cp_uuid, ) prefetch = Prefetch( - 'members', - queryset=Insuree.objects.filter( - validity_to__isnull=True).order_by('validity_from') + "members", + queryset=Insuree.objects.filter(validity_to__isnull=True).order_by( + "validity_from" + ), + ) + family = Family.objects.prefetch_related(prefetch).get( + id=kwargs.get("family_id") ) - family = Family.objects \ - .prefetch_related(prefetch) \ - .get(id=kwargs.get('family_id')) prev_policy = None - if 'prev_uuid' in kwargs: - prev_policy = Policy.objects.get(uuid=kwargs.get('prev_uuid')) + if "prev_uuid" in kwargs: + prev_policy = Policy.objects.get(uuid=kwargs.get("prev_uuid")) policy, warnings = policy_values(policy, family, prev_policy, info.context.user) return PolicyAndWarningsGQLType(policy=policy, warnings=warnings) @@ -150,41 +170,53 @@ def resolve_policies(self, info, **kwargs): if not info.context.user.has_perms(PolicyConfig.gql_query_policies_perms): raise PermissionDenied(_("unauthorized")) query = Policy.objects - if not kwargs.get('showHistory', False): + if not kwargs.get("showHistory", False): query = query.filter(*filter_validity(**kwargs)) - if kwargs.get('showInactive', False): - family_count = Insuree.objects.values('family_id')\ - .filter(validity_to__isnull=True).annotate(m_count=Count('id')) - family_sq = family_count.filter(family_id=OuterRef('family_id')) - covered_count = InsureePolicy.objects.values('policy_id')\ - .filter(validity_to__isnull=True).annotate(i_count=Count('id')) - covered_sq = covered_count.filter(policy_id=OuterRef('id')) + if kwargs.get("showInactive", False): + family_count = ( + Insuree.objects.values("family_id") + .filter(validity_to__isnull=True) + .annotate(m_count=Count("id")) + ) + family_sq = family_count.filter(family_id=OuterRef("family_id")) + covered_count = ( + InsureePolicy.objects.values("policy_id") + .filter(validity_to__isnull=True) + .annotate(i_count=Count("id")) + ) + covered_sq = covered_count.filter(policy_id=OuterRef("id")) query = query.annotate( - inactive_count=Subquery(family_sq.values( - 'm_count')) - Subquery(covered_sq.values('i_count')) + inactive_count=Subquery(family_sq.values("m_count")) + - Subquery(covered_sq.values("i_count")) ) query = query.filter(inactive_count__gt=0) - if kwargs.get('balance_lte') or kwargs.get('balance_gte') or kwargs.get('sum_premiums', False): - query=query.annotate( - sum_premiums=Policy.get_query_sum_premium() - ) - if kwargs.get('balance_lte') or kwargs.get('balance_gte'): - query = query.annotate( - balance=F('value') - F('sum_premiums')) - if kwargs.get('balance_lte'): - query = query.filter(balance__lte=kwargs.get('balance_lte')) - if kwargs.get('balance_gte'): - query = query.filter(balance__gte=kwargs.get('balance_gte')) - if kwargs.get('confirmationType'): - query = query.filter(family__confirmation_type=kwargs.get('confirmationType')) - location_id = kwargs.get('district_id') if kwargs.get( - 'district_id') else kwargs.get('region_id') + if ( + kwargs.get("balance_lte") + or kwargs.get("balance_gte") + or kwargs.get("sum_premiums", False) + ): + query = query.annotate(sum_premiums=Policy.get_query_sum_premium()) + if kwargs.get("balance_lte") or kwargs.get("balance_gte"): + query = query.annotate(balance=F("value") - F("sum_premiums")) + if kwargs.get("balance_lte"): + query = query.filter(balance__lte=kwargs.get("balance_lte")) + if kwargs.get("balance_gte"): + query = query.filter(balance__gte=kwargs.get("balance_gte")) + if kwargs.get("confirmationType"): + query = query.filter( + family__confirmation_type=kwargs.get("confirmationType") + ) + location_id = ( + kwargs.get("district_id") + if kwargs.get("district_id") + else kwargs.get("region_id") + ) if location_id: - location_level = 2 if kwargs.get('district_id') else 1 - f = '_id' + location_level = 2 if kwargs.get("district_id") else 1 + f = "_id" for i in range(len(LocationConfig.location_types) - location_level): f = "__parent" + f - f = PolicyConfig.policy_location_via + '__location' + f + f = PolicyConfig.policy_location_via + "__location" + f query = query.filter(Q(**{f: location_id})) return gql_optimizer.query(query.all(), info) @@ -218,30 +250,35 @@ def _to_policy_by_family_or_insuree_item(item): ) def resolve_policies_by_insuree(self, info, **kwargs): - if not info.context.user.has_perms(PolicyConfig.gql_query_policies_by_insuree_perms) \ - and not info.context.user.has_perms(ClaimConfig.gql_query_claims_perms): + if not info.context.user.has_perms( + PolicyConfig.gql_query_policies_by_insuree_perms + ) and not info.context.user.has_perms(ClaimConfig.gql_query_claims_perms): raise PermissionDenied(_("unauthorized")) req = ByInsureeRequest( - chf_id=kwargs.get('chf_id'), + chf_id=kwargs.get("chf_id"), active_or_last_expired_only=kwargs.get( - 'active_or_last_expired_only', False), - show_history=kwargs.get('show_history', False), - order_by=kwargs.get('order_by', None), - target_date=kwargs.get('target_date', None) + "active_or_last_expired_only", False + ), + show_history=kwargs.get("show_history", False), + order_by=kwargs.get("order_by", None), + target_date=kwargs.get("target_date", None), ) res = ByInsureeService(user=info.context.user).request(req) return [Query._to_policy_by_family_or_insuree_item(x) for x in res.items] def resolve_policies_by_family(self, info, **kwargs): - if not info.context.user.has_perms(PolicyConfig.gql_query_policies_by_family_perms): + if not info.context.user.has_perms( + PolicyConfig.gql_query_policies_by_family_perms + ): raise PermissionDenied(_("unauthorized")) req = ByFamilyRequest( - family_uuid=kwargs.get('family_uuid'), + family_uuid=kwargs.get("family_uuid"), active_or_last_expired_only=kwargs.get( - 'active_or_last_expired_only', False), - show_history=kwargs.get('show_history', False), - order_by=kwargs.get('order_by', None), - target_date=kwargs.get('target_date', None) + "active_or_last_expired_only", False + ), + show_history=kwargs.get("show_history", False), + order_by=kwargs.get("order_by", None), + target_date=kwargs.get("target_date", None), ) res = ByFamilyService(user=info.context.user).request(req) return [Query._to_policy_by_family_or_insuree_item(x) for x in res.items] @@ -267,51 +304,39 @@ def _resolve_policy_eligibility_by_insuree(user, req): service_left=res.service_left, item_left=res.item_left, is_item_ok=res.is_item_ok, - is_service_ok=res.is_service_ok + is_service_ok=res.is_service_ok, ) def resolve_policy_eligibility_by_insuree(self, info, **kwargs): if not info.context.user.has_perms(PolicyConfig.gql_query_eligibilities_perms): raise PermissionDenied(_("unauthorized")) - req = EligibilityRequest( - chf_id=kwargs.get('chfId') - ) + req = EligibilityRequest(chf_id=kwargs.get("chfId")) return Query._resolve_policy_eligibility_by_insuree( - user=info.context.user, - req=req + user=info.context.user, req=req ) def resolve_policy_item_eligibility_by_insuree(self, info, **kwargs): if not info.context.user.has_perms(PolicyConfig.gql_query_eligibilities_perms): raise PermissionDenied(_("unauthorized")) req = EligibilityRequest( - chf_id=kwargs.get('chfId'), - item_code=kwargs.get('itemCode') + chf_id=kwargs.get("chfId"), item_code=kwargs.get("itemCode") ) return Query._resolve_policy_eligibility_by_insuree( - user=info.context.user, - req=req + user=info.context.user, req=req ) def resolve_policy_service_eligibility_by_insuree(self, info, **kwargs): if not info.context.user.has_perms(PolicyConfig.gql_query_eligibilities_perms): raise PermissionDenied(_("unauthorized")) req = EligibilityRequest( - chf_id=kwargs.get('chfId'), - service_code=kwargs.get('serviceCode') + chf_id=kwargs.get("chfId"), service_code=kwargs.get("serviceCode") ) return Query._resolve_policy_eligibility_by_insuree( - user=info.context.user, - req=req + user=info.context.user, req=req ) def resolve_policy_officers( - self, - info, - search=None, - district=None, - region=None, - **kwargs + self, info, search=None, district=None, region=None, **kwargs ): if not info.context.user.has_perms( PolicyConfig.gql_query_policy_officers_perms @@ -323,14 +348,22 @@ def resolve_policy_officers( if location_id is not None: location = int(location_id) queryset = queryset.filter( - Q(officer_villages__location__isnull=False, - officer_villages__location__id=location) # villages - | Q(officer_villages__location__parent_id__isnull=False, - officer_villages__location__parent_id=location) # municipalities - | Q(officer_villages__location__parent__parent_id__isnull=False, - officer_villages__location__parent__parent_id=location) # districts - | Q(officer_villages__location__parent__parent__parent_id__isnull=False, - officer_villages__location__parent__parent__parent_id=location) # regions + Q( + officer_villages__location__isnull=False, + officer_villages__location__id=location, + ) # villages + | Q( + officer_villages__location__parent_id__isnull=False, + officer_villages__location__parent_id=location, + ) # municipalities + | Q( + officer_villages__location__parent__parent_id__isnull=False, + officer_villages__location__parent__parent_id=location, + ) # districts + | Q( + officer_villages__location__parent__parent__parent_id__isnull=False, + officer_villages__location__parent__parent__parent_id=location, + ) # regions ).distinct() if search is not None: @@ -352,16 +385,17 @@ class Mutation(graphene.ObjectType): def on_policy_mutation(sender, **kwargs): - uuids = kwargs['data'].get('uuids', []) + uuids = kwargs["data"].get("uuids", []) if not uuids: - uuid = kwargs['data'].get('policy_uuid', None) + uuid = kwargs["data"].get("policy_uuid", None) uuids = [uuid] if uuid else [] if not uuids: return [] impacted_policies = Policy.objects.filter(uuid__in=uuids).all() for policy in impacted_policies: PolicyMutation.objects.create( - policy=policy, mutation_id=kwargs['mutation_log_id']) + policy=policy, mutation_id=kwargs["mutation_log_id"] + ) return [] diff --git a/policy/services.py b/policy/services.py index d4de50d..519b780 100644 --- a/policy/services.py +++ b/policy/services.py @@ -1,9 +1,10 @@ import logging from dataclasses import dataclass from datetime import datetime as py_datetime, date as py_date +from django.core.cache import cache import core -from claim.models import ClaimService, Claim, ClaimItem +from claim.models import Claim, ClaimItem from django import dispatch from django.core.exceptions import PermissionDenied, ValidationError from django.db import connection @@ -13,9 +14,8 @@ from django.utils.translation import gettext as _ from graphene.utils.str_converters import to_snake_case -from policy.utils import get_queryset_valid_at_date from core.signals import register_service_signal -from insuree.models import Insuree, Family, InsureePolicy +from insuree.models import Insuree, InsureePolicy from insuree.services import create_insuree_renewal_detail from medical.models import Service, Item from policy.apps import PolicyConfig @@ -40,28 +40,32 @@ class PolicyService: def __init__(self, user): self.user = user - @register_service_signal('policy_service.create_or_update') - def update_or_create(self, data, user): - if isinstance(data['enroll_date'], str): - data['enroll_date'] = py_datetime.strptime(data['enroll_date'], "%Y-%m-%d").date() - policy_uuid = data.get('uuid', None) - if 'enroll_date' in data and data['enroll_date'] > py_date.today(): + @register_service_signal("policy_service.create_or_update") + def update_or_create(self, data, user): + if isinstance(data["enroll_date"], str): + data["enroll_date"] = py_datetime.strptime( + data["enroll_date"], "%Y-%m-%d" + ).date() + policy_uuid = data.get("uuid", None) + if "enroll_date" in data and data["enroll_date"] > py_date.today(): raise ValidationError("policy.enroll_date_in_the_future") if policy_uuid: return self.update_policy(data, user) else: return self.create_policy(data, user) - @register_service_signal('policy_service.update') + @register_service_signal("policy_service.update") def update_policy(self, data, user): if "is_paid" in data: data.pop("is_paid") members = None if "members_uuid" in data: members_uuid = data.pop("members_uuid", None) - members = Insuree.objects.filter(uuid__in=members_uuid) if members_uuid else None + members = ( + Insuree.objects.filter(uuid__in=members_uuid) if members_uuid else None + ) data = self._clean_mutation_info(data) - policy_uuid = data.pop('uuid') if 'uuid' in data else None + policy_uuid = data.pop("uuid") if "uuid" in data else None policy = Policy.objects.get(uuid=policy_uuid) members = get_members(policy, policy.family, user, members) policy.save_history() @@ -71,30 +75,45 @@ def update_policy(self, data, user): update_insuree_policies(policy, user, members=members) return policy - @register_service_signal('policy_service.create') + @register_service_signal("policy_service.create") def create_policy(self, data, user): is_paid = data.pop("is_paid", False) receipt = data.pop("receipt", None) members = None if "members_uuid" in data: members_uuid = data.pop("members_uuid", None) - members = Insuree.objects.filter(uuid__in=members_uuid) if members_uuid else None + members = ( + Insuree.objects.filter(uuid__in=members_uuid) if members_uuid else None + ) payer_uuid = data.pop("payer_uuid", None) data = self._clean_mutation_info(data) policy = Policy.objects.create(**data) if receipt is not None: - from contribution.services import check_unique_premium_receipt_code_within_product - is_invalid = check_unique_premium_receipt_code_within_product(code=receipt, policy_uuid=policy.uuid) + from contribution.services import ( + check_unique_premium_receipt_code_within_product, + ) + + is_invalid = check_unique_premium_receipt_code_within_product( + code=receipt, policy_uuid=policy.uuid + ) if is_invalid: raise ValidationError("Receipt already exist for a given product.") else: - receipt = self.generate_contribution_receipt(policy.product, policy.enroll_date) + receipt = self.generate_contribution_receipt( + policy.product, policy.enroll_date + ) policy.save() update_insuree_policies(policy, user, members=members) if is_paid: from contribution.gql_mutations import premium_action - premium_data = {"policy_uuid": policy.uuid, "amount": policy.value, - "receipt": receipt, "pay_date": data["enroll_date"], "pay_type": "C"} + + premium_data = { + "policy_uuid": policy.uuid, + "amount": policy.value, + "receipt": receipt, + "pay_date": data["enroll_date"], + "pay_type": "C", + } if payer_uuid is not None: premium_data["payer_uuid"] = payer_uuid premium_action(premium_data, user) @@ -102,21 +121,26 @@ def create_policy(self, data, user): def generate_contribution_receipt(self, product, enroll_date): from contribution.models import Premium + code_length = PolicyConfig.contribution_receipt_length if not code_length and type(code_length) is not int: - raise ValueError("Invalid config for `generate_contribution_receipt`, expected `code_length` value.") + raise ValueError( + "Invalid config for `generate_contribution_receipt`, expected `code_length` value." + ) prefix = "RE-" + str(product.code) + "-" + str(enroll_date) + "-" - last_contribution = Premium.objects.filter(validity_to__isnull=True, receipt__icontains=prefix) + last_contribution = Premium.objects.filter( + receipt__icontains=prefix, *core.filter_validity() + ) code = 0 if last_contribution: - code = int(last_contribution.latest('receipt').receipt[-code_length:]) + code = int(last_contribution.latest("receipt").receipt[-code_length:]) return prefix + str(code + 1).zfill(code_length) def _clean_mutation_info(self, data): if "client_mutation_id" in data: - data.pop('client_mutation_id') + data.pop("client_mutation_id") if "client_mutation_label" in data: - data.pop('client_mutation_label') + data.pop("client_mutation_label") return data def set_suspended(self, user, policy): @@ -128,20 +152,28 @@ def set_suspended(self, user, policy): return [] except Exception as exc: return { - 'title': policy.uuid, - 'list': [{ - 'message': _("policy.mutation.failed_to_suspend_policy") % {'uuid': policy.uuid}, - 'detail': policy.uuid}] + "title": policy.uuid, + "list": [ + { + "message": _("policy.mutation.failed_to_suspend_policy") + % {"uuid": policy.uuid}, + "detail": policy.uuid, + } + ], } def set_deleted(self, policy): - + if policy.claim_ded_rems: return { - 'title': policy.uuid, - 'list': [{ - 'message': _("policy.mutation.policy_is_used_in_claims") % {'policy': str(policy)}, - 'detail': policy.uuid}] + "title": policy.uuid, + "list": [ + { + "message": _("policy.mutation.policy_is_used_in_claims") + % {"policy": str(policy)}, + "detail": policy.uuid, + } + ], } try: insuree_policies = InsureePolicy.objects.filter(policy=policy) @@ -151,17 +183,30 @@ def set_deleted(self, policy): return [] except Exception as exc: return { - 'title': policy.uuid, - 'list': [{ - 'message': _("policy.mutation.failed_to_change_status_of_policy") % {'policy': str(policy)}, - 'detail': policy.uuid}] + "title": policy.uuid, + "list": [ + { + "message": _( + "policy.mutation.failed_to_change_status_of_policy" + ) + % {"policy": str(policy)}, + "detail": policy.uuid, + } + ], } @core.comparable class ByInsureeRequest(object): - def __init__(self, chf_id, active_or_last_expired_only=False, show_history=False, order_by=None, target_date=None): + def __init__( + self, + chf_id, + active_or_last_expired_only=False, + show_history=False, + order_by=None, + target_date=None, + ): self.chf_id = chf_id self.active_or_last_expired_only = active_or_last_expired_only self.show_history = show_history @@ -175,32 +220,33 @@ def __eq__(self, other): @core.comparable class ByFamilyOrInsureeResponseItem(object): - def __init__(self, - policy_id, - policy_uuid, - policy_value, - product_code, - product_name, - start_date, - enroll_date, - effective_date, - expiry_date, - officer_code, - officer_name, - status, - ded, - ded_in_patient, - ded_out_patient, - ceiling, - ceiling_in_patient, - ceiling_out_patient, - balance, - validity_from, - validity_to, - max_installments, - contribution_plan_code=None, - contribution_plan_name=None - ): + def __init__( + self, + policy_id, + policy_uuid, + policy_value, + product_code, + product_name, + start_date, + enroll_date, + effective_date, + expiry_date, + officer_code, + officer_name, + status, + ded, + ded_in_patient, + ded_out_patient, + ceiling, + ceiling_in_patient, + ceiling_out_patient, + balance, + validity_from, + validity_to, + max_installments, + contribution_plan_code=None, + contribution_plan_name=None, + ): self.policy_id = policy_id self.policy_uuid = policy_uuid self.policy_value = policy_value @@ -257,22 +303,39 @@ def _to_item(row): if row.product.max_op_treatment: ceiling_op = row.product.max_ip_treatment if row.product.max_insuree: - ceiling = row.product.max_insuree - (row.total_rem_g if row.total_rem_g else 0) + ceiling = row.product.max_insuree - ( + row.total_rem_g if row.total_rem_g else 0 + ) else: if row.product.max_ip_insuree: - ceiling_ip = row.product.max_ip_insuree - (row.total_rem_ip if row.total_rem_ip else 0) + ceiling_ip = row.product.max_ip_insuree - ( + row.total_rem_ip if row.total_rem_ip else 0 + ) if row.product.max_op_insuree: - ceiling_op = row.product.max_op_insuree - (row.total_rem_op if row.total_rem_op else 0) + ceiling_op = row.product.max_op_insuree - ( + row.total_rem_op if row.total_rem_op else 0 + ) members_count = row.family.members.count() threshold = row.product.threshold if row.product.threshold else 0 total_rem_g = row.total_rem_g if row.total_rem_g else 0 total_rem_ip = row.total_rem_ip if row.total_rem_ip else 0 total_rem_op = row.total_rem_op if row.total_rem_op else 0 - extra_member = row.product.max_policy_extra_member if row.product.max_policy_extra_member else 0 - extra_member_ip = row.product.max_policy_extra_member_ip if row.product.max_policy_extra_member_ip else 0 - extra_member_op = row.product.max_policy_extra_member_op if row.product.max_policy_extra_member_op else 0 - + extra_member = ( + row.product.max_policy_extra_member + if row.product.max_policy_extra_member + else 0 + ) + extra_member_ip = ( + row.product.max_policy_extra_member_ip + if row.product.max_policy_extra_member_ip + else 0 + ) + extra_member_op = ( + row.product.max_policy_extra_member_op + if row.product.max_policy_extra_member_op + else 0 + ) if row.product.max_policy: max_policy = row.product.max_policy @@ -298,8 +361,12 @@ def _to_item(row): if row.total_ded_g: balance -= row.total_ded_g - contribution_plan_code = row.contribution_plan.code if row.contribution_plan else None - contribution_plan_name = row.contribution_plan.name if row.contribution_plan else None + contribution_plan_code = ( + row.contribution_plan.code if row.contribution_plan else None + ) + contribution_plan_name = ( + row.contribution_plan.name if row.contribution_plan else None + ) return ByFamilyOrInsureeResponseItem( policy_id=row.id, policy_uuid=row.uuid, @@ -329,33 +396,48 @@ def _to_item(row): def build_query(self, req): # TODO: prevent direct dependency on claim_ded structure? - res = Policy.objects\ - .prefetch_related('product') \ - .prefetch_related('officer') \ - .annotate(total_ded_g=Sum('claim_ded_rems__ded_g')) \ - .annotate(total_ded_ip=Sum('claim_ded_rems__ded_ip')) \ - .annotate(total_ded_op=Sum('claim_ded_rems__ded_op')) \ - .annotate(total_rem_g=Sum('claim_ded_rems__rem_g')) \ - .annotate(total_rem_op=Sum('claim_ded_rems__rem_op')) \ - .annotate(total_rem_ip=Sum('claim_ded_rems__rem_ip')) \ - .annotate(total_rem_consult=Sum('claim_ded_rems__rem_consult')) \ - .annotate(total_rem_surgery=Sum('claim_ded_rems__rem_surgery')) \ - .annotate(total_rem_delivery=Sum('claim_ded_rems__rem_delivery')) \ - .annotate(total_rem_hospitalization=Sum('claim_ded_rems__rem_hospitalization')) \ - .annotate(total_rem_antenatal=Sum('claim_ded_rems__rem_antenatal')) - res.query.group_by = ['id'] - if hasattr(req, 'chf_id'): - res= res.filter(insuree_policies__insuree__chf_id = req.chf_id) + res = ( + Policy.objects.prefetch_related("product") + .prefetch_related("officer") + .annotate(total_ded_g=Sum("claim_ded_rems__ded_g")) + .annotate(total_ded_ip=Sum("claim_ded_rems__ded_ip")) + .annotate(total_ded_op=Sum("claim_ded_rems__ded_op")) + .annotate(total_rem_g=Sum("claim_ded_rems__rem_g")) + .annotate(total_rem_op=Sum("claim_ded_rems__rem_op")) + .annotate(total_rem_ip=Sum("claim_ded_rems__rem_ip")) + .annotate(total_rem_consult=Sum("claim_ded_rems__rem_consult")) + .annotate(total_rem_surgery=Sum("claim_ded_rems__rem_surgery")) + .annotate(total_rem_delivery=Sum("claim_ded_rems__rem_delivery")) + .annotate( + total_rem_hospitalization=Sum("claim_ded_rems__rem_hospitalization") + ) + .annotate(total_rem_antenatal=Sum("claim_ded_rems__rem_antenatal")) + ) + res.query.group_by = ["id"] + if hasattr(req, "chf_id"): + res = res.filter(insuree_policies__insuree__chf_id=req.chf_id) if not req.show_history: - if req.target_date: - res = res.filter(*core.filter_validity(), expiry_date__gt = req.target_date, effective_date__lte = req.target_date) + if req.target_date: + res = res.filter( + *core.filter_validity(), + expiry_date__gt=req.target_date, + effective_date__lte=req.target_date, + ) else: res = res.filter(*core.filter_validity()) if req.active_or_last_expired_only: # sort on status, so that any active policy (status = 2) pops up... - res = res.annotate(not_null_expiry_date=Coalesce('expiry_date', py_date.max)) \ - .annotate(not_null_validity_to=Coalesce('validity_to', py_datetime.max)) \ - .order_by('product__code', 'status', '-not_null_expiry_date', '-not_null_validity_to', '-validity_from') + res = ( + res.annotate(not_null_expiry_date=Coalesce("expiry_date", py_date.max)) + .annotate(not_null_validity_to=Coalesce("validity_to", py_datetime.max)) + .order_by( + "product__code", + "status", + "-not_null_expiry_date", + "-not_null_validity_to", + "-validity_from", + ) + ) return res @@ -369,30 +451,41 @@ def request(self, by_insuree_request): if by_insuree_request.active_or_last_expired_only: products = {} for policy in res: - if policy.status == Policy.STATUS_IDLE or policy.status == Policy.STATUS_READY: - products['policy.product.code-%s' % policy.uuid] = policy + if ( + policy.status == Policy.STATUS_IDLE + or policy.status == Policy.STATUS_READY + ): + products["policy.product.code-%s" % policy.uuid] = policy elif policy.product.code not in products.keys(): products[policy.product.code] = policy res = products.values() items = [FilteredPoliciesService._to_item(x) for x in res] # possible improvement: sort via the ORM # ... but beware of the active_or_last_expired_only filtering! - order_attr = to_snake_case(by_insuree_request.order_by if by_insuree_request.order_by else "expiry_date") + order_attr = to_snake_case( + by_insuree_request.order_by + if by_insuree_request.order_by + else "expiry_date" + ) desc = False - if order_attr.startswith('-'): + if order_attr.startswith("-"): order_attr = order_attr[1:] desc = True items = sorted(items, key=lambda x: getattr(x, order_attr), reverse=desc) - return ByInsureeResponse( - by_insuree_request=by_insuree_request, - items=items - ) + return ByInsureeResponse(by_insuree_request=by_insuree_request, items=items) @core.comparable class ByFamilyRequest(object): - def __init__(self, family_uuid, active_or_last_expired_only=False, show_history=False, order_by=None, target_date=None): + def __init__( + self, + family_uuid, + active_or_last_expired_only=False, + show_history=False, + order_by=None, + target_date=None, + ): self.family_uuid = family_uuid self.active_or_last_expired_only = active_or_last_expired_only self.show_history = show_history @@ -425,18 +518,16 @@ def request(self, by_family_request): if by_family_request.active_or_last_expired_only: products = {} for policy in res: - if policy.status == Policy.STATUS_IDLE or policy.status == Policy.STATUS_READY: - products['policy.product.code-%s' % policy.uuid] = policy + if ( + policy.status == Policy.STATUS_IDLE + or policy.status == Policy.STATUS_READY + ): + products["policy.product.code-%s" % policy.uuid] = policy elif policy.product.code not in products.keys(): products[policy.product.code] = policy res = products.values() - items = tuple( - map(lambda x: FilteredPoliciesService._to_item(x), res) - ) - return ByFamilyResponse( - by_family_request=by_family_request, - items=items - ) + items = tuple(map(lambda x: FilteredPoliciesService._to_item(x), res)) + return ByFamilyResponse(by_family_request=by_family_request, items=items) # --- ELIGIBILITY -- @@ -458,13 +549,29 @@ def __eq__(self, other): class EligibilityResponse(object): - def __init__(self, eligibility_request, prod_id=None, total_admissions_left=0, total_visits_left=0, - total_consultations_left=0, total_surgeries_left=0, - total_deliveries_left=0, total_antenatal_left=0, consultation_amount_left=0, surgery_amount_left=0, - delivery_amount_left=0, - hospitalization_amount_left=0, antenatal_amount_left=0, - min_date_service=None, min_date_item=None, service_left=0, item_left=0, is_item_ok=False, - is_service_ok=False, final=False): + def __init__( + self, + eligibility_request, + prod_id=None, + total_admissions_left=0, + total_visits_left=0, + total_consultations_left=0, + total_surgeries_left=0, + total_deliveries_left=0, + total_antenatal_left=0, + consultation_amount_left=0, + surgery_amount_left=0, + delivery_amount_left=0, + hospitalization_amount_left=0, + antenatal_amount_left=0, + min_date_service=None, + min_date_item=None, + service_left=0, + item_left=0, + is_item_ok=False, + is_service_ok=False, + final=False, + ): self.eligibility_request = eligibility_request self.prod_id = prod_id self.total_admissions_left = total_admissions_left @@ -495,30 +602,33 @@ def str_none(x): # Comparison should take into account the date vs AdDate return ( - self.eligibility_request == other.eligibility_request and - self.prod_id == other.prod_id and - self.total_admissions_left == other.total_admissions_left and - self.total_visits_left == other.total_visits_left and - self.total_consultations_left == other.total_consultations_left and - self.total_surgeries_left == other.total_surgeries_left and - self.total_deliveries_left == other.total_deliveries_left and - self.total_antenatal_left == other.total_antenatal_left and - self.consultation_amount_left == other.consultation_amount_left and - self.surgery_amount_left == other.surgery_amount_left and - self.delivery_amount_left == other.delivery_amount_left and - self.hospitalization_amount_left == other.hospitalization_amount_left and - self.antenatal_amount_left == other.antenatal_amount_left and - str_none(self.min_date_service) == str_none(other.min_date_service) and - str_none(self.min_date_item) == str_none(other.min_date_item) and - self.service_left == other.service_left and - self.item_left == other.item_left and - self.is_item_ok == other.is_item_ok and - self.is_service_ok == other.is_service_ok) + self.eligibility_request == other.eligibility_request + and self.prod_id == other.prod_id + and self.total_admissions_left == other.total_admissions_left + and self.total_visits_left == other.total_visits_left + and self.total_consultations_left == other.total_consultations_left + and self.total_surgeries_left == other.total_surgeries_left + and self.total_deliveries_left == other.total_deliveries_left + and self.total_antenatal_left == other.total_antenatal_left + and self.consultation_amount_left == other.consultation_amount_left + and self.surgery_amount_left == other.surgery_amount_left + and self.delivery_amount_left == other.delivery_amount_left + and self.hospitalization_amount_left == other.hospitalization_amount_left + and self.antenatal_amount_left == other.antenatal_amount_left + and str_none(self.min_date_service) == str_none(other.min_date_service) + and str_none(self.min_date_item) == str_none(other.min_date_item) + and self.service_left == other.service_left + and self.item_left == other.item_left + and self.is_item_ok == other.is_item_ok + and self.is_service_ok == other.is_service_ok + ) def __str__(self): - return f"Eligibility for {self.eligibility_request} gave product {self.prod_id} " \ - f"with item/svc ok {self.is_item_ok}/{self.is_service_ok} " \ - f" left: {self.item_left}/{self.service_left}" + return ( + f"Eligibility for {self.eligibility_request} gave product {self.prod_id} " + f"with item/svc ok {self.is_item_ok}/{self.is_service_ok} " + f" left: {self.item_left}/{self.service_left}" + ) def __repr__(self): return self.__str__() @@ -534,13 +644,16 @@ def __init__(self, user): self.service = NativeEligibilityService(user) def request(self, request): - if not self.user or not self.user.has_perms(PolicyConfig.gql_query_eligibilities_perms): + if not self.user or not self.user.has_perms( + PolicyConfig.gql_query_eligibilities_perms + ): raise PermissionDenied() # The response is passed along in signals and functions. Setting the final parameter will stop response = EligibilityResponse(eligibility_request=request) responses = signal_eligibility_service_before.send( - self.__class__, user=self.user, request=request, response=response) + self.__class__, user=self.user, request=request, response=response + ) response = EligibilityService._get_final_response(responses, "before", response) if not response.final and not PolicyConfig.default_eligibility_disabled: @@ -548,8 +661,11 @@ def request(self, request): if not response.final: responses = signal_eligibility_service_after.send( - self.__class__, user=self.user, request=request, response=response) - response = EligibilityService._get_final_response(responses, "after", response) + self.__class__, user=self.user, request=request, response=response + ) + response = EligibilityService._get_final_response( + responses, "after", response + ) return response @@ -560,9 +676,11 @@ def _get_final_response(cls, responses, sig_name, default_response): final_responses = [r for f, r in responses if r.final] if len(final_responses) > 0: if len(final_responses) > 1: - logger.warning("Eligibility service got more than one final *%s* signal response: %s", - sig_name, - [f for f, r in responses if r.final]) + logger.warning( + "Eligibility service got more than one final *%s* signal response: %s", + sig_name, + [f for f, r in responses if r.final], + ) return final_responses[0] else: return responses[-1] @@ -584,20 +702,34 @@ def request(self, req, response): @isItemOK = @isItemOK OUTPUT, @isServiceOK = @isServiceOK OUTPUT; SELECT @MinDateService, @MinDateItem, @ServiceLeft, @ItemLeft, @isItemOK, @isServiceOK """ - cur.execute(sql, (req.chf_id, - req.service_code, - req.item_code)) + cur.execute(sql, (req.chf_id, req.service_code, req.item_code)) res = cur.fetchone() # retrieve the stored proc @Result table if res is None: return response - (prod_id, total_admissions_left, total_visits_left, total_consultations_left, total_surgeries_left, - total_deliveries_left, total_antenatal_left, consultation_amount_left, surgery_amount_left, - delivery_amount_left, - hospitalization_amount_left, antenatal_amount_left) = res + ( + prod_id, + total_admissions_left, + total_visits_left, + total_consultations_left, + total_surgeries_left, + total_deliveries_left, + total_antenatal_left, + consultation_amount_left, + surgery_amount_left, + delivery_amount_left, + hospitalization_amount_left, + antenatal_amount_left, + ) = res cur.nextset() - (min_date_service, min_date_item, service_left, - item_left, is_item_ok, is_service_ok) = cur.fetchone() + ( + min_date_service, + min_date_item, + service_left, + item_left, + is_item_ok, + is_service_ok, + ) = cur.fetchone() return EligibilityResponse( eligibility_request=req, prod_id=prod_id or None, @@ -617,7 +749,7 @@ def request(self, req, response): service_left=service_left, item_left=item_left, is_item_ok=is_item_ok is True, - is_service_ok=is_service_ok is True + is_service_ok=is_service_ok is True, ) @@ -625,13 +757,16 @@ class NativeEligibilityService(object): def __init__(self, user): self.user = user - def get_eligibility(self, insuree, item_or_service, model, req, now): if insuree.is_adult(): - waiting_period_field = f"policy__product__{item_or_service}s__waiting_period_adult" + waiting_period_field = ( + f"policy__product__{item_or_service}s__waiting_period_adult" + ) limit_field = f"policy__product__{item_or_service}s__limit_no_adult" else: - waiting_period_field = f"policy__product__{item_or_service}s__waiting_period_child" + waiting_period_field = ( + f"policy__product__{item_or_service}s__waiting_period_child" + ) limit_field = f"policy__product__{item_or_service}s__limit_no_child" item_or_service_code = req.service_code @@ -639,62 +774,116 @@ def get_eligibility(self, insuree, item_or_service, model, req, now): item_or_service_code = req.item_code # TODO validity is checked but should be optional in get_queryset - item_or_service_obj = model.get_queryset(None, self.user).get(code__iexact=item_or_service_code) + item_or_service_obj = model.get_queryset(None, self.user).get( + code__iexact=item_or_service_code, *core.filter_validity() + ) # Beware that MonthAdd() is in Gregorian calendar, not Nepalese or anything else - queryset_item_or_service = InsureePolicy.objects\ - .filter(validity_to__isnull=True)\ - .filter(policy__validity_to__isnull=True)\ - .filter(**{f"policy__product__{item_or_service}s__validity_to__isnull": True}, - **{f"policy__product__{item_or_service}s__{item_or_service}_id": item_or_service_obj.id}) \ - .filter(policy__status=Policy.STATUS_ACTIVE) \ - .filter(insuree=insuree) \ - .filter(Q(insuree__claim__validity_to__isnull=True, - **{f"insuree__claim__{item_or_service}s__{item_or_service}_id": item_or_service_obj.id}) - & Q(**{f"insuree__claim__{item_or_service}s__validity_to__isnull": True}) - & (Q(**{f"insuree__claim__{item_or_service}s__status": ClaimItem.STATUS_PASSED}) - | Q(**{f"insuree__claim__{item_or_service}s__status__isnull": True})) - & (Q(insuree__claim__status__gt=Claim.STATUS_ENTERED) - | Q(insuree__claim__status__isnull=True))) \ - .values("effective_date", - "policy__product_id", - waiting_period=F(waiting_period_field), - limit_no=F(limit_field)) \ - .annotate(min_date=MonthsAdd("effective_date", Coalesce(F(waiting_period_field), 0))) \ - .annotate(count=Sum(Coalesce( - f"insuree__claim__{item_or_service}s__qty_approved", - f'insuree__claim__{item_or_service}s__qty_provided' - ))) \ + queryset_item_or_service = ( + InsureePolicy.objects.filter( + policy__status=Policy.STATUS_ACTIVE, + insuree=insuree, + *core.filter_validity(prefix=""), + *core.filter_validity(prefix="policy__"), + *core.filter_validity(prefix=f"policy__product__{item_or_service}s__"), + *core.filter_validity(prefix="policy__"), + **{ + f"policy__product__{item_or_service}s__{item_or_service}_id": item_or_service_obj.id + }, + ) + .filter( + Q( + *core.filter_validity(prefix="insuree__claim__"), + **{ + f"insuree__claim__{item_or_service}s__{item_or_service}_id": item_or_service_obj.id + }, + ) + & Q( + *core.filter_validity( + prefix=f"insuree__claim__{item_or_service}s__" + ) + ) + & ( + Q( + **{ + f"insuree__claim__{item_or_service}s__status": ClaimItem.STATUS_PASSED + } + ) + | Q(**{f"insuree__claim__{item_or_service}s__status__isnull": True}) + ) + & ( + Q(insuree__claim__status__gt=Claim.STATUS_ENTERED) + | Q(insuree__claim__status__isnull=True) + ) + ) + .values( + "effective_date", + "policy__product_id", + waiting_period=F(waiting_period_field), + limit_no=F(limit_field), + ) + .annotate( + min_date=MonthsAdd( + "effective_date", Coalesce(F(waiting_period_field), 0) + ) + ) + .annotate( + count=Sum( + Coalesce( + f"insuree__claim__{item_or_service}s__qty_approved", + f"insuree__claim__{item_or_service}s__qty_provided", + ) + ) + ) .annotate(left=F("limit_no") - F("count")) + ) min_date_qs = queryset_item_or_service.aggregate( min_date_lte=Min("min_date", filter=Q(min_date__lte=now)), min_date_all=Min("min_date"), ) - from core import datetime - min_date_item = datetime.date.from_ad_date(min_date_qs["min_date_lte"] - if min_date_qs["min_date_lte"] - else min_date_qs["min_date_all"]) + min_date_item = core.datetime.date.from_ad_date( + min_date_qs["min_date_lte"] + if min_date_qs["min_date_lte"] + else min_date_qs["min_date_all"] + ) - if queryset_item_or_service.filter(min_date__lte=now).filter(left__isnull=True).order_by('-validity_from').first(): + if ( + queryset_item_or_service.filter(min_date__lte=now) + .filter(left__isnull=True) + .order_by("-validity_from") + .first() + ): items_or_services_left = None else: - items_or_services_left = queryset_item_or_service\ - .filter(Q(min_date__isnull=True) | Q(min_date__lte=now))\ - .aggregate(Max("left"))["left__max"] + items_or_services_left = queryset_item_or_service.filter( + Q(min_date__isnull=True) | Q(min_date__lte=now) + ).aggregate(Max("left"))["left__max"] return item_or_service_obj, min_date_item, items_or_services_left - def request(self, req, response): - insuree = Insuree.get_queryset(None, self.user)\ - .filter(validity_to__isnull=True)\ - .get(chf_id=req.chf_id) # Will throw an exception if not found + def get_total_filter(category): + return Q( + insuree__claim__status__gt=Claim.STATUS_ENTERED, + insuree__claim__category=category, + *core.filter_validity(prefix="insuree__"), + *core.filter_validity(prefix="insuree__claim__"), + *core.filter_validity(prefix="insuree__claim__services__"), + ) & ( # Not sure this one is necessary + Q(insuree__claim__services__rejection_reason=0) + | Q(insuree__claim__services__rejection_reason__isnull=True) + ) + insuree = Insuree.get_queryset(None, self.user).get( + chf_id=req.chf_id, *core.filter_validity() + ) # Will throw an exception if not found now = core.datetime.datetime.now() eligibility = response if req.service_code: - service, min_date_service, services_left = self.get_eligibility(insuree, "service", Service, req, now) + service, min_date_service, services_left = self.get_eligibility( + insuree, "service", Service, req, now + ) else: service = None services_left = None @@ -703,7 +892,9 @@ def request(self, req, response): eligibility.service_left = services_left if req.item_code: - item, min_date_item, items_left = self.get_eligibility(insuree, "item", Item, req, now) + item, min_date_item, items_left = self.get_eligibility( + insuree, "item", Item, req, now + ) else: item = None items_left = None @@ -711,24 +902,19 @@ def request(self, req, response): eligibility.min_date_item = min_date_item eligibility.item_left = items_left - def get_total_filter(category): - return ( - Q(insuree__claim__category=category) - & Q(insuree__validity_to__isnull=True) # Not sure this one is necessary - & Q(insuree__claim__validity_to__isnull=True) - & Q(insuree__claim__services__validity_to__isnull=True) - & Q(insuree__claim__status__gt=Claim.STATUS_ENTERED) - & (Q(insuree__claim__services__rejection_reason=0) - | Q(insuree__claim__services__rejection_reason__isnull=True)) - ) - # InsPol -> Policy -> Product -> dedrem - result = InsureePolicy.objects \ - .filter(policy__product__validity_to__isnull=True) \ - .filter(policy__validity_to__isnull=True) \ - .filter(validity_to__isnull=True) \ - .filter(insuree=insuree) \ - .values("policy__product_id", + result = cache.get( + f"eligibility_{insuree.family_id or insuree.id}_{insuree.id}" + ) + if not result: + result = ( + InsureePolicy.objects.filter( + insuree=insuree, + *core.filter_validity(prefix="policy__product__"), + *core.filter_validity(prefix="policy__"), + ) + .values( + "policy__product_id", "policy__product__max_no_surgery", "policy__product__max_amount_surgery", "policy__product__max_amount_consultation", @@ -736,35 +922,99 @@ def get_total_filter(category): "policy__product__max_amount_delivery", "policy__product__max_amount_antenatal", "policy__product__max_amount_hospitalization", - ) \ - .annotate(total_admissions=Coalesce(Count("insuree__claim", - filter=get_total_filter(Service.CATEGORY_HOSPITALIZATION), - distinct=True), 0)) \ - .annotate(total_admissions_left=F("policy__product__max_no_hospitalization") - - F("total_admissions")) \ - .annotate(total_consultations=Coalesce(Count("insuree__claim", - filter=get_total_filter(Service.CATEGORY_CONSULTATION), - distinct=True), 0)) \ - .annotate(total_consultations_left=F("policy__product__max_no_consultation") - - F("total_consultations")) \ - .annotate(total_surgeries=Coalesce(Count("insuree__claim", - filter=get_total_filter(Service.CATEGORY_SURGERY), - distinct=True), 0)) \ - .annotate(total_surgeries_left=F("policy__product__max_no_surgery") - F("total_surgeries")) \ - .annotate(total_deliveries=Coalesce(Count("insuree__claim", - filter=get_total_filter(Service.CATEGORY_DELIVERY), - distinct=True), 0)) \ - .annotate(total_deliveries_left=F("policy__product__max_no_delivery") - F("total_deliveries")) \ - .annotate(total_antenatal=Coalesce(Count("insuree__claim", - filter=get_total_filter(Service.CATEGORY_ANTENATAL), - distinct=True), 0)) \ - .annotate(total_antenatal_left=F("policy__product__max_no_antenatal") - F("total_antenatal")) \ - .annotate(total_visits=Coalesce(Count("insuree__claim", - filter=get_total_filter(Service.CATEGORY_VISIT), - distinct=True), 0)) \ - .annotate(total_visits_left=F("policy__product__max_no_visits") - F("total_visits")) \ - .order_by('-expiry_date')\ - .first() + ) + .annotate( + total_admissions=Coalesce( + Count( + "insuree__claim", + filter=get_total_filter(Service.CATEGORY_HOSPITALIZATION), + distinct=True, + ), + 0, + ) + ) + .annotate( + total_admissions_left=F("policy__product__max_no_hospitalization") + - F("total_admissions") + ) + .annotate( + total_consultations=Coalesce( + Count( + "insuree__claim", + filter=get_total_filter(Service.CATEGORY_CONSULTATION), + distinct=True, + ), + 0, + ) + ) + .annotate( + total_consultations_left=F("policy__product__max_no_consultation") + - F("total_consultations") + ) + .annotate( + total_surgeries=Coalesce( + Count( + "insuree__claim", + filter=get_total_filter(Service.CATEGORY_SURGERY), + distinct=True, + ), + 0, + ) + ) + .annotate( + total_surgeries_left=F("policy__product__max_no_surgery") + - F("total_surgeries") + ) + .annotate( + total_deliveries=Coalesce( + Count( + "insuree__claim", + filter=get_total_filter(Service.CATEGORY_DELIVERY), + distinct=True, + ), + 0, + ) + ) + .annotate( + total_deliveries_left=F("policy__product__max_no_delivery") + - F("total_deliveries") + ) + .annotate( + total_antenatal=Coalesce( + Count( + "insuree__claim", + filter=get_total_filter(Service.CATEGORY_ANTENATAL), + distinct=True, + ), + 0, + ) + ) + .annotate( + total_antenatal_left=F("policy__product__max_no_antenatal") + - F("total_antenatal") + ) + .annotate( + total_visits=Coalesce( + Count( + "insuree__claim", + filter=get_total_filter(Service.CATEGORY_VISIT), + distinct=True, + ), + 0, + ) + ) + .annotate( + total_visits_left=F("policy__product__max_no_visits") + - F("total_visits") + ) + .order_by("-expiry_date") + .first() + ) + cache.set( + f"eligibility_{insuree.family_id or insuree.id}_{insuree.id}", + result, + None, + ) if result is None: eligibility.total_admissions_left = 0 @@ -783,50 +1033,93 @@ def get_total_filter(category): return eligibility eligibility.prod_id = result["policy__product_id"] - total_admissions_left = result["total_admissions_left"] \ - if result["total_admissions_left"] is None or result["total_admissions_left"] >= 0 else 0 - total_consultations_left = result["total_consultations_left"] \ - if result["total_consultations_left"] is None or result["total_consultations_left"] >= 0 else 0 - total_surgeries_left = result["total_surgeries_left"] \ - if result["total_surgeries_left"] is None or result["total_surgeries_left"] >= 0 else 0 - total_deliveries_left = result["total_deliveries_left"] \ - if result["total_deliveries_left"] is None or result["total_deliveries_left"] >= 0 else 0 - total_antenatal_left = result["total_antenatal_left"] \ - if result["total_antenatal_left"] is None or result["total_antenatal_left"] >= 0 else 0 - total_visits_left = result["total_visits_left"] \ - if result["total_visits_left"] is None or result["total_visits_left"] >= 0 else 0 + total_admissions_left = ( + result["total_admissions_left"] + if result["total_admissions_left"] is None + or result["total_admissions_left"] >= 0 + else 0 + ) + total_consultations_left = ( + result["total_consultations_left"] + if result["total_consultations_left"] is None + or result["total_consultations_left"] >= 0 + else 0 + ) + total_surgeries_left = ( + result["total_surgeries_left"] + if result["total_surgeries_left"] is None + or result["total_surgeries_left"] >= 0 + else 0 + ) + total_deliveries_left = ( + result["total_deliveries_left"] + if result["total_deliveries_left"] is None + or result["total_deliveries_left"] >= 0 + else 0 + ) + total_antenatal_left = ( + result["total_antenatal_left"] + if result["total_antenatal_left"] is None + or result["total_antenatal_left"] >= 0 + else 0 + ) + total_visits_left = ( + result["total_visits_left"] + if result["total_visits_left"] is None or result["total_visits_left"] >= 0 + else 0 + ) eligibility.surgery_amount_left = result["policy__product__max_amount_surgery"] - eligibility.consultation_amount_left = result["policy__product__max_amount_consultation"] - eligibility.delivery_amount_left = result["policy__product__max_amount_delivery"] - eligibility.antenatal_amount_left = result["policy__product__max_amount_antenatal"] - eligibility.hospitalization_amount_left = result["policy__product__max_amount_hospitalization"] + eligibility.consultation_amount_left = result[ + "policy__product__max_amount_consultation" + ] + eligibility.delivery_amount_left = result[ + "policy__product__max_amount_delivery" + ] + eligibility.antenatal_amount_left = result[ + "policy__product__max_amount_antenatal" + ] + eligibility.hospitalization_amount_left = result[ + "policy__product__max_amount_hospitalization" + ] if service: if service.category == Service.CATEGORY_SURGERY: - if total_surgeries_left == 0 \ - or services_left == 0 \ - or (min_date_service and min_date_service > now) \ - or (result["policy__product__max_amount_surgery"] is not None - and result["policy__product__max_amount_surgery"] <= 0): + if ( + total_surgeries_left == 0 + or services_left == 0 + or (min_date_service and min_date_service > now) + or ( + result["policy__product__max_amount_surgery"] is not None + and result["policy__product__max_amount_surgery"] <= 0 + ) + ): eligibility.is_service_ok = False else: eligibility.is_service_ok = True elif service.category == Service.CATEGORY_CONSULTATION: - if total_consultations_left == 0 \ - or services_left == 0 \ - or (min_date_service and min_date_service > now) \ - or (result["policy__product__max_amount_consultation"] is not None - and result["policy__product__max_amount_consultation"] <= 0): + if ( + total_consultations_left == 0 + or services_left == 0 + or (min_date_service and min_date_service > now) + or ( + result["policy__product__max_amount_consultation"] is not None + and result["policy__product__max_amount_consultation"] <= 0 + ) + ): eligibility.is_service_ok = False else: eligibility.is_service_ok = True elif service.category == Service.CATEGORY_DELIVERY: - if total_deliveries_left == 0 \ - or services_left == 0 \ - or (min_date_service and min_date_service > now) \ - or (result["policy__product__max_amount_delivery"] is not None - and result["policy__product__max_amount_delivery"] <= 0): + if ( + total_deliveries_left == 0 + or services_left == 0 + or (min_date_service and min_date_service > now) + or ( + result["policy__product__max_amount_delivery"] is not None + and result["policy__product__max_amount_delivery"] <= 0 + ) + ): eligibility.is_service_ok = False else: eligibility.is_service_ok = True @@ -855,17 +1148,25 @@ def get_total_filter(category): return eligibility -def insert_renewals(date_from=None, date_to=None, officer_id=None, reminding_interval=None, location_id=None, location_levels=4): +def insert_renewals( + date_from=None, + date_to=None, + officer_id=None, + reminding_interval=None, + location_id=None, + location_levels=4, +): if reminding_interval is None: reminding_interval = PolicyConfig.policy_renewal_interval - from core import datetime - now = datetime.datetime.now() + now = core.datetime.datetime.now() policies = Policy.objects.filter( status__in=[Policy.STATUS_EXPIRED, Policy.STATUS_ACTIVE], validity_to__isnull=True, ) if reminding_interval: - policies = policies.filter(expiry_date__lte=now + core.datetimedelta(days=reminding_interval)) + policies = policies.filter( + expiry_date__lte=now + core.datetimedelta(days=reminding_interval) + ) if location_id: # TODO support the various levels policies = policies.filter( @@ -896,8 +1197,12 @@ def insert_renewals(date_from=None, date_to=None, officer_id=None, reminding_int previous_products.append(product) product = product.conversion_product if product in previous_products: - logger.error("The product %s has a substitution chain with a loop: %s, continuing with %s", - policy.product_id, [p.id for p in previous_products], product.id) + logger.error( + "The product %s has a substitution chain with a loop: %s, continuing with %s", + policy.product_id, + [p.id for p in previous_products], + product.id, + ) # TODO allow this kind of comparison where the left side is a datetime # if datetime.datetime(product.date_from) <= renewal_date <= product.date_to: @@ -915,42 +1220,52 @@ def insert_renewals(date_from=None, date_to=None, officer_id=None, reminding_int previous_officers.append(officer) officer = officer.substitution_officer if officer in previous_officers: - logger.error("The product %s has a substitution chain with a loop: %s, continuing with %s", - policy.officer_id, [o.id for o in previous_officers], officer.id) + logger.error( + "The product %s has a substitution chain with a loop: %s, continuing with %s", + policy.officer_id, + [o.id for o in previous_officers], + officer.id, + ) if officer.works_to and renewal_date > officer.works_to: renewal_warning |= 4 # Check if the policy has another following policy - following_policies = Policy.objects.filter(family_id=policy.family_id) \ - .filter(Q(product_id=policy.product_id) | Q(product_id=product.id)) \ + following_policies = ( + Policy.objects.filter(family_id=policy.family_id) + .filter(Q(product_id=policy.product_id) | Q(product_id=product.id)) .filter(start_date__gte=renewal_date) + ) if not following_policies.first(): - policy_renewal, policy_renewal_created = PolicyRenewal.objects.get_or_create( - policy=policy, - validity_to=None, - defaults=dict( - renewal_prompt_date=now, - renewal_date=renewal_date, - new_officer=officer, - phone_number=officer.phone, - sms_status=0, - insuree=policy.family.head_insuree, + policy_renewal, policy_renewal_created = ( + PolicyRenewal.objects.get_or_create( policy=policy, - new_product=product, - renewal_warnings=renewal_warning, - validity_from=now, - audit_user_id=0, + validity_to=None, + defaults=dict( + renewal_prompt_date=now, + renewal_date=renewal_date, + new_officer=officer, + phone_number=officer.phone, + sms_status=0, + insuree=policy.family.head_insuree, + policy=policy, + new_product=product, + renewal_warnings=renewal_warning, + validity_from=now, + audit_user_id=0, + ), ) ) if policy_renewal_created: - create_insuree_renewal_detail(policy_renewal) # The insuree module can create additional renewal data + create_insuree_renewal_detail( + policy_renewal + ) # The insuree module can create additional renewal data def update_renewals(): - from core import datetime - now = datetime.datetime.now() - updated_policies = Policy.objects.filter(validity_to__isnull=True, expiry_date__lt=now) \ - .update(status=Policy.STATUS_EXPIRED) + now = core.datetime.datetime.now() + updated_policies = Policy.objects.filter( + validity_to__isnull=True, expiry_date__lt=now + ).update(status=Policy.STATUS_EXPIRED) logger.debug("update_renewals set %s policies to expired status", updated_policies) return updated_policies @@ -962,7 +1277,9 @@ class SmsQueueItem: sms_message: str -def policy_renewal_sms(family_message_template, range_from=None, range_to=None, sms_header_template=None): +def policy_renewal_sms( + family_message_template, range_from=None, range_to=None, sms_header_template=None +): if sms_header_template is None: sms_header_template = """--Renewal-- {{renewal.renewal_date}} @@ -980,8 +1297,7 @@ def policy_renewal_sms(family_message_template, range_from=None, range_to=None, """ sms_header = Template(sms_header_template) family_message = Template(family_message_template) - from core import datetime - now = datetime.datetime.now() + now = core.datetime.datetime.now() sms_queue = [] i_count = 0 # TODO: remove and make this method a generator @@ -990,14 +1306,16 @@ def policy_renewal_sms(family_message_template, range_from=None, range_to=None, if not range_to: range_to = now - renewals = PolicyRenewal.objects.filter(phone_number__isnull=False) \ - .filter(renewal_prompt_date__gte=range_from) \ - .filter(renewal_prompt_date__lte=range_to) \ - .prefetch_related("insuree") \ - .prefetch_related("new_officer") \ - .prefetch_related("new_product") \ - .prefetch_related("details") \ + renewals = ( + PolicyRenewal.objects.filter(phone_number__isnull=False) + .filter(renewal_prompt_date__gte=range_from) + .filter(renewal_prompt_date__lte=range_to) + .prefetch_related("insuree") + .prefetch_related("new_officer") + .prefetch_related("new_product") + .prefetch_related("details") .prefetch_related("details__insuree") + ) for renewal in renewals: # Leaving the original code in comment to show it used to be handled. It is now delegated to the Django @@ -1014,33 +1332,42 @@ def policy_renewal_sms(family_message_template, range_from=None, range_to=None, # sms_photos += f"--Photos--{head_text}{sms_photos}" # added to sms_header village = renewal.policy.family.location - sms_header_context = Context(dict( - renewal=renewal, - district_name=village.parent.parent if village and village.parent else None, - ward_name=village.parent if village else None, - village_name=village, - )) + sms_header_context = Context( + dict( + renewal=renewal, + district_name=( + village.parent.parent if village and village.parent else None + ), + ward_name=village.parent if village else None, + village_name=village, + ) + ) sms_header_text = sms_header.render(sms_header_context) sms_message = sms_header_text if renewal.new_officer.phone_communication: - sms_queue.append(SmsQueueItem(i_count, renewal.new_officer.phone, sms_message)) + sms_queue.append( + SmsQueueItem(i_count, renewal.new_officer.phone, sms_message) + ) i_count += 1 # Create SMS for the family if family_message and renewal.insuree.phone: expiry_date = renewal.renewal_date - core.datetimedelta(days=1) - new_family_message = family_message.render(Context(dict( - insuree=renewal.insuree, - renewal=renewal, - expiry_date=expiry_date, - ))) - # new_family_message = "" # REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@FamilyMessage, '@@InsuranceID', @CHFID), '@@LastName', @InsLastName), - # '@@OtherNames', @InsOtherNames), '@@ProductCode', @ProductCode), '@@ProductName', @ProductName), - # '@@ExpiryDate', FORMAT(@ExpiryDate,'dd MMM yyyy')) + new_family_message = family_message.render( + Context( + dict( + insuree=renewal.insuree, + renewal=renewal, + expiry_date=expiry_date, + ) + ) + ) if new_family_message: - sms_queue.append(SmsQueueItem(i_count, renewal.insuree.phone, new_family_message)) + sms_queue.append( + SmsQueueItem(i_count, renewal.insuree.phone, new_family_message) + ) i_count += 1 return sms_queue @@ -1049,24 +1376,35 @@ def policy_renewal_sms(family_message_template, range_from=None, range_to=None, def update_insuree_policies(policy, user, members=None): members = get_members(policy, policy.family, user, members) for member in members: - existing_ip = InsureePolicy.objects.filter(validity_to__isnull=True, insuree=member, policy=policy).first() + existing_ip = InsureePolicy.objects.filter( + validity_to__isnull=True, insuree=member, policy=policy + ).first() if existing_ip: existing_ip.save_history() - ip, ip_created = InsureePolicy.objects.filter(validity_to__isnull=True).update_or_create( - insuree=member, policy=policy, + ip, ip_created = InsureePolicy.objects.filter( + validity_to__isnull=True + ).update_or_create( + insuree=member, + policy=policy, defaults=dict( enrollment_date=policy.enroll_date, start_date=policy.start_date, effective_date=policy.effective_date, expiry_date=policy.expiry_date, offline=policy.offline, - audit_user_id=user.id_for_audit if hasattr(user, 'audit_user_id') else user - ) + audit_user_id=( + user.id_for_audit if hasattr(user, "audit_user_id") else user + ), + ), ) if ip_created: - logger.debug("Created InsureePolicy(%s) %s - %s", ip.id, member.chf_id, policy.uuid) + logger.debug( + "Created InsureePolicy(%s) %s - %s", ip.id, member.chf_id, policy.uuid + ) else: - logger.debug("Updated InsureePolicy(%s) %s - %s", ip.id, member.chf_id, policy.uuid) + logger.debug( + "Updated InsureePolicy(%s) %s - %s", ip.id, member.chf_id, policy.uuid + ) def policy_status_premium_paid(policy, effective_date): @@ -1078,6 +1416,8 @@ def policy_status_premium_paid(policy, effective_date): def policy_status_payment_matched(policy): - if PolicyConfig.activation_option == PolicyConfig.ACTIVATION_OPTION_PAYMENT \ - and policy.status == Policy.STATUS_IDLE: + if ( + PolicyConfig.activation_option == PolicyConfig.ACTIVATION_OPTION_PAYMENT + and policy.status == Policy.STATUS_IDLE + ): policy.status = Policy.STATUS_ACTIVE diff --git a/policy/signals.py b/policy/signals.py index edc78ce..80b06d0 100644 --- a/policy/signals.py +++ b/policy/signals.py @@ -1,7 +1,7 @@ from .apps import PolicyConfig from core.signals import Signal -from core.service_signals import ServiceSignalBindType -from core.signals import bind_service_signal _check_formal_sector_for_policy_signal_params = ["user", "policy_id"] -signal_check_formal_sector_for_policy = Signal(_check_formal_sector_for_policy_signal_params) +signal_check_formal_sector_for_policy = Signal( + _check_formal_sector_for_policy_signal_params +) diff --git a/policy/tasks.py b/policy/tasks.py index 8ef0648..3c385a1 100644 --- a/policy/tasks.py +++ b/policy/tasks.py @@ -5,8 +5,18 @@ logger = logging.getLogger(__name__) -def get_policies_for_renewal(interval=None, region=None, district=None, ward=None, village=None, officer=None, - date_from=None, date_to=None, family_message_template=None, sms_header_template=None): +def get_policies_for_renewal( + interval=None, + region=None, + district=None, + ward=None, + village=None, + officer=None, + date_from=None, + date_to=None, + family_message_template=None, + sms_header_template=None, +): """ Find policies that are due for renewal, add them to the renewal queue, mark the expired policies as expired All parameters are optional. @@ -31,9 +41,17 @@ def get_policies_for_renewal(interval=None, region=None, district=None, ward=Non break else: location = None - insert_renewals(date_from, date_to, officer_id=officer, reminding_interval=interval, location_id=location) + insert_renewals( + date_from, + date_to, + officer_id=officer, + reminding_interval=interval, + location_id=location, + ) update_renewals() - sms_queue = policy_renewal_sms(family_message_template, date_from, date_to, sms_header_template) + sms_queue = policy_renewal_sms( + family_message_template, date_from, date_to, sms_header_template + ) for sms in sms_queue: send_sms(sms) @@ -44,4 +62,8 @@ def send_sms(sms): the needs of the gateway, its processing status etc. :param sms: sms queue item, contains phone, sms_message, index """ - logger.warning("Sending an SMS needs a defined gateway, pretending to send to %s:\n%s", sms.phone, sms.sms_message) + logger.warning( + "Sending an SMS needs a defined gateway, pretending to send to %s:\n%s", + sms.phone, + sms.sms_message, + ) diff --git a/policy/test_helpers.py b/policy/test_helpers.py index 6f0d5a5..b9c1049 100644 --- a/policy/test_helpers.py +++ b/policy/test_helpers.py @@ -1,26 +1,30 @@ from contribution.models import Premium -from insuree.models import InsureePolicy, Family, Gender, Insuree +from insuree.models import InsureePolicy from insuree.test_helpers import create_test_insuree from policy.models import Policy from policy.values import policy_values from product.models import Product from core.utils import filter_validity -from core.models.user import User from core.test_helpers import create_test_interactive_user import datetime -def create_test_policy(product, insuree, link=True, valid=True, custom_props=None, check=False): +def create_test_policy( + product, insuree, link=True, valid=True, custom_props=None, check=False +): """ Compatibility method that only return the Policy """ return create_test_policy2(product, insuree, link, valid, custom_props, check)[0] + def dts(s): return datetime.datetime.strptime(s, "%Y-%m-%d").date() -def create_test_policy2(product, insuree, link=True, valid=True, custom_props=None, check=False): +def create_test_policy2( + product, insuree, link=True, valid=True, custom_props=None, check=False +): user = create_test_interactive_user() """ Creates a Policy and optionally an InsureePolicy @@ -33,9 +37,7 @@ def create_test_policy2(product, insuree, link=True, valid=True, custom_props=No :return: The created Policy and InsureePolicy """ policy_qs = Policy.objects.filter( - family=insuree.family, - product=product, - *filter_validity() + family=insuree.family, product=product, *filter_validity() ) if custom_props is None: custom_props = {} @@ -44,18 +46,10 @@ def create_test_policy2(product, insuree, link=True, valid=True, custom_props=No if custom_props: policy_qs = policy_qs.filter(**custom_props) policy = policy_qs.first() - - fallback_start_date = datetime.date( - datetime.date.today().year, - 1, - 1 - ) - fallback_end_date = datetime.date( - datetime.date.today().year, - 12, - 31 - ) - + + fallback_start_date = datetime.date(datetime.date.today().year, 1, 1) + fallback_end_date = datetime.date(datetime.date.today().year, 12, 31) + if not policy: policy = Policy.objects.create( **{ @@ -70,17 +64,16 @@ def create_test_policy2(product, insuree, link=True, valid=True, custom_props=No "expiry_date": fallback_end_date, "validity_to": None if valid else fallback_start_date, "audit_user_id": -1, - **custom_props + **custom_props, } ) elif custom_props: policy.update(**custom_props) - + if link: insuree_policy = InsureePolicy.objects.filter( - insuree=insuree, - policy=policy, - *filter_validity()).first() + insuree=insuree, policy=policy, *filter_validity() + ).first() if not insuree_policy: insuree_policy = InsureePolicy.objects.create( insuree=insuree, @@ -94,14 +87,18 @@ def create_test_policy2(product, insuree, link=True, valid=True, custom_props=No else: insuree_policy = None # Was added for OMT-333 but breaks tests that explicitly call policy_values - policy, warnings = policy_values(policy, insuree.family, None, user, members=[insuree]) + policy, warnings = policy_values( + policy, insuree.family, None, user, members=[insuree] + ) if check and warnings: raise Exception("Policy has warnings: {}".format(warnings)) return policy, insuree_policy -def create_test_policy_with_IPs(product, insuree, valid=True, policy_props=None, IP_props=None, premium_props=None): +def create_test_policy_with_IPs( + product, insuree, valid=True, policy_props=None, IP_props=None, premium_props=None +): """ Creates a Policy with its related InsureePolicys for each Family member. :param product: Product on which this Policy is based (or its ID) @@ -131,7 +128,7 @@ def create_test_policy_with_IPs(product, insuree, valid=True, policy_props=None, "value": value, "validity_to": None if valid else default_date, "audit_user_id": -1, - **(policy_props if policy_props else {}) + **(policy_props if policy_props else {}), } ) @@ -147,7 +144,7 @@ def create_test_policy_with_IPs(product, insuree, valid=True, policy_props=None, "enrollment_date": default_date, "validity_from": default_date, "validity_to": None if valid else default_date, - **(IP_props if IP_props else {}) + **(IP_props if IP_props else {}), } ) @@ -162,7 +159,7 @@ def create_test_policy_with_IPs(product, insuree, valid=True, policy_props=None, "audit_user_id": -1, "validity_to": None if valid else default_date, "created_date": default_date, - **(premium_props if premium_props else {}) + **(premium_props if premium_props else {}), } ) # reseting custom props to avoid having it in next calls @@ -172,14 +169,16 @@ def create_test_policy_with_IPs(product, insuree, valid=True, policy_props=None, return policy -def create_test_insuree_for_policy(with_family=True, is_head=False, custom_props=None, family_custom_props=None): +def create_test_insuree_for_policy( + with_family=True, is_head=False, custom_props=None, family_custom_props=None +): # To establish the mandatory reference between "Insuree" and "Family" objects, we can insert the "Family" object # with a temporary ID and later update it to associate with the respective "Insuree" object. insuree = create_test_insuree( with_family=with_family, is_head=is_head, custom_props=custom_props, - family_custom_props=family_custom_props + family_custom_props=family_custom_props, ) family_custom_props = {} diff --git a/policy/tests/test_services.py b/policy/tests/test_services.py index 811cc30..e36a09c 100644 --- a/policy/tests/test_services.py +++ b/policy/tests/test_services.py @@ -1,22 +1,37 @@ from unittest import mock, skip -from claim.test_helpers import create_test_claim, create_test_claimservice, create_test_claimitem -from claim.validations import validate_claim, validate_assign_prod_to_claimitems_and_services, process_dedrem +from claim.test_helpers import ( + create_test_claim, + create_test_claimservice, + create_test_claimitem, +) +from claim.validations import ( + validate_claim, + validate_assign_prod_to_claimitems_and_services, + process_dedrem, +) from core.models import InteractiveUser, User from core.test_helpers import create_test_officer from django.conf import settings from django.test import TestCase from insuree.test_helpers import create_test_photo from medical.test_helpers import create_test_item, create_test_service -from medical_pricelist.test_helpers import add_service_to_hf_pricelist, add_item_to_hf_pricelist +from medical_pricelist.test_helpers import ( + add_service_to_hf_pricelist, + add_item_to_hf_pricelist, +) from insuree.test_helpers import create_test_insuree from policy.test_helpers import create_test_policy2, create_test_insuree_for_policy -from product.test_helpers import create_test_product, create_test_product_service, create_test_product_item +from product.test_helpers import ( + create_test_product, + create_test_product_service, + create_test_product_item, +) from location.test_helpers import create_test_health_facility from policy.services import * from medical_pricelist.test_helpers import ( create_test_item_pricelist, - create_test_service_pricelist + create_test_service_pricelist, ) from django.db import connection @@ -36,9 +51,13 @@ def test_eligibility_request_permission_denied(self): service = EligibilityService(mock_user) with self.assertRaises(PermissionDenied) as cm: service.request(req) - mock_user.has_perms.assert_called_with(PolicyConfig.gql_query_eligibilities_perms) + mock_user.has_perms.assert_called_with( + PolicyConfig.gql_query_eligibilities_perms + ) - @skip("this test hangs on psql, the mock destroys normal queries happening inside EligibilityRequest") + @skip( + "this test hangs on psql, the mock destroys normal queries happening inside EligibilityRequest" + ) def test_eligibility_request_all_good(self): with mock.patch("django.db.backends.utils.CursorWrapper") as mock_cursor: return_values = [ @@ -55,9 +74,13 @@ def test_eligibility_request_all_good(self): # required for all modules tests mock_cursor.return_value.fetchone.side_effect = return_values # required for policy module tests - mock_cursor.return_value.__enter__.return_value.fetchone.side_effect = return_values + mock_cursor.return_value.__enter__.return_value.fetchone.side_effect = ( + return_values + ) mock_user = mock.Mock(is_anonymous=False) - insuree, family = create_test_insuree_for_policy(custom_props={"chf_id": "tier1234" }) + insuree, family = create_test_insuree_for_policy( + custom_props={"chf_id": "tier1234"} + ) product = create_test_product("ELI1") create_test_policy2(product, insuree) mock_user.has_perm = mock.MagicMock(return_value=True) @@ -133,14 +156,20 @@ def test_eligibility_stored_proc_serv(self): self.eligibility_serv(category) def eligibility_serv(self, category): - insuree, family = create_test_insuree_for_policy(custom_props={"chf_id": "elgsp" + category}) + insuree, family = create_test_insuree_for_policy( + custom_props={"chf_id": "elgsp" + category} + ) product = create_test_product("ELI1") (policy, insuree_policy) = create_test_policy2(product, insuree) service = create_test_service(category) svc_pl_detail = add_service_to_hf_pricelist(service) - product_service = create_test_product_service(product, service, custom_props={"limit_no_adult": 20}) + product_service = create_test_product_service( + product, service, custom_props={"limit_no_adult": 20} + ) claim = create_test_claim(custom_props={"insuree_id": insuree.id}) - claim_service = create_test_claimservice(claim, custom_props={"service_id": service.id}) + claim_service = create_test_claimservice( + claim, custom_props={"service_id": service.id} + ) errors = validate_claim(claim, True) errors += validate_assign_prod_to_claimitems_and_services(claim) errors += process_dedrem(claim, -1, True) @@ -174,16 +203,20 @@ def eligibility_serv(self, category): native_response = native_el_svc.request(req, EligibilityResponse(req)) self.assertIsNotNone(native_response) self.assertEquals(native_response, expected_resposnse) - + def test_eligibility_item(self): insuree, family = create_test_insuree_for_policy() product = create_test_product("ELI1") (policy, insuree_policy) = create_test_policy2(product, insuree) item = create_test_item("A") item_pl_detail = add_item_to_hf_pricelist(item) - product_item = create_test_product_item(product, item, custom_props={"limit_no_adult": 12}) + product_item = create_test_product_item( + product, item, custom_props={"limit_no_adult": 12} + ) claim = create_test_claim(custom_props={"insuree_id": insuree.id}) - claim_item = create_test_claimitem(claim, "A", custom_props={"item_id": item.id}) + claim_item = create_test_claimitem( + claim, "A", custom_props={"item_id": item.id} + ) errors = validate_claim(claim, True) errors += validate_assign_prod_to_claimitems_and_services(claim) errors += process_dedrem(claim, -1, True) @@ -219,16 +252,19 @@ def test_eligibility_item(self): self.assertIsNotNone(native_response) self.assertEquals(native_response, expected_resposnse) - def test_eligibility_by_insuree(self): insuree, family = create_test_insuree_for_policy() product = create_test_product("ELI1") (policy, insuree_policy) = create_test_policy2(product, insuree) item = create_test_item("A") item_pl_detail = add_item_to_hf_pricelist(item) - product_item = create_test_product_item(product, item, custom_props={"limit_no_adult": 12}) + product_item = create_test_product_item( + product, item, custom_props={"limit_no_adult": 12} + ) claim = create_test_claim(custom_props={"insuree_id": insuree.id}) - claim_item = create_test_claimitem(claim, "A", custom_props={"item_id": item.id}) + claim_item = create_test_claimitem( + claim, "A", custom_props={"item_id": item.id} + ) errors = validate_claim(claim, True) errors += validate_assign_prod_to_claimitems_and_services(claim) errors += process_dedrem(claim, -1, True) @@ -264,21 +300,25 @@ def test_eligibility_by_insuree(self): self.assertIsNotNone(native_response) self.assertEquals(native_response, expected_resposnse) result = PolicyService(self.user).set_deleted(policy) - self.assertNotEquals(result, [], "the policy cannot be deleted as it has some DedRem on it") - - - + self.assertNotEquals( + result, [], "the policy cannot be deleted as it has some DedRem on it" + ) - @skip("Not sure what is the proper behaviour when an IP is not present, skipping for now so that the main case" - "can be fixed.") + @skip( + "Not sure what is the proper behaviour when an IP is not present, skipping for now so that the main case" + "can be fixed." + ) def test_eligibility_stored_proc_item_no_insuree_policy(self): insuree = create_test_insuree_for_policy() product = create_test_product("ELI1") (policy, _) = create_test_policy2( - product, insuree, link=False, custom_props={"status": Policy.STATUS_IDLE}) + product, insuree, link=False, custom_props={"status": Policy.STATUS_IDLE} + ) item = create_test_item("A") item_pl_detail = add_item_to_hf_pricelist(item) - product_item = create_test_product_item(product, item, custom_props={"limit_no_adult": 12}) + product_item = create_test_product_item( + product, item, custom_props={"limit_no_adult": 12} + ) sp_el_svc = StoredProcEligibilityService(self.user) native_el_svc = NativeEligibilityService(self.user) @@ -293,19 +333,25 @@ def test_eligibility_stored_proc_item_no_insuree_policy(self): self.assertEquals(native_response, sp_response) def test_eligibility_signal(self): - + insuree, family = create_test_insuree_for_policy() - #spl = create_test_service_pricelist(location_id=family.location.parent.id) - #ipl = create_test_item_pricelist(location_id=family.location.parent.id) - #hf =create_test_health_facility(code= 'tst-18', location_id=family.location.parent.id, custom_props={'id':18, 'items_pricelist': ipl, 'services_pricelist': spl }) + # spl = create_test_service_pricelist(location_id=family.location.parent.id) + # ipl = create_test_item_pricelist(location_id=family.location.parent.id) + # hf =create_test_health_facility(code= 'tst-18', location_id=family.location.parent.id, custom_props={'id':18, 'items_pricelist': ipl, 'services_pricelist': spl }) product = create_test_product("ELI1") (policy, insuree_policy) = create_test_policy2(product, insuree) item = create_test_item("A") item_pl_detail = add_item_to_hf_pricelist(item) - product_item = create_test_product_item(product, item, custom_props={"limit_no_adult": 12}) - claim = create_test_claim(custom_props={"insuree_id": insuree.id, 'date_to': None}) - claim_item = create_test_claimitem(claim, "A", custom_props={"item_id": item.id}) + product_item = create_test_product_item( + product, item, custom_props={"limit_no_adult": 12} + ) + claim = create_test_claim( + custom_props={"insuree_id": insuree.id, "date_to": None} + ) + claim_item = create_test_claimitem( + claim, "A", custom_props={"item_id": item.id} + ) errors = validate_claim(claim, True) errors += validate_assign_prod_to_claimitems_and_services(claim) errors += process_dedrem(claim, -1, True) @@ -329,7 +375,6 @@ def signal_before(sender, **kwargs): signal_eligibility_service_before.disconnect(signal_before) - class RenewalsTestCase(TestCase): item_1 = None @@ -375,8 +420,6 @@ def test_insert_renewals(self): should_not_renew = renewals.filter(policy=policy_not_expiring).first() self.assertIsNone(should_not_renew) - - def test_update_renewals(self): # Given from core import datetime, datetimedelta @@ -409,18 +452,21 @@ def test_update_renewals(self): self.assertEquals(policy_expiring.status, Policy.STATUS_EXPIRED) self.assertEquals(policy_not_expired_yet.status, Policy.STATUS_ACTIVE) - - def test_renewals_sms(self): # Given from core import datetime, datetimedelta insuree, family = create_test_insuree_for_policy( - custom_props={"chf_id": "TESTCHFSMS", 'last_name':'Test Last',"phone": "+33644444719"} ) + custom_props={ + "chf_id": "TESTCHFSMS", + "last_name": "Test Last", + "phone": "+33644444719", + } + ) product = create_test_product("VISIT") officer = create_test_officer( custom_props={"phone": "+32444444444", "phone_communication": True}, - villages = [family.location] + villages=[family.location], ) (policy_expiring, _) = create_test_policy2( @@ -452,7 +498,8 @@ def test_renewals_sms(self): insuree_sms = [sms for sms in sms_queue if sms.phone == "+33644444719"] self.assertEquals(len(insuree_sms), 1) self.assertEquals( - insuree_sms[0].sms_message, f"FAMSMS;{insuree.chf_id};Test Last;Test product VISIT" + insuree_sms[0].sms_message, + f"FAMSMS;{insuree.chf_id};Test Last;Test product VISIT", ) officer_sms = [sms for sms in sms_queue if sms.phone == "+32444444444"] @@ -463,18 +510,26 @@ def test_renewals_sms(self): self.assertIn(family.location.parent.parent.name, officer_sms[0].sms_message) self.assertIn("Test product VISIT", officer_sms[0].sms_message) - - def test_insert_renewal_details(self): # Given from core import datetime, datetimedelta insuree_newpic, family_newpic = create_test_insuree_for_policy( - custom_props={"photo_date": datetime.datetime.now() - datetimedelta(days=30)}) + custom_props={ + "photo_date": datetime.datetime.now() - datetimedelta(days=30) + } + ) insuree_oldpic, family_oldpic = create_test_insuree_for_policy( - custom_props={"photo_date": "2010-01-01", "chf_id": "CHFMARK", 'last_name':'Test Last'}) # 5 years by default + custom_props={ + "photo_date": "2010-01-01", + "chf_id": "CHFMARK", + "last_name": "Test Last", + } + ) # 5 years by default product = create_test_product("VISIT") - officer = create_test_officer(custom_props={"phone": "+32444444444", "phone_communication": True}) + officer = create_test_officer( + custom_props={"phone": "+32444444444", "phone_communication": True} + ) photo_newpic = create_test_photo(insuree_newpic.id, officer.id) photo_oldpic = create_test_photo(insuree_oldpic.id, officer.id) @@ -513,7 +568,12 @@ def test_insert_renewal_details(self): # ALSO WHEN sms_queue = policy_renewal_sms("UNUSED") # Uses the default template self.assertEquals(len(sms_queue), 2) - old_sms = [sms.sms_message for sms in sms_queue if insuree_oldpic.chf_id in sms.sms_message] + old_sms = [ + sms.sms_message + for sms in sms_queue + if insuree_oldpic.chf_id in sms.sms_message + ] self.assertEquals(len(old_sms), 1) - self.assertTrue(f"HOF\n{insuree_oldpic.chf_id}\nTest Last First Second\n\n" in old_sms[0]) - + self.assertTrue( + f"HOF\n{insuree_oldpic.chf_id}\nTest Last First Second\n\n" in old_sms[0] + ) diff --git a/policy/tests/test_values.py b/policy/tests/test_values.py index ec33765..a66b697 100644 --- a/policy/tests/test_values.py +++ b/policy/tests/test_values.py @@ -10,40 +10,54 @@ from core.test_helpers import create_test_interactive_user from dateutil.relativedelta import relativedelta from core.models.user import User + + class PolicyValuesTestCase(TestCase): - test_child_dob = py_datetime.date.today()- relativedelta(years= CoreConfig.age_of_majority-2) + test_child_dob = py_datetime.date.today() - relativedelta( + years=CoreConfig.age_of_majority - 2 + ) user = None - - + @classmethod def setUpClass(cls): super().setUpClass() cls.user = create_test_interactive_user() - - + def test_new_policy_basis(self): - head_insuree = create_test_insuree(with_family=True, custom_props={"dob": core.datetime.date(1985, 5, 5)}) + head_insuree = create_test_insuree( + with_family=True, custom_props={"dob": core.datetime.date(1985, 5, 5)} + ) spouse = Relation.objects.get(id=8) - create_test_insuree( with_family = False, + create_test_insuree( + with_family=False, custom_props={ "dob": core.datetime.date(1989, 9, 9), "relationship": spouse, - "family": head_insuree.family - }) + "family": head_insuree.family, + }, + ) - product = create_test_product("SIMPLE", custom_props={ - "max_members": 5, - "administration_period": 0, - "lump_sum": 0, - "premium_adult": 300, - "premium_child": 200, - "registration_lump_sum": 250, - "general_assembly_lump_sum": 130, - "insurance_period": 12, - }) - policy = create_test_policy(product, head_insuree, link=False, custom_props={ - "enroll_date": core.datetime.date(2020, 11, 10), - }) + product = create_test_product( + "SIMPLE", + custom_props={ + "max_members": 5, + "administration_period": 0, + "lump_sum": 0, + "premium_adult": 300, + "premium_child": 200, + "registration_lump_sum": 250, + "general_assembly_lump_sum": 130, + "insurance_period": 12, + }, + ) + policy = create_test_policy( + product, + head_insuree, + link=False, + custom_props={ + "enroll_date": core.datetime.date(2020, 11, 10), + }, + ) policy, warnings = policy_values(policy, head_insuree.family, None, self.user) self.assertEquals(policy.start_date, core.datetime.date(2020, 11, 10)) self.assertEquals(policy.expiry_date, core.datetime.date(2021, 11, 9)) @@ -51,49 +65,68 @@ def test_new_policy_basis(self): # let's add a child and shift date to 1st of a month child = Relation.objects.get(id=4) - create_test_insuree(with_family = False, + create_test_insuree( + with_family=False, custom_props={ "dob": self.test_child_dob, "relationship": child, - "family": head_insuree.family - }) - policy = create_test_policy(product, head_insuree, link=False, custom_props={ - "enroll_date": core.datetime.date(2020, 11, 1), - }) + "family": head_insuree.family, + }, + ) + policy = create_test_policy( + product, + head_insuree, + link=False, + custom_props={ + "enroll_date": core.datetime.date(2020, 11, 1), + }, + ) policy, warnings = policy_values(policy, head_insuree.family, None, self.user) self.assertEquals(policy.start_date, core.datetime.date(2020, 11, 1)) self.assertEquals(policy.expiry_date, core.datetime.date(2021, 10, 31)) self.assertEquals(policy.value, 1180) # 2 x 300 + 200 + 250 + 130 def test_new_policy_lump_sum_and_cycles(self): - head_insuree = create_test_insuree(with_family=True, custom_props={"dob": core.datetime.date(1985, 5, 5)}) + head_insuree = create_test_insuree( + with_family=True, custom_props={"dob": core.datetime.date(1985, 5, 5)} + ) spouse = Relation.objects.get(id=8) - create_test_insuree( with_family = False, + create_test_insuree( + with_family=False, custom_props={ "dob": core.datetime.date(1989, 9, 9), "relationship": spouse, - "family": head_insuree.family - }) + "family": head_insuree.family, + }, + ) - product = create_test_product("SIMPLE", custom_props={ - "max_members": 5, - "administration_period": 0, - "lump_sum": 200, - "threshold": 2, - "grace_period_enrolment": 1, - "start_cycle_1": "01-01", - "start_cycle_2": "01-06", - "premium_adult": 300, - "premium_child": 200, - "registration_lump_sum": 0, - "registration_fee": 10, - "general_assembly_lump_sum": 0, - "general_assembly_fee": 5, - "insurance_period": 12, - }) - policy = create_test_policy(product, head_insuree, link=False, custom_props={ - "enroll_date": core.datetime.date(2020, 11, 10), - }) + product = create_test_product( + "SIMPLE", + custom_props={ + "max_members": 5, + "administration_period": 0, + "lump_sum": 200, + "threshold": 2, + "grace_period_enrolment": 1, + "start_cycle_1": "01-01", + "start_cycle_2": "01-06", + "premium_adult": 300, + "premium_child": 200, + "registration_lump_sum": 0, + "registration_fee": 10, + "general_assembly_lump_sum": 0, + "general_assembly_fee": 5, + "insurance_period": 12, + }, + ) + policy = create_test_policy( + product, + head_insuree, + link=False, + custom_props={ + "enroll_date": core.datetime.date(2020, 11, 10), + }, + ) policy, warnings = policy_values(policy, head_insuree.family, None, self.user) self.assertEquals(policy.start_date, core.datetime.date(2021, 1, 1)) self.assertEquals(policy.expiry_date, core.datetime.date(2021, 12, 31)) @@ -101,51 +134,73 @@ def test_new_policy_lump_sum_and_cycles(self): # let's add a child (outside threshold) and shift date in 1st cycle grace period child = Relation.objects.get(id=4) - create_test_insuree( with_family = False, + create_test_insuree( + with_family=False, custom_props={ "dob": self.test_child_dob, "relationship": child, - "family": head_insuree.family - }) - policy = create_test_policy(product, head_insuree, link=False, custom_props={ - "enroll_date": core.datetime.date(2021, 1, 11), - }) + "family": head_insuree.family, + }, + ) + policy = create_test_policy( + product, + head_insuree, + link=False, + custom_props={ + "enroll_date": core.datetime.date(2021, 1, 11), + }, + ) policy, warnings = policy_values(policy, head_insuree.family, None, self.user) self.assertEquals(policy.start_date, core.datetime.date(2021, 1, 1)) self.assertEquals(policy.expiry_date, core.datetime.date(2021, 12, 31)) self.assertEquals(policy.value, 445) # 200 + 1 x 200 + 3 x 10 + 3 x 5 def test_new_policy_admin_period_max_members_insurance_period(self): - head_insuree = create_test_insuree(with_family=True, custom_props={"chf_id":"tnpapmmip","dob": core.datetime.date(1985, 5, 5)}) + head_insuree = create_test_insuree( + with_family=True, + custom_props={"chf_id": "tnpapmmip", "dob": core.datetime.date(1985, 5, 5)}, + ) spouse = Relation.objects.get(id=8) - create_test_insuree(with_family = False, + create_test_insuree( + with_family=False, custom_props={ "dob": core.datetime.date(1989, 9, 9), "relationship": spouse, - "family": head_insuree.family - }) + "family": head_insuree.family, + }, + ) - product = create_test_product("SIMPLE", custom_props={ - "max_members": 2, - "administration_period": 1, - "lump_sum": 200, - "threshold": 1, - "grace_period_enrolment": 1, - "start_cycle_1": "01-01", - "start_cycle_2": "01-06", - "premium_adult": 300, - "premium_child": 200, - "registration_lump_sum": 0, - "registration_fee": 10, - "general_assembly_lump_sum": 0, - "general_assembly_fee": 5, - "insurance_period": 6, - }) - policy = create_test_policy(product, head_insuree, link=False, custom_props={ - "enroll_date": core.datetime.date(2021, 1, 10), - }) + product = create_test_product( + "SIMPLE", + custom_props={ + "max_members": 2, + "administration_period": 1, + "lump_sum": 200, + "threshold": 1, + "grace_period_enrolment": 1, + "start_cycle_1": "01-01", + "start_cycle_2": "01-06", + "premium_adult": 300, + "premium_child": 200, + "registration_lump_sum": 0, + "registration_fee": 10, + "general_assembly_lump_sum": 0, + "general_assembly_fee": 5, + "insurance_period": 6, + }, + ) + policy = create_test_policy( + product, + head_insuree, + link=False, + custom_props={ + "enroll_date": core.datetime.date(2021, 1, 10), + }, + ) policy, warnings = policy_values(policy, head_insuree.family, None, self.user) - self.assertEquals(policy.start_date, core.datetime.date(2021, 6, 1)) # enroll + admin outside cycle 1 + grace + self.assertEquals( + policy.start_date, core.datetime.date(2021, 6, 1) + ) # enroll + admin outside cycle 1 + grace self.assertEquals(policy.expiry_date, core.datetime.date(2021, 11, 30)) self.assertEquals(policy.value, 530) # 200 + 300 + 2 x 10 + 2 x 5 @@ -156,13 +211,22 @@ def test_new_policy_admin_period_max_members_insurance_period(self): custom_props={ "dob": self.test_child_dob, "relationship": child, - "family": head_insuree.family - }) - policy = create_test_policy(product, head_insuree, link=False, custom_props={ + "family": head_insuree.family, + } + ) + policy = create_test_policy( + product, + head_insuree, + link=False, + custom_props={ "enroll_date": core.datetime.date(2021, 12, 11), - }, check = False) + }, + check=False, + ) policy, warnings = policy_values(policy, head_insuree.family, None, self.user) - self.assertEquals(policy.start_date, core.datetime.date(2022, 1, 1)) # enroll + admin in cycle 1 + grace + self.assertEquals( + policy.start_date, core.datetime.date(2022, 1, 1) + ) # enroll + admin in cycle 1 + grace self.assertEquals(policy.expiry_date, core.datetime.date(2022, 6, 30)) self.assertEquals(policy.value, 530) # 200 + 300 + 2 x 10 + 2 x 5 diff --git a/policy/tests/tests.py b/policy/tests/tests.py index 3afe82e..26693c1 100644 --- a/policy/tests/tests.py +++ b/policy/tests/tests.py @@ -6,15 +6,32 @@ from contribution.test_helpers import create_test_premium from core.test_helpers import create_test_officer from policy.models import Policy -from policy.test_helpers import create_test_policy2,create_test_policy_with_IPs,create_test_insuree_for_policy +from policy.test_helpers import ( + create_test_policy2, + create_test_policy_with_IPs, + create_test_insuree_for_policy, +) from product.test_helpers import create_test_product, create_test_product_service class TaskGroupServiceTest(TestCase): def test_helper(self): - insuree, family = create_test_insuree_for_policy(with_family=True, is_head=False, custom_props={"chf_id": "paysimp"}, family_custom_props={}) + insuree, family = create_test_insuree_for_policy( + with_family=True, + is_head=False, + custom_props={"chf_id": "paysimp"}, + family_custom_props={}, + ) product = create_test_product("ELI1") - (policy, insuree_policy) = create_test_policy2(product, insuree, custom_props={ - "value": 1000, "status": Policy.STATUS_IDLE}) + (policy, insuree_policy) = create_test_policy2( + product, insuree, custom_props={"value": 1000, "status": Policy.STATUS_IDLE} + ) - policy = create_test_policy_with_IPs(product, insuree, valid=True, policy_props={"value": 1000, "status": Policy.STATUS_ACTIVE}, IP_props=None, premium_props=None) + policy = create_test_policy_with_IPs( + product, + insuree, + valid=True, + policy_props={"value": 1000, "status": Policy.STATUS_ACTIVE}, + IP_props=None, + premium_props=None, + ) diff --git a/policy/tests/tests_gql.py b/policy/tests/tests_gql.py index 713b391..1aeaa22 100644 --- a/policy/tests/tests_gql.py +++ b/policy/tests/tests_gql.py @@ -7,14 +7,12 @@ from core.models.openimis_graphql_test_case import openIMISGraphQLTestCase from django.conf import settings -from medical.models import Service +from medical.models import Service from graphene_django.utils.testing import GraphQLTestCase from graphql_jwt.shortcuts import get_token -#credits https://docs.graphene-python.org/projects/django/en/latest/testing/ -from medical.test_helpers import ( - create_test_item, - create_test_service -) + +# credits https://docs.graphene-python.org/projects/django/en/latest/testing/ +from medical.test_helpers import create_test_item, create_test_service from insuree.test_helpers import create_test_insuree from policy.test_helpers import create_test_policy, dts from contribution.test_helpers import create_test_premium @@ -24,45 +22,50 @@ create_test_product_service, create_test_product_item, ) -from location.test_helpers import ( - create_test_health_facility, - create_test_village -) +from location.test_helpers import create_test_health_facility, create_test_village from uuid import UUID + + @dataclass class DummyContext: - """ Just because we need a context to generate. """ + """Just because we need a context to generate.""" + user: User + class PolicyGraphQLTestCase(openIMISGraphQLTestCase): admin_user = None - + @classmethod def setUpClass(cls): super().setUpClass() cls.admin_user = create_test_interactive_user(username="testLocationAdmin") cls.admin_token = get_token(cls.admin_user, DummyContext(user=cls.admin_user)) - - cls.test_village =create_test_village() - cls.test_ward =cls.test_village.parent - cls.test_region =cls.test_village.parent.parent.parent + + cls.test_village = create_test_village() + cls.test_ward = cls.test_village.parent + cls.test_region = cls.test_village.parent.parent.parent cls.test_district = cls.test_village.parent.parent # Given - cls.insuree = create_test_insuree(custom_props={'current_village':cls.test_village}) - - cls.service = create_test_service("A", custom_props={"name": "test_simple_batch"}) + cls.insuree = create_test_insuree( + custom_props={"current_village": cls.test_village} + ) + + cls.service = create_test_service( + "A", custom_props={"name": "test_simple_batch"} + ) cls.item = create_test_item("A", custom_props={"name": "test_simple_batch"}) - + cls.product = create_test_product( "BCUL0001", custom_props={ "name": "simplebatch", "lump_sum": 10_000, - "location_id": cls.test_region.id + "location_id": cls.test_region.id, }, ) - + cls.product_service = create_test_product_service( cls.product, cls.service, @@ -74,27 +77,30 @@ def setUpClass(cls): custom_props={"price_origin": ProductItemOrService.ORIGIN_RELATIVE}, ) cls.policy = create_test_policy(cls.product, cls.insuree, link=True) - cls.premium = create_test_premium( - policy_id=cls.policy.id, custom_props={} + cls.premium = create_test_premium(policy_id=cls.policy.id, custom_props={}) + cls.policy_past = create_test_policy( + cls.product, + cls.insuree, + link=True, + custom_props={ + "enroll_date": dts("2010-01-01"), + "start_date": dts("2010-01-01"), + "validity_from": dts("2010-01-01"), + "effective_date": dts("2010-01-01"), + "expiry_date": dts("2011-01-01"), + }, ) - cls.policy_past = create_test_policy(cls.product, cls.insuree, link=True, custom_props={ - "enroll_date": dts("2010-01-01"), - "start_date": dts("2010-01-01"), - "validity_from": dts("2010-01-01"), - "effective_date": dts("2010-01-01"), - "expiry_date": dts("2011-01-01"), - }) cls.premium_past = create_test_premium( - policy_id=cls.policy_past.id, custom_props={'pay_date':dts('2010-01-01')} + policy_id=cls.policy_past.id, custom_props={"pay_date": dts("2010-01-01")} + ) + cls.not_insuree = create_test_insuree( + with_family=False, custom_props={"family": cls.insuree.family} ) - cls.not_insuree = create_test_insuree(with_family= False, custom_props={'family':cls.insuree.family}) - - def test_insuree_policy_query(self): - + response = self.query( - ''' + """ query { policies(first: 10,orderBy: ["-enrollDate"], balanceLte: 100) { @@ -109,7 +115,7 @@ def test_insuree_policy_query(self): } } } - ''', + """, headers={"HTTP_AUTHORIZATION": f"Bearer {self.admin_token}"}, ) @@ -120,10 +126,11 @@ def test_insuree_policy_query(self): # Add some more asserts if you like ... + def test_query_not_insured_family_member(self): response = self.query( - ''' - + """ + query policiesByInsuree($chfid: String!) { policiesByInsuree(chfId:$chfid) { @@ -138,21 +145,24 @@ def test_query_not_insured_family_member(self): } } } - ''', + """, headers={"HTTP_AUTHORIZATION": f"Bearer {self.admin_token}"}, - variables={'chfid': self.not_insuree.chf_id, 'targetDate':self.policy.effective_date.strftime("%Y-%m-%d")} + variables={ + "chfid": self.not_insuree.chf_id, + "targetDate": self.policy.effective_date.strftime("%Y-%m-%d"), + }, ) content = json.loads(response.content) # This validates the status code and if you get errors self.assertResponseNoErrors(response) - self.assertEqual(content['data']['policiesByInsuree']['totalCount'], 0) + self.assertEqual(content["data"]["policiesByInsuree"]["totalCount"], 0) def test_query_with_variables(self): response = self.query( - ''' - + """ + query policiesByInsuree($chfid: String!) { policiesByInsuree(chfId:$chfid) { @@ -167,21 +177,24 @@ def test_query_with_variables(self): } } } - ''', + """, headers={"HTTP_AUTHORIZATION": f"Bearer {self.admin_token}"}, - variables={'chfid': self.insuree.chf_id, 'targetDate':self.policy.effective_date.strftime("%Y-%m-%d")} + variables={ + "chfid": self.insuree.chf_id, + "targetDate": self.policy.effective_date.strftime("%Y-%m-%d"), + }, ) content = json.loads(response.content) # This validates the status code and if you get errors self.assertResponseNoErrors(response) - self.assertEqual(content['data']['policiesByInsuree']['totalCount'], 2) - + self.assertEqual(content["data"]["policiesByInsuree"]["totalCount"], 2) + def test_query_with_variables_2(self): response = self.query( - ''' - + """ + query policiesByInsuree($chfid: String!, $activeOrLastExpiredOnly: Boolean!) { policiesByInsuree(chfId:$chfid, activeOrLastExpiredOnly:$activeOrLastExpiredOnly) { @@ -196,22 +209,27 @@ def test_query_with_variables_2(self): } } } - ''', + """, headers={"HTTP_AUTHORIZATION": f"Bearer {self.admin_token}"}, - variables={'chfid': self.insuree.chf_id, 'activeOrLastExpiredOnly':True} + variables={"chfid": self.insuree.chf_id, "activeOrLastExpiredOnly": True}, ) content = json.loads(response.content) # This validates the status code and if you get errors self.assertResponseNoErrors(response) - self.assertEqual(content['data']['policiesByInsuree']['totalCount'], 1) - self.assertEqual(UUID(content['data']['policiesByInsuree']['edges'][0]['node']['policyUuid']), UUID(self.policy.uuid)) - + self.assertEqual(content["data"]["policiesByInsuree"]["totalCount"], 1) + self.assertEqual( + UUID( + content["data"]["policiesByInsuree"]["edges"][0]["node"]["policyUuid"] + ), + UUID(self.policy.uuid), + ) + def test_query_with_variables_3(self): response = self.query( - ''' - + """ + query policiesByInsuree($chfid: String!, $targetDate: Date! ) { policiesByInsuree(chfId:$chfid ,targetDate: $targetDate) { @@ -226,21 +244,29 @@ def test_query_with_variables_3(self): } } } - ''', + """, headers={"HTTP_AUTHORIZATION": f"Bearer {self.admin_token}"}, - variables={'chfid': self.insuree.chf_id, 'targetDate':self.policy.effective_date.strftime("%Y-%m-%d")} + variables={ + "chfid": self.insuree.chf_id, + "targetDate": self.policy.effective_date.strftime("%Y-%m-%d"), + }, ) content = json.loads(response.content) # This validates the status code and if you get errors self.assertResponseNoErrors(response) - self.assertEqual(content['data']['policiesByInsuree']['totalCount'], 1) - self.assertEqual(UUID(content['data']['policiesByInsuree']['edges'][0]['node']['policyUuid']), UUID(self.policy.uuid)) - + self.assertEqual(content["data"]["policiesByInsuree"]["totalCount"], 1) + self.assertEqual( + UUID( + content["data"]["policiesByInsuree"]["edges"][0]["node"]["policyUuid"] + ), + UUID(self.policy.uuid), + ) + def test_family_query_with_variables(self): response = self.query( - ''' + """ query policiesByFamily($familyUuid: String!, $targetDate: Date! ) { policiesByFamily(orderBy: "expiryDate",activeOrLastExpiredOnly: true,familyUuid:$familyUuid ,targetDate: $targetDate,first: 5) { @@ -252,40 +278,38 @@ def test_family_query_with_variables(self): { policyUuid,productCode,productName,officerCode,officerName,enrollDate,effectiveDate,startDate,expiryDate,status,policyValue,balance,ded,dedInPatient,dedOutPatient,ceiling,ceilingInPatient,ceilingOutPatient } - } + } } - } - ''', + } + """, headers={"HTTP_AUTHORIZATION": f"Bearer {self.admin_token}"}, - variables={'familyUuid': str(self.insuree.family.uuid), 'targetDate':self.policy.effective_date.strftime("%Y-%m-%d")} + variables={ + "familyUuid": str(self.insuree.family.uuid), + "targetDate": self.policy.effective_date.strftime("%Y-%m-%d"), + }, ) content = json.loads(response.content) - - # This validates the status code and if you get errors self.assertResponseNoErrors(response) - self.assertEqual(content['data']['policiesByFamily']['totalCount'], 1) - self.assertEqual(UUID(content['data']['policiesByFamily']['edges'][0]['node']['policyUuid']), UUID(self.policy.uuid)) + self.assertEqual(content["data"]["policiesByFamily"]["totalCount"], 1) + self.assertEqual( + UUID(content["data"]["policiesByFamily"]["edges"][0]["node"]["policyUuid"]), + UUID(self.policy.uuid), + ) - - def test_insuree_policy_query(self): - - - - - + response = self.query( - f''' + f""" {{ policyServiceEligibilityByInsuree(chfId:"{self.insuree.chf_id}", serviceCode:"{self.service.code}") {{ minDateService, serviceLeft, isServiceOk }} }} - ''', + """, headers={"HTTP_AUTHORIZATION": f"Bearer {self.admin_token}"}, ) @@ -294,17 +318,16 @@ def test_insuree_policy_query(self): # This validates the status code and if you get errors self.assertResponseNoErrors(response) - # Add some more asserts if you like response = self.query( - f''' + f""" {{ policyItemEligibilityByInsuree(chfId:"{self.insuree.chf_id}",itemCode:"{self.item.code}") {{ minDateItem,itemLeft,isItemOk }} }} - ''', + """, headers={"HTTP_AUTHORIZATION": f"Bearer {self.admin_token}"}, ) @@ -319,13 +342,13 @@ def test_insuree_policy_query(self): def test_mutation_simple(self): muuid = "203327cd-501e-41e1-a026-ed742e360081" response = self.query( - f''' + f""" mutation {{ createPolicy( input: {{ clientMutationId: "{muuid}" clientMutationLabel: "Création de la police ttttt eeeee (123123123) - 2024-06-01 : 2025-05-31" - + enrollDate: "2024-04-07" startDate: "2024-06-01" expiryDate: "2025-05-31" @@ -339,26 +362,30 @@ def test_mutation_simple(self): internalId }} }} - ''', + """, headers={"HTTP_AUTHORIZATION": f"Bearer {self.admin_token}"}, - variables={'chfid': self.insuree.chf_id, 'activeOrLastExpiredOnly':True} + variables={"chfid": self.insuree.chf_id, "activeOrLastExpiredOnly": True}, ) content = self.get_mutation_result(muuid, self.admin_token) - + def test_insuree_policy_value_query(self): response = self.query( - f''' - {{ - policyValues(stage: "R",enrollDate: "2019-09-26T00:00:00",productId: {self.product.id},familyId: {self.insuree.family.id}) + f""" {{ + policyValues( + stage: "R", + enrollDate: "2019-09-26T00:00:00", + productId: {self.product.id}, + familyId: {self.insuree.family.id} + ){{ policy{{startDate expiryDate value}},warnings }} - }} ''', + }} """, headers={"HTTP_AUTHORIZATION": f"Bearer {self.admin_token}"}, ) content = json.loads(response.content) # This validates the status code and if you get errors - self.assertResponseNoErrors(response) \ No newline at end of file + self.assertResponseNoErrors(response) diff --git a/policy/tests/tests_reports.py b/policy/tests/tests_reports.py index d95a5e1..92551f9 100644 --- a/policy/tests/tests_reports.py +++ b/policy/tests/tests_reports.py @@ -1,42 +1,45 @@ from core.test_helpers import create_test_interactive_user from rest_framework import status from rest_framework.test import APITestCase -from dataclasses import copy, dataclass +from dataclasses import dataclass from graphql_jwt.shortcuts import get_token from core.models import User from django.conf import settings from django.db import connection + @dataclass class DummyContext: - """ Just because we need a context to generate. """ + """Just because we need a context to generate.""" + user: User -class ReportAPITests( APITestCase): +class ReportAPITests(APITestCase): admin_user = None admin_token = None - POI_URL = f'/{settings.SITE_ROOT()}report/policy_primary_operational_indicators/pdf/?yearMonth=2019-04-01' - PR_URL = f'/{settings.SITE_ROOT()}report/policy_renewals/pdf/?date_start=2019-04-01&date_end=2019-04-30' + POI_URL = f"/{settings.SITE_ROOT()}report/policy_primary_operational_indicators/pdf/?yearMonth=2019-04-01" + PR_URL = f"/{settings.SITE_ROOT()}report/policy_renewals/pdf/?date_start=2019-04-01&date_end=2019-04-30" + @classmethod def setUpClass(cls): super().setUpClass() cls.admin_user = create_test_interactive_user(username="testLocationAdmin") cls.admin_token = get_token(cls.admin_user, DummyContext(user=cls.admin_user)) - + def test_primary_operational_indicators_report(self): if not connection.vendor == "postgresql": self.skipTest("This test can only be executed for MSSQL database") - headers={"HTTP_AUTHORIZATION": f"Bearer {self.admin_token}"} - response = self.client.get(self.POI_URL, format='application/pdf', **headers) + headers = {"HTTP_AUTHORIZATION": f"Bearer {self.admin_token}"} + response = self.client.get(self.POI_URL, format="application/pdf", **headers) self.assertEqual(response.status_code, status.HTTP_200_OK) - content = b''.join(response.streaming_content) + content = b"".join(response.streaming_content) self.assertTrue(len(content) > 0) - + def test_policy_renewal_report(self): - headers={"HTTP_AUTHORIZATION": f"Bearer {self.admin_token}"} - response = self.client.get(self.PR_URL, format='application/pdf ', **headers) + headers = {"HTTP_AUTHORIZATION": f"Bearer {self.admin_token}"} + response = self.client.get(self.PR_URL, format="application/pdf ", **headers) self.assertEqual(response.status_code, status.HTTP_200_OK) - content = b''.join(response.streaming_content) - self.assertTrue(len(content) > 0) \ No newline at end of file + content = b"".join(response.streaming_content) + self.assertTrue(len(content) > 0) diff --git a/policy/urls.py b/policy/urls.py index 2012ebe..637600f 100644 --- a/policy/urls.py +++ b/policy/urls.py @@ -1 +1 @@ -urlpatterns = [] \ No newline at end of file +urlpatterns = [] diff --git a/policy/utils.py b/policy/utils.py index a28cfa4..24430b6 100644 --- a/policy/utils.py +++ b/policy/utils.py @@ -1,5 +1,4 @@ from django.db.models import Func, DateTimeField -from django.db.models.functions import Cast, Coalesce from calculation.services import run_calculation_rules from contribution_plan.models import ContributionPlan @@ -10,12 +9,13 @@ class MonthsAdd(Func): it might need an update with custom resolve. Usage: Foo.objects.annotate(end_date=MonthsAdd('start_date', 'duration')).filter(end_date__gt=datetime.now) """ + # https://stackoverflow.com/questions/33981468/using-dateadd-in-django-filter arg_joiner = " + CAST(" template = "%(expressions)s || 'months' as INTERVAL)" - template_mssql = '%(function)s(MONTH, %(expressions)s)' + template_mssql = "%(function)s(MONTH, %(expressions)s)" function_mssql = "DATEADD" output_field = DateTimeField() @@ -24,10 +24,13 @@ class MonthsAdd(Func): def as_sql(self, compiler, connection, **extra_context): - if connection.vendor == 'microsoft': - self.arg_joiner = ', ' - self.source_expressions = self.get_source_expressions( - )[::-1] if self.COUNTER == 0 else self.get_source_expressions() + if connection.vendor == "microsoft": + self.arg_joiner = ", " + self.source_expressions = ( + self.get_source_expressions()[::-1] + if self.COUNTER == 0 + else self.get_source_expressions() + ) self.template = self.template_mssql self.function = self.function_mssql self.COUNTER += 1 @@ -35,35 +38,38 @@ def as_sql(self, compiler, connection, **extra_context): def get_queryset_valid_at_date(queryset, date): - filtered_qs = queryset.filter( - validity_to__gte=date, - validity_from__lte=date - ) + filtered_qs = queryset.filter(validity_to__gte=date, validity_from__lte=date) if len(filtered_qs) > 0: return filtered_qs return queryset.filter(validity_from__date__lte=date, validity_to__isnull=True) - def get_members(policy, family, user, members=None): if members: return members # get the current policy members - + # look in calculation rules if policy.contribution_plan: - instance = ContributionPlan.objects.filter( - uuid=str( - policy.contribution_plan - ) - ).first() if policy.contribution_plan else None + instance = ( + ContributionPlan.objects.filter(uuid=str(policy.contribution_plan)).first() + if policy.contribution_plan + else None + ) if instance: members = run_calculation_rules( - sender=instance.__class__.__name__, instance=instance, user=user, context="members", family=family + sender=instance.__class__.__name__, + instance=instance, + user=user, + context="members", + family=family, ) if not members and policy.insuree_policies: - members = [ip.insuree for ip in policy.insuree_policies.filter(validity_to__isnull=True)] + members = [ + ip.insuree + for ip in policy.insuree_policies.filter(validity_to__isnull=True) + ] # fallback on family if not members: members = family.members.filter(validity_to__isnull=True).all() - return members \ No newline at end of file + return members diff --git a/policy/validations.py b/policy/validations.py index 4623d01..3decae4 100644 --- a/policy/validations.py +++ b/policy/validations.py @@ -4,14 +4,19 @@ def validate_idle_policy(policy_input): errors = [] - policy_uuid = policy_input.get('uuid') + policy_uuid = policy_input.get("uuid") if policy_uuid: - policy = Policy.objects.filter(uuid=policy_uuid, validity_to__isnull=True).first() + policy = Policy.objects.filter( + uuid=policy_uuid, validity_to__isnull=True + ).first() if policy is None: - return [{ - 'message': _("policy.mutation.failed_to_update_policy"), - 'detail': _("policy.validation.id_does_not_exist") % {'id': policy_uuid} - }] + return [ + { + "message": _("policy.mutation.failed_to_update_policy"), + "detail": _("policy.validation.id_does_not_exist") + % {"id": policy_uuid}, + } + ] errors += check_can_update(policy) # TODO: check dates,... return errors @@ -19,8 +24,10 @@ def validate_idle_policy(policy_input): def check_can_update(policy): if policy.status != Policy.STATUS_IDLE: - return [{ - 'message': _("policy.mutation.failed_to_update_policy"), - 'detail': _("policy.mutation.policy_not_idle") - }] + return [ + { + "message": _("policy.mutation.failed_to_update_policy"), + "detail": _("policy.mutation.policy_not_idle"), + } + ] return [] diff --git a/policy/values.py b/policy/values.py index 1bc1a63..47561a4 100644 --- a/policy/values.py +++ b/policy/values.py @@ -1,10 +1,8 @@ from django.utils.translation import gettext as _ -from django.db.models import Q, Count import datetime as py_datetime from decimal import Decimal from .models import Policy from calculation.services import run_calculation_rules -from contract.models import ContractContributionPlanDetails as ContractContributionPlanDetailsModel from core.apps import CoreConfig from dateutil.relativedelta import relativedelta from contribution_plan.models import ContributionPlan @@ -15,24 +13,28 @@ def cycle_start(product, cycle, ref_date): c = getattr(product, "start_cycle_%s" % (cycle + 1), None) if not c: return None - if CoreConfig.secondary_calendar == 'Nepal': + if CoreConfig.secondary_calendar == "Nepal": import nepali_datetime - nepali_start = nepali_datetime.datetime.strptime("%s-%s" % (c, nepali_datetime.date.today().year), '%d-%m-%Y') + + nepali_start = nepali_datetime.datetime.strptime( + "%s-%s" % (c, nepali_datetime.date.today().year), "%d-%m-%Y" + ) start = nepali_start.to_datetime_date() else: - start = py_datetime.datetime.strptime("%s-%s" % (c, ref_date.year), '%d-%m-%Y') + start = py_datetime.datetime.strptime("%s-%s" % (c, ref_date.year), "%d-%m-%Y") if ref_date <= start: return start def set_start_date(policy): from core import datetime, datetimedelta + product = policy.product ref_enroll_date = policy.enroll_date if policy.stage == Policy.STAGE_NEW and product.administration_period: ref_enroll_date = ( - datetime.date.from_ad_date(ref_enroll_date) + - datetimedelta(months=product.administration_period) + datetime.date.from_ad_date(ref_enroll_date) + + datetimedelta(months=product.administration_period) ).to_ad_date() if not product.has_cycle(): @@ -45,51 +47,63 @@ def set_start_date(policy): elif policy.stage == Policy.STAGE_RENEWED and product.grace_period_renewal: grace = product.grace_period_renewal - ref_date = (datetime.date.from_ad_date(ref_enroll_date) - datetimedelta(months=grace)).to_ad_date() + ref_date = ( + datetime.date.from_ad_date(ref_enroll_date) - datetimedelta(months=grace) + ).to_ad_date() for i in range(4): start = cycle_start(product, i, ref_date) if start: policy.start_date = datetime.date.from_ad_date(start) return - policy.start_date = datetime.date.from_ad_date(py_datetime.datetime.strptime( - "%s-%s" % (product.start_cycle_1, ref_date.year + 1), - '%d-%m-%Y' - )) + policy.start_date = datetime.date.from_ad_date( + py_datetime.datetime.strptime( + "%s-%s" % (product.start_cycle_1, ref_date.year + 1), "%d-%m-%Y" + ) + ) def set_expiry_date(policy): product = policy.product from core import datetime, datetimedelta - insurance_period = datetimedelta( - months=product.insurance_period) if product.insurance_period % 12 != 0 else datetimedelta( - years=product.insurance_period // 12) + insurance_period = ( + datetimedelta(months=product.insurance_period) + if product.insurance_period % 12 != 0 + else datetimedelta(years=product.insurance_period // 12) + ) policy.expiry_date = ( - datetime.date.from_ad_date(policy.start_date) + - insurance_period - - datetimedelta(days=1) + datetime.date.from_ad_date(policy.start_date) + + insurance_period + - datetimedelta(days=1) ).to_ad_date() -def count_member(members, majority_date, not_related=False, older=True): - return len(list( - filter( - lambda person: (person.relationship != 7) ^ not_related and (person.dob >= majority_date) ^ older, members +def count_member(members, majority_date, not_related=False, older=True): + return len( + list( + filter( + lambda person: (person.relationship != 7) ^ not_related + and (person.dob >= majority_date) ^ older, + members, + ) ) - )) + ) def family_counts(product, members=None): extra_adults = 0 extra_children = 0 - total = 0 - date_threshold = py_datetime.date.today() - relativedelta(years=CoreConfig.age_of_majority) + date_threshold = py_datetime.date.today() - relativedelta( + years=CoreConfig.age_of_majority + ) adults = count_member(members, date_threshold) children = count_member(members, date_threshold, older=False) - other_children = count_member(members, date_threshold, not_related=True, older=False) + other_children = count_member( + members, date_threshold, not_related=True, older=False + ) other_adults = count_member(members, date_threshold, not_related=True) - + over_children = 0 over_adults = 0 over_other_children = 0 @@ -98,27 +112,44 @@ def family_counts(product, members=None): if product.max_members: over_adults = max(0, adults - product.max_members) over_children = max(0, adults + children - over_adults - product.max_members) - over_other_children = max(0, adults + children + other_children - over_adults - over_children- product.max_members) - over_other_adults = max(0, adults + other_adults + children + other_children - over_adults - over_other_children - over_children - product.max_members) - + over_other_children = max( + 0, + adults + + children + + other_children + - over_adults + - over_children + - product.max_members, + ) + over_other_adults = max( + 0, + adults + + other_adults + + children + + other_children + - over_adults + - over_other_children + - over_children + - product.max_members, + ) + # remove over from count children -= over_children adults -= over_adults other_children -= over_other_children other_adults -= over_other_adults - if product.threshold: - extra_adults = max(0, adults - product.threshold) - extra_children = max(0,children + adults - extra_adults - product.threshold) - + if product.threshold: + extra_adults = max(0, adults - product.threshold) + extra_children = max(0, children + adults - extra_adults - product.threshold) return { - "adults":adults -extra_adults, # adult part of the "lump sum" - "extra_adults": extra_adults, # adult not part of the "lump sum" because of threshold - "other_adults": other_adults , # adult never of the "lump sum" - "children": children-extra_children, # children part of the "lump sum" - "extra_children": extra_children, # children never part of the "lump sum" because of threshold - "other_children": other_children,# children never part of the "lump sum" - "total": adults + other_adults + children + other_children, + "adults": adults - extra_adults, # adult part of the "lump sum" + "extra_adults": extra_adults, # adult not part of the "lump sum" because of threshold + "other_adults": other_adults, # adult never of the "lump sum" + "children": children - extra_children, # children part of the "lump sum" + "extra_children": extra_children, # children never part of the "lump sum" because of threshold + "other_children": other_children, # children never part of the "lump sum" + "total": adults + other_adults + children + other_children, } @@ -130,8 +161,8 @@ def get_attr(product, attr): def sum_contributions(product, f_counts): contributions = 0 - premium_adult = get_attr(product, 'premium_adult') - premium_child = get_attr(product, 'premium_child') + premium_adult = get_attr(product, "premium_adult") + premium_child = get_attr(product, "premium_child") if product.lump_sum: contributions = product.lump_sum contributions += f_counts["extra_adults"] * premium_adult @@ -147,7 +178,7 @@ def sum_contributions(product, f_counts): def sum_general_assemblies(product, f_counts): if product.general_assembly_lump_sum: return product.general_assembly_lump_sum - return f_counts["total"] * get_attr(product, 'general_assembly_fee') + return f_counts["total"] * get_attr(product, "general_assembly_fee") def sum_registrations(policy, product, f_counts): @@ -155,15 +186,17 @@ def sum_registrations(policy, product, f_counts): return 0 if product.registration_lump_sum: return product.registration_lump_sum - return f_counts["total"] * get_attr(product, 'registration_fee') + return f_counts["total"] * get_attr(product, "registration_fee") def discount_new(policy): product = policy.product if product.has_enrolment_discount() and product.has_cycle(): from core import datetime, datetimedelta + min_discount_date = ( - datetime.date.from_ad_date(policy.start_date) - datetimedelta(months=product.enrolment_discount_period) + datetime.date.from_ad_date(policy.start_date) + - datetimedelta(months=product.enrolment_discount_period) ).to_ad_datetime() if policy.enroll_date <= min_discount_date: policy.value -= policy.value * product.enrolment_discount_perc / 100 @@ -173,10 +206,11 @@ def discount_renew(policy, prev_policy): product = policy.product if product.has_renewal_discount(): from core import datetime, datetimedelta + min_discount_date = ( - datetime.date.from_ad_date(prev_policy.expiry_date) + - datetimedelta(days=1) - - datetimedelta(months=product.renewal_discount_period) + datetime.date.from_ad_date(prev_policy.expiry_date) + + datetimedelta(days=1) + - datetimedelta(months=product.renewal_discount_period) ).to_ad_datetime() if policy.enroll_date <= min_discount_date: policy.value -= policy.value * product.renewal_discount_perc / 100 @@ -198,12 +232,22 @@ def set_value(policy, members, prev_policy, user): policy.value = Decimal(contributions + general_assembly + registration) discount(policy, prev_policy) # try to get policy value from calcrule - instance = ContributionPlan.objects.filter( - uuid=policy.contribution_plan - ).first() if policy.contribution_plan else None - policy_value = run_calculation_rules( - sender=instance.__class__.__name__, instance=instance, user=user, context="policy_value", family=family - ) if instance else None + instance = ( + ContributionPlan.objects.filter(uuid=policy.contribution_plan).first() + if policy.contribution_plan + else None + ) + policy_value = ( + run_calculation_rules( + sender=instance.__class__.__name__, + instance=instance, + user=user, + context="policy_value", + family=policy.family, + ) + if instance + else None + ) # to allow 0 as policy value if policy_value is not None: policy.value = Decimal(policy_value) @@ -225,7 +269,10 @@ def policy_values(policy, family, prev_policy, user, members=None): above_max = max(0, len(members or []) - max_members) warnings = [] if above_max: - warnings.append(_("policy.validation.members_count_above_max") % {'max': max_members, 'count': len(members)}) + warnings.append( + _("policy.validation.members_count_above_max") + % {"max": max_members, "count": len(members)} + ) set_start_date(policy) set_expiry_date(policy) set_value(policy, members, prev_policy, user) diff --git a/setup.py b/setup.py index f040777..868fecf 100644 --- a/setup.py +++ b/setup.py @@ -1,40 +1,40 @@ import os from setuptools import find_packages, setup -with open(os.path.join(os.path.dirname(__file__), 'README.md')) as readme: +with open(os.path.join(os.path.dirname(__file__), "README.md")) as readme: README = readme.read() # allow setup.py to be run from any path os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir))) setup( - name='openimis-be-policy', - version='1.3.0', + name="openimis-be-policy", + version="1.3.0", packages=find_packages(), include_package_data=True, - license='GNU AGPL v3', - description='The openIMIS Backend Policy reference module.', + license="GNU AGPL v3", + description="The openIMIS Backend Policy reference module.", # long_description=README, - url='https://openimis.org/', - author='Xavier Gillmann', - author_email='xgillmann@bluesquarehub.com', + url="https://openimis.org/", + author="Xavier Gillmann", + author_email="xgillmann@bluesquarehub.com", install_requires=[ - 'django', - 'django-db-signals', - 'djangorestframework', - 'openimis-be-claim', - 'openimis-be-insuree', - 'openimis-be-product', + "django", + "django-db-signals", + "djangorestframework", + "openimis-be-claim", + "openimis-be-insuree", + "openimis-be-product", ], classifiers=[ - 'Environment :: Web Environment', - 'Framework :: Django', - 'Framework :: Django :: 3.0', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: GNU Affero General Public License v3', - 'Operating System :: OS Independent', - 'Programming Language :: Python', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', + "Environment :: Web Environment", + "Framework :: Django", + "Framework :: Django :: 3.0", + "Intended Audience :: Developers", + "License :: OSI Approved :: GNU Affero General Public License v3", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", ], )