# app/models/operacao.rb # # Operações são as tabelas EXTERNAS que o cliente carrega no banco, uma por # operação/mês (ex.: "gade_entregas_ubs_sul_leste_jun_2026"). Cada tabela tem a # coluna `nota_fiscal`, que casa com db_reem_simplerout_2026.reference_id::text. # # Estas tabelas são SOMENTE LEITURA — apenas SELECT, nunca migration/INSERT/etc. # # Como o nome da tabela entra em SQL (FROM dinâmico), TODA entrada externa passa # por uma whitelist (#nomes_validos) antes de ser usada — proteção anti-injection. class Operacao PREFIXO = ENV.fetch('OPERACOES_TABLE_PREFIX', 'gade_entregas_').freeze class << self # [{ tabela: 'gade_entregas_...', label: 'Ubs Sul Leste Jun 2026' }, ...] def todas nomes_validos.map { |t| { tabela: t, label: label(t) } } end # Nomes reais de tabelas no schema public que batem com o prefixo. # Sem cache de processo: o cliente sobe novas tabelas de operação a qualquer # momento e elas precisam aparecer sem reiniciar o servidor (a consulta ao # catálogo é barata). def nomes_validos conn.select_values( "SELECT table_name FROM information_schema.tables " \ "WHERE table_schema = 'public' AND table_name LIKE #{conn.quote("#{PREFIXO}%")} ORDER BY table_name" ) end # Interseção da lista recebida com as tabelas que existem de fato. def sanitizar(lista) Array(lista).map(&:to_s).uniq & nomes_validos end def valida?(nome) nomes_validos.include?(nome.to_s) end # Rótulo amigável: tira o prefixo e formata. def label(tabela) tabela.to_s.delete_prefix(PREFIXO).tr('_', ' ').strip.titleize end # Dados agregados das entregas das operações informadas, para pré-preencher o # formulário de nova consolidação. Une as NFs de todas as tabelas válidas. def dados(tabelas) validas = sanitizar(tabelas) return vazio if validas.empty? entregas = Entrega.da_conta_gade.da_operacoes(validas) { 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 } end private def vazio { motoristas: [], veiculos: [], inicio: nil, fim: nil } end def conn ActiveRecord::Base.connection end end end