Commit Graph

18 Commits

Author SHA1 Message Date
Marco ba830947d2 Add Kleinunternehmerregelung (§19 UStG) support end-to-end
Checkout forces 0% VAT without de-grossing prices when the tenant is a
Kleinunternehmer (a business decision, not just an engineering default —
unlike the existing intra-community VAT exemption, which does de-gross).
Snapshotted onto the order at checkout time so a later toggle of the
company-settings checkbox never rewrites an already-issued invoice's tax
treatment — same reasoning as the existing vatExempt field.

Threaded through: checkout route, order creation/confirmation email,
on-demand invoice/Storno/Gutschrift downloads, the Bestellbestätigung
page, and the account order-detail page. The four storefront "inkl. X%
MwSt." price hints (shop grid, cart upsell, ToDo-Karten landing page,
homepage spotlight) drop that clause live when the setting is on. The
company-settings Live Preview reflects the checkbox in real time too.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-24 15:08:13 +00:00
Marco 89dd11bf77 Bump @einfach-produktiv/invoicing for the Netto sizing fix
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 21:34:41 +00:00
Marco 14b1d5685c Bump @einfach-produktiv/invoicing for the Netto/MwSt reorder fix
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 21:27:37 +00:00
Marco c60e936b7e Show company name + VAT ID on the account order-detail page; pick up Netto-row invoicing update
Order detail (/konto/bestellungen/[orderNumber]) already carried
companyName/vatId/vatExempt on CustomerOrderDetail but never rendered
them — a B2B customer looking at their own order couldn't see the
company/VAT info that's already on their invoice. Now shown in the
billing-address block, with a note when the order was VAT-exempt.

Also bumps @einfach-produktiv/invoicing to pick up the Netto-row fix
(now shown on every invoice, not just non-exempt ones).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 20:59:23 +00:00
Marco 6802636d1d Add innergemeinschaftliche-Lieferung VAT exemption for cross-border B2B
A validated EU business buyer (Österreich, the one cross-border option
this checkout offers) gets the sale zero-rated per §4 Nr. 1b UStG —
but only after a live VIES lookup confirms the VAT ID is actually
registered right now, never from format-validity alone (real
compliance risk otherwise). VIES unreachable fails closed: normal VAT
applies, no guessed exemption.

- lib/vies.ts: calls the EU's public VIES REST API.
- lib/vatExemption.ts: de-grosses item/shipping prices and computes
  the exempt totals; also picks the actual destination country
  (shipping override when set, billing otherwise).
- api/checkout/validate-vat: on-blur live check for instant feedback;
  api/checkout/route.ts re-runs the same check server-side at submit
  as the actual source of truth, and re-prices every line net-of-VAT
  when exempt.
- CheckoutContent.tsx: VIES status + a live exempt-totals preview;
  BestellbestaetigungContent.tsx mirrors it from the persisted
  snapshot. Both blur-validate every other checkout field now too
  (immediate inline errors, not just on submit).
- vatExempt/vatIdValidatedAt threaded through orderServer.ts,
  customerAuth.ts, orderEmail.ts, and both invoice-download routes so
  the invoice PDF and its e-invoice XML (companion payload-repo
  commit) reflect the exemption correctly wherever it's rendered.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 19:10:01 +00:00
Marco 1212b9d115 Pick up invoice paid-badge placement fix (outside the summary card) 2026-07-23 14:54:27 +00:00
Marco 03a29cf93c Validate PLZ/Packstationnummer/Postnummer format at checkout, pick up invoice layout fix
Native HTML5 pattern validation, mirroring the same rules the backend now enforces (Orders.ts/Customers.ts, same commit on that repo): PLZ digit count by country (5/DE, 4/AT+CH), Packstationnummer 1-3 digits, Postnummer 6-10 digits — catches the exact mix-up (long number in Packstationnummer, short number in Postnummer) found in a real order's data while investigating this. Client-side only for instant feedback; the backend re-validates regardless.

Also bumps @einfach-produktiv/invoicing to pick up the invoice PDF layout fixes (centered footer, clustered summary rows, relocated paid badge, stacked Packstation/Postnummer address lines).
2026-07-23 14:40:02 +00:00
Marco bd884357b0 Pick up critical e-invoice fix: shipping/discount degross bug + BR-CO-25
Every AllowanceCharge (shipping/discount) generated since the integer-cents pipeline landed was ~19%/7% too large — the /(1+rate/100) degross step was silently dropped in that rewrite. Real impact confirmed on a real order via an external checker: PayableRoundingAmount was showing >1 EUR instead of a normal few-cents residual. Also adds PrepaidAmount/PaymentTerms handling for BR-CO-25. Bumps to git.mk360.de/Marco/einfach-produktiv-invoicing@f6d8e9e.
2026-07-23 14:13:32 +00:00
Marco 0c3f7ddf2e Pick up e-invoicing shared-package fixes (EN16931 conformance bugs)
Phase 4's Mustang CI check found and fixed several real EN16931/PDF-A-3 compliance bugs in the shared package after Phase 3 had already shipped: a missing CII Delivery element, line amounts reported gross instead of net, missing AllowanceCharge entries for shipping/discount, and a BR-CO-16 rounding mismatch. Bumps the git-dependency lockfile to git.mk360.de/Marco/einfach-produktiv-invoicing@fd5d8e6 to get the fix into production-generated invoices.
2026-07-23 12:44:59 +00:00
Marco b70aefd5cc Phase 3: wire e-invoice generation into checkout email + download routes
invoiceData.ts's generateInvoicePdf()/generateCorrectionInvoicePdf() now
call renderInvoiceEInvoice()/renderCorrectionInvoiceEInvoice() instead of
the plain PDF renderers — both are the single wrapper every caller
already goes through (orderEmail.ts's checkout attachment, and the two
on-demand /invoice and /correction-invoice download routes), so this one
change switches all three. Buffer.from() wraps the library's Uint8Array
return value — every downstream consumer already expects a Buffer,
unchanged.

