# app/models/romaneio.rb # # Um plano do SimpliRoute importado para virar o "CONTROLE DE ENTREGA" — a folha # que o motorista assina ao retirar a carga, uma página A4 paisagem por veículo. # # O romaneio é NOSSO (tabela gravável). As fontes continuam somente leitura: o # espelho de rastreio (Entrega) e as tabelas de operação (gade_entregas_*). class Romaneio < ApplicationRecord ORIGENS = %w[api espelho planilha].freeze # Máximo de paradas que cabem numa folha. O número é da GEOMETRIA do PDF, então a # fonte é Pdf::RomaneioPdf — duplicar aqui criaria duas verdades, e o dia em que # alguém mexesse na margem a tela passaria a avisar "sai em 1 folha" para um # romaneio que sai em 2. Lido em tempo de chamada (e não numa constante) para não # amarrar a ordem de carga dos autoloads. def self.linhas_por_folha Pdf::RomaneioPdf::MAX_LINHAS end belongs_to :importado_por, class_name: 'User', optional: true has_many :romaneio_veiculos, -> { order(:posicao, :veiculo) }, dependent: :destroy, inverse_of: :romaneio has_many :romaneio_linhas, dependent: :destroy, inverse_of: :romaneio validates :planned_date, presence: true validates :origem, inclusion: { in: ORIGENS } validates :planned_date, uniqueness: { scope: :operacao_tabela } scope :recentes, -> { order(planned_date: :desc, id: :desc) } # Linhas que entram no PDF: as que continuam no plano, na ordem do roteiro. # `removido_em` marca a parada que saiu do plano mas tinha edição humana — ela # fica visível na tela para o operador decidir, e NUNCA sai impressa. def linhas_vigentes romaneio_linhas.where(removido_em: nil).order(:veiculo, :ordem, :id) end def linhas_removidas romaneio_linhas.where.not(removido_em: nil).order(:veiculo, :ordem, :id) end # { 'GADE_001' => 29, ... } — alimenta o seletor de veículo e o aviso de folhas. def contagem_por_veiculo @contagem_por_veiculo ||= linhas_vigentes.reorder(nil).group(:veiculo).count end # { novos: 184, recorrentes: 1863 } — quantos pacientes recebem APARELHO. # # POR QUE APARECE NA TELA: é a conferência que o programa antigo fazia com uma # caixa de diálogo ("184 entrega(s) marcada(s) como NOVO") logo depois de # carregar o status. Sem esse número, um romaneio importado SEM operação # vinculada — em que ninguém é marcado como novo — parece idêntico a um # importado certo, e o erro só aparece no papel, com aparelho não entregue. # "0 novos" na tela é a pergunta que o operador precisa se fazer. def contagem_aparelho @contagem_aparelho ||= begin grupos = linhas_vigentes.reorder(nil).group(:aparelho).count total = grupos.values.sum novos = grupos.sum { |valor, qtd| valor.to_s.strip.casecmp('SIM').zero? ? qtd : 0 } { novos: novos, recorrentes: total - novos } end end def folhas_de(veiculo) total = contagem_por_veiculo[veiculo].to_i return 1 if total.zero? (total.to_f / self.class.linhas_por_folha).ceil end # Veículos que estouram uma folha — a tela avisa antes de imprimir, porque # descobrir isso só no papel é descobrir tarde. def veiculos_multifolha contagem_por_veiculo.select { |_v, n| n > self.class.linhas_por_folha } end def operacao_label operacao_tabela.present? ? Operacao.label(operacao_tabela) : nil end # Rótulo do canto direito do PDF ("EMAD 09.2026"). Só uma SUGESTÃO: não existe # fonte canônica para esse texto, então ele é editável na tela. def self.rotulo_sugerido(tabela, data) base = tabela.present? ? Operacao.label(tabela).split.first : nil [base, data.to_date.strftime('%m.%Y')].compact.join(' ') end end