// server/api/members/overdue-count.get.ts export default defineEventHandler(async (event) => { try { const { getMembers } = await import('~/server/utils/nocodb'); const { calculateDuesStatus } = await import('~/server/utils/dues-calculator'); // Get all members const allMembers = await getMembers(); if (!allMembers?.list) { return { success: true, data: { count: 0, overdueMembers: [], dataSource: 'unavailable' } }; } const today = new Date(); const overdueMembers: any[] = []; for (const member of allMembers.list) { // Use dues-calculator for proper dues status calculation const duesStatus = await calculateDuesStatus(member); // Only include members who are overdue if (duesStatus.isOverdue && duesStatus.daysOverdue !== null) { // Format overdue duration const totalMonths = Math.floor(duesStatus.daysOverdue / 30.44); const years = Math.floor(totalMonths / 12); const months = totalMonths % 12; let formattedDuration = ''; if (years > 0) { formattedDuration = `${years} year${years !== 1 ? 's' : ''}`; if (months > 0) { formattedDuration += ` ${months} month${months !== 1 ? 's' : ''}`; } } else if (totalMonths > 0) { formattedDuration = `${totalMonths} month${totalMonths !== 1 ? 's' : ''}`; } else { const days = duesStatus.daysOverdue; formattedDuration = `${days} day${days !== 1 ? 's' : ''}`; } formattedDuration += ' overdue'; overdueMembers.push({ id: member.Id, name: member.FullName || `${member.first_name} ${member.last_name}`, email: member.email, status: member.membership_status, overdueDuration: formattedDuration, daysOverdue: duesStatus.daysOverdue, totalMonthsOverdue: totalMonths, dueDate: duesStatus.dueDate, amount: duesStatus.amount, currency: duesStatus.currency, dataSource: duesStatus.source, confidence: duesStatus.confidence, isInactive: member.membership_status === 'Inactive' }); } } // Sort by most overdue first (by days) overdueMembers.sort((a, b) => b.daysOverdue - a.daysOverdue); // Log data quality metrics const highConfidenceCount = overdueMembers.filter(m => m.confidence === 'high').length; const mediumConfidenceCount = overdueMembers.filter(m => m.confidence === 'medium').length; const lowConfidenceCount = overdueMembers.filter(m => m.confidence === 'low').length; console.log(`[overdue-count] Found ${overdueMembers.length} overdue members:`, { highConfidence: highConfidenceCount, mediumConfidence: mediumConfidenceCount, lowConfidence: lowConfidenceCount }); // Alert if many low confidence results if (lowConfidenceCount > overdueMembers.length * 0.3) { console.warn(`[overdue-count] High percentage of low confidence dues data (${lowConfidenceCount}/${overdueMembers.length}). Data quality review recommended.`); } return { success: true, data: { count: overdueMembers.length, overdueMembers: overdueMembers, dataQuality: { highConfidence: highConfidenceCount, mediumConfidence: mediumConfidenceCount, lowConfidence: lowConfidenceCount } } }; } 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' }); } });