Blog categories (hasMany), shipping-address contact fields, product SKU on invoices

- Posts.categories is now hasMany — blog list/detail/live-preview render
  a comma-joined list instead of a single category.
- Checkout's "Abweichende Lieferadresse" gains optional Firma + Kontakt-
  E-Mail/Telefon fields (handed to the shipping carrier, not used for
  customer communication).
- Customer profile can now store its own shipping address (mirroring
  Customers.ts's new "Lieferadresse" tab), prefilling the checkout
  override instead of always starting blank.
- Product-level optional SKU (previously only on variants) snapshots onto
  each order item and shows up on invoices (visual + EN16931 XML),
  the confirmation email, and the account order detail page.
This commit is contained in:
Marco
2026-07-30 08:41:39 +00:00
parent 1467750db6
commit e3352d7e32
18 changed files with 403 additions and 34 deletions
+66 -1
View File
@@ -13,7 +13,32 @@ export async function PATCH(request: Request) {
if (!session) return NextResponse.json({ ok: false, reason: "Bitte zuerst einloggen." }, { status: 401 });
const body = await request.json().catch(() => null);
const { firstName, lastName, deliveryMethod, street, packstationNumber, postNumber, zip, city, country, companyName, vatId } = body ?? {};
const {
firstName,
lastName,
deliveryMethod,
street,
packstationNumber,
postNumber,
zip,
city,
country,
companyName,
vatId,
hasDifferentShippingAddress,
shippingFirstName,
shippingLastName,
shippingCompanyName,
shippingDeliveryMethod,
shippingStreet,
shippingPackstationNumber,
shippingPostNumber,
shippingZip,
shippingCity,
shippingCountry,
shippingContactEmail,
shippingContactPhone,
} = body ?? {};
if (
typeof firstName !== "string" ||
!firstName ||
@@ -42,6 +67,33 @@ export async function PATCH(request: Request) {
return NextResponse.json({ ok: false, reason: "Ungültiges USt-IdNr.-Format (z. B. DE123456789)." }, { status: 400 });
}
// Same shape as the checkout's own shipping-address-override validation
// (api/checkout/route.ts) — required fields only apply when the toggle
// is actually on, since this whole block is optional otherwise.
if (hasDifferentShippingAddress) {
if (
typeof shippingFirstName !== "string" ||
!shippingFirstName ||
typeof shippingLastName !== "string" ||
!shippingLastName ||
(shippingDeliveryMethod !== "address" && shippingDeliveryMethod !== "packstation") ||
typeof shippingZip !== "string" ||
!shippingZip ||
typeof shippingCity !== "string" ||
!shippingCity ||
typeof shippingCountry !== "string" ||
!shippingCountry
) {
return NextResponse.json({ ok: false, reason: "Bitte alle Pflichtfelder der Lieferadresse ausfüllen." }, { status: 400 });
}
if (shippingDeliveryMethod === "address" && !shippingStreet) {
return NextResponse.json({ ok: false, reason: "Bitte Straße und Hausnummer der Lieferadresse angeben." }, { status: 400 });
}
if (shippingDeliveryMethod === "packstation" && (!shippingPackstationNumber || !shippingPostNumber)) {
return NextResponse.json({ ok: false, reason: "Bitte Packstation- und Postnummer der Lieferadresse angeben." }, { status: 400 });
}
}
const result = await updateCustomerProfile(session.token, session.customer.id, {
firstName,
lastName,
@@ -54,6 +106,19 @@ export async function PATCH(request: Request) {
country,
companyName: typeof companyName === "string" && companyName ? companyName : undefined,
vatId: normalizedVatId,
hasDifferentShippingAddress: Boolean(hasDifferentShippingAddress),
shippingFirstName: hasDifferentShippingAddress ? shippingFirstName : undefined,
shippingLastName: hasDifferentShippingAddress ? shippingLastName : undefined,
shippingCompanyName: hasDifferentShippingAddress && shippingCompanyName ? shippingCompanyName : undefined,
shippingDeliveryMethod: hasDifferentShippingAddress ? shippingDeliveryMethod : undefined,
shippingStreet: hasDifferentShippingAddress ? shippingStreet : undefined,
shippingPackstationNumber: hasDifferentShippingAddress ? shippingPackstationNumber : undefined,
shippingPostNumber: hasDifferentShippingAddress ? shippingPostNumber : undefined,
shippingZip: hasDifferentShippingAddress ? shippingZip : undefined,
shippingCity: hasDifferentShippingAddress ? shippingCity : undefined,
shippingCountry: hasDifferentShippingAddress ? shippingCountry : undefined,
shippingContactEmail: hasDifferentShippingAddress && shippingContactEmail ? shippingContactEmail : undefined,
shippingContactPhone: hasDifferentShippingAddress && shippingContactPhone ? shippingContactPhone : undefined,
});
return NextResponse.json(result, { status: result.ok ? 200 : 400 });
}