diff --git a/app/controllers/admin/edicao_lancamentos_controller.rb b/app/controllers/admin/edicao_lancamentos_controller.rb
index dbb4568..fa70edc 100644
--- a/app/controllers/admin/edicao_lancamentos_controller.rb
+++ b/app/controllers/admin/edicao_lancamentos_controller.rb
@@ -48,7 +48,17 @@ class Admin::EdicaoLancamentosController < ApplicationController
notes: visita['notes'],
checkout_time: visita['checkout_time'],
checkout_latitude: visita['checkout_latitude'],
- checkout_longitude: visita['checkout_longitude']
+ checkout_longitude: visita['checkout_longitude'],
+ planned_date: visita['planned_date'],
+ contato: visita['contact_name'],
+ telefone: visita['contact_phone'],
+ # Fotos do card: fachada vem do rastreio (mesma foto do mapa de operações);
+ # pictures/assinatura vêm da API. Só URLs http(s) chegam ao .
+ foto_fachada: url_imagem(entrega.try(:foto_da_fachada)),
+ pictures: Array(visita['pictures']).filter_map { |u| url_imagem(u) },
+ signature: url_imagem(visita['signature']),
+ motorista: entrega.driver,
+ veiculo: entrega.vehicle
},
motivos: motivos_seguros
}
@@ -59,20 +69,26 @@ class Admin::EdicaoLancamentosController < ApplicationController
end
# PATCH /admin/edicao_lancamento/atualizar
+ # HTML: fluxo de form clássico (fallback). JSON: salvar-por-campo do card
+ # (click-to-edit) — o fetch manda visit_id + só o campo editado.
def atualizar
authorize :edicao_lancamento
id = params[:visit_id].to_s.strip
- return redirect_to(admin_edicao_lancamento_path, alert: 'Visita não identificada.') if id.blank?
+ return responder_erro('Visita não identificada.') if id.blank?
anterior = client.visita(id)
attrs = mudancas(anterior)
+ # Confirmar um valor idêntico não é erro no inline edit — só não há o que fazer.
if attrs.empty?
- return redirect_to(admin_edicao_lancamento_path, alert: 'Nenhuma alteração informada.')
+ return respond_to do |format|
+ format.json { render json: { ok: true, campos: [], visita: {} } }
+ format.html { redirect_to admin_edicao_lancamento_path, alert: 'Nenhuma alteração informada.' }
+ end
end
if attrs['status'].present? && STATUS_VALIDOS.exclude?(attrs['status'])
- return redirect_to(admin_edicao_lancamento_path, alert: 'Status inválido.')
+ return responder_erro('Status inválido.')
end
atualizada = client.atualizar_visita(id, attrs)
@@ -87,17 +103,41 @@ class Admin::EdicaoLancamentosController < ApplicationController
request: request
)
- redirect_to admin_edicao_lancamento_path,
- notice: "Lançamento da NF #{atualizada['reference']} atualizado no SimpliRoute. " \
- 'O painel refletirá na próxima sincronização.'
+ respond_to do |format|
+ format.json do
+ render json: { ok: true, campos: attrs.keys, visita: atualizada.slice(*CAMPOS_EDITAVEIS) }
+ end
+ format.html do
+ redirect_to admin_edicao_lancamento_path,
+ notice: "Lançamento da NF #{atualizada['reference']} atualizado no SimpliRoute. " \
+ 'O painel refletirá na próxima sincronização.'
+ end
+ end
rescue SimpliRoute::Error => e
- redirect_to admin_edicao_lancamento_path, alert: "Não foi possível atualizar: #{e.message}"
+ responder_erro("Não foi possível atualizar: #{e.message}", status: :bad_gateway)
end
- # GET /admin/edicao_lancamento/historico?visit_id=123 → JSON (best-effort).
+ # GET /admin/edicao_lancamento/historico?visit_id=123 → JSON com as DUAS fontes:
+ # o histórico da API do SimpliRoute (best-effort, costuma vir vazio) e a nossa
+ # trilha do AuditoriaLog (quem editou, quando, de→para).
def historico
authorize :edicao_lancamento
- render json: { ok: true, historico: client.historico(params[:visit_id]) }
+
+ id = params[:visit_id].to_s.strip
+ interno = AuditoriaLog.por_entidade('SimpliRoute::Visita')
+ .where(entidade_id: id)
+ .includes(:user).recentes.limit(50)
+ .map do |log|
+ {
+ usuario: log.user&.nome_display || '—',
+ acao: log.acao,
+ quando: log.created_at.in_time_zone.strftime('%d/%m/%Y %H:%M'),
+ de: log.dados_anteriores,
+ para: log.dados_novos
+ }
+ end
+
+ render json: { ok: true, api: client.historico(id), interno: interno }
end
private
@@ -163,6 +203,21 @@ class Admin::EdicaoLancamentosController < ApplicationController
render json: { ok: false, erro: msg }, status: status
end
+ # Erro do atualizar nos dois formatos (JSON p/ inline edit, HTML p/ fallback).
+ def responder_erro(msg, status: :unprocessable_entity)
+ respond_to do |format|
+ format.json { render json: { ok: false, erro: msg }, status: status }
+ format.html { redirect_to admin_edicao_lancamento_path, alert: msg }
+ end
+ end
+
+ # Só aceita URL http(s) — evita injetar lixo no
do card (mesmo
+ # filtro do foto_url do OperacaoMetricas).
+ def url_imagem(valor)
+ url = valor.to_s.strip
+ url.match?(%r{\Ahttps?://}i) ? url : nil
+ end
+
# Sem token não há o que fazer — avisa e volta.
def garantir_configurado
return if SimpliRoute.configurado?
diff --git a/app/views/admin/edicao_lancamentos/show.html.erb b/app/views/admin/edicao_lancamentos/show.html.erb
index f9d45fc..20a8158 100644
--- a/app/views/admin/edicao_lancamentos/show.html.erb
+++ b/app/views/admin/edicao_lancamentos/show.html.erb
@@ -1,10 +1,18 @@
<%# app/views/admin/edicao_lancamentos/show.html.erb %>
-
Corrige status, motivo e comentários de uma entrega direto no SimpliRoute
+Corrige status, motivo e dados de checkout de uma entrega direto no SimpliRoute
As alterações são gravadas no SimpliRoute na hora, mas o painel e as - consolidações só refletem após a próxima sincronização da base de rastreio. - Toda alteração fica registrada na auditoria.
+As alterações são gravadas no SimpliRoute na hora (cada campo salva ao confirmar ✓), + mas o painel e as consolidações só refletem após a próxima sincronização da base de + rastreio. Toda alteração fica registrada na auditoria.
Consultando SimpliRoute…
—
-—
-NF — · visita —
-—
+Foto da fachada
+ +Fotos do SimpliRoute
+ ++ Dados do lançamento — clique num valor para editar +
+ +Carregando…
+