- 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>
590 lines
20 KiB
PHP
590 lines
20 KiB
PHP
<?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');
|
|
}
|