Atualização das configuração de mesagens do Zap

This commit is contained in:
2026-08-24 19:05:48 -03:00
parent 568f185d8c
commit 7e093f3af8
12 changed files with 376 additions and 18 deletions

View File

@@ -14,16 +14,31 @@ class Contato < ApplicationRecord
EMAIL_RE = /\A[^@\s]+@[^@\s]+\z/
# Um destinatário é uma PESSOA (telefone e/ou e-mail) ou um GRUPO do WhatsApp.
# O grupo entra aqui, e não em tabela própria, para herdar grupo interno,
# assinatura de eventos e log de envios sem duplicar essa máquina.
TIPOS = %w[pessoa grupo_whatsapp].freeze
# Identificador de grupo do WhatsApp: "120363012345678901@g.us".
JID_GRUPO_RE = /\A\d{5,}@g\.us\z/
validates :nome, presence: true
validates :tipo, inclusion: { in: TIPOS }
validates :email, format: { with: EMAIL_RE }, allow_blank: true
validate :telefone_ou_email
validate :telefone_valido
validate :telefone_ou_email, if: :pessoa?
validate :telefone_valido, if: :pessoa?
validate :grupo_identificado, if: :grupo_whatsapp?
before_validation :normalizar
scope :ativos, -> { where(ativo: true) }
scope :ordenados, -> { order(Arel.sql('LOWER(nome)')) }
scope :com_whatsapp, -> { where.not(telefone: [nil, '']) }
scope :pessoas, -> { where(tipo: 'pessoa') }
scope :grupos_whatsapp, -> { where(tipo: 'grupo_whatsapp') }
# "Tem WhatsApp" = número de pessoa OU identificador de grupo.
scope :com_whatsapp, -> {
where.not(telefone: [nil, '']).or(where.not(whatsapp_grupo_jid: [nil, '']))
}
scope :com_email, -> { where.not(email: [nil, '']) }
# Filtra pelo canal do disparo. 'ambos' = quem tiver qualquer um dos dois.
@@ -35,22 +50,53 @@ class Contato < ApplicationRecord
end
}
def whatsapp? = telefone.present?
def email? = email.present?
def pessoa? = tipo.to_s != 'grupo_whatsapp'
def grupo_whatsapp? = tipo.to_s == 'grupo_whatsapp'
def whatsapp? = destino_whatsapp.present?
def email? = email.present? && pessoa?
# Para onde o WhatsApp vai: o JID do grupo ou o número da pessoa. É o que a
# ponte recebe — ela aceita as duas formas.
def destino_whatsapp
grupo_whatsapp? ? whatsapp_grupo_jid.presence : telefone.presence
end
# {{contato}} na mensagem. Em grupo não existe "primeiro nome" — cortar
# "Motoristas SP" em "Motoristas" só empobreceria o texto.
def primeiro_nome
nome.to_s.split.first.to_s
grupo_whatsapp? ? nome.to_s : nome.to_s.split.first.to_s
end
private
def normalizar
self.nome = nome.to_s.strip.squeeze(' ').presence
self.email = email.to_s.strip.downcase.presence
# Mantém o que foi digitado se não der para normalizar — a validação acusa e
# o ADM vê o próprio valor de volta no campo para corrigir.
bruto = telefone.to_s.strip.presence
self.telefone = bruto && (ConfiguracaoNotificacao.normalizar_telefone(bruto) || bruto)
self.tipo = tipo.presence || 'pessoa'
if grupo_whatsapp?
# Grupo não tem telefone nem e-mail: limpar evita um cadastro meio pessoa,
# meio grupo, que mandaria a mesma mensagem duas vezes.
self.telefone = nil
self.email = nil
self.whatsapp_grupo_jid = whatsapp_grupo_jid.to_s.strip.presence
self.whatsapp_grupo_nome = whatsapp_grupo_nome.to_s.strip.presence
else
self.whatsapp_grupo_jid = nil
self.whatsapp_grupo_nome = nil
self.email = email.to_s.strip.downcase.presence
# Mantém o que foi digitado se não der para normalizar — a validação acusa
# e o ADM vê o próprio valor de volta no campo para corrigir.
bruto = telefone.to_s.strip.presence
self.telefone = bruto && (ConfiguracaoNotificacao.normalizar_telefone(bruto) || bruto)
end
end
def grupo_identificado
return if whatsapp_grupo_jid.to_s.match?(JID_GRUPO_RE)
errors.add(:whatsapp_grupo_jid,
'inválido: escolha um grupo da lista (o identificador termina em @g.us)')
end
def telefone_ou_email