# Plan: Módulo Dashboard con IA — Novasis ERP

## Decisiones confirmadas

| Decisión            | Valor                                                              |
| ------------------- | ------------------------------------------------------------------ |
| Audiencia inicial   | Gerencial (nivel CEO/supervisor)                                   |
| Proveedor IA        | Multi-proveedor (Claude, OpenAI, otros) — configurable por empresa |
| API key             | Cada empresa configura la propia (traslado de costo)               |
| Frecuencia insights | Configurable por empresa + forzar manual                           |
| Acceso API key      | Nuevo módulo "IA / Integraciones" (protegido)                      |
| Alertas             | Solo dashboard ahora; arquitectura extensible a email/WhatsApp     |
| Predicciones        | Todas: cobranza, clientes en riesgo, ventas                        |
| Timeline            | IA decide relevancia + umbrales configurables                      |
| Chat historial      | Guardado + favoritos                                               |
| UI layout           | Sidebar izquierdo + contenido principal                            |
| Ratio contenido     | 80% texto inteligente, 20% gráficos                                |

---

## Filosofía de diseño

> La IA no muestra datos. **Piensa por el usuario.**

- Cada métrica tiene explicación del **POR QUÉ**
- Cada insight sugiere una **acción concreta**
- El lenguaje es humano, no técnico
- **NO** generar IA en tiempo real siempre → precalcular y cachear (ahorro de tokens)
- El gerente abre el dashboard y ya tiene todo listo, no espera

---

## Arquitectura general

```
┌─────────────────────────────────────────────────────────────┐
│  FRONTEND (React + MUI)                                     │
│  ┌──────────┐  ┌──────────────────────────────────────────┐ │
│  │ Sidebar  │  │ Contenido principal                      │ │
│  │ ─────── │  │  Resumen Ejecutivo │ Alertas              │ │
│  │ Resumen  │  │  Timeline         │ Predicciones         │ │
│  │ Alertas  │  │  Chat IA          │ Reportes             │ │
│  │ Timeline │  └──────────────────────────────────────────┘ │
│  │ Predict. │                                               │
│  │ Chat IA  │                                               │
│  │ Config   │                                               │
│  └──────────┘                                               │
└─────────────────────────────────────────────────────────────┘
         │ REST API
┌─────────────────────────────────────────────────────────────┐
│  BACKEND (NestJS)                                           │
│                                                             │
│  AiDashboardModule                                          │
│  ├── InsightsService    (precálculo + caché)                │
│  ├── AlertsService      (detección anomalías)               │
│  ├── TimelineService    (eventos relevantes)                │
│  ├── PredictionsService (proyecciones)                      │
│  ├── ChatService        (text-to-SQL + LLM)                 │
│  └── AiProviderService  (abstracción multi-proveedor)       │
│                                                             │
│  AiConfigModule                                             │
│  └── Configuración API keys, modelos, frecuencia            │
│                                                             │
│  CronJobs                                                   │
│  └── RecalcInsightsCron (frecuencia configurable/empresa)   │
└─────────────────────────────────────────────────────────────┘
         │
┌─────────────────────────────────────────────────────────────┐
│  BASE DE DATOS (PostgreSQL)                                 │
│  Tablas nuevas:                                             │
│  ├── ai_empresa_config      (keys, modelo, frecuencia)      │
│  ├── ai_insights            (insights precalculados)        │
│  ├── ai_alertas             (alertas detectadas)            │
│  ├── ai_timeline_eventos    (eventos del timeline)          │
│  ├── ai_chat_sesiones       (historial conversaciones)      │
│  ├── ai_chat_mensajes       (mensajes por sesión)           │
│  └── ai_chat_favoritos      (queries favoritas)             │
└─────────────────────────────────────────────────────────────┘
```

---

## Tablas nuevas (Prisma migrations)

### `ai_empresa_config`

