geofind-contract-generator
Version:
Sistema profesional de generación de contratos de arrendamiento y envío de correos
432 lines (408 loc) • 17.2 kB
JavaScript
const nodemailer = require('nodemailer');
const fs = require('fs');
const path = require('path');
require('dotenv').config({ path: __dirname + '/../.env' });
class EmailSender {
constructor() {
this.senderEmail = process.env.EMAIL_USER;
this.senderPassword = process.env.EMAIL_PASSWORD;
this.smtpServer = "smtp.gmail.com";
this.smtpPort = 587;
this.transporter = nodemailer.createTransport({
host: this.smtpServer,
port: this.smtpPort,
secure: false,
auth: {
user: this.senderEmail,
pass: this.senderPassword
}
});
}
// Situación donde se acepta el apartado
async sendAcceptanceEmail(recipientEmail, clientName, contractDetails, bankDetails = {}, attachmentPath = null) {
const subject = "✅ RECERVA ACEPTADA";
const htmlBody = `
<html>
<head>
<meta charset="UTF-8">
<style>
body {
font-family: 'Arial', sans-serif;
background-color: #f0f8f0;
padding: 20px;
margin: 0;
color: #404040;
}
.container {
max-width: 650px;
margin: auto;
background: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
.header {
background: linear-gradient(135deg, #006400, #008000);
color: white;
padding: 25px;
text-align: center;
border-bottom: 3px solid #004d00;
}
.header h1 {
margin: 0;
font-size: 24px;
font-weight: bold;
text-transform: uppercase;
letter-spacing: 1px;
}
.header p {
margin: 5px 0 0 0;
opacity: 0.9;
font-size: 14px;
}
.content {
padding: 30px;
}
.greeting {
font-size: 16px;
margin-bottom: 20px;
color: #404040;
}
.badge {
background: #2e7d32;
color: white;
padding: 10px 20px;
border-radius: 20px;
display: inline-block;
font-weight: bold;
font-size: 14px;
margin: 10px 0;
}
.section {
margin: 25px 0;
border-radius: 6px;
overflow: hidden;
}
.section-header {
background: #e8f5e9;
color: #2e7d32;
padding: 12px 15px;
font-weight: bold;
font-size: 14px;
border-left: 4px solid #2e7d32;
}
.section-content {
background: #f8fff8;
padding: 15px;
border: 1px solid #e0e0e0;
border-top: none;
}
.detail-item {
display: flex;
justify-content: space-between;
margin: 8px 0;
padding: 5px 0;
border-bottom: 1px solid #f0f0f0;
}
.detail-item:last-child {
border-bottom: none;
}
.map-container {
text-align: center;
margin: 15px 0;
padding: 10px;
}
.map-button {
display: inline-block;
background: linear-gradient(135deg, #1976D2, #42A5F5);
color: white;
padding: 12px 24px;
border-radius: 25px;
text-decoration: none;
font-weight: bold;
font-size: 14px;
border: none;
cursor: pointer;
box-shadow: 0 4px 8px rgba(25, 118, 210, 0.3);
transition: all 0.3s ease;
}
.map-button:hover {
background: linear-gradient(135deg, #1565C0, #1E88E5);
box-shadow: 0 6px 12px rgba(25, 118, 210, 0.4);
transform: translateY(-2px);
}
.bank-section {
background: linear-gradient(135deg, #1e3c72, #2a5298);
color: white;
padding: 20px;
border-radius: 6px;
margin: 20px 0;
border: 1px solid #1e3c72;
}
.bank-section h3 {
margin-top: 0;
text-align: center;
color: white;
font-weight: bold;
}
.bank-item {
display: flex;
justify-content: space-between;
margin: 10px 0;
padding: 8px 0;
border-bottom: 1px solid rgba(255,255,255,0.4);
}
.bank-item:last-child {
border-bottom: none;
}
.instructions {
background: #E3F2FD;
padding: 15px;
border-radius: 6px;
margin: 20px 0;
border-left: 4px solid #1976D2;
}
.instructions h4 {
margin-top: 0;
color: #1565C0;
}
.attachment-note {
background: #fff3e0;
padding: 12px;
border-radius: 6px;
border-left: 4px solid #ff9800;
margin: 20px 0;
font-weight: bold;
}
.footer {
background: #f5f5f5;
padding: 15px;
text-align: center;
font-size: 12px;
color: #666;
border-top: 1px solid #e0e0e0;
}
.icon {
margin-right: 8px;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>CONTRATO DE ARRENDAMIENTO</h1>
<p>Sistema de GeoFind</p>
</div>
<div class="content">
<div class="greeting">
Hola <strong>${clientName}</strong>,
</div>
<div class="badge">✅ RESERVA CONFIRMADA Y ACEPTADA</div>
<div class="section">
<div class="section-header">
📋 INFORMACIÓN DE LA RESERVA
</div>
<div class="section-content">
<div class="detail-item">
<span>🏠 Propiedad:</span>
<span><strong>${contractDetails.property_name || 'N/A'}</strong></span>
</div>
<div class="detail-item">
<span>📍 Dirección:</span>
<span>${contractDetails.property_address || 'N/A'}</span>
</div>
${contractDetails.map_property ? `
<div class="map-container">
<a href="${contractDetails.map_property}" target="_blank" class="map-button">
🗺️ Ver ubicación
</a>
</div>
` : ''}
<div class="detail-item">
<span>📅 Período de Renta:</span>
<span>${contractDetails.period || 'N/A'}</span>
</div>
<div class="detail-item">
<span>💰 Renta Mensual:</span>
<span><strong>$${contractDetails.price || '0'} MXN</strong></span>
</div>
</div>
</div>
${bankDetails && Object.keys(bankDetails).length > 0 ? `
<div class="bank-section">
<h3>🏦 INFORMACIÓN BANCARIA PARA PAGO</h3>
<div class="bank-item">
<span>🏛️ Banco:</span>
<span><strong>${bankDetails.bank_name || ''}</strong></span>
</div>
<div class="bank-item">
<span>🔢 Número de Cuenta:</span>
<span><strong>${bankDetails.account_number || ''}</strong></span>
</div>
<div class="bank-item">
<span>👤 Titular:</span>
<span><strong>${bankDetails.account_holder || ''}</strong></span>
</div>
${bankDetails.clabe ? `
<div class="bank-item">
<span>🏧 CLABE:</span>
<span><strong>${bankDetails.clabe || ''}</strong></span>
</div>` : ''}
</div>
<div class="instructions">
<h4>💳 INSTRUCCIONES DE PAGO</h4>
<p>• Realiza el depósito por el monto total en la cuenta bancaria indicada</p>
<p>• Envía el comprobante de pago a nuestro correo como confirmación</p>
<p>• Tu reserva se activará completamente una vez verificado el pago</p>
<p>⏰ <strong>Tienes 48 horas hábiles para realizar el pago y asegurar tu reserva</strong></p>
</div>
` : ''}
<div class="attachment-note">
📎 <strong>CONTRATO ADJUNTO:</strong> Encuentra tu contrato oficial en PDF adjunto a este correo.
Revísalo cuidadosamente y conserva una copia para tus registros.
</div>
</div>
<div class="footer">
<p>📧 Este correo fue generado automáticamente por el Sistema de GeoFind</p>
<p>📍 ${new Date().getFullYear()} - Todos los derechos reservados</p>
</div>
</div>
</body>
</html>
`;
// Adjuntar el contrato si existe la ruta
if (!attachmentPath) {
console.warn('⚠️ No se proporcionó ruta de contrato para adjuntar');
}
return this._sendEmail(recipientEmail, subject, htmlBody, attachmentPath);
}
async sendRejectionEmail(recipientEmail, clientName, reason) {
const subject = "❌ RESERVA RECHASADA";
const htmlBody = `
<html>
<head>
<meta charset="UTF-8">
<style>
body {
font-family: 'Arial', sans-serif;
background-color: #fff5f5;
padding: 20px;
margin: 0;
color: #404040;
}
.container {
max-width: 650px;
margin: auto;
background: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
.header {
background: linear-gradient(135deg, #cc0000, #ff4444);
color: white;
padding: 25px;
text-align: center;
border-bottom: 3px solid #990000;
}
.header h1 {
margin: 0;
font-size: 24px;
font-weight: bold;
text-transform: uppercase;
letter-spacing: 1px;
}
.header p {
margin: 5px 0 0 0;
opacity: 0.9;
font-size: 14px;
}
.content {
padding: 30px;
}
.greeting {
font-size: 16px;
margin-bottom: 20px;
color: #404040;
}
.rejection-box {
background: #ffebee;
padding: 20px;
border-radius: 6px;
border-left: 4px solid #c62828;
margin: 20px 0;
}
.rejection-box h3 {
margin-top: 0;
color: #c62828;
}
.footer {
background: #f5f5f5;
padding: 15px;
text-align: center;
font-size: 12px;
color: #666;
border-top: 1px solid #e0e0e0;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>SOLICITUD DE ARRENDAMIENTO RECHAZADA</h1>
<p>Sistema de GeoFind</p>
</div>
<div class="content">
<div class="greeting">
Hola <strong>${clientName}</strong>,
</div>
<div class="rejection-box">
<h3>❌ SOLICITUD RECHAZADA</h3>
<p><strong>Razón:</strong> ${reason}</p>
<p>Lamentamos informarte que tu solicitud de reserva no ha sido aceptada en esta ocasión.</p>
</div>
<p style="text-align: center; color: #666;">
Si crees que esto es un error o deseas más información,
no dudes en contactarnos respondiendo este correo.
</p>
</div>
<div class="footer">
<p>📧 Este correo fue generado automáticamente por el Sistema de GeoFind</p>
<p>📍 ${new Date().getFullYear()} - Todos los derechos reservados</p>
</div>
</div>
</body>
</html>
`;
return this._sendEmail(recipientEmail, subject, htmlBody);
}
async _sendEmail(to, subject, htmlBody, attachmentPath = null) {
try {
const mailOptions = {
from: `"Sistema GeoFind" <${this.senderEmail}>`,
to,
subject,
html: htmlBody,
};
// Adjuntar el archivo si existe
if (attachmentPath && fs.existsSync(attachmentPath)) {
mailOptions.attachments = [{
filename: `Contrato_${path.basename(attachmentPath)}`,
path: attachmentPath
}];
console.log(`📎 Contrato adjuntado: ${attachmentPath}`);
} else if (attachmentPath) {
console.warn(`⚠️ El archivo no existe: ${attachmentPath}`);
}
const result = await this.transporter.sendMail(mailOptions);
console.log(`✅ Correo enviado exitosamente a ${to}`);
return [true, 'Correo enviado correctamente'];
} catch (err) {
console.error(`❌ Error al enviar correo: ${err.message}`);
return [false, `Error al enviar correo: ${err.message}`];
}
}
}
module.exports = EmailSender;