Implandação da customização das permissões dos usuarios e das notificaçoes

This commit is contained in:
2026-08-26 17:24:25 -03:00
parent 808c2c7545
commit 5bb5d74e22
71 changed files with 2487 additions and 269 deletions

View File

@@ -15,7 +15,15 @@ class ApplicationPolicy
def edit? = update?
def destroy? = user.admin?
# Helpers de papel disponíveis para todas as policies (delegam para o User)
# Régua padrão de TODAS as policies: pergunta ao perfil de acesso do usuário
# (User#pode?), não ao papel dele. As chaves estão em Permissao::TODAS.
def pode?(chave)
user.pode?(chave)
end
# Helpers de papel — LEGADO. Continuam aqui porque `admin?` ainda distingue
# tipo de conta em pontos que não são permissão (ex.: escopo de listagem).
# Regra nova não deve usá-los: use `pode?`.
def admin?
user.admin?
end

View File

@@ -0,0 +1,16 @@
# app/policies/auditoria_log_policy.rb
#
# Existe para a auditoria parar de cair no default genérico da ApplicationPolicy
# (`policy_class: ApplicationPolicy` no controller): a régua de lá é a de
# consolidação, e quem podia consolidar entrava na auditoria pela URL, embora o
# menu nunca oferecesse.
class AuditoriaLogPolicy < ApplicationPolicy
def index? = pode?('admin.auditoria')
def show? = index?
class Scope < Scope
def resolve
user.pode?('admin.auditoria') ? scope.all : scope.none
end
end
end

View File

@@ -1,22 +1,7 @@
# app/policies/configuracao_notificacao_policy.rb
#
# Mais restrito que ConfiguracaoPolicy (que libera index? para gerente): estas
# são credenciais de envio — senha de e-mail e token do Twilio —, então só ADM.
#
# ⚠️ Cada action nova do controller precisa de um método `<action>?` aqui,
# senão o `authorize` estoura.
# Credenciais de SMTP/Twilio e limites de envio.
class ConfiguracaoNotificacaoPolicy < ApplicationPolicy
def show?
admin?
end
def update?
admin?
end
class Scope < Scope
def resolve
scope.all
end
end
def show? = pode?('notificacao.credenciais')
def update? = show?
end

View File

@@ -1,20 +1,10 @@
# app/policies/configuracao_policy.rb
class ConfiguracaoPolicy < ApplicationPolicy
def index?
admin_ou_gerente?
end
def index? = pode?('admin.configuracoes')
def show? = index?
def edit?
admin?
end
def update?
admin?
end
class Scope < Scope
def resolve
scope.all
end
end
# Alterar preço muda o valor de todo fechamento daqui para a frente — chave
# própria, separada de só olhar a tabela.
def edit? = pode?('admin.configuracoes_editar')
def update? = edit?
end

View File

@@ -1,20 +1,42 @@
# app/policies/consolidacao_policy.rb
class ConsolidacaoPolicy < ApplicationPolicy
def index? = user.pode_consolidar?
def arquivadas? = user.pode_consolidar?
def show? = user.pode_consolidar?
def create? = user.pode_consolidar?
def update? = user.pode_consolidar? && !record_finalizada_para_operador?
def destroy? = user.admin? || user.gerente?
def index? = pode?('consolidacao.ver')
def arquivadas? = index?
def show? = index?
def create? = pode?('consolidacao.criar')
def new? = create?
# Registrar/estornar pagamento: mesma régua de arquivar/excluir.
def registrar_pagamento? = user.admin? || user.gerente?
def cancelar_pagamento? = registrar_pagamento?
# Editar uma consolidação JÁ FINALIZADA é outra permissão: mexer depois do
# fechamento muda o valor que o motorista vai receber. Antes isso era "operador
# não pode, gerente pode" cravado no código.
def update?
pode?('consolidacao.editar') && (!record_finalizada? || pode?('consolidacao.editar_finalizada'))
end
def edit? = update?
def finalizar? = pode?('consolidacao.finalizar')
def finalizar_parcial? = finalizar?
def arquivar? = pode?('consolidacao.arquivar')
def reativar? = arquivar?
def destroy? = pode?('consolidacao.excluir')
def excluir_motorista? = destroy?
# Pagamento: registrar e estornar são chaves distintas — estornar desfaz algo
# que já foi comunicado ao motorista.
def registrar_pagamento? = pode?('consolidacao.registrar_pagamento')
def cancelar_pagamento? = pode?('consolidacao.cancelar_pagamento')
# Quem entra e quem sai do fechamento (sem excluir de vez).
def gerir_motoristas? = pode?('consolidacao.gerir_motoristas') && update?
def exportar_pdf? = pode?('consolidacao.exportar_pdf')
private
# Operador não mexe em consolidação finalizada
def record_finalizada_para_operador?
record.respond_to?(:finalizada?) && record.finalizada? && user.operador?
def record_finalizada?
record.respond_to?(:finalizada?) && record.finalizada?
end
end

