+ Menu Hover no Layout Rail, Twilio, Sms, Email, Templates, LNovo Layout Configurações
This commit is contained in:
132
database-novo/migrations/001_twilio_whatsapp_subaccount.sql
Normal file
132
database-novo/migrations/001_twilio_whatsapp_subaccount.sql
Normal file
@@ -0,0 +1,132 @@
|
||||
-- =============================================================================
|
||||
-- AgenciaPsi — Migration 001: Twilio WhatsApp Subaccounts
|
||||
-- =============================================================================
|
||||
-- Adiciona suporte a subcontas Twilio com número WhatsApp dedicado por tenant.
|
||||
-- Cada clínica/terapeuta recebe sua própria subconta Twilio com número exclusivo.
|
||||
-- =============================================================================
|
||||
|
||||
-- ── 1. Campos de subconta Twilio em notification_channels ──────────────────
|
||||
|
||||
ALTER TABLE public.notification_channels
|
||||
ADD COLUMN IF NOT EXISTS twilio_subaccount_sid text,
|
||||
ADD COLUMN IF NOT EXISTS twilio_phone_number text,
|
||||
ADD COLUMN IF NOT EXISTS twilio_phone_sid text,
|
||||
ADD COLUMN IF NOT EXISTS webhook_url text,
|
||||
ADD COLUMN IF NOT EXISTS cost_per_message_usd numeric(8,6) DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS price_per_message_brl numeric(8,4) DEFAULT 0,
|
||||
ADD COLUMN IF NOT EXISTS provisioned_at timestamp with time zone;
|
||||
|
||||
COMMENT ON COLUMN public.notification_channels.twilio_subaccount_sid IS 'SID da subconta Twilio criada para este tenant';
|
||||
COMMENT ON COLUMN public.notification_channels.twilio_phone_number IS 'Número WhatsApp provisionado (E.164, ex: +5511999990000)';
|
||||
COMMENT ON COLUMN public.notification_channels.twilio_phone_sid IS 'SID do número de telefone na subconta Twilio';
|
||||
COMMENT ON COLUMN public.notification_channels.webhook_url IS 'URL do webhook configurada na Twilio para receber callbacks de status';
|
||||
COMMENT ON COLUMN public.notification_channels.cost_per_message_usd IS 'Custo real Twilio por mensagem WhatsApp (USD)';
|
||||
COMMENT ON COLUMN public.notification_channels.price_per_message_brl IS 'Valor cobrado do tenant por mensagem (BRL, inclui margem SaaS)';
|
||||
COMMENT ON COLUMN public.notification_channels.provisioned_at IS 'Timestamp do provisionamento da subconta';
|
||||
|
||||
-- Índice para busca rápida por subconta
|
||||
CREATE INDEX IF NOT EXISTS idx_notification_channels_twilio_subaccount_sid
|
||||
ON public.notification_channels (twilio_subaccount_sid)
|
||||
WHERE twilio_subaccount_sid IS NOT NULL;
|
||||
|
||||
-- ── 2. Tabela de consumo por subconta ─────────────────────────────────────
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.twilio_subaccount_usage (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
tenant_id uuid NOT NULL,
|
||||
channel_id uuid NOT NULL,
|
||||
twilio_subaccount_sid text NOT NULL,
|
||||
period_start date NOT NULL,
|
||||
period_end date NOT NULL,
|
||||
messages_sent integer DEFAULT 0 NOT NULL,
|
||||
messages_delivered integer DEFAULT 0 NOT NULL,
|
||||
messages_failed integer DEFAULT 0 NOT NULL,
|
||||
cost_usd numeric(12,6) DEFAULT 0 NOT NULL,
|
||||
cost_brl numeric(12,4) DEFAULT 0 NOT NULL,
|
||||
revenue_brl numeric(12,4) DEFAULT 0 NOT NULL,
|
||||
margin_brl numeric(12,4) GENERATED ALWAYS AS (revenue_brl - cost_brl) STORED,
|
||||
usd_brl_rate numeric(8,4) DEFAULT 0,
|
||||
synced_at timestamp with time zone DEFAULT now(),
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
|
||||
CONSTRAINT twilio_subaccount_usage_pkey PRIMARY KEY (id),
|
||||
CONSTRAINT twilio_subaccount_usage_channel_fk
|
||||
FOREIGN KEY (channel_id) REFERENCES public.notification_channels(id) ON DELETE CASCADE,
|
||||
CONSTRAINT twilio_subaccount_usage_period_check
|
||||
CHECK (period_end >= period_start)
|
||||
);
|
||||
|
||||
COMMENT ON TABLE public.twilio_subaccount_usage IS
|
||||
'Consumo mensal de mensagens WhatsApp por subconta Twilio. Sincronizado via Edge Function.';
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_twilio_usage_tenant_period
|
||||
ON public.twilio_subaccount_usage (tenant_id, period_start DESC);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_twilio_usage_channel
|
||||
ON public.twilio_subaccount_usage (channel_id, period_start DESC);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_twilio_usage_unique_period
|
||||
ON public.twilio_subaccount_usage (channel_id, period_start, period_end);
|
||||
|
||||
ALTER TABLE public.twilio_subaccount_usage OWNER TO supabase_admin;
|
||||
|
||||
-- ── 3. RLS: twilio_subaccount_usage ───────────────────────────────────────
|
||||
|
||||
ALTER TABLE public.twilio_subaccount_usage ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- Tenant vê apenas seu próprio consumo
|
||||
CREATE POLICY "tenant_select_own_usage"
|
||||
ON public.twilio_subaccount_usage
|
||||
FOR SELECT
|
||||
USING (
|
||||
tenant_id IN (
|
||||
SELECT tenant_id FROM public.tenant_members
|
||||
WHERE user_id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
-- Apenas service_role pode inserir/atualizar (via Edge Function)
|
||||
CREATE POLICY "service_role_manage_usage"
|
||||
ON public.twilio_subaccount_usage
|
||||
FOR ALL
|
||||
USING (auth.role() = 'service_role');
|
||||
|
||||
-- ── 4. RLS: notification_channels — acesso ao twilio_subaccount_sid ───────
|
||||
-- As políticas existentes já cobrem SELECT/UPDATE. Nenhuma alteração necessária.
|
||||
|
||||
-- ── 5. View: resumo de subcontas para o painel SaaS admin ─────────────────
|
||||
|
||||
CREATE OR REPLACE VIEW public.v_twilio_whatsapp_overview AS
|
||||
SELECT
|
||||
nc.id AS channel_id,
|
||||
nc.tenant_id,
|
||||
nc.owner_id,
|
||||
nc.is_active,
|
||||
nc.connection_status,
|
||||
nc.display_name,
|
||||
nc.twilio_subaccount_sid,
|
||||
nc.twilio_phone_number,
|
||||
nc.twilio_phone_sid,
|
||||
nc.cost_per_message_usd,
|
||||
nc.price_per_message_brl,
|
||||
nc.provisioned_at,
|
||||
nc.created_at,
|
||||
nc.updated_at,
|
||||
-- Uso do mês atual
|
||||
COALESCE(u.messages_sent, 0) AS current_month_sent,
|
||||
COALESCE(u.messages_delivered, 0) AS current_month_delivered,
|
||||
COALESCE(u.messages_failed, 0) AS current_month_failed,
|
||||
COALESCE(u.cost_usd, 0) AS current_month_cost_usd,
|
||||
COALESCE(u.cost_brl, 0) AS current_month_cost_brl,
|
||||
COALESCE(u.revenue_brl, 0) AS current_month_revenue_brl,
|
||||
COALESCE(u.margin_brl, 0) AS current_month_margin_brl
|
||||
FROM public.notification_channels nc
|
||||
LEFT JOIN public.twilio_subaccount_usage u
|
||||
ON u.channel_id = nc.id
|
||||
AND u.period_start = date_trunc('month', CURRENT_DATE)::date
|
||||
WHERE nc.channel = 'whatsapp'
|
||||
AND nc.provider = 'twilio'
|
||||
AND nc.deleted_at IS NULL;
|
||||
|
||||
COMMENT ON VIEW public.v_twilio_whatsapp_overview IS
|
||||
'Visão consolidada de subcontas Twilio WhatsApp com uso do mês corrente.';
|
||||
Reference in New Issue
Block a user