-- ========================================================================== -- Agencia PSI — Migracao: Notas internas de conversa (CRM Grupo 3.3) -- ========================================================================== -- Criado por: Leonardo Nohama -- Data: 2026-04-21 · Sao Carlos/SP — Brasil -- -- Notas internas da equipe em cada thread de conversa (WhatsApp/SMS/etc). -- NAO sao enviadas ao paciente — apenas visiveis aos membros do tenant. -- -- thread_key segue o padrao de conversation_threads: -- - '' → thread de paciente conhecido -- - 'anon:' → thread de numero nao identificado -- -- RLS: -- - READ/CREATE: qualquer membro ativo do tenant -- - UPDATE/DELETE: apenas o criador da nota OU saas_admin -- ========================================================================== CREATE TABLE IF NOT EXISTS public.conversation_notes ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE, thread_key TEXT NOT NULL, patient_id UUID REFERENCES public.patients(id) ON DELETE SET NULL, contact_number TEXT, body TEXT NOT NULL CHECK (length(body) > 0 AND length(body) <= 4000), created_by UUID NOT NULL REFERENCES auth.users(id) ON DELETE SET NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), deleted_at TIMESTAMPTZ ); -- Indexes CREATE INDEX IF NOT EXISTS idx_conv_notes_tenant_thread ON public.conversation_notes (tenant_id, thread_key, created_at DESC) WHERE deleted_at IS NULL; CREATE INDEX IF NOT EXISTS idx_conv_notes_patient ON public.conversation_notes (patient_id, created_at DESC) WHERE deleted_at IS NULL AND patient_id IS NOT NULL; CREATE INDEX IF NOT EXISTS idx_conv_notes_created_by ON public.conversation_notes (created_by, created_at DESC) WHERE deleted_at IS NULL; -- Trigger de updated_at (usa funcao existente set_updated_at) DROP TRIGGER IF EXISTS trg_conv_notes_updated_at ON public.conversation_notes; CREATE TRIGGER trg_conv_notes_updated_at BEFORE UPDATE ON public.conversation_notes FOR EACH ROW EXECUTE FUNCTION public.set_updated_at(); COMMENT ON TABLE public.conversation_notes IS 'Notas internas por thread de conversa. Visiveis apenas aos membros do tenant; nao enviadas ao paciente.'; -- -------------------------------------------------------------------------- -- RLS -- -------------------------------------------------------------------------- ALTER TABLE public.conversation_notes ENABLE ROW LEVEL SECURITY; -- SELECT: membro ativo do tenant OU saas_admin DROP POLICY IF EXISTS "conv_notes: select tenant members" ON public.conversation_notes; CREATE POLICY "conv_notes: select tenant members" ON public.conversation_notes FOR SELECT TO authenticated USING ( deleted_at IS NULL AND ( public.is_saas_admin() OR EXISTS ( SELECT 1 FROM public.tenant_members tm WHERE tm.user_id = auth.uid() AND tm.tenant_id = conversation_notes.tenant_id AND tm.status = 'active' ) ) ); -- INSERT: membro ativo do tenant, created_by deve ser o proprio usuario DROP POLICY IF EXISTS "conv_notes: insert tenant members" ON public.conversation_notes; CREATE POLICY "conv_notes: insert tenant members" ON public.conversation_notes FOR INSERT TO authenticated WITH CHECK ( created_by = auth.uid() AND ( public.is_saas_admin() OR EXISTS ( SELECT 1 FROM public.tenant_members tm WHERE tm.user_id = auth.uid() AND tm.tenant_id = conversation_notes.tenant_id AND tm.status = 'active' ) ) ); -- UPDATE: apenas criador OU saas_admin DROP POLICY IF EXISTS "conv_notes: update creator or saas" ON public.conversation_notes; CREATE POLICY "conv_notes: update creator or saas" ON public.conversation_notes FOR UPDATE TO authenticated USING ( deleted_at IS NULL AND (created_by = auth.uid() OR public.is_saas_admin()) ) WITH CHECK ( created_by = (SELECT created_by FROM public.conversation_notes WHERE id = conversation_notes.id) ); -- DELETE: soft delete via UPDATE deleted_at (nao permite hard delete) -- Mantemos politica de DELETE bloqueada por default (sem policy = nao permitido) -- ========================================================================== -- FIM DA MIGRACAO -- ==========================================================================