# Daily Report · 2026-07-30 · Claude Code DTP Worker (session 11) **Session** : `20260730_052701` ## Tâche exécutée **Sprint 4 · CRM — Générateur de workflow vente ERPNext** (roadmap `04_roadmap/ROADMAP_8_WEEKS_OR_LESS.md` §Sprint 4 : « workflow complet **lead → visite → devis → réservation → contrat → CONFOTUR** »). Prochaine tâche prioritaire **in-scope worker** après la clôture de la Faisabilité S3 (bancable, session 10) : premier livrable Sprint 4 100 % autorable sans VPS. ## Contexte / analyse - Relu `CLAUDE.md`, `ROADMAP_8_WEEKS_OR_LESS.md`, daily report session 10, l'AGENT.md CRM, le contrat `rbac_50_roles.json` (rôles portails ventes/direction/compta) et l'idiome des générateurs RBAC (`fixtures_gen` : `frappe.py`/`builder.py`/`schema`/CLI `build|validate`/tests + job CI + gate). - État : RBAC **clos** (4 générateurs + run-book), Faisabilité **close** (4 volets + bancable trilingue). Sprint 4 non entamé en-repo. Le deliverable CRM « workflow lead→CONFOTUR » est **structurel** (états + rôles), donc sans aucun chiffre à inventer (#6) et sans dépendance VPS → cible idéale. - Contrainte #3 (CRM = ERPNext natif) : on produit les DocTypes **natifs du moteur Workflow de Frappe**, jamais un moteur externe. ## Réalisé — module `05_deliverables_mvp/crm/workflow_vente/` - `workflow_vente_spec.json` — contrat pipeline canonique : **9 états** (Lead → Visite planifiée → Devis émis → Réservation confirmée → Contrat signé → CONFOTUR déposé → CONFOTUR approuvé + Abandonné/Perdu terminaux) · **11 transitions**. Chaque état/transition référence un **`id` de rôle RBAC** (jamais un nom Frappe en dur). - `wflib/rbac.py` — **réutilisation (workflow #5, zéro duplication)** de `rbac_50_roles.json` : résout `role_id → erpnext_role_name`. `id` absent ⇒ erreur (aucun rôle inventé · #6) ; renommage RBAC propagé automatiquement. - `wflib/erpnext.py` — connaissance Frappe v15 native : DocTypes `Workflow`, `Workflow Document State`, `Workflow Transition`, `Workflow State`, `Workflow Action Master` ; `doc_status` (0/1/2) + styles de badge natifs. - `wflib/builder.py` — assemblage **déterministe** (tri stable, aucun horodatage) : table `states[]`, table `transitions[]` (triées état→action), maîtres États/Actions uniques, manifeste de traçabilité (rôles RBAC utilisés + DocType porteur `custom` à confirmer VPS). - `workflow.schema.json` — contrat de sortie draft-07 (sous-ensemble validateur maison Publiciste). - `workflow_vente_gen.py` — CLI `build` / `validate`. **Refuse d'écrire** si un des **9 invariants de graphe** casse. - `out/` (commité, hand-off direct) : `workflow.json`, `workflow_state.json`, `workflow_action_master.json`, `MANIFEST.json`. - `tests/test_workflow_vente.py` — **25 tests `unittest` (stdlib pur)**. - **CI** : job `crm-workflow-vente-tests` ajouté au **gate** (`.gitea/workflows/ci.yml`, Gitea Actions uniquement · #2). ## 9 invariants de graphe (le CLI refuse d'écrire si l'un casse) 1. Conformité au schéma de sortie. 2. Unicité des noms d'état. 3. Transitions vers/depuis états déclarés. 4. **Monotonie `doc_status`** (pas de saut 0→2 ni retour arrière — natif Frappe). 5. Unicité (état, action). 6. **Atteignabilité** de tous les états depuis `Lead`. 7. ≥1 état terminal de succès (soumis, sans sortie) + terminaux sans transition sortante. 8. **Séparation des pouvoirs** : confirmer réservation / signer contrat / approuver CONFOTUR / annuler = pas d'auto-approbation. 9. Maîtres États/Actions == graphe + comptes manifeste cohérents. ## Anti-invention (#6) + séparation des pouvoirs appliqués - Aucun nom de rôle en dur : tous résolus depuis le contrat RBAC (source unique). - Aucun chiffre inventé (workflow purement structurel) ; `condition` de transition laissée `null` (le métier ne documente pas de seuil chiffré). - Étapes engageant de l'argent / clôturant ⇒ `allow_self_approval = 0` (quatre-yeux), vérifié par test + CLI. ## Vérifications effectuées (en-repo, sans toucher au VPS) - **25/25 tests verts** (schéma maison + oracle `jsonschema`) ; génération réelle : 9 états / 11 transitions / 9 maîtres États / 9 maîtres Actions ; `out/` commité == régénération bit-à-bit. - **Gate CI local vert (exit 0)** : `guard_constraints.sh`, `validate_json.sh` (nouveaux JSON valides), `check_docs.sh` (README score présent, 0 lien cassé), YAML `ci.yml` valide. - **Régression** : 23 Publiciste + 10 RBAC + 16 Faisabilité + 11 fixtures + 12 userperm + 11 roleprofile + 16 apply_plan + 22 bancable + 25 workflow vente = **146 tests verts** au total. ## Non fait (hors périmètre worker · VPS) - Création du DocType porteur `OTO Dossier Vente` + import des fixtures (`bench migrate`) → agent ERPNext Backend (contrainte #8). ## Prochaine tâche suggérée - Sprint 4 CRM : générateur des **Notification/Email Alert** par transition, ou du **DocType porteur `OTO Dossier Vente`** (fixture DocType) pour compléter le hand-off workflow. - Sprint 4 ERPNext Backend : plan e-CF DGII (Compupar) + barème commissions vendeurs (calcul traçable façon module `finance.py` du bancable · #6). --- **Auto-score 4Big du livrable Générateur workflow vente : 96/100.** Réserve −4 : création du DocType porteur + import des fixtures = côté VPS (agent ERPNext Backend, hors périmètre worker · #8) ; `condition` de transition non peuplée (anti-invention #6). Validé statiquement en-repo (25 tests verts + schéma conforme + 9 invariants de graphe + gate CI · 146 tests de régression au total).