# 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).
