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]>
62 lines
2.6 KiB
TypeScript
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 } },
|
|
});
|
|
}
|
|
}
|