diff --git a/manager/scratch/fix_valores.cjs b/manager/scratch/fix_valores.cjs new file mode 100644 index 0000000..3ed42e4 --- /dev/null +++ b/manager/scratch/fix_valores.cjs @@ -0,0 +1,26 @@ +const pg = require('pg'); +const pool = new pg.Pool({ connectionString: 'postgresql://edumanager:EduManager2026!Seguro@150.230.87.131:5432/edumanager' }); + +async function fix() { + // 1. Corrigir parcelas PAGAS: valor=170, amount_original=170, valor_pago=150 + const r1 = await pool.query( + "UPDATE alunos_cobrancas SET valor = 170, amount_original = 170, valor_pago = 150 WHERE status = 'PAGO' AND discount = 20" + ); + console.log('Parcelas PAGAS corrigidas:', r1.rowCount); + + // 2. Corrigir parcelas PENDENTES que tenham valor inflado + const r2 = await pool.query( + "UPDATE alunos_cobrancas SET amount_original = valor WHERE amount_original != valor AND discount = 20" + ); + console.log('Amount_original alinhado:', r2.rowCount); + + // 3. Verificar resultado + const { rows } = await pool.query( + "SELECT status, count(*), sum(valor) as total_valor, sum(valor_pago) as total_pago, sum(discount) as total_desconto FROM alunos_cobrancas GROUP BY status" + ); + console.table(rows); + + await pool.end(); +} + +fix().catch(e => { console.error(e); pool.end(); }); diff --git a/manager/server.selfhosted.js b/manager/server.selfhosted.js index 55abd17..33bfa41 100644 --- a/manager/server.selfhosted.js +++ b/manager/server.selfhosted.js @@ -752,25 +752,19 @@ app.post('/api/webhook_asaas', async (req, res) => { data_pagamento: payload.payment.confirmedDate || payload.payment.paymentDate || new Date().toISOString().split('T')[0] }; - // [Bugfix Crítico]: Recuperar o valor BRUTO caso o Asaas mande o líquido + // Preservar o valor original da parcela — não inflar com desconto try { const cobRes = await pool.query('SELECT valor, discount, amount_original FROM alunos_cobrancas WHERE asaas_payment_id = $1', [asaasPaymentId]); if (cobRes.rows.length > 0) { const cob = cobRes.rows[0]; - const currentAmount = Number(cob.valor || 0); + const storedValor = Number(cob.valor || 0); const discount = Number(cob.discount || 0); - const amountOriginal = Number(cob.amount_original || 0); const receivedValue = Number(payload.payment.value); - // Se o valor recebido for menor que o bruto registrado e a diferença bater com o desconto, - // mantemos o bruto no campo 'valor'. - if (receivedValue < currentAmount && Math.abs((currentAmount - discount) - receivedValue) < 0.01) { - // Já está correto (bruto > recebido), não mexemos no 'valor' - } else if (receivedValue === currentAmount && discount > 0) { - // Se o 'valor' no banco já era o líquido, restauramos para o bruto - updateData.valor = receivedValue + discount; - } else if (amountOriginal > receivedValue) { - updateData.valor = amountOriginal; + // Se o Asaas enviou o valor líquido (menor que o registrado), + // registramos como valor_pago e preservamos o valor da parcela + if (receivedValue < storedValor) { + updateData.valor_pago = receivedValue; } } } catch (e) { console.error('[Webhook:Recovery] Erro:', e.message); } @@ -1739,8 +1733,8 @@ async function syncPaymentsWithAsaasAPI() { ON CONFLICT (asaas_payment_id) DO UPDATE SET status = EXCLUDED.status, data_pagamento = EXCLUDED.data_pagamento, - valor_pago = GREATEST(alunos_cobrancas.valor_pago, EXCLUDED.valor_pago), - valor = GREATEST(alunos_cobrancas.valor, EXCLUDED.valor) + valor_pago = CASE WHEN EXCLUDED.valor_pago > 0 THEN EXCLUDED.valor_pago ELSE alunos_cobrancas.valor_pago END, + valor = COALESCE(NULLIF(EXCLUDED.valor, 0), alunos_cobrancas.valor) `, [payment.id, valorNum, payment.dueDate, internalStatus, payment.confirmedDate || payment.paymentDate, receivedValue]).catch(() => {}); // B. Atualiza JSON @@ -1812,7 +1806,7 @@ async function syncRelationalToJsonPayments() { const hasChanges = p.status !== newStatus || Number(p.valor_pago || 0) !== Number(match.valor_pago || 0) || - Number(p.amount || 0) !== Math.max(Number(match.amount_original || 0), Number(match.valor || 0)); + Number(p.amount || 0) !== Number(match.valor || 0); if (hasChanges) { updatedCount++; @@ -1821,7 +1815,7 @@ async function syncRelationalToJsonPayments() { status: newStatus, paidDate: match.data_pagamento || p.paidDate, valor_pago: Number(match.valor_pago || 0), - amount: Math.max(Number(match.amount_original || 0), Number(match.valor || 0)) + amount: Number(match.valor || p.amount || 0) }; } } diff --git a/manager/services/database.js b/manager/services/database.js index cbba17d..dcc8162 100644 --- a/manager/services/database.js +++ b/manager/services/database.js @@ -556,15 +556,14 @@ export async function syncJsonToRelationalTables() { const amount = Number(p.amount || 0); const discount = Number(p.discount || 0); - // Se está pago, o 'amount' do JSON geralmente é o líquido. - // O valor principal (valor) deve ser o BRUTO. + // O valor da parcela (face value) é sempre o amount do JSON (ex: 170) + // O desconto é condicional e NÃO altera o valor base da parcela let valorBruto = amount; let valorPago = 0; if (isPaid) { - valorPago = amount; - // Se o amount vindo do JSON for o líquido (igual ou menor que o bruto esperado), restauramos o bruto - valorBruto = amount + discount; + // Usar valor_pago explícito do JSON se disponível, senão calcular (amount - discount) + valorPago = Number(p.valor_pago || 0) || (amount - discount); } await client.query( @@ -578,7 +577,7 @@ export async function syncJsonToRelationalTables() { aluno_id = EXCLUDED.aluno_id, asaas_installment_id = COALESCE(EXCLUDED.asaas_installment_id, alunos_cobrancas.asaas_installment_id), installment = COALESCE(EXCLUDED.installment, alunos_cobrancas.installment), - valor = GREATEST(alunos_cobrancas.valor, EXCLUDED.valor), + valor = EXCLUDED.valor, vencimento = EXCLUDED.vencimento, link_boleto = COALESCE(EXCLUDED.link_boleto, alunos_cobrancas.link_boleto), status = CASE WHEN alunos_cobrancas.status = 'PAGO' THEN alunos_cobrancas.status ELSE EXCLUDED.status END, @@ -589,7 +588,7 @@ export async function syncJsonToRelationalTables() { total_installments = COALESCE(EXCLUDED.total_installments, alunos_cobrancas.total_installments), contract_id = COALESCE(EXCLUDED.contract_id, alunos_cobrancas.contract_id), asaas_payment_url = COALESCE(EXCLUDED.asaas_payment_url, alunos_cobrancas.asaas_payment_url), - amount_original = GREATEST(COALESCE(alunos_cobrancas.amount_original, 0), EXCLUDED.amount_original), + amount_original = COALESCE(EXCLUDED.amount_original, alunos_cobrancas.amount_original), data_pagamento = COALESCE(EXCLUDED.data_pagamento, alunos_cobrancas.data_pagamento), valor_pago = EXCLUDED.valor_pago`, [