# Model: Encounter

**Table**: `clinic_encounters` (new, Phase 6)

## Purpose

The structured successor to `clinic_medical_records`. One row per clinical documentation event —
a visit, a walk-in follow-up, or a post-op note — carrying the actual narrative and structured
problem list, with `Vital`, `Diagnosis`, and the Phase 7 specialty tables hanging off it.
Deliberately **not** hard-wired 1:1 to a `Booking` (unlike `MedicalRecord`), because chronic-care
follow-ups and post-op notes don't always originate from a paid booking slot.

## Fields

| Column | Type | Constraints | Notes |
|---|---|---|---|
| `id` | bigint | PK | |
| `booking_id` | bigint | `foreignId('booking_id')->nullable()->constrained('clinic_bookings')->nullOnDelete()` | set when the encounter originates from a scheduled booking; null for ad-hoc follow-ups |
| `surgery_case_id` | bigint | `foreignId('surgery_case_id')->nullable()->constrained('clinic_surgery_cases')->nullOnDelete()` | added in Phase 10; set only for `encounter_type = post_op` |
| `patient_id` | bigint | `foreignId('patient_id')->constrained('users')->restrictOnDelete()` | same convention as `Booking.patient_id` |
| `doctor_id` | bigint | `foreignId('doctor_id')->constrained('clinic_doctors')->restrictOnDelete()` | |
| `branch_id` | bigint | `foreignId('branch_id')->constrained('branches')->restrictOnDelete()` | for branch scoping, same reasoning as `Booking.branch_id` |
| `encounter_type` | string | default `'consultation'` | `consultation` / `follow_up` / `pre_op` / `post_op` |
| `chief_complaint` | text | nullable | |
| `history_text` | text | nullable | HPI / relevant history |
| `examination_text` | text | nullable | |
| `assessment_text` | text | nullable | free-text summary alongside the structured `Diagnosis` rows |
| `plan_text` | text | nullable | |
| `status` | string | default `'draft'` | `draft` → `finalized` |
| `finalized_at` | datetime | nullable | |
| `created_by` | bigint | `foreignId(...)->nullable()->constrained('admins')->nullOnDelete()` | null when the doctor authored it directly (doctor guard, not admin) |
| `created_at` / `updated_at` | timestamp | | |

**No soft deletes** — a clinical record is never deleted, only superseded/corrected via an edit
+ `ActivityLog` entry, matching the existing `MedicalRecord` edit-audit pattern.

## Relationships

| Relation | Type | Target |
|---|---|---|
| `booking()` | `belongsTo` | `Booking` |
| `surgeryCase()` | `belongsTo` | `SurgeryCase` |
| `patient()` | `belongsTo` | `User` |
| `doctor()` | `belongsTo` | `Doctor` |
| `branch()` | `belongsTo` | `Core\Branch` |
| `vitals()` | `hasOne` | `Vital` |
| `diagnoses()` | `hasMany` | `Diagnosis` |
| `diabetesScreening()` | `hasOne` | `DiabetesScreening` |
| `bariatricAssessment()` | `hasOne` | `BariatricAssessment` |
| `nutritionPlan()` | `hasOne` | `NutritionPlan` |
| `prescription()` | `hasOne` | `Prescription` |
| `labOrders()` | `hasMany` | `LabOrder` |

## Decisions

- Kept independent of `Booking` (nullable FK, not required) precisely because a diabetes
  follow-up weight check or a post-op note may not correspond to a fresh paid booking — forcing a
  `Booking` row for every one of those would mean fabricating bookings just to satisfy a
  constraint, which existing `MedicalRecord` (via `booking_id` unique FK) would require.
- `status: draft → finalized` mirrors the existing `MedicalRecord.published_at` idea (a doctor can
  save a partial note and come back) but as an explicit enum instead of a nullable timestamp, so a
  "list my draft encounters" query is a plain `where('status', 'draft')`.
- Specialty tables (`DiabetesScreening`, `BariatricAssessment`, `NutritionPlan`) and `Prescription`
  are all optional `hasOne` — an encounter is valid with none, one, or several of them populated.
