fix: corrige erro 500 e reference error na sincronização do asaas

This commit is contained in:
Sidney 2026-05-08 13:59:00 -03:00
parent 41c32d53d2
commit 657f7f39ae
2 changed files with 37 additions and 64 deletions

View File

@ -188,45 +188,13 @@ const Finance: React.FC<FinanceProps> = ({ data, updateData }) => {
const syncResult = await syncResp.json(); const syncResult = await syncResp.json();
if (syncResult.success && syncResult.updatedCount > 0) { if (syncResult.success && syncResult.updatedCount > 0) {
showAlert('Sincronização Ativa', `${syncResult.updatedCount} pagamentos foram atualizados diretamente do Asaas e salvos no sistema.`, 'success'); showAlert('Sincronização Ativa', `${syncResult.updatedCount} pagamentos foram atualizados diretamente do Asaas. A página será atualizada.`, 'success');
setTimeout(() => window.location.reload(), 2000);
return;
} else if (syncResult.success) { } else if (syncResult.success) {
console.log('[Sync] Tudo atualizado com o Asaas.'); console.log('[Sync] Tudo atualizado com o Asaas.');
} }
// 2. Busca os dados atualizados do SQL para refletir na UI
const resp = await fetch('/api/admin/cobrancas');
if (!resp.ok) throw new Error('API fetch failed');
const cloudPayments = await resp.json();
// 3. ATUALIZAÇÃO CRÍTICA: Mescla os dados do SQL com o estado local do React
if (Array.isArray(cloudPayments) && cloudPayments.length > 0) {
setData(prev => {
const newPayments = [...prev.payments];
let updated = false;
cloudPayments.forEach((cp: any) => {
const idx = newPayments.findIndex(p => p.asaasPaymentId === cp.asaas_payment_id);
if (idx !== -1) {
const statusStr = (cp.status || '').toLowerCase();
const newStatus = statusStr === 'pago' ? 'paid' :
statusStr === 'atrasado' ? 'overdue' :
statusStr === 'cancelado' ? 'cancelled' : 'pending';
if (newPayments[idx].status !== newStatus) {
newPayments[idx] = {
...newPayments[idx],
status: newStatus as any,
paidDate: cp.data_pagamento || newPayments[idx].paidDate
};
updated = true;
}
}
});
return updated ? { ...prev, payments: newPayments } : prev;
});
}
if (cloudPayments && cloudPayments.length > 0) { if (cloudPayments && cloudPayments.length > 0) {
let updatedCount = 0; let updatedCount = 0;
const currentPayments = dataPaymentsRef.current; const currentPayments = dataPaymentsRef.current;

View File

@ -1328,23 +1328,33 @@ async function syncPaymentsWithAsaasAPI() {
try { try {
console.log(`[Asaas:Sync] 🚀 Iniciando Sincronização JSON-First...`); console.log(`[Asaas:Sync] 🚀 Iniciando Sincronização JSON-First...`);
// 1. Carregamos o JSON principal (Fonte de Verdade da UI) // 1. Carregamos o JSON principal
const appData = await getSchoolData(); const appData = await getSchoolData();
if (!appData || !appData.payments) { if (!appData || !appData.payments) {
throw new Error('Dados da escola ou pagamentos não encontrados no JSON.'); console.error('[Asaas:Sync] ❌ JSON school_data ou payments não localizado.');
return 0;
} }
// 2. Definimos as URLs de busca (Pagos e Confirmados) // 2. URLs de busca (Usando a chave global ASAAS_KEY e ASAAS_BASE_URL)
const url = `${ASAAS_BASE_URL}/v3/payments?limit=100&status=RECEIVED&paymentDate%5Bge%5D=2026-01-01`; const url = `${ASAAS_BASE_URL}/v3/payments?limit=100&status=RECEIVED&paymentDate%5Bge%5D=2026-01-01`;
const urlConfirmed = `${ASAAS_BASE_URL}/v3/payments?limit=100&status=CONFIRMED`; const urlConfirmed = `${ASAAS_BASE_URL}/v3/payments?limit=100&status=CONFIRMED`;
const fetchPayments = async (targetUrl) => { const fetchPayments = async (targetUrl) => {
try { try {
const response = await fetch(targetUrl, { headers: { 'access_token': ASAAS_KEY } }); const response = await fetch(targetUrl, {
if (!response.ok) return []; headers: { 'access_token': process.env.ASAAS_API_KEY || ASAAS_KEY }
});
if (!response.ok) {
const errText = await response.text();
console.error(`[Asaas:Sync] Erro na API (${response.status}):`, errText);
return [];
}
const data = await response.json(); const data = await response.json();
return data.data || []; return data.data || [];
} catch (e) { return []; } } catch (e) {
console.error(`[Asaas:Sync] Falha de rede na URL ${targetUrl}:`, e.message);
return [];
}
}; };
const received = await fetchPayments(url); const received = await fetchPayments(url);
@ -1352,65 +1362,60 @@ async function syncPaymentsWithAsaasAPI() {
const allRecent = [...received, ...confirmed]; const allRecent = [...received, ...confirmed];
if (allRecent.length === 0) { if (allRecent.length === 0) {
console.log('[Asaas:Sync] Nenhum pagamento confirmado encontrado no Asaas.'); console.log('[Asaas:Sync] Nenhum pagamento confirmado/recebido no Asaas.');
return 0; return 0;
} }
const statusMap = { const statusMap = {
'PENDING': 'PENDENTE',
'OVERDUE': 'ATRASADO',
'RECEIVED': 'PAGO', 'RECEIVED': 'PAGO',
'CONFIRMED': 'PAGO', 'CONFIRMED': 'PAGO',
'RECEIVED_IN_CASH': 'PAGO', 'RECEIVED_IN_CASH': 'PAGO',
'REFUNDED': 'CANCELADO', 'OVERDUE': 'ATRASADO',
'DELETED': 'CANCELADO' 'REFUNDED': 'CANCELADO'
}; };
// Mapeamento para o JSON legado
const jsonStatusMap = { const jsonStatusMap = {
'PAGO': 'paid', 'PAGO': 'paid',
'ATRASADO': 'overdue', 'ATRASADO': 'overdue',
'CANCELADO': 'cancelled', 'CANCELADO': 'cancelled'
'PENDENTE': 'pending'
}; };
let totalUpdated = 0; let totalUpdated = 0;
for (const payment of allRecent) { for (const payment of allRecent) {
const internalStatus = statusMap[payment.status] || 'PENDENTE'; const internalStatus = statusMap[payment.status];
if (!internalStatus) continue;
const valorNum = Number(payment.value); const valorNum = Number(payment.value);
// A. Atualiza o SQL (Backup e Relatórios) // A. Atualiza SQL (Silencioso)
await pool.query(` await pool.query(`
INSERT INTO alunos_cobrancas (asaas_payment_id, valor, vencimento, status, data_pagamento, link_boleto) INSERT INTO alunos_cobrancas (asaas_payment_id, valor, vencimento, status, data_pagamento)
VALUES ($1, $2, $3, $4, $5, $6) VALUES ($1, $2, $3, $4, $5)
ON CONFLICT (asaas_payment_id) DO UPDATE SET ON CONFLICT (asaas_payment_id) DO UPDATE SET status = EXCLUDED.status, data_pagamento = EXCLUDED.data_pagamento
status = EXCLUDED.status, `, [payment.id, valorNum, payment.dueDate, internalStatus, payment.confirmedDate || payment.paymentDate]).catch(() => {});
data_pagamento = EXCLUDED.data_pagamento
`, [payment.id, valorNum, payment.dueDate, internalStatus, payment.confirmedDate || payment.paymentDate, payment.bankSlipUrl || payment.invoiceUrl]);
// B. Atualiza o JSON (Visualização na Tela) // B. Atualiza JSON
const pIdx = appData.payments.findIndex(p => p.asaasPaymentId === payment.id); const pIdx = appData.payments.findIndex(p => p.asaasPaymentId === payment.id);
if (pIdx !== -1) { if (pIdx !== -1) {
const newJsonStatus = jsonStatusMap[internalStatus] || 'pending'; const newStatus = jsonStatusMap[internalStatus];
if (appData.payments[pIdx].status !== newJsonStatus) { if (appData.payments[pIdx].status !== newStatus) {
appData.payments[pIdx].status = newJsonStatus; appData.payments[pIdx].status = newStatus;
appData.payments[pIdx].paidDate = payment.confirmedDate || payment.paymentDate || appData.payments[pIdx].paidDate; appData.payments[pIdx].paidDate = payment.confirmedDate || payment.paymentDate || appData.payments[pIdx].paidDate;
totalUpdated++; totalUpdated++;
} }
} }
} }
// 3. Salva o JSON atualizado (Isso reflete na UI imediatamente)
if (totalUpdated > 0) { if (totalUpdated > 0) {
appData.lastUpdated = new Date().toISOString(); appData.lastUpdated = new Date().toISOString();
await saveSchoolData(appData); await saveSchoolData(appData);
console.log(`[Asaas:Sync] ✅ Sincronização concluída: ${totalUpdated} pagamentos atualizados no JSON.`); console.log(`[Asaas:Sync] ✅ Sucesso! ${totalUpdated} pagamentos atualizados.`);
} }
return totalUpdated; return totalUpdated;
} catch (err) { } catch (err) {
console.error('[Asaas:Sync] ❌ Erro na sincronização JSON-First:', err.message); console.error('[Asaas:Sync] ❌ Erro Fatal:', err.message);
throw err; throw err;
} }
} }