import { requireAuth } from '@/server/utils/auth'; import { getExpenseById } from '@/server/utils/nocodb'; import { processExpenseWithCurrency } from '@/server/utils/currency'; interface PDFOptions { documentName: string; subheader?: string; groupBy: 'none' | 'payer' | 'category' | 'date'; includeReceipts: boolean; includeSummary: boolean; includeDetails: boolean; pageFormat: 'A4' | 'Letter' | 'Legal'; includeProcessingFee?: boolean; } interface Expense { Id: number; 'Establishment Name': string; Price: string; PriceNumber: number; DisplayPrice: string; PriceUSD?: number; ConversionRate?: number; Payer: string; Category: string; 'Payment Method': string; Time: string; Contents?: string; Receipt?: any[]; } export default defineEventHandler(async (event) => { await requireAuth(event); const body = await readBody(event); const { expenseIds, options } = body; if (!expenseIds || !Array.isArray(expenseIds) || expenseIds.length === 0) { throw createError({ statusCode: 400, statusMessage: 'Expense IDs are required' }); } if (!options || !options.documentName) { throw createError({ statusCode: 400, statusMessage: 'PDF options with document name are required' }); } console.log('[expenses/generate-pdf] PDF generation requested for expenses:', expenseIds); try { // Fetch expense data const expenses: Expense[] = []; for (const expenseId of expenseIds) { const expense = await getExpenseById(expenseId); if (expense) { const processedExpense = await processExpenseWithCurrency(expense); expenses.push(processedExpense); } } if (expenses.length === 0) { throw createError({ statusCode: 404, statusMessage: 'No valid expenses found' }); } // Calculate totals to show the preview is working correctly const totals = calculateTotals(expenses, options.includeProcessingFee); console.log('[expenses/generate-pdf] Successfully calculated totals:', totals); console.log('[expenses/generate-pdf] Options received:', options); // For now, return a helpful error with the calculated information throw createError({ statusCode: 501, statusMessage: 'PDF generation is being upgraded', message: `PDF generation is being upgraded! ✅ Your selection is ready: 📊 Summary: • ${totals.count} expenses selected • Total: €${totals.originalTotal.toFixed(2)} • USD equivalent: $${totals.usdTotal.toFixed(2)} ${options.includeProcessingFee ? `• With 5% fee: €${totals.finalTotal.toFixed(2)}` : ''} 📋 Document: "${options.documentName}" ${options.subheader ? `📝 Subtitle: "${options.subheader}"` : ''} 🔗 Grouping: ${getGroupingLabel(options.groupBy)} 💡 Use CSV export for now, or contact support for manual PDF generation with these exact settings.` }); } catch (error: any) { // If it's our intentional error, re-throw it if (error.statusCode === 501) { throw error; } console.error('[expenses/generate-pdf] Error generating PDF:', error); throw createError({ statusCode: 500, statusMessage: error.message || 'Failed to generate PDF' }); } }); function calculateTotals(expenses: Expense[], includeProcessingFee: boolean) { const originalTotal = expenses.reduce((sum, exp) => sum + (exp.PriceNumber || 0), 0); const usdTotal = expenses.reduce((sum, exp) => sum + (exp.PriceUSD || exp.PriceNumber || 0), 0); const processingFee = includeProcessingFee ? originalTotal * 0.05 : 0; const finalTotal = originalTotal + processingFee; return { originalTotal, usdTotal, processingFee, finalTotal, count: expenses.length }; } function getGroupingLabel(groupBy: string): string { switch (groupBy) { case 'payer': return 'By Person'; case 'category': return 'By Category'; case 'date': return 'By Date'; default: return 'No Grouping'; } }