First commit - backup RDOC

This commit is contained in:
2026-02-20 07:20:32 -03:00
commit b7415f0586
259 changed files with 51707 additions and 0 deletions

View File

@@ -0,0 +1 @@
v2.65.5

View File

@@ -0,0 +1 @@
v2.182.1

View File

@@ -0,0 +1 @@
postgresql://postgres.ympbgdymeesivfajmgat:[YOUR-PASSWORD]@aws-0-us-west-2.pooler.supabase.com:6543/postgres

View File

@@ -0,0 +1 @@
17.6.1.054

View File

@@ -0,0 +1 @@
mnwrnblzabxgqtgjwxgl

View File

@@ -0,0 +1 @@
v13.0.5

View File

@@ -0,0 +1 @@
iceberg-catalog-ids

View File

@@ -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';

View File

@@ -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';

View 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';

View 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';

View 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())
);

View 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;

View 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);

View 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;