Files
agenciapsilmno/src/features/agenda/composables/useFinancialExceptions.js
T
Leonardo a7f6bcbe66 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>
2026-06-13 04:44:59 -03:00

109 lines
4.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
|--------------------------------------------------------------------------
| Agência PSI
|--------------------------------------------------------------------------
| Criado e desenvolvido por Leonardo Nohama
|
| Tecnologia aplicada à escuta.
| Estrutura para o cuidado.
|
| Arquivo: src/features/agenda/composables/useFinancialExceptions.js
| Data: 2026
| Local: São Carlos/SP — Brasil
|--------------------------------------------------------------------------
| © 2026 — Todos os direitos reservados
|--------------------------------------------------------------------------
*/
// CRUD sobre a tabela public.financial_exceptions.
//
// Interface pública:
// exceptions ref([]) lista de regras do owner (próprias + da clínica)
// loading ref(false)
// error ref('')
//
// load(ownerId) carrega registros do owner + regras globais (owner_id IS NULL)
// save(payload) cria ou atualiza (id presente = update dos campos editáveis)
// remove(id) hard delete (apenas registros do próprio owner)
import { ref } from 'vue';
import { supabase } from '@/lib/supabase/client';
import { tenantDb } from '@/lib/supabase/tenantClient';
export function useFinancialExceptions() {
const exceptions = ref([]);
const loading = ref(false);
const error = ref('');
// ── Carregar exceções do owner + regras globais da clínica ───────────
async function load(ownerId) {
if (!ownerId) return;
loading.value = true;
error.value = '';
try {
const { data, error: err } = await tenantDb().from('financial_exceptions').select('*').or(`owner_id.eq.${ownerId},owner_id.is.null`).order('exception_type', { ascending: true }).order('created_at', { ascending: true });
if (err) throw err;
exceptions.value = data || [];
} catch (e) {
error.value = e?.message || 'Falha ao carregar exceções financeiras.';
exceptions.value = [];
} finally {
loading.value = false;
}
}
// ── Criar ou atualizar uma exceção ───────────────────────────────────
// Para UPDATE, apenas os campos editáveis são enviados:
// charge_mode, charge_value, charge_pct, min_hours_notice
// Regras globais (owner_id IS NULL) não devem ser editadas — o chamador
// é responsável por não chamar save() nesses registros.
async function save(payload) {
error.value = '';
try {
if (payload.id) {
const { error: err } = await tenantDb().from('financial_exceptions')
.update({
charge_mode: payload.charge_mode,
charge_value: payload.charge_value ?? null,
charge_pct: payload.charge_pct ?? null,
min_hours_notice: payload.min_hours_notice ?? null,
default_consume_on_miss: !!payload.default_consume_on_miss
})
.eq('id', payload.id);
if (err) throw err;
} else {
const { error: err } = await tenantDb().from('financial_exceptions').insert({
owner_id: payload.owner_id,
exception_type: payload.exception_type,
charge_mode: payload.charge_mode,
charge_value: payload.charge_value ?? null,
charge_pct: payload.charge_pct ?? null,
min_hours_notice: payload.min_hours_notice ?? null,
default_consume_on_miss: !!payload.default_consume_on_miss
});
if (err) throw err;
}
} catch (e) {
error.value = e?.message || 'Falha ao salvar exceção financeira.';
throw e;
}
}
// ── Hard delete — apenas registros do próprio owner ──────────────────
// Regras globais (owner_id IS NULL) são protegidas pelo RLS do banco;
// a UI também deve esconder o botão de remover nesses casos.
async function remove(id) {
error.value = '';
try {
const { error: err } = await tenantDb().from('financial_exceptions').delete().eq('id', id);
if (err) throw err;
exceptions.value = exceptions.value.filter((e) => e.id !== id);
} catch (e) {
error.value = e?.message || 'Falha ao remover exceção financeira.';
throw e;
}
}
return { exceptions, loading, error, load, save, remove };
}