diff --git a/Imagens para correção /Imagem colada (10).png b/Imagens para correção /Imagem colada (10).png new file mode 100644 index 0000000..736190e Binary files /dev/null and b/Imagens para correção /Imagem colada (10).png differ diff --git a/Imagens para correção /Imagem colada (11).png b/Imagens para correção /Imagem colada (11).png new file mode 100644 index 0000000..cacc7ac Binary files /dev/null and b/Imagens para correção /Imagem colada (11).png differ diff --git a/app/controllers/consolidacao_entregas_controller.rb b/app/controllers/consolidacao_entregas_controller.rb index e965b37..4f49b1f 100644 --- a/app/controllers/consolidacao_entregas_controller.rb +++ b/app/controllers/consolidacao_entregas_controller.rb @@ -11,14 +11,15 @@ class ConsolidacaoEntregasController < ApplicationController authorize @consolidacao, :show? @motorista = params[:motorista] - # Entregas pagas do período no banco existente (read-only) + # Entregas pagas do período no banco existente (read-only). + # Período pela DATA REAL (checkout), não pela planejada. @entregas = Entrega.pagas .da_conta_gade - .no_periodo(@consolidacao.data_inicio, @consolidacao.data_fim) + .no_periodo_checkout(@consolidacao.data_inicio, @consolidacao.data_fim) .do_motorista(@motorista) @entregas = @entregas.da_veiculo(@consolidacao.vehicle_ids) if @consolidacao.vehicle_ids.present? @entregas = @entregas.da_operacoes(@consolidacao.operacoes) if @consolidacao.operacoes.present? - @entregas = @entregas.order(:planned_date) + @entregas = @entregas.order(:checkout) # Classificações já feitas — agrupadas por tracking_id (uma entrega pode ter # vários pilares). { tracking_id => [ConsolidacaoEntrega, ...] } @@ -184,7 +185,7 @@ class ConsolidacaoEntregasController < ApplicationController return [] unless motorista.present? rel = Entrega.pagas.da_conta_gade - .no_periodo(@consolidacao.data_inicio, @consolidacao.data_fim) + .no_periodo_checkout(@consolidacao.data_inicio, @consolidacao.data_fim) .do_motorista(motorista) rel = rel.da_veiculo(@consolidacao.vehicle_ids) if @consolidacao.vehicle_ids.present? rel = rel.da_operacoes(@consolidacao.operacoes) if @consolidacao.operacoes.present? diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb index f32a5cc..f0cee71 100644 --- a/app/controllers/dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -38,14 +38,18 @@ class DashboardController < ApplicationController @operacoes_agrupadas = Operacao.agrupadas_por_mes @operacao_filtro = Operacao.sanitizar(params[:operacoes]) - # Entregas do período (tabela read-only) — restritas à conta Gade - entregas = Entrega.da_conta_gade.no_periodo(@periodo_inicio, @periodo_fim) - entregas = entregas.da_operacoes(@operacao_filtro) if @operacao_filtro.present? + # PAGAS pela DATA REAL (checkout) — base de toda a análise financeira por data. + pagas = Entrega.da_conta_gade.pagas.no_periodo_checkout(@periodo_inicio, @periodo_fim) + pagas = pagas.da_operacoes(@operacao_filtro) if @operacao_filtro.present? + + # PENDENTES (não concluídas) pela data planejada — não têm checkout. + pendentes = Entrega.da_conta_gade.pendentes.no_periodo(@periodo_inicio, @periodo_fim) + pendentes = pendentes.da_operacoes(@operacao_filtro) if @operacao_filtro.present? # Totais gerais - @total_entregas = entregas.count - @entregas_pagas = entregas.pagas.count - @entregas_pendentes = @total_entregas - @entregas_pagas + @entregas_pagas = pagas.count + @entregas_pendentes = pendentes.count + @total_entregas = @entregas_pagas + @entregas_pendentes # Configurações de preço config = Configuracao.mapa_de_precos @@ -54,12 +58,11 @@ class DashboardController < ApplicationController @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| + @motoristas = pagas.group(:driver) + .count + .sort_by { |_, v| -v } + .first(10) + .map do |driver, qtd| { nome: driver, entregas: qtd, @@ -68,15 +71,14 @@ class DashboardController < ApplicationController end # Por local de entrega (contact_name) - @por_operacao = entregas.pagas - .group(:contact_name) - .count - .sort_by { |_, v| -v } - .first(6) - .to_h + @por_operacao = 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]) + # Evolução diária no período (para Chart.js) — por data real (checkout) + @grafico_diario = build_grafico_diario(pagas, config[:entrega]) # Consolidações cujo período (data_inicio..data_fim) cruza o período do dashboard # — independe de quando foram criadas (created_at). @@ -174,18 +176,20 @@ class DashboardController < ApplicationController .sort_by { |_, v| -v }.first(10) end - def build_grafico_diario(entregas, preco_entrega) + # `pagas` já vem filtrado por checkout no período. Agrupa pela DATA REAL + # (DATE(checkout)) — DATE() sobre timestamp não faz conversão de fuso. + def build_grafico_diario(pagas, 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) + contagem = pagas.group("DATE(checkout)").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 + contagem = pagas.group("TO_CHAR(checkout, '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) } diff --git a/app/models/entrega.rb b/app/models/entrega.rb index 0d28652..ff42d66 100644 --- a/app/models/entrega.rb +++ b/app/models/entrega.rb @@ -27,6 +27,14 @@ class Entrega < ApplicationRecord where(planned_date: inicio.to_date.beginning_of_day..fim.to_date.end_of_day) } + # Período pela DATA REAL da entrega (checkout) — usada nas análises financeiras + # ("quando a entrega foi concluída"). Comparação naïve com Date (intervalo + # meio-aberto: >= início e < fim+1), SEM conversão de fuso — bate com a análise + # feita direto no banco. Só faz sentido sobre entregas .pagas (checkout != nil). + scope :no_periodo_checkout, ->(inicio, fim) { + where('checkout >= ? AND checkout < ?', inicio.to_date, fim.to_date + 1) + } + scope :do_motorista, ->(nome) { where(driver: nome) } @@ -98,9 +106,10 @@ class Entrega < ApplicationRecord base.distinct.order(:vehicle).pluck(:vehicle).compact.reject(&:empty?) end - # Contagem de entregas pagas para cálculo estimado + # Contagem de entregas pagas para cálculo estimado. + # Período pela DATA REAL da entrega (checkout) — ver no_periodo_checkout. def self.contar_pagas(inicio:, fim:, motorista: nil, vehicle: nil, operacoes: nil) - base = pagas.da_conta_gade.no_periodo(inicio, fim) + base = pagas.da_conta_gade.no_periodo_checkout(inicio, fim) base = base.do_motorista(motorista) if motorista.present? base = base.da_veiculo(vehicle) if vehicle.present? base = base.da_operacoes(operacoes) if operacoes.present? diff --git a/app/models/operacao.rb b/app/models/operacao.rb index 29b8095..9fbc56b 100644 --- a/app/models/operacao.rb +++ b/app/models/operacao.rb @@ -74,11 +74,14 @@ class Operacao return vazio if validas.empty? entregas = Entrega.da_conta_gade.da_operacoes(validas) + pagas = entregas.pagas { motoristas: entregas.distinct.order(:driver).pluck(:driver).compact.reject(&:blank?), veiculos: entregas.distinct.order(:vehicle).pluck(:vehicle).compact.reject(&:blank?), - inicio: entregas.minimum(:planned_date)&.to_date, - fim: entregas.maximum(:planned_date)&.to_date + # Período sugerido pela DATA REAL (checkout), igual ao filtro de + # elegibilidade; cai para planned_date se ainda não houver pagas. + inicio: (pagas.minimum(:checkout) || entregas.minimum(:planned_date))&.to_date, + fim: (pagas.maximum(:checkout) || entregas.maximum(:planned_date))&.to_date } end diff --git a/app/views/consolidacao_entregas/validar.html.erb b/app/views/consolidacao_entregas/validar.html.erb index 34ae985..208023e 100644 --- a/app/views/consolidacao_entregas/validar.html.erb +++ b/app/views/consolidacao_entregas/validar.html.erb @@ -100,7 +100,7 @@

📍 <%= e.address.presence || 'sem endereço' %>

- 📅 <%= e.planned_date %> · 🚚 <%= e.vehicle.presence || 'sem veículo' %> + 📅 <%= e.checkout&.strftime('%d/%m/%Y') || '—' %> · 🚚 <%= e.vehicle.presence || 'sem veículo' %> <% if e.atraso_minutos > 0 %> · ⏱️ atraso <%= e.atraso_minutos %>min<% end %>