Restrict Rücksendung anfragen to delivered orders, fix Impressum email link

- customerOrderAction() only offers "Rücksendung anfragen" once an order
  is delivered, not already at shipped — a return before the package
  arrived doesn't make sense yet. UI-only change (a stricter subset of
  what the backend's CUSTOMER_ALLOWED_TRANSITIONS already permits).
- AnbieterAngaben.tsx's seller email is now a real mailto: link — it was
  plain text, the only non-clickable email on the site.
This commit is contained in:
Marco
2026-07-30 10:39:08 +00:00
parent 4adb844f8d
commit 2e537bc78d
5 changed files with 44 additions and 7 deletions
+25
View File
@@ -2072,6 +2072,31 @@ had `isManualPayment: false` — the Vorkasse/Überweisung branch (and its
new switch-option mention) was never previewable in the admin at all
before this.
**Follow-up same day: switch-to-stripe eligibility narrowed.**
`canSwitchPayment` (page.tsx) and the `switch-to-stripe` route's own
re-check both changed from `paymentStatus !== "paid"` to an explicit
allowlist (`"pending"` or `"not_applicable"`) — see the Payload backend's
own README for why the blocklist form was too permissive. No visible
behavior change for the normal case, just closes an edge case
(`"failed"`/`"refunded"`/`"partially_refunded"` on a manual order — not
realistic today, but not meaningful states to switch *from* either).
**Two more same-day fixes:**
- `customerOrderAction()` now only offers "Rücksendung anfragen" once an
order is actually `delivered`, not already at `shipped` — a (partial)
return before the package has even arrived doesn't make sense yet. The
backend's own `CUSTOMER_ALLOWED_TRANSITIONS` still technically permits
`shipped``return_requested` too (an extensive existing test suite is
built around that as its base fixture, see the Payload backend's
README) — this only narrows what the UI itself offers, a stricter
subset of what the backend already allows, not a security boundary
being loosened.
- `/impressum`'s "Angaben zum Anbieter" email (`AnbieterAngaben.tsx`) was
plain text, not a `mailto:` link — the only email address on the whole
site that wasn't clickable, since this one section is rendered
straight from `company-settings` fields rather than through the CMS
richText renderer (which already links emails/URLs correctly).
## Deployment
- **Dockerfile**: 3-stage build (`deps``builder``runner`) with
@@ -21,10 +21,13 @@ export async function POST(request: Request, { params }: { params: Promise<{ ord
if (!order) return NextResponse.json({ ok: false, reason: "Bestellung nicht gefunden." }, { status: 404 });
// Same eligibility the backend endpoint re-checks authoritatively —
// checked here too for a friendly error instead of a bare 409. An admin
// can mark an Überweisung order paid by hand (Orders.ts's paymentStatus
// field) — that order must never also be switched to Stripe.
if (order.paymentProvider !== "manual" || order.status !== "received" || order.paymentStatus === "paid") {
// checked here too for a friendly error instead of a bare 409. An
// explicit allowlist ('pending'/'not_applicable'), not a
// paymentStatus !== "paid" blocklist — see switchPaymentToStripe.ts's
// own comment on why 'failed'/'refunded'/'partially_refunded' aren't
// meaningful states to switch from either.
const eligiblePaymentStatus = order.paymentStatus === "pending" || order.paymentStatus === "not_applicable";
if (order.paymentProvider !== "manual" || order.status !== "received" || !eligiblePaymentStatus) {
return NextResponse.json({ ok: false, reason: "Die Zahlungsart kann für diese Bestellung gerade nicht geändert werden." }, { status: 400 });
}
+3 -1
View File
@@ -76,7 +76,9 @@ export function AnbieterAngaben({ seller }: { seller: CompanySettings }) {
{seller.sellerZip} {seller.sellerCity}
</P>
<P>{seller.sellerCountry}</P>
<P>E-Mail: {seller.sellerEmail}</P>
<P>
E-Mail: <a href={`mailto:${seller.sellerEmail}`} className="text-brand hover:underline">{seller.sellerEmail}</a>
</P>
</div>
{seller.vatId && (
@@ -55,7 +55,7 @@ export default async function KontoBestellungDetailPage({ params }: { params: Pr
const canSwitchPayment =
order.paymentProvider === "manual" &&
order.status === "received" &&
order.paymentStatus !== "paid" &&
(order.paymentStatus === "pending" || order.paymentStatus === "not_applicable") &&
groupPaymentMethodsForCheckout(await getPaymentMethods()).some((m) => m.provider === "stripe");
return (
+8 -1
View File
@@ -455,7 +455,14 @@ export const ORDER_STATUS_LABEL: Record<string, string> = {
// decide which button, if any, to show).
export function customerOrderAction(status: string): "cancel" | "request-return" | null {
if (status === "received") return "cancel";
if (status === "shipped" || status === "delivered") return "request-return";
// Only once actually delivered — a (partial) return before the package
// even arrived doesn't make sense yet. The backend's own
// CUSTOMER_ALLOWED_TRANSITIONS still technically permits 'shipped' →
// 'return_requested' too (an extensive existing test suite is built
// around that as its base fixture) — this only narrows what the UI
// itself offers, a stricter subset of what the backend already allows,
// not a security boundary being loosened.
if (status === "delivered") return "request-return";
return null;
}