Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions care/emr/api/viewsets/charge_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,13 @@ def apply_charge_item_defs(self, request, *args, **kwargs):
raise ValidationError(
"Charge item definition is not associated with the facility"
)
if (
not charge_item_definition.separately_billable
and not charge_item_request.service_resource
):
raise ValidationError(
"Charge item definition cannot be applied manually. It must be linked to a resource."
)
patient = None
encounter = None
if charge_item_request.encounter:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 6.0 on 2026-01-22 13:44

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('emr', '0060_chargeitemdefinition_tags_and_more'),
]

operations = [
migrations.AddField(
model_name='chargeitemdefinition',
name='separately_billable',
field=models.BooleanField(default=True),
),
]
31 changes: 31 additions & 0 deletions care/emr/migrations/0062_sync_seprately_billable_values.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from django.db import migrations


def sync_separately_billable(apps, schema_editor):
ChargeItemDefinition = apps.get_model("emr", "ChargeItemDefinition")
Product = apps.get_model("emr", "Product")
Schedule = apps.get_model("emr", "Schedule")
ActivityDefinition = apps.get_model("emr", "ActivityDefinition")

for charge_item_def in ChargeItemDefinition.objects.all():
is_linked = (
Product.objects.filter(charge_item_definition_id=charge_item_def.id).exists()
or Schedule.objects.filter(charge_item_definition_id=charge_item_def.id).exists()
or Schedule.objects.filter(revisit_charge_item_definition_id=charge_item_def.id).exists()
or ActivityDefinition.objects.filter(charge_item_definitions__contains=[charge_item_def.id]).exists()
)

if is_linked and charge_item_def.separately_billable:
charge_item_def.separately_billable = False
charge_item_def.save(update_fields=["separately_billable"])


class Migration(migrations.Migration):

dependencies = [
("emr", "0061_chargeitemdefinition_separately_billable"),
]

operations = [
migrations.RunPython(sync_separately_billable),
]
1 change: 1 addition & 0 deletions care/emr/models/charge_item_definition.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ class ChargeItemDefinition(SlugBaseModel):
null=True,
blank=True,
)
separately_billable = models.BooleanField(default=True)
1 change: 1 addition & 0 deletions care/emr/resources/charge_item_definition/spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class ChargeItemDefinitionSpec(EMRResource):
description: str | None = None
purpose: str | None = None
price_components: list[MonetaryComponent]
separately_billable: bool = True


class ChargeItemDefinitionWriteSpec(ChargeItemDefinitionSpec):
Expand Down
1 change: 1 addition & 0 deletions care/emr/signals/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from .charge_item_def import * # noqa F403
from .patient import * # noqa F403
3 changes: 3 additions & 0 deletions care/emr/signals/charge_item_def/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .activity_definition import * # noqa F403
from .product import * # noqa F403
from .schedule import * # noqa F403
49 changes: 49 additions & 0 deletions care/emr/signals/charge_item_def/activity_definition.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver

from care.emr.models.activity_definition import ActivityDefinition
from care.emr.utils.charge_item_definition import (
recalculate_separately_billable,
set_separately_billable_false,
)

_OLD_CHARGE_ITEM_DEFS_IDS = "_old_charge_item_definitions"
_OLD_DELETED = "_old_deleted"


@receiver(pre_save, sender=ActivityDefinition)
def capture_old_charge_item_defs_activity(sender, instance, **kwargs):
if instance.id:
old_instance = ActivityDefinition.objects.get(id=instance.id)
setattr(
instance,
_OLD_CHARGE_ITEM_DEFS_IDS,
list(old_instance.charge_item_definitions or []),
)
setattr(instance, _OLD_DELETED, old_instance.deleted)
else:
setattr(instance, _OLD_CHARGE_ITEM_DEFS_IDS, [])
setattr(instance, _OLD_DELETED, False)


@receiver(post_save, sender=ActivityDefinition)
def update_separately_billable_activity(sender, instance, created, **kwargs):
old_ids = set(getattr(instance, _OLD_CHARGE_ITEM_DEFS_IDS, []) or [])
new_ids = set(instance.charge_item_definitions or [])
old_deleted = getattr(instance, _OLD_DELETED, False)

is_soft_deleted = not old_deleted and instance.deleted

if is_soft_deleted:
for charge_item_def_id in new_ids:
recalculate_separately_billable(charge_item_def_id)
return

added_ids = new_ids - old_ids
for charge_item_def_id in added_ids:
if not instance.deleted:
set_separately_billable_false(charge_item_def_id)

