diff --git a/database-novo/migrations/20260423000004_whatsapp_low_balance_notify.sql b/database-novo/migrations/20260423000004_whatsapp_low_balance_notify.sql new file mode 100644 index 0000000..8ea04e7 --- /dev/null +++ b/database-novo/migrations/20260423000004_whatsapp_low_balance_notify.sql @@ -0,0 +1,96 @@ +-- ========================================================================== +-- Agencia PSI — Migracao: Alerta de saldo baixo WhatsApp +-- ========================================================================== +-- Criado por: Leonardo Nohama +-- Data: 2026-04-23 · Sao Carlos/SP — Brasil +-- +-- Fecha o loop do Marco B: tenant compra creditos → usa → saldo zera sem +-- aviso → PIX recusado surpresa. Agora o tenant e admins recebem +-- notificacao quando saldo cai abaixo do threshold configurado. +-- +-- Modelo: +-- - whatsapp_credits_balance ja tem: +-- low_balance_threshold (default 20) +-- low_balance_alerted_at (anti-spam; reset via add_whatsapp_credits) +-- - Falta so o trigger que dispara a notificacao. +-- +-- Fluxo: +-- 1. UPDATE em whatsapp_credits_balance (via deduct/adjust) +-- 2. Trigger BEFORE UPDATE detecta balance < threshold E alerted_at IS NULL +-- 3. Insere system_alert pros stakeholders (owner do canal WhatsApp +-- ativo + clinic_admin + tenant_admin do tenant, deduplicado) +-- 4. Seta NEW.low_balance_alerted_at = now() pra nao realertar +-- 5. Reset do alerted_at acontece em add_whatsapp_credits / topup +-- (ja existe: "low_balance_alerted_at = NULL" na RPC) +-- ========================================================================== + +CREATE OR REPLACE FUNCTION public.fn_whatsapp_low_balance_notify() +RETURNS TRIGGER +LANGUAGE plpgsql +SECURITY DEFINER +SET search_path = public +AS $$ +DECLARE + v_detail TEXT; +BEGIN + -- So alerta na transicao (alerted_at NULL) E se esta abaixo do threshold + IF NEW.balance < NEW.low_balance_threshold + AND NEW.low_balance_alerted_at IS NULL THEN + + v_detail := format( + 'Saldo atual: %s credito(s). Alerta configurado em %s. ' + 'Compre mais na loja para nao interromper envios via WhatsApp Oficial.', + NEW.balance, + NEW.low_balance_threshold + ); + + -- Stakeholders: owner do canal WhatsApp ativo + admins ativos do tenant + INSERT INTO public.notifications + (owner_id, tenant_id, type, ref_id, ref_table, payload) + SELECT + u.user_id, + NEW.tenant_id, + 'system_alert', + NEW.tenant_id, + 'whatsapp_credits_balance', + jsonb_build_object( + 'title', 'Saldo de WhatsApp baixo', + 'detail', v_detail, + 'severity', 'warn', + 'deeplink', '/configuracoes/creditos-whatsapp' + ) + FROM ( + SELECT owner_id AS user_id + FROM public.notification_channels + WHERE tenant_id = NEW.tenant_id + AND channel = 'whatsapp' + AND is_active = true + AND deleted_at IS NULL + UNION + SELECT user_id + FROM public.tenant_members + WHERE tenant_id = NEW.tenant_id + AND role IN ('clinic_admin', 'tenant_admin') + AND status = 'active' + ) u + WHERE u.user_id IS NOT NULL; + + -- Anti-spam: so alerta de novo depois que add_whatsapp_credits + -- reseta alerted_at pra NULL (acontece em purchase/topup) + NEW.low_balance_alerted_at := now(); + END IF; + + RETURN NEW; +END; +$$; + +COMMENT ON FUNCTION public.fn_whatsapp_low_balance_notify() IS + 'Trigger BEFORE UPDATE em whatsapp_credits_balance. Dispara notificacao system_alert quando saldo cruza low_balance_threshold pra baixo. Anti-spam via low_balance_alerted_at (resetado pelo add_whatsapp_credits).'; + +DROP TRIGGER IF EXISTS trg_whatsapp_low_balance_notify + ON public.whatsapp_credits_balance; + +CREATE TRIGGER trg_whatsapp_low_balance_notify + BEFORE UPDATE ON public.whatsapp_credits_balance + FOR EACH ROW + EXECUTE FUNCTION public.fn_whatsapp_low_balance_notify();