v1.6.5: fix attendance unique constraint, schema sync, seed script

- Change attendance idx_attendance_user_date from unique to index (allow multiple shifts per day)
- Reset migrations to single baseline init migration
- Add seed script with admin user (admin/admin)
- Update CLAUDE.md with migration workflow documentation
- Various frontend fixes (queries, pages, hooks)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
BOHA
2026-05-17 16:21:34 +02:00
parent 1db5060c42
commit cd6d3daf43
75 changed files with 1748 additions and 1169 deletions

View File

@@ -100,7 +100,7 @@ export default async function attendanceRoutes(
// --- action=balances: leave balance overview for all users ---
if (action === "balances") {
if (!authData.permissions.includes("attendance.admin")) {
if (!authData.permissions.includes("attendance.balances")) {
return error(reply, "Nedostatečná oprávnění", 403);
}
const yr = Number(query.year) || new Date().getFullYear();
@@ -110,7 +110,7 @@ export default async function attendanceRoutes(
// --- action=workfund: monthly work fund overview ---
if (action === "workfund") {
if (!authData.permissions.includes("attendance.admin")) {
if (!authData.permissions.includes("attendance.manage")) {
return error(reply, "Nedostatečná oprávnění", 403);
}
const yr = Number(query.year) || new Date().getFullYear();
@@ -120,7 +120,7 @@ export default async function attendanceRoutes(
// --- action=project_report: monthly project hours ---
if (action === "project_report") {
if (!authData.permissions.includes("attendance.admin")) {
if (!authData.permissions.includes("attendance.manage")) {
return error(reply, "Nedostatečná oprávnění", 403);
}
const yr = Number(query.year) || new Date().getFullYear();
@@ -130,7 +130,7 @@ export default async function attendanceRoutes(
// --- action=print: attendance print data for admin ---
if (action === "print") {
if (!authData.permissions.includes("attendance.admin")) {
if (!authData.permissions.includes("attendance.manage")) {
return error(reply, "Nedostatečná oprávnění", 403);
}
@@ -145,8 +145,7 @@ export default async function attendanceRoutes(
// --- action=attendance_users: users with attendance.record permission ---
if (action === "attendance_users") {
if (
!authData.permissions.includes("attendance.admin") &&
!authData.permissions.includes("attendance.view") &&
!authData.permissions.includes("attendance.manage") &&
!authData.permissions.includes("attendance.record")
) {
return error(reply, "Nedostatečná oprávnění", 403);
@@ -182,10 +181,7 @@ export default async function attendanceRoutes(
// --- action=project_logs: get project logs for a specific attendance record ---
if (action === "project_logs") {
if (
!authData.permissions.includes("attendance.view") &&
!authData.permissions.includes("attendance.record")
) {
if (!authData.permissions.includes("attendance.record")) {
return error(reply, "Nedostatečná oprávnění", 403);
}
const attendanceId = Number(query.attendance_id);
@@ -200,7 +196,7 @@ export default async function attendanceRoutes(
if (!id) return error(reply, "Missing id", 400);
const record = await attendanceService.getLocationRecord(id);
if (!record) return error(reply, "Záznam nenalezen", 404);
const isAdmin = authData.permissions.includes("attendance.admin");
const isAdmin = authData.permissions.includes("attendance.manage");
if (record.user_id !== authData.userId && !isAdmin) {
return error(reply, "Nedostatečná oprávnění", 403);
}
@@ -209,7 +205,7 @@ export default async function attendanceRoutes(
// --- Default: paginated records list ---
const { page, limit, skip, order } = parsePagination(query);
const isAdmin = authData.permissions.includes("attendance.admin");
const isAdmin = authData.permissions.includes("attendance.manage");
const userId = query.user_id ? Number(query.user_id) : undefined;
const result = await attendanceService.listAttendance({
@@ -285,7 +281,7 @@ export default async function attendanceRoutes(
// --- action=bulk_attendance: bulk fill month ---
if (postQuery.action === "bulk_attendance") {
if (!authData.permissions.includes("attendance.admin")) {
if (!authData.permissions.includes("attendance.manage")) {
return error(reply, "Nedostatečná oprávnění", 403);
}
@@ -328,7 +324,7 @@ export default async function attendanceRoutes(
if (
leaveBody.user_id != null &&
leaveBody.user_id !== authData.userId &&
!authData.permissions.includes("attendance.admin")
!authData.permissions.includes("attendance.manage")
) {
return error(reply, "Nedostatečná oprávnění", 403);
}
@@ -384,7 +380,7 @@ export default async function attendanceRoutes(
if (
body.user_id != null &&
body.user_id !== authData.userId &&
!authData.permissions.includes("attendance.admin")
!authData.permissions.includes("attendance.manage")
) {
return error(reply, "Nedostatečná oprávnění", 403);
}
@@ -429,7 +425,7 @@ export default async function attendanceRoutes(
// PUT /api/admin/attendance/:id
fastify.put<{ Params: { id: string } }>(
"/:id",
{ preHandler: requirePermission("attendance.edit") },
{ preHandler: requirePermission("attendance.manage") },
async (request, reply) => {
const id = parseId(request.params.id, reply);
if (id === null) return;
@@ -438,7 +434,7 @@ export default async function attendanceRoutes(
const body = parsed.data;
const authData = request.authData!;
const isAdmin = authData.permissions.includes("attendance.admin");
const isAdmin = authData.permissions.includes("attendance.manage");
const result = await attendanceService.updateAttendance(
id,
@@ -481,7 +477,7 @@ export default async function attendanceRoutes(
// DELETE /api/admin/attendance/:id
fastify.delete<{ Params: { id: string } }>(
"/:id",
{ preHandler: requirePermission("attendance.admin") },
{ preHandler: requirePermission("attendance.manage") },
async (request, reply) => {
const id = parseId(request.params.id, reply);
if (id === null) return;