from odoo import models, fields, api from odoo.exceptions import ValidationError import logging _logger = logging.getLogger(__name__) class WhatsAppComposer(models.TransientModel): _inherit = 'whatsapp.composer' # Campos para soporte básico de grupos (solo por ID string, sin Many2one) # La funcionalidad completa de grupos con Many2one está en whatsapp_web_groups recipient_type = fields.Selection([ ('phone', 'Phone Number'), ('group', 'WhatsApp Group') ], string='Send To', default='phone', help="Choose recipient type") # Campo para ID de grupo como string whatsapp_group_id_char = fields.Char(string='Group ID', help="WhatsApp Group ID (e.g., 120363158956331133@g.us)") # Campo para mensaje libre (sin plantilla) body = fields.Html(string='Message Body', help="Free text message (for WhatsApp Web accounts without template)") @api.constrains('recipient_type', 'phone', 'whatsapp_group_id_char', 'wa_template_id', 'body') def _check_recipient_configuration(self): """Validar configuración de destinatario en composer""" for record in self: # Si está en contexto de skip_template_validation, saltar validaciones de plantilla if self.env.context.get('skip_template_validation'): # Solo validar configuración básica de destinatario if record.recipient_type == 'group' and not record.whatsapp_group_id_char: raise ValidationError("Please enter a Group ID when sending to groups") elif record.recipient_type == 'phone' and not record.phone: raise ValidationError("Please provide a phone number when sending to individuals") return # Saltar el resto de validaciones # Detectar si hay cuentas de WhatsApp Web disponibles whatsapp_web_accounts = record.env['whatsapp.account'].search([ ('whatsapp_web_url', '!=', False) ]) has_whatsapp_web = bool(whatsapp_web_accounts) if record.recipient_type == 'group' and not record.whatsapp_group_id_char: raise ValidationError("Please enter a Group ID when sending to groups") elif record.recipient_type == 'phone' and not record.phone: raise ValidationError("Please provide a phone number when sending to individuals") # Validar que haya contenido (plantilla o mensaje libre) if not record.wa_template_id and not record.body: if has_whatsapp_web: raise ValidationError("Please provide either a template or write a free text message") else: raise ValidationError("Template is required for WhatsApp Business API") # Si usa mensaje libre, debe haber WhatsApp Web if record.body and not record.wa_template_id and not has_whatsapp_web: raise ValidationError("Free text messages require WhatsApp Web account configuration") @api.depends('phone', 'batch_mode', 'recipient_type', 'whatsapp_group_id_char') def _compute_invalid_phone_number_count(self): """Override SOLO para casos específicos de grupos - NO interferir con funcionalidad nativa""" for composer in self: # SOLO intervenir si es un caso muy específico de grupo if (hasattr(composer, 'recipient_type') and composer.recipient_type == 'group'): composer.invalid_phone_number_count = 0 continue # SOLO intervenir si el phone es explícitamente un ID de grupo if composer.phone and composer.phone.endswith('@g.us'): composer.invalid_phone_number_count = 0 continue # TODOS LOS DEMÁS CASOS: usar lógica original sin modificar super(WhatsAppComposer, composer)._compute_invalid_phone_number_count() def action_send_whatsapp_template(self): """Override del método de envío SOLO para casos específicos de WhatsApp Web sin plantilla""" # SOLO intervenir si es un caso muy específico: # 1. No hay plantilla # 2. Hay mensaje libre (body) # 3. Es tipo grupo O hay WhatsApp Web disponible whatsapp_web_accounts = self.env['whatsapp.account'].search([ ('whatsapp_web_url', '!=', False) ]) has_whatsapp_web = bool(whatsapp_web_accounts) # CONDICIÓN MUY ESPECÍFICA para no interferir con funcionalidad nativa is_special_case = ( not self.wa_template_id and # Sin plantilla self.body and # Con mensaje libre has_whatsapp_web and # Con WhatsApp Web disponible (self.recipient_type == 'group' or # Es grupo (hasattr(self, 'phone') and self.phone and self.phone.endswith('@g.us'))) # O es ID de grupo directo ) if is_special_case: return self._send_whatsapp_web_message() # TODOS LOS DEMÁS CASOS: usar método original sin modificar return super().action_send_whatsapp_template() def _send_whatsapp_web_message(self): """Enviar mensaje WhatsApp Web sin plantilla - siguiendo lógica original""" records = self._get_active_records() for record in records: # Determinar destinatario if self.recipient_type == 'group': if self.whatsapp_group_id_char: mobile_number = self.whatsapp_group_id_char else: raise ValidationError("Please specify a group ID") else: mobile_number = self.phone if not mobile_number: raise ValidationError("Please provide a phone number") # Crear mail.message con adjuntos si existen (siguiendo lógica original) post_values = { 'attachment_ids': [self.attachment_id.id] if self.attachment_id else [], 'body': self.body, 'message_type': 'whatsapp_message', 'partner_ids': hasattr(record, '_mail_get_partners') and record._mail_get_partners()[record.id].ids or record._whatsapp_get_responsible().partner_id.ids, } if hasattr(records, '_message_log'): message = record._message_log(**post_values) else: message = self.env['mail.message'].create( dict(post_values, res_id=record.id, model=self.res_model, subtype_id=self.env['ir.model.data']._xmlid_to_res_id("mail.mt_note")) ) # Crear mensaje WhatsApp (siguiendo estructura original) whatsapp_message = self.env['whatsapp.message'].create({ 'mail_message_id': message.id, 'mobile_number': mobile_number, 'mobile_number_formatted': mobile_number, 'recipient_type': self.recipient_type, 'wa_template_id': False, # Sin plantilla 'wa_account_id': self._get_whatsapp_web_account().id, 'state': 'outgoing', }) # Enviar mensaje usando la lógica original de _send_message whatsapp_message._send_message() return {'type': 'ir.actions.act_window_close'} def _prepare_whatsapp_message_values(self, record): """Override SOLO para agregar información de grupo - NO interferir con funcionalidad nativa""" # SIEMPRE usar lógica original primero values = super()._prepare_whatsapp_message_values(record) # SOLO agregar información de grupo si es caso específico if (hasattr(self, 'recipient_type') and self.recipient_type == 'group'): if self.whatsapp_group_id_char: values.update({ 'recipient_type': 'group', 'mobile_number': self.whatsapp_group_id_char, 'mobile_number_formatted': self.whatsapp_group_id_char, }) # Siempre agregar recipient_type para compatibilidad if not values.get('recipient_type'): values['recipient_type'] = 'phone' return values def _get_whatsapp_web_account(self): """Obtener cuenta de WhatsApp Web disponible""" # Primero intentar usar la cuenta de la plantilla si existe if self.wa_template_id and self.wa_template_id.wa_account_id and self.wa_template_id.wa_account_id.whatsapp_web_url: return self.wa_template_id.wa_account_id # Si no, buscar cualquier cuenta con WhatsApp Web account = self.env['whatsapp.account'].search([ ('whatsapp_web_url', '!=', False) ], limit=1) if not account: raise ValidationError("No WhatsApp Web account configured") return account def _send_whatsapp_message_without_template(self, body, phone=None, group_id=None): """Enviar mensaje de WhatsApp sin plantilla (solo para WhatsApp Web)""" # Solo funciona con WhatsApp Web, no con API oficial if not (phone or group_id): raise ValidationError("Debe especificar teléfono o grupo") # Crear mensaje directamente message_vals = { 'body': body, 'mobile_number': group_id or phone, 'recipient_type': 'group' if group_id else 'phone', 'wa_template_id': False, # Sin plantilla 'state': 'outgoing', } # Nota: El campo whatsapp_group_id Many2one está en whatsapp_web_groups # Crear mail.message mail_message = self.env['mail.message'].create({ 'body': body, 'message_type': 'whatsapp_message', }) message_vals['mail_message_id'] = mail_message.id # Crear y enviar mensaje WhatsApp whatsapp_message = self.env['whatsapp.message'].create(message_vals) whatsapp_message._send_message() return whatsapp_message