diff --git a/src/app/admin/actions/orders.ts b/src/app/admin/actions/orders.ts index 110d964..5c66dff 100644 --- a/src/app/admin/actions/orders.ts +++ b/src/app/admin/actions/orders.ts @@ -6,7 +6,7 @@ import { logAdminAction } from "@/lib/audit-log"; import { sendMail } from "@/lib/mail"; import { shipmentEmail, pickupReadyEmail } from "@/lib/email-templates"; import { getCarrierTrackingUrl } from "@/lib/shipment-tracking"; -import { restockOrderItems } from "@/lib/inventory"; +import { needsRestockOnCancel, restockOrderItems } from "@/lib/inventory"; import { checkOrderDeletable, needsRestockOnDelete } from "@/lib/order-delete"; import { getStripeClient } from "@/lib/stripe"; import { isLocale, defaultLocale } from "@/i18n/config"; @@ -139,6 +139,7 @@ export async function updateOrderStatusAction(formData: FormData): Promise stripeCheckoutSessionId: true, payment: { select: { status: true, stripePaymentIntentId: true } }, items: { select: { productId: true, quantity: true } }, + returnRequest: { select: { status: true } }, }, }); if (!previous) return null; @@ -164,7 +165,9 @@ export async function updateOrderStatusAction(formData: FormData): Promise }); if (claim.count === 1) { cancelledNow = true; - await restockOrderItems(tx, previous.items); + if (needsRestockOnCancel(previous.returnRequest?.status ?? null)) { + await restockOrderItems(tx, previous.items); + } } } else { await tx.order.update({ where: { id: orderId }, data: { status } }); diff --git a/src/app/admin/retouren/actions.ts b/src/app/admin/retouren/actions.ts index 71f44a5..6e6bb17 100644 --- a/src/app/admin/retouren/actions.ts +++ b/src/app/admin/retouren/actions.ts @@ -87,8 +87,16 @@ export async function refundReturnRequestAction(formData: FormData): Promise { + test("bucht zurueck, wenn es keine Retoure gibt", () => { + expect(needsRestockOnCancel(null)).toBe(true); + }); + + test("bucht zurueck, solange die Retoure noch nicht erstattet ist", () => { + expect(needsRestockOnCancel("ANGEFRAGT")).toBe(true); + expect(needsRestockOnCancel("ANGENOMMEN")).toBe(true); + expect(needsRestockOnCancel("ABGELEHNT")).toBe(true); + }); + + test("bucht nicht noch einmal zurueck, wenn die Retoure die Ware schon zurueckgebucht hat", () => { + expect(needsRestockOnCancel("ERSTATTET")).toBe(false); + }); +}); diff --git a/src/lib/inventory.ts b/src/lib/inventory.ts index d63f9ee..bc82077 100644 --- a/src/lib/inventory.ts +++ b/src/lib/inventory.ts @@ -1,4 +1,18 @@ -import type { Prisma } from "@/generated/prisma/client"; +import type { Prisma, ReturnStatus } from "@/generated/prisma/client"; + +/** + * Muss beim Stornieren einer Bestellung die Ware zurueckgebucht werden? + * + * Nein, wenn eine Retoure zu dieser Bestellung bereits erstattet wurde: + * refundReturnRequestAction (admin/retouren/actions.ts) hat die Ware dann + * schon zurueckgebucht. Ein zweites Gutschreiben wuerde den Bestand um Glaeser + * erhoehen, die es nicht gibt — genau das ist bei einer Testbestellung + * passiert (Retoure erstattet, danach "STORNIERT" gesetzt: +1 Glas zu viel). + * Gegenstueck zu needsRestockOnDelete in order-delete.ts. + */ +export function needsRestockOnCancel(returnStatus: ReturnStatus | null): boolean { + return returnStatus !== "ERSTATTET"; +} /** * Bucht reservierten/verkauften Bestand zurueck — genutzt beim Stornieren