# Sessions Module — Architecture Map

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

---

## 1. Purpose & Responsibilities

The Sessions module manages the **training session lifecycle** for academy groups. It handles:
- Viewing and filtering training sessions (list + calendar views)
- Weekly session generation from training group schedules
- Session status management (scheduled → in_progress → completed / cancelled)
- Attendance recording (present, absent, late, excused, makeup)
- Makeup credit system (credits issued on cancellation or absence, expirable)
- Session feedback collection (player ratings of sessions/coaches)
- Coach feedback summaries and trends
- Season schedule generation
- Holiday override (bulk cancellation)
- Schedule conflict resolution (alternative slot suggestions)

It does **NOT** own:
- Training groups (Training module)
- Academies (Academies module)
- Players (PlayerAffairs/SportsActivity)
- Coaches (Coaches module)
- Facilities (Facilities module)

---

## 2. Directory & File Structure

```
app/Modules/Sessions/
├── bootstrap.php                              # Permissions only (no menu, no events)
├── Routes.php                                 # 12 routes
├── Controllers/
│   ├── SessionController.php                 # Index, calendar, show, cancel, complete, generate
│   ├── SessionAttendanceController.php       # Attendance form + save
│   └── SessionFeedbackController.php         # Feedback create, store, report, coach summary
├── Models/
│   ├── TrainingSession.php                   # Main session model (search, calendar, types)
│   ├── SessionAttendance.php                 # Attendance record model
│   ├── SessionFeedback.php                   # Feedback model (averages, coach summary)
│   └── MakeupCredit.php                      # Makeup credit model
├── Services/
│   ├── SessionManagementService.php          # Generate, cancel, record attendance, complete
│   ├── MakeupService.php                     # Use credits, expire old credits, get player credits
│   ├── ScheduleConflictResolver.php          # Suggest alt slots, generate season, holiday overrides
│   └── SessionFeedbackService.php            # Submit feedback, get session report, trends
└── Views/
    ├── index.php                             # Paginated session list with filters
    ├── calendar.php                          # Monthly calendar view
    ├── show.php                              # Single session detail + attendance
    ├── attendance.php                        # Attendance recording form
    ├── feedback_form.php                     # Player feedback submission form
    ├── feedback_report.php                   # Session feedback report
    └── feedback_coach_summary.php            # Coach-level feedback summary
```

---

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

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

| Column | Type | Nullable | Key | Default | Notes |
|--------|------|----------|-----|---------|-------|
| id | bigint unsigned | NO | PRI | auto_increment | |
| group_id | bigint unsigned | NO | MUL | | FK to training_groups |
| academy_id | bigint unsigned | NO | MUL | | FK to academies |
| coach_id | bigint unsigned | YES | MUL | NULL | FK to coaches |
| facility_id | bigint unsigned | YES | MUL | NULL | FK to facilities |
| session_date | date | NO | MUL | | |
| start_time | time | NO | | | |
| end_time | time | NO | | | |
| session_type | varchar(20) | NO | | regular | regular, makeup, extra, assessment |
| status | varchar(20) | NO | MUL | scheduled | scheduled, in_progress, completed, cancelled |
| cancellation_reason | varchar(500) | YES | | NULL | |
| cancelled_by | bigint unsigned | YES | | NULL | FK to employees |
| players_expected | int unsigned | NO | | 0 | From group current_count |
| players_attended | int unsigned | NO | | 0 | Updated on attendance save |
| notes | text | YES | | NULL | |
| created_at | timestamp | NO | | CURRENT_TIMESTAMP | |
| updated_at | timestamp | NO | | CURRENT_TIMESTAMP | On update |
| created_by | bigint unsigned | YES | | NULL | FK to employees |
| updated_by | bigint unsigned | YES | | NULL | FK to employees |

**Session types:** regular (عادية), makeup (تعويضية), extra (إضافية), assessment (تقييم)

**Status lifecycle:**
```
scheduled → in_progress → completed
scheduled → cancelled
in_progress → completed
```

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

| Column | Type | Nullable | Key | Default | Notes |
|--------|------|----------|-----|---------|-------|
| id | bigint unsigned | NO | PRI | auto_increment | |
| session_id | bigint unsigned | NO | MUL | | FK to training_sessions |
| player_id | bigint unsigned | NO | MUL | | FK to players |
| enrollment_id | bigint unsigned | YES | MUL | NULL | FK to academy_enrollments |
| status | varchar(20) | NO | MUL | present | present, absent, late, excused, makeup |
| check_in_time | time | YES | | NULL | |
| check_out_time | time | YES | | NULL | |
| is_makeup | tinyint(1) | NO | | 0 | |
| makeup_for_session_id | bigint unsigned | YES | | NULL | FK to training_sessions |
| notes | text | YES | | NULL | |
| created_at | timestamp | NO | | CURRENT_TIMESTAMP | |
| created_by | bigint unsigned | YES | | NULL | FK to employees |

