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