# Avancement - Lot 1 (Espace Équipe éducative, §5 du cahier des charges v4.1)

*Mis à jour après chaque fonctionnalité livrée. Remplace le rôle de suivi de
`reprise-lot1-creche.md`, qui reste comme trace historique du point de
départ du Lot 1.*

## §5.2 Fonctionnalités

| § | Fonctionnalité | État | Détail |
| --- | --- | --- | --- |
| a) | Authentification & organisation | ✅ | Connexion session (`SecurityController`), vue par groupe (`GroupeVoter`) |
| b) | Saisie du suivi quotidien | ✅ | Repas/sieste/soins/activités/humeur, individuel + groupé (`SuiviQuotidienService`, `EquipeController`, `FicheEnfantController`) |
| **c)** | **Photos et vidéos + consentement** | ✅ | `PhotoService`, `PhotoController`, `PhotoDownloadController`, écran `photos_galerie.html.twig` - 2026-08-02 (photos uniquement, vidéos en backlog) |
| d) | Présences & départs | ✅ | `PresenceService`, `PersonneAutoriseeService`, `PresenceController`, écran `presences_groupe.html.twig` + `liste_evacuation.html.twig` - 2026-08-02 |
| e) | Transmissions / relève d'équipe | ✅ | `TransmissionEquipeService`, `TransmissionEquipeController`, écran `releve_equipe.html.twig` - 2026-08-02 |
| f) | Messagerie avec les parents | ✅ | `MessageService`, `MessagerieParentController`, `MessageEquipeController`, `AdministrationMessagerieController` - 2026-08-02 |
| g) | Alertes prioritaires | ✅ | Version minimale (`AlerteEnfant`, `AlerteService`) - sera absorbée par le dossier médical/familial du Lot 3 |
| **h)** | **Sécurité & santé (médicaments)** | ✅ | `AdministrationMedicamentService`, `TraitementService`, `IncidentService`, `SanteController`, écran `sante_registre.html.twig` - 2026-08-02 |
| **i)** | **Mode dégradé (panne réseau)** | ✅ | `FicheSecoursService`, `FicheSecoursController`, écran `fiche_secours.html.twig` - 2026-08-02 (agrège l'existant, aucune nouvelle entité) |
| j) | Ergonomie de saisie | ⚠️ | Brouillons + saisie groupée faits ; favoris/duplication pas encore |

**Tout le §5.2 du Lot 1 est désormais traité.**

## Revue de code d'ensemble (2026-08-03)

Revue critique du Lot 1 complet (3 passes en parallèle : modèle de
données, services/logique métier, contrôleurs/sécurité/templates).
Corrections apportées suite à cette revue :

- **Notifications email robustes** (`NotificationMessageMailer`) :
  validation du format email à la saisie (`DestinataireNotificationService`,
  `CompteParentService`) + capture large des échecs d'envoi (logués via
  `LoggerInterface`, jamais remontés) - une adresse mal formée en base ne
  casse plus l'envoi d'un message.
- **Races corrigées sur les patterns get-or-create** (`Presence`,
  `AdministrationMedicament`) : remplacées par un upsert SQL atomique
  (`PresenceRepository::assurerLigneDuJour`,
  `AdministrationMedicamentRepository::assurerOccurrenceDuJour`) - vérifié
  empiriquement qu'un simple `catch` après un `flush()` en échec fermait
  définitivement l'EntityManager pour le reste de la requête.
- **`security.yaml`** : filet de sécurité deny-par-défaut
  (`{ path: ^/, roles: ROLE_USER }`) pour toute future route qui
  oublierait sa propre règle d'accès.
- **`StockageLocalPhoto`** : validation du format de la clé de stockage
  avant de la concaténer dans un chemin disque (respecte enfin le contrat
  "clé opaque" de `StockagePhotoInterface`).
- **Comparaisons d'utilisateurs robustes** : nouvelle méthode
  `User::estMemeCompte()` (par id si les deux comptes sont persistés,
  repli sur l'identité d'objet sinon) remplace les comparaisons `===`
  nues dans `AdministrationMedicamentService::verifier()` (double
  validation) et `ParentEnfantVoter`.
