# Cadrage — Plateforme de gestion des présences pour écoles de musique

> Document de référence. Statut : cadrage validé, prêt pour la Phase 0.
> Rôles : **David** = chef de projet · **Claude** = développeur.

---

## 1. Vision

Plateforme **SaaS multi-écoles** (multi-tenant) clé en main pour écoles de musique, dont David est l'opérateur. Chaque école est un espace autonome et isolé. Le socle fonctionnel hérite de l'idée initiale (validation de présence par **QR code** affiché en salle), enrichi des besoins métier d'une école de musique : planning, inscriptions, suivi RH des professeurs, suivi financier des élèves, conformité RGPD.

Principe directeur : **simplicité et fluidité d'usage** avant tout. Pas de système anti-fraude complexe. La validation du début/fin de cours par le professeur est prévue mais **jamais bloquante**.

### Hiérarchie d'accès (4 niveaux)

1. **Super-admin (plateforme)** — David : crée/suspend les écoles, gère leurs abonnements. N'accède pas aux données pédagogiques d'une école sauf support explicite.
2. **École (tenant)** — l'unité d'isolation. Tout porte un `school_id`.
3. **Admin / Professeur d'école** — rôles internes, permissions fines (un prof ne voit que ses élèves).
4. **Élève (+ responsable légal si mineur)** — accès minimal, mobile-first.

---

## 2. Décisions validées

