feat(plan): add hard-delete for categories (blocked when in use)

DELETE /plan/categories/:id now hard-deletes the row, guarded: refuses when any live entry/override uses the key (suggests hide instead) and keeps >=1 active category. Modal gains a per-row Smazat with inline confirm. Hiding stays via PATCH is_active.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
BOHA
2026-06-06 01:12:40 +02:00
parent aef4ab92ec
commit 4d47897e27
5 changed files with 167 additions and 14 deletions

View File

@@ -35,7 +35,7 @@ import {
listPlanCategories,
createPlanCategory,
updatePlanCategory,
deactivatePlanCategory,
deletePlanCategory,
} from "../../services/planCategory.service";
const MAX_RANGE_DAYS = 92;
@@ -206,14 +206,14 @@ export default async function planRoutes(app: FastifyInstance) {
},
);
// --- DELETE /plan/categories/:id (deactivate) ---
// --- DELETE /plan/categories/:id (hard delete; blocked if in use) ---
app.delete(
"/categories/:id",
{ preHandler: requirePermission("attendance.manage") },
async (request: FastifyRequest, reply: FastifyReply) => {
const id = parseId((request.params as any).id, reply);
if (id === null) return;
const result = await deactivatePlanCategory(id);
const result = await deletePlanCategory(id);
if ("error" in result) return error(reply, result.error, result.status);
await logAudit({
request,
@@ -221,9 +221,9 @@ export default async function planRoutes(app: FastifyInstance) {
action: "delete",
entityType: "plan_category",
entityId: id,
description: "Kategorie deaktivována",
description: "Kategorie smazána",
});
return success(reply, { ok: true }, 200, "Kategorie deaktivována");
return success(reply, { ok: true }, 200, "Kategorie smazána");
},
);