From c8c6deb4dea38ecce97be36ca21c74ac23dd94bb Mon Sep 17 00:00:00 2001 From: Claude AI Date: Thu, 29 Jan 2026 22:43:08 +0000 Subject: [PATCH] feat(odoo): add WhatsApp account model Co-Authored-By: Claude Opus 4.5 --- odoo_whatsapp_hub/models/whatsapp_account.py | 106 +++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 odoo_whatsapp_hub/models/whatsapp_account.py diff --git a/odoo_whatsapp_hub/models/whatsapp_account.py b/odoo_whatsapp_hub/models/whatsapp_account.py new file mode 100644 index 0000000..cc47087 --- /dev/null +++ b/odoo_whatsapp_hub/models/whatsapp_account.py @@ -0,0 +1,106 @@ +from odoo import models, fields, api +from odoo.exceptions import UserError +import requests +import logging + +_logger = logging.getLogger(__name__) + + +class WhatsAppAccount(models.Model): + _name = 'whatsapp.account' + _description = 'WhatsApp Account' + _order = 'name' + + name = fields.Char(string='Nombre', required=True) + phone_number = fields.Char(string='Número de Teléfono') + status = fields.Selection([ + ('disconnected', 'Desconectado'), + ('connecting', 'Conectando'), + ('connected', 'Conectado'), + ], string='Estado', default='disconnected', readonly=True) + qr_code = fields.Text(string='Código QR') + external_id = fields.Char(string='ID Externo', help='ID en WhatsApp Central') + api_url = fields.Char( + string='URL API', + default='http://localhost:8000', + required=True, + ) + api_key = fields.Char(string='API Key') + is_default = fields.Boolean(string='Cuenta por Defecto') + company_id = fields.Many2one( + 'res.company', + string='Compañía', + default=lambda self: self.env.company, + ) + conversation_count = fields.Integer( + string='Conversaciones', + compute='_compute_conversation_count', + ) + + @api.depends() + def _compute_conversation_count(self): + for account in self: + account.conversation_count = self.env['whatsapp.conversation'].search_count([ + ('account_id', '=', account.id) + ]) + + @api.model + def get_default_account(self): + """Get default WhatsApp account""" + account = self.search([('is_default', '=', True)], limit=1) + if not account: + account = self.search([], limit=1) + return account + + def action_sync_status(self): + """Sync status from WhatsApp Central""" + self.ensure_one() + if not self.external_id: + raise UserError('Esta cuenta no está vinculada a WhatsApp Central') + + try: + response = requests.get( + f'{self.api_url}/api/whatsapp/accounts/{self.external_id}', + headers=self._get_headers(), + timeout=10, + ) + if response.status_code == 200: + data = response.json() + self.write({ + 'status': data.get('status', 'disconnected'), + 'phone_number': data.get('phone_number'), + 'qr_code': data.get('qr_code'), + }) + except Exception as e: + _logger.error(f'Error syncing WhatsApp account: {e}') + raise UserError(f'Error de conexión: {e}') + + def action_view_conversations(self): + """Open conversations for this account""" + self.ensure_one() + return { + 'type': 'ir.actions.act_window', + 'name': 'Conversaciones', + 'res_model': 'whatsapp.conversation', + 'view_mode': 'tree,form', + 'domain': [('account_id', '=', self.id)], + 'context': {'default_account_id': self.id}, + } + + def _get_headers(self): + """Get API headers""" + headers = {'Content-Type': 'application/json'} + if self.api_key: + headers['Authorization'] = f'Bearer {self.api_key}' + return headers + + @api.model + def create(self, vals): + if vals.get('is_default'): + self.search([('is_default', '=', True)]).write({'is_default': False}) + return super().create(vals) + + def write(self, vals): + if vals.get('is_default'): + self.search([('is_default', '=', True), ('id', 'not in', self.ids)]).write({'is_default': False}) + return super().write(vals)