```prisma
model ai_empresa_config {
  id                  String    @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
  empresa_id          String    @unique @db.Uuid
  proveedor           String    @default("anthropic") @db.VarChar(20)
  // anthropic | openai | gemini | ollama
  modelo              String    @default("claude-haiku-4-5-20251001") @db.VarChar(60)
  api_key_encrypted   String?   @db.Text  // AES-256 encriptado
  activo              Boolean   @default(false)
  frecuencia_cron     String    @default("0 2 * * *") @db.VarChar(50) // cron expression
  ultimo_recalculo    DateTime? @db.Timestamp(6)
  umbral_alerta_mora  Int       @default(30)  // días sin pagar = alerta
  umbral_stock_bajo   Int       @default(5)   // unidades mínimas
  umbral_venta_alta   Decimal   @default(1000000) @db.Decimal(19,4) // venta notable
  created_at          DateTime  @default(now()) @db.Timestamp(6)
  updated_at          DateTime  @default(now()) @db.Timestamp(6)
  empresas            empresas  @relation(fields: [empresa_id], references: [id])
}
```

### `ai_insights`

```prisma
model ai_insights {
  id              String    @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
  empresa_id      String    @db.Uuid
  tipo            String    @db.VarChar(50)
  // resumen_ejecutivo | ventas | cobranza | inventario | cobradores
  periodo         String    @db.VarChar(20)  // 2026-04, 2026-W15, 2026
  titulo          String    @db.VarChar(200)
  narrativa       String    @db.Text          // texto generado por IA (80%)
  datos_json      Json?                       // datos crudos para gráficos (20%)
  tokens_usados   Int?
  modelo_usado    String?   @db.VarChar(60)
  generado_at     DateTime  @default(now()) @db.Timestamp(6)
  vigente         Boolean   @default(true)
  empresas        empresas  @relation(fields: [empresa_id], references: [id])

  @@index([empresa_id, tipo, periodo])
  @@index([empresa_id, vigente])
}
```

### `ai_alertas`

```prisma
model ai_alertas {
  id              String    @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
  empresa_id      String    @db.Uuid
  tipo            String    @db.VarChar(50)
  // mora_critica | stock_bajo | cobrador_bajo_rendimiento |
  // cliente_crecimiento | venta_anomala | limite_credito_superado
  criticidad      String    @default("media") @db.VarChar(10) // alta | media | baja
  titulo          String    @db.VarChar(200)
  descripcion     String    @db.Text
  accion_sugerida String?   @db.Text
  entidad_tipo    String?   @db.VarChar(30)  // cliente | producto | cobrador
  entidad_id      String?   @db.Uuid
  leida           Boolean   @default(false)
  resuelta        Boolean   @default(false)
  canales_enviados Json?    // { email: false, whatsapp: false } — para futuro
  created_at      DateTime  @default(now()) @db.Timestamp(6)
  empresas        empresas  @relation(fields: [empresa_id], references: [id])

  @@index([empresa_id, leida, resuelta])
  @@index([empresa_id, criticidad])
}
```

### `ai_timeline_eventos`

```prisma
model ai_timeline_eventos {
  id              String    @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
  empresa_id      String    @db.Uuid
  fecha_evento    DateTime  @db.Timestamp(6)
  tipo            String    @db.VarChar(50)
  relevancia      Int       @default(50)  // 0-100, IA asigna
  titulo          String    @db.VarChar(200)
  descripcion     String    @db.Text
  icono           String?   @db.VarChar(50)  // nombre del ícono MUI
  color           String?   @db.VarChar(20)  // success | warning | error | info
  entidad_tipo    String?   @db.VarChar(30)
  entidad_id      String?   @db.Uuid
  created_at      DateTime  @default(now()) @db.Timestamp(6)
  empresas        empresas  @relation(fields: [empresa_id], references: [id])

  @@index([empresa_id, fecha_evento])
  @@index([empresa_id, relevancia])
}
```

### `ai_chat_sesiones`

