Correção Dash board e implantação Relaório de ADM

This commit is contained in:
2026-06-22 17:11:53 -03:00
parent e23ca0f10d
commit 94d8420bbd
16 changed files with 453 additions and 39 deletions

View File

@@ -0,0 +1,103 @@
# app/services/pdf/relatorio_financeiro_consolidacao_pdf.rb
#
# Relatório Financeiro de uma consolidação: resumo por motorista (pago x
# pendente), composição por tipo de lançamento e totais. Para o admin imprimir
# para conferência e histórico.
#
module Pdf
class RelatorioFinanceiroConsolidacaoPdf < BasePdf
VERDE = '22C55E'
def initialize(consolidacao:)
super()
@consolidacao = consolidacao
@motoristas = consolidacao.consolidacao_motoristas.order(:motorista_nome)
@entregas = consolidacao.consolidacao_entregas
end
private
def corpo
secao "RELATÓRIO FINANCEIRO — CONSOLIDAÇÃO"
@pdf.text "Consolidação: #{@consolidacao.nome}", size: 11, style: :bold
@pdf.text "Período: #{@consolidacao.data_inicio.strftime('%d/%m/%Y')} a #{@consolidacao.data_fim.strftime('%d/%m/%Y')}", size: 10
@pdf.fill_color CINZA
@pdf.text "Status: #{@consolidacao.status.humanize} · Pagamento: #{@consolidacao.status_pagamento.to_s.humanize}", size: 10
@pdf.fill_color '000000'
secao "POR MOTORISTA (#{@motoristas.size})"
tabela_motoristas
secao "COMPOSIÇÃO POR TIPO"
tabela_por_tipo
@pdf.start_new_page if @pdf.cursor < 140
secao "TOTAIS"
totais
end
def tabela_motoristas
if @motoristas.any?
linhas = [['#', 'Motorista', 'Pagamento', 'Forma', 'Valor']]
@motoristas.each_with_index do |cm, i|
pagamento = cm.pago? ? "Pago em #{cm.pago_em.strftime('%d/%m/%Y')}" : 'Pendente'
linhas << [
(i + 1).to_s,
cm.motorista_nome,
pagamento,
cm.forma_pagamento&.humanize || '—',
moeda(cm.valor_total)
]
end
@pdf.table(linhas, header: true, width: @pdf.bounds.width,
column_widths: { 0 => 22, 4 => 80 },
cell_style: { size: 8, padding: [4, 6] }) do |t|
t.row(0).background_color = PRETO
t.row(0).text_color = 'FFFFFF'
t.row(0).font_style = :bold
t.columns(4).align = :right
t.row(1..-1).borders = [:bottom]
t.row(1..-1).border_color = 'DDDDDD'
end
else
@pdf.fill_color CINZA
@pdf.text 'Nenhum motorista nesta consolidação.', size: 10
@pdf.fill_color '000000'
end
end
def tabela_por_tipo
soma = @entregas.reorder(nil).group(:tipo).sum(:valor_aplicado)
qtd = @entregas.reorder(nil).group(:tipo).count
linhas = [['Tipo', 'Quantidade', 'Valor']]
ConsolidacaoEntrega::TIPO_CORES.each do |tipo, cfg|
valor = soma[tipo].to_f
sinal = tipo == 'desconto' ? '-' : ''
linhas << [cfg[:label], (qtd[tipo] || 0).to_s, "#{sinal}#{moeda(valor)}"]
end
@pdf.table(linhas, width: 360, cell_style: { size: 9, padding: [4, 8] }) do |t|
t.row(0).background_color = LARANJA
t.row(0).text_color = '000000'
t.row(0).font_style = :bold
t.columns(2).align = :right
end
end
def totais
linhas = [
['Custo total', moeda(@consolidacao.valor_total)],
['Pago', moeda(@consolidacao.valor_pago)],
['A pagar', moeda(@consolidacao.valor_pendente)]
]
@pdf.table(linhas, width: 300, cell_style: { size: 11, padding: [6, 10] }) do |t|
t.columns(0).font_style = :bold
t.columns(1).align = :right
t.row(1).text_color = VERDE
end
end
end
end

View File

