# Scheduling Module — Architecture Map

> **Last updated:** 2026-06-10
> **Status:** Living document — incrementally updated as new information is discovered

---

## 1. Purpose & Responsibilities

The Scheduling module provides **smart conflict detection and scheduling rule enforcement** for the sports/training ecosystem. It manages:
- Configurable scheduling rules (coach max hours, facility buffers, rest periods, day-offs, maintenance windows)
- Real-time conflict detection (coach overlap, facility overlap, player overlap, rule violations)
- Conflict logging and resolution workflow
- Schedule change requests (from coaches/players) with approval/rejection workflow
- Alternative time slot suggestion engine

It is a **pure backend logic module** — it does NOT own any entities like sessions, reservations, or coaches. Instead, it:
- Listens to events from other modules (session.created, session.rescheduled, reservation.created)
- Checks proposed time slots against scheduling rules
- Logs detected conflicts for manual resolution

---

## 2. Directory & File Structure

```
app/Modules/Scheduling/
├── bootstrap.php                              # Menu + permissions + event listeners
├── Routes.php                                 # 12 routes
├── Controllers/
│   └── SchedulingController.php              # Dashboard, rules CRUD, conflicts, change requests
├── Models/
│   └── SchedulingRule.php                    # Rule model (scheduling_rules table)
├── Services/
│   ├── ConflictDetectionService.php          # Core conflict detection engine
│   └── ScheduleChangeService.php            # Change request submission/approval/rejection
└── Views/
    ├── dashboard.php                         # Overview: pending requests, recent conflicts, stats
    ├── rules.php                             # List of scheduling rules
    ├── rule_create.php                       # Create new scheduling rule form
    ├── conflicts.php                         # Conflict list with resolution actions
    └── change_requests.php                   # Change request queue with approve/reject
```

---

## 3. Database Schema (Production — Source of Truth)

### 3.1 `scheduling_rules` Table (0 rows as of 2026-06-10)

| Column | Type | Nullable | Key | Default | Notes |
|--------|------|----------|-----|---------|-------|
| id | bigint unsigned | NO | PRI | auto_increment | |
| rule_name_ar | varchar(200) | NO | | | Arabic rule name |
| rule_type | varchar(30) | NO | MUL | | One of 8 types (see below) |
| applies_to | varchar(30) | NO | | all | 'all', 'coach', 'facility', etc. |
| applies_to_id | bigint unsigned | YES | | NULL | FK to specific entity |
| config_json | json | NO | | | Rule parameters (type-specific) |
| priority | int unsigned | NO | | 5 | Lower = higher priority |
| is_active | tinyint(1) | NO | MUL | 1 | |
| created_at | timestamp | NO | | CURRENT_TIMESTAMP | |
| updated_at | timestamp | NO | | CURRENT_TIMESTAMP | On update |

**Rule types:**
- `coach_max_hours` — max hours per day for a coach (`{max_hours: 8}`)
- `facility_buffer` — minutes between facility reservations (`{buffer_minutes: 15}`)
- `age_restriction` — age limits on facility/time
- `gender_separation` — gender-specific time slots
- `rest_between_sessions` — player rest requirement (`{rest_minutes: 30}`)
- `max_daily_sessions` — max sessions per day for a player (`{max_sessions: 3}`)
- `coach_day_off` — weekly off-day for a coach (`{day_of_week: 5}`)
- `facility_maintenance` — recurring maintenance window (`{day_of_week, start_time, end_time}`)

### 3.2 `scheduling_conflicts` Table (0 rows as of 2026-06-10)

| Column | Type | Nullable | Key | Default | Notes |
|--------|------|----------|-----|---------|-------|
| id | bigint unsigned | NO | PRI | auto_increment | |
| conflict_type | varchar(30) | NO | MUL | | coach_overlap, facility_overlap, player_overlap, rule_violation |
| severity | varchar(10) | NO | MUL | warning | 'error' or 'warning' |
| entity_a_type | varchar(30) | NO | | | 'session', 'reservation', 'player' |
| entity_a_id | bigint unsigned | NO | | | |
| entity_b_type | varchar(30) | YES | | NULL | |
| entity_b_id | bigint unsigned | YES | | NULL | |
| conflict_date | date | NO | MUL | | Date of the conflict |
| conflict_time_start | time | NO | | | |
| conflict_time_end | time | NO | | | |
| description_ar | text | NO | | | Arabic description |
| resolution | varchar(30) | YES | MUL | | 'pending', 'resolved', 'dismissed' |
| resolved_by | bigint unsigned | YES | | NULL | FK to employees |
| resolved_at | datetime | YES | | NULL | |
| resolution_notes | text | YES | | NULL | |
| detected_at | datetime | NO | | | When conflict was detected |
| created_at | timestamp | NO | | CURRENT_TIMESTAMP | |

### 3.3 `schedule_change_requests` Table (0 rows as of 2026-06-10)

