ee82985dc3
DB (supabase_admin, manual/f6_2g_sql_to_plpgsql.supabase_admin.sql): SQL puro nao permite set_config dinamico -> converte 5 pra plpgsql + p_tenant_id + _tenant_route: - get_financial_summary, get_financial_report, get_patient_session_counts (remove filtro tenant_id IN, schema-scoped) - list_financial_records: RETURNS SETOF financial_records -> jsonb (array, transparente no FE) - get_entity_primary_phone (interno, 0 callers): herda search_path do chamador (sem SET, unqualified) Smoke: get_financial_summary + list_financial_records (array 5) OK. Frontend (3 arquivos): p_tenant_id de activeTenantId/resolveTenantId nas chamadas de get_financial_summary/list_financial_records/get_patient_session_ counts. Build passa. === F6.2 COMPLETA: 66 funcoes migradas (triggers A/B/C + RPCs D/E/F/G) === Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
127 lines
7.5 KiB
PL/PgSQL
127 lines
7.5 KiB
PL/PgSQL
-- =============================================================================
|
|
-- F6.2 Lote G — funções SQL puras → plpgsql + roteamento por tenant
|
|
--
|
|
-- ⚠️ APLICAR COMO supabase_admin.
|
|
--
|
|
-- SQL puro não permite set_config dinâmico do search_path (limitação 3 do
|
|
-- blueprint) → converter pra plpgsql. Adicionam p_tenant_id + _tenant_route.
|
|
-- RETURNS SETOF <tabela_tenant> → jsonb. get_entity_primary_phone (interno,
|
|
-- 0 callers) herda search_path do chamador (sem SET, unqualified).
|
|
-- =============================================================================
|
|
|
|
BEGIN;
|
|
|
|
DROP FUNCTION IF EXISTS public.get_financial_summary(uuid, integer, integer);
|
|
DROP FUNCTION IF EXISTS public.get_financial_summary(uuid, uuid, integer, integer);
|
|
CREATE FUNCTION public.get_financial_summary(p_tenant_id uuid, p_owner_id uuid, p_year integer, p_month integer)
|
|
RETURNS TABLE(total_receitas numeric, total_despesas numeric, total_pendente numeric, saldo_liquido numeric, total_repasse numeric, count_receitas bigint, count_despesas bigint)
|
|
LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public','pg_temp'
|
|
AS $$
|
|
BEGIN
|
|
PERFORM set_config('search_path', public._tenant_route(p_tenant_id) || ',public,pg_temp', true);
|
|
RETURN QUERY
|
|
SELECT
|
|
COALESCE(SUM(amount) FILTER (WHERE type='receita' AND status='paid'), 0),
|
|
COALESCE(SUM(amount) FILTER (WHERE type='despesa' AND status='paid'), 0),
|
|
COALESCE(SUM(amount) FILTER (WHERE status IN ('pending','overdue')), 0),
|
|
COALESCE(SUM(amount) FILTER (WHERE type='receita' AND status='paid'), 0) - COALESCE(SUM(amount) FILTER (WHERE type='despesa' AND status='paid'), 0),
|
|
COALESCE(SUM(clinic_fee_amount) FILTER (WHERE type='receita' AND status='paid'), 0),
|
|
COUNT(*) FILTER (WHERE type='receita' AND deleted_at IS NULL),
|
|
COUNT(*) FILTER (WHERE type='despesa' AND deleted_at IS NULL)
|
|
FROM financial_records
|
|
WHERE owner_id = p_owner_id AND deleted_at IS NULL
|
|
AND EXTRACT(YEAR FROM COALESCE(paid_at::date, due_date, created_at::date)) = p_year
|
|
AND EXTRACT(MONTH FROM COALESCE(paid_at::date, due_date, created_at::date)) = p_month;
|
|
END $$;
|
|
|
|
-- list_financial_records: RETURNS SETOF financial_records → jsonb (array)
|
|
DROP FUNCTION IF EXISTS public.list_financial_records(uuid, integer, integer, text, text, uuid, integer, integer);
|
|
DROP FUNCTION IF EXISTS public.list_financial_records(uuid, uuid, integer, integer, text, text, uuid, integer, integer);
|
|
CREATE FUNCTION public.list_financial_records(p_tenant_id uuid, p_owner_id uuid, p_year integer DEFAULT NULL, p_month integer DEFAULT NULL, p_type text DEFAULT NULL, p_status text DEFAULT NULL, p_patient_id uuid DEFAULT NULL, p_limit integer DEFAULT 50, p_offset integer DEFAULT 0)
|
|
RETURNS jsonb LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public','pg_temp'
|
|
AS $$
|
|
DECLARE v_result jsonb;
|
|
BEGIN
|
|
PERFORM set_config('search_path', public._tenant_route(p_tenant_id) || ',public,pg_temp', true);
|
|
SELECT COALESCE(jsonb_agg(row_json), '[]'::jsonb) INTO v_result FROM (
|
|
SELECT to_jsonb(fr) AS row_json
|
|
FROM financial_records fr
|
|
WHERE fr.owner_id = p_owner_id AND fr.deleted_at IS NULL
|
|
AND (p_type IS NULL OR fr.type::text = p_type)
|
|
AND (p_status IS NULL OR fr.status = p_status)
|
|
AND (p_patient_id IS NULL OR fr.patient_id = p_patient_id)
|
|
AND (p_year IS NULL OR EXTRACT(YEAR FROM COALESCE(fr.paid_at::date, fr.due_date, fr.created_at::date)) = p_year)
|
|
AND (p_month IS NULL OR EXTRACT(MONTH FROM COALESCE(fr.paid_at::date, fr.due_date, fr.created_at::date)) = p_month)
|
|
ORDER BY COALESCE(fr.paid_at, fr.due_date::timestamptz, fr.created_at) DESC
|
|
LIMIT p_limit OFFSET p_offset
|
|
) sub;
|
|
RETURN v_result;
|
|
END $$;
|
|
|
|
DROP FUNCTION IF EXISTS public.get_patient_session_counts(uuid[]);
|
|
DROP FUNCTION IF EXISTS public.get_patient_session_counts(uuid, uuid[]);
|
|
CREATE FUNCTION public.get_patient_session_counts(p_tenant_id uuid, p_patient_ids uuid[])
|
|
RETURNS TABLE(patient_id uuid, session_count integer, last_session_at timestamptz)
|
|
LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public','pg_temp'
|
|
AS $$
|
|
BEGIN
|
|
PERFORM set_config('search_path', public._tenant_route(p_tenant_id) || ',public,pg_temp', true);
|
|
RETURN QUERY
|
|
SELECT ae.patient_id, COUNT(*)::int, MAX(ae.inicio_em)
|
|
FROM agenda_eventos ae
|
|
WHERE ae.patient_id = ANY(p_patient_ids)
|
|
GROUP BY ae.patient_id;
|
|
END $$;
|
|
|
|
DROP FUNCTION IF EXISTS public.get_financial_report(uuid, date, date, text);
|
|
DROP FUNCTION IF EXISTS public.get_financial_report(uuid, uuid, date, date, text);
|
|
CREATE FUNCTION public.get_financial_report(p_tenant_id uuid, p_owner_id uuid, p_start_date date, p_end_date date, p_group_by text DEFAULT 'month')
|
|
RETURNS TABLE(group_key text, group_label text, total_receitas numeric, total_despesas numeric, saldo numeric, total_pendente numeric, total_overdue numeric, count_records bigint)
|
|
LANGUAGE plpgsql STABLE SECURITY DEFINER SET search_path TO 'public','pg_temp'
|
|
AS $$
|
|
BEGIN
|
|
PERFORM set_config('search_path', public._tenant_route(p_tenant_id) || ',public,pg_temp', true);
|
|
RETURN QUERY
|
|
WITH base AS (
|
|
SELECT fr.type, fr.amount, fr.final_amount, fr.status, fr.deleted_at,
|
|
CASE p_group_by
|
|
WHEN 'month' THEN TO_CHAR(COALESCE(fr.paid_at::date, fr.due_date, fr.created_at::date),'YYYY-MM')
|
|
WHEN 'week' THEN TO_CHAR(COALESCE(fr.paid_at::date, fr.due_date, fr.created_at::date),'IYYY-"W"IW')
|
|
WHEN 'category' THEN COALESCE(fr.category_id::text, fr.category, 'sem_categoria')
|
|
WHEN 'patient' THEN COALESCE(fr.patient_id::text, 'sem_paciente')
|
|
ELSE NULL END AS gkey,
|
|
CASE p_group_by
|
|
WHEN 'month' THEN TO_CHAR(COALESCE(fr.paid_at::date, fr.due_date, fr.created_at::date),'YYYY-MM')
|
|
WHEN 'week' THEN TO_CHAR(COALESCE(fr.paid_at::date, fr.due_date, fr.created_at::date),'IYYY-"W"IW')
|
|
WHEN 'category' THEN COALESCE(fc.name, fr.category, 'Sem categoria')
|
|
WHEN 'patient' THEN COALESCE(p.nome_completo, fr.patient_id::text, 'Sem paciente')
|
|
ELSE NULL END AS glabel
|
|
FROM financial_records fr
|
|
LEFT JOIN financial_categories fc ON fc.id = fr.category_id
|
|
LEFT JOIN patients p ON p.id = fr.patient_id
|
|
WHERE fr.owner_id = p_owner_id AND fr.deleted_at IS NULL
|
|
AND COALESCE(fr.paid_at::date, fr.due_date, fr.created_at::date) BETWEEN p_start_date AND p_end_date
|
|
)
|
|
SELECT gkey, glabel,
|
|
COALESCE(SUM(final_amount) FILTER (WHERE type='receita' AND status='paid'),0),
|
|
COALESCE(SUM(final_amount) FILTER (WHERE type='despesa' AND status='paid'),0),
|
|
COALESCE(SUM(final_amount) FILTER (WHERE type='receita' AND status='paid'),0) - COALESCE(SUM(final_amount) FILTER (WHERE type='despesa' AND status='paid'),0),
|
|
COALESCE(SUM(final_amount) FILTER (WHERE status='pending'),0),
|
|
COALESCE(SUM(final_amount) FILTER (WHERE status='overdue'),0),
|
|
COUNT(*)
|
|
FROM base WHERE gkey IS NOT NULL GROUP BY gkey, glabel ORDER BY gkey ASC;
|
|
END $$;
|
|
|
|
-- get_entity_primary_phone: interno (0 callers). Sem SET search_path → herda do
|
|
-- chamador (que roteia pro schema). Unqualified. Mantém assinatura.
|
|
CREATE OR REPLACE FUNCTION public.get_entity_primary_phone(p_entity_type text, p_entity_id uuid)
|
|
RETURNS text LANGUAGE sql STABLE SECURITY DEFINER
|
|
AS $$
|
|
SELECT number FROM contact_phones
|
|
WHERE entity_type = p_entity_type AND entity_id = p_entity_id
|
|
ORDER BY is_primary DESC, position ASC, created_at ASC
|
|
LIMIT 1;
|
|
$$;
|
|
|
|
COMMIT;
|