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 `
${agency.city}
USt-IdNr.: ${agency.vatId}
${agency.registerCourt} ${agency.registerNumber}
${invoice.company.name}
${invoice.company.legalForm ? `${invoice.company.legalForm}
` : ''} ${invoice.company.city ? `${invoice.company.city}
` : ''} ${invoice.company.vatId ? `USt-IdNr.: ${invoice.company.vatId}
` : ''}| 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)} |