refactor: odstraneni PSR-1 SideEffects warningu
- Handler funkce extrahovany z API souboru do api/admin/handlers/ - config.php rozdeleny na helpers.php (funkce) a constants.php (konstanty) - require_once odstranen z class souboru (AuditLog, JWTAuth, LeaveNotification) - vendor/autoload.php presunuto do config.php bootstrap - totp-handlers.php: pridany use deklarace pro TwoFactorAuth - phpstan.neon: bootstrapFiles, scanDirectories, dynamicConstantNames - Opraveny chybejici routing bloky v totp.php a session.php Vysledek: phpcs 0 errors 0 warnings, PHPStan 0 errors, ESLint 0 errors Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
589
api/admin/handlers/attendance-handlers.php
Normal file
589
api/admin/handlers/attendance-handlers.php
Normal file
@@ -0,0 +1,589 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
function handleGetCurrent(PDO $pdo, int $userId): void
|
||||
{
|
||||
$today = date('Y-m-d');
|
||||
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT * FROM attendance
|
||||
WHERE user_id = ? AND departure_time IS NULL AND (leave_type IS NULL OR leave_type = 'work')
|
||||
ORDER BY created_at DESC LIMIT 1
|
||||
");
|
||||
$stmt->execute([$userId]);
|
||||
$ongoingShift = $stmt->fetch();
|
||||
|
||||
$projectLogs = [];
|
||||
$activeProjectId = null;
|
||||
if ($ongoingShift) {
|
||||
$stmt = $pdo->prepare('SELECT * FROM attendance_project_logs WHERE attendance_id = ? ORDER BY started_at ASC');
|
||||
$stmt->execute([$ongoingShift['id']]);
|
||||
$projectLogs = $stmt->fetchAll();
|
||||
foreach ($projectLogs as $log) {
|
||||
if ($log['ended_at'] === null) {
|
||||
$activeProjectId = (int)$log['project_id'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT * FROM attendance
|
||||
WHERE user_id = ? AND shift_date = ?
|
||||
AND departure_time IS NOT NULL
|
||||
AND (leave_type IS NULL OR leave_type = 'work')
|
||||
ORDER BY arrival_time DESC
|
||||
");
|
||||
$stmt->execute([$userId, $today]);
|
||||
$todayShifts = $stmt->fetchAll();
|
||||
|
||||
$completedShiftIds = array_column($todayShifts, 'id');
|
||||
$completedProjectLogs = [];
|
||||
if (!empty($completedShiftIds)) {
|
||||
$placeholders = implode(',', array_fill(0, count($completedShiftIds), '?'));
|
||||
$stmt = $pdo->prepare(
|
||||
"SELECT * FROM attendance_project_logs
|
||||
WHERE attendance_id IN ($placeholders)
|
||||
ORDER BY started_at ASC"
|
||||
);
|
||||
$stmt->execute($completedShiftIds);
|
||||
$allLogs = $stmt->fetchAll();
|
||||
foreach ($allLogs as $log) {
|
||||
$completedProjectLogs[$log['attendance_id']][] = $log;
|
||||
}
|
||||
}
|
||||
|
||||
$leaveBalance = getLeaveBalance($pdo, $userId);
|
||||
|
||||
$currentYear = (int)date('Y');
|
||||
$currentMonth = (int)date('m');
|
||||
$fund = CzechHolidays::getMonthlyWorkFund($currentYear, $currentMonth);
|
||||
$businessDays = CzechHolidays::getBusinessDaysInMonth($currentYear, $currentMonth);
|
||||
|
||||
$startDate = date('Y-m-01');
|
||||
$endDate = date('Y-m-t');
|
||||
|
||||
$stmt = $pdo->prepare('
|
||||
SELECT * FROM attendance
|
||||
WHERE user_id = ? AND shift_date BETWEEN ? AND ?
|
||||
');
|
||||
$stmt->execute([$userId, $startDate, $endDate]);
|
||||
$monthRecords = $stmt->fetchAll();
|
||||
|
||||
$workedMinutes = 0;
|
||||
$leaveHoursMonth = 0;
|
||||
$vacationHours = 0;
|
||||
$sickHours = 0;
|
||||
$holidayHours = 0;
|
||||
$unpaidHours = 0;
|
||||
foreach ($monthRecords as $rec) {
|
||||
$lt = $rec['leave_type'] ?? 'work';
|
||||
$lh = (float)($rec['leave_hours'] ?? 0);
|
||||
if ($lt === 'work') {
|
||||
if ($rec['departure_time']) {
|
||||
$workedMinutes += calculateWorkMinutes($rec);
|
||||
}
|
||||
} elseif ($lt === 'vacation') {
|
||||
$vacationHours += $lh;
|
||||
$leaveHoursMonth += $lh;
|
||||
} elseif ($lt === 'sick') {
|
||||
$sickHours += $lh;
|
||||
$leaveHoursMonth += $lh;
|
||||
} elseif ($lt === 'holiday') {
|
||||
$holidayHours += $lh;
|
||||
} elseif ($lt === 'unpaid') {
|
||||
$unpaidHours += $lh;
|
||||
}
|
||||
}
|
||||
|
||||
$workedHours = round($workedMinutes / 60, 1);
|
||||
$covered = $workedHours + $leaveHoursMonth;
|
||||
$remaining = max(0, $fund - $covered);
|
||||
$overtime = max(0, round($covered - $fund, 1));
|
||||
|
||||
$monthlyFund = [
|
||||
'fund' => $fund,
|
||||
'business_days' => $businessDays,
|
||||
'worked' => $workedHours,
|
||||
'leave_hours' => $leaveHoursMonth,
|
||||
'vacation_hours' => $vacationHours,
|
||||
'sick_hours' => $sickHours,
|
||||
'holiday_hours' => $holidayHours,
|
||||
'unpaid_hours' => $unpaidHours,
|
||||
'covered' => $covered,
|
||||
'remaining' => $remaining,
|
||||
'overtime' => $overtime,
|
||||
'month_name' => getCzechMonthName($currentMonth) . ' ' . $currentYear,
|
||||
];
|
||||
|
||||
// Enrich project logs with names
|
||||
$allLogProjectIds = [];
|
||||
foreach ($projectLogs as $l) {
|
||||
$allLogProjectIds[$l['project_id']] = $l['project_id'];
|
||||
}
|
||||
foreach ($completedProjectLogs as $logs) {
|
||||
foreach ($logs as $l) {
|
||||
$allLogProjectIds[$l['project_id']] = $l['project_id'];
|
||||
}
|
||||
}
|
||||
$projNameMap = fetchProjectNames($allLogProjectIds);
|
||||
|
||||
foreach ($projectLogs as &$l) {
|
||||
$l['project_name'] = $projNameMap[$l['project_id']] ?? null;
|
||||
}
|
||||
unset($l);
|
||||
foreach ($completedProjectLogs as &$logs) {
|
||||
foreach ($logs as &$l) {
|
||||
$l['project_name'] = $projNameMap[$l['project_id']] ?? null;
|
||||
}
|
||||
unset($l);
|
||||
}
|
||||
unset($logs);
|
||||
|
||||
foreach ($todayShifts as &$shift) {
|
||||
$shift['project_logs'] = $completedProjectLogs[$shift['id']] ?? [];
|
||||
}
|
||||
unset($shift);
|
||||
|
||||
successResponse([
|
||||
'ongoing_shift' => $ongoingShift,
|
||||
'today_shifts' => $todayShifts,
|
||||
'date' => $today,
|
||||
'leave_balance' => $leaveBalance,
|
||||
'monthly_fund' => $monthlyFund,
|
||||
'project_logs' => $projectLogs,
|
||||
'active_project_id' => $activeProjectId,
|
||||
]);
|
||||
}
|
||||
|
||||
function handleGetHistory(PDO $pdo, int $userId): void
|
||||
{
|
||||
$month = validateMonth();
|
||||
$year = (int)substr($month, 0, 4);
|
||||
$monthNum = (int)substr($month, 5, 2);
|
||||
|
||||
$startDate = "{$month}-01";
|
||||
$endDate = date('Y-m-t', strtotime($startDate));
|
||||
|
||||
$stmt = $pdo->prepare('
|
||||
SELECT * FROM attendance
|
||||
WHERE user_id = ? AND shift_date BETWEEN ? AND ?
|
||||
ORDER BY shift_date DESC
|
||||
');
|
||||
$stmt->execute([$userId, $startDate, $endDate]);
|
||||
$records = $stmt->fetchAll();
|
||||
|
||||
enrichRecordsWithProjectLogs($pdo, $records);
|
||||
|
||||
$totalMinutes = 0;
|
||||
$vacationHours = 0;
|
||||
$sickHours = 0;
|
||||
$holidayHours = 0;
|
||||
$unpaidHours = 0;
|
||||
|
||||
foreach ($records as $record) {
|
||||
$leaveType = $record['leave_type'] ?? 'work';
|
||||
$leaveHours = (float)($record['leave_hours'] ?? 0);
|
||||
|
||||
if ($leaveType === 'vacation') {
|
||||
$vacationHours += $leaveHours;
|
||||
} elseif ($leaveType === 'sick') {
|
||||
$sickHours += $leaveHours;
|
||||
} elseif ($leaveType === 'holiday') {
|
||||
$holidayHours += $leaveHours;
|
||||
} elseif ($leaveType === 'unpaid') {
|
||||
$unpaidHours += $leaveHours;
|
||||
} else {
|
||||
$totalMinutes += calculateWorkMinutes($record);
|
||||
}
|
||||
}
|
||||
|
||||
$fund = CzechHolidays::getMonthlyWorkFund($year, $monthNum);
|
||||
$businessDays = CzechHolidays::getBusinessDaysInMonth($year, $monthNum);
|
||||
$workedHours = round($totalMinutes / 60, 1);
|
||||
$leaveHoursCovered = $vacationHours + $sickHours;
|
||||
$covered = $workedHours + $leaveHoursCovered;
|
||||
$remaining = max(0, round($fund - $covered, 1));
|
||||
$overtime = max(0, round($covered - $fund, 1));
|
||||
|
||||
$leaveBalance = getLeaveBalance($pdo, $userId, $year);
|
||||
|
||||
successResponse([
|
||||
'records' => $records,
|
||||
'month' => $month,
|
||||
'year' => $year,
|
||||
'month_name' => getCzechMonthName($monthNum) . ' ' . $year,
|
||||
'total_minutes' => $totalMinutes,
|
||||
'vacation_hours' => $vacationHours,
|
||||
'sick_hours' => $sickHours,
|
||||
'holiday_hours' => $holidayHours,
|
||||
'unpaid_hours' => $unpaidHours,
|
||||
'leave_balance' => $leaveBalance,
|
||||
'monthly_fund' => [
|
||||
'fund' => $fund,
|
||||
'business_days' => $businessDays,
|
||||
'worked' => $workedHours,
|
||||
'leave_hours' => $leaveHoursCovered,
|
||||
'covered' => $covered,
|
||||
'remaining' => $remaining,
|
||||
'overtime' => $overtime,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
function handlePunch(PDO $pdo, int $userId): void
|
||||
{
|
||||
$input = getJsonInput();
|
||||
$action = $input['punch_action'] ?? '';
|
||||
$today = date('Y-m-d');
|
||||
$rawNow = date('Y-m-d H:i:s');
|
||||
|
||||
$lat = isset($input['latitude']) && $input['latitude'] !== '' ? (float)$input['latitude'] : null;
|
||||
$lng = isset($input['longitude']) && $input['longitude'] !== '' ? (float)$input['longitude'] : null;
|
||||
$accuracy = isset($input['accuracy']) && $input['accuracy'] !== '' ? (float)$input['accuracy'] : null;
|
||||
$address = !empty($input['address']) ? $input['address'] : null;
|
||||
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT * FROM attendance
|
||||
WHERE user_id = ? AND departure_time IS NULL AND (leave_type IS NULL OR leave_type = 'work')
|
||||
ORDER BY created_at DESC LIMIT 1
|
||||
");
|
||||
$stmt->execute([$userId]);
|
||||
$ongoingShift = $stmt->fetch();
|
||||
|
||||
if ($action === 'arrival' && !$ongoingShift) {
|
||||
$now = roundUpTo15Minutes($rawNow);
|
||||
$stmt = $pdo->prepare('
|
||||
INSERT INTO attendance
|
||||
(user_id, shift_date, arrival_time, arrival_lat, arrival_lng, arrival_accuracy, arrival_address)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
');
|
||||
$stmt->execute([$userId, $today, $now, $lat, $lng, $accuracy, $address]);
|
||||
|
||||
AuditLog::logCreate('attendance', (int)$pdo->lastInsertId(), [
|
||||
'arrival_time' => $now,
|
||||
'location' => $address,
|
||||
], 'Příchod zaznamenán');
|
||||
|
||||
successResponse(null, 'Příchod zaznamenán');
|
||||
} elseif ($ongoingShift) {
|
||||
switch ($action) {
|
||||
case 'break_start':
|
||||
if ($ongoingShift['arrival_time'] && !$ongoingShift['break_start']) {
|
||||
$breakStart = roundToNearest10Minutes($rawNow);
|
||||
$breakEnd = date('Y-m-d H:i:s', strtotime($breakStart) + (30 * 60));
|
||||
|
||||
$stmt = $pdo->prepare('UPDATE attendance SET break_start = ?, break_end = ? WHERE id = ?');
|
||||
$stmt->execute([$breakStart, $breakEnd, $ongoingShift['id']]);
|
||||
|
||||
successResponse(null, 'Pauza zaznamenána');
|
||||
} else {
|
||||
errorResponse('Nelze zadat pauzu');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'departure':
|
||||
if ($ongoingShift['arrival_time'] && !$ongoingShift['departure_time']) {
|
||||
$now = roundDownTo15Minutes($rawNow);
|
||||
|
||||
// Auto-add break if shift is longer than 6h and no break
|
||||
if (!$ongoingShift['break_start'] && !$ongoingShift['break_end']) {
|
||||
$arrivalTime = strtotime($ongoingShift['arrival_time']);
|
||||
$departureTime = strtotime($now);
|
||||
$hoursWorked = ($departureTime - $arrivalTime) / 3600;
|
||||
|
||||
if ($hoursWorked > 12) {
|
||||
$midPoint = $arrivalTime + (($departureTime - $arrivalTime) / 2);
|
||||
$breakStart = roundToNearest10Minutes(date('Y-m-d H:i:s', $midPoint - (30 * 60)));
|
||||
$breakEnd = roundToNearest10Minutes(date('Y-m-d H:i:s', $midPoint + (30 * 60)));
|
||||
|
||||
$stmt = $pdo->prepare('UPDATE attendance SET break_start = ?, break_end = ? WHERE id = ?');
|
||||
$stmt->execute([$breakStart, $breakEnd, $ongoingShift['id']]);
|
||||
} elseif ($hoursWorked > 6) {
|
||||
$midPoint = $arrivalTime + (($departureTime - $arrivalTime) / 2);
|
||||
$breakStart = roundToNearest10Minutes(date('Y-m-d H:i:s', $midPoint - (15 * 60)));
|
||||
$breakEnd = roundToNearest10Minutes(date('Y-m-d H:i:s', $midPoint + (15 * 60)));
|
||||
|
||||
$stmt = $pdo->prepare('UPDATE attendance SET break_start = ?, break_end = ? WHERE id = ?');
|
||||
$stmt->execute([$breakStart, $breakEnd, $ongoingShift['id']]);
|
||||
}
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare('
|
||||
UPDATE attendance
|
||||
SET departure_time = ?, departure_lat = ?, departure_lng = ?,
|
||||
departure_accuracy = ?, departure_address = ?
|
||||
WHERE id = ?
|
||||
');
|
||||
$stmt->execute([$now, $lat, $lng, $accuracy, $address, $ongoingShift['id']]);
|
||||
|
||||
// Close any open project log
|
||||
$stmt = $pdo->prepare('
|
||||
UPDATE attendance_project_logs SET ended_at = ? WHERE attendance_id = ? AND ended_at IS NULL
|
||||
');
|
||||
$stmt->execute([$now, $ongoingShift['id']]);
|
||||
|
||||
AuditLog::logUpdate('attendance', $ongoingShift['id'], [], [
|
||||
'departure_time' => $now,
|
||||
'location' => $address,
|
||||
], 'Odchod zaznamenán');
|
||||
|
||||
successResponse(null, 'Odchod zaznamenán');
|
||||
} else {
|
||||
errorResponse('Nelze zadat odchod');
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
errorResponse('Neplatná akce');
|
||||
}
|
||||
} else {
|
||||
errorResponse('Neplatná akce - nemáte aktivní směnu');
|
||||
}
|
||||
}
|
||||
|
||||
function handleUpdateAddress(PDO $pdo, int $userId): void
|
||||
{
|
||||
$input = getJsonInput();
|
||||
$address = trim($input['address'] ?? '');
|
||||
$punchAction = $input['punch_action'] ?? '';
|
||||
|
||||
if (!$address) {
|
||||
successResponse(null);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($punchAction === 'arrival') {
|
||||
$stmt = $pdo->prepare("
|
||||
UPDATE attendance SET arrival_address = ?
|
||||
WHERE id = (
|
||||
SELECT id FROM (
|
||||
SELECT id FROM attendance
|
||||
WHERE user_id = ? AND (arrival_address IS NULL OR arrival_address = '')
|
||||
ORDER BY created_at DESC LIMIT 1
|
||||
) t
|
||||
)
|
||||
");
|
||||
} else {
|
||||
$stmt = $pdo->prepare("
|
||||
UPDATE attendance SET departure_address = ?
|
||||
WHERE id = (
|
||||
SELECT id FROM (
|
||||
SELECT id FROM attendance
|
||||
WHERE user_id = ? AND (departure_address IS NULL OR departure_address = '')
|
||||
AND departure_time IS NOT NULL
|
||||
ORDER BY created_at DESC LIMIT 1
|
||||
) t
|
||||
)
|
||||
");
|
||||
}
|
||||
$stmt->execute([$address, $userId]);
|
||||
|
||||
successResponse(null);
|
||||
}
|
||||
|
||||
function handleAddLeave(PDO $pdo, int $userId): void
|
||||
{
|
||||
$input = getJsonInput();
|
||||
|
||||
$leaveType = $input['leave_type'] ?? '';
|
||||
$leaveDate = $input['leave_date'] ?? '';
|
||||
$leaveHours = (float)($input['leave_hours'] ?? 8);
|
||||
$notes = trim($input['notes'] ?? '');
|
||||
|
||||
if (!$leaveType || !$leaveDate || $leaveHours <= 0) {
|
||||
errorResponse('Vyplňte všechna povinná pole');
|
||||
}
|
||||
|
||||
if (!in_array($leaveType, ['vacation', 'sick', 'unpaid'])) {
|
||||
errorResponse('Neplatný typ nepřítomnosti');
|
||||
}
|
||||
|
||||
if ($leaveType === 'vacation') {
|
||||
$year = (int)date('Y', strtotime($leaveDate));
|
||||
$balance = getLeaveBalance($pdo, $userId, $year);
|
||||
|
||||
if ($balance['vacation_remaining'] < $leaveHours) {
|
||||
errorResponse(
|
||||
"Nemáte dostatek hodin dovolené. Zbývá vám "
|
||||
. "{$balance['vacation_remaining']} hodin, požadujete {$leaveHours} hodin."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare('
|
||||
INSERT INTO attendance (user_id, shift_date, leave_type, leave_hours, notes)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
');
|
||||
$stmt->execute([$userId, $leaveDate, $leaveType, $leaveHours, $notes ?: null]);
|
||||
|
||||
updateLeaveBalance($pdo, $userId, $leaveDate, $leaveType, $leaveHours);
|
||||
|
||||
AuditLog::logCreate('attendance', (int)$pdo->lastInsertId(), [
|
||||
'leave_type' => $leaveType,
|
||||
'leave_hours' => $leaveHours,
|
||||
], "Zaznamenána nepřítomnost: $leaveType");
|
||||
|
||||
successResponse(null, 'Nepřítomnost byla zaznamenána');
|
||||
}
|
||||
|
||||
function handleSaveNotes(PDO $pdo, int $userId): void
|
||||
{
|
||||
$input = getJsonInput();
|
||||
$notes = trim($input['notes'] ?? '');
|
||||
|
||||
$stmt = $pdo->prepare('
|
||||
SELECT id FROM attendance
|
||||
WHERE user_id = ? AND departure_time IS NULL
|
||||
ORDER BY created_at DESC LIMIT 1
|
||||
');
|
||||
$stmt->execute([$userId]);
|
||||
$currentShift = $stmt->fetch();
|
||||
|
||||
if (!$currentShift) {
|
||||
errorResponse('Nemáte aktivní směnu');
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare('UPDATE attendance SET notes = ? WHERE id = ?');
|
||||
$stmt->execute([$notes, $currentShift['id']]);
|
||||
|
||||
successResponse(null, 'Poznámka byla uložena');
|
||||
}
|
||||
|
||||
function handleGetProjects(): void
|
||||
{
|
||||
try {
|
||||
$pdo = db();
|
||||
$stmt = $pdo->query(
|
||||
"SELECT id, project_number, name FROM projects
|
||||
WHERE status = 'aktivni' ORDER BY project_number ASC"
|
||||
);
|
||||
$projects = $stmt->fetchAll();
|
||||
successResponse(['projects' => $projects]);
|
||||
} catch (\Exception $e) {
|
||||
error_log('Failed to fetch projects: ' . $e->getMessage());
|
||||
successResponse(['projects' => []]);
|
||||
}
|
||||
}
|
||||
|
||||
function handleSwitchProject(PDO $pdo, int $userId): void
|
||||
{
|
||||
$input = getJsonInput();
|
||||
/** @var mixed $rawProjectId */
|
||||
$rawProjectId = $input['project_id'] ?? null;
|
||||
$projectId = isset($input['project_id']) && $rawProjectId !== '' && $rawProjectId !== null
|
||||
? (int)$rawProjectId
|
||||
: null;
|
||||
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT id FROM attendance
|
||||
WHERE user_id = ? AND departure_time IS NULL AND (leave_type IS NULL OR leave_type = 'work')
|
||||
ORDER BY created_at DESC LIMIT 1
|
||||
");
|
||||
$stmt->execute([$userId]);
|
||||
$currentShift = $stmt->fetch();
|
||||
|
||||
if (!$currentShift) {
|
||||
errorResponse('Nemáte aktivní směnu');
|
||||
}
|
||||
|
||||
$attendanceId = $currentShift['id'];
|
||||
$now = date('Y-m-d H:i:s');
|
||||
|
||||
$stmt = $pdo->prepare(
|
||||
'UPDATE attendance_project_logs SET ended_at = ?
|
||||
WHERE attendance_id = ? AND ended_at IS NULL'
|
||||
);
|
||||
$stmt->execute([$now, $attendanceId]);
|
||||
|
||||
if ($projectId) {
|
||||
$stmt = $pdo->prepare(
|
||||
'INSERT INTO attendance_project_logs
|
||||
(attendance_id, project_id, started_at) VALUES (?, ?, ?)'
|
||||
);
|
||||
$stmt->execute([$attendanceId, $projectId, $now]);
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare('UPDATE attendance SET project_id = ? WHERE id = ?');
|
||||
$stmt->execute([$projectId, $attendanceId]);
|
||||
|
||||
successResponse(null, $projectId ? 'Projekt přepnut' : 'Projekt zastaven');
|
||||
}
|
||||
|
||||
/** @param array<string, mixed> $authData */
|
||||
function handleGetProjectLogs(PDO $pdo, int $currentUserId, array $authData): void
|
||||
{
|
||||
$attendanceId = (int)($_GET['attendance_id'] ?? 0);
|
||||
if (!$attendanceId) {
|
||||
errorResponse('attendance_id je povinné');
|
||||
}
|
||||
|
||||
// Ověření vlastnictví záznamu nebo admin oprávnění
|
||||
if (!hasPermission($authData, 'attendance.admin')) {
|
||||
$ownerStmt = $pdo->prepare('SELECT user_id FROM attendance WHERE id = ?');
|
||||
$ownerStmt->execute([$attendanceId]);
|
||||
$owner = $ownerStmt->fetch();
|
||||
if (!$owner || (int)$owner['user_id'] !== $currentUserId) {
|
||||
errorResponse('Nemáte oprávnění zobrazit tyto záznamy', 403);
|
||||
}
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare('SELECT * FROM attendance_project_logs WHERE attendance_id = ? ORDER BY started_at ASC');
|
||||
$stmt->execute([$attendanceId]);
|
||||
$logs = $stmt->fetchAll();
|
||||
|
||||
$projectIds = [];
|
||||
foreach ($logs as $l) {
|
||||
$projectIds[$l['project_id']] = $l['project_id'];
|
||||
}
|
||||
$projNameMap = fetchProjectNames($projectIds);
|
||||
foreach ($logs as &$l) {
|
||||
$l['project_name'] = $projNameMap[$l['project_id']] ?? null;
|
||||
}
|
||||
unset($l);
|
||||
|
||||
successResponse(['logs' => $logs]);
|
||||
}
|
||||
|
||||
function handleSaveProjectLogs(PDO $pdo): void
|
||||
{
|
||||
$input = getJsonInput();
|
||||
$attendanceId = (int)($input['attendance_id'] ?? 0);
|
||||
$logs = $input['project_logs'] ?? [];
|
||||
|
||||
if (!$attendanceId) {
|
||||
errorResponse('attendance_id je povinné');
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare('SELECT * FROM attendance WHERE id = ?');
|
||||
$stmt->execute([$attendanceId]);
|
||||
$record = $stmt->fetch();
|
||||
if (!$record) {
|
||||
errorResponse('Záznam nebyl nalezen', 404);
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare('DELETE FROM attendance_project_logs WHERE attendance_id = ?');
|
||||
$stmt->execute([$attendanceId]);
|
||||
|
||||
if (!empty($logs)) {
|
||||
$stmt = $pdo->prepare(
|
||||
'INSERT INTO attendance_project_logs
|
||||
(attendance_id, project_id, hours, minutes) VALUES (?, ?, ?, ?)'
|
||||
);
|
||||
foreach ($logs as $log) {
|
||||
$projectId = (int)($log['project_id'] ?? 0);
|
||||
if (!$projectId) {
|
||||
continue;
|
||||
}
|
||||
$h = (int)($log['hours'] ?? 0);
|
||||
$m = (int)($log['minutes'] ?? 0);
|
||||
if ($h === 0 && $m === 0) {
|
||||
continue;
|
||||
}
|
||||
$stmt->execute([$attendanceId, $projectId, $h, $m]);
|
||||
}
|
||||
}
|
||||
|
||||
successResponse(null, 'Projektové záznamy byly uloženy');
|
||||
}
|
||||
Reference in New Issue
Block a user