Atualição de interface area consolidação e Relatório em PDF

This commit is contained in:
2026-06-16 16:38:42 -03:00
parent 7c57b8e09d
commit 80c068c798
5 changed files with 37 additions and 7 deletions

View File

@@ -140,7 +140,7 @@ class ConsolidacoesController < ApplicationController
# GET /consolidacoes/:id/gerar_pdf_holerite?motorista=X
def gerar_pdf_holerite
authorize @consolidacao, :show?
autorizar_holerite!(params[:motorista])
motorista = params[:motorista]
user_mot = User.motorista.find_by('LOWER(nome) = ?', motorista.to_s.downcase)
user_mot&.regenerar_login_token! if user_mot&.login_token.blank?
@@ -167,6 +167,17 @@ class ConsolidacoesController < ApplicationController
private
# Staff (show?) baixa qualquer holerite. O motorista baixa só o PRÓPRIO — o PDF
# já é filtrado pelo nome dele, então não há vazamento de dados de terceiros.
def autorizar_holerite!(motorista_nome)
if current_user.motorista?
raise Pundit::NotAuthorizedError unless motorista_nome.to_s.casecmp?(current_user.nome.to_s)
skip_authorization
else
authorize @consolidacao, :show?
end
end
def set_consolidacao
@consolidacao = Consolidacao.find(params[:id])
end

View File

@@ -6,7 +6,7 @@ import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = [
"linha", "checkbox", "toggles", "barra", "percentual",
"linha", "checkbox", "selecionarTodos", "toggles", "barra", "percentual",
"contadorClassificadas", "contadorTotal",
"qtdNormal", "qtdRetirada", "qtdBonus", "qtdDesconto", "valorTotal"
]
@@ -64,6 +64,12 @@ export default class extends Controller {
this.atualizarResumo(resp)
}
// ── Selecionar / desmarcar todos os checkboxes ─────────────
selecionarTodos(event) {
const marcar = event.currentTarget.checked
this.checkboxTargets.forEach(c => { c.checked = marcar })
}
// ── Marcar selecionados como X ─────────────────────────────
async marcarSelecionados(event) {
const tipo = event.currentTarget.dataset.tipo
@@ -81,6 +87,7 @@ export default class extends Controller {
})
ids.forEach(id => this.setToggle(id, tipo, true))
this.checkboxTargets.forEach(c => c.checked = false)
if (this.hasSelecionarTodosTarget) this.selecionarTodosTarget.checked = false
this.atualizarResumo(resp)
}

View File

@@ -29,13 +29,14 @@ module Pdf
secao "ENTREGAS (#{@entregas.size})"
if @entregas.any?
linhas = [['#', 'Tracking', 'NF / Local', 'Tipo', 'Valor']]
linhas = [['#', 'Tracking', 'NF', 'Endereço', 'Tipo', 'Valor']]
@entregas.each_with_index do |e, i|
original = e.entrega_original
linhas << [
(i + 1).to_s,
e.tracking_id.to_s.first(14),
original ? "NF #{original.numero_nf} · #{original.local.to_s.first(30)}" : '—',
original ? "NF #{original.numero_nf}" : '—',
original ? original.address.to_s.first(45) : '—',
e.tipo_cor[:label],
(e.desconto? ? '-' : '') + moeda(e.valor_aplicado)
]
@@ -46,7 +47,7 @@ module Pdf
t.row(0).background_color = PRETO
t.row(0).text_color = 'FFFFFF'
t.row(0).font_style = :bold
t.columns(4).align = :right
t.columns(5).align = :right
t.row(1..-1).borders = [:bottom]
t.row(1..-1).border_color = 'DDDDDD'
end

View File

@@ -6,7 +6,7 @@
<div class="mb-6">
<%= link_to '← Voltar (Passo 2)',
validar_consolidacao_consolidacao_entregas_path(@consolidacao, motorista: @motorista),
class: 'text-gray-500 hover:text-orange-500 text-sm' %>
class: 'inline-flex items-center gap-1 bg-[#1a1a1a] hover:bg-[#2a2a2a] text-white border border-[#2a2a2a] hover:border-orange-500 font-semibold px-4 py-2.5 rounded-lg text-sm min-h-[44px]' %>
<h1 class="text-2xl font-bold text-white mt-2">📋 Revisão — <%= @motorista %></h1>
<p class="text-gray-400 text-sm"><%= @consolidacao.nome %></p>
</div>
@@ -52,6 +52,7 @@
<thead class="bg-[#0a0a0a] text-gray-400">
<tr>
<th class="text-left px-4 py-3">Tracking / NF</th>
<th class="text-left px-4 py-3">Endereço</th>
<th class="text-left px-4 py-3">Tipo</th>
<th class="text-right px-4 py-3">Valor</th>
</tr>
@@ -60,6 +61,7 @@
<% @classificacoes.each do |c| %>
<tr>
<td class="px-4 py-3 text-white font-mono text-xs"><%= c.tracking_id %></td>
<td class="px-4 py-3 text-gray-300 text-xs"><%= c.entrega_original&.address.presence || '—' %></td>
<td class="px-4 py-3">
<span class="<%= c.tipo_cor[:bg] %> <%= c.tipo_cor[:text] %> px-2 py-1 rounded text-xs font-bold">
<%= c.tipo_cor[:label] %>

View File

@@ -9,7 +9,8 @@
<%# Header %>
<div class="mb-6">
<%= link_to '← Voltar (Passo 1)', wizard_consolidacao_path(@consolidacao), class: 'text-gray-500 hover:text-orange-500 text-sm' %>
<%= link_to '← Voltar (Passo 1)', wizard_consolidacao_path(@consolidacao),
class: 'inline-flex items-center gap-1 bg-[#1a1a1a] hover:bg-[#2a2a2a] text-white border border-[#2a2a2a] hover:border-orange-500 font-semibold px-4 py-2.5 rounded-lg text-sm min-h-[44px]' %>
<div class="flex items-center justify-between mt-2">
<div>
<h1 class="text-2xl font-bold text-white">🚚 <%= @motorista %></h1>
@@ -44,6 +45,13 @@
✓ Marcar todos como Normal
</button>
<label class="flex items-center gap-2 text-gray-300 text-sm cursor-pointer select-none border-l border-[#2a2a2a] pl-3">
<input type="checkbox" data-validacao-target="selecionarTodos"
data-action="change->validacao#selecionarTodos"
class="rounded text-orange-500 bg-[#0a0a0a] border-[#2a2a2a] w-5 h-5">
Selecionar todos
</label>
<div class="flex items-center gap-2">
<span class="text-gray-500 text-sm">Selecionados como:</span>
<button data-action="click->validacao#marcarSelecionados" data-tipo="entrega_normal"
@@ -84,6 +92,7 @@
<p class="text-white font-semibold truncate">
NF <%= e.numero_nf %> · <%= e.local %>
</p>
<p class="text-gray-400 text-xs truncate">📍 <%= e.address.presence || 'sem endereço' %></p>
<p class="text-gray-500 text-xs">
📅 <%= e.planned_date %> · 🚚 <%= e.vehicle.presence || 'sem veículo' %>
<% if e.atraso_minutos > 0 %> · ⏱️ atraso <%= e.atraso_minutos %>min<% end %>