[DTP-Worker] Sprint 5 · Générateur DocType CONFOTUR Application (ONAPI/Legal · roadmap L55)
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>
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user