agenda pacote saldo: fix root cause + sequential awaits
ROOT CAUSE DESCOBERTO durante C11/A com Andre Green:
billing_contracts NAO tem coluna updated_at. UPDATEs em
_applyStatusDecisions passavam updated_at -> Postgres retornava
"column updated_at does not exist" -> Promise.allSettled engolia
como {status: 'rejected'} silencioso -> toast warn generico que
user nao percebia. Resultado: sessions_used nunca incrementava.
Bug existia em DOIS lugares:
1. consumeSaldo block (faltou/cancelado pacote saldo) - afetaria
C11/B, C11/C, C11/D
2. generatePackageCharge block (realizado pacote saldo) - afetou
C11/A
Em ambos: removido updated_at do patch (.update({...})).
ADICIONAL: generatePackageCharge refatorado pra usar AWAITS
SEQUENCIAIS (igual onUsarSessao do MelissaLayout que sempre
funcionou):
- 4a) UPDATE agenda_eventos.billing_contract_id (faltava!)
- 4b) RPC create_financial_record_for_session
- 4c) UPDATE billing_contracts.sessions_used + status=completed
Cada step com try/catch + console.error + toast distinto. Sem mais
falhas escondidas em Promise.allSettled paralelo.
Backfill manual do estado do Andre Green: evento 6e70476f agora
amarrado ao contract 691118da com sessions_used=1.
Memoria nova: project_billing_contracts_no_updated_at.md pra evitar
o gotcha no futuro.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1442,13 +1442,14 @@ function _buildHandlers(deps) {
|
||||
const tasks = [];
|
||||
|
||||
// 1) Consumir saldo (pacote saldo + faltou/cancelado + decisão sim)
|
||||
// ⚠ billing_contracts NÃO tem coluna updated_at — passar esse campo
|
||||
// causa "column does not exist" silenciosamente em Promise.allSettled.
|
||||
if (decision.consumeSaldo && ctx.billingContract?.id) {
|
||||
tasks.push(
|
||||
supabase
|
||||
.from('billing_contracts')
|
||||
.update({
|
||||
sessions_used: (ctx.billingContract.sessions_used ?? 0) + 1,
|
||||
updated_at: new Date().toISOString()
|
||||
sessions_used: (ctx.billingContract.sessions_used ?? 0) + 1
|
||||
})
|
||||
.eq('id', ctx.billingContract.id)
|
||||
);
|
||||
@@ -1529,31 +1530,59 @@ function _buildHandlers(deps) {
|
||||
);
|
||||
}
|
||||
|
||||
// 4) Realizado em pacote saldo: cria cobrança individual + incrementa sessions_used
|
||||
// 4) Realizado em pacote saldo: amarra contract + cria cobrança + incrementa saldo
|
||||
// Refatorado pra usar AWAITS SEQUENCIAIS (igual onUsarSessao do MelissaLayout).
|
||||
// Antes era Promise.allSettled paralelo que escondia falhas silenciosas
|
||||
// — durante teste C11/A o sessions_used não incrementava + agenda_evento
|
||||
// ficava sem billing_contract_id. Ambos os updates não rodavam mas o
|
||||
// toast warn não aparecia. Agora cada step tem error explícito.
|
||||
if (decision.generatePackageCharge && ctx.billingContract?.id) {
|
||||
const amount = Number(row.price ?? 0);
|
||||
const amount = Number(row.price ?? (ctx.billingContract.total_sessions > 0 ? (Number(ctx.billingContract.package_price) || 0) / ctx.billingContract.total_sessions : 0));
|
||||
const dueIso = row.inicio_em ? new Date(row.inicio_em).toISOString().slice(0, 10) : new Date().toISOString().slice(0, 10);
|
||||
// Cria record
|
||||
tasks.push(
|
||||
supabase.rpc('create_financial_record_for_session', {
|
||||
|
||||
// 4a) Amarra agenda_evento ao contrato. Pra virtual recém-materializada,
|
||||
// _applyStatusUpdateOnly criou o evento SEM billing_contract_id —
|
||||
// precisa update separado aqui.
|
||||
try {
|
||||
const { error: linkErr } = await supabase.from('agenda_eventos').update({ billing_contract_id: ctx.billingContract.id, updated_at: new Date().toISOString() }).eq('id', eventoId);
|
||||
if (linkErr) throw linkErr;
|
||||
} catch (e) {
|
||||
console.error('[Fase5] erro amarrando billing_contract_id:', e?.message);
|
||||
toast.add({ severity: 'warn', summary: 'Pacote — amarração', detail: e?.message || 'Falha ao amarrar sessão ao pacote', life: 5000 });
|
||||
}
|
||||
|
||||
// 4b) Cria financial_record (RPC tolera idempotência)
|
||||
try {
|
||||
const { error: rpcErr } = await supabase.rpc('create_financial_record_for_session', {
|
||||
p_tenant_id: tenantId,
|
||||
p_owner_id: uid,
|
||||
p_patient_id: patientId,
|
||||
p_agenda_evento_id: eventoId,
|
||||
p_amount: amount,
|
||||
p_due_date: dueIso
|
||||
})
|
||||
);
|
||||
// Incrementa saldo usado
|
||||
tasks.push(
|
||||
supabase
|
||||
.from('billing_contracts')
|
||||
.update({
|
||||
sessions_used: (ctx.billingContract.sessions_used ?? 0) + 1,
|
||||
updated_at: new Date().toISOString()
|
||||
})
|
||||
.eq('id', ctx.billingContract.id)
|
||||
);
|
||||
});
|
||||
if (rpcErr) throw rpcErr;
|
||||
} catch (e) {
|
||||
console.error('[Fase5] erro RPC create_financial_record_for_session:', e?.message);
|
||||
toast.add({ severity: 'error', summary: 'Cobrança', detail: e?.message || 'Falha ao gerar cobrança do pacote', life: 7000 });
|
||||
}
|
||||
|
||||
// 4c) Incrementa sessions_used + completa contract se atingir total
|
||||
// ⚠ billing_contracts NÃO tem coluna updated_at — passar esse
|
||||
// campo causa "column does not exist" silenciosamente em
|
||||
// Promise.allSettled (era o root cause do saldo não incrementar).
|
||||
try {
|
||||
const newUsed = (ctx.billingContract.sessions_used ?? 0) + 1;
|
||||
const patchContract = { sessions_used: newUsed };
|
||||
if (newUsed >= (ctx.billingContract.total_sessions ?? 0)) {
|
||||
patchContract.status = 'completed';
|
||||
}
|
||||
const { error: incErr } = await supabase.from('billing_contracts').update(patchContract).eq('id', ctx.billingContract.id);
|
||||
if (incErr) throw incErr;
|
||||
} catch (e) {
|
||||
console.error('[Fase5] erro incrementando sessions_used:', e?.message);
|
||||
toast.add({ severity: 'warn', summary: 'Saldo do pacote', detail: e?.message || 'Falha ao atualizar saldo do pacote', life: 6000 });
|
||||
}
|
||||
}
|
||||
|
||||
// Roda tudo em paralelo (falha parcial é tolerável — toast warn)
|
||||
|
||||
Reference in New Issue
Block a user