Impĺantação Da função de alteração de cada lancamento no banco da simplerout
This commit is contained in:
124
app/services/simpli_route/client.rb
Normal file
124
app/services/simpli_route/client.rb
Normal file
@@ -0,0 +1,124 @@
|
||||
# app/services/simpli_route/client.rb
|
||||
#
|
||||
# Cliente HTTP da API do SimpliRoute (base de ESCRITA). A tabela de rastreio
|
||||
# local (model Entrega) é só leitura e sincroniza a partir daqui; para corrigir
|
||||
# um lançamento o ADM grava direto por este cliente.
|
||||
#
|
||||
# Usa Net::HTTP (stdlib) — o projeto não tem Faraday/HTTParty.
|
||||
#
|
||||
require 'net/http'
|
||||
require 'json'
|
||||
|
||||
module SimpliRoute
|
||||
class Error < StandardError; end
|
||||
|
||||
# Erro esperado/apresentável ao usuário (NF não achada, ambiguidade, etc.).
|
||||
class NotFound < Error; end
|
||||
|
||||
class Client
|
||||
# Motivos de insucesso são fixos (todos type=failed). Cache curto porque
|
||||
# muda raríssimo, mas evita bater na API a cada abertura de tela.
|
||||
OBSERVATIONS_CACHE_KEY = 'simpliroute/observations'.freeze
|
||||
OBSERVATIONS_TTL = 1.hour
|
||||
|
||||
def initialize(token: SimpliRoute.token, base_url: SimpliRoute.base_url)
|
||||
raise Error, 'SIMPLIROUTE_TOKEN não configurado no ambiente.' if token.blank?
|
||||
|
||||
@token = token
|
||||
@base_uri = URI.parse(base_url)
|
||||
end
|
||||
|
||||
# Lista de motivos [{ 'id' => uuid, 'type' => 'failed', 'label' => 'ÓBITO' }, ...]
|
||||
def observations
|
||||
Rails.cache.fetch(OBSERVATIONS_CACHE_KEY, expires_in: OBSERVATIONS_TTL) do
|
||||
Array(get('/v1/routes/observations/'))
|
||||
end
|
||||
end
|
||||
|
||||
# Visita única (hash) pelo id numérico.
|
||||
def visita(id)
|
||||
get("/v1/routes/visits/#{id}/")
|
||||
end
|
||||
|
||||
# Todas as visitas de uma data (Array de hashes).
|
||||
def visitas_da_data(data)
|
||||
Array(get("/v1/routes/visits/?planned_date=#{data.to_date.iso8601}"))
|
||||
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.
|
||||
# Levanta NotFound (nada) ou Error (ambiguidade sem tracking_id).
|
||||
def resolver_id(entrega)
|
||||
data = entrega.planned_date&.to_date
|
||||
raise NotFound, 'Entrega sem planned_date — impossível localizar na API.' if data.blank?
|
||||
|
||||
visitas = visitas_da_data(data)
|
||||
|
||||
if entrega.tracking_id.present?
|
||||
achada = visitas.find { |v| v['tracking_id'].to_s == entrega.tracking_id.to_s }
|
||||
return achada['id'] if achada
|
||||
end
|
||||
|
||||
nf = entrega.reference_id.to_s
|
||||
por_nf = visitas.select { |v| v['reference'].to_s == nf }
|
||||
raise NotFound, "Visita da NF #{nf} não encontrada na API na data #{data.strftime('%d/%m/%Y')}." if por_nf.empty?
|
||||
if por_nf.size > 1
|
||||
raise Error, "Mais de uma visita para a NF #{nf} em #{data.strftime('%d/%m/%Y')} — não é possível resolver com segurança."
|
||||
end
|
||||
|
||||
por_nf.first['id']
|
||||
end
|
||||
|
||||
# Atualiza campos da visita (PATCH — só o que vier em `attrs`). Retorna a
|
||||
# visita atualizada (hash). Preferimos PATCH ao checkout para não sobrescrever
|
||||
# assinatura/geo originais sem intenção.
|
||||
def atualizar_visita(id, attrs)
|
||||
patch("/v1/routes/visits/#{id}/", attrs)
|
||||
end
|
||||
|
||||
# Histórico da visita (best-effort — a API pode devolver []).
|
||||
def historico(id)
|
||||
Array(get("/v1/routes/visits/#{id}/history/"))
|
||||
rescue Error
|
||||
[]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def get(path)
|
||||
requisicao(Net::HTTP::Get.new(caminho(path)))
|
||||
end
|
||||
|
||||
def patch(path, body)
|
||||
req = Net::HTTP::Patch.new(caminho(path))
|
||||
req.body = body.to_json
|
||||
requisicao(req)
|
||||
end
|
||||
|
||||
def caminho(path)
|
||||
URI.join(@base_uri.to_s, path)
|
||||
end
|
||||
|
||||
def requisicao(req)
|
||||
req['Authorization'] = "Token #{@token}"
|
||||
req['Content-Type'] = 'application/json'
|
||||
req['Accept'] = 'application/json'
|
||||
|
||||
res = Net::HTTP.start(@base_uri.host, @base_uri.port, use_ssl: @base_uri.scheme == 'https',
|
||||
open_timeout: 10, read_timeout: 30) do |http|
|
||||
http.request(req)
|
||||
end
|
||||
|
||||
unless res.is_a?(Net::HTTPSuccess)
|
||||
raise Error, "SimpliRoute respondeu #{res.code}: #{res.body.to_s.truncate(300)}"
|
||||
end
|
||||
|
||||
res.body.present? ? JSON.parse(res.body) : nil
|
||||
rescue JSON::ParserError => e
|
||||
raise Error, "Resposta inválida do SimpliRoute: #{e.message}"
|
||||
rescue Net::OpenTimeout, Net::ReadTimeout
|
||||
raise Error, 'Tempo esgotado ao falar com o SimpliRoute. Tente de novo.'
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user