177 lines
5.1 KiB
TypeScript
177 lines
5.1 KiB
TypeScript
import { sendEmail } from '~/server/utils/email';
|
|
import { getMemberById } from '~/server/utils/nocodb';
|
|
import { calculateDaysUntilDue, calculateOverdueDays, getNextDuesDate, isPaymentOverOneYear, isDuesActuallyCurrent } from '~/utils/dues-calculations';
|
|
import { getRegistrationConfig } from '~/server/utils/admin-config';
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
try {
|
|
const memberId = getRouterParam(event, 'id');
|
|
const { reminderType } = await readBody(event);
|
|
|
|
if (!memberId) {
|
|
throw createError({
|
|
statusCode: 400,
|
|
statusMessage: 'Member ID is required'
|
|
});
|
|
}
|
|
|
|
if (!['due-soon', 'overdue'].includes(reminderType)) {
|
|
throw createError({
|
|
statusCode: 400,
|
|
statusMessage: 'Invalid reminder type. Must be "due-soon" or "overdue"'
|
|
});
|
|
}
|
|
|
|
// Get member data
|
|
const member = await getMemberById(memberId);
|
|
|
|
if (!member) {
|
|
throw createError({
|
|
statusCode: 404,
|
|
statusMessage: 'Member not found'
|
|
});
|
|
}
|
|
|
|
if (!member.email) {
|
|
throw createError({
|
|
statusCode: 400,
|
|
statusMessage: 'Member does not have an email address'
|
|
});
|
|
}
|
|
|
|
// Get current admin configuration for payment details
|
|
const registrationConfig = getRegistrationConfig();
|
|
|
|
// Calculate dues status
|
|
const memberName = member.FullName || `${member.first_name} ${member.last_name}`.trim() || 'Member';
|
|
const nextDueDate = getNextDuesDate(member);
|
|
const membershipFee = `€${registrationConfig.membershipFee}`;
|
|
const paymentIban = registrationConfig.iban || '';
|
|
const accountHolder = registrationConfig.accountHolder || '';
|
|
const portalUrl = `${process.env.NUXT_PUBLIC_DOMAIN || 'https://monacousa.org'}/dashboard`;
|
|
const currentYear = new Date().getFullYear();
|
|
|
|
// Format dates
|
|
const formatDate = (dateString: string | Date | null | undefined): string => {
|
|
if (!dateString) return '';
|
|
try {
|
|
const date = new Date(dateString);
|
|
return date.toLocaleDateString('en-US', {
|
|
year: 'numeric',
|
|
month: 'long',
|
|
day: 'numeric'
|
|
});
|
|
} catch {
|
|
return String(dateString);
|
|
}
|
|
};
|
|
|
|
let emailData;
|
|
let subject;
|
|
let template;
|
|
|
|
if (reminderType === 'due-soon') {
|
|
// Check if this member actually has dues coming due
|
|
const daysUntilDue = calculateDaysUntilDue(member);
|
|
|
|
if (!daysUntilDue || daysUntilDue.daysUntilDue <= 0) {
|
|
throw createError({
|
|
statusCode: 400,
|
|
statusMessage: 'Member does not have dues coming due soon'
|
|
});
|
|
}
|
|
|
|
subject = `MonacoUSA - Annual Dues Due in ${daysUntilDue.daysUntilDue} Days`;
|
|
template = 'dues-due-soon';
|
|
|
|
emailData = {
|
|
memberName,
|
|
memberId: member.member_id || member.Id,
|
|
memberEmail: member.email,
|
|
memberSince: formatDate(member.member_since),
|
|
amount: membershipFee,
|
|
dueDate: formatDate(daysUntilDue.nextDueDate),
|
|
daysUntilDue: daysUntilDue.daysUntilDue,
|
|
paymentIban,
|
|
accountHolder,
|
|
portalUrl,
|
|
currentYear
|
|
};
|
|
|
|
} else if (reminderType === 'overdue') {
|
|
// Check if this member is actually overdue
|
|
const isDuesCurrent = isDuesActuallyCurrent(member);
|
|
|
|
if (isDuesCurrent) {
|
|
throw createError({
|
|
statusCode: 400,
|
|
statusMessage: 'Member does not have overdue dues'
|
|
});
|
|
}
|
|
|
|
const overdueDays = calculateOverdueDays(member);
|
|
const originalDueDate = member.payment_due_date || nextDueDate;
|
|
|
|
subject = `MonacoUSA - URGENT: Overdue Dues Notice (${overdueDays} days overdue)`;
|
|
template = 'dues-overdue';
|
|
|
|
emailData = {
|
|
memberName,
|
|
memberId: member.member_id || member.Id,
|
|
memberEmail: member.email,
|
|
memberSince: formatDate(member.member_since),
|
|
amount: membershipFee,
|
|
originalDueDate: formatDate(originalDueDate),
|
|
daysOverdue: overdueDays,
|
|
paymentIban,
|
|
accountHolder,
|
|
portalUrl,
|
|
currentYear
|
|
};
|
|
}
|
|
|
|
// Send the email
|
|
console.log(`[Dues Reminder] Sending ${reminderType} reminder to ${member.email}`, {
|
|
memberId,
|
|
memberName,
|
|
reminderType,
|
|
emailData
|
|
});
|
|
|
|
await sendEmail({
|
|
to: member.email,
|
|
subject,
|
|
template,
|
|
data: emailData
|
|
});
|
|
|
|
// Log the reminder sent (could store in database for tracking)
|
|
console.log(`[Dues Reminder] Successfully sent ${reminderType} reminder to ${memberName} (${member.email})`);
|
|
|
|
return {
|
|
success: true,
|
|
message: `${reminderType === 'due-soon' ? 'Due soon' : 'Overdue'} reminder sent successfully`,
|
|
data: {
|
|
memberId,
|
|
memberName,
|
|
memberEmail: member.email,
|
|
reminderType,
|
|
sentAt: new Date().toISOString()
|
|
}
|
|
};
|
|
|
|
} catch (error: any) {
|
|
console.error('[Dues Reminder] Error sending reminder:', error);
|
|
|
|
// Handle specific error cases
|
|
if (error.statusCode) {
|
|
throw error;
|
|
}
|
|
|
|
throw createError({
|
|
statusCode: 500,
|
|
statusMessage: error.message || 'Failed to send dues reminder'
|
|
});
|
|
}
|
|
});
|