Ajuste Convenios e Particular

This commit is contained in:
Leonardo
2026-03-13 21:09:34 -03:00
parent 06fb369beb
commit 587079e414
13 changed files with 971 additions and 277 deletions
@@ -33,7 +33,7 @@ const BASE_SELECT = `
determined_commitment_id, link_online, extra_fields, modalidade,
recurrence_id, recurrence_date,
mirror_of_event_id, price,
insurance_plan_id, insurance_guide_number, insurance_value,
insurance_plan_id, insurance_guide_number, insurance_value, insurance_plan_service_id,
patients!agenda_eventos_patient_id_fkey (
id, nome_completo, avatar_url
),
@@ -1,15 +1,17 @@
// src/features/agenda/composables/useInsurancePlans.js
//
// CRUD sobre a tabela public.insurance_plans.
//
// Interface pública:
// plans ref([]) lista de planos ativos do owner
// loading ref(false)
// error ref('')
// plans ref([]) todos os planos do owner (ativos e inativos)
// loading ref(false)
// error ref(null)
//
// load(ownerId) carrega todos os planos ativos
// save(payload) cria ou atualiza (id presente = update)
// remove(id) soft-delete (active = false)
// load(ownerId) carrega planos com seus procedimentos
// save(payload) insert ou update do plano (name, notes)
// toggle(id, active) alterna active do plano
// remove(id) soft-delete do plano
// savePlanService(payload) insert ou update de procedimento { id?, insurance_plan_id, name, value }
// togglePlanService(id, active) alterna active do procedimento
// removePlanService(id) DELETE definitivo do procedimento
import { ref } from 'vue'
import { supabase } from '@/lib/supabase/client'
@@ -17,24 +19,27 @@ import { supabase } from '@/lib/supabase/client'
export function useInsurancePlans () {
const plans = ref([])
const loading = ref(false)
const error = ref('')
const error = ref(null)
async function load (ownerId) {
if (!ownerId) return
loading.value = true
error.value = ''
error.value = null
try {
const { data, error: err } = await supabase
.from('insurance_plans')
.select('id, name, notes, default_value, active')
.select(`
*,
insurance_plan_services (
id, name, value, active
)
`)
.eq('owner_id', ownerId)
.eq('active', true)
.order('name', { ascending: true })
.order('name')
if (err) throw err
plans.value = data || []
} catch (e) {
error.value = e?.message || 'Falha ao carregar convênios.'
error.value = e?.message || 'Erro ao carregar convênios'
plans.value = []
} finally {
loading.value = false
@@ -42,42 +47,141 @@ export function useInsurancePlans () {
}
async function save (payload) {
error.value = ''
error.value = null
try {
if (payload.id) {
const { id, owner_id, tenant_id, ...fields } = payload
const { error: err } = await supabase
.from('insurance_plans')
.update(fields)
.eq('id', id)
.eq('owner_id', owner_id)
.update({
name: payload.name,
notes: payload.notes || null,
updated_at: new Date().toISOString(),
})
.eq('id', payload.id)
if (err) throw err
} else {
const { error: err } = await supabase
.from('insurance_plans')
.insert(payload)
.insert({
owner_id: payload.owner_id,
tenant_id: payload.tenant_id,
name: payload.name,
notes: payload.notes || null,
})
if (err) throw err
}
} catch (e) {
error.value = e?.message || 'Falha ao salvar convênio.'
error.value = e?.message || 'Erro ao salvar convênio'
throw e
}
}
async function toggle (id, active) {
error.value = null
try {
const { error: err } = await supabase
.from('insurance_plans')
.update({ active })
.eq('id', id)
if (err) throw err
const plan = plans.value.find(p => p.id === id)
if (plan) plan.active = active
} catch (e) {
error.value = e?.message || 'Erro ao atualizar convênio'
throw e
}
}
async function remove (id) {
error.value = ''
error.value = null
try {
const { error: err } = await supabase
.from('insurance_plans')
.update({ active: false })
.eq('id', id)
if (err) throw err
plans.value = plans.value.filter(p => p.id !== id)
const plan = plans.value.find(p => p.id === id)
if (plan) plan.active = false
} catch (e) {
error.value = e?.message || 'Falha ao remover convênio.'
error.value = e?.message || 'Erro ao remover convênio'
throw e
}
}
return { plans, loading, error, load, save, remove }
async function savePlanService (payload) {
error.value = null
try {
if (payload.id) {
const { error: err } = await supabase
.from('insurance_plan_services')
.update({
name: payload.name,
value: payload.value,
})
.eq('id', payload.id)
if (err) throw err
} else {
const { error: err } = await supabase
.from('insurance_plan_services')
.insert({
insurance_plan_id: payload.insurance_plan_id,
name: payload.name,
value: payload.value,
})
if (err) throw err
}
} catch (e) {
error.value = e?.message || 'Erro ao salvar procedimento'
throw e
}
}
async function togglePlanService (id, active) {
error.value = null
try {
const { error: err } = await supabase
.from('insurance_plan_services')
.update({ active })
.eq('id', id)
if (err) throw err
} catch (e) {
error.value = e?.message || 'Erro ao atualizar procedimento'
throw e
}
}
async function removeDefinitivo (id) {
error.value = null
try {
const { error: err } = await supabase
.from('insurance_plans')
.delete()
.eq('id', id)
if (err) throw err
plans.value = plans.value.filter(p => p.id !== id)
} catch (e) {
error.value = e?.message || 'Erro ao remover convênio'
throw e
}
}
async function removePlanService (id) {
error.value = null
try {
const { error: err } = await supabase
.from('insurance_plan_services')
.delete()
.eq('id', id)
if (err) throw err
} catch (e) {
error.value = e?.message || 'Erro ao remover procedimento'
throw e
}
}
return {
plans, loading, error,
load, save, toggle, remove, removeDefinitivo,
savePlanService, togglePlanService, removePlanService,
}
}
@@ -308,6 +308,12 @@ function buildOccurrence (rule, date, originalIso, exception) {
extra_fields: exception?.extra_fields || rule.extra_fields || null,
price: rule.price ?? null,
// convênio — herdado da regra de recorrência
insurance_plan_id: rule.insurance_plan_id ?? null,
insurance_guide_number: rule.insurance_guide_number ?? null,
insurance_value: rule.insurance_value ?? null,
insurance_plan_service_id: rule.insurance_plan_service_id ?? null,
// estado da exceção
exception_type: exType,
exception_id: exception?.id || null,
+34 -26
View File
@@ -1,17 +1,16 @@
// src/features/agenda/composables/useServices.js
//
// CRUD completo sobre a tabela public.services.
//
// Interface pública:
// services ref([]) lista de serviços ativos do owner
// loading ref(false)
// error ref('')
// services ref([]) todos os serviços do owner (ativos e inativos)
// loading ref(false)
// error ref('')
//
// load(ownerId) carrega todos os serviços ativos
// save(payload) cria ou atualiza (id presente = update)
// remove(id) soft-delete (active = false)
// getDefaultPrice() preço do primeiro serviço ativo, ou null
// getPriceFor(serviceId) preço de um serviço específico, ou null
// load(ownerId) carrega todos os serviços (ativos e inativos)
// save(payload) cria ou atualiza
// toggle(id, active) alterna active
// remove(id) DELETE definitivo
// getDefaultPrice() preço do primeiro serviço ativo, ou null
// getPriceFor(id) preço de um serviço específico, ou null
import { ref } from 'vue'
import { supabase } from '@/lib/supabase/client'
@@ -21,7 +20,6 @@ export function useServices () {
const loading = ref(false)
const error = ref('')
// ── Carregar serviços ativos do owner ───────────────────────────────
async function load (ownerId) {
if (!ownerId) return
loading.value = true
@@ -31,7 +29,6 @@ export function useServices () {
.from('services')
.select('id, name, description, price, duration_min, active')
.eq('owner_id', ownerId)
.eq('active', true)
.order('created_at', { ascending: true })
if (err) throw err
@@ -44,9 +41,6 @@ export function useServices () {
}
}
// ── Criar ou atualizar um serviço ───────────────────────────────────
// payload deve conter: { owner_id, tenant_id, name, price, description?, duration_min? }
// Se payload.id estiver presente, faz UPDATE; caso contrário, INSERT.
async function save (payload) {
error.value = ''
try {
@@ -70,40 +64,54 @@ export function useServices () {
}
}
// ── Soft-delete: marca active = false ───────────────────────────────
async function toggle (id, active) {
error.value = ''
try {
const { error: err } = await supabase
.from('services')
.update({ active })
.eq('id', id)
if (err) throw err
const svc = services.value.find(s => s.id === id)
if (svc) svc.active = active
} catch (e) {
error.value = e?.message || 'Falha ao atualizar serviço.'
throw e
}
}
async function remove (id) {
error.value = ''
try {
const { error: err } = await supabase
.from('services')
.update({ active: false })
.delete()
.eq('id', id)
if (err) throw err
services.value = services.value.filter(s => s.id !== id)
} catch (e) {
error.value = e?.message || 'Falha ao remover serviço.'
const msg = String(e?.message || '')
if (msg.includes('commitment_services_service_id_fkey') || msg.includes('violates foreign key constraint')) {
error.value = 'Este serviço está vinculado a sessões e não pode ser removido. Use Desativar para ocultá-lo.'
} else {
error.value = e?.message || 'Falha ao remover serviço.'
}
throw e
}
}
// ── Helpers de preço ────────────────────────────────────────────────
// Retorna o preço de um serviço específico (serviceId fornecido) ou
// o preço do primeiro serviço ativo da lista (serviceId omitido).
// Retorna null se não houver serviços ou o id não for encontrado.
function getDefaultPrice (serviceId) {
if (serviceId) {
const svc = services.value.find(s => s.id === serviceId)
return svc?.price != null ? Number(svc.price) : null
}
const first = services.value[0]
const first = services.value.find(s => s.active)
return first?.price != null ? Number(first.price) : null
}
// Alias explícito para clareza nos chamadores que conhecem o id
function getPriceFor (serviceId) {
return getDefaultPrice(serviceId)
}
return { services, loading, error, load, save, remove, getDefaultPrice, getPriceFor }
return { services, loading, error, load, save, toggle, remove, getDefaultPrice, getPriceFor }
}