94 lines
3.7 KiB
Ruby
94 lines
3.7 KiB
Ruby
# app/services/analytics/planilha_entregas.rb
|
|
#
|
|
# Monta as linhas da "planilha Entregas" entregue ao cliente (modelo
|
|
# "Entregas SUDESTE MM.AAAA_FINAL.xlsx", aba ENTREGAS): as colunas A..V vêm da
|
|
# própria tabela da operação (gade_entregas_* — espelho da planilha original) e
|
|
# as colunas de resultado W..Z (STATUS/ENTREGA/DATA OCORRÊNCIA/OCORRÊNCIA) são
|
|
# preenchidas com o ÚLTIMO status de cada NF no rastreio — o preenchimento que
|
|
# hoje é feito manualmente.
|
|
#
|
|
# SEGURANÇA: tabela passa pela whitelist (Operacao.sanitizar) + quote_table_name;
|
|
# colunas são whitelist fixa e as ausentes viram NULL (mesmo padrão do
|
|
# OperacaoMetricas). Bases SOMENTE LEITURA.
|
|
module Analytics
|
|
class PlanilhaEntregas
|
|
# Colunas da tabela gade (snake_case) -> cabeçalho exato do modelo (A..V).
|
|
# Coluna que não existir em alguma operação sai vazia, preservando o layout.
|
|
COLUNAS_GADE = {
|
|
'ordem' => 'Ordem',
|
|
'operacao' => 'Operação',
|
|
'coordenadoria' => 'Coordenadoria',
|
|
'supervisao' => 'Supervisão',
|
|
'unidade' => 'Unidade',
|
|
'data_cadastro' => 'Data Cadastro',
|
|
'cartao_sus' => 'Cartão SUS',
|
|
'nome_completo' => 'Nome Completo',
|
|
'endereco_completo' => 'Endereço Completo',
|
|
'nome_responsavel' => 'Nome Responsável',
|
|
'telefones' => 'Telefones',
|
|
'area_de_risco' => 'Área de Risco',
|
|
'numero_ativo' => 'Numero Ativo',
|
|
'num_serie_base' => 'Num Serie Base',
|
|
'num_serie_pulverizador' => 'Num Serie Pulverizador',
|
|
'observacoes' => 'Observações',
|
|
'endereco_sem_complemento' => 'Endereço Sem Complemento',
|
|
'complemento' => 'Complemento',
|
|
'latitude' => 'Latitude',
|
|
'longitude' => 'Longitude',
|
|
'status' => 'STATUS',
|
|
'nota_fiscal' => 'NOTA FISCAL'
|
|
}.freeze
|
|
|
|
# Cabeçalho completo A..Z (gade + resultado do rastreio).
|
|
CABECALHO = (COLUNAS_GADE.values + ['STATUS', 'ENTREGA', 'DATA OCORRÊNCIA', 'OCORRÊNCIA']).freeze
|
|
|
|
attr_reader :tabela
|
|
|
|
def initialize(tabela)
|
|
@tabela = Operacao.sanitizar([tabela]).first
|
|
raise ArgumentError, "Operação inválida: #{tabela}" unless @tabela
|
|
end
|
|
|
|
def label
|
|
Operacao.label(@tabela)
|
|
end
|
|
|
|
# Array de hashes: chaves de COLUNAS_GADE + rastreio_status/checkout/observation.
|
|
# ORDER BY g.ctid preserva a ordem física de importação — a mesma ordem da
|
|
# planilha original do cliente (tabelas gade são read-only após o import).
|
|
def linhas
|
|
gade = conn.quote_table_name(@tabela)
|
|
sql = <<~SQL
|
|
WITH ultimo AS (
|
|
SELECT *,
|
|
ROW_NUMBER() OVER (PARTITION BY reference_id ORDER BY checkout DESC NULLS LAST) AS rn
|
|
FROM #{conn.quote_table_name(Entrega.table_name)}
|
|
WHERE reference_id IS NOT NULL
|
|
)
|
|
SELECT #{selects_gade},
|
|
r.status AS rastreio_status,
|
|
r.checkout AS rastreio_checkout,
|
|
r.observation AS rastreio_observation
|
|
FROM #{gade} g
|
|
LEFT JOIN ultimo r ON r.rn = 1 AND r.reference_id::text = g.nota_fiscal
|
|
ORDER BY g.ctid
|
|
SQL
|
|
conn.select_all(sql).to_a
|
|
end
|
|
|
|
private
|
|
|
|
def conn
|
|
ActiveRecord::Base.connection
|
|
end
|
|
|
|
# SELECT das colunas whitelisted; ausentes viram NULL com o mesmo alias.
|
|
def selects_gade
|
|
existentes = conn.columns(@tabela).map(&:name)
|
|
COLUNAS_GADE.keys.map do |c|
|
|
existentes.include?(c) ? "g.#{c} AS #{c}" : "CAST(NULL AS text) AS #{c}"
|
|
end.join(', ')
|
|
end
|
|
end
|
|
end
|