erp-core/docs/04-modelado/database-design/DDL-SPEC-core_rbac.md

29 KiB

DDL Specification: core_rbac Schema

Identificacion

Campo Valor
Schema core_rbac
Modulo MGN-003 Roles/RBAC
Version 1.0
Fecha 2025-12-05
Estado Ready

Diagrama ER

erDiagram
    roles ||--o{ role_permissions : has
    roles ||--o{ user_roles : assigned_to
    permissions ||--o{ role_permissions : granted_to
    users ||--o{ user_roles : has
    tenants ||--o{ roles : owns

    roles {
        uuid id PK
        uuid tenant_id FK
        string name
        string slug
        string description
        boolean is_built_in
        boolean is_active
        timestamp created_at
        timestamp updated_at
        uuid created_by FK
        uuid updated_by FK
        timestamp deleted_at
        uuid deleted_by FK
    }

    permissions {
        uuid id PK
        string code UK
        string name
        string description
        string module
        string parent_code FK
        boolean is_deprecated
        int sort_order
        timestamp created_at
    }

    role_permissions {
        uuid id PK
        uuid role_id FK
        uuid permission_id FK
        timestamp created_at
        uuid created_by FK
    }

    user_roles {
        uuid id PK
        uuid user_id FK
        uuid role_id FK
        timestamp assigned_at
        uuid assigned_by FK
        timestamp expires_at
    }

Tablas

1. roles

Almacena los roles del sistema, tanto built-in como personalizados por tenant.

CREATE TABLE core_rbac.roles (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES core_tenants.tenants(id),
    name VARCHAR(50) NOT NULL,
    slug VARCHAR(50) NOT NULL,
    description VARCHAR(500),
    is_built_in BOOLEAN NOT NULL DEFAULT false,
    is_active BOOLEAN NOT NULL DEFAULT true,
    created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
    created_by UUID REFERENCES core_users.users(id),
    updated_by UUID REFERENCES core_users.users(id),
    deleted_at TIMESTAMPTZ,
    deleted_by UUID REFERENCES core_users.users(id),

    CONSTRAINT uq_roles_tenant_name UNIQUE (tenant_id, name) WHERE deleted_at IS NULL,
    CONSTRAINT uq_roles_tenant_slug UNIQUE (tenant_id, slug) WHERE deleted_at IS NULL,
    CONSTRAINT chk_roles_name_length CHECK (char_length(name) >= 3),
    CONSTRAINT chk_roles_slug_format CHECK (slug ~ '^[a-z0-9_-]+$')
);

-- Indices
CREATE INDEX idx_roles_tenant_id ON core_rbac.roles(tenant_id) WHERE deleted_at IS NULL;
CREATE INDEX idx_roles_slug ON core_rbac.roles(slug) WHERE deleted_at IS NULL;
CREATE INDEX idx_roles_is_built_in ON core_rbac.roles(is_built_in);
CREATE INDEX idx_roles_created_at ON core_rbac.roles(created_at DESC);

-- Trigger para updated_at
CREATE TRIGGER trg_roles_updated_at
    BEFORE UPDATE ON core_rbac.roles
    FOR EACH ROW
    EXECUTE FUNCTION update_updated_at_column();

-- Comentarios
COMMENT ON TABLE core_rbac.roles IS 'Roles del sistema para RBAC';
COMMENT ON COLUMN core_rbac.roles.is_built_in IS 'true para roles del sistema (admin, user, etc.)';
COMMENT ON COLUMN core_rbac.roles.slug IS 'Identificador URL-friendly, unico por tenant';

2. permissions

Catalogo maestro de permisos del sistema. Son globales (no por tenant).

CREATE TABLE core_rbac.permissions (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    code VARCHAR(100) NOT NULL UNIQUE,
    name VARCHAR(100) NOT NULL,
    description VARCHAR(500),
    module VARCHAR(50) NOT NULL,
    parent_code VARCHAR(100) REFERENCES core_rbac.permissions(code),
    is_deprecated BOOLEAN NOT NULL DEFAULT false,
    sort_order INT NOT NULL DEFAULT 0,
    created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,

    CONSTRAINT chk_permissions_code_format CHECK (code ~ '^[a-z]+:[a-z_]+(:?:[a-z_]+)?$|^[a-z]+:\*$')
);

-- Indices
CREATE INDEX idx_permissions_module ON core_rbac.permissions(module);
CREATE INDEX idx_permissions_parent ON core_rbac.permissions(parent_code);
CREATE INDEX idx_permissions_deprecated ON core_rbac.permissions(is_deprecated);

-- Comentarios
COMMENT ON TABLE core_rbac.permissions IS 'Catalogo de permisos del sistema';
COMMENT ON COLUMN core_rbac.permissions.code IS 'Formato: modulo:accion o modulo:recurso:accion';
COMMENT ON COLUMN core_rbac.permissions.parent_code IS 'Para permisos wildcard (users:* -> users:read)';

3. role_permissions

Tabla de union entre roles y permisos (M:N).

CREATE TABLE core_rbac.role_permissions (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    role_id UUID NOT NULL REFERENCES core_rbac.roles(id) ON DELETE CASCADE,
    permission_id UUID NOT NULL REFERENCES core_rbac.permissions(id) ON DELETE CASCADE,
    created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
    created_by UUID REFERENCES core_users.users(id),

    CONSTRAINT uq_role_permissions UNIQUE (role_id, permission_id)
);

-- Indices
CREATE INDEX idx_role_permissions_role ON core_rbac.role_permissions(role_id);
CREATE INDEX idx_role_permissions_permission ON core_rbac.role_permissions(permission_id);

-- Comentarios
COMMENT ON TABLE core_rbac.role_permissions IS 'Asignacion de permisos a roles';

4. user_roles

Tabla de union entre usuarios y roles (M:N).

CREATE TABLE core_rbac.user_roles (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID NOT NULL REFERENCES core_users.users(id) ON DELETE CASCADE,
    role_id UUID NOT NULL REFERENCES core_rbac.roles(id) ON DELETE CASCADE,
    assigned_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
    assigned_by UUID REFERENCES core_users.users(id),
    expires_at TIMESTAMPTZ,

    CONSTRAINT uq_user_roles UNIQUE (user_id, role_id)
);

-- Indices
CREATE INDEX idx_user_roles_user ON core_rbac.user_roles(user_id);
CREATE INDEX idx_user_roles_role ON core_rbac.user_roles(role_id);
CREATE INDEX idx_user_roles_expires ON core_rbac.user_roles(expires_at) WHERE expires_at IS NOT NULL;

-- Comentarios
COMMENT ON TABLE core_rbac.user_roles IS 'Asignacion de roles a usuarios';
COMMENT ON COLUMN core_rbac.user_roles.expires_at IS 'Roles temporales con fecha de expiracion';

Data Seed: Roles Built-in

-- Roles del sistema (se crean para cada tenant nuevo)
-- Ref: Odoo base_groups.xml - group_user, group_portal, group_public, group_system, group_erp_manager
INSERT INTO core_rbac.roles (tenant_id, name, slug, description, is_built_in) VALUES
    (:tenant_id, 'Super Administrador', 'super_admin', 'Acceso total al sistema (Ref: Odoo group_system)', true),
    (:tenant_id, 'Propietario', 'tenant_owner', 'Propietario de la cuenta, gestiona billing y usuarios (MGN-015)', true),
    (:tenant_id, 'Administrador', 'admin', 'Gestion completa del tenant (Ref: Odoo group_erp_manager)', true),
    (:tenant_id, 'Gerente', 'manager', 'Supervision operativa y reportes', true),
    (:tenant_id, 'Usuario', 'user', 'Acceso basico al sistema (Ref: Odoo group_user)', true),
    (:tenant_id, 'Agente WhatsApp', 'whatsapp_agent', 'Atiende conversaciones de WhatsApp (MGN-017)', true),
    (:tenant_id, 'Usuario Portal', 'portal_user', 'Usuario externo con acceso limitado (Ref: Odoo group_portal)', true),
    (:tenant_id, 'Invitado', 'guest', 'Acceso de solo lectura limitado (Ref: Odoo group_public)', true);

Data Seed: Permisos del Sistema

-- Permisos de Auth (MGN-001)
INSERT INTO core_rbac.permissions (code, name, description, module, sort_order) VALUES
    ('auth:sessions:read', 'Ver sesiones', 'Ver sesiones activas del usuario', 'auth', 10),
    ('auth:sessions:revoke', 'Revocar sesiones', 'Cerrar sesiones activas', 'auth', 20);

-- Permisos de Users (MGN-002)
INSERT INTO core_rbac.permissions (code, name, description, module, sort_order) VALUES
    ('users:*', 'Todos los permisos de usuarios', 'Acceso completo a gestion de usuarios', 'users', 0),
    ('users:read', 'Leer usuarios', 'Ver listado y detalle de usuarios', 'users', 10),
    ('users:create', 'Crear usuarios', 'Crear nuevos usuarios', 'users', 20),
    ('users:update', 'Actualizar usuarios', 'Modificar datos de usuarios', 'users', 30),
    ('users:delete', 'Eliminar usuarios', 'Eliminar usuarios (soft delete)', 'users', 40),
    ('users:activate', 'Activar/Desactivar', 'Cambiar estado de usuarios', 'users', 50),
    ('users:export', 'Exportar usuarios', 'Exportar lista de usuarios a CSV', 'users', 60),
    ('users:import', 'Importar usuarios', 'Importar usuarios desde CSV', 'users', 70);

-- Establecer parent para wildcards
UPDATE core_rbac.permissions SET parent_code = 'users:*' WHERE module = 'users' AND code != 'users:*';

-- Permisos de Roles (MGN-003)
INSERT INTO core_rbac.permissions (code, name, description, module, sort_order) VALUES
    ('roles:*', 'Todos los permisos de roles', 'Acceso completo a gestion de roles', 'roles', 0),
    ('roles:read', 'Leer roles', 'Ver listado y detalle de roles', 'roles', 10),
    ('roles:create', 'Crear roles', 'Crear nuevos roles personalizados', 'roles', 20),
    ('roles:update', 'Actualizar roles', 'Modificar roles existentes', 'roles', 30),
    ('roles:delete', 'Eliminar roles', 'Eliminar roles personalizados', 'roles', 40),
    ('roles:assign', 'Asignar roles', 'Asignar roles a usuarios', 'roles', 50),
    ('permissions:read', 'Ver permisos', 'Ver catalogo de permisos', 'roles', 60);

UPDATE core_rbac.permissions SET parent_code = 'roles:*' WHERE module = 'roles' AND code NOT IN ('roles:*', 'permissions:read');

-- Permisos de Tenants (MGN-004)
INSERT INTO core_rbac.permissions (code, name, description, module, sort_order) VALUES
    ('tenants:read', 'Ver tenant', 'Ver configuracion del tenant', 'tenants', 10),
    ('tenants:update', 'Actualizar tenant', 'Modificar configuracion del tenant', 'tenants', 20),
    ('tenants:billing', 'Facturacion', 'Gestionar facturacion del tenant', 'tenants', 30);

-- Permisos de Settings (MGN-006)
INSERT INTO core_rbac.permissions (code, name, description, module, sort_order) VALUES
    ('settings:read', 'Ver configuracion', 'Ver configuracion del sistema', 'settings', 10),
    ('settings:update', 'Modificar configuracion', 'Cambiar configuracion del sistema', 'settings', 20);

-- Permisos de Audit (MGN-007)
INSERT INTO core_rbac.permissions (code, name, description, module, sort_order) VALUES
    ('audit:read', 'Ver auditoria', 'Ver logs de auditoria', 'audit', 10),
    ('audit:export', 'Exportar auditoria', 'Exportar logs de auditoria', 'audit', 20);

-- Permisos de Reports (MGN-009)
INSERT INTO core_rbac.permissions (code, name, description, module, sort_order) VALUES
    ('reports:*', 'Todos los permisos de reportes', 'Acceso completo a reportes', 'reports', 0),
    ('reports:read', 'Ver reportes', 'Ver reportes del sistema', 'reports', 10),
    ('reports:create', 'Crear reportes', 'Crear reportes personalizados', 'reports', 20),
    ('reports:export', 'Exportar reportes', 'Exportar reportes a PDF/Excel', 'reports', 30),
    ('reports:schedule', 'Programar reportes', 'Programar envio de reportes', 'reports', 40);

UPDATE core_rbac.permissions SET parent_code = 'reports:*' WHERE module = 'reports' AND code != 'reports:*';

-- Permisos de Financial (MGN-010)
INSERT INTO core_rbac.permissions (code, name, description, module, sort_order) VALUES
    ('financial:*', 'Todos los permisos financieros', 'Acceso completo a modulo financiero', 'financial', 0),
    ('financial:accounts:read', 'Ver cuentas', 'Ver plan de cuentas', 'financial', 10),
    ('financial:accounts:manage', 'Gestionar cuentas', 'Administrar plan de cuentas', 'financial', 20),
    ('financial:transactions:read', 'Ver transacciones', 'Ver movimientos contables', 'financial', 30),
    ('financial:transactions:create', 'Crear transacciones', 'Registrar movimientos', 'financial', 40),
    ('financial:transactions:approve', 'Aprobar transacciones', 'Aprobar movimientos pendientes', 'financial', 50),
    ('financial:reports:read', 'Reportes financieros', 'Ver reportes financieros', 'financial', 60);

UPDATE core_rbac.permissions SET parent_code = 'financial:*' WHERE module = 'financial' AND code != 'financial:*';

-- Permisos de Inventory (MGN-011)
INSERT INTO core_rbac.permissions (code, name, description, module, sort_order) VALUES
    ('inventory:*', 'Todos los permisos de inventario', 'Acceso completo a inventario', 'inventory', 0),
    ('inventory:products:read', 'Ver productos', 'Ver catalogo de productos', 'inventory', 10),
    ('inventory:products:create', 'Crear productos', 'Agregar productos al catalogo', 'inventory', 20),
    ('inventory:products:update', 'Actualizar productos', 'Modificar productos', 'inventory', 30),
    ('inventory:products:delete', 'Eliminar productos', 'Eliminar productos del catalogo', 'inventory', 40),
    ('inventory:stock:read', 'Ver stock', 'Ver niveles de inventario', 'inventory', 50),
    ('inventory:stock:adjust', 'Ajustar stock', 'Realizar ajustes de inventario', 'inventory', 60),
    ('inventory:movements:read', 'Ver movimientos', 'Ver historial de movimientos', 'inventory', 70),
    ('inventory:movements:create', 'Crear movimientos', 'Registrar entradas/salidas', 'inventory', 80);

UPDATE core_rbac.permissions SET parent_code = 'inventory:*' WHERE module = 'inventory' AND code != 'inventory:*';

-- Permisos de Portal (para usuarios externos) - Ref: Odoo portal module
INSERT INTO core_rbac.permissions (code, name, description, module, sort_order) VALUES
    ('portal:profile:read', 'Ver perfil propio', 'Ver datos del propio perfil', 'portal', 10),
    ('portal:profile:update', 'Actualizar perfil propio', 'Modificar datos del propio perfil', 'portal', 20),
    ('portal:documents:read', 'Ver documentos propios', 'Ver documentos asociados al usuario', 'portal', 30),
    ('portal:orders:read', 'Ver ordenes propias', 'Ver historial de ordenes', 'portal', 40),
    ('portal:invoices:read', 'Ver facturas propias', 'Ver facturas emitidas', 'portal', 50),
    ('portal:tickets:read', 'Ver tickets propios', 'Ver tickets de soporte', 'portal', 60),
    ('portal:tickets:create', 'Crear tickets', 'Crear nuevos tickets de soporte', 'portal', 70);

-- Permisos de Billing (MGN-015)
INSERT INTO core_rbac.permissions (code, name, description, module, sort_order) VALUES
    ('billing:*', 'Todos los permisos de billing', 'Acceso completo a facturacion SaaS', 'billing', 0),
    ('billing:subscription:read', 'Ver suscripcion', 'Ver plan y suscripcion actual', 'billing', 10),
    ('billing:subscription:manage', 'Gestionar suscripcion', 'Cambiar plan, cancelar, reactivar', 'billing', 20),
    ('billing:seats:manage', 'Gestionar asientos', 'Agregar/remover usuarios del plan', 'billing', 30),
    ('billing:payment_methods:read', 'Ver metodos de pago', 'Ver tarjetas y metodos guardados', 'billing', 40),
    ('billing:payment_methods:manage', 'Gestionar metodos de pago', 'Agregar/eliminar metodos de pago', 'billing', 50),
    ('billing:invoices:read', 'Ver facturas SaaS', 'Ver historial de facturas de la plataforma', 'billing', 60),
    ('billing:invoices:download', 'Descargar facturas', 'Descargar PDF/CFDI de facturas', 'billing', 70),
    ('billing:usage:read', 'Ver uso', 'Ver metricas de consumo del plan', 'billing', 80);

UPDATE core_rbac.permissions SET parent_code = 'billing:*' WHERE module = 'billing' AND code != 'billing:*';

-- Permisos de Payment Integrations (MGN-016)
INSERT INTO core_rbac.permissions (code, name, description, module, sort_order) VALUES
    ('payments:*', 'Todos los permisos de pagos', 'Acceso completo a integraciones de pago', 'payments', 0),
    ('payments:providers:read', 'Ver proveedores', 'Ver integraciones de pago configuradas', 'payments', 10),
    ('payments:providers:configure', 'Configurar proveedores', 'Conectar MercadoPago, Clip, Stripe', 'payments', 20),
    ('payments:terminals:read', 'Ver terminales', 'Ver terminales registradas', 'payments', 30),
    ('payments:terminals:manage', 'Gestionar terminales', 'Agregar/configurar terminales', 'payments', 40),
    ('payments:transactions:read', 'Ver transacciones', 'Ver historial de cobros', 'payments', 50),
    ('payments:transactions:process', 'Procesar pagos', 'Cobrar en terminal/gateway', 'payments', 60),
    ('payments:transactions:refund', 'Reembolsar', 'Procesar devoluciones', 'payments', 70),
    ('payments:reconciliation:read', 'Ver conciliacion', 'Ver estado de conciliacion', 'payments', 80),
    ('payments:reconciliation:manage', 'Gestionar conciliacion', 'Conciliar transacciones', 'payments', 90);

UPDATE core_rbac.permissions SET parent_code = 'payments:*' WHERE module = 'payments' AND code != 'payments:*';

-- Permisos de WhatsApp (MGN-017)
INSERT INTO core_rbac.permissions (code, name, description, module, sort_order) VALUES
    ('whatsapp:*', 'Todos los permisos de WhatsApp', 'Acceso completo a WhatsApp Business', 'whatsapp', 0),
    ('whatsapp:accounts:read', 'Ver cuentas', 'Ver cuentas de WhatsApp conectadas', 'whatsapp', 10),
    ('whatsapp:accounts:configure', 'Configurar cuentas', 'Conectar/desconectar cuentas WA', 'whatsapp', 20),
    ('whatsapp:templates:read', 'Ver templates', 'Ver plantillas de mensajes', 'whatsapp', 30),
    ('whatsapp:templates:manage', 'Gestionar templates', 'Crear/editar plantillas', 'whatsapp', 40),
    ('whatsapp:conversations:read', 'Ver conversaciones', 'Ver inbox de mensajes', 'whatsapp', 50),
    ('whatsapp:conversations:reply', 'Responder mensajes', 'Enviar mensajes en conversaciones', 'whatsapp', 60),
    ('whatsapp:conversations:assign', 'Asignar conversaciones', 'Asignar a otros agentes', 'whatsapp', 70),
    ('whatsapp:bulk:send', 'Envio masivo', 'Enviar campanas de marketing', 'whatsapp', 80),
    ('whatsapp:chatbot:read', 'Ver chatbot', 'Ver flujos de chatbot', 'whatsapp', 90),
    ('whatsapp:chatbot:manage', 'Gestionar chatbot', 'Crear/editar flujos', 'whatsapp', 100),
    ('whatsapp:analytics:read', 'Ver analiticas', 'Ver metricas de WhatsApp', 'whatsapp', 110);

UPDATE core_rbac.permissions SET parent_code = 'whatsapp:*' WHERE module = 'whatsapp' AND code != 'whatsapp:*';

-- Permisos de AI Agents (MGN-018)
INSERT INTO core_rbac.permissions (code, name, description, module, sort_order) VALUES
    ('ai_agents:*', 'Todos los permisos de IA', 'Acceso completo a agentes de IA', 'ai_agents', 0),
    ('ai_agents:agents:read', 'Ver agentes', 'Ver agentes configurados', 'ai_agents', 10),
    ('ai_agents:agents:manage', 'Gestionar agentes', 'Crear/editar/eliminar agentes', 'ai_agents', 20),
    ('ai_agents:kb:read', 'Ver bases de conocimiento', 'Ver KBs del tenant', 'ai_agents', 30),
    ('ai_agents:kb:manage', 'Gestionar KB', 'Crear/editar KBs y documentos', 'ai_agents', 40),
    ('ai_agents:kb:upload', 'Subir documentos', 'Agregar documentos a KB', 'ai_agents', 50),
    ('ai_agents:tools:read', 'Ver herramientas', 'Ver tools disponibles', 'ai_agents', 60),
    ('ai_agents:tools:manage', 'Gestionar herramientas', 'Configurar tools para agentes', 'ai_agents', 70),
    ('ai_agents:conversations:read', 'Ver conversaciones IA', 'Ver historial de chats con IA', 'ai_agents', 80),
    ('ai_agents:feedback:read', 'Ver feedback', 'Ver calificaciones de respuestas', 'ai_agents', 90),
    ('ai_agents:feedback:manage', 'Gestionar feedback', 'Corregir/entrenar agente', 'ai_agents', 100),
    ('ai_agents:analytics:read', 'Ver analiticas IA', 'Ver metricas de agentes', 'ai_agents', 110),
    ('ai_agents:usage:read', 'Ver consumo tokens', 'Ver uso de tokens de IA', 'ai_agents', 120);

UPDATE core_rbac.permissions SET parent_code = 'ai_agents:*' WHERE module = 'ai_agents' AND code != 'ai_agents:*';

Data Seed: Permisos por Rol Built-in

-- Funcion helper para asignar permisos
CREATE OR REPLACE FUNCTION core_rbac.assign_permissions_to_role(
    p_tenant_id UUID,
    p_role_slug VARCHAR,
    p_permission_codes TEXT[]
) RETURNS void AS $$
DECLARE
    v_role_id UUID;
    v_permission_id UUID;
    v_code TEXT;
BEGIN
    SELECT id INTO v_role_id FROM core_rbac.roles
    WHERE tenant_id = p_tenant_id AND slug = p_role_slug;

    FOREACH v_code IN ARRAY p_permission_codes LOOP
        SELECT id INTO v_permission_id FROM core_rbac.permissions WHERE code = v_code;
        IF v_permission_id IS NOT NULL THEN
            INSERT INTO core_rbac.role_permissions (role_id, permission_id)
            VALUES (v_role_id, v_permission_id)
            ON CONFLICT DO NOTHING;
        END IF;
    END LOOP;
END;
$$ LANGUAGE plpgsql;

-- Super Admin: Todos los permisos (wildcard)
SELECT core_rbac.assign_permissions_to_role(:tenant_id, 'super_admin', ARRAY[
    'users:*', 'roles:*', 'permissions:read', 'tenants:read', 'tenants:update', 'tenants:billing',
    'settings:read', 'settings:update', 'audit:read', 'audit:export',
    'reports:*', 'financial:*', 'inventory:*',
    'billing:*', 'payments:*', 'whatsapp:*', 'ai_agents:*'
]);

-- Tenant Owner: Propietario de cuenta (MGN-015)
SELECT core_rbac.assign_permissions_to_role(:tenant_id, 'tenant_owner', ARRAY[
    'users:*', 'roles:read', 'roles:assign',
    'tenants:read', 'tenants:update', 'tenants:billing',
    'billing:*',
    'payments:providers:read', 'payments:providers:configure',
    'whatsapp:accounts:read', 'whatsapp:accounts:configure', 'whatsapp:analytics:read',
    'ai_agents:agents:read', 'ai_agents:usage:read', 'ai_agents:analytics:read'
]);

-- Admin: Gestion del tenant
SELECT core_rbac.assign_permissions_to_role(:tenant_id, 'admin', ARRAY[
    'users:*', 'roles:read', 'roles:create', 'roles:update', 'roles:delete', 'roles:assign',
    'permissions:read', 'tenants:read', 'tenants:update',
    'settings:read', 'settings:update', 'audit:read',
    'reports:read', 'reports:export',
    'payments:*', 'whatsapp:*', 'ai_agents:*'
]);

-- Manager: Supervision
SELECT core_rbac.assign_permissions_to_role(:tenant_id, 'manager', ARRAY[
    'users:read', 'roles:read', 'permissions:read',
    'audit:read', 'reports:read', 'reports:export',
    'financial:accounts:read', 'financial:transactions:read', 'financial:reports:read',
    'inventory:products:read', 'inventory:stock:read', 'inventory:movements:read',
    'payments:transactions:read', 'payments:reconciliation:read',
    'whatsapp:conversations:read', 'whatsapp:analytics:read',
    'ai_agents:conversations:read', 'ai_agents:feedback:read', 'ai_agents:analytics:read'
]);

-- User: Acceso basico
SELECT core_rbac.assign_permissions_to_role(:tenant_id, 'user', ARRAY[
    'reports:read',
    'payments:transactions:read'
]);

-- WhatsApp Agent: Atiende conversaciones (MGN-017)
SELECT core_rbac.assign_permissions_to_role(:tenant_id, 'whatsapp_agent', ARRAY[
    'whatsapp:conversations:read', 'whatsapp:conversations:reply',
    'whatsapp:templates:read'
]);

-- Portal User: Acceso limitado para usuarios externos (Ref: Odoo group_portal)
SELECT core_rbac.assign_permissions_to_role(:tenant_id, 'portal_user', ARRAY[
    'portal:profile:read', 'portal:profile:update',
    'portal:documents:read', 'portal:orders:read',
    'portal:invoices:read', 'portal:tickets:read', 'portal:tickets:create'
]);

-- Guest: Solo lectura minima (Ref: Odoo group_public)
SELECT core_rbac.assign_permissions_to_role(:tenant_id, 'guest', ARRAY[
    -- Sin permisos especificos, solo acceso a dashboard publico
]);

Row Level Security (RLS)

-- Habilitar RLS
ALTER TABLE core_rbac.roles ENABLE ROW LEVEL SECURITY;
ALTER TABLE core_rbac.role_permissions ENABLE ROW LEVEL SECURITY;
ALTER TABLE core_rbac.user_roles ENABLE ROW LEVEL SECURITY;

-- Politicas para roles
CREATE POLICY roles_tenant_isolation ON core_rbac.roles
    FOR ALL
    USING (tenant_id = current_setting('app.tenant_id')::uuid);

CREATE POLICY roles_select_built_in ON core_rbac.roles
    FOR SELECT
    USING (is_built_in = true);

-- Politicas para role_permissions (via rol)
CREATE POLICY role_permissions_tenant_isolation ON core_rbac.role_permissions
    FOR ALL
    USING (
        role_id IN (
            SELECT id FROM core_rbac.roles
            WHERE tenant_id = current_setting('app.tenant_id')::uuid
        )
    );

-- Politicas para user_roles (via usuario)
CREATE POLICY user_roles_tenant_isolation ON core_rbac.user_roles
    FOR ALL
    USING (
        user_id IN (
            SELECT id FROM core_users.users
            WHERE tenant_id = current_setting('app.tenant_id')::uuid
        )
    );

Funciones de Utilidad

Verificar Permiso de Usuario

CREATE OR REPLACE FUNCTION core_rbac.user_has_permission(
    p_user_id UUID,
    p_permission_code VARCHAR
) RETURNS BOOLEAN AS $$
DECLARE
    v_has_permission BOOLEAN;
    v_module VARCHAR;
BEGIN
    -- Verificar permiso directo
    SELECT EXISTS (
        SELECT 1
        FROM core_rbac.user_roles ur
        JOIN core_rbac.role_permissions rp ON rp.role_id = ur.role_id
        JOIN core_rbac.permissions p ON p.id = rp.permission_id
        WHERE ur.user_id = p_user_id
        AND (ur.expires_at IS NULL OR ur.expires_at > NOW())
        AND p.code = p_permission_code
    ) INTO v_has_permission;

    IF v_has_permission THEN
        RETURN true;
    END IF;

    -- Verificar wildcard del modulo
    v_module := split_part(p_permission_code, ':', 1);

    SELECT EXISTS (
        SELECT 1
        FROM core_rbac.user_roles ur
        JOIN core_rbac.role_permissions rp ON rp.role_id = ur.role_id
        JOIN core_rbac.permissions p ON p.id = rp.permission_id
        WHERE ur.user_id = p_user_id
        AND (ur.expires_at IS NULL OR ur.expires_at > NOW())
        AND p.code = v_module || ':*'
    ) INTO v_has_permission;

    RETURN v_has_permission;
END;
$$ LANGUAGE plpgsql STABLE;

Obtener Permisos Efectivos de Usuario

CREATE OR REPLACE FUNCTION core_rbac.get_user_permissions(
    p_user_id UUID
) RETURNS TABLE (permission_code VARCHAR, source_role VARCHAR) AS $$
BEGIN
    RETURN QUERY
    SELECT DISTINCT p.code, r.name
    FROM core_rbac.user_roles ur
    JOIN core_rbac.roles r ON r.id = ur.role_id
    JOIN core_rbac.role_permissions rp ON rp.role_id = r.id
    JOIN core_rbac.permissions p ON p.id = rp.permission_id
    WHERE ur.user_id = p_user_id
    AND (ur.expires_at IS NULL OR ur.expires_at > NOW())
    AND r.is_active = true
    AND p.is_deprecated = false
    ORDER BY p.code;
END;
$$ LANGUAGE plpgsql STABLE;

Vistas

Vista: Roles con Conteo de Usuarios

CREATE VIEW core_rbac.vw_roles_summary AS
SELECT
    r.id,
    r.tenant_id,
    r.name,
    r.slug,
    r.description,
    r.is_built_in,
    r.is_active,
    COUNT(DISTINCT ur.user_id) AS users_count,
    COUNT(DISTINCT rp.permission_id) AS permissions_count,
    r.created_at
FROM core_rbac.roles r
LEFT JOIN core_rbac.user_roles ur ON ur.role_id = r.id
LEFT JOIN core_rbac.role_permissions rp ON rp.role_id = r.id
WHERE r.deleted_at IS NULL
GROUP BY r.id;

Vista: Permisos Agrupados por Modulo

CREATE VIEW core_rbac.vw_permissions_by_module AS
SELECT
    module,
    jsonb_agg(
        jsonb_build_object(
            'id', id,
            'code', code,
            'name', name,
            'description', description,
            'isDeprecated', is_deprecated
        ) ORDER BY sort_order
    ) AS permissions
FROM core_rbac.permissions
WHERE is_deprecated = false
GROUP BY module
ORDER BY module;

Resumen de Tablas

Tabla Columnas Descripcion
roles 12 Roles del sistema (built-in y custom)
permissions 9 Catalogo maestro de permisos
role_permissions 5 Union M:N roles-permisos
user_roles 6 Union M:N usuarios-roles

Total: 4 tablas, 32 columnas


Resumen de Roles Built-in

Rol Slug Descripcion
Super Administrador super_admin Acceso total al sistema
Propietario tenant_owner Gestiona billing y usuarios (MGN-015)
Administrador admin Gestion completa del tenant
Gerente manager Supervision operativa y reportes
Usuario user Acceso basico al sistema
Agente WhatsApp whatsapp_agent Atiende conversaciones (MGN-017)
Usuario Portal portal_user Usuario externo con acceso limitado
Invitado guest Solo lectura limitada

Resumen de Permisos por Modulo

Modulo Permisos Descripcion
auth 2 Sesiones y tokens
users 8 Gestion de usuarios
roles 7 Roles y permisos
tenants 3 Configuracion tenant
settings 2 Configuracion sistema
audit 2 Logs de auditoria
reports 5 Reportes
financial 7 Modulo financiero
inventory 9 Inventario
portal 7 Usuarios externos
billing 9 Billing SaaS (MGN-015)
payments 10 Pagos POS (MGN-016)
whatsapp 12 WhatsApp Business (MGN-017)
ai_agents 13 AI Agents (MGN-018)

Total: 96 permisos


Historial

Version Fecha Autor Cambios
1.0 2025-12-05 System Creacion inicial
1.1 2025-12-05 System Agregar rol portal_user y permisos portal:* (Ref: Odoo group_portal)
1.2 2025-12-05 System Roles: tenant_owner, whatsapp_agent. Permisos: billing:, payments:, whatsapp:, ai_agents: (MGN-015/016/017/018)