| Sujet | Décision |
|---|---|
| Modèle | SaaS multi-écoles, David opérateur, architecture multi-tenant |
| Isolation tenant | Base unique + colonne `school_id` + global scope (pas de base par école en V1) |
| Hébergement V1 | OVH mutualisé **Perform 4** (MySQL, SSH, cron horaire) |
| Base de données | MySQL/MariaDB (au lieu de PostgreSQL) |
| Paiements élèves | V1 = suivi simple (dû/payé, relances). Paiement en ligne = évolution |
| Rémunération profs | États de **pré-paie** mensuels (heures × taux), exportables. Pas de paie intégrée |
| Règle pré-paie | Basée sur la **séance tenue** (réel scanné, sinon prévu), **indépendante de la présence des élèves**. Séance non annulée = due au prof |
| Cours / séance | **Séparation** cours (offre + récurrence) / séance (occurrence datée) |
| Tarification | Plusieurs tarifs par école et par saison (adulte/enfant, résident/hors commune…), avec éligibilité |
| Forfaits | Annuel (1×) ou mensuel (N×), **prépayés**, échéancier généré à l'inscription |
| Paiement à la séance | Pour profs indépendants : chaque séance tenue génère une charge |
| Absence non annulée à temps | Facturée quand même (délai d'annulation paramétrable par école) |
| Adhésion association | Comptée **par élève**, par saison, avec règlement distinct (ou lissée dans le prix, au choix de l'école) |
| Environnement de dev | **Local** sur la machine de David (Laravel Herd), déploiement OVH après validation |

### Innovations retenues

- **Élève sans mot de passe** (lien magique / confirmation après scan).
- **QR statique par salle** : un QR fixe résout automatiquement la séance en cours (salle + heure + prof).
- **Planning glisser-déposer** avec récurrences + flux calendrier `.ics` abonnable.
- **PWA hors-ligne** pour le scan (file d'attente + synchro).
- **Relevés de pré-paie auto-générés** depuis les heures réalisées.

---

## 3. Architecture

### Modules fonctionnels

- Identité & accès (écoles, utilisateurs, rôles, responsables légaux)
- Cours & planning (récurrents vs ateliers, salles, calendrier)
- Inscriptions (élève↔cours, individuel/collectif)
- Présence (QR) — le socle
- RH profs (heures, taux, relevés de pré-paie)
- Finances élèves (dû/payé, relances)
- Notifications (email + in-app, extensible SMS/push)
- Exports & tableaux de bord (PDF, Excel, filtres)
- RGPD (accès/modif, suppression, anonymisation, journalisation)

### Contraintes de l'hébergement mutualisé (et réponses)

- **Pas de worker permanent** → notifications « immédiates » envoyées **en synchrone** au moment de l'action.
- **Cron ~1h minimum** → rappels 24h/12h pilotés par le scheduler Laravel via un cron horaire (granularité suffisante).
- **Limites mémoire/temps** → gros exports bornés (par école/période), générés via cron si besoin.
- **Email** → service transactionnel européen (Brevo/Mailjet) plutôt que SMTP OVH.
- **Évolution** : si temps réel ou gros traitements nécessaires → bascule vers un **VPS OVH** sans réécriture.

---

## 4. Stack technique

- **Backend** : Laravel 12, PHP 8.3+, MySQL/MariaDB
- **Auth** : Laravel Sanctum (token pour la PWA)
- **Rôles/permissions** : `spatie/laravel-permission` (teams = `school_id`)
- **Multi-tenant** : trait `BelongsToTenant` + global scope `school_id` (approche légère)
- **Journalisation** : `spatie/laravel-activitylog`
- **Front admin/prof** : Inertia.js + Vue 3 + Tailwind (Vite)
- **Surface élève** : PWA (`vite-plugin-pwa`)
- **QR** : `simplesoftwareio/simple-qrcode`
- **Export Excel** : `maatwebsite/excel` · **PDF** : `barryvdh/laravel-dompdf`
- **Email** : Brevo (transactionnel, RGPD)
- **Dev local** : Laravel Herd (Windows) ; déploiement OVH via SSH (`git pull` + `composer install` + `php artisan migrate`)

---

## 5. Modèle de données

Principes transversaux : chaque table métier porte `school_id` (isolation) ; soft delete (`deleted_at`) sur les entités sensibles (RGPD) ; `seasons` cadre inscriptions, tarifs et adhésions par année scolaire.

### Diagramme entité-relation

```mermaid
erDiagram
  SCHOOLS ||--o{ SEASONS : "saisons"
  SCHOOLS ||--o{ USERS : "comptes"
  SCHOOLS ||--o{ SUBSCRIPTIONS : ""
  PLANS ||--o{ SUBSCRIPTIONS : ""
  SCHOOLS ||--o{ TEACHERS : ""
  USERS ||--o| TEACHERS : "login"
  SCHOOLS ||--o{ STUDENTS : ""
  USERS ||--o| STUDENTS : "login"
  SCHOOLS ||--o{ GUARDIANS : ""
  STUDENTS }o--o{ GUARDIANS : "guardian_student"
  SCHOOLS ||--o{ SUBJECTS : ""
  TEACHERS }o--o{ SUBJECTS : "teacher_subject"
  SCHOOLS ||--o{ ROOMS : ""
  SCHOOLS ||--o{ COURSES : ""
  SUBJECTS ||--o{ COURSES : ""
  TEACHERS ||--o{ COURSES : ""
  ROOMS ||--o{ COURSES : ""
  COURSES ||--o{ SESSIONS : "occurrences"
  ROOMS ||--o{ SESSIONS : ""
  TEACHERS ||--o{ SESSIONS : "remplaçant"
  COURSES ||--o{ ENROLLMENTS : ""
  STUDENTS ||--o{ ENROLLMENTS : ""
  SEASONS ||--o{ ENROLLMENTS : ""
  TARIFFS ||--o{ ENROLLMENTS : ""
  SESSIONS ||--o{ ATTENDANCES : ""
  STUDENTS ||--o{ ATTENDANCES : ""
  SCHOOLS ||--o{ QR_CODES : ""
  ROOMS ||--o{ QR_CODES : "1 par salle"
  QR_CODES ||--o{ SCAN_EVENTS : ""
  SESSIONS ||--o{ SCAN_EVENTS : "résolue"
  TEACHERS ||--o{ PAYROLL_STATEMENTS : ""
  PAYROLL_STATEMENTS ||--o{ PAYROLL_LINES : ""
  SESSIONS ||--o{ PAYROLL_LINES : ""
  SEASONS ||--o{ TARIFFS : ""
  STUDENTS ||--o{ CHARGES : ""
  ENROLLMENTS ||--o{ CHARGES : "échéancier"
  TARIFFS ||--o{ CHARGES : ""
  SESSIONS ||--o| CHARGES : "à la séance"
  CHARGES ||--o{ PAYMENTS : ""
  SEASONS ||--o{ MEMBERSHIPS : ""
  STUDENTS ||--o{ MEMBERSHIPS : "adhésion"
  MEMBERSHIPS ||--|| CHARGES : "règlement"
  USERS ||--o{ NOTIFICATIONS : ""
  SCHOOLS ||--o{ DATA_REQUESTS : ""
  USERS ||--o{ CONSENTS : ""
  SCHOOLS ||--o{ ACTIVITY_LOGS : ""
  SCHOOLS ||--o{ EXPORTS : ""
```

### Entités principales

- **schools** : name, slug, status, address, contact, timezone, settings (json)
- **plans** / **subscriptions** : offres et abonnements (super-admin)
- **seasons** : name, start_date, end_date, active
- **users** : school_id (null=super-admin), first/last name, email, phone, password (nullable), status, role
- **teachers** : user_id, employment_type, default_hourly_rate, contract_hours · **teacher_subject**
- **students** : user_id (nullable), birthdate, is_resident, address, contacts, status · **guardians** + **guardian_student** (relationship, is_primary, authorizations)
- **subjects** · **rooms** (capacity)
- **courses** : title, subject_id, teacher_id, room_id, type (récurrent/atelier/individuel/collectif), capacity, recurrence_rule (RRULE), start/end_date
- **sessions** : course_id, date, start/end (prévus), room_id, teacher_id, status (prévu/en_cours/terminé/annulé/déplacé), actual_start_at, actual_end_at, moved_from_session_id, cancel_reason, comment
- **enrollments** : course_id, student_id, season_id, tariff_id, billing_mode, status
- **attendances** : session_id, student_id, status (présent/absent/absence_signalée/absence_excusée), validation_mode (qr/manuel/auto), validated_by, validated_at, signaled_at, reason — unique (session, student)
- **qr_codes** : room_id, token (signé), active · **scan_events** : qr_code_id, session_id, actor, scan_type (presence/course_start/course_end), scanned_at
- **payroll_statements** : teacher_id, period, total_hours, hourly_rate, amount, status · **payroll_lines** : session_id, hours, basis (réel/prévu)
- **tariffs** : season_id, name, billing_mode (forfait_annuel/forfait_mensuel/a_la_seance), amount, sessions_included, installments_count, audience, residency
- **charges** : student_id, enrollment_id, tariff_id, session_id (nullable), amount_due, due_date, status · **payments** : charge_id, amount, method, received_at
- **memberships** : student_id, season_id, amount, charge_id, status
- **notifications** : user_id, channel, type, data, related, sent_at, read_at
- **data_requests** : type, status, resolution · **consents** · **activity_logs** · **exports**

---

## 6. API par module

`/api/v1`, token Sanctum, accès filtré par rôle et `school_id`. ⭐ = inclus dans le MVP.

### Auth & compte
- ⭐ `POST /auth/login` · `POST /auth/logout` · `GET /auth/me`
- `POST /auth/forgot-password` · `POST /auth/reset-password`
- ⭐ `POST /auth/magic-link` · `POST /auth/magic-link/verify` (élève sans mot de passe)
- ⭐ `PUT /me/profile`

### Scan QR (flux public)
- ⭐ `GET /scan/{qrToken}` (résout salle + séance + élèves attendus)
- ⭐ `POST /scan/{qrToken}/identify`
- ⭐ `POST /scan/{qrToken}/attendance`
- ⭐ `POST /scan/{qrToken}/teacher/start` · `POST /scan/{qrToken}/teacher/end`

### Super-admin plateforme
- `GET|POST|PUT|DELETE /admin/schools` · `/admin/plans` · `/admin/subscriptions` · `GET /admin/dashboard`

### Paramètres & saisons
- ⭐ `GET|PUT /settings` · `GET|POST|PUT|DELETE /seasons`

### Utilisateurs & rôles
- ⭐ `GET|POST|PUT|DELETE /users` · `GET /roles`

### Professeurs
- ⭐ `GET|POST|PUT|DELETE /teachers` · `GET /teachers/{id}/planning` · `GET /teachers/{id}/stats`

### Élèves & responsables
- ⭐ `GET|POST|PUT|DELETE /students` · `GET /students/{id}/history` · `GET /students/{id}/upcoming`
- ⭐ `GET|POST|PUT|DELETE /guardians` · `POST /students/{id}/guardians`

### Matières & salles
- ⭐ `GET|POST|PUT|DELETE /subjects` · `/rooms`

### Cours, séances & inscriptions
- ⭐ `GET|POST|PUT|DELETE /courses` · `POST /courses/{id}/generate-sessions`
- ⭐ `GET|POST|PUT|DELETE /sessions` · `GET /sessions?from=&to=&room=&teacher=`
- ⭐ `POST /sessions/{id}/move` · `POST /sessions/{id}/cancel`
- ⭐ `GET|POST|DELETE /courses/{id}/enrollments`

### Présence
- ⭐ `GET /sessions/{id}/attendances` · `POST /sessions/{id}/attendances/bulk`
- ⭐ `PUT /attendances/{id}` · `POST /students/{id}/absences`

### Finances élèves
- `GET|POST|PUT|DELETE /tariffs` · `GET /students/{id}/charges` · `GET|POST|PUT /charges`
- `POST /charges/{id}/payments` · `GET|POST /memberships`

### RH profs
- `GET /teachers/{id}/hours` · `POST /payroll-statements/generate` · `GET /payroll-statements[/{id}]` · `POST /payroll-statements/{id}/validate`

### Notifications
- ⭐ `GET /notifications` · `POST /notifications/{id}/read`

### Exports & tableaux de bord
- ⭐(Excel) `POST /exports` · `GET /exports/{id}` · `GET /dashboard` · `GET /reports/attendance` · `GET /calendar/{token}.ics`

### RGPD
- ⭐ `GET /me/data` · `POST /data-requests` · `GET|PUT /admin/data-requests`

---

## 7. Plan de développement

| Phase | Contenu | Point de validation |
|---|---|---|
| **0 — Fondations** | Repo, Laravel + MySQL, déploiement Perform, auth Sanctum, rôles, scoping multi-tenant | Page authentifiée déployée + isolation école A/B prouvée |
| **1 — Socle pédagogique** | Écoles, saisons, users/rôles, profs, élèves (+responsables), matières, salles, cours/séances (récurrence), inscriptions | Cours récurrent générant ses séances + inscriptions sans perte d'association |
| **2 — Présence & QR** | QR par salle, scan public, élève sans mdp, validation présence, scan prof début/fin, pointage manuel, signalement absence | Scénario réel salle : scan élève → présence ; scan prof → horaires réels |
| **3 — Espaces** | Espace prof (planning, présences, historique), espace élève (cours, historique, statuts), backoffice admin | Chaque rôle a un parcours utilisable de bout en bout |
| **4 — Notifications** | Emails synchrones sur événements, rappels 24h/12h (cron horaire), in-app, Brevo | Annulation → email ; rappel dans la bonne tranche |
| **➤ Fin MVP** | Phases 0–4 + tranche minimale Phase 6 (export Excel, espace élève, suppression RGPD) | — |
| **5 — Finances & RH** | Tarifs, échéancier forfait, paiement à la séance, charges/payments, adhésions, pré-paie profs | Inscription forfait → échéancier ; séance tenue → ligne de pré-paie ; adhésion à part |
| **6 — Exports, BI & RGPD** | Export Excel/PDF, dashboards filtrables, `.ics`, demandes RGPD complètes, journalisation, consentements | Export Excel conforme ; demande de suppression traçable |
| **7 — Durcissement & prod** | Tests, sécurité, RGPD, sauvegardes, doc, école pilote | Recette passée + retour terrain pilote |

### Au-delà de la V1
Paiement en ligne (Stripe/SEPA), SMS, push, app native, super-admin commercial, stats avancées, portail parent enrichi, migration VPS.

---

## 8. Méthode de travail

- Le code vit dans le dossier `Présence`, versionné avec Git.
- Développement **phase par phase** ; David valide à chaque point de contrôle avant d'enchaîner.
- Claude produit code + migrations + seeders + tests ; David lance en local (Herd) et teste.
- Déploiement OVH **uniquement du code validé**, déclenché par David (SSH + Git).
- La production n'est jamais un environnement de test.

---

## 9. Points à confirmer avant / pendant la Phase 0

1. **PHP du Perform** : confirmer PHP 8.3+ disponible (au moment du déploiement ; en local, Herd s'en charge). *(différé)*
2. **Stratégie d'URL des écoles** : sous-domaine par école (`ecole.domaine.fr`) ou domaine unique + sélection ? Nom de domaine de la plateforme ? *(à confirmer)*
3. **Dépôt de code** : GitHub privé (GitHub Desktop en cours d'installation). *(à confirmer)*
4. **Compte Brevo** (ou équivalent) — Phase 4.
5. **École pilote** + données réelles pour tester.
6. **Identité visuelle** : nom de la plateforme, logo, couleurs.
7. **Langue** : FR seul en V1 ou i18n dès le départ ?
8. **RGPD** : durée de conservation, responsable de traitement.
9. **Réglages d'école par défaut** : délai d'annulation, mode d'adhésion.
