Correção de itens que estão fora do plano valores no dash principal

This commit is contained in:
2026-08-24 16:26:27 -03:00
parent 04b78bc7de
commit 94bbe7556f
10 changed files with 403 additions and 48 deletions

View File

@@ -16,11 +16,19 @@ module Analytics
# Teto da listagem na tela (os totais continuam contando tudo).
LIMITE = 300
# Colunas do espelho que podem carregar o nome do PLANO/rota de origem
# ("(Avulsa)", "INCLUSÃO"...). Nem toda base tem todas — as ausentes viram
# NULL, mesmo padrão de selects_gade em OperacaoMetricas. Whitelist fixa:
# nada aqui vem do usuário.
COLUNAS_PLANO = %w[title notes comments route_id].freeze
# Colunas extras do espelho que a tela usa. Nem toda base tem todas — as
# ausentes viram NULL, mesmo padrão de selects_gade em OperacaoMetricas.
# Whitelist fixa: nada aqui vem do usuário.
COLUNAS_EXTRA = %w[title notes comments route_id].freeze
# Candidatas ao nome do PLANO de origem ("(Avulsa)", "INCLUSÃO"...), na ordem
# de preferência.
#
# ⚠️ `title` NÃO entra aqui: conferido em 24/08/2026 com dado real, ele traz
# "NF 89096 - KAIQUE TAUAN DA SILVA" — o formato da coluna A da planilha de
# importação (`NF {nota_fiscal} - {nome_completo}`), ou seja, o destinatário.
# Agrupar por ele daria um grupo por NF, o que não informa nada.
COLUNAS_PLANO = %w[notes comments route_id].freeze
def initialize(inicio:, fim:)
@inicio = inicio&.to_date
@@ -61,21 +69,48 @@ module Analytics
total.positive?
end
# Agrupamento por plano de origem, quando a base tiver alguma das colunas de
# COLUNAS_PLANO preenchida. Serve para separar "(Avulsa)" de "INCLUSÃO".
def por_plano
linhas.group_by { |r| plano(r) }
.map { |nome, rows| { nome: nome, total: rows.size } }
.sort_by { |h| -h[:total] }
end
# Rótulo do plano de uma linha: primeira coluna de COLUNAS_PLANO preenchida.
# Nome do plano de origem: primeira coluna de COLUNAS_PLANO preenchida.
# nil quando o espelho não guarda essa informação.
def plano(registro)
COLUNAS_PLANO.each do |coluna|
valor = registro[coluna].to_s.strip
return valor if valor.present?
end
'SEM PLANO IDENTIFICADO'
nil
end
# Destinatário/título da visita (coluna `title` do espelho).
def titulo(registro)
registro['title'].to_s.strip.presence
end
def plano_identificado?
linhas.any? { |r| plano(r) }
end
# Quebra do painel em grupos. Quando o espelho traz o plano, separa
# "(Avulsa)" de "INCLUSÃO"; quando não traz, agrupar por título daria um
# grupo por NF — então cai para a unidade, que ainda diz algo útil.
# Devolve [rótulo do agrupamento, [{ nome:, total: }, ...]].
def agrupamento
if plano_identificado?
['Plano', agrupar { |r| plano(r) || 'SEM PLANO' }]
else
['Unidade', agrupar { |r| r['contact_name'].to_s.strip.presence || 'SEM UNIDADE' }]
end
end
# Rótulo legível do resultado da última visita.
RESULTADOS = { 'completed' => 'Entregue', 'failed' => 'Não entregue',
'pending' => 'Em aberto', 'in_progress' => 'Em rota' }.freeze
def resultado(registro)
status = registro['status'].to_s
RESULTADOS[status] || status.presence || 'sem status'
end
def sucesso?(registro)
registro['status'] == 'completed'
end
def listagem
@@ -88,6 +123,12 @@ module Analytics
private
def agrupar
linhas.group_by { |r| yield(r) }
.map { |nome, rows| { nome: nome, total: rows.size } }
.sort_by { |h| -h[:total] }
end
def ordem_visita(registro)
t = registro['checkout'].presence&.to_time
[t ? 1 : 0, t || Time.at(0)]
@@ -124,11 +165,11 @@ module Analytics
conn.select_all(sql).to_a
end
# Colunas de plano que existirem de fato; as demais viram NULL com o mesmo
# Colunas extras que existirem de fato; as demais viram NULL com o mesmo
# alias, para a leitura da linha não precisar saber quais existem.
def selects_plano(conn)
existentes = conn.columns(Entrega.table_name).map(&:name)
COLUNAS_PLANO.map do |coluna|
COLUNAS_EXTRA.map do |coluna|
existentes.include?(coluna) ? "r.#{coluna}" : "CAST(NULL AS text) AS #{coluna}"
end.join(', ')
end