---
description: Attendance auto-generation, grace periods, auto-absent, threshold enforcement.
globs: ["app/Domain/Attendance/**", "app/Domain/Training/**"]
---

# Attendance Engine

## Auto-Generation

When a training session is created:
1. Get all ACTIVE enrollments for the group → create `expected` records (subject_type='participant')
2. Get all ACTIVE assignments targeting this session/group → create `expected` records (subject_type matches role)
3. Do NOT generate for: frozen participants, cancelled enrollments

When enrollment activated:
- Generate `expected` for all FUTURE sessions of that group

When enrollment cancelled:
- DELETE all `expected` records for future sessions (keep past records intact)

## Status Values

```
expected, present, late, excused, absent, no_show, left_early, partial, cancelled, exempt
```

## Grace Period

- `actual_check_in <= expected_start + grace_period` → `present`
- `actual_check_in > expected_start + grace_period` → `late`, calculate `late_minutes`
- Grace periods: configurable per academy via system_settings
  - Participants: default 15 min
  - Trainers: default 10 min

## Auto-Absent (hourly job)

Records where:
- `status = 'expected'`
- Session `end_time` + 2 hours has passed
- No manual marking done

→ Set `status = 'absent'`, flag for admin review

## Attendance Rate Formula

```
rate = (present + late + partial) / (total - cancelled - exempt) × 100
```

- `excused` does NOT count (neither positive nor negative)
- `absent` + `no_show` count AGAINST

## Threshold Enforcement

- Consecutive absences (default 5) → auto-suspend participant
- Attendance % below minimum (default 75%) → notify guardian + admin
- Both thresholds configurable per academy in system_settings

## Assignment Override

Session-level assignment OVERRIDES group-level for that session:
- If session has its own trainer assignment → use that
- If not → fall back to group assignment
- The overridden trainer is NOT expected at that session