```prisma
model ai_chat_sesiones {
  id              String    @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
  empresa_id      String    @db.Uuid
  usuario_id      String    @db.Uuid
  titulo          String?   @db.VarChar(200)  // autogenerado del primer mensaje
  created_at      DateTime  @default(now()) @db.Timestamp(6)
  updated_at      DateTime  @default(now()) @db.Timestamp(6)
  empresas        empresas  @relation(fields: [empresa_id], references: [id])
  mensajes        ai_chat_mensajes[]
}
```

### `ai_chat_mensajes`

```prisma
model ai_chat_mensajes {
  id              String    @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
  sesion_id       String    @db.Uuid
  rol             String    @db.VarChar(10)  // user | assistant
  contenido       String    @db.Text
  sql_generado    String?   @db.Text
  datos_json      Json?
  tipo_grafico    String?   @db.VarChar(20)  // bar | line | pie | table | number
  tokens_usados   Int?
  created_at      DateTime  @default(now()) @db.Timestamp(6)
  sesion          ai_chat_sesiones @relation(fields: [sesion_id], references: [id], onDelete: Cascade)
}
```

### `ai_chat_favoritos`

```prisma
model ai_chat_favoritos {
  id              String    @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
  empresa_id      String    @db.Uuid
  usuario_id      String    @db.Uuid
  titulo          String    @db.VarChar(200)
  pregunta        String    @db.Text
  sql_guardado    String    @db.Text
  tipo_grafico    String?   @db.VarChar(20)
  created_at      DateTime  @default(now()) @db.Timestamp(6)
  empresas        empresas  @relation(fields: [empresa_id], references: [id])

  @@index([empresa_id, usuario_id])
}
```

---

## Servicio de Proveedor IA (`AiProviderService`)

Abstracción que soporta múltiples proveedores sin cambiar el resto del código:

```typescript
interface AiProviderConfig {
  proveedor: 'anthropic' | 'openai' | 'gemini' | 'ollama';
  modelo: string;
  apiKey: string;
}

interface AiResponse {
  content: string;
  tokensUsados: number;
}

class AiProviderService {
  async completar(config: AiProviderConfig, prompt: string): Promise<AiResponse>;
  // Internamente llama al SDK correspondiente:
  // anthropic → @anthropic-ai/sdk
  // openai    → openai npm package
  // gemini    → @google/generative-ai
  // ollama    → fetch a localhost:11434
}
```

**Modelos recomendados por proveedor:**
| Proveedor | Económico | Balanceado | Premium |
|-----------|-----------|------------|---------|
| Anthropic | claude-haiku-4-5-20251001 | claude-sonnet-4-6 | claude-opus-4-6 |
| OpenAI | gpt-4o-mini | gpt-4o | gpt-4o |
| Gemini | gemini-1.5-flash | gemini-1.5-pro | gemini-1.5-pro |

---

## Sistema de Insights Precalculados

### Qué calcular (por tipo)

**`resumen_ejecutivo`** — narrativa gerencial del mes/semana:

```sql
-- Fuentes: factura_cab, recibos_cobro, cuentas_cobrar, factura_subtotales
-- Métricas:
-- • Total facturado período vs período anterior
-- • Total cobrado vs proyectado
-- • Tasa de mora (cuentas_cobrar vencidas / total)
-- • Clientes nuevos vs churn
-- • Ticket promedio
```

Prompt al LLM: _"Sos el analista financiero de esta empresa. Con estos datos del mes, escribí un resumen ejecutivo de 3-4 párrafos explicando qué pasó, por qué es relevante y qué debería hacer el gerente. Sé directo y usa lenguaje de negocios simple."_

**`cobranza`**:

```sql
-- • Cuotas vencidas por cobrador (factura_cuotas JOIN vendedores_cobradores)
-- • Efectividad de cobro: recibos_cobro / cuotas vencidas
-- • Ranking cobradores: monto cobrado, cantidad visitas, mora promedio
-- • Clientes con promesas_pago incumplidas
```

**`ventas`**:

