Implandação da customização das permissões dos usuarios e das notificaçoes
This commit is contained in:
@@ -6,6 +6,10 @@ class User < ApplicationRecord
|
||||
:rememberable,
|
||||
:validatable
|
||||
|
||||
# Perfil de acesso: nulo é válido (motorista não usa; e quem ainda não foi
|
||||
# migrado cai no padrão do papel — ver #permissoes_efetivas).
|
||||
belongs_to :perfil_acesso, optional: true, inverse_of: :users
|
||||
|
||||
# ── Roles ──────────────────────────────────────────────────
|
||||
# externo: acesso somente-leitura ao dashboard (ex.: gerências de outras áreas
|
||||
# que só precisam visualizar os indicadores). Login normal por e-mail/senha.
|
||||
@@ -57,12 +61,112 @@ class User < ApplicationRecord
|
||||
ROLES_LABEL[role] || role.humanize
|
||||
end
|
||||
|
||||
# Conta desativada não entra — nem por e-mail/senha.
|
||||
#
|
||||
# Só o login por PIN checava `ativo?` (Users::SessionsController), então
|
||||
# desativar um gerente não impedia nada, apesar do formulário prometer
|
||||
# "usuários inativos não conseguem fazer login". Devise chama este método em
|
||||
# todo login e em toda request de sessão existente, então desativar alguém já
|
||||
# derruba a sessão aberta dele.
|
||||
def active_for_authentication?
|
||||
super && ativo?
|
||||
end
|
||||
|
||||
def inactive_message
|
||||
ativo? ? super : :conta_desativada
|
||||
end
|
||||
|
||||
# ── Permissões ──────────────────────────────────────────────
|
||||
# O que o usuário pode fazer sai do PERFIL DE ACESSO (PerfilAcesso), que o ADM
|
||||
# monta na tela. Enquanto ele não tem perfil, vale o padrão do papel legado
|
||||
# (Permissao::PADRAO_POR_ROLE) — é o que mantém o sistema funcionando durante a
|
||||
# migração e o que faz um usuário recém-criado não ficar sem nada.
|
||||
def pode?(chave)
|
||||
permissoes_efetivas.include?(chave.to_s)
|
||||
end
|
||||
|
||||
# Set (não Array): `pode?` é chamado dezenas de vezes por render de tela — uma
|
||||
# vez por item de menu e por botão.
|
||||
#
|
||||
# ⚠️ A primeira linha existe para NÃO tocar o banco quando não há perfil: os
|
||||
# specs de policy usam `build(:admin)` (objeto sem id), e `perfil_acesso`
|
||||
# dispararia consulta com id nulo.
|
||||
def permissoes_efetivas
|
||||
@permissoes_efetivas ||=
|
||||
if perfil_acesso_id.nil?
|
||||
Set.new(Permissao.do_role(role))
|
||||
else
|
||||
Set.new(perfil_acesso&.permissoes_validas || Permissao.do_role(role))
|
||||
end
|
||||
end
|
||||
|
||||
# A coluna nasce numa migration própria. Se o código subir antes de `db:migrate`
|
||||
# rodar, `perfil_acesso_id` não existe e TODA tela quebraria por NoMethodError —
|
||||
# aqui o sistema simplesmente continua no padrão do papel até a coluna chegar.
|
||||
# Pode sair depois que a migration estiver aplicada em produção.
|
||||
def perfil_acesso_id
|
||||
return nil unless self.class.column_names.include?('perfil_acesso_id')
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
# Trocar o perfil no meio da request (tela de usuários) tem que refletir.
|
||||
def recarregar_permissoes!
|
||||
@permissoes_efetivas = nil
|
||||
self
|
||||
end
|
||||
|
||||
# Primeira tela que este usuário pode abrir, na ordem em que o menu as mostra.
|
||||
#
|
||||
# Existe para não haver LOOP: a raiz do app é o dashboard e o tratamento de
|
||||
# "acesso negado" redireciona para a raiz — um perfil sem `dashboard.ver`
|
||||
# entraria em nega → raiz → nega para sempre. `nil` significa "não pode abrir
|
||||
# nada", e quem chama mostra a tela de sem-acesso.
|
||||
HOME_POR_PERMISSAO = [
|
||||
['dashboard.ver', :dashboard_path],
|
||||
['dashboard.operacoes', :operacoes_dashboard_path],
|
||||
['consolidacao.ver', :consolidacoes_path],
|
||||
['admin.usuarios', :admin_usuarios_path],
|
||||
['admin.perfis', :admin_perfis_path],
|
||||
['admin.configuracoes', :admin_configuracoes_path],
|
||||
['notificacao.contatos', :admin_contatos_path],
|
||||
['notificacao.eventos', :admin_eventos_path],
|
||||
['notificacao.envios', :admin_envios_path]
|
||||
].freeze
|
||||
|
||||
def home_rota
|
||||
return :motorista_dashboard_path if motorista?
|
||||
|
||||
HOME_POR_PERMISSAO.find { |chave, _| pode?(chave) }&.last
|
||||
end
|
||||
|
||||
# Mantidos porque a navbar e vários controllers já os chamam — agora são só
|
||||
# apelidos das chaves correspondentes.
|
||||
def pode_ver_config?
|
||||
admin? || gerente?
|
||||
pode?('admin.configuracoes')
|
||||
end
|
||||
|
||||
def pode_consolidar?
|
||||
admin? || gerente? || operador?
|
||||
pode?('consolidacao.ver')
|
||||
end
|
||||
|
||||
# ── Trava do último administrador ───────────────────────────
|
||||
# Quem, ATIVO, ainda consegue reabrir a porta do sistema (mexer em perfis e em
|
||||
# usuários). Sem isto, o ADM consegue se trancar para fora: a tela que controla
|
||||
# acesso é justamente a que ele perderia, e a volta só existiria por console.
|
||||
#
|
||||
# `ignorando` simula "e se esta pessoa for desativada, excluída ou trocada de
|
||||
# perfil?" — passe o usuário que está sendo alterado.
|
||||
def self.administradores_de_acesso(ignorando: nil)
|
||||
ativos.includes(:perfil_acesso)
|
||||
.reject { |u| ignorando && u.id == ignorando.id }
|
||||
.select { |u| u.pode?('admin.perfis') && u.pode?('admin.usuarios_gerenciar') }
|
||||
end
|
||||
|
||||
def self.ultimo_administrador?(usuario)
|
||||
return false unless usuario.pode?('admin.perfis') && usuario.pode?('admin.usuarios_gerenciar')
|
||||
|
||||
administradores_de_acesso(ignorando: usuario).empty?
|
||||
end
|
||||
|
||||
# Token de login rápido via QR Code (extrato) — Fase 7/8
|
||||
|
||||
Reference in New Issue
Block a user