# Validación de Dependencias: MGN-015, MGN-016, MGN-017, MGN-018

**Fecha:** 2025-12-05
**Estado:** REQUIERE ADAPTACIONES
**Revisión:** v1.0

## Resumen Ejecutivo

Se realizó un análisis exhaustivo de las dependencias entre los módulos nuevos/modificados (MGN-015 a MGN-018) y los módulos existentes (MGN-001 a MGN-014). Se identificaron **dependencias faltantes críticas**, **impactos no contemplados**, y **adaptaciones necesarias**.

### Veredicto General

| Módulo | Estado | Dependencias Faltantes | Impactos No Contemplados |
|--------|--------|------------------------|--------------------------|
| MGN-015 | ⚠️ Requiere ajustes | MGN-014, MGN-004 | 3 críticos |
| MGN-016 | ⚠️ Requiere ajustes | MGN-014 | 4 altos |
| MGN-017 | ⚠️ Requiere ajustes | MGN-014 | 3 altos |
| MGN-018 | ⚠️ Requiere ajustes | MGN-014 | 4 altos |

---

## 1. Dependencias Faltantes Críticas

### 1.1 Todos los módulos → MGN-014 (Mensajería y Notificaciones)

**Problema:** Ninguno de los módulos nuevos declara dependencia con MGN-014, pero TODOS requieren:
- Auditoría de cambios (Tracking automático)
- Notificaciones de eventos
- Sistema de mensajes internos

**Módulos Afectados:**
```
MGN-015 (Billing)    → Notificar cambios de suscripción, facturas
MGN-016 (Pagos)      → Confirmar pagos, alertar fallas
MGN-017 (WhatsApp)   → Logging de mensajes, escalaciones
MGN-018 (AI Agents)  → Historial de conversaciones, feedback
```

**Acción Requerida:** Agregar `MGN-014` como dependencia en todos los READMEs.

---

### 1.2 MGN-015 → MGN-004 (Financiero)

**Problema:** MGN-015 genera facturas de suscripción pero no tiene integración para generar asientos contables.

**Impacto:**
- Ingresos por suscripción no se registran en contabilidad
- No hay conciliación entre facturación SaaS y libro mayor
- Imposible cerrar mes contable correctamente

**Acción Requerida:**
```sql
-- Agregar a MGN-015 esquema de integración con MGN-004
CREATE TABLE billing.accounting_mappings (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    event_type VARCHAR(50) NOT NULL, -- subscription_payment, refund, credit
    debit_account_id UUID REFERENCES financial.accounts(id),
    credit_account_id UUID REFERENCES financial.accounts(id),
    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
```

---

### 1.3 MGN-016 → MGN-004 (Financiero)

**Problema:** Los pagos procesados por MercadoPago/Clip deben generar asientos contables automáticos.

**Impacto:**
- Pagos recibidos no se reflejan en contabilidad
- Comisiones de procesadores no se registran
- Conciliación bancaria imposible

**Acción Requerida:** Crear webhook interno que dispare asientos:
```typescript
// Al confirmar pago en MGN-016
async function onPaymentConfirmed(payment: PaymentTransaction) {
  await createAccountingEntry({
    journal: 'bank',
    entries: [
      { account: 'bank_account', debit: payment.net_amount },
      { account: 'payment_fees', debit: payment.fee_amount },
      { account: 'accounts_receivable', credit: payment.amount }
    ],
    reference: payment.id,
    date: payment.approved_at
  });
}
```

---

## 2. Interdependencia Circular: MGN-017 ↔ MGN-018

### Problema Identificado

```
MGN-017 (WhatsApp)
  └─ RF-005 (Chatbot) necesita MGN-018 (AI Agents)

MGN-018 (AI Agents)
  └─ Necesita MGN-017 como canal de comunicación
```

### Riesgo

- Ciclo de dependencia que complica el orden de implementación
- ¿Quién se implementa primero?

### Solución: Arquitectura en Capas

