this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add Discount Configuration

+359 -70
+20 -7
care/emr/api/viewsets/facility.py
··· 13 13 14 14 from care.emr.api.viewsets.base import EMRModelReadOnlyViewSet, EMRModelViewSet 15 15 from care.emr.models import Organization, SchedulableResource 16 + from care.emr.models.facility_config import FacilityMonetoryConfig 16 17 from care.emr.models.organization import FacilityOrganizationUser, OrganizationUser 17 18 from care.emr.resources.facility.spec import ( 18 19 FacilityCreateSpec, ··· 139 140 request=FacilityMonetaryCodeSpec, 140 141 ) 141 142 @action(methods=["POST"], detail=True) 142 - def set_monetary_codes(self, request, *args, **kwargs): 143 + def set_monetary_config(self, request, *args, **kwargs): 143 144 instance = self.get_object() 144 145 self.authorize_update({}, instance) 146 + facility_monetory_config = FacilityMonetoryConfig.get_monetory_config( 147 + instance.id 148 + ) 145 149 serializer_obj = FacilityMonetaryCodeSpec.model_validate( 146 150 request.data, 147 151 context={ 148 152 "is_update": True, 149 - "object": instance, 153 + "object": facility_monetory_config, 150 154 **self.get_serializer_update_context(), 151 155 }, 152 156 ) 153 - model_instance = serializer_obj.de_serialize(obj=instance) 154 - self.perform_update(model_instance) 157 + model_instance = serializer_obj.de_serialize(obj=facility_monetory_config) 158 + model_instance.updated_by = request.user 159 + model_instance.save() 160 + instance = self.get_object() 155 161 return Response( 156 - self.get_retrieve_pydantic_model().serialize(model_instance).to_json() 162 + self.get_retrieve_pydantic_model().serialize(instance).to_json() 157 163 ) 158 164 159 165 @extend_schema( ··· 163 169 def set_invoice_expression(self, request, *args, **kwargs): 164 170 instance = self.get_object() 165 171 self.authorize_update({}, instance) 172 + facility_monetory_config = FacilityMonetoryConfig.get_monetory_config( 173 + instance.id 174 + ) 166 175 request_params = FacilityInvoiceExpressionSpec(**request.data) 167 - instance.invoice_number_expression = request_params.invoice_number_expression 168 - self.perform_update(instance) 176 + facility_monetory_config.invoice_number_expression = ( 177 + request_params.invoice_number_expression 178 + ) 179 + facility_monetory_config.updated_by = request.user 180 + facility_monetory_config.save() 181 + instance = self.get_object() 169 182 return Response( 170 183 self.get_retrieve_pydantic_model().serialize(instance).to_json() 171 184 )
+51
care/emr/migrations/0074_facilitymonetoryconfig.py
··· 1 + # Generated by Django 6.0 on 2026-03-01 15:23 2 + 3 + import django.db.models.deletion 4 + import uuid 5 + from django.conf import settings 6 + from django.db import migrations, models 7 + 8 + 9 + def migrate_facility_monetory_config(apps, schema_editor): 10 + Facility = apps.get_model('facility', 'Facility') 11 + FacilityMonetoryConfig = apps.get_model('emr', 'FacilityMonetoryConfig') 12 + for facility in Facility.objects.all(): 13 + FacilityMonetoryConfig.objects.create( 14 + facility=facility, 15 + discount_codes=facility.discount_codes, 16 + discount_monetary_components=facility.discount_monetary_components, 17 + invoice_number_expression=facility.invoice_number_expression, 18 + ) 19 + 20 + class Migration(migrations.Migration): 21 + 22 + dependencies = [ 23 + ('emr', '0073_product_purchase_price_and_more'), 24 + ('facility', '0483_remove_facility_corona_testing_and_more'), 25 + migrations.swappable_dependency(settings.AUTH_USER_MODEL), 26 + ] 27 + 28 + operations = [ 29 + migrations.CreateModel( 30 + name='FacilityMonetoryConfig', 31 + fields=[ 32 + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 33 + ('external_id', models.UUIDField(db_index=True, default=uuid.uuid4, unique=True)), 34 + ('created_date', models.DateTimeField(auto_now_add=True, db_index=True, null=True)), 35 + ('modified_date', models.DateTimeField(auto_now=True, db_index=True, null=True)), 36 + ('deleted', models.BooleanField(db_index=True, default=False)), 37 + ('history', models.JSONField(default=dict)), 38 + ('meta', models.JSONField(default=dict)), 39 + ('discount_codes', models.JSONField(default=list)), 40 + ('discount_monetary_components', models.JSONField(default=list)), 41 + ('invoice_number_expression', models.CharField(blank=True, default=None, max_length=1000, null=True)), 42 + ('created_by', models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(app_label)s_%(class)s_created_by', to=settings.AUTH_USER_MODEL)), 43 + ('facility', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='facility.facility', unique=True)), 44 + ('updated_by', models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(app_label)s_%(class)s_updated_by', to=settings.AUTH_USER_MODEL)), 45 + ], 46 + options={ 47 + 'abstract': False, 48 + }, 49 + ), 50 + migrations.RunPython(migrate_facility_monetory_config, reverse_code=migrations.RunPython.noop), 51 + ]
+35
care/emr/migrations/0075_chargeitem_discount_configuration_and_more.py
··· 1 + # Generated by Django 6.0 on 2026-03-01 21:08 2 + 3 + import django.db.models.deletion 4 + from django.db import migrations, models 5 + 6 + 7 + class Migration(migrations.Migration): 8 + 9 + dependencies = [ 10 + ('emr', '0074_facilitymonetoryconfig'), 11 + ('facility', '0484_remove_facility_discount_codes_and_more'), 12 + ] 13 + 14 + operations = [ 15 + migrations.AddField( 16 + model_name='chargeitem', 17 + name='discount_configuration', 18 + field=models.JSONField(blank=True, default=None, null=True), 19 + ), 20 + migrations.AddField( 21 + model_name='chargeitemdefinition', 22 + name='discount_configuration', 23 + field=models.JSONField(blank=True, default=None, null=True), 24 + ), 25 + migrations.AddField( 26 + model_name='facilitymonetoryconfig', 27 + name='discount_configuration', 28 + field=models.JSONField(blank=True, default=dict, null=True), 29 + ), 30 + migrations.AlterField( 31 + model_name='facilitymonetoryconfig', 32 + name='facility', 33 + field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='facility.facility'), 34 + ), 35 + ]
+1
care/emr/models/__init__.py
··· 7 7 from .device import * # noqa F403 8 8 from .diagnostic_report import * # noqa F403 9 9 from .encounter import * # noqa F403 10 + from .facility_config import * # noqa F003 10 11 from .file_upload import * # noqa F403 11 12 from .healthcare_service import * # noqa F403 12 13 from .inventory_item import * # noqa F403
+1
care/emr/models/charge_item.py
··· 53 53 blank=True, 54 54 default=None, 55 55 ) 56 + discount_configuration = models.JSONField(null=True, blank=True, default=None)
+1
care/emr/models/charge_item_definition.py
··· 25 25 null=True, 26 26 blank=True, 27 27 ) 28 + discount_configuration = models.JSONField(null=True, blank=True, default=None)
+78
care/emr/models/facility_config.py
··· 1 + from django.core.cache import cache 2 + from django.db import models 3 + 4 + from care.emr.models import EMRBaseModel 5 + 6 + 7 + class FacilityMonetoryConfig(EMRBaseModel): 8 + facility = models.OneToOneField( 9 + "facility.Facility", on_delete=models.CASCADE, unique=True 10 + ) 11 + discount_codes = models.JSONField(default=list) 12 + discount_monetary_components = models.JSONField(default=list) 13 + discount_configuration = models.JSONField(default=dict, null=True, blank=True) 14 + invoice_number_expression = models.CharField( 15 + max_length=1000, blank=True, null=True, default=None 16 + ) 17 + 18 + @classmethod 19 + def get_monetory_config(cls, facility_id): 20 + obj = cls.objects.filter(facility_id=facility_id).first() 21 + if not obj: 22 + obj = cls.objects.create(facility_id=facility_id) 23 + return obj 24 + 25 + @classmethod 26 + def get_component_key(cls, component): 27 + return ( 28 + component.get("code", {}).get("system", "") 29 + + "/" 30 + + component.get("code", {}).get("code", "") 31 + ) 32 + 33 + @classmethod 34 + def get_monetory_component_cache_key(cls, facility_id): 35 + return f"facility:{facility_id}:monetory_component" 36 + 37 + @classmethod 38 + def get_discount_configuration_cache_key(cls, facility_id): 39 + return f"facility:{facility_id}:discount_configuration" 40 + 41 + @classmethod 42 + def calculate_monetory_components(cls, components): 43 + component_cache = {} 44 + for component in components: 45 + component_cache[cls.get_component_key(component)] = component 46 + return component_cache 47 + 48 + @classmethod 49 + def get_monetory_component(cls, facility_id): 50 + cached_data = cache.get(cls.get_monetory_component_cache_key(facility_id)) 51 + if cached_data: 52 + return cached_data 53 + facility = cls.get_monetory_config(facility_id) 54 + monetory_component = cls.calculate_monetory_components( 55 + facility.discount_monetary_components 56 + ) 57 + cache.set(cls.get_monetory_component_cache_key(facility_id), monetory_component) 58 + return monetory_component 59 + 60 + @classmethod 61 + def get_discount_configuration(cls, facility_id): 62 + cached_data = cache.get(cls.get_discount_configuration_cache_key(facility_id)) 63 + if cached_data: 64 + return cached_data 65 + facility = cls.get_monetory_config(facility_id) 66 + discount_configuration = facility.discount_configuration 67 + if not discount_configuration: 68 + discount_configuration = {} 69 + cache.set( 70 + cls.get_discount_configuration_cache_key(facility_id), 71 + discount_configuration, 72 + ) 73 + return discount_configuration 74 + 75 + def save(self, *args, **kwargs): 76 + cache.delete(self.get_monetory_component_cache_key(self.facility_id)) 77 + cache.delete(self.get_discount_configuration_cache_key(self.facility_id)) 78 + super().save(*args, **kwargs)
+13 -3
care/emr/resources/charge_item/apply_charge_item_definition.py
··· 1 1 from care.emr.models.charge_item import ChargeItem 2 + from care.emr.models.facility_config import FacilityMonetoryConfig 2 3 from care.emr.models.resource_category import merge_monetary_components 3 4 from care.emr.resources.account.default_account import get_default_account 4 5 from care.emr.resources.charge_item.spec import ChargeItemStatusOptions 5 6 from care.emr.resources.charge_item.sync_charge_item_costs import sync_charge_item_costs 6 - from care.facility.models.facility import Facility 7 7 from care.utils.evaluators.interpretation_evaluator import InterpretationEvaluator 8 8 from care.utils.rounding.covert_type import convert_to_decimal 9 9 ··· 17 17 18 18 def compute_global_components(charge_item_definition, price_components): 19 19 facility = charge_item_definition.facility 20 - components_override = Facility.get_monetory_component(facility.id) 20 + components_override = FacilityMonetoryConfig.get_monetory_component(facility.id) 21 21 price_components_new = [] 22 22 for component in price_components: 23 23 if component.get("global_component", None): 24 - component_key = Facility.get_component_key(component) 24 + component_key = FacilityMonetoryConfig.get_component_key(component) 25 25 if component_key in components_override: 26 26 price_components_new.append(components_override[component_key]) 27 27 else: 28 28 price_components_new.append(component) 29 29 return price_components_new 30 + 31 + 32 + def compute_discount_configuration(charge_item_definition): 33 + discount_configuration = charge_item_definition.discount_configuration 34 + if discount_configuration: 35 + return discount_configuration 36 + return FacilityMonetoryConfig.get_discount_configuration( 37 + charge_item_definition.facility_id 38 + ) 30 39 31 40 32 41 def apply_charge_item_definition( ··· 81 90 status=ChargeItemStatusOptions.billable.value, 82 91 quantity=quantity, 83 92 unit_price_components=selected_components, 93 + discount_configuration=compute_discount_configuration(charge_item_definition), 84 94 ) 85 95 sync_charge_item_costs(charge_item, reverse=reverse or negative_allowed) 86 96 return charge_item
+1
care/emr/resources/charge_item/spec.py
··· 159 159 performer_actor: dict | None = None 160 160 created_by: dict | None = None 161 161 updated_by: dict | None = None 162 + discount_configuration: dict | None = None 162 163 163 164 @classmethod 164 165 def perform_extra_serialization(cls, mapping, obj):
+26 -2
care/emr/resources/charge_item/sync_charge_item_costs.py
··· 20 20 raise ValidationError("Amount or factor is required") 21 21 22 22 23 + def apply_discount_configuration(discount_components, discount_configuration): 24 + if discount_configuration: 25 + max_applicable = discount_configuration.get("max_applicable", 0) 26 + applicability_order = discount_configuration.get( 27 + "applicability_order", "total_asc" 28 + ) 29 + if applicability_order == "total_asc": 30 + discount_components.sort(key=lambda x: convert_to_decimal(x["amount"])) 31 + elif applicability_order == "total_desc": 32 + discount_components.sort( 33 + key=lambda x: convert_to_decimal(x["amount"]), reverse=True 34 + ) 35 + if max_applicable == 0: 36 + return [] 37 + discount_components = discount_components[:max_applicable] 38 + return discount_components 39 + 40 + 23 41 def sync_charge_item_costs(charge_item, reverse=None): 24 42 """ 25 43 Calculate total cost of charge item based on quantity and other factors ··· 43 61 total_price += _component.amount 44 62 components.append(_component.model_dump(mode="json", exclude_defaults=True)) 45 63 net_price = total_price 64 + discounts = [] 46 65 for component in charge_item_price_components: 47 66 if component.monetary_component_type == MonetaryComponentType.discount.value: 48 67 _component = calculate_amount(component, quantity, net_price) 49 - total_price -= _component.amount 50 - components.append(_component.model_dump(mode="json", exclude_defaults=True)) 68 + # total_price -= _component.amount 69 + discounts.append(_component.model_dump(mode="json", exclude_defaults=True)) 70 + discount_configuration = charge_item.discount_configuration 71 + discounts = apply_discount_configuration(discounts, discount_configuration) 72 + for discount in discounts: 73 + total_price -= convert_to_decimal(discount["amount"]) 74 + components.append(discount) 51 75 taxable_price = total_price 52 76 for component in charge_item_price_components: 53 77 if component.monetary_component_type == MonetaryComponentType.tax.value:
+5 -1
care/emr/resources/charge_item_definition/spec.py
··· 6 6 from care.emr.models.charge_item_definition import ChargeItemDefinition 7 7 from care.emr.models.resource_category import ResourceCategory 8 8 from care.emr.resources.base import EMRResource 9 - from care.emr.resources.common.monetary_component import MonetaryComponent 9 + from care.emr.resources.common.monetary_component import ( 10 + DiscountConfiguration, 11 + MonetaryComponent, 12 + ) 10 13 from care.emr.resources.resource_category.spec import ResourceCategoryReadSpec 11 14 from care.emr.tagging.base import SingleFacilityTagManager 12 15 from care.emr.utils.slug_type import ExtendedSlugType, SlugType ··· 32 35 purpose: str | None = None 33 36 price_components: list[MonetaryComponent] 34 37 can_edit_charge_item: bool 38 + discount_configuration: DiscountConfiguration | None 35 39 36 40 37 41 class ChargeItemDefinitionWriteSpec(ChargeItemDefinitionSpec):
+10
care/emr/resources/common/monetary_component.py
··· 139 139 if self.monetary_component_type == MonetaryComponentType.base.value: 140 140 raise ValueError("Base component is not allowed in definition.") 141 141 return self 142 + 143 + 144 + class DiscountApplicability(str, Enum): 145 + total_asc = "total_asc" 146 + total_desc = "total_desc" 147 + 148 + 149 + class DiscountConfiguration(BaseModel): 150 + max_applicable: int = Field(ge=0) 151 + applicability_order: DiscountApplicability
+25 -9
care/emr/resources/facility/spec.py
··· 7 7 from pydantic_extra_types.coordinate import Latitude, Longitude 8 8 9 9 from care.emr.models import Organization 10 + from care.emr.models.facility_config import FacilityMonetoryConfig 10 11 from care.emr.models.patient import PatientIdentifierConfigCache 11 12 from care.emr.resources.base import EMRResource, cacheable, model_from_cache 12 13 from care.emr.resources.common.coding import Coding 13 - from care.emr.resources.common.monetary_component import MonetaryComponentDefinition 14 + from care.emr.resources.common.monetary_component import ( 15 + DiscountConfiguration, 16 + MonetaryComponentDefinition, 17 + ) 14 18 from care.emr.resources.invoice.default_expression_evaluator import ( 15 19 evaluate_invoice_dummy_expression, 16 20 ) ··· 159 163 read_cover_image_url: str 160 164 geo_organization: dict = {} 161 165 created_by: dict = {} 162 - invoice_number_expression: str | None = None 163 166 164 167 @classmethod 165 168 def perform_extra_serialization(cls, mapping, obj): ··· 180 183 flags: list[str] = [] 181 184 discount_codes: list[dict] = [] 182 185 discount_monetary_components: list[dict] = [] 186 + discount_configuration: dict | None = None 187 + 183 188 instance_discount_codes: list[dict] = [] 184 189 instance_discount_monetary_components: list[dict] = [] 185 190 instance_tax_codes: list[dict] = [] ··· 188 193 # Identifiers 189 194 patient_instance_identifier_configs: list[dict] = [] 190 195 patient_facility_identifier_configs: list[dict] = [] 191 - 192 - # Product 193 - extensions_schema_product: dict = {} 194 - extensions_schema_supply_delivery: dict = {} 195 - extensions_schema_supply_delivery_order: dict = {} 196 - extensions_schema_account: dict = {} 196 + invoice_number_expression: str | None = None 197 197 198 198 print_templates: list[dict] = [] 199 199 200 200 @classmethod 201 201 def perform_extra_serialization(cls, mapping, obj): 202 + from care.emr.models.facility_config import FacilityMonetoryConfig 203 + 202 204 super().perform_extra_serialization(mapping, obj) 205 + facility_monetory_config = FacilityMonetoryConfig.get_monetory_config(obj.id) 206 + 207 + mapping["invoice_number_expression"] = ( 208 + facility_monetory_config.invoice_number_expression 209 + ) 210 + mapping["discount_codes"] = facility_monetory_config.discount_codes 211 + mapping["discount_monetary_components"] = ( 212 + facility_monetory_config.discount_monetary_components 213 + ) 214 + mapping["discount_configuration"] = ( 215 + facility_monetory_config.discount_configuration 216 + ) 217 + 203 218 mapping["flags"] = obj.get_facility_flags() 204 219 mapping["instance_discount_codes"] = settings.DISCOUNT_CODES 205 220 mapping["instance_discount_monetary_components"] = ( ··· 219 234 220 235 221 236 class FacilityMonetaryCodeSpec(EMRResource): 222 - __model__ = Facility 237 + __model__ = FacilityMonetoryConfig 223 238 __exclude__ = [] 224 239 225 240 discount_codes: list[Coding] 226 241 discount_monetary_components: list[MonetaryComponentDefinition] 242 + discount_configuration: DiscountConfiguration | None 227 243 228 244 @model_validator(mode="after") 229 245 def validate_count(self):
+6 -2
care/emr/resources/invoice/default_expression_evaluator.py
··· 1 + from care.emr.models.facility_config import FacilityMonetoryConfig 1 2 from care.emr.models.invoice import Invoice 2 3 from care.emr.utils.expression_evaluator import evaluate_expression 3 4 from care.utils.time_util import care_now ··· 11 12 "current_year_yyyy": care_now().year, 12 13 "current_year_yy": care_now().year % 100, 13 14 } 14 - if not facility.invoice_number_expression: 15 + expression = FacilityMonetoryConfig.get_monetory_config( 16 + facility.id 17 + ).invoice_number_expression 18 + if not expression: 15 19 return "" 16 - return evaluate_expression(facility.invoice_number_expression, context) 20 + return evaluate_expression(expression, context) 17 21 18 22 19 23 def evaluate_invoice_dummy_expression(expression):
+5
care/emr/tests/test_charge_item_definition_api.py
··· 66 66 }, 67 67 } 68 68 ], 69 + "discount_configuration": None, 69 70 } 70 71 data.update(**kwargs) 71 72 return data ··· 453 454 "slug_value": "test-def", 454 455 "price_components": [self.get_valid_monetary_component()], 455 456 "can_edit_charge_item": True, 457 + "discount_configuration": None, 456 458 } 457 459 spec = ChargeItemDefinitionWriteSpec(**valid_data) 458 460 self.assertEqual(spec.title, "Test Definition") ··· 464 466 title="Test Definition", 465 467 slug_value="test-def", 466 468 price_components=[], 469 + discount_configuration=None, 467 470 ) 468 471 469 472 def test_charge_item_definition_spec_duplicate_price_components(self): ··· 523 526 "slug_value": f"test-def-{status_option.value}", 524 527 "price_components": [], 525 528 "can_edit_charge_item": True, 529 + "discount_configuration": None, 526 530 } 527 531 spec = ChargeItemDefinitionWriteSpec(**spec_data) 528 532 self.assertEqual(spec.status, status_option.value) ··· 600 604 }, 601 605 } 602 606 ], 607 + "discount_configuration": None, 603 608 } 604 609 data.update(**kwargs) 605 610 return data
+54
care/facility/migrations/0483_remove_facility_corona_testing_and_more.py
··· 1 + # Generated by Django 6.0 on 2026-03-01 15:23 2 + 3 + from django.db import migrations, models 4 + 5 + 6 + class Migration(migrations.Migration): 7 + 8 + dependencies = [ 9 + ('facility', '0482_facility_print_templates'), 10 + ] 11 + 12 + operations = [ 13 + migrations.RemoveField( 14 + model_name='facility', 15 + name='corona_testing', 16 + ), 17 + migrations.RemoveField( 18 + model_name='facility', 19 + name='expected_oxygen_requirement', 20 + ), 21 + migrations.RemoveField( 22 + model_name='facility', 23 + name='expected_type_b_cylinders', 24 + ), 25 + migrations.RemoveField( 26 + model_name='facility', 27 + name='expected_type_c_cylinders', 28 + ), 29 + migrations.RemoveField( 30 + model_name='facility', 31 + name='expected_type_d_cylinders', 32 + ), 33 + migrations.RemoveField( 34 + model_name='facility', 35 + name='oxygen_capacity', 36 + ), 37 + migrations.RemoveField( 38 + model_name='facility', 39 + name='type_b_cylinders', 40 + ), 41 + migrations.RemoveField( 42 + model_name='facility', 43 + name='type_c_cylinders', 44 + ), 45 + migrations.RemoveField( 46 + model_name='facility', 47 + name='type_d_cylinders', 48 + ), 49 + migrations.AlterField( 50 + model_name='facility', 51 + name='print_templates', 52 + field=models.JSONField(default=list), 53 + ), 54 + ]
+26
care/facility/migrations/0484_remove_facility_discount_codes_and_more.py
··· 1 + # Generated by Django 6.0 on 2026-03-01 18:28 2 + 3 + from django.db import migrations 4 + 5 + 6 + class Migration(migrations.Migration): 7 + 8 + dependencies = [ 9 + ('facility', '0483_remove_facility_corona_testing_and_more'), 10 + ('emr', '0074_facilitymonetoryconfig'), 11 + ] 12 + 13 + operations = [ 14 + migrations.RemoveField( 15 + model_name='facility', 16 + name='discount_codes', 17 + ), 18 + migrations.RemoveField( 19 + model_name='facility', 20 + name='discount_monetary_components', 21 + ), 22 + migrations.RemoveField( 23 + model_name='facility', 24 + name='invoice_number_expression', 25 + ), 26 + ]
+1 -46
care/facility/models/facility.py
··· 184 184 ) 185 185 internal_organization_cache = ArrayField(models.IntegerField(), default=list) 186 186 187 - oxygen_capacity = models.IntegerField(default=0) 188 - type_b_cylinders = models.IntegerField(default=0) 189 - type_c_cylinders = models.IntegerField(default=0) 190 - type_d_cylinders = models.IntegerField(default=0) 191 - 192 - expected_oxygen_requirement = models.IntegerField(default=0) 193 - expected_type_b_cylinders = models.IntegerField(default=0) 194 - expected_type_c_cylinders = models.IntegerField(default=0) 195 - expected_type_d_cylinders = models.IntegerField(default=0) 196 - 197 187 phone_number = models.CharField( 198 188 max_length=14, blank=True, validators=[mobile_or_landline_number_validator] 199 189 ) 200 - corona_testing = models.BooleanField(default=False) 201 190 created_by = models.ForeignKey( 202 191 User, on_delete=models.SET_NULL, null=True, blank=True 203 192 ) ··· 209 198 210 199 is_public = models.BooleanField(default=False) 211 200 212 - discount_codes = models.JSONField(default=list) 213 - discount_monetary_components = models.JSONField(default=list) 214 - 215 - invoice_number_expression = models.CharField( 216 - max_length=1000, blank=True, null=True, default=None 217 - ) 218 - 219 - print_templates = models.JSONField(default=dict) 201 + print_templates = models.JSONField(default=list) 220 202 221 203 class Meta: 222 204 verbose_name_plural = "Facilities" 223 205 224 - @classmethod 225 - def get_component_key(cls, component): 226 - return component.get("code" , {}).get("system" , "") + "/" + component.get("code" , {}).get("code","") 227 - 228 - @classmethod 229 - def get_monetory_component_cache_key(cls , facility_id): 230 - return f"facility:{facility_id}:monetory_component" 231 - 232 - 233 - @classmethod 234 - def calculate_monetory_components(cls , components): 235 - component_cache = {} 236 - for component in components: 237 - component_cache[cls.get_component_key(component)] = component 238 - return component_cache 239 - 240 - @classmethod 241 - def get_monetory_component(cls , facility_id): 242 - cached_data = cache.get(cls.get_monetory_component_cache_key(facility_id)) 243 - if cached_data: 244 - return cached_data 245 - else: 246 - facility = cls.objects.get(id=facility_id) 247 - monetory_component = cls.calculate_monetory_components(facility.discount_monetary_components) 248 - cache.set(cls.get_monetory_component_cache_key(facility_id), monetory_component) 249 - return monetory_component 250 206 251 207 def read_cover_image_url(self): 252 208 if self.cover_image_url: ··· 282 238 283 239 def save(self, *args, **kwargs) -> None: 284 240 is_create = self.pk is None 285 - cache.delete(self.get_monetory_component_cache_key(self.id)) 286 241 super().save(*args, **kwargs) 287 242 288 243 if is_create: