| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 |
- 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
|