diff --git a/src/controllers/time-tracking.controller.js b/src/controllers/time-tracking.controller.js index 84750a6..be7bc7d 100644 --- a/src/controllers/time-tracking.controller.js +++ b/src/controllers/time-tracking.controller.js @@ -208,11 +208,13 @@ export const getTimeEntryWithRelations = async (req, res) => { */ export const updateTimeEntry = async (req, res) => { try { - const userId = req.userId; const { entryId } = req.params; const { startTime, endTime, projectId, todoId, companyId, description } = req.body; - const entry = await timeTrackingService.updateTimeEntry(entryId, userId, { + const entry = await timeTrackingService.updateTimeEntry(entryId, { + userId: req.userId, + role: req.user.role, + }, { startTime, endTime, projectId, @@ -238,10 +240,12 @@ export const updateTimeEntry = async (req, res) => { */ export const deleteTimeEntry = async (req, res) => { try { - const userId = req.userId; const { entryId } = req.params; - const result = await timeTrackingService.deleteTimeEntry(entryId, userId); + const result = await timeTrackingService.deleteTimeEntry(entryId, { + userId: req.userId, + role: req.user.role, + }); res.status(200).json(result); } catch (error) { diff --git a/src/services/time-tracking.service.js b/src/services/time-tracking.service.js index bba0338..179f949 100644 --- a/src/services/time-tracking.service.js +++ b/src/services/time-tracking.service.js @@ -1,7 +1,7 @@ import { db } from '../config/database.js'; import { timeEntries, projects, todos, companies, users, timesheets } from '../db/schema.js'; import { eq, and, gte, lte, desc } from 'drizzle-orm'; -import { NotFoundError, BadRequestError } from '../utils/errors.js'; +import { NotFoundError, BadRequestError, ForbiddenError } from '../utils/errors.js'; import ExcelJS from 'exceljs'; import fs from 'fs/promises'; import path from 'path'; @@ -506,12 +506,13 @@ export const generateMonthlyTimesheet = async (userId, year, month) => { /** * Update time entry */ -export const updateTimeEntry = async (entryId, userId, data) => { +export const updateTimeEntry = async (entryId, actor, data) => { + const { userId, role } = actor; const entry = await getTimeEntryById(entryId); - // Verify ownership - if (entry.userId !== userId) { - throw new BadRequestError('Nemáte oprávnenie upraviť tento záznam'); + // Verify ownership (admin can edit anyone) + if (entry.userId !== userId && role !== 'admin') { + throw new ForbiddenError('Nemáte oprávnenie upraviť tento záznam'); } if (entry.isRunning) { @@ -567,6 +568,9 @@ export const updateTimeEntry = async (entryId, userId, data) => { const newEndTime = endTime ? new Date(endTime) : (entry.endTime ? new Date(entry.endTime) : null); if (newEndTime) { + if (newEndTime <= newStartTime) { + throw new BadRequestError('Čas ukončenia musí byť po čase začiatku'); + } newDuration = Math.round((newEndTime - newStartTime) / 60000); } @@ -592,12 +596,13 @@ export const updateTimeEntry = async (entryId, userId, data) => { /** * Delete time entry */ -export const deleteTimeEntry = async (entryId, userId) => { +export const deleteTimeEntry = async (entryId, actor) => { + const { userId, role } = actor; const entry = await getTimeEntryById(entryId); - // Verify ownership - if (entry.userId !== userId) { - throw new BadRequestError('Nemáte oprávnenie odstrániť tento záznam'); + // Verify ownership (admin can delete anyone) + if (entry.userId !== userId && role !== 'admin') { + throw new ForbiddenError('Nemáte oprávnenie odstrániť tento záznam'); } if (entry.isRunning) { diff --git a/uploads/timesheets/68927352-725c-4e95-adb6-d4002b22bef5/2025/11/Clockify_Time_Report_Weekly_03_11_2025-09_11_2025-1764058023490-260402898.pdf b/uploads/timesheets/68927352-725c-4e95-adb6-d4002b22bef5/2025/11/Clockify_Time_Report_Weekly_03_11_2025-09_11_2025-1764058023490-260402898.pdf new file mode 100644 index 0000000..ad69b25 Binary files /dev/null and b/uploads/timesheets/68927352-725c-4e95-adb6-d4002b22bef5/2025/11/Clockify_Time_Report_Weekly_03_11_2025-09_11_2025-1764058023490-260402898.pdf differ diff --git a/uploads/timesheets/68927352-725c-4e95-adb6-d4002b22bef5/2025/11/timesheet-2025-11-1764053316289.xlsx b/uploads/timesheets/ae65f40f-e22a-45c6-9647-36005c6d31e8/2025/11/timesheet-2025-11-1764056127271.xlsx similarity index 52% rename from uploads/timesheets/68927352-725c-4e95-adb6-d4002b22bef5/2025/11/timesheet-2025-11-1764053316289.xlsx rename to uploads/timesheets/ae65f40f-e22a-45c6-9647-36005c6d31e8/2025/11/timesheet-2025-11-1764056127271.xlsx index 8b024ee..ccb4a31 100644 Binary files a/uploads/timesheets/68927352-725c-4e95-adb6-d4002b22bef5/2025/11/timesheet-2025-11-1764053316289.xlsx and b/uploads/timesheets/ae65f40f-e22a-45c6-9647-36005c6d31e8/2025/11/timesheet-2025-11-1764056127271.xlsx differ diff --git a/uploads/timesheets/ae65f40f-e22a-45c6-9647-36005c6d31e8/2025/11/timesheet-2025-11-1764057851169.xlsx b/uploads/timesheets/ae65f40f-e22a-45c6-9647-36005c6d31e8/2025/11/timesheet-2025-11-1764057851169.xlsx new file mode 100644 index 0000000..6c61d7d Binary files /dev/null and b/uploads/timesheets/ae65f40f-e22a-45c6-9647-36005c6d31e8/2025/11/timesheet-2025-11-1764057851169.xlsx differ