# Application de gestion de crèche

Application web de gestion pour crèche (Côte d'Ivoire), développée sur
mesure à partir du cahier des charges `documentation/cahier-des-charges-creche-v4.1.md`
(v4.1). Couvre le suivi quotidien des enfants, les présences, la
facturation et la comptabilité, l'espace parent, la messagerie, le
pilotage/reporting, et la gestion administrative de l'établissement.

Le cahier des charges v4.1 est intégralement livré. Les décisions
prises après coup avec le client (fonctionnalités ajoutées, reportées
ou volontairement abandonnées) sont documentées au fil de l'eau dans
`documentation/backlog-v2.md` — c'est la source de vérité sur "qu'est-ce
qui a été construit et pourquoi", à consulter avant toute nouvelle
demande d'évolution pour éviter de redécouvrir un arbitrage déjà pris.

Pour un guide non technique (rôles, fonctionnalités, restrictions),
voir `documentation/guide-utilisateur.pdf`.

## Stack technique

- **PHP 8.3+**, **Symfony 7.4** (framework web, monolithique — pas de
  découpage microservices)
- **MySQL 8** (Doctrine DBAL/ORM 3) — choix explicite du cahier des
  charges §2, indépendant du `compose.yaml` par défaut généré par
  Symfony Flex (qui pointe vers Postgres et n'est **pas** utilisé en
  pratique sur ce projet, ni en dev ni en cible de production)
- **Twig** pour le rendu HTML, thème **Metronic 8.3** (Bootstrap 5)
  vendoré localement sous `public/assets/` (pas de CDN)
- **DataTables.net** (jQuery) pour la pagination/tri/recherche
  côté client des listes à fort volume (factures, dépenses, avances,
  journal d'audit) — protocole confiné à `App\Pagination\DataTablesAdapter`,
  seule classe du projet qui le connaît
- **Dompdf** pour la génération de PDF (factures, modèle de reçu)
- **PHPUnit 11** pour les tests (unitaires uniquement — aucun test
  fonctionnel/navigateur, cf. "Tests" plus bas)

## Choix d'architecture (à connaître avant de contribuer)

Ces conventions sont appliquées de façon rigoureusement uniforme dans
tout le code — les respecter en priorité à toute préférence
personnelle avant d'ajouter une fonctionnalité :

- **Pas de composant Symfony Form.** Tous les formulaires sont du HTML
  écrit à la main, avec jeton CSRF manuel (`csrf_token('intention')`
  côté Twig, `isCsrfTokenValid()` côté contrôleur). Choix assumé dès le
  départ, pas une dette technique.
- **Contrôleurs fins, Services épais.** Toute la logique métier
  (validation, calculs, transitions d'état) vit dans `src/Service/`.
  Un contrôleur lit la requête, appelle un service, gère le
  flash/redirect. Les vérifications de rôle (`denyAccessUnlessGranted`)
  se font systématiquement au niveau Contrôleur, jamais dans un
  Service.
- **Entités à mutation contrôlée.** Les entités qui suivent un workflow
  de validation (`Depense`, `MouvementFondateur`, `Facture`, etc.)
  n'exposent jamais de setter brut pour leur statut : uniquement des
  méthodes métier explicites (`marquerValide()`, `marquerRefuse()`,
  `resilier()`...), documentées "à appeler par le Service, jamais un
  simple setter". La plupart des entités de saisie sont par ailleurs
  immuables une fois créées (pas d'édition a posteriori) — la
  correction se fait par re-saisie, pour garder une traçabilité
  complète.
- **`StatutValidationFinanciere`** (`en_attente`/`valide`/`refuse`) et
  **`ValidationFinanciereGuard`** sont le point de vérité unique de la
  règle "double intervenant" (§7.2.j : la personne qui valide doit être
  différente de celle qui a saisi, sauf compte fondateur — cumul
  `ROLE_DIRECTION`+`ROLE_ADMIN_TECHNIQUE`, `User::peutAutoValiderFinancier()`).
  Toute nouvelle fonctionnalité financière soumise à double validation
  doit réutiliser ce couple, pas en réinventer un.
- **`JournalAudit`** (append-only, jamais purgé) trace les actions
  sensibles. **`NotificationEchouee`** trace les échecs d'envoi d'email
  jugés importants (sécurité/accès enfant, relance impayé, messagerie)
  — le reste (photo/vidéo publiée, proposition entre parents) reste en
  best-effort silencieux (log `warning` uniquement), par décision
  client explicite.
- **Aucune donnée n'est jamais supprimée en dur.** Archivage
  (`Enfant.actif`), résiliation, révocation, statut "refusé" — jamais
  de `DELETE` sur les entités métier. Seule `app:purge:donnees`
  supprime, selon des durées de conservation documentées par entité
  (§2.4).
- **Pas de N+1 sur les écrans à fort volume.** Les agrégations
  (trésorerie, statuts de paiement, sommes par catégorie) sont
  calculées en SQL (`SUM`/`GROUP BY`), jamais en itérant des entités
  hydratées en PHP — cf. `documentation/ameliorations-performance.md`
  pour l'historique des correctifs.

## Rôles applicatifs

Sept rôles, chacun gated par préfixe de route dans
`config/packages/security.yaml` (`access_control`, deny-by-default en
dernière règle) : `ROLE_PARENT`, `ROLE_EDUCATEUR`, `ROLE_COMPTABILITE`,
`ROLE_DIRECTION`, `ROLE_ADMIN_TECHNIQUE`, `ROLE_CANTINE`,
`ROLE_INVESTISSEUR`. Le détail (qui peut créer un compte de quel rôle,
ce que chacun voit/peut faire) est dans
`documentation/guide-utilisateur.pdf` — ce README ne duplique pas cette
matrice pour éviter qu'elle diverge en deux endroits.

`ROLE_CANTINE` et `ROLE_INVESTISSEUR` sont deux extensions hors
périmètre §11 d'origine, ajoutées après coup avec le client — cf.
`documentation/specificites-hors-cahier-des-charges.md` et
`documentation/backlog-v2.md`.

## Prérequis techniques / infrastructure

Cible d'hébergement réelle : **mutualisé OVH** (pas de VPS, pas d'accès
root, pas de démon persistant, pas de gestionnaire de paquets système).
Cette contrainte a déjà écarté plusieurs options techniques
classiques — à garder en tête avant de proposer une dépendance qui en
aurait besoin :

- **Pas de ffmpeg** → les vidéos enfant sont en téléchargement seul,
  aucune vignette générée côté serveur (`VideoEnfant`, distinct de
  `Photo`).
- **Pas de ClamAV / antivirus à l'upload** → risque accepté avec le
  client, mitigé par stockage hors `public/` (jamais exécutable
  directement) + whitelist stricte MIME/extension à chaque upload
  (PDF/JPEG/PNG, 8 Mo max) + lien de téléchargement signé et temporaire.
  Détail et alternatives écartées : `documentation/backlog-v2.md`,
  section "Antivirus à l'upload".
- **Pas de tâche cron pré-câblée** → `app:purge:donnees` doit être
  planifiée côté hébergeur (mensuel suggéré, avec `--force`).
- **Stockage fichiers en local sur disque** (`var/photos/`, `var/share/`),
  pas de S3/object storage. Prendre en compte le quota de l'hébergement
  mutualisé si le volume de photos/vidéos grandit.

Variables d'environnement clé (voir `.env` pour la liste complète,
valeurs par défaut à surcharger dans `.env.local`, jamais commité) :

| Variable | Rôle |
| --- | --- |
| `APP_ENV` / `APP_SECRET` | Environnement Symfony standard |
| `DATABASE_URL` | Connexion MySQL |
| `MAILER_DSN` | Envoi d'email (notifications parent/équipe) |
| `MAILER_FROM` | Expéditeur des notifications (§5.2.f) |
| `VIDEO_TAILLE_MAX_MO` | Taille max d'upload vidéo (défaut 20 Mo) — à réconcilier avec `upload_max_filesize`/`post_max_size` PHP réels côté hébergeur |
| `APP_SHARE_DIR` | Répertoire de partage Symfony (cache/lock inter-process) |

## Installation locale

```bash
composer install

# créer .env.local (non commité, surcharge .env) avec au minimum :
# DATABASE_URL="mysql://<user>:<mot-de-passe>@127.0.0.1:3306/<base>?serverVersion=8.0.32&charset=utf8mb4"
# APP_SECRET=<chaîne aléatoire>
# APP_ENV=dev

php bin/console doctrine:database:create
php bin/console doctrine:migrations:migrate --no-interaction
php bin/console doctrine:fixtures:load --no-interaction   # comptes de test, cf. ci-dessous

symfony server:start   # ou : php -S 127.0.0.1:8000 -t public
```

### Comptes de test (fixtures, `src/DataFixtures/AppFixtures.php`)

| Rôle | Email | Mot de passe |
| --- | --- | --- |
| Éducateur | `educateur@creche-test.local` | `educateur123` |
| Direction | `direction@creche-test.local` | `direction123` |
| Comptabilité | `comptabilite@creche-test.local` | `comptabilite123` |
| Admin technique | `admin-technique@creche-test.local` | `admintech123` |
| Cantine | `cantine@creche-test.local` | `cantine123` |
| Parent | `moussa.traore@example-parent.local` | `parent123` |

Aucun compte investisseur en fixture (créé à la demande par Direction
via `/administration/investisseurs`, cf. guide utilisateur).

## Tests

```bash
php bin/phpunit
php bin/console lint:container   # vérifie le câblage de l'injection de dépendances
```

Uniquement des tests unitaires (`tests/Service/`), mockant repositories
et dépendances externes. **Aucun test fonctionnel/navigateur** — la
vérification des contrôleurs/templates se fait manuellement, en
navigateur réel contre une vraie base de données, à chaque
fonctionnalité livrée (cf. les sections "Vérifié manuellement" de
`documentation/backlog-v2.md`).

## Commandes CLI notables

- `app:export:reversibilite` — exporte l'intégralité des données de
  l'application (44 entités), réversibilité contractuelle en fin de
  mandat (§10).
- `app:purge:donnees [--force]` — purge les données au-delà de leur
  durée de conservation par entité (§2.4). Sans `--force` : mode
  simulation (affiche ce qui serait supprimé). À planifier côté
  hébergeur, aucune tâche cron n'est fournie par le dépôt.

## Migrations

`php bin/console make:migration` génère un squelette à partir du diff
d'entités — **toujours relire et corriger la description** (citer le §
du cahier des charges concerné et la date de décision client) avant de
migrer. Pour un changement de colonne sur une table non vide, l'ALTER
auto-généré doit être réécrit à la main en trois étapes (ajout colonne
nullable → backfill en SQL brut → `MODIFY ... NOT NULL`), jamais un
`NOT NULL` direct qui échouerait sur les lignes existantes.

## Documentation complémentaire

- `documentation/cahier-des-charges-creche-v4.1.md` — spécification
  d'origine, source de toute référence `§X.Y` dans le code.
- `documentation/backlog-v2.md` — **journal de toutes les décisions**
  prises après la livraison initiale : fonctionnalités ajoutées,
  reportées ou abandonnées, avec la raison et la date. À lire avant de
  proposer une évolution qui y ressemble.
- `documentation/avancement-lot1.md` à `lot6.md` — détail de chaque
  lot livré.
- `documentation/specificites-hors-cahier-des-charges.md` — extensions
  ajoutées hors périmètre §11 (`ROLE_CANTINE`, `ROLE_ADMIN_TECHNIQUE`).
- `documentation/ameliorations-performance.md` — historique des
  correctifs de performance (N+1, pagination).
- `documentation/guide-utilisateur.pdf` — guide non technique (rôles,
  fonctionnalités, restrictions), destiné au client/aux utilisateurs
  finaux.
