Skip to content
Open
Show file tree
Hide file tree
Changes from all 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 @@ -324,6 +324,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 5.1.4 on 2026-01-27 07:48

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('emr', '0065_account_primary_encounter'),
]

operations = [
migrations.AddField(
model_name='chargeitemdefinition',
name='separately_billable',
field=models.BooleanField(default=True),
),
]
33 changes: 33 additions & 0 deletions care/emr/migrations/0067_sync_separately_billable_values.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Generated by Django 5.1.4 on 2026-01-27 07:48

from django.db import migrations
Comment thread
praffq marked this conversation as resolved.


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', '0066_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