// Create a new file: src/services/notificationService.js const nodemailer = require('nodemailer'); const config = require('../config'); /** * Service for handling notifications including stock alerts */ const notificationService = { /** * Create email transporter * @returns {Object} Configured nodemailer transporter */ createTransporter() { return nodemailer.createTransport({ host: config.email.host, port: config.email.port, auth: { user: config.email.user, pass: config.email.pass } }); }, /** * Process pending low stock notifications * @param {Object} pool - Database connection pool * @param {Function} query - Database query function * @returns {Promise} Number of notifications processed */ async processLowStockNotifications(pool, query) { const client = await pool.connect(); let processedCount = 0; try { await client.query('BEGIN'); // Get pending low stock notifications const pendingNotifications = await client.query(` SELECT id FROM notification_logs WHERE notification_type = 'low_stock_alert' AND status = 'pending' LIMIT 50 `); if (pendingNotifications.rows.length === 0) { await client.query('COMMIT'); return 0; } // Get products with current stock below threshold const lowStockProducts = await client.query(` SELECT p.id, p.name, p.stock_quantity, p.stock_notification FROM products p WHERE p.stock_notification IS NOT NULL AND p.stock_notification->>'enabled' = 'true' AND p.stock_notification->>'email' IS NOT NULL AND p.stock_quantity <= (p.stock_notification->>'threshold')::int `); if (lowStockProducts.rows.length === 0) { // Mark notifications as processed with no action const notificationIds = pendingNotifications.rows.map(n => n.id); await client.query(` UPDATE notification_logs SET status = 'completed', error_message = 'No eligible products found' WHERE id = ANY($1) `, [notificationIds]); await client.query('COMMIT'); return 0; } // Initialize email transporter const transporter = this.createTransporter(); // Send notifications for each low stock product console.log(JSON.stringify(lowStockProducts, null, 4)) for (const product of lowStockProducts.rows) { console.log(JSON.stringify(product, null, 4)) console.log(typeof product.stock_notification) const notification = JSON.parse(product.stock_notification); try { // Send email notification await transporter.sendMail({ from: config.email.reply, to: notification.email, subject: `Low Stock Alert: ${product.name}`, html: this.generateLowStockEmailTemplate(product) }); // Mark one notification as processed if (pendingNotifications.rows[processedCount]) { await client.query(` UPDATE notification_logs SET status = 'success' WHERE id = $1 `, [pendingNotifications.rows[processedCount].id]); processedCount++; } } catch (error) { console.error(`Error sending low stock notification for product ${product.id}:`, error); // Mark notification as failed if (pendingNotifications.rows[processedCount]) { await client.query(` UPDATE notification_logs SET status = 'failed', error_message = $2 WHERE id = $1 `, [pendingNotifications.rows[processedCount].id, error.message]); processedCount++; } } } await client.query('COMMIT'); return processedCount; } catch (error) { await client.query('ROLLBACK'); console.error('Error processing low stock notifications:', error); throw error; } finally { client.release(); } }, /** * Generate email template for low stock notification * @param {Object} product - Product with low stock * @returns {string} HTML email template */ generateLowStockEmailTemplate(product) { const stockNotification = JSON.parse(product.stock_notification); const threshold = stockNotification.threshold || 0; return `

Low Stock Alert

Hello,

This is an automated notification to inform you that the following product is running low on stock:

${product.name}

Current Stock: ${product.stock_quantity}

Threshold: ${threshold}

You might want to restock this item soon to avoid running out of inventory.

This is an automated notification. You received this because you set up stock notifications for this product.

© ${new Date().getFullYear()} Rocks, Bones & Sticks. All rights reserved.

`; } }; module.exports = notificationService;