Correção do função de editar lançamento para quer consiga listar todas
This commit is contained in:
@@ -33,6 +33,9 @@ class Admin::EdicaoLancamentosController < ApplicationController
|
||||
#
|
||||
# Com um único lançamento já vem o card montado, para não custar um segundo
|
||||
# round-trip no caso comum.
|
||||
#
|
||||
# Aceita ?de=&ate= para varrer um período (a API só filtra por UM dia, então
|
||||
# varremos dia a dia). Sem período, usa as datas que o espelho conhece.
|
||||
def buscar
|
||||
authorize :edicao_lancamento
|
||||
|
||||
@@ -40,15 +43,21 @@ class Admin::EdicaoLancamentosController < ApplicationController
|
||||
return render_erro('Informe o número da NF.') if nf.blank?
|
||||
|
||||
entregas = Entrega.por_nf(nf).to_a
|
||||
return render_erro("NF #{nf} não encontrada na base de rastreio.") if entregas.empty?
|
||||
datas = datas_de_busca(entregas)
|
||||
return render_erro(sem_datas_msg(nf, entregas)) if datas.empty?
|
||||
|
||||
ocorrencias = ocorrencias_da_nf(nf, entregas)
|
||||
falhas = []
|
||||
ocorrencias = ocorrencias_da_nf(nf, entregas, datas, falhas)
|
||||
unica = ocorrencias.first if ocorrencias.one? && ocorrencias.first[:id].present?
|
||||
|
||||
render json: {
|
||||
ok: true,
|
||||
ocorrencias: ocorrencias,
|
||||
visita: (unica && payload_visita(unica[:id], entrega_de(entregas, unica[:tracking_id]))),
|
||||
visita: (unica && payload_seguro(unica, entregas, falhas)),
|
||||
periodo: { de: datas.first, ate: datas.last, dias: datas.size },
|
||||
# Dias que a API não respondeu. Vão para a tela: uma busca que devolve
|
||||
# menos do que existe TEM que dizer que devolveu menos.
|
||||
falhas: falhas,
|
||||
motivos: motivos_seguros
|
||||
}
|
||||
rescue SimpliRoute::NotFound => e
|
||||
@@ -152,18 +161,53 @@ class Admin::EdicaoLancamentosController < ApplicationController
|
||||
@client ||= SimpliRoute::Client.new
|
||||
end
|
||||
|
||||
# Máximo de dias varridos numa busca por período. A API só filtra por UM dia,
|
||||
# então cada dia é uma chamada — com `search` cada uma custa ~0,6 s e elas vão
|
||||
# em lotes paralelos, o que torna dois meses viável; o teto existe para a tela
|
||||
# não virar uma varredura sem fim.
|
||||
MAX_DIAS_VARREDURA = 62
|
||||
|
||||
# Quais dias consultar na API:
|
||||
# • com ?de=/?ate= — o período pedido (o SimpliRoute duplica plano para
|
||||
# outra data, e aí o espelho não tem como saber qual é);
|
||||
# • sem período — as datas que o espelho conhece (barato: 1 ou 2 chamadas).
|
||||
def datas_de_busca(entregas)
|
||||
de = data_param(:de)
|
||||
ate = data_param(:ate)
|
||||
|
||||
if de || ate
|
||||
inicio, fim = [de || ate, ate || de].minmax
|
||||
(inicio..fim).first(MAX_DIAS_VARREDURA)
|
||||
else
|
||||
entregas.filter_map { |e| e.planned_date&.to_date }.uniq.sort
|
||||
end
|
||||
end
|
||||
|
||||
def data_param(chave)
|
||||
valor = params[chave].to_s.strip
|
||||
return nil if valor.blank?
|
||||
|
||||
Date.parse(valor)
|
||||
rescue Date::Error
|
||||
nil
|
||||
end
|
||||
|
||||
# Sem datas não há o que consultar — e o motivo muda a saída para o ADM.
|
||||
def sem_datas_msg(nf, entregas)
|
||||
if entregas.empty?
|
||||
"NF #{nf} não encontrada na base de rastreio. Informe um período para procurar direto no SimpliRoute."
|
||||
else
|
||||
"NF #{nf} está na base de rastreio mas sem data planejada. Informe um período para procurar na API."
|
||||
end
|
||||
end
|
||||
|
||||
# Todos os lançamentos da NF, cruzando as DUAS fontes:
|
||||
# • espelho de rastreio (Entrega) — traz motorista/veículo;
|
||||
# • API do SimpliRoute, nas datas desses lançamentos — pega a visita
|
||||
# duplicada que a sincronização ainda não trouxe para o espelho.
|
||||
#
|
||||
# A API não tem busca global por NF (só por data), então as datas vêm do
|
||||
# espelho: 1 chamada por data distinta (plano duplicado costuma cair no mesmo
|
||||
# dia, e aí é 1 chamada só).
|
||||
def ocorrencias_da_nf(nf, entregas)
|
||||
datas = entregas.filter_map { |e| e.planned_date&.to_date }.uniq
|
||||
# • API do SimpliRoute, nas datas varridas — pega a visita duplicada que a
|
||||
# sincronização ainda não trouxe para o espelho.
|
||||
def ocorrencias_da_nf(nf, entregas, datas, falhas)
|
||||
trackings = entregas.map { |e| e.tracking_id.to_s }
|
||||
do_dia = datas.flat_map { |data| client.visitas_da_data(data) }
|
||||
do_dia = visitas_dos_dias(datas, nf, falhas)
|
||||
|
||||
visitas = do_dia.select { |v| v['reference'].to_s == nf || trackings.include?(v['tracking_id'].to_s) }
|
||||
.uniq { |v| v['id'] }
|
||||
@@ -176,13 +220,48 @@ class Admin::EdicaoLancamentosController < ApplicationController
|
||||
# ele existe em vez de sumir silenciosamente — que é o bug que estamos
|
||||
# corrigindo.
|
||||
vistos = visitas.map { |v| v['tracking_id'].to_s }
|
||||
orfas = entregas.reject { |e| vistos.include?(e.tracking_id.to_s) }.map { |e| ocorrencia_do_espelho(e) }
|
||||
orfas = entregas.reject { |e| vistos.include?(e.tracking_id.to_s) }
|
||||
.map { |e| ocorrencia_do_espelho(e, datas.include?(e.planned_date&.to_date)) }
|
||||
|
||||
# Mais recente primeiro (a data vem como texto ISO da API e como timestamp
|
||||
# do espelho — os 10 primeiros caracteres normalizam as duas).
|
||||
(achadas + orfas).sort_by { |o| [o[:data].to_s[0, 10], o[:id].to_i] }.reverse
|
||||
end
|
||||
|
||||
# Dias consultados de uma vez. Cada chamada abre a própria conexão HTTP (ver
|
||||
# SimpliRoute::Client#requisicao) e não toca o banco, então dá para
|
||||
# paralelizar em lotes; o lote pequeno evita martelar a API do SimpliRoute.
|
||||
MAX_PARALELO_VARREDURA = 6
|
||||
|
||||
# Passa a NF como `search` — sem isso cada dia baixaria ~3,8 MB (o dia inteiro)
|
||||
# e um mês seria inviável. Ver a nota em SimpliRoute::Client#visitas_da_data.
|
||||
def visitas_dos_dias(datas, nf, falhas)
|
||||
mutex = Mutex.new
|
||||
|
||||
datas.each_slice(MAX_PARALELO_VARREDURA).flat_map do |lote|
|
||||
lote.map { |data| Thread.new { visitas_do_dia(data, nf, falhas, mutex) } }.flat_map(&:value)
|
||||
end
|
||||
end
|
||||
|
||||
# Um dia que a API não responde NÃO pode derrubar a busca inteira: registra a
|
||||
# falha e segue com os outros dias. (Foi assim que uma NF com visitas em duas
|
||||
# datas passou a não devolver nada em vez de devolver o que deu certo.)
|
||||
def visitas_do_dia(data, nf, falhas, mutex)
|
||||
client.visitas_da_data(data, busca: nf)
|
||||
rescue SimpliRoute::Error => e
|
||||
mutex.synchronize { falhas << { data: data.strftime('%d/%m/%Y'), erro: e.message } }
|
||||
[]
|
||||
end
|
||||
|
||||
# Abrir o card do lançamento único é conveniência: se falhar, a lista ainda
|
||||
# tem que aparecer.
|
||||
def payload_seguro(ocorrencia, entregas, falhas)
|
||||
payload_visita(ocorrencia[:id], entrega_de(entregas, ocorrencia[:tracking_id]))
|
||||
rescue SimpliRoute::Error => e
|
||||
falhas << { data: ocorrencia[:data].to_s[0, 10], erro: "não foi possível abrir o lançamento: #{e.message}" }
|
||||
nil
|
||||
end
|
||||
|
||||
def entrega_de(entregas, tracking_id)
|
||||
return nil if tracking_id.blank?
|
||||
|
||||
@@ -233,7 +312,7 @@ class Admin::EdicaoLancamentosController < ApplicationController
|
||||
}
|
||||
end
|
||||
|
||||
def ocorrencia_do_espelho(entrega)
|
||||
def ocorrencia_do_espelho(entrega, data_varrida)
|
||||
{
|
||||
id: nil,
|
||||
tracking_id: entrega.tracking_id,
|
||||
@@ -246,7 +325,11 @@ class Admin::EdicaoLancamentosController < ApplicationController
|
||||
veiculo: entrega.vehicle,
|
||||
editavel: false,
|
||||
no_painel: true,
|
||||
aviso: 'Só no painel — não está entre as visitas desta data no SimpliRoute, então não dá para editar'
|
||||
aviso: (if data_varrida
|
||||
'Só no painel — não está entre as visitas desta data no SimpliRoute, então não dá para editar'
|
||||
else
|
||||
'Fora do período consultado — inclua a data deste lançamento no período para poder editá-lo'
|
||||
end)
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user