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).
3.7 KiB
3.7 KiB
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 rejectsstatus == open. - Status vocabulary:
open → in_progress → resolved → closed(complaints additionally havepartially_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_atis 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
ComplaintInvolvedDepartmentso 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_departmentdepartment_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 viainquiry_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_endept_response_acceptance_status). One department at a time.
- One-level review: champion responds → PX accepts/rejects → resolve.
- Status change (
observation_change_status) requires thetriage_observationpermission 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.