Atualição de interface area consolidação e Relatório em PDF
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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] %>
|
||||
|
||||
@@ -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 %>
|
||||
|
||||
Reference in New Issue
Block a user