-- ===================================================== -- 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.)';