feat(rechnung): branded invoice with VAT ID and per-rate grouping

The invoice PDF and the invoice page now share one branded layout:
logo (uploaded brand logo, otherwise the drawn honeycomb mark) and
wordmark in the header, honey accents, a forest-green table header,
a meta box with invoice number, date, order number and VAT ID, and a
footer with address, contact, VAT ID and page numbers on every page.

A mixed cart (e.g. honey at 7 % and a beeswax candle at 19 %) lists
its items in one block per VAT rate with a subtotal each. Next to the
totals, a VAT table shows net, VAT and gross per rate plus a sum row.
Long invoices continue on further pages with the table header repeated.

The VAT identification number is a new company field
(company.vatId), editable in Admin > Inhalte, defaulting to the
number already stated in the imprint.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Opus 5.5 committed 2026-10-01 00:49:59 +02:00
1 parent 1daef79026
commit 77bdd9958c
12 files changed
+965 -246

No files matched your search

+1
View File
@@ -70,6 +70,7 @@ export const CONTENT_REGISTRY: ContentFieldDef[] = [
{ key: "company.location", group: "company", labelDe: "Standort (Anzeigetext)", labelEn: "Location (display text)", type: "text", localized: false, getDefault: () => siteConfig.location },
{ key: "company.email", group: "company", labelDe: "E-Mail", labelEn: "Email", type: "text", localized: false, getDefault: () => siteConfig.email },
{ key: "company.phone", group: "company", labelDe: "Telefon", labelEn: "Phone", type: "text", localized: false, getDefault: () => siteConfig.phone },
{ key: "company.vatId", group: "company", labelDe: "Umsatzsteuer-Identifikationsnummer (erscheint auf jeder Rechnung)", labelEn: "VAT identification number (shown on every invoice)", type: "text", localized: false, getDefault: () => siteConfig.vatId },
{ key: "company.street", group: "company", labelDe: "Straße", labelEn: "Street", type: "text", localized: false, getDefault: () => siteConfig.address.street },
{ key: "company.zip", group: "company", labelDe: "PLZ", labelEn: "ZIP", type: "text", localized: false, getDefault: () => siteConfig.address.zip },
{ key: "company.city", group: "company", labelDe: "Ort", labelEn: "City", type: "text", localized: false, getDefault: () => siteConfig.address.city },