**Attendance statuses:** present (حاضر), absent (غائب), late (متأخر), excused (معتذر), makeup (تعويضي)

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

| Column | Type | Nullable | Key | Default | Notes |
|--------|------|----------|-----|---------|-------|
| id | int unsigned | NO | PRI | auto_increment | |
| session_id | int unsigned | NO | MUL | | FK to training_sessions |
| player_id | int unsigned | NO | MUL | | FK to players |
| rating | tinyint unsigned | NO | | 3 | 1-5 star rating |
| energy_level | enum('low','medium','high') | NO | | medium | |
| enjoyment_level | tinyint unsigned | NO | | 3 | 1-5 |
| difficulty_level | tinyint unsigned | NO | | 3 | 1-5 |
| comments_ar | text | YES | | NULL | |
| is_anonymous | tinyint(1) | NO | | 0 | Hide player name |
| created_at | datetime | NO | | CURRENT_TIMESTAMP | |

### 3.4 `makeup_credits` Table (0 rows as of 2026-06-10)

| Column | Type | Nullable | Key | Default | Notes |
|--------|------|----------|-----|---------|-------|
| id | bigint unsigned | NO | PRI | auto_increment | |
| player_id | bigint unsigned | NO | MUL | | FK to players |
| enrollment_id | bigint unsigned | NO | | | FK to academy_enrollments |
| missed_session_id | bigint unsigned | NO | MUL | | FK to training_sessions |
| used_session_id | bigint unsigned | YES | | NULL | FK to training_sessions (redeemed at) |
| status | varchar(20) | NO | MUL | available | available, used, expired |
| expires_at | date | YES | | NULL | 30 days from creation |
| created_at | timestamp | NO | | CURRENT_TIMESTAMP | |

---

## 4. Routes

| Method | Path | Handler | Permission |
|--------|------|---------|------------|
| GET | /sessions | SessionController@index | session.view |
| GET | /sessions/calendar | SessionController@calendar | session.view |
| GET | /sessions/{id} | SessionController@show | session.view |
| GET | /sessions/{id}/attendance | SessionAttendanceController@form | session.attendance |
| POST | /sessions/{id}/attendance | SessionAttendanceController@save | session.attendance |
| POST | /sessions/{id}/cancel | SessionController@cancel | session.manage |
| POST | /sessions/{id}/complete | SessionController@complete | session.manage |
| POST | /sessions/generate | SessionController@generate | session.manage |
| GET | /sessions/{id}/feedback | SessionFeedbackController@create | session.view |
| POST | /sessions/{id}/feedback | SessionFeedbackController@store | session.view |
| GET | /sessions/{id}/feedback/report | SessionFeedbackController@report | session.view_feedback |
| GET | /coaches/{id}/feedback-summary | SessionFeedbackController@coachSummary | session.view_feedback |

---

## 5. Events Dispatched

| Event | Payload | Dispatched From |
|-------|---------|-----------------|
| `session.cancelled` | `{session_id, reason, group_id}` | SessionManagementService::cancelSession |
| `session.attendance.recorded` | `{session_id, players_attended}` | SessionManagementService::recordAttendance |
| `session.completed` | `{session_id}` | SessionManagementService::completeSession |
| `session.feedback_submitted` | `{feedback_id, session_id, player_id, rating}` | SessionFeedbackService::submitFeedback |

---

## 6. Events Consumed

| Event | Listener Module | Action |
|-------|----------------|--------|
| `session.cancelled` | Notifications | Sends notification to affected players |
| `session.rescheduled` | Notifications | Sends notification about schedule change |
| `session.feedback_submitted` | Notifications | Notifies coach of new feedback |
| `session.created` | Scheduling | Conflict detection (listens but event not dispatched by this module) |
| `session.rescheduled` | Scheduling | Re-checks conflicts |

**Note:** This module does NOT dispatch `session.created` — the Scheduling module listens for it but it is never fired from here. The `generateWeeklySessions()` method creates sessions without dispatching any event.

---

## 7. Cross-Module Dependencies

### 7.1 Sessions 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_groups` (Training) — schedule data, group name, coach/facility assignment
- `group_memberships` (Training) — active members for attendance list
- `academies` (Academies) — academy name for display
- `coaches` (Coaches) — coach name for display
- `facilities` (Facilities) — facility name for display
- `players` (PlayerAffairs) — player name/phone for attendance display

### 7.2 Other modules that reference Sessions tables:

| Module | What it references |
|--------|-------------------|
| Scheduling | Queries `training_sessions` for overlap detection |
| Training | Has its own `SessionFeedback` model and `SessionFeedbackController` (duplicate/parallel implementation) |

