# Avancement — Lot 5 (Pilotage / Reporting, Module 5, §8 du cahier des charges v4.1)

*Même principe que `documentation/avancement-lot4.md`. Points hors
scope consolidés dans `documentation/backlog-v2.md` ; questions de
conception ouvertes dans `documentation/decisions-a-prendre.md`.*

## §8.2 Fonctionnalités

| Fonctionnalité | État | Détail |
| --- | --- | --- |
| Tableau de bord : taux d'occupation, effectif par groupe | ✅ | Par groupe et global, à partir des données déjà modélisées (Module 3) — 2026-08-05 |
| Statistiques : inscriptions, chiffre d'affaires, impayés | ✅ | Compteurs par statut, somme facturée hors annulées, liste + export CSV — 2026-08-05 |
| Reporting RH basique | ✅ | Effectif par rôle, comptes actifs/révoqués, accès temporaires expirés — 2026-08-05 |

## Détail (livré le 2026-08-05, incrément unique)

Module 4 clos (10/10 sous-items applicables — e) acomptes/dépôt de
garantie confirmé sans objet par le client, cf.
`documentation/avancement-lot4.md`). Module 5 est la suite logique
dans l'ordre du cahier des charges (§3).

**Aucune nouvelle entité** : ce module est un pur agrégateur en
lecture sur des données déjà modélisées par les modules précédents
(`Groupe`/`Enfant` — Module 3 ; `User` — Lot 0 ; `Facture`/`Paiement`/
`AvoirFacture` — Module 4 ; `PreInscription` — Module 3). Aucune
migration.

Décisions de scope :
- **Route `/pilotage`, `ROLE_DIRECTION`** — préfixe dédié (règle
  `access_control` ajoutée dans `security.yaml`) plutôt que de
  surcharger `/administration` (déjà `ROLE_DIRECTION` mais dédié à un
  autre module), cohérent avec le découpage par module fonctionnel
  déjà en place (`/comptabilite`, `/equipe`, `/administration`).
- **Effectif par groupe** réutilise directement
  `GroupeRepository::compterEnfantsActifs()`, déjà présent depuis le
  Module 3 (§6.3) — aucune nouvelle requête d'agrégation.
- **Taux d'occupation** calculé par groupe (`effectif / capaciteMax`)
  et globalement (somme des effectifs / somme des capacités
  définies) ; `null` si aucune capacité n'est renseignée sur un
  groupe — pas de nouveau champ de capacité sur `Etablissement`
  (absent du modèle, confirmé).
- **Reporting RH "basique" volontairement limité** : le cahier des
  charges ne prévoit aucune substance RH (pas de salaire, contrat de
  travail, absences) et aucune n'existe dans le modèle de données.
  Le reporting se limite donc à l'effectif du personnel par rôle, aux
  comptes actifs vs révoqués et aux accès temporaires expirés — les
  seules données réellement présentes sur `User`/`Groupe` — plutôt que
  d'inventer un module RH complet non demandé.
- **Chiffre d'affaires et impayés excluent les factures annulées**
  (`AvoirFactureService::estAnnulee()`, Module 4 j) — une facture
  annulée n'est ni du chiffre d'affaires réel ni un impayé à relancer.
- **Impayés réutilise le calcul déjà établi** par
  `SuiviPaiementsController`/`PaiementService::statut()`/
  `montantRestant()` (Module 4, d), avec le filtre d'exclusion des
  annulées en plus.
- **Export CSV limité aux impayés** (liste actionnable, à relancer),
  pas au chiffre d'affaires (un seul nombre) ni aux inscriptions
  (quelques compteurs) — mieux affichés à l'écran. Même pattern BOM
  UTF-8 + `;` que `ExportComptableService` (Module 4, i), dupliqué
  plutôt que partagé (cohérent avec les autres duplications déjà
  actées dans ce module).

**Nouvelle méthode `PreInscriptionRepository::findParEtablissement()`**
(filtrée par établissement, contrairement aux méthodes existantes
`findParStatut()`/`findToutes()`, reliquat mono-site non filtré — non
modifiées, pas de régression sur `InscriptionController`).

**Nouveau `TableauDeBordService::genererVue(Etablissement): array`** :
groupes (effectif/capacité/taux) + totaux + personnel (par rôle,
actifs/révoqués, accès expirés).

**Nouveau `StatistiquesService`** : `inscriptions()` (compte par
statut), `chiffreAffaires()`, `impayes()`, `exporterImpayes()` (CSV,
journalisé comme `ExportComptableService`).

**Nouveau `PilotageController`** sous `/pilotage` : `GET /pilotage`
(dashboard), `GET /pilotage/statistiques` (filtre de période
optionnel), `GET /pilotage/statistiques/impayes.csv` (export).

**Nouveaux templates** : `templates/pilotage/_layout.html.twig`,
`dashboard.html.twig`, `statistiques.html.twig`.

Vérifié manuellement (serveur PHP local + curl, comptes Direction/
Comptabilité/Éducateur), avec les données déjà en base :
- Dashboard : groupe "Bébés" (3 enfants actifs, pas de capacité
  définie → taux "—"), groupe "Moyens" (0 enfant, capacité 10 → 0 %),
  totaux **3 effectif / 10 capacité / 30 %** — exact. Personnel : 3
  comptes (1 par rôle Direction/Comptabilité/Éducateur), tous actifs,
  aucun accès expiré.
- Statistiques : inscriptions **1 admise / 1 refusée** (0 en
  attente/liste d'attente) — exact. Chiffre d'affaires **52500.00**
  (3000 + 45000 + 4500, la facture de 50000 étant annulée — exclue
  correctement). Impayés : 3 lignes (FAC-2026-0002 3000.00 impayée,
  FAC-2026-0003 45000.00 partiellement payée avec 20000.00 restant dû
  après corrections de paiement du Module 4 j, FAC-2026-0004 4500.00
  impayée) — la facture annulée n'apparaît pas dans la liste.
- Export CSV `impayes.csv` : BOM UTF-8 + `;` + 3 lignes correctes,
  journalisé (`ACTION_CONSULTATION`, `Facture`, `rapport: impayes`).
- **403 pour Comptabilité et pour Éducateur** sur `/pilotage` (garde
  `ROLE_DIRECTION` de `security.yaml`, Direction n'hérite pas vers le
  bas dans le sens inverse).

`php bin/phpunit` : 214 tests, 511 assertions, tous verts (2 nouveaux
fichiers de tests, 12 cas). `doctrine:schema:validate` : mapping et
base synchronisés (aucune migration, aucune nouvelle entité).
`lint:twig`/`lint:container` : OK.

## Prochaine étape

Module 5 clos. Module 6 (Communication générale, §9 : annonces
générales, calendrier des événements, sondages simples) est la suite
logique dans l'ordre du cahier des charges (§3).