```sql
-- • Top 10 productos por factura_det.dtotopeitem
-- • Ventas por vendedor
-- • Evolución diaria/semanal: factura_cab GROUP BY DATE(dfeemide)
-- • Productos sin movimiento (stock_deposito sin factura_det reciente)
```

**`inventario`**:

```sql
-- • Stock crítico: stock_deposito WHERE cantidad <= umbral_stock_bajo
-- • Rotación: movimientos_inventario últimos 30 días
-- • Productos más vendidos vs stock disponible
```

### Flujo de precálculo

```
CronJob (frecuencia de la empresa)
    │
    ├── Para cada empresa con ai_empresa_config.activo = true:
    │       1. Calcular métricas SQL (prisma.$queryRaw)
    │       2. Construir prompt con datos
    │       3. Llamar AiProviderService.completar()
    │       4. Guardar en ai_insights (marcar anterior como vigente=false)
    │       5. Detectar anomalías → crear ai_alertas
    │       6. Generar eventos timeline → crear ai_timeline_eventos
    │       7. Actualizar ai_empresa_config.ultimo_recalculo
    │
    └── Endpoint manual: POST /ai-dashboard/recalcular
        (mismo flujo, forzado, solo para la empresa del usuario autenticado)
```

---

## Alertas Inteligentes

### Tipos de alertas y su detección

| Tipo                        | Condición SQL                                                                                 | Criticidad         |
| --------------------------- | --------------------------------------------------------------------------------------------- | ------------------ |
| `mora_critica`              | `factura_cuotas.dvenccuo < NOW() - umbral_dias AND estado = 'pendiente'` agrupado por cliente | alta               |
| `limite_credito_superado`   | `clientes.saldo_pendiente > clientes.limite_credito`                                          | alta               |
| `cobrador_bajo_rendimiento` | cobrador con < 60% efectividad últimos 30 días vs promedio empresa                            | media              |
| `cliente_crecimiento`       | cliente con +40% ventas vs mes anterior                                                       | baja (oportunidad) |
| `stock_bajo`                | `stock_deposito.cantidad <= umbral_stock_bajo`                                                | media              |
| `venta_anomala`             | factura con monto > 3x ticket promedio de ese cliente                                         | media              |
| `sin_cobros_hoy`            | 0 recibos_cobro en el día (cuando debería haber)                                              | alta               |
| `descuento_excesivo`        | `autorizaciones_descuento` con porcentaje > umbral                                            | media              |

### Estructura de una alerta bien redactada

```
[ALTA] Cliente "Armando Cristaldo" — Mora crítica
───────────────────────────────────────────────
"Este cliente lleva 45 días sin pagar.
Tiene 3 cuotas vencidas por un total de Gs. 660.000.
Su último pago fue el 15/02/2026.

→ Acción sugerida: Contactar al cobrador Juan López
  para coordinar visita esta semana antes de que
  supere los 60 días y entre en gestión judicial."
```

### Arquitectura extensible para notificaciones externas

```typescript
interface AlertaNotificacion {
  alerta: ai_alertas;
  canales: ('dashboard' | 'email' | 'whatsapp')[];
}

class NotificacionService {
  async notificar(notif: AlertaNotificacion): Promise<void> {
    // dashboard: siempre (guarda en DB)
    // email: si canal activo → usar nodemailer/empresas_correo
    // whatsapp: si canal activo → usar Twilio (ya integrado)
  }
}
```

---

## Timeline Inteligente

La IA evalúa eventos del día y asigna relevancia 0-100:

### Fuentes de eventos

| Tabla                      | Evento                         | Relevancia base |
| -------------------------- | ------------------------------ | --------------- |
| `factura_cab`              | Factura emitida por monto alto | 60-90           |
| `recibos_cobro`            | Cobro grande realizado         | 50-80           |
| `factura_cuotas`           | Cuota vencida sin pagar        | 70              |
| `clientes`                 | Cliente nuevo registrado       | 40              |
| `stock_deposito`           | Stock llegó a mínimo           | 75              |
| `sesiones_caja`            | Cierre de caja con diferencia  | 85              |
| `autorizaciones_descuento` | Descuento aprobado/rechazado   | 45              |
| `promesas_pago`            | Promesa incumplida             | 80              |