```
┌─────────────────────────────────────────────────────────────┐
│                    CAPA DE APLICACIÓN                        │
│  ┌─────────────────────────────────────────────────────────┐│
│  │  MGN-017-005 (Chatbot) = Orquestador de flujos          ││
│  │  - Usa MGN-017 para enviar/recibir mensajes             ││
│  │  - Usa MGN-018 para respuestas inteligentes             ││
│  └─────────────────────────────────────────────────────────┘│
├─────────────────────────────────────────────────────────────┤
│                    CAPA DE SERVICIOS                         │
│  ┌────────────────────┐    ┌────────────────────┐          │
│  │ MGN-017 (WhatsApp) │    │ MGN-018 (AI Core)  │          │
│  │ - Conexión API     │    │ - LLM Processing   │          │
│  │ - Mensajería       │    │ - Knowledge Base   │          │
│  │ - Templates        │    │ - Tools            │          │
│  └────────────────────┘    └────────────────────┘          │
└─────────────────────────────────────────────────────────────┘
```

**Orden de Implementación:**
1. MGN-017 RF-001 a RF-004 (infraestructura WhatsApp) - NO requiere MGN-018
2. MGN-018 RF-001 a RF-006 (infraestructura AI) - NO requiere MGN-017
3. MGN-017 RF-005 (Chatbot) - Integra ambos módulos

---

## 3. Impactos en Módulos Existentes

### 3.1 Impacto en MGN-001 (Autenticación/Multi-Tenant)

**Cambios Requeridos:**

| Entidad | Cambio | Razón |
|---------|--------|-------|
| `core_tenants.tenants` | Agregar `subscription_status` | Bloquear acceso si suscripción vencida |
| `core_rbac.roles` | Nuevos roles | `billing_admin`, `ai_admin`, `messaging_admin` |
| `core_rbac.permissions` | Nuevos permisos | `billing:*`, `ai:*`, `whatsapp:*`, `payments:*` |

**SQL Requerido:**
```sql
-- Agregar campo de estado de suscripción para control de acceso
ALTER TABLE core_tenants.tenants
ADD COLUMN IF NOT EXISTS subscription_status VARCHAR(20) DEFAULT 'trial';

-- Nuevos roles
INSERT INTO core_rbac.roles (code, name, description, is_system) VALUES
('billing_admin', 'Administrador de Facturación', 'Gestiona planes y suscripciones', true),
('ai_admin', 'Administrador de IA', 'Configura agentes y knowledge bases', true),
('messaging_admin', 'Administrador de Mensajería', 'Gestiona WhatsApp y chatbots', true),
('payments_admin', 'Administrador de Pagos', 'Configura terminales y procesa pagos', true)
ON CONFLICT (code) DO NOTHING;
```

---

### 3.2 Impacto en MGN-003 (Catálogos)

**Cambios Requeridos:**

| Entidad | Cambio | Razón |
|---------|--------|-------|
| `catalogs.contacts` | Agregar `whatsapp_number` | MGN-017 necesita identificar contactos |
| `catalogs.contacts` | Agregar `whatsapp_opt_in` | Cumplimiento legal para marketing |
| `catalogs.payment_methods` | Agregar tipos | `mercadopago`, `clip`, `stripe_terminal` |

**SQL Requerido:**
```sql
-- Campos para WhatsApp en contactos
ALTER TABLE catalogs.contacts
ADD COLUMN IF NOT EXISTS whatsapp_number VARCHAR(20),
ADD COLUMN IF NOT EXISTS whatsapp_opt_in BOOLEAN DEFAULT false,
ADD COLUMN IF NOT EXISTS whatsapp_opt_in_date TIMESTAMPTZ;

CREATE INDEX IF NOT EXISTS idx_contacts_whatsapp
ON catalogs.contacts(whatsapp_number) WHERE whatsapp_number IS NOT NULL;

-- Validación de formato
ALTER TABLE catalogs.contacts
ADD CONSTRAINT chk_whatsapp_format
CHECK (whatsapp_number IS NULL OR whatsapp_number ~ '^\+[1-9][0-9]{6,14}$');
```

---

### 3.3 Impacto en MGN-007 (Ventas)

**Cambios Requeridos:**

| Entidad | Cambio | Razón |
|---------|--------|-------|
| `sales.orders` | Agregar `payment_status` | Rastrear estado de pago de MGN-016 |
| `sales.orders` | Agregar `payment_transaction_id` | Referencia a transacción |
| Workflow | Nuevo estado | `awaiting_payment` antes de `confirmed` |

