From 32f9baacce34c950d5b4b70df8374cc10264e7a3 Mon Sep 17 00:00:00 2001 From: Corentin JOGUET Date: Wed, 17 Jun 2026 15:55:30 +0200 Subject: [PATCH] docs(domaines): documentation par domaine fonctionnel (7 fiches) (#44) --- docs/domaines/README.md | 18 ++++++++++++++++++ docs/domaines/auth.md | 29 +++++++++++++++++++++++++++++ docs/domaines/borne.md | 31 +++++++++++++++++++++++++++++++ docs/domaines/catalogue.md | 29 +++++++++++++++++++++++++++++ docs/domaines/rbac.md | 30 ++++++++++++++++++++++++++++++ docs/domaines/stats.md | 26 ++++++++++++++++++++++++++ docs/domaines/stock-recettes.md | 31 +++++++++++++++++++++++++++++++ docs/domaines/users.md | 31 +++++++++++++++++++++++++++++++ 8 files changed, 225 insertions(+) create mode 100644 docs/domaines/README.md create mode 100644 docs/domaines/auth.md create mode 100644 docs/domaines/borne.md create mode 100644 docs/domaines/catalogue.md create mode 100644 docs/domaines/rbac.md create mode 100644 docs/domaines/stats.md create mode 100644 docs/domaines/stock-recettes.md create mode 100644 docs/domaines/users.md diff --git a/docs/domaines/README.md b/docs/domaines/README.md new file mode 100644 index 0000000..e972b3a --- /dev/null +++ b/docs/domaines/README.md @@ -0,0 +1,18 @@ +# Documentation par domaine + +Une fiche par domaine fonctionnel livre : **perimetre**, **ce qui est livre** (code + +routes), **regles metier** (RG-T* de `docs/merise/mlt.md`), **decisions** (renvoi +`docs/adr/`), **tables**. Vue d'ensemble : `docs/ARCHITECTURE.md`. + +**Auteur : BYAN** (formalisation ; arbitrage et validation par l'auteur). + +| Domaine | Fiche | Statut | +|---|---|---| +| Authentification & sessions | [auth.md](auth.md) | Livre (P2) | +| Catalogue (categories, produits, menus) | [catalogue.md](catalogue.md) | Livre (P3) | +| Stock & recettes (ingredients) | [stock-recettes.md](stock-recettes.md) | Livre (P3) | +| Comptes utilisateurs | [users.md](users.md) | Livre (P3) | +| RBAC (roles & permissions) | [rbac.md](rbac.md) | Livre (P3) | +| Statistiques | [stats.md](stats.md) | Livre (P3, KPIs vente differes P4) | +| Borne (kiosk) | [borne.md](borne.md) | Front P5 (API au swap P4) | +| Commande | — | P4 (schema pret, workflow a venir) | diff --git a/docs/domaines/auth.md b/docs/domaines/auth.md new file mode 100644 index 0000000..7ec332d --- /dev/null +++ b/docs/domaines/auth.md @@ -0,0 +1,29 @@ +# Domaine — Authentification & sessions + +## Perimetre +Connexion back-office, deconnexion, reinitialisation de mot de passe, garde de session, +PIN d'action sensible. Pas d'auth cote borne (front public). + +## Ce qui est livre +- `App\Auth\AuthService` (login 12.1 / logout 12.2), `PasswordResetService` (12.3). +- `SessionManager` (seul a toucher `$_SESSION`/cookie, mode test memoire), `SessionGuard` + (RG-6/RG-T02 : idle 4h, absolu 10h, `is_active`), `Csrf` (jeton synchroniseur). +- `PasswordHasher` (argon2id + leurre de timing), `PinVerifier`, `PinThrottle`, + `ThrottlePolicy` (backoff degressif). +- Controleurs `AuthController`, `PasswordResetController`, `ProfileController` (set-PIN + self-service), `MeController` (`/api/me`). + +## Regles metier +- RG-6 / RG-T02 : session valide (idle + absolu + compte actif) sinon 302 `/login`. +- RG-8 / RG-9 : throttle login par compte + par IP (`login_throttle`), backoff degressif. +- RG-T13 : PIN d'action sensible (voir [users](users.md), [rbac](rbac.md), stock). +- Anti-enumeration : reponses neutres (reset, login) ; leurre de timing argon2id. + +## Decisions +[ADR-0001](../adr/0001-php-from-scratch-sans-composer.md) (from scratch), +[ADR-0004](../adr/0004-pin-action-sensible-audit.md) (PIN), +[ADR-0005](../adr/0005-throttle-pin-separe-du-login.md) (throttle PIN). + +## Tables +`user`, `login_throttle`, `pin_throttle`, `audit_log` (login + pin.failed). Detail : +`docs/merise/mlt.md` section 12 + 22. diff --git a/docs/domaines/borne.md b/docs/domaines/borne.md new file mode 100644 index 0000000..24aae20 --- /dev/null +++ b/docs/domaines/borne.md @@ -0,0 +1,31 @@ +# Domaine — Borne (kiosk) + +## Perimetre +Front client tactile (Bloc 1) : parcours welcome -> categories -> produit -> panier -> +confirmation. HTML/CSS/JS vanilla, servi en statique par Apache. + +## Ce qui est livre +- Pages : `index`, `categories`, `products`, `product`, `cart`, `payment`, + `confirmation` (`src/public/borne/`). +- JS modules ES6 (`assets/js/`) : `data.js` (chargement, point de swap P4), `state.js` + (panier), `page-*.js`, `nav.js`, et `allergens.js` (modale generale 14 INCO sur carte + et fiche). +- Donnees : JSON statiques (`data/`) en P5 ; basculent sur `/api/*` DB-backed au swap P4. + +## Regles metier / conventions +- Allergenes : info **generale** (les 14 INCO, reglement UE 1169/2011), pas un calcul + par produit (mapping `ingredient_allergen` differe). +- CSP-safe pour le code projet : pas de script inline ajoute (donnees via `data-*`, + `addEventListener`). Source allergenes = liste fixe `data/allergens.json`, se branchera + sur `/api/allergens` au swap P4. + +## Tests +Harnais front `node:test` + jsdom (`tests/js/allergens.test.js`) : 14 INCO, bouton "i", +ouverture/fermeture (bouton/overlay/Echap), idempotence. Job CI `js-tests` (Node 20). + +## Decisions +Swap point P5 -> API au P4 (cf. `data.js` + journaux). Modele = app self-hostable +([ADR-0009](../adr/0009-compose-standalone-et-prod-gitignore.md)). + +## Tables (au swap P4) +`category`, `product`, `menu` + `allergen` (lecture). Aujourd'hui : JSON statiques. diff --git a/docs/domaines/catalogue.md b/docs/domaines/catalogue.md new file mode 100644 index 0000000..55d8a90 --- /dev/null +++ b/docs/domaines/catalogue.md @@ -0,0 +1,29 @@ +# Domaine — Catalogue (categories, produits, menus) + +## Perimetre +CRUD des categories, produits et menus composes (borne de base + slots). Base du +catalogue consomme par la borne. + +## Ce qui est livre +- Repositories : `CategoryRepository`, `ProductRepository`, `MenuRepository`. +- Controleurs : `CategoryController` (`category.manage`), `ProductController` + (`product.read/create/update/delete`), `MenuController` (`menu.read/create/update/delete`). +- Menus composes : burger de base + `menu_slot` / `menu_slot_option`, editeur slots en + JS vanilla CSP-safe (champ cache `slots_json`), reecriture delete-and-reinsert en tx. + +## Regles metier +- RG-T16 (allowlist colonnes), RG-T18 (validation serveur bornee : prix > 0, TVA dans + {55,100}, etc.), RG-T15 (sorties echappees). +- Produit : PIN equipier + audit UNIQUEMENT si prix ou TVA change (mlt 8.2 RG-4) ; + suppression = PIN + audit (8.3). Menu : suppression = PIN + audit (8.6). +- Pas de suppression dure si reference (FK RESTRICT depuis order_item / menu / selection) + -> 409, alternative = desactivation (`is_available`). + +## Decisions +[ADR-0002](../adr/0002-back-office-mvc-rendu-serveur.md) (MVC serveur), +[ADR-0006](../adr/0006-http-409-conflit-422-validation.md) (409/422), +[ADR-0004](../adr/0004-pin-action-sensible-audit.md) (PIN + audit). + +## Tables +`category`, `product`, `menu`, `menu_slot`, `menu_slot_option`. Detail : +`docs/merise/mlt.md` section 8. diff --git a/docs/domaines/rbac.md b/docs/domaines/rbac.md new file mode 100644 index 0000000..d2c9b30 --- /dev/null +++ b/docs/domaines/rbac.md @@ -0,0 +1,30 @@ +# Domaine — RBAC (roles & permissions) + +## Perimetre +Gestion des roles et de la matrice role/permission (mlt 10.4 MANAGE_RBAC), permission +`role.manage`. Catalogue de permissions fige au seed (lecture seule). + +## Ce qui est livre +- `RoleRepository` (App\Auth) : roles (CRUD, code immuable), permissions (lecture), + matrice (`permissionIdsFor`/`permissionCodesFor`, `setPermissions` tx + + `replacePermissions` raw), `role_visible_source` (`setVisibleSources` / raw). +- `RoleController` (`role.manage`) : index, create/store (role custom RG-4), edit/update + (champs role + matrice + sources visibles en UNE transaction). Vues `admin/roles/{index,form}`. +- Matrice soumise en champs **scalaires** (`perm_`, `source_`) : `Request::formBody` + ne garde que les scalaires (pas de `name[]`, pas de JS). + +## Regles metier +- RG-6 (mlt 10.4) : PIN equipier + `audit_log` (`role.manage`) dans une transaction ; + `details` JSON = **diff** des codes de permission (ajoutes/retires), calcule avant la + reecriture delete-and-reinsert. +- `Authorizer::can` recharge les permissions a chaque verification (effet immediat). +- Garde-fous anti-lockout : le role `admin` conserve `role.manage` ET reste actif ; + `code` immuable apres creation ; `order_source` borne a l'ENUM ; code dupli -> 409. + +## Decisions +[ADR-0004](../adr/0004-pin-action-sensible-audit.md) (PIN + audit), +[ADR-0006](../adr/0006-http-409-conflit-422-validation.md) (409). + +## Tables +`role`, `permission`, `role_permission`, `role_visible_source`, `audit_log`. Detail : +`docs/merise/mlt.md` section 10.4. diff --git a/docs/domaines/stats.md b/docs/domaines/stats.md new file mode 100644 index 0000000..b99fccf --- /dev/null +++ b/docs/domaines/stats.md @@ -0,0 +1,26 @@ +# Domaine — Statistiques + +## Perimetre +Tableau de bord de pilotage (mlt domaine 11), permission `stats.read`. Landing par +defaut du role manager. + +## Ce qui est livre +- `StatsRepository` : `counts()` (compteurs catalogue : produits/menus/categories/ + ingredients, total + actifs/disponibles), `stockHealth()` (repartition des ingredients + actifs par bande RG-T21 + liste d'alerte triee du plus critique). +- `StatsController` (`stats.read`) -> `/admin/stats` + vue `admin/stats/index` (cartes + KPI + table d'alerte stock) + lien nav "Pilotage". + +## Regles metier / perimetre +- KPIs sur les **donnees disponibles** en P3 : sante catalogue + stock. **Ferme le 404** + du landing manager (`role.default_route = /admin/stats`). +- KPIs de **vente** (CA, volumes, `service_day`) = **P4** : ils dependent du domaine + commande (encore en schema seul). +- Sante stock = reutilise `IngredientRepository::stockBand` (source unique RG-T21). + +## Decisions +[ADR-0003](../adr/0003-stock-pourcentage-dispo-calculee.md) (bandes RG-T21). + +## Tables +Lecture seule : `product`, `menu`, `category`, `ingredient` (compteurs + bandes). +KPIs vente (P4) : `customer_order`, `order_item`. Detail : `docs/merise/mlt.md` section 11. diff --git a/docs/domaines/stock-recettes.md b/docs/domaines/stock-recettes.md new file mode 100644 index 0000000..2eafb98 --- /dev/null +++ b/docs/domaines/stock-recettes.md @@ -0,0 +1,31 @@ +# Domaine — Stock & recettes (ingredients) + +## Perimetre +Gestion des ingredients, du stock (reappro + inventaire), des mouvements de stock, et de +la composition des produits (recettes). Sous-tend la disponibilite produit calculee. + +## Ce qui est livre +- `IngredientRepository` : CRUD, stock %/bande calcules, `restock` (tx), `inventoryCount` + (tx, ecrit une ligne meme a delta=0, RG-3), `movements` (borne), `isReferenced`. +- `IngredientController` : CRUD (`ingredient.manage`, sans PIN), RESTOCK (`stock.manage`, + sans PIN), INVENTORY_COUNT (`stock.count` + PIN), mouvements (`stock.read`). +- `ProductRepository` : composition (`product_ingredient`), `setComposition` + (delete-and-reinsert tx), `isOrderable` (RG-T21), `autoUnavailableIds`. +- Editeur de recette (`ProductController::recipeForm/saveRecipe`, `ingredient.manage`). + +## Regles metier +- RG-T13 : INVENTORY_COUNT seule action sensible du stock (PIN equipier) ; succes -> + `stock_movement.user_id`, **sans** `audit_log` (RG-T14 : le mouvement EST la trace). + RESTOCK et CRUD ingredient ne sont PAS sensibles. +- RG-T22 : echec PIN inventaire -> `pin.failed` + throttle dans une transaction. +- RG-T21 : disponibilite produit calculee (cf. [ADR-0003](../adr/0003-stock-pourcentage-dispo-calculee.md)). +- FK : `product_ingredient`/`stock_movement` RESTRICT sur l'ingredient (hard-delete -> 409) ; + `product_ingredient.product_id` CASCADE (trace du nombre de lignes a la suppression, dette #27). + +## Decisions +[ADR-0003](../adr/0003-stock-pourcentage-dispo-calculee.md) (stock % + RG-T21), +[ADR-0004](../adr/0004-pin-action-sensible-audit.md) / RG-T14 (attribution sans double-journal). + +## Tables +`ingredient`, `product_ingredient`, `stock_movement`, `allergen`, `ingredient_allergen` +(mapping differe). Detail : `docs/merise/mlt.md` sections 8.8 + 9. diff --git a/docs/domaines/users.md b/docs/domaines/users.md new file mode 100644 index 0000000..a424ce1 --- /dev/null +++ b/docs/domaines/users.md @@ -0,0 +1,31 @@ +# Domaine — Comptes utilisateurs + +## Perimetre +Gestion des comptes back-office (mlt domaine 10.1-10.3 + 10.5) : creation, edition, +desactivation, reinitialisation de PIN, effacement RGPD. + +## Ce qui est livre +- `UserRepository` (App\Auth) : all (JOIN role) / find / emailExists / activeRoleExists / + create / update (allowlist) / setPasswordHash / clearPin / deactivate / anonymise / + activeAdminCount / isAdmin. +- `UserController` : index (`user.read`), create/store (`user.create`), edit/update + (`user.update`), deactivate (`user.deactivate`), reset-pin, erase-PII. Vues + `admin/users/{index,form,confirm}`. + +## Regles metier +- RG-T13/14 : **toutes** les mutations sont sensibles -> PIN equipier + `audit_log` + (`user.create/update/deactivate/erase_pii`) dans la meme transaction ; `details` JSON = + noms de champs / role (pas de PII). Throttle RG-T22. +- RG-T16 : allowlist (email/prenom/nom/role_id/is_active) ; `is_active` pose serveur a + la creation. Unicite email -> 409. +- Self-protection : pas d'auto-desactivation (403 SELF_DEACTIVATION) ; on ne retire pas + le statut du **dernier admin actif** (update/deactivate/erase) ; effacement deja fait -> 409. + +## Decisions +[ADR-0004](../adr/0004-pin-action-sensible-audit.md) (PIN + audit), +[ADR-0007](../adr/0007-rgpd-anonymisation-tombstone.md) (anonymisation RGPD), +[ADR-0006](../adr/0006-http-409-conflit-422-validation.md) (409/422). + +## Tables +`user` (+ `anonymized_at` pour RGPD), `audit_log`, `role` (FK). Detail : +`docs/merise/mlt.md` section 10.1-10.5.