import { NextResponse } from 'next/server' import { db } from '@/lib/db' export const dynamic = 'force-dynamic' export const maxDuration = 15 // GET /api/aos/invoices/[id]/pdf // Generates a real PDF invoice (German layout) using HTML → print-ready format. // Returns the invoice as an HTML page that browsers can "Print to PDF". // In production, this would use Typst (PRD §5.2) for server-side PDF generation. export async function GET( _req: Request, { params }: { params: Promise<{ id: string }> } ) { try { const { id } = await params const invoice = await db.invoice.findUnique({ where: { id }, include: { company: true, lines: true }, }) if (!invoice) { return NextResponse.json({ error: 'invoice not found' }, { status: 404 }) } // Get agency settings for the invoice header const settings = await db.setting.findMany() const getSetting = (key: string) => settings.find((s) => s.key === key)?.value ?? '' const agencyName = getSetting('company.name') || 'Nordlicht Digital GmbH' const agencyLegalForm = getSetting('company.legalForm') || 'GmbH' const agencyVatId = getSetting('company.vatId') || '' const agencyRegisterCourt = getSetting('company.registerCourt') || '' const agencyRegisterNumber = getSetting('company.registerNumber') || '' const agencyCity = getSetting('company.city') || '' const agencyBank = getSetting('company.bank') || '' const agencyBic = getSetting('company.bic') || '' // Generate German invoice HTML (print-ready) const html = generateInvoiceHTML({ invoice, agency: { name: agencyName, legalForm: agencyLegalForm, vatId: agencyVatId, registerCourt: agencyRegisterCourt, registerNumber: agencyRegisterNumber, city: agencyCity, bank: agencyBank, bic: agencyBic }, }) return new Response(html, { headers: { 'content-type': 'text/html; charset=utf-8', 'content-disposition': `inline; filename="${invoice.number}.html"`, 'cache-control': 'no-store', }, }) } catch (err) { const msg = err instanceof Error ? err.message : 'internal server error' return NextResponse.json({ error: msg }, { status: 500 }) } } function generateInvoiceHTML({ invoice, agency }: { invoice: { number: string; status: string; type: string; issueDate: Date; dueDate: Date; netCents: number; taxBp: number; grossCents: number; zugferd: boolean; xrechnung: boolean; company: { name: string; legalForm: string | null; city: string | null; vatId: string | null }; lines: { description: string; quantity: number; unitCents: number; totalCents: number }[]; }; agency: { name: string; legalForm: string; vatId: string; registerCourt: string; registerNumber: string; city: string; bank: string; bic: string }; }): string { const fmtDate = (d: Date) => d.toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' }) const fmtEuro = (cents: number) => (cents / 100).toLocaleString('de-DE', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + ' €' const taxPct = (invoice.taxBp / 100).toFixed(0) return ` Rechnung ${invoice.number}

${agency.name}

${agency.city}

USt-IdNr.: ${agency.vatId}

${agency.registerCourt} ${agency.registerNumber}

RECHNUNG

Nr.: ${invoice.number}

Datum: ${fmtDate(invoice.issueDate)}

Fällig: ${fmtDate(invoice.dueDate)}

${invoice.zugferd ? '

✓ ZUGFeRD

' : ''} ${invoice.xrechnung ? '

✓ XRechnung

' : ''}

Rechnungsempfänger

${invoice.company.name}

${invoice.company.legalForm ? `

${invoice.company.legalForm}

` : ''} ${invoice.company.city ? `

${invoice.company.city}

` : ''} ${invoice.company.vatId ? `

USt-IdNr.: ${invoice.company.vatId}

` : ''}
${invoice.lines.map((l) => ` `).join('')}
Beschreibung Menge Einzelpreis Gesamt
${l.description} ${l.quantity} ${fmtEuro(l.unitCents)} ${fmtEuro(l.totalCents)}
Netto${fmtEuro(invoice.netCents)}
USt (${taxPct}%)${fmtEuro(invoice.grossCents - invoice.netCents)}
Gesamt${fmtEuro(invoice.grossCents)}
` }