import type { Metadata } from "next"; import { noticeUrl } from "@/lib/notice"; import { noIndexPrivate } from "@/lib/seo"; import { notFound, redirect } from "next/navigation"; import { Container } from "@/components/ui"; import { BackLink } from "@/components/back-link"; import { getCurrentCustomer } from "@/lib/auth"; import { getInvoiceForCustomer } from "@/lib/account-data"; import { formatPrice, formatDate } from "@/lib/format"; import { getCompanyInfo } from "@/lib/company-info"; import { invoiceVatBreakdown } from "@/lib/vat"; import { getLocale } from "@/i18n/locale"; import { getDictionary } from "@/i18n/get-dictionary"; // Rechnungen enthalten Name, Anschrift und Kaufhistorie. noIndexPrivate // unterbindet zusaetzlich Cache-Kopie und Snippet (siehe lib/seo.ts). export const metadata: Metadata = { title: "Rechnung", robots: noIndexPrivate }; export const dynamic = "force-dynamic"; export default async function InvoicePage(props: PageProps<"/rechnung/[invoiceNumber]">) { const t = getDictionary(await getLocale()); const { invoiceNumber } = await props.params; const customer = await getCurrentCustomer(); if (!customer) redirect(noticeUrl("invoice-login")); const invoice = await getInvoiceForCustomer(invoiceNumber, customer.id); if (!invoice) notFound(); const { order } = invoice; const company = await getCompanyInfo(); // Aufstellung je Steuersatz — eine Bestellung kann Honig (ermaessigt) und // eine Bienenwachskerze (voll) zugleich enthalten. Dieselbe Funktion nutzt // das PDF, damit beide Darstellungen desselben Belegs uebereinstimmen. const vatLines = invoiceVatBreakdown(order); const isSmallBusiness = vatLines.length > 0 && vatLines.every((line) => line.ratePercent === 0); return (
{t.invoicePage.downloadPdf}

{company.name.toUpperCase()}

{company.address.street}
{company.address.zip} {company.address.city}

{t.invoicePage.invoiceLabel(invoice.invoiceNumber)}

{t.invoicePage.orderLabel(order.orderNumber)}

{formatDate(invoice.issuedAt)}

{order.firstName} {order.lastName}

{order.street}

{order.zip} {order.city}

{order.items.map((item) => ( ))}
{t.invoicePage.itemHeader} {t.invoicePage.quantityHeader} {t.invoicePage.priceHeader} {t.invoicePage.totalHeader}
{item.productName} ({item.weightGrams} g) {item.quantity} {formatPrice(item.unitPriceCents)} {formatPrice(item.totalCents)}
{t.invoicePage.subtotalLabel} {formatPrice(order.subtotalCents, order.currency)}
{t.invoicePage.shippingLabel} {order.shippingCents === 0 ? t.invoicePage.freeShipping : formatPrice(order.shippingCents, order.currency)}
{order.discountCents > 0 ? (
{t.invoicePage.discountLabel}{order.discountCode ? ` (${order.discountCode.code})` : ""} −{formatPrice(order.discountCents, order.currency)}
) : null} {/* Steuerausweis mit demselben an der Rechnung festgeschriebenen Satz wie im PDF — beide Darstellungen desselben Belegs muessen uebereinstimmen. */} {vatLines .filter((line) => line.ratePercent > 0) .map((line) => (
{t.invoicePage.netLabel} {formatPrice(line.netCents, order.currency)}
{t.invoicePage.vatLabel(line.ratePercent)} {formatPrice(line.vatCents, order.currency)}
))}
{t.invoicePage.totalLabel} {formatPrice(invoice.totalCents, order.currency)}
{/* Pflichtangabe bei Kleinunternehmerregelung — steht anstelle der Steuerzeilen, nicht zusaetzlich. */} {isSmallBusiness ? (

{t.invoicePage.smallBusinessNote}

) : null}

{t.invoicePage.legalNote}

); }