From 3fe234827efc8e4e6aa6687957420e8d77760d06 Mon Sep 17 00:00:00 2001 From: Sidney Date: Fri, 8 May 2026 09:58:02 -0300 Subject: [PATCH] =?UTF-8?q?fix:=20busca=20h=C3=ADbrida=20de=20alunos=20e?= =?UTF-8?q?=20resili=C3=AAncia=20de=20telefone=20nos=20disparos=20autom?= =?UTF-8?q?=C3=A1ticos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- manager/server.selfhosted.js | 60 ++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/manager/server.selfhosted.js b/manager/server.selfhosted.js index 66b55ae..365742c 100644 --- a/manager/server.selfhosted.js +++ b/manager/server.selfhosted.js @@ -490,26 +490,43 @@ async function sendEvolutionMessage(asaasPaymentId, eventType, paymentPayload = sentCache.add(cacheKey); setTimeout(() => sentCache.delete(cacheKey), 30000); - const aluno = appData.students?.find(s => s.id === cob.aluno_id); - if (!aluno) return console.log('[WhatsApp] Aluno não encontrado.'); + let aluno = appData.students?.find(s => s.id === cob.aluno_id); + + // Fallback: Se não achar no JSON, busca na tabela SQL de alunos + if (!aluno) { + const { rows } = await pool.query('SELECT * FROM alunos WHERE id = $1', [cob.aluno_id]); + if (rows[0]) { + const a = rows[0]; + aluno = { + id: a.id, + name: a.nome, + phone: a.telefone, + guardianPhone: a.telefone_responsavel, + birthDate: a.data_nascimento, + enrollmentNumber: a.numero_matricula + }; + } + } - const birthDateStr = aluno.data_nascimento || aluno.birthDate || ''; - let age = 18; - if (birthDateStr && birthDateStr.includes('-')) { - const parts = birthDateStr.split('T')[0].split('-'); - const year = parseInt(parts[0], 10); - const month = parseInt(parts[1], 10); - const day = parseInt(parts[2], 10); - const birthDate = new Date(year, month - 1, day); + if (!aluno) return console.log('[WhatsApp] Aluno não encontrado:', cob.aluno_id); + + let age = 18; // Padrão adulto para evitar travas se bday faltar + if (aluno.birthDate) { + const bDate = new Date(aluno.birthDate); const today = new Date(); - age = today.getFullYear() - birthDate.getFullYear(); - const m = today.getMonth() - birthDate.getMonth(); - if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate())) age--; + age = today.getFullYear() - bDate.getFullYear(); + const m = today.getMonth() - bDate.getMonth(); + if (m < 0 || (m === 0 && today.getDate() < bDate.getDate())) age--; } const isMinor = age < 18; - const targetPhone = (isMinor && (aluno.telefone_responsavel || aluno.guardianPhone)) ? (aluno.telefone_responsavel || aluno.guardianPhone) : (aluno.telefone || aluno.phone); - const targetName = (isMinor && (aluno.nome_responsavel || aluno.guardianName)) ? (aluno.nome_responsavel || aluno.guardianName) : (aluno.nome || aluno.name); + // Seleção resiliente: Tenta responsável se menor, mas aceita o do aluno se o do pai faltar (e vice-versa) + const targetPhone = isMinor + ? (aluno.guardianPhone || aluno.telefone_responsavel || aluno.phone || aluno.telefone) + : (aluno.phone || aluno.telefone || aluno.guardianPhone || aluno.telefone_responsavel); + + const targetName = (isMinor && (aluno.nome_responsavel || aluno.guardianName)) ? (aluno.nome_responsavel || aluno.guardianName) : (aluno.name || aluno.nome); + if (!targetPhone) return console.log('[WhatsApp] Sem telefone.'); let cleanPhone = targetPhone.replace(/\D/g, ''); @@ -557,11 +574,14 @@ async function sendEvolutionMessage(asaasPaymentId, eventType, paymentPayload = if (!templateText) return; + const valNum = parseFloat(fallbackValor); + const valorFormatado = !isNaN(valNum) ? valNum.toFixed(2).replace('.', ',') : '—'; + let msgFinal = templateText .replace(/{nome}/g, targetName) - .replace(/{nome_aluno}/g, aluno.name) - .replace(/{matricula}/g, aluno.enrollmentNumber || aluno.matricula || '—') - .replace(/{valor}/g, parseFloat(fallbackValor).toFixed(2).replace('.', ',')) + .replace(/{nome_aluno}/g, aluno.name || aluno.nome) + .replace(/{matricula}/g, aluno.enrollmentNumber || aluno.numero_matricula || aluno.matricula || '—') + .replace(/{valor}/g, valorFormatado) .replace(/{vencimento}/g, formatCobrancaDate(typeof fallbackVencimento === 'string' ? fallbackVencimento : (fallbackVencimento instanceof Date ? fallbackVencimento.toISOString().split('T')[0] : ''))) .replace(/{link_boleto}/g, pdfUrl) .replace(/{descricao}/g, descricao); @@ -1164,7 +1184,7 @@ async function executarRotinaCobrancas(tipo = 'ambos') { const jaEnviadoHoje = !rules.ignoreDailyLock && lastWarn && lastWarn.toDateString() === hoje.toDateString(); if (!jaEnviadoHoje && (diasDesdeUltimoAviso === null || diasDesdeUltimoAviso >= repeatEveryDays)) { - const sent = await sendEvolutionMessage(cob.asaas_payment_id, 'PAYMENT_OVERDUE'); + const sent = await sendEvolutionMessage(cob.asaas_payment_id, 'PAYMENT_OVERDUE', cob.valor, cob.vencimento); if (sent) { const currentCount = parseInt(cob.overdue_warnings_count) || 0; @@ -1203,7 +1223,7 @@ async function executarRotinaCobrancas(tipo = 'ambos') { const jaEnviadoHoje = !rules.ignoreDailyLock && lastWarn && lastWarn.toDateString() === hoje.toDateString(); if (!jaEnviadoHoje) { - const sent = await sendEvolutionMessage(cob.asaas_payment_id, 'PAYMENT_UPCOMING'); + const sent = await sendEvolutionMessage(cob.asaas_payment_id, 'PAYMENT_UPCOMING', cob.valor, cob.vencimento); if (sent) { await pool.query(