, correção da pagina de gerar romaneio e outros fix
This commit is contained in:
@@ -29,6 +29,9 @@ class Admin::RomaneiosController < ApplicationController
|
|||||||
@linhas = @romaneio.linhas_vigentes.do_veiculo(@veiculo).to_a if @veiculo
|
@linhas = @romaneio.linhas_vigentes.do_veiculo(@veiculo).to_a if @veiculo
|
||||||
@removidas = @romaneio.linhas_removidas.to_a
|
@removidas = @romaneio.linhas_removidas.to_a
|
||||||
@colunas_ausentes = colunas_ausentes_da_operacao
|
@colunas_ausentes = colunas_ausentes_da_operacao
|
||||||
|
# Mesma lista da tela de criação: o vínculo manual da operação acontece aqui
|
||||||
|
# também, porque é aqui que se descobre que APARELHO saiu vazio.
|
||||||
|
@operacoes_agrupadas = Operacao.agrupadas_por_mes
|
||||||
end
|
end
|
||||||
|
|
||||||
# Caminho A: busca o plano no SimpliRoute (API, com queda para o espelho).
|
# Caminho A: busca o plano no SimpliRoute (API, com queda para o espelho).
|
||||||
@@ -131,6 +134,39 @@ class Admin::RomaneiosController < ApplicationController
|
|||||||
redirect_to admin_romaneio_path(@romaneio, veiculo: params[:veiculo]), alert: e.message
|
redirect_to admin_romaneio_path(@romaneio, veiculo: params[:veiculo]), alert: e.message
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Vincula a operação do mês à mão e preenche APARELHO/TELEFONES/NOME a partir
|
||||||
|
# dela. É a saída para quando o casamento pelo nome do plano não acontece — sem
|
||||||
|
# isso o único caminho era reimportar, que depende da API responder ou de o
|
||||||
|
# operador ainda ter o .xlsx do plano.
|
||||||
|
def vincular_operacao
|
||||||
|
authorize @romaneio
|
||||||
|
|
||||||
|
resultado = Romaneios::VinculadorDeOperacao.new(romaneio: @romaneio,
|
||||||
|
tabela: params[:operacao_tabela]).aplicar!
|
||||||
|
auditar!(:vincular_operacao, @romaneio,
|
||||||
|
dados_novos: resultado.to_h.merge(operacao_tabela: @romaneio.operacao_tabela))
|
||||||
|
|
||||||
|
# Sem a coluna STATUS a operação não sabe quem é NOVO: o vínculo deu certo e
|
||||||
|
# mesmo assim ninguém recebe aparelho. Dizer isso aqui evita a leitura de que
|
||||||
|
# "vinculei e continua vazio, logo o sistema está errado".
|
||||||
|
aviso = if resultado.colunas_ausentes.include?('status')
|
||||||
|
" Atenção: esta operação não tem a coluna STATUS, então APARELHO continua vazio e precisa ser preenchido à mão."
|
||||||
|
else
|
||||||
|
''
|
||||||
|
end
|
||||||
|
|
||||||
|
redirect_to admin_romaneio_path(@romaneio, veiculo: params[:veiculo]),
|
||||||
|
notice: "Operação #{@romaneio.operacao_label} vinculada — #{resultado.resumo}.#{aviso}"
|
||||||
|
rescue ArgumentError
|
||||||
|
redirect_to admin_romaneio_path(@romaneio, veiculo: params[:veiculo]),
|
||||||
|
alert: 'Selecione uma operação válida.'
|
||||||
|
rescue Romaneios::VinculadorDeOperacao::Conflito => e
|
||||||
|
redirect_to admin_romaneio_path(@romaneio, veiculo: params[:veiculo]),
|
||||||
|
alert: "#{e.message} Abra o romaneio de " \
|
||||||
|
"#{l e.existente.planned_date, format: '%d/%m/%Y'} (#{e.existente.rotulo_plano.presence || 'sem rótulo'}) " \
|
||||||
|
'em vez de duplicar o documento do dia.'
|
||||||
|
end
|
||||||
|
|
||||||
# Com ?veiculo= sai INLINE (é a prévia do editor, uma folha só, sub-100ms);
|
# Com ?veiculo= sai INLINE (é a prévia do editor, uma folha só, sub-100ms);
|
||||||
# sem veiculo sai o romaneio inteiro como download.
|
# sem veiculo sai o romaneio inteiro como download.
|
||||||
def pdf
|
def pdf
|
||||||
|
|||||||
@@ -10,8 +10,9 @@ class RomaneioPolicy < ApplicationPolicy
|
|||||||
def create? = index?
|
def create? = index?
|
||||||
def destroy? = index?
|
def destroy? = index?
|
||||||
|
|
||||||
def importar_planilha? = index?
|
def importar_planilha? = index?
|
||||||
def reimportar? = index?
|
def reimportar? = index?
|
||||||
|
def vincular_operacao? = index?
|
||||||
def pdf? = index?
|
def pdf? = index?
|
||||||
def atualizar_linha? = index?
|
def atualizar_linha? = index?
|
||||||
def atualizar_veiculo? = index?
|
def atualizar_veiculo? = index?
|
||||||
|
|||||||
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
|
||||||
@@ -30,6 +30,8 @@
|
|||||||
<p>
|
<p>
|
||||||
Este romaneio foi importado <strong>sem operação vinculada</strong>: a coluna APARELHO
|
Este romaneio foi importado <strong>sem operação vinculada</strong>: a coluna APARELHO
|
||||||
não é preenchida sozinha e o telefone vem só das anotações do plano.
|
não é preenchida sozinha e o telefone vem só das anotações do plano.
|
||||||
|
Escolha a operação do mês em <strong>“Vincular operação”</strong>, logo acima — não
|
||||||
|
é preciso reimportar o plano.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
76
app/views/admin/romaneios/_operacao.html.erb
Normal file
76
app/views/admin/romaneios/_operacao.html.erb
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
<%# app/views/admin/romaneios/_operacao.html.erb
|
||||||
|
Vínculo MANUAL da operação do mês, feito na própria tela do romaneio.
|
||||||
|
|
||||||
|
Por que aqui e não só na criação: é aqui que o operador descobre que a coluna
|
||||||
|
APARELHO saiu vazia. Mandar ele reimportar o plano (única saída até então)
|
||||||
|
depende da API do SimpliRoute responder ou de o .xlsx ainda estar em mãos.
|
||||||
|
|
||||||
|
Sem JavaScript de propósito: <details> nativo + form comum. Este controle é o
|
||||||
|
conserto de um romaneio que já está errado — precisa funcionar mesmo quando o
|
||||||
|
Stimulus não carrega (foi exatamente esse o caso em 28/08/2026, com os assets
|
||||||
|
dando 404 no servidor e a tela inteira sem JS). %>
|
||||||
|
<% vinculada = @romaneio.operacao_tabela.present? %>
|
||||||
|
|
||||||
|
<%# O cabeçalho e o formulário ficam EMPILHADOS, não lado a lado: aberto, o
|
||||||
|
seletor + botão precisam da largura toda da caixa; espremidos numa coluna
|
||||||
|
lateral o rótulo do estado saía cortado justamente quando ele é a informação
|
||||||
|
que importa ("Nenhuma vinculada"). %>
|
||||||
|
<div class="bg-[#111] border border-white/5 rounded-2xl p-4 space-y-3">
|
||||||
|
<div>
|
||||||
|
<p class="text-xs uppercase tracking-wide text-gray-500">Operação do mês</p>
|
||||||
|
<p class="<%= vinculada ? 'text-white' : 'text-amber-300' %> font-semibold">
|
||||||
|
<%= vinculada ? @romaneio.operacao_label : 'Nenhuma vinculada — APARELHO não é preenchido sozinho' %>
|
||||||
|
</p>
|
||||||
|
<p class="text-xs text-gray-500 mt-0.5 max-w-prose">
|
||||||
|
É ela que diz quem é paciente <strong>NOVO</strong>, e quem é novo sempre leva aparelho.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<%# Já vinculada: fica fechado, para não convidar a trocar sem motivo. Sem
|
||||||
|
operação: abre sozinho, porque é a pendência da tela. %>
|
||||||
|
<%# group-open: aberto, o gatilho fica discreto. Aberto ele vira só o título do
|
||||||
|
painel — deixá-lo laranja poria DOIS botões laranja um sobre o outro e o
|
||||||
|
operador não saberia qual confirma. %>
|
||||||
|
<details class="group" <%= 'open' unless vinculada %>>
|
||||||
|
<summary class="cursor-pointer list-none px-4 py-2.5 rounded-xl min-h-[48px] inline-flex items-center gap-2
|
||||||
|
border border-white/10 group-open:bg-[#1a1a1a] group-open:text-gray-300
|
||||||
|
group-open:font-normal
|
||||||
|
<%= vinculada ? 'bg-[#1a1a1a] hover:bg-[#242424] text-white'
|
||||||
|
: 'bg-[#f97316] hover:bg-orange-500 text-white font-semibold border-transparent' %>">
|
||||||
|
<%# cor: nil e espaco: false — o fundo laranja apagaria o laranja da marca
|
||||||
|
e o container já tem gap-2. %>
|
||||||
|
<%= icone :operacao, cor: nil, espaco: false %>
|
||||||
|
<%= vinculada ? 'Trocar operação' : 'Vincular operação' %>
|
||||||
|
</summary>
|
||||||
|
|
||||||
|
<%= form_with url: vincular_operacao_admin_romaneio_path(@romaneio), method: :patch,
|
||||||
|
class: 'mt-3 flex flex-col sm:flex-row gap-2 sm:items-center' do %>
|
||||||
|
<input type="hidden" name="veiculo" value="<%= @veiculo %>">
|
||||||
|
<select name="operacao_tabela" required
|
||||||
|
class="bg-[#1a1a1a] border border-white/10 rounded-xl px-3 py-3 text-white
|
||||||
|
focus:outline-none focus:border-orange-500 min-h-[48px] sm:min-w-[280px]">
|
||||||
|
<option value="">Escolha a operação…</option>
|
||||||
|
<% @operacoes_agrupadas.each do |titulo, ops| %>
|
||||||
|
<optgroup label="<%= titulo %>">
|
||||||
|
<% ops.each do |op| %>
|
||||||
|
<option value="<%= op[:tabela] %>" <%= 'selected' if op[:tabela] == @romaneio.operacao_tabela %>>
|
||||||
|
<%= op[:label] %>
|
||||||
|
</option>
|
||||||
|
<% end %>
|
||||||
|
</optgroup>
|
||||||
|
<% end %>
|
||||||
|
</select>
|
||||||
|
<button type="submit"
|
||||||
|
class="px-4 py-2.5 bg-[#f97316] hover:bg-orange-500 text-white font-semibold
|
||||||
|
rounded-xl min-h-[48px] whitespace-nowrap">
|
||||||
|
Vincular e preencher APARELHO
|
||||||
|
</button>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<p class="text-xs text-gray-500 mt-2 max-w-prose">
|
||||||
|
Preenche APARELHO, TELEFONES e NOME pelas notas fiscais já importadas.
|
||||||
|
O que você digitou na tabela é mantido — só os campos sem edição são
|
||||||
|
atualizados.
|
||||||
|
</p>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
@@ -66,6 +66,7 @@
|
|||||||
<p class="text-gray-500 text-sm mt-1">Clique em “Reimportar plano” ou envie a planilha do plano na tela anterior.</p>
|
<p class="text-gray-500 text-sm mt-1">Clique em “Reimportar plano” ou envie a planilha do plano na tela anterior.</p>
|
||||||
</div>
|
</div>
|
||||||
<% else %>
|
<% else %>
|
||||||
|
<%= render 'operacao' %>
|
||||||
<%= render 'avisos' %>
|
<%= render 'avisos' %>
|
||||||
|
|
||||||
<%# Duas zonas na tela: VEÍCULOS e a edição. A prévia saiu do fluxo e virou
|
<%# Duas zonas na tela: VEÍCULOS e a edição. A prévia saiu do fluxo e virou
|
||||||
|
|||||||
@@ -436,9 +436,11 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div id="pagamentos-feitos-paginacao" class="flex items-center justify-between mt-3 hidden">
|
<div id="pagamentos-feitos-paginacao" class="flex flex-wrap items-center justify-between gap-2 mt-3 hidden">
|
||||||
<p class="text-gray-400 text-xs" id="pagamentos-feitos-info"></p>
|
<p class="text-gray-400 text-xs" id="pagamentos-feitos-info"></p>
|
||||||
<div class="flex gap-1" id="pagamentos-feitos-abas"></div>
|
<%# flex-wrap: com muitas páginas a fila de abas passava da largura da tela
|
||||||
|
e o dashboard inteiro ganhava scroll horizontal (1338px em 1280px). %>
|
||||||
|
<div class="flex flex-wrap justify-end gap-1" id="pagamentos-feitos-abas"></div>
|
||||||
</div>
|
</div>
|
||||||
<% else %>
|
<% else %>
|
||||||
<p class="text-gray-400 text-sm py-3">Nenhum pagamento realizado neste período.</p>
|
<p class="text-gray-400 text-sm py-3">Nenhum pagamento realizado neste período.</p>
|
||||||
@@ -472,9 +474,9 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div id="pagamentos-pendentes-paginacao" class="flex items-center justify-between mt-3 hidden">
|
<div id="pagamentos-pendentes-paginacao" class="flex flex-wrap items-center justify-between gap-2 mt-3 hidden">
|
||||||
<p class="text-gray-400 text-xs" id="pagamentos-pendentes-info"></p>
|
<p class="text-gray-400 text-xs" id="pagamentos-pendentes-info"></p>
|
||||||
<div class="flex gap-1" id="pagamentos-pendentes-abas"></div>
|
<div class="flex flex-wrap justify-end gap-1" id="pagamentos-pendentes-abas"></div>
|
||||||
</div>
|
</div>
|
||||||
<% else %>
|
<% else %>
|
||||||
<p class="text-gray-400 text-sm py-3">Nenhuma pendência no período. <%= icone :sucesso, cor: 'text-green-500', espaco: false %></p>
|
<p class="text-gray-400 text-sm py-3">Nenhuma pendência no período. <%= icone :sucesso, cor: 'text-green-500', espaco: false %></p>
|
||||||
|
|||||||
@@ -115,7 +115,24 @@ if [ "${RAILS_ENV:-development}" = "production" ]; then
|
|||||||
log "assets: limpando restos e pré-compilando (public/assets)"
|
log "assets: limpando restos e pré-compilando (public/assets)"
|
||||||
rm -rf public/assets tmp/cache/assets
|
rm -rf public/assets tmp/cache/assets
|
||||||
if bundle exec rails assets:precompile; then
|
if bundle exec rails assets:precompile; then
|
||||||
log "assets prontos"
|
# CONFERÊNCIA À PARTE, e não confiança no código de saída: sem o manifesto o
|
||||||
|
# Propshaft NÃO quebra a página — ele cai no resolver dinâmico e continua
|
||||||
|
# montando URLs com o digest certo do arquivo atual. O resultado é a pior
|
||||||
|
# combinação possível: a tela abre com aparência normal e CADA /assets/*
|
||||||
|
# devolve 404, então some o JS inteiro (prévia do PDF, carrossel, edição
|
||||||
|
# inline) sem nenhum erro no log. Foi assim em 28/08/2026: o HTML pedia
|
||||||
|
# romaneio_controller-a123ef51.js, o arquivo não existia, e como o Cloudflare
|
||||||
|
# ainda servia os controllers antigos do cache parecia defeito de dois
|
||||||
|
# arquivos — na origem, TODOS os assets estavam 404.
|
||||||
|
total=$(find public/assets -type f 2>/dev/null | wc -l)
|
||||||
|
if [ ! -f public/assets/.manifest.json ] || [ "$total" -lt 2 ]; then
|
||||||
|
log "ALERTA: precompile terminou sem erro mas public/assets tem $total arquivo(s)."
|
||||||
|
log " O site vai subir SEM JS: cada /assets/* responde 404."
|
||||||
|
log " Rode 'docker compose exec app bundle exec rails assets:precompile'"
|
||||||
|
log " e confira com: curl -sI SEU_DOMINIO/assets/controllers/index-<digest>.js"
|
||||||
|
else
|
||||||
|
log "assets prontos ($total arquivo(s) em public/assets)"
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
# NÃO sai com erro: sem JS o sistema fica ruim, mas ainda dá para entrar e
|
# NÃO sai com erro: sem JS o sistema fica ruim, mas ainda dá para entrar e
|
||||||
# ler os dados — e um `exit 1` aqui viraria loop de reinício com o
|
# ler os dados — e um `exit 1` aqui viraria loop de reinício com o
|
||||||
|
|||||||
@@ -145,6 +145,10 @@ Rails.application.routes.draw do
|
|||||||
# do romaneio inteiro. Uma rota só porque é o mesmo documento.
|
# do romaneio inteiro. Uma rota só porque é o mesmo documento.
|
||||||
get :pdf
|
get :pdf
|
||||||
post :reimportar
|
post :reimportar
|
||||||
|
# Vincula a operação do mês DEPOIS da importação e preenche APARELHO com
|
||||||
|
# o que a gade_entregas_* sabe — sem depender do plano estar disponível
|
||||||
|
# de novo (ver Romaneios::VinculadorDeOperacao).
|
||||||
|
patch :vincular_operacao
|
||||||
patch :atualizar_linha # JSON — um campo de uma parada
|
patch :atualizar_linha # JSON — um campo de uma parada
|
||||||
patch :atualizar_veiculo # JSON — motorista da folha
|
patch :atualizar_veiculo # JSON — motorista da folha
|
||||||
patch :atualizar_rotulo # JSON — o texto "PLANO:" do cabeçalho
|
patch :atualizar_rotulo # JSON — o texto "PLANO:" do cabeçalho
|
||||||
|
|||||||
@@ -139,6 +139,21 @@ agent-browser eval "fetch(document.querySelector('a[href*=pdf]').href).then(r=>r
|
|||||||
**PASSOU se**: 4a="none", 4b="flex" com src preenchido, 4c="none", 4d=200,
|
**PASSOU se**: 4a="none", 4b="flex" com src preenchido, 4c="none", 4d=200,
|
||||||
Teste 0 limpo. **NÃO** clicar em "Reimportar plano" (tem confirmação).
|
Teste 0 limpo. **NÃO** clicar em "Reimportar plano" (tem confirmação).
|
||||||
|
|
||||||
|
### Caso 4e — Vínculo manual da operação (só leitura)
|
||||||
|
|
||||||
|
O bloco "Operação do mês" tem que aparecer em todo romaneio, e o formulário
|
||||||
|
precisa funcionar **sem JavaScript** (é `<details>` nativo + form comum):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
agent-browser eval "const d=[...document.querySelectorAll('details')].find(x=>x.textContent.includes('operação')); d ? 'existe | aberto:'+d.open+' | opcoes:'+d.querySelectorAll('option').length : 'AUSENTE'"
|
||||||
|
```
|
||||||
|
|
||||||
|
**PASSOU se**: o bloco existe; num romaneio **sem** operação ele vem `aberto:true`
|
||||||
|
(a pendência fica à vista) e num **com** operação vem `aberto:false`; a lista tem
|
||||||
|
mais de uma `option`.
|
||||||
|
⚠️ **NÃO enviar o formulário** — vincular altera dados do romaneio. Só conferir
|
||||||
|
que o controle está lá e abre.
|
||||||
|
|
||||||
### Caso 5 — Planilha SimpliRoute
|
### Caso 5 — Planilha SimpliRoute
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
Reference in New Issue
Block a user