# 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/`, commité · byte-déterministe)

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