fix(sync): correct financial sync logic to prevent inflating installment values with discount
This commit is contained in:
parent
054bd5ef7b
commit
a9f8559462
|
|
@ -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(); });
|
||||||
|
|
@ -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]
|
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 {
|
try {
|
||||||
const cobRes = await pool.query('SELECT valor, discount, amount_original FROM alunos_cobrancas WHERE asaas_payment_id = $1', [asaasPaymentId]);
|
const cobRes = await pool.query('SELECT valor, discount, amount_original FROM alunos_cobrancas WHERE asaas_payment_id = $1', [asaasPaymentId]);
|
||||||
if (cobRes.rows.length > 0) {
|
if (cobRes.rows.length > 0) {
|
||||||
const cob = cobRes.rows[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 discount = Number(cob.discount || 0);
|
||||||
const amountOriginal = Number(cob.amount_original || 0);
|
|
||||||
const receivedValue = Number(payload.payment.value);
|
const receivedValue = Number(payload.payment.value);
|
||||||
|
|
||||||
// Se o valor recebido for menor que o bruto registrado e a diferença bater com o desconto,
|
// Se o Asaas enviou o valor líquido (menor que o registrado),
|
||||||
// mantemos o bruto no campo 'valor'.
|
// registramos como valor_pago e preservamos o valor da parcela
|
||||||
if (receivedValue < currentAmount && Math.abs((currentAmount - discount) - receivedValue) < 0.01) {
|
if (receivedValue < storedValor) {
|
||||||
// Já está correto (bruto > recebido), não mexemos no 'valor'
|
updateData.valor_pago = receivedValue;
|
||||||
} 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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) { console.error('[Webhook:Recovery] Erro:', e.message); }
|
} catch (e) { console.error('[Webhook:Recovery] Erro:', e.message); }
|
||||||
|
|
@ -1739,8 +1733,8 @@ async function syncPaymentsWithAsaasAPI() {
|
||||||
ON CONFLICT (asaas_payment_id) DO UPDATE SET
|
ON CONFLICT (asaas_payment_id) DO UPDATE SET
|
||||||
status = EXCLUDED.status,
|
status = EXCLUDED.status,
|
||||||
data_pagamento = EXCLUDED.data_pagamento,
|
data_pagamento = EXCLUDED.data_pagamento,
|
||||||
valor_pago = GREATEST(alunos_cobrancas.valor_pago, EXCLUDED.valor_pago),
|
valor_pago = CASE WHEN EXCLUDED.valor_pago > 0 THEN EXCLUDED.valor_pago ELSE alunos_cobrancas.valor_pago END,
|
||||||
valor = GREATEST(alunos_cobrancas.valor, EXCLUDED.valor)
|
valor = COALESCE(NULLIF(EXCLUDED.valor, 0), alunos_cobrancas.valor)
|
||||||
`, [payment.id, valorNum, payment.dueDate, internalStatus, payment.confirmedDate || payment.paymentDate, receivedValue]).catch(() => {});
|
`, [payment.id, valorNum, payment.dueDate, internalStatus, payment.confirmedDate || payment.paymentDate, receivedValue]).catch(() => {});
|
||||||
|
|
||||||
// B. Atualiza JSON
|
// B. Atualiza JSON
|
||||||
|
|
@ -1812,7 +1806,7 @@ async function syncRelationalToJsonPayments() {
|
||||||
|
|
||||||
const hasChanges = p.status !== newStatus ||
|
const hasChanges = p.status !== newStatus ||
|
||||||
Number(p.valor_pago || 0) !== Number(match.valor_pago || 0) ||
|
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) {
|
if (hasChanges) {
|
||||||
updatedCount++;
|
updatedCount++;
|
||||||
|
|
@ -1821,7 +1815,7 @@ async function syncRelationalToJsonPayments() {
|
||||||
status: newStatus,
|
status: newStatus,
|
||||||
paidDate: match.data_pagamento || p.paidDate,
|
paidDate: match.data_pagamento || p.paidDate,
|
||||||
valor_pago: Number(match.valor_pago || 0),
|
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)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -556,15 +556,14 @@ export async function syncJsonToRelationalTables() {
|
||||||
const amount = Number(p.amount || 0);
|
const amount = Number(p.amount || 0);
|
||||||
const discount = Number(p.discount || 0);
|
const discount = Number(p.discount || 0);
|
||||||
|
|
||||||
// Se está pago, o 'amount' do JSON geralmente é o líquido.
|
// O valor da parcela (face value) é sempre o amount do JSON (ex: 170)
|
||||||
// O valor principal (valor) deve ser o BRUTO.
|
// O desconto é condicional e NÃO altera o valor base da parcela
|
||||||
let valorBruto = amount;
|
let valorBruto = amount;
|
||||||
let valorPago = 0;
|
let valorPago = 0;
|
||||||
|
|
||||||
if (isPaid) {
|
if (isPaid) {
|
||||||
valorPago = amount;
|
// Usar valor_pago explícito do JSON se disponível, senão calcular (amount - discount)
|
||||||
// Se o amount vindo do JSON for o líquido (igual ou menor que o bruto esperado), restauramos o bruto
|
valorPago = Number(p.valor_pago || 0) || (amount - discount);
|
||||||
valorBruto = amount + discount;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await client.query(
|
await client.query(
|
||||||
|
|
@ -578,7 +577,7 @@ export async function syncJsonToRelationalTables() {
|
||||||
aluno_id = EXCLUDED.aluno_id,
|
aluno_id = EXCLUDED.aluno_id,
|
||||||
asaas_installment_id = COALESCE(EXCLUDED.asaas_installment_id, alunos_cobrancas.asaas_installment_id),
|
asaas_installment_id = COALESCE(EXCLUDED.asaas_installment_id, alunos_cobrancas.asaas_installment_id),
|
||||||
installment = COALESCE(EXCLUDED.installment, alunos_cobrancas.installment),
|
installment = COALESCE(EXCLUDED.installment, alunos_cobrancas.installment),
|
||||||
valor = GREATEST(alunos_cobrancas.valor, EXCLUDED.valor),
|
valor = EXCLUDED.valor,
|
||||||
vencimento = EXCLUDED.vencimento,
|
vencimento = EXCLUDED.vencimento,
|
||||||
link_boleto = COALESCE(EXCLUDED.link_boleto, alunos_cobrancas.link_boleto),
|
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,
|
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),
|
total_installments = COALESCE(EXCLUDED.total_installments, alunos_cobrancas.total_installments),
|
||||||
contract_id = COALESCE(EXCLUDED.contract_id, alunos_cobrancas.contract_id),
|
contract_id = COALESCE(EXCLUDED.contract_id, alunos_cobrancas.contract_id),
|
||||||
asaas_payment_url = COALESCE(EXCLUDED.asaas_payment_url, alunos_cobrancas.asaas_payment_url),
|
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),
|
data_pagamento = COALESCE(EXCLUDED.data_pagamento, alunos_cobrancas.data_pagamento),
|
||||||
valor_pago = EXCLUDED.valor_pago`,
|
valor_pago = EXCLUDED.valor_pago`,
|
||||||
[
|
[
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue