Módulo Corporativo
¿Qué es?
Section titled “¿Qué es?”El módulo corporativo implementa multi-tenancy B2B. Una organización es un tenant aislado que agrupa a sus empleados (CORPORATE_USER) bajo un único administrador (CORPORATE_ADMIN). A diferencia de una cuenta individual, una organización:
- Tiene un pool de seats (licencias) con límite configurado por el SUPER_ADMIN
- Puede asignar rutas de aprendizaje del catálogo público o rutas privadas exclusivas
- Dispone de un portal dedicado (
/corporate/*) con dashboard, gestión de miembros, analíticas y ajustes - Puede invitar empleados por email; la invitación expira en 7 días
- Tiene branding personalizable (colores primario y secundario) que se aplica a los certificados emitidos bajo esa organización
Archivos clave:
apps/api/src/modules/organizations/— módulo NestJS (controller, service)apps/web/src/pages/corporate/— 6 páginas del portal corporativoapps/web/src/store/api/corporateApi.ts— slice RTK Query con todos los tipos y endpoints
Planes corporativos
Section titled “Planes corporativos”Los planes están definidos en el enum OrgPlan del schema Prisma y en el modelo OrgPlanDefinition (configurable por SUPER_ADMIN desde la BD).
| Plan | Precio base/seat | Notas |
|---|---|---|
TEAM | $49/mes | Plan de entrada |
BUSINESS | $89/mes | Plan estándar |
ENTERPRISE | $129/mes | Incluye SSO (SAML), branding avanzado |
Ciclo de vida de una organización (OrgStatus):
| Estado | Descripción |
|---|---|
TRIAL | Período de prueba — trialEndsAt controla la caducidad |
ACTIVE | Operativa y facturada |
SUSPENDED | Acceso bloqueado temporalmente |
CANCELLED | Contrato cancelado |
CHURNED | Baja definitiva |
Diagrama de entidades y flujo de invitación
Section titled “Diagrama de entidades y flujo de invitación”erDiagram Organization ||--o{ User : "miembros" Organization ||--o{ Department : "departamentos" Organization ||--o{ OrgPath : "rutas asignadas" Organization ||--o{ Invitation : "invitaciones" Organization ||--o{ OrgWebhook : "webhooks" Organization ||--o{ OrgApiKey : "API keys" Organization ||--o{ OrgIntegration : "integraciones" OrgPath }o--|| Path : "path del catálogo" User ||--o{ Enrollment : "matrículas" User }o--o| Department : "departamento"sequenceDiagram participant CA as CORPORATE_ADMIN participant API as organizations.controller participant DB as PostgreSQL participant EMP as Empleado (email)
CA->>API: POST /organizations/:orgId/invitations\n{ emails, role, department } API->>DB: Invitation.create (status=PENDING, expiresAt=+7d) API-->>CA: { sent: N } Note over EMP: Recibe email con token único EMP->>API: POST /auth/accept-invitation?token=... API->>DB: User.create / update organizationId\nInvitation.status = ACCEPTED API-->>EMP: JWT + redirect /corporate/dashboardFlujo de onboarding corporativo
Section titled “Flujo de onboarding corporativo”- Captación B2B — Un lead rellena el formulario en
/enterprise→ se crea unDemoRequest - Alta de organización — El SUPER_ADMIN crea la
Organizationdesde/adminy asigna plan, seats y precio - Designación del admin — El SUPER_ADMIN asigna el rol
CORPORATE_ADMINal primer usuario o envía la primera invitación con ese rol - Configuración del portal — El
CORPORATE_ADMINpersonaliza perfil, branding, departamentos y política de seguridad desde/corporate/settings - Asignación de rutas — El admin añade rutas del catálogo público a la organización y las asigna a miembros concretos o a todos
- Invitación del equipo — El admin envía invitaciones por email (batch o individual) desde
/corporate/members - Seguimiento — Dashboard en
/corporatecon KPIs semanales y alertas de churn
Gestión de seats
Section titled “Gestión de seats”El conteo de seats es la suma de miembros activos (User.organizationId = orgId) más invitaciones pendientes (Invitation.status = PENDING).
// Respuesta de GET /organizations/:orgId/seats{ maxSeats: number // límite contratado active: number // usuarios con organizationId activo pending: number // invitaciones PENDING deactivated: number // siempre 0 por ahora seatAutoReclaim: boolean // reclama seat si el usuario lleva X días inactivo seatAlertAt90: boolean // alerta cuando se alcanza el 90% de seats usados}¿Qué pasa al superar el límite? La lógica de bloqueo al crear invitaciones cuando usedSeats + pending >= maxSeats está planificada — actualmente el backend permite la creación sin validación de cupo (la validación se hará en el controller antes del createInvitation).
Políticas configurables:
seatAutoReclaim— cuando está activo, el sistema puede revocar seats de usuarios inactivos automáticamenteseatAlertAt90— notificación automática alCORPORATE_ADMINcuando el uso supera el 90%
Remoción de un miembro: DELETE /organizations/:orgId/members/:memberId desvincula al usuario de la organización (organizationId = null) y rebaja su rol a FREE. No elimina el registro de usuario.
Portal CORPORATE_ADMIN (/corporate/*)
Section titled “Portal CORPORATE_ADMIN (/corporate/*)”El portal es accesible para CORPORATE_ADMIN y SUPER_ADMIN (guard CorporateGuard). Se compone de seis páginas:
| Ruta | Página | Funcionalidad |
|---|---|---|
/corporate | OrgDashboard | KPIs del equipo, Fluidity Score, alertas de churn, top miembros, rutas asignadas |
/corporate/members | OrgMembersPage | Lista de miembros con filtros, invitar, remover, nudge individual, exportar CSV |
/corporate/paths | OrgPathsPage | Rutas asignadas a la org, añadir del catálogo, asignar a miembros |
/corporate/reports | OrgReportsPage | Análisis de brecha de skills, exportación de reportes (PDF/XLSX/CSV) |
/corporate/team-progress | TeamProgressPage | Progreso detallado por miembro |
/corporate/settings | OrgSettingsPage | Perfil, branding, departamentos, facturación, seats, seguridad, notificaciones, webhooks, integraciones, API key |
Dashboard (OrgDashboard)
Section titled “Dashboard (OrgDashboard)”El dashboard agrega en una sola llamada (GET /corporate/dashboard):
- Fluidity Score del equipo — promedio de
fluidityScorede todos los miembros, con dimensiones (Comprensión, Aplicación, Integración, Evaluación, Creación), comparativa con sector y tip de mejora - Stats semanales — lecciones completadas esta semana, promedio por miembro, certificados este mes, nuevos miembros
- Alertas de churn — usuarios sin actividad en los últimos 14 días (máx. 5 en el dashboard), con opción de nudge
- Top miembros — los 6 miembros con mayor puntuación
- Rutas asignadas — estado de asignación y progreso medio por path
Gestión de miembros (OrgMembersPage)
Section titled “Gestión de miembros (OrgMembersPage)”- Filtros — todos, activos (
lastActiveAt< 7 días), en riesgo (> 14 días sin actividad) - Búsqueda — por nombre o email
- Estados de un miembro:
active,risk,inactive,new(creado en los últimos 7 días) - Nudge — envío de recordatorio individual o masivo a miembros en riesgo (
sendBulkNudge) - Exportar CSV —
GET /organizations/:orgId/members/exportdevuelveequipo-YYYY-MM-DD.csv
Reportes y analítica
Section titled “Reportes y analítica”| Endpoint | Tipo de dato | Formato |
|---|---|---|
GET /organizations/:orgId/analytics/skills-gap | Brecha por dimensión (actual vs. objetivo) | JSON en tiempo real |
GET /organizations/:orgId/reports/:type?format=pdf&period=30d | Reporte generado | 📋 Planificado — actualmente devuelve stub JSON; en producción encolaría a reports.queue |
GET /organizations/:orgId/activity/export?period=30d | Log de actividad | 📋 Planificado |
Tipos de reporte disponibles en el frontend (UI implementada, generación en cola pendiente):
| ID | Nombre | Formatos |
|---|---|---|
executive-summary | Resumen ejecutivo | PDF, PPT |
skills-gap | Brecha de competencias | PDF, XLSX |
individual-progress | Progreso individual | PDF, XLSX, CSV |
team-evolution | Evolución del equipo | PDF, PPT |
certificates | Certificados emitidos | PDF, CSV |
hr | Informe RR.HH. | PDF, PPT, XLSX |
Modelo en base de datos
Section titled “Modelo en base de datos”Organization
Section titled “Organization”model Organization { id String @id @default(uuid()) name String slug String @unique plan OrgPlan @default(TEAM) // TEAM | BUSINESS | ENTERPRISE maxSeats Int @default(5) status OrgStatus @default(ACTIVE) // TRIAL | ACTIVE | SUSPENDED | CANCELLED | CHURNED // Comercial customPricePerSeat Decimal? @db.Decimal(10,2) billingCycle BillingCycle @default(MONTHLY) discountPercent Int? @default(0) // Branding brandPrimaryColor String? @default("#7C3AED") brandSecondaryColor String? @default("#1E1E2D") // Seguridad require2FA Boolean @default(false) restrictDomain Boolean @default(false) // SSO (Enterprise) ssoEntityId String? ssoUrl String? ssoX509Certificate String? ssoAutoProvisioning Boolean @default(false) // Relaciones members User[] orgPaths OrgPath[] invitations Invitation[] departments Department[] webhooks OrgWebhook[] apiKeys OrgApiKey[] integrations OrgIntegration[]}Modelos relacionados
Section titled “Modelos relacionados”| Modelo | Descripción |
|---|---|
Department | Agrupación lógica de miembros; @@unique([organizationId, name]) |
OrgPath | Relación M:M entre Organization y Path; indica qué rutas del catálogo están activas para la org |
Invitation | Invitación por email con token único, estado PENDING / ACCEPTED / EXPIRED / REVOKED, expira en 7 días |
OrgWebhook | Webhooks salientes (URL + lista de eventos + secret HMAC) |
OrgApiKey | API key de la organización para integraciones externas; solo se devuelve completa en el momento de rotación |
OrgIntegration | Conexiones a Slack, Microsoft Teams y Google Workspace |
OrgAuditLog | Registro interno de acciones administrativas dentro de la organización |
OrgPlanDefinition | Definición de planes con precios, límites y features (gestionada por SUPER_ADMIN) |
API — endpoints
Section titled “API — endpoints”Todos los endpoints requieren JwtAuthGuard + RolesGuard con roles CORPORATE_ADMIN | SUPER_ADMIN.
Dashboard
Section titled “Dashboard”/api/corporate/dashboard Datos del dashboard corporativo del usuario autenticado (usa organizationId del JWT) Estadísticas
Section titled “Estadísticas”/api/organizations/:orgId/stats KPIs del equipo: total, activos esta semana, en riesgo, score promedio /api/organizations/:orgId/members/stats Stats de miembros: seats usados, invites pendientes, admins, activos Miembros
Section titled “Miembros”/api/organizations/:orgId/members Lista de miembros con score, estado, rutas y última actividad. Soporta ?filter=risk|active y ?q=búsqueda /api/organizations/:orgId/members/export Descarga CSV del equipo (equipo-YYYY-MM-DD.csv) /api/organizations/:orgId/members/:memberId Detalle de un miembro incluyendo actividad reciente /api/organizations/:orgId/members/:memberId Actualiza rol del miembro /api/organizations/:orgId/members/:memberId Retira al miembro de la organización (role → FREE, organizationId → null) Invitaciones
Section titled “Invitaciones”/api/organizations/:orgId/invitations Lista invitaciones pendientes /api/organizations/:orgId/invitations Invita uno o varios miembros por email. Body: { emails[], role?, department? } /api/organizations/:orgId/invitations/:invitationId/resend Reenvía la invitación y renueva la fecha de expiración (+7 días) /api/organizations/:orgId/members/:memberId/nudge Envía recordatorio de actividad a un miembro /api/organizations/:orgId/nudge/bulk Nudge masivo. Body: { memberIds? } — si se omite, se envía a todos los miembros en riesgo /api/organizations/:orgId/paths Rutas asignadas a la organización con conteo de matriculados /api/organizations/:orgId/paths/stats Estadísticas de rutas: total, activas, privadas, tasa de finalización /api/organizations/:orgId/paths/catalog Rutas del catálogo público aún no añadidas a la organización /api/organizations/:orgId/paths Añade una ruta del catálogo a la organización. Body: { pathId } /api/organizations/:orgId/paths/:pathId/assign Matricula miembros específicos en una ruta. Body: { memberIds[] } Analítica
Section titled “Analítica”/api/organizations/:orgId/analytics/skills-gap Análisis de brecha por dimensión (Comprensión, Aplicación, Integración, Evaluación, Creación) con objetivo por dimensión /api/organizations/:orgId/reports/:type Descarga de reporte (stub — producción encolaría a reports.queue). ?format=pdf|xlsx|csv &period=7d|30d|quarterly|yearly Ajustes
Section titled “Ajustes”/api/organizations/:orgId Perfil completo de la organización /api/organizations/:orgId Actualiza nombre, industria, web, país, tamaño, descripción, contacto /api/organizations/:orgId/branding Actualiza colores de branding. Body: { brandPrimaryColor?, brandSecondaryColor? } /api/organizations/:orgId/departments Lista departamentos con conteo de miembros /api/organizations/:orgId/departments Crea un departamento. Body: { name } /api/organizations/:orgId/departments/:deptId Elimina departamento (desvincula miembros antes de borrar) /api/organizations/:orgId/billing Info de facturación: plan, precio efectivo, seats, próxima renovación e historial de facturas /api/organizations/:orgId/seats Estado actual de seats: activos, pendientes, límite y políticas /api/organizations/:orgId/settings Actualiza políticas de seat. Body: { seatAutoReclaim?, seatAlertAt90? } /api/organizations/:orgId/security-policy Obtiene política de seguridad: 2FA, restricción de dominio, timeout de sesión, etc. /api/organizations/:orgId/security-policy Actualiza política de seguridad /api/organizations/:orgId/notification-preferences Preferencias de notificaciones del admin y del equipo /api/organizations/:orgId/notification-preferences Actualiza preferencias de notificaciones /api/organizations/:orgId/webhooks Lista webhooks configurados /api/organizations/:orgId/webhooks Crea webhook. Body: { url, events[] }. Genera secret HMAC aleatorio. /api/organizations/:orgId/webhooks/:webhookId Actualiza URL, eventos o estado de un webhook /api/organizations/:orgId/webhooks/:webhookId Elimina webhook /api/organizations/:orgId/api-keys Devuelve la API key enmascarada (prefijo + ● × 20 + sufijo) /api/organizations/:orgId/api-keys/rotate Rota la API key. La clave completa se devuelve SOLO en esta respuesta. /api/organizations/:orgId/integrations Estado de integraciones: Slack, Microsoft Teams, Google Workspace /api/organizations/:orgId/integrations/:provider/connect Conecta una integración /api/organizations/:orgId/integrations/:provider/disconnect Desconecta una integración Flujo de uso típico (empresa)
Section titled “Flujo de uso típico (empresa)”flowchart TD A[SUPER_ADMIN crea Organization\nplan=BUSINESS, maxSeats=50] --> B[Asigna CORPORATE_ADMIN\npor email o rol existente] B --> C[CORPORATE_ADMIN configura\nperfil, branding, departamentos] C --> D[Añade rutas del catálogo\nPOST /organizations/:id/paths] D --> E[Invita al equipo\nPOST /organizations/:id/invitations] E --> F[Empleados aceptan invitación\nrole=CORPORATE_USER] F --> G[Asigna rutas a miembros\nPOST /paths/:id/assign] G --> H[Dashboard semanal\nFluidity Score + churn alerts] H --> I{¿Miembro en riesgo?} I -->|Sí| J[Nudge individual o masivo] I -->|No| K[Exporta reporte mensual\nPDF / XLSX]Integración con certificados
Section titled “Integración con certificados”Los certificados emitidos dentro de una organización llevan el campo organizationId en el modelo Certificate. Esto permite al CORPORATE_ADMIN filtrar y exportar los certificados de su equipo, y al sistema aplicar el branding de la organización (colores, logo) al generar el PDF del certificado.