Configurar Permisos
Exportar contenido
Ajusta el acceso del asesor a cada sección del negocio.
Para invitaciones pendientes
Sección titulada «Para invitaciones pendientes»- Ve a Configuración → Asesores → Enviadas.
- Localiza la invitación con estado “Pendiente”.
- Clic en el icono de “Configuración” (engranaje) — se abre el modal.
- Ajusta los permisos por sección.
- Clic en “Guardar” — permisos preconfigurados actualizados.
Nota: Los permisos se aplicarán cuando el asesor acepte la invitación.
Para asesores activos
Sección titulada «Para asesores activos»- Ve a Configuración → Asesores → Activos.
- Localiza el asesor activo.
- Clic en el icono de “Configuración” (engranaje) — se abre el modal.
- Ajusta los permisos por sección.
- Clic en “Guardar” — cambios aplicados inmediatamente.
Opciones de configuración
Sección titulada «Opciones de configuración»Cada sección tiene tres niveles de acceso:
| Nivel | Icono | Capabilities | Ejemplo de uso |
|---|---|---|---|
| Ninguno | 🚫 | No ve la sección en su panel | Asesor no necesita ver Proyectos |
| Lectura | 👁️ | Ver, exportar, descargar | Solo consulta de Facturas emitidas |
| Edición | ✏️ | Ver, crear, editar, eliminar | Gestión completa de Gastos |
Configuración recomendada por tipo de asesor:
Asesor fiscal (declaraciones trimestrales)
Sección titulada «Asesor fiscal (declaraciones trimestrales)»✓ Facturas → Lectura (consulta para declaraciones)✓ Gastos → Lectura (consulta para deducciones)✓ Tickets → Ninguno (no relevante)✓ Clientes → Lectura (información de clientes)✓ Reportes → Lectura (consulta de informes fiscales)✗ Proyectos → Ninguno✗ Tareas → NingunoGestor contable (contabilidad mensual)
Sección titulada «Gestor contable (contabilidad mensual)»✓ Facturas → Edición (gestión completa)✓ Gastos → Edición (registro de gastos)✓ Tickets → Edición (gestión de tickets)✓ Productos → Edición (actualización de catálogo)✓ Clientes → Edición (gestión de clientes)✓ Reportes → Lectura (consulta de informes)✗ Proyectos → Ninguno✗ Tareas → NingunoCómo sé que lo hice bien
Sección titulada «Cómo sé que lo hice bien»Invitación pendiente
Sección titulada «Invitación pendiente»- Toast: “Permisos pre-configurados guardados”.
- Icono de engranaje muestra que hay permisos configurados.
- Al aceptar, asesor tendrá estos permisos.
Asesor activo
Sección titulada «Asesor activo»- Toast: “Permisos actualizados correctamente”.
- Cambios aplicados inmediatamente.
- Asesor ve cambios en su próximo acceso (o al refrescar).
Errores comunes
Sección titulada «Errores comunes»| Mensaje | Causa | Solución |
|---|---|---|
| ”No se encontró el asesor” | Asesor eliminado o desvinculado | Verifica en pestaña “Activos" |
| "Error al guardar los permisos” | Error de conexión o servidor | Reintenta o contacta soporte |
Detalles técnicos — Configurar Permisos
Sección titulada «Detalles técnicos — Configurar Permisos»Ver implementación técnica
Para invitaciones pendientes:
// Actualiza campo permissions en advisor_invitationsconst { error } = await supabase .from("advisor_invitations") .update({ permissions: permissions, metadata: { configured_permissions: permissions } }) .eq("id", invitationId);Para asesores activos:
// 1. Obtiene advisor_id (desde invitación o RPC)let advisorId = selectedInvitation.advisor_id;
if (!advisorId && selectedInvitation.email) { const { data: rpcAdvisorId } = await supabase.rpc( "get_advisor_id_by_email", { p_email: selectedInvitation.email } ); advisorId = rpcAdvisorId;}
// 2. Elimina permisos existentesawait supabase .from("advisor_section_permissions") .delete() .eq("advisor_id", advisorId) .eq("client_business_id", businessId);
// 3. Inserta nuevos permisosconst permissionsToInsert = permissions.map(p => ({ advisor_id: advisorId, client_business_id: businessId, section: p.section, access_level: p.access}));
await supabase .from("advisor_section_permissions") .insert(permissionsToInsert);Tabla: advisor_section_permissions
{ id: UUID; // Auto-generado advisor_id: UUID; // FK a auth.users client_business_id: UUID; // FK a businesses section: string; // invoices, expenses, etc. access_level: 'none' | 'read' | 'edit'; created_at: TIMESTAMPTZ; // NOW() updated_at: TIMESTAMPTZ; // NOW()}Constraint de secciones válidas:
CONSTRAINT advisor_section_permissions_section_checkCHECK (section IN ( 'invoices', 'tickets', 'quotes', 'customers', 'products', 'expenses', 'projects', 'tasks', 'reports'))Constraint de niveles de acceso:
CONSTRAINT advisor_section_permissions_access_level_checkCHECK (access_level IN ('none', 'read', 'edit'))Query de diagnóstico:
-- Permisos actuales de un asesorSELECT asp.section, asp.access_level, asp.updated_at, b.name as business_nameFROM advisor_section_permissions aspJOIN businesses b ON asp.client_business_id = b.idWHERE asp.advisor_id = '<advisor_id>' AND asp.client_business_id = '<business_id>'ORDER BY asp.section;
-- Comparar permisos preconfigurados vs aplicadosSELECT ai.email, ai.permissions as preconfig_permissions, COALESCE( json_agg( json_build_object( 'section', asp.section, 'access', asp.access_level ) ) FILTER (WHERE asp.id IS NOT NULL), '[]' ) as applied_permissionsFROM advisor_invitations aiLEFT JOIN profiles p ON ai.email = p.emailLEFT JOIN advisor_section_permissions asp ON asp.advisor_id = p.user_id AND asp.client_business_id = ai.business_idWHERE ai.id = '<invitation_id>'GROUP BY ai.email, ai.permissions;Función RPC para resolver advisor_id:
CREATE OR REPLACE FUNCTION get_advisor_id_by_email(p_email TEXT)RETURNS UUIDLANGUAGE plpgsqlSECURITY DEFINERAS $$DECLARE v_advisor_id UUID;BEGIN SELECT user_id INTO v_advisor_id FROM profiles WHERE email = p_email LIMIT 1;
RETURN v_advisor_id;END;$$;