Ir al contenido

Revocar Acceso

Elimina el acceso de un asesor activo a tu negocio.

  1. Ve a Configuración → Asesores.
  2. Identifica el asesor a revocar en una de las pestañas:
    • Enviadas (si invitación aceptada)
    • Activos (directamente)
  3. Clic en el icono de “Eliminar” (papelera) — se abre diálogo de confirmación.
  4. Lee el mensaje de advertencia.
  5. Clic en “Confirmar” — acceso revocado inmediatamente.

Si es invitación pendiente:

  • Invitación eliminada permanentemente
  • Email de invitación queda inválido
  • Puedes enviar nueva invitación en el futuro

Si es asesor activo:

  • Vínculo desactivado (status = ‘inactive’)
  • Permisos eliminados de todas las secciones
  • Asesor pierde acceso inmediato al negocio
  • Invitación original eliminada
  • Notificación enviada al asesor
  • Si era su único cliente: rol de asesor removido

El diálogo muestra advertencia diferente según el caso:

¿Eliminar invitación?
Esta acción eliminará permanentemente la invitación
enviada a [email del asesor].
⚠️ Revocar acceso de asesor
El asesor [email] perderá inmediatamente el acceso
al negocio y no podrá ver ni exportar más documentación.
  • Toast de confirmación: “Acceso revocado correctamente” o “Invitación eliminada”.
  • Asesor desaparece de la lista correspondiente.
  • Asesor no puede acceder al negocio.
  • Asesor recibe notificación de revocación (si estaba activo).
MensajeCausaSolución
”Invitación no encontrada…”Invitación ya eliminada o ID inválidoRefresca la página
”Error al revocar el acceso”Error de servidorReintenta o contacta soporte

ALERTA: Esta acción es inmediata e irreversible. El asesor perderá acceso al instante.

Alternativa recomendada: Si solo quieres limitar el acceso temporalmente, considera ajustar los permisos a “Ninguno” en lugar de revocar completamente.

Ver implementación técnica

Edge Function: revoke-advisor-access

Input:

{
invitationId: string; // UUID de la invitación
}

Validación de autorización:

// Verifica que el usuario autenticado es el propietario
const { data: invitation } = await supabase
.from("advisor_invitations")
.select("*")
.eq("id", invitationId)
.eq("owner_id", user.id) // Solo el propietario puede revocar
.single();
if (!invitation) {
return { error: "Invitación no encontrada o no tienes permiso" };
}

Lógica de revocación:

// 1. Marca invitación como cancelled y la elimina
await supabase
.from("advisor_invitations")
.update({ status: "cancelled" })
.eq("id", invitationId);
await supabase
.from("advisor_invitations")
.delete()
.eq("id", invitationId);
// 2. Si la invitación estaba aceptada, desvincula al asesor
if (invitation.status === "accepted") {
// Obtiene user_id del asesor
const { data: advisorData } = await supabase
.from("profiles")
.select("user_id")
.eq("email", invitation.email)
.single();
if (advisorData) {
const advisorId = advisorData.user_id;
// Desactiva vínculo
await supabase
.from("advisor_client_links")
.update({ status: "inactive" })
.eq("advisor_id", advisorId)
.eq("client_business_id", invitation.business_id);
// Verifica si tiene otros clientes activos
const { data: activeLinks } = await supabase
.from("advisor_client_links")
.select("id")
.eq("advisor_id", advisorId)
.eq("status", "active");
// Si no tiene más clientes, remueve rol
if (!activeLinks || activeLinks.length === 0) {
await supabase
.from("user_roles")
.delete()
.eq("user_id", advisorId)
.eq("role", "advisor");
// Marca perfil como inactivo
await supabase
.from("advisor_profiles")
.update({ onboarding_completed: false })
.eq("user_id", advisorId);
}
// Notifica al asesor
await supabase
.from("advisor_notifications")
.insert({
advisor_id: advisorId,
client_business_id: invitation.business_id,
notification_type: "access_revoked",
title: "Acceso revocado",
message: "Tu acceso a este negocio ha sido revocado por el propietario"
});
}
}

Eliminación de permisos (automática via cascade):

Los permisos en advisor_section_permissions se eliminan automáticamente cuando:

  • Se desactiva el vínculo en advisor_client_links (si hay FK con cascade)
  • O se eliminan manualmente en el proceso

Output:

{
success: true;
message: "Invitación eliminada y acceso revocado correctamente"
}

Tabla afectada: advisor_client_links

// Cambio de estado
{
status: 'active''inactive'
}

Notificación creada:

{
id: UUID;
advisor_id: UUID;
client_business_id: UUID;
notification_type: 'access_revoked';
title: 'Acceso revocado';
message: 'Tu acceso a este negocio ha sido revocado por el propietario';
is_read: false;
created_at: TIMESTAMPTZ; // NOW()
}

Query de diagnóstico:

-- Verificar que vínculo está inactivo
SELECT
acl.status,
acl.linked_at,
acl.last_accessed_at,
b.name as business_name
FROM advisor_client_links acl
JOIN businesses b ON acl.client_business_id = b.id
WHERE acl.advisor_id = '<advisor_id>'
AND acl.client_business_id = '<business_id>';
-- Verificar que permisos fueron removidos
SELECT COUNT(*) as remaining_permissions
FROM advisor_section_permissions
WHERE advisor_id = '<advisor_id>'
AND client_business_id = '<business_id>';
-- Debería retornar 0
-- Ver notificación enviada
SELECT *
FROM advisor_notifications
WHERE advisor_id = '<advisor_id>'
AND client_business_id = '<business_id>'
AND notification_type = 'access_revoked'
ORDER BY created_at DESC
LIMIT 1;
-- Verificar si asesor perdió el rol
SELECT *
FROM user_roles
WHERE user_id = '<advisor_id>'
AND role = 'advisor';
-- Debería estar vacío si no tiene más clientes

Log de auditoría (recomendado):

-- Registrar revocación en tabla de auditoría
INSERT INTO audit_logs (
user_id,
action,
resource_type,
resource_id,
metadata,
created_at
) VALUES (
'<owner_id>',
'revoke_advisor_access',
'advisor_invitation',
'<invitation_id>',
jsonb_build_object(
'advisor_email', '<advisor_email>',
'business_id', '<business_id>'
),
NOW()
);