118 lines
4.2 KiB
Ruby
118 lines
4.2 KiB
Ruby
# app/controllers/dashboard_controller.rb
|
|
class DashboardController < ApplicationController
|
|
MAX_DIAS_GRAFICO = 92 # limita a granularidade diária do gráfico (≈ 1 trimestre)
|
|
|
|
def index
|
|
skip_authorization
|
|
|
|
# Motorista tem painel próprio — não enxerga o dashboard administrativo
|
|
return redirect_to(motorista_dashboard_path) if current_user.motorista?
|
|
|
|
@periodo_inicio, @periodo_fim = periodo_selecionado
|
|
carregar_dados_dashboard
|
|
end
|
|
|
|
private
|
|
|
|
# Faixa de datas vinda do filtro de calendário (params inicio/fim).
|
|
# Default: do início do mês corrente até hoje. Datas inválidas caem no default
|
|
# e a ordem é normalizada caso venham invertidas.
|
|
def periodo_selecionado
|
|
inicio = parse_data(params[:inicio]) || Date.current.beginning_of_month
|
|
fim = parse_data(params[:fim]) || Date.current
|
|
inicio, fim = fim, inicio if fim < inicio
|
|
[inicio, fim]
|
|
end
|
|
|
|
# Parse tolerante: data inválida/ausente vira nil (para o default assumir).
|
|
def parse_data(str)
|
|
return nil if str.blank?
|
|
Date.parse(str)
|
|
rescue ArgumentError, TypeError
|
|
nil
|
|
end
|
|
|
|
def carregar_dados_dashboard
|
|
# Entregas do período (tabela read-only) — restritas à conta Gade
|
|
entregas = Entrega.da_conta_gade.no_periodo(@periodo_inicio, @periodo_fim)
|
|
|
|
# Totais gerais
|
|
@total_entregas = entregas.count
|
|
@entregas_pagas = entregas.pagas.count
|
|
@entregas_pendentes = @total_entregas - @entregas_pagas
|
|
|
|
# Configurações de preço
|
|
config = Configuracao.mapa_de_precos
|
|
|
|
# Valor estimado total
|
|
@valor_estimado = (@entregas_pagas * config[:entrega]).round(2)
|
|
|
|
# Por motorista (top 10)
|
|
@motoristas = entregas.pagas
|
|
.group(:driver)
|
|
.count
|
|
.sort_by { |_, v| -v }
|
|
.first(10)
|
|
.map do |driver, qtd|
|
|
{
|
|
nome: driver,
|
|
entregas: qtd,
|
|
valor: (qtd * config[:entrega]).round(2)
|
|
}
|
|
end
|
|
|
|
# Por local de entrega (contact_name)
|
|
@por_operacao = entregas.pagas
|
|
.group(:contact_name)
|
|
.count
|
|
.sort_by { |_, v| -v }
|
|
.first(6)
|
|
.to_h
|
|
|
|
# Evolução diária no período (para Chart.js)
|
|
@grafico_diario = build_grafico_diario(entregas, config[:entrega])
|
|
|
|
# Consolidações cujo período (data_inicio..data_fim) cruza o período do dashboard
|
|
# — independe de quando foram criadas (created_at).
|
|
@consolidacoes_mes = Consolidacao.ativas
|
|
.where('data_inicio <= ? AND data_fim >= ?', @periodo_fim, @periodo_inicio)
|
|
@consolidacoes_abertas = @consolidacoes_mes.where(status: :rascunho).count
|
|
@consolidacoes_fechadas = @consolidacoes_mes.where(status: :finalizada).count
|
|
|
|
# Histórico estimado mais recente
|
|
@historico_recente = HistoricoEstimado.order(created_at: :desc).limit(5)
|
|
end
|
|
|
|
def build_grafico_diario(entregas, preco_entrega)
|
|
dias = (@periodo_inicio..@periodo_fim).to_a
|
|
|
|
if dias.size <= MAX_DIAS_GRAFICO
|
|
# Granularidade diária (períodos curtos)
|
|
contagem = entregas.pagas.group("DATE(planned_date)").count.transform_keys(&:to_s)
|
|
labels = dias.map { |d| d.strftime('%d/%m') }
|
|
valores = dias.map { |d| ((contagem[d.to_s] || 0) * preco_entrega).round(2) }
|
|
qtds = dias.map { |d| contagem[d.to_s] || 0 }
|
|
else
|
|
# Período longo: agrega por mês para o gráfico não ficar vazio nem poluído.
|
|
contagem = entregas.pagas.group("TO_CHAR(planned_date, 'YYYY-MM')").count
|
|
meses = meses_no_periodo
|
|
labels = meses.map { |m| Date.strptime(m, '%Y-%m').strftime('%m/%Y') }
|
|
valores = meses.map { |m| ((contagem[m] || 0) * preco_entrega).round(2) }
|
|
qtds = meses.map { |m| contagem[m] || 0 }
|
|
end
|
|
|
|
{ labels: labels, valores: valores, qtds: qtds }
|
|
end
|
|
|
|
# Meses ("YYYY-MM") do início ao fim do período selecionado, inclusive.
|
|
def meses_no_periodo
|
|
meses = []
|
|
cursor = @periodo_inicio.beginning_of_month
|
|
while cursor <= @periodo_fim
|
|
meses << cursor.strftime('%Y-%m')
|
|
cursor = cursor.next_month
|
|
end
|
|
meses
|
|
end
|
|
end
|