- **Verrouillage optimiste sur `Photo`** (`#[ORM\Version]`) : deux
  éditions concurrentes (flouter/recadrer/publier) ne s'écrasent plus en
  silence, `PhotoController` traduit le conflit en message "réessayez".
- **`AdministrationMedicamentService::corriger()`** exige désormais que
  le statut soit déjà `ADMINISTRE`.
- **`DestinataireNotificationMessage.email`** unique en base ; réactive
  une adresse désactivée plutôt que de créer un doublon.
- Suppression d'une méthode morte sur `Message`.

Point de politique confirmé avec l'utilisateur (pas un bug) : la boîte de
réception équipe (`MessageEquipeController`) reste volontairement non
filtrée par groupe.

## Détail de d) Présences & départs (livré le 2026-08-02)

Couvert :
- Présence prévue vs réelle, retard/départ anticipé calculés à l'affichage
  (jamais stockés)
- Remise/récupération par responsable légal, personne autorisée active, ou
  texte libre
- Personnes autorisées "actives maintenant" (permanentes validées +
  ponctuelles du jour non expirées), calculées à la volée
- Blocage automatique du départ si restriction familiale/judiciaire active
  (même règle que celle déjà en place pour `AlerteEnfant`, §4.2.e)
- Signalement d'une tentative de départ non autorisée (nouvelle entité
  `TentativeAccesNonAutorisee`)
