commit a92a7efccc112f6f226b64099bc30a5c8419ef41 Author: Claude AI Date: Thu Jan 29 09:29:57 2026 +0000 Initial commit: Documentación completa del proyecto WhatsApp Centralizado - README principal con descripción del proyecto - Documento de diseño completo (arquitectura, DB, flujos) - Documentación de API REST y WebSocket - Guía del Flow Builder (30+ tipos de nodos) - Documentación de integración con Odoo - Guía de despliegue con Docker - Esquema de base de datos - Estructura de carpetas del proyecto - Archivo .env.example con todas las variables Co-Authored-By: Claude Opus 4.5 diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..3fba72f --- /dev/null +++ b/.env.example @@ -0,0 +1,109 @@ +# ============================================================================= +# WhatsApp Centralizado - Variables de Entorno +# ============================================================================= +# Copiar este archivo a .env y configurar los valores + +# ----------------------------------------------------------------------------- +# Base de Datos PostgreSQL +# ----------------------------------------------------------------------------- +DB_USER=whatsapp_admin +DB_PASSWORD=cambiar_por_password_seguro_32_caracteres +DB_HOST=postgres +DB_PORT=5432 +DB_NAME=whatsapp_central + +# URL completa de conexión (generada automáticamente en docker-compose) +# DATABASE_URL=postgresql://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME} + +# ----------------------------------------------------------------------------- +# Redis +# ----------------------------------------------------------------------------- +REDIS_HOST=redis +REDIS_PORT=6379 +REDIS_URL=redis://redis:6379 + +# ----------------------------------------------------------------------------- +# JWT / Autenticación +# ----------------------------------------------------------------------------- +# Generar con: openssl rand -base64 64 +JWT_SECRET=cambiar_por_secreto_aleatorio_64_caracteres +JWT_ACCESS_TOKEN_EXPIRE_MINUTES=60 +JWT_REFRESH_TOKEN_EXPIRE_DAYS=7 + +# ----------------------------------------------------------------------------- +# Dominio / URLs +# ----------------------------------------------------------------------------- +DOMAIN=localhost +FRONTEND_URL=http://localhost:3000 +API_URL=http://localhost:8000 +WHATSAPP_CORE_URL=http://whatsapp-core:3001 + +# ----------------------------------------------------------------------------- +# WhatsApp Core (Node.js) +# ----------------------------------------------------------------------------- +NODE_ENV=production +WS_PORT=3001 + +# ----------------------------------------------------------------------------- +# Odoo (Opcional) +# ----------------------------------------------------------------------------- +# Configurar después de instalar +ODOO_URL=https://odoo.tuempresa.com +ODOO_DB=production +ODOO_USER=api-whatsapp@tuempresa.com +ODOO_API_KEY= + +# ----------------------------------------------------------------------------- +# OpenAI / IA (Opcional) +# ----------------------------------------------------------------------------- +# Para nodos de AI Response +OPENAI_API_KEY= +OPENAI_MODEL=gpt-4 + +# Anthropic Claude (alternativa) +ANTHROPIC_API_KEY= +ANTHROPIC_MODEL=claude-3-sonnet-20240229 + +# Ollama local (alternativa) +OLLAMA_URL=http://localhost:11434 +OLLAMA_MODEL=llama2 + +# ----------------------------------------------------------------------------- +# Email (Opcional - para notificaciones) +# ----------------------------------------------------------------------------- +SMTP_HOST=smtp.tuempresa.com +SMTP_PORT=587 +SMTP_USER=notificaciones@tuempresa.com +SMTP_PASSWORD= +SMTP_FROM=WhatsApp Central + +# ----------------------------------------------------------------------------- +# Webhooks +# ----------------------------------------------------------------------------- +# Secret para validar webhooks entrantes de Odoo +ODOO_WEBHOOK_SECRET=cambiar_por_secreto_aleatorio + +# ----------------------------------------------------------------------------- +# Seguridad +# ----------------------------------------------------------------------------- +# Bcrypt rounds para passwords +BCRYPT_ROUNDS=12 + +# Rate limiting +RATE_LIMIT_REQUESTS=100 +RATE_LIMIT_WINDOW=60 + +# CORS (separado por comas) +CORS_ORIGINS=http://localhost:3000,https://chat.tuempresa.com + +# ----------------------------------------------------------------------------- +# Logging +# ----------------------------------------------------------------------------- +LOG_LEVEL=INFO +LOG_FORMAT=json + +# ----------------------------------------------------------------------------- +# Desarrollo (solo para dev) +# ----------------------------------------------------------------------------- +DEBUG=false +RELOAD=false diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a7b627c --- /dev/null +++ b/.gitignore @@ -0,0 +1,197 @@ +# ============================================================================= +# WhatsApp Centralizado - Git Ignore +# ============================================================================= + +# ----------------------------------------------------------------------------- +# Environment +# ----------------------------------------------------------------------------- +.env +.env.local +.env.*.local +*.env + +# ----------------------------------------------------------------------------- +# Python +# ----------------------------------------------------------------------------- +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +.pytest_cache/ +.coverage +htmlcov/ +.tox/ +.nox/ +.hypothesis/ +.mypy_cache/ +.ruff_cache/ +*.cover + +# Virtual environments +venv/ +ENV/ +env/ +.venv/ + +# ----------------------------------------------------------------------------- +# Node.js +# ----------------------------------------------------------------------------- +node_modules/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.npm +.yarn-integrity +.pnpm-store/ + +# Build outputs +dist/ +build/ +.next/ +out/ + +# ----------------------------------------------------------------------------- +# IDE / Editors +# ----------------------------------------------------------------------------- +.idea/ +.vscode/ +*.swp +*.swo +*~ +.project +.classpath +.settings/ +*.sublime-workspace +*.sublime-project + +# ----------------------------------------------------------------------------- +# OS +# ----------------------------------------------------------------------------- +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db +*.bak +*.tmp +*.temp + +# ----------------------------------------------------------------------------- +# Docker +# ----------------------------------------------------------------------------- +# No ignorar docker-compose.yml ni Dockerfiles +# Solo ignorar archivos temporales +*.log +docker-compose.override.yml + +# ----------------------------------------------------------------------------- +# Sessions / Data +# ----------------------------------------------------------------------------- +sessions/ +*.session +*.session.json + +# WhatsApp Baileys sessions +services/whatsapp-core/sessions/ +auth_info*/ + +# ----------------------------------------------------------------------------- +# Uploads / Media +# ----------------------------------------------------------------------------- +uploads/ +media/ +*.mp3 +*.mp4 +*.wav +*.ogg +*.webm +*.pdf +*.doc +*.docx +*.xls +*.xlsx + +# Excepto archivos de ejemplo/docs +!docs/**/*.pdf + +# ----------------------------------------------------------------------------- +# Logs +# ----------------------------------------------------------------------------- +logs/ +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# ----------------------------------------------------------------------------- +# Database +# ----------------------------------------------------------------------------- +*.sql +*.sqlite +*.db +!database/migrations/*.sql + +# ----------------------------------------------------------------------------- +# Backups +# ----------------------------------------------------------------------------- +backups/ +*.bak +*.backup +*.dump + +# ----------------------------------------------------------------------------- +# Secrets / Certificates +# ----------------------------------------------------------------------------- +*.pem +*.key +*.crt +*.p12 +*.pfx +secrets/ +nginx/ssl/ + +# ----------------------------------------------------------------------------- +# Test +# ----------------------------------------------------------------------------- +coverage/ +.nyc_output/ +test-results/ +playwright-report/ + +# ----------------------------------------------------------------------------- +# Misc +# ----------------------------------------------------------------------------- +.cache/ +*.pid +*.seed +*.pid.lock +.grunt +.lock-wscript +.eslintcache +.stylelintcache +*.tsbuildinfo + +# Temporary files +tmp/ +temp/ +.tmp/ +.claude/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..f39f33f --- /dev/null +++ b/README.md @@ -0,0 +1,141 @@ +# WhatsApp Centralizado + +Plataforma de mensajería centralizada con automatización de chatbots, gestión multi-agente e integración profunda con Odoo. + +## Descripción + +WhatsApp Centralizado es una solución empresarial similar a Kommo, Wasapi, ManyChat y Brevo, diseñada para: + +- **Automatizar conversaciones** con un potente Flow Builder visual (drag & drop) +- **Gestionar equipos de agentes** con colas inteligentes, SLA y métricas +- **Integrar con Odoo** de forma bidireccional (CRM, Ventas, Inventario, Helpdesk, etc.) +- **Conectar múltiples números** de WhatsApp desde una sola plataforma + +## Características Principales + +### Flow Builder Visual +- 30+ tipos de nodos (mensajes, lógica, validación, acciones) +- Editor drag & drop con React Flow +- Variables y contexto de conversación +- A/B Testing integrado +- Integración con IA (GPT, Claude, Ollama) +- Sub-flujos reutilizables + +### Gestión Multi-Agente +- Sistema de colas (Ventas, Soporte, etc.) +- Asignación inteligente (round-robin, least-busy, skill-based) +- Transferencia bot → humano → bot +- Panel de supervisor en tiempo real +- SLA tracking con alertas +- Encuestas CSAT integradas + +### Integración Odoo +- Conexión bidireccional via XML-RPC +- 8 módulos soportados (Contactos, CRM, Ventas, Inventario, Helpdesk, Facturación, Calendario, Productos) +- 20+ acciones disponibles en flujos +- Automatizaciones Odoo → WhatsApp +- Módulo Odoo con widget de chat + +## Stack Tecnológico + +| Componente | Tecnología | +|------------|------------| +| WhatsApp Core | Node.js + TypeScript + Baileys | +| API Gateway | Python + FastAPI | +| Flow Engine | Python | +| Frontend | React + TypeScript | +| Base de Datos | PostgreSQL + Redis | +| Despliegue | Docker + Docker Compose | + +## Arquitectura + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ FRONTEND (React) │ +│ Dashboard │ Inbox Chat │ Flow Builder (React Flow) │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ API GATEWAY (FastAPI) │ +│ JWT Auth │ REST API │ WebSocket (tiempo real) │ +└─────────────────────────────────────────────────────────────────┘ + │ + ┌───────────────┼───────────────┐ + ▼ ▼ ▼ +┌──────────────────┐ ┌──────────────┐ ┌──────────────────────────┐ +│ WHATSAPP CORE │ │ FLOW ENGINE │ │ INTEGRATIONS │ +│ (Node.js) │ │ (Python) │ │ (Python) │ +│ Baileys │ │ Motor bot │ │ Odoo, Webhooks │ +└──────────────────┘ └──────────────┘ └──────────────────────────┘ + │ + ┌─────────┴─────────┐ + ▼ ▼ + ┌──────────┐ ┌──────────┐ + │PostgreSQL│ │ Redis │ + └──────────┘ └──────────┘ +``` + +## Inicio Rápido + +### Requisitos +- Docker 24.0+ +- Docker Compose 2.20+ +- 4GB RAM mínimo (8GB recomendado) + +### Instalación + +```bash +# Clonar repositorio +git clone https://git.consultoria-as.com/tu-usuario/WhatsAppCentralizado.git +cd WhatsAppCentralizado + +# Copiar configuración +cp .env.example .env + +# Editar variables de entorno +nano .env + +# Iniciar servicios +docker-compose up -d + +# Aplicar migraciones +docker-compose exec api-gateway alembic upgrade head + +# Crear usuario admin +docker-compose exec api-gateway python scripts/create_admin.py +``` + +### Acceso +- Frontend: http://localhost:3000 +- API: http://localhost:8000 +- Docs API: http://localhost:8000/docs + +## Documentación + +- [Diseño del Sistema](docs/plans/2026-01-29-whatsapp-centralizado-design.md) +- [Arquitectura](docs/architecture/README.md) +- [API Reference](docs/api/README.md) +- [Flow Builder](docs/flow-builder/README.md) +- [Integración Odoo](docs/odoo-integration/README.md) +- [Guía de Despliegue](docs/deployment/README.md) + +## Roadmap + +- [x] Diseño y arquitectura +- [ ] Fase 1: Fundación (WhatsApp Core + API + Frontend básico) +- [ ] Fase 2: Flow Engine Básico +- [ ] Fase 3: Inbox Avanzado + Multi-agente +- [ ] Fase 4: Flow Engine Avanzado +- [ ] Fase 5: Integración Odoo Completa +- [ ] Fase 6: Módulo Odoo +- [ ] Fase 7: Reportes y Analytics +- [ ] Fase 8: Multi-canal (Email, SMS) + +## Licencia + +Propietario - Todos los derechos reservados. + +## Contacto + +Desarrollado para uso interno empresarial. diff --git a/database/migrations/.gitkeep b/database/migrations/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/api/README.md b/docs/api/README.md new file mode 100644 index 0000000..9d5f339 --- /dev/null +++ b/docs/api/README.md @@ -0,0 +1,746 @@ +# API Reference + +## Información General + +- **Base URL:** `https://tu-dominio.com/api` +- **Autenticación:** Bearer Token (JWT) +- **Content-Type:** `application/json` + +## Autenticación + +### Login +```http +POST /auth/login +``` + +**Request:** +```json +{ + "email": "usuario@empresa.com", + "password": "tu_password" +} +``` + +**Response:** +```json +{ + "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...", + "token_type": "bearer", + "user": { + "id": "uuid", + "email": "usuario@empresa.com", + "name": "Usuario", + "role": "admin" + } +} +``` + +### Refresh Token +```http +POST /auth/refresh +``` + +### Logout +```http +POST /auth/logout +``` + +### Usuario Actual +```http +GET /auth/me +``` + +--- + +## Usuarios + +### Listar Usuarios +```http +GET /api/users +``` + +**Query Parameters:** +| Parámetro | Tipo | Descripción | +|-----------|------|-------------| +| role | string | Filtrar por rol (admin, supervisor, agent) | +| status | string | Filtrar por estado (online, offline, away) | +| page | int | Página (default: 1) | +| limit | int | Límite por página (default: 20) | + +### Crear Usuario +```http +POST /api/users +``` + +**Request:** +```json +{ + "email": "nuevo@empresa.com", + "password": "password123", + "name": "Nuevo Usuario", + "role": "agent" +} +``` + +### Obtener Usuario +```http +GET /api/users/{user_id} +``` + +### Actualizar Usuario +```http +PUT /api/users/{user_id} +``` + +### Eliminar Usuario +```http +DELETE /api/users/{user_id} +``` + +### Cambiar Estado +```http +PUT /api/users/{user_id}/status +``` + +**Request:** +```json +{ + "status": "away" +} +``` + +--- + +## Cuentas WhatsApp + +### Listar Cuentas +```http +GET /api/whatsapp/accounts +``` + +### Crear Cuenta (Iniciar conexión) +```http +POST /api/whatsapp/accounts +``` + +**Request:** +```json +{ + "name": "Ventas Principal" +} +``` + +**Response:** +```json +{ + "id": "uuid", + "name": "Ventas Principal", + "status": "disconnected", + "qr_code": null +} +``` + +### Obtener QR Code +```http +GET /api/whatsapp/accounts/{account_id}/qr +``` + +**Response:** +```json +{ + "qr_code": "data:image/png;base64,..." +} +``` + +### Estado de Cuenta +```http +GET /api/whatsapp/accounts/{account_id}/status +``` + +### Desconectar Cuenta +```http +POST /api/whatsapp/accounts/{account_id}/disconnect +``` + +### Reconectar Cuenta +```http +POST /api/whatsapp/accounts/{account_id}/reconnect +``` + +--- + +## Contactos + +### Listar Contactos +```http +GET /api/contacts +``` + +**Query Parameters:** +| Parámetro | Tipo | Descripción | +|-----------|------|-------------| +| search | string | Buscar por nombre, teléfono o email | +| tags | string[] | Filtrar por etiquetas | +| has_odoo | boolean | Filtrar sincronizados con Odoo | +| page | int | Página | +| limit | int | Límite | + +### Crear Contacto +```http +POST /api/contacts +``` + +**Request:** +```json +{ + "phone_number": "+525551234567", + "name": "Juan Pérez", + "email": "juan@email.com", + "company": "Empresa ABC", + "tags": ["lead", "interesado"], + "metadata": { + "producto_interes": "Laptop Pro" + } +} +``` + +### Obtener Contacto +```http +GET /api/contacts/{contact_id} +``` + +### Actualizar Contacto +```http +PUT /api/contacts/{contact_id} +``` + +### Agregar Etiquetas +```http +POST /api/contacts/{contact_id}/tags +``` + +**Request:** +```json +{ + "tags": ["vip", "cliente"] +} +``` + +### Sincronizar con Odoo +```http +POST /api/contacts/{contact_id}/sync-odoo +``` + +--- + +## Conversaciones + +### Listar Conversaciones +```http +GET /api/conversations +``` + +**Query Parameters:** +| Parámetro | Tipo | Descripción | +|-----------|------|-------------| +| status | string | bot, waiting, active, resolved | +| queue_id | uuid | Filtrar por cola | +| assigned_to | uuid | Filtrar por agente | +| priority | string | low, normal, high, urgent | +| page | int | Página | +| limit | int | Límite | + +### Obtener Conversación +```http +GET /api/conversations/{conversation_id} +``` + +**Response:** +```json +{ + "id": "uuid", + "contact": { + "id": "uuid", + "name": "Juan Pérez", + "phone_number": "+525551234567" + }, + "whatsapp_account": { + "id": "uuid", + "name": "Ventas Principal" + }, + "status": "active", + "priority": "normal", + "assigned_to": { + "id": "uuid", + "name": "María García" + }, + "queue": { + "id": "uuid", + "name": "Ventas" + }, + "current_flow": null, + "sla_first_response_met": true, + "last_message_at": "2024-01-15T14:30:00Z", + "created_at": "2024-01-15T14:25:00Z" +} +``` + +### Asignar Conversación +```http +POST /api/conversations/{conversation_id}/assign +``` + +**Request:** +```json +{ + "user_id": "uuid" +} +``` + +### Transferir a Cola +```http +POST /api/conversations/{conversation_id}/transfer +``` + +**Request:** +```json +{ + "queue_id": "uuid" +} +``` + +### Resolver Conversación +```http +POST /api/conversations/{conversation_id}/resolve +``` + +**Request:** +```json +{ + "resolution_note": "Problema resuelto" +} +``` + +### Reabrir Conversación +```http +POST /api/conversations/{conversation_id}/reopen +``` + +--- + +## Mensajes + +### Listar Mensajes de Conversación +```http +GET /api/conversations/{conversation_id}/messages +``` + +**Query Parameters:** +| Parámetro | Tipo | Descripción | +|-----------|------|-------------| +| before | uuid | Mensajes antes de este ID | +| limit | int | Límite (default: 50) | + +### Enviar Mensaje +```http +POST /api/conversations/{conversation_id}/messages +``` + +**Request (texto):** +```json +{ + "type": "text", + "content": "Hola, ¿en qué te puedo ayudar?" +} +``` + +**Request (imagen):** +```json +{ + "type": "image", + "media_url": "https://example.com/image.jpg", + "content": "Caption opcional" +} +``` + +**Request (botones):** +```json +{ + "type": "buttons", + "content": "¿Qué necesitas?", + "metadata": { + "buttons": [ + {"id": "ventas", "text": "Ventas"}, + {"id": "soporte", "text": "Soporte"}, + {"id": "otro", "text": "Otro"} + ] + } +} +``` + +**Request (lista):** +```json +{ + "type": "list", + "content": "Selecciona una opción:", + "metadata": { + "button_text": "Ver opciones", + "sections": [ + { + "title": "Productos", + "rows": [ + {"id": "laptop", "title": "Laptops", "description": "Ver laptops disponibles"}, + {"id": "telefono", "title": "Teléfonos", "description": "Ver teléfonos"} + ] + } + ] + } +} +``` + +### Enviar Nota Interna +```http +POST /api/conversations/{conversation_id}/notes +``` + +**Request:** +```json +{ + "content": "Cliente interesado, dar seguimiento" +} +``` + +--- + +## Flujos + +### Listar Flujos +```http +GET /api/flows +``` + +### Crear Flujo +```http +POST /api/flows +``` + +**Request:** +```json +{ + "name": "Bienvenida", + "description": "Flujo de bienvenida para nuevos contactos", + "trigger_type": "welcome", + "trigger_value": null, + "nodes": { + "nodes": [...], + "edges": [...], + "viewport": {...} + }, + "is_active": true +} +``` + +### Obtener Flujo +```http +GET /api/flows/{flow_id} +``` + +### Actualizar Flujo +```http +PUT /api/flows/{flow_id} +``` + +### Duplicar Flujo +```http +POST /api/flows/{flow_id}/duplicate +``` + +### Activar/Desactivar Flujo +```http +PUT /api/flows/{flow_id}/toggle +``` + +### Probar Flujo +```http +POST /api/flows/{flow_id}/test +``` + +**Request:** +```json +{ + "phone_number": "+525551234567", + "initial_message": "Hola" +} +``` + +--- + +## Colas + +### Listar Colas +```http +GET /api/queues +``` + +### Crear Cola +```http +POST /api/queues +``` + +**Request:** +```json +{ + "name": "Ventas", + "description": "Cola de atención de ventas", + "assignment_method": "least_busy", + "max_per_agent": 8, + "sla_first_response": 180, + "sla_resolution": 3600, + "business_hours": { + "monday": {"start": "09:00", "end": "18:00"}, + "tuesday": {"start": "09:00", "end": "18:00"}, + "wednesday": {"start": "09:00", "end": "18:00"}, + "thursday": {"start": "09:00", "end": "18:00"}, + "friday": {"start": "09:00", "end": "17:00"}, + "saturday": {"start": "10:00", "end": "14:00"}, + "sunday": null + }, + "fallback_flow_id": "uuid" +} +``` + +### Obtener Cola +```http +GET /api/queues/{queue_id} +``` + +### Actualizar Cola +```http +PUT /api/queues/{queue_id} +``` + +### Agregar Agente a Cola +```http +POST /api/queues/{queue_id}/agents +``` + +**Request:** +```json +{ + "user_id": "uuid", + "is_supervisor": false, + "skills": ["ventas", "ingles"] +} +``` + +### Remover Agente de Cola +```http +DELETE /api/queues/{queue_id}/agents/{user_id} +``` + +### Estadísticas de Cola +```http +GET /api/queues/{queue_id}/stats +``` + +**Response:** +```json +{ + "waiting": 5, + "active": 12, + "resolved_today": 47, + "avg_first_response": 142, + "avg_resolution": 1823, + "sla_compliance": 0.91, + "agents": [ + { + "user_id": "uuid", + "name": "María García", + "status": "online", + "active_conversations": 5, + "resolved_today": 12 + } + ] +} +``` + +--- + +## Respuestas Rápidas + +### Listar Respuestas Rápidas +```http +GET /api/quick-replies +``` + +### Crear Respuesta Rápida +```http +POST /api/quick-replies +``` + +**Request:** +```json +{ + "shortcut": "/saludo", + "content": "¡Hola {{contact.name}}! Soy {{agent.name}}, ¿en qué te puedo ayudar?", + "queue_id": null +} +``` + +### Actualizar Respuesta Rápida +```http +PUT /api/quick-replies/{id} +``` + +### Eliminar Respuesta Rápida +```http +DELETE /api/quick-replies/{id} +``` + +--- + +## Odoo + +### Probar Conexión +```http +POST /api/odoo/test-connection +``` + +### Sincronizar Contactos +```http +POST /api/odoo/sync-contacts +``` + +### Buscar Partner +```http +GET /api/odoo/partners/search +``` + +**Query Parameters:** +| Parámetro | Tipo | Descripción | +|-----------|------|-------------| +| phone | string | Buscar por teléfono | +| email | string | Buscar por email | +| name | string | Buscar por nombre | + +### Buscar Pedidos +```http +GET /api/odoo/orders +``` + +**Query Parameters:** +| Parámetro | Tipo | Descripción | +|-----------|------|-------------| +| partner_id | int | ID del partner en Odoo | +| state | string | Estado del pedido | +| limit | int | Límite | + +### Consultar Stock +```http +GET /api/odoo/products/{product_id}/stock +``` + +### Crear Lead +```http +POST /api/odoo/leads +``` + +**Request:** +```json +{ + "name": "Interés en Producto X", + "contact_name": "Juan Pérez", + "phone": "+525551234567", + "email": "juan@email.com", + "description": "Cliente interesado en laptops", + "expected_revenue": 5000 +} +``` + +--- + +## WebSocket + +### Conexión +```javascript +const socket = io('wss://tu-dominio.com', { + auth: { + token: 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...' + } +}); +``` + +### Eventos del Servidor + +**Nuevo mensaje:** +```javascript +socket.on('new_message', (data) => { + // data: { conversation_id, message } +}); +``` + +**Estado de mensaje:** +```javascript +socket.on('message_status', (data) => { + // data: { message_id, status: 'delivered' | 'read' } +}); +``` + +**Conversación asignada:** +```javascript +socket.on('conversation_assigned', (data) => { + // data: { conversation_id, assigned_to } +}); +``` + +**QR Code:** +```javascript +socket.on('qr_code', (data) => { + // data: { account_id, qr_code } +}); +``` + +**Estado de conexión WhatsApp:** +```javascript +socket.on('connection_status', (data) => { + // data: { account_id, status: 'connected' | 'disconnected' } +}); +``` + +### Eventos del Cliente + +**Unirse a conversación:** +```javascript +socket.emit('join_conversation', { conversation_id: 'uuid' }); +``` + +**Escribiendo:** +```javascript +socket.emit('typing', { conversation_id: 'uuid' }); +``` + +--- + +## Códigos de Error + +| Código | Descripción | +|--------|-------------| +| 400 | Bad Request - Datos inválidos | +| 401 | Unauthorized - Token inválido o expirado | +| 403 | Forbidden - Sin permisos | +| 404 | Not Found - Recurso no encontrado | +| 409 | Conflict - Conflicto de datos | +| 422 | Unprocessable Entity - Validación fallida | +| 429 | Too Many Requests - Rate limit excedido | +| 500 | Internal Server Error | + +**Formato de error:** +```json +{ + "detail": { + "code": "VALIDATION_ERROR", + "message": "El campo email es requerido", + "field": "email" + } +} +``` diff --git a/docs/architecture/README.md b/docs/architecture/README.md new file mode 100644 index 0000000..830dd19 --- /dev/null +++ b/docs/architecture/README.md @@ -0,0 +1,227 @@ +# Arquitectura del Sistema + +## Visión General + +WhatsApp Centralizado está diseñado como una arquitectura de microservicios, donde cada componente tiene una responsabilidad específica y se comunica con los demás a través de APIs REST y WebSocket. + +## Diagrama de Arquitectura + +``` + ┌─────────────────┐ + │ NGINX │ + │ (Reverse Proxy)│ + │ :80 / :443 │ + └────────┬────────┘ + │ + ┌────────────────────────┼────────────────────────┐ + │ │ │ + ▼ ▼ ▼ + ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ + │ Frontend │ │ API Gateway │ │ WhatsApp Core │ + │ (React) │ │ (FastAPI) │ │ (Node.js) │ + │ :3000 │ │ :8000 │ │ :3001 │ + └───────────────┘ └───────┬───────┘ └───────┬───────┘ + │ │ + │ │ + ┌──────────────────┼──────────────────┐ │ + │ │ │ │ + ▼ ▼ ▼ │ + ┌─────────────────┐ ┌─────────────────┐ ┌──────────────────┐ + │ Flow Engine │ │ Integrations │ │ Redis │ + │ (Python) │ │ (Python) │ │ (Cache/PubSub)│ + │ :8001 │ │ :8002 │ │ :6379 │ + └────────┬────────┘ └────────┬────────┘ └──────────────────┘ + │ │ ▲ + │ │ │ + └─────────┬─────────┘ │ + │ │ + ▼ │ + ┌─────────────────┐ │ + │ PostgreSQL │────────────────────┘ + │ (Database) │ + │ :5432 │ + └─────────────────┘ +``` + +## Componentes + +### Frontend (React + TypeScript) +- **Puerto:** 3000 +- **Responsabilidad:** Interfaz de usuario +- **Tecnologías:** + - React 18 + - TypeScript + - React Flow (editor visual) + - Ant Design / shadcn/ui + - TanStack Query + - Zustand (estado) + - Socket.io-client + +**Módulos:** +- Dashboard con métricas +- Inbox de conversaciones +- Flow Builder visual +- Gestión de números WhatsApp +- Panel de supervisor +- Configuración + +### API Gateway (Python + FastAPI) +- **Puerto:** 8000 +- **Responsabilidad:** Punto de entrada único, autenticación, orquestación +- **Tecnologías:** + - FastAPI + - SQLAlchemy + - Pydantic + - python-jose (JWT) + - Celery (tareas async) + +**Endpoints principales:** +- `/auth/*` - Autenticación +- `/api/conversations/*` - Conversaciones +- `/api/messages/*` - Mensajes +- `/api/flows/*` - Flujos de chatbot +- `/api/contacts/*` - Contactos +- `/api/users/*` - Usuarios +- `/api/queues/*` - Colas +- `/api/whatsapp/*` - Gestión WhatsApp +- `/ws/*` - WebSocket + +### WhatsApp Core (Node.js + TypeScript) +- **Puerto:** 3001 +- **Responsabilidad:** Conexión con WhatsApp via Baileys +- **Tecnologías:** + - Node.js 20 + - TypeScript + - Baileys + - Socket.io + +**Funcionalidades:** +- Gestión de sesiones multi-número +- Generación de QR +- Envío/recepción de mensajes +- Manejo de media +- Reconexión automática +- Estados de mensajes + +### Flow Engine (Python) +- **Puerto:** 8001 (interno) +- **Responsabilidad:** Ejecutar flujos de chatbot +- **Tecnologías:** + - Python + - Motor de ejecución propio + +**Funcionalidades:** +- Parsear definición de flujos (JSONB) +- Ejecutar nodos secuencialmente +- Manejar bifurcaciones y condiciones +- Gestionar variables y contexto +- Integrar con IA (OpenAI, Claude, Ollama) + +### Integrations (Python) +- **Puerto:** 8002 (interno) +- **Responsabilidad:** Conexión con sistemas externos +- **Tecnologías:** + - Python + - xmlrpc.client (Odoo) + - httpx (webhooks) + +**Integraciones:** +- Odoo via XML-RPC +- Webhooks entrantes/salientes +- Email (futuro) +- SMS (futuro) + +### PostgreSQL +- **Puerto:** 5432 +- **Responsabilidad:** Persistencia de datos +- **Datos almacenados:** + - Usuarios y roles + - Conversaciones y mensajes + - Contactos + - Flujos de chatbot + - Configuración + - Logs de auditoría + +### Redis +- **Puerto:** 6379 +- **Responsabilidad:** Cache, colas, pub/sub +- **Usos:** + - Sesiones de usuario + - Cache de datos frecuentes + - Cola de mensajes salientes + - Pub/Sub para tiempo real + - Rate limiting + +## Comunicación entre Servicios + +### HTTP/REST +- Frontend → API Gateway +- API Gateway → Flow Engine +- API Gateway → Integrations +- WhatsApp Core → API Gateway + +### WebSocket +- Frontend ↔ API Gateway (tiempo real) +- API Gateway ↔ WhatsApp Core (eventos) + +### Pub/Sub (Redis) +- Eventos de nuevos mensajes +- Cambios de estado +- Notificaciones + +## Flujo de Datos + +### Mensaje Entrante +``` +WhatsApp → Baileys → WhatsApp Core → API Gateway → Flow Engine + ↓ + PostgreSQL + ↓ + Frontend (via WebSocket) +``` + +### Mensaje Saliente (Bot) +``` +Flow Engine → API Gateway → WhatsApp Core → Baileys → WhatsApp + ↓ +PostgreSQL +``` + +### Mensaje Saliente (Agente) +``` +Frontend → API Gateway → WhatsApp Core → Baileys → WhatsApp + ↓ + PostgreSQL +``` + +## Escalabilidad + +### Horizontal +- WhatsApp Core: Múltiples instancias para más números +- API Gateway: Load balancer con múltiples instancias +- Flow Engine: Workers independientes + +### Vertical +- PostgreSQL: Aumentar recursos según carga +- Redis: Cluster para alta disponibilidad + +## Seguridad + +### Autenticación +- JWT con access tokens (1h) y refresh tokens (7d) +- Refresh tokens en httpOnly cookies +- Bcrypt para passwords (cost 12) + +### Autorización +- RBAC: Admin, Supervisor, Agente +- Permisos granulares por endpoint + +### Red +- HTTPS obligatorio +- Rate limiting +- Validación de entrada con Pydantic + +### Datos +- Encriptación en reposo (PostgreSQL) +- Secrets en variables de entorno +- No logs de datos sensibles diff --git a/docs/database/README.md b/docs/database/README.md new file mode 100644 index 0000000..92f7af8 --- /dev/null +++ b/docs/database/README.md @@ -0,0 +1,452 @@ +# Esquema de Base de Datos + +## Diagrama ER + +``` +┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ +│ users │ │ queues │ │ whatsapp_accounts│ +├─────────────────┤ ├─────────────────┤ ├─────────────────┤ +│ id (PK) │ │ id (PK) │ │ id (PK) │ +│ email │ │ name │ │ phone_number │ +│ password_hash │ │ description │ │ name │ +│ name │ │ assignment_method │ status │ +│ role │ │ max_per_agent │ │ session_data │ +│ status │ │ sla_first_resp │ │ qr_code │ +│ is_active │ │ sla_resolution │ │ created_at │ +│ created_at │ │ business_hours │ └────────┬────────┘ +│ updated_at │ │ fallback_flow_id│ │ +└────────┬────────┘ │ is_active │ │ + │ └────────┬────────┘ │ + │ │ │ + │ ┌────────────────────┼─────────────────────────┤ + │ │ │ │ + │ ▼ ▼ ▼ + │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ + │ │ queue_agents │ │ conversations │ │ contacts │ + │ ├─────────────────┤ ├─────────────────┤ ├─────────────────┤ + │ │ id (PK) │ │ id (PK) │ │ id (PK) │ + │ │ queue_id (FK)───┼──│ queue_id (FK)───│ │ phone_number │ + ├──│ user_id (FK) │ │ whatsapp_acc_id─┼──│ name │ + │ │ is_supervisor │ │ contact_id (FK)─┼──│ email │ + │ │ skills │ │ assigned_to (FK)│ │ company │ + │ └─────────────────┘ │ status │ │ metadata │ + │ │ priority │ │ tags │ + │ │ current_flow_id │ │ odoo_partner_id │ + │ │ flow_context │ │ created_at │ + │ │ sla_* │ └─────────────────┘ + │ │ csat_* │ + │ │ created_at │ + │ └────────┬────────┘ + │ │ + │ │ + │ ▼ + │ ┌─────────────────┐ + │ │ messages │ + │ ├─────────────────┤ + │ │ id (PK) │ + │ │ conversation_id │ + │ │ direction │ + │ │ type │ + │ │ content │ + └───────────────────────│ sent_by (FK) │ + │ is_internal_note│ + │ status │ + │ created_at │ + └─────────────────┘ + +┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ +│ flows │ │ message_templates│ │ quick_replies │ +├─────────────────┤ ├─────────────────┤ ├─────────────────┤ +│ id (PK) │ │ id (PK) │ │ id (PK) │ +│ name │ │ name │ │ shortcut │ +│ description │ │ content │ │ content │ +│ trigger_type │ │ attachments │ │ attachments │ +│ trigger_value │ │ variables │ │ queue_id (FK) │ +│ nodes (JSONB) │ │ created_at │ │ created_by (FK) │ +│ variables │ └─────────────────┘ │ created_at │ +│ is_active │ └─────────────────┘ +│ version │ +│ created_at │ ┌─────────────────┐ ┌─────────────────┐ +└─────────────────┘ │ odoo_config │ │odoo_automations │ + ├─────────────────┤ ├─────────────────┤ +┌─────────────────┐ │ id (PK) │ │ id (PK) │ +│ tags │ │ url │ │ name │ +├─────────────────┤ │ database │ │ odoo_model │ +│ id (PK) │ │ username │ │ odoo_trigger │ +│ name │ │ api_key_enc │ │ odoo_condition │ +│ color │ │ is_active │ │ template_id (FK)│ +│ created_at │ │ last_sync_at │ │ is_active │ +└─────────────────┘ └─────────────────┘ │ created_at │ + └─────────────────┘ +``` + +--- + +## Tablas Detalladas + +### users + +Usuarios del sistema (admins, supervisores, agentes). + +| Campo | Tipo | Descripción | +|-------|------|-------------| +| id | UUID | Identificador único | +| email | VARCHAR(255) | Email único | +| password_hash | VARCHAR(255) | Hash bcrypt | +| name | VARCHAR(100) | Nombre completo | +| role | ENUM | admin, supervisor, agent | +| status | ENUM | online, offline, away, busy, lunch, on_call | +| is_active | BOOLEAN | Usuario activo | +| created_at | TIMESTAMP | Fecha creación | +| updated_at | TIMESTAMP | Última modificación | + +**Índices:** +- `idx_users_email` (email) +- `idx_users_role` (role) +- `idx_users_status` (status) + +--- + +### whatsapp_accounts + +Números de WhatsApp conectados. + +| Campo | Tipo | Descripción | +|-------|------|-------------| +| id | UUID | Identificador único | +| phone_number | VARCHAR(20) | Número con código país | +| name | VARCHAR(100) | Alias (ej: "Ventas") | +| status | ENUM | connected, disconnected, banned | +| session_data | JSONB | Datos de sesión Baileys | +| qr_code | TEXT | QR para reconexión | +| created_at | TIMESTAMP | Fecha creación | + +**Índices:** +- `idx_wa_phone` (phone_number) +- `idx_wa_status` (status) + +--- + +### contacts + +Contactos (clientes que escriben). + +| Campo | Tipo | Descripción | +|-------|------|-------------| +| id | UUID | Identificador único | +| phone_number | VARCHAR(20) | Teléfono único | +| name | VARCHAR(100) | Nombre | +| email | VARCHAR(255) | Email | +| company | VARCHAR(100) | Empresa | +| metadata | JSONB | Datos personalizados | +| tags | VARCHAR[] | Array de etiquetas | +| odoo_partner_id | INTEGER | ID en Odoo | +| created_at | TIMESTAMP | Primer contacto | + +**Índices:** +- `idx_contacts_phone` UNIQUE (phone_number) +- `idx_contacts_email` (email) +- `idx_contacts_odoo` (odoo_partner_id) +- `idx_contacts_tags` GIN (tags) + +--- + +### queues + +Colas de atención. + +| Campo | Tipo | Descripción | +|-------|------|-------------| +| id | UUID | Identificador único | +| name | VARCHAR(100) | Nombre de cola | +| description | TEXT | Descripción | +| assignment_method | ENUM | round_robin, least_busy, skill_based, sticky | +| max_per_agent | INTEGER | Máx. conversaciones por agente | +| sla_first_response | INTEGER | Segundos para primera respuesta | +| sla_resolution | INTEGER | Segundos para resolución | +| business_hours | JSONB | Horario de atención | +| fallback_flow_id | UUID | Flujo fuera de horario | +| is_active | BOOLEAN | Cola activa | + +--- + +### queue_agents + +Relación agentes-colas. + +| Campo | Tipo | Descripción | +|-------|------|-------------| +| id | UUID | Identificador único | +| queue_id | UUID | FK a queues | +| user_id | UUID | FK a users | +| is_supervisor | BOOLEAN | Es supervisor de cola | +| skills | VARCHAR[] | Habilidades del agente | + +**Índices:** +- `idx_qa_queue` (queue_id) +- `idx_qa_user` (user_id) +- `idx_qa_unique` UNIQUE (queue_id, user_id) + +--- + +### conversations + +Conversaciones. + +| Campo | Tipo | Descripción | +|-------|------|-------------| +| id | UUID | Identificador único | +| whatsapp_account_id | UUID | FK a whatsapp_accounts | +| contact_id | UUID | FK a contacts | +| queue_id | UUID | FK a queues (nullable) | +| assigned_to | UUID | FK a users (nullable) | +| status | ENUM | bot, waiting, active, resolved | +| priority | ENUM | low, normal, high, urgent | +| current_flow_id | UUID | Flujo en ejecución | +| flow_context | JSONB | Estado del flujo | +| sla_first_response_at | TIMESTAMP | Hora primera respuesta | +| sla_first_response_met | BOOLEAN | SLA cumplido | +| resolved_at | TIMESTAMP | Hora resolución | +| csat_score | INTEGER | Calificación 1-5 | +| csat_feedback | TEXT | Feedback del cliente | +| last_message_at | TIMESTAMP | Último mensaje | +| created_at | TIMESTAMP | Inicio conversación | + +**Índices:** +- `idx_conv_contact` (contact_id) +- `idx_conv_status` (status) +- `idx_conv_assigned` (assigned_to) +- `idx_conv_queue` (queue_id) +- `idx_conv_last_msg` (last_message_at) +- `idx_conv_created` (created_at) + +--- + +### messages + +Mensajes de conversaciones. + +| Campo | Tipo | Descripción | +|-------|------|-------------| +| id | UUID | Identificador único | +| conversation_id | UUID | FK a conversations | +| direction | ENUM | inbound, outbound | +| type | ENUM | text, image, audio, video, document, buttons, list, location, contact, sticker | +| content | TEXT | Contenido del mensaje | +| media_url | VARCHAR(500) | URL de media | +| metadata | JSONB | Datos adicionales (botones, etc.) | +| sent_by | UUID | FK a users (si fue agente) | +| is_internal_note | BOOLEAN | Nota interna | +| status | ENUM | pending, sent, delivered, read, failed | +| created_at | TIMESTAMP | Fecha envío/recepción | + +**Índices:** +- `idx_msg_conv` (conversation_id) +- `idx_msg_created` (created_at) +- `idx_msg_direction` (direction) +- `idx_msg_status` (status) + +**Particionamiento:** Por fecha (mensual) para alto volumen. + +--- + +### flows + +Flujos de chatbot. + +| Campo | Tipo | Descripción | +|-------|------|-------------| +| id | UUID | Identificador único | +| name | VARCHAR(100) | Nombre del flujo | +| description | TEXT | Descripción | +| trigger_type | ENUM | welcome, keyword, fallback, event, schedule, manual | +| trigger_value | VARCHAR(255) | Valor del trigger | +| nodes | JSONB | Definición del flujo | +| variables | JSONB | Variables del flujo | +| is_active | BOOLEAN | Flujo activo | +| version | INTEGER | Versión del flujo | +| created_at | TIMESTAMP | Fecha creación | + +**Índices:** +- `idx_flows_trigger` (trigger_type, is_active) +- `idx_flows_active` (is_active) + +--- + +### message_templates + +Templates de mensajes reutilizables. + +| Campo | Tipo | Descripción | +|-------|------|-------------| +| id | UUID | Identificador único | +| name | VARCHAR(100) | Nombre del template | +| content | TEXT | Contenido con variables | +| attachments | JSONB | Archivos adjuntos | +| variables | VARCHAR[] | Variables usadas | +| created_at | TIMESTAMP | Fecha creación | + +--- + +### quick_replies + +Respuestas rápidas para agentes. + +| Campo | Tipo | Descripción | +|-------|------|-------------| +| id | UUID | Identificador único | +| shortcut | VARCHAR(50) | Atajo (/saludo) | +| content | TEXT | Contenido | +| attachments | JSONB | Archivos adjuntos | +| queue_id | UUID | FK a queues (opcional) | +| created_by | UUID | FK a users | +| created_at | TIMESTAMP | Fecha creación | + +**Índices:** +- `idx_qr_shortcut` (shortcut) +- `idx_qr_queue` (queue_id) + +--- + +### tags + +Etiquetas para contactos. + +| Campo | Tipo | Descripción | +|-------|------|-------------| +| id | UUID | Identificador único | +| name | VARCHAR(50) | Nombre único | +| color | VARCHAR(7) | Color hex (#FF5733) | +| created_at | TIMESTAMP | Fecha creación | + +--- + +### odoo_config + +Configuración de conexión Odoo. + +| Campo | Tipo | Descripción | +|-------|------|-------------| +| id | UUID | Identificador único | +| url | VARCHAR(255) | URL de Odoo | +| database | VARCHAR(100) | Nombre de BD | +| username | VARCHAR(100) | Usuario API | +| api_key_encrypted | TEXT | API key encriptada | +| is_active | BOOLEAN | Conexión activa | +| last_sync_at | TIMESTAMP | Última sincronización | +| created_at | TIMESTAMP | Fecha creación | + +--- + +### odoo_automations + +Automatizaciones Odoo → WhatsApp. + +| Campo | Tipo | Descripción | +|-------|------|-------------| +| id | UUID | Identificador único | +| name | VARCHAR(100) | Nombre | +| odoo_model | VARCHAR(100) | Modelo Odoo (sale.order) | +| odoo_trigger | VARCHAR(100) | Evento trigger | +| odoo_condition | JSONB | Condiciones | +| message_template_id | UUID | FK a templates | +| is_active | BOOLEAN | Automatización activa | +| created_at | TIMESTAMP | Fecha creación | + +--- + +## Migraciones + +### Crear migración + +```bash +docker compose exec api-gateway alembic revision --autogenerate -m "descripcion" +``` + +### Aplicar migraciones + +```bash +docker compose exec api-gateway alembic upgrade head +``` + +### Revertir migración + +```bash +docker compose exec api-gateway alembic downgrade -1 +``` + +### Ver historial + +```bash +docker compose exec api-gateway alembic history +``` + +--- + +## Índices Recomendados + +```sql +-- Búsqueda full-text en mensajes +CREATE INDEX idx_messages_content_fts ON messages +USING gin(to_tsvector('spanish', content)); + +-- Búsqueda en metadata JSONB +CREATE INDEX idx_contacts_metadata ON contacts +USING gin(metadata); + +-- Queries frecuentes +CREATE INDEX idx_conv_active ON conversations (status, assigned_to) +WHERE status IN ('active', 'waiting'); + +CREATE INDEX idx_msg_recent ON messages (conversation_id, created_at DESC); +``` + +--- + +## Mantenimiento + +### Vacuum + +```sql +-- Vacuum manual +VACUUM ANALYZE conversations; +VACUUM ANALYZE messages; + +-- Configurar autovacuum agresivo para messages +ALTER TABLE messages SET ( + autovacuum_vacuum_scale_factor = 0.05, + autovacuum_analyze_scale_factor = 0.02 +); +``` + +### Archivado de mensajes antiguos + +```sql +-- Mover mensajes > 1 año a tabla de archivo +INSERT INTO messages_archive +SELECT * FROM messages +WHERE created_at < NOW() - INTERVAL '1 year'; + +DELETE FROM messages +WHERE created_at < NOW() - INTERVAL '1 year'; +``` + +### Estadísticas + +```sql +-- Tamaño de tablas +SELECT + relname as table, + pg_size_pretty(pg_total_relation_size(relid)) as size +FROM pg_catalog.pg_statio_user_tables +ORDER BY pg_total_relation_size(relid) DESC; + +-- Mensajes por día +SELECT + DATE(created_at) as date, + COUNT(*) as count +FROM messages +GROUP BY DATE(created_at) +ORDER BY date DESC +LIMIT 30; +``` diff --git a/docs/deployment/README.md b/docs/deployment/README.md new file mode 100644 index 0000000..82ca958 --- /dev/null +++ b/docs/deployment/README.md @@ -0,0 +1,539 @@ +# Guía de Despliegue + +## Requisitos del Servidor + +### Hardware Mínimo +- **CPU:** 2 cores +- **RAM:** 4 GB +- **Disco:** 50 GB SSD + +### Hardware Recomendado +- **CPU:** 4 cores +- **RAM:** 8 GB +- **Disco:** 100 GB SSD + +### Software +- Ubuntu 22.04 LTS (recomendado) o Debian 12 +- Docker 24.0+ +- Docker Compose 2.20+ +- Git + +--- + +## Instalación + +### 1. Preparar Servidor + +```bash +# Actualizar sistema +sudo apt update && sudo apt upgrade -y + +# Instalar Docker +curl -fsSL https://get.docker.com -o get-docker.sh +sudo sh get-docker.sh + +# Agregar usuario al grupo docker +sudo usermod -aG docker $USER + +# Instalar Docker Compose +sudo apt install docker-compose-plugin -y + +# Verificar instalación +docker --version +docker compose version +``` + +### 2. Clonar Repositorio + +```bash +cd /opt +sudo git clone https://git.consultoria-as.com/tu-usuario/WhatsAppCentralizado.git +sudo chown -R $USER:$USER WhatsAppCentralizado +cd WhatsAppCentralizado +``` + +### 3. Configurar Variables de Entorno + +```bash +cp .env.example .env +nano .env +``` + +**Variables requeridas:** + +```bash +# Base de datos +DB_USER=whatsapp_admin +DB_PASSWORD= + +# JWT Secret (generar aleatorio) +JWT_SECRET= + +# Dominio +DOMAIN=chat.tuempresa.com + +# Odoo (opcional, configurar después) +ODOO_URL=https://odoo.tuempresa.com +ODOO_DB=production +ODOO_USER=api-whatsapp@tuempresa.com +ODOO_API_KEY= + +# OpenAI (opcional, para AI Response) +OPENAI_API_KEY=sk-... +``` + +**Generar secrets:** +```bash +# Generar password DB +openssl rand -base64 32 + +# Generar JWT secret +openssl rand -base64 64 +``` + +### 4. Construir e Iniciar + +```bash +# Construir imágenes +docker compose build + +# Iniciar servicios +docker compose up -d + +# Ver logs +docker compose logs -f +``` + +### 5. Inicializar Base de Datos + +```bash +# Aplicar migraciones +docker compose exec api-gateway alembic upgrade head + +# Crear usuario admin +docker compose exec api-gateway python scripts/create_admin.py +``` + +### 6. Configurar SSL con Let's Encrypt + +```bash +# Instalar certbot +sudo apt install certbot -y + +# Obtener certificado +sudo certbot certonly --standalone -d chat.tuempresa.com + +# Los certificados quedan en: +# /etc/letsencrypt/live/chat.tuempresa.com/fullchain.pem +# /etc/letsencrypt/live/chat.tuempresa.com/privkey.pem + +# Copiar a directorio del proyecto +sudo cp /etc/letsencrypt/live/chat.tuempresa.com/fullchain.pem nginx/ssl/cert.pem +sudo cp /etc/letsencrypt/live/chat.tuempresa.com/privkey.pem nginx/ssl/key.pem + +# Reiniciar nginx +docker compose restart nginx +``` + +### 7. Configurar Renovación Automática + +```bash +# Crear script de renovación +sudo nano /etc/cron.d/certbot-renew +``` + +``` +0 3 * * * root certbot renew --quiet --post-hook "cp /etc/letsencrypt/live/chat.tuempresa.com/*.pem /opt/WhatsAppCentralizado/nginx/ssl/ && docker compose -f /opt/WhatsAppCentralizado/docker-compose.yml restart nginx" +``` + +--- + +## Configuración de Nginx + +### nginx/nginx.conf + +```nginx +events { + worker_connections 1024; +} + +http { + upstream frontend { + server frontend:80; + } + + upstream api { + server api-gateway:8000; + } + + upstream websocket { + server whatsapp-core:3001; + } + + # Redirect HTTP to HTTPS + server { + listen 80; + server_name chat.tuempresa.com; + return 301 https://$server_name$request_uri; + } + + # HTTPS Server + server { + listen 443 ssl http2; + server_name chat.tuempresa.com; + + ssl_certificate /etc/nginx/ssl/cert.pem; + ssl_certificate_key /etc/nginx/ssl/key.pem; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256; + ssl_prefer_server_ciphers off; + + # Security headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + + # Frontend + location / { + proxy_pass http://frontend; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # API + location /api { + proxy_pass http://api; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # Timeouts para operaciones largas + proxy_read_timeout 300; + proxy_connect_timeout 300; + proxy_send_timeout 300; + } + + # Auth endpoints + location /auth { + proxy_pass http://api; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } + + # WebSocket + location /ws { + proxy_pass http://websocket; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_read_timeout 86400; + } + + # Webhook de Odoo + location /api/odoo/webhook { + proxy_pass http://api; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } + + # Media files + location /media { + alias /var/www/media; + expires 30d; + add_header Cache-Control "public, immutable"; + } + } +} +``` + +--- + +## Backups + +### Script de Backup + +```bash +#!/bin/bash +# /opt/WhatsAppCentralizado/scripts/backup.sh + +BACKUP_DIR="/opt/backups/whatsapp" +DATE=$(date +%Y%m%d_%H%M%S) +RETENTION_DAYS=30 + +mkdir -p $BACKUP_DIR + +# Backup PostgreSQL +docker compose exec -T postgres pg_dump -U $DB_USER whatsapp_central | gzip > $BACKUP_DIR/db_$DATE.sql.gz + +# Backup sesiones WhatsApp +tar -czf $BACKUP_DIR/sessions_$DATE.tar.gz -C /var/lib/docker/volumes whatsapp_sessions + +# Backup configuración +tar -czf $BACKUP_DIR/config_$DATE.tar.gz .env docker-compose.yml nginx/ + +# Eliminar backups antiguos +find $BACKUP_DIR -type f -mtime +$RETENTION_DAYS -delete + +echo "Backup completado: $DATE" +``` + +### Programar Backup + +```bash +# Editar crontab +crontab -e +``` + +``` +# Backup diario a las 3 AM +0 3 * * * /opt/WhatsAppCentralizado/scripts/backup.sh >> /var/log/whatsapp-backup.log 2>&1 +``` + +### Restaurar Backup + +```bash +# Restaurar base de datos +gunzip -c backup/db_20240115.sql.gz | docker compose exec -T postgres psql -U $DB_USER whatsapp_central + +# Restaurar sesiones +docker compose down +tar -xzf backup/sessions_20240115.tar.gz -C /var/lib/docker/volumes +docker compose up -d +``` + +--- + +## Monitoreo + +### Logs + +```bash +# Ver todos los logs +docker compose logs -f + +# Ver logs de servicio específico +docker compose logs -f api-gateway +docker compose logs -f whatsapp-core + +# Ver últimas 100 líneas +docker compose logs --tail=100 api-gateway +``` + +### Healthchecks + +```bash +# Estado de contenedores +docker compose ps + +# Uso de recursos +docker stats +``` + +### Verificar Servicios + +```bash +# API +curl -s https://chat.tuempresa.com/api/health | jq + +# Frontend +curl -I https://chat.tuempresa.com + +# WebSocket +wscat -c wss://chat.tuempresa.com/ws +``` + +--- + +## Actualización + +### Proceso de Actualización + +```bash +cd /opt/WhatsAppCentralizado + +# Hacer backup primero +./scripts/backup.sh + +# Obtener cambios +git pull origin main + +# Reconstruir imágenes +docker compose build + +# Aplicar migraciones +docker compose exec api-gateway alembic upgrade head + +# Reiniciar servicios +docker compose up -d + +# Verificar logs +docker compose logs -f +``` + +### Rollback + +```bash +# Volver a versión anterior +git checkout + +# Reconstruir +docker compose build +docker compose up -d + +# Restaurar base de datos si es necesario +gunzip -c backup/db_.sql.gz | docker compose exec -T postgres psql -U $DB_USER whatsapp_central +``` + +--- + +## Troubleshooting + +### Problemas Comunes + +#### Contenedor no inicia +```bash +# Ver logs detallados +docker compose logs + +# Verificar configuración +docker compose config +``` + +#### Error de conexión a PostgreSQL +```bash +# Verificar que postgres está corriendo +docker compose ps postgres + +# Verificar credenciales +docker compose exec postgres psql -U $DB_USER -d whatsapp_central +``` + +#### WhatsApp no conecta +```bash +# Verificar logs de whatsapp-core +docker compose logs whatsapp-core + +# Verificar sesiones +ls -la volumes/whatsapp_sessions/ + +# Reiniciar servicio +docker compose restart whatsapp-core +``` + +#### Frontend no carga +```bash +# Verificar build +docker compose logs frontend + +# Reconstruir frontend +docker compose build frontend +docker compose up -d frontend +``` + +### Comandos Útiles + +```bash +# Reiniciar todo +docker compose restart + +# Reiniciar servicio específico +docker compose restart api-gateway + +# Reconstruir sin cache +docker compose build --no-cache + +# Limpiar volúmenes (¡CUIDADO!) +docker compose down -v + +# Ver uso de disco +docker system df + +# Limpiar imágenes no usadas +docker image prune -a +``` + +--- + +## Seguridad + +### Firewall + +```bash +# Instalar ufw +sudo apt install ufw + +# Configurar reglas +sudo ufw default deny incoming +sudo ufw default allow outgoing +sudo ufw allow ssh +sudo ufw allow 80/tcp +sudo ufw allow 443/tcp + +# Activar +sudo ufw enable +``` + +### Fail2ban + +```bash +# Instalar +sudo apt install fail2ban + +# Configurar para nginx +sudo nano /etc/fail2ban/jail.local +``` + +```ini +[nginx-http-auth] +enabled = true +port = http,https +logpath = /var/log/nginx/error.log +maxretry = 3 +bantime = 3600 +``` + +### Actualizaciones de Seguridad + +```bash +# Habilitar actualizaciones automáticas +sudo apt install unattended-upgrades +sudo dpkg-reconfigure unattended-upgrades +``` + +--- + +## Escalamiento + +### Múltiples Números WhatsApp + +Para manejar muchos números, escalar whatsapp-core: + +```yaml +# docker-compose.override.yml +services: + whatsapp-core: + deploy: + replicas: 3 + volumes: + - whatsapp_sessions:/app/sessions +``` + +### Alta Disponibilidad + +Para producción de alta disponibilidad: + +1. **Load Balancer** externo (HAProxy, AWS ALB) +2. **PostgreSQL** en cluster (Patroni) o managed (RDS) +3. **Redis** en cluster o managed (ElastiCache) +4. **Storage** compartido para sesiones (NFS, EFS) + +--- + +## Contacto + +Para soporte técnico, contactar al equipo de desarrollo. diff --git a/docs/flow-builder/README.md b/docs/flow-builder/README.md new file mode 100644 index 0000000..df8c6e7 --- /dev/null +++ b/docs/flow-builder/README.md @@ -0,0 +1,786 @@ +# Flow Builder - Guía Completa + +## Introducción + +El Flow Builder es el editor visual para crear chatbots sin código. Permite diseñar flujos de conversación arrastrando y conectando nodos. + +## Interfaz + +``` +┌─────────────────────────────────────────────────────────────────────┐ +│ Flujo: Bienvenida [Guardar] [Probar] [Activar] │ +├───────────┬─────────────────────────────────────────────────────────┤ +│ NODOS │ CANVAS │ +│ ───────── │ │ +│ 📨 Mensaje│ [Nodos conectados con líneas] │ +│ 🔘 Botones│ │ +│ 📋 Lista │ │ +│ ❓ Input │ │ +│ ⑂ Condición │ +│ ... │ │ +├───────────┼─────────────────────────────────────────────────────────┤ +│ │ PANEL DE PROPIEDADES (al seleccionar un nodo) │ +└───────────┴─────────────────────────────────────────────────────────┘ +``` + +--- + +## Tipos de Nodos + +### Nodos de Trigger (Inicio) + +#### welcome +Inicia el flujo cuando un contacto escribe por primera vez. + +```json +{ + "type": "welcome", + "id": "trigger_1", + "data": { + "label": "Primer mensaje" + } +} +``` + +#### keyword +Inicia el flujo cuando el mensaje contiene palabras clave. + +```json +{ + "type": "keyword", + "id": "trigger_2", + "data": { + "keywords": ["precio", "costo", "cuanto"], + "match_type": "contains" + } +} +``` + +**Match types:** +- `exact` - Coincidencia exacta +- `contains` - Contiene la palabra +- `starts_with` - Empieza con +- `regex` - Expresión regular + +#### fallback +Se activa cuando ningún otro flujo coincide. + +```json +{ + "type": "fallback", + "id": "trigger_3", + "data": { + "label": "No entendido" + } +} +``` + +#### event +Se activa por eventos externos (webhook, Odoo). + +```json +{ + "type": "event", + "id": "trigger_4", + "data": { + "event_type": "odoo_order_confirmed", + "filters": { + "amount_total": { "gt": 1000 } + } + } +} +``` + +--- + +### Nodos de Mensaje + +#### text +Envía un mensaje de texto. + +```json +{ + "type": "text", + "id": "msg_1", + "data": { + "content": "¡Hola {{contact.name}}! 👋\n\n¿En qué te puedo ayudar?", + "typing_delay": 1500 + } +} +``` + +**Variables disponibles:** +- `{{contact.name}}` - Nombre del contacto +- `{{contact.phone}}` - Teléfono +- `{{agent.name}}` - Nombre del agente +- `{{variables.xxx}}` - Variables capturadas + +#### image +Envía una imagen. + +```json +{ + "type": "image", + "id": "msg_2", + "data": { + "url": "https://example.com/producto.jpg", + "caption": "Nuestro producto estrella" + } +} +``` + +#### video +Envía un video. + +```json +{ + "type": "video", + "id": "msg_3", + "data": { + "url": "https://example.com/demo.mp4", + "caption": "Video demostrativo" + } +} +``` + +#### document +Envía un documento (PDF, Excel, etc). + +```json +{ + "type": "document", + "id": "msg_4", + "data": { + "url": "https://example.com/catalogo.pdf", + "filename": "Catálogo 2024.pdf" + } +} +``` + +#### location +Envía una ubicación. + +```json +{ + "type": "location", + "id": "msg_5", + "data": { + "latitude": 19.4326, + "longitude": -99.1332, + "name": "Nuestra Oficina", + "address": "Av. Reforma 123, CDMX" + } +} +``` + +#### buttons +Envía mensaje con botones (máximo 3). + +```json +{ + "type": "buttons", + "id": "msg_6", + "data": { + "content": "¿Qué necesitas?", + "buttons": [ + { "id": "ventas", "text": "Ventas" }, + { "id": "soporte", "text": "Soporte" }, + { "id": "otro", "text": "Otro" } + ] + } +} +``` + +#### list +Envía mensaje con lista de opciones (máximo 10). + +```json +{ + "type": "list", + "id": "msg_7", + "data": { + "content": "Selecciona una categoría:", + "button_text": "Ver opciones", + "sections": [ + { + "title": "Productos", + "rows": [ + { "id": "laptops", "title": "Laptops", "description": "Computadoras portátiles" }, + { "id": "phones", "title": "Teléfonos", "description": "Smartphones" } + ] + }, + { + "title": "Servicios", + "rows": [ + { "id": "soporte", "title": "Soporte Técnico" }, + { "id": "garantia", "title": "Garantías" } + ] + } + ] + } +} +``` + +#### template +Usa un mensaje guardado como template. + +```json +{ + "type": "template", + "id": "msg_8", + "data": { + "template_id": "uuid-del-template", + "variables": { + "producto": "{{variables.producto_seleccionado}}" + } + } +} +``` + +--- + +### Nodos de Lógica + +#### condition +Bifurca según condiciones. + +```json +{ + "type": "condition", + "id": "cond_1", + "data": { + "conditions": [ + { + "groups": [ + { + "field": "{{message.text}}", + "operator": "contains", + "value": "precio" + } + ], + "logic": "AND" + } + ], + "outputs": ["true", "false"] + } +} +``` + +**Operadores:** +- Texto: `equals`, `not_equals`, `contains`, `not_contains`, `starts_with`, `ends_with`, `regex` +- Número: `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `between` +- Lista: `in`, `not_in`, `is_empty`, `is_not_empty` +- General: `exists`, `not_exists`, `is_null`, `is_not_null` + +#### switch +Múltiples salidas según valor. + +```json +{ + "type": "switch", + "id": "switch_1", + "data": { + "field": "{{variables.departamento}}", + "cases": [ + { "value": "ventas", "output": "ventas" }, + { "value": "soporte", "output": "soporte" }, + { "value": "cobranza", "output": "cobranza" } + ], + "default_output": "default" + } +} +``` + +#### wait_input +Espera respuesta del usuario y la guarda. + +```json +{ + "type": "wait_input", + "id": "input_1", + "data": { + "variable": "nombre_cliente", + "timeout_seconds": 300, + "timeout_output": "timeout", + "validation": { + "type": "text", + "min_length": 2, + "max_length": 100 + }, + "error_message": "Por favor ingresa un nombre válido." + } +} +``` + +#### delay +Pausa antes de continuar. + +```json +{ + "type": "delay", + "id": "delay_1", + "data": { + "seconds": 3, + "random_range": [2, 5] + } +} +``` + +#### set_variable +Asigna o modifica una variable. + +```json +{ + "type": "set_variable", + "id": "set_1", + "data": { + "variable": "contador", + "operation": "increment", + "value": 1 + } +} +``` + +**Operaciones:** +- `set` - Asignar valor +- `increment` - Incrementar +- `decrement` - Decrementar +- `append` - Agregar a lista +- `concat` - Concatenar texto + +#### random +A/B Testing - bifurca aleatoriamente. + +```json +{ + "type": "random", + "id": "ab_1", + "data": { + "test_name": "Test saludo", + "variants": [ + { "name": "A", "weight": 50, "output": "variant_a" }, + { "name": "B", "weight": 50, "output": "variant_b" } + ], + "track_metric": "response_rate" + } +} +``` + +#### loop +Repetir bloque. + +```json +{ + "type": "loop", + "id": "loop_1", + "data": { + "max_iterations": 3, + "condition": { + "field": "{{variables.confirmado}}", + "operator": "not_equals", + "value": "si" + }, + "outputs": ["continue", "exit"] + } +} +``` + +#### go_to +Salta a otro flujo o nodo. + +```json +{ + "type": "go_to", + "id": "goto_1", + "data": { + "target_type": "flow", + "target_id": "uuid-del-flujo", + "pass_context": true + } +} +``` + +#### sub_flow +Ejecuta un sub-flujo y retorna. + +```json +{ + "type": "sub_flow", + "id": "sub_1", + "data": { + "flow_id": "uuid-del-subflujo", + "input_variables": { + "producto_id": "{{variables.producto}}" + }, + "output_variable": "resultado_subflujo" + } +} +``` + +#### javascript +Ejecuta código JavaScript personalizado. + +```json +{ + "type": "javascript", + "id": "js_1", + "data": { + "code": "const total = variables.precio * variables.cantidad;\nreturn { total, descuento: total > 1000 ? 0.1 : 0 };", + "output_variable": "calculo" + } +} +``` + +--- + +### Nodos de Validación + +#### validate_email +Valida formato de email. + +```json +{ + "type": "validate_email", + "id": "val_1", + "data": { + "input": "{{message.text}}", + "output_variable": "email_valido", + "outputs": ["valid", "invalid"] + } +} +``` + +#### validate_phone +Valida formato de teléfono. + +```json +{ + "type": "validate_phone", + "id": "val_2", + "data": { + "input": "{{message.text}}", + "country": "MX", + "output_variable": "telefono_valido", + "outputs": ["valid", "invalid"] + } +} +``` + +#### validate_number +Valida número en rango. + +```json +{ + "type": "validate_number", + "id": "val_3", + "data": { + "input": "{{message.text}}", + "min": 1, + "max": 100, + "output_variable": "cantidad", + "outputs": ["valid", "invalid"] + } +} +``` + +#### validate_date +Valida formato de fecha. + +```json +{ + "type": "validate_date", + "id": "val_4", + "data": { + "input": "{{message.text}}", + "format": "DD/MM/YYYY", + "min_date": "today", + "max_date": "+30days", + "output_variable": "fecha", + "outputs": ["valid", "invalid"] + } +} +``` + +#### validate_options +Valida contra lista de opciones. + +```json +{ + "type": "validate_options", + "id": "val_5", + "data": { + "input": "{{message.text}}", + "options": ["1", "2", "3", "uno", "dos", "tres"], + "case_insensitive": true, + "output_variable": "opcion", + "outputs": ["valid", "invalid"] + } +} +``` + +#### retry_input +Reintenta captura con mensaje de error. + +```json +{ + "type": "retry_input", + "id": "retry_1", + "data": { + "max_retries": 3, + "error_message": "No entendí tu respuesta. Por favor intenta de nuevo.", + "final_fallback": "transfer", + "outputs": ["continue", "max_retries"] + } +} +``` + +--- + +### Nodos de Acción + +#### transfer +Transfiere a agente humano. + +```json +{ + "type": "transfer", + "id": "act_1", + "data": { + "target_type": "queue", + "target_id": "uuid-de-cola", + "priority": "high", + "message": "Te comunico con un agente. Por favor espera." + } +} +``` + +#### close +Cierra la conversación. + +```json +{ + "type": "close", + "id": "act_2", + "data": { + "resolution": "resolved", + "send_csat": true, + "message": "¡Gracias por contactarnos! ¿Hay algo más en que pueda ayudarte?" + } +} +``` + +#### tag +Agrega o quita etiquetas. + +```json +{ + "type": "tag", + "id": "act_3", + "data": { + "add": ["lead", "interesado"], + "remove": ["nuevo"] + } +} +``` + +#### note +Agrega nota interna. + +```json +{ + "type": "note", + "id": "act_4", + "data": { + "content": "Cliente interesado en {{variables.producto}}. Presupuesto: {{variables.presupuesto}}" + } +} +``` + +#### notify +Envía notificación. + +```json +{ + "type": "notify", + "id": "act_5", + "data": { + "type": "email", + "recipients": ["admin@empresa.com"], + "subject": "Nuevo lead VIP", + "message": "El cliente {{contact.name}} está interesado en {{variables.producto}}" + } +} +``` + +#### webhook +Llama API externa. + +```json +{ + "type": "webhook", + "id": "act_6", + "data": { + "url": "https://api.example.com/leads", + "method": "POST", + "headers": { + "Authorization": "Bearer {{secrets.api_key}}" + }, + "body": { + "name": "{{contact.name}}", + "phone": "{{contact.phone}}", + "product": "{{variables.producto}}" + }, + "output_variable": "api_response", + "retry": { + "max_attempts": 3, + "delay_seconds": 5 + } + } +} +``` + +#### ai_response +Genera respuesta con IA. + +```json +{ + "type": "ai_response", + "id": "act_7", + "data": { + "provider": "openai", + "model": "gpt-4", + "system_prompt": "Eres un asistente de ventas amable. Productos: {{odoo.products}}", + "include_context": { + "last_messages": 10, + "contact_info": true, + "odoo_data": true + }, + "allowed_actions": ["transfer", "create_lead"], + "output_variable": "ai_response", + "fallback_on_error": "transfer" + } +} +``` + +--- + +### Nodos de Odoo + +Ver [Integración Odoo](../odoo-integration/README.md) para documentación completa. + +--- + +## Variables del Sistema + +``` +# Contacto +{{contact.id}} +{{contact.name}} +{{contact.phone}} +{{contact.email}} +{{contact.company}} +{{contact.tags}} +{{contact.odoo_id}} +{{contact.created_at}} +{{contact.custom.xxx}} + +# Conversación +{{conversation.id}} +{{conversation.status}} +{{conversation.assigned_to}} +{{conversation.queue}} +{{conversation.started_at}} + +# Mensaje actual +{{message.id}} +{{message.text}} +{{message.type}} +{{message.media_url}} + +# Sistema +{{system.date}} +{{system.time}} +{{system.datetime}} +{{system.day_of_week}} +{{system.business_hours}} +{{system.timestamp}} + +# Agente (si asignado) +{{agent.id}} +{{agent.name}} +{{agent.email}} + +# Odoo (si conectado) +{{odoo.partner.id}} +{{odoo.partner.name}} +{{odoo.partner.email}} +{{odoo.partner.balance}} +{{odoo.last_order.name}} +{{odoo.last_order.total}} +{{odoo.last_order.state}} + +# Variables capturadas +{{variables.xxx}} +``` + +--- + +## Estructura de Flujo (JSON) + +```json +{ + "id": "uuid", + "name": "Bienvenida", + "description": "Flujo de bienvenida", + "trigger_type": "welcome", + "trigger_value": null, + "nodes": { + "nodes": [ + { + "id": "trigger_1", + "type": "welcome", + "position": { "x": 100, "y": 100 }, + "data": { "label": "Inicio" } + }, + { + "id": "msg_1", + "type": "text", + "position": { "x": 100, "y": 200 }, + "data": { "content": "¡Hola!" } + } + ], + "edges": [ + { + "id": "e1", + "source": "trigger_1", + "target": "msg_1", + "sourceHandle": "default" + } + ], + "viewport": { + "x": 0, + "y": 0, + "zoom": 1 + } + }, + "variables": { + "producto": { "type": "string", "default": "" }, + "cantidad": { "type": "number", "default": 1 } + }, + "is_active": true, + "version": 1 +} +``` + +--- + +## Mejores Prácticas + +1. **Nombra los nodos claramente** para fácil identificación +2. **Usa sub-flujos** para lógica repetida (validaciones, etc.) +3. **Siempre incluye fallback** para respuestas no entendidas +4. **Valida inputs** antes de usarlos en acciones +5. **Limita reintentos** para evitar loops infinitos +6. **Usa A/B testing** para optimizar mensajes +7. **Transfiere a humano** cuando el bot no puede resolver +8. **Guarda contexto** para conversaciones que se retoman después diff --git a/docs/odoo-integration/README.md b/docs/odoo-integration/README.md new file mode 100644 index 0000000..1c771a6 --- /dev/null +++ b/docs/odoo-integration/README.md @@ -0,0 +1,572 @@ +# Integración con Odoo + +## Visión General + +La integración con Odoo es bidireccional: +- **WhatsApp → Odoo**: Crear leads, consultar pedidos, verificar stock, etc. +- **Odoo → WhatsApp**: Notificaciones automáticas de eventos (pedido enviado, factura vencida, etc.) + +## Configuración + +### 1. Crear Usuario API en Odoo + +1. En Odoo, ir a **Configuración → Usuarios y Compañías → Usuarios** +2. Crear nuevo usuario: + - Nombre: `API WhatsApp` + - Email: `api-whatsapp@tuempresa.com` + - Tipo de usuario: `Usuario interno` +3. Asignar permisos según módulos a usar: + - CRM: Usuario + - Ventas: Usuario + - Inventario: Usuario (lectura) + - Helpdesk: Usuario + - Facturación: Contable (lectura) +4. Generar API Key: + - Ir a Preferencias del usuario + - En "Claves API", crear nueva clave + - Guardar la clave de forma segura + +### 2. Configurar en WhatsApp Central + +```bash +# .env +ODOO_URL=https://odoo.tuempresa.com +ODOO_DB=production +ODOO_USER=api-whatsapp@tuempresa.com +ODOO_API_KEY=tu_api_key_aqui +``` + +### 3. Probar Conexión + +```http +POST /api/odoo/test-connection +Authorization: Bearer {token} +``` + +--- + +## Módulos Soportados + +### Contactos (res.partner) + +| Acción | Nodo | Descripción | +|--------|------|-------------| +| Buscar | `odoo_search_partner` | Buscar por teléfono, email o nombre | +| Crear | `odoo_create_partner` | Crear nuevo contacto | +| Actualizar | `odoo_update_partner` | Modificar datos | +| Sincronizar | Automático | Sync bidireccional | + +**Ejemplo: Buscar cliente** +```json +{ + "type": "odoo_search_partner", + "data": { + "search_by": "phone", + "value": "{{contact.phone}}", + "output_variable": "odoo_partner" + } +} +``` + +**Variables resultantes:** +``` +{{odoo_partner.id}} +{{odoo_partner.name}} +{{odoo_partner.email}} +{{odoo_partner.phone}} +{{odoo_partner.street}} +{{odoo_partner.city}} +{{odoo_partner.credit}} // Saldo a favor +{{odoo_partner.debit}} // Deuda +``` + +--- + +### CRM (crm.lead) + +| Acción | Nodo | Descripción | +|--------|------|-------------| +| Crear lead | `odoo_create_lead` | Nueva oportunidad | +| Actualizar etapa | `odoo_update_lead_stage` | Mover en pipeline | +| Agregar nota | `odoo_add_lead_note` | Agregar actividad/nota | + +**Ejemplo: Crear lead** +```json +{ + "type": "odoo_create_lead", + "data": { + "name": "Interés en {{variables.producto}}", + "contact_name": "{{contact.name}}", + "phone": "{{contact.phone}}", + "email": "{{variables.email}}", + "description": "Cliente contactó por WhatsApp.\nProducto: {{variables.producto}}\nPresupuesto: {{variables.presupuesto}}", + "expected_revenue": "{{variables.presupuesto}}", + "team_id": 1, + "user_id": 5, + "output_variable": "lead" + } +} +``` + +**Ejemplo: Mover etapa** +```json +{ + "type": "odoo_update_lead_stage", + "data": { + "lead_id": "{{lead.id}}", + "stage": "qualified" + } +} +``` + +Etapas disponibles: +- `new` - Nuevo +- `qualified` - Calificado +- `proposition` - Propuesta +- `negotiation` - Negociación +- `won` - Ganado +- `lost` - Perdido + +--- + +### Ventas (sale.order) + +| Acción | Nodo | Descripción | +|--------|------|-------------| +| Buscar pedidos | `odoo_search_orders` | Pedidos del cliente | +| Crear cotización | `odoo_create_quotation` | Nueva cotización | +| Confirmar pedido | `odoo_confirm_order` | Cotización → Pedido | +| Enviar PDF | `odoo_send_quotation_pdf` | Enviar por WhatsApp | + +**Ejemplo: Buscar pedidos** +```json +{ + "type": "odoo_search_orders", + "data": { + "partner_id": "{{odoo_partner.id}}", + "states": ["sale", "done"], + "limit": 5, + "output_variable": "orders" + } +} +``` + +**Variables resultantes:** +``` +{{orders}} // Array de pedidos +{{orders[0].id}} +{{orders[0].name}} // SO-2024-001 +{{orders[0].state}} +{{orders[0].amount_total}} +{{orders[0].date_order}} +{{orders[0].commitment_date}} // Fecha compromiso +``` + +**Ejemplo: Crear cotización** +```json +{ + "type": "odoo_create_quotation", + "data": { + "partner_id": "{{odoo_partner.id}}", + "lines": [ + { + "product_id": "{{variables.producto_id}}", + "quantity": "{{variables.cantidad}}" + } + ], + "validity_days": 30, + "output_variable": "quotation" + } +} +``` + +**Ejemplo: Enviar PDF** +```json +{ + "type": "odoo_send_quotation_pdf", + "data": { + "order_id": "{{quotation.id}}", + "message": "Aquí está tu cotización {{quotation.name}}" + } +} +``` + +--- + +### Inventario (stock) + +| Acción | Nodo | Descripción | +|--------|------|-------------| +| Verificar stock | `odoo_check_stock` | Disponibilidad | +| Estado envío | `odoo_tracking_status` | Tracking de pedido | + +**Ejemplo: Verificar stock** +```json +{ + "type": "odoo_check_stock", + "data": { + "search_by": "name", + "value": "{{variables.producto}}", + "location_id": 8, + "output_variable": "stock" + } +} +``` + +**Variables resultantes:** +``` +{{stock.product_id}} +{{stock.product_name}} +{{stock.qty_available}} // Stock disponible +{{stock.virtual_available}} // Stock proyectado +{{stock.incoming_qty}} // Por recibir +{{stock.outgoing_qty}} // Por enviar +``` + +**Ejemplo: Estado de envío** +```json +{ + "type": "odoo_tracking_status", + "data": { + "order_id": "{{order.id}}", + "output_variable": "tracking" + } +} +``` + +**Variables resultantes:** +``` +{{tracking.state}} // assigned, done, cancel +{{tracking.carrier}} // Nombre paquetería +{{tracking.tracking_ref}} // Número de guía +{{tracking.tracking_url}} // URL de rastreo +{{tracking.scheduled_date}} +{{tracking.date_done}} +``` + +--- + +### Helpdesk (helpdesk.ticket) + +| Acción | Nodo | Descripción | +|--------|------|-------------| +| Crear ticket | `odoo_create_ticket` | Nuevo ticket soporte | +| Estado ticket | `odoo_ticket_status` | Consultar estado | + +**Ejemplo: Crear ticket** +```json +{ + "type": "odoo_create_ticket", + "data": { + "name": "{{variables.asunto}}", + "description": "Reporte desde WhatsApp:\n\n{{variables.descripcion}}\n\n---\nConversación: {{conversation.id}}", + "partner_id": "{{odoo_partner.id}}", + "team_id": 1, + "priority": "2", + "attach_conversation": true, + "output_variable": "ticket" + } +} +``` + +**Variables resultantes:** +``` +{{ticket.id}} +{{ticket.number}} // TICKET-001 +{{ticket.name}} +{{ticket.stage}} // Nombre de etapa +{{ticket.user_id}} // Asignado a +{{ticket.priority}} +``` + +--- + +### Facturación (account.move) + +| Acción | Nodo | Descripción | +|--------|------|-------------| +| Saldo cliente | `odoo_check_balance` | Deuda/crédito | +| Enviar factura | `odoo_send_invoice_pdf` | PDF por WhatsApp | + +**Ejemplo: Verificar saldo** +```json +{ + "type": "odoo_check_balance", + "data": { + "partner_id": "{{odoo_partner.id}}", + "output_variable": "balance" + } +} +``` + +**Variables resultantes:** +``` +{{balance.total_due}} // Total adeudado +{{balance.total_overdue}} // Vencido +{{balance.credit}} // A favor +{{balance.last_payment_date}} +{{balance.last_payment_amount}} +{{balance.invoices}} // Lista de facturas pendientes +``` + +**Ejemplo: Enviar factura** +```json +{ + "type": "odoo_send_invoice_pdf", + "data": { + "invoice_id": "{{variables.invoice_id}}", + "message": "Aquí está tu factura. Total: ${{invoice.amount_total}}" + } +} +``` + +--- + +### Productos (product) + +| Acción | Nodo | Descripción | +|--------|------|-------------| +| Buscar productos | `odoo_search_products` | Catálogo | +| Detalles producto | `odoo_product_details` | Info completa | + +**Ejemplo: Buscar productos** +```json +{ + "type": "odoo_search_products", + "data": { + "search": "{{message.text}}", + "category_id": null, + "available_only": true, + "limit": 5, + "output_variable": "products" + } +} +``` + +**Variables resultantes:** +``` +{{products}} // Array +{{products[0].id}} +{{products[0].name}} +{{products[0].list_price}} +{{products[0].qty_available}} +{{products[0].image_url}} +{{products[0].description}} +``` + +--- + +### Calendario (calendar.event) + +| Acción | Nodo | Descripción | +|--------|------|-------------| +| Disponibilidad | `odoo_check_availability` | Horarios libres | +| Crear cita | `odoo_create_appointment` | Agendar evento | + +**Ejemplo: Verificar disponibilidad** +```json +{ + "type": "odoo_check_availability", + "data": { + "user_id": 5, + "date_from": "{{system.date}}", + "date_to": "{{system.date}}+7days", + "duration_hours": 1, + "output_variable": "slots" + } +} +``` + +**Variables resultantes:** +``` +{{slots}} // Array de horarios +{{slots[0].date}} +{{slots[0].start_time}} +{{slots[0].end_time}} +``` + +**Ejemplo: Crear cita** +```json +{ + "type": "odoo_create_appointment", + "data": { + "name": "Cita con {{contact.name}}", + "start": "{{variables.fecha}} {{variables.hora}}", + "duration": 1, + "partner_id": "{{odoo_partner.id}}", + "user_id": 5, + "description": "Cita agendada desde WhatsApp", + "send_invitation": true, + "output_variable": "appointment" + } +} +``` + +--- + +## Automatizaciones Odoo → WhatsApp + +### Configuración de Webhooks + +1. Instalar módulo de webhooks en Odoo o usar Automated Actions +2. Configurar URL de webhook: + ``` + https://chat.tuempresa.com/api/odoo/webhook + ``` +3. Configurar secret para validación + +### Eventos Disponibles + +#### Pedido Confirmado +```json +{ + "event": "sale.order.confirmed", + "trigger": "state changed to 'sale'", + "template": { + "message": "✅ ¡Tu pedido ha sido confirmado!\n\nPedido: {{order.name}}\nTotal: ${{order.amount_total}}\n\nTe notificaremos cuando sea enviado.", + "attach_pdf": true + } +} +``` + +#### Pedido Enviado +```json +{ + "event": "stock.picking.done", + "trigger": "state changed to 'done'", + "template": { + "message": "🚚 ¡Tu pedido está en camino!\n\nNúmero de guía: {{picking.tracking_ref}}\nPaquetería: {{picking.carrier}}\n\nRastrear: {{picking.tracking_url}}" + } +} +``` + +#### Pago Recibido +```json +{ + "event": "account.payment.posted", + "trigger": "state changed to 'posted'", + "template": { + "message": "💰 ¡Gracias por tu pago!\n\nMonto: ${{payment.amount}}\nReferencia: {{payment.name}}\n\nTu saldo actual es: ${{partner.balance}}" + } +} +``` + +#### Factura Vencida +```json +{ + "event": "account.move.overdue", + "trigger": "invoice_date_due < today AND state = 'posted'", + "delay": "3 days", + "template": { + "message": "⚠️ Recordatorio de pago\n\nFactura: {{invoice.name}}\nMonto: ${{invoice.amount_residual}}\nVencimiento: {{invoice.invoice_date_due}}\n\n¿Necesitas ayuda con el pago?" + }, + "retry": { + "interval_days": 7, + "max_attempts": 3 + } +} +``` + +#### Recordatorio de Cita +```json +{ + "event": "calendar.event.reminder", + "trigger": "start - 24 hours", + "template": { + "message": "📅 Recordatorio: Tienes una cita mañana\n\n📍 {{event.location}}\n🕐 {{event.start_time}}\n\n¿Confirmas tu asistencia?", + "buttons": [ + {"id": "confirm", "text": "Confirmar"}, + {"id": "reschedule", "text": "Reagendar"}, + {"id": "cancel", "text": "Cancelar"} + ] + } +} +``` + +#### Ticket Resuelto +```json +{ + "event": "helpdesk.ticket.closed", + "trigger": "stage.is_close = true", + "template": { + "message": "✅ Tu ticket #{{ticket.number}} ha sido resuelto.\n\n¿Cómo calificarías la atención recibida?", + "send_csat": true + } +} +``` + +--- + +## Sincronización de Contactos + +### WhatsApp → Odoo +- Al recibir mensaje de número nuevo, buscar en Odoo +- Si no existe, opcionalmente crear partner +- Vincular `contact.odoo_partner_id` + +### Odoo → WhatsApp +- Importar partners con campo `mobile` lleno +- Filtrar por categoría/etiqueta si se desea +- Ejecutar sync programado o manual + +### Mapeo de Campos + +| WhatsApp | Odoo | +|----------|------| +| phone_number | mobile | +| name | name | +| email | email | +| company | parent_id.name | +| tags | category_id | +| custom.rfc | vat | +| custom.direccion | street + city | + +--- + +## Módulo Odoo + +### Instalación + +```bash +# Copiar módulo a addons +cp -r odoo_whatsapp_hub /odoo/addons/ + +# Actualizar lista de apps +# En Odoo: Apps → Actualizar lista de aplicaciones + +# Instalar módulo +# Buscar "WhatsApp Hub" e instalar +``` + +### Funcionalidades + +1. **Tab WhatsApp en Partner** + - Ver conversaciones del cliente + - Historial de mensajes + - Enviar mensaje rápido + +2. **Widget de Chat** + - Chat en tiempo real desde Odoo + - Ver estado de conversación + - Transferir a agente + +3. **Acciones Automáticas** + - Enviar WhatsApp al confirmar pedido + - Notificar cambios de estado + - Recordatorios automáticos + +4. **Envío Masivo** + - Seleccionar partners + - Usar templates + - Programar envío + +--- + +## Mejores Prácticas + +1. **Usa cache** para consultas frecuentes (productos, stock) +2. **Limita llamadas** a Odoo para no saturar +3. **Maneja errores** con fallback a agente humano +4. **Valida permisos** del usuario API +5. **Loguea transacciones** para auditoría +6. **Sincroniza en horarios** de baja demanda diff --git a/docs/plans/2026-01-29-whatsapp-centralizado-design.md b/docs/plans/2026-01-29-whatsapp-centralizado-design.md new file mode 100644 index 0000000..4b025df --- /dev/null +++ b/docs/plans/2026-01-29-whatsapp-centralizado-design.md @@ -0,0 +1,706 @@ +# WhatsApp Centralizado - Documento de Diseño + +**Fecha:** 2026-01-29 +**Versión:** 1.0 +**Estado:** Aprobado para implementación + +--- + +## 1. Resumen Ejecutivo + +Plataforma de mensajería centralizada con automatización de chatbots, gestión multi-agente e integración profunda con Odoo. Similar a Kommo, Wasapi, ManyChat y Brevo pero con enfoque en integración empresarial. + +### Decisiones Clave + +| Aspecto | Decisión | +|---------|----------| +| **Enfoque** | Chatbots + Multi-agente + Multi-canal | +| **Canal inicial** | WhatsApp (Baileys) | +| **Stack backend** | Node.js (WhatsApp) + Python/FastAPI (API) | +| **Stack frontend** | React + TypeScript | +| **Base de datos** | PostgreSQL + Redis | +| **Editor chatbot** | Visual drag & drop (React Flow) | +| **Modelo** | Single-tenant | +| **Despliegue** | Docker Compose en servidores propios | +| **Integración principal** | Odoo (bidireccional) | + +--- + +## 2. Arquitectura General + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ FRONTEND (React) │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │ +│ │ Dashboard │ │ Inbox Chat │ │ Flow Builder (React │ │ +│ │ │ │ (Agentes) │ │ Flow) │ │ +│ └─────────────┘ └─────────────┘ └─────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ API GATEWAY (Python/FastAPI) │ +│ • Autenticación JWT • REST API • WebSocket para tiempo real │ +└─────────────────────────────────────────────────────────────────┘ + │ + ┌───────────────┼───────────────┐ + ▼ ▼ ▼ +┌──────────────────┐ ┌──────────────┐ ┌──────────────────────────┐ +│ WHATSAPP CORE │ │ FLOW ENGINE │ │ INTEGRATION SERVICE │ +│ (Node.js + │ │ (Python) │ │ (Python) │ +│ Baileys) │ │ Motor de │ │ • Webhooks │ +│ • Multi-número │ │ chatbot │ │ • Odoo XML-RPC │ +│ • Sesiones │ │ │ │ • APIs externas │ +└──────────────────┘ └──────────────┘ └──────────────────────────┘ + │ │ │ + └───────────────┼───────────────┘ + ▼ + ┌──────────────────┐ + │ PostgreSQL+Redis │ + └──────────────────┘ +``` + +--- + +## 3. Estructura del Proyecto + +``` +WhatsAppCentralizado/ +├── docker-compose.yml +├── .env.example +│ +├── services/ +│ ├── whatsapp-core/ # Node.js + Baileys +│ │ ├── Dockerfile +│ │ ├── package.json +│ │ └── src/ +│ │ ├── index.ts +│ │ ├── sessions/ # Gestión de sesiones WhatsApp +│ │ ├── handlers/ # Procesamiento de mensajes +│ │ └── api/ # API interna +│ │ +│ ├── api-gateway/ # Python + FastAPI +│ │ ├── Dockerfile +│ │ ├── requirements.txt +│ │ └── app/ +│ │ ├── main.py +│ │ ├── routers/ +│ │ ├── models/ +│ │ ├── schemas/ +│ │ └── services/ +│ │ +│ ├── flow-engine/ # Python - Motor de chatbot +│ │ ├── Dockerfile +│ │ └── app/ +│ │ ├── engine.py +│ │ ├── nodes/ +│ │ └── context.py +│ │ +│ └── integrations/ # Python - Integraciones +│ ├── Dockerfile +│ └── app/ +│ ├── odoo.py +│ └── webhooks.py +│ +├── frontend/ # React + TypeScript +│ ├── Dockerfile +│ ├── package.json +│ └── src/ +│ ├── pages/ +│ │ ├── Dashboard/ +│ │ ├── Inbox/ +│ │ └── FlowBuilder/ +│ ├── components/ +│ └── hooks/ +│ +└── database/ + └── migrations/ +``` + +--- + +## 4. Modelo de Datos + +### 4.1 Tablas Principales + +```sql +-- Usuarios y Autenticación +users +├── id (UUID, PK) +├── email (UNIQUE) +├── password_hash +├── name +├── role (admin, supervisor, agent) +├── status (online, away, busy, offline) +├── is_active +├── created_at +└── updated_at + +-- Números de WhatsApp conectados +whatsapp_accounts +├── id (UUID, PK) +├── phone_number +├── name (alias) +├── status (connected, disconnected, banned) +├── session_data (JSONB) +├── qr_code (TEXT) +└── created_at + +-- Contactos +contacts +├── id (UUID, PK) +├── phone_number (UNIQUE) +├── name +├── email +├── company +├── metadata (JSONB) +├── tags (ARRAY) +├── odoo_partner_id (INT) +└── created_at + +-- Colas de atención +queues +├── id (UUID, PK) +├── name +├── description +├── assignment_method (round_robin, least_busy, skill_based) +├── max_per_agent (INT) +├── sla_first_response (INT, segundos) +├── sla_resolution (INT, segundos) +├── business_hours (JSONB) +├── fallback_flow_id (FK) +└── is_active + +-- Agentes en colas +queue_agents +├── id (UUID, PK) +├── queue_id (FK) +├── user_id (FK) +├── is_supervisor +└── skills (ARRAY) + +-- Conversaciones +conversations +├── id (UUID, PK) +├── whatsapp_account_id (FK) +├── contact_id (FK) +├── queue_id (FK, nullable) +├── assigned_to (FK → users, nullable) +├── status (bot, waiting, active, resolved) +├── priority (low, normal, high, urgent) +├── current_flow_id (FK, nullable) +├── flow_context (JSONB) +├── sla_first_response_at (TIMESTAMP) +├── sla_first_response_met (BOOLEAN) +├── resolved_at (TIMESTAMP) +├── csat_score (INT, 1-5) +├── csat_feedback (TEXT) +├── last_message_at +└── created_at + +-- Mensajes +messages +├── id (UUID, PK) +├── conversation_id (FK) +├── direction (inbound, outbound) +├── type (text, image, audio, video, document, buttons, list, location, contact) +├── content (TEXT) +├── media_url +├── metadata (JSONB) +├── sent_by (FK → users, nullable) +├── is_internal_note (BOOLEAN) +├── status (pending, sent, delivered, read, failed) +└── created_at + +-- Flujos de chatbot +flows +├── id (UUID, PK) +├── name +├── description +├── trigger_type (keyword, welcome, fallback, manual, event) +├── trigger_value +├── nodes (JSONB) +├── variables (JSONB) +├── is_active +├── version (INT) +└── created_at + +-- Templates de mensajes +message_templates +├── id (UUID, PK) +├── name +├── content (TEXT) +├── attachments (JSONB) +├── variables (ARRAY) +└── created_at + +-- Respuestas rápidas +quick_replies +├── id (UUID, PK) +├── shortcut +├── content +├── attachments (JSONB) +├── queue_id (FK, nullable) +├── created_by (FK) +└── created_at + +-- Etiquetas +tags +├── id (UUID, PK) +├── name +├── color +└── created_at + +-- Configuración Odoo +odoo_config +├── id (UUID, PK) +├── url +├── database +├── username +├── api_key_encrypted +├── is_active +├── last_sync_at +└── created_at + +-- Automatizaciones Odoo → WhatsApp +odoo_automations +├── id (UUID, PK) +├── name +├── odoo_model (sale.order, stock.picking, etc.) +├── odoo_trigger (state change, field change) +├── odoo_condition (JSONB) +├── message_template_id (FK) +├── is_active +└── created_at +``` + +--- + +## 5. Flow Builder + +### 5.1 Tipos de Nodos + +#### Nodos de Trigger +- `welcome` - Primer mensaje del contacto +- `keyword` - Palabras clave específicas +- `fallback` - Cuando ningún otro flujo coincide +- `event` - Evento externo (webhook, Odoo) +- `schedule` - Horario programado + +#### Nodos de Mensaje +- `text` - Texto con variables y formateo +- `image` - Imagen con caption +- `video` - Video con caption +- `audio` - Audio/nota de voz +- `document` - PDF, Excel, Word +- `location` - Ubicación +- `contact` - Tarjeta de contacto +- `sticker` - Stickers +- `buttons` - Hasta 3 botones +- `list` - Lista con secciones (hasta 10 opciones) +- `template` - Mensaje reutilizable +- `carousel` - Múltiples tarjetas + +#### Nodos de Lógica +- `condition` - Condiciones múltiples (AND/OR/NOT) +- `switch` - Múltiples ramas según valor +- `wait_input` - Esperar respuesta con timeout +- `wait_event` - Esperar evento externo +- `delay` - Delay fijo o aleatorio +- `schedule` - Ejecutar en horario específico +- `random` - A/B testing +- `loop` - Repetir bloque +- `set_variable` - Asignar/modificar variables +- `javascript` - Código custom +- `go_to` - Saltar a otro flujo/nodo +- `sub_flow` - Ejecutar sub-flujo + +#### Nodos de Validación +- `validate_email` - Formato email +- `validate_phone` - Formato teléfono +- `validate_number` - Rango numérico +- `validate_date` - Fecha/hora +- `validate_regex` - Expresión regular +- `validate_options` - Lista de opciones +- `retry_input` - Reintentar con mensaje error + +#### Nodos de Acción +- `transfer` - Transferir a agente/cola +- `close` - Cerrar conversación +- `tag` - Agregar/quitar etiquetas +- `note` - Nota interna +- `assign` - Asignar a agente +- `notify` - Notificar a agente/admin +- `webhook` - HTTP request +- `odoo_action` - Acciones Odoo +- `email` - Enviar email +- `sms` - Enviar SMS (futuro) +- `ai_response` - Respuesta con IA +- `human_takeover` - Pausar bot + +### 5.2 Sistema de Variables + +``` +{{contact.name}} - Nombre del contacto +{{contact.phone}} - Teléfono +{{contact.email}} - Email +{{contact.tags}} - Etiquetas +{{contact.odoo_id}} - ID en Odoo +{{contact.custom.*}} - Campos personalizados + +{{conversation.id}} - ID conversación +{{conversation.channel}} - Canal +{{conversation.agent}} - Agente asignado +{{conversation.status}} - Estado actual + +{{message.text}} - Texto del último mensaje +{{message.type}} - Tipo de mensaje + +{{system.date}} - Fecha actual +{{system.time}} - Hora actual +{{system.day_of_week}} - Día de la semana +{{system.business_hours}} - Si está en horario + +{{odoo.partner.*}} - Datos del partner +{{odoo.last_order.*}} - Último pedido +{{odoo.balance}} - Saldo del cliente +``` + +### 5.3 A/B Testing + +- Distribución configurable entre variantes +- Métricas: tasa de respuesta, conversión, CSAT +- Auto-selección de ganador +- Duración por tiempo o número de contactos + +--- + +## 6. Inbox y Gestión de Agentes + +### 6.1 Sistema de Colas + +- **Round-robin**: Distribuir equitativamente +- **Least-busy**: Al agente con menos conversaciones +- **Skill-based**: Según habilidades del agente +- **Sticky**: Mismo agente si contacto fue atendido antes +- **Priority**: VIPs primero, luego por tiempo + +### 6.2 Configuración de Cola + +- Nombre y descripción +- Método de asignación +- Máximo conversaciones por agente +- Re-asignación por timeout +- Horario de atención +- Flujo fuera de horario +- SLA primera respuesta +- SLA resolución + +### 6.3 Estados de Agente + +- 🟢 **Online** - Recibe nuevas conversaciones +- 🟡 **Ausente** - No recibe nuevas, mantiene activas +- 🔴 **Ocupado** - Atendiendo urgente +- 🍽️ **Almuerzo** - Pausa temporal +- 📞 **En llamada** - No recibe nuevas +- ⚫ **Offline** - Desconectado + +### 6.4 Panel de Supervisor + +- Estado de todos los agentes +- Conversaciones en cola +- SLA en tiempo real +- Conversaciones críticas (alerta) +- Métricas por agente +- Re-asignación manual +- Actividad en tiempo real + +### 6.5 CSAT + +- Encuesta después de resolución +- Escala 1-5 con emojis +- Pregunta de feedback si negativo +- Notificación a supervisor +- Métricas por agente/cola + +--- + +## 7. Integración Odoo + +### 7.1 Módulos Soportados + +| Módulo | Modelo | Acciones | +|--------|--------|----------| +| Contactos | res.partner | Buscar, crear, actualizar, sincronizar | +| CRM | crm.lead | Crear lead, actualizar etapa, agregar nota | +| Ventas | sale.order | Consultar, crear cotización, confirmar, enviar PDF | +| Inventario | stock.* | Consultar stock, disponibilidad | +| Helpdesk | helpdesk.ticket | Crear ticket, estado, asignar | +| Facturación | account.move | Consultar, enviar PDF, estado pago | +| Calendario | calendar.event | Disponibilidad, crear cita | +| Productos | product.* | Buscar, detalles, precio | +| Envíos | stock.picking | Estado, tracking | + +### 7.2 Nodos Odoo en Flow Builder + +**Contactos:** +- `odoo_search_partner` - Buscar cliente +- `odoo_create_partner` - Crear cliente +- `odoo_update_partner` - Actualizar cliente + +**CRM:** +- `odoo_create_lead` - Crear oportunidad +- `odoo_update_lead_stage` - Mover en pipeline +- `odoo_add_lead_note` - Agregar nota + +**Ventas:** +- `odoo_search_orders` - Buscar pedidos +- `odoo_create_quotation` - Crear cotización +- `odoo_confirm_order` - Confirmar pedido +- `odoo_send_quotation_pdf` - Enviar PDF + +**Inventario:** +- `odoo_check_stock` - Consultar disponibilidad +- `odoo_tracking_status` - Estado de envío + +**Helpdesk:** +- `odoo_create_ticket` - Crear ticket +- `odoo_ticket_status` - Estado de ticket + +**Productos:** +- `odoo_search_products` - Buscar productos +- `odoo_product_details` - Información detallada + +**Facturación:** +- `odoo_check_balance` - Saldo del cliente +- `odoo_send_invoice_pdf` - Enviar factura + +**Calendario:** +- `odoo_check_availability` - Disponibilidad +- `odoo_create_appointment` - Crear cita + +### 7.3 Automatizaciones Odoo → WhatsApp + +- Pedido confirmado → Mensaje de confirmación +- Pedido enviado → Mensaje con tracking +- Pago recibido → Agradecimiento +- Factura vencida → Recordatorio +- Recordatorio de cita → 24h antes +- Ticket resuelto → Encuesta satisfacción + +### 7.4 Módulo Odoo + +``` +odoo_whatsapp_hub/ +├── __manifest__.py +├── models/ +│ ├── res_partner.py # Campos WhatsApp en partner +│ ├── whatsapp_account.py +│ ├── whatsapp_conversation.py +│ └── whatsapp_message.py +├── controllers/ +│ ├── webhook.py # Eventos de WhatsApp Central +│ └── api.py +├── wizards/ +│ ├── send_whatsapp.py # Enviar mensaje +│ └── mass_whatsapp.py # Envío masivo +├── views/ +│ ├── res_partner_views.xml # Tab WhatsApp +│ └── whatsapp_*.xml +├── static/src/ +│ ├── js/chat_widget.js # Widget de chat +│ └── css/whatsapp.css +└── security/ir.model.access.csv +``` + +--- + +## 8. Autenticación y Roles + +### 8.1 Roles + +| Rol | Permisos | +|-----|----------| +| **Admin** | Todo acceso: config, flujos, usuarios, integraciones | +| **Supervisor** | Ver todas las conversaciones, reasignar, reportes equipo | +| **Agente** | Solo conversaciones asignadas, responder, transferir | + +### 8.2 JWT + +- Access token: 1 hora +- Refresh token: 7 días (httpOnly cookie) +- Permisos en payload + +--- + +## 9. Docker Compose + +```yaml +version: '3.8' + +services: + postgres: + image: postgres:16-alpine + environment: + POSTGRES_DB: whatsapp_central + POSTGRES_USER: ${DB_USER} + POSTGRES_PASSWORD: ${DB_PASSWORD} + volumes: + - postgres_data:/var/lib/postgresql/data + ports: + - "5432:5432" + + redis: + image: redis:7-alpine + volumes: + - redis_data:/data + ports: + - "6379:6379" + + whatsapp-core: + build: ./services/whatsapp-core + environment: + NODE_ENV: production + API_GATEWAY_URL: http://api-gateway:8000 + REDIS_URL: redis://redis:6379 + volumes: + - whatsapp_sessions:/app/sessions + ports: + - "3001:3001" + + api-gateway: + build: ./services/api-gateway + environment: + DATABASE_URL: postgresql://${DB_USER}:${DB_PASSWORD}@postgres:5432/whatsapp_central + REDIS_URL: redis://redis:6379 + JWT_SECRET: ${JWT_SECRET} + ports: + - "8000:8000" + + flow-engine: + build: ./services/flow-engine + environment: + DATABASE_URL: postgresql://${DB_USER}:${DB_PASSWORD}@postgres:5432/whatsapp_central + REDIS_URL: redis://redis:6379 + + integrations: + build: ./services/integrations + environment: + DATABASE_URL: postgresql://${DB_USER}:${DB_PASSWORD}@postgres:5432/whatsapp_central + ODOO_URL: ${ODOO_URL} + ODOO_DB: ${ODOO_DB} + ODOO_USER: ${ODOO_USER} + ODOO_PASSWORD: ${ODOO_PASSWORD} + + frontend: + build: ./frontend + ports: + - "3000:80" + + nginx: + image: nginx:alpine + volumes: + - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro + ports: + - "80:80" + - "443:443" + +volumes: + postgres_data: + redis_data: + whatsapp_sessions: +``` + +--- + +## 10. Roadmap de Implementación + +### Fase 1: Fundación +- Estructura proyecto + Docker Compose +- whatsapp-core: Baileys, sesiones, multi-número +- api-gateway: Auth JWT, roles +- Frontend: Login, Dashboard, gestión números +- Inbox básico: Lista conversaciones, chat + +### Fase 2: Flow Engine Básico +- Motor de ejecución de flujos +- Nodos básicos: mensaje, botones, wait_input, condición +- Sistema de variables +- Frontend: Flow Builder con React Flow +- Triggers: welcome, keyword, fallback + +### Fase 3: Inbox Avanzado + Multi-agente +- Sistema de colas +- Asignación inteligente +- Transferencia bot → humano → bot +- Estados de agente +- Respuestas rápidas +- Notas internas +- Panel supervisor +- SLA tracking + +### Fase 4: Flow Engine Avanzado +- Nodos avanzados: switch, loop, random, sub-flow +- Nodos validación +- Templates reutilizables +- Variables globales +- Nodo JavaScript +- A/B Testing +- Nodo AI Response + +### Fase 5: Integración Odoo Completa +- Conexión XML-RPC +- Sincronización contactos +- Todos los nodos Odoo +- Eventos Odoo → WhatsApp +- Envío de PDFs + +### Fase 6: Módulo Odoo +- Módulo odoo_whatsapp_hub +- Widget de chat en partner +- Historial conversaciones +- Enviar WhatsApp desde Odoo +- Acciones automáticas +- Envío masivo + +### Fase 7: Reportes y Analytics +- Dashboard analytics +- Métricas por agente/cola/flujo +- CSAT +- Exportación +- Reportes programados + +### Fase 8: Multi-canal (Futuro) +- Email (SMTP/IMAP) +- SMS (Twilio) +- Inbox unificado +- WhatsApp Business API oficial + +--- + +## 11. Requisitos de Servidor + +- **RAM**: 4GB mínimo, 8GB recomendado +- **CPU**: 2 cores mínimo +- **Disco**: SSD, 50GB+ +- **SO**: Linux (Ubuntu 22.04 recomendado) +- **Docker**: 24.0+ +- **Docker Compose**: 2.20+ + +--- + +## 12. Consideraciones de Seguridad + +- Passwords con bcrypt (cost 12) +- JWT con refresh tokens en httpOnly cookies +- Rate limiting en auth +- HTTPS obligatorio (Let's Encrypt) +- Secrets en variables de entorno +- No commitear .env +- Backup automatizado de PostgreSQL y sesiones +- Futuro: 2FA para admins + +--- + +*Documento generado durante sesión de brainstorming.* +*Aprobado para iniciar implementación.* diff --git a/frontend/src/.gitkeep b/frontend/src/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/scripts/.gitkeep b/scripts/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/services/api-gateway/app/.gitkeep b/services/api-gateway/app/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/services/flow-engine/app/.gitkeep b/services/flow-engine/app/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/services/integrations/app/.gitkeep b/services/integrations/app/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/services/whatsapp-core/src/.gitkeep b/services/whatsapp-core/src/.gitkeep new file mode 100644 index 0000000..e69de29