diff --git a/Imagens para implantação /Imagem colada (22).png b/Imagens para implantação /Imagem colada (22).png new file mode 100644 index 0000000..808f3a4 Binary files /dev/null and b/Imagens para implantação /Imagem colada (22).png differ diff --git a/app/controllers/consolidacao_entregas_controller.rb b/app/controllers/consolidacao_entregas_controller.rb index 976d5be..0721808 100644 --- a/app/controllers/consolidacao_entregas_controller.rb +++ b/app/controllers/consolidacao_entregas_controller.rb @@ -48,6 +48,12 @@ class ConsolidacaoEntregasController < ApplicationController # Nomes dos motoristas ATIVOS — alimenta o seletor do apontamento 100% manual. @motoristas_consolidacao = @consolidacao.consolidacao_motoristas.ativos.pluck(:motorista_nome) + # Lançamentos manuais do motorista (apontamentos + termo) — feedback no resumo + # e na barra de progresso. Ficam fora do conjunto elegível do período. + @manuais = @consolidacao.consolidacao_entregas + .where(motorista_nome: @motorista, manual: true) + .distinct.count(:tracking_id) + # Filtro "apenas não classificadas" if params[:apenas_pendentes] == '1' @entregas = @entregas.reject { |e| @classificacoes.key?(e.tracking_id) } @@ -58,7 +64,8 @@ class ConsolidacaoEntregasController < ApplicationController retirada: Configuracao.preco_retirada, bonus: Configuracao.preco_bonus, desconto: Configuracao.preco_desconto, - extraordinaria: Configuracao.preco_extraordinaria + extraordinaria: Configuracao.preco_extraordinaria, + termo: Configuracao.preco_termo } end @@ -450,6 +457,7 @@ class ConsolidacaoEntregasController < ApplicationController ok: true, classificadas: classificadas, classificadas_escopo: escopo, + manuais: classificacoes.where(manual: true).distinct.count(:tracking_id), por_tipo: classificacoes.group(:tipo).count, valor_total: classificacoes.sum { |e| e.tipo == 'desconto' ? -e.valor_aplicado : e.valor_aplicado }.to_f, veiculos_status: status diff --git a/app/controllers/consolidacoes_controller.rb b/app/controllers/consolidacoes_controller.rb index e215600..3051995 100644 --- a/app/controllers/consolidacoes_controller.rb +++ b/app/controllers/consolidacoes_controller.rb @@ -165,6 +165,8 @@ class ConsolidacoesController < ApplicationController authorize @consolidacao, :update? @arquivados = @consolidacao.consolidacao_motoristas.arquivados.order(:motorista_nome) + # Alimenta o datalist do "Adicionar motorista" (mesma fonte da criação). + @motoristas_disponiveis = Entrega.motoristas_ativos @motoristas = @consolidacao.consolidacao_motoristas.ativos.order(:motorista_nome).map do |cm| total = @consolidacao.entregas_elegiveis_count(cm.motorista_nome) classificadas = @consolidacao.classificadas_count(cm.motorista_nome) @@ -299,6 +301,59 @@ class ConsolidacoesController < ApplicationController notice: "#{cm.motorista_nome} restaurado para a consolidação." end + # DELETE /consolidacoes/:id/excluir_motorista — apaga de vez 1 motorista desta + # consolidação (só admin/gerente). Caminho destrutivo (o reversível é arquivar): + # como consolidacao_entregas se liga ao motorista por STRING (motorista_nome, + # sem FK/cascade), apagamos os lançamentos dele à mão antes de remover o registro. + def excluir_motorista + authorize @consolidacao, :destroy? + cm = @consolidacao.consolidacao_motoristas.find(params[:consolidacao_motorista_id]) + + if cm.pago? + return redirect_to wizard_consolidacao_path(@consolidacao), + alert: "#{cm.motorista_nome} já está pago — estorne o pagamento antes de excluir." + end + + nome = cm.motorista_nome + ActiveRecord::Base.transaction do + @consolidacao.consolidacao_entregas.where(motorista_nome: nome).delete_all + cm.destroy! + end + # cm já não existe — atualiza o total da consolidação direto (soma dos ativos). + @consolidacao.update_column(:valor_total, @consolidacao.consolidacao_motoristas.ativos.sum(:valor_total)) + auditar!(:editar, @consolidacao, dados_novos: { excluir_motorista: nome }) + redirect_to wizard_consolidacao_path(@consolidacao), + notice: "#{nome} excluído desta consolidação." + end + + # POST /consolidacoes/:id/adicionar_motorista — inclui um motorista nesta + # consolidação (ou restaura, se estava arquivado). Útil para refazer rápido + # depois de excluir/arquivar por erro de precificação. + def adicionar_motorista + authorize @consolidacao, :update? + nome = params[:motorista_nome].to_s.strip + + if nome.blank? + return redirect_to wizard_consolidacao_path(@consolidacao), alert: 'Informe o nome do motorista.' + end + + cm = @consolidacao.consolidacao_motoristas.find_or_initialize_by(motorista_nome: nome) + aviso = + if cm.persisted? && cm.arquivado? + cm.desarquivar!(current_user) + "#{nome} restaurado para a consolidação." + elsif cm.persisted? + "#{nome} já está nesta consolidação." + else + cm.save! + "#{nome} adicionado à consolidação." + end + + @consolidacao.recalcular_motorista!(nome) + auditar!(:editar, @consolidacao, dados_novos: { adicionar_motorista: nome }) + redirect_to wizard_consolidacao_path(@consolidacao), notice: aviso + end + # ── FASE 7 — PDFs ──────────────────────────────────────────── # GET /consolidacoes/:id/gerar_pdf_relatorio?motorista=X diff --git a/app/javascript/controllers/adicionar_motorista_controller.js b/app/javascript/controllers/adicionar_motorista_controller.js new file mode 100644 index 0000000..e6d9909 --- /dev/null +++ b/app/javascript/controllers/adicionar_motorista_controller.js @@ -0,0 +1,23 @@ +// app/javascript/controllers/adicionar_motorista_controller.js +// Stimulus — modal "Adicionar motorista" no Passo 1. Só abre/fecha o modal; o +// formulário em si submete normalmente (Turbo), redirecionando de volta ao wizard. + +import { Controller } from "@hotwired/stimulus" + +export default class extends Controller { + static targets = ["modal", "input"] + + abrir() { + this.modalTarget.classList.remove("hidden") + if (this.hasInputTarget) this.inputTarget.focus() + } + + fechar() { + this.modalTarget.classList.add("hidden") + } + + // Fecha ao clicar no fundo (fora do card) + fundo(event) { + if (event.target === this.modalTarget) this.fechar() + } +} diff --git a/app/javascript/controllers/validacao_controller.js b/app/javascript/controllers/validacao_controller.js index 5b74e2f..f91f422 100644 --- a/app/javascript/controllers/validacao_controller.js +++ b/app/javascript/controllers/validacao_controller.js @@ -7,9 +7,10 @@ import { Controller } from "@hotwired/stimulus" export default class extends Controller { static targets = [ "linha", "checkbox", "selecionarTodos", "modoRemover", "rotuloAcao", - "toggles", "barra", "percentual", "chipVeiculo", - "contadorClassificadas", "contadorTotal", - "qtdNormal", "qtdRetirada", "qtdBonus", "qtdDesconto", "qtdExtraordinaria", "valorTotal" + "toggles", "barra", "barraManuais", "percentual", "chipVeiculo", + "contadorClassificadas", "contadorTotal", "contadorManuais", "manuaisInfo", + "qtdNormal", "qtdRetirada", "qtdBonus", "qtdDesconto", "qtdExtraordinaria", + "qtdTermo", "qtdManuais", "valorTotal" ] static values = { @@ -181,20 +182,36 @@ export default class extends Controller { this.qtdDescontoTarget.textContent = porTipo["desconto"] || 0 if (this.hasQtdExtraordinariaTarget) this.qtdExtraordinariaTarget.textContent = porTipo["extraordinaria"] || 0 + if (this.hasQtdTermoTarget) + this.qtdTermoTarget.textContent = porTipo["termo"] || 0 const valor = dados.valor_total || 0 this.valorTotalTarget.textContent = "R$ " + valor.toLocaleString("pt-BR", { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + // Lançamentos manuais do motorista (apontamentos + termo): feedback visual. + // Ficam fora do denominador de elegíveis (o gate de finalização não muda). + const manuais = dados.manuais || 0 + if (this.hasQtdManuaisTarget) this.qtdManuaisTarget.textContent = manuais + if (this.hasContadorManuaisTarget) this.contadorManuaisTarget.textContent = manuais + if (this.hasManuaisInfoTarget) this.manuaisInfoTarget.classList.toggle("hidden", manuais <= 0) + // Barra/contador refletem o ESCOPO atual (veículo selecionado, se houver). const classificadas = (dados.classificadas_escopo ?? dados.classificadas) || 0 this.contadorClassificadasTarget.textContent = classificadas const total = this.totalValue || parseInt(this.contadorTotalTarget.textContent) const pct = total > 0 ? Math.round((classificadas / total) * 100) : 0 - this.barraTarget.style.width = `${Math.min(pct, 100)}%` this.percentualTarget.textContent = `${Math.min(pct, 100)}%` + // Barra com 2 segmentos, proporcionais ao total geral (elegíveis + manuais): + // laranja/verde = elegíveis classificadas · azul = lançamentos manuais. + const denom = total + manuais + this.barraTarget.style.width = denom > 0 ? `${(classificadas / denom) * 100}%` : "0%" + if (this.hasBarraManuaisTarget) + this.barraManuaisTarget.style.width = denom > 0 ? `${(manuais / denom) * 100}%` : "0%" + + // Cor do segmento elegível: verde quando todas as elegíveis estão classificadas. this.barraTarget.classList.toggle("bg-green-500", pct >= 100) this.barraTarget.classList.toggle("bg-orange-500", pct < 100) diff --git a/app/views/consolidacao_entregas/validar.html.erb b/app/views/consolidacao_entregas/validar.html.erb index 26eb535..c577657 100644 --- a/app/views/consolidacao_entregas/validar.html.erb +++ b/app/views/consolidacao_entregas/validar.html.erb @@ -31,11 +31,15 @@ <% if @veiculo.present? %>🚚 <%= @veiculo %> — <% end %> <%= @classificadas %> de <%= @total_entregas %> entregas classificadas + · + +<%= @manuais %> manuais -
⭐ Bônus: <%= moeda(@precos[:bonus]) %>
⚠️ Desconto: -<%= moeda(@precos[:desconto]) %>
✨ Extraordinária: <%= moeda(@precos[:extraordinaria]) %> (valor por entrega)
+📄 Termo: <%= moeda(@precos[:termo]) %>
📅 <%= l @consolidacao.data_inicio, format: :short %> → <%= l @consolidacao.data_fim, format: :short %> @@ -31,19 +31,36 @@
Inclua um motorista nesta consolidação. Se ele estava arquivado, será restaurado.
+ + <%= form_with url: adicionar_motorista_consolidacao_path(@consolidacao), method: :post do |f| %> + + + +<%= cm.motorista_nome %>
Arquivado · não conta no total
- <% if pode_arquivar_motorista %> - <%= button_to '♻️ Restaurar', - desarquivar_motorista_consolidacao_path(@consolidacao, consolidacao_motorista_id: cm.id), - method: :post, - class: 'text-green-400 hover:text-green-300 border border-[#2a2a2a] hover:border-green-500/50 px-3 py-2 rounded-lg text-xs min-h-[40px]' %> - <% end %> +