Adição da opção de baixar a planilha preenchida para o cliente
This commit is contained in:
93
app/services/analytics/planilha_entregas.rb
Normal file
93
app/services/analytics/planilha_entregas.rb
Normal file
@@ -0,0 +1,93 @@
|
||||
# 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
|
||||
60
app/services/analytics/planilha_entregas_xlsx.rb
Normal file
60
app/services/analytics/planilha_entregas_xlsx.rb
Normal file
@@ -0,0 +1,60 @@
|
||||
# app/services/analytics/planilha_entregas_xlsx.rb
|
||||
#
|
||||
# Gera o binário .xlsx da planilha Entregas do cliente (aba ENTREGAS) a partir
|
||||
# das linhas montadas por Analytics::PlanilhaEntregas — layout A..Z idêntico ao
|
||||
# modelo "Entregas SUDESTE MM.AAAA_FINAL.xlsx".
|
||||
require 'caxlsx'
|
||||
|
||||
module Analytics
|
||||
class PlanilhaEntregasXlsx
|
||||
def initialize(linhas)
|
||||
@linhas = linhas
|
||||
end
|
||||
|
||||
# String binária do .xlsx.
|
||||
def gerar
|
||||
pkg = Axlsx::Package.new
|
||||
pkg.workbook.add_worksheet(name: 'ENTREGAS') do |sheet|
|
||||
negrito = sheet.workbook.styles.add_style(b: true)
|
||||
sheet.add_row(PlanilhaEntregas::CABECALHO, style: negrito)
|
||||
@linhas.each do |l|
|
||||
valores, tipos = linha(l)
|
||||
sheet.add_row(valores, types: tipos)
|
||||
end
|
||||
sheet.auto_filter = "A1:Z#{@linhas.size + 1}"
|
||||
end
|
||||
pkg.to_stream.read
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# A..V direto da tabela gade (texto); W..Z do rastreio:
|
||||
# W STATUS (completed/failed...), X ENTREGA (Sim/Não), Y DATA OCORRÊNCIA
|
||||
# (data real do checkout), Z OCORRÊNCIA (motivo do insucesso).
|
||||
def linha(l)
|
||||
status_r = l['rastreio_status'].to_s
|
||||
entregue = if status_r == 'completed'
|
||||
'Sim'
|
||||
elsif Entrega::STATUS_FALHA.include?(status_r)
|
||||
'Não'
|
||||
else
|
||||
''
|
||||
end
|
||||
data_ocorrencia = data_de(l['rastreio_checkout'])
|
||||
|
||||
valores = PlanilhaEntregas::COLUNAS_GADE.keys.map { |c| l[c].to_s } +
|
||||
[status_r, entregue, data_ocorrencia || '', l['rastreio_observation'].to_s]
|
||||
tipos = Array.new(PlanilhaEntregas::COLUNAS_GADE.size + 2, :string) +
|
||||
[(data_ocorrencia ? :date : :string), :string]
|
||||
[valores, tipos]
|
||||
end
|
||||
|
||||
def data_de(valor)
|
||||
return nil if valor.nil?
|
||||
return valor.to_date if valor.respond_to?(:to_date)
|
||||
Date.parse(valor.to_s)
|
||||
rescue ArgumentError, TypeError
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user