From 2829308befbd94823cb133820382a46dd0387cbe Mon Sep 17 00:00:00 2001 From: ismail Date: Mon, 20 Jul 2026 21:50:12 +0300 Subject: [PATCH] fix(observation): dept_manager of assigned dept can now respond Matches the complaint predicate. Previously a department manager could respond to a complaint sent to their dept but not to an observation. --- .../observations/test_workflow_realignment.py | 69 +++++++++++++++++++ apps/observations/views.py | 1 + 2 files changed, 70 insertions(+) create mode 100644 apps/observations/test_workflow_realignment.py diff --git a/apps/observations/test_workflow_realignment.py b/apps/observations/test_workflow_realignment.py new file mode 100644 index 0000000..884cf86 --- /dev/null +++ b/apps/observations/test_workflow_realignment.py @@ -0,0 +1,69 @@ +"""Tests for the observation workflow realignment.""" +from django.contrib.auth.models import Group + +import pytest +from django.test import TestCase +from django.urls import reverse + +from apps.accounts.models import User +from apps.organizations.models import Department, Hospital +from apps.observations.models import Observation + + +def _ensure_group(name): + """Get or create a role group (pytest runs with --nomigrations, so seeded + groups may not exist in the test DB).""" + grp, _ = Group.objects.get_or_create(name=name) + return grp + + +@pytest.mark.django_db +class TestObservationDepartmentResponsePermission(TestCase): + """A department manager of the assigned department can respond. + + Uses self.client (full middleware incl. django.contrib.messages) — the + view calls messages.error() on the permission-denied path. The project's + root conftest.py disables ManifestStaticFilesStorage for all tests. + """ + + def setUp(self): + self.hospital = Hospital.objects.create(name="Test Hospital", code="TH02") + self.dept = Department.objects.create(name="Dept A", hospital=self.hospital, code="OA") + self.other_dept = Department.objects.create(name="Dept B", hospital=self.hospital, code="OB") + + _ensure_group("Department Manager") + self.dept_manager = User.objects.create_user( + username="odmgr", email="odmgr@test", password="x", department=self.dept + ) + self.dept_manager.groups.add(Group.objects.get(name="Department Manager")) + + self.other_manager = User.objects.create_user( + username="oother", email="oo@test", password="x", department=self.other_dept + ) + self.other_manager.groups.add(Group.objects.get(name="Department Manager")) + + self.observation = Observation.objects.create( + description="Test observation description here", + hospital=self.hospital, + assigned_department=self.dept, + sent_to_department=True, + status="in_progress", + ) + + def test_dept_manager_of_assigned_dept_can_access_response_view(self): + """GET to observation_department_response succeeds (200) for dept manager of assigned dept.""" + self.client.force_login(self.dept_manager) + response = self.client.get( + reverse("observations:observation_department_response", kwargs={"pk": self.observation.pk}) + ) + assert response.status_code == 200, \ + f"dept_manager of assigned dept should be allowed, got {response.status_code}" + + def test_dept_manager_of_other_dept_cannot_access(self): + """Dept manager of a non-assigned department is denied (302 redirect).""" + self.client.force_login(self.other_manager) + response = self.client.get( + reverse("observations:observation_department_response", kwargs={"pk": self.observation.pk}) + ) + assert response.status_code == 302, \ + f"dept_manager of other dept should be redirected (denied), got {response.status_code}" diff --git a/apps/observations/views.py b/apps/observations/views.py index 770d3e1..91d38fa 100644 --- a/apps/observations/views.py +++ b/apps/observations/views.py @@ -1628,6 +1628,7 @@ def observation_department_response(request, pk): or user.is_px_management() or user.is_px_employee() or (user.is_champion() and observation.assigned_department == user.department) + or (user.is_department_manager() and observation.assigned_department == user.department) ): messages.error(request, "You don't have permission to respond to this observation.") return redirect("observations:observation_detail", pk=pk)