docs: remediation audit vague 1 - sync Merise + journal/ADR + claims faux #106

Merged
Corentin merged 4 commits from docs/audit-remediation-truthup into dev 2026-06-25 10:02:18 +02:00
5 changed files with 302 additions and 1 deletions
Showing only changes of commit 0604d743fc - Show all commits

View file

@ -0,0 +1,43 @@
# ADR-0011 — POS tactile a tuiles pour la saisie comptoir/drive
- Statut : Accepte
- Date : 2026-06-25
## Contexte
La saisie de commande comptoir/drive (mlt 4.1, `CREATE_COUNTER_ORDER`) avait d'abord ete
livree comme formulaire-liste enrichi (#100) : une liste de produits avec champs de
quantite, prix, verrou du mode de service au canal drive, file des commandes recentes.
Cet ecran est destine a des equipiers non-techniques, sur tablette, dans un contexte de
caisse ou la rapidite compte. Le formulaire-liste se prete mal au tactile (petites cibles,
defilement) et ne ressemble pas au geste deja appris cote borne client. Options :
conserver et raffiner le formulaire-liste ; adopter un paradigme de caisse (POS) a tuiles
reutilisant l'UX borne ; integrer une bibliotheque de POS tierce.
## Decision
La saisie comptoir/drive devient un **POS tactile a tuiles** (#104) calque sur la borne
client : onglets categories en haut, grille de tuiles produits/menus, panneau commande
persistant a droite ; un tap ajoute le produit, un produit a modificateurs ou un menu
ouvre une modale de composition. Le panier est construit cote client en vanilla JS
CSP-safe (`'self'`, zero handler inline) a partir d'un script JSON inerte, puis serialise
dans un champ cache `items_json`. Le **serveur reste seul juge** : il revalide la forme
(RG-T18), recalcule les prix et resout les modificateurs (RG-T16) ; les prix affiches cote
client sont indicatifs. Un **seul controleur** sert les deux canaux, la `source`
(counter/drive) etant derivee du chemin de la requete.
## Consequences
- (+) Geste unifie borne/comptoir : moins d'apprentissage, reutilisation des patterns
composeur deja eprouves cote borne.
- (+) Cibles tactiles larges adaptees a la tablette ; zero jargon dev a l'ecran
(utilisateurs non-techniques).
- (+) Decoupage par chemin (`/drive...` vs `/counter...`) : les canaux restent etanches,
un equipier ne peut pas requalifier sa commande via un champ falsifie.
- (+) Sans framework front (coherent ADR-0002) ; coherent avec ADR-0001 (pas de
dependance tierce, donc pas de POS externe).
- (-) Le POS est interactif par nature : sans JS, la grille ne s'affiche pas (un message
invite a activer JS). Un repli legacy `qty_<id>` reste accepte quand `items_json` est
absent, mais l'experience cible suppose JS.
- (-) Cet ecran a ete refondu deux fois a court intervalle (#100 puis #104) ; le palier
#100 est assume comme etape, pas comme dette cachee.
- Fichiers : `src/app/Controllers/CounterOrderController.php`,
`src/app/Views/admin/counter/new.php`, `src/public/admin/assets/js/counter-order.js`.
Detail : journal `docs/journal/2026-06-25--audit-remediation-et-features-94-105.md`.

View file

@ -0,0 +1,41 @@
# ADR-0012 — Page Stock en tableau de bord (alertes + reapprovisionnement en avant)
- Statut : Accepte
- Date : 2026-06-25
## Contexte
La page d'accueil Ingredients/Stock (domaine 8.8 + 9) etait une liste-CRUD exhaustive :
tous les ingredients, avec les actions creer/modifier/supprimer en premier plan. Elle
etait jugee trop chargee et opaque pour un equipier non-technique. Le besoin metier
quotidien est de voir vite ce qui manque et de reapprovisionner ; l'edition de fiches est
rare. De plus, le lien entre stock et disponibilite borne (un ingredient requis sous le
seuil critique rend indisponibles les produits qui l'utilisent, RG-T21, cf. ADR-0003)
n'etait pas visible a l'ecran. Options : garder la liste exhaustive triable ; basculer
vers un tableau de bord oriente action ; deux pages distinctes (dashboard + gestion).
## Decision
La page Stock devient un **tableau de bord oriente action** (#105) : un bandeau explicite
le lien stock -> disponibilite borne (RG-T21) ; un resume compte les ingredients
critiques / en alerte / au-dessus du seuil ; une section "A reapprovisionner" met en avant
les ingredients bas (critiques d'abord) avec barre de niveau et bouton de
reapprovisionnement direct. La liste complete passe au second plan et le CRUD est relegue.
Les compteurs par etat sont **calcules cote serveur** dans `IngredientController::index()`
a partir du `stock_band` deja resolu par le depot, pour garder la vue declarative et la
valeur directement testable. Les sous-pages (reappro, inventaire, mouvements, creation)
restent inchangees.
## Consequences
- (+) L'ecran s'aligne sur le geste quotidien (reperer le manque, reapprovisionner) plutot
que sur l'edition de fiches.
- (+) Le lien stock -> disponibilite borne (RG-T21) devient explicite a l'ecran, pas
seulement dans le code.
- (+) Compteurs testables (la logique de comptage est cote serveur, pas dans la vue) :
+4 cas de test `IngredientController` (bandeau, promotion d'un critique en section
reappro, etat vide positif, compteurs par etat).
- (-) La liste exhaustive et le CRUD sont moins immediats (un cran plus loin) : choix
assume, ces operations etant moins frequentes que la lecture d'alerte.
- (-) Le tri/filtre avance de l'ancienne liste n'est pas reconduit en premier plan.
- Coherent avec ADR-0002 (MVC rendu serveur) et ADR-0003 (disponibilite calculee depuis
le stock). Fichiers : `src/app/Controllers/IngredientController.php`,
`src/app/Views/admin/ingredients/index.php`, `src/public/admin/assets/css/admin.css`.
Detail : journal `docs/journal/2026-06-25--audit-remediation-et-features-94-105.md`.

View file

@ -18,6 +18,8 @@ une decision revisee donne une nouvelle fiche qui *supersede* l'ancienne (statut
| [0008](0008-makefile-vers-compose-migrate.md) | Du Makefile a `docker compose up` (service wakdo-migrate) | Accepte |
| [0009](0009-compose-standalone-et-prod-gitignore.md) | docker-compose.yml standalone + docker-compose.prod.yml gitignore | Accepte |
| [0010](0010-cookie-secure-conditionnel-https.md) | Cookie de session Secure conditionnel au HTTPS | Accepte |
| [0011](0011-pos-tactile-tuiles-comptoir-drive.md) | POS tactile a tuiles pour la saisie comptoir/drive | Accepte |
| [0012](0012-page-stock-tableau-de-bord.md) | Page Stock en tableau de bord (alertes + reappro en avant) | Accepte |
## Modele de fiche

View file

@ -0,0 +1,210 @@
# 2026-06-25 — Synthese : CD prod, SMTP reel, durcissement borne, POS comptoir/drive, dashboard stock (#94-#105)
**Auteur : BYAN.** Retrospective de synthese couvrant douze PR mergees apres la session
du 2026-06-18 (front login + amorce P4 commande). Elles se regroupent en quatre fils :
mise en production reelle (#94-#97), finition du parcours borne client (#98, #99, #101,
#102, #103), et refonte de la saisie comptoir/drive et de la page stock cote back-office
(#100, #104, #105). Entree descriptive : on decrit ce qui est livre, pas ce qui est
promis.
---
## Ce qui a ete livre (PR mergees)
| PR | Bloc | Objet |
|----|------|-------|
| #94 | CD | Deploiement push-based vers Vision (prod) + preuve de version dans `GET /api/health` |
| #95 | CD | Modeles versionnes `docker-compose.prod.yml.example` + `.env.prod.example` |
| #96 | Auth | Envoi reel de l'email de reset via relais SMTP (Brevo) — client SMTP maison |
| #97 | CD | Passage des variables SMTP/MAIL au conteneur `wakdo-app` (correctif #96) |
| #98 | Borne | Menu Maxi agrandit la boisson en 50cl + transport du format choisi |
| #99 | Borne | Produit/menu en rupture de stock rendu non commandable (RG-T21) |
| #100 | Back-office | Refonte saisie comptoir/drive : prix, verrou du mode, navigation, file |
| #101 | Borne | Panier unique = panneau persistant (retrait de `cart.html` et `product.html`) |
| #102 | Borne | Confirmation avant l'abandon de la commande |
| #103 | Borne | Bascule des allergenes sur `/api/allergens` + menage des donnees/docs statiques |
| #104 | Back-office | Saisie comptoir/drive en POS tactile a tuiles (refonte de #100) |
| #105 | Back-office | Page Stock en tableau de bord (alertes + reapprovisionnement en avant) |
---
## Bloc 1 — Mise en production reelle (#94, #95, #96, #97)
### Ce qui a ete fait
- **CD push-based (#94)** : `.forgejo/workflows/deploy.yml` ouvre, sur push `main`, une
session SSH vers Vision (l'hote de prod). `scripts/deploy.sh` y recupere `main` en
fast-forward, ecrit un marqueur de version (`src/VERSION` : SHA + date), journalise une
ligne dans `deploy.log`, puis reconstruit et recree la stack. `GET /api/health` expose
desormais `version` et `deployed_at`, lus depuis ce marqueur : c'est la preuve cote app
qu'un deploiement a bien repris le dernier commit. Doc : `docs/architecture/deployment.md`.
- **Modeles de prod versionnes (#95)** : `docker-compose.prod.yml.example` et
`.env.prod.example` entrent au depot comme gabarits. Le fichier reel reste gitignore
(specifique a l'hote : Traefik, reseau externe), conformement a ADR-0009 ; le `.example`
documente la forme attendue.
- **SMTP reel (#96, #97)** : la reinitialisation de mot de passe envoyait jusque-la un mail
inerte. Un client SMTP maison (`SmtpClient`, `SmtpMailer`, transport via flux PHP
`StreamSmtpTransport` derriere l'interface `SmtpTransport`) parle a un relais reel
(Brevo en l'occurrence). `PasswordResetController` s'y branche. #97 corrige un oubli :
les variables SMTP/MAIL n'etaient pas transmises au conteneur `wakdo-app` (declarees
dans les deux fichiers compose).
### Pourquoi — decisions et alternatives
- **Decision : CD par SSH, pas par Docker-in-CI.** Le runner Forgejo (sur Stark) n'a pas
acces au socket Docker, par choix de securite : un job CI ne pilote pas Docker sur son
hote. Le deploiement vers Vision se fait donc par SSH avec une *forced command* cote
serveur. *Alternative ecartee* : donner le socket Docker au runner — rejetee pour la
surface d'attaque. C'est le prolongement de la decision E2E-CI du 2026-06-18 (meme
contrainte de socket).
- **Decision : client SMTP maison plutot qu'une bibliotheque.** ADR-0001 fige le projet
sans Composer ni dependance tierce ; un client SMTP minimal (EHLO/AUTH/MAIL/RCPT/DATA
sur flux) reste coherent avec cette contrainte et reste testable via un faux transport
(`FakeSmtpTransport`). *Alternative ecartee* : `mail()` de PHP — sans relais
authentifie, la delivrabilite est aleatoire et la configuration sort du depot.
- **Decision : marqueur de version dans `src/VERSION` lu a chaud.** Le marqueur est sous
le mount du code (`./src` -> `/var/www/html`), donc relu sans rebuild. Cela donne une
preuve de deploiement observable de l'exterieur sans instrumentation supplementaire.
### Criteres RNCP couverts
- **Bloc 3 - deploiement** : chaine de livraison continue tracable (`deploy.yml`,
`deploy.sh`, `deployment.md`), preuve de version exposee.
- **Bloc 1 - securite** : separation runner/prod sans socket Docker ; secrets de prod
hors du depot (gabarits `.example` seulement).
---
## Bloc 2 — Finition du parcours borne client (#98, #99, #101, #102, #103)
### Ce qui a ete fait
- **Menu Maxi -> boisson 50cl (#98)** : choisir le format Maxi d'un menu fait passer la
boisson de 33cl a 50cl. La migration `0007_product_size_variant` et le seed
`0006_drink_maxi_variant` portent la variante en base ; le format choisi est transporte
jusqu'au paiement (`checkout.js`, `page-product-menu.js`). Tests `OrderRepository` +
tests JS du composeur.
- **Rupture non commandable (#99, RG-T21)** : un produit (ou un menu dont un composant
requis) sous le seuil critique de stock est marque indisponible et non ajoutable a la
borne ; le serveur refuse aussi la creation cote `OrderRepository` (defense en
profondeur, le client n'est pas seul juge). Visuel d'indisponibilite cote `style.css`.
- **Panier unique = panneau persistant (#101)** : suppression des pages `cart.html` et
`product.html` ; le panier devient un panneau lateral persistant (`order-panel.js`)
present sur les pages au lieu d'une page dediee. Le net du diff est negatif
(-784 lignes) : c'est une simplification de l'architecture front borne.
- **Confirmation d'abandon (#102)** : abandonner la commande ouvre une modale de
confirmation (`confirm-modal.js`) au lieu de vider le panier au premier clic.
- **Allergenes via API (#103)** : la borne lisait des fichiers JSON statiques
(`allergens.json`, `categories.json`, `produits.json`) ; elle consomme desormais
`/api/allergens` (et le catalogue par API). Les JSON statiques et la doc afferente sont
retires (menage). `docs/api/conventions.md` et `docs/design/maquette-vs-build.md` mis a
jour.
### Pourquoi — decisions et alternatives
- **Decision : rupture controlee cote serveur ET cote client (#99).** L'indisponibilite
est calculee au plus pres de la verite (RG-T21, ADR-0003) ; le client la reflete pour
l'UX, mais `OrderRepository` revalide a la creation. *Alternative ecartee* : masquer
cote client seulement — laisse une fenetre ou une commande forgee passerait.
- **Decision : panier persistant plutot que page panier (#101).** Sur une borne, l'aller-
retour vers une page panier ajoute une etape ; un panneau visible en permanence reduit
la navigation. Le retrait de deux pages reduit aussi la surface a maintenir.
- **Decision : source de verite unique pour les donnees borne (#103).** Les JSON statiques
dupliquaient le catalogue de la base ; ils pouvaient diverger silencieusement. Passer par
l'API supprime la duplication et fait converger borne et back-office sur le meme modele.
### Criteres RNCP couverts
- **Bloc 2 - front client** : parcours borne complet (composeur, panier, abandon,
indisponibilite) sur donnees reelles par API.
- **Bloc 1 - regles metier** : RG-T21 (disponibilite calculee) appliquee de bout en bout.
---
## Bloc 3 — Saisie comptoir/drive en POS tactile (#100 puis #104)
### Ce qui a ete fait
- **#100** a d'abord refondu la saisie comptoir/drive en tant que formulaire enrichi :
prix affiches, verrou du mode de service au canal drive, navigation, file des commandes
recentes. `CounterOrderController` derive la `source` (counter/drive) du chemin de la
requete ; ajout de la liste des commandes recentes par canal.
- **#104** a ensuite remplace ce formulaire-liste par un **POS tactile a tuiles** facon
borne : onglets categories en haut, grille de tuiles produits/menus, panneau commande
persistant a droite. Pense pour la tablette (grandes cibles, un tap = ajout). Le panier
est construit cote client (`counter-order.js`, CSP `'self'`, vanilla, zero handler
inline) a partir d'un script JSON inerte, puis serialise dans un champ cache
`items_json`. Le serveur revalide la forme (RG-T18), recalcule les prix (RG-T16) et
resout les modificateurs : les prix cote client sont indicatifs.
### Pourquoi — decisions et alternatives
- **Decision : POS a tuiles, reutilisant l'UX borne (#104).** Cf. ADR-0011. L'equipier
comptoir et le client borne font le meme geste (choisir des produits, composer) ; un
meme paradigme tuiles+panneau reduit l'apprentissage et reutilise les patterns deja
eprouves. *Alternative ecartee* : garder le formulaire-liste (#100) — moins adapte au
tactile et a la rapidite attendue d'une caisse.
- **Decision : serveur seul juge des prix et de la composition.** Le client propose, le
serveur fige (RG-T16). Coherent avec le reste du domaine commande.
- **Decision : un controleur pour deux canaux, source derivee du chemin.** Le decoupage
par chemin (`/drive...` vs `/counter...`) plutot que par parametre rend les deux canaux
etanches : un equipier ne peut pas requalifier sa commande en falsifiant un champ.
### Comment — points techniques cles
- `CounterOrderController` : `source()` lue depuis le chemin ; `store()` decode
`items_json`, revalide, delegue a `createStaffOrder` (commande creee directement `paid`,
encaissement immediat, sans PIN — la permission `order.create` suffit). Repli legacy
`qty_<id>` accepte quand `items_json` est absent (degradation sans JS).
- `counter-order.js` : construction des onglets/grille/panneau, modale de composition pour
produits a modificateurs et menus, serialisation a la soumission.
### Criteres RNCP couverts
- **Bloc 2 - back-office** : ecran de caisse pour equipiers non-techniques (zero jargon),
CSP-safe sans framework front.
- **Bloc 1 - regles metier** : recalcul/revalidation serveur (RG-T16, RG-T18), etancheite
des canaux.
---
## Bloc 4 — Page Stock en tableau de bord (#105)
### Ce qui a ete fait
La page d'accueil Ingredients/Stock, jugee trop chargee et opaque, est refondue en
tableau de bord. Elle porte desormais : un bandeau expliquant le lien stock ->
disponibilite borne (un ingredient requis sous le seuil critique rend indisponibles les
produits qui l'utilisent, RG-T21) ; un resume comptant les ingredients critiques / en
alerte / au-dessus du seuil ; une section "A reapprovisionner" mettant en avant les
ingredients bas (critiques d'abord) avec barre de niveau et bouton de reapprovisionnement
direct. La liste complete passe au second plan et le CRUD est relegue. Les sous-pages
(reappro, inventaire, mouvements, creation) restent inchangees. `index()` expose des
compteurs par etat, calcules cote serveur a partir de `stock_band` deja resolu par le
depot, pour garder la vue declarative et la valeur testable.
### Pourquoi — decisions et alternatives
Cf. ADR-0012. **Decision : dashboard oriente action plutot que liste-CRUD.** Le metier
quotidien d'un equipier stock est de voir vite ce qui manque et de reapprovisionner, pas
d'editer des fiches. Mettre les alertes et le bouton de reappro en avant aligne l'ecran
sur ce geste. *Alternative ecartee* : garder une liste exhaustive triable — exhaustive
mais muette sur l'urgence.
### Criteres RNCP couverts
- **Bloc 2 - back-office** : ergonomie orientee tache pour utilisateur non-technique.
- **Bloc 1 - regles metier** : lien stock -> disponibilite (RG-T21) rendu explicite a
l'ecran.
---
## Verifications
A la cloture du lot : suite JS 135 tests verte (verifiee en local), suites PHPUnit et PHPStan niveau 6 vertes en CI Forgejo,
`php -l` propre. Chaque PR est passee par la CI Forgejo (checks requis) avant merge natif.
## Points d'amelioration conscients
- **#100 puis #104** : la refonte POS (#104) remplace une premiere refonte (#100) du meme
ecran a quelques jours d'intervalle. Le formulaire enrichi de #100 a servi de palier
avant le pivot tactile ; l'iteration est assumee plutot que masquee.
- **SMTP** : le client maison couvre le cas d'usage reset (un destinataire, texte). Il
n'est pas un agent mail generaliste ; tout besoin plus large (pieces jointes, files)
serait a reevaluer.
## Liens vers artefacts
- Commits : `8c5d942` (#94) -> `03ef99d` (#105).
- ADR associes : `docs/adr/0011-pos-tactile-tuiles-comptoir-drive.md`,
`docs/adr/0012-page-stock-tableau-de-bord.md`.
- Fichiers principaux : `.forgejo/workflows/deploy.yml`, `scripts/deploy.sh`,
`src/app/Controllers/HealthController.php`, `src/app/Auth/SmtpClient.php`,
`src/app/Controllers/CounterOrderController.php`,
`src/public/admin/assets/js/counter-order.js`,
`src/app/Controllers/IngredientController.php`,
`src/app/Views/admin/ingredients/index.php`.

View file

@ -33,8 +33,13 @@ Les fichiers sont ordonnes chronologiquement par leur nom.
| 2026-06-04 | [conception-prodlike-revision](2026-06-04--conception-prodlike-revision.md) | Revue d'alignement P1 + decisions prod-like du modele de donnees (drop commande_event, nommage EN, TVA par produit apres fact-check BOFiP, perso menus/ingredients, allergenes, ~16 entites) | `feat/p1-conception` |
| 2026-06-15 | [p3-throttle-pin-rg-t22](2026-06-15--p3-throttle-pin-rg-t22.md) | P3 securite : throttle du PIN d'action sensible (RG-T22) — design multi-agents + verification adversariale, dimension "utilisateur agissant", entite 22 `pin_throttle` | `feat/p3-pin-throttle` -> `dev` |
| 2026-06-16 | [audit-reel-livrables-p2-p3](2026-06-16--audit-reel-livrables-p2-p3.md) | Verification sur pieces des livrables du 2026-06-15 (sweep 10 dimensions + adversarial) : socle SbD confirme, miss confirmes par gravite (php.ini non deploye, CI sans tests DB, XSS kiosk, liens morts...) et remediations | `docs/journal-audit-2026-06-16` -> `dev` (PR #19/#20/#21) |
| 2026-06-04 | [p1-merise-v0.2-rewrite-and-forgejo-migration](2026-06-04--p1-merise-v0.2-rewrite-and-forgejo-migration.md) | P1 Merise v0.2 (prod-like) reecrit + migration vers Forgejo auto-heberge | `feat/p1-conception` |
| 2026-06-17 | [makefile-to-compose-migrate](2026-06-17--makefile-to-compose-migrate.md) | Du Makefile a `docker compose up` : service one-shot `wakdo-migrate` (migrate + seed idempotents) | `feat/compose-migrate` -> `dev` |
| 2026-06-17 | [session-infra-doc-e2e](2026-06-17--session-infra-doc-e2e.md) | Session infra compose, documentation Forge, amorce des tests E2E Playwright | `dev` (PR #37/#38/#39 et suivantes) |
| 2026-06-18 | [front-login-ui-admin-p4-commande](2026-06-18--front-login-ui-admin-p4-commande.md) | Page login, refonte UI admin (equipiers non-techniques), humanisation des libelles, amorce P4 commande (creation + encaissement) | `dev` (PR #48 a #58) |
| 2026-06-25 | [audit-remediation-et-features-94-105](2026-06-25--audit-remediation-et-features-94-105.md) | Synthese #94-#105 : CD push-based vers Vision + preuve de version, modeles compose prod, SMTP reel reset (Brevo), durcissement borne (Maxi 50cl, rupture non commandable, panier persistant, confirm abandon, allergenes /api), POS tactile comptoir/drive, page Stock en tableau de bord | `dev`/`main` (PR #94 a #105) |
*Mis a jour a chaque nouvelle entree.*
*Mis a jour a chaque nouvelle entree. Les entrees sont ordonnees par leur nom de fichier (date) ; cet index les liste dans l'ordre de redaction.*
---