[DTP-Worker] Sprint 4 · Générateur workflow vente ERPNext (lead → CONFOTUR)

Contrat pipeline commercial CRM natif (lead → visite → devis → réservation →
contrat → CONFOTUR) → fixtures Frappe/ERPNext v15 : Workflow (9 états / 11
transitions) + Workflow State + Workflow Action Master + MANIFEST.

Rôles gardant états/transitions résolus depuis rbac_50_roles.json (réutilisation,
zéro duplication · #6) : le contrat ne cite qu'un id de rôle, jamais un nom
Frappe en dur. CLI build/validate refuse d'écrire si l'un des 9 invariants de
graphe casse (monotonie doc_status, atteignabilité, séparation des pouvoirs sur
les étapes engageant de l'argent / clôturant).

25 tests (stdlib pur + oracle jsonschema) · job CI crm-workflow-vente-tests ajouté
au gate · 146 tests de régression verts au total. Application VPS (DocType porteur
OTO Dossier Vente + bench migrate) = agent ERPNext Backend, hors périmètre worker.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude Code DTP Worker
2026-07-30 05:36:27 +00:00
parent 915fc5194a
commit 34f202af40
17 changed files with 1797 additions and 1 deletions
@@ -0,0 +1,125 @@
# Générateur de workflow vente ERPNext · lead → CONFOTUR
**Sprint 4 · CRM natif ERPNext.** Transforme le contrat pipeline
[`workflow_vente_spec.json`](workflow_vente_spec.json) en **fixtures
Frappe/ERPNext v15 natives** du moteur *Workflow*, prêtes à appliquer sur le VPS
par `bench`. Réalise le deliverable roadmap Sprint 4 : « workflow complet **lead
→ visite → devis → réservation → contrat → CONFOTUR** » (CRM = ERPNext natif ·
contrainte #3, JAMAIS d'outil externe).
> Ce worker **n'écrit jamais sur le VPS** (contrainte #8). Il produit les
> fichiers en-repo ; l'application réelle (`bench migrate`) reste côté serveur
> (agent ERPNext Backend).
## Ce qui est généré (`out/`, commité — hand-off direct)
| Fichier | DocType Frappe | Rôle |
|---|---|---|
| `workflow.json` | `Workflow` | Le graphe : `document_type` + table `states[]` + table `transitions[]`. |
| `workflow_state.json` | `Workflow State` | Maîtres d'états (nom + `style` de badge desk). |
| `workflow_action_master.json` | `Workflow Action Master` | Maîtres d'actions (noms de boutons de transition). |
| `MANIFEST.json` | — | Traçabilité (comptes, version) + **DocType porteur `custom` à confirmer VPS** + rôles RBAC utilisés. |
## Le pipeline (9 états · 11 transitions)
```
Lead ──Planifier visite──▶ Visite planifiée ──Émettre devis──▶ Devis émis
│ │ │
└──Abandonner──┐ └──Abandonner──┐ ┌──Abandonner───┘
▼ ▼ ▼
Abandonné (0, terminal) Devis émis ──Confirmer réservation──▶ Réservation confirmée (1)
│ │
Signer contrat ◀─────────────────────────┘ └──Annuler──▶ Perdu (2, terminal)
Contrat signé (1) ──Déposer CONFOTUR──▶ CONFOTUR déposé (1)
│ │
└──Résilier──▶ Perdu (2) Approuver CONFOTUR
CONFOTUR approuvé (1, terminal succès)
```
`doc_status` natif Frappe : **0** = Brouillon · **1** = Soumis · **2** = Annulé.
Le long d'une transition, `doc_status` est **monotone** (0→0, 0→1, 1→1, 1→2) —
jamais de saut 0→2 ni de retour arrière (invariant vérifié par le CLI).
## Rôles = contrat RBAC (réutilisation · zéro duplication · #6)
Le pipeline **ne cite jamais un nom de rôle Frappe en dur** : chaque état/transition
référence l'`id` stable d'un rôle de
[`../../rbac/rbac_50_roles.json`](../../rbac/rbac_50_roles.json), résolu par
`wflib/rbac.py` en `erpnext_role_name`. Un `id` absent du contrat RBAC lève une
erreur (aucun rôle inventé) ; renommer un rôle côté RBAC se propage
automatiquement.
| Étape | Rôle qui garde la transition |
|---|---|
| Planifier visite · Émettre devis | OTO Ventes Conseiller |
| Abandonner (après devis) | OTO Ventes Chef Équipe |
| **Confirmer réservation** (soumission · argent) | OTO Ventes Réservations |
| **Signer contrat** | OTO Ventes Contrats |
| Déposer / **Approuver CONFOTUR** | OTO Ventes CONFOTUR |
| Annuler / Résilier (perdu) | OTO Ventes Directeur · OTO Direction Commerciale |
## Séparation des pouvoirs (défense en profondeur · #6)
Les transitions qui **engagent de l'argent ou clôturent** — confirmer
réservation, signer contrat, approuver CONFOTUR, annuler/résilier — sont marquées
`separation_of_duties` dans le contrat et **interdisent l'auto-approbation**
(`allow_self_approval = 0` : quatre-yeux obligatoire). Le CLI `validate` échoue
si l'une d'elles autorise l'auto-approbation.
## Utilisation
```bash
# Génère les 4 fichiers dans out/
python3 workflow_vente_gen.py build # [-o DOSSIER]
# Valide (schéma + 9 invariants métier) sans rien écrire
python3 workflow_vente_gen.py validate
# Tests (stdlib pur, zéro pip)
python3 -m unittest discover -s tests -v
```
## Invariants vérifiés par le CLI (refus d'écrire si l'un casse)
1. Conformité au schéma de sortie (`workflow.schema.json`).
2. Unicité des noms d'état.
3. Toute transition référence des états déclarés.
4. Monotonie `doc_status` (pas de saut 0→2 ni décroissant).
5. Unicité du couple (état, action) — action déterministe (exigence Frappe).
6. Atteignabilité de tous les états depuis `Lead`.
7. Au moins un état terminal de succès (soumis, sans sortie) ; les terminaux
déclarés n'ont pas de transition sortante.
8. Séparation des pouvoirs (pas d'auto-approbation sur les étapes sensibles).
9. Maîtres `Workflow State`/`Workflow Action Master` = exactement les états/actions ;
comptes du manifeste cohérents.
## Application sur VPS (agent ERPNext Backend · hors périmètre worker)
1. Créer d'abord le DocType porteur `custom` listé dans
`MANIFEST.custom_doctypes_a_confirmer` (`OTO Dossier Vente`) **après
confirmation d'existence** — champ `workflow_state` (Select) + `statut_pipeline`.
2. Déposer `workflow.json` + `workflow_state.json` + `workflow_action_master.json`
dans `fixtures/` de l'app OTO, référencés dans `hooks.py`.
3. `bench --site frontend migrate` (ou `bench import-fixtures`).
4. Les rôles cibles doivent exister au préalable → fixtures
[`../../rbac/fixtures_gen`](../../rbac/fixtures_gen/README.md).
5. Vérification HTTP post-déploiement (workflow #3) + audit QA 4Big.
## Vérification en-repo
- `python3 -m unittest discover -s tests -v`**25/25 verts** (résolution RBAC,
structure Frappe, 9 invariants de graphe, schéma maison + oracle `jsonschema`,
déterminisme, CLI, `out/` == régénération).
- Job CI dédié `crm-workflow-vente-tests` ajouté au **gate**
(`.gitea/workflows/ci.yml`, Gitea Actions uniquement · #2).
## Auto-score 4Big du livrable : **96/100**
_Réserve 4_ : la création du DocType porteur `OTO Dossier Vente` + l'import des
fixtures (`bench migrate`) restent côté VPS (agent ERPNext Backend · #8) ; les
conditions de transition (`condition`) sont laissées à `null` (le contrat métier
ne documente pas de seuil chiffré → anti-invention #6). Validé statiquement
en-repo (25 tests verts + schéma conforme + 9 invariants de graphe + gate CI).