F3 schema-per-tenant: frontend usa tenantDb() pra tabelas tenant
- useTenantDb composable + lib/supabase/tenantClient (tenantDb/tenantSchemaName)
- tenantStore: getters activeTenantSlug/activeTenantSchema; my_tenants() RPC
passa a devolver slug+nome (migration 07)
- codemod scripts/codemod-tenant-db.py: supabase.from('<84 tabelas + 6 views
tenant>') -> tenantDb().from(...) em 139 arquivos (777 chamadas), remove
.eq('tenant_id') das cadeias tenant (173)
- passada manual (4 agentes): remove tenant_id de payloads insert/upsert/update,
selects, .or/.is de defaults; onConflict ajustado pros uniques sem tenant_id
(singletons usam 'singleton'); realtime de tabelas tenant aponta pro schema
do tenant ativo; repos dropam tenant_id defensivamente de payloads externos
- agendaSelects: tenant_id fora do AGENDA_EVENT_SELECT (quebraria PostgREST)
- zero embeds cross-schema (todos FK embeds sao tenant->tenant ou global->global)
- build de producao passa; 67 .js checados
Pendente (fora do escopo F3, sao cross-tenant/anon -> F4/F6):
- AgendadorPublicoPage (anon, resolve tenant por link_slug)
- Saas{Feriados,NotificationTemplates,DocumentTemplates,Whatsapp}Page
(gerenciam defaults do sistema / views cross-tenant)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
import { supabase } from '@/lib/supabase/client';
|
||||
import { tenantDb } from '@/lib/supabase/tenantClient';
|
||||
import { useTenantStore } from '@/stores/tenantStore';
|
||||
import { assertTenantId, getUid } from './_tenantGuards';
|
||||
import { BILLING_CONTRACT_SELECT } from './financialSelects';
|
||||
@@ -31,7 +32,7 @@ export async function listForPatient(patientId, { tenantId, includeDeleted = fal
|
||||
if (!patientId) return [];
|
||||
const tid = resolveTenantId(tenantId);
|
||||
|
||||
let q = supabase.from('billing_contracts').select(BILLING_CONTRACT_SELECT).eq('tenant_id', tid).eq('patient_id', patientId).order('created_at', { ascending: false });
|
||||
let q = tenantDb().from('billing_contracts').select(BILLING_CONTRACT_SELECT).eq('patient_id', patientId).order('created_at', { ascending: false });
|
||||
|
||||
if (!includeDeleted) q = q.is('deleted_at', null);
|
||||
|
||||
@@ -48,7 +49,7 @@ export async function getById(contractId, { tenantId } = {}) {
|
||||
if (!contractId) throw new Error('contractId obrigatório.');
|
||||
const tid = resolveTenantId(tenantId);
|
||||
|
||||
const { data, error } = await supabase.from('billing_contracts').select(BILLING_CONTRACT_SELECT).eq('id', contractId).eq('tenant_id', tid).maybeSingle();
|
||||
const { data, error } = await tenantDb().from('billing_contracts').select(BILLING_CONTRACT_SELECT).eq('id', contractId).maybeSingle();
|
||||
|
||||
if (error) throw error;
|
||||
return data || null;
|
||||
@@ -65,7 +66,6 @@ export async function create(payload) {
|
||||
const tid = resolveTenantId(payload.tenantId);
|
||||
|
||||
const row = {
|
||||
tenant_id: tid,
|
||||
owner_id: uid,
|
||||
patient_id: payload.patient_id,
|
||||
charging_style: payload.charging_style,
|
||||
@@ -77,7 +77,7 @@ export async function create(payload) {
|
||||
end_date: payload.end_date || null
|
||||
};
|
||||
|
||||
const { data, error } = await supabase.from('billing_contracts').insert([row]).select(BILLING_CONTRACT_SELECT).single();
|
||||
const { data, error } = await tenantDb().from('billing_contracts').insert([row]).select(BILLING_CONTRACT_SELECT).single();
|
||||
|
||||
if (error) throw error;
|
||||
return data;
|
||||
@@ -93,7 +93,7 @@ export async function update(contractId, patch, { tenantId } = {}) {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const { updated_at: _dropped, ...safePatch } = patch || {};
|
||||
|
||||
const { data, error } = await supabase.from('billing_contracts').update(safePatch).eq('id', contractId).eq('tenant_id', tid).select(BILLING_CONTRACT_SELECT).single();
|
||||
const { data, error } = await tenantDb().from('billing_contracts').update(safePatch).eq('id', contractId).select(BILLING_CONTRACT_SELECT).single();
|
||||
|
||||
if (error) throw error;
|
||||
return data;
|
||||
@@ -128,7 +128,7 @@ export async function findRecordsByRecurrence(recurrenceId, { tenantId } = {}) {
|
||||
if (!recurrenceId) return [];
|
||||
const tid = resolveTenantId(tenantId);
|
||||
|
||||
const { data, error } = await supabase.from('financial_records').select('id, status, agenda_evento_id, billing_contract_id').eq('tenant_id', tid).is('deleted_at', null).not('agenda_evento_id', 'is', null);
|
||||
const { data, error } = await tenantDb().from('financial_records').select('id, status, agenda_evento_id, billing_contract_id').is('deleted_at', null).not('agenda_evento_id', 'is', null);
|
||||
// NOTE: filter por recurrence_id requer join — fica como TODO no orchestrator
|
||||
// (memória project_cross_week_propagation: query records cross-week por recurrence_id).
|
||||
|
||||
|
||||
Reference in New Issue
Block a user