# Arquitectura SaaS Multi-tenant - ERP Construcción **Versión:** 2.0 SaaS **Fecha:** 2025-11-17 **Modelo:** SaaS Multi-tenant B2B --- ## 📋 Resumen Ejecutivo **De desarrollo a medida → Plataforma SaaS** El sistema evoluciona de un ERP a medida a una **plataforma SaaS multi-tenant** tipo SAP Cloud, donde: ✅ **Un solo código base** sirve a múltiples empresas constructoras ✅ **Módulos activables** por cliente según su plan de suscripción ✅ **Portal de administración** para gestionar tenants, usuarios y configuraciones ✅ **Marketplace de extensiones** para customizaciones específicas sin tocar el core ✅ **Onboarding automatizado** en minutos vs semanas de implementación ✅ **Pricing por módulos** con planes Básico/Profesional/Enterprise --- ## 🏗️ Arquitectura Multi-tenant ### Modelo de Aislamiento **Enfoque: Row-Level Security (RLS) con discriminador `constructora_id`** ``` ┌─────────────────────────────────────┐ │ Capa de Aplicación (Stateless) │ │ │ │ ┌─────────┐ ┌─────────┐ │ │ │ API 1 │ │ API 2 │ ... │ │ └─────────┘ └─────────┘ │ └──────────────┬──────────────────────┘ │ ┌──────────────▼──────────────────────┐ │ PostgreSQL Database │ │ │ │ ┌──────────────────────────────┐ │ │ │ Schema: constructoras │ │ │ │ - constructoras (tenant) │ │ │ │ - user_constructoras │ │ │ └──────────────────────────────┘ │ │ │ │ ┌──────────────────────────────┐ │ │ │ Schema: projects │ │ │ │ - projects │ │ │ │ ├─ constructora_id (FK) │ │ ← Discriminador │ │ └─ RLS Policy │ │ │ │ - budgets │ │ │ │ ├─ constructora_id (FK) │ │ │ │ └─ RLS Policy │ │ │ └──────────────────────────────┘ │ │ │ │ ┌──────────────────────────────┐ │ │ │ Schema: auth_management │ │ │ │ - profiles │ │ │ │ ├─ constructora_id (FK) │ │ │ │ └─ RLS Policy │ │ │ └──────────────────────────────┘ │ │ │ │ Contexto por sesión: │ │ app.current_constructora_id = X │ │ app.current_user_role = Y │ └─────────────────────────────────────┘ ``` **Ventajas:** - ✅ Escalabilidad ilimitada (millones de constructoras) - ✅ Migraciones simples (un solo schema por dominio) - ✅ Queries cross-tenant posibles (analytics globales) - ✅ Menor overhead de gestión y mantenimiento - ✅ **90% de reutilización de código de GAMILIT** - ✅ Aislamiento lógico robusto mediante RLS policies **Mitigación de riesgos:** - Seguridad: RLS policies a nivel de BD (no bypasseable desde aplicación) - Performance: Índices en `constructora_id` (sin degradación) - Compliance: Audit logging detallado por constructora - Testing: Tests de aislamiento en CI/CD (validar RLS) --- ### Identificación de Constructora (Tenant) **1. Por Subdominio:** ``` https://constructora-abc.erp-construccion.com → constructora: constructora-abc https://viviendas-xyz.erp-construccion.com → constructora: viviendas-xyz ``` **2. Por JWT Claim (principal):** ```json // Token JWT contiene { "userId": "uuid-...", "constructoraId": "uuid-abc-123", "role": "engineer", "email": "usuario@constructora-abc.com" } ``` **3. Por Header HTTP (API externa):** ```http GET /api/projects Host: api.erp-construccion.com X-Constructora-ID: uuid-abc-123 Authorization: Bearer ``` **Middleware de Constructora Resolver:** ```typescript // apps/backend/src/middleware/constructora-resolver.middleware.ts export class ConstructoraResolverMiddleware implements NestMiddleware { constructor( private constructoraService: ConstructoraService, private dataSource: DataSource ) {} async use(req: Request, res: Response, next: NextFunction) { // Extraer constructora desde: // 1. JWT claim (más común, ya autenticado) const constructoraId = req.user?.constructoraId; // 2. Header (para APIs externas) const headerConstructoraId = req.headers['x-constructora-id']; // 3. Subdomain (UX amigable, requiere lookup) let subdomain = null; if (req.hostname.includes('erp-construccion.com')) { subdomain = req.hostname.split('.')[0]; } const finalConstructoraId = constructoraId || headerConstructoraId; if (!finalConstructoraId && !subdomain) { throw new UnauthorizedException('Constructora not identified'); } // Validar que constructora existe y está activa let constructora; if (subdomain) { constructora = await this.constructoraService.findBySubdomain(subdomain); } else { constructora = await this.constructoraService.findById(finalConstructoraId); } if (!constructora || !constructora.active) { throw new UnauthorizedException('Invalid or inactive constructora'); } // Verificar que usuario tiene acceso a esta constructora if (req.user?.userId) { const hasAccess = await this.constructoraService.userHasAccess( req.user.userId, constructora.id ); if (!hasAccess) { throw new ForbiddenException('User does not have access to this constructora'); } } // Inyectar en contexto de request req.constructora = constructora; // ⭐ CRÍTICO: Configurar contexto RLS en la sesión de BD await this.setRLSContext(constructora.id, req.user?.role); next(); } private async setRLSContext(constructoraId: string, role?: string) { // Establecer variables de sesión para RLS policies await this.dataSource.query(` SELECT set_config('app.current_constructora_id', $1, true), set_config('app.current_user_role', $2, true) `, [constructoraId, role || 'guest']); } } ``` **Políticas RLS en Base de Datos:** ```sql -- Ejemplo: Tabla projects.projects CREATE POLICY "projects_select_own_constructora" ON projects.projects FOR SELECT TO authenticated USING ( constructora_id::text = current_setting('app.current_constructora_id', true) ); CREATE POLICY "projects_insert_own_constructora" ON projects.projects FOR INSERT TO authenticated WITH CHECK ( constructora_id::text = current_setting('app.current_constructora_id', true) ); -- Similar para UPDATE y DELETE ``` --- ### Contexto RLS por Request **Interceptor NestJS (aplicado globalmente):** ```typescript // apps/backend/src/interceptors/set-rls-context.interceptor.ts @Injectable() export class SetRlsContextInterceptor implements NestInterceptor { constructor(private dataSource: DataSource) {} intercept(context: ExecutionContext, next: CallHandler): Observable { const request = context.switchToHttp().getRequest(); const user = request.user; const constructora = request.constructora; if (!constructora?.id) { // Contexto público o sin autenticación return next.handle(); } // Establecer contexto RLS al inicio del request return from( this.dataSource.query(` SELECT set_config('app.current_constructora_id', $1, true), set_config('app.current_user_id', $2, true), set_config('app.current_user_role', $3, true) `, [ constructora.id, user?.userId || null, user?.role || 'guest' ]) ).pipe( switchMap(() => next.handle()), // El contexto se limpia automáticamente al finalizar la transacción ); } } // Registro global en main.ts app.useGlobalInterceptors(new SetRlsContextInterceptor(dataSource)); ``` **Funciones Helper en PostgreSQL:** ```sql -- apps/database/ddl/schemas/public/functions/ -- Obtener constructora del contexto actual CREATE OR REPLACE FUNCTION public.get_current_constructora_id() RETURNS UUID AS $$ BEGIN RETURN current_setting('app.current_constructora_id', true)::UUID; EXCEPTION WHEN OTHERS THEN RETURN NULL; END; $$ LANGUAGE plpgsql STABLE; -- Obtener user_id del contexto actual CREATE OR REPLACE FUNCTION public.get_current_user_id() RETURNS UUID AS $$ BEGIN RETURN current_setting('app.current_user_id', true)::UUID; EXCEPTION WHEN OTHERS THEN RETURN NULL; END; $$ LANGUAGE plpgsql STABLE; -- Obtener rol del contexto actual CREATE OR REPLACE FUNCTION public.get_current_user_role() RETURNS TEXT AS $$ BEGIN RETURN current_setting('app.current_user_role', true); EXCEPTION WHEN OTHERS THEN RETURN 'guest'; END; $$ LANGUAGE plpgsql STABLE; -- Verificar si usuario tiene acceso a constructora CREATE OR REPLACE FUNCTION public.user_has_access_to_constructora( p_user_id UUID, p_constructora_id UUID ) RETURNS BOOLEAN AS $$ BEGIN RETURN EXISTS ( SELECT 1 FROM auth_management.user_constructoras WHERE user_id = p_user_id AND constructora_id = p_constructora_id AND active = true ); END; $$ LANGUAGE plpgsql STABLE; ``` **Ventajas de este enfoque:** - ✅ Un solo pool de conexiones (eficiente) - ✅ Contexto por request, no por conexión - ✅ Compatible con transacciones - ✅ Fácil de testear (mock del contexto) - ✅ Reutilización directa de código GAMILIT --- ## 🎛️ Portal de Administración SaaS ### Roles del Portal | Rol | Descripción | Accesos | |-----|-------------|---------| | **Super Admin** | Administrador de la plataforma | Todos los tenants, configuración global | | **Tenant Admin** | Administrador de empresa cliente | Su tenant, usuarios, módulos, configuración | | **Support** | Soporte técnico | Ver datos, ayudar clientes (no modificar) | | **Billing** | Facturación y cobranza | Ver uso, generar facturas, suspender por falta de pago | --- ### Funcionalidades del Portal #### 1. Gestión de Tenants **Dashboard Principal:** | Tenant | Plan | Usuarios | Módulos Activos | Estado | MRR | Acciones | |--------|------|----------|-----------------|--------|-----|----------| | constructora-abc | Enterprise | 45/50 | 15/18 | 🟢 Activo | $1,500 | Ver · Editar · Facturar | | viviendas-xyz | Profesional | 18/25 | 10/18 | 🟢 Activo | $750 | Ver · Editar · Facturar | | obras-norte | Básico | 8/10 | 6/18 | 🟡 Prueba | $0 | Ver · Editar · Convertir | | desarrollos-sur | Enterprise | 12/100 | 18/18 | 🔴 Suspendido | $2,000 | Ver · Editar · Reactivar | **Métricas Globales:** - Total tenants: 234 - Activos: 198 (84.6%) - En prueba: 28 (12%) - Suspendidos: 8 (3.4%) - MRR Total: $156,780 - Usuarios totales: 4,523 --- #### 2. Onboarding de Nuevo Tenant **Flujo Automatizado (5 minutos):** ```yaml onboarding_steps: - step: 1 name: "Registro inicial" fields: - company_name: "Constructora ABC SA de CV" - rfc: "CABC850101AAA" - industry: "Construcción de vivienda" - employees_count: "50-100" - subdomain: "constructora-abc" # Auto-sugerido - admin_email: "admin@constructora-abc.com" - admin_name: "Juan Pérez" - phone: "+52 442 123 4567" duration: "2 min" - step: 2 name: "Selección de plan" options: - plan: "Básico" price: "$399/mes" users: "10" modules: "6 módulos core" - plan: "Profesional" # ← Seleccionado price: "$799/mes" users: "25" modules: "12 módulos" - plan: "Enterprise" price: "$1,499/mes" users: "100" modules: "Todos (18)" duration: "1 min" - step: 3 name: "Configuración de módulos" modules_selected: - MAI-001: "Fundamentos" (incluido) - MAI-002: "Proyectos" (incluido) - MAI-003: "Presupuestos" (incluido) - MAI-004: "Compras" (incluido) - MAI-005: "Control de Obra" (incluido) - MAI-006: "Reportes" (incluido) - MAI-007: "RRHH" ($100/mes adicional) - MAI-008: "Estimaciones" (incluido) - MAI-009: "Calidad" ($50/mes adicional) - MAI-010: "CRM" (incluido) duration: "1 min" - step: 4 name: "Provisioning automático" actions: - "Crear registro en tabla constructoras.constructoras" - "Generar UUID único para constructora" - "Crear usuario admin con relación a constructora" - "Insertar registro en user_constructoras (rol admin)" - "Activar módulos seleccionados (feature flags)" - "Configurar subdomain DNS (CNAME a aplicación)" - "Generar datos seed (catálogos base)" - "Generar datos demo (opcional)" - "Enviar email de bienvenida con credenciales" duration: "< 1 min (background job)" - step: 5 name: "Primer login" url: "https://constructora-abc.erp-construccion.com" credentials: - email: "admin@constructora-abc.com" - temp_password: "xxxxxx" (cambiar en primer login) welcome_tour: true sample_data: true duration: "Inmediato" total_time: "5 minutos" status: "✅ Tenant activo" ``` --- #### 3. Configuración de Módulos por Tenant **Panel de Módulos:** ``` ┌─────────────────────────────────────────────────────┐ │ Módulos Activos: constructora-abc (Plan Profesional)│ ├─────────────────────────────────────────────────────┤ │ │ │ FASE 1: ALCANCE INICIAL │ │ ┌─────────────────────────────────────┐ │ │ │ ✅ MAI-001 Fundamentos Incluido│ [●] │ │ │ ✅ MAI-002 Proyectos Incluido│ [●] │ │ │ ✅ MAI-003 Presupuestos Incluido│ [●] │ │ │ ✅ MAI-004 Compras Incluido│ [●] │ │ │ ✅ MAI-005 Control de Obra Incluido│ [●] │ │ │ ✅ MAI-006 Reportes Incluido│ [●] │ │ │ ✅ MAI-007 RRHH +$100/mes│ [●] │ │ │ ✅ MAI-008 Estimaciones Incluido│ [●] │ │ │ ⚪ MAI-009 Calidad +$50/mes │ [ ] ← │ │ │ ✅ MAI-010 CRM Incluido│ [●] │ │ │ ⚪ MAI-011 INFONAVIT +$75/mes │ [ ] │ │ │ ⚪ MAI-012 Contratos +$75/mes │ [ ] │ │ │ ⚪ MAI-013 Administración Incluido │ [ ] │ │ └─────────────────────────────────────┘ │ │ │ │ FASE 2: ENTERPRISE │ │ ┌─────────────────────────────────────┐ │ │ │ ⚪ MAE-014 Finanzas +$200/mes│ [ ] │ │ │ ⚪ MAE-015 Activos +$150/mes│ [ ] │ │ │ ⚪ MAE-016 DMS +$100/mes│ [ ] │ │ └─────────────────────────────────────┘ │ │ │ │ FASE 3: AVANZADA │ │ ┌─────────────────────────────────────┐ │ │ │ ⚪ MAA-017 HSE + IA +$300/mes│ [ ] │ │ └─────────────────────────────────────┘ │ │ │ │ Subtotal Plan Profesional: $799/mes │ │ Add-ons activados: +$100/mes │ │ ───────────────────────────────────────── │ │ Total mensual: $899/mes │ │ │ │ [Guardar Cambios] [Vista Previa] │ └─────────────────────────────────────────────────────┘ ``` **Al activar/desactivar módulos:** - Se ejecuta migration específica del módulo - Se actualizan permisos de usuarios - Se calcula nuevo pricing - Se notifica a tenant admin - Cambios efectivos en <5 minutos --- #### 4. Gestión de Usuarios por Tenant **Vista de Tenant Admin:** | Usuario | Email | Rol | Módulos | Último acceso | Estado | Acciones | |---------|-------|-----|---------|---------------|--------|----------| | Juan Pérez | admin@const-abc.com | Admin | Todos | Hace 2 hrs | 🟢 Activo | Editar · Desactivar | | María López | maria@const-abc.com | Engineer | 8 módulos | Hace 1 día | 🟢 Activo | Editar · Desactivar | | Pedro Martínez | pedro@const-abc.com | Resident | 6 módulos | Hace 3 hrs | 🟢 Activo | Editar · Desactivar | | ... | ... | ... | ... | ... | ... | ... | **Usuarios: 18 / 25 (72% de capacidad)** [+ Invitar Usuario] [Importar CSV] [Exportar] --- #### 5. Configuración de Tenant **Categorías de configuración:** **General:** - Nombre de empresa - Logo (usado en reportes y emails) - Zona horaria - Idioma (ES/EN) - Moneda (MXN/USD) **Personalización:** - Colores de marca (primary, secondary) - Email remitente personalizado - Dominio custom (opcional): `erp.constructora-abc.com` **Seguridad:** - 2FA obligatorio (sí/no) - Política de contraseñas - Sesiones concurrentes - IP whitelisting **Integraciones:** - SAP/CONTPAQi (credenciales) - WhatsApp Business API - SMS provider - Storage (AWS S3 / Azure Blob) **Facturación:** - Método de pago - Datos fiscales - Historial de facturas - Uso mensual --- ## 💳 Modelo de Pricing ### Planes Base | Plan | Precio/mes | Usuarios | Módulos Incluidos | Almacenamiento | Soporte | |------|------------|----------|-------------------|----------------|---------| | **Básico** | $399 USD | 10 | 6 core | 10 GB | Email (48h) | | **Profesional** | $799 USD | 25 | 12 módulos | 50 GB | Email + Chat (24h) | | **Enterprise** | $1,499 USD | 100 | Todos (18) | 200 GB | Dedicado (4h) | | **Enterprise Plus** | Custom | Ilimitado | Todos + Custom | Ilimitado | Dedicado (1h) | --- ### Módulos Add-on (por módulo/mes) | Módulo | Precio/mes | Disponible en | |--------|------------|---------------| | MAI-007 RRHH Avanzado | $100 | Todos los planes | | MAI-009 Calidad y Postventa | $50 | Profesional+ | | MAI-011 INFONAVIT | $75 | Profesional+ | | MAI-012 Contratos | $75 | Profesional+ | | MAE-014 Finanzas | $200 | Enterprise | | MAE-015 Activos | $150 | Enterprise | | MAE-016 DMS | $100 | Profesional+ | | MAA-017 HSE + IA | $300 | Enterprise | --- ### Usuarios Adicionales | Plan | Precio/usuario/mes | |------|--------------------| | Básico | $20 USD | | Profesional | $15 USD | | Enterprise | $10 USD | --- ### Cálculo de Ejemplo **Constructora ABC (Plan Profesional):** ``` Plan Profesional base: $799/mes - 25 usuarios incluidos - 12 módulos Add-ons activados: + MAI-007 RRHH $100/mes + MAI-011 INFONAVIT $75/mes Usuarios adicionales: + 5 usuarios × $15 $75/mes Almacenamiento adicional: + 20 GB × $2/GB $40/mes ───────────────────────────────────────── Total mensual: $1,089/mes Anual (15% descuento): $11,107/año ($925/mes) ``` --- ### Costos de Contratación Inicial (One-Time) Además de la suscripción mensual, existe un **costo único de implementación inicial** que cubre: ✅ **Migración de datos** desde sistemas legacy (Excel, ERP anterior, etc.) ✅ **Capacitación** a usuarios (sesiones remotas + material) ✅ **Adaptación al negocio** (configuración de workflows, catálogos, permisos) ✅ **Implementaciones dentro de configuraciones** (reportes personalizados, dashboards, etc.) --- #### Paquetes de Onboarding | Paquete | Precio | Usuarios | Registros a Migrar | Horas Capacitación | Horas Configuración | Ideal para | |---------|--------|----------|-------------------|-------------------|---------------------|------------| | **Starter** | $2,500 USD | <10 | <5,000 | 4 horas | 8 horas | Empresas pequeñas con datos simples | | **Profesional** | $7,500 USD | 10-50 | <50,000 | 12 horas | 20 horas | Empresas medianas con procesos establecidos | | **Enterprise** | $15,000 USD | 50-100 | <200,000 | 24 horas | 40 horas | Constructoras grandes con ERP previo | | **Enterprise Plus** | Custom | 100+ | Ilimitado | Custom | Custom | Corporativos multinacionales | --- #### Desglose del Servicio de Onboarding **1. Migración de Datos (30% del tiempo)** Incluye: - Análisis de datos fuente (Excel, CSVs, base de datos legacy) - Limpieza y normalización de datos - Mapping de campos a esquema del ERP - Importación automatizada con validaciones - Verificación de integridad post-migración **Entregables:** - Plan de migración documentado - Scripts de importación - Reporte de validación con discrepancias - Backup de datos originales **Datos migrados típicos:** - Catálogo de clientes/proveedores - Proyectos históricos (últimos 2 años) - Presupuestos y estimaciones - Personal y nóminas - Inventarios de almacén - Documentos y planos (opcional) --- **2. Capacitación (25% del tiempo)** **Metodología:** - Sesiones remotas por Zoom/Teams - Grabaciones disponibles 1 año - Material didáctico en PDF - Certificado de participación **Programa:** | Sesión | Audiencia | Duración | Contenido | |--------|-----------|----------|-----------| | **Sesión 1: Administradores** | Admins de sistema | 3 hrs | Portal admin, usuarios, módulos, configuración | | **Sesión 2: Operaciones** | Residentes de obra, almacén | 3 hrs | Proyectos, control de obra, compras, inventarios | | **Sesión 3: Finanzas** | Contadores, finanzas | 2 hrs | Presupuestos, estimaciones, reportes financieros | | **Sesión 4: RRHH** | Recursos humanos | 2 hrs | Nóminas, asistencias, incidencias | | **Sesión 5: Ejecutivos** | Directores, gerentes | 2 hrs | Dashboards, analytics, reportes ejecutivos | **Material incluido:** - Manual de usuario por módulo (PDF) - Videos tutoriales (10-15 min c/u) - FAQs y troubleshooting - Acceso a knowledge base --- **3. Adaptación al Negocio (25% del tiempo)** Configuraciones personalizadas sin código: **Catálogos maestros:** - Tipos de proyecto específicos (residencial, industrial, etc.) - Catálogo de conceptos de obra (partidas estándar) - Plantillas de presupuesto por tipo de obra - Roles y permisos personalizados - Centros de costo / áreas organizacionales **Workflows de aprobación:** - Flujo de aprobación de compras (niveles, montos) - Flujo de estimaciones (revisión, autorización) - Flujo de requisiciones de almacén - Flujo de incidencias de calidad **Branding:** - Logo de empresa en sistema - Colores corporativos - Plantillas de reportes con membrete - Emails transaccionales personalizados --- **4. Implementaciones de Configuración (20% del tiempo)** Desarrollo de reportes y dashboards personalizados: **Reportes custom:** - Reporte ejecutivo mensual (formato específico del cliente) - Reporte de avance de obra para clientes finales - Formatos oficiales (INFONAVIT, CFE, etc.) - Reporte de rentabilidad por proyecto **Dashboards:** - Dashboard ejecutivo C-level - Dashboard de obra para residentes - Dashboard financiero para contadores **Integraciones:** - Configuración de integración SAP/CONTPAQi - Configuración de WhatsApp Business API - Configuración de storage (S3/Azure) --- #### Calendario de Implementación **Paquete Starter (2-3 semanas):** ``` Semana 1: - Día 1-2: Kickoff + análisis de datos - Día 3-4: Migración de datos - Día 5: Validación de migración Semana 2: - Día 1-2: Configuración de catálogos y workflows - Día 3-4: Capacitación usuarios (2 sesiones) - Día 5: Ajustes finales Semana 3: - Día 1-2: Configuración de reportes - Día 3: Sesión final y go-live - Día 4-5: Soporte post go-live ``` **Paquete Profesional (4-6 semanas):** ``` Semana 1-2: Migración de datos + validación Semana 3: Configuración avanzada Semana 4-5: Capacitación (4-5 sesiones) Semana 6: Reportes custom + go-live ``` **Paquete Enterprise (8-12 semanas):** ``` Semana 1-3: Análisis y migración de datos complejos Semana 4-6: Configuración enterprise + integraciones Semana 7-9: Capacitación intensiva (6+ sesiones) Semana 10-11: Desarrollo de reportes y dashboards Semana 12: UAT, ajustes y go-live ``` --- #### Soporte Post-Onboarding **Incluido en el onboarding (primeros 30 días):** - Soporte prioritario vía email/chat - Webinars de Q&A semanales - Ajustes menores de configuración - Resolución de dudas operativas **Posterior (según plan de suscripción):** - Plan Básico: Email (48h) - Plan Profesional: Email + Chat (24h) - Plan Enterprise: Soporte dedicado (4h) --- #### Servicios Adicionales (Opcionales) | Servicio | Precio | Descripción | |----------|--------|-------------| | **Capacitación on-site** | $3,000 USD/día + viáticos | Sesiones presenciales en oficinas del cliente | | **Migración de documentos** | $0.10 USD/documento | Digitalización y clasificación de planos/contratos | | **Desarrollo de extensión custom** | $150 USD/hora | Funcionalidad no disponible en configuración estándar | | **Consultoría de procesos** | $200 USD/hora | Optimización de workflows y mejores prácticas | | **Integración legacy custom** | Desde $5,000 USD | Integración con sistemas propietarios complejos | | **Auditoría de datos** | $2,000 USD | Validación exhaustiva de integridad de datos migrados | --- #### Garantía de Onboarding **Compromiso:** - Sistema funcional al 100% al término del onboarding - Usuarios capacitados y productivos - Datos migrados con >98% de precisión **Si no se cumple:** - Extensión de soporte sin costo hasta lograrlo - Reembolso parcial si no se alcanza funcionalidad mínima acordada - Consultoría adicional sin cargo --- #### Ejemplo de Presupuesto Completo **Constructora ABC (50 empleados, 15,000 registros, ERP previo):** ``` INVERSIÓN INICIAL (One-time): Paquete Profesional Onboarding: $7,500 USD ✓ Migración 15,000 registros ✓ 12 horas capacitación (4 sesiones) ✓ 20 horas configuración ✓ Soporte 30 días post go-live Servicios adicionales: + Migración 2,000 planos PDF $200 USD + Integración CONTPAQi $5,000 USD ─────────────────────────────────────────── Subtotal inicial: $12,700 USD SUSCRIPCIÓN MENSUAL: Plan Profesional base: $799/mes ✓ 25 usuarios incluidos ✓ 12 módulos ✓ 50 GB almacenamiento ✓ Soporte 24h Add-ons activados: + MAI-007 RRHH Avanzado $100/mes + MAI-011 INFONAVIT $75/mes + MAI-012 Contratos $75/mes Usuarios adicionales: + 5 usuarios × $15 $75/mes ─────────────────────────────────────────── Total mensual: $1,124/mes COSTO TOTAL AÑO 1: Inversión inicial: $12,700 USD Suscripción 12 meses: $13,488 USD (1,124 × 12) ─────────────────────────────────────────── Total año 1: $26,188 USD COSTO TOTAL AÑOS SUBSECUENTES: Suscripción 12 meses: $13,488 USD/año (sin costo de onboarding) ROI vs ERP Tradicional: SAP/Oracle implementación: $150K-$500K inicial SAP/Oracle suscripción: $50K-$150K/año Ahorro año 1: $123K-$473K (vs SAP mínimo) Payback: Inmediato ``` --- ## 🔌 Marketplace de Extensiones ### Tipos de Extensiones | Tipo | Descripción | Ejemplo | |------|-------------|---------| | **Integraciones** | Conectores a sistemas externos | Integración con WhatsApp Business, Slack, Zoom | | **Reportes Custom** | Plantillas de reportes específicas | Reporte para licitaciones CFE, reporte INFONAVIT especial | | **Módulos Verticales** | Funcionalidad específica de industria | Módulo de Obra Civil Pesada, Módulo de Edificación Alta | | **Workflows Custom** | Flujos de aprobación personalizados | Workflow de estimaciones 5 niveles | | **Dashboards** | Dashboards temáticos | Dashboard Ejecutivo C-Level | | **Templates** | Plantillas de documentos | Contratos tipo, formatos oficiales | --- ### Catálogo de Marketplace ``` ┌────────────────────────────────────────────────┐ │ 🛒 Marketplace de Extensiones │ ├────────────────────────────────────────────────┤ │ │ │ INTEGRACIONES │ │ ┌──────────────────────────────────┐ │ │ │ 📱 WhatsApp Business API │ │ │ │ Notificaciones automáticas │ │ │ │ ⭐⭐⭐⭐⭐ (45 reviews) │ │ │ │ Gratis | [Instalar] │ │ │ └──────────────────────────────────┘ │ │ │ │ ┌──────────────────────────────────┐ │ │ │ 💼 SAP S/4HANA Connector │ │ │ │ Export de pólizas contables │ │ │ │ ⭐⭐⭐⭐ (23 reviews) │ │ │ │ $99/mes | [Instalar] │ │ │ └──────────────────────────────────┘ │ │ │ │ REPORTES CUSTOM │ │ ┌──────────────────────────────────┐ │ │ │ 📊 Reporte INFONAVIT EVC │ │ │ │ Formato oficial actualizado 2025 │ │ │ │ ⭐⭐⭐⭐⭐ (89 reviews) │ │ │ │ $49 único | [Comprar] │ │ │ └──────────────────────────────────┘ │ │ │ │ MÓDULOS VERTICALES │ │ ┌──────────────────────────────────┐ │ │ │ 🏗️ Obra Civil Pesada │ │ │ │ Puentes, carreteras, presas │ │ │ │ ⭐⭐⭐⭐ (12 reviews) │ │ │ │ $299/mes | [Ver Demo] │ │ │ └──────────────────────────────────┘ │ │ │ │ [Explorar Más] [Mis Extensiones] │ └────────────────────────────────────────────────┘ ``` --- ### Desarrollo de Extensiones **SDK de Extensiones:** ```typescript // apps/extensions/ejemplo-extension/index.ts import { Extension, Hook, MenuItem } from '@erp-construccion/sdk'; @Extension({ id: 'whatsapp-notifier', name: 'WhatsApp Notifier', version: '1.0.0', author: 'ERP Construcción', description: 'Envía notificaciones por WhatsApp', permissions: ['notifications.send', 'users.read'], pricing: { type: 'free', }, }) export class WhatsAppNotifierExtension { // Hook: Se ejecuta cuando se crea una estimación @Hook('estimations.created') async onEstimationCreated(estimation: Estimation) { const tenant = this.context.tenant; const users = await this.api.users.findByRole('finance'); for (const user of users) { if (user.phone && user.notificationsEnabled) { await this.sendWhatsApp(user.phone, { template: 'estimation_created', params: { estimationNumber: estimation.number, amount: estimation.amount, project: estimation.project.name, }, }); } } } // Agregar ítem al menú lateral @MenuItem({ section: 'settings', label: 'Configurar WhatsApp', icon: 'whatsapp', route: '/settings/whatsapp', }) menuItem() { return { component: WhatsAppSettingsPage, }; } private async sendWhatsApp(phone: string, message: any) { // Implementación... } } ``` --- ## 🔄 Ciclo de Vida del Tenant ### Estados de Tenant ```mermaid stateDiagram-v2 [*] --> Registering Registering --> Trial: Onboarding completado Trial --> Active: Pago confirmado Trial --> Expired: 14 días sin pago Active --> Suspended: Falta de pago Suspended --> Active: Pago recibido Suspended --> Canceled: 30 días suspendido Active --> Canceled: Solicitud de cliente Expired --> Active: Pago recibido Canceled --> [*] ``` | Estado | Descripción | Acceso | Duración | |--------|-------------|--------|----------| | **Registering** | Alta en proceso | No | <5 min | | **Trial** | Período de prueba | Completo | 14 días | | **Active** | Suscripción activa | Completo | Indefinido | | **Suspended** | Falta de pago | Solo lectura | Hasta 30 días | | **Expired** | Trial vencido | Login deshabilitado | Hasta reactivación | | **Canceled** | Cancelado por cliente o sistema | No | Soft-delete 90 días | --- ### Políticas de Cancelación **Cancelación por Cliente:** 1. Cliente solicita cancelación desde portal 2. Confirmación con razón (opcional: encuesta) 3. Export de datos ofrecido (formato SQL/Excel) 4. Tenant pasa a estado `Canceled` 5. Datos retenidos 90 días (compliance) 6. Eliminación permanente tras 90 días **Suspensión por Falta de Pago:** 1. Intento de cargo fallido (día 1) 2. Reintento automático (día 3) 3. Email de recordatorio (día 5) 4. Último reintento (día 7) 5. **Suspensión** (día 8): Solo lectura 6. Email de suspensión con link de pago 7. Cancelación automática si no paga en 30 días --- ## 🛠️ Gestión de Configuraciones ### Niveles de Configuración ``` 1. Global (Platform-level) ├── Configuración de infraestructura ├── Límites globales └── Features flags 2. Tenant-level ├── Módulos activados ├── Usuarios y permisos ├── Branding ├── Integraciones └── Datos maestros 3. User-level ├── Preferencias personales ├── Dashboard layout └── Notificaciones ``` --- ### Feature Flags Permiten activar/desactivar funcionalidades sin deploy: ```typescript // apps/backend/src/config/feature-flags.service.ts export class FeatureFlagsService { async isFeatureEnabled( featureName: string, tenantId?: string ): Promise { // 1. Verificar a nivel global const globalFlag = await this.getGlobalFlag(featureName); if (globalFlag === false) return false; // 2. Verificar a nivel tenant (si aplica) if (tenantId) { const tenantFlag = await this.getTenantFlag(featureName, tenantId); if (tenantFlag !== null) return tenantFlag; } // 3. Default return globalFlag; } } // Uso en controlador @Get('ai-insights') @UseGuards(FeatureGuard('ai_risk_prediction')) async getAIInsights() { // Solo accesible si feature está habilitada para el tenant // ... } ``` **Casos de uso:** - Gradual rollout de nuevas features - A/B testing - Deprecación controlada de features - Habilitación por plan (Enterprise features) --- ## 📊 Métricas SaaS ### KPIs del Negocio | Métrica | Descripción | Target | |---------|-------------|--------| | **MRR** | Monthly Recurring Revenue | Crecimiento 15% M/M | | **ARR** | Annual Recurring Revenue | $2M año 1 | | **Churn Rate** | % de clientes que cancelan | <5% mensual | | **CAC** | Customer Acquisition Cost | <$1,500 | | **LTV** | Lifetime Value | >$18,000 (12× CAC) | | **Activation Rate** | % que activan módulos en 7 días | >80% | | **NPS** | Net Promoter Score | >50 | --- ### Métricas Técnicas | Métrica | Target | Monitoreo | |---------|--------|-----------| | **Uptime** | 99.9% | StatusPage.io | | **API Response Time** | p95 <200ms | DataDog | | **Database Query Time** | p95 <100ms | pg_stat_statements | | **Onboarding Time** | <5 min | Analytics | | **Time to First Value** | <1 hr | Mixpanel | --- ## 🚀 Roadmap SaaS ### Fase 1: MVP SaaS (Semanas 1-8) - ✅ Arquitectura multi-tenant - ✅ Portal de admin básico - ✅ Onboarding automatizado - ✅ 6 módulos core - ✅ Pricing y billing ### Fase 2: Enterprise Features (Semanas 9-16) - ✅ 12 módulos adicionales - ✅ Módulos activables dinámicamente - ✅ Marketplace MVP - ✅ Extensiones SDK - ✅ Custom domains ### Fase 3: Scale & Growth (Semanas 17-24) - ⏳ IA predictiva - ⏳ Analytics avanzado - ⏳ Integraciones nativas (SAP, WhatsApp) - ⏳ Mobile app completa - ⏳ API pública para partners ### Fase 4: Expansión (Semanas 25+) - 📋 Marketplace público - 📋 White-label para partners - 📋 Internacionalización (US, LATAM) - 📋 Cumplimiento (SOC2, ISO 27001) --- ## 🔐 Seguridad Multi-tenant ### Aislamiento de Datos 1. **Row-level security (RLS)**: Aislamiento lógico mediante políticas PostgreSQL 2. **Columna discriminadora**: `constructora_id` en todas las tablas multi-tenant 3. **Contexto por sesión**: `app.current_constructora_id` configurado por request 4. **API-level validation**: Validación de acceso a constructora en middleware 5. **Audit logging**: Registro de accesos con constructora_id en cada operación 6. **Testing de aislamiento**: Tests automáticos que validan RLS policies ### Prevención de Data Leakage ```typescript // Guard que previene acceso cross-constructora @Injectable() export class ConstructoraGuard implements CanActivate { constructor(private constructoraService: ConstructoraService) {} async canActivate(context: ExecutionContext): Promise { const request = context.switchToHttp().getRequest(); const user = request.user; const constructora = request.constructora; if (!user || !constructora) { throw new UnauthorizedException('User or constructora not identified'); } // Validar que el usuario tiene acceso a esta constructora const hasAccess = await this.constructoraService.userHasAccess( user.userId, constructora.id ); if (!hasAccess) { // Intentó acceder a datos de otra constructora await this.auditService.logSecurityViolation({ userId: user.userId, attemptedConstructoraId: constructora.id, event: 'cross_constructora_access_denied', ip: request.ip, }); throw new ForbiddenException('Access denied to this constructora'); } return true; } } // Uso en controlador @Controller('projects') @UseGuards(JwtAuthGuard, ConstructoraGuard) export class ProjectsController { // Todos los endpoints requieren acceso válido a constructora } ``` **Tests de Aislamiento:** ```typescript // apps/backend/test/security/rls-isolation.spec.ts describe('RLS Isolation Tests', () => { it('should prevent cross-constructora data access', async () => { // Setup: Crear 2 constructoras y usuarios const constructoraA = await createConstructora('Constructora A'); const constructoraB = await createConstructora('Constructora B'); const userA = await createUser({ constructoraId: constructoraA.id }); const userB = await createUser({ constructoraId: constructoraB.id }); // Crear proyecto para constructora A const projectA = await createProject({ name: 'Proyecto A', constructoraId: constructoraA.id }); // Login como usuario B const tokenB = await loginAs(userB); // Intentar acceder a proyecto de constructora A (debe fallar) const response = await request(app) .get(`/api/projects/${projectA.id}`) .set('Authorization', `Bearer ${tokenB}`) .expect(403); expect(response.body.message).toContain('Access denied'); }); it('should enforce RLS at database level', async () => { // Setup similar... // Intentar query directo con constructora incorrecta en contexto await dataSource.query(` SELECT set_config('app.current_constructora_id', $1, true) `, [constructoraB.id]); // Query debe retornar 0 resultados (RLS bloquea) const projects = await dataSource.query(` SELECT * FROM projects.projects WHERE id = $1 `, [projectA.id]); expect(projects).toHaveLength(0); // RLS bloqueó el acceso }); }); ``` --- ## 📝 Conclusión Esta arquitectura SaaS multi-tenant permite: ✅ **Escalabilidad**: De 10 a 10,000 tenants sin cambios arquitectónicos ✅ **Time-to-market**: Onboarding de clientes en minutos ✅ **Flexibilidad**: Módulos activables, extensiones, customización ✅ **Economía**: Costo operativo distribuido, mejor margen ✅ **Innovación**: Feature flags, A/B testing, rollout gradual **Próximo paso:** Implementar la transformación en el MVP-APP.md principal. --- **Generado:** 2025-11-17 **Versión:** 2.0 SaaS **Modelo:** Multi-tenant B2B SaaS