Implantação da função filtro de Operções exemplo UBS***
This commit is contained in:
@@ -130,10 +130,16 @@ class Consolidacao < ApplicationRecord
|
||||
inicio: data_inicio,
|
||||
fim: data_fim,
|
||||
motorista: motorista,
|
||||
vehicle: vehicle_ids.presence
|
||||
vehicle: vehicle_ids.presence,
|
||||
operacoes: operacoes.presence
|
||||
)
|
||||
end
|
||||
|
||||
# Rótulos amigáveis das operações vinculadas (para exibição).
|
||||
def operacoes_label
|
||||
Array(operacoes).map { |t| Operacao.label(t) }
|
||||
end
|
||||
|
||||
# Entrega conta como classificada se tiver ≥1 pilar (distinct por tracking_id,
|
||||
# já que agora pode haver vários pilares por entrega).
|
||||
def classificadas_count(motorista)
|
||||
|
||||
@@ -35,6 +35,21 @@ class Entrega < ApplicationRecord
|
||||
where(vehicle: vehicle)
|
||||
}
|
||||
|
||||
# Restringe às entregas cujas NFs (reference_id) constam na(s) tabela(s) de
|
||||
# operação informada(s). O JOIN é o mesmo do script de gestão do cliente:
|
||||
# reference_id::text = <tabela_operacao>.nota_fiscal
|
||||
#
|
||||
# SEGURANÇA: o nome da tabela entra no SQL, então passa pela whitelist
|
||||
# (Operacao.sanitizar) e por quote_table_name. Nomes inválidos são descartados;
|
||||
# se nada sobrar, não filtra (all) — o filtro de operação é opcional.
|
||||
scope :da_operacoes, ->(tabelas) {
|
||||
validas = Operacao.sanitizar(tabelas)
|
||||
next all if validas.empty?
|
||||
|
||||
selects = validas.map { |t| "SELECT nota_fiscal FROM #{connection.quote_table_name(t)}" }
|
||||
where("reference_id::text IN (#{selects.join(' UNION ')})")
|
||||
}
|
||||
|
||||
scope :do_mes, ->(data = Date.current) {
|
||||
where(planned_date: data.beginning_of_month..data.end_of_month)
|
||||
}
|
||||
@@ -84,10 +99,11 @@ class Entrega < ApplicationRecord
|
||||
end
|
||||
|
||||
# Contagem de entregas pagas para cálculo estimado
|
||||
def self.contar_pagas(inicio:, fim:, motorista: nil, vehicle: nil)
|
||||
def self.contar_pagas(inicio:, fim:, motorista: nil, vehicle: nil, operacoes: nil)
|
||||
base = pagas.da_conta_gade.no_periodo(inicio, fim)
|
||||
base = base.do_motorista(motorista) if motorista.present?
|
||||
base = base.da_veiculo(vehicle) if vehicle.present?
|
||||
base = base.do_motorista(motorista) if motorista.present?
|
||||
base = base.da_veiculo(vehicle) if vehicle.present?
|
||||
base = base.da_operacoes(operacoes) if operacoes.present?
|
||||
base.count
|
||||
end
|
||||
|
||||
|
||||
70
app/models/operacao.rb
Normal file
70
app/models/operacao.rb
Normal file
@@ -0,0 +1,70 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user