corentin_wakdo/docs/merise/mct.md
Imugiii 5a4897921e
Some checks failed
CI / php-lint (push) Successful in 19s
CI / secret-scan (pull_request) Successful in 8s
CI / secret-scan (push) Successful in 10s
CI / static-tests (push) Successful in 30s
CI / php-lint (pull_request) Successful in 19s
CI / auto-merge (push) Has been skipped
CI / static-tests (pull_request) Successful in 30s
CI / auto-merge (pull_request) Failing after 4s
feat(admin): throttle du PIN d'action sensible par acteur (RG-T22)
Ferme le finding HIGH de la revue Produits (#17) : le PIN d'action sensible
etait verifie sans limitation de tentatives. Conception via panel multi-agents
(3 lentilles + synthese + passe adversariale, holds=true) puis revue de
l'implementation (holds=true).

Dimension du throttle = UTILISATEUR AGISSANT (identite de session, RG-T02), pas
l'email cible (contournable par rotation) ni l'IP (collateral sur poste partage).
Table dediee pin_throttle (entite 22) STRICTEMENT SEPAREE des compteurs de login
(user.failed_login_attempts / login_throttle) : un echec de PIN n'incremente aucun
compteur de connexion (pas d'escalade DoS vers le login).

- db/migrations/0002_pin_throttle.sql : table cle sur actor_user_id (UNIQUE, FK
  -> user ON DELETE CASCADE), separee du login. Appliquee a la base dev.
- ThrottlePolicy : dimension 'pin' (bornes propres PIN_THROTTLE_*, 30s..300s, plus
  permissives que le login : controle de dissuasion, residuel Faible).
- PinThrottle (nouveau) : isLocked / recordFailure (upsert atomique + backoff, une
  transaction, miroir d'AuthService) / reset (UPDATE simple). N'ecrit jamais
  user/login_throttle/audit_log.
- PinVerifier::payTimingDecoy : parite de timing du chemin verrouille.
- ProductController update/destroy : gate AVANT verification (leurre + 422
  generique, pas de pin.failed sous verrou actif = borne anti-flood de l'audit) ;
  recordFailure sur PIN faux ; reset sur succes, cle sur l'acteur de SESSION.
- Docs Merise 21 -> 22 entites : RG-T22 (mlt), entite 22 pin_throttle
  (mcd/mld/dictionary), couverture MCT 22/22 (mct).
- .env.example + docker-compose : PIN_THROTTLE_THRESHOLD/BASE/MAX/WINDOW.
- Journal RNCP : docs/journal/2026-06-15--p3-throttle-pin-rg-t22.md.

Tests : 188 verts (525 assertions), PHPStan L6 propre.
2026-06-15 22:03:07 +00:00

43 KiB

Modele Conceptuel des Traitements (MCT) — Wakdo

Phase Merise : P1 - Conception, etape 3 (apres le MCD) Version : v0.2 — prod-like, machine a 4 etats (+ couche security-by-design 2026-06-11) Date : 2026-06-04 (ajouts security-by-design 2026-06-11) Branche : feat/p1-conception Statut : prod-like — toutes les decisions D1-D8 + stock appliquees (voir docs/notes/revue-alignement-p1.md §7) ; operations security-by-design ajoutees (ERASE_USER_PII, RESET_PASSWORD, ensemble sensible protege par PIN, ecritures audit_log, throttling d'authentification) — 28 operations Auteur : BYAN (couche methodologie)


1. Objectif

Le MCT (Modele Conceptuel des Traitements) decrit les operations metier du domaine Wakdo sous la forme canonique Merise : evenement declencheur -> operation -> resultat emis.

Il repond a la question : que se passe-t-il dans le domaine, et quand ? Il ne repond pas a : qui fait quoi, sur quel poste de travail, dans quel ordre organisationnel (le niveau MOT est volontairement saute — raccourci agile, coherent avec le cadre RNCP solo).

Le MCT couvre :

  • Le cycle de vie de la commande de bout en bout (kiosk, comptoir, drive)
  • La gestion du catalogue (manager / admin)
  • La gestion des utilisateurs et des roles (admin)
  • L'authentification back-office (tous les acteurs back-office)

Acteurs identifies :

Acteur Code Interface
Client (kiosk) CUSTOMER Borne tactile (public, non authentifie)
Personnel comptoir COUNTER Back-office, role counter
Personnel drive DRIVE Back-office, role drive
Personnel cuisine KITCHEN Back-office, role kitchen (lecture seule sur les commandes)
Manager MANAGER Back-office, role manager
Administrateur ADMIN Back-office, role admin
Systeme SYS API interne / logique PHP

Reference croisee MCD : chaque operation reference des entites du MCD (section 14). Le MCT est coherent avec la machine a etats de customer_order.status :

pending_payment -> paid -> delivered
      |              |
      +--------------+-----------> cancelled (from any non-terminal state)

Etats supprimes (par rapport a v0.1) : preparing et ready sont retires. Justification : dans un contexte fast-food, l'affichage cuisine (KDS) est un systeme visuel ; le personnel lit le ticket et agit. L'unique geste du personnel est « delivrer ». Le KPI est le temps total delivered_at - paid_at (SLA approx. 10 min). Le code couleur du KDS est calcule a partir de now - paid_at ; aucun etat stocke supplementaire n'est requis.

Operations supprimees (par rapport a v0.1) : MARK_IN_PREPARATION (MARQUER_EN_PREPARATION) et MARK_READY (MARQUER_PRETE) sont retirees car leurs etats intermediaires n'existent plus. DELIVER_ORDER devient la seule action faisant avancer le statut pour le personnel comptoir/drive.

Couche security-by-design (2026-06-11) : deux operations sont ajoutees — RESET_PASSWORD (12.3) et ERASE_USER_PII (10.5, anonymisation RGPD). Un sous-ensemble d'operations est protege par PIN : les sessions back-office restent partagees par poste de travail, mais un PIN par membre du personnel re-autorise l'ensemble sensible — CANCEL_ORDER (7.1), UPDATE_PRODUCT/DELETE_PRODUCT (8.2/8.3), DELETE_MENU (8.6), INVENTORY_COUNT (9.2), gestion des utilisateurs (10.1-10.3), MANAGE_RBAC (10.4), ERASE_USER_PII (10.5). Ces actions hors stock ajoutent une ligne audit_log immuable (acteur, action, cible) ; les actions de stock enregistrent l'attribution dans stock_movement. La logique de traitement (PIN, audit, throttling, idempotence, decrement atomique du stock, disponibilite produit calculee) est specifiee dans mlt.md (regles RG-T13-T21). Cela ajoute les entites 20 audit_log et 21 login_throttle au modele.


2. Conventions de representation

Format des operations

[TRIGGERING EVENT(S)]
        |
        | [SYNCHRONISATION RULE / CONDITION]
        v
   ( OPERATION )
        |
        v
[EMITTED RESULT(S)]

Synchronisations :

  • AND : tous les evenements doivent etre presents simultanement pour declencher l'operation.
  • OR : l'un quelconque des evenements suffit.

Conditions : exprimees entre crochets [condition] sur l'arc entrant.

Notation textuelle

Pour chaque operation, le document fournit :

  • Evenement(s) declencheur(s) : ce qui survient et provoque l'operation.
  • Acteur(s) : qui initie (ou valide).
  • Synchronisation : AND / OR si plusieurs evenements, plus la condition.
  • Operation : nom et description de ce qu'elle fait.
  • Entites MCD touchees : lecture (R) ou ecriture (W).
  • Resultat(s) : ce qui est emis ou produit.

3. Domaine 1 — Cycle de vie de la commande (kiosk)

3.1 LOAD_CATALOGUE

Champ Valeur
Evenement declencheur Le client ouvre le kiosk (connexion a l'endpoint du kiosk)
Acteur CUSTOMER
Synchronisation Aucune (evenement unique)
Condition Le kiosk est en service (dans les horaires d'ouverture 10:00-01:00)
Operation LOAD_CATALOGUE
Description Recuperation des categories actives, des produits disponibles et des menus disponibles (avec leurs slots et options eligibles) pour affichage sur l'ecran du kiosk. La disponibilite des produits est CALCULEE : un produit est commandable seulement si son flag is_available est positionne ET que chaque ingredient non retirable (is_removable=0) de son product_ingredient est au-dessus de la bande critique (stock_quantity > stock_capacity * critical_stock_pct/100). Voir la regle RG-T21 dans mlt.md.
Entites MCD R: category (is_active=1), product (is_available=1), menu (is_available=1), menu_slot, menu_slot_option, ingredient (is_active=1), allergen, ingredient_allergen
Resultat Catalogue charge ; le kiosk affiche l'ecran d'accueil

3.2 COMPOSE_CART

Champ Valeur
Evenement declencheur Le client selectionne un produit ou un menu sur le kiosk
Acteur CUSTOMER
Synchronisation Evenement repetable (OR : ajouter produit, ajouter menu, modifier quantite, retirer un article, choisir un slot de menu, choisir le format Normal/Maxi, ajouter/retirer un modificateur d'ingredient)
Condition Le produit ou le menu selectionne a is_available=1
Operation COMPOSE_CART
Description Construction du panier en memoire : ajouter un article (produit autonome ou menu), selectionner les produits des slots (order_item_selection), modifier optionnellement les ingredients (order_item_modifier), choisir le format Normal ou Maxi pour les menus, recalculer le total TTC. Le panier est une structure volatile cote client ; aucune ecriture en base a ce stade.
Entites MCD R: product, menu, menu_slot, menu_slot_option, ingredient, product_ingredient — W: aucune (etat volatile front-end)
Resultat Panier mis a jour, total recalcule, recapitulatif affiche

3.3 CREATE_ORDER

Champ Valeur
Evenements declencheurs 1. Le client confirme le panier (appuie sur « Valider ») AND 2. Le client saisit son numero de commande (substitut de paiement RNCP)
Acteur CUSTOMER
Synchronisation AND (les deux actions requises)
Condition Le panier contient au moins 1 article. Le numero de commande saisi est non vide.
Operation CREATE_ORDER
Description Creation atomique de la commande : INSERT customer_order avec statut pending_payment, source kiosk, snapshot des totaux HT/TVA/TTC (calcules ligne par ligne en utilisant vat_rate snapshote par article). INSERT des lignes order_item avec label_snapshot, unit_price_cents_snapshot, vat_rate_snapshot. INSERT order_item_selection pour chaque slot rempli dans un article de menu. INSERT order_item_modifier pour chaque modification d'ingredient. Decrement de ingredient.stock_quantity pour chaque ingredient consomme (ajuste par les modificateurs : retrait => pas de decrement ; ajout => decrement supplementaire) ; INSERT d'une ligne stock_movement de type sale par unite d'ingredient affectee. Les decrements de stock et l'insertion de la commande sont dans la meme transaction. Apres que le client a saisi son numero de commande, le statut passe pending_payment -> paid dans la meme transaction ; paid_at est positionne. Le systeme genere le numero de commande au format K-YYYY-MM-DD-NNN.
Entites MCD R: product, menu, ingredient, product_ingredient (snapshot) — W: customer_order (INSERT status pending_payment then UPDATE status paid, paid_at), order_item (INSERT N lines), order_item_selection (INSERT per menu slot chosen), order_item_modifier (INSERT per modification), ingredient (UPDATE stock_quantity), stock_movement (INSERT type sale per unit)
Resultat Commande creee (statut paid en fin d'operation), numero de commande affiche au client, evenement logique ORDER_CREATED emis vers le domaine de preparation

3.4 DISPLAY_CONFIRMATION

Champ Valeur
Evenement declencheur ORDER_CREATED (reponse API 201 apres CREATE_ORDER)
Acteur SYS
Synchronisation Aucune
Condition La reponse API contient un id, un order_number et le statut paid
Operation DISPLAY_CONFIRMATION
Description Affichage de l'ecran de confirmation sur le kiosk avec le numero de commande. Le kiosk se reinitialise ensuite pour le client suivant.
Entites MCD R: aucune (les donnees sont dans la reponse API)
Resultat Ecran de confirmation affiche ; kiosk disponible pour la commande suivante

4. Domaine 2 — Cycle de vie de la commande (comptoir et drive)

4.1 CREATE_COUNTER_ORDER

Champ Valeur
Evenement declencheur Un membre du personnel comptoir ou drive initie une nouvelle commande depuis le back-office
Acteur COUNTER ou DRIVE
Synchronisation Aucune
Condition L'acteur est authentifie et detient la permission order.create. La source est counter ou drive (auto-taggee depuis role.order_source).
Operation CREATE_COUNTER_ORDER
Description Composition manuelle de la commande via le back-office : selectionner produits et menus, choisir le mode de service (dine_in/takeaway/drive), remplir les slots de menu, ajouter des modificateurs d'ingredient. Logique de creation identique a CREATE_ORDER (snapshot, decrement de stock dans la meme transaction, transition atomique pending_payment -> paid). La source est auto-taggee depuis role.order_source (counter -> counter, drive -> drive). Format du numero de commande : C-YYYY-MM-DD-NNN (comptoir) ou D-YYYY-MM-DD-NNN (drive). Contrainte croisee : si source = 'drive' alors service_mode = 'drive' (verifie a la creation).
Entites MCD R: product, menu, menu_slot, menu_slot_option, ingredient, product_ingredient — W: customer_order (INSERT status pending_payment then UPDATE status paid, paid_at), order_item, order_item_selection, order_item_modifier, ingredient (stock decrement), stock_movement (INSERT type sale)
Resultat Commande creee (statut paid), numero de commande communique au client

5. Domaine 3 — Affichage de preparation (cuisine)

5.1 LIST_ORDERS_DISPLAY

Champ Valeur
Evenement declencheur Le personnel cuisine accede a l'affichage de preparation ou le rafraichit
Acteur KITCHEN (ou COUNTER, DRIVE, ADMIN)
Synchronisation Aucune
Condition L'acteur est authentifie et detient la permission order.read.
Operation LIST_ORDERS_DISPLAY
Description Lecture des lignes customer_order avec statut paid, filtrees par les sources visibles selon le role de l'acteur (depuis role_visible_source) : la cuisine voit toutes les sources ; le comptoir voit kiosk+counter ; le drive voit drive. Les commandes sont triees par paid_at ascendant (les plus anciennes en premier). Pour chaque commande, afficher : numero de commande, source, contenu (order_item avec label_snapshot, quantity, format, selections de slots, modificateurs d'ingredient). La couleur KDS est calculee a partir de now - paid_at par rapport au seuil de SLA (approx. 10 min), non stockee. Le personnel cuisine n'effectue aucune transition de statut — c'est une operation en lecture seule.
Entites MCD R: customer_order (status=paid), order_item, order_item_selection, order_item_modifier, role_visible_source
Resultat Liste d'affichage de preparation montree, triee par heure de paiement ascendante

6. Domaine 4 — Livraison au client

6.1 DELIVER_ORDER

Champ Valeur
Evenements declencheurs 1. La commande est au statut paid AND 2. Le personnel comptoir ou drive clique sur « Livre »
Acteur COUNTER ou DRIVE
Synchronisation AND
Condition La commande a le statut paid. L'acteur detient la permission order.deliver. Le role de l'acteur est coherent avec la source de la commande (le personnel comptoir traite les commandes kiosk+counter ; le personnel drive traite les commandes drive — filtre par role_visible_source).
Operation DELIVER_ORDER
Description Transition en geste unique paid -> delivered. Positionne delivered_at = NOW(). La commande passe en historique. Cette operation remplace la sequence en deux etapes de v0.1 (marquer-prete puis livrer) ; la confirmation visuelle de la cuisine (KDS) suffit avant cette action.
Entites MCD W: customer_order (UPDATE status paid -> delivered, delivered_at = NOW())
Resultat Commande au statut delivered, cycle de vie complet

7. Domaine 5 — Annulation

7.1 CANCEL_ORDER

Champ Valeur
Evenement declencheur Un acteur autorise demande l'annulation d'une commande
Acteur COUNTER, DRIVE ou ADMIN
Synchronisation Aucune
Condition La commande existe. customer_order.status est dans ['pending_payment', 'paid']. Les statuts terminaux delivered et cancelled ne peuvent pas transiter vers cancelled. L'acteur detient la permission order.cancel.
Operation CANCEL_ORDER
Description Transition du statut courant vers cancelled. Positionne cancelled_at = NOW(). La commande est conservee en base pour l'historique et les stats (pas de suppression physique). Si le statut courant est paid, le stock est recredite : pour chaque ingredient consomme par la commande (en tenant compte des modificateurs), ingredient.stock_quantity est incremente ; une ligne stock_movement de type cancellation est inseree par unite d'ingredient affectee. Le recredit du stock et la mise a jour du statut sont dans la meme transaction.
Entites MCD R: order_item, order_item_modifier, ingredient, product_ingredient — W: customer_order (UPDATE status -> cancelled, cancelled_at = NOW()), ingredient (UPDATE stock_quantity, conditional on status paid), stock_movement (INSERT type cancellation, conditional on status paid)
Resultat Commande au statut cancelled, visible dans l'historique admin

8. Domaine 6 — Gestion du catalogue

8.1 CREATE_PRODUCT

Champ Valeur
Evenement declencheur L'admin ou le manager soumet le formulaire de creation de produit
Acteur ADMIN ou MANAGER
Synchronisation Aucune
Condition L'acteur detient la permission product.create. La categorie cible existe et is_active=1. name est non vide. price_cents > 0.
Operation CREATE_PRODUCT
Description INSERT d'un nouveau product avec sa categorie, son nom, son prix en centimes, son taux de TVA en pour-mille (vat_rate : 100=10%, 55=5.5%, defaut 100), chemin d'image optionnel. is_available=1 par defaut.
Entites MCD R: category (FK validation) — W: product (INSERT)
Resultat Produit cree, redirection vers la liste des produits

8.2 UPDATE_PRODUCT

Champ Valeur
Evenement declencheur L'admin ou le manager soumet le formulaire de modification de produit
Acteur ADMIN ou MANAGER
Synchronisation Aucune
Condition L'acteur detient la permission product.update. Le produit existe. Les nouvelles valeurs respectent les contraintes (price_cents > 0, nom non vide).
Operation UPDATE_PRODUCT
Description UPDATE des colonnes modifiables (name, description, price_cents, vat_rate, image_path, is_available, display_order, category_id). Les snapshots deja stockes dans order_item ne sont pas affectes (integrite historique garantie par conception).
Entites MCD W: product (UPDATE)
Resultat Produit mis a jour, liste des produits rafraichie

8.3 DELETE_PRODUCT

Champ Valeur
Evenement declencheur L'admin confirme la suppression d'un produit
Acteur ADMIN
Synchronisation Aucune
Condition L'acteur detient la permission product.delete. Le produit n'est option de slot dans aucun menu_slot_option (FK ON DELETE RESTRICT). Le produit n'est reference dans aucune ligne historique order_item (FK ON DELETE RESTRICT). Verification prealable requise.
Operation DELETE_PRODUCT
Description Suppression physique du produit si aucune contrainte FK ne la bloque. Si le produit est reference dans un slot de menu ou une ligne de commande historique, la suppression est bloquee. L'alternative recommandee est de le desactiver (is_available=0). Bloque egalement si le produit est le burger_product_id d'un menu.
Entites MCD W: product (DELETE — blocked if referenced in menu_slot_option, order_item, or menu.burger_product_id)
Resultat Produit supprime OU erreur « produit utilise »

8.4 CREATE_MENU

Champ Valeur
Evenement declencheur L'admin ou le manager soumet le formulaire de creation de menu avec sa configuration de slots
Acteur ADMIN ou MANAGER
Synchronisation Aucune
Condition L'acteur detient la permission menu.create. name est non vide. price_normal_cents > 0, price_maxi_cents > 0. burger_product_id reference un produit existant. Au moins un slot est defini avec au moins une option.
Operation CREATE_MENU
Description Transaction : INSERT menu (avec burger_product_id, price_normal_cents, price_maxi_cents), puis INSERT des lignes menu_slot (une par slot : boisson, accompagnement, sauce...), puis INSERT des lignes menu_slot_option (produits eligibles par slot).
Entites MCD R: product (burger FK validation, slot options validation), category — W: menu (INSERT), menu_slot (INSERT), menu_slot_option (INSERT)
Resultat Menu cree avec sa configuration de slots, visible sur le kiosk

8.5 UPDATE_MENU

Champ Valeur
Evenement declencheur L'admin ou le manager soumet le formulaire de modification de menu
Acteur ADMIN ou MANAGER
Synchronisation Aucune
Condition L'acteur detient la permission menu.update. Le menu existe. La configuration mise a jour preserve au moins un slot avec au moins une option.
Operation UPDATE_MENU
Description UPDATE des colonnes menu. Si la configuration des slots est modifiee : DELETE de toutes les lignes menu_slot_option pour les slots de ce menu, DELETE des lignes menu_slot, puis re-INSERT (pattern delete-and-reinsert, atomique en transaction). Les snapshots dans order_item ne sont pas affectes.
Entites MCD W: menu (UPDATE), menu_slot (DELETE + INSERT), menu_slot_option (DELETE + INSERT)
Resultat Menu mis a jour

8.6 DELETE_MENU

Champ Valeur
Evenement declencheur L'admin confirme la suppression d'un menu
Acteur ADMIN
Synchronisation Aucune
Condition L'acteur detient la permission menu.delete. Le menu n'est reference dans aucune ligne historique order_item (FK ON DELETE RESTRICT). Verification prealable requise.
Operation DELETE_MENU
Description Si aucun order_item ne reference ce menu : DELETE menu_slot_option (CASCADE from menu_slot), DELETE menu_slot (CASCADE from menu), DELETE menu. Si des references historiques existent, proposer la desactivation (is_available=0) a la place.
Entites MCD W: menu_slot_option (DELETE CASCADE), menu_slot (DELETE CASCADE), menu (DELETE — blocked if referenced in order_item)
Resultat Menu supprime OU erreur « menu present dans des commandes historiques »

8.7 MANAGE_CATEGORY

Champ Valeur
Evenement declencheur L'admin ou le manager cree, modifie ou desactive une categorie
Acteur ADMIN ou MANAGER
Synchronisation OR (creation, modification, desactivation)
Condition L'acteur detient la permission category.manage. Pour la desactivation : les produits et menus de la categorie ne sont pas auto-desactives en base (pas de CASCADE sur is_active) ; la couche applicative propose de desactiver les produits/menus enfants.
Operation MANAGE_CATEGORY
Description CRUD sur category. La desactivation (is_active=0) masque la categorie et ses produits du kiosk sans suppression physique. La suppression physique est bloquee si des produits ou des menus referencent cette categorie (FK ON DELETE RESTRICT).
Entites MCD W: category (INSERT / UPDATE / conditional DELETE)
Resultat Categorie creee / modifiee / desactivee

8.8 MANAGE_INGREDIENT

Champ Valeur
Evenement declencheur L'admin ou le manager cree, modifie ou desactive un ingredient ; ou gere la composition produit (product_ingredient) ou le mapping allergene (ingredient_allergen)
Acteur ADMIN ou MANAGER
Synchronisation OR (creer ingredient, modifier ingredient, modifier composition, modifier mapping allergene)
Condition L'acteur detient la permission ingredient.manage.
Operation MANAGE_INGREDIENT
Description CRUD sur ingredient (name, unit, pack_size, pack_label, stock_capacity, low_stock_pct, critical_stock_pct, is_active). Gestion de la composition product_ingredient (quantity_normal, quantity_maxi, is_removable, is_addable, extra_price_cents) pour tout produit. Gestion du mapping ingredient_allergen (14 allergenes reglementes UE). Desactiver un ingredient (is_active=0) le masque du configurateur sans suppression. La suppression physique de ingredient est bloquee s'il est reference dans product_ingredient (FK ON DELETE RESTRICT) ou stock_movement (FK ON DELETE RESTRICT).
Entites MCD R: product (FK validation), allergen (FK validation) — W: ingredient (INSERT/UPDATE/DELETE conditional), product_ingredient (INSERT/UPDATE/DELETE), ingredient_allergen (INSERT/DELETE)
Resultat Ingredient / composition / mapping allergene mis a jour

9. Domaine 7 — Gestion du stock

9.1 RESTOCK

Champ Valeur
Evenement declencheur Le manager ou l'admin enregistre une livraison de packs d'ingredient
Acteur MANAGER ou ADMIN
Synchronisation Aucune
Condition L'acteur detient la permission stock.manage. L'ingredient existe et is_active=1. Nombre de packs N >= 1.
Operation RESTOCK
Description UPDATE ingredient.stock_quantity += N * pack_size. INSERT d'une ligne stock_movement : type restock, delta += N * pack_size, user_id de l'acteur, note optionnelle (ex. reference de livraison). Les deux ecritures sont dans la meme transaction.
Entites MCD R: ingredient — W: ingredient (UPDATE stock_quantity), stock_movement (INSERT type restock)
Resultat Stock incremente, mouvement journalise

9.2 INVENTORY_COUNT

Champ Valeur
Evenement declencheur Un membre du personnel ou un manager enregistre le resultat d'un inventaire physique
Acteur KITCHEN, COUNTER, DRIVE, MANAGER ou ADMIN
Synchronisation Aucune
Condition L'acteur detient la permission stock.count. L'ingredient existe. Comptage physique actual_quantity >= 0.
Operation INVENTORY_COUNT
Description Calcul de delta = actual_quantity - ingredient.stock_quantity (peut etre negatif ou positif). UPDATE ingredient.stock_quantity = actual_quantity. INSERT d'une ligne stock_movement : type inventory_correction, delta = ecart calcule, user_id de l'acteur, note optionnelle. Les deux ecritures dans la meme transaction.
Entites MCD R: ingredient (read current stock_quantity) — W: ingredient (UPDATE stock_quantity), stock_movement (INSERT type inventory_correction)
Resultat Stock reconcilie au comptage physique, ecart journalise

9.3 READ_STOCK

Champ Valeur
Evenement declencheur Un acteur autorise accede a la vue du stock
Acteur KITCHEN, COUNTER, DRIVE, MANAGER ou ADMIN
Synchronisation Aucune
Condition L'acteur detient la permission stock.read.
Operation READ_STOCK
Description Lecture de la liste ingredient avec le stock_quantity courant, stock_capacity, stock_pct calcule, low_stock_pct, critical_stock_pct, pack_size, pack_label. Bandes de stock calculees au moment de l'affichage : low_stock lorsque stock_quantity <= stock_capacity * low_stock_pct/100, critical_stock lorsque stock_quantity <= stock_capacity * critical_stock_pct/100. Optionnel : lecture de l'historique stock_movement pour un ingredient donne, filtre par plage de dates.
Entites MCD R: ingredient, stock_movement (optional history)
Resultat Liste du stock affichee avec indicateurs de stock bas

10. Domaine 8 — Gestion des utilisateurs et des roles (admin)

10.1 CREATE_USER

Champ Valeur
Evenement declencheur L'admin soumet le formulaire de creation d'utilisateur
Acteur ADMIN
Synchronisation Aucune
Condition L'acteur detient la permission user.create. L'email n'existe pas deja dans user.email (contrainte UNIQUE). Un role_id valide et actif est selectionne.
Operation CREATE_USER
Description INSERT de l'utilisateur avec un hash de mot de passe argon2id. L'email est unique. role_id est obligatoire (FK NOT NULL). is_active=1 par defaut. last_login_at=NULL a la creation.
Entites MCD R: role (FK validation) — W: user (INSERT)
Resultat Utilisateur cree, peut se connecter au back-office

10.2 UPDATE_USER

Champ Valeur
Evenement declencheur L'admin soumet le formulaire de modification d'utilisateur
Acteur ADMIN
Synchronisation Aucune
Condition L'acteur detient la permission user.update. L'utilisateur existe. Si un nouveau mot de passe est fourni, il est re-hashe.
Operation UPDATE_USER
Description UPDATE des champs modifiables (first_name, last_name, email, role_id, is_active). Si un nouveau mot de passe est fourni, il remplace le hash existant (rehash argon2id).
Entites MCD W: user (UPDATE)
Resultat Utilisateur mis a jour

10.3 DEACTIVATE_USER

Champ Valeur
Evenement declencheur L'admin clique sur « Desactiver » pour un utilisateur
Acteur ADMIN
Synchronisation Aucune
Condition L'acteur detient la permission user.deactivate. L'admin ne peut pas desactiver son propre compte (protection au niveau applicatif).
Operation DEACTIVATE_USER
Description UPDATE is_active=0. La session active de l'utilisateur est invalidee au prochain acces (le middleware verifie is_active=1 a chaque requete authentifiee). L'utilisateur n'est pas supprime ; l'historique reste tracable.
Entites MCD W: user (UPDATE is_active=0)
Resultat Utilisateur desactive, acces back-office bloque

10.4 MANAGE_RBAC

Champ Valeur
Evenement declencheur L'admin modifie les affectations de permissions pour un role, ou cree / modifie un role personnalise
Acteur ADMIN
Synchronisation OR (modifier les permissions du role, creer un role personnalise, modifier les attributs du role)
Condition L'acteur detient la permission role.manage. Les permissions selectionnees existent dans le catalogue permission.
Operation MANAGE_RBAC
Description Mise a jour de role_permission pour un role donne : DELETE des affectations existantes, INSERT des nouvelles (delete-and-reinsert, atomique en transaction). Les permissions elles-memes sont statiques (declarees en migration, non modifiables via l'UI). Couvre egalement : CREATE/UPDATE d'un role personnalise (code, label, description, default_route, order_source), UPDATE de role_visible_source (sources de tableau de bord visibles pour le role). Regle d'architecture RBAC : le code applicatif teste les permissions, pas les noms de role — ajouter un nouveau role avec les bonnes permissions ne requiert aucun changement de code.
Entites MCD R: role, permission — W: role_permission (DELETE + INSERT), role (INSERT/UPDATE for custom roles), role_visible_source (INSERT/DELETE)
Resultat Matrice RBAC mise a jour, effective immediatement pour les nouvelles requetes des utilisateurs porteurs de ce role

10.5 ERASE_USER_PII (security-by-design)

Champ Valeur
Evenement declencheur Une demande d'effacement RGPD est traitee pour un utilisateur back-office
Acteur ADMIN (protege par PIN)
Synchronisation Aucune
Condition L'acteur detient la permission user.update et s'est re-autorise via PIN. L'utilisateur cible existe et n'est pas deja anonymise.
Operation ERASE_USER_PII
Description Le droit a l'effacement RGPD est honore par anonymisation, non par suppression physique : les PII (email, first_name, last_name) sont effacees/remplacees par un placeholder non identifiant, les identifiants invalides, anonymized_at positionne. La ligne persiste afin que les liens referentiels (stock_movement, customer_order, audit_log) restent valides et se resolvent vers un principal anonymise. Voir mlt.md 10.5 et la note 13 du dictionnaire.
Entites MCD W: user (UPDATE — PII cleared, anonymized_at set), audit_log (INSERT)
Resultat Utilisateur anonymise ; PII supprimees ; liens d'imputabilite preserves ; une ligne audit_log enregistree

11. Domaine 9 — Stats et KPI

11.1 READ_STATS

Champ Valeur
Evenement declencheur Le manager ou l'admin accede au tableau de bord des stats
Acteur MANAGER ou ADMIN
Synchronisation Aucune
Condition L'acteur detient la permission stats.read.
Operation READ_STATS
Description Requetes d'agregation sur customer_order et order_item. Agregations cles : nombre de commandes et chiffre d'affaires (TTC) par service_day (calcule avec CASE WHEN HOUR(created_at) < 10 THEN DATE(created_at) - INTERVAL 1 DAY ELSE DATE(created_at) END ; coupure a 10:00) ; top produits par COUNT de label_snapshot dans order_item ; taux d'annulation ; temps de livraison moyen delivered_at - paid_at ; ventilation par source et service_mode. Les requetes excluent les commandes annulees des sommes de chiffre d'affaires mais les incluent dans les comptages de volume. Pas de colonne stockee supplementaire pour service_day ; calcul au moment de la requete.
Entites MCD R: customer_order, order_item
Resultat Tableau de bord des stats affiche

12. Domaine 10 — Authentification back-office

12.1 AUTHENTICATE_USER

Champ Valeur
Evenement declencheur Un acteur soumet le formulaire de connexion
Acteur COUNTER / DRIVE / KITCHEN / MANAGER / ADMIN
Synchronisation Aucune
Condition Le compte n'est pas dans une fenetre de throttling (lockout_until). L'email existe en base. Le mot de passe correspond au hash argon2id. L'utilisateur is_active=1.
Operation AUTHENTICATE_USER
Description Verification des identifiants. Si valide : regeneration de l'ID de session (protection contre la fixation de session), stockage de user_id et role_id en session, UPDATE last_login_at, remise a zero du compteur d'echecs de connexion. En cas d'echec : incrementation de failed_login_attempts et application d'un backoff degressif (lockout_until), erreur generique resistante a l'enumeration. Idle timeout : 4h. Absolute timeout : 10h. Redirection vers role.default_route. Voir mlt.md 12.1.
Entites MCD R: user (verification), role (load permissions, default_route), role_permission, login_throttle (the per-IP throttle gate) — W: user (UPDATE last_login_at, failed_login_attempts, lockout_until), login_throttle (upsert failed_attempts/lockout_until on failure, clear on success), audit_log (INSERT login success/failure)
Resultat Session ouverte, redirection vers la vue par defaut specifique au role ; ou echec throttle journalise

12.2 LOGOUT_USER

Champ Valeur
Evenement declencheur L'acteur clique sur « Deconnexion » OU la session expire
Acteur COUNTER / DRIVE / KITCHEN / MANAGER / ADMIN / SYS (expiration)
Synchronisation OR
Condition Une session valide est ouverte
Operation LOGOUT_USER
Description Destruction de la session PHP (session_destroy()). Session supprimee cote serveur. Cookie de session invalide.
Entites MCD Aucune ecriture en base (la gestion des sessions est en PHP natif, hors base pour ce projet)
Resultat Session detruite, redirection vers la page de connexion

12.3 RESET_PASSWORD (security-by-design)

Champ Valeur
Evenement declencheur Un utilisateur demande une reinitialisation de mot de passe, puis la confirme via le lien envoye par email
Acteur COUNTER / DRIVE / KITCHEN / MANAGER / ADMIN
Synchronisation Sequentielle en deux phases : demande, puis confirmation
Condition Demande : l'email soumis est traite de maniere resistante a l'enumeration (meme reponse neutre qu'il existe ou non). Confirmation : un token valide et non expire est presente.
Operation RESET_PASSWORD
Description La phase de demande genere un token aleatoire, stocke son hash + expiration, et envoie le token brut une seule fois par email. La phase de confirmation valide le hash du token + expiration, remplace password_hash (argon2id), efface le token et remet a zero le compteur d'echecs de connexion. Voir mlt.md 12.3.
Entites MCD W: user (UPDATE password_reset_token_hash + password_reset_expires_at on request; UPDATE password_hash, clear token, reset failed_login_attempts/lockout_until on confirm), audit_log (INSERT)
Resultat Mot de passe reinitialise via un token a usage unique et a duree limitee ; une ligne audit_log enregistree

13. Machine a etats — customer_order.status

Recapitulatif des transitions couvertes par les operations MCT.

               [CUSTOMER / COUNTER / DRIVE]
               CREATE_ORDER
               CREATE_COUNTER_ORDER
                      |
                      v
           [ pending_payment ]  (order composed, payment pending)
                      |
    [CUSTOMER / COUNTER / DRIVE] payment confirmed
    (atomic within CREATE_ORDER / CREATE_COUNTER_ORDER)
                      |
                      v
                 [ paid ]
                      |
      [COUNTER / DRIVE] DELIVER_ORDER
                      |
                      v
               [ delivered ]  (terminal, cannot be cancelled)


  From pending_payment / paid:
  [COUNTER, DRIVE, or ADMIN] CANCEL_ORDER
                      |
                      v
               [ cancelled ]  (terminal)

Note sur la transition pending_payment -> paid : dans le contexte RNCP, le paiement est remplace par la saisie du numero de commande par le client (kiosk) ou par la validation du personnel (comptoir/drive). La transition est atomique au sein de CREATE_ORDER et CREATE_COUNTER_ORDER. Le statut pending_payment n'est pas observable en dehors de la transaction.

Supprime de v0.1 : etats preparing et ready ; operations MARK_IN_PREPARATION et MARK_READY. Le personnel cuisine a une vue en lecture seule des commandes paid (LIST_ORDERS_DISPLAY). L'unique action de livraison (DELIVER_ORDER) condense la sequence en trois etapes de v0.1 en un seul geste.


14. Tableau recapitulatif des operations

# Operation Domaine Acteur Entites W Entites R
1 LOAD_CATALOGUE Order kiosk CUSTOMER category, product, menu, menu_slot, menu_slot_option, ingredient, allergen, ingredient_allergen
2 COMPOSE_CART Order kiosk CUSTOMER — (volatile) product, menu, menu_slot, menu_slot_option, ingredient, product_ingredient
3 CREATE_ORDER Order kiosk CUSTOMER customer_order, order_item, order_item_selection, order_item_modifier, ingredient, stock_movement product, menu, ingredient, product_ingredient
4 DISPLAY_CONFIRMATION Order kiosk SYS
5 CREATE_COUNTER_ORDER Order counter/drive COUNTER/DRIVE customer_order, order_item, order_item_selection, order_item_modifier, ingredient, stock_movement product, menu, menu_slot, menu_slot_option, ingredient, product_ingredient
6 LIST_ORDERS_DISPLAY Preparation KITCHEN/COUNTER/DRIVE/ADMIN customer_order, order_item, order_item_selection, order_item_modifier, role_visible_source
7 DELIVER_ORDER Delivery COUNTER/DRIVE customer_order
8 CANCEL_ORDER Cancellation COUNTER/DRIVE/ADMIN customer_order, ingredient, stock_movement order_item, order_item_modifier, ingredient, product_ingredient
9 CREATE_PRODUCT Catalogue ADMIN/MANAGER product category
10 UPDATE_PRODUCT Catalogue ADMIN/MANAGER product
11 DELETE_PRODUCT Catalogue ADMIN product menu_slot_option, order_item, menu
12 CREATE_MENU Catalogue ADMIN/MANAGER menu, menu_slot, menu_slot_option product, category
13 UPDATE_MENU Catalogue ADMIN/MANAGER menu, menu_slot, menu_slot_option
14 DELETE_MENU Catalogue ADMIN menu_slot_option, menu_slot, menu order_item
15 MANAGE_CATEGORY Catalogue ADMIN/MANAGER category product, menu
16 MANAGE_INGREDIENT Catalogue ADMIN/MANAGER ingredient, product_ingredient, ingredient_allergen product, allergen
17 RESTOCK Stock MANAGER/ADMIN ingredient, stock_movement ingredient
18 INVENTORY_COUNT Stock KITCHEN/COUNTER/DRIVE/MANAGER/ADMIN ingredient, stock_movement ingredient
19 READ_STOCK Stock KITCHEN/COUNTER/DRIVE/MANAGER/ADMIN ingredient, stock_movement
20 CREATE_USER RBAC ADMIN user role
21 UPDATE_USER RBAC ADMIN user
22 DEACTIVATE_USER RBAC ADMIN user
23 MANAGE_RBAC RBAC ADMIN role_permission, role, role_visible_source role, permission
24 READ_STATS Stats MANAGER/ADMIN customer_order, order_item
25 AUTHENTICATE_USER Auth ALL BACK user user, role, role_permission
26 LOGOUT_USER Auth ALL BACK
27 ERASE_USER_PII RBAC ADMIN user, audit_log user
28 RESET_PASSWORD Auth ALL BACK user, audit_log user

Total : 28 operations (26 prod-like + ERASE_USER_PII et RESET_PASSWORD de la couche security-by-design).

Ecritures du journal d'audit (security-by-design) : les operations sensibles 7.1 (annulation), 8.2/8.3 (modification/suppression de produit), 8.6 (suppression de menu), 10.1-10.5 (utilisateur/RBAC/effacement) et 12.1 (connexion) ecrivent egalement une ligne audit_log (entite W non repetee par ligne ci-dessus pour garder le tableau lisible). Les operations de stock 9.1/9.2 enregistrent leur attribution via stock_movement.user_id. Ensemble protege par PIN selon mlt.md RG-T13.


15. Verification croisee MCT -> MCD (mantra #34)

Verification que chaque entite MCD participe a au moins une operation MCT.

Entite MCD Operations en lecture Operations en ecriture Couverture
category 1, 9, 12, 15 15 OK
product 1, 2, 3, 5, 9, 11, 12 9, 10, 11 OK
menu 1, 2, 3, 5, 12, 14 12, 13, 14 OK
menu_slot 1, 2, 5 12, 13, 14 OK
menu_slot_option 1, 2, 5, 11 12, 13, 14 OK
ingredient 1, 2, 3, 5, 8, 16, 17, 18, 19 3, 5, 8, 16, 17, 18 OK
product_ingredient 2, 3, 5, 8 16 OK
allergen 1 — (static seed) OK (*)
ingredient_allergen 1 16 OK
customer_order 6, 8, 24 3, 5, 7, 8 OK
order_item 6, 8, 14, 24 3, 5 OK
order_item_selection 6 3, 5 OK
order_item_modifier 6, 8 3, 5 OK
user 25 20, 21, 22, 25 OK
role 20, 23, 25 23 OK
role_visible_source 6 23 OK
permission 23 — (static seed) OK (*)
role_permission 25 23 OK
stock_movement 19 3, 5, 8, 17, 18 OK
audit_log (vue d'audit admin) 8, 10, 11, 14, 20, 21, 22, 23, 25, 27, 28 OK
login_throttle 25 25 OK

(*) allergen et permission sont en lecture seule au niveau MCT : leurs valeurs sont declarees dans les migrations de seed et ne sont pas modifiables via l'UI. allergen est gere indirectement via ingredient_allergen dans MANAGE_INGREDIENT.

(**) audit_log (entite 20, security-by-design) est principalement en ecriture : il est ajoute par les operations sensibles ci-dessus et lu via une vue d'audit admin (une operation de lecture dediee peut etre formalisee lorsque l'UI d'audit sera specifiee en P3).

(***) login_throttle (entite 21, security-by-design) est le verrou de throttling anti-force-brute par IP source : il est lu ET ecrit (upserte) par AUTHENTICATE_USER (25). Sa purge quotidienne des lignes obsoletes est un cron, documente dans mlt.md, hors du perimetre des operations MCT.

(****) pin_throttle (entite 22, security-by-design, RG-T22) est le verrou de throttling du PIN d'action sensible par utilisateur AGISSANT : il est lu (gate avant verification) ET ecrit (upserte sur echec, remis a zero sur succes) par les operations sensibles sous PIN (ex. UPDATE_PRODUCT prix/TVA, DELETE_PRODUCT). Sa purge quotidienne suit celle de login_throttle (cron, mlt.md), hors du perimetre des operations MCT.

Conclusion : 22/22 entites couvertes (19 prod-like + audit_log + login_throttle + pin_throttle). Coherence MCT <-> MCD validee.