35a20247f8
DocType custom porteur des dossiers d'incitation touristique CONFOTUR (Ley 158-01, RD), référencé par RBAC (3 rôles) et le workflow vente mais jamais généré. Permissions = permissions_cibles RBAC mot pour mot ; is_submittable déduit de l'action submit ; estado/dossier_vente dérivés du workflow ; aucun taux/loi/montant fabriqué (#6). 14 invariants, 44 tests, hand-off dans out/. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
58 lines
2.2 KiB
Python
58 lines
2.2 KiB
Python
"""Lecture des roles RBAC qui referencent un DocType donne (source unique · #6).
|
|
|
|
Le contrat `rbac_50_roles.json` est l'UNIQUE source de verite des roles ERPNext
|
|
(valide par `rbac.schema.json` dans le job CI `rbac-tests`). Ici on ne fait que
|
|
le LIRE : pour le DocType `CONFOTUR Application`, quels roles y ont acces et avec
|
|
quelles actions. Ces `permissions_cibles` deviennent, mot pour mot, les DocPerm
|
|
du DocType genere -> les permissions du fixture SONT le contrat RBAC, pas une
|
|
reinvention.
|
|
|
|
Reutilisation (workflow #5, zero duplication) : la resolution `id -> nom Frappe`
|
|
et le `portail` restent delegues au `RoleResolver` du module CRM voisin — on ne
|
|
redefinit ni le format des roles ni leur nommage.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
from typing import Any
|
|
|
|
_HERE = os.path.dirname(os.path.abspath(__file__))
|
|
_DELIVERABLES = os.path.normpath(os.path.join(_HERE, "..", "..", ".."))
|
|
RBAC_CONTRACT_PATH = os.path.join(_DELIVERABLES, "rbac", "rbac_50_roles.json")
|
|
|
|
|
|
def load_contract(path: str = RBAC_CONTRACT_PATH) -> dict[str, Any]:
|
|
with open(path, encoding="utf-8") as fh:
|
|
return json.load(fh)
|
|
|
|
|
|
def roles_targeting(doctype: str, contract: dict[str, Any]) -> list[dict[str, Any]]:
|
|
"""Roles dont un `permissions_cibles` vise `doctype`.
|
|
|
|
Retour trie par `id` de role (determinisme). Chaque element :
|
|
{role_id, actions: [..], custom: bool}
|
|
`actions` reprend, sans ajout ni retrait, la liste declaree dans le contrat.
|
|
Leve si un meme role declare deux fois le meme doctype (contrat ambigu).
|
|
"""
|
|
out: list[dict[str, Any]] = []
|
|
for role in contract.get("roles", []):
|
|
matches = [pc for pc in role.get("permissions_cibles", [])
|
|
if pc.get("doctype") == doctype]
|
|
if not matches:
|
|
continue
|
|
if len(matches) > 1:
|
|
raise ValueError(
|
|
f"Role {role.get('id')!r} declare {doctype!r} plusieurs fois "
|
|
f"dans permissions_cibles (contrat ambigu)."
|
|
)
|
|
pc = matches[0]
|
|
out.append({
|
|
"role_id": role["id"],
|
|
"actions": list(pc.get("actions", [])),
|
|
"custom": bool(pc.get("custom", False)),
|
|
})
|
|
out.sort(key=lambda r: r["role_id"])
|
|
return out
|