[DTP-Worker] Sprint 6 · Générateur Chat OTOIA embarqué par portail (Custom Block natif) (ERPNext Backend · roadmap L63)
CI / Contraintes NON-NÉGOCIABLES (CLAUDE.md) (push) Has been cancelled
CI / Validation JSON (schémas Faisabilité) (push) Has been cancelled
CI / Qualité documentaire (liens + 4Big) (push) Has been cancelled
CI / Publiciste · parser + schéma + generator (unittest) (push) Has been cancelled
CI / RBAC · 50 rôles + schéma (unittest) (push) Has been cancelled
CI / Faisabilité · générateur 4 volets + round-trip (unittest) (push) Has been cancelled
CI / RBAC · fixtures ERPNext (Role + Custom DocPerm) (push) Has been cancelled
CI / RBAC · plan User Permission (row-level) (push) Has been cancelled
CI / RBAC · Role Profile (bundles par portail) (push) Has been cancelled
CI / RBAC · run-book d'application unifié (agrégat 3 volets) (push) Has been cancelled
CI / Faisabilité · dossier bancable trilingue FR/EN/ES (push) Has been cancelled
CI / CRM · workflow vente ERPNext (lead → CONFOTUR) (push) Has been cancelled
CI / CRM · DocType porteur OTO Dossier Vente (push) Has been cancelled
CI / CRM · barème commissions vendeurs (push) Has been cancelled
CI / Fiscal · e-CF DGII (Compupar) (push) Has been cancelled
CI / Frontend · Workspaces 5 portails rôle (push) Has been cancelled
CI / Legal · DocType CONFOTUR Application (push) Has been cancelled
CI / QA · Audit 5D conformité (push) Has been cancelled
CI / SEO · mots-clés trilingues + schema.org + hreflang (push) Has been cancelled
CI / Chat OTOIA · montage par portail (Custom Block) (push) Has been cancelled
CI / E2E baseline Playwright (manuel) (push) Has been cancelled
CI / Gate qualité (agrégat) (push) Has been cancelled

Carrier natif ERPNext v15 (Custom Block) + config runtime par portail.
Ancrage RBAC : roles_allowed/knowledge_scope = surface exacte du portail,
synchronisés avec les Has Role des Workspaces. Endpoint OTOIA null (a_confirmer).
Persona Amélie + capabilities + langues FR/EN/ES sourcés (anti-invention #6).
14 invariants · 31 tests · régression 408 tests verts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude Code DTP Worker
2026-07-30 09:37:51 +00:00
parent 0d3b2420c3
commit 17ad5c0a01
17 changed files with 1712 additions and 1 deletions
@@ -0,0 +1,104 @@
"""Dépendances partagées du générateur Chat OTOIA · réutilisation stricte (#5).
On NE réimplémente rien qui existe déjà dans le mandat :
- `validator` (validateur JSON-Schema draft-07 maison) et `branding` (tokens de
marque #4) sont importés du livrable Publiciste, source unique de vérité.
- la résolution RBAC → (rôles / DocTypes autorisés) par portail est importée du
builder des Workspaces (`frontend/portails/wslib/builder.py`) : le Chat s'ancre
sur EXACTEMENT la même surface RBAC que le portail qui l'héberge — aucune
logique dupliquée, aucune dérive possible.
Entrées consommées (toutes déjà validées/commitées en amont) :
- rbac_50_roles.json (contrat RBAC · rôles + permissions_cibles)
- frontend/portails/portails_spec.json (les 5 portails métier + libellés)
- frontend/portails/out/workspace.json (Has Role réels des Workspaces · hand-off)
- seo/seo_spec.json (langues trilingues FR/EN/ES · source unique)
"""
from __future__ import annotations
import json
import os
import sys
_HERE = os.path.dirname(os.path.abspath(__file__))
_CHAT = os.path.dirname(_HERE) # frontend/chat_otoia/
_FRONTEND = os.path.normpath(os.path.join(_CHAT, "..")) # frontend/
_DELIVERABLES = os.path.normpath(os.path.join(_FRONTEND, "..")) # 05_deliverables_mvp/
_PUBLICISTE = os.path.join(_DELIVERABLES, "publiciste")
_PORTAILS = os.path.join(_FRONTEND, "portails")
_RBAC_DIR = os.path.join(_DELIVERABLES, "rbac")
_SEO = os.path.join(_DELIVERABLES, "seo")
# Réutilisation : validateur + branding Publiciste, builder des Workspaces.
for _p in (_PUBLICISTE, _PORTAILS):
if _p not in sys.path:
sys.path.insert(0, _p)
from lib import branding # type: ignore # noqa: E402
from lib import validator as maison # type: ignore # noqa: E402
from wslib import builder as portails_builder # type: ignore # noqa: E402
# Chemins des contrats/entrées.
SPEC_PATH = os.path.join(_CHAT, "chat_spec.json")
SCHEMA_PATH = os.path.join(_CHAT, "chat.schema.json")
CONTRACT_PATH = os.path.join(_RBAC_DIR, "rbac_50_roles.json")
PORTAILS_SPEC_PATH = os.path.join(_PORTAILS, "portails_spec.json")
WORKSPACE_OUT_PATH = os.path.join(_PORTAILS, "out", "workspace.json")
SEO_SPEC_PATH = os.path.join(_SEO, "seo_spec.json")
def load_json(path: str) -> object:
with open(path, encoding="utf-8") as fh:
return json.load(fh)
def load_spec() -> dict:
return load_json(SPEC_PATH)
def load_contract() -> dict:
return load_json(CONTRACT_PATH)
def load_portails_spec() -> dict:
return load_json(PORTAILS_SPEC_PATH)
def load_workspaces() -> list:
return load_json(WORKSPACE_OUT_PATH)
def load_langs() -> tuple[list[str], str]:
"""Langues + langue par défaut DÉRIVÉES du seo_spec (source unique · #5)."""
seo = load_json(SEO_SPEC_PATH)
site = seo["site"]
return list(site["langs"]), site["default_lang"]
def validate(instance, schema: dict) -> list[str]:
return list(maison.validate(instance, schema))
# Réexports pour le builder (surface RBAC = celle des Workspaces).
roles_by_portail = portails_builder.roles_by_portail
doctypes_by_portail = portails_builder.doctypes_by_portail
__all__ = [
"branding",
"load_json",
"load_spec",
"load_contract",
"load_portails_spec",
"load_workspaces",
"load_langs",
"validate",
"roles_by_portail",
"doctypes_by_portail",
"SPEC_PATH",
"SCHEMA_PATH",
"CONTRACT_PATH",
"PORTAILS_SPEC_PATH",
"WORKSPACE_OUT_PATH",
"SEO_SPEC_PATH",
]