First commit - backup RDOC
This commit is contained in:
1
supabase/.temp/cli-latest
Normal file
1
supabase/.temp/cli-latest
Normal file
@@ -0,0 +1 @@
|
||||
v2.65.5
|
||||
1
supabase/.temp/gotrue-version
Normal file
1
supabase/.temp/gotrue-version
Normal file
@@ -0,0 +1 @@
|
||||
v2.182.1
|
||||
1
supabase/.temp/pooler-url
Normal file
1
supabase/.temp/pooler-url
Normal file
@@ -0,0 +1 @@
|
||||
postgresql://postgres.ympbgdymeesivfajmgat:[YOUR-PASSWORD]@aws-0-us-west-2.pooler.supabase.com:6543/postgres
|
||||
1
supabase/.temp/postgres-version
Normal file
1
supabase/.temp/postgres-version
Normal file
@@ -0,0 +1 @@
|
||||
17.6.1.054
|
||||
1
supabase/.temp/project-ref
Normal file
1
supabase/.temp/project-ref
Normal file
@@ -0,0 +1 @@
|
||||
mnwrnblzabxgqtgjwxgl
|
||||
1
supabase/.temp/rest-version
Normal file
1
supabase/.temp/rest-version
Normal file
@@ -0,0 +1 @@
|
||||
v13.0.5
|
||||
1
supabase/.temp/storage-version
Normal file
1
supabase/.temp/storage-version
Normal file
@@ -0,0 +1 @@
|
||||
iceberg-catalog-ids
|
||||
@@ -0,0 +1,502 @@
|
||||
-- ========================================
|
||||
-- MIGRATION: Multi-Tenant SaaS Schema (FIXED)
|
||||
-- Data: 2024-12-02
|
||||
-- Descrição: Estrutura completa para SaaS multi-tenant
|
||||
-- ========================================
|
||||
|
||||
-- Habilitar extensões necessárias
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
|
||||
|
||||
-- ========================================
|
||||
-- 1. TABELA DE ORGANIZAÇÕES (TENANTS)
|
||||
-- ========================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.organizacoes (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
|
||||
-- Identificação
|
||||
slug VARCHAR(100) UNIQUE NOT NULL,
|
||||
nome VARCHAR(255) NOT NULL,
|
||||
razao_social VARCHAR(255),
|
||||
cnpj VARCHAR(18) UNIQUE,
|
||||
|
||||
-- Contato
|
||||
email_contato VARCHAR(255),
|
||||
telefone VARCHAR(20),
|
||||
endereco TEXT,
|
||||
cidade VARCHAR(100),
|
||||
estado VARCHAR(2),
|
||||
cep VARCHAR(10),
|
||||
|
||||
-- Plano e limites
|
||||
plano VARCHAR(50) DEFAULT 'trial' CHECK (plano IN ('trial', 'basic', 'professional', 'enterprise')),
|
||||
max_usuarios INTEGER DEFAULT 5,
|
||||
max_obras INTEGER DEFAULT 3,
|
||||
max_rdos_mes INTEGER DEFAULT 100,
|
||||
max_storage_mb INTEGER DEFAULT 500,
|
||||
|
||||
-- Personalização
|
||||
logo_url TEXT,
|
||||
cor_primaria VARCHAR(7) DEFAULT '#3B82F6',
|
||||
cor_secundaria VARCHAR(7) DEFAULT '#1E40AF',
|
||||
configuracoes JSONB DEFAULT '{}'::jsonb,
|
||||
|
||||
-- Status e controle
|
||||
status VARCHAR(50) DEFAULT 'ativa' CHECK (status IN ('ativa', 'suspensa', 'cancelada', 'trial')),
|
||||
data_trial_inicio TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
data_trial_fim TIMESTAMP WITH TIME ZONE DEFAULT (NOW() + INTERVAL '14 days'),
|
||||
data_proxima_cobranca TIMESTAMP WITH TIME ZONE,
|
||||
|
||||
-- Auditoria
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
|
||||
-- Constraints
|
||||
CONSTRAINT slug_format CHECK (slug ~ '^[a-z0-9-]+$'),
|
||||
CONSTRAINT slug_length CHECK (length(slug) >= 3 AND length(slug) <= 100)
|
||||
);
|
||||
|
||||
-- Índices para organizações
|
||||
CREATE INDEX IF NOT EXISTS idx_organizacoes_slug ON public.organizacoes(slug);
|
||||
CREATE INDEX IF NOT EXISTS idx_organizacoes_status ON public.organizacoes(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_organizacoes_plano ON public.organizacoes(plano);
|
||||
|
||||
-- ========================================
|
||||
-- 2. TABELA DE USUÁRIOS
|
||||
-- ========================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.usuarios (
|
||||
id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
|
||||
organizacao_id UUID REFERENCES public.organizacoes(id) ON DELETE CASCADE,
|
||||
|
||||
-- Informações pessoais
|
||||
nome VARCHAR(255) NOT NULL,
|
||||
email VARCHAR(255) NOT NULL,
|
||||
telefone VARCHAR(20),
|
||||
cpf VARCHAR(14),
|
||||
cargo VARCHAR(100),
|
||||
|
||||
-- Avatar
|
||||
avatar_url TEXT,
|
||||
|
||||
-- Status
|
||||
ativo BOOLEAN DEFAULT true,
|
||||
|
||||
-- Auditoria
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
|
||||
-- Constraints
|
||||
UNIQUE(organizacao_id, email)
|
||||
);
|
||||
|
||||
-- Índices para usuários
|
||||
CREATE INDEX IF NOT EXISTS idx_usuarios_org ON public.usuarios(organizacao_id, id);
|
||||
CREATE INDEX IF NOT EXISTS idx_usuarios_email ON public.usuarios(email);
|
||||
CREATE INDEX IF NOT EXISTS idx_usuarios_ativo ON public.usuarios(ativo);
|
||||
|
||||
-- ========================================
|
||||
-- 3. TABELA DE ROLES E PERMISSÕES
|
||||
-- ========================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.organizacao_usuarios (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
organizacao_id UUID NOT NULL REFERENCES public.organizacoes(id) ON DELETE CASCADE,
|
||||
usuario_id UUID NOT NULL REFERENCES public.usuarios(id) ON DELETE CASCADE,
|
||||
|
||||
-- Role na organização
|
||||
role VARCHAR(50) NOT NULL CHECK (role IN ('owner', 'admin', 'engenheiro', 'mestre_obra', 'usuario')),
|
||||
|
||||
-- Permissões customizadas (opcional)
|
||||
permissoes_customizadas JSONB DEFAULT '{}'::jsonb,
|
||||
|
||||
-- Status
|
||||
ativo BOOLEAN DEFAULT true,
|
||||
|
||||
-- Auditoria
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
|
||||
-- Constraints
|
||||
UNIQUE(organizacao_id, usuario_id)
|
||||
);
|
||||
|
||||
-- Índices
|
||||
CREATE INDEX IF NOT EXISTS idx_org_usuarios_org ON public.organizacao_usuarios(organizacao_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_org_usuarios_user ON public.organizacao_usuarios(usuario_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_org_usuarios_role ON public.organizacao_usuarios(role);
|
||||
|
||||
-- ========================================
|
||||
-- 4. TABELA DE CONVITES
|
||||
-- ========================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.convites (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
organizacao_id UUID NOT NULL REFERENCES public.organizacoes(id) ON DELETE CASCADE,
|
||||
|
||||
-- Dados do convite
|
||||
email VARCHAR(255) NOT NULL,
|
||||
role VARCHAR(50) NOT NULL CHECK (role IN ('admin', 'engenheiro', 'mestre_obra', 'usuario')),
|
||||
token VARCHAR(255) UNIQUE NOT NULL DEFAULT encode(random()::text::bytea, 'hex'),
|
||||
|
||||
-- Quem convidou
|
||||
convidado_por UUID REFERENCES public.usuarios(id),
|
||||
|
||||
-- Status
|
||||
status VARCHAR(50) DEFAULT 'pendente' CHECK (status IN ('pendente', 'aceito', 'expirado', 'cancelado')),
|
||||
|
||||
-- Datas
|
||||
expira_em TIMESTAMP WITH TIME ZONE DEFAULT (NOW() + INTERVAL '7 days'),
|
||||
aceito_em TIMESTAMP WITH TIME ZONE,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
|
||||
-- Constraints
|
||||
UNIQUE(organizacao_id, email, status)
|
||||
);
|
||||
|
||||
-- Índices
|
||||
CREATE INDEX IF NOT EXISTS idx_convites_org ON public.convites(organizacao_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_convites_token ON public.convites(token);
|
||||
CREATE INDEX IF NOT EXISTS idx_convites_status ON public.convites(status);
|
||||
|
||||
-- ========================================
|
||||
-- 5. TABELA DE OBRAS
|
||||
-- ========================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.obras (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
organizacao_id UUID NOT NULL REFERENCES public.organizacoes(id) ON DELETE CASCADE,
|
||||
|
||||
-- Informações básicas
|
||||
nome VARCHAR(255) NOT NULL,
|
||||
descricao TEXT,
|
||||
codigo VARCHAR(50),
|
||||
|
||||
-- Localização
|
||||
endereco TEXT,
|
||||
cidade VARCHAR(100),
|
||||
estado VARCHAR(2),
|
||||
cep VARCHAR(10),
|
||||
coordenadas JSONB,
|
||||
|
||||
-- Cliente e contrato
|
||||
cliente VARCHAR(255),
|
||||
contrato VARCHAR(100),
|
||||
valor_contrato DECIMAL(15,2),
|
||||
|
||||
-- Responsável
|
||||
responsavel_id UUID REFERENCES public.usuarios(id),
|
||||
|
||||
-- Datas
|
||||
data_inicio DATE,
|
||||
data_prevista_fim DATE,
|
||||
data_conclusao DATE,
|
||||
|
||||
-- Progresso
|
||||
progresso_geral DECIMAL(5,2) DEFAULT 0 CHECK (progresso_geral >= 0 AND progresso_geral <= 100),
|
||||
|
||||
-- Status
|
||||
status VARCHAR(50) DEFAULT 'planejamento' CHECK (status IN ('planejamento', 'ativa', 'pausada', 'concluida', 'cancelada')),
|
||||
|
||||
-- Configurações específicas da obra
|
||||
configuracoes JSONB DEFAULT '{}'::jsonb,
|
||||
|
||||
-- Observações
|
||||
observacoes TEXT,
|
||||
|
||||
-- Auditoria
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Índices
|
||||
CREATE INDEX IF NOT EXISTS idx_obras_org ON public.obras(organizacao_id, id);
|
||||
CREATE INDEX IF NOT EXISTS idx_obras_status ON public.obras(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_obras_responsavel ON public.obras(responsavel_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_obras_data_inicio ON public.obras(data_inicio);
|
||||
|
||||
-- ========================================
|
||||
-- 6. TABELA DE RDOs
|
||||
-- ========================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.rdos (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
organizacao_id UUID NOT NULL REFERENCES public.organizacoes(id) ON DELETE CASCADE,
|
||||
obra_id UUID NOT NULL REFERENCES public.obras(id) ON DELETE CASCADE,
|
||||
|
||||
-- Número sequencial por obra
|
||||
numero INTEGER NOT NULL,
|
||||
|
||||
-- Criador
|
||||
criado_por UUID NOT NULL REFERENCES public.usuarios(id),
|
||||
|
||||
-- Data do relatório
|
||||
data_relatorio DATE NOT NULL DEFAULT CURRENT_DATE,
|
||||
|
||||
-- Condições climáticas
|
||||
condicoes_climaticas VARCHAR(50),
|
||||
temperatura_min DECIMAL(5,2),
|
||||
temperatura_max DECIMAL(5,2),
|
||||
observacoes_clima TEXT,
|
||||
|
||||
-- Observações gerais
|
||||
observacoes_gerais TEXT,
|
||||
|
||||
-- Status e aprovação
|
||||
status VARCHAR(50) DEFAULT 'rascunho' CHECK (status IN ('rascunho', 'enviado', 'aprovado', 'rejeitado')),
|
||||
aprovado_por UUID REFERENCES public.usuarios(id),
|
||||
aprovado_em TIMESTAMP WITH TIME ZONE,
|
||||
motivo_rejeicao TEXT,
|
||||
|
||||
-- Auditoria
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
|
||||
-- Constraints
|
||||
UNIQUE(obra_id, numero),
|
||||
UNIQUE(obra_id, data_relatorio)
|
||||
);
|
||||
|
||||
-- Índices
|
||||
CREATE INDEX IF NOT EXISTS idx_rdos_org ON public.rdos(organizacao_id, id);
|
||||
CREATE INDEX IF NOT EXISTS idx_rdos_obra ON public.rdos(obra_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_rdos_criador ON public.rdos(criado_por);
|
||||
CREATE INDEX IF NOT EXISTS idx_rdos_data ON public.rdos(data_relatorio);
|
||||
CREATE INDEX IF NOT EXISTS idx_rdos_status ON public.rdos(status);
|
||||
|
||||
-- ========================================
|
||||
-- 7. TABELAS RELACIONADAS AO RDO
|
||||
-- ========================================
|
||||
|
||||
-- 7.1 Atividades do RDO
|
||||
CREATE TABLE IF NOT EXISTS public.rdo_atividades (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
organizacao_id UUID NOT NULL REFERENCES public.organizacoes(id) ON DELETE CASCADE,
|
||||
rdo_id UUID NOT NULL REFERENCES public.rdos(id) ON DELETE CASCADE,
|
||||
|
||||
tipo_atividade VARCHAR(100) NOT NULL,
|
||||
descricao TEXT NOT NULL,
|
||||
localizacao VARCHAR(255),
|
||||
percentual_concluido DECIMAL(5,2) DEFAULT 0 CHECK (percentual_concluido >= 0 AND percentual_concluido <= 100),
|
||||
ordem INTEGER DEFAULT 0,
|
||||
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_rdo_atividades_org ON public.rdo_atividades(organizacao_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_rdo_atividades_rdo ON public.rdo_atividades(rdo_id);
|
||||
|
||||
-- 7.2 Mão de Obra
|
||||
CREATE TABLE IF NOT EXISTS public.rdo_mao_obra (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
organizacao_id UUID NOT NULL REFERENCES public.organizacoes(id) ON DELETE CASCADE,
|
||||
rdo_id UUID NOT NULL REFERENCES public.rdos(id) ON DELETE CASCADE,
|
||||
|
||||
nome VARCHAR(255) NOT NULL,
|
||||
funcao VARCHAR(100) NOT NULL,
|
||||
quantidade INTEGER DEFAULT 1,
|
||||
horas_trabalhadas DECIMAL(5,2) DEFAULT 8,
|
||||
observacoes TEXT,
|
||||
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_rdo_mao_obra_org ON public.rdo_mao_obra(organizacao_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_rdo_mao_obra_rdo ON public.rdo_mao_obra(rdo_id);
|
||||
|
||||
-- 7.3 Equipamentos
|
||||
CREATE TABLE IF NOT EXISTS public.rdo_equipamentos (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
organizacao_id UUID NOT NULL REFERENCES public.organizacoes(id) ON DELETE CASCADE,
|
||||
rdo_id UUID NOT NULL REFERENCES public.rdos(id) ON DELETE CASCADE,
|
||||
|
||||
nome_equipamento VARCHAR(255) NOT NULL,
|
||||
tipo VARCHAR(100),
|
||||
horas_utilizadas DECIMAL(5,2) DEFAULT 0,
|
||||
combustivel_gasto DECIMAL(10,2) DEFAULT 0,
|
||||
observacoes TEXT,
|
||||
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_rdo_equipamentos_org ON public.rdo_equipamentos(organizacao_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_rdo_equipamentos_rdo ON public.rdo_equipamentos(rdo_id);
|
||||
|
||||
-- 7.4 Ocorrências
|
||||
CREATE TABLE IF NOT EXISTS public.rdo_ocorrencias (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
organizacao_id UUID NOT NULL REFERENCES public.organizacoes(id) ON DELETE CASCADE,
|
||||
rdo_id UUID NOT NULL REFERENCES public.rdos(id) ON DELETE CASCADE,
|
||||
|
||||
tipo_ocorrencia VARCHAR(100) NOT NULL,
|
||||
descricao TEXT NOT NULL,
|
||||
gravidade VARCHAR(50) DEFAULT 'media' CHECK (gravidade IN ('baixa', 'media', 'alta', 'critica')),
|
||||
acao_tomada TEXT,
|
||||
resolvida BOOLEAN DEFAULT false,
|
||||
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_rdo_ocorrencias_org ON public.rdo_ocorrencias(organizacao_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_rdo_ocorrencias_rdo ON public.rdo_ocorrencias(rdo_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_rdo_ocorrencias_gravidade ON public.rdo_ocorrencias(gravidade);
|
||||
|
||||
-- 7.5 Anexos (Fotos e Documentos)
|
||||
CREATE TABLE IF NOT EXISTS public.rdo_anexos (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
organizacao_id UUID NOT NULL REFERENCES public.organizacoes(id) ON DELETE CASCADE,
|
||||
rdo_id UUID NOT NULL REFERENCES public.rdos(id) ON DELETE CASCADE,
|
||||
|
||||
nome_arquivo VARCHAR(255) NOT NULL,
|
||||
tipo_arquivo VARCHAR(100),
|
||||
url_storage TEXT NOT NULL,
|
||||
tamanho_bytes BIGINT,
|
||||
descricao TEXT,
|
||||
ordem INTEGER DEFAULT 0,
|
||||
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_rdo_anexos_org ON public.rdo_anexos(organizacao_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_rdo_anexos_rdo ON public.rdo_anexos(rdo_id);
|
||||
|
||||
-- 7.6 Inspeções de Solda (específico para estruturas metálicas)
|
||||
CREATE TABLE IF NOT EXISTS public.rdo_inspecoes_solda (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
organizacao_id UUID NOT NULL REFERENCES public.organizacoes(id) ON DELETE CASCADE,
|
||||
rdo_id UUID NOT NULL REFERENCES public.rdos(id) ON DELETE CASCADE,
|
||||
|
||||
identificacao_junta VARCHAR(100) NOT NULL,
|
||||
status_inspecao VARCHAR(50) DEFAULT 'pendente' CHECK (status_inspecao IN ('aprovado', 'reprovado', 'pendente')),
|
||||
metodo_inspecao VARCHAR(100),
|
||||
observacoes TEXT,
|
||||
inspecionado_por VARCHAR(255),
|
||||
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_rdo_inspecoes_org ON public.rdo_inspecoes_solda(organizacao_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_rdo_inspecoes_rdo ON public.rdo_inspecoes_solda(rdo_id);
|
||||
|
||||
-- 7.7 Verificações de Torque (específico para estruturas metálicas)
|
||||
CREATE TABLE IF NOT EXISTS public.rdo_verificacoes_torque (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
organizacao_id UUID NOT NULL REFERENCES public.organizacoes(id) ON DELETE CASCADE,
|
||||
rdo_id UUID NOT NULL REFERENCES public.rdos(id) ON DELETE CASCADE,
|
||||
|
||||
identificacao_parafuso VARCHAR(100) NOT NULL,
|
||||
torque_especificado DECIMAL(10,2) DEFAULT 0,
|
||||
torque_aplicado DECIMAL(10,2) NOT NULL,
|
||||
status_verificacao VARCHAR(50) DEFAULT 'conforme' CHECK (status_verificacao IN ('conforme', 'nao_conforme')),
|
||||
observacoes TEXT,
|
||||
verificado_por VARCHAR(255),
|
||||
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_rdo_verificacoes_org ON public.rdo_verificacoes_torque(organizacao_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_rdo_verificacoes_rdo ON public.rdo_verificacoes_torque(rdo_id);
|
||||
|
||||
-- ========================================
|
||||
-- 8. TABELA DE TAREFAS
|
||||
-- ========================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.tarefas (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
organizacao_id UUID NOT NULL REFERENCES public.organizacoes(id) ON DELETE CASCADE,
|
||||
obra_id UUID NOT NULL REFERENCES public.obras(id) ON DELETE CASCADE,
|
||||
|
||||
titulo VARCHAR(255) NOT NULL,
|
||||
descricao TEXT,
|
||||
|
||||
status VARCHAR(50) DEFAULT 'pendente' CHECK (status IN ('pendente', 'em_andamento', 'concluida', 'cancelada')),
|
||||
prioridade VARCHAR(50) DEFAULT 'media' CHECK (prioridade IN ('baixa', 'media', 'alta', 'urgente')),
|
||||
|
||||
responsavel_id UUID REFERENCES public.usuarios(id),
|
||||
|
||||
data_inicio DATE,
|
||||
data_fim DATE,
|
||||
progresso DECIMAL(5,2) DEFAULT 0 CHECK (progresso >= 0 AND progresso <= 100),
|
||||
|
||||
metadados JSONB DEFAULT '{}'::jsonb,
|
||||
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_tarefas_org ON public.tarefas(organizacao_id, id);
|
||||
CREATE INDEX IF NOT EXISTS idx_tarefas_obra ON public.tarefas(obra_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_tarefas_responsavel ON public.tarefas(responsavel_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_tarefas_status ON public.tarefas(status);
|
||||
|
||||
-- ========================================
|
||||
-- 9. TABELA DE LOGS DE TAREFAS
|
||||
-- ========================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.task_logs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
organizacao_id UUID NOT NULL REFERENCES public.organizacoes(id) ON DELETE CASCADE,
|
||||
task_id UUID NOT NULL REFERENCES public.tarefas(id) ON DELETE CASCADE,
|
||||
usuario_id UUID NOT NULL REFERENCES public.usuarios(id),
|
||||
|
||||
tipo_evento VARCHAR(50) NOT NULL CHECK (tipo_evento IN ('inicio', 'pausa', 'retomada', 'conclusao', 'revisao', 'edicao', 'cancelamento')),
|
||||
descricao TEXT,
|
||||
detalhes JSONB DEFAULT '{}'::jsonb,
|
||||
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_task_logs_org ON public.task_logs(organizacao_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_task_logs_task ON public.task_logs(task_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_task_logs_usuario ON public.task_logs(usuario_id);
|
||||
|
||||
-- ========================================
|
||||
-- 10. TABELA DE MÉTRICAS E USO
|
||||
-- ========================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.organizacao_metricas (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
organizacao_id UUID NOT NULL REFERENCES public.organizacoes(id) ON DELETE CASCADE,
|
||||
mes_referencia DATE NOT NULL,
|
||||
|
||||
-- Contadores
|
||||
total_usuarios INTEGER DEFAULT 0,
|
||||
total_obras INTEGER DEFAULT 0,
|
||||
total_rdos INTEGER DEFAULT 0,
|
||||
storage_usado_mb DECIMAL(10,2) DEFAULT 0,
|
||||
|
||||
-- Limites do plano
|
||||
limite_usuarios INTEGER,
|
||||
limite_obras INTEGER,
|
||||
limite_rdos_mes INTEGER,
|
||||
limite_storage_mb INTEGER,
|
||||
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
|
||||
UNIQUE(organizacao_id, mes_referencia)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_org_metricas_org ON public.organizacao_metricas(organizacao_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_org_metricas_mes ON public.organizacao_metricas(mes_referencia);
|
||||
|
||||
-- ========================================
|
||||
-- COMENTÁRIOS PARA DOCUMENTAÇÃO
|
||||
-- ========================================
|
||||
|
||||
COMMENT ON TABLE public.organizacoes IS 'Organizações/empresas (tenants) do sistema SaaS';
|
||||
COMMENT ON TABLE public.usuarios IS 'Usuários do sistema vinculados a organizações';
|
||||
COMMENT ON TABLE public.organizacao_usuarios IS 'Relacionamento entre usuários e organizações com roles';
|
||||
COMMENT ON TABLE public.convites IS 'Convites pendentes para novos usuários';
|
||||
COMMENT ON TABLE public.obras IS 'Obras/projetos de construção';
|
||||
COMMENT ON TABLE public.rdos IS 'Relatórios Diários de Obra';
|
||||
COMMENT ON TABLE public.rdo_atividades IS 'Atividades executadas registradas no RDO';
|
||||
COMMENT ON TABLE public.rdo_mao_obra IS 'Mão de obra presente no dia do RDO';
|
||||
COMMENT ON TABLE public.rdo_equipamentos IS 'Equipamentos utilizados no dia do RDO';
|
||||
COMMENT ON TABLE public.rdo_ocorrencias IS 'Ocorrências e problemas reportados no RDO';
|
||||
COMMENT ON TABLE public.rdo_anexos IS 'Fotos e documentos anexados ao RDO';
|
||||
COMMENT ON TABLE public.rdo_inspecoes_solda IS 'Inspeções de solda para estruturas metálicas';
|
||||
COMMENT ON TABLE public.rdo_verificacoes_torque IS 'Verificações de torque de parafusos';
|
||||
COMMENT ON TABLE public.tarefas IS 'Tarefas planejadas para as obras';
|
||||
COMMENT ON TABLE public.task_logs IS 'Histórico de eventos das tarefas';
|
||||
COMMENT ON TABLE public.organizacao_metricas IS 'Métricas de uso por organização';
|
||||
@@ -0,0 +1,405 @@
|
||||
-- ========================================
|
||||
-- MIGRATION: Functions and Triggers (FIXED)
|
||||
-- Data: 2024-12-02
|
||||
-- Descrição: Funções auxiliares e triggers automáticos
|
||||
-- ========================================
|
||||
|
||||
-- ========================================
|
||||
-- 1. FUNÇÃO PARA ATUALIZAR UPDATED_AT
|
||||
-- ========================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = NOW();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Aplicar trigger em todas as tabelas relevantes
|
||||
CREATE TRIGGER update_organizacoes_updated_at BEFORE UPDATE ON public.organizacoes
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
CREATE TRIGGER update_usuarios_updated_at BEFORE UPDATE ON public.usuarios
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
CREATE TRIGGER update_organizacao_usuarios_updated_at BEFORE UPDATE ON public.organizacao_usuarios
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
CREATE TRIGGER update_obras_updated_at BEFORE UPDATE ON public.obras
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
CREATE TRIGGER update_rdos_updated_at BEFORE UPDATE ON public.rdos
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
CREATE TRIGGER update_tarefas_updated_at BEFORE UPDATE ON public.tarefas
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- ========================================
|
||||
-- 2. FUNÇÃO PARA CRIAR USUÁRIO AUTOMATICAMENTE
|
||||
-- ========================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.handle_new_user()
|
||||
RETURNS TRIGGER AS $$
|
||||
DECLARE
|
||||
v_org_id UUID;
|
||||
v_nome TEXT;
|
||||
v_email TEXT;
|
||||
BEGIN
|
||||
-- Extrair dados do metadata
|
||||
v_nome := COALESCE(NEW.raw_user_meta_data->>'nome', NEW.email);
|
||||
v_email := NEW.email;
|
||||
v_org_id := (NEW.raw_user_meta_data->>'organizacao_id')::UUID;
|
||||
|
||||
-- Se não tem org_id no metadata, não criar o perfil ainda
|
||||
-- (será criado quando aceitar um convite)
|
||||
IF v_org_id IS NULL THEN
|
||||
RETURN NEW;
|
||||
END IF;
|
||||
|
||||
-- Criar perfil de usuário
|
||||
INSERT INTO public.usuarios (id, organizacao_id, nome, email)
|
||||
VALUES (NEW.id, v_org_id, v_nome, v_email)
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
|
||||
-- Trigger para criar usuário automaticamente
|
||||
DROP TRIGGER IF EXISTS on_auth_user_created ON auth.users;
|
||||
CREATE TRIGGER on_auth_user_created
|
||||
AFTER INSERT ON auth.users
|
||||
FOR EACH ROW EXECUTE FUNCTION public.handle_new_user();
|
||||
|
||||
-- ========================================
|
||||
-- 3. FUNÇÃO PARA AUTO-INCREMENTAR NÚMERO DO RDO
|
||||
-- ========================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.set_rdo_numero()
|
||||
RETURNS TRIGGER AS $$
|
||||
DECLARE
|
||||
v_max_numero INTEGER;
|
||||
BEGIN
|
||||
-- Se já tem número, não fazer nada
|
||||
IF NEW.numero IS NOT NULL THEN
|
||||
RETURN NEW;
|
||||
END IF;
|
||||
|
||||
-- Buscar o maior número para esta obra
|
||||
SELECT COALESCE(MAX(numero), 0) INTO v_max_numero
|
||||
FROM public.rdos
|
||||
WHERE obra_id = NEW.obra_id;
|
||||
|
||||
-- Atribuir próximo número
|
||||
NEW.numero := v_max_numero + 1;
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER set_rdo_numero_trigger
|
||||
BEFORE INSERT ON public.rdos
|
||||
FOR EACH ROW EXECUTE FUNCTION public.set_rdo_numero();
|
||||
|
||||
-- ========================================
|
||||
-- 4. FUNÇÃO PARA PROPAGAR ORGANIZACAO_ID
|
||||
-- ========================================
|
||||
|
||||
-- Quando criar um RDO, copiar organizacao_id da obra
|
||||
CREATE OR REPLACE FUNCTION public.set_rdo_organizacao_id()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF NEW.organizacao_id IS NULL THEN
|
||||
SELECT organizacao_id INTO NEW.organizacao_id
|
||||
FROM public.obras
|
||||
WHERE id = NEW.obra_id;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER set_rdo_organizacao_id_trigger
|
||||
BEFORE INSERT ON public.rdos
|
||||
FOR EACH ROW EXECUTE FUNCTION public.set_rdo_organizacao_id();
|
||||
|
||||
-- Propagar para tabelas relacionadas ao RDO
|
||||
CREATE OR REPLACE FUNCTION public.set_rdo_child_organizacao_id()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF NEW.organizacao_id IS NULL THEN
|
||||
SELECT organizacao_id INTO NEW.organizacao_id
|
||||
FROM public.rdos
|
||||
WHERE id = NEW.rdo_id;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER set_rdo_atividades_org_trigger
|
||||
BEFORE INSERT ON public.rdo_atividades
|
||||
FOR EACH ROW EXECUTE FUNCTION public.set_rdo_child_organizacao_id();
|
||||
|
||||
CREATE TRIGGER set_rdo_mao_obra_org_trigger
|
||||
BEFORE INSERT ON public.rdo_mao_obra
|
||||
FOR EACH ROW EXECUTE FUNCTION public.set_rdo_child_organizacao_id();
|
||||
|
||||
CREATE TRIGGER set_rdo_equipamentos_org_trigger
|
||||
BEFORE INSERT ON public.rdo_equipamentos
|
||||
FOR EACH ROW EXECUTE FUNCTION public.set_rdo_child_organizacao_id();
|
||||
|
||||
CREATE TRIGGER set_rdo_ocorrencias_org_trigger
|
||||
BEFORE INSERT ON public.rdo_ocorrencias
|
||||
FOR EACH ROW EXECUTE FUNCTION public.set_rdo_child_organizacao_id();
|
||||
|
||||
CREATE TRIGGER set_rdo_anexos_org_trigger
|
||||
BEFORE INSERT ON public.rdo_anexos
|
||||
FOR EACH ROW EXECUTE FUNCTION public.set_rdo_child_organizacao_id();
|
||||
|
||||
CREATE TRIGGER set_rdo_inspecoes_org_trigger
|
||||
BEFORE INSERT ON public.rdo_inspecoes_solda
|
||||
FOR EACH ROW EXECUTE FUNCTION public.set_rdo_child_organizacao_id();
|
||||
|
||||
CREATE TRIGGER set_rdo_verificacoes_org_trigger
|
||||
BEFORE INSERT ON public.rdo_verificacoes_torque
|
||||
FOR EACH ROW EXECUTE FUNCTION public.set_rdo_child_organizacao_id();
|
||||
|
||||
-- Propagar para tarefas
|
||||
CREATE OR REPLACE FUNCTION public.set_tarefa_organizacao_id()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF NEW.organizacao_id IS NULL THEN
|
||||
SELECT organizacao_id INTO NEW.organizacao_id
|
||||
FROM public.obras
|
||||
WHERE id = NEW.obra_id;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER set_tarefa_organizacao_id_trigger
|
||||
BEFORE INSERT ON public.tarefas
|
||||
FOR EACH ROW EXECUTE FUNCTION public.set_tarefa_organizacao_id();
|
||||
|
||||
-- Propagar para task_logs
|
||||
CREATE OR REPLACE FUNCTION public.set_task_log_organizacao_id()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF NEW.organizacao_id IS NULL THEN
|
||||
SELECT organizacao_id INTO NEW.organizacao_id
|
||||
FROM public.tarefas
|
||||
WHERE id = NEW.task_id;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER set_task_log_organizacao_id_trigger
|
||||
BEFORE INSERT ON public.task_logs
|
||||
FOR EACH ROW EXECUTE FUNCTION public.set_task_log_organizacao_id();
|
||||
|
||||
-- ========================================
|
||||
-- 5. FUNÇÃO PARA ATUALIZAR MÉTRICAS
|
||||
-- ========================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.atualizar_metricas_organizacao()
|
||||
RETURNS TRIGGER AS $$
|
||||
DECLARE
|
||||
v_org_id UUID;
|
||||
v_mes_ref DATE;
|
||||
BEGIN
|
||||
-- Determinar organizacao_id baseado na operação
|
||||
IF TG_OP = 'DELETE' THEN
|
||||
v_org_id := OLD.organizacao_id;
|
||||
ELSE
|
||||
v_org_id := NEW.organizacao_id;
|
||||
END IF;
|
||||
|
||||
v_mes_ref := DATE_TRUNC('month', CURRENT_DATE);
|
||||
|
||||
-- Inserir ou atualizar métricas
|
||||
INSERT INTO public.organizacao_metricas (
|
||||
organizacao_id,
|
||||
mes_referencia,
|
||||
total_usuarios,
|
||||
total_obras,
|
||||
total_rdos,
|
||||
limite_usuarios,
|
||||
limite_obras,
|
||||
limite_rdos_mes,
|
||||
limite_storage_mb
|
||||
)
|
||||
SELECT
|
||||
v_org_id,
|
||||
v_mes_ref,
|
||||
(SELECT COUNT(*) FROM public.usuarios WHERE organizacao_id = v_org_id AND ativo = true),
|
||||
(SELECT COUNT(*) FROM public.obras WHERE organizacao_id = v_org_id),
|
||||
(SELECT COUNT(*) FROM public.rdos WHERE organizacao_id = v_org_id AND DATE_TRUNC('month', created_at) = v_mes_ref),
|
||||
o.max_usuarios,
|
||||
o.max_obras,
|
||||
o.max_rdos_mes,
|
||||
o.max_storage_mb
|
||||
FROM public.organizacoes o
|
||||
WHERE o.id = v_org_id
|
||||
ON CONFLICT (organizacao_id, mes_referencia)
|
||||
DO UPDATE SET
|
||||
total_usuarios = EXCLUDED.total_usuarios,
|
||||
total_obras = EXCLUDED.total_obras,
|
||||
total_rdos = EXCLUDED.total_rdos,
|
||||
limite_usuarios = EXCLUDED.limite_usuarios,
|
||||
limite_obras = EXCLUDED.limite_obras,
|
||||
limite_rdos_mes = EXCLUDED.limite_rdos_mes,
|
||||
limite_storage_mb = EXCLUDED.limite_storage_mb,
|
||||
updated_at = NOW();
|
||||
|
||||
IF TG_OP = 'DELETE' THEN
|
||||
RETURN OLD;
|
||||
ELSE
|
||||
RETURN NEW;
|
||||
END IF;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Triggers para atualizar métricas
|
||||
CREATE TRIGGER atualizar_metricas_usuarios
|
||||
AFTER INSERT OR UPDATE OR DELETE ON public.usuarios
|
||||
FOR EACH ROW EXECUTE FUNCTION public.atualizar_metricas_organizacao();
|
||||
|
||||
CREATE TRIGGER atualizar_metricas_obras
|
||||
AFTER INSERT OR DELETE ON public.obras
|
||||
FOR EACH ROW EXECUTE FUNCTION public.atualizar_metricas_organizacao();
|
||||
|
||||
CREATE TRIGGER atualizar_metricas_rdos
|
||||
AFTER INSERT OR DELETE ON public.rdos
|
||||
FOR EACH ROW EXECUTE FUNCTION public.atualizar_metricas_organizacao();
|
||||
|
||||
-- ========================================
|
||||
-- 6. FUNÇÃO PARA VERIFICAR LIMITES DO PLANO
|
||||
-- ========================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.verificar_limite_usuarios()
|
||||
RETURNS TRIGGER AS $$
|
||||
DECLARE
|
||||
v_total_usuarios INTEGER;
|
||||
v_limite INTEGER;
|
||||
BEGIN
|
||||
-- Contar usuários ativos da organização
|
||||
SELECT COUNT(*) INTO v_total_usuarios
|
||||
FROM public.usuarios
|
||||
WHERE organizacao_id = NEW.organizacao_id AND ativo = true;
|
||||
|
||||
-- Buscar limite do plano
|
||||
SELECT max_usuarios INTO v_limite
|
||||
FROM public.organizacoes
|
||||
WHERE id = NEW.organizacao_id;
|
||||
|
||||
-- Verificar se excedeu o limite
|
||||
IF v_total_usuarios >= v_limite THEN
|
||||
RAISE EXCEPTION 'Limite de usuários atingido para esta organização. Plano atual permite % usuários.', v_limite;
|
||||
END IF;
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER verificar_limite_usuarios_trigger
|
||||
BEFORE INSERT ON public.usuarios
|
||||
FOR EACH ROW EXECUTE FUNCTION public.verificar_limite_usuarios();
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.verificar_limite_obras()
|
||||
RETURNS TRIGGER AS $$
|
||||
DECLARE
|
||||
v_total_obras INTEGER;
|
||||
v_limite INTEGER;
|
||||
BEGIN
|
||||
-- Contar obras da organização
|
||||
SELECT COUNT(*) INTO v_total_obras
|
||||
FROM public.obras
|
||||
WHERE organizacao_id = NEW.organizacao_id;
|
||||
|
||||
-- Buscar limite do plano
|
||||
SELECT max_obras INTO v_limite
|
||||
FROM public.organizacoes
|
||||
WHERE id = NEW.organizacao_id;
|
||||
|
||||
-- Verificar se excedeu o limite
|
||||
IF v_total_obras >= v_limite THEN
|
||||
RAISE EXCEPTION 'Limite de obras atingido para esta organização. Plano atual permite % obras.', v_limite;
|
||||
END IF;
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER verificar_limite_obras_trigger
|
||||
BEFORE INSERT ON public.obras
|
||||
FOR EACH ROW EXECUTE FUNCTION public.verificar_limite_obras();
|
||||
|
||||
-- ========================================
|
||||
-- 7. FUNÇÕES AUXILIARES PARA QUERIES
|
||||
-- ========================================
|
||||
|
||||
-- Função para obter role do usuário em uma organização
|
||||
CREATE OR REPLACE FUNCTION public.get_user_role(p_user_id UUID, p_org_id UUID)
|
||||
RETURNS TEXT AS $$
|
||||
BEGIN
|
||||
RETURN (
|
||||
SELECT role
|
||||
FROM public.organizacao_usuarios
|
||||
WHERE usuario_id = p_user_id
|
||||
AND organizacao_id = p_org_id
|
||||
AND ativo = true
|
||||
);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
|
||||
-- Função para verificar se usuário tem permissão
|
||||
CREATE OR REPLACE FUNCTION public.user_has_permission(
|
||||
p_user_id UUID,
|
||||
p_org_id UUID,
|
||||
p_permissao TEXT
|
||||
)
|
||||
RETURNS BOOLEAN AS $$
|
||||
DECLARE
|
||||
v_role TEXT;
|
||||
BEGIN
|
||||
v_role := public.get_user_role(p_user_id, p_org_id);
|
||||
|
||||
-- Lógica de permissões baseada em role
|
||||
RETURN CASE
|
||||
WHEN v_role = 'owner' THEN true
|
||||
WHEN v_role = 'admin' THEN true
|
||||
WHEN v_role = 'engenheiro' AND p_permissao IN ('criar_rdo', 'aprovar_rdo', 'criar_obra', 'editar_obra') THEN true
|
||||
WHEN v_role = 'mestre_obra' AND p_permissao IN ('criar_rdo', 'editar_rdo') THEN true
|
||||
WHEN v_role = 'usuario' AND p_permissao IN ('visualizar_rdo', 'visualizar_obra') THEN true
|
||||
ELSE false
|
||||
END;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
|
||||
-- Função para obter organizacao_id do usuário atual
|
||||
CREATE OR REPLACE FUNCTION public.get_current_user_org_id()
|
||||
RETURNS UUID AS $$
|
||||
BEGIN
|
||||
RETURN (
|
||||
SELECT organizacao_id
|
||||
FROM public.usuarios
|
||||
WHERE id = auth.uid()
|
||||
LIMIT 1
|
||||
);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
|
||||
-- ========================================
|
||||
-- COMENTÁRIOS
|
||||
-- ========================================
|
||||
|
||||
COMMENT ON FUNCTION update_updated_at_column() IS 'Atualiza automaticamente o campo updated_at';
|
||||
COMMENT ON FUNCTION handle_new_user() IS 'Cria perfil de usuário automaticamente após registro no auth';
|
||||
COMMENT ON FUNCTION set_rdo_numero() IS 'Define automaticamente o número sequencial do RDO por obra';
|
||||
COMMENT ON FUNCTION atualizar_metricas_organizacao() IS 'Atualiza métricas de uso da organização';
|
||||
COMMENT ON FUNCTION verificar_limite_usuarios() IS 'Verifica se organização não excedeu limite de usuários do plano';
|
||||
COMMENT ON FUNCTION verificar_limite_obras() IS 'Verifica se organização não excedeu limite de obras do plano';
|
||||
COMMENT ON FUNCTION get_user_role(UUID, UUID) IS 'Retorna o role do usuário em uma organização';
|
||||
COMMENT ON FUNCTION user_has_permission(UUID, UUID, TEXT) IS 'Verifica se usuário tem uma permissão específica';
|
||||
COMMENT ON FUNCTION get_current_user_org_id() IS 'Retorna o organizacao_id do usuário autenticado';
|
||||
452
supabase/migrations/20241202000003_create_rls_policies.sql
Normal file
452
supabase/migrations/20241202000003_create_rls_policies.sql
Normal file
@@ -0,0 +1,452 @@
|
||||
-- ========================================
|
||||
-- MIGRATION: Row Level Security Policies
|
||||
-- Data: 2024-12-02
|
||||
-- Descrição: Políticas RLS para isolamento multi-tenant
|
||||
-- ========================================
|
||||
|
||||
-- ========================================
|
||||
-- HABILITAR RLS EM TODAS AS TABELAS
|
||||
-- ========================================
|
||||
|
||||
ALTER TABLE public.organizacoes ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.usuarios ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.organizacao_usuarios ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.convites ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.obras ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.rdos ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.rdo_atividades ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.rdo_mao_obra ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.rdo_equipamentos ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.rdo_ocorrencias ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.rdo_anexos ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.rdo_inspecoes_solda ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.rdo_verificacoes_torque ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.tarefas ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.task_logs ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.organizacao_metricas ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- ========================================
|
||||
-- POLÍTICAS PARA ORGANIZAÇÕES
|
||||
-- ========================================
|
||||
|
||||
-- Usuários podem ver organizações onde são membros
|
||||
CREATE POLICY "Usuários veem suas organizações" ON public.organizacoes
|
||||
FOR SELECT USING (
|
||||
id IN (
|
||||
SELECT organizacao_id
|
||||
FROM public.organizacao_usuarios
|
||||
WHERE usuario_id = auth.uid() AND ativo = true
|
||||
)
|
||||
);
|
||||
|
||||
-- Apenas owners podem atualizar organização
|
||||
CREATE POLICY "Owners podem atualizar organização" ON public.organizacoes
|
||||
FOR UPDATE USING (
|
||||
id IN (
|
||||
SELECT organizacao_id
|
||||
FROM public.organizacao_usuarios
|
||||
WHERE usuario_id = auth.uid()
|
||||
AND role = 'owner'
|
||||
AND ativo = true
|
||||
)
|
||||
);
|
||||
|
||||
-- Qualquer usuário autenticado pode criar organização (signup)
|
||||
CREATE POLICY "Usuários autenticados podem criar organização" ON public.organizacoes
|
||||
FOR INSERT WITH CHECK (auth.uid() IS NOT NULL);
|
||||
|
||||
-- ========================================
|
||||
-- POLÍTICAS PARA USUÁRIOS
|
||||
-- ========================================
|
||||
|
||||
-- Usuários veem outros usuários da mesma organização
|
||||
CREATE POLICY "Usuários veem membros da organização" ON public.usuarios
|
||||
FOR SELECT USING (
|
||||
organizacao_id IN (
|
||||
SELECT organizacao_id
|
||||
FROM public.organizacao_usuarios
|
||||
WHERE usuario_id = auth.uid() AND ativo = true
|
||||
)
|
||||
);
|
||||
|
||||
-- Usuários podem atualizar próprio perfil
|
||||
CREATE POLICY "Usuários podem atualizar próprio perfil" ON public.usuarios
|
||||
FOR UPDATE USING (id = auth.uid());
|
||||
|
||||
-- Admins e owners podem inserir novos usuários
|
||||
CREATE POLICY "Admins podem criar usuários" ON public.usuarios
|
||||
FOR INSERT WITH CHECK (
|
||||
organizacao_id IN (
|
||||
SELECT organizacao_id
|
||||
FROM public.organizacao_usuarios
|
||||
WHERE usuario_id = auth.uid()
|
||||
AND role IN ('owner', 'admin')
|
||||
AND ativo = true
|
||||
)
|
||||
);
|
||||
|
||||
-- ========================================
|
||||
-- POLÍTICAS PARA ORGANIZACAO_USUARIOS
|
||||
-- ========================================
|
||||
|
||||
-- Usuários veem membros da própria organização
|
||||
CREATE POLICY "Ver membros da organização" ON public.organizacao_usuarios
|
||||
FOR SELECT USING (
|
||||
organizacao_id IN (
|
||||
SELECT organizacao_id
|
||||
FROM public.organizacao_usuarios
|
||||
WHERE usuario_id = auth.uid() AND ativo = true
|
||||
)
|
||||
);
|
||||
|
||||
-- Owners e admins podem gerenciar membros
|
||||
CREATE POLICY "Admins gerenciam membros" ON public.organizacao_usuarios
|
||||
FOR ALL USING (
|
||||
organizacao_id IN (
|
||||
SELECT organizacao_id
|
||||
FROM public.organizacao_usuarios
|
||||
WHERE usuario_id = auth.uid()
|
||||
AND role IN ('owner', 'admin')
|
||||
AND ativo = true
|
||||
)
|
||||
);
|
||||
|
||||
-- ========================================
|
||||
-- POLÍTICAS PARA CONVITES
|
||||
-- ========================================
|
||||
|
||||
-- Membros da organização veem convites
|
||||
CREATE POLICY "Ver convites da organização" ON public.convites
|
||||
FOR SELECT USING (
|
||||
organizacao_id IN (
|
||||
SELECT organizacao_id
|
||||
FROM public.organizacao_usuarios
|
||||
WHERE usuario_id = auth.uid() AND ativo = true
|
||||
)
|
||||
);
|
||||
|
||||
-- Admins podem criar convites
|
||||
CREATE POLICY "Admins criam convites" ON public.convites
|
||||
FOR INSERT WITH CHECK (
|
||||
organizacao_id IN (
|
||||
SELECT organizacao_id
|
||||
FROM public.organizacao_usuarios
|
||||
WHERE usuario_id = auth.uid()
|
||||
AND role IN ('owner', 'admin')
|
||||
AND ativo = true
|
||||
)
|
||||
);
|
||||
|
||||
-- Admins podem atualizar/cancelar convites
|
||||
CREATE POLICY "Admins gerenciam convites" ON public.convites
|
||||
FOR UPDATE USING (
|
||||
organizacao_id IN (
|
||||
SELECT organizacao_id
|
||||
FROM public.organizacao_usuarios
|
||||
WHERE usuario_id = auth.uid()
|
||||
AND role IN ('owner', 'admin')
|
||||
AND ativo = true
|
||||
)
|
||||
);
|
||||
|
||||
-- ========================================
|
||||
-- POLÍTICAS PARA OBRAS
|
||||
-- ========================================
|
||||
|
||||
-- Usuários veem obras da organização
|
||||
CREATE POLICY "Ver obras da organização" ON public.obras
|
||||
FOR SELECT USING (
|
||||
organizacao_id IN (
|
||||
SELECT organizacao_id
|
||||
FROM public.organizacao_usuarios
|
||||
WHERE usuario_id = auth.uid() AND ativo = true
|
||||
)
|
||||
);
|
||||
|
||||
-- Engenheiros, admins e owners podem criar obras
|
||||
CREATE POLICY "Engenheiros criam obras" ON public.obras
|
||||
FOR INSERT WITH CHECK (
|
||||
organizacao_id IN (
|
||||
SELECT organizacao_id
|
||||
FROM public.organizacao_usuarios
|
||||
WHERE usuario_id = auth.uid()
|
||||
AND role IN ('owner', 'admin', 'engenheiro')
|
||||
AND ativo = true
|
||||
)
|
||||
);
|
||||
|
||||
-- Responsáveis, engenheiros, admins e owners podem atualizar obras
|
||||
CREATE POLICY "Responsáveis atualizam obras" ON public.obras
|
||||
FOR UPDATE USING (
|
||||
organizacao_id IN (
|
||||
SELECT organizacao_id
|
||||
FROM public.organizacao_usuarios
|
||||
WHERE usuario_id = auth.uid()
|
||||
AND (
|
||||
role IN ('owner', 'admin', 'engenheiro')
|
||||
OR usuario_id = responsavel_id
|
||||
)
|
||||
AND ativo = true
|
||||
)
|
||||
);
|
||||
|
||||
-- Apenas admins e owners podem deletar obras
|
||||
CREATE POLICY "Admins deletam obras" ON public.obras
|
||||
FOR DELETE USING (
|
||||
organizacao_id IN (
|
||||
SELECT organizacao_id
|
||||
FROM public.organizacao_usuarios
|
||||
WHERE usuario_id = auth.uid()
|
||||
AND role IN ('owner', 'admin')
|
||||
AND ativo = true
|
||||
)
|
||||
);
|
||||
|
||||
-- ========================================
|
||||
-- POLÍTICAS PARA RDOs
|
||||
-- ========================================
|
||||
|
||||
-- Usuários veem RDOs da organização
|
||||
CREATE POLICY "Ver RDOs da organização" ON public.rdos
|
||||
FOR SELECT USING (
|
||||
organizacao_id IN (
|
||||
SELECT organizacao_id
|
||||
FROM public.organizacao_usuarios
|
||||
WHERE usuario_id = auth.uid() AND ativo = true
|
||||
)
|
||||
);
|
||||
|
||||
-- Usuários podem criar RDOs em obras da organização
|
||||
CREATE POLICY "Criar RDOs" ON public.rdos
|
||||
FOR INSERT WITH CHECK (
|
||||
organizacao_id IN (
|
||||
SELECT organizacao_id
|
||||
FROM public.organizacao_usuarios
|
||||
WHERE usuario_id = auth.uid() AND ativo = true
|
||||
)
|
||||
AND criado_por = auth.uid()
|
||||
);
|
||||
|
||||
-- Criadores podem atualizar próprios RDOs em rascunho
|
||||
-- Engenheiros e admins podem atualizar qualquer RDO
|
||||
CREATE POLICY "Atualizar RDOs" ON public.rdos
|
||||
FOR UPDATE USING (
|
||||
organizacao_id IN (
|
||||
SELECT organizacao_id
|
||||
FROM public.organizacao_usuarios
|
||||
WHERE usuario_id = auth.uid()
|
||||
AND (
|
||||
role IN ('owner', 'admin', 'engenheiro')
|
||||
OR (criado_por = auth.uid() AND status = 'rascunho')
|
||||
)
|
||||
AND ativo = true
|
||||
)
|
||||
);
|
||||
|
||||
-- Apenas admins podem deletar RDOs
|
||||
CREATE POLICY "Admins deletam RDOs" ON public.rdos
|
||||
FOR DELETE USING (
|
||||
organizacao_id IN (
|
||||
SELECT organizacao_id
|
||||
FROM public.organizacao_usuarios
|
||||
WHERE usuario_id = auth.uid()
|
||||
AND role IN ('owner', 'admin')
|
||||
AND ativo = true
|
||||
)
|
||||
);
|
||||
|
||||
-- ========================================
|
||||
-- POLÍTICAS PARA TABELAS RELACIONADAS AO RDO
|
||||
-- ========================================
|
||||
|
||||
-- Política genérica para todas as tabelas filhas do RDO
|
||||
-- Acesso baseado no acesso ao RDO pai
|
||||
|
||||
CREATE POLICY "Acesso via RDO - atividades" ON public.rdo_atividades
|
||||
FOR ALL USING (
|
||||
organizacao_id IN (
|
||||
SELECT organizacao_id
|
||||
FROM public.organizacao_usuarios
|
||||
WHERE usuario_id = auth.uid() AND ativo = true
|
||||
)
|
||||
);
|
||||
|
||||
CREATE POLICY "Acesso via RDO - mao_obra" ON public.rdo_mao_obra
|
||||
FOR ALL USING (
|
||||
organizacao_id IN (
|
||||
SELECT organizacao_id
|
||||
FROM public.organizacao_usuarios
|
||||
WHERE usuario_id = auth.uid() AND ativo = true
|
||||
)
|
||||
);
|
||||
|
||||
CREATE POLICY "Acesso via RDO - equipamentos" ON public.rdo_equipamentos
|
||||
FOR ALL USING (
|
||||
organizacao_id IN (
|
||||
SELECT organizacao_id
|
||||
FROM public.organizacao_usuarios
|
||||
WHERE usuario_id = auth.uid() AND ativo = true
|
||||
)
|
||||
);
|
||||
|
||||
CREATE POLICY "Acesso via RDO - ocorrencias" ON public.rdo_ocorrencias
|
||||
FOR ALL USING (
|
||||
organizacao_id IN (
|
||||
SELECT organizacao_id
|
||||
FROM public.organizacao_usuarios
|
||||
WHERE usuario_id = auth.uid() AND ativo = true
|
||||
)
|
||||
);
|
||||
|
||||
CREATE POLICY "Acesso via RDO - anexos" ON public.rdo_anexos
|
||||
FOR ALL USING (
|
||||
organizacao_id IN (
|
||||
SELECT organizacao_id
|
||||
FROM public.organizacao_usuarios
|
||||
WHERE usuario_id = auth.uid() AND ativo = true
|
||||
)
|
||||
);
|
||||
|
||||
CREATE POLICY "Acesso via RDO - inspecoes" ON public.rdo_inspecoes_solda
|
||||
FOR ALL USING (
|
||||
organizacao_id IN (
|
||||
SELECT organizacao_id
|
||||
FROM public.organizacao_usuarios
|
||||
WHERE usuario_id = auth.uid() AND ativo = true
|
||||
)
|
||||
);
|
||||
|
||||
CREATE POLICY "Acesso via RDO - verificacoes" ON public.rdo_verificacoes_torque
|
||||
FOR ALL USING (
|
||||
organizacao_id IN (
|
||||
SELECT organizacao_id
|
||||
FROM public.organizacao_usuarios
|
||||
WHERE usuario_id = auth.uid() AND ativo = true
|
||||
)
|
||||
);
|
||||
|
||||
-- ========================================
|
||||
-- POLÍTICAS PARA TAREFAS
|
||||
-- ========================================
|
||||
|
||||
-- Usuários veem tarefas da organização
|
||||
CREATE POLICY "Ver tarefas da organização" ON public.tarefas
|
||||
FOR SELECT USING (
|
||||
organizacao_id IN (
|
||||
SELECT organizacao_id
|
||||
FROM public.organizacao_usuarios
|
||||
WHERE usuario_id = auth.uid() AND ativo = true
|
||||
)
|
||||
);
|
||||
|
||||
-- Usuários podem criar tarefas
|
||||
CREATE POLICY "Criar tarefas" ON public.tarefas
|
||||
FOR INSERT WITH CHECK (
|
||||
organizacao_id IN (
|
||||
SELECT organizacao_id
|
||||
FROM public.organizacao_usuarios
|
||||
WHERE usuario_id = auth.uid() AND ativo = true
|
||||
)
|
||||
);
|
||||
|
||||
-- Responsáveis e superiores podem atualizar tarefas
|
||||
CREATE POLICY "Atualizar tarefas" ON public.tarefas
|
||||
FOR UPDATE USING (
|
||||
organizacao_id IN (
|
||||
SELECT organizacao_id
|
||||
FROM public.organizacao_usuarios
|
||||
WHERE usuario_id = auth.uid()
|
||||
AND (
|
||||
role IN ('owner', 'admin', 'engenheiro')
|
||||
OR usuario_id = responsavel_id
|
||||
)
|
||||
AND ativo = true
|
||||
)
|
||||
);
|
||||
|
||||
-- Apenas admins podem deletar tarefas
|
||||
CREATE POLICY "Admins deletam tarefas" ON public.tarefas
|
||||
FOR DELETE USING (
|
||||
organizacao_id IN (
|
||||
SELECT organizacao_id
|
||||
FROM public.organizacao_usuarios
|
||||
WHERE usuario_id = auth.uid()
|
||||
AND role IN ('owner', 'admin')
|
||||
AND ativo = true
|
||||
)
|
||||
);
|
||||
|
||||
-- ========================================
|
||||
-- POLÍTICAS PARA TASK_LOGS
|
||||
-- ========================================
|
||||
|
||||
-- Usuários veem logs de tarefas da organização
|
||||
CREATE POLICY "Ver logs da organização" ON public.task_logs
|
||||
FOR SELECT USING (
|
||||
organizacao_id IN (
|
||||
SELECT organizacao_id
|
||||
FROM public.organizacao_usuarios
|
||||
WHERE usuario_id = auth.uid() AND ativo = true
|
||||
)
|
||||
);
|
||||
|
||||
-- Usuários podem criar logs
|
||||
CREATE POLICY "Criar logs" ON public.task_logs
|
||||
FOR INSERT WITH CHECK (
|
||||
organizacao_id IN (
|
||||
SELECT organizacao_id
|
||||
FROM public.organizacao_usuarios
|
||||
WHERE usuario_id = auth.uid() AND ativo = true
|
||||
)
|
||||
AND usuario_id = auth.uid()
|
||||
);
|
||||
|
||||
-- ========================================
|
||||
-- POLÍTICAS PARA MÉTRICAS
|
||||
-- ========================================
|
||||
|
||||
-- Apenas admins e owners veem métricas
|
||||
CREATE POLICY "Admins veem métricas" ON public.organizacao_metricas
|
||||
FOR SELECT USING (
|
||||
organizacao_id IN (
|
||||
SELECT organizacao_id
|
||||
FROM public.organizacao_usuarios
|
||||
WHERE usuario_id = auth.uid()
|
||||
AND role IN ('owner', 'admin')
|
||||
AND ativo = true
|
||||
)
|
||||
);
|
||||
|
||||
-- Sistema pode atualizar métricas (via triggers)
|
||||
CREATE POLICY "Sistema atualiza métricas" ON public.organizacao_metricas
|
||||
FOR ALL USING (true);
|
||||
|
||||
-- ========================================
|
||||
-- PERMISSÕES PARA ROLES
|
||||
-- ========================================
|
||||
|
||||
-- Garantir permissões para roles anon e authenticated
|
||||
GRANT USAGE ON SCHEMA public TO anon, authenticated;
|
||||
GRANT ALL ON ALL TABLES IN SCHEMA public TO authenticated;
|
||||
GRANT ALL ON ALL SEQUENCES IN SCHEMA public TO authenticated;
|
||||
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA public TO authenticated;
|
||||
|
||||
-- Permissões específicas para anon (apenas leitura limitada)
|
||||
GRANT SELECT ON public.organizacoes TO anon;
|
||||
GRANT SELECT ON public.convites TO anon;
|
||||
|
||||
-- ========================================
|
||||
-- COMENTÁRIOS
|
||||
-- ========================================
|
||||
|
||||
COMMENT ON POLICY "Usuários veem suas organizações" ON public.organizacoes IS
|
||||
'Usuários só podem ver organizações onde são membros ativos';
|
||||
|
||||
COMMENT ON POLICY "Ver obras da organização" ON public.obras IS
|
||||
'Isolamento multi-tenant: usuários só veem obras da própria organização';
|
||||
|
||||
COMMENT ON POLICY "Ver RDOs da organização" ON public.rdos IS
|
||||
'Isolamento multi-tenant: usuários só veem RDOs da própria organização';
|
||||
|
||||
COMMENT ON POLICY "Atualizar RDOs" ON public.rdos IS
|
||||
'Criadores podem editar RDOs em rascunho, engenheiros e admins podem editar qualquer RDO';
|
||||
381
supabase/migrations/20241202000004_seed_initial_data.sql
Normal file
381
supabase/migrations/20241202000004_seed_initial_data.sql
Normal file
@@ -0,0 +1,381 @@
|
||||
-- ========================================
|
||||
-- MIGRATION: Seed Initial Data
|
||||
-- Data: 2024-12-02
|
||||
-- Descrição: Dados iniciais e configurações padrão
|
||||
-- ========================================
|
||||
|
||||
-- ========================================
|
||||
-- 1. CRIAR ORGANIZAÇÃO DEMO (OPCIONAL)
|
||||
-- ========================================
|
||||
|
||||
-- Inserir organização demo para testes
|
||||
INSERT INTO public.organizacoes (
|
||||
slug,
|
||||
nome,
|
||||
razao_social,
|
||||
email_contato,
|
||||
plano,
|
||||
max_usuarios,
|
||||
max_obras,
|
||||
max_rdos_mes,
|
||||
max_storage_mb,
|
||||
status,
|
||||
configuracoes
|
||||
) VALUES (
|
||||
'demo-construcoes',
|
||||
'Demo Construções',
|
||||
'Demo Construções Ltda',
|
||||
'contato@demo.com',
|
||||
'professional',
|
||||
20,
|
||||
10,
|
||||
500,
|
||||
5000,
|
||||
'ativa',
|
||||
'{
|
||||
"tipos_atividade": [
|
||||
"Montagem de Estrutura Metálica",
|
||||
"Soldagem",
|
||||
"Pintura",
|
||||
"Instalação de Telhas",
|
||||
"Fundação",
|
||||
"Concretagem",
|
||||
"Alvenaria",
|
||||
"Instalações Elétricas",
|
||||
"Instalações Hidráulicas",
|
||||
"Acabamento"
|
||||
],
|
||||
"funcoes_mao_obra": [
|
||||
"Engenheiro",
|
||||
"Mestre de Obras",
|
||||
"Soldador",
|
||||
"Montador",
|
||||
"Pintor",
|
||||
"Pedreiro",
|
||||
"Servente",
|
||||
"Eletricista",
|
||||
"Encanador",
|
||||
"Carpinteiro",
|
||||
"Armador"
|
||||
],
|
||||
"tipos_equipamento": [
|
||||
"Guindaste",
|
||||
"Empilhadeira",
|
||||
"Betoneira",
|
||||
"Compressor",
|
||||
"Gerador",
|
||||
"Andaime",
|
||||
"Plataforma Elevatória",
|
||||
"Máquina de Solda",
|
||||
"Esmerilhadeira",
|
||||
"Furadeira"
|
||||
],
|
||||
"condicoes_climaticas": [
|
||||
"Ensolarado",
|
||||
"Parcialmente Nublado",
|
||||
"Nublado",
|
||||
"Chuvisco",
|
||||
"Chuva Leve",
|
||||
"Chuva Forte",
|
||||
"Tempestade",
|
||||
"Neblina"
|
||||
],
|
||||
"tipos_ocorrencia": [
|
||||
"Acidente de Trabalho",
|
||||
"Falta de Material",
|
||||
"Equipamento Quebrado",
|
||||
"Atraso de Fornecedor",
|
||||
"Problema de Qualidade",
|
||||
"Condição Climática Adversa",
|
||||
"Falta de Energia",
|
||||
"Problema de Acesso",
|
||||
"Outro"
|
||||
],
|
||||
"aprovacao_automatica_rdo": false,
|
||||
"notificacoes_email": true,
|
||||
"backup_automatico": true,
|
||||
"campos_rdo_obrigatorios": ["condicoes_climaticas", "observacoes_gerais"]
|
||||
}'::jsonb
|
||||
) ON CONFLICT (slug) DO NOTHING;
|
||||
|
||||
-- ========================================
|
||||
-- 2. CONFIGURAÇÕES PADRÃO PARA NOVAS ORGANIZAÇÕES
|
||||
-- ========================================
|
||||
|
||||
-- Criar função para aplicar configurações padrão
|
||||
CREATE OR REPLACE FUNCTION public.aplicar_configuracoes_padrao()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
-- Se configuracoes está vazio, aplicar padrões
|
||||
IF NEW.configuracoes = '{}'::jsonb THEN
|
||||
NEW.configuracoes := '{
|
||||
"tipos_atividade": [
|
||||
"Montagem de Estrutura Metálica",
|
||||
"Soldagem",
|
||||
"Pintura",
|
||||
"Instalação de Telhas",
|
||||
"Fundação",
|
||||
"Concretagem",
|
||||
"Alvenaria"
|
||||
],
|
||||
"funcoes_mao_obra": [
|
||||
"Engenheiro",
|
||||
"Mestre de Obras",
|
||||
"Soldador",
|
||||
"Montador",
|
||||
"Pedreiro",
|
||||
"Servente"
|
||||
],
|
||||
"tipos_equipamento": [
|
||||
"Guindaste",
|
||||
"Empilhadeira",
|
||||
"Betoneira",
|
||||
"Compressor",
|
||||
"Gerador"
|
||||
],
|
||||
"condicoes_climaticas": [
|
||||
"Ensolarado",
|
||||
"Nublado",
|
||||
"Chuvoso"
|
||||
],
|
||||
"tipos_ocorrencia": [
|
||||
"Acidente de Trabalho",
|
||||
"Falta de Material",
|
||||
"Equipamento Quebrado",
|
||||
"Outro"
|
||||
],
|
||||
"aprovacao_automatica_rdo": false,
|
||||
"notificacoes_email": true,
|
||||
"backup_automatico": true
|
||||
}'::jsonb;
|
||||
END IF;
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER aplicar_configuracoes_padrao_trigger
|
||||
BEFORE INSERT ON public.organizacoes
|
||||
FOR EACH ROW EXECUTE FUNCTION public.aplicar_configuracoes_padrao();
|
||||
|
||||
-- ========================================
|
||||
-- 3. FUNÇÃO PARA CRIAR PRIMEIRA ORGANIZAÇÃO
|
||||
-- ========================================
|
||||
|
||||
-- Função helper para criar organização completa com primeiro usuário
|
||||
CREATE OR REPLACE FUNCTION public.criar_organizacao_com_owner(
|
||||
p_slug VARCHAR(100),
|
||||
p_nome VARCHAR(255),
|
||||
p_email_usuario VARCHAR(255),
|
||||
p_nome_usuario VARCHAR(255),
|
||||
p_user_id UUID
|
||||
)
|
||||
RETURNS UUID AS $$
|
||||
DECLARE
|
||||
v_org_id UUID;
|
||||
BEGIN
|
||||
-- Criar organização
|
||||
INSERT INTO public.organizacoes (slug, nome, email_contato, status)
|
||||
VALUES (p_slug, p_nome, p_email_usuario, 'trial')
|
||||
RETURNING id INTO v_org_id;
|
||||
|
||||
-- Criar usuário
|
||||
INSERT INTO public.usuarios (id, organizacao_id, nome, email)
|
||||
VALUES (p_user_id, v_org_id, p_nome_usuario, p_email_usuario)
|
||||
ON CONFLICT (id) DO UPDATE SET
|
||||
organizacao_id = v_org_id,
|
||||
nome = p_nome_usuario,
|
||||
email = p_email_usuario;
|
||||
|
||||
-- Associar usuário como owner
|
||||
INSERT INTO public.organizacao_usuarios (organizacao_id, usuario_id, role)
|
||||
VALUES (v_org_id, p_user_id, 'owner');
|
||||
|
||||
RETURN v_org_id;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
|
||||
-- ========================================
|
||||
-- 4. FUNÇÃO PARA ACEITAR CONVITE
|
||||
-- ========================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.aceitar_convite(
|
||||
p_token VARCHAR(255),
|
||||
p_user_id UUID,
|
||||
p_nome_usuario VARCHAR(255),
|
||||
p_email_usuario VARCHAR(255)
|
||||
)
|
||||
RETURNS JSONB AS $$
|
||||
DECLARE
|
||||
v_convite RECORD;
|
||||
v_org_id UUID;
|
||||
BEGIN
|
||||
-- Buscar convite
|
||||
SELECT * INTO v_convite
|
||||
FROM public.convites
|
||||
WHERE token = p_token
|
||||
AND status = 'pendente'
|
||||
AND expira_em > NOW();
|
||||
|
||||
IF NOT FOUND THEN
|
||||
RETURN jsonb_build_object(
|
||||
'success', false,
|
||||
'error', 'Convite inválido ou expirado'
|
||||
);
|
||||
END IF;
|
||||
|
||||
-- Verificar se email corresponde
|
||||
IF v_convite.email != p_email_usuario THEN
|
||||
RETURN jsonb_build_object(
|
||||
'success', false,
|
||||
'error', 'Email não corresponde ao convite'
|
||||
);
|
||||
END IF;
|
||||
|
||||
v_org_id := v_convite.organizacao_id;
|
||||
|
||||
-- Criar usuário
|
||||
INSERT INTO public.usuarios (id, organizacao_id, nome, email)
|
||||
VALUES (p_user_id, v_org_id, p_nome_usuario, p_email_usuario)
|
||||
ON CONFLICT (id) DO UPDATE SET
|
||||
organizacao_id = v_org_id,
|
||||
nome = p_nome_usuario,
|
||||
email = p_email_usuario;
|
||||
|
||||
-- Associar usuário à organização com role do convite
|
||||
INSERT INTO public.organizacao_usuarios (organizacao_id, usuario_id, role)
|
||||
VALUES (v_org_id, p_user_id, v_convite.role)
|
||||
ON CONFLICT (organizacao_id, usuario_id) DO UPDATE SET
|
||||
role = v_convite.role,
|
||||
ativo = true;
|
||||
|
||||
-- Marcar convite como aceito
|
||||
UPDATE public.convites
|
||||
SET status = 'aceito', aceito_em = NOW()
|
||||
WHERE id = v_convite.id;
|
||||
|
||||
RETURN jsonb_build_object(
|
||||
'success', true,
|
||||
'organizacao_id', v_org_id,
|
||||
'role', v_convite.role
|
||||
);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
|
||||
-- ========================================
|
||||
-- 5. FUNÇÃO PARA CRIAR CONVITE
|
||||
-- ========================================
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.criar_convite(
|
||||
p_organizacao_id UUID,
|
||||
p_email VARCHAR(255),
|
||||
p_role VARCHAR(50),
|
||||
p_convidado_por UUID
|
||||
)
|
||||
RETURNS JSONB AS $$
|
||||
DECLARE
|
||||
v_convite_id UUID;
|
||||
v_token VARCHAR(255);
|
||||
v_user_role TEXT;
|
||||
BEGIN
|
||||
-- Verificar se quem está convidando tem permissão
|
||||
SELECT role INTO v_user_role
|
||||
FROM public.organizacao_usuarios
|
||||
WHERE organizacao_id = p_organizacao_id
|
||||
AND usuario_id = p_convidado_por
|
||||
AND ativo = true;
|
||||
|
||||
IF v_user_role NOT IN ('owner', 'admin') THEN
|
||||
RETURN jsonb_build_object(
|
||||
'success', false,
|
||||
'error', 'Sem permissão para criar convites'
|
||||
);
|
||||
END IF;
|
||||
|
||||
-- Cancelar convites pendentes anteriores para o mesmo email
|
||||
UPDATE public.convites
|
||||
SET status = 'cancelado'
|
||||
WHERE organizacao_id = p_organizacao_id
|
||||
AND email = p_email
|
||||
AND status = 'pendente';
|
||||
|
||||
-- Criar novo convite
|
||||
INSERT INTO public.convites (
|
||||
organizacao_id,
|
||||
email,
|
||||
role,
|
||||
convidado_por
|
||||
) VALUES (
|
||||
p_organizacao_id,
|
||||
p_email,
|
||||
p_role,
|
||||
p_convidado_por
|
||||
)
|
||||
RETURNING id, token INTO v_convite_id, v_token;
|
||||
|
||||
RETURN jsonb_build_object(
|
||||
'success', true,
|
||||
'convite_id', v_convite_id,
|
||||
'token', v_token
|
||||
);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
|
||||
-- ========================================
|
||||
-- 6. VIEW PARA ESTATÍSTICAS DA ORGANIZAÇÃO
|
||||
-- ========================================
|
||||
|
||||
CREATE OR REPLACE VIEW public.v_organizacao_estatisticas AS
|
||||
SELECT
|
||||
o.id AS organizacao_id,
|
||||
o.slug,
|
||||
o.nome,
|
||||
o.plano,
|
||||
o.status,
|
||||
|
||||
-- Contadores
|
||||
COUNT(DISTINCT u.id) FILTER (WHERE u.ativo = true) AS total_usuarios_ativos,
|
||||
COUNT(DISTINCT ob.id) AS total_obras,
|
||||
COUNT(DISTINCT ob.id) FILTER (WHERE ob.status = 'ativa') AS obras_ativas,
|
||||
COUNT(DISTINCT r.id) AS total_rdos,
|
||||
COUNT(DISTINCT r.id) FILTER (WHERE r.status = 'aprovado') AS rdos_aprovados,
|
||||
COUNT(DISTINCT t.id) AS total_tarefas,
|
||||
COUNT(DISTINCT t.id) FILTER (WHERE t.status = 'concluida') AS tarefas_concluidas,
|
||||
|
||||
-- Limites
|
||||
o.max_usuarios,
|
||||
o.max_obras,
|
||||
o.max_rdos_mes,
|
||||
o.max_storage_mb,
|
||||
|
||||
-- Percentuais de uso
|
||||
ROUND((COUNT(DISTINCT u.id) FILTER (WHERE u.ativo = true)::DECIMAL / NULLIF(o.max_usuarios, 0)) * 100, 2) AS percentual_usuarios,
|
||||
ROUND((COUNT(DISTINCT ob.id)::DECIMAL / NULLIF(o.max_obras, 0)) * 100, 2) AS percentual_obras,
|
||||
|
||||
-- Datas
|
||||
o.data_trial_fim,
|
||||
o.data_proxima_cobranca,
|
||||
o.created_at
|
||||
|
||||
FROM public.organizacoes o
|
||||
LEFT JOIN public.usuarios u ON u.organizacao_id = o.id
|
||||
LEFT JOIN public.obras ob ON ob.organizacao_id = o.id
|
||||
LEFT JOIN public.rdos r ON r.organizacao_id = o.id
|
||||
LEFT JOIN public.tarefas t ON t.organizacao_id = o.id
|
||||
GROUP BY o.id;
|
||||
|
||||
-- ========================================
|
||||
-- COMENTÁRIOS
|
||||
-- ========================================
|
||||
|
||||
COMMENT ON FUNCTION criar_organizacao_com_owner(VARCHAR, VARCHAR, VARCHAR, VARCHAR, UUID) IS
|
||||
'Cria uma nova organização com o primeiro usuário como owner';
|
||||
|
||||
COMMENT ON FUNCTION aceitar_convite(VARCHAR, UUID, VARCHAR, VARCHAR) IS
|
||||
'Processa aceitação de convite e vincula usuário à organização';
|
||||
|
||||
COMMENT ON FUNCTION criar_convite(UUID, VARCHAR, VARCHAR, UUID) IS
|
||||
'Cria um novo convite para usuário entrar na organização';
|
||||
|
||||
COMMENT ON VIEW v_organizacao_estatisticas IS
|
||||
'View com estatísticas consolidadas de cada organização';
|
||||
99
supabase/migrations/20241206120000_fix_rls_recursion.sql
Normal file
99
supabase/migrations/20241206120000_fix_rls_recursion.sql
Normal file
@@ -0,0 +1,99 @@
|
||||
-- Migration to fix infinite recursion in RLS policies
|
||||
-- Date: 2024-12-06
|
||||
-- Description: Introduces security definer function to fetch user organizations safely and updates policies.
|
||||
|
||||
-- 1. Create helper function to get user's organizations (Bypasses RLS)
|
||||
CREATE OR REPLACE FUNCTION public.get_my_org_ids()
|
||||
RETURNS UUID[] AS $$
|
||||
BEGIN
|
||||
RETURN ARRAY(
|
||||
SELECT organizacao_id
|
||||
FROM public.organizacao_usuarios
|
||||
WHERE usuario_id = auth.uid()
|
||||
AND ativo = true
|
||||
);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
|
||||
COMMENT ON FUNCTION public.get_my_org_ids() IS 'Retorna lista de IDs das organizações do usuário (Security Definer para evitar recursão)';
|
||||
|
||||
-- 2. Drop recursive policies
|
||||
|
||||
-- Organizacao Usuarios
|
||||
DROP POLICY IF EXISTS "Ver membros da organização" ON public.organizacao_usuarios;
|
||||
DROP POLICY IF EXISTS "Admins gerenciam membros" ON public.organizacao_usuarios;
|
||||
|
||||
-- Usuarios
|
||||
DROP POLICY IF EXISTS "Usuários veem membros da organização" ON public.usuarios;
|
||||
DROP POLICY IF EXISTS "Admins podem criar usuários" ON public.usuarios;
|
||||
|
||||
|
||||
-- 3. Re-create policies using the safe function
|
||||
|
||||
-- ========================================
|
||||
-- ORGANIZACAO_USUARIOS
|
||||
-- ========================================
|
||||
|
||||
-- Usuários veem membros das organizações que participam
|
||||
CREATE POLICY "Ver membros da organização" ON public.organizacao_usuarios
|
||||
FOR SELECT USING (
|
||||
organizacao_id = ANY(public.get_my_org_ids())
|
||||
);
|
||||
|
||||
-- Admins gerenciam membros (precisamos verificar role também)
|
||||
CREATE POLICY "Admins gerenciam membros" ON public.organizacao_usuarios
|
||||
FOR ALL USING (
|
||||
organizacao_id = ANY(public.get_my_org_ids())
|
||||
AND (
|
||||
SELECT role FROM public.organizacao_usuarios
|
||||
WHERE usuario_id = auth.uid()
|
||||
AND organizacao_id = public.organizacao_usuarios.organizacao_id
|
||||
) IN ('owner', 'admin')
|
||||
);
|
||||
-- Note: The subquery above might still be recursive if not careful.
|
||||
-- But since we are checking the row being accessed 'public.organizacao_usuarios.organizacao_id',
|
||||
-- and matching it against 'auth.uid()', we are looking up OUR OWN role in that specific org.
|
||||
-- To be absolutely safe and avoid recursion in 'role' lookup, we should use get_user_role() function which is SECURITY DEFINER.
|
||||
|
||||
DROP POLICY IF EXISTS "Admins gerenciam membros" ON public.organizacao_usuarios;
|
||||
CREATE POLICY "Admins gerenciam membros" ON public.organizacao_usuarios
|
||||
FOR ALL USING (
|
||||
organizacao_id = ANY(public.get_my_org_ids())
|
||||
AND public.get_user_role(auth.uid(), organizacao_id) IN ('owner', 'admin')
|
||||
);
|
||||
|
||||
|
||||
-- ========================================
|
||||
-- USUARIOS
|
||||
-- ========================================
|
||||
|
||||
-- Usuários veem outros usuários da mesma organização
|
||||
CREATE POLICY "Usuários veem membros da organização" ON public.usuarios
|
||||
FOR SELECT USING (
|
||||
organizacao_id = ANY(public.get_my_org_ids())
|
||||
);
|
||||
|
||||
-- Admins podem criar usuários
|
||||
CREATE POLICY "Admins podem criar usuários" ON public.usuarios
|
||||
FOR INSERT WITH CHECK (
|
||||
organizacao_id = ANY(public.get_my_org_ids())
|
||||
AND public.get_user_role(auth.uid(), organizacao_id) IN ('owner', 'admin')
|
||||
);
|
||||
|
||||
-- ========================================
|
||||
-- OUTRAS TABELAS (Otimização opcional, mas recomendada para consistência)
|
||||
-- ========================================
|
||||
|
||||
-- Atualizar convites
|
||||
DROP POLICY IF EXISTS "Ver convites da organização" ON public.convites;
|
||||
CREATE POLICY "Ver convites da organização" ON public.convites
|
||||
FOR SELECT USING (
|
||||
organizacao_id = ANY(public.get_my_org_ids())
|
||||
);
|
||||
|
||||
-- Atualizar tarefas
|
||||
DROP POLICY IF EXISTS "Ver tarefas da organização" ON public.tarefas;
|
||||
CREATE POLICY "Ver tarefas da organização" ON public.tarefas
|
||||
FOR SELECT USING (
|
||||
organizacao_id = ANY(public.get_my_org_ids())
|
||||
);
|
||||
85
supabase/migrations/20241206130000_create_aux_tables.sql
Normal file
85
supabase/migrations/20241206130000_create_aux_tables.sql
Normal file
@@ -0,0 +1,85 @@
|
||||
-- Migration to create missing auxiliary tables and stop frontend errors
|
||||
-- Date: 2024-12-06
|
||||
|
||||
-- 1. TIPOS_ATIVIDADE
|
||||
CREATE TABLE IF NOT EXISTS public.tipos_atividade (
|
||||
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
|
||||
nome TEXT NOT NULL,
|
||||
ativo BOOLEAN DEFAULT true,
|
||||
ordem INTEGER DEFAULT 0,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Enable RLS
|
||||
ALTER TABLE public.tipos_atividade ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- Policy: Authenticated users can read
|
||||
CREATE POLICY "Authenticated users can read tipos_atividade" ON public.tipos_atividade
|
||||
FOR SELECT USING (auth.role() = 'authenticated');
|
||||
|
||||
-- Insert default data
|
||||
INSERT INTO public.tipos_atividade (nome, ordem) VALUES
|
||||
('Escavação', 1),
|
||||
('Fundação', 2),
|
||||
('Concretagem', 3),
|
||||
('Alvenaria', 4),
|
||||
('Instalação Elétrica', 5),
|
||||
('Instalação Hidráulica', 6),
|
||||
('Revestimento', 7),
|
||||
('Pintura', 8)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
|
||||
-- 2. CONDICOES_CLIMATICAS
|
||||
CREATE TABLE IF NOT EXISTS public.condicoes_climaticas (
|
||||
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
|
||||
nome TEXT NOT NULL,
|
||||
valor TEXT NOT NULL,
|
||||
descricao TEXT,
|
||||
ativo BOOLEAN DEFAULT true,
|
||||
ordem INTEGER DEFAULT 0,
|
||||
icone TEXT,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Enable RLS
|
||||
ALTER TABLE public.condicoes_climaticas ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- Policy: Authenticated users can read
|
||||
CREATE POLICY "Authenticated users can read condicoes_climaticas" ON public.condicoes_climaticas
|
||||
FOR SELECT USING (auth.role() = 'authenticated');
|
||||
|
||||
-- Insert default data
|
||||
INSERT INTO public.condicoes_climaticas (nome, valor, ordem, icone) VALUES
|
||||
('Ensolarado', 'ensolarado', 1, 'Sun'),
|
||||
('Parcialmente Nublado', 'parcialmente_nublado', 2, 'Cloud'),
|
||||
('Nublado', 'nublado', 3, 'Cloud'),
|
||||
('Chuvisco', 'chuvisco', 4, 'CloudRain'),
|
||||
('Chuva Leve', 'chuva_leve', 5, 'CloudRain'),
|
||||
('Chuva Forte', 'chuva_forte', 6, 'CloudRain')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
|
||||
-- 3. FUNCIONARIOS (Mão de Obra / Laborers)
|
||||
CREATE TABLE IF NOT EXISTS public.funcionarios (
|
||||
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
|
||||
nome TEXT NOT NULL,
|
||||
funcao TEXT,
|
||||
ativo BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Enable RLS
|
||||
ALTER TABLE public.funcionarios ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- Policy: Authenticated users can read
|
||||
CREATE POLICY "Authenticated users can read funcionarios" ON public.funcionarios
|
||||
FOR SELECT USING (auth.role() = 'authenticated');
|
||||
|
||||
-- Insert default data (Mock)
|
||||
INSERT INTO public.funcionarios (nome, funcao) VALUES
|
||||
('João da Silva', 'Pedreiro'),
|
||||
('José Santos', 'Servente'),
|
||||
('Maria Oliveira', 'Engenheira'),
|
||||
('Carlos Souza', 'Mestre de Obras')
|
||||
ON CONFLICT DO NOTHING;
|
||||
173
supabase/migrations/fix_table_permissions.sql
Normal file
173
supabase/migrations/fix_table_permissions.sql
Normal file
@@ -0,0 +1,173 @@
|
||||
-- Fix permissions for all existing tables
|
||||
-- Grant basic read access to anon role and full access to authenticated role
|
||||
|
||||
-- Core tables
|
||||
GRANT SELECT ON usuarios TO anon;
|
||||
GRANT ALL PRIVILEGES ON usuarios TO authenticated;
|
||||
|
||||
GRANT SELECT ON empresas TO anon;
|
||||
GRANT ALL PRIVILEGES ON empresas TO authenticated;
|
||||
|
||||
GRANT SELECT ON funcoes_cargos TO anon;
|
||||
GRANT ALL PRIVILEGES ON funcoes_cargos TO authenticated;
|
||||
|
||||
GRANT SELECT ON funcionarios TO anon;
|
||||
GRANT ALL PRIVILEGES ON funcionarios TO authenticated;
|
||||
|
||||
GRANT SELECT ON obras TO anon;
|
||||
GRANT ALL PRIVILEGES ON obras TO authenticated;
|
||||
|
||||
GRANT SELECT ON obra_funcionarios TO anon;
|
||||
GRANT ALL PRIVILEGES ON obra_funcionarios TO authenticated;
|
||||
|
||||
-- Equipment and materials
|
||||
GRANT SELECT ON tipos_equipamento TO anon;
|
||||
GRANT ALL PRIVILEGES ON tipos_equipamento TO authenticated;
|
||||
|
||||
GRANT SELECT ON equipamentos TO anon;
|
||||
GRANT ALL PRIVILEGES ON equipamentos TO authenticated;
|
||||
|
||||
GRANT SELECT ON materiais TO anon;
|
||||
GRANT ALL PRIVILEGES ON materiais TO authenticated;
|
||||
|
||||
-- Activity and occurrence types
|
||||
GRANT SELECT ON tipos_atividade TO anon;
|
||||
GRANT ALL PRIVILEGES ON tipos_atividade TO authenticated;
|
||||
|
||||
GRANT SELECT ON tipos_ocorrencia TO anon;
|
||||
GRANT ALL PRIVILEGES ON tipos_ocorrencia TO authenticated;
|
||||
|
||||
GRANT SELECT ON condicoes_climaticas TO anon;
|
||||
GRANT ALL PRIVILEGES ON condicoes_climaticas TO authenticated;
|
||||
|
||||
-- RDO tables
|
||||
GRANT SELECT ON rdos TO anon;
|
||||
GRANT ALL PRIVILEGES ON rdos TO authenticated;
|
||||
|
||||
GRANT SELECT ON rdo_atividades TO anon;
|
||||
GRANT ALL PRIVILEGES ON rdo_atividades TO authenticated;
|
||||
|
||||
GRANT SELECT ON rdo_mao_obra TO anon;
|
||||
GRANT ALL PRIVILEGES ON rdo_mao_obra TO authenticated;
|
||||
|
||||
GRANT SELECT ON rdo_equipamentos TO anon;
|
||||
GRANT ALL PRIVILEGES ON rdo_equipamentos TO authenticated;
|
||||
|
||||
GRANT SELECT ON rdo_materiais TO anon;
|
||||
GRANT ALL PRIVILEGES ON rdo_materiais TO authenticated;
|
||||
|
||||
GRANT SELECT ON rdo_ocorrencias TO anon;
|
||||
GRANT ALL PRIVILEGES ON rdo_ocorrencias TO authenticated;
|
||||
|
||||
-- Tasks
|
||||
GRANT SELECT ON tarefas TO anon;
|
||||
GRANT ALL PRIVILEGES ON tarefas TO authenticated;
|
||||
|
||||
GRANT SELECT ON tarefa_logs TO anon;
|
||||
GRANT ALL PRIVILEGES ON tarefa_logs TO authenticated;
|
||||
|
||||
GRANT SELECT ON tarefa_dependencias TO anon;
|
||||
GRANT ALL PRIVILEGES ON tarefa_dependencias TO authenticated;
|
||||
|
||||
GRANT SELECT ON tarefa_anexos TO anon;
|
||||
GRANT ALL PRIVILEGES ON tarefa_anexos TO authenticated;
|
||||
|
||||
-- Files and documents
|
||||
GRANT SELECT ON tipos_arquivo TO anon;
|
||||
GRANT ALL PRIVILEGES ON tipos_arquivo TO authenticated;
|
||||
|
||||
GRANT SELECT ON arquivos TO anon;
|
||||
GRANT ALL PRIVILEGES ON arquivos TO authenticated;
|
||||
|
||||
GRANT SELECT ON obra_fotos TO anon;
|
||||
GRANT ALL PRIVILEGES ON obra_fotos TO authenticated;
|
||||
|
||||
GRANT SELECT ON obra_documentos TO anon;
|
||||
GRANT ALL PRIVILEGES ON obra_documentos TO authenticated;
|
||||
|
||||
GRANT SELECT ON rdo_fotos TO anon;
|
||||
GRANT ALL PRIVILEGES ON rdo_fotos TO authenticated;
|
||||
|
||||
-- Digital signatures
|
||||
GRANT SELECT ON assinaturas_digitais TO anon;
|
||||
GRANT ALL PRIVILEGES ON assinaturas_digitais TO authenticated;
|
||||
|
||||
-- Reports
|
||||
GRANT SELECT ON relatorio_templates TO anon;
|
||||
GRANT ALL PRIVILEGES ON relatorio_templates TO authenticated;
|
||||
|
||||
GRANT SELECT ON relatorios_gerados TO anon;
|
||||
GRANT ALL PRIVILEGES ON relatorios_gerados TO authenticated;
|
||||
|
||||
-- Metrics and dashboard
|
||||
GRANT SELECT ON metricas_obra TO anon;
|
||||
GRANT ALL PRIVILEGES ON metricas_obra TO authenticated;
|
||||
|
||||
GRANT SELECT ON dashboard_widgets TO anon;
|
||||
GRANT ALL PRIVILEGES ON dashboard_widgets TO authenticated;
|
||||
|
||||
-- Notifications and audit
|
||||
GRANT SELECT ON notificacoes TO anon;
|
||||
GRANT ALL PRIVILEGES ON notificacoes TO authenticated;
|
||||
|
||||
GRANT SELECT ON auditoria TO anon;
|
||||
GRANT ALL PRIVILEGES ON auditoria TO authenticated;
|
||||
|
||||
-- Enable RLS on tables that don't have it yet
|
||||
ALTER TABLE rdo_materiais ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE obra_fotos ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE relatorios_gerados ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE rdo_ocorrencias ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE dashboard_widgets ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE metricas_obra ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE rdo_fotos ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE tipos_atividade ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE tipos_ocorrencia ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE condicoes_climaticas ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE tipos_arquivo ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE empresas ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE funcoes_cargos ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE relatorio_templates ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE rdo_atividades ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE tipos_equipamento ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE assinaturas_digitais ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE rdo_equipamentos ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE tarefa_anexos ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE tarefa_logs ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE tarefa_dependencias ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE obra_documentos ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE materiais ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE rdo_mao_obra ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE usuarios ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE obra_funcionarios ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- Create basic RLS policies for authenticated users
|
||||
CREATE POLICY "Allow authenticated users full access" ON usuarios FOR ALL TO authenticated USING (true);
|
||||
CREATE POLICY "Allow authenticated users full access" ON empresas FOR ALL TO authenticated USING (true);
|
||||
CREATE POLICY "Allow authenticated users full access" ON funcoes_cargos FOR ALL TO authenticated USING (true);
|
||||
CREATE POLICY "Allow authenticated users full access" ON funcionarios FOR ALL TO authenticated USING (true);
|
||||
CREATE POLICY "Allow authenticated users full access" ON obras FOR ALL TO authenticated USING (true);
|
||||
CREATE POLICY "Allow authenticated users full access" ON equipamentos FOR ALL TO authenticated USING (true);
|
||||
CREATE POLICY "Allow authenticated users full access" ON materiais FOR ALL TO authenticated USING (true);
|
||||
CREATE POLICY "Allow authenticated users full access" ON tipos_atividade FOR ALL TO authenticated USING (true);
|
||||
CREATE POLICY "Allow authenticated users full access" ON tipos_ocorrencia FOR ALL TO authenticated USING (true);
|
||||
CREATE POLICY "Allow authenticated users full access" ON condicoes_climaticas FOR ALL TO authenticated USING (true);
|
||||
CREATE POLICY "Allow authenticated users full access" ON rdos FOR ALL TO authenticated USING (true);
|
||||
CREATE POLICY "Allow authenticated users full access" ON rdo_atividades FOR ALL TO authenticated USING (true);
|
||||
CREATE POLICY "Allow authenticated users full access" ON rdo_mao_obra FOR ALL TO authenticated USING (true);
|
||||
CREATE POLICY "Allow authenticated users full access" ON rdo_equipamentos FOR ALL TO authenticated USING (true);
|
||||
CREATE POLICY "Allow authenticated users full access" ON rdo_materiais FOR ALL TO authenticated USING (true);
|
||||
CREATE POLICY "Allow authenticated users full access" ON rdo_ocorrencias FOR ALL TO authenticated USING (true);
|
||||
CREATE POLICY "Allow authenticated users full access" ON tarefas FOR ALL TO authenticated USING (true);
|
||||
CREATE POLICY "Allow authenticated users full access" ON arquivos FOR ALL TO authenticated USING (true);
|
||||
CREATE POLICY "Allow authenticated users full access" ON notificacoes FOR ALL TO authenticated USING (true);
|
||||
CREATE POLICY "Allow authenticated users full access" ON auditoria FOR ALL TO authenticated USING (true);
|
||||
|
||||
-- Allow anon users to read reference data
|
||||
CREATE POLICY "Allow anon read access" ON tipos_atividade FOR SELECT TO anon USING (true);
|
||||
CREATE POLICY "Allow anon read access" ON tipos_ocorrencia FOR SELECT TO anon USING (true);
|
||||
CREATE POLICY "Allow anon read access" ON condicoes_climaticas FOR SELECT TO anon USING (true);
|
||||
CREATE POLICY "Allow anon read access" ON funcoes_cargos FOR SELECT TO anon USING (true);
|
||||
CREATE POLICY "Allow anon read access" ON tipos_equipamento FOR SELECT TO anon USING (true);
|
||||
CREATE POLICY "Allow anon read access" ON tipos_arquivo FOR SELECT TO anon USING (true);
|
||||
CREATE POLICY "Allow anon read access" ON materiais FOR SELECT TO anon USING (true);
|
||||
67
supabase/migrations/insert_mock_data.sql
Normal file
67
supabase/migrations/insert_mock_data.sql
Normal file
@@ -0,0 +1,67 @@
|
||||
-- Script para inserir dados fictícios adicionais no banco de dados
|
||||
-- Usando UUIDs únicos e valores que não conflitam com dados existentes
|
||||
|
||||
-- Inserir tipos de atividade adicionais (se não existirem)
|
||||
INSERT INTO tipos_atividade (id, nome, descricao, ativo, created_at) VALUES
|
||||
('750e8400-e29b-41d4-a716-446655440001', 'Estrutura Metálica', 'Montagem de estruturas metálicas', true, NOW()),
|
||||
('750e8400-e29b-41d4-a716-446655440002', 'Alvenaria', 'Construção de paredes e muros', true, NOW()),
|
||||
('750e8400-e29b-41d4-a716-446655440003', 'Instalações Elétricas', 'Instalação de sistemas elétricos', true, NOW()),
|
||||
('750e8400-e29b-41d4-a716-446655440004', 'Instalações Hidráulicas', 'Instalação de sistemas hidráulicos', true, NOW()),
|
||||
('750e8400-e29b-41d4-a716-446655440005', 'Acabamento', 'Serviços de acabamento e pintura', true, NOW())
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
-- Inserir condições climáticas adicionais (com valores únicos)
|
||||
INSERT INTO condicoes_climaticas (id, nome, valor, descricao, ativo, created_at) VALUES
|
||||
('650e8400-e29b-41d4-a716-446655440011', 'Tempo Bom', 'tempo_bom', 'Condições ideais para trabalho', true, NOW()),
|
||||
('650e8400-e29b-41d4-a716-446655440012', 'Garoa', 'garoa', 'Chuva muito leve', true, NOW()),
|
||||
('650e8400-e29b-41d4-a716-446655440013', 'Neblina', 'neblina', 'Visibilidade reduzida', true, NOW()),
|
||||
('650e8400-e29b-41d4-a716-446655440014', 'Calor Intenso', 'calor_intenso', 'Temperatura muito alta', true, NOW())
|
||||
ON CONFLICT (valor) DO NOTHING;
|
||||
|
||||
-- Inserir tipos de ocorrência
|
||||
INSERT INTO tipos_ocorrencia (id, nome, descricao, ativo, created_at) VALUES
|
||||
('550e8400-e29b-41d4-a716-446655440001', 'Acidente de Trabalho', 'Acidentes durante execução', true, NOW()),
|
||||
('550e8400-e29b-41d4-a716-446655440002', 'Problema de Qualidade', 'Problemas na qualidade do serviço', true, NOW()),
|
||||
('550e8400-e29b-41d4-a716-446655440003', 'Atraso de Material', 'Atraso na entrega de materiais', true, NOW()),
|
||||
('550e8400-e29b-41d4-a716-446655440004', 'Problema Climático', 'Interrupção por condições climáticas', true, NOW())
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
-- Inserir funções e cargos
|
||||
INSERT INTO funcoes_cargos (id, nome, descricao, ativo, created_at) VALUES
|
||||
('850e8400-e29b-41d4-a716-446655440001', 'Soldador', 'Profissional especializado em soldagem', true, NOW()),
|
||||
('850e8400-e29b-41d4-a716-446655440002', 'Montador', 'Responsável pela montagem de estruturas', true, NOW()),
|
||||
('850e8400-e29b-41d4-a716-446655440003', 'Pintor Industrial', 'Especialista em pintura anticorrosiva', true, NOW()),
|
||||
('850e8400-e29b-41d4-a716-446655440004', 'Inspetor de Qualidade', 'Responsável pela inspeção e controle', true, NOW()),
|
||||
('850e8400-e29b-41d4-a716-446655440005', 'Operador de Guindaste', 'Opera equipamentos de elevação', true, NOW()),
|
||||
('850e8400-e29b-41d4-a716-446655440006', 'Encarregado', 'Supervisiona equipes de trabalho', true, NOW())
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
-- Inserir usuários fictícios
|
||||
INSERT INTO usuarios (id, nome, email, telefone, tipo_usuario, ativo, created_at) VALUES
|
||||
('950e8400-e29b-41d4-a716-446655440001', 'João Silva', 'joao.silva@empresa.com', '(11) 99999-0001', 'engenheiro', true, NOW()),
|
||||
('950e8400-e29b-41d4-a716-446655440002', 'Maria Santos', 'maria.santos@empresa.com', '(11) 99999-0002', 'gestor', true, NOW()),
|
||||
('950e8400-e29b-41d4-a716-446655440003', 'Pedro Oliveira', 'pedro.oliveira@empresa.com', '(11) 99999-0003', 'mestre', true, NOW())
|
||||
ON CONFLICT (email) DO NOTHING;
|
||||
|
||||
-- Inserir obras fictícias
|
||||
INSERT INTO obras (id, nome, descricao, endereco, status, data_inicio, created_at) VALUES
|
||||
('a50e8400-e29b-41d4-a716-446655440001', 'Edifício Aurora', 'Construção de edifício comercial', 'Rua das Flores, 123 - Centro', 'em_andamento', '2024-01-15', NOW()),
|
||||
('a50e8400-e29b-41d4-a716-446655440002', 'Galpão Industrial Beta', 'Construção de galpão para indústria', 'Av. Industrial, 456 - Distrito Industrial', 'em_andamento', '2024-02-01', NOW())
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
-- Inserir funcionários fictícios
|
||||
INSERT INTO funcionarios (id, nome, cpf, funcao_id, ativo, created_at) VALUES
|
||||
('b50e8400-e29b-41d4-a716-446655440001', 'Carlos Ferreira', '123.456.789-01', '850e8400-e29b-41d4-a716-446655440001', true, NOW()),
|
||||
('b50e8400-e29b-41d4-a716-446655440002', 'Ana Costa', '987.654.321-02', '850e8400-e29b-41d4-a716-446655440002', true, NOW()),
|
||||
('b50e8400-e29b-41d4-a716-446655440003', 'Roberto Lima', '456.789.123-03', '850e8400-e29b-41d4-a716-446655440003', true, NOW()),
|
||||
('b50e8400-e29b-41d4-a716-446655440004', 'Lucia Mendes', '789.123.456-04', '850e8400-e29b-41d4-a716-446655440004', true, NOW())
|
||||
ON CONFLICT (cpf) DO NOTHING;
|
||||
|
||||
-- Conceder permissões para as tabelas
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON tipos_atividade TO anon, authenticated;
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON condicoes_climaticas TO anon, authenticated;
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON tipos_ocorrencia TO anon, authenticated;
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON funcoes_cargos TO anon, authenticated;
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON usuarios TO anon, authenticated;
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON obras TO anon, authenticated;
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON funcionarios TO anon, authenticated;
|
||||
Reference in New Issue
Block a user