# app/jobs/detectar_mudancas_operacao_job.rb # # Compara os números de cada operação com o retrato anterior (OperacaoSnapshot) # e dispara `operacao_alterada` no que mudou. # # POR QUE VARREDURA e não gatilho no código: a maior parte das mudanças da # operação NÃO passa por aqui — ela acontece no SimpliRoute e chega pelo sync do # espelho, que é read-only do nosso lado. Sem comparar retratos, "o que mudou" # seria só o que o próprio sistema editou, que é a minoria dos casos. # # A PRIMEIRA execução de cada operação só grava o retrato e não avisa nada: sem # retrato anterior, toda operação existente pareceria "nova" e o primeiro deploy # dispararia uma mensagem por operação cadastrada. class DetectarMudancasOperacaoJob < ApplicationJob queue_as :default def perform return unless EventoNotificacao.ativos.exists?(gatilho: 'operacao_alterada') Operacao.nomes_validos.each { |tabela| verificar(tabela) } rescue StandardError => e Rails.logger.error("[MudancasOperacao] #{e.class}: #{e.message}") end private def verificar(tabela) atuais = contar(tabela) snapshot = OperacaoSnapshot.para(tabela) if snapshot.new_record? snapshot.registrar!(atuais) return end diferenca = snapshot.diferenca(atuais) return if diferenca.empty? Notificacao::Gatilhos.operacao_alterada( operacao: Operacao.label(tabela), o_que_mudou: OperacaoSnapshot.descrever(diferenca) ) snapshot.registrar!(atuais) rescue StandardError => e # Uma operação problemática não pode impedir a varredura das demais. Rails.logger.error("[MudancasOperacao] #{tabela}: #{e.class}: #{e.message}") end # Sem filtro de data: a comparação é do estado ATUAL da operação inteira, que # é o que muda quando uma NF entra, sai ou troca de status. # # ⚠️ CUSTO: OperacaoMetricas carrega as visitas da operação em memória para # deduplicar por NF. Uma operação mensal tem alguns milhares de linhas, e o # job varre TODAS as tabelas a cada execução (8×/dia pelo schedule). É o # preço de reusar a mesma contagem do dashboard em vez de escrever um SQL # paralelo que divergiria dele. Se passar a incomodar, o caminho é restringir # a varredura às operações do mês corrente e do anterior — as antigas não # mudam mais. def contar(tabela) m = Analytics::OperacaoMetricas.new(tabelas: [tabela]) { total_notas: m.total, entregues: m.sucesso, nao_entregues: m.recusas, pendentes: m.pendentes } end end