**SQL Requerido:**
```sql
-- Campos para integración con MGN-016
ALTER TABLE sales.orders
ADD COLUMN IF NOT EXISTS payment_status VARCHAR(20) DEFAULT 'pending',
ADD COLUMN IF NOT EXISTS payment_transaction_id UUID,
ADD COLUMN IF NOT EXISTS paid_at TIMESTAMPTZ;

-- Actualizar constraint de estados
ALTER TABLE sales.orders DROP CONSTRAINT IF EXISTS chk_order_status;
ALTER TABLE sales.orders ADD CONSTRAINT chk_order_status
CHECK (status IN ('draft', 'sent', 'awaiting_payment', 'confirmed', 'done', 'cancelled'));
```

---

### 3.4 Impacto en MGN-009 (CRM)

**Cambios Requeridos:**

| Entidad | Cambio | Razón |
|---------|--------|-------|
| `crm.leads` | Agregar `whatsapp_conversation_id` | Vincular con MGN-017 |
| `crm.leads` | Agregar `source_channel` | Identificar origen (whatsapp, web, etc.) |
| `crm.leads` | Agregar `ai_score` | Lead scoring de MGN-018 |

**SQL Requerido:**
```sql
-- Campos para integración con WhatsApp y AI
ALTER TABLE crm.leads
ADD COLUMN IF NOT EXISTS whatsapp_conversation_id UUID,
ADD COLUMN IF NOT EXISTS source_channel VARCHAR(50) DEFAULT 'manual',
ADD COLUMN IF NOT EXISTS ai_score DECIMAL(5,2),
ADD COLUMN IF NOT EXISTS ai_score_updated_at TIMESTAMPTZ;

-- Índice para búsqueda por conversación
CREATE INDEX IF NOT EXISTS idx_leads_whatsapp_conv
ON crm.leads(whatsapp_conversation_id) WHERE whatsapp_conversation_id IS NOT NULL;
```

---

## 4. Feature Flags Centralizados

### Problema

Cada módulo nuevo define sus propios feature flags en `subscription_plans.features`, pero no hay una forma centralizada de consultarlos.

### Solución: Función Auxiliar

```sql
-- Crear en schema billing
CREATE OR REPLACE FUNCTION billing.has_feature(
    p_tenant_id UUID,
    p_feature_name VARCHAR
) RETURNS BOOLEAN AS $$
DECLARE
    v_features JSONB;
BEGIN
    SELECT sp.features INTO v_features
    FROM billing.subscriptions bs
    JOIN billing.subscription_plans sp ON bs.plan_id = sp.id
    WHERE bs.tenant_id = p_tenant_id
      AND bs.status = 'active'
    ORDER BY bs.created_at DESC
    LIMIT 1;

    IF v_features IS NULL THEN
        RETURN false;
    END IF;

    RETURN COALESCE((v_features->>p_feature_name)::boolean, false);
END;
$$ LANGUAGE plpgsql STABLE;

-- Función para obtener límite numérico
CREATE OR REPLACE FUNCTION billing.get_feature_limit(
    p_tenant_id UUID,
    p_feature_name VARCHAR
) RETURNS INTEGER AS $$
DECLARE
    v_features JSONB;
BEGIN
    SELECT sp.features INTO v_features
    FROM billing.subscriptions bs
    JOIN billing.subscription_plans sp ON bs.plan_id = sp.id
    WHERE bs.tenant_id = p_tenant_id
      AND bs.status = 'active'
    ORDER BY bs.created_at DESC
    LIMIT 1;

    IF v_features IS NULL THEN
        RETURN 0;
    END IF;

    RETURN COALESCE((v_features->>p_feature_name)::integer, 0);
END;
$$ LANGUAGE plpgsql STABLE;
```

### Uso en Módulos

```typescript
// En cualquier módulo que necesite verificar feature
async function checkFeatureEnabled(tenantId: string, feature: string): Promise<boolean> {
  const result = await db.query(
    'SELECT billing.has_feature($1, $2) as enabled',
    [tenantId, feature]
  );
  return result.rows[0].enabled;
}

// Ejemplo en MGN-018
const canUseAI = await checkFeatureEnabled(tenantId, 'ai_agents_enabled');
const tokenLimit = await db.query(
  'SELECT billing.get_feature_limit($1, $2) as limit',
  [tenantId, 'ai_monthly_token_limit']
);
```

---

## 5. Matriz de Features por Plan

### Features que deben definirse en planes

