# lib/tasks/entregas_motorista.rake # # Lista as entregas de um motorista num período lendo o ESPELHO LOCAL # (model Entrega / tabela db_reem_simplerout_2026) — sem tocar na API do # SimpliRoute, logo sem precisar de SIMPLIROUTE_TOKEN. # # Por que aqui e não na API: a API não tem filtro de intervalo nem de motorista # (ver bin/entregas_do_motorista), então um período vira N requisições de vários # MB. No espelho é uma query só. O preço é a defasagem da sync — se precisar do # estado AO VIVO, use bin/entregas_do_motorista. # # SÓ LEITURA: Entrega#readonly? é true; nada aqui escreve. # # rake "entregas:motorista[JOSE DA SILVA,2026-08-01,2026-08-14]" # rake "entregas:motorista[JOSE DA SILVA,2026-08-01,2026-08-14,/tmp/saida.csv]" # rake "entregas:motoristas[2026-08-01,2026-08-14]" # quem operou no período # # ⚠️ O nome tem que ser a grafia EXATA da coluna `driver` (do_motorista usa # igualdade). Rode `entregas:motoristas` primeiro para ver a lista; se o nome # passado não bater exatamente, a task sugere os parecidos em vez de devolver # vazio silenciosamente. # # ⚠️ Período pelo `planned_date` (dia planejado da visita), que é o mesmo eixo # do dashboard. As análises FINANCEIRAS usam `checkout` (data real da entrega) — # são recortes diferentes; passe FONTE=checkout para usar esse outro eixo. namespace :entregas do desc 'Entregas de um motorista no período, lidas do espelho local (sem API)' task :motorista, %i[nome inicio fim csv] => :environment do |_t, args| nome = args[:nome].to_s.strip inicio = args[:inicio].to_s.strip fim = args[:fim].to_s.strip if nome.empty? || inicio.empty? || fim.empty? abort 'Uso: rake "entregas:motorista[NOME,YYYY-MM-DD,YYYY-MM-DD,/caminho/opcional.csv]"' end d1 = Date.parse(inicio) d2 = Date.parse(fim) abort 'Início é depois do fim.' if d1 > d2 # planned_date = dia planejado; checkout = dia em que a entrega ocorreu. por_checkout = ENV['FONTE'].to_s.casecmp('checkout').zero? base = Entrega.da_conta_gade base = por_checkout ? base.no_periodo_checkout(d1, d2) : base.no_periodo(d1, d2) entregas = base.do_motorista(nome).order(:planned_date, :checkout).to_a if entregas.empty? puts "Nenhuma entrega de #{nome.inspect} entre #{d1.strftime('%d/%m/%Y')} e #{d2.strftime('%d/%m/%Y')}." # Nome quase certo é o erro mais comum aqui — mostrar o vizinho evita # concluir "o motorista não rodou" quando na verdade a grafia divergiu. alvo = nome.upcase parecidos = Entrega.motoristas_ativos(inicio: d1, fim: d2) .select { |n| n.upcase.include?(alvo) || alvo.include?(n.upcase) } puts "Nomes parecidos no período: #{parecidos.inspect}" if parecidos.any? puts 'Rode: rake "entregas:motoristas[' + "#{d1},#{d2}" + ']" para ver a grafia exata.' next end eixo = por_checkout ? 'checkout (data real)' : 'planned_date (dia planejado)' puts "#{'─' * 104}" puts "#{entregas.size} entrega(s) de #{nome} · #{d1.strftime('%d/%m/%Y')} a #{d2.strftime('%d/%m/%Y')} · eixo: #{eixo}" puts '─' * 104 printf("%-11s %-10s %-10s %-10s %-17s %-38s\n", 'DATA', 'NF', 'VEÍCULO', 'STATUS', 'CHECKOUT', 'DESTINO') entregas.each do |e| printf("%-11s %-10s %-10s %-10s %-17s %-38s\n", e.planned_date&.strftime('%d/%m/%Y').to_s, e.reference_id.to_s[0, 10], e.vehicle.to_s[0, 10], e.status.to_s[0, 10], e.checkout&.strftime('%d/%m %H:%M').to_s, e.local.to_s[0, 38]) end puts '─' * 104 por_status = entregas.group_by { |e| e.status.to_s }.transform_values(&:size) puts "Total: #{entregas.size} " + por_status.sort_by { |_, n| -n }.map { |s, n| "#{s}=#{n}" }.join(' ') # "Atendidas" = foi ao local (sucesso OU insucesso) com checkout — é o # conjunto elegível da consolidação, e não o mesmo que "entregues". puts "Entregues (completed + checkout) ..... #{entregas.count { |e| e.concluida? && e.checkout_registrado? }}" puts "Insucessos (failed) .................. #{entregas.count(&:falhada?)}" puts "Atendidas (foi ao local c/ checkout) . #{entregas.count { |e| Entrega::STATUS_ATENDIDO.include?(e.status) && e.checkout_registrado? }}" puts "Dias com entrega ..................... #{entregas.map { |e| e.planned_date&.to_date }.compact.uniq.size}" puts "Veículos ............................. #{entregas.map(&:vehicle).compact.reject(&:empty?).uniq.join(', ')}" if args[:csv].present? require 'csv' CSV.open(args[:csv], 'w') do |csv| csv << ['Data planejada', 'NF', 'Tracking', 'Motorista', 'Veículo', 'Status', 'Checkout', 'Local', 'Endereço', 'Motivo'] entregas.each do |e| csv << [e.planned_date, e.reference_id, e.tracking_id, e.driver, e.vehicle, e.status, e.checkout, e.local, e.try(:address), e.try(:observation)] end end puts "\nCSV gravado em #{args[:csv]}" end end desc 'Motoristas que aparecem no espelho dentro do período (grafia exata + volume)' task :motoristas, %i[inicio fim] => :environment do |_t, args| abort 'Uso: rake "entregas:motoristas[YYYY-MM-DD,YYYY-MM-DD]"' if args[:inicio].blank? || args[:fim].blank? d1 = Date.parse(args[:inicio]) d2 = Date.parse(args[:fim]) contagem = Entrega.da_conta_gade.no_periodo(d1, d2) .where.not(driver: [nil, '']).group(:driver).count if contagem.empty? puts "Nenhuma entrega no espelho entre #{d1.strftime('%d/%m/%Y')} e #{d2.strftime('%d/%m/%Y')}." next end puts "Motoristas · #{d1.strftime('%d/%m/%Y')} a #{d2.strftime('%d/%m/%Y')} (#{contagem.values.sum} entregas)" contagem.sort_by { |_, n| -n }.each { |nome, n| puts " #{n.to_s.rjust(5)} #{nome}" } end end