### Prompt para relevancia

_"Dado este evento del sistema ERP, asignale una relevancia del 0 al 100 para el gerente de la empresa, donde 100 es algo crítico que requiere acción inmediata. Devolvé JSON: { relevancia: number, titulo: string, descripcion: string, color: 'success'|'warning'|'error'|'info' }"_

Solo se muestran en el timeline eventos con relevancia ≥ 40.

---

## Predicciones

### P1 — Proyección de cobranza (próximo mes)

```sql
-- Base: factura_cuotas WHERE dvenccuo BETWEEN hoy AND hoy+30
--        AND estado = 'pendiente'
-- Ajuste: multiplicar por tasa_cobro_historica del cobrador
--   tasa = SUM(cobrado) / SUM(esperado) últimos 3 meses
-- Resultado: { esperado: X, probable: X*tasa, optimista: X*0.9, pesimista: X*0.6 }
```

### P2 — Clientes en riesgo de mora

```sql
-- Score de riesgo por cliente:
-- • días_desde_ultimo_pago / dias_plazo_promedio → peso 40%
-- • cantidad_promesas_incumplidas → peso 30%
-- • tendencia_saldo (subiendo/bajando) → peso 30%
-- Umbral: score > 0.7 → "en riesgo"
```

### P3 — Proyección de ventas

```sql
-- Serie temporal: factura_cab GROUP BY DATE_TRUNC('week', dfeemide)
-- Método: promedio móvil 4 semanas + tendencia lineal
-- Proyección: próximas 4 semanas con banda de confianza
```

Las predicciones se precalculan junto con los insights y se guardan en `ai_insights` con `tipo = 'prediccion_cobranza'`, etc.

---

## Chat IA (text-to-SQL)

### Flujo de una consulta

```
Usuario: "Cuánto cobró Juan López este mes?"
    │
    ▼
ChatService.consultar(empresaId, sesionId, pregunta)
    │
    ├── 1. Cargar contexto schema (string estático, no consulta DB cada vez)
    ├── 2. Cargar últimos 6 mensajes de la sesión (contexto conversacional)
    ├── 3. Construir prompt:
    │      SYSTEM: [schema context + reglas de seguridad + empresa_id filter]
    │      HUMAN:  [pregunta del usuario]
    │
    ├── 4. Llamar AiProviderService → recibe JSON:
    │      { sql: string, titulo: string, tipo_grafico: string, explicacion: string }
    │
    ├── 5. Validar SQL:
    │      • Solo SELECT
    │      • Sin DROP/DELETE/UPDATE/INSERT
    │      • WHERE empresa_id = $1 presente (seguridad multi-tenant)
    │      • Timeout 10 segundos
    │
    ├── 6. Ejecutar: prisma.$queryRawUnsafe(sql, empresaId)
    │
    ├── 7. Guardar mensaje en ai_chat_mensajes
    │
    └── 8. Retornar: { datos, tipoGrafico, titulo, explicacion, sqlGenerado }
```

### Schema context (string estático)

