, correção da pagina de gerar romaneio

This commit is contained in:
2026-08-28 11:43:41 -03:00
parent e778036df5
commit 94b2e5a0a5
3 changed files with 67 additions and 7 deletions

View File

@@ -20,7 +20,17 @@ module Romaneios
class PlanoDoDia
# Chaves candidatas nos hashes da API. Só as usamos se vierem PREENCHIDAS —
# a API devolve chave existente com valor nil o tempo todo.
CHAVES_VEICULO = %w[vehicle vehicle_name vehicle_id].freeze
#
# ⚠️ A ORDEM AQUI É O CONSERTO DE UM BUG REAL, não estilo. Na API o campo
# `vehicle` da rota é o **ID numérico** do veículo (630011), e o nome que a
# operação usa — o mesmo que a planilha do plano traz na coluna "Veículo" —
# é GADE_038. Com `vehicle` na frente, o ID ganhava sempre e o romaneio saía
# impresso "CONTROLE DE ENTREGA — 630011", com o motorista sem saber que
# carro pegar. Nome primeiro; ID só como último recurso, porque veículo
# vazio faz a linha ser descartada e o romaneio sair sem paradas.
CHAVES_VEICULO = %w[vehicle_name vehicle_label vehicle vehicle_id].freeze
# Dentro do objeto do veículo (quando a API aninha em vez de mandar o id).
CHAVES_NOME_VEIC = %w[name label display_name plate].freeze
CHAVES_MOTORISTA = %w[driver driver_name].freeze
CHAVES_ORDEM = %w[order sequence position route_order].freeze
@@ -59,7 +69,7 @@ module Romaneios
return [] if visitas.empty?
linhas = por_rotas(cliente, visitas)
linhas = por_visitas(visitas) if linhas.empty?
linhas = por_visitas(visitas, cliente) if linhas.empty?
@origem = 'api' if linhas.any?
linhas
rescue SimpliRoute::Error
@@ -77,7 +87,7 @@ module Romaneios
linhas = []
rotas.each do |rota|
veiculo = primeiro_valor(rota, CHAVES_VEICULO)
veiculo = nome_do_veiculo(rota, cliente)
next if veiculo.empty?
ids = Array(rota['visits']).map { |v| v.is_a?(Hash) ? v['id'] : v }
@@ -96,11 +106,11 @@ module Romaneios
# (b) A visita traz o veículo direto; a ordem sai de `order`/`sequence` ou,
# na falta, do horário estimado de chegada.
def por_visitas(visitas)
com_veiculo = visitas.reject { |v| primeiro_valor(v, CHAVES_VEICULO).empty? }
def por_visitas(visitas, cliente)
com_veiculo = visitas.reject { |v| nome_do_veiculo(v, cliente).empty? }
return [] if com_veiculo.empty?
com_veiculo.group_by { |v| primeiro_valor(v, CHAVES_VEICULO) }.flat_map do |veiculo, lista|
com_veiculo.group_by { |v| nome_do_veiculo(v, cliente) }.flat_map do |veiculo, lista|
ordenadas(lista).each_with_index.map do |visita, i|
monta(visita, veiculo, primeiro_valor(visita, CHAVES_MOTORISTA), i + 1)
end
@@ -123,6 +133,39 @@ module Romaneios
)
end
# O NOME do veículo como a operação o conhece (GADE_038), nunca o id.
#
# Três formas já vistas/possíveis no mesmo campo, por isso as três camadas:
# { "vehicle_name" => "GADE_038" } -> nome direto
# { "vehicle" => { "name" => "GADE_038" } } -> aninhado
# { "vehicle" => 630011 } -> só o id, precisa resolver
#
# Quando sobra só o id, procuramos o nome na lista de veículos da conta. Se
# essa busca não responder, devolvemos o id mesmo: um romaneio com o número
# do carro é ruim, um romaneio VAZIO (que é o que acontece devolvendo "") é
# pior — a linha sem veículo é descartada.
def nome_do_veiculo(hash, cliente)
aninhado = hash['vehicle']
return primeiro_valor(aninhado, CHAVES_NOME_VEIC) if aninhado.is_a?(Hash)
valor = primeiro_valor(hash, CHAVES_VEICULO)
return valor unless valor.match?(/\A\d+\z/)
nomes_de_veiculo(cliente)[valor].presence || valor
end
# id (string) => nome. Uma chamada por importação, memoizada: são ~70 veículos
# e a alternativa seria uma consulta por rota.
def nomes_de_veiculo(cliente)
@nomes_de_veiculo ||= cliente.veiculos.each_with_object({}) do |v, memo|
next unless v.is_a?(Hash)
id = v['id'].to_s.strip
nome = primeiro_valor(v, CHAVES_NOME_VEIC)
memo[id] = nome unless id.empty? || nome.empty?
end
end
def primeiro_valor(hash, chaves)
chaves.filter_map { |k| hash[k] }.map(&:to_s).find { |v| !v.strip.empty? }.to_s.strip
end

View File

@@ -80,6 +80,19 @@ module SimpliRoute
[]
end
# Veículos da conta, para traduzir o id numérico que vem nas rotas
# (`vehicle: 630011`) no nome que a operação usa (`GADE_038`).
#
# Best-effort pelo mesmo motivo de `rotas_da_data`: sem token na máquina de
# desenvolvimento não dá para verificar o caminho, então endpoint ausente
# devolve [] e Romaneios::PlanoDoDia segue com o id em vez de quebrar a
# importação inteira.
def veiculos
Array(get('/v1/routes/vehicles/'))
rescue Error
[]
end
# Resolve o `id` numérico (usado na URL de escrita) a partir de uma Entrega
# do espelho local, que só tem tracking_id (SR...) + reference_id (NF) +
# planned_date. Casa pelo tracking_id; se não achar, tenta pela NF.

View File

@@ -145,9 +145,13 @@ O bloco "Operação do mês" tem que aparecer em todo romaneio, e o formulário
precisa funcionar **sem JavaScript** (é `<details>` nativo + form comum):
```bash
agent-browser eval "const d=[...document.querySelectorAll('details')].find(x=>x.textContent.includes('operação')); d ? 'existe | aberto:'+d.open+' | opcoes:'+d.querySelectorAll('option').length : 'AUSENTE'"
agent-browser eval "(()=>{const d=[...document.querySelectorAll('details')].find(x=>x.querySelector('select[name=operacao_tabela]')); return d ? 'existe | aberto:'+d.open+' | opcoes:'+d.querySelectorAll('option').length : 'AUSENTE';})()"
```
⚠️ Procure pelo `select[name=operacao_tabela]`, **não** pelo texto "Operação do
mês": esse rótulo fica FORA do `<details>` e é renderizado em maiúsculas pelo
CSS, então busca por texto dá falso negativo (já aconteceu num diagnóstico).
**PASSOU se**: o bloco existe; num romaneio **sem** operação ele vem `aberto:true`
(a pendência fica à vista) e num **com** operação vem `aberto:false`; a lista tem
mais de uma `option`.