| Column | Type | Nullable | Key | Default | Notes |
|--------|------|----------|-----|---------|-------|
| id | bigint unsigned | NO | PRI | auto_increment | |
| requester_type | varchar(20) | NO | MUL | | 'coach', 'player', 'admin' |
| requester_id | bigint unsigned | NO | | | FK to coaches/players/employees |
| request_type | varchar(30) | NO | MUL | | 'reschedule', 'cancel' |
| current_session_id | bigint unsigned | YES | | NULL | FK to training_sessions |
| current_group_id | bigint unsigned | YES | | NULL | FK to training_groups |
| requested_day_of_week | tinyint unsigned | YES | | NULL | 0=Sun, 6=Sat |
| requested_start_time | time | YES | | NULL | |
| requested_end_time | time | YES | | NULL | |
| requested_date | date | YES | | NULL | |
| requested_facility_id | bigint unsigned | YES | | NULL | FK to facilities |
| reason_ar | text | NO | | | Arabic reason |
| status | varchar(20) | NO | MUL | pending | 'pending', 'approved', 'rejected' |
| reviewed_by | bigint unsigned | YES | | NULL | FK to employees |
| reviewed_at | datetime | YES | | NULL | |
| review_notes | text | YES | | NULL | |
| auto_suggestions_json | json | YES | | NULL | Alternative slots suggested by system |
| created_at | timestamp | NO | | CURRENT_TIMESTAMP | |
| updated_at | timestamp | NO | | CURRENT_TIMESTAMP | On update |

---

## 4. Routes

| Method | Path | Handler | Permission |
|--------|------|---------|------------|
| GET | /scheduling | dashboard | scheduling.view |
| GET | /scheduling/rules | rules | scheduling.manage_rules |
| GET | /scheduling/rules/create | createRule | scheduling.manage_rules |
| POST | /scheduling/rules | storeRule | scheduling.manage_rules |
| POST | /scheduling/rules/{id}/toggle | toggleRule | scheduling.manage_rules |
| POST | /scheduling/rules/{id}/delete | deleteRule | scheduling.manage_rules |
| GET | /scheduling/conflicts | conflicts | scheduling.view |
| POST | /scheduling/conflicts/{id}/resolve | resolveConflict | scheduling.resolve |
| GET | /scheduling/requests | changeRequests | scheduling.manage_requests |
| POST | /scheduling/requests/{id}/approve | approveRequest | scheduling.manage_requests |
| POST | /scheduling/requests/{id}/reject | rejectRequest | scheduling.manage_requests |
| POST | /scheduling/check-conflicts | checkConflicts (JSON) | scheduling.view |

---

## 5. Events Consumed

| Event | Dispatched By | Action |
|-------|---------------|--------|
| `session.created` | (expected from Sessions/Training — not found dispatching) | Detects conflicts for new session, logs them |
| `session.rescheduled` | ScheduleChangeService (when request approved) | Re-checks conflicts for moved session |
| `reservation.created` | Reservations/ReservationService | Checks facility overlap for new reservation |

---

## 6. Events Dispatched

| Event | Payload | Dispatched From |
|-------|---------|-----------------|
| `schedule.change_requested` | `{request_id, requester_type, requester_id, request_type}` | ScheduleChangeService::submitRequest |
| `session.rescheduled` | `{session_id, request_id}` | ScheduleChangeService::approveRequest (reschedule type) |
| `session.cancelled` | `{session_id, reason}` | ScheduleChangeService::approveRequest (cancel type) |

---

## 7. Cross-Module Dependencies

### 7.1 Scheduling IMPORTS FROM:

| Module | What it uses |
|--------|-------------|
| (none) | No explicit `use` imports from other modules |

However, it queries these tables owned by other modules directly:
- `training_sessions` (Sessions/Training) — for coach/date overlap checks
- `training_groups` (Training) — for group_name in conflict descriptions
- `reservations` (Reservations) — for facility overlap checks
- `session_attendance` (Sessions) — for player overlap checks
- `coaches` / `players` — for requester_name resolution in change requests

### 7.2 Other modules that IMPORT FROM Scheduling:

| Module | What it imports |
|--------|----------------|
| Reservations | ConflictDetectionService::checkFacilityOverlap (private match bookings) |

---

## 8. Permissions

| Key | Description |
|-----|-------------|
| scheduling.view | View scheduling dashboard and conflicts |
| scheduling.manage_rules | Create, toggle, delete scheduling rules |
| scheduling.resolve | Resolve conflicts |
| scheduling.manage_requests | View, approve, reject change requests |

---

## 9. High-Risk Areas

### 9.1 Conflict Detection Performance
- `detectConflicts()` runs 4 separate checks (coach, facility, player, rules), each querying the DB
- `suggestAlternatives()` loops 7 days x 16 hours calling `detectConflicts()` per slot — potentially 100+ DB queries per call
- If called in real-time (the `/scheduling/check-conflicts` endpoint), could be slow under load

### 9.2 Event Listener Dependency
- The module listens to `session.created` but this event does NOT appear to be dispatched anywhere in the codebase
- The conflict detection on session creation only works if the source module dispatches that event

### 9.3 Direct Table Mutation via Change Requests
- `ScheduleChangeService::approveRequest()` directly updates `training_sessions` table (date, time, facility, status)
- This bypasses any business logic in the Sessions/Training module
- Could leave session data in an inconsistent state if those modules have additional constraints

### 9.4 All Tables Empty (0 rows)
- Module appears to be deployed but unused in production
- No rules configured = conflict detection via rules always passes

---

## 10. Menu Registration

Registered in sidebar as "الجدولة الذكية" (Smart Scheduling) at order 730 with children:
- لوحة الجدولة (Dashboard)
- القواعد (Rules)
- التعارضات (Conflicts)
- طلبات التغيير (Change Requests)

---

## 11. Key Design Patterns

1. **Reactive conflict detection**: Conflicts are detected via event listeners (after the fact) rather than blocking creation
2. **Real-time check endpoint**: `/scheduling/check-conflicts` provides pre-creation conflict checking (JSON response for AJAX use)
3. **Auto-suggestions**: When conflicts exist, the system suggests alternative time slots automatically
4. **Rules are JSON-configurable**: Each rule type has its own config schema stored as JSON
5. **Approval workflow**: Change requests go through pending → approved/rejected lifecycle with employee tracking
