-- ========================================================================== -- Agencia PSI — Migracao: Analytics SaaS de receita WhatsApp (Grupo 5) -- ========================================================================== -- 4 funcoes RPC pra popular cards novos no SaasDashboard: -- - saas_wa_credits_revenue_stats(from, to) → agregados globais -- - saas_wa_credits_top_packages(from, to) → ranking de pacotes -- - saas_wa_credits_usage_summary(from, to) → vendidos vs usados -- - saas_wa_credits_revenue_evolution(from,to,bucket) → serie temporal -- -- Fonte: whatsapp_credit_purchases (paid) + whatsapp_credits_balance/ -- whatsapp_credits_transactions. -- -- Apenas saas_admin pode chamar (RLS da policy + SECURITY DEFINER check). -- ========================================================================== -- --------------------------------------------------------------------------- -- saas_wa_credits_revenue_stats: totais do periodo -- --------------------------------------------------------------------------- CREATE OR REPLACE FUNCTION public.saas_wa_credits_revenue_stats( p_from TIMESTAMPTZ DEFAULT (now() - interval '30 days'), p_to TIMESTAMPTZ DEFAULT now() ) RETURNS TABLE ( revenue_brl NUMERIC, purchases_count INT, tenants_count INT, credits_sold INT, avg_ticket_brl NUMERIC ) LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path = public AS $$ BEGIN IF NOT public.is_saas_admin() THEN RAISE EXCEPTION 'permission_denied'; END IF; RETURN QUERY SELECT COALESCE(SUM(p.amount_brl), 0)::NUMERIC AS revenue_brl, COUNT(*)::INT AS purchases_count, COUNT(DISTINCT p.tenant_id)::INT AS tenants_count, COALESCE(SUM(p.credits), 0)::INT AS credits_sold, CASE WHEN COUNT(*) = 0 THEN 0 ELSE ROUND(COALESCE(AVG(p.amount_brl), 0), 2) END AS avg_ticket_brl FROM public.whatsapp_credit_purchases p WHERE p.status = 'paid' AND p.paid_at >= p_from AND p.paid_at <= p_to; END; $$; REVOKE ALL ON FUNCTION public.saas_wa_credits_revenue_stats(TIMESTAMPTZ, TIMESTAMPTZ) FROM PUBLIC; GRANT EXECUTE ON FUNCTION public.saas_wa_credits_revenue_stats(TIMESTAMPTZ, TIMESTAMPTZ) TO authenticated, service_role; -- --------------------------------------------------------------------------- -- saas_wa_credits_top_packages: ranking dos pacotes mais vendidos -- --------------------------------------------------------------------------- CREATE OR REPLACE FUNCTION public.saas_wa_credits_top_packages( p_from TIMESTAMPTZ DEFAULT (now() - interval '30 days'), p_to TIMESTAMPTZ DEFAULT now() ) RETURNS TABLE ( package_id UUID, package_name TEXT, purchases_count INT, revenue_brl NUMERIC, credits_sold INT ) LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path = public AS $$ BEGIN IF NOT public.is_saas_admin() THEN RAISE EXCEPTION 'permission_denied'; END IF; RETURN QUERY SELECT p.package_id, -- Nome snapshot do momento da compra; se tem package_id, usa o nome -- atual pra consolidar pacotes renomeados COALESCE( (SELECT pk.name FROM public.whatsapp_credit_packages pk WHERE pk.id = p.package_id), p.package_name ) AS package_name, COUNT(*)::INT AS purchases_count, SUM(p.amount_brl)::NUMERIC AS revenue_brl, SUM(p.credits)::INT AS credits_sold FROM public.whatsapp_credit_purchases p WHERE p.status = 'paid' AND p.paid_at >= p_from AND p.paid_at <= p_to GROUP BY p.package_id, p.package_name ORDER BY revenue_brl DESC LIMIT 10; END; $$; REVOKE ALL ON FUNCTION public.saas_wa_credits_top_packages(TIMESTAMPTZ, TIMESTAMPTZ) FROM PUBLIC; GRANT EXECUTE ON FUNCTION public.saas_wa_credits_top_packages(TIMESTAMPTZ, TIMESTAMPTZ) TO authenticated, service_role; -- --------------------------------------------------------------------------- -- saas_wa_credits_usage_summary: vendidos vs usados (snapshot atual) -- --------------------------------------------------------------------------- CREATE OR REPLACE FUNCTION public.saas_wa_credits_usage_summary() RETURNS TABLE ( lifetime_purchased INT, lifetime_used INT, current_balance INT, usage_rate NUMERIC, tenants_with_balance INT ) LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path = public AS $$ BEGIN IF NOT public.is_saas_admin() THEN RAISE EXCEPTION 'permission_denied'; END IF; RETURN QUERY SELECT COALESCE(SUM(lifetime_purchased), 0)::INT AS lifetime_purchased, COALESCE(SUM(lifetime_used), 0)::INT AS lifetime_used, COALESCE(SUM(balance), 0)::INT AS current_balance, CASE WHEN COALESCE(SUM(lifetime_purchased), 0) = 0 THEN 0 ELSE ROUND(100.0 * COALESCE(SUM(lifetime_used), 0) / SUM(lifetime_purchased), 1) END AS usage_rate, COUNT(*)::INT AS tenants_with_balance FROM public.whatsapp_credits_balance; END; $$; REVOKE ALL ON FUNCTION public.saas_wa_credits_usage_summary() FROM PUBLIC; GRANT EXECUTE ON FUNCTION public.saas_wa_credits_usage_summary() TO authenticated, service_role; -- --------------------------------------------------------------------------- -- saas_wa_credits_revenue_evolution: serie temporal -- --------------------------------------------------------------------------- CREATE OR REPLACE FUNCTION public.saas_wa_credits_revenue_evolution( p_from TIMESTAMPTZ DEFAULT (now() - interval '30 days'), p_to TIMESTAMPTZ DEFAULT now(), p_bucket_days INT DEFAULT 7 ) RETURNS TABLE ( bucket_start TIMESTAMPTZ, purchases_count INT, revenue_brl NUMERIC ) LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path = public AS $$ BEGIN IF NOT public.is_saas_admin() THEN RAISE EXCEPTION 'permission_denied'; END IF; RETURN QUERY WITH purchases AS ( SELECT p.paid_at, p.amount_brl FROM public.whatsapp_credit_purchases p WHERE p.status = 'paid' AND p.paid_at >= p_from AND p.paid_at <= p_to ), bucketed AS ( SELECT p_from + ( FLOOR(EXTRACT(EPOCH FROM (paid_at - p_from)) / (p_bucket_days * 86400))::INT * p_bucket_days * interval '1 day' ) AS bucket_start, amount_brl FROM purchases ) SELECT bucket_start, COUNT(*)::INT AS purchases_count, SUM(amount_brl)::NUMERIC AS revenue_brl FROM bucketed GROUP BY bucket_start ORDER BY bucket_start; END; $$; REVOKE ALL ON FUNCTION public.saas_wa_credits_revenue_evolution(TIMESTAMPTZ, TIMESTAMPTZ, INT) FROM PUBLIC; GRANT EXECUTE ON FUNCTION public.saas_wa_credits_revenue_evolution(TIMESTAMPTZ, TIMESTAMPTZ, INT) TO authenticated, service_role; COMMENT ON FUNCTION public.saas_wa_credits_revenue_stats(TIMESTAMPTZ, TIMESTAMPTZ) IS 'Totais de receita WhatsApp creditos no periodo (saas_admin).'; COMMENT ON FUNCTION public.saas_wa_credits_top_packages(TIMESTAMPTZ, TIMESTAMPTZ) IS 'Ranking pacotes mais vendidos no periodo (saas_admin).'; COMMENT ON FUNCTION public.saas_wa_credits_usage_summary() IS 'Snapshot atual: vendidos lifetime vs usados vs saldo (saas_admin).'; COMMENT ON FUNCTION public.saas_wa_credits_revenue_evolution(TIMESTAMPTZ, TIMESTAMPTZ, INT) IS 'Serie temporal de receita em buckets de N dias (saas_admin).';