, correção da pagina de gerar romaneio e outros fix
This commit is contained in:
121
app/services/romaneios/vinculador_de_operacao.rb
Normal file
121
app/services/romaneios/vinculador_de_operacao.rb
Normal file
@@ -0,0 +1,121 @@
|
||||
# app/services/romaneios/vinculador_de_operacao.rb
|
||||
#
|
||||
# Vincula (ou troca) a operação do mês de um romaneio JÁ IMPORTADO e preenche
|
||||
# APARELHO/TELEFONES/NOME a partir da gade_entregas_* — sem rebuscar o plano.
|
||||
#
|
||||
# ── POR QUE EXISTE ──────────────────────────────────────────────────────────
|
||||
# Quem diz que o paciente é NOVO é a operação do mês, e NOVO é exatamente quem
|
||||
# recebe aparelho. Até aqui só havia dois jeitos de vincular, os dois na hora da
|
||||
# importação: escolher a operação no formulário ou deixar o
|
||||
# Romaneios::CasadorDeOperacao deduzir pelo nome do plano. Quando o casamento não
|
||||
# acontece (nome fora do padrão, empate proposital, operação subida no banco
|
||||
# DEPOIS da importação), o romaneio ficava preso sem operação: a coluna APARELHO
|
||||
# saía vazia para todos e a única saída era reimportar o plano — que depende da
|
||||
# API do SimpliRoute responder ou de o operador ainda ter o .xlsx em mãos.
|
||||
#
|
||||
# Este serviço é o caminho manual: escolhida a operação, ele lê as NFs que já
|
||||
# estão gravadas e aplica o que a operação sabe, mantendo a MESMA precedência do
|
||||
# Romaneios::Importador (operação > plano) e o MESMO respeito a edição humana —
|
||||
# campo que alguém digitou na tela não é sobrescrito.
|
||||
#
|
||||
# A tabela continua SOMENTE LEITURA: quem fala com ela é o EnriquecimentoGade
|
||||
# (SELECT com Operacao.valida? + quote_table_name).
|
||||
module Romaneios
|
||||
class VinculadorDeOperacao
|
||||
# Já existe outro romaneio para a mesma data com a operação escolhida. Não é
|
||||
# erro de programação: é a chave única (planned_date, operacao_tabela)
|
||||
# avisando que o operador está prestes a criar um segundo documento do mesmo
|
||||
# dia — a tela precisa dizer isso com o link do outro, não estourar 500.
|
||||
class Conflito < StandardError
|
||||
attr_reader :existente
|
||||
|
||||
def initialize(existente)
|
||||
@existente = existente
|
||||
super('Já existe um romaneio desta data para essa operação.')
|
||||
end
|
||||
end
|
||||
|
||||
Resultado = Struct.new(:com_aparelho, :sem_correspondencia, :preservados,
|
||||
:total, :colunas_ausentes, keyword_init: true) do
|
||||
def resumo
|
||||
partes = ["#{com_aparelho} com aparelho (NOVOS) de #{total} parada(s)"]
|
||||
partes << "#{sem_correspondencia} sem correspondência na operação" if sem_correspondencia.to_i.positive?
|
||||
partes << "#{preservados} campo(s) mantido(s) por edição" if preservados.to_i.positive?
|
||||
partes.join(', ')
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(romaneio:, tabela:)
|
||||
@romaneio = romaneio
|
||||
@tabela = tabela.to_s
|
||||
end
|
||||
|
||||
# Devolve um Resultado. Levanta ArgumentError (operação inexistente) ou
|
||||
# Conflito (chave única) — os dois viram mensagem na tela.
|
||||
def aplicar!
|
||||
raise ArgumentError, "Operação inválida: #{@tabela}" unless Operacao.valida?(@tabela)
|
||||
|
||||
conflitante = Romaneio.where(planned_date: @romaneio.planned_date, operacao_tabela: @tabela)
|
||||
.where.not(id: @romaneio.id).first
|
||||
raise Conflito, conflitante if conflitante
|
||||
|
||||
gade = EnriquecimentoGade.new(@tabela)
|
||||
linhas = @romaneio.linhas_vigentes.to_a
|
||||
dados = gade.para_notas(linhas.map(&:nota_fiscal))
|
||||
|
||||
resultado = nil
|
||||
# Transação: vincular sem enriquecer deixaria a tela dizendo que a operação
|
||||
# está ligada com a coluna APARELHO ainda vazia — o pior dos dois mundos,
|
||||
# porque some o aviso que hoje denuncia o problema.
|
||||
Romaneio.transaction do
|
||||
@romaneio.update!(operacao_tabela: @tabela)
|
||||
resultado = enriquecer(linhas, dados)
|
||||
end
|
||||
|
||||
resultado.colunas_ausentes = gade.colunas_ausentes
|
||||
resultado
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def enriquecer(linhas, dados)
|
||||
com_aparelho = sem_correspondencia = preservados = 0
|
||||
|
||||
linhas.each do |linha|
|
||||
extra = dados[linha.nota_fiscal.to_s]
|
||||
sem_correspondencia += 1 if extra.nil?
|
||||
extra ||= {}
|
||||
|
||||
# `origem` é o que a linha vale SEM edição humana — é para onde o campo
|
||||
# volta quando o operador limpa a edição na tela. Ele passa a refletir a
|
||||
# operação recém-vinculada; sem isso, "restaurar" devolveria o valor de
|
||||
# antes do vínculo e o aparelho sumiria de novo.
|
||||
origem = linha.origem.to_h
|
||||
novos = {
|
||||
'aparelho' => extra[:aparelho].to_s,
|
||||
# Mesma precedência do Importador: a coluna `telefones` da operação é a
|
||||
# fonte completa; o "TEL:" das anotações do plano só entra quando ela
|
||||
# não tem número. Idem para o nome.
|
||||
'telefones' => extra[:telefones].to_s.presence || origem['telefones'].to_s,
|
||||
'nome' => extra[:nome].to_s.presence || origem['nome'].to_s
|
||||
}
|
||||
|
||||
novos.each do |campo, valor|
|
||||
if linha.editado?(campo)
|
||||
preservados += 1
|
||||
next
|
||||
end
|
||||
linha[campo] = valor
|
||||
end
|
||||
|
||||
linha.origem = origem.merge(novos)
|
||||
linha.save!
|
||||
|
||||
com_aparelho += 1 if linha.aparelho.to_s.strip.present?
|
||||
end
|
||||
|
||||
Resultado.new(com_aparelho: com_aparelho, sem_correspondencia: sem_correspondencia,
|
||||
preservados: preservados, total: linhas.size)
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user