import { requireSalesOrAdmin } from '@/server/utils/auth'; import { getExpenses, getCurrentMonthExpenses } from '@/server/utils/nocodb'; import { processExpenseWithCurrency } from '@/server/utils/currency'; import type { ExpenseFilters } from '@/utils/types'; export default defineEventHandler(async (event) => { console.log('[get-expenses] API called with query:', getQuery(event)); try { // Set proper headers setHeader(event, 'Cache-Control', 'no-cache'); setHeader(event, 'Content-Type', 'application/json'); // Check authentication first try { await requireSalesOrAdmin(event); console.log('[get-expenses] Authentication successful'); } catch (authError: any) { console.error('[get-expenses] Authentication failed:', authError); // Return proper error status if (authError.statusCode === 403) { throw createError({ statusCode: 403, statusMessage: 'Access denied. You need sales or admin role to view expenses.' }); } // Re-throw other auth errors throw authError; } const query = getQuery(event); // If no date filters provided, default to current month if (!query.startDate && !query.endDate) { console.log('[get-expenses] No date filters provided, defaulting to current month'); try { const result = await getCurrentMonthExpenses(); // Process expenses with currency conversion const processedExpenses = await Promise.all( result.list.map((expense: any) => processExpenseWithCurrency(expense)) ); return { ...result, list: processedExpenses }; } catch (dbError: any) { console.error('[get-expenses] Database error (current month):', dbError); if (dbError.statusCode === 403) { throw createError({ statusCode: 503, statusMessage: 'Expense database is currently unavailable. Please contact your administrator or try again later.' }); } throw createError({ statusCode: 500, statusMessage: 'Unable to fetch expense data. Please try again later.' }); } } // Build filters from query parameters const filters: ExpenseFilters = {}; if (query.startDate && typeof query.startDate === 'string') { filters.startDate = query.startDate; } if (query.endDate && typeof query.endDate === 'string') { filters.endDate = query.endDate; } if (query.payer && typeof query.payer === 'string') { filters.payer = query.payer; } if (query.category && typeof query.category === 'string') { filters.category = query.category as any; // Cast to ExpenseCategory } console.log('[get-expenses] Fetching expenses with filters:', filters); try { const result = await getExpenses(filters); // Process expenses with currency conversion const processedExpenses = await Promise.all( result.list.map((expense: any) => processExpenseWithCurrency(expense)) ); // Add formatted dates const transformedExpenses = processedExpenses.map(expense => ({ ...expense, FormattedDate: new Date(expense.Time).toLocaleDateString(), FormattedTime: new Date(expense.Time).toLocaleTimeString() })); // Calculate summary with USD totals const usdTotal = transformedExpenses.reduce((sum, e) => sum + (e.PriceUSD || e.PriceNumber || 0), 0); const originalTotal = transformedExpenses.reduce((sum, e) => sum + (e.PriceNumber || 0), 0); return { expenses: transformedExpenses, PageInfo: result.PageInfo, totalCount: result.PageInfo?.totalRows || transformedExpenses.length, summary: { total: originalTotal, // Original currency total (mixed currencies) totalUSD: usdTotal, // USD converted total count: transformedExpenses.length, uniquePayers: [...new Set(transformedExpenses.map(e => e.Payer))].length, currencies: [...new Set(transformedExpenses.map(e => e.currency))].filter(Boolean) } }; } catch (dbError: any) { console.error('[get-expenses] Database error (filtered):', dbError); if (dbError.statusCode === 403) { throw createError({ statusCode: 503, statusMessage: 'Expense database is currently unavailable. Please contact your administrator or try again later.' }); } throw createError({ statusCode: 500, statusMessage: 'Unable to fetch expense data. Please try again later.' }); } } catch (error: any) { console.error('[get-expenses] Top-level error:', error); // If it's already a proper H3 error, re-throw it if (error.statusCode) { throw error; } // Handle authentication errors specifically if (error.message?.includes('authentication') || error.message?.includes('auth')) { throw createError({ statusCode: 401, statusMessage: 'Authentication required. Please log in again.' }); } // Handle database connection errors if (error.message?.includes('database') || error.message?.includes('connection')) { throw createError({ statusCode: 503, statusMessage: 'Database temporarily unavailable. Please try again later.' }); } // Generic server error for anything else throw createError({ statusCode: 500, statusMessage: 'An unexpected error occurred. Please try again later.' }); } });