Phase 2: Dual-write for financial edits - new PUT /api/admin/cobrancas/:id route + Finance.tsx sync

This commit is contained in:
Sidney 2026-05-14 21:46:39 -03:00
parent b440023add
commit ce82f30598
3 changed files with 53 additions and 3 deletions

View File

@ -206,7 +206,7 @@ const Contracts: React.FC<ContractsProps> = ({ data, updateData }) => {
} }
}; };
const handleGenerate = () => { const handleGenerate = async () => {
if (!contractToGenerate) return; if (!contractToGenerate) return;
const contract = contractToGenerate; const contract = contractToGenerate;
@ -242,7 +242,13 @@ const Contracts: React.FC<ContractsProps> = ({ data, updateData }) => {
}); });
} }
// 1. Salvar no JSON (manter compatibilidade)
updateData({ payments: [...data.payments, ...newPayments] }); 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(); closeModal();
}; };

View File

@ -742,15 +742,26 @@ const Finance: React.FC<FinanceProps> = ({ data, updateData }) => {
setIsEditing(true); setIsEditing(true);
try { try {
const targetId = paymentToEdit.asaasPaymentId || paymentToEdit.id; const targetId = paymentToEdit.asaasPaymentId || paymentToEdit.id;
const newValor = parseFloat(editValue.replace(',', '.'));
// 1. Atualizar no Asaas
const response = await fetch(`/api/cobrancas/${targetId}`, { const response = await fetch(`/api/cobrancas/${targetId}`, {
method: 'PUT', method: 'PUT',
headers: { 'Content-Type': 'application/json' }, 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(); const result = await response.json();
if (response.ok) { 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({ 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'); showAlert('Sucesso', 'Cobrança atualizada!', 'success');
setPaymentToEdit(null); setPaymentToEdit(null);

View File

@ -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 // Webhook Evolution
app.post('/api/webhooks/evolution', (req, res) => { app.post('/api/webhooks/evolution', (req, res) => {
try { try {