```
Sos un analista SQL experto del ERP Novasis.
SIEMPRE filtrá por empresa_id = $1 en la tabla principal.
Solo generás SELECT. Nunca DELETE, UPDATE, INSERT, DROP.

TABLAS DISPONIBLES:
factura_cab (id, empresa_id, cliente_id, vendedor_id, cobrador_id,
             dfeemide=fecha_emision, total_factura, saldo_pendiente,
             estado, icondcred=tipo_credito[1=plazo,2=cuotas], dcuotas)
  → JOIN clientes ON cliente_id = clientes.id
  → JOIN personas ON clientes.persona_id = personas.id  [razon_social, nro_documento]
  → JOIN vendedores_cobradores vendedor ON vendedor_id = vendedor.id [nombre]
  → JOIN vendedores_cobradores cobrador ON cobrador_id = cobrador.id [nombre]

factura_subtotales (factura_cab_id, dtotalgs=total_gs, diva10, dbasegrav10)

factura_det (factura_cab_id, producto_id, ddesproser=descripcion,
             dcantproser=cantidad, duniproser=precio_unit,
             dtotopeitem=total_con_descuento, dtotopegs=total_gs)

factura_cuotas (id, factura_cab_id, cuenta_id, dmoncuota=monto,
                dvenccuo=fecha_vencimiento, estado, saldo_pendiente)

cuentas_cobrar (id, empresa_id, cliente_id, factura_venta_id,
                monto_total, saldo_pendiente, estado, fecha_vencimiento)

recibos_cobro (id, empresa_id, cliente_id, cobrador_id,
               fecha_emision, monto_total, estado)
  → JOIN vendedores_cobradores cobrador ON cobrador_id = cobrador.id

recibo_cobro_detalle (recibo_cobro_id, factura_cuota_id, monto_pagado, mora_monto)

stock_deposito (id, empresa_id, producto_id, cantidad, deposito_id)

movimientos_inventario (id, empresa_id, producto_id, tipo_movimiento,
                        cantidad, fecha_movimiento)

sesiones_caja (id, empresa_id, caja_id, fecha_apertura, fecha_cierre,
               total_ingresos, total_egresos, diferencia)

vendedores_cobradores (id, empresa_id, nombre, tipo[vendedor|cobrador|ambos], activo)

personas (id, razon_social, nro_documento, telefono, celular)

clientes (id, persona_id, saldo_pendiente, limite_credito,
          bloqueado_credito, fecha_ultimo_pago, dias_mora_maximo)

Devolvé SIEMPRE JSON válido con esta estructura:
{
  "sql": "SELECT ...",
  "titulo": "título descriptivo del reporte",
  "tipo_grafico": "bar|line|pie|table|number|none",
  "explicacion": "1-2 oraciones explicando qué muestra este reporte"
}
```

### Seguridad SQL

```typescript
function validarSQL(sql: string, empresaId: string): void {
  const upper = sql.toUpperCase().trim();
  if (!upper.startsWith('SELECT')) throw new Error('Solo se permiten consultas SELECT');
  const peligrosas = ['DROP', 'DELETE', 'UPDATE', 'INSERT', 'ALTER', 'TRUNCATE', 'EXEC', '--'];
  for (const p of peligrosas) {
    if (upper.includes(p)) throw new Error(`Operación no permitida: ${p}`);
  }
  // El empresa_id se pasa como parámetro $1, no interpolado
}
```

---

## Frontend — Estructura de componentes

```
pages/
└── AIDashboard/
    ├── AIDashboardPage.tsx          ← Layout principal con sidebar
    ├── components/
    │   ├── sidebar/
    │   │   └── AIDashboardSidebar.tsx
    │   ├── resumen/
    │   │   ├── ResumenEjecutivo.tsx  ← Narrativa principal + KPIs
    │   │   └── KpiCard.tsx           ← Número + explicación + tendencia
    │   ├── alertas/
    │   │   ├── AlertasPanel.tsx      ← Lista alertas con criticidad
    │   │   └── AlertaCard.tsx        ← Tarjeta individual con acción sugerida
    │   ├── timeline/
    │   │   └── TimelineInteligente.tsx
    │   ├── predicciones/
    │   │   ├── PrediccionesPanel.tsx
    │   │   ├── ProyeccionCobranza.tsx
    │   │   ├── ClientesRiesgo.tsx
    │   │   └── ProyeccionVentas.tsx
    │   ├── chat/
    │   │   ├── ChatIA.tsx            ← Interface conversacional
    │   │   ├── ChatMensaje.tsx       ← Burbuja user/assistant
    │   │   ├── ChatResultado.tsx     ← Tabla/gráfico del resultado
    │   │   ├── ChatFavoritos.tsx     ← Panel favoritos
    │   │   └── DynamicChart.tsx     ← Recharts dinámico por tipo
    │   └── config/
    │       └── AIConfigPanel.tsx     ← API key, modelo, frecuencia
```