removed_ids = old_ids - new_ids
for charge_item_def_id in removed_ids:
recalculate_separately_billable(charge_item_def_id)
Comment thread
praffq marked this conversation as resolved.
43 changes: 43 additions & 0 deletions care/emr/signals/charge_item_def/product.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver

from care.emr.models.product import Product
from care.emr.utils.charge_item_definition import (
recalculate_separately_billable,
set_separately_billable_false,
)

_OLD_CHARGE_ITEM_DEF_ID = "_old_charge_item_definition_id"
_OLD_DELETED = "_old_deleted"


@receiver(pre_save, sender=Product)
def capture_old_charge_item_def_product(sender, instance, **kwargs):
if instance.id:
old_instance = Product.objects.get(id=instance.id)
setattr(
instance, _OLD_CHARGE_ITEM_DEF_ID, old_instance.charge_item_definition_id
)
setattr(instance, _OLD_DELETED, old_instance.deleted)
else:
setattr(instance, _OLD_CHARGE_ITEM_DEF_ID, None)
setattr(instance, _OLD_DELETED, False)


@receiver(post_save, sender=Product)
def update_separately_billable_product(sender, instance, created, **kwargs):
old_charge_item_def_id = getattr(instance, _OLD_CHARGE_ITEM_DEF_ID, None)
new_charge_item_def_id = instance.charge_item_definition_id
old_deleted = getattr(instance, _OLD_DELETED, False)

is_soft_deleted = not old_deleted and instance.deleted

if is_soft_deleted and new_charge_item_def_id:
recalculate_separately_billable(new_charge_item_def_id)
return

if old_charge_item_def_id and old_charge_item_def_id != new_charge_item_def_id:
recalculate_separately_billable(old_charge_item_def_id)

if new_charge_item_def_id and not instance.deleted:
set_separately_billable_false(new_charge_item_def_id)
Comment thread
praffq marked this conversation as resolved.
61 changes: 61 additions & 0 deletions care/emr/signals/charge_item_def/schedule.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver

from care.emr.models.scheduling.schedule import Schedule
from care.emr.utils.charge_item_definition import (
recalculate_separately_billable,
set_separately_billable_false,
)

_OLD_CHARGE_ITEM_DEF_ID = "_old_charge_item_definition_id"
_OLD_REVISIT_CHARGE_ITEM_DEF_ID = "_old_revisit_charge_item_definition_id"
_OLD_DELETED = "_old_deleted"


@receiver(pre_save, sender=Schedule)
def capture_old_charge_item_def_schedule(sender, instance, **kwargs):
if instance.id:
old_instance = Schedule.objects.get(id=instance.id)
setattr(
instance, _OLD_CHARGE_ITEM_DEF_ID, old_instance.charge_item_definition_id
)
setattr(
instance,
_OLD_REVISIT_CHARGE_ITEM_DEF_ID,
old_instance.revisit_charge_item_definition_id,
)
setattr(instance, _OLD_DELETED, old_instance.deleted)
else:
setattr(instance, _OLD_CHARGE_ITEM_DEF_ID, None)
setattr(instance, _OLD_REVISIT_CHARGE_ITEM_DEF_ID, None)
setattr(instance, _OLD_DELETED, False)


@receiver(post_save, sender=Schedule)
def update_separately_billable_schedule(sender, instance, created, **kwargs):
old_charge_item_def_id = getattr(instance, _OLD_CHARGE_ITEM_DEF_ID, None)
new_charge_item_def_id = instance.charge_item_definition_id
old_revisit_id = getattr(instance, _OLD_REVISIT_CHARGE_ITEM_DEF_ID, None)
new_revisit_id = instance.revisit_charge_item_definition_id
old_deleted = getattr(instance, _OLD_DELETED, False)

is_soft_deleted = not old_deleted and instance.deleted

if is_soft_deleted:
if new_charge_item_def_id:
recalculate_separately_billable(new_charge_item_def_id)
if new_revisit_id:
recalculate_separately_billable(new_revisit_id)
return

if old_charge_item_def_id and old_charge_item_def_id != new_charge_item_def_id:
recalculate_separately_billable(old_charge_item_def_id)

if new_charge_item_def_id and not instance.deleted:
set_separately_billable_false(new_charge_item_def_id)

if old_revisit_id and old_revisit_id != new_revisit_id:
recalculate_separately_billable(old_revisit_id)

if new_revisit_id and not instance.deleted:
set_separately_billable_false(new_revisit_id)
Comment thread
praffq marked this conversation as resolved.
Loading