Protocole QA OpsFlux v3 — 200 étapes opérationnelles, conditions réelles#
Évolution de v2 (
QA-PROTOCOL-200-v2.md, 255 étapes méta). v3 est exécutable : chaque étape porte URL/payload/attendu concrets, tous les champs sont remplis sans exception, et le moteur d'exécution est le Chrome MCP (mcp__Claude_in_Chrome__*) qui retourne du DOM-aware compact (~1-3 k tokens / page) au lieu de screenshots.Couverture : Tiers, Projets, Planner, PaxLog, PackLog, TravelWiz + 4 phases transverses.
Statut par étape (à reporter dans
QA-LOG.md) :✅ PASS·❌ FAIL: <tag> <description>·🔧 FIXED:<sha>·⏭️ SKIP:<raison>
Préconditions#
| Item | Valeur |
|---|---|
| Frontend | https://app.opsflux.io |
| Backend | https://api.opsflux.io |
| Admin | admin@opsflux.io / RldgAHGJqlrq6TRjsZq3is |
| Branche | main (auto-deploy Dokploy via webhook GitHub) |
| Compose ID Dokploy | g4-VkoUKMCuO9i4Y3OgbG |
| Browser | Chrome MCP (mcp__Claude_in_Chrome__*) — évite computer-use coûteux |
| Test user 2 | À créer en étape 1.4 (qa.viewer@opsflux.io, role viewer, permissions read-only) |
| Test user 3 | À créer en étape 1.5 (qa.manager@opsflux.io, role manager PaxLog uniquement) |
| Données graines | Une Entity = "Cameroun", une Entity = "Atlas Operations", 25 tiers existants |
9 dimensions vérifiées à CHAQUE étape (rappel v2)#
🌐 i18n 📱 resp 🔐 séc 🔑 perm 🧭 ergo 🖼️ UI 📊 data 🏃 perf 💬 cnsl
14 tags bug (rappel v2 + nouveaux v3)#
[i18n-miss] [hardcode] [ui-overflow] [ui-stack] [ui-inconsistent] [broken]
[perm-leak] [perm-block] [no-feedback] [xss-risk] [todo] [shortcut-miss]
[a11y] [ergo-bad]
+ v3 : [dead-code] [unused-route] [orphan-i18n-key] [broken-ws] [404-route] [stale-doc]
Phase 0 — Recon code statique (étapes 1-10)#
Avant d'ouvrir le browser, on grep le repo pour les dette technique évidente.
| # | Action | Attendu (clean) | Bug si |
|---|---|---|---|
| 1 | Grep "TODO|FIXME|HACK|XXX" dans app/, apps/main/src/ |
< 30 hits totaux | Liste à attaquer en [todo] |
| 2 | Grep console\.(log\|warn\|error) dans apps/main/src/ (hors vendor/, hors *.test.*) |
< 10 hits | [dead-code] à nettoyer |
| 3 | Grep any\b dans apps/main/src/**/*.ts(x) |
< 50 hits | Typage faible |
| 4 | Grep dangerouslySetInnerHTML |
0 sauf rendu Markdown identifié | [xss-risk] |
| 5 | Grep localStorage\.\|sessionStorage\. pour mots-clés token\|password\|secret |
0 | [xss-risk] storage non-httpOnly |
| 6 | Glob apps/main/src/**/*.tsx puis grep clés i18n t\("([^"]+)" → comparer à apps/main/src/locales/fr/*.json |
toutes clés présentes | [i18n-miss] |
| 7 | Lister routes définies (createBrowserRouter) vs routes pointées par sidebar (sidebar-config.ts) |
0 différence | [404-route] ou [unused-route] |
| 8 | Grep pass\|password dans app/api/**/*.py pour vérifier qu'aucun mot de passe n'est loggué |
0 hit suspect | [xss-risk] log fuite |
| 9 | Vérifier que tous les modèles SQLAlchemy ayant archived héritent bien de SoftDeleteMixin |
100% | [broken] soft-delete cassé |
| 10 | Lister migrations alembic alembic history --verbose → 0 branche orpheline, head unique |
1 head | [broken] chain cassée |
Phase 1 — Auth, permissions, MFA, délégations (étapes 11-25)#
| # | Action | Attendu | Tags bug à surveiller |
|---|---|---|---|
| 11 | GET /api/v1/auth/login/config (sans token) |
200 + mfa_trust_device_enabled, mfa_trust_device_max_days |
[perm-leak] si secrets présents |
| 12 | POST /api/v1/auth/login payload {email:"admin@opsflux.io", password:"...", remember_device:true, remember_days:30} |
200, set-cookie mfa_trust=... HttpOnly Secure SameSite |
[xss-risk] cookies sans flags |
| 13 | GET /api/v1/auth/me |
200, contient permissions, roles, default_entity_id |
[perm-leak] champ password_hash exposé |
| 14 | Création user qa.viewer@opsflux.io via UI /admin/users/new (tous champs : email, first_name, last_name, civility=Mr, language=fr, timezone=Africa/Douala, role=viewer, default_entity=Cameroun, password gen) |
201 + email d'invite envoyé | [i18n-miss] [no-feedback] |
| 15 | Idem pour qa.manager@opsflux.io (role=paxlog_manager) |
201 | [perm-block] si rôle absent du picker |
| 16 | Login avec qa.viewer → vérifier sidebar : modules en lecture seule, boutons "Créer" cachés |
UI dégradée correctement | [perm-leak] boutons visibles |
| 17 | Login qa.viewer → tenter POST /api/v1/tiers (devrait être 403) |
403 propre, pas 500, body {detail: "..."} traduit |
[perm-block] ou trace stack exposée |
| 18 | Login qa.viewer → tenter GET /api/v1/users/<id>/permissions d'un autre user |
403 | [perm-leak] IDOR |
| 19 | Délégation : admin crée délégation à qa.viewer portée=permissions=["paxlog:create"], dates 2026-05-15 → 2026-06-15, message="Test QA v3" |
201 + email reçu + PDF ISO généré (attachment) | [broken] ISO PDF si manquant |
| 20 | Délégation reçue : qa.viewer se reconnecte → vérifie en bas profil "Délégations actives" → 1 entrée |
UI affiche scope + dates + bouton "Voir certificat" | [i18n-miss] [hardcode] |
| 21 | qa.viewer peut désormais POST /api/v1/tiers (via délégation) ? Doit dépendre du scope (ici scope=paxlog donc non) |
403 (scope ≠ tiers) | [perm-leak] si autorisé à tort |
| 22 | Révocation délégation par admin → soft-delete (active=false) + 2e PDF "REVOKED" + email à délégataire | OK + attachment conservé pour ISO | [broken] si hard-delete |
| 23 | MFA : admin active MFA TOTP sur son compte (UI /me/security) → scan QR → confirme code 6 chiffres |
200 + 10 codes de récup affichés une seule fois | [broken] [i18n-miss] |
| 24 | Logout admin → relogin → demande code TOTP. Saisir code valide | 200 + cookie mfa_trust créé (vu remember_device=true) |
[broken] |
| 25 | Logout + relogin dans les 30j → MFA SKIPPED (cookie trust valide) | login direct, pas de demande code | [broken] cookie trust ignoré |
Phase 2 — Tiers (étapes 26-55, 30 étapes)#
Module Tiers = entreprises + contacts + identifiants légaux + adresses + tags + notes + imputations. Champs créés exhaustivement. Voir
app/models/common.py:684 Tierpour la liste complète.
Création Entité Tier (1 entité = 50+ champs à remplir)#
Étape 26 — Création Tier "complet" via UI /tiers/new
Tous les champs (sans aucune exception) :
code : QA-DEMO-001
name : Société de Test QA SARL
alias : QATestCorp
trade_name : QATest Trading
logo_url : https://placehold.co/200x80?text=QATest (ou upload local)
type : client
website : https://qatest.example.com
phone (legacy) : +237 691 23 45 67
fax : +237 233 50 11 22
email (legacy) : contact@qatest.example.com
legal_form : SARL
registration_n : RC-DLA-2025-B-0042
tax_id : NIU-M021900008765K
vat_number : TVA-CM-998877
capital : 50000000
currency : XAF
fiscal_year_start: 1
industry : Oil & Gas Services
founded_date : 2018-03-15
payment_terms : 30 jours fin de mois
incoterm : DDP
incoterm_city : Douala
description : Entreprise test multi-lignes\n\nAvec un saut.
address_line1 : 1234 Boulevard de la Liberté
address_line2 : Immeuble Atlantique, 4ème étage
city : Douala
state : Littoral
zip_code : 4105
country : Cameroun
timezone : Africa/Douala
language : fr
active : true
metadata_ : {"sap_code":"V-100542","kyc_status":"verified"}
social_networks : {"linkedin":"https://linkedin.com/company/qatest","twitter":"@qatest"}
opening_hours : {"mon":"08:00-17:00","tue":"08:00-17:00","wed":"08:00-17:00","thu":"08:00-17:00","fri":"08:00-13:00","sat":null,"sun":null}
notes : Note libre interne (rich-text markdown)
is_blocked : false
scope : local
| # | Action | Attendu | Tags |
|---|---|---|---|
| 27 | Submit du formulaire | 201, redirect /tiers/<id>, toast "Entreprise créée" en FR |
[i18n-miss] [no-feedback] [hardcode] |
| 28 | Recharger la page → tous les champs persistent | OK F5 | [broken] [data] |
| 29 | Switch FR ↔ EN (sélecteur header) → labels et messages traduits | 0 clé brute | [i18n-miss] |
| 30 | Vérifier polymorphic Address : ouvrir onglet "Adresses" → ajouter 2 adresses (siège + facturation) avec tous les champs | 2 adresses listées, primary toggle OK | [broken] [hardcode] (cf bug #38 fixé) |
| 31 | Onglet "Téléphones" → ajouter 3 phones (mobile, fixe, fax) avec libellé + pays + extension | 3 phones, primary unique | [broken] [i18n-miss] |
| 32 | Onglet "Emails" → ajouter 2 emails (commercial + facturation) | 2 emails, primary unique | [broken] |
| 33 | Onglet "Identifiants légaux" → ajouter SIRET, RCCM, NIU, TVA (4 entrées avec country + valid_from + valid_to + verified_by) | 4 IDs persistés | [broken] [i18n-miss] |
| 34 | Onglet "Tags" → ajouter 5 tags (vip, kyc-ok, priorité-haute, 2025, oil-services) avec couleurs |
5 tags affichés sous nom | [broken] |
| 35 | Onglet "Notes" → créer 3 notes (publique + privée + tâche), avec markdown enrichi | 3 notes, filtrage par type OK | [broken] [i18n-miss] |
| 36 | Onglet "Imputations" → lier le Tier à un cost_center "OPS-2026" + un imputation_reference | OK | [broken] |
| 37 | Onglet "Compliance" → ajouter 3 records (assurance RC pro, attestation fiscale, attestation CNPS) avec expiry_date, attachment_id, status=valid |
3 records, badges expiry vert/orange/rouge | [broken] [ui-stack] |
| 38 | Onglet "Pièces jointes" → upload 3 fichiers (PDF + image + xlsx) ≤ 10 Mo | 3 attachments, vignettes | [broken] [no-feedback] |
| 39 | Onglet "External refs" → ajouter ref SAP V-100542 + ref Salesforce 001AB000001ZxYzQAU |
2 refs OK | [broken] |
| 40 | Création Contact dans ce Tier (/tiers/<id>/contacts/new) : tous champs (civility, first_name, last_name, position, department, job_position_id, is_primary, active, language, timezone, …) |
201 contact | [broken] [i18n-miss] |
| 41 | Sur le contact créé, ajouter 2 phones, 2 emails, 1 adresse (polymorphic owner_type=tier_contact) |
OK | [broken] [ui-inconsistent] (vs Tier) |
| 42 | Promouvoir contact → PaxProfile (bouton Promouvoir en PAX) |
Redirect /paxlog/profiles/ |
[broken] [ergo-bad] |
| 43 | Liste tiers /tiers : 26 entreprises (25 graines + nouveau), tri par nom desc |
Tri OK, count OK | [broken] [ui-stack] |
| 44 | Recherche dans la barre /tiers : taper "QATest" → résultat unique |
< 1s | [perf] [broken] |
| 45 | Filtres avancés : type=client + active=true + country=Cameroun → comptage filtré | OK | [broken] |
| 46 | Export CSV de la liste filtrée → fichier téléchargé avec entête + bonnes colonnes (16 cols min) | OK | [broken] [hardcode] headers |
| 47 | Export PDF rapport entité → 1 page, logo entité, tous champs principaux | OK ou 501 si pas implémenté | [broken] [404-route] |
| 48 | Bloquer le Tier (is_blocked=true) avec raison + date |
UI grise tout le tier, badge "BLOQUÉ" rouge | [broken] [i18n-miss] |
| 49 | Tenter créer un Project lié à ce Tier bloqué | 422 ou warning bloquant | [broken] [perm-leak] si autorisé |
| 50 | Débloquer le Tier | OK, badge disparaît | [broken] |
| 51 | Soft-delete (archiver) le Tier | Disparait des listes par défaut, visible avec filtre archivés=true, archived=true + deleted_at NOT NULL en BDD |
[broken] [data] |
| 52 | Restaurer le Tier archivé | OK | [broken] |
| 53 | Hard-delete (admin only) → 204 et BDD vide pour cet ID | OK + 0 row | [broken] [perm-leak] |
| 54 | Tenter SQL injection dans champ search : ' OR 1=1-- puis <script>alert(1)</script> |
Aucune injection, pas d'XSS, pas d'erreur 500 | [xss-risk] |
| 55 | Vérifier console DevTools sur le parcours complet : 0 erreur JS, 0 warning React clé/controlled | clean | [broken] |
Phase 3 — Projets (étapes 56-85, 30 étapes)#
Création Projet "complet" /projets/new#
Étape 56 — Champs Project (cf modèle app/models/project.py) :
code : PRJ-QA-V3-001
name : Refonte stockage GPL Bonaberi
client_tier_id : QA-DEMO-001 (créé phase 2)
project_manager : qa.manager@opsflux.io
sponsor : admin@opsflux.io
status : in_progress
priority : high
weather : sunny
budget : 12500000.00
currency : XAF
start_date : 2026-06-01
end_date : 2026-12-15
actual_start : 2026-06-03
actual_end : null
progress_pct : 12.5
description : Description riche markdown\n\n## Phase 1\n- ...
location_field : Field-Bonaberi
location_site : Site-BBR-01
location_install : INST-GPL-A
tags : ["gpl","stockage","bonaberi"]
custom_fields : {"poste_budget":"CAPEX-2026-Q3","wbs":"100.42.7"}
| # | Action | Attendu | Tags |
|---|---|---|---|
| 57 | Submit form complet | 201, redirect /projets/<id>, toast "Projet créé" |
[broken] [i18n-miss] |
| 58 | Onglet "Tâches" → créer 5 tâches : 1 milestone (is_milestone=true), 4 normales, avec dépendances FS |
OK + diagramme Gantt mis à jour | [broken] (cf bug #37 fixé) |
| 59 | Tâches : assigner à qa.manager, due_date, estimated_hours, real_hours, status (todo/in_progress/done) |
OK | [broken] [ui-inconsistent] |
| 60 | Vue Gantt → drag d'une tâche change ses dates | OK + sauvegarde immédiate | [broken] [no-feedback] |
| 61 | Vue Kanban → drag tâche entre colonnes change status |
OK + audit trail | [broken] |
| 62 | Onglet "Risques" → ajouter 3 risques (impact, probabilité, owner, mitigation) | OK | [broken] [404-route] si module pas implémenté |
| 63 | Onglet "Documents" → upload PDF cahier des charges + 2 plans DWG | OK ≤ 25 Mo | [broken] |
| 64 | Onglet "Météo" → date picker + visualisation prévisions site lié | OK ou indique "non lié" | [broken] [404-route] |
| 65 | Onglet "Activités liées" → vérifier que les activités Planner liées (créées Phase 4) apparaissent | OK | [broken] ou vide attendu |
| 66 | Onglet "Imputations" → lier project à 2 cost_centers avec % de répartition (60/40) | OK + somme = 100% | [broken] [broken] validation |
| 67 | Onglet "MOC" → créer un MOC lié au project (tous champs MOC : type, motif, impact_safety, impact_env, impact_prod, signataires) | OK | [broken] [404-route] si MOC pas branché ici |
| 68 | Onglet "Activity feed" → créer un événement manuel "Réunion de cadrage", lister les events | OK ordre desc | [broken] |
| 69 | Calcul CPM (chemin critique) → GET /api/v1/projects/<id>/cpm |
200 avec liste tâches critiques + dates au plus tôt/tard | [broken] [404-route] |
| 70 | Liste /projets?tab=projets → pagination, tri Code, recherche, filtre statut+priorité+manager |
OK | [broken] [perf] |
| 71 | Sync Gouti (4 entrées attendues) → bouton "Synchroniser" → toast et nouvelles tâches importées | OK ou explicite si pas configuré | [broken] [no-feedback] |
| 72 | Dashboard /projets (TdB) → 5 widgets : KPIs (5 cards), table Projets actifs, donut Météo, table Échéances 14j, top 5 projets |
tous remplis (post fix widget) | [broken] widget 0 |
| 73 | Click sur une row de la table "Projets actifs" → navigation vers le projet | OK | [broken] [ergo-bad] |
| 74 | Modifier le projet : tous champs editables, optimistic UI | OK + rollback si erreur | [broken] |
| 75 | Permissions : qa.viewer peut voir mais pas modifier (boutons cachés) |
OK | [perm-leak] |
| 76 | Permissions : qa.manager (PaxLog only) ne voit pas le module Projets dans sidebar |
OK 403 sur direct URL | [perm-leak] |
| 77 | Tenter changer le code projet (souvent immutable) → 422 si bloqué | OK | [broken] si autorisé |
| 78 | Archiver projet → onglets/edit grisés, banner "ARCHIVÉ" | OK | [broken] [i18n-miss] |
| 79 | Restaurer projet | OK | [broken] |
| 80 | Hard-delete (admin) → 204, cascade vérifiée (tâches, MOC, attachments) | 0 row orpheline | [broken] [data] |
| 81 | Création projet via template (clone) → POST /api/v1/projects/from-template/<tpl_id> avec name + start_date |
201, structure dupliquée (tâches, jalons, risques modèles) | [broken] [404-route] |
| 82 | Liste /admin/project-templates → 1 template min, CRUD complet |
OK | [broken] |
| 83 | Champ progress_pct : tester valeur > 100 ou < 0 |
422 validation côté API et frontend | [broken] [xss-risk] |
| 84 | Vue rapport PDF projet → 2 pages, logo entité, KPIs, liste tâches | OK ou 501 explicite | [broken] |
| 85 | DevTools console clean sur tout le parcours Projets | 0 erreur | [broken] |
Phase 4 — Planner (étapes 86-110, 25 étapes)#
Création Activité Planner#
Étape 86 — Champs Activity (app/models/planner.py:Activity) :
code : ACT-QA-V3-001
title : Maintenance pompe alimentation Slug Catcher
type : maintenance (ou : meeting / inspection / training / permanent_ops / mob_demob)
status : planned (planned / in_progress / done / cancelled / postponed)
priority : high
start_at : 2026-06-10T08:00:00Z
end_at : 2026-06-10T16:00:00Z
all_day : false
location_field : Field-Bonaberi
location_site : Site-BBR-01
location_install : INST-COMP-A
project_id : PRJ-QA-V3-001 (créé phase 3)
moc_id : null (ou créer)
owner_id : qa.manager@opsflux.io
participants : [admin, qa.viewer]
pax_count : 5
shift : day
recurrence_rule : FREQ=WEEKLY;BYDAY=MO,WE,FR;COUNT=12
description : Description rich-markdown
risk_level : medium
permit_required : true
permit_number : PTW-2026-04-0042
tags : ["maintenance", "pompe", "critique"]
custom_fields : {"sap_wo":"WO-100542","ph_check":true}
| # | Action | Attendu | Tags |
|---|---|---|---|
| 87 | Submit | 201, calendar updated, recurrence génère 12 occurrences | [broken] [i18n-miss] |
| 88 | Vue Calendrier mensuelle → activité visible aux 12 dates | OK + couleur par type | [broken] [ui-inconsistent] |
| 89 | Vue Calendrier semaine → drag activité → change start_at/end_at | OK + propagation aux occurrences | [broken] [no-feedback] |
| 90 | Vue Liste filtrée par type+status+priority+owner | tri OK, < 1s | [perf] [broken] |
| 91 | Vue Heatmap charge par site (5×7 jours) | densité visible | [broken] [ui-stack] |
| 92 | Création conflit (2 activités même PAX overlap) → POST /api/v1/planner/check-conflicts |
retourne conflits | [broken] |
| 93 | Onglet "Conflits" → résoudre conflit (status resolved avec commentaire) |
OK + timeline trace | [broken] [i18n-miss] |
| 94 | Activité avec permit_required=true → bloque transition in_progress si pas de permis valide attaché |
OK warning UI | [broken] [no-feedback] |
| 95 | Modifier le permit_number → audit trail | OK | [broken] |
| 96 | Cancel activité → status=cancelled + raison obligatoire |
UI dégradée | [broken] [i18n-miss] |
| 97 | Bulk action : sélectionner 5 activités → "Reporter de 2 jours" | OK + 5 audits | [broken] [ergo-bad] si pas de bulk |
| 98 | Permissions : qa.viewer voit mais pas créer |
OK | [perm-leak] |
| 99 | Dashboard /planner widgets : Vue d'ensemble (compteur), Conflits (post fix), Types (donut), Statuts (bar), PAX par site, Heatmap |
post fix #42 + #53 = OK | [broken] |
| 100 | Export iCal .ics → fichier valide importable Google Calendar |
OK | [broken] [404-route] |
| 101 | Notifications : créer activité dans 24h → alerte aux participants J-1 | mail + in-app notif | [broken] |
| 102 | Onglet "PAX" sur activité → voir + gérer la liste, capacité max | OK | [broken] [i18n-miss] |
| 103 | Onglet "Documents" → upload PV de réunion post-activité | OK | [broken] |
| 104 | Soft-delete activité avec récurrence → option "uniquement cette occurrence" / "toutes les futures" / "toutes" | OK choix UI | [broken] [ergo-bad] |
| 105 | Hard-delete (admin) | OK | [broken] |
| 106 | Filtre par installation : sélectionner INST-COMP-A → tableau filtré | OK | [broken] [perf] |
| 107 | Tester injection XSS dans title : <img src=x onerror=alert(1)> |
escape OK | [xss-risk] |
| 108 | Console DevTools clean sur Planner | 0 erreur | [broken] |
| 109 | Activité permanent_ops → vérifier libellé "Opérations permanentes" en FR (cf bug #42) | OK | [i18n-miss] régression |
| 110 | Vérifier widget "Conflits" KPI affiche le bon nombre (cf bug #53 fixé) | OK | [broken] régression |
Phase 5 — PaxLog (étapes 111-145, 35 étapes)#
Création PaxProfile complet#
Étape 111 — Champs PaxProfile (app/models/paxlog.py:PaxProfile) :
first_name : Jean
last_name : Mballa
civility : Mr
date_of_birth : 1985-04-12
nationality : CMR
gender : M
badge_number : BDG-2026-04200
employee_number : EMP-005421
position : Operator I&E
department : Maintenance
employer_tier_id : QA-DEMO-001 (créé phase 2)
linked_user_id : null (ou qa.manager)
linked_contact_id: contact créé étape 40
emergency_contact: {"name":"Marie Mballa","phone":"+237 691 99 88 77","relation":"épouse"}
medical_status : fit
last_medical_at : 2026-04-10
next_medical_at : 2027-04-10
trainings : (créés en étapes suivantes)
documents : (idem)
photo_url : (upload jpg ID photo)
active : true
metadata_ : {"site_assigned":"BBR-01"}
notes : Note interne
| # | Action | Attendu | Tags |
|---|---|---|---|
| 112 | Submit profile | 201 | [broken] [i18n-miss] |
| 113 | Onglet "Credentials" → ajouter 5 certifications (BOSIET, FOET, H2S, HUET, médical), avec credential_type_id, issue_date, expiry_date, status, attachment |
5 lignes, badges expiry colorés | [broken] [ui-stack] |
| 114 | Vérifier widget "Conformité PAX" affiche % avec % (cf bug #41 fixé) |
post fix | [i18n-miss] régression |
| 115 | Onglet "Mouvements" → POST /api/v1/pax/<id>/movements IN avec from_location, to_location, transport_vector, voyage_id, at |
201 | [broken] |
| 116 | Onglet "Mouvements" → OUT correspondant | 201 + balance OK | [broken] |
| 117 | KPI "PAX sur site" → mis à jour en temps réel (post IN) | OK | [broken] [perf] |
| 118 | Onglet "Trainings" → créer training "First Aid" planning + completion | OK | [broken] |
| 119 | Onglet "Incidents" → ouvrir incident type=near_miss severity=low (24 champs incident) |
201 + counter "Incidents actifs" +1 (cf bug #49) | [broken] régression |
| 120 | Résoudre incident → resolved_at set, counter -1 |
post fix #49 OK | [broken] |
| 121 | Onglet "Compliance Records" → grouper par catégorie (post fix #50) | OK donut | [broken] régression |
| 122 | Génération badge PDF (1 page recto, photo, code QR, expiry, …) | OK ou 501 explicite | [broken] [404-route] |
| 123 | Manifest sortie/entrée par voyage → GET /api/v1/voyages/<id>/manifest |
OK | [broken] |
| 124 | Liste profiles /paxlog : 100+ entrées, filtres par employer, status, certification expirante |
OK | [broken] [perf] |
| 125 | Bulk import CSV (fichier 50 lignes) → preview → confirm | OK + 50 rows | [broken] [no-feedback] |
| 126 | Bulk export CSV de la liste filtrée | OK 16 cols | [broken] |
| 127 | Conflit dates : ajouter mouvement IN qui chevauche autre mouvement actif | 422 explicite | [broken] [no-feedback] |
| 128 | Permissions : qa.viewer voit liste, pas créer/edit |
OK | [perm-leak] |
| 129 | Permissions : qa.manager (PaxLog manager) peut tout, mais pas accéder Tiers |
OK 403 sur Tiers | [perm-leak] |
| 130 | Dashboard /paxlog widgets : 8 onglets fonctionnels, post fixes #41/#49/#50 |
OK | [broken] |
| 131 | Recherche full-text dans paxlog (nom + badge + employer) | < 1s | [perf] [broken] |
| 132 | Mark profile médical expiré → badge rouge + bloque mouvements futurs | OK | [broken] [no-feedback] |
| 133 | Soft-delete profile actif → confirme avec raison | OK | [broken] [ergo-bad] |
| 134 | Restaurer | OK | [broken] |
| 135 | Hard-delete (admin) | OK + cascade credentials/movements | [broken] [data] |
| 136 | Tenter XSS dans notes rich-text |
escape | [xss-risk] |
| 137 | Vérifier que badge_number est unique (tenter doublon) |
422 message clair | [broken] [i18n-miss] |
| 138 | Vue calendrier des mouvements (IN/OUT par jour) | OK | [broken] [404-route] |
| 139 | Notifications : alerte J-30 expiry credential → email + in-app | OK | [broken] |
| 140 | Modifier date d'expiry credential → recalcul couleur instantané | OK | [broken] |
| 141 | Onglet "ADS" (autorisations de sortie) → liste 2+ ADS, statut, dates | OK (cf dashboard étape 130 mentionne 2 ADS attente) | [broken] [404-route] |
| 142 | Création ADS : tous champs (PAX, dates, motif, manager, statut) | 201 | [broken] [i18n-miss] |
| 143 | Workflow ADS : pending → approved → printed → returned | 4 transitions audit | [broken] |
| 144 | Console DevTools PaxLog clean | 0 erreur | [broken] |
| 145 | Vérifier endpoint /api/v1/dashboards/widget-data pour paxlog_compliance_rate retourne nombre + unité % |
post fix OK | [broken] régression |
Phase 6 — PackLog (étapes 146-165, 20 étapes)#
Création Cargo Request + Manifest + Items#
Étape 146 — POST /api/v1/packlog/requests (CargoRequest) :
code : DEM-QA-V3-001
type : standard
priority : high
requester_user_id: qa.manager@opsflux.io
origin_asset_id : Site-BBR-01
destination_asset: INST-OFFSHORE-RIG-1
required_at : 2026-07-01
status : pending
description : Pièces compresseur K-101
items_summary : "3 colis, 145 kg total, 1 m3"
sap_wo : WO-100542
budget_code : OPEX-Q3-2026
contact_phone : +237 691 23 45 67
contact_email : contact@qatest.example.com
metadata_ : {"hazmat":false,"refrigerated":false}
| # | Action | Attendu | Tags |
|---|---|---|---|
| 147 | Submit request | 201 | [broken] [i18n-miss] |
| 148 | Créer Voyage (voyages) lié à un vector (créé en phase 7) avec ETD/ETA |
201 | [broken] |
| 149 | Créer VoyageManifest rattaché au voyage | 201 | [broken] |
| 150 | Créer CargoItems (3 items) liés au manifest avec tracking_code, description, weight_kg, volume_m3, workflow_status=created, dimensions |
201 ×3 | [broken] [i18n-miss] |
| 151 | Vérifier widget packlog_overview (post fix #44) : 1 demande + 3 colis |
OK chiffres | [broken] régression |
| 152 | Vérifier widget packlog_tracking (post fix #52) : tableau 3 items avec tracking_code, workflow_status, voyage_code |
OK colonnes | [broken] régression |
| 153 | Workflow CargoItem : created → packed → in_transit → delivered → returned (5 transitions) |
OK + audit + dates | [broken] [i18n-miss] |
| 154 | Champ tracking_code : génération automatique format CGO-YYYY-XXXX |
unique | [broken] |
| 155 | Catalogue SAP : import 1 catégorie d'articles (CSV) | OK liste | [broken] [404-route] |
| 156 | Recherche dans catalogue par code SAP / désignation | < 1s | [perf] [broken] |
| 157 | Liste demandes filtrée par statut + dates + origin/destination | OK | [broken] |
| 158 | Bouton "Imprimer étiquette" colis → PDF avec QR + tracking_code + destination | OK ou 501 | [broken] [404-route] |
| 159 | Onglet "Alertes" : vérifier les 5 alertes mentionnées sur dashboard étape 130 | OK | [broken] |
| 160 | Permissions : qa.viewer lecture, qa.manager (PaxLog only) → 403 sur PackLog |
OK | [perm-leak] |
| 161 | Bulk update statut sur 3 colis | OK | [broken] [ergo-bad] |
| 162 | Bug #43 cosmétique : destination affichée en --- au lieu de — |
post check, fix | [ui-inconsistent] |
| 163 | Soft-delete request → archive (cascade items optionnel) | OK | [broken] [data] |
| 164 | Hard-delete (admin) | OK + cascade | [broken] |
| 165 | Console DevTools PackLog clean | 0 erreur | [broken] |
Phase 7 — TravelWiz (étapes 166-185, 20 étapes)#
Création TransportVector + Voyage#
Étape 166 — POST /api/v1/travelwiz/vectors (TransportVector) :
code : VEC-QA-V3-001
name : MV Atlantique Express
type : vessel (vessel / helicopter / truck / bus)
operator_tier_id : QA-DEMO-001
capacity_pax : 12
capacity_cargo_kg: 5000
mmsi : 123456789 (vessel)
imo : IMO9876543 (vessel)
callsign : TR-ATX-1
home_port : Douala
flag_country : CMR
year_built : 2018
gross_tonnage : 250
length_m : 32.5
beam_m : 8.4
draft_m : 2.1
fuel_type : diesel
active : true
metadata_ : {"insurance_expires":"2027-03-15"}
| # | Action | Attendu | Tags |
|---|---|---|---|
| 167 | Submit vector | 201 | [broken] [i18n-miss] |
| 168 | Onglet "Positions" : POST /vector/positions (lat, lng, speed_knots, heading, recorded_at) |
201, marker apparaît carte flotte (post fix #47) | [broken] régression |
| 169 | Vue carte fleet_map : 1 marker avec status visible (post fix #47) |
OK | [broken] régression |
| 170 | Création Voyage (voyages) : code, vector_id, departure_port, arrival_port, ETD, ETA, status=scheduled |
201 | [broken] |
| 171 | Manifest PAX : ajouter 5 PAX au voyage (PAX créés phase 5) | OK | [broken] |
| 172 | Manifest cargo : ajouter VoyageManifest + 3 items (déjà fait phase 6) → réutiliser | OK | [broken] |
| 173 | Workflow voyage : scheduled → boarding → at_sea → arrived → completed | 5 transitions audit | [broken] [i18n-miss] |
| 174 | Génération bulletin de voyage PDF (PAX list + cargo manifest) | OK ou 501 | [broken] [404-route] |
| 175 | Vue calendrier voyages | OK | [broken] |
| 176 | Carte météo weather_sites (post fix #45) |
sites + bulletin OpenWeather | [broken] régression |
| 177 | Carte assets assets_map (post fix #48) |
3 layers (champs/sites/installations) avec markers | [broken] régression |
| 178 | Onglet "Alertes" : alertes opérationnelles (météo, retards, conflits) | OK | [broken] |
| 179 | Onglet "Voyages du jour" → liste vide ou items du jour | OK | [broken] |
| 180 | Onglet "Cargo en attente" : 2 cargos mentionnés dashboard | OK | [broken] |
| 181 | Onglet "KPIs flotte" 3/3 vecteurs : disponibilité, utilisation, pannes | OK | [broken] [i18n-miss] |
| 182 | Permissions cross : qa.viewer lecture vecteurs/voyages, pas créer |
OK | [perm-leak] |
| 183 | Tester import voyage CSV bulk | OK ou explicite | [broken] [no-feedback] |
| 184 | Soft-delete voyage avec PAX + cargo manifests → confirme cascade | OK + records préservés (soft) | [broken] [data] |
| 185 | Console DevTools TravelWiz clean | 0 erreur | [broken] |
Phase 8 — Cross-modules + cohérence UI globale (étapes 186-195, 10 étapes)#
| # | Action | Attendu | Tags |
|---|---|---|---|
| 186 | Sidebar : 11 modules + 8 admin = 19 entries cliquables, icônes uniques, libellés FR/EN cohérents | OK | [ui-inconsistent] [i18n-miss] |
| 187 | Topbar : workspace switcher 4 entries, search global, création rapide menu, mode sombre toggle, notifs, assistant | OK | [ui-inconsistent] |
| 188 | Mode sombre : activer → tester chaque module 30s → 0 contraste cassé, 0 texte invisible | OK | [ui-stack] [a11y] |
| 189 | Switch FR↔EN sur 6 modules → 0 clé brute, 0 mot non-traduit | OK | [i18n-miss] [hardcode] |
| 190 | Scroll horizontal jamais (overflow-x:hidden hors carte) | OK | [ui-overflow] |
| 191 | DataTables : pattern uniforme (search bar position, pagination, export, filters) entre Tiers/Projets/Planner/PaxLog/PackLog/TravelWiz | OK | [ui-inconsistent] |
| 192 | Toasts confirmations : style + position + durée + dismiss identiques partout | OK | [ui-inconsistent] |
| 193 | Loading states : skeleton vs spinner cohérent par contexte | OK | [ui-inconsistent] [no-feedback] |
| 194 | Empty states : illustration + message + CTA cohérents (pas juste "Aucune donnée") | OK | [ergo-bad] [i18n-miss] |
| 195 | Breadcrumbs : présents et corrects sur chaque page (Module > Liste > Item > Onglet) | OK | [ergo-bad] |
Phase 9 — Responsive + raccourcis clavier (étapes 196-200, 5 étapes)#
| # | Action | Attendu | Tags |
|---|---|---|---|
| 196 | Responsive 360px (iPhone SE) : sidebar collapse, tableaux scroll horizontal, formulaires colonne unique, touch targets ≥44px | OK | [resp] [ui-overflow] [a11y] |
| 197 | Responsive 768px (iPad) : sidebar reste visible, tableaux 2 cols, dashboards 2-col grid | OK | [resp] [ui-stack] |
| 198 | Responsive 1280px (laptop) : layout cible | OK | [resp] |
| 199 | Raccourcis clavier : ? ouvre cheatsheet, / focus search global, gt go to Tiers, gp projects, cmd+k palette commandes, Esc ferme modale, Ctrl+S save form, Tab cycle inputs |
au moins 6/8 OK | [shortcut-miss] [a11y] |
| 200 | Lighthouse audit en mode incognito sur /, /tiers, /projets, /planner, /paxlog, /packlog, /travelwiz : Performance ≥ 80, Accessibility ≥ 90, Best Practices ≥ 90, SEO N/A (app interne) |
au moins 5/7 pages OK | [perf] [a11y] |
Annexe — Méthode d'exécution recommandée#
Pour Bastien (manuel)#
- Ouvrir
QA-LOG.md→ ajouter section## Session 18 — Exécution v3 - Pour chaque étape : noter
✅ PASSou❌ FAIL: [tag] description - Bugs FAIL : créer ticket dans le tracker, capturer screenshot, lien vers étape
Pour moi (autonome via Chrome MCP)#
- Phase 0 :
Grepdirect, pas de browser - Phases 1-9 :
mcp__Claude_in_Chrome__navigate+find+form_input+read_page(DOM compact) - Bug détecté → tag + description compacte dans QA-LOG, fix immédiat si trivial, sinon documenter
- Commit batch toutes les 25-30 étapes
Coût token estimé Chrome MCP#
- 200 étapes × ~3 k tokens (DOM read + actions) = 600 k tokens total
- À comparer : computer-use screenshots à 30 k/page = 6 M tokens (10× plus)
Données de test à ne pas perdre#
Codes uniques utilisés (pour cleanup ou re-test) :
Tier : QA-DEMO-001
Contact : (généré, lien Tier QA-DEMO-001)
Project : PRJ-QA-V3-001
Activity : ACT-QA-V3-001
Pax : badge BDG-2026-04200
CargoReq : DEM-QA-V3-001
Vector : VEC-QA-V3-001
Voyage : (généré au step 170)
Users : qa.viewer@opsflux.io, qa.manager@opsflux.io
Délégation : créée step 19, révoquée step 22
MOC : créée step 67
Total étapes par phase#
| Phase | Module | Étapes | Sous-total |
|---|---|---|---|
| 0 | Recon code | 10 | 10 |
| 1 | Auth + permissions + MFA | 15 | 25 |
| 2 | Tiers | 30 | 55 |
| 3 | Projets | 30 | 85 |
| 4 | Planner | 25 | 110 |
| 5 | PaxLog | 35 | 145 |
| 6 | PackLog | 20 | 165 |
| 7 | TravelWiz | 20 | 185 |
| 8 | Cross + cohérence UI | 10 | 195 |
| 9 | Responsive + raccourcis | 5 | 200 |