# Patient-feedback workflow architecture This document records the **intentional** differences between the three department-response workflows so they don't get "fixed" into inconsistencies. ## Shared rules (apply to all three) - **Activation gate:** an item cannot be worked on or sent to a department until it has been activated (`open → in_progress`). Every send-to-department endpoint rejects `status == open`. - **Status vocabulary:** `open → in_progress → resolved → closed` (complaints additionally have `partially_resolved`, `cancelled`, `pending_external`, `ovr_pending`). Invalid statuses are rejected at the model (`clean()`) and DB (`CheckConstraint`) level. - **"Sent" signal:** `sent_to_department` (bool) + `sent_to_department_at` is set by every module's send flow so cross-module reporting works uniformly. - **Reject loops:** when PX marks a department response `not_acceptable`, the response is cleared and the item returns to the champion. ## Per-module differences (intentional) ### Complaint — multi-department + manager review + investigation - Uses a **join model** `ComplaintInvolvedDepartment` so **multiple departments** can be involved in one complaint simultaneously. - **Two-level review:** champion submits a response → **department manager** approves/rejects → PX accepts/rejects → resolve. - **Investigation sub-flow:** the champion can create questions that are emailed (token links) to involved staff, who answer; the champion reviews the answers before writing the department response. - Resolve is direct from any active status. ### Inquiry — single department, no manager review - **Flat fields** on `Inquiry` (`transferred_to_department` / `outgoing_department` + `department_response_en` + `dept_response_acceptance_status`). One department at a time. - **One-level review:** champion responds → PX accepts/rejects → resolve. - **Resolve requires a PX `response`** (`inquiry.response`, set via `inquiry_respond`) — the PX-team must write their reply to the inquirer before the inquiry can be marked resolved. - Token-response path: emailed link to `/inquiries//respond//`. ### Observation — single department, no manager review - **Flat fields** on `Observation` (`assigned_department` + `department_response_en` + `dept_response_acceptance_status`). One department at a time. - **One-level review:** champion responds → PX accepts/rejects → resolve. - Status change (`observation_change_status`) requires the `triage_observation` permission OR px_admin OR hospital_admin. - Token-response path: emailed link to `/observations//respond//`. ## Field-name reference (the "same concept, different name" map) | Concept | Complaint (join) | Inquiry | Observation | |--------|------------------|---------|-------------| | target dept | `ComplaintInvolvedDepartment.department` | `transferred_to_department` / `outgoing_department` | `assigned_department` | | sent flag | `ComplaintInvolvedDepartment.sent` | `sent_to_department` | `sent_to_department` | | response text | `response_notes_en/ar` | `department_response_en/ar` | `department_response_en/ar` | | response at/by | `response_submitted_at` (+ `ComplaintExplanation`) | `department_responded_at/_by` | `department_responded_at/_by` | | acceptance | `acceptance_status` | `dept_response_acceptance_status` | `dept_response_acceptance_status` | | manager review | `manager_review_status` | — (none) | — (none) | | token | `ComplaintExplanation` / `InvestigationResponse` | `response_token` | `response_token` | The naming drift between complaint and the other two is a direct consequence of the architecture difference (join vs flat) and is **intentional**.