Files
Wabenhain/src/lib/inventory.ts
T
Nick SzaunigandClaude Opus 5.5 d52a762e8a fix: Produkt-Formular ueberschreibt keinen zwischenzeitlich gebuchten Bestand
Das Speichern schrieb den Formularwert immer fest in den Lagerbestand.
Wurde zwischen Oeffnen und Speichern ein Glas verkauft, kam es dadurch
wieder in den Bestand, ohne dass es je zurueckkam.

- Das Formular schickt den Bestand beim Oeffnen mit (quantityLoaded)
- Feld unveraendert: Bestand wird nicht angefasst
- Feld geaendert: Schreiben nur, wenn der Bestand noch dem geladenen Stand
  entspricht (Bedingung atomar im UPDATE); sonst wird nichts gespeichert
  und der aktuelle Wert angezeigt
- Fehlt quantityLoaded (alter Tab), muss neu geladen werden

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-26 05:55:36 +02:00

62 lines
2.6 KiB
TypeScript

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 type InventoryEditPlan =
| { kind: "keep" }
| { kind: "set"; expected: number; quantity: number }
| { kind: "reload" };
/**
* Was soll das Speichern des Produkt-Formulars mit dem Lagerbestand tun?
*
* Das Formular schickt neben dem eingegebenen Bestand ("submitted") den
* Bestand mit, der beim Oeffnen angezeigt wurde ("loaded", verstecktes Feld).
* Frueher wurde der Formularwert immer fest geschrieben: wurde zwischen
* Oeffnen und Speichern ein Glas verkauft, kam es durch das Speichern
* wieder in den Bestand, ohne dass es je zurueckkam.
*
* - Feld unveraendert: Bestand nicht anfassen ("keep").
* - Feld geaendert: nur schreiben, solange der Bestand noch "loaded" ist
* ("set"; die Bedingung prueft updateProductAction atomar im UPDATE).
* - "loaded" fehlt oder ist ungueltig: neu laden lassen ("reload"), statt
* zu raten, welchen Stand die Imkerei vor Augen hatte.
*/
export function planInventoryEdit(input: { loaded: string; submitted: number }): InventoryEditPlan {
if (!/^\d+$/.test(input.loaded)) return { kind: "reload" };
const loaded = Number(input.loaded);
if (!Number.isSafeInteger(loaded)) return { kind: "reload" };
if (loaded === input.submitted) return { kind: "keep" };
return { kind: "set", expected: loaded, quantity: input.submitted };
}
export function needsRestockOnCancel(returnStatus: ReturnStatus | null): boolean {
return returnStatus !== "ERSTATTET";
}
/**
* Bucht reservierten/verkauften Bestand zurueck — genutzt beim Stornieren
* einer Bestellung (admin/actions.ts, api/checkout/webhook/route.ts) und bei
* einer angenommenen Retoure (admin/retouren/actions.ts). Alle drei Stellen
* hatten dieselbe Schleife dupliziert; jetzt ein gemeinsamer Aufruf.
*/
export async function restockOrderItems(
tx: Prisma.TransactionClient,
items: { productId: string; quantity: number }[]
): Promise<void> {
for (const item of items) {
await tx.inventory.updateMany({
where: { productId: item.productId },
data: { quantity: { increment: item.quantity } },
});
}
}