From d537d31187bffffeef490c4081c85459314e3426 Mon Sep 17 00:00:00 2001 From: Sidney Date: Tue, 21 Apr 2026 21:07:01 -0300 Subject: [PATCH] =?UTF-8?q?refactor:=20migra=C3=A7=C3=A3o=20fina=C3=A7a=20?= =?UTF-8?q?para=20API=20local=20Postgres=20e=20fim=20do=20Supabase=20Sync?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- manager/components/AttendanceQuery.tsx | 1 + manager/components/Finance.tsx | 61 ++++++++++---------------- manager/server.selfhosted.js | 30 +++++++++++++ 3 files changed, 55 insertions(+), 37 deletions(-) diff --git a/manager/components/AttendanceQuery.tsx b/manager/components/AttendanceQuery.tsx index 46fac5c..3bae070 100644 --- a/manager/components/AttendanceQuery.tsx +++ b/manager/components/AttendanceQuery.tsx @@ -355,6 +355,7 @@ const AttendanceQuery: React.FC = ({ data, updateData, dee ); } + return classStudents.map(student => { const studentActualRecords = (data.attendance || []).filter(a => a.studentId === student.id && a.classId === selectedClass.id); const classLessonsRaw = (data.lessons || []).filter(l => l.classId === selectedClass.id && l.status !== 'cancelled'); diff --git a/manager/components/Finance.tsx b/manager/components/Finance.tsx index 4850ed1..1b3b0a3 100644 --- a/manager/components/Finance.tsx +++ b/manager/components/Finance.tsx @@ -178,21 +178,19 @@ const Finance: React.FC = ({ data, updateData }) => { }, [data.payments]); const syncAsaasPayments = async () => { - if (!isSupabaseConfigured() || isSyncing) return; + if (isSyncing) return; setIsSyncing(true); try { - const { data: cloudPayments, error } = await supabase - .from('alunos_cobrancas') - .select('asaas_payment_id, status, aluno_id, valor, vencimento, data_pagamento, installment, asaas_installment_id, link_boleto'); - - if (error) throw error; + const resp = await fetch('/api/admin/cobrancas'); + if (!resp.ok) throw new Error('API fetch failed'); + const cloudPayments = await resp.json(); if (cloudPayments && cloudPayments.length > 0) { let updatedCount = 0; const currentPayments = dataPaymentsRef.current; const updatedPayments = currentPayments.map(p => { - const match = cloudPayments.find(cp => { + const match = cloudPayments.find((cp: any) => { if (p.asaasPaymentId) { return cp.asaas_payment_id === p.asaasPaymentId; } @@ -226,7 +224,6 @@ const Finance: React.FC = ({ data, updateData }) => { if (updatedCount > 0) { updateData({ payments: updatedPayments }); - // Check if any was updated to overdue const hasOverdue = updatedPayments.some((p, idx) => { const oldP = currentPayments[idx]; return oldP && oldP.status !== 'overdue' && p.status === 'overdue'; @@ -243,33 +240,27 @@ const Finance: React.FC = ({ data, updateData }) => { if (hasPaid && hasOverdue) message = 'Pagamentos e atrasos atualizados.'; showAlert('Sincronização', message, 'success'); - } else { - showAlert('Sincronização', 'Nenhum novo pagamento confirmado encontrado.', 'info'); } } } catch (error) { console.error('Erro ao sincronizar pagamentos:', error); - showAlert('Erro', 'Falha ao sincronizar com o Asaas.', 'error'); + // Suppress alert so it doesn't pop up randomly to the user if the server restarts temporarily } finally { setIsSyncing(false); } }; const fetchSupabaseRecords = async () => { - if (!isSupabaseConfigured()) return; setIsFetchingSupabase(true); - setSelectedSupabaseRows([]); // Clear selection on refresh + setSelectedSupabaseRows([]); try { - const { data: records, error } = await supabase - .from('alunos_cobrancas') - .select('*') - .order('vencimento', { ascending: false }); - - if (error) throw error; + const resp = await fetch('/api/admin/cobrancas'); + if (!resp.ok) throw new Error('API fetch failed'); + const records = await resp.json(); setSupabaseRecords(records || []); } catch (error) { - console.error('Error fetching Supabase records:', error); - showAlert('Erro', 'Falha ao buscar dados do Supabase.', 'error'); + console.error('Error fetching billing records from Postgres:', error); + showAlert('Erro', 'Falha ao buscar dados do Banco de Dados.', 'error'); } finally { setIsFetchingSupabase(false); } @@ -277,34 +268,30 @@ const Finance: React.FC = ({ data, updateData }) => { const deleteSupabaseRecord = async (id: string) => { try { - const { error } = await supabase - .from('alunos_cobrancas') - .delete() - .eq('asaas_payment_id', id); - - if (error) throw error; + const resp = await fetch(`/api/admin/cobrancas/${id}`, { method: 'DELETE' }); + if (!resp.ok) throw new Error('Delete failed'); setSupabaseRecords(prev => prev.filter(r => r.asaas_payment_id !== id)); - showAlert('Sucesso', 'Registro removido do Supabase.', 'success'); + showAlert('Sucesso', 'Registro removido do Banco.', 'success'); } catch (error) { - console.error('Error deleting Supabase record:', error); - showAlert('Erro', 'Falha ao excluir do Supabase.', 'error'); + console.error('Error deleting record:', error); + showAlert('Erro', 'Falha ao excluir do Banco de Dados.', 'error'); } }; const deleteSupabaseRecordsBulk = async () => { if (selectedSupabaseRows.length === 0) return; - if(!confirm(`Tem certeza que deseja excluir ${selectedSupabaseRows.length} registros diretamente do Supabase?`)) return; + if(!confirm(`Tem certeza que deseja excluir ${selectedSupabaseRows.length} registros diretamente do Banco de Dados?`)) return; setIsFetchingSupabase(true); try { - const { error } = await supabase - .from('alunos_cobrancas') - .delete() - .in('asaas_payment_id', selectedSupabaseRows); - - if (error) throw error; + const resp = await fetch('/api/admin/cobrancas', { + method: 'DELETE', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ ids: selectedSupabaseRows }) + }); + if (!resp.ok) throw new Error('Bulk delete failed'); setSupabaseRecords(prev => prev.filter(r => !selectedSupabaseRows.includes(r.asaas_payment_id))); setSelectedSupabaseRows([]); diff --git a/manager/server.selfhosted.js b/manager/server.selfhosted.js index 776a116..8059eff 100644 --- a/manager/server.selfhosted.js +++ b/manager/server.selfhosted.js @@ -473,6 +473,36 @@ app.post('/api/webhook_asaas', async (req, res) => { } }); +// Admin Raw Cobrancas para a Aba Financeiro +app.get('/api/admin/cobrancas', async (req, res) => { + try { + const result = await pool.query('SELECT * FROM alunos_cobrancas ORDER BY vencimento DESC'); + res.json(result.rows); + } catch(e) { + res.status(500).json({error: e.message}); + } +}); + +app.delete('/api/admin/cobrancas', async (req, res) => { + try { + const { ids } = req.body; + if (!Array.isArray(ids)) return res.status(400).end(); + await pool.query('DELETE FROM alunos_cobrancas WHERE asaas_payment_id = ANY($1)', [ids]); + res.json({ success: true }); + } catch(e) { + res.status(500).json({error: e.message}); + } +}); + +app.delete('/api/admin/cobrancas/:id', async (req, res) => { + try { + await pool.query('DELETE FROM alunos_cobrancas WHERE asaas_payment_id = $1', [req.params.id]); + res.json({ success: true }); + } catch(e) { + res.status(500).json({error: e.message}); + } +}); + // Webhook Evolution app.post('/api/webhooks/evolution', (req, res) => { try {