# Wakdo — Project Context **Source de verite du projet.** Ce document est injecte comme contexte dans tous les agents BYAN utilises pour ce projet (expert-merise-agile, architect, dev, ux-designer, tea, quinn, etc.). Il doit etre mis a jour des qu'une decision structurante change. --- ## 1. Identite projet | Champ | Valeur | |---|---| | Nom du projet | **Wakdo** — borne de commande fictive (pastiche McDonald's) | | Auteur | Corentin | | Cadre | Epreuve RNCP 37805 — Titre Developpeur Web, B2, option **DevOps** | | Centre | Acadenice | | Contexte pro | Alternance en tant qu'admin sys + etudiant B2 DevOps | | Deadline soutenance | **Septembre 2026** | | Budget heures | 10-15 h/semaine — cible ~240 h effectives | | Mode de travail | Solo | | Date de creation du doc | 2026-04-23 | --- ## 2. Contexte metier Wakdo est une **borne de commande tactile** pour un restaurant de restauration rapide. L'utilisateur final (client) compose sa commande sur ecran tactile, valide, recupere un numero, puis retire son produit au comptoir. ### Acteurs | Acteur | Role RBAC | Interface | |---|---|---| | **Client** | (non authentifie) | Borne tactile (Bloc 1, canal `kiosk`) | | **Counter** | `counter` | Back-office : saisit les commandes au **comptoir**, les remet au client, peut annuler | | **Drive** | `drive` | Back-office : saisit les commandes au **drive** (intercom + casque), les remet, peut annuler | | **Kitchen** | `kitchen` | Back-office : voit la file des commandes `paid` triees par `paid_at` croissant, en **lecture seule** (KDS visuel, aucune transition) | | **Manager** | `manager` | Back-office : catalogue (create/update), stock/reappro, statistiques | | **Administration** | `admin` | Back-office : catalogue complet (+ suppressions), gestion utilisateurs, roles et permissions (RBAC), stats | > Modele v0.2 : 5 roles RBAC (`admin`, `manager`, `kitchen`, `counter`, `drive`) > + Customer non authentifie. RBAC permission-driven (le code teste une > permission, pas un nom de role) ; catalogue de 23 permissions fige au seed. > Voir `docs/merise/dictionary.md` 3.15-3.18 et `docs/uml/use-cases.md`. ### Processus metier cle ``` Client Borne (Bloc 1) API (Bloc 2) BDD │ │ │ │ │─compose panier──────▶│ │ │ │ │─GET menus,produits───▶│───SELECT──────────▶│ │ │◀──────────JSON────────│◀───────────────────│ │─valide────────────────│ │ │ │─saisit numero─────────│ │ │ │ │─POST /api/orders─────▶│───INSERT──────────▶│ │ │◀──────────201─────────│ │ │─recupere au comptoir │ │ │ Kitchen voit la file des commandes paid (lecture seule, KDS) Counter / Drive remettent au client → declarent "livree" (geste unique paid -> delivered) ``` ### Regles metier (MCT - a modeliser en Merise) - Un **menu** = burger fixe + slots a choix (boisson, accompagnement, sauce). Modele relationnel `menu_slot` + `menu_slot_option` (voir `dictionary.md` 3.4-3.5) - Format **Normal / Maxi** au niveau du menu (deux prix : `price_normal_cents`, `price_maxi_cents`) ; le Maxi agrandit accompagnement + boisson uniquement - **Personnalisation des ingredients** (retirer = gratuit, ajouter = supplement) sur les sandwichs composes, via le configurateur (`ingredient`, `product_ingredient`, `order_item_modifier`) - **TVA portee par le produit** (`vat_rate` : 10% defaut, 5,5% contenant conservable), calculee ligne par ligne et snapshotee sur `order_item` (fact-check BOFiP, voir `dictionary.md` note 9) - Une **commande** a un **numero** saisi par le client, prefixe par canal `K`/`C`/`D` (remplace le paiement dans le cadre de l'exam) - Statuts commande (machine a **4 etats**) : `pending_payment` -> `paid` -> `delivered` (+ `cancelled`). La transition `pending_payment -> paid` est **atomique** a la creation (saisie du numero = substitut de paiement). `cancelled` est atteignable depuis `pending_payment` et `paid` (pas depuis `delivered`). Plus de `preparing` / `ready` : la cuisine est en lecture seule, la remise est un geste unique - **Source commande** (trace sur chaque commande) : `kiosk` (borne autonome) | `counter` (comptoir) | `drive` (drive-thru) - Le canal de prepa (`kitchen`/`counter`/`drive`) voit la file des commandes `paid` triee par `paid_at` **croissant**, filtree par `role_visible_source` (kitchen voit tout ; counter voit kiosk+counter ; drive voit drive) - **Horaires service** : 10h00 → 01h00 du matin (service continu 15h, pas de fermeture intermediaire) - **Pas de notion de "session de service" a modeliser** : les equipiers se relaient, chacun se connecte a sa prise de poste et se deconnecte a la fin. Pas de "shift" a tracer dans la BDD (hors scope RNCP) - **Fenetre de maintenance systeme** : 01h30 → 09h30 (crons lourds, backups, agregations) — evite toute interference avec le service actif - **Notion de "jour de service"** (important pour les stats et l'agregation) : - Un jour de service J = toutes les commandes creees entre **J 10h00** et **J+1 01h00** - Exemple : la soiree du 22/04 = commandes `22/04 10:00 → 23/04 01:00`, regroupees sous `service_day = 2026-04-22` - Une commande creee a 23h55 le 22/04 et livree a 00h30 le 23/04 appartient au meme jour de service (22/04) - **Implementation** : colonne `service_day` calculee sur la table `orders`, ou vue SQL : ```sql service_day = CASE WHEN HOUR(created_at) < 10 THEN DATE(created_at - INTERVAL 1 DAY) ELSE DATE(created_at) END ``` - Les requetes de stats (CA jour, produits top, etc.) utilisent `service_day` et non `DATE(created_at)` brut --- ## 3. Contexte academique — RNCP 37805 **Referentiel** : [certifpro.francecompetences.fr](https://certifpro.francecompetences.fr/api/fiches/refActivity/24345/472313) ### Blocs couverts | Bloc | Nom | Statut | Activites | |---|---|---|---| | **Bloc 1** | Developpement Front-End | Tronc commun obligatoire | A1 (integration), A2 (fonctionnalites JS) | | **Bloc 2** | Developpement Back-End | Tronc commun obligatoire | A3 (data/BDD), A4 (back-end) | | **Bloc 5** | DevOps (option 3) | Option choisie | A7 (automatisation + conteneurisation + CI/CD) | ### Validation - **50 % minimum par bloc** pour le valider - **50 % moyenne globale** pour valider le titre - **Stage** = 20 % de la note finale (couvert par l'alternance) - **Controle continu** 30 % + **Examens jurys** 50 % --- ## 4. Strategie de projet ### Strategie B — unifiee **Un seul codebase, deux FQDN d'exposition publique.** Le front Bloc 1 et le back Bloc 2 coexistent dans la meme arborescence. Une bascule mode JSON-seuls (Bloc 1 isole) vs mode API-connecte doit rester possible via configuration. **Pourquoi pas strategie A (deux rendus isoles)** : le Bloc 5 DevOps impose une conteneurisation **unique** qui lance la stack complete avec `docker compose up` en une commande (Cr 7.c.4). Deux codebases isolees seraient incoherentes avec cette exigence. ### Compatibilite evaluation par bloc - **Jury Bloc 1** : voit le front seul ; le front consomme les donnees via `fetch` sur l'API (`/api/*`). Le fallback JSON statique initialement envisage a ete retire (la borne est branchee directement sur l'API DB-backed). - **Jury Bloc 2** : voit le back-office + teste l'API via curl/Postman de maniere autonome, sans dependre du front. - **Jury Bloc 5** : lance `docker compose up` ou `docker compose up`, verifie la CI/CD, les crons, l'archi, les scripts. --- ## 5. Architecture ### 2 FQDN exposes | FQDN | Role | Bloc | Auth | |---|---|---|---| | `corentin-wakdo.stark.a3n.fr` | Borne client (kiosk tactile) | Bloc 1 | Public | | `corentin-wakdo-admin.stark.a3n.fr` | Back-office + API REST (sous `/api/*`) | Bloc 2 | Sessions (back-office) + tokens (API ecriture) | **CORS** : la borne (`corentin-wakdo.stark.a3n.fr`) consomme l'API (`corentin-wakdo-admin.stark.a3n.fr/api/*`). Headers CORS explicites avec origine precise (pas de wildcard `*`), argumentable comme durcissement securite face au jury. ### Services Docker ``` ┌─────────────────────────────────────────────────────────────────┐ │ Traefik (existant) │ │ (admin_proxy network) │ └──────────┬────────────────────────────┬─────────────────────────┘ │ │ corentin-wakdo.stark.a3n.fr corentin-wakdo-admin.stark.a3n.fr │ │ ▼ ▼ ┌──────────────────────────────────────────┐ │ wakdo-web (Apache Alpine) │ │ Front controller + reverse vers FPM │ └──────────┬───────────────────────────────┘ │ FastCGI :9000 ▼ ┌──────────────────────────────────────────┐ │ wakdo-app (PHP 8.3-fpm Alpine) │ │ POO MVC — borne.php, admin.php, api.php │ └──────────┬───────────────────────────────┘ │ PDO MySQL ▼ ┌──────────────────────────────────────────┐ │ wakdo-db (MariaDB 11) │ │ volume persistant │ └──────────────────────────────────────────┘ ┌──────────────────────────────────────────┐ │ wakdo-cron (Alpine + PHP CLI) │ │ crond — backup BDD, purge sessions, ... │ └──────────────────────────────────────────┘ ``` Reseaux : - `admin_proxy` (external) — expose `wakdo-web` a Traefik - `wakdo_internal` (bridge, interne) — isole `wakdo-app`, `wakdo-db`, `wakdo-cron` --- ## 6. Stack technique lockee | Couche | Choix | Version | Raison | |---|---|---|---| | Front langages | HTML5 + CSS3 + JavaScript | Standards 2024+ | Exigence Bloc 1 (vanilla) | | Front framework | **Aucun** | — | Sujet Bloc 1 impose | | Front dep JS | **Aucune** | — | Exigence explicite | | Back langage | PHP | **8.3** LTS | Moderne, support jusqu'a 2027 | | Back framework | **Aucun** | — | Sujet Bloc 2 "from scratch" | | Autoloader | **Manuel** (spl_autoload_register, PSR-4) | — | Defend "from scratch", Cr 4.c.3 compatible | | Tests | **PHPUnit** via `.phar` autonome | 11.x | Cr 4.g.2 sans Composer | | Composer | **Non utilise** | — | Colle au "from scratch" | | BDD | MariaDB | **11** LTS | Compatible MySQL, LTS 2028 | | Driver BDD | PDO (prepared statements uniquement) | natif PHP | Cr 4.e.1 anti-injection | | Serveur web | Apache httpd | Alpine latest | Reverse proxy vers PHP-FPM | | Serveur app | PHP-FPM | 8.3-fpm-alpine | Contenairisation propre | | Reverse proxy | Traefik (deja en place) | existant | `admin_proxy` network | | TLS | Let's Encrypt via Traefik | auto | `acme.json` existant | | Conteneurisation | Docker + docker compose | v2 | Cr 7.c | | Orchestration locale | docker compose (service wakdo-migrate) | — | Cr 7.b (script) + Cr 7.c.4 (une commande) | | CI/CD | Forgejo Actions (act_runner auto-heberge) | — | Cr 7.d | | Versioning | Git + Forgejo auto-heberge (push-mirror GitHub) | — | Cr 4.f (collaboration) | | Hooks Git | pre-commit (refus main/dev + php -l) + commit-msg (format Conventional Commits) | versionnes dans `.githooks/`, actives via `scripts/install-hooks.sh` | Conventional Commits | --- ## 7. Scope fonctionnel ### Bloc 1 — Borne client (Front) **IN scope :** - Affichage dynamique menus + produits (charges par `fetch` depuis l'API `/api/*`) - Composition panier : produits unitaires OU menus (burger + accompagnement + boisson + sauce) - Options taille (normale / grande, +0,50 € sur grande) pour accompagnements et boissons - Options de personnalisation simples (ex : sans oignon, avec fromage) - Recapitulatif panier (ajout, modification quantite, suppression) - Validation commande + saisie numero (remplace paiement) - Envoi POST JSON de la commande vers l'API - Ecran de confirmation avec numero - Responsive cible 1920x1080 portrait (borne) **+ adaptatif** autres resolutions - **Accessibilite RGAA** : police OpenDys pour dyslexiques, navigation clavier, contrastes, alts, pas d'info via couleur seule - **SEO / semantique** : balises HTML5 (`article`, `aside`, `nav`), schema.org, meta tags uniques, canonical, favicon **OUT scope :** - Paiement reel (remplace par numero de commande) - Authentification client (pas de compte fidelite) - Multi-langue (FR uniquement) - Offline mode ### Bloc 2 — Back-office + API (Back) **IN scope — Back-office :** - Authentification sessions securisees (hash bcrypt/argon2, protection CSRF, fixation session) — duree de session adaptee a un poste complet d'equipier (idle timeout 4h, absolute timeout 10h) - 5 roles RBAC seed : `admin`, `manager`, `kitchen`, `counter`, `drive` (RBAC permission-driven, 23 permissions figees au seed ; roles personnalises possibles) - **Admin** : CRUD complet catalogue (+ suppressions), gestion utilisateurs, roles et permissions (RBAC), stats - **Manager** : catalogue (create/update), stock (reappro + inventaire), statistiques ; utilisateurs en **lecture seule** (`user.read`, pas de creation/modification/desactivation), pas d'acces RBAC - **Kitchen** : file des commandes `paid` triee par `paid_at` croissant, en **lecture seule** (KDS visuel) ; inventaire - **Counter** / **Drive** : saisir une commande (comptoir / drive-thru via casque/intercom), bouton "declarer livree" (geste unique `paid -> delivered`), annuler ; `source` auto-tague depuis `role.order_source` ; inventaire - Upload images produits : **non implemente** ; prevu (validation type MIME + taille + stockage dans volume `wakdo_uploads`) - Historique commandes par statut - Stats de base (commandes du jour, CA jour, produits top) **IN scope — API REST :** - `GET /api/categories` — liste categories produits - `GET /api/products` — liste produits (filtrable par categorie) - `GET /api/menus` — liste menus avec compositions et options - `POST /api/orders` — creer une commande (body JSON, retour `{id, number, status}`) - `GET /api/orders/{number}` — recuperer statut commande - Headers CORS explicites pour l'origine borne - Reponses JSON standardisees : `{data: ..., error: null}` ou `{data: null, error: {code, message}}` **IN scope — Transverse :** - Architecture **MVC** stricte (Models / Views / Controllers) - **Heritage** entre classes (ex : `BaseController` -> `AdminController` -> `ProductController`) - **Namespaces** + autoloader PSR-4 manuel - **Anti-injection** : PDO prepared statements exclusivement - **RGPD** : hash mdp, droit consultation/modif/suppr user, info stockage/utilisation donnees **OUT scope :** - Paiement reel, systeme comptable - Multi-restaurants / multi-bornes - Fidelite client - Rapports avances / exports CSV ### Bloc 5 — DevOps **IN scope :** - Dockerfile custom PHP-FPM avec extensions - `docker-compose.yml` orchestrant 5 services : 4 longs (web, app, db, cron) + 1 one-shot (`wakdo-migrate`) - `docker compose up` lance toute la stack (service one-shot `wakdo-migrate` : migrations + seed idempotents) en une commande (Cr 7.c.4) - Scripts Bash d'automatisation (backup, restore, deploy, migrate) - **Cron tab** avec 3 jobs actifs planifies dans la fenetre de maintenance (01h30-09h30) : - `0 3 * * *` — backup BDD quotidien a 03h00 (entre fin service 01h et ouverture 10h) - `15 4 * * *` — purge du journal d'audit au-dela de la fenetre de retention (~12 mois) - `45 4 * * *` — purge des compteurs de throttle expires - Differes (templates commentes dans `docker/cron/crontab`, a activer plus tard) : purge des sessions expirees, agregation des stats sur le jour de service - **CI Forgejo Actions** (act_runner auto-heberge) : lint PHP + PHPStan + PHPUnit + secret-scan (gitleaks) + js-tests sur PR -> dev - **CD : deploiement scripte a declenchement humain** (`scripts/deploy.sh` : recupere `main` depuis Forgejo puis `docker compose build --pull && up -d` -- les images wakdo sont buildees localement depuis les Dockerfiles, pas de registre). Choix solo dev sur un environnement de prod unique. L'automatisation visee est **pull-based** : un job cron cote hote qui detecte un nouveau `main` et lance `deploy.sh` (a armer ensuite, reutilise le meme script) - `.env.example` documente (parametres securite : argon2id, lockout, seuils throttle, retention RGPD), secrets hors du repo - `php.ini` durci (expose_php off, session cookies httponly/secure/samesite, upload limite) - Healthcheck Traefik + readiness probes - Logs centralises (stdout des conteneurs) - Documentation deploiement + architecture (schemas dans `docs/`) **OUT scope :** - Kubernetes / Swarm - Monitoring complet type Prometheus/Grafana (trop chronophage) - Multi-environnements (pre-prod, staging) - Blue-green deployment --- ## 8. Criteres RNCP — mapping feature ### Bloc 1 | Critere | Libelle court | Feature Wakdo couvrant | |---|---|---| | Cr 1.a.1 | Integration conforme maquette | Integration HTML/CSS fidele au prototype | | Cr 1.a.2 | Normes W3C + accessibilite | Validator W3C vert + RGAA | | Cr 1.a.3 | Tests validator passent | Test en soutenance via W3C validator | | Cr 1.a.4 | Code commente, indente | Conventions de code appliquees partout | | Cr 1.a.5 | Balises semantiques | `
`, `