### Librería de gráficos: **Recharts**

- Liviana, declarativa, compatible React
- `BarChart`, `LineChart`, `PieChart` para el 20% visual
- `ResponsiveContainer` para adaptarse al layout

### UX del Chat

```
┌─────────────────────────────────────────────────────┐
│  Chat con IA                            [Favoritos] │
│ ─────────────────────────────────────────────────  │
│                                                     │
│  👤 ¿Cuánto cobró cada cobrador este mes?           │
│                                                     │
│  🤖 En abril 2026, tus cobradores registraron       │
│     los siguientes cobros:                          │
│     ┌──────────────────────────────────┐           │
│     │ [Gráfico de barras horizontal]   │           │
│     └──────────────────────────────────┘           │
│     Juan López lidera con Gs. 8.2M cobrados         │
│     en 34 recibos. Ana García tuvo su mejor         │
│     semana en la 2da quincena.              [★]     │
│                                                     │
│  ┌─────────────────────────────────────────────┐   │
│  │ Preguntá algo...                    [Enviar] │   │
│  └─────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────┘
```

---

## Módulo de Configuración IA (`/configuracion/ia`)

Protegido por rol `admin` o `gerente`:

```
┌─────────────────────────────────────────────────────┐
│  Configuración IA                                   │
│ ─────────────────────────────────────────────────  │
│  Proveedor: [Anthropic ▾]  Modelo: [Sonnet 4.6 ▾]  │
│  API Key:   [••••••••••••••••••••]  [Probar] [Ver]  │
│                                                     │
│  Frecuencia de análisis: [Diario a las 2:00 AM ▾]  │
│  (o expresión cron personalizada)                   │
│                                                     │
│  Umbrales de alerta:                                │
│  • Mora crítica:     [30] días                      │
│  • Stock bajo:       [5] unidades                   │
│  • Venta notable:    [Gs. 1.000.000]               │
│                                                     │
│  [Guardar]          [Forzar recálculo ahora]        │
│                                                     │
│  Último análisis: hace 3 horas (tokens: 2.450)     │
└─────────────────────────────────────────────────────┘
```

---

## Endpoints backend

```
POST   /ai-config/configurar           → guardar config empresa
GET    /ai-config/estado               → estado actual + ultimo recalculo
POST   /ai-config/probar-conexion      → test API key
POST   /ai-config/recalcular           → forzar recálculo manual

GET    /ai-dashboard/resumen           → insight resumen_ejecutivo vigente
GET    /ai-dashboard/alertas           → alertas no leídas/no resueltas
PATCH  /ai-dashboard/alertas/:id/leer  → marcar leída
PATCH  /ai-dashboard/alertas/:id/resolver → marcar resuelta
GET    /ai-dashboard/timeline          → eventos ordenados por relevancia
GET    /ai-dashboard/predicciones      → todas las predicciones vigentes

POST   /ai-chat/sesiones               → crear nueva sesión
GET    /ai-chat/sesiones               → listar sesiones del usuario
GET    /ai-chat/sesiones/:id           → sesión con mensajes
POST   /ai-chat/sesiones/:id/mensajes  → enviar pregunta
POST   /ai-chat/favoritos              → guardar favorito
GET    /ai-chat/favoritos              → listar favoritos
DELETE /ai-chat/favoritos/:id          → eliminar favorito
POST   /ai-chat/favoritos/:id/ejecutar → re-ejecutar favorito
```

---

## Fases de implementación

### Fase 1 — Infraestructura base (sin LLM)

**Objetivo:** Tener las tablas, endpoints y UI base funcionando con datos reales pero sin llamadas a IA todavía.

