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