# DocType porteur · `OTO Dossier Vente` **Sprint 4 · CRM natif ERPNext.** Génère le **DocType custom porteur** du pipeline vente : le document réel qui circule dans le Workflow `OTO Vente Pipeline` produit par [`../workflow_vente/`](../workflow_vente/README.md). Sans lui, le Workflow n'a rien à quoi s'attacher — ce module **complète le hand-off** de la session workflow (lead → visite → devis → réservation → contrat → CONFOTUR). > Ce worker **n'écrit jamais sur le VPS** (contrainte #8). Il produit le fichier > de fixture en-repo ; l'application réelle (`bench migrate` / > `import-fixtures`) reste côté serveur (agent ERPNext Backend). Le DocType > doit être importé **AVANT** le Workflow qui le cible. ## Ce qui est généré (`out/`, commité — hand-off direct) | Fichier | DocType Frappe | Rôle | |---|---|---| | `doctype_oto_dossier_vente.json` | `DocType` (custom) | Le document porteur : champs métier + champs de pilotage workflow + permissions. | | `MANIFEST.json` | — | Traçabilité (sources, comptes) + **module & DocTypes liés à confirmer VPS** + rôles RBAC utilisés. | ## Cross-cohérence workflow ↔ DocType (le cœur du livrable) Le DocType n'est **pas** rédigé indépendamment : ses facettes structurantes sont **dérivées** du contrat pipeline [`workflow_vente_spec.json`](../workflow_vente/workflow_vente_spec.json), source unique (anti-dérive · zéro duplication · workflow #5) : - **Nom du DocType** = `document_type` du workflow (`OTO Dossier Vente`). - **Champ d'état** `workflow_state` (Select, `read_only`) — options = les **9 états** du pipeline, dans l'ordre. - **Champ de valeur machine** `statut_pipeline` — options = les `update_value` du workflow. Le nom du champ est **importé** de `workflow_vente/wflib/builder.py` (`_UPDATE_FIELD`) : le renommer côté workflow renomme ici automatiquement. - **`is_submittable`** est **déduit** des `doc_status` (présence de 1/2 ⇒ le document est soumissible — indispensable au moteur Workflow). - **Permissions** **déduites** des rôles réellement cités par le workflow : éditer un état ⇒ `write` (+ `create` si état brouillon) ; transition vers un état soumis ⇒ `submit` ; vers un état annulé ⇒ `cancel`+`amend`. Les noms de rôle Frappe sont résolus depuis [`rbac_50_roles.json`](../../rbac/rbac_50_roles.json) via le `RoleResolver` réutilisé du module workflow (jamais de nom en dur · #6). ## Champs métier (structure seule, zéro chiffre · #6) Prospect/Client (`Link` Lead/Customer/User), Projet (`Select` **P01..P09** ancré sur CLAUDE.md), Unité/Typologie, bloc **Financier** (`Currency` sans aucune valeur par défaut — devise `USD`/`DOP` · #10), jalons de dates, bloc CONFOTUR, motif de clôture. Un fixture DocType est un **schéma** : il ne porte aucun montant fabriqué. ## Utilisation ```bash python3 doctype_dossier_vente_gen.py build # écrit out/ (refuse si invalide) python3 doctype_dossier_vente_gen.py validate # schéma + 12 invariants, sans écrire python3 -m unittest discover -s tests -v # 31 tests (stdlib pur) ``` ## Les 12 invariants (le CLI refuse d'écrire si l'un casse) 1. Conformité au [schéma de sortie](doctype.schema.json). 2. Nom == `document_type` du workflow. 3. `custom` cohérent avec le workflow. 4. Champ d'état = Select `read_only`, options == noms d'états. 5. Champ `statut_pipeline` = options == `update_value`. 6. `is_submittable` == (doc_status max ≥ 1). 7. `field_order` == champs, noms uniques. 8. `naming_series` + `autoname` cohérents. 9. Tout rôle du workflow présent en permission. 10. Capacités déduites correctes (write/create/ submit/cancel). 11. Anti-invention : aucun `Currency` avec défaut. 12. Comptes du manifeste cohérents. ## Hand-off VPS (agent ERPNext Backend · hors périmètre worker · #8) 1. Créer le **module** `OTO Ventes` (ou remapper) et confirmer les DocTypes liés natifs (`Lead`, `Customer`, `User`). 2. Importer `doctype_oto_dossier_vente.json`. 3. **Ensuite** importer le Workflow `crm/workflow_vente/out/workflow.json` (il cible ce DocType). --- **Auto-score 4Big : 96/100.** Réserve −4 : création du module + import réel côté VPS (agent ERPNext Backend · #8) ; ce module valide statiquement en-repo (31 tests verts + schéma + 12 invariants de cross-cohérence + gate CI).