erp-construccion/docs/00-vision-general/ARQUITECTURA-SAAS.md

1304 lines
42 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 <JWT>
```
**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<any> {
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 (/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<boolean> {
// 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<boolean> {
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