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
9 changes: 9 additions & 0 deletions care/emr/api/viewsets/patient.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,15 @@ def perform_create(self, instance):
def perform_update(self, instance):
identifiers = instance._identifiers # noqa: SLF001
with transaction.atomic():
if (
instance.deceased_datetime is None
and self.get_object().deceased_datetime
and not AuthorizationController.call(
"can_unmark_deceased_patient", self.request.user
)
):
raise PermissionDenied(detail="Cannot mark deceased patient alive")

super().perform_update(instance)
for identifier in identifiers:
config = get_object_or_404(
Expand Down
2 changes: 2 additions & 0 deletions care/emr/resources/patient/spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,8 @@ def perform_extra_deserialization(self, is_update, obj):
obj.year_of_birth = timezone.now().year - self.age
elif self.date_of_birth:
obj.year_of_birth = self.date_of_birth.year
if self.deceased_datetime is None:
obj.deceased_datetime = None
if not self.pincode:
obj.pincode = None

Expand Down
71 changes: 66 additions & 5 deletions care/emr/tests/test_patient_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,69 @@ def test_invalid_date_of_birth_and_death_date(self):
self.assertEqual(error["type"], "validation_error")
self.assertIn("Date of birth cannot be after the date of death", error["msg"])

def test_update_deceased_patient_to_alive(self):
"""Test that a superuser can set deceased_datetime back to None"""
geo_organization = self.create_organization(org_type="govt")
superuser = self.create_super_user()
role = self.create_role_with_permissions(
permissions=[
PatientPermissions.can_create_patient.name,
PatientPermissions.can_write_patient.name,
PatientPermissions.can_list_patients.name,
]
)
self.attach_role_organization_user(geo_organization, superuser, role)
self.client.force_authenticate(user=superuser)

# Create patient with deceased_datetime
deceased_time = care_now() - datetime.timedelta(days=2)
patient_data = self.generate_patient_data(
geo_organization=geo_organization.external_id,
deceased_datetime=deceased_time,
)
response = self.client.post(self.base_url, patient_data, format="json")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertIsNotNone(response.data["deceased_datetime"])
patient_id = response.data["id"]

# Update patient to set deceased_datetime to None
update_url = reverse("patient-detail", kwargs={"external_id": patient_id})
patient_data["deceased_datetime"] = None
response = self.client.put(update_url, patient_data, format="json")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertIsNone(response.data["deceased_datetime"])

def test_non_superuser_cannot_unmark_deceased(self):
"""Test that a non-superuser cannot set deceased_datetime back to None"""
user = self.create_user()
geo_organization = self.create_organization(org_type="govt")
role = self.create_role_with_permissions(
permissions=[
PatientPermissions.can_create_patient.name,
PatientPermissions.can_write_patient.name,
PatientPermissions.can_list_patients.name,
]
)
self.attach_role_organization_user(geo_organization, user, role)
self.client.force_authenticate(user=user)

# Create patient with deceased_datetime
deceased_time = care_now() - datetime.timedelta(days=2)
patient_data = self.generate_patient_data(
geo_organization=geo_organization.external_id,
deceased_datetime=deceased_time,
)
response = self.client.post(self.base_url, patient_data, format="json")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertIsNotNone(response.data["deceased_datetime"])
patient_id = response.data["id"]

# Attempt to set deceased_datetime to None should be denied
update_url = reverse("patient-detail", kwargs={"external_id": patient_id})
patient_data["deceased_datetime"] = None
response = self.client.put(update_url, patient_data, format="json")
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

def test_create_patient_with_future_deceased_datetime(self):
user = self.create_user()
geo_organization = self.create_organization(org_type="govt")
Expand Down Expand Up @@ -243,11 +306,7 @@ def test_delete_patient_as_non_superuser(self):
user = self.create_user()
geo_organization = self.create_organization(org_type="govt")
role = self.create_role_with_permissions(
permissions=[
PatientPermissions.can_create_patient.name,
PatientPermissions.can_write_patient.name,
PatientPermissions.can_list_patients.name,
]
permissions=[PatientPermissions.can_create_patient.name]
)
self.attach_role_organization_user(geo_organization, user, role)
self.client.force_authenticate(user=user)
Expand All @@ -260,6 +319,8 @@ def test_delete_patient_as_non_superuser(self):
delete_url = reverse("patient-detail", kwargs={"external_id": patient_id})
response = self.client.delete(delete_url)
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

def test_invalid_age_and_death_date(self):
user = self.create_user()
geo_organization = self.create_organization(org_type="govt")
role = self.create_role_with_permissions(
Expand Down
4 changes: 4 additions & 0 deletions care/security/authorization/patient.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,5 +127,9 @@ def get_filtered_patients(self, qs, user):
| Q(users_cache__overlap=[user.id])
)

def can_unmark_deceased_patient(self, user):
"""Permission to reverse a patient's deceased status"""
return user.is_superuser


AuthorizationController.register_internal_controller(PatientAccess)
Loading