Imports from "@einfach-produktiv/invoicing/einvoice" (a new subpath, not
the package's main entry) — @e-invoice-eu/core pulls in Node-only
dependencies that broke the client bundle when reachable from the main
entry, which a Client Component also imports transitively (Live
Preview). See that package's own commit for the fix.

Existing failure-handling is unchanged and covers this: a PDF-generation
error still doesn't block the confirmation email, it just sends without
the attachment and alerts admin (see orderEmail.ts) — same safety net
that already existed for the plain-PDF path.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 11:32:46 +00:00
Marco 05a3b009d3 Phase 2: iban/bic instead of bankDetails, matching the backend collection
CompanySettings type now mirrors the payload repo's split bankDetails ->
iban/bic (see that repo's own commit for the reasoning). No behavior
change here beyond the type/fallback update — the actual footer
rendering lives in @einfach-produktiv/invoicing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 10:20:17 +00:00
Marco 66ac184a6f Move invoice/tax-breakdown PDF generation into @einfach-produktiv/invoicing
Phase 0 of the e-invoicing migration plan (see the E-Rechnung planning
session) — moves invoicePdf.tsx, correctionInvoicePdf.tsx, and
taxBreakdown.ts into a new shared package, consumed as a git dependency
by both this repo and the payload backend, instead of hand-duplicating
the correction-invoice logic between them (see that package's own README
for the three real drifts the duplication had already caused).

Consumed as raw TS/TSX source via next.config.ts's transpilePackages, not
a pre-built package. Needs `git` in the Docker deps stage and a
project .npmrc (allow-git=root) to let npm ci fetch a git-URL dependency
at all — npm 12+ disables that by default.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 09:18:20 +00:00
Marco 51fee198f4 Add a Vitest unit test suite (cart totals, invoice tax grouping, bundle contents)
No test infrastructure existed in this repo yet. Covers the pure logic
most likely to silently produce wrong numbers on a live order: discount/
shipping math, per-rate invoice grouping, and bundle-contents string
building. Extracted describeBundleContents() out of the checkout route
into its own module so it's importable from a test (route.ts files only
allow HTTP-method exports).
2026-07-22 11:40:31 +00:00
Marco 5232b14cdf Generate invoice PDFs attached to order confirmation, and send emails on order status changes
Invoice PDFs (§14 UStG line items, tenant-configurable VAT rate) are now
generated at checkout and attached to the confirmation email, plus
available on demand from the order-detail page. Payload-side, orders now
also email the customer on shipped/cancelled/return_requested/returned,
with Stornorechnung/Gutschrift correction PDFs attached for the latter two
so the original invoice's immutable number stays honest.
2026-07-22 09:49:56 +00:00
Marco df05ea5358 Add rate limiting, sliding sessions, email verification, GDPR self-service, order cancellation/returns, and critical-error alerting
Complements Payload's per-account login lockout with per-IP rate limiting
on auth routes; proxy.ts silently refreshes an active customer's session
via Payload's built-in refresh-token endpoint instead of a long-lived
token. Registration now sends a non-blocking email-verification link
(doesn't gate login, since checkout registers and immediately logs in
mid-purchase). /konto/profil gets GDPR export/delete; order detail pages
get self-service cancel/return-request, backed by a Payload hook that
closes a real gap (a customer's JWT could previously PATCH any field of
their own order, not just status). Checkout failures now email an alert
independent of Payload's own health, since Kuma's uptime checks can't see
an order silently failing to persist.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-22 07:28:01 +00:00
Marco 26ae4a15f4 Wire testimonials CMS collection and Payload Live Preview
Testimonials on /todo-cards, /newsletter, /challenge now come from the new
Payload testimonials collection via a shared TestimonialsGrid component,
instead of 3 separately hardcoded arrays.

Adds Next.js Draft Mode (/api/preview) plus Live-Preview-aware client
wrappers (LiveRichText, LiveTestimonialsGrid, LivePostContent) for posts,
legal pages, and testimonials — mounted only while Draft Mode is enabled,
so ordinary visitors keep getting the plain static components.
2026-07-21 19:02:20 +00:00
Marco e4f0c66d7b Add ToDo-Karten product page, fluid design tokens, and Navbar/Hero fixes
New /todo-cards page (Hero, How-it-works, Focus, Testimonials, Pricing)
built with the fluid clamp() token system (app/lib/fluid.ts) and
scroll-reveal animations (app/components/Reveal.tsx), matching styling
consistency with /challenge's testimonial section.

Navbar: page-aware active state and anchor-link navigation from any
route (not just "/"), fixed logo click to actually navigate instead of
silently rewriting the URL, fluid nav text size, custom hash-scroll
handling for cross-page anchor links.

Hero: responsive breakpoint fix for the text/image split at Tablet
widths, entrance animation for the brand's orange dot, removed a red
dot artifact from hero.png.

Also includes prior uncommitted work on About/Blog/Newsletter/Footer
and the cart lib (app/lib/cart.ts).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-19 11:34:52 +00:00
Marco 3b7905c7ac init: Next.js project einfach-produktiv 2026-06-30 19:03:57 +00:00