# Models: SurgeryCase + SurgeryPreopAssessment

**Tables**: `clinic_surgery_cases`, `clinic_surgery_preop_assessments` (new, Phase 10)

## Purpose

Surgery scheduling and pre-op/consent documentation — **scheduling and documentation only**, per
the user's confirmed decision. No room/bed/ward field exists anywhere in either table; there is no
admission/inpatient-stay record in this design.

## Fields — `clinic_surgery_cases`

| Column | Type | Constraints | Notes |
|---|---|---|---|
| `id` | bigint | PK | |
| `patient_id` | bigint | `foreignId('patient_id')->constrained('users')->restrictOnDelete()` | |
| `branch_id` | bigint | `foreignId('branch_id')->constrained('branches')->restrictOnDelete()` | for branch scoping, same as `Booking.branch_id` |
| `booking_id` | bigint | `foreignId('booking_id')->nullable()->constrained('clinic_bookings')->nullOnDelete()` | if scheduled from an existing booking flow; nullable since surgery scheduling can also start from the surgeon's own case list |
| `procedure_type` | string | | e.g. `sleeve_gastrectomy` / `gastric_bypass` / general-surgery procedure name — free text or a short seeded list via `clinic_medical_history_options` (`type = 'surgery_procedure'`), same catalog reused from `BariatricAssessment.proposed_procedure` |
| `surgeon_id` | bigint | `foreignId('surgeon_id')->constrained('clinic_doctors')->restrictOnDelete()` | a `Doctor` row |
| `assistant_ids` | json | nullable | array of `clinic_doctors.id` — kept as JSON since the count varies per case and doesn't need independent querying beyond "who was in this case" |
| `anesthesiologist_id` | bigint | `foreignId('anesthesiologist_id')->nullable()->constrained('clinic_doctors')->nullOnDelete()` | |
| `scheduled_at` | datetime | | |
| `duration_estimate_minutes` | unsignedSmallInteger | nullable | |
| `status` | string | default `'scheduled'` | `scheduled` → `pre_op_ready` (assessment + consent complete) → `completed` / `cancelled` |
| `created_by` | bigint | `foreignId(...)->nullable()->constrained('admins')->nullOnDelete()` | |
| `created_at` / `updated_at` | timestamp | | |

## Fields — `clinic_surgery_preop_assessments`

| Column | Type | Constraints | Notes |
|---|---|---|---|
| `id` | bigint | PK | |
| `surgery_case_id` | bigint | `foreignId('surgery_case_id')->constrained('clinic_surgery_cases')->cascadeOnDelete()` unique | 1:1 with the case |
| `fitness_confirmed` | boolean | default `false` | |
| `labs_reviewed` | boolean | default `false` | references the relevant `LabOrder`/`LabResult` rows for this patient, reviewed manually — no automated cross-check in this phase |
| `fasting_confirmed` | boolean | default `false` | |
| `consent_signed_at` | datetime | nullable | |
| `consent_file_path` | string | nullable | scanned/uploaded signed consent, same storage pattern as `clinic_patient_attachments.file_path` |
| `notes` | text | nullable | |
| `created_at` / `updated_at` | timestamp | | |

## Relationships

| Relation | Type | Target |
|---|---|---|
| `SurgeryCase::patient()` | `belongsTo` | `User` |
| `SurgeryCase::branch()` | `belongsTo` | `Core\Branch` |
| `SurgeryCase::booking()` | `belongsTo` | `Booking` |
| `SurgeryCase::surgeon()` | `belongsTo` | `Doctor` |
| `SurgeryCase::anesthesiologist()` | `belongsTo` | `Doctor` |
| `SurgeryCase::preopAssessment()` | `hasOne` | `SurgeryPreopAssessment` |
| `SurgeryCase::postOpEncounters()` | `hasMany` | `Encounter` (via `Encounter.surgery_case_id`) |

## Decisions

- `status` can only move to `pre_op_ready` once the linked `SurgeryPreopAssessment` has
  `fitness_confirmed`, `fasting_confirmed`, and a non-null `consent_signed_at` — enforced in
  `SurgeryCaseService`, not a DB constraint, so the checklist logic stays in one place and is easy
  to extend.
- No `bed_id`/`room_id`/`ward_id` column, and no `discharge_at` field — confirmed out of scope.
  Post-op recovery/follow-up is tracked as an `Encounter` with `encounter_type = post_op`, not a
  stay record (see `Encounter.md`).
- `assistant_ids` as JSON rather than a pivot table — case-assistant assignment doesn't need to be
  independently queried/reported on in this phase (no "assistant workload" report requested); if
  that need arises later, a pivot table is a straightforward follow-up migration.
