# app/models/romaneio_linha.rb # # Uma PARADA do romaneio — vira uma linha da tabela do PDF. # # ⚠️ Nome de tabela fixado: "RomaneioLinha".tableize já dá `romaneio_linhas`, mas a # declaração explícita é a trava contra alguém acrescentar um irregular ao # config/initializers/inflections.rb e mudar isso sem perceber (ConsolidacaoMotorista # já quebrou /motorista em produção exatamente assim). Coberto por # spec/models/table_names_spec.rb. class RomaneioLinha < ApplicationRecord self.table_name = 'romaneio_linhas' # Os únicos campos que o operador edita — e a whitelist que o controller usa # para não deixar um PATCH escrever em `veiculo`, `ordem` ou `chave_origem`. # # `veiculo` e `ordem` NÃO são editáveis de propósito: mover uma parada de veículo # aqui produziria um romaneio que contradiz a rota que o motorista recebe no # celular — dois papéis, mesma entrega, respostas diferentes. Veículo errado se # corrige no SimpliRoute e se reimporta. CAMPOS_EDITAVEIS = %w[nota_fiscal nome endereco aparelho telefones].freeze belongs_to :romaneio, inverse_of: :romaneio_linhas validates :veiculo, :chave_origem, presence: true scope :vigentes, -> { where(removido_em: nil) } scope :removidas, -> { where.not(removido_em: nil) } scope :do_veiculo, ->(veiculo) { where(veiculo: veiculo) } # Identidade ESTÁVEL entre reimportações. A NF quando existe; senão um digest do # título + endereço, porque nem toda linha do plano casa "NF 90507 - FULANO" e # duas linhas sem NF colidiriam no índice único, derrubando a importação inteira. # # Veículo e ordem ficam FORA da chave: mudam quando o plano é reotimizado e a # parada continua sendo a mesma entrega. def self.chave_para(nota_fiscal:, titulo: nil, endereco: nil) nf = nota_fiscal.to_s.strip return nf if nf.present? "T:#{Digest::MD5.hexdigest("#{titulo}|#{endereco}")}" end def editado?(campo) campos_editados.include?(campo.to_s) end def editada? campos_editados.present? end # Valor que o PLANO trouxe na última importação — é o que o botão "restaurar" # devolve, e o que a reconciliação compara. def valor_do_plano(campo) origem[campo.to_s].to_s end # Marca o campo como tocado por humano. É ISTO que sobrevive à reimportação: # o dirty tracking é POR CAMPO, não por linha — "corrigi o telefone" não pode # significar "também respondo pelo endereço" e congelar um endereço velho quando # o SimpliRoute regeocodificar. def marcar_editado!(campo) self.campos_editados = (campos_editados + [campo.to_s]).uniq end def desmarcar_editado!(campo) self.campos_editados = campos_editados - [campo.to_s] end end