Add overdue dues management system with notifications and status updates
All checks were successful
Build And Push Image / docker (push) Successful in 2m55s
All checks were successful
Build And Push Image / docker (push) Successful in 2m55s
- Create DuesOverdueBanner component for displaying overdue member counts - Add API endpoints for overdue count retrieval and status updates - Integrate overdue dues banner into admin and member list dashboards - Implement handlers for viewing overdue members and updating statuses - Add dues management section to admin dashboard
This commit is contained in:
@@ -22,9 +22,51 @@ export default defineEventHandler(async (event) => {
|
||||
|
||||
const overdueMembers: any[] = [];
|
||||
const upcomingMembers: any[] = [];
|
||||
const severelyOverdueMembers: any[] = [];
|
||||
|
||||
for (const member of allMembers.list) {
|
||||
// Skip if dues are already paid
|
||||
// Check for severely overdue members (more than 1 year past due date)
|
||||
let isSeverelyOverdue = false;
|
||||
|
||||
if (member.current_year_dues_paid === 'true' && member.membership_date_paid) {
|
||||
// If dues are marked as paid, check if it's been more than 1 year since payment
|
||||
const lastPaidDate = new Date(member.membership_date_paid);
|
||||
const oneYearFromPayment = new Date(lastPaidDate);
|
||||
oneYearFromPayment.setFullYear(oneYearFromPayment.getFullYear() + 1);
|
||||
|
||||
if (today > oneYearFromPayment) {
|
||||
isSeverelyOverdue = true;
|
||||
}
|
||||
} else if (member.current_year_dues_paid !== 'true') {
|
||||
// If dues are not paid, check payment due date or member since date
|
||||
let dueDate: Date;
|
||||
|
||||
if (member.payment_due_date) {
|
||||
dueDate = new Date(member.payment_due_date);
|
||||
} else if (member.member_since) {
|
||||
// Fallback: 1 year from member since date
|
||||
dueDate = new Date(member.member_since);
|
||||
dueDate.setFullYear(dueDate.getFullYear() + 1);
|
||||
} else {
|
||||
// Skip if we can't determine due date
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if more than 1 year overdue
|
||||
const oneYearOverdue = new Date(dueDate);
|
||||
oneYearOverdue.setFullYear(oneYearOverdue.getFullYear() + 1);
|
||||
|
||||
if (today > oneYearOverdue) {
|
||||
isSeverelyOverdue = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isSeverelyOverdue) {
|
||||
severelyOverdueMembers.push(member);
|
||||
continue; // Don't add to other lists
|
||||
}
|
||||
|
||||
// Skip if dues are already paid (and not severely overdue)
|
||||
if (member.current_year_dues_paid === 'true') {
|
||||
continue;
|
||||
}
|
||||
@@ -34,7 +76,7 @@ export default defineEventHandler(async (event) => {
|
||||
const dueDate = new Date(member.payment_due_date);
|
||||
|
||||
if (dueDate < today) {
|
||||
// Overdue
|
||||
// Overdue (but not severely overdue)
|
||||
overdueMembers.push(member);
|
||||
} else if (dueDate <= thirtyDaysFromNow) {
|
||||
// Due within 30 days
|
||||
|
||||
74
server/api/members/overdue-count.get.ts
Normal file
74
server/api/members/overdue-count.get.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
// server/api/members/overdue-count.get.ts
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
const { getMembers } = await import('~/server/utils/nocodb');
|
||||
|
||||
// Get all members
|
||||
const allMembers = await getMembers();
|
||||
|
||||
if (!allMembers?.list) {
|
||||
return {
|
||||
success: true,
|
||||
data: { count: 0 }
|
||||
};
|
||||
}
|
||||
|
||||
const today = new Date();
|
||||
let severelyOverdueCount = 0;
|
||||
|
||||
for (const member of allMembers.list) {
|
||||
// Check for severely overdue members (more than 1 year past due date)
|
||||
let isSeverelyOverdue = false;
|
||||
|
||||
if (member.current_year_dues_paid === 'true' && member.membership_date_paid) {
|
||||
// If dues are marked as paid, check if it's been more than 1 year since payment
|
||||
const lastPaidDate = new Date(member.membership_date_paid);
|
||||
const oneYearFromPayment = new Date(lastPaidDate);
|
||||
oneYearFromPayment.setFullYear(oneYearFromPayment.getFullYear() + 1);
|
||||
|
||||
if (today > oneYearFromPayment) {
|
||||
isSeverelyOverdue = true;
|
||||
}
|
||||
} else if (member.current_year_dues_paid !== 'true') {
|
||||
// If dues are not paid, check payment due date or member since date
|
||||
let dueDate: Date;
|
||||
|
||||
if (member.payment_due_date) {
|
||||
dueDate = new Date(member.payment_due_date);
|
||||
} else if (member.member_since) {
|
||||
// Fallback: 1 year from member since date
|
||||
dueDate = new Date(member.member_since);
|
||||
dueDate.setFullYear(dueDate.getFullYear() + 1);
|
||||
} else {
|
||||
// Skip if we can't determine due date
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if more than 1 year overdue
|
||||
const oneYearOverdue = new Date(dueDate);
|
||||
oneYearOverdue.setFullYear(oneYearOverdue.getFullYear() + 1);
|
||||
|
||||
if (today > oneYearOverdue) {
|
||||
isSeverelyOverdue = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isSeverelyOverdue) {
|
||||
severelyOverdueCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: { count: severelyOverdueCount }
|
||||
};
|
||||
|
||||
} catch (error: any) {
|
||||
console.error('[API] Error fetching overdue count:', error);
|
||||
|
||||
throw createError({
|
||||
statusCode: error.statusCode || 500,
|
||||
statusMessage: error.message || 'Failed to fetch overdue count'
|
||||
});
|
||||
}
|
||||
});
|
||||
108
server/api/members/update-overdue-statuses.post.ts
Normal file
108
server/api/members/update-overdue-statuses.post.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
// server/api/members/update-overdue-statuses.post.ts
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
const { getMembers, updateMember } = await import('~/server/utils/nocodb');
|
||||
|
||||
// Get all members
|
||||
const allMembers = await getMembers();
|
||||
|
||||
if (!allMembers?.list) {
|
||||
return {
|
||||
success: true,
|
||||
data: { updatedCount: 0 },
|
||||
message: 'No members found'
|
||||
};
|
||||
}
|
||||
|
||||
const today = new Date();
|
||||
const membersToUpdate: any[] = [];
|
||||
|
||||
for (const member of allMembers.list) {
|
||||
// Skip if already inactive
|
||||
if (member.membership_status === 'Inactive') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if member has overdue dues (more than 1 year)
|
||||
let isOverdue = false;
|
||||
|
||||
if (member.current_year_dues_paid === 'true' && member.membership_date_paid) {
|
||||
// If dues are marked as paid, check if it's been more than 1 year since payment
|
||||
const lastPaidDate = new Date(member.membership_date_paid);
|
||||
const oneYearFromPayment = new Date(lastPaidDate);
|
||||
oneYearFromPayment.setFullYear(oneYearFromPayment.getFullYear() + 1);
|
||||
|
||||
if (today > oneYearFromPayment) {
|
||||
isOverdue = true;
|
||||
}
|
||||
} else if (member.current_year_dues_paid !== 'true') {
|
||||
// If dues are not paid, check payment due date or member since date
|
||||
let dueDate: Date;
|
||||
|
||||
if (member.payment_due_date) {
|
||||
dueDate = new Date(member.payment_due_date);
|
||||
} else if (member.member_since) {
|
||||
// Fallback: 1 year from member since date
|
||||
dueDate = new Date(member.member_since);
|
||||
dueDate.setFullYear(dueDate.getFullYear() + 1);
|
||||
} else {
|
||||
// Skip if we can't determine due date
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if more than 1 year overdue
|
||||
const oneYearOverdue = new Date(dueDate);
|
||||
oneYearOverdue.setFullYear(oneYearOverdue.getFullYear() + 1);
|
||||
|
||||
if (today > oneYearOverdue) {
|
||||
isOverdue = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isOverdue) {
|
||||
membersToUpdate.push({
|
||||
id: member.Id,
|
||||
name: member.FullName || `${member.first_name} ${member.last_name}`,
|
||||
currentStatus: member.membership_status
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Update all overdue members to inactive
|
||||
let updatedCount = 0;
|
||||
const errors: string[] = [];
|
||||
|
||||
for (const memberInfo of membersToUpdate) {
|
||||
try {
|
||||
await updateMember(memberInfo.id, {
|
||||
membership_status: 'Inactive'
|
||||
});
|
||||
updatedCount++;
|
||||
console.log(`[API] Marked member ${memberInfo.name} (${memberInfo.id}) as inactive due to overdue dues`);
|
||||
} catch (error: any) {
|
||||
console.error(`[API] Failed to update member ${memberInfo.name} (${memberInfo.id}):`, error);
|
||||
errors.push(`Failed to update ${memberInfo.name}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`[API] Updated ${updatedCount} of ${membersToUpdate.length} overdue members to inactive status`);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
updatedCount,
|
||||
totalOverdue: membersToUpdate.length,
|
||||
errors: errors.length > 0 ? errors : undefined
|
||||
},
|
||||
message: `Successfully updated ${updatedCount} overdue member${updatedCount !== 1 ? 's' : ''} to inactive status`
|
||||
};
|
||||
|
||||
} catch (error: any) {
|
||||
console.error('[API] Error updating overdue member statuses:', error);
|
||||
|
||||
throw createError({
|
||||
statusCode: error.statusCode || 500,
|
||||
statusMessage: error.message || 'Failed to update overdue member statuses'
|
||||
});
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user