View File

@@ -1,20 +1,24 @@
# app/policies/contato_policy.rb
#
# Contatos e grupos são a lista de quem recebe mensagem em nome da empresa —
# admin e gerente administram, operador não.
#
# ⚠️ Cada action nova do controller precisa de um `<action>?` aqui, senão o
# `authorize` estoura.
# Contatos e grupos (GrupoContatoPolicy herda esta) — quem RECEBE as mensagens.
class ContatoPolicy < ApplicationPolicy
def index? = admin_ou_gerente?
def show? = admin_ou_gerente?
def create? = admin_ou_gerente?
def new? = create?
def update? = admin_ou_gerente?
def edit? = update?
def destroy? = admin?
def index? = pode?('notificacao.contatos')
def show? = index?
def create? = pode?('notificacao.contatos_gerenciar')
def new? = create?
def update? = create?
def edit? = update?
# Excluir destinatário é separado de editar: some do histórico de quem recebe.
def destroy? = pode?('notificacao.contatos_excluir')
# Grupos do WhatsApp carregados sob demanda pelo form.
def grupos_whatsapp? = create?
class Scope < Scope
def resolve = scope.all
def resolve
user.pode?('notificacao.contatos') ? scope.all : scope.none
end
end
end

View File

@@ -1,15 +1,17 @@
# app/policies/dashboard_policy.rb
class DashboardPolicy < Struct.new(:user, :dashboard)
def metricas?
user.admin? || user.gerente? || user.operador?
end
#
# Policy headless (`authorize :dashboard, :x?`). Deixou de ser Struct para herdar
# o `pode?` da ApplicationPolicy.
class DashboardPolicy < ApplicationPolicy
def ver? = pode?('dashboard.ver')
def index? = ver?
def index?
user.admin? || user.gerente?
end
# Custos, pagamentos, relatório financeiro e entregas em aberto. Era o buraco
# do `externo`: o controller usava skip_authorization e ele via tudo.
def financeiro? = pode?('dashboard.financeiro')
def pendentes? = financeiro?
# Dashboard de operações: todos menos motorista (mesma regra do dashboard HTML).
def operacoes?
!user.motorista?
end
def operacoes? = pode?('dashboard.operacoes')
def exportar? = pode?('dashboard.exportar')
def metricas? = pode?('dashboard.metricas')
end

View File

@@ -1,16 +1,11 @@
# app/policies/edicao_lancamento_policy.rb
#
# Correção de lançamento grava DIRETO na base de produção do SimpliRoute
# operação sensível e irreversível. Restrita a admin.
# Policy "headless": autorizada com o símbolo :edicao_lancamento (sem model).
# Policy headless (`authorize :edicao_lancamento`). Escreve na API do SimpliRoute:
# altera o dado de ORIGEM do rastreio, então é chave própria.
class EdicaoLancamentoPolicy < ApplicationPolicy
# ⚠️ Uma entrada por action do controller: o Pundit chama "<action>?" e, se o
# método não existir, levanta NoMethodError (500) — foi o que aconteceu ao
# adicionar `carregar` sem passar por aqui. Ação nova no controller ⇒ linha
# nova aqui.
def show? = admin?
def buscar? = admin?
def carregar? = admin?
def atualizar? = admin?
def historico? = admin?
def show? = pode?('admin.edicao_lancamento')
def buscar? = show?
def carregar? = show?
def atualizar? = show?
def historico? = show?
end

View File

@@ -1,23 +1,25 @@
# app/policies/evento_notificacao_policy.rb
#
# Eventos definem o que a empresa dispara e para quem — inclusive o botão de
# disparo manual, que manda mensagem de verdade. Só ADM.
class EventoNotificacaoPolicy < ApplicationPolicy
def index? = admin?
def show? = admin?
def create? = admin?
def new? = create?
def update? = admin?
def edit? = update?
def disparar? = admin?
def index? = pode?('notificacao.eventos')
def show? = index?
# Evento de sistema não se apaga: o código dispara por `chave` e ficaria
# chamando no vazio. A tela oferece desativar.
def create? = pode?('notificacao.eventos_gerenciar')
def new? = create?
def update? = create?
def edit? = update?
# Disparar manda mensagem DE VERDADE, em nome da empresa — chave própria,
# separada de configurar o evento.
def disparar? = pode?('notificacao.disparar_manual')
# Evento de sistema não é apagável: o código dispara por ele.
def destroy?
admin? && record.respond_to?(:apagavel?) && record.apagavel?
create? && record.respond_to?(:apagavel?) && record.apagavel?
end
class Scope < Scope
def resolve = scope.all
def resolve
user.pode?('notificacao.eventos') ? scope.all : scope.none
end
end
end

View File

