# Récré

Un logiciel de caisse et de stock pour les associations d'un lycée — foyer, BDE, sorties, tombolas.

Il fait tourner un stand : on encaisse sur un téléphone, on clôture la caisse le soir, on compte le stock, on commande ce qui manque. Et il sert de support de cours : ce qu'il fait faire aux élèves — analyser un cadencier, décider une quantité, pointer une réception, expliquer un écart de caisse — c'est le métier qu'on leur enseigne.

Plusieurs associations cohabitent dans la même installation, sans jamais se voir.

## Ce qu'il fait

**Vendre.** Un front mobile d'abord, pensé pour le pouce d'un élève au stand pendant une récré de quinze minutes. Panier, espèces ou SumUp, et c'est tout.

**Tenir une caisse.** Une session par caisse, plusieurs vendeurs dessus. À la clôture, le comptage se fait **à l'aveugle** : la fiche des quatorze coupures de la série euro, aucun montant attendu affiché avant d'avoir validé. L'écart n'apparaît qu'après — sinon il ne mesure rien.

**Suivre un stock.** Entrées, ventes, ajustements, démarques. `Product.stock` reste le cumul exact du journal des mouvements, et une vente n'est **jamais** bloquée par le stock : sur un stand, le stock informatique ment, le carton dit vrai.

**Recaler.** Le vendeur compte devant le carton, l'admin valide. L'écart se calcule au moment de la validation, pas de la saisie.

**Commander.** Le cadencier dit ce qui part et combien de temps le stock tient encore ; l'écran de commande en déduit une quantité. Le vendeur prépare, l'admin valide, le vendeur pointe ce qui arrive.

**Comprendre.** Statistiques, 20/80, évolution, et surtout les **ruptures** — combien de jours un produit a manqué, reconstruit depuis le journal des mouvements. C'est là que se perdent les ventes, et ça ne se voit nulle part ailleurs.

## Démarrer

Il faut Docker, et c'est tout — PHP, MariaDB et nginx tournent dedans.

```bash
docker compose up -d
```

Puis, une seule fois :

```bash
docker compose exec php composer install
docker compose exec php php bin/console doctrine:migrations:migrate --no-interaction
```

L'application répond sur **http://localhost:8088**.

Le front-end passe par Webpack Encore. Il a besoin de Node :

```bash
npm install
npm run build
```

Un premier compte super-admin se crée en ligne de commande — c'est lui qui créera ensuite les associations et leurs administrateurs :

```bash
docker compose exec php php bin/console app:create-super-admin
```

### Un bac à sable pour regarder

Une fixture pose une association de démonstration avec des produits, une caisse et des ventes. Elle s'ajoute sans purger la base :

```bash
docker compose exec php php bin/console doctrine:fixtures:load --append --group=asso-test
```

Elle crée un compte vendeur : `test.vendeur@recre.local` / `test1234`.

## Tester

La suite tourne dans le conteneur, sur une base dédiée `recre_test`. Sa mise en place, une seule fois, est décrite dans [docs/conventions.md](docs/conventions.md).

```bash
docker compose exec php php bin/phpunit
```

Chaque test qui touche la base s'exécute dans une transaction annulée au teardown : la base reste propre entre les tests, et l'ordre n'a aucune importance.

## La pile

Symfony 7 · PHP 8.3 · MariaDB 11 · Doctrine ORM 3 · EasyAdmin 4 pour le back-office · Twig et Stimulus pour le front · Dompdf pour les exports · Brevo pour les rares e-mails · Webpack Encore · Docker · auto-hébergé.

Pas de framework JavaScript. Les écrans sont rendus côté serveur et enrichis par quelques contrôleurs Stimulus ; les graphiques sont en CSS pur, parce que les mêmes écrans doivent pouvoir partir en PDF.

## Trois règles qui expliquent le reste

**Une vente n'est jamais refusée pour un motif de stock.** Un stock négatif n'est pas une erreur à empêcher, c'est un signal : le suivi a décroché du réel. Voir [ADR-0007](docs/decisions/0007-vente-jamais-bloquee-par-stock.md).

**On compte avant de savoir ce qu'on devrait trouver.** Caisse comme inventaire. Un chiffre attendu affiché pendant le comptage est un chiffre qu'on finit par recopier. Voir [ADR-0008](docs/decisions/0008-comptage-a-l-aveugle.md).

**Toute donnée métier appartient à une association.** L'isolation est portée par un filtre Doctrine, et un test la verrouille entité par entité. C'est la règle dure du projet.

## La documentation

| | |
|---|---|
| [docs/architecture.md](docs/architecture.md) | Vue d'ensemble, modèle de données, et les pourquoi |
| [docs/conventions.md](docs/conventions.md) | Nommage, patterns, structure, mise en place des tests |
| [docs/specs/](docs/specs/) | Une spec par feature, avec ses critères de « fait » |
| [docs/decisions/](docs/decisions/) | Les ADR : ce qui a été tranché, et contre quoi |
| [docs/chantiers.md](docs/chantiers.md) | La file d'attente : ce qui reste, en lots |
| [STATE.md](STATE.md) | Où en est le projet, aujourd'hui |
| [/aide](src/Service/AideService.php) | L'aide de chaque écran, en un seul endroit — le *pourquoi*, pas le *comment* |

Les specs et les ADR ne sont pas de la décoration : ils portent les arbitrages qui rendent le code lisible. Une décision surprenante dans le code a presque toujours son explication là-bas.

## Licence

Aucune licence publique pour l'instant. Le code n'est pas encore diffusé.

---

Créé et développé par **David Laignelet**.
