# API Reference - WhatsApp Web Integration ## Índice - [Modelos](#modelos) - [Métodos](#métodos) - [Campos](#campos) - [Ejemplos de Uso](#ejemplos-de-uso) - [Respuestas de API](#respuestas-de-api) ## Modelos ### whatsapp.account (Extendido) Extensión del modelo base de cuentas WhatsApp para soportar WhatsApp Web. #### Campos Adicionales | Campo | Tipo | Descripción | |-------|------|-------------| | `whatsapp_web_url` | Char | URL del servidor whatsapp-web.js | #### Métodos ##### `get_groups()` Obtiene la lista de grupos disponibles en WhatsApp Web. **Parámetros:** Ninguno **Retorna:** `list` - Lista de diccionarios con información de grupos **Ejemplo:** ```python account = self.env['whatsapp.account'].browse(1) groups = account.get_groups() ``` **Respuesta esperada:** ```python [ { 'id': {'_serialized': '120363158956331133@g.us'}, 'name': 'Mi Grupo', 'members': [ { 'id': {'_serialized': '5215551234567@c.us'}, 'number': '5551234567', 'name': 'Juan Pérez', 'isAdmin': False, 'isSuperAdmin': False } ] } ] ``` --- ### whatsapp.message (Extendido) Extensión del modelo base de mensajes WhatsApp para soportar grupos. #### Campos Adicionales | Campo | Tipo | Descripción | |-------|------|-------------| | `recipient_type` | Selection | Tipo de destinatario: 'phone' o 'group' | | `whatsapp_group_id` | Many2one | Referencia al grupo (modelo ww.group) | | `final_recipient` | Char | Destinatario final calculado | #### Métodos ##### `_compute_final_recipient()` Calcula el destinatario final basado en el tipo de destinatario. **Parámetros:** Ninguno (compute method) **Retorna:** `str` - ID del destinatario final ##### `_get_final_destination()` Método mejorado para obtener destino final (grupo o teléfono). **Parámetros:** Ninguno **Retorna:** `str|False` - ID del destino final o False **Ejemplo:** ```python message = self.env['whatsapp.message'].browse(1) destination = message._get_final_destination() ``` ##### `_send_message(with_commit=False)` Override del método de envío para soportar WhatsApp Web. **Parámetros:** - `with_commit` (bool): Si hacer commit después del envío **Retorna:** `None` **Ejemplo:** ```python message = self.env['whatsapp.message'].browse(1) message._send_message(with_commit=True) ``` --- ### whatsapp.composer (Extendido) Extensión del composer de WhatsApp para soportar grupos y mensajes libres. #### Campos Adicionales | Campo | Tipo | Descripción | |-------|------|-------------| | `recipient_type` | Selection | Tipo de destinatario: 'phone' o 'group' | | `whatsapp_group_id` | Many2one | Referencia al grupo | | `whatsapp_group_id_char` | Char | ID de grupo manual | | `body` | Html | Mensaje libre (sin plantilla) | #### Métodos ##### `_check_recipient_configuration()` Valida la configuración del destinatario en el composer. **Parámetros:** Ninguno (constraint method) **Retorna:** `None` (raise ValidationError si hay error) ##### `_compute_invalid_phone_number_count()` Override para casos específicos de grupos. **Parámetros:** Ninguno (compute method) **Retorna:** `None` ##### `_onchange_recipient_type()` Limpia campos al cambiar tipo de destinatario. **Parámetros:** Ninguno (onchange method) **Retorna:** `None` ##### `action_send_whatsapp_template()` Override del método de envío para casos específicos de WhatsApp Web. **Parámetros:** Ninguno **Retorna:** `dict` - Acción de ventana o resultado del envío ##### `_send_whatsapp_web_message()` Envía mensaje WhatsApp Web sin plantilla. **Parámetros:** Ninguno **Retorna:** `dict` - Acción de cierre de ventana **Ejemplo:** ```python composer = self.env['whatsapp.composer'].create({ 'recipient_type': 'group', 'whatsapp_group_id': group.id, 'body': 'Hola grupo!' }) result = composer._send_whatsapp_web_message() ``` ##### `_prepare_whatsapp_message_values(record)` Override para agregar información de grupo. **Parámetros:** - `record` (recordset): Registro relacionado **Retorna:** `dict` - Valores para crear mensaje WhatsApp ##### `_get_whatsapp_web_account()` Obtiene cuenta de WhatsApp Web disponible. **Parámetros:** Ninguno **Retorna:** `whatsapp.account` - Cuenta WhatsApp Web **Ejemplo:** ```python composer = self.env['whatsapp.composer'].create({}) account = composer._get_whatsapp_web_account() ``` ##### `_send_whatsapp_message_without_template(body, phone=None, group_id=None)` Envía mensaje de WhatsApp sin plantilla. **Parámetros:** - `body` (str): Contenido del mensaje - `phone` (str, opcional): Número de teléfono - `group_id` (str, opcional): ID del grupo **Retorna:** `whatsapp.message` - Mensaje creado **Ejemplo:** ```python composer = self.env['whatsapp.composer'].create({}) message = composer._send_whatsapp_message_without_template( body="Mensaje de prueba", group_id="120363158956331133@g.us" ) ``` --- ## Ejemplos de Uso ### Envío de Mensaje Individual ```python # Crear composer para número individual composer = self.env['whatsapp.composer'].create({ 'recipient_type': 'phone', 'phone': '+525551234567', 'wa_template_id': template.id, 'res_model': 'res.partner', 'res_id': partner.id }) # Enviar mensaje result = composer.action_send_whatsapp_template() ``` ### Envío de Mensaje a Grupo ```python # Crear composer para grupo composer = self.env['whatsapp.composer'].create({ 'recipient_type': 'group', 'whatsapp_group_id': group.id, 'body': '
Hola grupo! Este es un mensaje libre.
', 'res_model': 'ww.group', 'res_id': group.id }) # Enviar mensaje result = composer.action_send_whatsapp_template() ``` ### Envío con Adjunto ```python # Crear attachment attachment = self.env['ir.attachment'].create({ 'name': 'documento.pdf', 'type': 'binary', 'datas': base64.b64encode(pdf_content), 'mimetype': 'application/pdf' }) # Crear composer con adjunto composer = self.env['whatsapp.composer'].create({ 'recipient_type': 'phone', 'phone': '+525551234567', 'body': 'Adjunto documento importante', 'attachment_id': attachment.id, 'res_model': 'res.partner', 'res_id': partner.id }) # Enviar mensaje result = composer.action_send_whatsapp_template() ``` ### Obtener Grupos de WhatsApp Web ```python # Obtener cuenta WhatsApp Web account = self.env['whatsapp.account'].search([ ('whatsapp_web_url', '!=', False) ], limit=1) # Obtener grupos groups = account.get_groups() # Procesar grupos for group_data in groups: group_id = group_data['id']['_serialized'] group_name = group_data['name'] members = group_data.get('members', []) print(f"Grupo: {group_name} ({group_id})") print(f"Miembros: {len(members)}") ``` ### Envío Directo de Mensaje ```python # Crear mensaje directamente message = self.env['whatsapp.message'].create({ 'recipient_type': 'group', 'whatsapp_group_id': group.id, 'mobile_number': group.whatsapp_web_id, 'body': 'Mensaje directo al grupo', 'wa_account_id': account.id, 'state': 'outgoing' }) # Enviar mensaje message._send_message(with_commit=True) ``` ## Respuestas de API ### Respuesta Exitosa de Envío ```json { "_data": { "id": { "_serialized": "3EB0C767D26A3D1B7B4A_5215551234567@c.us" } } } ``` ### Respuesta de Grupos ```json [ { "id": { "_serialized": "120363158956331133@g.us" }, "name": "Mi Grupo de Trabajo", "description": "Grupo para coordinación de proyectos", "members": [ { "id": { "_serialized": "5215551234567@c.us" }, "number": "5551234567", "name": "Juan Pérez", "pushname": "Juan", "isAdmin": true, "isSuperAdmin": false }, { "id": { "_serialized": "5215557654321@c.us" }, "number": "5557654321", "name": "María García", "pushname": "María", "isAdmin": false, "isSuperAdmin": false } ], "messages": [ { "id": { "_serialized": "3EB0C767D26A3D1B7B4A" }, "body": "Hola grupo!", "author": "5215551234567@c.us", "timestamp": 1640995200, "hasQuotedMsg": false } ] } ] ``` ### Respuesta de Error ```json { "error": "Método no implementado", "code": 501 } ``` ## Códigos de Estado HTTP | Código | Descripción | |--------|-------------| | 200 | Petición exitosa | | 400 | Error en los parámetros | | 401 | No autorizado | | 404 | Endpoint no encontrado | | 500 | Error interno del servidor | | 501 | Método no implementado | ## Formato de Números ### Números Individuales - **Formato de entrada:** `+525551234567`, `5551234567`, `5215551234567` - **Formato interno:** `5215551234567@c.us` - **Reglas de formateo:** - Se eliminan espacios, guiones y el símbolo + - Si empieza con "52" y tiene 12 dígitos, se agrega "1" - Si tiene 10 dígitos, se agrega "521" - Se agrega sufijo "@c.us" ### Grupos - **Formato:** `120363158956331133@g.us` - **Identificación:** Termina en "@g.us" - **No se aplica formateo:** Se usa tal como viene de WhatsApp Web ## Manejo de Errores ### Excepciones Comunes #### ValidationError ```python from odoo.exceptions import ValidationError try: composer.action_send_whatsapp_template() except ValidationError as e: print(f"Error de validación: {e}") ``` #### ConnectionError ```python import requests try: response = requests.post(url, data=payload, headers=headers) except requests.exceptions.ConnectionError: print("Error de conexión con el servidor WhatsApp Web") ``` ### Logs de Debugging ```python import logging _logger = logging.getLogger(__name__) # Log de envío exitoso _logger.info('WHATSAPP WEB SEND MESSAGE: %s', url) # Log de error _logger.error("Error en la petición de groups: %s", response.text) ``` ## Configuración de Timeouts ```python # Configurar timeout para requests import requests response = requests.post( url, data=payload, headers=headers, timeout=30 # 30 segundos ) ``` ## Rate Limiting El módulo incluye delays aleatorios entre envíos: ```python import time import random # Delay aleatorio entre 3-7 segundos time.sleep(random.randint(3, 7)) ```