feat: versão 1.5 - CRM Beta com leads, funis, campanhas e portal do cliente
This commit is contained in:
70
postgres/migrations/015_create_crm_leads.sql
Normal file
70
postgres/migrations/015_create_crm_leads.sql
Normal file
@@ -0,0 +1,70 @@
|
||||
-- Tabela de leads do CRM (multi-tenant)
|
||||
CREATE TABLE IF NOT EXISTS crm_leads (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
|
||||
-- Dados básicos
|
||||
name VARCHAR(255),
|
||||
email VARCHAR(255),
|
||||
phone VARCHAR(50),
|
||||
|
||||
-- Origem do lead
|
||||
source VARCHAR(50) DEFAULT 'import',
|
||||
source_meta JSONB DEFAULT '{}'::jsonb,
|
||||
|
||||
-- Status
|
||||
status VARCHAR(50) DEFAULT 'novo',
|
||||
|
||||
-- Informações adicionais
|
||||
notes TEXT,
|
||||
tags TEXT[],
|
||||
|
||||
-- Controle
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
created_by UUID REFERENCES users(id),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
-- Constraint: email deve ser único por agência (pode repetir entre agências)
|
||||
CONSTRAINT unique_lead_email_per_tenant UNIQUE (tenant_id, email)
|
||||
);
|
||||
|
||||
-- Relacionamento N:N entre leads e listas
|
||||
CREATE TABLE IF NOT EXISTS crm_lead_lists (
|
||||
lead_id UUID REFERENCES crm_leads(id) ON DELETE CASCADE,
|
||||
list_id UUID REFERENCES crm_lists(id) ON DELETE CASCADE,
|
||||
added_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
added_by UUID REFERENCES users(id),
|
||||
PRIMARY KEY (lead_id, list_id)
|
||||
);
|
||||
|
||||
-- Índices para performance
|
||||
CREATE INDEX IF NOT EXISTS idx_crm_leads_tenant_id ON crm_leads(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_crm_leads_email ON crm_leads(email);
|
||||
CREATE INDEX IF NOT EXISTS idx_crm_leads_phone ON crm_leads(phone);
|
||||
CREATE INDEX IF NOT EXISTS idx_crm_leads_status ON crm_leads(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_crm_leads_is_active ON crm_leads(is_active);
|
||||
CREATE INDEX IF NOT EXISTS idx_crm_leads_tags ON crm_leads USING GIN(tags);
|
||||
CREATE INDEX IF NOT EXISTS idx_crm_leads_source ON crm_leads(source);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_crm_lead_lists_lead_id ON crm_lead_lists(lead_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_crm_lead_lists_list_id ON crm_lead_lists(list_id);
|
||||
|
||||
-- Trigger para atualizar updated_at (usa a função update_updated_at_column criada em 014_create_crm_tables.sql)
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (
|
||||
SELECT 1
|
||||
FROM pg_proc
|
||||
WHERE proname = 'update_updated_at_column'
|
||||
) THEN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM pg_trigger
|
||||
WHERE tgname = 'update_crm_leads_updated_at'
|
||||
) THEN
|
||||
CREATE TRIGGER update_crm_leads_updated_at BEFORE UPDATE ON crm_leads
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
END IF;
|
||||
END IF;
|
||||
END $$;
|
||||
11
postgres/migrations/016_add_customer_to_leads.sql
Normal file
11
postgres/migrations/016_add_customer_to_leads.sql
Normal file
@@ -0,0 +1,11 @@
|
||||
-- Adicionar relacionamento entre leads e customers (clientes)
|
||||
-- Um lead pertence a um cliente da agência
|
||||
|
||||
ALTER TABLE crm_leads
|
||||
ADD COLUMN customer_id UUID REFERENCES crm_customers(id) ON DELETE SET NULL;
|
||||
|
||||
-- Índice para performance
|
||||
CREATE INDEX IF NOT EXISTS idx_crm_leads_customer_id ON crm_leads(customer_id);
|
||||
|
||||
-- Comentário explicativo
|
||||
COMMENT ON COLUMN crm_leads.customer_id IS 'Cliente (customer) ao qual este lead pertence. Permite que agências gerenciem leads de seus clientes.';
|
||||
20
postgres/migrations/017_create_crm_share_tokens.sql
Normal file
20
postgres/migrations/017_create_crm_share_tokens.sql
Normal file
@@ -0,0 +1,20 @@
|
||||
-- Migration: Create CRM share tokens table
|
||||
-- Description: Allows generating secure shareable links for customers to view their leads
|
||||
|
||||
CREATE TABLE IF NOT EXISTS crm_share_tokens (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
customer_id UUID NOT NULL REFERENCES crm_customers(id) ON DELETE CASCADE,
|
||||
token VARCHAR(64) NOT NULL UNIQUE,
|
||||
expires_at TIMESTAMP,
|
||||
created_by UUID NOT NULL REFERENCES users(id),
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX idx_crm_share_tokens_token ON crm_share_tokens(token);
|
||||
CREATE INDEX idx_crm_share_tokens_customer_id ON crm_share_tokens(customer_id);
|
||||
CREATE INDEX idx_crm_share_tokens_tenant_id ON crm_share_tokens(tenant_id);
|
||||
|
||||
COMMENT ON TABLE crm_share_tokens IS 'Tokens for sharing customer lead data externally';
|
||||
COMMENT ON COLUMN crm_share_tokens.token IS 'Unique secure token for accessing shared data';
|
||||
COMMENT ON COLUMN crm_share_tokens.expires_at IS 'Optional expiration date (NULL = never expires)';
|
||||
21
postgres/migrations/018_add_customer_auth.sql
Normal file
21
postgres/migrations/018_add_customer_auth.sql
Normal file
@@ -0,0 +1,21 @@
|
||||
-- Migration 018: Adicionar autenticação para clientes (Portal do Cliente)
|
||||
-- Permite que clientes (CRMCustomer) façam login e vejam seus próprios leads
|
||||
|
||||
-- Adicionar coluna de senha para clientes
|
||||
ALTER TABLE crm_customers
|
||||
ADD COLUMN IF NOT EXISTS password_hash VARCHAR(255),
|
||||
ADD COLUMN IF NOT EXISTS has_portal_access BOOLEAN DEFAULT false,
|
||||
ADD COLUMN IF NOT EXISTS portal_last_login TIMESTAMP,
|
||||
ADD COLUMN IF NOT EXISTS portal_created_at TIMESTAMP DEFAULT NOW();
|
||||
|
||||
-- Criar índice para busca por email (login)
|
||||
CREATE INDEX IF NOT EXISTS idx_crm_customers_email ON crm_customers(email);
|
||||
|
||||
-- Criar índice para clientes com acesso ao portal
|
||||
CREATE INDEX IF NOT EXISTS idx_crm_customers_portal_access ON crm_customers(has_portal_access) WHERE has_portal_access = true;
|
||||
|
||||
-- Comentários
|
||||
COMMENT ON COLUMN crm_customers.password_hash IS 'Hash bcrypt da senha para acesso ao portal do cliente';
|
||||
COMMENT ON COLUMN crm_customers.has_portal_access IS 'Define se o cliente tem acesso ao portal';
|
||||
COMMENT ON COLUMN crm_customers.portal_last_login IS 'Último login do cliente no portal';
|
||||
COMMENT ON COLUMN crm_customers.portal_created_at IS 'Data de criação do acesso ao portal';
|
||||
9
postgres/migrations/019_add_customer_to_campaigns.sql
Normal file
9
postgres/migrations/019_add_customer_to_campaigns.sql
Normal file
@@ -0,0 +1,9 @@
|
||||
-- Adicionar relacionamento entre campanhas (crm_lists) e clientes (crm_customers)
|
||||
ALTER TABLE crm_lists
|
||||
ADD COLUMN customer_id UUID REFERENCES crm_customers(id) ON DELETE CASCADE;
|
||||
|
||||
-- Índice para performance
|
||||
CREATE INDEX IF NOT EXISTS idx_crm_lists_customer_id ON crm_lists(customer_id);
|
||||
|
||||
-- Comentário explicativo
|
||||
COMMENT ON COLUMN crm_lists.customer_id IS 'Cliente ao qual esta campanha pertence.';
|
||||
65
postgres/migrations/020_create_crm_funnels.sql
Normal file
65
postgres/migrations/020_create_crm_funnels.sql
Normal file
@@ -0,0 +1,65 @@
|
||||
-- Migration: Create CRM funnels and stages
|
||||
-- Description: Allows agencies to create custom lead monitoring pipelines
|
||||
|
||||
CREATE TABLE IF NOT EXISTS crm_funnels (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
description TEXT,
|
||||
is_default BOOLEAN DEFAULT false,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT unique_funnel_per_tenant UNIQUE (tenant_id, name)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS crm_funnel_stages (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
funnel_id UUID NOT NULL REFERENCES crm_funnels(id) ON DELETE CASCADE,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
description TEXT,
|
||||
color VARCHAR(7) DEFAULT '#3b82f6',
|
||||
order_index INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- Add funnel and stage to leads
|
||||
ALTER TABLE crm_leads
|
||||
ADD COLUMN funnel_id UUID REFERENCES crm_funnels(id) ON DELETE SET NULL,
|
||||
ADD COLUMN stage_id UUID REFERENCES crm_funnel_stages(id) ON DELETE SET NULL;
|
||||
|
||||
-- Indices
|
||||
CREATE INDEX idx_crm_funnels_tenant_id ON crm_funnels(tenant_id);
|
||||
CREATE INDEX idx_crm_funnel_stages_funnel_id ON crm_funnel_stages(funnel_id);
|
||||
CREATE INDEX idx_crm_leads_funnel_id ON crm_leads(funnel_id);
|
||||
CREATE INDEX idx_crm_leads_stage_id ON crm_leads(stage_id);
|
||||
|
||||
-- Triggers for updated_at
|
||||
CREATE TRIGGER update_crm_funnels_updated_at BEFORE UPDATE ON crm_funnels
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
CREATE TRIGGER update_crm_funnel_stages_updated_at BEFORE UPDATE ON crm_funnel_stages
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- Function to create default funnel for a tenant
|
||||
CREATE OR REPLACE FUNCTION create_default_crm_funnel(t_id UUID)
|
||||
RETURNS UUID AS $$
|
||||
DECLARE
|
||||
f_id UUID;
|
||||
BEGIN
|
||||
INSERT INTO crm_funnels (tenant_id, name, description, is_default)
|
||||
VALUES (t_id, 'Funil de Vendas Padrão', 'Monitoramento básico de leads', true)
|
||||
RETURNING id INTO f_id;
|
||||
|
||||
INSERT INTO crm_funnel_stages (funnel_id, name, color, order_index) VALUES
|
||||
(f_id, 'Novo', '#3b82f6', 0),
|
||||
(f_id, 'Qualificado', '#10b981', 1),
|
||||
(f_id, 'Proposta', '#f59e0b', 2),
|
||||
(f_id, 'Negociação', '#8b5cf6', 3),
|
||||
(f_id, 'Fechado', '#10b981', 4),
|
||||
(f_id, 'Perdido', '#ef4444', 5);
|
||||
|
||||
RETURN f_id;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
8
postgres/migrations/021_link_funnels_to_campaigns.sql
Normal file
8
postgres/migrations/021_link_funnels_to_campaigns.sql
Normal file
@@ -0,0 +1,8 @@
|
||||
-- Migration: Link funnels to campaigns (lists)
|
||||
-- Description: Allows associating a campaign with a specific sales funnel
|
||||
|
||||
ALTER TABLE crm_lists
|
||||
ADD COLUMN funnel_id UUID REFERENCES crm_funnels(id) ON DELETE SET NULL;
|
||||
|
||||
-- Index for performance
|
||||
CREATE INDEX idx_crm_lists_funnel_id ON crm_lists(funnel_id);
|
||||
26
postgres/setup_owner_role.sql
Normal file
26
postgres/setup_owner_role.sql
Normal file
@@ -0,0 +1,26 @@
|
||||
-- Script para configurar o primeiro usuário de uma agência como "owner"
|
||||
-- Este script deve ser executado manualmente após criar o primeiro usuário
|
||||
|
||||
-- Opção 1: Se você conhece o email do usuário, use:
|
||||
-- UPDATE users
|
||||
-- SET agency_role = 'owner'
|
||||
-- WHERE email = 'seu-email@exemplo.com' AND role = 'ADMIN_AGENCIA';
|
||||
|
||||
-- Opção 2: Se você quer configurar o primeiro ADMIN_AGENCIA de cada agência como owner:
|
||||
UPDATE users u1
|
||||
SET agency_role = 'owner'
|
||||
WHERE role = 'ADMIN_AGENCIA'
|
||||
AND agency_role IS NULL
|
||||
AND u1.id = (
|
||||
SELECT id FROM users u2
|
||||
WHERE u2.tenant_id = u1.tenant_id
|
||||
AND u2.role = 'ADMIN_AGENCIA'
|
||||
ORDER BY u2.created_at ASC
|
||||
LIMIT 1
|
||||
);
|
||||
|
||||
-- Verificar resultado
|
||||
SELECT id, email, role, agency_role, tenant_id
|
||||
FROM users
|
||||
WHERE role = 'ADMIN_AGENCIA'
|
||||
ORDER BY tenant_id, created_at;
|
||||
Reference in New Issue
Block a user