feat(plan): add plan category CRUD routes with audit
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -27,6 +27,16 @@ import {
|
|||||||
listEntries,
|
listEntries,
|
||||||
listOverrides,
|
listOverrides,
|
||||||
} from "../../services/plan.service";
|
} from "../../services/plan.service";
|
||||||
|
import {
|
||||||
|
CreatePlanCategorySchema,
|
||||||
|
UpdatePlanCategorySchema,
|
||||||
|
} from "../../schemas/planCategory.schema";
|
||||||
|
import {
|
||||||
|
listPlanCategories,
|
||||||
|
createPlanCategory,
|
||||||
|
updatePlanCategory,
|
||||||
|
deactivatePlanCategory,
|
||||||
|
} from "../../services/planCategory.service";
|
||||||
|
|
||||||
const MAX_RANGE_DAYS = 92;
|
const MAX_RANGE_DAYS = 92;
|
||||||
|
|
||||||
@@ -135,6 +145,88 @@ export default async function planRoutes(app: FastifyInstance) {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// --- GET /plan/categories (any planner can read; needed for display) ---
|
||||||
|
app.get(
|
||||||
|
"/categories",
|
||||||
|
{
|
||||||
|
preHandler: requireAnyPermission(
|
||||||
|
"attendance.manage",
|
||||||
|
"attendance.record",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
async (_request: FastifyRequest, reply: FastifyReply) => {
|
||||||
|
const cats = await listPlanCategories(true);
|
||||||
|
return success(reply, cats);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// --- POST /plan/categories ---
|
||||||
|
app.post(
|
||||||
|
"/categories",
|
||||||
|
{ preHandler: requirePermission("attendance.manage") },
|
||||||
|
async (request: FastifyRequest, reply: FastifyReply) => {
|
||||||
|
const body = parseBody(CreatePlanCategorySchema, request.body);
|
||||||
|
if ("error" in body) return error(reply, body.error, 400);
|
||||||
|
const result = await createPlanCategory(body.data!);
|
||||||
|
if ("error" in result) return error(reply, result.error, result.status);
|
||||||
|
await logAudit({
|
||||||
|
request,
|
||||||
|
authData: request.authData,
|
||||||
|
action: "create",
|
||||||
|
entityType: "plan_category",
|
||||||
|
entityId: result.data.id,
|
||||||
|
newValues: result.data,
|
||||||
|
description: `Kategorie: ${result.data.label}`,
|
||||||
|
});
|
||||||
|
return success(reply, result.data, 201, "Kategorie vytvořena");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// --- PATCH /plan/categories/:id ---
|
||||||
|
app.patch(
|
||||||
|
"/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 body = parseBody(UpdatePlanCategorySchema, request.body);
|
||||||
|
if ("error" in body) return error(reply, body.error, 400);
|
||||||
|
const result = await updatePlanCategory(id, body.data!);
|
||||||
|
if ("error" in result) return error(reply, result.error, result.status);
|
||||||
|
await logAudit({
|
||||||
|
request,
|
||||||
|
authData: request.authData,
|
||||||
|
action: "update",
|
||||||
|
entityType: "plan_category",
|
||||||
|
entityId: id,
|
||||||
|
newValues: result.data,
|
||||||
|
description: `Kategorie: ${result.data.label}`,
|
||||||
|
});
|
||||||
|
return success(reply, result.data, 200, "Kategorie aktualizována");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// --- DELETE /plan/categories/:id (deactivate) ---
|
||||||
|
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);
|
||||||
|
if ("error" in result) return error(reply, result.error, result.status);
|
||||||
|
await logAudit({
|
||||||
|
request,
|
||||||
|
authData: request.authData,
|
||||||
|
action: "delete",
|
||||||
|
entityType: "plan_category",
|
||||||
|
entityId: id,
|
||||||
|
description: "Kategorie deaktivována",
|
||||||
|
});
|
||||||
|
return success(reply, { ok: true }, 200, "Kategorie deaktivována");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// --- POST /plan/entries ---
|
// --- POST /plan/entries ---
|
||||||
app.post(
|
app.post(
|
||||||
"/entries",
|
"/entries",
|
||||||
|
|||||||
@@ -145,4 +145,5 @@ export type EntityType =
|
|||||||
| "warehouse_location"
|
| "warehouse_location"
|
||||||
| "warehouse_receipt_attachment"
|
| "warehouse_receipt_attachment"
|
||||||
| "work_plan_entry"
|
| "work_plan_entry"
|
||||||
| "work_plan_override";
|
| "work_plan_override"
|
||||||
|
| "plan_category";
|
||||||
|
|||||||
Reference in New Issue
Block a user