HH/docs/workflows.md
ismail 45b75eb9ef
All checks were successful
Build and Push Docker Image / build (push) Successful in 2m27s
fix: align workflow behavior (Phase 1) + lucide icon-race mitigation
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).
2026-06-14 23:27:03 +03:00

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**.