feat: Ernten und Chargen im Admin loeschbar machen
Docker Image bauen und veroeffentlichen / build-and-push (push) Successful in 2m27s
Docker Image bauen und veroeffentlichen / build-and-push (push) Successful in 2m27s
Neue deleteHarvestAction/deleteBatchAction, jeweils mit vorheriger Referenzpruefung statt rohem Datenbankfehler: eine Ernte laesst sich nur loeschen, wenn keine Chargen/Produkte mehr darauf verweisen, eine Charge nur, wenn sie keinem Produkt zugeordnet ist und in keiner Bestellung verwendet wurde (Chargen-Code steht auf Rechnungen/QR-Codes und darf aus Nachweispflicht nie verschwinden). Loeschen-Button erscheint in den Listen nur, wenn die Loeschung tatsaechlich moeglich ist, mit Bestaetigungsdialog wie beim bestehenden Benutzer-Loeschen. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
1 parent
c7286041a1
commit
a3b11cc8e2
6 files changed
+138
-9
No files matched your search
@@ -5,7 +5,7 @@ import { revalidatePath } from "next/cache";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { logAdminAction } from "@/lib/audit-log";
|
||||
import { isValidUploadPath } from "@/lib/uploads";
|
||||
import { requireAdmin, str, num, strArray } from "./_shared";
|
||||
import { requireAdmin, fail, str, num, strArray } from "./_shared";
|
||||
|
||||
export async function createHarvestAction(formData: FormData): Promise<void> {
|
||||
const admin = await requireAdmin();
|
||||
@@ -68,6 +68,45 @@ export async function updateHarvestAction(formData: FormData): Promise<void> {
|
||||
redirect("/admin/ernten");
|
||||
}
|
||||
|
||||
/**
|
||||
* Loescht eine Ernte unwiderruflich. harvestId ist auf Batch UND
|
||||
* harvestId/batchId auf Product jeweils onDelete: Restrict (siehe
|
||||
* prisma/schema.prisma) — die Datenbank wuerde ein direktes delete() also
|
||||
* ohnehin ablehnen, sobald noch Chargen oder Produkte auf diese Ernte
|
||||
* verweisen. Die Zaehlung hier VOR dem Loeschversuch liefert dafuer eine
|
||||
* konkrete, verstaendliche Fehlermeldung statt eines rohen Datenbankfehlers.
|
||||
*/
|
||||
export async function deleteHarvestAction(formData: FormData): Promise<void> {
|
||||
const admin = await requireAdmin();
|
||||
const id = str(formData, "id");
|
||||
|
||||
const harvest = await prisma.harvest.findUnique({
|
||||
where: { id },
|
||||
include: { _count: { select: { batches: true, products: true } } },
|
||||
});
|
||||
if (!harvest) fail("/admin/ernten", "Ernte nicht gefunden.");
|
||||
|
||||
if (harvest._count.batches > 0 || harvest._count.products > 0) {
|
||||
fail(
|
||||
"/admin/ernten",
|
||||
`Diese Ernte hat noch ${harvest._count.batches} Charge(n) und ${harvest._count.products} Produkt(e). Lösche oder verschiebe diese zuerst.`
|
||||
);
|
||||
}
|
||||
|
||||
await prisma.harvest.delete({ where: { id } });
|
||||
|
||||
await logAdminAction(admin, {
|
||||
action: "harvest.delete",
|
||||
entityType: "Harvest",
|
||||
entityId: id,
|
||||
detail: `${harvest.type} ${harvest.year}`,
|
||||
});
|
||||
|
||||
revalidatePath("/admin/ernten");
|
||||
revalidatePath("/ernten");
|
||||
redirect("/admin/ernten");
|
||||
}
|
||||
|
||||
/**
|
||||
* Isolierte "Veroeffentlichen"/"Verbergen"-Aktion, getrennt von
|
||||
* updateHarvestAction — so kann der Status per Knopfdruck direkt aus der
|
||||
@@ -136,3 +175,43 @@ export async function updateBatchAction(formData: FormData): Promise<void> {
|
||||
revalidatePath("/admin/chargen");
|
||||
redirect("/admin/chargen");
|
||||
}
|
||||
|
||||
/**
|
||||
* Loescht eine Charge unwiderruflich. productId/batchId auf OrderItem sind
|
||||
* onDelete: Restrict (siehe prisma/schema.prisma) — eine Charge, die schon
|
||||
* in mindestens einer Bestellposition verwendet wurde, darf aus
|
||||
* Rechnungs-/Nachweispflicht NIE geloescht werden (Chargen-Code steht auf
|
||||
* dem QR-Code des Glases und in Rechnungen). Batch.products ist ebenfalls
|
||||
* Restrict. Die Zaehlung hier VOR dem Loeschversuch liefert dafuer eine
|
||||
* konkrete, verstaendliche Fehlermeldung statt eines rohen Datenbankfehlers.
|
||||
*/
|
||||
export async function deleteBatchAction(formData: FormData): Promise<void> {
|
||||
const admin = await requireAdmin();
|
||||
const id = str(formData, "id");
|
||||
|
||||
const batch = await prisma.batch.findUnique({
|
||||
where: { id },
|
||||
include: { _count: { select: { products: true, orderItems: true } } },
|
||||
});
|
||||
if (!batch) fail("/admin/chargen", "Charge nicht gefunden.");
|
||||
|
||||
if (batch._count.orderItems > 0) {
|
||||
fail(
|
||||
"/admin/chargen",
|
||||
`Charge "${batch.code}" wurde bereits in ${batch._count.orderItems} Bestellposition(en) verwendet und kann deshalb nicht gelöscht werden.`
|
||||
);
|
||||
}
|
||||
if (batch._count.products > 0) {
|
||||
fail(
|
||||
"/admin/chargen",
|
||||
`Charge "${batch.code}" ist noch ${batch._count.products} Produkt(en) zugeordnet. Löse diese Zuordnung zuerst.`
|
||||
);
|
||||
}
|
||||
|
||||
await prisma.batch.delete({ where: { id } });
|
||||
|
||||
await logAdminAction(admin, { action: "batch.delete", entityType: "Batch", entityId: id, detail: batch.code });
|
||||
|
||||
revalidatePath("/admin/chargen");
|
||||
redirect("/admin/chargen");
|
||||
}
|
||||
Reference in new issue
Block a user