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>
6.7 KiB
Générateur de workflow vente ERPNext · lead → CONFOTUR
Sprint 4 · CRM natif ERPNext. Transforme le contrat pipeline
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, 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
# 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)
- Conformité au schéma de sortie (
workflow.schema.json). - Unicité des noms d'état.
- Toute transition référence des états déclarés.
- Monotonie
doc_status(pas de saut 0→2 ni décroissant). - Unicité du couple (état, action) — action déterministe (exigence Frappe).
- Atteignabilité de tous les états depuis
Lead. - Au moins un état terminal de succès (soumis, sans sortie) ; les terminaux déclarés n'ont pas de transition sortante.
- Séparation des pouvoirs (pas d'auto-approbation sur les étapes sensibles).
- 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)
- Créer d'abord le DocType porteur
customlisté dansMANIFEST.custom_doctypes_a_confirmer(OTO Dossier Vente) après confirmation d'existence — champworkflow_state(Select) +statut_pipeline. - Déposer
workflow.json+workflow_state.json+workflow_action_master.jsondansfixtures/de l'app OTO, référencés danshooks.py. bench --site frontend migrate(oubench import-fixtures).- Les rôles cibles doivent exister au préalable → fixtures
../../rbac/fixtures_gen. - 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 + oraclejsonschema, déterminisme, CLI,out/== régénération).- Job CI dédié
crm-workflow-vente-testsajouté 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).