Ir al contenido

Configurar Permisos

Ajusta el acceso del asesor a cada sección del negocio.

  1. Ve a Configuración → Asesores → Enviadas.
  2. Localiza la invitación con estado “Pendiente”.
  3. Clic en el icono de “Configuración” (engranaje) — se abre el modal.
  4. Ajusta los permisos por sección.
  5. Clic en “Guardar” — permisos preconfigurados actualizados.

Nota: Los permisos se aplicarán cuando el asesor acepte la invitación.

  1. Ve a Configuración → Asesores → Activos.
  2. Localiza el asesor activo.
  3. Clic en el icono de “Configuración” (engranaje) — se abre el modal.
  4. Ajusta los permisos por sección.
  5. Clic en “Guardar” — cambios aplicados inmediatamente.

Cada sección tiene tres niveles de acceso:

NivelIconoCapabilitiesEjemplo de uso
Ninguno🚫No ve la sección en su panelAsesor no necesita ver Proyectos
Lectura👁️Ver, exportar, descargarSolo consulta de Facturas emitidas
Edición✏️Ver, crear, editar, eliminarGestión completa de Gastos

Configuración recomendada por tipo de asesor:

✓ 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 → Ninguno
✓ 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 → Ninguno
  • Toast: “Permisos pre-configurados guardados”.
  • Icono de engranaje muestra que hay permisos configurados.
  • Al aceptar, asesor tendrá estos permisos.
  • Toast: “Permisos actualizados correctamente”.
  • Cambios aplicados inmediatamente.
  • Asesor ve cambios en su próximo acceso (o al refrescar).
MensajeCausaSolución
”No se encontró el asesor”Asesor eliminado o desvinculadoVerifica en pestaña “Activos"
"Error al guardar los permisos”Error de conexión o servidorReintenta o contacta soporte
Ver implementación técnica

Para invitaciones pendientes:

// Actualiza campo permissions en advisor_invitations
const { 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 existentes
await supabase
.from("advisor_section_permissions")
.delete()
.eq("advisor_id", advisorId)
.eq("client_business_id", businessId);
// 3. Inserta nuevos permisos
const 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_check
CHECK (section IN (
'invoices', 'tickets', 'quotes', 'customers',
'products', 'expenses', 'projects', 'tasks', 'reports'
))

Constraint de niveles de acceso:

CONSTRAINT advisor_section_permissions_access_level_check
CHECK (access_level IN ('none', 'read', 'edit'))

Query de diagnóstico:

-- Permisos actuales de un asesor
SELECT
asp.section,
asp.access_level,
asp.updated_at,
b.name as business_name
FROM advisor_section_permissions asp
JOIN businesses b ON asp.client_business_id = b.id
WHERE asp.advisor_id = '<advisor_id>'
AND asp.client_business_id = '<business_id>'
ORDER BY asp.section;
-- Comparar permisos preconfigurados vs aplicados
SELECT
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_permissions
FROM advisor_invitations ai
LEFT JOIN profiles p ON ai.email = p.email
LEFT JOIN advisor_section_permissions asp
ON asp.advisor_id = p.user_id
AND asp.client_business_id = ai.business_id
WHERE 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 UUID
LANGUAGE plpgsql
SECURITY DEFINER
AS $$
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;
$$;