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
|
# GET /consolidacoes/:id/gerar_pdf_holerite?motorista=X
|
||||||
def gerar_pdf_holerite
|
def gerar_pdf_holerite
|
||||||
authorize @consolidacao, :show?
|
autorizar_holerite!(params[:motorista])
|
||||||
motorista = params[:motorista]
|
motorista = params[:motorista]
|
||||||
user_mot = User.motorista.find_by('LOWER(nome) = ?', motorista.to_s.downcase)
|
user_mot = User.motorista.find_by('LOWER(nome) = ?', motorista.to_s.downcase)
|
||||||
user_mot&.regenerar_login_token! if user_mot&.login_token.blank?
|
user_mot&.regenerar_login_token! if user_mot&.login_token.blank?
|
||||||
@@ -167,6 +167,17 @@ class ConsolidacoesController < ApplicationController
|
|||||||
|
|
||||||
private
|
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
|
def set_consolidacao
|
||||||
@consolidacao = Consolidacao.find(params[:id])
|
@consolidacao = Consolidacao.find(params[:id])
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { Controller } from "@hotwired/stimulus"
|
|||||||
|
|
||||||
export default class extends Controller {
|
export default class extends Controller {
|
||||||
static targets = [
|
static targets = [
|
||||||
"linha", "checkbox", "toggles", "barra", "percentual",
|
"linha", "checkbox", "selecionarTodos", "toggles", "barra", "percentual",
|
||||||
"contadorClassificadas", "contadorTotal",
|
"contadorClassificadas", "contadorTotal",
|
||||||
"qtdNormal", "qtdRetirada", "qtdBonus", "qtdDesconto", "valorTotal"
|
"qtdNormal", "qtdRetirada", "qtdBonus", "qtdDesconto", "valorTotal"
|
||||||
]
|
]
|
||||||
@@ -64,6 +64,12 @@ export default class extends Controller {
|
|||||||
this.atualizarResumo(resp)
|
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 ─────────────────────────────
|
// ── Marcar selecionados como X ─────────────────────────────
|
||||||
async marcarSelecionados(event) {
|
async marcarSelecionados(event) {
|
||||||
const tipo = event.currentTarget.dataset.tipo
|
const tipo = event.currentTarget.dataset.tipo
|
||||||
@@ -81,6 +87,7 @@ export default class extends Controller {
|
|||||||
})
|
})
|
||||||
ids.forEach(id => this.setToggle(id, tipo, true))
|
ids.forEach(id => this.setToggle(id, tipo, true))
|
||||||
this.checkboxTargets.forEach(c => c.checked = false)
|
this.checkboxTargets.forEach(c => c.checked = false)
|
||||||
|
if (this.hasSelecionarTodosTarget) this.selecionarTodosTarget.checked = false
|
||||||
this.atualizarResumo(resp)
|
this.atualizarResumo(resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,13 +29,14 @@ module Pdf
|
|||||||
secao "ENTREGAS (#{@entregas.size})"
|
secao "ENTREGAS (#{@entregas.size})"
|
||||||
|
|
||||||
if @entregas.any?
|
if @entregas.any?
|
||||||
linhas = [['#', 'Tracking', 'NF / Local', 'Tipo', 'Valor']]
|
linhas = [['#', 'Tracking', 'NF', 'Endereço', 'Tipo', 'Valor']]
|
||||||
@entregas.each_with_index do |e, i|
|
@entregas.each_with_index do |e, i|
|
||||||
original = e.entrega_original
|
original = e.entrega_original
|
||||||
linhas << [
|
linhas << [
|
||||||
(i + 1).to_s,
|
(i + 1).to_s,
|
||||||
e.tracking_id.to_s.first(14),
|
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.tipo_cor[:label],
|
||||||
(e.desconto? ? '-' : '') + moeda(e.valor_aplicado)
|
(e.desconto? ? '-' : '') + moeda(e.valor_aplicado)
|
||||||
]
|
]
|
||||||
@@ -46,7 +47,7 @@ module Pdf
|
|||||||
t.row(0).background_color = PRETO
|
t.row(0).background_color = PRETO
|
||||||
t.row(0).text_color = 'FFFFFF'
|
t.row(0).text_color = 'FFFFFF'
|
||||||
t.row(0).font_style = :bold
|
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).borders = [:bottom]
|
||||||
t.row(1..-1).border_color = 'DDDDDD'
|
t.row(1..-1).border_color = 'DDDDDD'
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<div class="mb-6">
|
<div class="mb-6">
|
||||||
<%= link_to '← Voltar (Passo 2)',
|
<%= link_to '← Voltar (Passo 2)',
|
||||||
validar_consolidacao_consolidacao_entregas_path(@consolidacao, motorista: @motorista),
|
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>
|
<h1 class="text-2xl font-bold text-white mt-2">📋 Revisão — <%= @motorista %></h1>
|
||||||
<p class="text-gray-400 text-sm"><%= @consolidacao.nome %></p>
|
<p class="text-gray-400 text-sm"><%= @consolidacao.nome %></p>
|
||||||
</div>
|
</div>
|
||||||
@@ -52,6 +52,7 @@
|
|||||||
<thead class="bg-[#0a0a0a] text-gray-400">
|
<thead class="bg-[#0a0a0a] text-gray-400">
|
||||||
<tr>
|
<tr>
|
||||||
<th class="text-left px-4 py-3">Tracking / NF</th>
|
<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-left px-4 py-3">Tipo</th>
|
||||||
<th class="text-right px-4 py-3">Valor</th>
|
<th class="text-right px-4 py-3">Valor</th>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -60,6 +61,7 @@
|
|||||||
<% @classificacoes.each do |c| %>
|
<% @classificacoes.each do |c| %>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="px-4 py-3 text-white font-mono text-xs"><%= c.tracking_id %></td>
|
<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">
|
<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">
|
<span class="<%= c.tipo_cor[:bg] %> <%= c.tipo_cor[:text] %> px-2 py-1 rounded text-xs font-bold">
|
||||||
<%= c.tipo_cor[:label] %>
|
<%= c.tipo_cor[:label] %>
|
||||||
|
|||||||
@@ -9,7 +9,8 @@
|
|||||||
|
|
||||||
<%# Header %>
|
<%# Header %>
|
||||||
<div class="mb-6">
|
<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 class="flex items-center justify-between mt-2">
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-2xl font-bold text-white">🚚 <%= @motorista %></h1>
|
<h1 class="text-2xl font-bold text-white">🚚 <%= @motorista %></h1>
|
||||||
@@ -44,6 +45,13 @@
|
|||||||
✓ Marcar todos como Normal
|
✓ Marcar todos como Normal
|
||||||
</button>
|
</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">
|
<div class="flex items-center gap-2">
|
||||||
<span class="text-gray-500 text-sm">Selecionados como:</span>
|
<span class="text-gray-500 text-sm">Selecionados como:</span>
|
||||||
<button data-action="click->validacao#marcarSelecionados" data-tipo="entrega_normal"
|
<button data-action="click->validacao#marcarSelecionados" data-tipo="entrega_normal"
|
||||||
@@ -84,6 +92,7 @@
|
|||||||
<p class="text-white font-semibold truncate">
|
<p class="text-white font-semibold truncate">
|
||||||
NF <%= e.numero_nf %> · <%= e.local %>
|
NF <%= e.numero_nf %> · <%= e.local %>
|
||||||
</p>
|
</p>
|
||||||
|
<p class="text-gray-400 text-xs truncate">📍 <%= e.address.presence || 'sem endereço' %></p>
|
||||||
<p class="text-gray-500 text-xs">
|
<p class="text-gray-500 text-xs">
|
||||||
📅 <%= e.planned_date %> · 🚚 <%= e.vehicle.presence || 'sem veículo' %>
|
📅 <%= e.planned_date %> · 🚚 <%= e.vehicle.presence || 'sem veículo' %>
|
||||||
<% if e.atraso_minutos > 0 %> · ⏱️ atraso <%= e.atraso_minutos %>min<% end %>
|
<% if e.atraso_minutos > 0 %> · ⏱️ atraso <%= e.atraso_minutos %>min<% end %>
|
||||||
|
|||||||
Reference in New Issue
Block a user