@@ -0,0 +1,154 @@
# app/services/pdf/relatorio_financeiro_periodo_pdf.rb
#
# Relatório Financeiro consolidado de um período: espelha a visão financeira do
# dashboard (KPIs, custo por operação/motorista/tipo e pagamentos) num PDF para
# o admin imprimir para conferência e histórico. Recebe os agregados já
# calculados pelo DashboardController (mesma fonte de números da tela).
#
module Pdf
class RelatorioFinanceiroPeriodoPdf < BasePdf
VERDE = '22C55E'
def initialize(periodo_inicio:, periodo_fim:, operacao_label:, dados:)
super()
@inicio = periodo_inicio
@fim = periodo_fim
@op = operacao_label
@d = dados
end
private
def corpo
secao "RELATÓRIO FINANCEIRO — PERÍODO"
@pdf.text "Período: #{@inicio.strftime('%d/%m/%Y')} a #{@fim.strftime('%d/%m/%Y')}", size: 11, style: :bold
@pdf.fill_color CINZA
@pdf.text "Operação: #{@op}", size: 10
@pdf.fill_color '000000'
secao "INDICADORES"
kpis
secao "CUSTO POR OPERAÇÃO"
tabela_pares(@d[:por_operacao], 'Operação')
secao "CUSTO POR MOTORISTA"
tabela_pares(@d[:por_motorista], 'Motorista')
secao "COMPOSIÇÃO POR TIPO"
tabela_por_tipo
@pdf.start_new_page if @pdf.cursor < 160
secao "PAGAMENTOS REALIZADOS NO PERÍODO"
tabela_pagamentos_feitos
secao "PAGAMENTOS PENDENTES"
tabela_pagamentos_pendentes
end
def kpis
linhas = [
['Custo total', moeda(@d[:custo_total])],
['Ticket médio', moeda(@d[:ticket_medio])],
['Pago', moeda(@d[:pago])],
['A pagar', moeda(@d[:pendente])]
]
@pdf.table(linhas, width: 300, cell_style: { size: 11, padding: [6, 10] }) do |t|
t.columns(0).font_style = :bold
t.columns(1).align = :right
t.row(2).text_color = VERDE
end
end
# pares: Array de [label, valor]
def tabela_pares(pares, titulo_col)
if pares.present?
linhas = [[titulo_col, 'Valor']]
pares.each { |label, valor| linhas << [label.to_s, moeda(valor)] }
@pdf.table(linhas, header: true, width: @pdf.bounds.width,
cell_style: { size: 9, padding: [4, 8] }) do |t|
t.row(0).background_color = LARANJA
t.row(0).text_color = '000000'
t.row(0).font_style = :bold
t.columns(1).align = :right
t.row(1..-1).borders = [:bottom]
t.row(1..-1).border_color = 'DDDDDD'
end
else
sem_dados
end
end
def tabela_por_tipo
if @d[:por_tipo].present?
linhas = [['Tipo', 'Valor']]
@d[:por_tipo].each { |h| linhas << [h[:label], moeda(h[:valor])] }
@pdf.table(linhas, header: true, width: 360, cell_style: { size: 9, padding: [4, 8] }) do |t|
t.row(0).background_color = LARANJA
t.row(0).text_color = '000000'
t.row(0).font_style = :bold
t.columns(1).align = :right
end
else
sem_dados
end
end
def tabela_pagamentos_feitos
registros = @d[:pagamentos_feitos]
if registros.present?
linhas = [['Pago em', 'Motorista', 'Consolidação', 'Forma', 'Valor']]
registros.each do |cm|
linhas << [
cm.pago_em.strftime('%d/%m/%Y'),
cm.motorista_nome,
cm.consolidacao.nome,
cm.forma_pagamento&.humanize || '—',
moeda(cm.valor_total)
]
end
tabela_listagem(linhas)
@pdf.move_down 6
@pdf.fill_color VERDE
@pdf.text "Total pago no período: #{moeda(@d[:pag_pago_valor])} · #{@d[:pag_pago_qtd]} pagamento(s)", size: 10, style: :bold
@pdf.fill_color '000000'
else
sem_dados('Nenhum pagamento realizado neste período.')
end
end
def tabela_pagamentos_pendentes
registros = @d[:pagamentos_pendentes]
if registros.present?
linhas = [['Motorista', 'Consolidação', 'Valor']]
registros.each do |cm|
linhas << [cm.motorista_nome, cm.consolidacao.nome, moeda(cm.valor_total)]
end
tabela_listagem(linhas)
@pdf.move_down 6
@pdf.text "Total a pagar: #{moeda(@d[:pag_pend_valor])} · #{@d[:pag_pend_qtd]} motorista(s)", size: 10, style: :bold
else
sem_dados('Nenhuma pendência no período.')
end
end
def tabela_listagem(linhas)
@pdf.table(linhas, header: true, width: @pdf.bounds.width,
cell_style: { size: 8, padding: [4, 6] }) do |t|
t.row(0).background_color = PRETO
t.row(0).text_color = 'FFFFFF'
t.row(0).font_style = :bold
t.columns(-1).align = :right
t.row(1..-1).borders = [:bottom]
t.row(1..-1).border_color = 'DDDDDD'
end
end
def sem_dados(msg = 'Sem dados no período.')
@pdf.fill_color CINZA
@pdf.text msg, size: 10
@pdf.fill_color '000000'
end
end
end