diff --git a/manager/components/Contracts.tsx b/manager/components/Contracts.tsx index 811523e..5653d54 100644 --- a/manager/components/Contracts.tsx +++ b/manager/components/Contracts.tsx @@ -206,7 +206,7 @@ const Contracts: React.FC = ({ data, updateData }) => { } }; - const handleGenerate = () => { + const handleGenerate = async () => { if (!contractToGenerate) return; const contract = contractToGenerate; @@ -242,7 +242,13 @@ const Contracts: React.FC = ({ data, updateData }) => { }); } + // 1. Salvar no JSON (manter compatibilidade) updateData({ payments: [...data.payments, ...newPayments] }); + + // 2. Fase 2: Salvar no SQL via sincronização no próximo boot + // Nota: Parcelas de contrato sem Asaas serão migradas pelo syncJsonToRelationalTables no próximo restart. + // A Fase 3 eliminará essa dependência quando o Manager ler direto do SQL. + closeModal(); }; diff --git a/manager/components/Finance.tsx b/manager/components/Finance.tsx index e95c33a..18d7a05 100644 --- a/manager/components/Finance.tsx +++ b/manager/components/Finance.tsx @@ -742,15 +742,26 @@ const Finance: React.FC = ({ data, updateData }) => { setIsEditing(true); try { const targetId = paymentToEdit.asaasPaymentId || paymentToEdit.id; + const newValor = parseFloat(editValue.replace(',', '.')); + + // 1. Atualizar no Asaas const response = await fetch(`/api/cobrancas/${targetId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ valor: parseFloat(editValue.replace(',', '.')), vencimento: editDate }) + body: JSON.stringify({ valor: newValor, vencimento: editDate }) }); const result = await response.json(); if (response.ok) { + // 2. Escrita dupla: Atualizar no SQL (Fase 2) + fetch(`/api/admin/cobrancas/${targetId}`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ valor: newValor, vencimento: editDate, amount_original: newValor }) + }).catch(err => console.warn('[Fase2:SQL] Erro ao sincronizar edição:', err)); + + // 3. Atualizar no JSON (manter compatibilidade) updateData({ - payments: data.payments.map(p => p.id === paymentToEdit.id ? { ...p, amount: parseFloat(editValue.replace(',', '.')), dueDate: editDate } : p) + payments: data.payments.map(p => p.id === paymentToEdit.id ? { ...p, amount: newValor, dueDate: editDate } : p) }); showAlert('Sucesso', 'Cobrança atualizada!', 'success'); setPaymentToEdit(null); diff --git a/manager/server.selfhosted.js b/manager/server.selfhosted.js index 23dac01..c4e2223 100644 --- a/manager/server.selfhosted.js +++ b/manager/server.selfhosted.js @@ -892,6 +892,39 @@ app.delete('/api/admin/cobrancas/:id', async (req, res) => { } }); +// Fase 2: Escrita dupla — Manager pode atualizar campos ricos diretamente no SQL +app.put('/api/admin/cobrancas/:id', async (req, res) => { + try { + const { valor, vencimento, description, type, discount, installment_number, total_installments, amount_original } = req.body; + + const updates = []; + const values = []; + let paramIdx = 1; + + if (valor !== undefined) { updates.push(`valor = $${paramIdx++}`); values.push(valor); } + if (vencimento !== undefined) { updates.push(`vencimento = $${paramIdx++}`); values.push(vencimento); } + if (description !== undefined) { updates.push(`description = $${paramIdx++}`); values.push(description); } + if (type !== undefined) { updates.push(`type = $${paramIdx++}`); values.push(type); } + if (discount !== undefined) { updates.push(`discount = $${paramIdx++}`); values.push(discount); } + if (installment_number !== undefined) { updates.push(`installment_number = $${paramIdx++}`); values.push(installment_number); } + if (total_installments !== undefined) { updates.push(`total_installments = $${paramIdx++}`); values.push(total_installments); } + if (amount_original !== undefined) { updates.push(`amount_original = $${paramIdx++}`); values.push(amount_original); } + + if (updates.length === 0) return res.status(400).json({ error: 'Nenhum campo para atualizar.' }); + + values.push(req.params.id); + await pool.query( + `UPDATE alunos_cobrancas SET ${updates.join(', ')} WHERE asaas_payment_id = $${paramIdx}`, + values + ); + + res.json({ success: true }); + } catch(e) { + console.error('[Admin:Cobrancas:PUT] Erro:', e.message); + res.status(500).json({ error: e.message }); + } +}); + // Webhook Evolution app.post('/api/webhooks/evolution', (req, res) => { try {