# app/controllers/consolidacao_entregas_controller.rb class ConsolidacaoEntregasController < ApplicationController include Auditavel before_action :set_consolidacao before_action :bloquear_se_finalizada, except: %i[validar revisar] # Rótulo/sentinela do grupo de entregas sem veículo no filtro "fechar por carro". SEM_VEICULO = '(sem veículo)'.freeze # GET /consolidacoes/:consolidacao_id/consolidacao_entregas/validar?motorista=X # WIZARD PASSO 2 — tela de validação com toggles def validar authorize @consolidacao, :show? @motorista = params[:motorista] @veiculos_sel = veiculos_param(params[:vehicle]) # filtro por veículo(s) — multi-seleção # Entregas atendidas do período no banco existente (read-only): sucesso + # insucesso, pois o motorista foi ao local e a entrega precisa constar. # Período pela DATA REAL (checkout), não pela planejada. @entregas = Entrega.atendidas .da_conta_gade .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 = filtrar_por_veiculo(@entregas, @veiculos_sel) if @veiculos_sel.any? @entregas = @entregas.order(:checkout) # Classificações já feitas — agrupadas por tracking_id (uma entrega pode ter # vários pilares). { tracking_id => [ConsolidacaoEntrega, ...] } @classificacoes = @consolidacao.consolidacao_entregas .where(motorista_nome: @motorista) .group_by(&:tracking_id) # Status "fechado" por veículo (filtro + acompanhamento) e totais com escopo: # quando um carro está selecionado, a barra reflete só aquele carro. @veiculos_status = veiculos_status(@motorista) if @veiculos_sel.any? sel = @veiculos_status.select { |v| @veiculos_sel.include?(v[:vehicle]) } @total_entregas = sel.sum { |v| v[:eligible] } @classificadas = sel.sum { |v| v[:classificadas] } else @total_entregas = @consolidacao.entregas_elegiveis_count(@motorista) @classificadas = @consolidacao.classificadas_count(@motorista) end # 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 = contar_manuais(@consolidacao.consolidacao_entregas .where(motorista_nome: @motorista, manual: true)) # Filtro "apenas não classificadas" if params[:apenas_pendentes] == '1' @entregas = @entregas.reject { |e| @classificacoes.key?(e.tracking_id) } end @precos = { entrega_normal: Configuracao.preco_entrega, retirada: Configuracao.preco_retirada, bonus: Configuracao.preco_bonus, desconto: Configuracao.preco_desconto, extraordinaria: Configuracao.preco_extraordinaria, termo: Configuracao.preco_termo } end # POST /consolidacoes/:consolidacao_id/consolidacao_entregas/classificar # Classifica UMA entrega (chamado pelo toggle via Turbo/fetch) def classificar authorize @consolidacao, :update? return render_nao_elegivel unless entrega_elegivel?(params[:motorista], params[:tracking_id]) # Toggle do pilar: se já existe, remove; senão, adiciona (permite combinar # vários pilares na mesma entrega). ce = @consolidacao.consolidacao_entregas .find_by(tracking_id: params[:tracking_id], tipo: params[:tipo]) if ce ce.destroy ativo = false else @consolidacao.consolidacao_entregas.create!( tracking_id: params[:tracking_id], motorista_nome: params[:motorista], tipo: params[:tipo], valor_aplicado: valor_classificacao(params[:tipo]), vehicle: veiculo_do_tracking(params[:tracking_id]), # snapshot p/ fechar por veículo created_by: current_user.id ) ativo = true end recalcular_motorista(params[:motorista]) respond_to do |format| format.json do render json: resumo_json(params[:motorista], vehicles: params[:vehicle]).merge( ativo: ativo, tracking_id: params[:tracking_id], tipo: params[:tipo] ) end format.html { redirect_back fallback_location: wizard_consolidacao_path(@consolidacao) } end end # POST /consolidacoes/:consolidacao_id/consolidacao_entregas/classificar_em_massa # Ações em massa: marcar todos / marcar selecionados def classificar_em_massa authorize @consolidacao, :update? tipo = params[:tipo] motorista = params[:motorista] tracking_ids = Array(params[:tracking_ids]).reject(&:blank?) return render_nao_elegivel unless motorista_na_consolidacao?(motorista) # Recorte por veículo(s) selecionado(s). Sem veículo = todos. elegiveis = tracking_ids_elegiveis(motorista, params[:vehicle]) # Sem tracking_ids = todas as elegíveis do período/veículo (botão "todos"). # Com tracking_ids → mantém só as que de fato são elegíveis para o motorista. tracking_ids = tracking_ids.empty? ? elegiveis : (tracking_ids & elegiveis) remover = params[:acao].to_s == 'remover' ActiveRecord::Base.transaction do if remover # Desmarca o pilar das entregas selecionadas (mantém os demais pilares). @consolidacao.consolidacao_entregas .where(motorista_nome: motorista, tipo: tipo, tracking_id: tracking_ids) .destroy_all else # ADICIONA o pilar às entregas (não remove os demais já marcados). valor = valor_classificacao(tipo) veiculos = veiculos_dos_trackings(tracking_ids) # tracking_id => vehicle (snapshot) tracking_ids.each do |tid| ce = @consolidacao.consolidacao_entregas.find_or_initialize_by(tracking_id: tid, tipo: tipo) ce.assign_attributes(motorista_nome: motorista, valor_aplicado: valor, created_by: current_user.id) ce.vehicle = veiculos[tid] if ce.vehicle.blank? ce.save! end end end recalcular_motorista(motorista) auditar!(:editar, @consolidacao, dados_novos: { acao_massa: tipo, modo: (remover ? 'remover' : 'adicionar'), qtd: tracking_ids.size, motorista: motorista }) respond_to do |format| format.json { render json: resumo_json(motorista, vehicles: params[:vehicle]) } format.html do verbo = remover ? 'desmarcadas de' : 'marcadas como' redirect_to validar_consolidacao_consolidacao_entregas_path(@consolidacao, motorista: motorista, vehicle: veiculos_param(params[:vehicle]).presence), notice: "#{tracking_ids.size} entregas #{verbo} #{tipo.humanize}." end end end # GET /consolidacoes/:consolidacao_id/consolidacao_entregas/revisar?motorista=X # WIZARD PASSO 3 — revisão do motorista def revisar authorize @consolidacao, :show? @motorista = params[:motorista] todas = @consolidacao.consolidacao_entregas.where(motorista_nome: @motorista) # Agregados calculados sobre o conjunto COMPLETO (não só a página exibida). # sum(:quantidade) (default 1) conta os termos lançados em lote como N entregas. @resumo = todas.group(:tipo).sum(:quantidade) @total = todas.sum do |c| c.tipo == 'desconto' ? -c.valor_aplicado : c.valor_aplicado end @total_pilares = todas.count @total_classificadas = todas.distinct.count(:tracking_id) # Paginação só da tabela exibida — 20 padrão, com seletor 20/50/100 @por_pagina = [20, 50, 100].include?(params[:por_pagina].to_i) ? params[:por_pagina].to_i : 20 begin @pagy, @classificacoes = pagy(todas.order(:created_at), limit: @por_pagina) rescue Pagy::OverflowError @pagy, @classificacoes = pagy(todas.order(:created_at), limit: @por_pagina, page: 1) end # Próximo motorista pendente (para botão "Próximo Motorista") — só ativos nomes_completos = @consolidacao.consolidacao_motoristas.ativos.map(&:motorista_nome) @proximo = nomes_completos.find do |nome| next false if nome == @motorista total = @consolidacao.entregas_elegiveis_count(nome) @consolidacao.classificadas_count(nome) < total end end # GET /consolidacoes/:consolidacao_id/consolidacao_entregas/buscar_nf?nf=123 # APONTAMENTO MANUAL — busca a NF na SimpleRoute (sem filtro de período). def buscar_nf authorize @consolidacao, :update? nf = params[:nf].to_s.strip return render(json: { ok: false, erro: 'Informe o número da nota.' }, status: :unprocessable_entity) if nf.blank? entregas = Entrega.da_conta_gade.por_nf(nf).order(:checkout) if entregas.empty? render json: { ok: false, erro: "Nota #{nf} não encontrada na base de rastreio." } else render json: { ok: true, nf: nf, entregas: entregas.map(&:resumo_apontamento) } end end # POST /consolidacoes/:consolidacao_id/consolidacao_entregas/apontar # Cria um apontamento manual a partir de um tracking_id encontrado pela NF. # Aceita um ou vários pilares (tipos[]) na mesma entrega. def apontar authorize @consolidacao, :update? entrega = Entrega.da_conta_gade.find_by(tracking_id: params[:tracking_id]) return render_apontamento_erro('Entrega não encontrada na base de rastreio.') unless entrega motorista = @consolidacao.adicionar_apontamento!(entrega, tipos_param, current_user) auditar!(:editar, @consolidacao, dados_novos: { apontamento_manual: entrega.numero_nf, tipos: tipos_param, motorista: motorista }) respond_to do |format| format.json { render json: resumo_json(motorista).merge(motorista: motorista) } format.html do redirect_to validar_consolidacao_consolidacao_entregas_path(@consolidacao, motorista: motorista), notice: "Apontamento da nota #{entrega.numero_nf} adicionado para #{motorista}." end end rescue ArgumentError => e render_apontamento_erro(e.message) end # POST /consolidacoes/:consolidacao_id/consolidacao_entregas/apontar_manual # Apontamento 100% MANUAL — não consulta a base de rastreio. O admin informa o # motorista (pode ser outro que não o da tela), a NF (texto livre), uma # observação/local e os pilares. Usado quando o motorista foi ao local mas a # NF ficou registrada com outro motorista no rastreio. def apontar_manual authorize @consolidacao, :update? motorista = @consolidacao.adicionar_apontamento_manual!( motorista: params[:motorista].to_s.strip, tipos: tipos_param, nf: params[:nf], obs: params[:obs], valor_extra: valor_extraordinaria_param, vehicle: params[:vehicle], # veículo escolhido no lançamento (Q3) user: current_user ) auditar!(:editar, @consolidacao, dados_novos: { apontamento_100_manual: params[:nf], motorista: motorista, tipos: tipos_param }) respond_to do |format| format.json { render json: resumo_json(motorista).merge(motorista: motorista) } format.html do redirect_to validar_consolidacao_consolidacao_entregas_path(@consolidacao, motorista: motorista), notice: "Apontamento manual adicionado para #{motorista}." end end rescue ArgumentError => e render_apontamento_erro(e.message) end # POST /consolidacoes/:consolidacao_id/consolidacao_entregas/apontar_termo # ENTREGA DE TERMO — lança um lote de N termos para o motorista. Não há NF nem # código de rastreio: só a quantidade, a um preço fixo configurável. def apontar_termo authorize @consolidacao, :update? motorista = @consolidacao.adicionar_termos!( motorista: params[:motorista].to_s.strip, quantidade: params[:quantidade], vehicle: params[:vehicle], # veículo escolhido no lançamento (Q3) user: current_user ) auditar!(:editar, @consolidacao, dados_novos: { entrega_termo: params[:quantidade].to_i, motorista: motorista }) respond_to do |format| format.json { render json: resumo_json(motorista).merge(motorista: motorista) } format.html do redirect_to validar_consolidacao_consolidacao_entregas_path(@consolidacao, motorista: motorista), notice: "#{params[:quantidade].to_i} entrega(s) de termo adicionada(s) para #{motorista}." end end rescue ArgumentError => e render_apontamento_erro(e.message) end # DELETE /consolidacoes/:consolidacao_id/consolidacao_entregas/remover_apontamento # Remove um apontamento manual — por ?id= (um pilar) ou por ?tracking_id=&motorista= # (todos os pilares daquela nota). Não passa pelo guard de elegibilidade, pois o # tracking está fora do período (por isso não reusa o toggle classificar). def remover_apontamento authorize @consolidacao, :update? scope = @consolidacao.consolidacao_entregas.apontamentos registros = if params[:id].present? Array(scope.find_by(id: params[:id])) else scope.where(tracking_id: params[:tracking_id], motorista_nome: params[:motorista]).to_a end return render_apontamento_erro('Apontamento não encontrado.') if registros.empty? motorista = registros.first.motorista_nome tracking = registros.first.tracking_id registros.each(&:destroy) @consolidacao.recalcular_motorista!(motorista) auditar!(:editar, @consolidacao, dados_novos: { remover_apontamento_manual: tracking, motorista: motorista }) respond_to do |format| format.json { render json: resumo_json(motorista).merge(motorista: motorista) } format.html { redirect_back fallback_location: consolidacao_path(@consolidacao), notice: 'Apontamento removido.' } end end # POST /consolidacoes/:consolidacao_id/consolidacao_entregas/fechar_veiculo # Consolida (fecha) UM veículo do motorista. Exige que o veículo esteja 100% # classificado. O pagamento continua sendo por motorista (não é pago aqui). def fechar_veiculo authorize @consolidacao, :update? motorista = params[:motorista] veiculo = params[:vehicle].to_s return render_veiculo_erro('Veículo/motorista inválido.') unless motorista_na_consolidacao?(motorista) && veiculo.present? status = veiculos_status(motorista).find { |v| v[:vehicle] == veiculo } return render_veiculo_erro('Classifique todas as entregas deste veículo antes de consolidar.') if status && !status[:completo] cv = @consolidacao.consolidacao_veiculos.find_or_create_by!(motorista_nome: motorista, vehicle: veiculo) cv.fechar!(current_user) auditar!(:editar, @consolidacao, dados_novos: { fechar_veiculo: veiculo, motorista: motorista }) responder_veiculo(motorista, "Veículo #{veiculo} consolidado.") end # POST .../reabrir_veiculo — reabre um veículo já consolidado (reversível). def reabrir_veiculo authorize @consolidacao, :update? motorista = params[:motorista] cv = @consolidacao.consolidacao_veiculos.find_by(motorista_nome: motorista, vehicle: params[:vehicle].to_s) return render_veiculo_erro('Veículo não encontrado.') unless cv cv.reabrir!(current_user) auditar!(:editar, @consolidacao, dados_novos: { reabrir_veiculo: cv.vehicle, motorista: motorista }) responder_veiculo(motorista, "Veículo #{cv.vehicle} reaberto.") end # POST .../arquivar_veiculo — tira um veículo do gate de finalização (reversível). # As entregas já classificadas dele continuam somando no pagamento do motorista. def arquivar_veiculo authorize @consolidacao, :update? motorista = params[:motorista] veiculo = params[:vehicle].to_s return render_veiculo_erro('Veículo/motorista inválido.') unless motorista_na_consolidacao?(motorista) && veiculo.present? cv = @consolidacao.consolidacao_veiculos.find_or_create_by!(motorista_nome: motorista, vehicle: veiculo) cv.arquivar!(current_user) auditar!(:editar, @consolidacao, dados_novos: { arquivar_veiculo: veiculo, motorista: motorista }) responder_veiculo(motorista, "Veículo #{veiculo} arquivado — fora da finalização.") end # POST .../desarquivar_veiculo — restaura um veículo arquivado. def desarquivar_veiculo authorize @consolidacao, :update? motorista = params[:motorista] cv = @consolidacao.consolidacao_veiculos.find_by(motorista_nome: motorista, vehicle: params[:vehicle].to_s) return render_veiculo_erro('Veículo não encontrado.') unless cv cv.desarquivar!(current_user) auditar!(:editar, @consolidacao, dados_novos: { desarquivar_veiculo: cv.vehicle, motorista: motorista }) responder_veiculo(motorista, "Veículo #{cv.vehicle} restaurado.") end private def set_consolidacao @consolidacao = Consolidacao.find(params[:consolidacao_id]) end # Resposta padrão das ações de veículo: JSON com o resumo atualizado (barra + # chips com o novo estado) ou redirect de volta pra tela de validação. def responder_veiculo(motorista, aviso) respond_to do |format| format.json { render json: resumo_json(motorista, vehicles: params[:vehicle_sel]) } format.html do redirect_to validar_consolidacao_consolidacao_entregas_path( @consolidacao, motorista: motorista, vehicle: veiculos_param(params[:vehicle_sel]).presence ), notice: aviso end end end def render_veiculo_erro(msg) respond_to do |format| format.json { render json: { ok: false, erro: msg }, status: :unprocessable_entity } format.html do redirect_to validar_consolidacao_consolidacao_entregas_path(@consolidacao, motorista: params[:motorista]), alert: msg end end end # Veículo (snapshot da rastreio) de UM tracking_id. nil para sintéticos. def veiculo_do_tracking(tracking_id) Entrega.da_conta_gade.find_by(tracking_id: tracking_id)&.vehicle end # Mapa tracking_id => vehicle para um lote (uma query só). def veiculos_dos_trackings(ids) return {} if ids.blank? Entrega.da_conta_gade.where(tracking_id: ids).pluck(:tracking_id, :vehicle).to_h end def bloquear_se_finalizada return unless @consolidacao.finalizada? && current_user.operador? redirect_to @consolidacao, alert: 'Consolidação finalizada — apenas admin/gerente podem editar.' end # Motorista realmente faz parte desta consolidação? (ignora arquivados) def motorista_na_consolidacao?(nome) nome.present? && @consolidacao.consolidacao_motoristas.ativos.exists?(motorista_nome: nome) end # tracking_ids atendidos/elegíveis para o motorista no período (respeita veículos). # Atendidas = sucesso + insucesso com checkout (o motorista foi ao local). # `vehicle` (opcional) recorta para UM carro — é o que faz "Marcar todos" fechar # só o veículo selecionado. def tracking_ids_elegiveis(motorista, vehicle = nil) return [] unless motorista.present? rel = Entrega.atendidas.da_conta_gade .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? rel = filtrar_por_veiculo(rel, vehicle) if vehicle.present? rel.pluck(:tracking_id) end # Normaliza o parâmetro de veículo (string única OU array vindo de vehicle[]) # numa lista limpa de selecionados. def veiculos_param(raw) Array(raw).reject(&:blank?) end # Recorte por um OU MAIS veículos (trata o grupo "sem veículo"). Lista vazia # = sem recorte (todos os veículos). def filtrar_por_veiculo(rel, veiculos) lista = veiculos_param(veiculos) return rel if lista.empty? valores = lista.reject { |v| v == SEM_VEICULO } valores += [nil, ''] if lista.include?(SEM_VEICULO) rel.where(vehicle: valores) end # Status "fechado" por veículo do motorista: lista de # { vehicle:, eligible:, classificadas:, fechado: }. Fonte única do filtro de # carros e do acompanhamento (chips). 2 queries: elegíveis (com a coluna do # veículo) e as já classificadas (não-manuais). # Status por veículo do motorista: { vehicle, eligible, classificadas, completo, # fechado, arquivado }. `completo` = 100% classificado (verde, pode consolidar); # `fechado` = veículo consolidado (cadeado, vem de consolidacao_veiculos); # `arquivado` = fora do gate. Une os veículos com entregas elegíveis (rastreio) # com os que só existem como linha de consolidacao_veiculos (arquivados/manuais). def veiculos_status(motorista) return [] unless motorista.present? rel = Entrega.atendidas.da_conta_gade .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? por_veiculo = rel.pluck(:tracking_id, :vehicle).group_by { |(_id, v)| v.presence || SEM_VEICULO } classificadas = @consolidacao.consolidacao_entregas .where(motorista_nome: motorista, manual: false) .distinct.pluck(:tracking_id).to_set estados = @consolidacao.consolidacao_veiculos.where(motorista_nome: motorista).index_by(&:vehicle) veiculos = (por_veiculo.keys + estados.keys).uniq veiculos.map do |veiculo| ids = (por_veiculo[veiculo] || []).map(&:first) feito = ids.count { |id| classificadas.include?(id) } cv = estados[veiculo] { vehicle: veiculo, eligible: ids.size, classificadas: feito, completo: ids.size.positive? && feito >= ids.size, fechado: cv&.fechado? || false, arquivado: cv&.arquivado? || false } end.sort_by { |v| v[:vehicle] } end # Valida que a entrega pertence ao motorista informado e ao período/veículo da # consolidação — impede classificar tracking_ids arbitrários via requisição. def entrega_elegivel?(motorista, tracking_id) motorista_na_consolidacao?(motorista) && tracking_id.present? && tracking_ids_elegiveis(motorista).include?(tracking_id) end # Pilares (tipos) selecionados no modal — aceita tipos[] (vários) ou tipo (um). def tipos_param tipos = Array(params[:tipos]).presence || Array(params[:tipo]) tipos.reject(&:blank?) end def render_apontamento_erro(msg) respond_to do |format| format.json { render json: { ok: false, erro: msg }, status: :unprocessable_entity } format.html { redirect_to wizard_consolidacao_path(@consolidacao), alert: msg } end end def render_nao_elegivel respond_to do |format| format.json { render json: { ok: false, erro: 'Entrega não elegível para este motorista.' }, status: :unprocessable_entity } format.html do redirect_to validar_consolidacao_consolidacao_entregas_path(@consolidacao, motorista: params[:motorista]), alert: 'Entrega não elegível para este motorista.' end end end def valor_para_tipo(tipo) ConsolidacaoEntrega.valor_para(tipo) end # Valor a aplicar na classificação. Para o pilar "extraordinária" o admin pode # informar um valor customizado (params[:valor]) por ser uma exceção; se vier # ausente/inválido, cai no preço padrão de Configurações. Demais pilares usam # sempre o preço configurado. def valor_classificacao(tipo) return valor_para_tipo(tipo) unless tipo.to_s == 'extraordinaria' valor = params[:valor].to_s.strip.tr(',', '.') valor = BigDecimal(valor) rescue nil valor.present? && valor >= 0 ? valor : Configuracao.preco_extraordinaria end # Valor customizado da Extraordinária vindo do formulário (params[:valor]), # aceitando vírgula decimal. Retorna nil se ausente/inválido — aí o model usa # o preço padrão de Configurações. def valor_extraordinaria_param bruto = params[:valor].to_s.strip.tr(',', '.') return nil if bruto.blank? valor = BigDecimal(bruto) rescue nil valor if valor && valor >= 0 end def recalcular_motorista(nome) @consolidacao.recalcular_motorista!(nome) end # Conta lançamentos manuais como ENTREGAS: cada termo vale `quantidade` (lote # numa só linha); os demais apontamentos manuais valem 1 por nota (tracking). def contar_manuais(rel_manual) rel_manual.termo.sum(:quantidade) + rel_manual.where.not(tipo: ConsolidacaoEntrega.tipos[:termo]).distinct.count(:tracking_id) end # `vehicles` (opcional, string ou array): quando presente, TODO o resumo # (barra de progresso, contagem por pilar e valor total) reflete apenas o(s) # veículo(s) selecionado(s). `veiculos_status` acompanha o payload para o front # atualizar os chips sem recarregar. def resumo_json(motorista, vehicles: nil) lista = veiculos_param(vehicles) classificacoes = @consolidacao.consolidacao_entregas.where(motorista_nome: motorista) status = veiculos_status(motorista) classificadas_motorista = classificacoes.distinct.count(:tracking_id) if lista.any? # Recorta as classificações às NFs dos veículos selecionados. ids = tracking_ids_elegiveis(motorista, lista) classificacoes = classificacoes.where(tracking_id: ids) escopo = status.select { |v| lista.include?(v[:vehicle]) }.sum { |v| v[:classificadas] } else escopo = classificadas_motorista end { ok: true, classificadas: classificadas_motorista, classificadas_escopo: escopo, manuais: contar_manuais(classificacoes.where(manual: true)), por_tipo: classificacoes.group(:tipo).sum(:quantidade), valor_total: classificacoes.sum { |e| e.tipo == 'desconto' ? -e.valor_aplicado : e.valor_aplicado }.to_f, veiculos_status: status } end end