UNPKG

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
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 = ` <!DOCTYPE html> <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 = ` <!DOCTYPE html> <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;