- [ ] Migración DB: crear las 7 tablas nuevas
- [ ] `AiConfigModule`: CRUD configuración + encriptación API key (AES-256)
- [ ] `InsightsService`: calcular métricas SQL (sin LLM, devolver datos crudos)
- [ ] `AlertsService`: detección de anomalías basada en reglas (sin LLM)
- [ ] UI: layout sidebar + secciones vacías + panel configuración
- [ ] UI: KpiCards con datos reales del período

**Valor entregado:** Dashboard con métricas reales aunque sin narrativa IA.

---

### Fase 2 — Narrativa IA + Alertas inteligentes

**Objetivo:** Primer contacto con el LLM. Insights con texto generado.

- [ ] `AiProviderService`: abstracción multi-proveedor (Anthropic primero)
- [ ] `InsightsService`: integrar LLM para generar narrativa
- [ ] `CronJob`: precálculo nocturno con frecuencia configurable
- [ ] Endpoint recálculo manual
- [ ] `AlertsService`: enriquecer alertas con descripción y acción sugerida via LLM
- [ ] UI: `ResumenEjecutivo` con narrativa real
- [ ] UI: `AlertasPanel` con tarjetas de alerta + acción sugerida

**Valor entregado:** El gerente abre el dashboard y lee un análisis real del negocio.

---

### Fase 3 — Chat IA

**Objetivo:** El gerente puede pedir cualquier reporte en lenguaje natural.

- [ ] `ChatService`: text-to-SQL + validación + ejecución segura
- [ ] Schema context string completo
- [ ] Historial de sesiones + mensajes
- [ ] UI: `ChatIA` completo con burbujas, gráficos dinámicos
- [ ] UI: `ChatFavoritos` + guardar/ejecutar favoritos
- [ ] Soporte OpenAI en `AiProviderService`

**Valor entregado:** Reportes ad-hoc sin necesidad de desarrollo.

---

### Fase 4 — Timeline + Predicciones

**Objetivo:** Visión proactiva y predictiva del negocio.

- [ ] `TimelineService`: detección y scoring de eventos
- [ ] `PredictionsService`: proyección cobranza + riesgo mora + ventas
- [ ] UI: `TimelineInteligente` visual
- [ ] UI: `PrediccionesPanel` con bandas de confianza
- [ ] Soporte Gemini en `AiProviderService`

**Valor entregado:** El sistema anticipa problemas antes de que ocurran.

---

### Fase 5 — Notificaciones externas (futuro)

- [ ] Email via `empresas_correo`
- [ ] WhatsApp via Twilio (ya integrado)
- [ ] Configuración por empresa: qué tipos de alerta van por qué canal
- [ ] `NotificacionService` genérico

---

## Dependencias npm necesarias

**Backend:**

```json
{
  "@anthropic-ai/sdk": "^0.x",
  "openai": "^4.x",
  "@google/generative-ai": "^0.x",
  "node-cron": "^3.x",
  "crypto": "built-in node"
}
```

**Frontend:**

```json
{
  "recharts": "^2.x"
}
```

---

## Consideraciones de seguridad

1. **API keys encriptadas** en DB con AES-256, nunca en texto plano
2. **Multi-tenant**: todo filtrado por `empresa_id` del usuario autenticado
3. **SQL injection**: parámetros via `$1`, no string interpolation
4. **Whitelist SQL**: solo SELECT, validación antes de ejecutar
5. **Timeout**: 10s máximo por query
6. **Rate limiting**: máx 20 consultas chat/hora por empresa (evitar abuso)
7. **Logs de uso**: registrar tokens usados por empresa para transparencia

---

## Estimación de costo IA por empresa/mes

| Modelo        | Insights diarios | Chat (50 consultas/mes) | Total aprox.   |
| ------------- | ---------------- | ----------------------- | -------------- |
| Claude Haiku  | ~$0.10           | ~$0.20                  | **~$0.30/mes** |
| Claude Sonnet | ~$1.50           | ~$3.00                  | **~$4.50/mes** |
| GPT-4o-mini   | ~$0.05           | ~$0.15                  | **~$0.20/mes** |

_Cada empresa paga directamente a su proveedor con su propia API key._
