Treatment Plan Lifecycle
Plan Status State Machine
┌───────┐
Create plan │ draft │◄──────────────────┐
└───┬───┘ │
│ │
┌───────────────┤ │
│ │ │ Reject / Revise
│ requires_approval = false │
│ │ │
│ Publish │ Publish │
│ │ │
▼ ▼ │
┌────────┐ ┌──────────────────┐ │
│ active │ │ pending_approval │──────────┘
└────┬───┘ └────────┬─────────┘
│ │
│ │ Approve
│ │
│ ▼
│ ┌────────┐
├◄───────────┤ active │
│ └────────┘
│
├──────────────┐
│ │
│ Pause │
▼ │
┌────────┐ │
│ paused │──────┐ │
└────┬───┘ │ │
│ │ │
│ Resume │ │
│ │ │
▼ │ │
┌────────┐ │ │
│ active │ │ │
└────┬───┘ │ │
│ │ │
┌────┼──────────┼───┤
│ │ │ │
│ ▼ ▼ ▼
│ ┌───────────┐ ┌───────────┐
│ │ cancelled │ │ cancelled │
│ └───────────┘ └───────────┘
│
├─── All sessions done ──► ┌───────────┐
│ │ completed │
│ └───────────┘
│
└─── end_date passed ────► ┌─────────┐
(background job) │ expired │
└─────────┘State Transitions
| From | To | Trigger | Who | Validation |
|---|---|---|---|---|
draft | active | Publish (no approval) | specialist, admin | At least 1 session with 1 exercise |
draft | pending_approval | Publish (approval required) | specialist, admin | At least 1 session with 1 exercise |
pending_approval | active | Approve | specialist, admin | Must be authorized approver |
pending_approval | draft | Reject / Revise | specialist, admin | Returns to editable state |
active | paused | Manual pause | specialist, admin | - |
paused | active | Resume | specialist, admin | end_date not passed |
active | completed | All sessions done | system (auto) | sessions_completed >= sessions_total |
active | cancelled | Manual cancel | specialist, admin | Clinical decision |
paused | cancelled | Manual cancel | specialist, admin | - |
active | expired | Background job | system (cron) | end_date < NOW() |
paused | expired | Background job | system (cron) | end_date < NOW() |
Terminal States
These states are final — no further transitions allowed:
- completed — All sessions done successfully
- cancelled — Manually cancelled by specialist/admin
- expired — Plan validity period ended
Approval Workflow
Organization Configuration
Each organization decides whether plans require specialist approval before patient activation:
// Treatment plan creation
{
"requires_approval": true // org policy or per-plan setting
}Without Approval (Default)
Staff creates plan → Publishes → Assigns to patient → Active immediately- Specialist or admin creates plan in
draft - Adds sessions and exercises
- Publishes plan → status becomes
active - Assigns to patient →
patient_treatment_plans.status = 'active' - Patient can start sessions immediately
With Approval
Staff creates plan → Publishes → Assigns to patient (pending_approval)
│
Specialist reviews plan
│
┌───────────────┼───────────────┐
│ │ │
Approves Requests changes Rejects
│ │ │
Plan active Returns to draft Cancelled- Staff creates plan in
draft - Adds sessions and exercises
- Publishes plan → status becomes
pending_approval - Assigns to patient →
patient_treatment_plans.status = 'pending_approval' - Specialist reviews:
- Approve:
POST /v1/patient-treatment-plans/{id}/approve - Reject/Revise:
PUT /v1/patient-treatment-plans/{id}/status→draft
- Approve:
- On approval: patient_treatment_plan becomes
active, patient is notified
Versioning Workflow
Treatment plans use the same versioning pattern as form templates:
Creating Versions
Version 1 (draft):
Session 1: Exercise A (3×10), Exercise B (2×15)
Publish → Version 1 snapshot created
sessions_snapshot: [{...}]
Edit → New draft (modifying current state):
Session 1: Exercise A (3×12), Exercise B (2×15), Exercise C (3×8)
Publish → Version 2 snapshot created
sessions_snapshot: [{...}]Version Assignment
When assigning a plan to a patient, the system always uses the latest published version:
INSERT INTO patient_treatment_plans (
patient_id, treatment_plan_id, treatment_plan_version, ...
) VALUES (
123, 'plan-uuid', 2, ... -- version 2 (latest)
);Existing Enrollments
Existing patient enrollments are NOT affected by new versions:
- Patient A enrolled on version 1 → continues on version 1
- Patient B enrolled on version 2 → continues on version 2
This ensures clinical continuity — a patient's program doesn't change mid-treatment.
Rollback
Rolling back restores a previous version's session structure but creates a NEW version number:
Rollback to version 1:
Current version: 2
After rollback: version 3 (with version 1's content)This preserves the audit trail — you can always see what happened and when.
Background Jobs
Plan Expiration Checker
A periodic background job (runs daily) checks for expired plans:
UPDATE patient_treatment_plans
SET status = 'expired', updated_at = NOW()
WHERE status IN ('active', 'paused')
AND end_date < CURRENT_DATE;For each expired plan, the system:
- Transitions status to
expired - Fires
treatment_plan.expiredautomation trigger - Notifies patient (via automation action)
Session Reminders
A daily background job can send session reminders:
SELECT ptp.*
FROM patient_treatment_plans ptp
WHERE ptp.status = 'active'
AND ptp.start_date <= CURRENT_DATE
AND ptp.end_date >= CURRENT_DATE
-- Patient hasn't completed a session today
AND NOT EXISTS (
SELECT 1 FROM patient_session_completions psc
WHERE psc.patient_treatment_plan_id = ptp.id
AND psc.started_at::DATE = CURRENT_DATE
);This is implemented as an automation action (send_session_reminder) that can be configured per organization.
Pause/Resume
Pausing a plan temporarily suspends it:
- Patient cannot start new sessions while paused
- The plan's
end_dateis NOT extended (the validity window continues ticking) - Resuming simply returns the plan to
active
If an organization wants to extend the plan duration after a pause, they should create a new enrollment or adjust the end_date manually.