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 # Sin plantilla and self.body # Con mensaje libre and has_whatsapp_web # Con WhatsApp Web disponible and ( self.recipient_type == "group" # Es grupo or ( 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 def _create_whatsapp_messages(self, force_create=False): """ Sobrescritura para inyectar 'is_batch' = True si el composer está en modo batch. """ messages = super()._create_whatsapp_messages(force_create=force_create) if self.batch_mode and messages: _logger.info("Marcando %d mensajes como BATCH (Lote)", len(messages)) messages.write({"is_batch": True}) return messages