### 7.3 Duplicate Implementation Warning

The **Training module** has its own parallel implementations:
- `Training/Models/SessionFeedback.php`
- `Training/Services/SessionFeedbackService.php`
- `Training/Controllers/SessionFeedbackController.php`
- Routes at `/session-feedback/{sessionId}/...`

Both modules operate on the same `session_feedback` table. This is a legacy overlap that needs reconciliation.

---

## 8. Permissions

| Key | Description |
|-----|-------------|
| session.view | View session list, calendar, details |
| session.attendance | Record attendance |
| session.manage | Cancel/complete sessions, generate weekly sessions |
| session.view_feedback | View feedback reports and coach summaries |

---

## 9. Core Business Flows

### 9.1 Weekly Session Generation

```
POST /sessions/generate {week_start}
  ↓
SessionManagementService::generateWeeklySessions()
  ↓
Queries training_groups with day_of_week + start_time + end_time set
  ↓
For each group: calculates session_date from week_start + day_of_week
  ↓
Checks if session already exists (group_id + date + not cancelled)
  ↓
Inserts training_sessions record with status='scheduled'
  ↓
Returns count of created sessions
```

### 9.2 Attendance Recording

```
POST /sessions/{id}/attendance {player_ids[], statuses[], ...}
  ↓
If session status='scheduled' → updates to 'in_progress'
  ↓
For each player: upserts session_attendance record
  ↓
If status='absent' + has enrollment_id → creates makeup_credit (expires in 30 days)
  ↓
Updates training_sessions.players_attended count
  ↓
Dispatches session.attendance.recorded
```

### 9.3 Session Cancellation

```
POST /sessions/{id}/cancel {cancellation_reason}
  ↓
SessionManagementService::cancelSession()
  ↓
Updates session status to 'cancelled'
  ↓
Gets all active group_memberships for this group
  ↓
Issues makeup_credit to EVERY active member (expires in 30 days)
  ↓
Dispatches session.cancelled
```

### 9.4 Makeup Credit Lifecycle

```
Credits issued when:
  1. Session cancelled → ALL group members get credit
  2. Player marked absent → individual credit

Credit states: available → used (redeemed at another session)
                        → expired (30 days elapsed)

MakeupService::expireOldCredits() — batch expiration (for cron)
MakeupService::useMakeupCredit() — validates available + not expired, then marks used
```

---

## 10. High-Risk Areas

### 10.1 No `session.created` Event Dispatch
- `generateWeeklySessions()` creates sessions without firing `session.created`
- The Scheduling module listens for this event but never receives it
- Conflicts are NOT automatically detected for generated sessions

### 10.2 Duplicate Feedback Implementation
- Both Sessions and Training modules have feedback controllers/services operating on `session_feedback`
- Risk of conflicting validation or business logic
- Routes overlap (Sessions: `/sessions/{id}/feedback`, Training: `/session-feedback/{id}/...`)

### 10.3 Makeup Credit Expiration
- `MakeupService::expireOldCredits()` is available but not registered in any cron job
- Credits may remain "available" past their `expires_at` date indefinitely
- The `getPlayerCredits()` filters by expiry date, so expired credits won't show up in UI, but the DB won't reflect the correct status

### 10.4 Season Schedule Generation
- `ScheduleConflictResolver::generateSeasonSchedule()` creates sessions for an entire season
- Does not fire any events, meaning Scheduling module won't detect conflicts
- Does not check against existing reservations or rules

### 10.5 All Tables Empty (0 rows)
- Module is deployed but appears unused in production
- Training groups may not have `day_of_week`/`start_time`/`end_time` set yet

---

## 11. Background Processes

| Service | Method | Purpose | Cron Status |
|---------|--------|---------|-------------|
| MakeupService | expireOldCredits() | Batch-expire credits past expires_at | NOT registered |
| SessionManagementService | generateWeeklySessions() | Auto-create next week's sessions | NOT registered (manual only) |
| ScheduleConflictResolver | applyHolidayOverrides() | Bulk-cancel sessions in date range | Manual call only |

---

## 12. Menu Registration

**No sidebar menu registered.** The Sessions module is accessed via direct URL or linked from other modules (Training, Academies). Only permissions are registered in `bootstrap.php`.

---

## 13. Key Design Patterns

1. **Upsert attendance**: Existing records are updated, new ones inserted — safe for re-submission
2. **Automatic status transition**: First attendance submission moves session from 'scheduled' to 'in_progress'
3. **Credit-based makeup system**: Compensatory credits with expiration, one credit per missed session
4. **Multi-dimensional feedback**: rating + energy + enjoyment + difficulty + comments, with anonymous option
5. **Calendar-first approach**: Both list and calendar views with academy/group/coach filters