@@ -1,12 +1,13 @@
# app/policies/notificacao_envio_policy.rb
#
# Log de envios: o corpo das mensagens passa por aqui, então leitura é de
# admin/gerente. Nada é editável — o log é registro, não cadastro.
# Histórico do que saiu (e do que falhou).
class NotificacaoEnvioPolicy < ApplicationPolicy
def index? = admin_ou_gerente?
def show? = admin_ou_gerente?
def index? = pode?('notificacao.envios')
def show? = index?
class Scope < Scope
def resolve = scope.all
def resolve
user.pode?('notificacao.envios') ? scope.all : scope.none
end
end
end

View File

@@ -0,0 +1,24 @@
# app/policies/perfil_acesso_policy.rb
#
# Quem monta perfil decide o acesso de todo mundo — é a permissão mais sensível
# do sistema, por isso ela é uma chave própria (`admin.perfis`) e não vem junto
# de "gerenciar usuários".
class PerfilAcessoPolicy < ApplicationPolicy
def index? = user.pode?('admin.perfis')
def show? = index?
def create? = index?
def new? = create?
def update? = index?
def edit? = update?
# Perfil de sistema ou em uso não sai (ver PerfilAcesso#apagavel?).
def destroy?
index? && record.respond_to?(:apagavel?) && record.apagavel?
end
class Scope < Scope
def resolve
user.pode?('admin.perfis') ? scope.all : scope.none
end
end
end

View File

@@ -1,9 +1,7 @@
# app/policies/planilha_simpliroute_policy.rb
# app/policies/planilha_simpli_route_policy.rb
#
# Geração da planilha de carga do SimpliRoute — tarefa de preparação de operação.
# Liberada para admin e gerente (quem prepara as cargas). Policy "headless"
# (autorizada com o símbolo :planilha_simpliroute).
# Policy headless (`authorize :planilha_simpli_route`).
class PlanilhaSimpliRoutePolicy < ApplicationPolicy
def show? = admin_ou_gerente?
def baixar? = admin_ou_gerente?
def show? = pode?('admin.planilha_simpli_route')
def baixar? = show?
end

View File

@@ -1,44 +1,35 @@
# app/policies/user_policy.rb
class UserPolicy < ApplicationPolicy
def index?
admin_ou_gerente?
end
def index? = pode?('admin.usuarios')
def show?
admin? || record == user
end
# Cada um enxerga a própria ficha (é assim que a tela de perfil pessoal
# funciona), além de quem administra usuários.
def show? = pode?('admin.usuarios') || record == user
def create?
admin?
end
def create? = pode?('admin.usuarios_gerenciar')
def new? = create?
def new?
create?
end
# ⚠️ `record == user` CONTINUA aqui de propósito: é o que permite alguém editar
# o próprio cadastro (nome, senha). O que NÃO pode é essa mesma pessoa mudar o
# próprio papel/perfil e se promover — isso é barrado em #alterar_acesso?, que
# o controller usa para decidir quais campos aceita.
def update? = pode?('admin.usuarios_gerenciar') || record == user
def edit? = update?
def update?
admin? || record == user
end
def destroy? = create? && record != user
def toggle_ativo? = create? && record != user
def edit?
update?
end
def destroy?
admin? && record != user
end
def toggle_ativo?
admin?
end
# admin? e admin_ou_gerente? vêm da ApplicationPolicy.
# Pode mexer no TIPO DE CONTA e no PERFIL DE ACESSO de alguém? Nunca no
# próprio: senão qualquer usuário se promove editando a própria ficha.
def alterar_acesso? = pode?('admin.usuarios_gerenciar') && record != user
class Scope < Scope
def resolve
# Continua por papel: filtrar por permissão exigiria carregar o perfil de
# cada linha (N+1) para responder algo que a policy já cobre na ação.
if user.admin?
scope.all
elsif user.gerente?
elsif user.pode?('admin.usuarios')
scope.where.not(role: :admin)
else
scope.where(id: user.id)

View File

@@ -0,0 +1,19 @@
# app/policies/variavel_personalizada_policy.rb
#
# Variável personalizada entra no texto de mensagens enviadas em nome da empresa
# — mesma régua de quem configura evento.
class VariavelPersonalizadaPolicy < ApplicationPolicy
def index? = pode?('notificacao.variaveis')
def show? = index?
def create? = index?
def new? = create?
def update? = index?
def edit? = update?
def destroy? = index?
class Scope < Scope
def resolve
user.pode?('notificacao.variaveis') ? scope.all : scope.none
end
end
end

View File

@@ -1,13 +1,9 @@
# app/policies/whatsapp_sessao_policy.rb
#
# Parear o WhatsApp por QR é ligar o número da empresa a este servidor, e
# desconectar derruba o canal para todo mundo. Só ADM.
# Policy headless (`authorize :whatsapp_sessao`). Parear o WhatsApp é o que
# habilita mandar mensagem em nome da empresa.
class WhatsappSessaoPolicy < ApplicationPolicy
def show? = admin?
def desconectar? = admin?
def testar? = admin?
class Scope < Scope
def resolve = scope.all
end
def show? = pode?('notificacao.whatsapp_sessao')
def desconectar? = show?
def testar? = show?
end