```json
{
  "starter": {
    "max_users": 5,
    "max_companies": 1,

    "mercadopago_enabled": false,
    "clip_enabled": false,
    "stripe_enabled": false,

    "whatsapp_enabled": false,
    "whatsapp_chatbot": false,
    "whatsapp_marketing": false,
    "whatsapp_max_accounts": 0,

    "ai_agents_enabled": false,
    "ai_max_agents": 0,
    "ai_kb_max_documents": 0,
    "ai_monthly_token_limit": 0
  },

  "professional": {
    "max_users": 25,
    "max_companies": 3,

    "mercadopago_enabled": true,
    "clip_enabled": true,
    "stripe_enabled": false,

    "whatsapp_enabled": true,
    "whatsapp_chatbot": true,
    "whatsapp_marketing": false,
    "whatsapp_max_accounts": 1,

    "ai_agents_enabled": true,
    "ai_max_agents": 3,
    "ai_kb_max_documents": 100,
    "ai_monthly_token_limit": 500000
  },

  "enterprise": {
    "max_users": -1,
    "max_companies": -1,

    "mercadopago_enabled": true,
    "clip_enabled": true,
    "stripe_enabled": true,

    "whatsapp_enabled": true,
    "whatsapp_chatbot": true,
    "whatsapp_marketing": true,
    "whatsapp_max_accounts": 10,

    "ai_agents_enabled": true,
    "ai_max_agents": -1,
    "ai_kb_max_documents": 500,
    "ai_monthly_token_limit": 5000000,
    "ai_custom_tools": true
  }
}
```

---

## 6. Webhooks de Integración Requeridos

### Eventos que deben propagarse entre módulos

```typescript
// Eventos de MGN-015 (Billing)
type BillingEvents = {
  'subscription.created': { tenant_id: string; plan_id: string; };
  'subscription.upgraded': { tenant_id: string; old_plan: string; new_plan: string; };
  'subscription.downgraded': { tenant_id: string; old_plan: string; new_plan: string; };
  'subscription.canceled': { tenant_id: string; reason: string; };
  'subscription.payment_failed': { tenant_id: string; invoice_id: string; };
  'invoice.paid': { tenant_id: string; invoice_id: string; amount: number; };
};

// Eventos de MGN-016 (Pagos)
type PaymentEvents = {
  'payment.approved': { tenant_id: string; order_id: string; amount: number; };
  'payment.declined': { tenant_id: string; order_id: string; reason: string; };
  'payment.refunded': { tenant_id: string; transaction_id: string; amount: number; };
  'terminal.connected': { tenant_id: string; terminal_id: string; };
  'terminal.disconnected': { tenant_id: string; terminal_id: string; };
};

// Eventos de MGN-017 (WhatsApp)
type WhatsAppEvents = {
  'message.received': { tenant_id: string; conversation_id: string; from: string; };
  'message.sent': { tenant_id: string; conversation_id: string; to: string; };
  'conversation.escalated': { tenant_id: string; conversation_id: string; reason: string; };
  'quality.warning': { tenant_id: string; account_id: string; rating: string; };
};

// Eventos de MGN-018 (AI Agents)
type AIAgentEvents = {
  'conversation.started': { tenant_id: string; agent_id: string; channel: string; };
  'conversation.completed': { tenant_id: string; conversation_id: string; resolved: boolean; };
  'tokens.threshold_reached': { tenant_id: string; usage_percent: number; };
  'tokens.limit_exceeded': { tenant_id: string; };
  'feedback.received': { tenant_id: string; conversation_id: string; rating: number; };
};
```

### Sistema de Eventos Centralizado

```sql
-- Tabla de eventos del sistema (para debugging y auditoría)
CREATE TABLE IF NOT EXISTS system.event_log (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID,
    event_type VARCHAR(100) NOT NULL,
    source_module VARCHAR(20) NOT NULL,
    payload JSONB NOT NULL,
    processed BOOLEAN DEFAULT false,
    processed_at TIMESTAMPTZ,
    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_events_tenant ON system.event_log(tenant_id);
CREATE INDEX idx_events_type ON system.event_log(event_type);
CREATE INDEX idx_events_unprocessed ON system.event_log(processed) WHERE processed = false;
```

---

## 7. Plan de Implementación de Adaptaciones

### Fase 1: Preparación de Infraestructura (Semana 1)

