diff --git a/app-backend/src/controllers/payroll.controller.js b/app-backend/src/controllers/payroll.controller.js index 1cfbf8d75..a04fd5f70 100644 --- a/app-backend/src/controllers/payroll.controller.js +++ b/app-backend/src/controllers/payroll.controller.js @@ -1,3 +1,22 @@ +/** + * @file controllers/payroll.controller.js + * @description Payroll endpoint handlers for SecureShift. + * + * Endpoints + * GET /api/v1/payroll – Generate / retrieve payroll summaries + * POST /api/v1/payroll/approve – Approve one or more PENDING payroll records + * POST /api/v1/payroll/process – Process one or more APPROVED payroll records + * GET /api/v1/payroll/export – Export payroll data as CSV or PDF + * + * POST /api/v1/payroll/attendance – Record attendance (admin / guard) + * GET /api/v1/payroll/attendance/:shiftId – Get attendance for a shift + */ + +import mongoose from 'mongoose'; +import Payroll from '../models/Payroll.js'; +import ShiftAttendance from '../models/ShiftAttendance.js'; +import Shift from '../models/Shift.js'; +import User from '../models/User.js'; import { ACTIONS } from '../middleware/logger.js'; import { approvePayrollRecords, @@ -6,6 +25,10 @@ import { getPayrollRecords, processPayrollRecords, } from '../services/payroll.service.js'; +import { + sendPayrollApproved, + sendPayrollProcessed, +} from '../utils/sendEmail.js'; const sendError = (res, error, fallbackMessage) => { return res.status(error.statusCode || 500).json({ diff --git a/app-backend/src/utils/sendEmail.js b/app-backend/src/utils/sendEmail.js index b947996fc..b2d4df4ef 100644 --- a/app-backend/src/utils/sendEmail.js +++ b/app-backend/src/utils/sendEmail.js @@ -143,6 +143,73 @@ export const sendOTP = async (email, otp, recipientName = '') => { } }; +/** + * Notify a guard that their payroll has been APPROVED. + * In dev mode (EMAIL_ENABLED=false) prints to terminal. + */ +export const sendPayrollApproved = async (email, guardName, grossPay, periodType, startDate, endDate) => { + if (!EMAIL_ENABLED) { + console.log(''); + console.log('┌─────────────────────────────────────────┐'); + console.log(`│ 💰 Payroll APPROVED for ${guardName.substring(0, 15).padEnd(15)} │`); + console.log(`│ Period : ${String(periodType).padEnd(30)}│`); + console.log(`│ From : ${String(startDate).padEnd(30)}│`); + console.log(`│ To : ${String(endDate).padEnd(30)}│`); + console.log(`│ Gross : $${String(grossPay.toFixed(2)).padEnd(29)}│`); + console.log('└─────────────────────────────────────────┘'); + console.log(''); + return; + } + + const mailOptions = { + from: `"SecureShift" <${process.env.SMTP_USER}>`, + to: email, + subject: 'Your SecureShift Payroll Has Been Approved', + text: `Hi ${guardName},\n\nYour payroll for the period ${startDate} to ${endDate} has been approved.\n\nGross Pay: $${grossPay.toFixed(2)}\n\nIt will be processed shortly.\n\nBest regards,\nSecureShift Team`, + }; + + try { + await transporter.sendMail(mailOptions); + console.log(`✅ Payroll approval email sent to ${email}`); + } catch (err) { + console.error('❌ Failed to send payroll approval email:', err.message); + } +}; + +/** + * Notify a guard that their payroll has been PROCESSED (payment on the way). + * In dev mode (EMAIL_ENABLED=false) prints to terminal. + */ +export const sendPayrollProcessed = async (email, guardName, grossPay, periodType, startDate, endDate) => { + if (!EMAIL_ENABLED) { + console.log(''); + console.log('┌─────────────────────────────────────────┐'); + console.log(`│ ✅ Payroll PROCESSED for ${guardName.substring(0, 14).padEnd(14)} │`); + console.log(`│ Period : ${String(periodType).padEnd(30)}│`); + console.log(`│ From : ${String(startDate).padEnd(30)}│`); + console.log(`│ To : ${String(endDate).padEnd(30)}│`); + console.log(`│ Gross : $${String(grossPay.toFixed(2)).padEnd(29)}│`); + console.log('│ Payment is on its way! │'); + console.log('└─────────────────────────────────────────┘'); + console.log(''); + return; + } + + const mailOptions = { + from: `"SecureShift" <${process.env.SMTP_USER}>`, + to: email, + subject: 'Your SecureShift Payroll Has Been Processed', + text: `Hi ${guardName},\n\nGreat news! Your payroll for the period ${startDate} to ${endDate} has been processed.\n\nGross Pay: $${grossPay.toFixed(2)}\n\nPayment is on its way.\n\nBest regards,\nSecureShift Team`, + }; + + try { + await transporter.sendMail(mailOptions); + console.log(`✅ Payroll processed email sent to ${email}`); + } catch (err) { + console.error('❌ Failed to send payroll processed email:', err.message); + } +}; + export const sendEmployerCredentials = async (email, tempPassword, contactPerson, companyName) => { transporter = createTransporter();