# Model: Offer

**Table**: `clinic_offers` (new)

## Purpose

Discounts/packages/promotions. Either platform-wide (managed by Super Admin) or local to one
clinic (managed by that Clinic Admin), per the brief ("Super Admin manages general offers;
details unspecified — can include discounts, monthly packages, promotional offers").

## Fields

| Column | Type | Constraints | Notes |
|---|---|---|---|
| `id` | bigint | PK | |
| `title` | string | | |
| `description` | text | nullable | |
| `discount_type` | string | `'percentage'` / `'fixed'` | |
| `discount_value` | decimal(10,2) | | |
| `branch_id` | bigint | `foreignId('branch_id')->nullable()->constrained('branches')->cascadeOnDelete()` | `null` = platform-wide (Super Admin only), set = local to that clinic (Clinic Admin can manage their own) |
| `specialty_id` | bigint | `foreignId('specialty_id')->nullable()->constrained('clinic_specialties')->nullOnDelete()` | optional targeting |
| `doctor_id` | bigint | `foreignId('doctor_id')->nullable()->constrained('clinic_doctors')->nullOnDelete()` | optional targeting |
| `starts_at` | datetime | | |
| `ends_at` | datetime | | |
| `is_active` | boolean | default true | |
| `created_by` | bigint | `foreignId(...)->nullable()->constrained('admins')->nullOnDelete()` | |
| `created_at` / `updated_at` | timestamp | | |

## Relationships

| Relation | Type | Target |
|---|---|---|
| `branch()` | `belongsTo` (nullable) | `Core\Branch` |
| `specialty()` | `belongsTo` (nullable) | `Specialty` |
| `doctor()` | `belongsTo` (nullable) | `Doctor` |

## Decisions

- `branch_id = null` distinguishes a platform-wide offer from a branch-local one — the branch
  scoping rule from `03-permissions-and-roles.md` applies here too: a Clinic Admin's offer list
  is scoped to `branch_id = their own branch OR branch_id IS NULL` (they can see platform-wide
  offers but only manage their own local ones); Super Admin sees/manages everything.
- Exact discount application logic (how `discount_value` reduces `Booking.price` at checkout) is
  intentionally left open here since the source brief doesn't specify it — flag as a decision to
  confirm with the user before Phase 4 implementation.
