All checks were successful
Build and Push Docker Image / build (push) Successful in 2m27s
Behavioral consistency across complaint/inquiry/observation (re-run: 0 FAIL):
- activation gate on complaints: complaint_send_to + send_to_department_form now
reject status=open ("Activate this complaint before sending it to a department")
- observation resolve dead-end fixed: observation_change_status now allows
hospital_admin (was triage_perm/px_admin only) + added a Resolve action on the
observation detail page -> accepted dept responses can be resolved from the flow
- status validation: Observation.clean() rejects invalid statuses + a DB
CheckConstraint (migration 0016 normalizes legacy "new"->"open" first)
- inquiry sent_to_department consistency: inquiry_transfer_to_department now also
sets sent_to_department=True/At (matches observation/complaint for cross-module queries)
Lucide icon-race mitigation: added a defensive `window.lucide || {createIcons:noop}`
shim to the three base layouts + standalone CDN pages (login, select_hospital,
password reset) so the "lucide is not defined" ReferenceError can't throw during
navigation races. Audit listener classifies any residual occurrence as WARN (cosmetic).
Docs: docs/workflows.md records the intentional complaint vs inquiry/observation
differences (multi-dept join + manager review vs single-dept flat) + the field-name map.
Specs: champion spec now activates before send (matches the new gate).
62 lines
3.7 KiB
Markdown
62 lines
3.7 KiB
Markdown
# 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/<id>/respond/<token>/`.
|
|
|
|
### 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/<id>/respond/<token>/`.
|
|
|
|
## 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**.
|