feat: Bestellungen ohne Geschaeftsvorfall lassen sich im Admin loeschen
Docker Image bauen und veroeffentlichen / build-and-push (push) Successful in 2m12s
Docker Image bauen und veroeffentlichen / build-and-push (push) Successful in 2m12s
Stornieren gab es bereits, Loeschen nicht — abgebrochene Checkouts und Testbestellungen blieben dauerhaft in der Liste stehen. Loeschbar ist bewusst nur, was nie ein Geschaeftsvorfall war: keine Zahlung erfasst, keine Rechnung erzeugt, Status NEU oder STORNIERT. Eine bezahlte Bestellung und ihre Rechnung sind Buchungsbelege (§ 147 AO, § 257 HGB) und muessen jahrelang vorgelegt werden koennen; ein Loeschknopf darauf wuerde nicht aufraeumen, sondern Unterlagen vernichten. Dafuer gibt es den Storno: er erstattet das Geld, bucht die Ware zurueck und laesst den Beleg stehen. Die Regel steht in lib/order-delete.ts als reine Logik ohne Datenbankzugriff — dieselbe Trennung wie bei shipping-calc.ts. Server Action und Admin-Seite teilen sie sich, statt die Bedingung zweimal zu formulieren und irgendwann auseinanderzulaufen, und sie laesst sich ohne DATABASE_URL testen (13 neue Faelle, u.a. dass bei Rechnung UND Zahlung die Rechnung als Grund gemeldet wird). Drei Dinge, die ein blosses delete() falsch gemacht haette: - LAGERBESTAND: bei NEU liegt die Ware reserviert und wird zurueckgebucht, bei STORNIERT ist das beim Stornieren bereits geschehen. Dort erneut gutzuschreiben wuerde den Bestand erhoehen, ohne dass je ein Glas zurueckkam. - ZAHLUNGSSITZUNG: eine Bestellung im Status NEU kann eine offene Stripe-Sitzung haben. Wird sie geloescht, ohne die Sitzung zu schliessen, kann Minuten spaeter noch bezahlt werden — der Webhook findet dann keine Bestellung mehr, und das Geld liegt bei Stripe, ohne dass im Shop etwas davon zu sehen waere. Die Sitzung wird deshalb VOR dem Loeschen geschlossen; gelingt das nicht sicher, wird gar nicht geloescht. Ist bei Stripe zwischenzeitlich bezahlt worden, verweigert die Aktion das Loeschen. - WETTLAUF: die Bedingungen stehen zusaetzlich in der WHERE-Klausel des DELETE. Zwischen Pruefung und Loeschen koennte der Webhook eine Zahlung eingetragen haben; die Pruefung sieht nur einen Schnappschuss, die WHERE-Klausel wird atomar gegen den aktuellen Zeilenstand ausgewertet. Der Audit-Log-Eintrag ist ausfuehrlicher als bei anderen Aktionen und traegt Bestellnummer, Status, Betrag, E-Mail und Positionsanzahl — der Datensatz ist danach weg, dieser Eintrag ist die einzige verbleibende Spur. Die Oberflaeche zeigt den Knopf am Seitenende in einem abgesetzten Bereich, damit er nicht neben dem Statusfeld liegt, mit Browser-Rueckfrage ueber ConfirmForm. Ist die Bestellung nicht loeschbar, steht dort statt des Knopfes der Grund und der Verweis auf den Storno. Die Bestellliste zeigt jetzt auch Erfolgsmeldungen, nicht nur Fehler. tsc: 0 Fehler. vitest: 372/372 gruen. eslint: 0 Warnungen. next build: gruen. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
1 parent
256e823c4f
commit
c35d61bfd6
8 files changed
+318
-2
No files matched your search
@@ -12,7 +12,7 @@ import { getDictionary } from "@/i18n/get-dictionary";
|
||||
export default async function AdminOrdersPage(props: PageProps<"/admin/bestellungen">) {
|
||||
const t = getDictionary(await getLocale());
|
||||
const statusLabels = t.orderStatusBadge;
|
||||
const { q, status, error } = await props.searchParams;
|
||||
const { q, status, error, message } = await props.searchParams;
|
||||
const query = typeof q === "string" ? q.trim() : "";
|
||||
const statusFilter = typeof status === "string" && status in statusLabels ? (status as OrderStatus) : undefined;
|
||||
|
||||
@@ -41,6 +41,9 @@ export default async function AdminOrdersPage(props: PageProps<"/admin/bestellun
|
||||
{/* Die DHL-Aktionen leiten bei einer nicht mehr auffindbaren Bestellung
|
||||
hierher zurueck (admin/actions/dhl.ts). Ohne diese Ausgabe sah das
|
||||
aus wie ein folgenloser Klick. */}
|
||||
{message ? (
|
||||
<p className="mb-6 text-sm text-forest bg-forest/5 border border-forest/20 px-4 py-3">{message}</p>
|
||||
) : null}
|
||||
{error ? (
|
||||
<p className="mb-6 text-sm text-red-700 bg-red-50 border border-red-200 px-4 py-3">{error}</p>
|
||||
) : null}
|
||||
|
||||
Reference in new issue
Block a user