[DTP-Worker] Sprint 2 · Générateur Role Profile par portail (RBAC 50 rôles → 6 bundles assignables ERPNext v15)

- roleprofile_gen/ : profilelib (frappe Role Profile + Has Role natif v15,
  builder déterministe), roleprofile.schema.json, CLI build/validate refusant
  d'écrire si invariant cassé, 11 tests stdlib, README, .gitignore (out/).
- 6 profils couvrant les 50 rôles de façon bijective (5 portails métier +
  console technique plateforme). Aucun DocType custom (que du natif).
- Anti-invention #6 : rôles 100 % issus du contrat, nom de profil = convention
  déterministe dérivée de la clé portail.
- CI : job rbac-roleprofile-tests ajouté au gate (.gitea/workflows/ci.yml).
- Doc : SPEC §7 ét.5 + encart livré. Daily report session 8.
- Régression : 83 tests verts (72 + 11). Gate CI local vert.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude Code DTP Worker
2026-07-30 04:04:04 +00:00
parent b1b011ab81
commit 70e022ccd6
11 changed files with 879 additions and 2 deletions
@@ -0,0 +1,103 @@
# Générateur de `Role Profile` · RBAC 50 rôles → bundles par portail
**Sprint 2 · agent ERPNext Backend.** Troisième et dernier maillon RBAC en-repo,
complément des générateurs [`Role` + `Custom DocPerm`](../fixtures_gen/README.md)
(quels verbes sur quel DocType) et [plan `User Permission`](../userperm_gen/README.md)
(sur quelles lignes). Il produit les **bundles assignables** : un `Role Profile`
ERPNext v15 **natif** par portail, qui permet d'attribuer à un utilisateur, en un
seul geste (`User.role_profile_name`), l'ensemble des rôles de SON portail.
Transforme le contrat [`../rbac_50_roles.json`](../rbac_50_roles.json) (validé par
[`../rbac.schema.json`](../rbac.schema.json)) en fixtures Frappe prêtes à importer.
> Ce worker **n'écrit jamais sur le VPS** (contrainte #8). Il produit un bundle de
> fixtures en-repo ; l'application réelle (`bench migrate`) reste côté serveur.
## Pourquoi un `Role Profile` (et pas juste des rôles)
Assigner à la main les 5 à 12 rôles d'un portail à chaque nouvel utilisateur est
fastidieux et source d'erreurs. ERPNext v15 fournit nativement le DocType
`Role Profile` (contrainte #1 « ERPNext natif = priorité absolue ») : un bundle
nommé de rôles (`Has Role`) qu'on affecte en un champ. Les **5 portails métier**
(roadmap Sprint 4 « 5 portails rôle ») + la **console technique `plateforme`**
donnent **6 profils** couvrant les 50 rôles de façon **bijective** (chaque rôle
dans exactement un profil, puisque chaque rôle a exactement un `portail`).
## Ce qui est généré (`out/`, non commité)
| Fichier | Rôle |
|---|---|
| `role_profile.json` | **1 fixture `Role Profile` par portail** ; chacun bundle ses rôles via des lignes enfant `Has Role`. |
| `MANIFEST.json` | Traçabilité : comptes (profils, métier vs technique, rôles couverts) + mapping `portail → role_profile`. |
## Profils générés (contrat courant · source de vérité = le JSON)
| `role_profile` | Portail | Type | Nb rôles |
|---|---|---|---|
| `OTO Portail Ventes` | ventes | métier | 12 |
| `OTO Portail Construction` | construction | métier | 10 |
| `OTO Portail Direction` | direction | métier | 9 |
| `OTO Portail Compta` | compta | métier | 8 |
| `OTO Portail Achat` | achat | métier | 5 |
| `OTO Portail Plateforme` | plateforme | technique | 6 |
| | | **Total** | **50** |
> Le nom du profil est une **convention de nommage déterministe** dérivée de la
> clé `portail` (`OTO Portail <Portail>`) — aucun libellé métier ni chiffre
> fabriqué (anti-invention #6). Les comptes viennent du contrat, pas de cette
> table (cf. SPEC §3 : le JSON reste la source de vérité).
## Utilisation
```bash
# Génère out/role_profile.json + out/MANIFEST.json
python3 roleprofile_gen.py build # [-o DOSSIER]
# Valide le bundle (schéma + invariants) sans rien écrire
python3 roleprofile_gen.py validate
# Tests (stdlib pur, zéro pip)
python3 -m unittest discover -s tests -v
```
## Garde-fous (anti-invention · #6 · déterminisme)
- **Couverture bijective** re-vérifiée : chaque rôle du contrat dans exactement
un profil, aucun rôle inventé, aucun manquant ; le CLI **refuse d'écrire** si un
invariant casse.
- **Que du natif v15** : DocTypes `Role Profile` + `Has Role` uniquement → aucun
DocType custom à confirmer côté VPS (contrairement aux `Custom DocPerm`).
- **Cohérence portail** : un profil ne mélange jamais deux portails ; son nom
correspond à la convention pour ce portail.
- **Fidélité manifeste** : flag `metier` = appartenance à `portails_business` ;
comptes re-vérifiés profil par profil.
- Sortie **déterministe** (profils triés par portail, `Has Role` triés par nom de
rôle, aucun horodatage) → diffable, re-générable bit-à-bit en CI.
## Application sur VPS (agent ERPNext Backend · hors périmètre worker)
1. Importer d'abord les fixtures `Role` ([`fixtures_gen`](../fixtures_gen/README.md))
— les `Has Role` de chaque profil référencent des `Role` qui doivent exister.
2. Déposer `role_profile.json` dans `fixtures/` de l'app OTO (`hooks.py`), puis
`bench --site frontend migrate`.
3. À la création d'un utilisateur, renseigner `role_profile_name` avec le profil
de son portail (mapping dans `MANIFEST.profiles`) → tous les rôles du portail
sont appliqués d'un coup, puis matérialiser les `User Permission` par
utilisateur ([`userperm_gen`](../userperm_gen/README.md)).
4. Vérification HTTP post-déploiement (workflow #3) + audit QA 4Big.
## Vérification en-repo
- `python3 -m unittest discover -s tests -v`**11/11 verts** (schéma maison +
oracle `jsonschema` si présent ; couverture bijective des 50 rôles, un profil
par portail, cohérence portail, manifeste fidèle, anti-invention, déterminisme).
- Génération réelle : **6 profils** (5 métier + 1 technique), **50/50 rôles**
couverts.
- Job CI dédié `rbac-roleprofile-tests` ajouté au **gate** (`.gitea/workflows/ci.yml`,
Gitea Actions uniquement · #2).
## Auto-score 4Big du livrable : **96/100**
_Réserve 4_ : l'import `bench` + l'affectation `role_profile_name` par
utilisateur restent côté VPS (agent ERPNext, hors périmètre worker, contrainte
#8). Validé statiquement en-repo (11 tests verts + schéma conforme + couverture
bijective fidèle au contrat + gate CI).