| Tarea | Prioridad | Esfuerzo |
|-------|-----------|----------|
| Crear funciones `has_feature()` y `get_feature_limit()` | CRÍTICA | 2h |
| Crear tabla `system.event_log` | CRÍTICA | 1h |
| Actualizar roles en MGN-001 | ALTA | 4h |
| Agregar campos en MGN-003 (contacts) | ALTA | 2h |

### Fase 2: Integración MGN-015 ↔ MGN-004 (Semana 2)

| Tarea | Prioridad | Esfuerzo |
|-------|-----------|----------|
| Crear `billing.accounting_mappings` | CRÍTICA | 4h |
| Implementar auto-generación de asientos | CRÍTICA | 8h |
| Crear job de conciliación diaria | ALTA | 4h |

### Fase 3: Integración MGN-016 ↔ MGN-007 ↔ MGN-004 (Semana 3)

| Tarea | Prioridad | Esfuerzo |
|-------|-----------|----------|
| Agregar campos en MGN-007 (orders) | ALTA | 2h |
| Webhook `payment.approved` → actualizar orden | CRÍTICA | 4h |
| Auto-generar asientos de pagos | CRÍTICA | 8h |
| Reconciliador de comisiones | MEDIA | 4h |

### Fase 4: Integración MGN-017 ↔ MGN-009 (Semana 4)

| Tarea | Prioridad | Esfuerzo |
|-------|-----------|----------|
| Agregar campos en MGN-009 (leads) | ALTA | 2h |
| Auto-crear leads desde WhatsApp | ALTA | 8h |
| Vincular conversaciones a leads | ALTA | 4h |

### Fase 5: Integración MGN-018 ↔ MGN-015 (Semana 5)

| Tarea | Prioridad | Esfuerzo |
|-------|-----------|----------|
| Implementar control de tokens | CRÍTICA | 8h |
| Alertas de consumo | ALTA | 4h |
| Rate limiting por tenant | ALTA | 4h |

---

## 8. Riesgos y Mitigaciones

| Riesgo | Probabilidad | Impacto | Mitigación |
|--------|--------------|---------|------------|
| Feature flags no sincronizados | ALTA | CRÍTICO | Función centralizada + cache |
| Ciclo de dependencia 017↔018 | MEDIA | ALTO | Arquitectura en capas |
| Fallo en webhook de pago | MEDIA | CRÍTICO | Dead letter queue + retry |
| Exceso de tokens en AI | ALTA | ALTO | Rate limiting estricto |
| Datos sensibles en logs | MEDIA | CRÍTICO | PII masking automático |
| Asientos contables incorrectos | BAJA | CRÍTICO | Validación + rollback |

---

## 9. Checklist de Validación Pre-Implementación

### MGN-015 (Billing)
- [ ] Agregar dependencia con MGN-014 en README
- [ ] Agregar dependencia con MGN-004 en README
- [ ] Crear `billing.has_feature()` function
- [ ] Definir eventos de suscripción
- [ ] Mapear cuentas contables

### MGN-016 (Pagos)
- [ ] Agregar dependencia con MGN-014 en README
- [ ] Definir estados de pago en MGN-007
- [ ] Crear webhooks de integración
- [ ] Mapear asientos de pagos y comisiones

### MGN-017 (WhatsApp)
- [ ] Agregar dependencia con MGN-014 en README
- [ ] Agregar campos en MGN-003 (contacts)
- [ ] Agregar campos en MGN-009 (leads)
- [ ] Definir templates de notificación para MGN-007

### MGN-018 (AI Agents)
- [ ] Agregar dependencia con MGN-014 en README
- [ ] Implementar control de tokens en MGN-015
- [ ] Definir tools para MGN-007 y MGN-009
- [ ] Implementar rate limiting

---

## 10. Conclusión

Las modificaciones a los módulos MGN-015, MGN-016, MGN-017 y MGN-018 **pueden implementarse** pero requieren las adaptaciones documentadas en este análisis. Las principales áreas de atención son:

1. **Dependencia faltante con MGN-014** en todos los módulos nuevos
2. **Integración contable** entre MGN-015/MGN-016 y MGN-004
3. **Control centralizado de features** mediante funciones auxiliares
4. **Interdependencia 017↔018** resuelta con arquitectura en capas
5. **Campos adicionales** en MGN-003, MGN-007 y MGN-009

Implementando estas adaptaciones, el sistema quedará correctamente integrado y sin impactos inesperados.
