diff --git a/docs/adr/0011-pos-tactile-tuiles-comptoir-drive.md b/docs/adr/0011-pos-tactile-tuiles-comptoir-drive.md new file mode 100644 index 0000000..803be47 --- /dev/null +++ b/docs/adr/0011-pos-tactile-tuiles-comptoir-drive.md @@ -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_` 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`. diff --git a/docs/adr/0012-page-stock-tableau-de-bord.md b/docs/adr/0012-page-stock-tableau-de-bord.md new file mode 100644 index 0000000..f311da0 --- /dev/null +++ b/docs/adr/0012-page-stock-tableau-de-bord.md @@ -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`. diff --git a/docs/adr/README.md b/docs/adr/README.md index 384b7e8..2a53cfb 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -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 diff --git a/docs/journal/2026-06-25--audit-remediation-et-features-94-105.md b/docs/journal/2026-06-25--audit-remediation-et-features-94-105.md new file mode 100644 index 0000000..04770cb --- /dev/null +++ b/docs/journal/2026-06-25--audit-remediation-et-features-94-105.md @@ -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_` 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`. diff --git a/docs/journal/README.md b/docs/journal/README.md index 73947df..96d25a0 100644 --- a/docs/journal/README.md +++ b/docs/journal/README.md @@ -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.* ---