# Data Model — Spec 027 RBAC por Função ## Enum `desk_role` (expansão) ```text # Ops (Spec 003) super_admin | ops_lead | technician | noc # Comercial (Spec 027 — Roger) sales_admin | sales_support # Negócio / plataforma (Spec 027) finance | marketing | seo | developer | devops | security_analyst | content_editor | agentic_operator # Sistema api_service | agent_system ``` **Removido:** `sales` (genérico) → usar `sales_admin` ou `sales_support`. ## Hierarquia UI (sem herança automática) ```text super_admin ├── ops_lead → technician ├── sales_admin → sales_support ├── finance ├── marketing → seo, content_editor ├── developer ├── devops ├── security_analyst ├── agentic_operator └── noc (paralelo, read-only) ``` ## Tabela `desk_users` (alteração) | Coluna | Tipo | Notas | |--------|------|-------| | `role` | TEXT | CHECK contra enum expandido | | `function_modules` | JSON | override opcional — senão default da função | | `vm123_provisioned` | JSON | `{foss_staff_id, odoo_uid, openpanel}` — Fase 3 | ## Tabela `role_module_defaults` (Fase 2) | Coluna | Tipo | |--------|------| | `role` | TEXT PK | | `modules_json` | JSON array | | `updated_at` | TEXT ISO8601 | ## Tabela `vm123_identity_map` (Fase 3 — nova) | Coluna | Tipo | |--------|------| | `desk_username` | TEXT PK | | `foss_staff_id` | INTEGER NULL | | `odoo_uid` | INTEGER NULL | | `openpanel_username` | TEXT NULL | | `provisioned_at` | TEXT | ## Permission helpers (novos) ```python SALES_ROLES = frozenset({"sales_admin", "sales_support"}) BUSINESS_ROLES = frozenset({"finance", "marketing", "seo", "developer", "sales_admin", "sales_support"}) def can_validate_billing(role: str) -> bool: return role in ("super_admin", "ops_lead", "finance", "sales_admin") def can_create_foss_order(role: str) -> bool: return role in ("super_admin", "ops_lead", "finance", "sales_admin", "sales_support") def can_access_foss_admin(role: str) -> bool: return role in ("super_admin", "finance", "sales_admin") def can_access_openadmin(role: str) -> bool: return role in ("super_admin", "devops", "sales_admin") def can_openpanel_autologin(role: str) -> bool: return role in ("super_admin", "sales_admin", "sales_support", "marketing", "seo", "content_editor", "technician") ``` ## Mapeamento externo VM123 Ver [`contracts/vm123-product-roles.md`](contracts/vm123-product-roles.md) — tabela mestre Desk → FOSS group → Odoo → OpenPanel.