# 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.

Complété depuis (§14-§19, retour client 2026-08-10, cf.
`documentation/suivi-caisses-rh.md`) par : caisses internes, dépenses
fixes et suivi des salaires, échéancier de paiement en plusieurs
tranches, catalogue de produits de saison, produits vendus avec suivi
de stock, accueil ponctuel (garderie) sans compte parent, et gestion RH
du personnel (contrats de travail, congés).

Complété encore depuis (2026-08-24, cf. `documentation/backlog-v2.md`)
par : calcul automatique et paramétrable du solde de congés,
estimation du seuil de rentabilité (tarif par groupe, charges fixes),
documents administratifs de l'établissement classés par type, et plan
de communication (argumentaire d'accueil partagé avec l'équipe).

Complété encore depuis (2026-09-16, cf. `documentation/backlog-v2.md`)
par : gestion complète des comptes utilisateurs (modification,
suppression bornée dans le temps, réservées au compte fondateur),
inscription directe sans étape "en attente", auto-validation des
achats cantine, emails HTML, traduction française des messages
système, téléchargement du guide utilisateur depuis l'application
(ouvert à tous les rôles internes), plusieurs corrections d'ergonomie
(comptes parents, responsables légaux, montants, avatar, page
Personnel), et validation Direction du suivi quotidien (repas, sieste,
soins, activités, humeur) dès qu'un commentaire ou une particularité
est renseigné - sur le même principe que la synthèse journalière,
avec auto-validation pour le compte fondateur.

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`, généré depuis sa source
versionnée `documentation/guide-utilisateur.html` - ne jamais éditer le
PDF directement, éditer le HTML puis lancer
`php bin/console app:documentation:guide-utilisateur`.

## 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` |
| Investisseur | `investisseur@creche-test.local` | `investisseur123` |
| Fondateur (cumul Direction + Admin technique) | `fondateur@creche-test.local` | `fondateur123` |
| Parent | `moussa.traore@example-parent.local` | `parent123` |

Un mot de passe distinct par rôle (pas de mot de passe partagé) -
raccourci cliquable disponible directement sur l'écran de connexion en
dehors de l'environnement de production (`SecurityController::comptesTest()`).

## 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.
- `app:documentation:guide-utilisateur` - régénère
  `documentation/guide-utilisateur.pdf` depuis sa source
  `documentation/guide-utilisateur.html` (Dompdf, même moteur que les
  factures).

## 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/suivi-caisses-rh.md` - chantier "caisses internes,
  accueil ponctuel, RH" (§14-§19, retour client 2026-08-10) : statut
  détaillé module par module, ordre d'implémentation suivi, micro-ajustements
  non bloquants restants.
- `documentation/guide-utilisateur.pdf` (généré depuis
  `documentation/guide-utilisateur.html`, cf. commande CLI ci-dessus) -
  guide non technique (rôles, fonctionnalités, restrictions), destiné
  au client/aux utilisateurs finaux.