- Correction a posteriori d'un pointage, motif obligatoire, tracée dans
  `JournalAudit` (premier Service à écrire réellement dans ce journal -
  jusqu'ici seulement annoncé dans des docblocks)
- Liste d'évacuation en temps réel, vue imprimable
- Déclaration d'une autorisation ponctuelle, réservée à Direction (§11),
  expiration fin de journée automatique, blocage si restriction active

Hors scope, assumé explicitement (voir plan d'implémentation) :
- "Proposer une personne autorisée permanente" reste au parcours Parents
  (Lot 2, pas encore construit)
- Pas de planning/horaires contractuels en base (Lot 3) : heure prévue
  saisie manuellement par l'éducateur
- Vérification d'identité réelle = geste humain de l'éducateur, non
  automatisée

Vérifié manuellement en conditions quasi réelles (serveur PHP local + curl,
comptes éducateur et direction des fixtures) : arrivée/départ, blocage sur
restriction, signalement, correction avec trace dans `journal_audit`, liste
d'évacuation, et déclaration d'autorisation ponctuelle réservée à Direction
(403 pour un éducateur).

## Détail de e) Transmissions / relève d'équipe (livré le 2026-08-02)

Note interne (`TransmissionEquipe`) rattachée à un groupe, optionnellement à
un enfant précis. Écran dédié `/equipe/groupe/{id}/releve` : fenêtre
glissante des 3 derniers jours (pas seulement "aujourd'hui", pour qu'une
note du soir reste visible à l'équipe du lendemain matin), formulaire
d'ajout avec sélection facultative d'un enfant du groupe. Append-only,
non visible des parents (aucun accès Lot 2 n'existe encore de toute façon).

Vérifié manuellement (serveur PHP local + curl, compte éducateur des
fixtures) : ajout d'une note générale et d'une note liée à un enfant, ordre
antéchronologique, contenu obligatoire (400 si vide), enfant hors groupe
rejeté (400), groupe non affecté à l'éducateur rejeté (403), CSRF invalide
rejeté (400).

## Détail de f) Messagerie avec les parents (livré le 2026-08-02)

Décidé avec l'utilisateur : v1 simple (un message par enfant, jamais un
chat de groupe partagé entre familles), avec un vrai **compte parent**
plutôt qu'un accès par token - le Lot 0 avait déjà posé le terrain
(`ResponsableLegal.compteUtilisateur`, `access_control: ^/parents ->
ROLE_PARENT`), donc ce n'était pas un chantier Lot 2 complet. Couvre les
deux sens (parent→équipe et équipe→parent, y compris l'initiative de
l'équipe, pas seulement la réponse) :

- Entité unique `Message`, flat comme `Repas`/`Activite` : la conversation
  d'un enfant = tous ses messages triés par date. Diffusion "à tout un
  groupe" = fan-out (un `Message` par enfant actif), jamais un fil partagé
- Priorité normale/urgente sur chaque message
- "Prise en charge" **atomique** (UPDATE DQL conditionnel, jamais
  lecture-puis-écriture) pour qu'aucun message parent ne reçoive deux
  réponses simultanées - `MessageDejaPrisEnChargeException` sinon
- Boîte de réception équipe non restreinte par groupe (§11), fil par
  enfant borné comme le reste du Lot 1 (`GroupeVoter`)
- Nouveau `ParentEnfantVoter` : accès borné à ses propres enfants côté
  parent
- Notifications email (nouvelle dépendance `symfony/mailer`, best-effort,
  n'échoue jamais l'action métier) : liste de diffusion équipe
  configurable (`DestinataireNotificationMessage`) pour les messages
  parents, email aux responsables légaux pour les messages équipe
- Création de compte parent par Direction (`/administration/messagerie`) :
  email + mot de passe temporaire, pas de self-service par invitation -
  explicitement différé, c'est un chantier Lot 2 à part entière

Hors scope, assumé explicitement :
- Self-service (invitation par email, parent définit son propre mot de
  passe) - Direction crée le compte directement pour l'instant
- "Proposer une personne autorisée permanente" par un parent (déjà noté
  hors scope pour d), toujours vrai ici - la messagerie ne s'en occupe pas

Vérifié manuellement (serveur PHP local + curl, 3 rôles : parent, éducateur,
Direction) : envoi d'un message urgent par un parent, boîte de réception
équipe, prise en charge (le message disparaît du formulaire "à traiter" et
affiche le nom du preneur), réponse, diffusion à tout un groupe (fan-out
vérifié en base, un message par enfant actif), création de compte parent
par Direction (refus propre si compte déjà existant), ajout/désactivation
d'un destinataire de notification, et toutes les frontières d'accès
(parent → enfant d'une autre famille : 403 ; parent → `/equipe` : 403 ;
éducateur → `/administration` : 403 ; éducateur → `/parents` : 403).

## Détail de c) Photos et vidéos + consentement (livré le 2026-08-02)

Photos uniquement (vidéos en backlog, cf. `documentation/backlog-v2.md`).
Stockage hors base de données (§2.5), derrière une interface à nous
(`StockagePhotoInterface`) avec une seule implémentation V1 (disque local,
`StockageLocalPhoto`, hors `public/`) - le jour d'une bascule vers un
stockage objet compatible S3 en production, seule une nouvelle
implémentation de cette interface est à écrire, aucun Service/Contrôleur
ne change. `symfony/filesystem` (déjà présent) et l'extension GD
(déjà disponible) suffisent, aucune nouvelle dépendance.

Couvert :
- Upload avec détection réelle du type MIME sur le contenu (jamais une
  donnée fournie par le client), formats JPEG/PNG/WebP, 8 Mo max
- Compression automatique à l'import + génération d'une miniature (GD)
- **Blocage automatique de la publication** si un enfant tagué n'a pas de
  consentement `SUIVI_QUOTIDIEN_INTERNE` accordé - recalculé à chaque
  fois, jamais stocké (même principe que la restriction familiale/
  judiciaire pour les présences, §4.2.e)
- Recadrage et floutage d'une zone rectangulaire (GD), avec retrait
  optionnel de l'enfant concerné du taggage - lève le blocage
- Liens temporaires signés (HMAC + expiration, clé = `%kernel.secret%`,
  aucun nouveau secret) via une route dédiée publique
  (`PhotoDownloadController`) - jamais d'URL publique permanente
- Suppression réelle des fichiers physiques (seule exception du projet à
  "désactiver plutôt que supprimer", exigée explicitement par §2.5)
- Traçabilité minimale des téléchargements (`JournalAudit`, image complète
  seulement, pas les miniatures)

Hors scope, assumé explicitement - consolidé dans
`documentation/backlog-v2.md` avec tous les autres points hors scope du
Lot 1 (vidéos, antivirus, quota de stockage, sélecteur graphique de zone,
correction EXIF, consultation côté parents).

Vérifié manuellement (serveur PHP local + curl, éducateur des fixtures) :
upload, blocage de publication pour un enfant sans consentement, floutage
de la zone entière pour le retirer du taggage → publication acceptée
ensuite, téléchargement via lien signé valide, rejet propre (403, pas de
redirection vers `/login`) pour signature falsifiée / lien expiré / clé
inconnue, suppression réelle des fichiers sur disque vérifiée après coup,
et accès refusé (403) à un éducateur sur un groupe qui n'est pas le sien.

## Détail de h) Sécurité & santé + i) Mode dégradé (livrés le 2026-08-02)

Traités ensemble : h) est le point le plus exigeant du Lot 1 (nouvelles
entités, préconditions, double validation) ; i) se limite en pratique à un
écran d'agrégation en lecture seule sur des données déjà toutes en base,
et sert aussi de "fiche d'urgence imprimable" pour h) - un seul écran
couvre les deux points du cahier des charges.

**h) Sécurité & santé** - `Traitement` (prescription/protocole),
`AdministrationMedicament` (une occurrence par jour, même pattern
get-or-create que `Presence`), `Incident` (registre incidents/accidents).
Couvert :
- Préconditions vérifiées **avant** toute administration (§5.2.h : "aide à
  empêcher, pas seulement enregistrer après coup") : traitement autorisé
  par un responsable légal, période de validité, utilisateur habilité
  (`User::habiliteMedicaments`, présent depuis le Lot 0 mais jamais
  vérifié nulle part avant ce jour)
- **Double validation nominative et horodatée séparément** pour les
  traitements sensibles : refuse si la même personne tente d'administrer
  et de vérifier - vérifié en conditions réelles (éducateur bloqué,
  Direction accepte la vérification)
- Incohérence dose/horaire détectée automatiquement (calculée, jamais
  stockée), tolérance de 60 minutes
- Motif obligatoire pour une non-administration
- Correction a posteriori tracée dans `JournalAudit` (même mécanique que
  `Presence::corrigerPointage`)
- Registre des incidents avec traçage de l'appel au responsable légal

**i) Mode dégradé** - aucune nouvelle entité. `FicheSecoursService` agrège
`Presence`, `EnfantResponsableLegal.estContactUrgence`, `AlerteEnfant` et
`PresenceService::personnesAutoriseesPour` (déjà existant) en une fiche
imprimable par groupe (`window.print()`, comme `liste_evacuation.html.twig`
- pas de nouvelle dépendance PDF). Le pointage provisoire pendant une
panne n'a pas de code dédié : `PresenceController` pour pointer sur
papier reconstitué, `PresenceService::corrigerPointage` pour resaisir a
posteriori au retour du réseau - déjà livrés au d).

Vérifié manuellement (serveur PHP local + curl, éducateur + Direction des
fixtures) : administration d'un traitement sensible, refus de la double
validation par la même personne, validation réussie par une personne
différente habilitée, déclaration d'incident + traçage de l'appel
responsable, fiche de secours à jour (contacts, allergies, personnes
autorisées, liste d'évacuation), accès refusé (403) à un éducateur sur un
groupe qui n'est pas le sien sur les deux écrans.

## Prochaine étape

**Tout le §5.2 du Lot 1 est traité.** Restent en ergonomie (j) : favoris
et duplication de saisie (amélioration, pas un point bloquant). Le Lot 1
peut être considéré fonctionnellement complet pour une revue d'ensemble
avec l'utilisateur avant de décider : approfondir l'ergonomie, écrire des
tests plus larges (fonctionnels/E2E), ou démarrer le Lot 2 (Espace
Parents), pour lequel une bonne partie du socle existe déjà (comptes
parents, `ParentEnfantVoter`, espace `/parents`).
