-- ============================================================================= -- MICHANGARRITO - 13 REFERRALS -- ============================================================================= -- Programa de referidos: codigos, referidos y recompensas -- ============================================================================= -- Codigos de referido CREATE TABLE IF NOT EXISTS subscriptions.referral_codes ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE, code VARCHAR(20) UNIQUE NOT NULL, active BOOLEAN DEFAULT true, uses_count INTEGER DEFAULT 0, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX idx_referral_codes_tenant ON subscriptions.referral_codes(tenant_id); CREATE INDEX idx_referral_codes_code ON subscriptions.referral_codes(code); CREATE TRIGGER update_referral_codes_updated_at BEFORE UPDATE ON subscriptions.referral_codes FOR EACH ROW EXECUTE FUNCTION update_updated_at(); -- Referidos CREATE TABLE IF NOT EXISTS subscriptions.referrals ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), referrer_tenant_id UUID NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE, referred_tenant_id UUID NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE, code_used VARCHAR(20) NOT NULL, status VARCHAR(20) DEFAULT 'pending', -- pending: referido registrado, no ha pagado -- converted: referido pago primer mes -- rewarded: recompensa aplicada al referidor -- expired: referido no pago en tiempo (30 dias) referred_discount_applied BOOLEAN DEFAULT false, referrer_reward_applied BOOLEAN DEFAULT false, converted_at TIMESTAMPTZ, reward_applied_at TIMESTAMPTZ, expires_at TIMESTAMPTZ, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE(referred_tenant_id) ); CREATE INDEX idx_referrals_referrer ON subscriptions.referrals(referrer_tenant_id); CREATE INDEX idx_referrals_referred ON subscriptions.referrals(referred_tenant_id); CREATE INDEX idx_referrals_status ON subscriptions.referrals(status); CREATE TRIGGER update_referrals_updated_at BEFORE UPDATE ON subscriptions.referrals FOR EACH ROW EXECUTE FUNCTION update_updated_at(); -- Recompensas de referidos CREATE TABLE IF NOT EXISTS subscriptions.referral_rewards ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE, referral_id UUID NOT NULL REFERENCES subscriptions.referrals(id) ON DELETE CASCADE, type VARCHAR(20) NOT NULL DEFAULT 'free_month', -- free_month: mes gratis de suscripcion -- discount: descuento porcentual months_earned INTEGER DEFAULT 0, months_used INTEGER DEFAULT 0, discount_percent INTEGER DEFAULT 0, expires_at TIMESTAMPTZ, status VARCHAR(20) DEFAULT 'available', -- available: disponible para usar -- used: ya se uso -- expired: expiro sin usarse created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX idx_referral_rewards_tenant ON subscriptions.referral_rewards(tenant_id); CREATE INDEX idx_referral_rewards_status ON subscriptions.referral_rewards(status); CREATE TRIGGER update_referral_rewards_updated_at BEFORE UPDATE ON subscriptions.referral_rewards FOR EACH ROW EXECUTE FUNCTION update_updated_at(); -- Funcion para generar codigo de referido unico CREATE OR REPLACE FUNCTION generate_referral_code(prefix TEXT DEFAULT 'MCH') RETURNS TEXT AS $$ DECLARE new_code TEXT; code_exists BOOLEAN; BEGIN LOOP -- Generar codigo alfanumerico de 6 caracteres new_code := prefix || '-' || upper(substr(md5(random()::text), 1, 6)); -- Verificar si ya existe SELECT EXISTS( SELECT 1 FROM subscriptions.referral_codes WHERE code = new_code ) INTO code_exists; -- Si no existe, salir del loop EXIT WHEN NOT code_exists; END LOOP; RETURN new_code; END; $$ LANGUAGE plpgsql; -- Funcion para obtener estadisticas de referidos CREATE OR REPLACE FUNCTION get_referral_stats(p_tenant_id UUID) RETURNS TABLE ( total_invited INTEGER, total_converted INTEGER, total_pending INTEGER, total_expired INTEGER, months_earned INTEGER, months_available INTEGER ) AS $$ BEGIN RETURN QUERY SELECT COUNT(*)::INTEGER as total_invited, COUNT(*) FILTER (WHERE r.status IN ('converted', 'rewarded'))::INTEGER as total_converted, COUNT(*) FILTER (WHERE r.status = 'pending')::INTEGER as total_pending, COUNT(*) FILTER (WHERE r.status = 'expired')::INTEGER as total_expired, COALESCE(SUM(rw.months_earned), 0)::INTEGER as months_earned, COALESCE(SUM(rw.months_earned - rw.months_used) FILTER (WHERE rw.status = 'available'), 0)::INTEGER as months_available FROM subscriptions.referrals r LEFT JOIN subscriptions.referral_rewards rw ON rw.referral_id = r.id AND rw.tenant_id = p_tenant_id WHERE r.referrer_tenant_id = p_tenant_id; END; $$ LANGUAGE plpgsql;