Correção de horario e implantação da geração de Romaneio
This commit is contained in:
101
app/services/romaneios/enriquecimento_gade.rb
Normal file
101
app/services/romaneios/enriquecimento_gade.rb
Normal file
@@ -0,0 +1,101 @@
|
||||
# app/services/romaneios/enriquecimento_gade.rb
|
||||
#
|
||||
# Preenche APARELHO, TELEFONES e NOME do romaneio a partir da tabela da operação
|
||||
# (gade_entregas_*). É o que dispensa a SEGUNDA PLANILHA do programa Python: lá o
|
||||
# operador subia um .xlsx só com NOTA FISCAL + STATUS para marcar quem é NOVO; aqui
|
||||
# o STATUS já está no banco, na própria operação.
|
||||
#
|
||||
# A tabela é SOMENTE LEITURA (apenas SELECT), o nome passa por Operacao.valida? +
|
||||
# quote_table_name e as NFs por conn.quote.
|
||||
module Romaneios
|
||||
class EnriquecimentoGade
|
||||
# chave usada no SELECT => coluna real na gade.
|
||||
#
|
||||
# ⚠️ `status` PRECISA sair aliasado: o espelho de rastreio também tem uma coluna
|
||||
# `status` (o estado da visita), e qualquer query que junte as duas trocaria uma
|
||||
# pela outra em silêncio. Analytics::OperacaoMetricas já faz esse mesmo alias
|
||||
# (GADE_OPCIONAIS) pelo mesmo motivo.
|
||||
COLUNAS = {
|
||||
'nota_fiscal' => 'nota_fiscal',
|
||||
'nome_completo' => 'nome_completo',
|
||||
'telefones' => 'telefones',
|
||||
'status' => 'status_gade'
|
||||
}.freeze
|
||||
|
||||
# Sem essas, não há o que enriquecer.
|
||||
ESSENCIAIS = %w[nota_fiscal].freeze
|
||||
|
||||
attr_reader :tabela
|
||||
|
||||
def initialize(tabela)
|
||||
@tabela = tabela.to_s
|
||||
raise ArgumentError, "Operação inválida: #{@tabela}" unless Operacao.valida?(@tabela)
|
||||
end
|
||||
|
||||
# { '90507' => { aparelho: 'SIM', telefones: '(11) ...', nome: 'CICERA ...' } }
|
||||
# NF que não estiver na operação simplesmente não vem no Hash — é assim que a
|
||||
# tela marca "fora da operação" (plano avulso / de inclusão), mesma semântica de
|
||||
# Analytics::NotasForaOperacao.
|
||||
def para_notas(notas)
|
||||
lista = Array(notas).map { |n| n.to_s.strip }.reject(&:empty?).uniq
|
||||
return {} if lista.empty?
|
||||
|
||||
registros(lista).each_with_object({}) do |r, memo|
|
||||
nf = r['nota_fiscal'].to_s.strip
|
||||
next if nf.empty?
|
||||
|
||||
memo[nf] = {
|
||||
aparelho: Normalizador.aparelho_por_status(r['status_gade']),
|
||||
telefones: r['telefones'].to_s.strip,
|
||||
nome: Normalizador.nome(r['nome_completo'])
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
# Colunas que esta operação NÃO tem — a tela precisa DIZER isso.
|
||||
#
|
||||
# O esquema das gade_entregas_* varia por operação (só `nota_fiscal` é
|
||||
# garantida). Quando falta `status`, o APARELHO sai vazio para TODO MUNDO — e um
|
||||
# romaneio em que ninguém recebe aparelho, entregue calado, é pior do que um
|
||||
# aviso feio na tela.
|
||||
def colunas_ausentes
|
||||
@colunas_ausentes ||= COLUNAS.keys - existentes
|
||||
end
|
||||
|
||||
def utilizavel?
|
||||
(ESSENCIAIS - existentes).empty?
|
||||
end
|
||||
|
||||
def label
|
||||
Operacao.label(@tabela)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def conn
|
||||
ActiveRecord::Base.connection
|
||||
end
|
||||
|
||||
def existentes
|
||||
@existentes ||= conn.columns(@tabela).map(&:name)
|
||||
end
|
||||
|
||||
def registros(lista)
|
||||
return [] unless utilizavel?
|
||||
|
||||
valores = lista.map { |n| conn.quote(n) }.join(', ')
|
||||
sql = "SELECT #{selects} FROM #{conn.quote_table_name(@tabela)} g " \
|
||||
"WHERE g.nota_fiscal IN (#{valores})"
|
||||
conn.select_all(sql).to_a
|
||||
end
|
||||
|
||||
# Coluna que não existir vira NULL com o mesmo alias, preservando a forma do
|
||||
# resultado — mesmo padrão de SimpliRoute::PlanilhaCarga#selects. As chaves de
|
||||
# COLUNAS são whitelist fixa (não há entrada de usuário no SQL).
|
||||
def selects
|
||||
COLUNAS.map do |coluna, apelido|
|
||||
existentes.include?(coluna) ? "g.#{coluna} AS #{apelido}" : "CAST(NULL AS text) AS #{apelido}"
|
||||
end.join(', ')
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user