Implandação da customização das permissões dos usuarios e das notificaçoes
This commit is contained in:
31
db/migrate/20260826000001_create_perfis_acesso.rb
Normal file
31
db/migrate/20260826000001_create_perfis_acesso.rb
Normal file
@@ -0,0 +1,31 @@
|
||||
# Perfil de acesso: o conjunto de permissões que o ADM monta na tela e atribui a
|
||||
# um usuário.
|
||||
#
|
||||
# Antes, quem-vê-o-quê era o papel fixo em `users.role` e um punhado de `if
|
||||
# admin? || gerente?` espalhado pelas policies: tirar as Configurações de UM
|
||||
# gerente exigia editar código e fazer deploy.
|
||||
#
|
||||
# `permissoes` é jsonb (lista de chaves de Permissao::TODAS) e não tabela filha
|
||||
# de propósito: é um conjunto pequeno, sempre lido junto com o perfil, e nunca
|
||||
# consultado ao contrário ("quem tem tal permissão?" se responde em memória). A
|
||||
# validação de quais chaves existem fica no model — o catálogo é código, muda com
|
||||
# deploy, e o banco não pode travar um perfil salvo com uma chave que saiu.
|
||||
class CreatePerfisAcesso < ActiveRecord::Migration[7.1]
|
||||
def change
|
||||
create_table :perfis_acesso do |t|
|
||||
t.string :nome, null: false
|
||||
t.string :descricao
|
||||
t.jsonb :permissoes, null: false, default: []
|
||||
# sistema: nasce com o app (Administrador, Gerente, ...). Editável e
|
||||
# desativável, mas não apagável — mesma regra de EventoNotificacao#apagavel?
|
||||
t.boolean :sistema, null: false, default: false
|
||||
t.boolean :ativo, null: false, default: true
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
|
||||
# Nome é o que identifica o perfil na tela; duplicar só gera erro humano.
|
||||
add_index :perfis_acesso, 'LOWER(nome)', unique: true,
|
||||
name: 'index_perfis_acesso_nome_unico'
|
||||
end
|
||||
end
|
||||
17
db/migrate/20260826000002_add_perfil_acesso_to_users.rb
Normal file
17
db/migrate/20260826000002_add_perfil_acesso_to_users.rb
Normal file
@@ -0,0 +1,17 @@
|
||||
# Liga o usuário ao perfil de acesso.
|
||||
#
|
||||
# NULO É VÁLIDO e não é descuido: motorista não usa perfil (o que ele acessa é a
|
||||
# área própria, governada pelo tipo de conta), e um usuário sem perfil cai no
|
||||
# padrão do papel legado (Permissao::PADRAO_POR_ROLE) — é o que mantém o sistema
|
||||
# funcionando entre o deploy do código e a migration de dados.
|
||||
class AddPerfilAcessoToUsers < ActiveRecord::Migration[7.1]
|
||||
def change
|
||||
# ⚠️ `to_table:` é OBRIGATÓRIO. Com `foreign_key: true` o Rails pluraliza o
|
||||
# nome da referência para achar a tabela alvo, e o Inflector não fala
|
||||
# português: `perfil_acesso` viraria "perfil_acessos". A tabela real é
|
||||
# `perfis_acesso` (plural no primeiro termo) — mesma pegadinha documentada em
|
||||
# 20260824000002_create_notificacao_eventos.rb.
|
||||
add_reference :users, :perfil_acesso, null: true,
|
||||
foreign_key: { to_table: :perfis_acesso }
|
||||
end
|
||||
end
|
||||
78
db/migrate/20260826000003_semear_perfis_acesso.rb
Normal file
78
db/migrate/20260826000003_semear_perfis_acesso.rb
Normal file
@@ -0,0 +1,78 @@
|
||||
# Cria os perfis pré-prontos e atribui a cada usuário existente o perfil do papel
|
||||
# que ele já tinha. Sem isso, o dia seguinte ao deploy seria "todo mundo no
|
||||
# fallback" — funciona, mas a tela de perfis nasceria vazia e o ADM não teria de
|
||||
# onde partir para montar o dele.
|
||||
#
|
||||
# ⚠️ A lista de chaves está DUPLICADA aqui e em Permissao::PADRAO_POR_ROLE, de
|
||||
# propósito: migration não pode depender de model. Se o catálogo mudar daqui a
|
||||
# seis meses, o histórico do banco não pode mudar junto — quem já rodou esta
|
||||
# migration continua com o perfil que foi criado naquele dia.
|
||||
#
|
||||
# Idempotente (o teste e a produção migram em momentos diferentes, e a migration
|
||||
# pode encontrar um banco onde o ADM já criou perfis à mão).
|
||||
class SemearPerfisAcesso < ActiveRecord::Migration[7.1]
|
||||
TODAS = %w[
|
||||
dashboard.ver dashboard.financeiro dashboard.operacoes dashboard.exportar dashboard.metricas
|
||||
consolidacao.ver consolidacao.criar consolidacao.editar consolidacao.editar_finalizada
|
||||
consolidacao.finalizar consolidacao.arquivar consolidacao.excluir
|
||||
consolidacao.registrar_pagamento consolidacao.cancelar_pagamento
|
||||
consolidacao.gerir_motoristas consolidacao.exportar_pdf
|
||||
notificacao.contatos notificacao.contatos_gerenciar notificacao.contatos_excluir
|
||||
notificacao.eventos notificacao.eventos_gerenciar notificacao.disparar_manual
|
||||
notificacao.variaveis notificacao.envios notificacao.credenciais notificacao.whatsapp_sessao
|
||||
admin.usuarios admin.usuarios_gerenciar admin.perfis
|
||||
admin.configuracoes admin.configuracoes_editar admin.auditoria
|
||||
admin.edicao_lancamento admin.planilha_simpli_route
|
||||
].freeze
|
||||
|
||||
GERENTE = %w[
|
||||
dashboard.ver dashboard.financeiro dashboard.operacoes dashboard.exportar dashboard.metricas
|
||||
consolidacao.ver consolidacao.criar consolidacao.editar consolidacao.editar_finalizada
|
||||
consolidacao.finalizar consolidacao.arquivar consolidacao.excluir
|
||||
consolidacao.registrar_pagamento consolidacao.cancelar_pagamento
|
||||
consolidacao.gerir_motoristas consolidacao.exportar_pdf
|
||||
notificacao.contatos notificacao.contatos_gerenciar notificacao.envios
|
||||
admin.usuarios admin.configuracoes admin.auditoria admin.planilha_simpli_route
|
||||
].freeze
|
||||
|
||||
OPERADOR = %w[
|
||||
dashboard.ver dashboard.financeiro dashboard.operacoes dashboard.exportar dashboard.metricas
|
||||
consolidacao.ver consolidacao.criar consolidacao.editar consolidacao.finalizar
|
||||
consolidacao.gerir_motoristas consolidacao.exportar_pdf
|
||||
].freeze
|
||||
|
||||
EXTERNO = %w[dashboard.ver dashboard.operacoes].freeze
|
||||
|
||||
# nome, descrição, permissões, role legada que recebe este perfil
|
||||
PERFIS = [
|
||||
['Administrador', 'Acesso total ao sistema.', :todas, 0],
|
||||
['Gerente', 'Opera e paga; administra usuários, preços e destinatários.', GERENTE, 1],
|
||||
['Operador', 'Monta e finaliza consolidações; não paga nem administra.', OPERADOR, 2],
|
||||
['Externo', 'Somente leitura dos indicadores de operação.', EXTERNO, 4]
|
||||
].freeze
|
||||
|
||||
def up
|
||||
PERFIS.each do |nome, descricao, permissoes, role|
|
||||
lista = (permissoes == :todas ? TODAS : permissoes)
|
||||
|
||||
id = select_value("SELECT id FROM perfis_acesso WHERE LOWER(nome) = #{quote(nome.downcase)}")
|
||||
if id.nil?
|
||||
execute(<<~SQL.squish)
|
||||
INSERT INTO perfis_acesso (nome, descricao, permissoes, sistema, ativo, created_at, updated_at)
|
||||
VALUES (#{quote(nome)}, #{quote(descricao)}, #{quote(lista.to_json)}::jsonb, true, true, NOW(), NOW())
|
||||
SQL
|
||||
id = select_value("SELECT id FROM perfis_acesso WHERE LOWER(nome) = #{quote(nome.downcase)}")
|
||||
end
|
||||
|
||||
# Só quem ainda não tem perfil: se o ADM já ajustou alguém à mão entre o
|
||||
# deploy e a migration, a escolha dele vence.
|
||||
execute("UPDATE users SET perfil_acesso_id = #{id}, updated_at = NOW() " \
|
||||
"WHERE role = #{role} AND perfil_acesso_id IS NULL")
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
execute('UPDATE users SET perfil_acesso_id = NULL')
|
||||
execute('DELETE FROM perfis_acesso WHERE sistema = true')
|
||||
end
|
||||
end
|
||||
22
db/migrate/20260826000004_create_variaveis_personalizadas.rb
Normal file
22
db/migrate/20260826000004_create_variaveis_personalizadas.rb
Normal file
@@ -0,0 +1,22 @@
|
||||
# Variáveis que o ADM cria na tela e usa em qualquer mensagem: {{telefone_suporte}},
|
||||
# {{horario_atendimento}}, {{site}}…
|
||||
#
|
||||
# Antes, toda informação fixa que precisasse aparecer numa mensagem era digitada
|
||||
# de novo em cada template — e mudar o telefone do suporte significava abrir um
|
||||
# por um. Aqui a chave é única e o valor mora num lugar só.
|
||||
class CreateVariaveisPersonalizadas < ActiveRecord::Migration[7.1]
|
||||
def change
|
||||
create_table :variaveis_personalizadas do |t|
|
||||
t.string :chave, null: false # sem {{ }}: telefone_suporte
|
||||
t.string :valor, null: false, default: ''
|
||||
t.string :descricao # o que a tela mostra ao lado
|
||||
t.boolean :ativo, null: false, default: true
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
|
||||
# A chave é o que aparece no texto ({{chave}}); duas iguais tornariam o
|
||||
# resultado imprevisível.
|
||||
add_index :variaveis_personalizadas, :chave, unique: true
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user