367 lines
11 KiB
SQL
367 lines
11 KiB
SQL
-- =====================================================
|
|
-- SCHEMA: crm
|
|
-- PROPOSITO: Customer Relationship Management
|
|
-- MODULOS: MGN-CRM (CRM)
|
|
-- FECHA: 2025-11-24
|
|
-- =====================================================
|
|
|
|
-- Crear schema
|
|
CREATE SCHEMA IF NOT EXISTS crm;
|
|
|
|
-- =====================================================
|
|
-- TYPES (ENUMs)
|
|
-- =====================================================
|
|
|
|
CREATE TYPE crm.lead_status AS ENUM (
|
|
'new',
|
|
'contacted',
|
|
'qualified',
|
|
'converted',
|
|
'lost'
|
|
);
|
|
|
|
CREATE TYPE crm.opportunity_status AS ENUM (
|
|
'open',
|
|
'won',
|
|
'lost'
|
|
);
|
|
|
|
CREATE TYPE crm.activity_type AS ENUM (
|
|
'call',
|
|
'email',
|
|
'meeting',
|
|
'task',
|
|
'note'
|
|
);
|
|
|
|
CREATE TYPE crm.lead_source AS ENUM (
|
|
'website',
|
|
'phone',
|
|
'email',
|
|
'referral',
|
|
'social_media',
|
|
'advertising',
|
|
'event',
|
|
'other'
|
|
);
|
|
|
|
-- =====================================================
|
|
-- TABLES
|
|
-- =====================================================
|
|
|
|
-- Tabla: lead_stages (Etapas del pipeline de leads)
|
|
CREATE TABLE crm.lead_stages (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
|
|
name VARCHAR(100) NOT NULL,
|
|
sequence INTEGER NOT NULL DEFAULT 10,
|
|
is_won BOOLEAN DEFAULT FALSE,
|
|
probability DECIMAL(5, 2) DEFAULT 0,
|
|
requirements TEXT,
|
|
|
|
active BOOLEAN DEFAULT TRUE,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
UNIQUE(tenant_id, name)
|
|
);
|
|
|
|
-- Tabla: opportunity_stages (Etapas del pipeline de oportunidades)
|
|
CREATE TABLE crm.opportunity_stages (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
|
|
name VARCHAR(100) NOT NULL,
|
|
sequence INTEGER NOT NULL DEFAULT 10,
|
|
is_won BOOLEAN DEFAULT FALSE,
|
|
probability DECIMAL(5, 2) DEFAULT 0,
|
|
requirements TEXT,
|
|
|
|
active BOOLEAN DEFAULT TRUE,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
UNIQUE(tenant_id, name)
|
|
);
|
|
|
|
-- Tabla: lost_reasons (Razones de perdida)
|
|
CREATE TABLE crm.lost_reasons (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
|
|
name VARCHAR(100) NOT NULL,
|
|
description TEXT,
|
|
|
|
active BOOLEAN DEFAULT TRUE,
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
UNIQUE(tenant_id, name)
|
|
);
|
|
|
|
-- Tabla: leads (Prospectos/Leads)
|
|
CREATE TABLE crm.leads (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
company_id UUID NOT NULL REFERENCES auth.companies(id) ON DELETE CASCADE,
|
|
|
|
-- Numeracion
|
|
name VARCHAR(255) NOT NULL,
|
|
ref VARCHAR(100),
|
|
|
|
-- Contacto
|
|
contact_name VARCHAR(255),
|
|
email VARCHAR(255),
|
|
phone VARCHAR(50),
|
|
mobile VARCHAR(50),
|
|
website VARCHAR(255),
|
|
|
|
-- Empresa del prospecto
|
|
company_name VARCHAR(255),
|
|
job_position VARCHAR(100),
|
|
industry VARCHAR(100),
|
|
employee_count VARCHAR(50),
|
|
annual_revenue DECIMAL(15, 2),
|
|
|
|
-- Direccion
|
|
street VARCHAR(255),
|
|
city VARCHAR(100),
|
|
state VARCHAR(100),
|
|
zip VARCHAR(20),
|
|
country VARCHAR(100),
|
|
|
|
-- Pipeline
|
|
stage_id UUID REFERENCES crm.lead_stages(id),
|
|
status crm.lead_status NOT NULL DEFAULT 'new',
|
|
|
|
-- Asignacion
|
|
user_id UUID REFERENCES auth.users(id),
|
|
sales_team_id UUID REFERENCES sales.sales_teams(id),
|
|
|
|
-- Origen
|
|
source crm.lead_source,
|
|
campaign_id UUID, -- Para futuro modulo marketing
|
|
medium VARCHAR(100),
|
|
|
|
-- Valoracion
|
|
priority INTEGER DEFAULT 0 CHECK (priority >= 0 AND priority <= 3),
|
|
probability DECIMAL(5, 2) DEFAULT 0,
|
|
expected_revenue DECIMAL(15, 2),
|
|
|
|
-- Fechas
|
|
date_open TIMESTAMP WITH TIME ZONE,
|
|
date_closed TIMESTAMP WITH TIME ZONE,
|
|
date_deadline DATE,
|
|
date_last_activity TIMESTAMP WITH TIME ZONE,
|
|
|
|
-- Conversion
|
|
partner_id UUID REFERENCES core.partners(id),
|
|
opportunity_id UUID, -- Se llena al convertir
|
|
|
|
-- Perdida
|
|
lost_reason_id UUID REFERENCES crm.lost_reasons(id),
|
|
lost_notes TEXT,
|
|
|
|
-- Notas
|
|
description TEXT,
|
|
notes TEXT,
|
|
tags VARCHAR(255)[],
|
|
|
|
-- Auditoria
|
|
created_by UUID REFERENCES auth.users(id),
|
|
updated_by UUID REFERENCES auth.users(id),
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- Tabla: opportunities (Oportunidades de venta)
|
|
CREATE TABLE crm.opportunities (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
company_id UUID NOT NULL REFERENCES auth.companies(id) ON DELETE CASCADE,
|
|
|
|
-- Numeracion
|
|
name VARCHAR(255) NOT NULL,
|
|
ref VARCHAR(100),
|
|
|
|
-- Cliente
|
|
partner_id UUID NOT NULL REFERENCES core.partners(id),
|
|
contact_name VARCHAR(255),
|
|
email VARCHAR(255),
|
|
phone VARCHAR(50),
|
|
|
|
-- Pipeline
|
|
stage_id UUID REFERENCES crm.opportunity_stages(id),
|
|
status crm.opportunity_status NOT NULL DEFAULT 'open',
|
|
|
|
-- Asignacion
|
|
user_id UUID REFERENCES auth.users(id),
|
|
sales_team_id UUID REFERENCES sales.sales_teams(id),
|
|
|
|
-- Valoracion
|
|
priority INTEGER DEFAULT 0 CHECK (priority >= 0 AND priority <= 3),
|
|
probability DECIMAL(5, 2) DEFAULT 0,
|
|
expected_revenue DECIMAL(15, 2),
|
|
recurring_revenue DECIMAL(15, 2),
|
|
recurring_plan VARCHAR(50),
|
|
|
|
-- Fechas
|
|
date_deadline DATE,
|
|
date_closed TIMESTAMP WITH TIME ZONE,
|
|
date_last_activity TIMESTAMP WITH TIME ZONE,
|
|
|
|
-- Origen (si viene de lead)
|
|
lead_id UUID REFERENCES crm.leads(id),
|
|
source crm.lead_source,
|
|
campaign_id UUID,
|
|
medium VARCHAR(100),
|
|
|
|
-- Cierre
|
|
lost_reason_id UUID REFERENCES crm.lost_reasons(id),
|
|
lost_notes TEXT,
|
|
|
|
-- Relaciones
|
|
quotation_id UUID REFERENCES sales.quotations(id),
|
|
order_id UUID REFERENCES sales.sales_orders(id),
|
|
|
|
-- Notas
|
|
description TEXT,
|
|
notes TEXT,
|
|
tags VARCHAR(255)[],
|
|
|
|
-- Auditoria
|
|
created_by UUID REFERENCES auth.users(id),
|
|
updated_by UUID REFERENCES auth.users(id),
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- Actualizar referencia circular en leads
|
|
ALTER TABLE crm.leads ADD CONSTRAINT fk_leads_opportunity
|
|
FOREIGN KEY (opportunity_id) REFERENCES crm.opportunities(id);
|
|
|
|
-- Tabla: crm_activities (Actividades CRM)
|
|
CREATE TABLE crm.activities (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
|
|
-- Referencia polimorfica
|
|
res_model VARCHAR(100) NOT NULL,
|
|
res_id UUID NOT NULL,
|
|
|
|
-- Actividad
|
|
activity_type crm.activity_type NOT NULL,
|
|
summary VARCHAR(255),
|
|
description TEXT,
|
|
|
|
-- Fechas
|
|
date_deadline DATE,
|
|
date_done TIMESTAMP WITH TIME ZONE,
|
|
|
|
-- Asignacion
|
|
user_id UUID REFERENCES auth.users(id),
|
|
assigned_to UUID REFERENCES auth.users(id),
|
|
|
|
-- Estado
|
|
done BOOLEAN DEFAULT FALSE,
|
|
|
|
-- Auditoria
|
|
created_by UUID REFERENCES auth.users(id),
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- =====================================================
|
|
-- INDEXES
|
|
-- =====================================================
|
|
|
|
CREATE INDEX idx_lead_stages_tenant ON crm.lead_stages(tenant_id);
|
|
CREATE INDEX idx_opportunity_stages_tenant ON crm.opportunity_stages(tenant_id);
|
|
CREATE INDEX idx_lost_reasons_tenant ON crm.lost_reasons(tenant_id);
|
|
|
|
CREATE INDEX idx_leads_tenant ON crm.leads(tenant_id);
|
|
CREATE INDEX idx_leads_company ON crm.leads(company_id);
|
|
CREATE INDEX idx_leads_status ON crm.leads(status);
|
|
CREATE INDEX idx_leads_stage ON crm.leads(stage_id);
|
|
CREATE INDEX idx_leads_user ON crm.leads(user_id);
|
|
CREATE INDEX idx_leads_partner ON crm.leads(partner_id);
|
|
CREATE INDEX idx_leads_email ON crm.leads(email);
|
|
|
|
CREATE INDEX idx_opportunities_tenant ON crm.opportunities(tenant_id);
|
|
CREATE INDEX idx_opportunities_company ON crm.opportunities(company_id);
|
|
CREATE INDEX idx_opportunities_status ON crm.opportunities(status);
|
|
CREATE INDEX idx_opportunities_stage ON crm.opportunities(stage_id);
|
|
CREATE INDEX idx_opportunities_user ON crm.opportunities(user_id);
|
|
CREATE INDEX idx_opportunities_partner ON crm.opportunities(partner_id);
|
|
|
|
CREATE INDEX idx_crm_activities_tenant ON crm.activities(tenant_id);
|
|
CREATE INDEX idx_crm_activities_model ON crm.activities(res_model, res_id);
|
|
CREATE INDEX idx_crm_activities_user ON crm.activities(assigned_to);
|
|
CREATE INDEX idx_crm_activities_deadline ON crm.activities(date_deadline);
|
|
|
|
-- =====================================================
|
|
-- TRIGGERS
|
|
-- =====================================================
|
|
|
|
CREATE TRIGGER update_lead_stages_timestamp
|
|
BEFORE UPDATE ON crm.lead_stages
|
|
FOR EACH ROW EXECUTE FUNCTION core.update_timestamp();
|
|
|
|
CREATE TRIGGER update_opportunity_stages_timestamp
|
|
BEFORE UPDATE ON crm.opportunity_stages
|
|
FOR EACH ROW EXECUTE FUNCTION core.update_timestamp();
|
|
|
|
CREATE TRIGGER update_leads_timestamp
|
|
BEFORE UPDATE ON crm.leads
|
|
FOR EACH ROW EXECUTE FUNCTION core.update_timestamp();
|
|
|
|
CREATE TRIGGER update_opportunities_timestamp
|
|
BEFORE UPDATE ON crm.opportunities
|
|
FOR EACH ROW EXECUTE FUNCTION core.update_timestamp();
|
|
|
|
CREATE TRIGGER update_crm_activities_timestamp
|
|
BEFORE UPDATE ON crm.activities
|
|
FOR EACH ROW EXECUTE FUNCTION core.update_timestamp();
|
|
|
|
-- =====================================================
|
|
-- ROW LEVEL SECURITY
|
|
-- =====================================================
|
|
|
|
-- Habilitar RLS
|
|
ALTER TABLE crm.lead_stages ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE crm.opportunity_stages ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE crm.lost_reasons ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE crm.leads ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE crm.opportunities ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE crm.activities ENABLE ROW LEVEL SECURITY;
|
|
|
|
-- Políticas de aislamiento por tenant
|
|
CREATE POLICY tenant_isolation_lead_stages ON crm.lead_stages
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_opportunity_stages ON crm.opportunity_stages
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_lost_reasons ON crm.lost_reasons
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_leads ON crm.leads
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_opportunities ON crm.opportunities
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
CREATE POLICY tenant_isolation_crm_activities ON crm.activities
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
|
|
|
-- =====================================================
|
|
-- COMMENTS
|
|
-- =====================================================
|
|
|
|
COMMENT ON TABLE crm.lead_stages IS 'Etapas del pipeline de leads';
|
|
COMMENT ON TABLE crm.opportunity_stages IS 'Etapas del pipeline de oportunidades';
|
|
COMMENT ON TABLE crm.lost_reasons IS 'Razones de perdida de leads/oportunidades';
|
|
COMMENT ON TABLE crm.leads IS 'Prospectos/leads de ventas';
|
|
COMMENT ON TABLE crm.opportunities IS 'Oportunidades de venta';
|
|
COMMENT ON TABLE crm.activities IS 'Actividades CRM (llamadas, reuniones, etc.)';
|