Files
Reem-Notas/app/services/notificacao/catalogo_variaveis.rb

197 lines
9.3 KiB
Ruby
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# app/services/notificacao/catalogo_variaveis.rb
#
# TODAS as variáveis que o editor de mensagem oferece, de quatro origens:
#
# :contexto — vêm com o disparo (motorista, consolidação, valor…). Só têm
# valor nos gatilhos que as produzem; em outro gatilho saem
# vazias, e a tela avisa isso.
# :sistema — não dependem de contexto nenhum (empresa, data, hora, mês).
# :dados — CONSULTAM O BANCO na hora do envio (entregas do mês, total a
# pagar…). Resolvidas sob demanda: só a variável realmente
# escrita no template vira query.
# :personalizada — cadastradas pelo ADM (VariavelPersonalizada), valor fixo.
#
# Antes, `Notificacao::Variaveis::POR_GATILHO` amarrava a lista ao gatilho: o ADM
# via 6 variáveis e nada mais, e qualquer texto com número (“entregas do mês”)
# precisava de deploy. Aqui a lista é aberta — e o que não se aplica é MARCADO em
# vez de escondido.
#
# ⚠️ Um `resolver` NUNCA pode levantar: notificação não derruba fechamento. O
# ResolvedorVariaveis engole e devolve vazio.
module Notificacao
module CatalogoVariaveis
ORIGENS = {
contexto: 'Do que aconteceu',
sistema: 'Empresa e data',
dados: 'Números do sistema',
personalizada: 'Suas variáveis'
}.freeze
# ── Sistema: sempre disponíveis ───────────────────────────
SISTEMA = {
'empresa' => {
descricao: 'Nome da empresa', amostra: 'Reem Transportes',
resolver: -> { Configuracao.valor('empresa_nome').presence || 'Reem Transportes' }
},
'data' => {
descricao: 'Data de hoje (dd/mm/aaaa)', amostra: '26/08/2026',
resolver: -> { Date.current.strftime('%d/%m/%Y') }
},
'hora' => {
descricao: 'Hora do envio', amostra: '14:35',
resolver: -> { Time.current.strftime('%H:%M') }
},
'dia_semana' => {
descricao: 'Dia da semana por extenso', amostra: 'quarta-feira',
resolver: -> { I18n.l(Date.current, format: '%A') rescue Date::DAYNAMES[Date.current.wday] }
},
'mes' => {
descricao: 'Mês por extenso', amostra: 'agosto',
resolver: -> { I18n.l(Date.current, format: '%B') rescue Date::MONTHNAMES[Date.current.month] }
},
'mes_ano' => {
descricao: 'Mês/ano (mm/aaaa)', amostra: '08/2026',
resolver: -> { Date.current.strftime('%m/%Y') }
},
'link_sistema' => {
descricao: 'Endereço do sistema', amostra: 'https://app.reemtransportes.com.br',
resolver: -> { Configuracao.valor('url_sistema').presence || ENV.fetch('APP_URL', '') }
}
}.freeze
# ── Contexto: quem produz é o gatilho ─────────────────────
# `gatilhos:` diz em quais a variável tem valor — é o que a tela usa para
# avisar "esta sai vazia aqui".
CONTEXTO = {
'contato' => { descricao: 'Primeiro nome de quem recebe', amostra: 'Carlos', gatilhos: :todos },
'quem' => { descricao: 'Quem executou a ação no sistema', amostra: 'Ana Paula', gatilhos: :todos },
'motorista' => { descricao: 'Nome do motorista', amostra: 'Carlos Matheus Pimentel' },
'consolidacao' => { descricao: 'Nome da consolidação', amostra: 'UBS NORTE 0114/08' },
'periodo' => { descricao: 'Período do fechamento', amostra: '01/08/2026 a 14/08/2026' },
'valor' => { descricao: 'Valor do motorista', amostra: 'R$ 5.060,00' },
'entregas' => { descricao: 'Entregas atendidas no fechamento', amostra: '280' },
'link_painel' => { descricao: 'Link do painel do motorista', amostra: 'https://app.reem.com.br/motorista' },
'o_que_mudou' => { descricao: 'O que foi alterado', amostra: 'Desconto de R$ 120,00 (avaria)' },
'operacao' => { descricao: 'Operação afetada', amostra: 'UBS NORTE AGO 2026' },
'nf' => { descricao: 'Nota fiscal', amostra: '85382' },
'entregues' => { descricao: 'Notas entregues (resumo)', amostra: '4.851' },
'recusas' => { descricao: 'Notas não entregues (resumo)', amostra: '122' },
'pendentes' => { descricao: 'Notas em aberto (resumo)', amostra: '0' },
'retentativas' => { descricao: 'Visitas além da primeira (resumo)', amostra: '4' },
'fora_operacao' => { descricao: 'Notas fora da operação (resumo)', amostra: '2' }
}.freeze
# Quais variáveis de contexto cada gatilho realmente preenche.
POR_GATILHO = {
'manual' => [],
'consolidacao_finalizada' => %w[motorista consolidacao periodo valor entregas link_painel],
'pagamento_efetuado' => %w[motorista consolidacao periodo valor entregas link_painel],
'valor_alterado' => %w[motorista consolidacao periodo valor entregas link_painel o_que_mudou],
'operacao_alterada' => %w[operacao o_que_mudou nf],
'agendado' => %w[operacao periodo entregues recusas pendentes retentativas fora_operacao]
}.freeze
# ── Dados: consultam o banco na hora do envio ─────────────
# Cada uma responde a uma pergunta que o ADM hoje só conseguiria escrevendo à
# mão — e que ficaria desatualizada na mensagem seguinte.
DADOS = {
'entregas_mes' => {
descricao: 'Entregas atendidas no mês corrente', amostra: '4.977',
resolver: -> { Entrega.atendidas.da_conta_gade.no_periodo_checkout(Date.current.beginning_of_month, Date.current).count }
},
'entregas_hoje' => {
descricao: 'Entregas atendidas hoje', amostra: '213',
resolver: -> { Entrega.atendidas.da_conta_gade.no_periodo_checkout(Date.current, Date.current).count }
},
'motoristas_ativos_mes' => {
descricao: 'Motoristas com entrega no mês', amostra: '17',
resolver: -> { Entrega.motoristas_ativos(inicio: Date.current.beginning_of_month, fim: Date.current).size }
},
'consolidacoes_abertas' => {
descricao: 'Consolidações em rascunho', amostra: '3',
resolver: -> { Consolidacao.ativas.rascunho.count }
},
'total_a_pagar_mes' => {
descricao: 'Total a pagar no mês (fechado e não pago)', amostra: 'R$ 72.508,00',
resolver: lambda {
totais = Analytics::TotaisPorMotorista.new(inicio: Date.current.beginning_of_month,
fim: Date.current.end_of_month)
Formatador.moeda(totais.total_pendente)
}
},
'total_pago_mes' => {
descricao: 'Total já pago no mês', amostra: 'R$ 21.300,00',
resolver: lambda {
totais = Analytics::TotaisPorMotorista.new(inicio: Date.current.beginning_of_month,
fim: Date.current.end_of_month)
Formatador.moeda(totais.total_pago)
}
},
'total_consolidado_mes' => {
descricao: 'Total consolidado no mês', amostra: 'R$ 93.808,00',
resolver: lambda {
totais = Analytics::TotaisPorMotorista.new(inicio: Date.current.beginning_of_month,
fim: Date.current.end_of_month)
Formatador.moeda(totais.total_geral)
}
}
}.freeze
module_function
# Todas as variáveis conhecidas, incluindo as que o ADM cadastrou.
# { chave => { origem:, descricao:, amostra: } }
def todas
base = {}
SISTEMA.each { |c, m| base[c] = { origem: :sistema, descricao: m[:descricao], amostra: m[:amostra] } }
CONTEXTO.each { |c, m| base[c] = { origem: :contexto, descricao: m[:descricao], amostra: m[:amostra] } }
DADOS.each { |c, m| base[c] = { origem: :dados, descricao: m[:descricao], amostra: m[:amostra] } }
personalizadas.each do |v|
base[v.chave] = { origem: :personalizada, descricao: v.descricao.presence || 'Sua variável', amostra: v.valor }
end
base
end
# A variável tem valor NESTE gatilho? Contexto de outro gatilho sai vazio —
# a tela mostra, em vez de esconder a variável (foi o pedido: liberdade com
# aviso, não lista curta).
def aplicavel?(chave, gatilho)
meta = CONTEXTO[chave.to_s]
return true if meta.nil? # sistema/dados/personalizada: sempre
return true if meta[:gatilhos] == :todos
Array(POR_GATILHO[gatilho.to_s]).include?(chave.to_s)
end
def origem(chave)
todas.dig(chave.to_s, :origem)
end
def existe?(chave) = todas.key?(chave.to_s)
# Agrupadas para a tela do editor, na ordem de ORIGENS.
def por_origem(gatilho = nil)
agrupadas = ORIGENS.keys.index_with { |o| [] }
todas.each do |chave, meta|
agrupadas[meta[:origem]] << meta.merge(chave: chave, aplicavel: gatilho.nil? || aplicavel?(chave, gatilho))
end
agrupadas
end
# Contexto do PREVIEW: valores de amostra, nunca de envio real.
def amostra(gatilho = nil)
todas.filter_map { |chave, meta|
[chave, meta[:amostra].to_s] if gatilho.nil? || aplicavel?(chave, gatilho)
}.to_h
end
def personalizadas
return [] unless VariavelPersonalizada.table_exists?
VariavelPersonalizada.ativas.to_a
rescue StandardError
[]
end
end
end