Merge branch 'main' of https://git.ctb-it.de/clemens/praxis-creutzburg-web
This commit is contained in:
+191
-197
@@ -1,197 +1,191 @@
|
||||
<?php
|
||||
// API: returns JSON events for FullCalendar
|
||||
session_start();
|
||||
require_once(__DIR__ . '/../inc/config.inc.php');
|
||||
require_once(__DIR__ . '/../inc/functions.inc.php');
|
||||
|
||||
// Enable full error reporting for API debugging
|
||||
ini_set('display_errors', '1');
|
||||
ini_set('display_startup_errors', '1');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
$user = check_user();
|
||||
$isAdmin = is_admin_user();
|
||||
|
||||
$start = $_GET['start'] ?? null; // expected ISO date
|
||||
$end = $_GET['end'] ?? null;
|
||||
$onlyApproved = isset($_GET['only_approved']) && ($_GET['only_approved'] == '1' || $_GET['only_approved'] === 'true');
|
||||
// public allows non-admin users to request all *approved* vacations (team view)
|
||||
$public = isset($_GET['public']) && ($_GET['public'] == '1' || $_GET['public'] === 'true');
|
||||
// include_rejected if set to 1 will return rejected entries; default behavior: do not return rejected for regular users
|
||||
$includeRejected = isset($_GET['include_rejected']) && ($_GET['include_rejected'] == '1' || $_GET['include_rejected'] === 'true');
|
||||
// only_personal forces the API to return only the current user's vacations (useful even if the user is admin)
|
||||
$onlyPersonal = isset($_GET['only_personal']) && ($_GET['only_personal'] == '1' || $_GET['only_personal'] === 'true');
|
||||
// public_all when used with public=1 returns all non-rejected team vacations (approved + beantragt)
|
||||
$publicAll = isset($_GET['public_all']) && ($_GET['public_all'] == '1' || $_GET['public_all'] === 'true');
|
||||
|
||||
if (!$start || !$end) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'start and end required']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$events = [];
|
||||
try {
|
||||
$branch = 'unknown';
|
||||
$debugMode = isset($_GET['debug']) && ($_GET['debug'] == '1' || $_GET['debug'] === 'true');
|
||||
|
||||
// Vacations: support a personal-only mode, admin mode, and public/team mode
|
||||
if ($onlyPersonal) {
|
||||
$branch = 'onlyPersonal';
|
||||
if ($onlyApproved) {
|
||||
$branch = 'onlyPersonal_onlyApproved';
|
||||
$stmt = $pdo->prepare("SELECT v.*, u.vorname, u.nachname FROM vacations v JOIN users u ON v.user_id = u.id WHERE v.user_id = ? AND v.start_date <= ? AND v.end_date >= ? AND LOWER(TRIM(v.status)) = 'genehmigt' ORDER BY v.start_date");
|
||||
$stmt->execute([$_SESSION['userid'], $end, $start]);
|
||||
} else {
|
||||
if ($includeRejected) {
|
||||
$branch = 'onlyPersonal_includeRejected';
|
||||
$stmt = $pdo->prepare("SELECT v.*, u.vorname, u.nachname FROM vacations v JOIN users u ON v.user_id = u.id WHERE v.user_id = ? AND v.start_date <= ? AND v.end_date >= ? ORDER BY v.start_date");
|
||||
$stmt->execute([$_SESSION['userid'], $end, $start]);
|
||||
} else {
|
||||
$branch = 'onlyPersonal_excludeRejected';
|
||||
$stmt = $pdo->prepare("SELECT v.*, u.vorname, u.nachname FROM vacations v JOIN users u ON v.user_id = u.id WHERE v.user_id = ? AND v.start_date <= ? AND v.end_date >= ? AND (v.status IS NULL OR LOWER(TRIM(v.status)) != 'abgelehnt') ORDER BY v.start_date");
|
||||
$stmt->execute([$_SESSION['userid'], $end, $start]);
|
||||
}
|
||||
}
|
||||
} elseif ($isAdmin) {
|
||||
$branch = 'admin';
|
||||
if ($onlyApproved) {
|
||||
$branch = 'admin_onlyApproved';
|
||||
$stmt = $pdo->prepare("SELECT v.*, u.vorname, u.nachname FROM vacations v JOIN users u ON v.user_id = u.id WHERE v.start_date <= ? AND v.end_date >= ? AND LOWER(TRIM(v.status)) = 'genehmigt' ORDER BY v.start_date");
|
||||
$stmt->execute([$end, $start]);
|
||||
} else {
|
||||
// By default admins see genehmigt + beantragt; include_rejected=1 can override
|
||||
if ($includeRejected) {
|
||||
$stmt = $pdo->prepare("SELECT v.*, u.vorname, u.nachname FROM vacations v JOIN users u ON v.user_id = u.id WHERE v.start_date <= ? AND v.end_date >= ? ORDER BY v.start_date");
|
||||
$stmt->execute([$end, $start]);
|
||||
} else {
|
||||
$stmt = $pdo->prepare("SELECT v.*, u.vorname, u.nachname FROM vacations v JOIN users u ON v.user_id = u.id WHERE v.start_date <= ? AND v.end_date >= ? AND (v.status IS NULL OR LOWER(TRIM(v.status)) IN ('genehmigt','beantragt')) ORDER BY v.start_date");
|
||||
$stmt->execute([$end, $start]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$branch = 'public_or_regular';
|
||||
if ($public && $onlyApproved) {
|
||||
$branch = 'public_onlyApproved';
|
||||
// public team view: show all approved vacations (read-only)
|
||||
$stmt = $pdo->prepare("SELECT v.*, u.vorname, u.nachname FROM vacations v JOIN users u ON v.user_id = u.id WHERE v.start_date <= ? AND v.end_date >= ? AND LOWER(TRIM(v.status)) = 'genehmigt' ORDER BY v.start_date");
|
||||
$stmt->execute([$end, $start]);
|
||||
} elseif ($public && $publicAll) {
|
||||
$branch = 'public_publicAll';
|
||||
// public team view: explicitly show only approved (genehmigt) and pending (beantragt) vacations
|
||||
$stmt = $pdo->prepare("SELECT v.*, u.vorname, u.nachname FROM vacations v JOIN users u ON v.user_id = u.id WHERE v.start_date <= ? AND v.end_date >= ? AND (v.status IS NULL OR LOWER(TRIM(v.status)) IN ('genehmigt','beantragt')) ORDER BY v.start_date");
|
||||
$stmt->execute([$end, $start]);
|
||||
} else {
|
||||
if ($onlyApproved) {
|
||||
$stmt = $pdo->prepare("SELECT v.*, u.vorname, u.nachname FROM vacations v JOIN users u ON v.user_id = u.id WHERE v.user_id = ? AND v.start_date <= ? AND v.end_date >= ? AND LOWER(TRIM(v.status)) = 'genehmigt' ORDER BY v.start_date");
|
||||
$stmt->execute([$_SESSION['userid'], $end, $start]);
|
||||
} else {
|
||||
// By default exclude rejected ('abgelehnt') for regular users; include if include_rejected=1
|
||||
if ($includeRejected) {
|
||||
$branch = 'regular_includeRejected';
|
||||
$stmt = $pdo->prepare("SELECT v.*, u.vorname, u.nachname FROM vacations v JOIN users u ON v.user_id = u.id WHERE v.user_id = ? AND v.start_date <= ? AND v.end_date >= ? ORDER BY v.start_date");
|
||||
$stmt->execute([$_SESSION['userid'], $end, $start]);
|
||||
} else {
|
||||
$branch = 'regular_excludeRejected';
|
||||
$stmt = $pdo->prepare("SELECT v.*, u.vorname, u.nachname FROM vacations v JOIN users u ON v.user_id = u.id WHERE v.user_id = ? AND v.start_date <= ? AND v.end_date >= ? AND (v.status IS NULL OR LOWER(TRIM(v.status)) != 'abgelehnt') ORDER BY v.start_date");
|
||||
$stmt->execute([$_SESSION['userid'], $end, $start]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$vacations = $stmt->fetchAll();
|
||||
|
||||
// If debug mode is enabled, prepare meta information
|
||||
if ($debugMode) {
|
||||
$rawStatuses = array_map(function($r){ return $r['status'] ?? null; }, $vacations);
|
||||
$meta = [
|
||||
'branch' => $branch,
|
||||
'count' => count($vacations),
|
||||
'raw_statuses' => $rawStatuses
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($vacations as $v) {
|
||||
// Normalize status: collapse whitespace (including NBSP), trim, lowercase
|
||||
if (isset($v['status'])) {
|
||||
$normalized = preg_replace('/\s+/u', ' ', $v['status']);
|
||||
$status = mb_strtolower(trim($normalized));
|
||||
} else {
|
||||
$status = '';
|
||||
}
|
||||
// Defensive filter: do not expose rejected ('abgelehnt') entries to non-admins
|
||||
if (!$isAdmin && !$includeRejected && mb_stripos($status, 'abgelehnt') !== false) {
|
||||
continue;
|
||||
}
|
||||
$isApproved = (mb_stripos($status, 'genehmigt') !== false);
|
||||
if ($isAdmin) {
|
||||
$title = $v['vorname'] . ' ' . $v['nachname'] . ' (' . ($v['status'] ?? 'beantragt') . ')';
|
||||
} else {
|
||||
$title = $isApproved ? 'Urlaub' : 'Urlaubsantrag';
|
||||
}
|
||||
// Safely compute end date; fallback to start_date if invalid
|
||||
try {
|
||||
$endInclusive = (new DateTime($v['end_date']))->modify('+1 day')->format('Y-m-d');
|
||||
} catch (Exception $e) {
|
||||
$endInclusive = $v['start_date'];
|
||||
}
|
||||
$events[] = [
|
||||
'id' => 'vac_' . $v['id'],
|
||||
'title' => $title,
|
||||
'start' => $v['start_date'],
|
||||
'end' => $endInclusive,
|
||||
'allDay' => true,
|
||||
'color' => ($isApproved) ? '#28a745' : '#ffc107',
|
||||
'extendedProps' => [
|
||||
'type' => 'user',
|
||||
'user_id' => $v['user_id'],
|
||||
'status' => $v['status'],
|
||||
'comment' => $v['comment_user'] ?? ''
|
||||
]
|
||||
];
|
||||
}
|
||||
} catch (Exception $ex) {
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
$payload = ['error' => $ex->getMessage(), 'branch' => $branch, 'trace' => $ex->getTraceAsString()];
|
||||
echo json_encode($payload);
|
||||
exit;
|
||||
}
|
||||
|
||||
} catch (Exception $ex) {
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
$payload = ['error' => $ex->getMessage(), 'branch' => $branch, 'trace' => $ex->getTraceAsString()];
|
||||
echo json_encode($payload);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Company holidays (visible to all)
|
||||
$stmt = $pdo->prepare("SELECT * FROM company_holidays WHERE start_date <= ? AND end_date >= ? ORDER BY start_date");
|
||||
$stmt->execute([$end, $start]);
|
||||
$holidays = $stmt->fetchAll();
|
||||
|
||||
foreach ($holidays as $h) {
|
||||
$endInclusive = (new DateTime($h['end_date']))->modify('+1 day')->format('Y-m-d');
|
||||
$events[] = [
|
||||
'id' => 'com_' . $h['id'],
|
||||
'title' => $h['description'] ?: 'Betriebsurlaub',
|
||||
'start' => $h['start_date'],
|
||||
'end' => $endInclusive,
|
||||
'allDay' => true,
|
||||
'color' => '#007bff',
|
||||
'extendedProps' => [
|
||||
'type' => 'company',
|
||||
'description' => $h['description']
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
if ($debugMode) {
|
||||
echo json_encode(['events' => $events, 'meta' => $meta]);
|
||||
} else {
|
||||
echo json_encode($events);
|
||||
}
|
||||
|
||||
?>
|
||||
<?php
|
||||
session_start();
|
||||
require_once(__DIR__ . '/../inc/config.inc.php');
|
||||
require_once(__DIR__ . '/../inc/functions.inc.php');
|
||||
|
||||
ini_set('display_errors', '1');
|
||||
ini_set('display_startup_errors', '1');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
$user = check_user();
|
||||
$isAdmin = is_admin_user();
|
||||
|
||||
$start = $_GET['start'] ?? null;
|
||||
$end = $_GET['end'] ?? null;
|
||||
$onlyApproved = isset($_GET['only_approved']) && ($_GET['only_approved'] == '1' || $_GET['only_approved'] === 'true');
|
||||
$public = isset($_GET['public']) && ($_GET['public'] == '1' || $_GET['public'] === 'true');
|
||||
$includeRejected = isset($_GET['include_rejected']) && ($_GET['include_rejected'] == '1' || $_GET['include_rejected'] === 'true');
|
||||
$onlyPersonal = isset($_GET['only_personal']) && ($_GET['only_personal'] == '1' || $_GET['only_personal'] === 'true');
|
||||
$publicAll = isset($_GET['public_all']) && ($_GET['public_all'] == '1' || $_GET['public_all'] === 'true');
|
||||
|
||||
if (!$start || !$end) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'start and end required']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$events = [];
|
||||
try {
|
||||
$branch = 'unknown';
|
||||
$debugMode = isset($_GET['debug']) && ($_GET['debug'] == '1' || $_GET['debug'] === 'true');
|
||||
$showEmployeeNames = $isAdmin || $public;
|
||||
|
||||
if ($onlyPersonal) {
|
||||
$branch = 'onlyPersonal';
|
||||
if ($onlyApproved) {
|
||||
$branch = 'onlyPersonal_onlyApproved';
|
||||
$stmt = $pdo->prepare("SELECT v.*, u.vorname, u.nachname FROM vacations v JOIN users u ON v.user_id = u.id WHERE v.user_id = ? AND v.start_date <= ? AND v.end_date >= ? AND LOWER(TRIM(v.status)) = 'genehmigt' ORDER BY v.start_date");
|
||||
$stmt->execute([$_SESSION['userid'], $end, $start]);
|
||||
} else {
|
||||
if ($includeRejected) {
|
||||
$branch = 'onlyPersonal_includeRejected';
|
||||
$stmt = $pdo->prepare("SELECT v.*, u.vorname, u.nachname FROM vacations v JOIN users u ON v.user_id = u.id WHERE v.user_id = ? AND v.start_date <= ? AND v.end_date >= ? ORDER BY v.start_date");
|
||||
$stmt->execute([$_SESSION['userid'], $end, $start]);
|
||||
} else {
|
||||
$branch = 'onlyPersonal_excludeRejected';
|
||||
$stmt = $pdo->prepare("SELECT v.*, u.vorname, u.nachname FROM vacations v JOIN users u ON v.user_id = u.id WHERE v.user_id = ? AND v.start_date <= ? AND v.end_date >= ? AND (v.status IS NULL OR LOWER(TRIM(v.status)) != 'abgelehnt') ORDER BY v.start_date");
|
||||
$stmt->execute([$_SESSION['userid'], $end, $start]);
|
||||
}
|
||||
}
|
||||
} elseif ($isAdmin) {
|
||||
$branch = 'admin';
|
||||
if ($onlyApproved) {
|
||||
$branch = 'admin_onlyApproved';
|
||||
$stmt = $pdo->prepare("SELECT v.*, u.vorname, u.nachname FROM vacations v JOIN users u ON v.user_id = u.id WHERE v.start_date <= ? AND v.end_date >= ? AND LOWER(TRIM(v.status)) = 'genehmigt' ORDER BY v.start_date");
|
||||
$stmt->execute([$end, $start]);
|
||||
} else {
|
||||
if ($includeRejected) {
|
||||
$stmt = $pdo->prepare("SELECT v.*, u.vorname, u.nachname FROM vacations v JOIN users u ON v.user_id = u.id WHERE v.start_date <= ? AND v.end_date >= ? ORDER BY v.start_date");
|
||||
$stmt->execute([$end, $start]);
|
||||
} else {
|
||||
$stmt = $pdo->prepare("SELECT v.*, u.vorname, u.nachname FROM vacations v JOIN users u ON v.user_id = u.id WHERE v.start_date <= ? AND v.end_date >= ? AND (v.status IS NULL OR LOWER(TRIM(v.status)) IN ('genehmigt','beantragt')) ORDER BY v.start_date");
|
||||
$stmt->execute([$end, $start]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$branch = 'public_or_regular';
|
||||
if ($public && $onlyApproved) {
|
||||
$branch = 'public_onlyApproved';
|
||||
$stmt = $pdo->prepare("SELECT v.*, u.vorname, u.nachname FROM vacations v JOIN users u ON v.user_id = u.id WHERE v.start_date <= ? AND v.end_date >= ? AND LOWER(TRIM(v.status)) = 'genehmigt' ORDER BY v.start_date");
|
||||
$stmt->execute([$end, $start]);
|
||||
} elseif ($public && $publicAll) {
|
||||
$branch = 'public_publicAll';
|
||||
$stmt = $pdo->prepare("SELECT v.*, u.vorname, u.nachname FROM vacations v JOIN users u ON v.user_id = u.id WHERE v.start_date <= ? AND v.end_date >= ? AND (v.status IS NULL OR LOWER(TRIM(v.status)) IN ('genehmigt','beantragt')) ORDER BY v.start_date");
|
||||
$stmt->execute([$end, $start]);
|
||||
} else {
|
||||
if ($onlyApproved) {
|
||||
$stmt = $pdo->prepare("SELECT v.*, u.vorname, u.nachname FROM vacations v JOIN users u ON v.user_id = u.id WHERE v.user_id = ? AND v.start_date <= ? AND v.end_date >= ? AND LOWER(TRIM(v.status)) = 'genehmigt' ORDER BY v.start_date");
|
||||
$stmt->execute([$_SESSION['userid'], $end, $start]);
|
||||
} else {
|
||||
if ($includeRejected) {
|
||||
$branch = 'regular_includeRejected';
|
||||
$stmt = $pdo->prepare("SELECT v.*, u.vorname, u.nachname FROM vacations v JOIN users u ON v.user_id = u.id WHERE v.user_id = ? AND v.start_date <= ? AND v.end_date >= ? ORDER BY v.start_date");
|
||||
$stmt->execute([$_SESSION['userid'], $end, $start]);
|
||||
} else {
|
||||
$branch = 'regular_excludeRejected';
|
||||
$stmt = $pdo->prepare("SELECT v.*, u.vorname, u.nachname FROM vacations v JOIN users u ON v.user_id = u.id WHERE v.user_id = ? AND v.start_date <= ? AND v.end_date >= ? AND (v.status IS NULL OR LOWER(TRIM(v.status)) != 'abgelehnt') ORDER BY v.start_date");
|
||||
$stmt->execute([$_SESSION['userid'], $end, $start]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$vacations = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($debugMode) {
|
||||
$rawStatuses = array_map(function($r){ return $r['status'] ?? null; }, $vacations);
|
||||
$meta = [
|
||||
'branch' => $branch,
|
||||
'count' => count($vacations),
|
||||
'raw_statuses' => $rawStatuses
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($vacations as $v) {
|
||||
if (isset($v['status'])) {
|
||||
$normalized = preg_replace('/\s+/u', ' ', $v['status']);
|
||||
$status = mb_strtolower(trim($normalized));
|
||||
} else {
|
||||
$status = '';
|
||||
}
|
||||
|
||||
if (!$isAdmin && !$includeRejected && mb_stripos($status, 'abgelehnt') !== false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$isApproved = (mb_stripos($status, 'genehmigt') !== false);
|
||||
$employeeName = trim(($v['vorname'] ?? '') . ' ' . ($v['nachname'] ?? ''));
|
||||
if ($showEmployeeNames && $employeeName !== '') {
|
||||
$title = $employeeName;
|
||||
if ($v['status'] !== null && $v['status'] !== '') {
|
||||
$title .= ' (' . $v['status'] . ')';
|
||||
}
|
||||
} elseif ($isApproved) {
|
||||
$title = 'Urlaub';
|
||||
} else {
|
||||
$title = 'Urlaubsantrag';
|
||||
}
|
||||
|
||||
try {
|
||||
$endInclusive = (new DateTime($v['end_date']))->modify('+1 day')->format('Y-m-d');
|
||||
} catch (Exception $e) {
|
||||
$endInclusive = $v['start_date'];
|
||||
}
|
||||
|
||||
$events[] = [
|
||||
'id' => 'vac_' . $v['id'],
|
||||
'title' => $title,
|
||||
'start' => $v['start_date'],
|
||||
'end' => $endInclusive,
|
||||
'allDay' => true,
|
||||
'color' => ($isApproved) ? '#28a745' : '#ffc107',
|
||||
'extendedProps' => [
|
||||
'type' => 'user',
|
||||
'user_id' => $v['user_id'],
|
||||
'employee_name' => $employeeName,
|
||||
'status' => $v['status'],
|
||||
'comment' => $v['comment_user'] ?? ''
|
||||
]
|
||||
];
|
||||
}
|
||||
} catch (Exception $ex) {
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
$payload = ['error' => $ex->getMessage(), 'branch' => $branch, 'trace' => $ex->getTraceAsString()];
|
||||
echo json_encode($payload);
|
||||
exit;
|
||||
}
|
||||
} catch (Exception $ex) {
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
$payload = ['error' => $ex->getMessage(), 'branch' => $branch, 'trace' => $ex->getTraceAsString()];
|
||||
echo json_encode($payload);
|
||||
exit;
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare("SELECT * FROM company_holidays WHERE start_date <= ? AND end_date >= ? ORDER BY start_date");
|
||||
$stmt->execute([$end, $start]);
|
||||
$holidays = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
foreach ($holidays as $h) {
|
||||
$endInclusive = (new DateTime($h['end_date']))->modify('+1 day')->format('Y-m-d');
|
||||
$events[] = [
|
||||
'id' => 'com_' . $h['id'],
|
||||
'title' => $h['description'] ?: 'Betriebsurlaub',
|
||||
'start' => $h['start_date'],
|
||||
'end' => $endInclusive,
|
||||
'allDay' => true,
|
||||
'color' => '#007bff',
|
||||
'extendedProps' => [
|
||||
'type' => 'company',
|
||||
'description' => $h['description']
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
if ($debugMode) {
|
||||
echo json_encode(['events' => $events, 'meta' => $meta]);
|
||||
} else {
|
||||
echo json_encode($events);
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -1,90 +1,159 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once('inc/config.inc.php');
|
||||
require_once('inc/functions.inc.php');
|
||||
|
||||
$user = check_user();
|
||||
if (!is_admin_user()) {
|
||||
die('Zugriff verweigert. Nur Chefs dürfen Betriebsurlaub verwalten.');
|
||||
}
|
||||
|
||||
// Create table if not exists (optional helper)
|
||||
// Administrators can also run the SQL directly in DB. This is just a convenience.
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['start_date']) && isset($_POST['end_date'])) {
|
||||
$start = $_POST['start_date'];
|
||||
$end = $_POST['end_date'];
|
||||
$desc = trim($_POST['description'] ?? 'Betriebsurlaub');
|
||||
|
||||
$stmt = $pdo->prepare("INSERT INTO company_holidays (start_date, end_date, description, created_by) VALUES (?, ?, ?, ?)");
|
||||
$stmt->execute([$start, $end, $desc, $_SESSION['userid']]);
|
||||
|
||||
header('Location: company_holidays.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
include 'header.php';
|
||||
|
||||
$stmt = $pdo->prepare("SELECT * FROM company_holidays ORDER BY start_date DESC");
|
||||
$stmt->execute();
|
||||
$holidays = $stmt->fetchAll();
|
||||
|
||||
?>
|
||||
|
||||
<div class="container">
|
||||
<h2>Betriebsurlaub verwalten</h2>
|
||||
|
||||
<form method="post" class="form-inline mb-3">
|
||||
<div class="form-group mr-2">
|
||||
<label>Von:</label>
|
||||
<input type="date" name="start_date" class="form-control" required>
|
||||
</div>
|
||||
<div class="form-group mr-2">
|
||||
<label>Bis:</label>
|
||||
<input type="date" name="end_date" class="form-control" required>
|
||||
</div>
|
||||
<div class="form-group mr-2">
|
||||
<label>Beschreibung:</label>
|
||||
<input type="text" name="description" class="form-control" placeholder="z. B. Betriebsurlaub Weihnachten">
|
||||
</div>
|
||||
<button class="btn btn-primary">Hinzufügen</button>
|
||||
</form>
|
||||
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Von</th>
|
||||
<th>Bis</th>
|
||||
<th>Beschreibung</th>
|
||||
<th>Erstellt von</th>
|
||||
<th>Aktion</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($holidays as $h): ?>
|
||||
<tr>
|
||||
<td><?php echo $h['start_date']; ?></td>
|
||||
<td><?php echo $h['end_date']; ?></td>
|
||||
<td><?php echo htmlspecialchars($h['description']); ?></td>
|
||||
<td><?php
|
||||
$s = $pdo->prepare("SELECT vorname, nachname FROM users WHERE id = ?");
|
||||
$s->execute([$h['created_by']]);
|
||||
$u = $s->fetch();
|
||||
echo htmlspecialchars($u['vorname'] . ' ' . $u['nachname']);
|
||||
?></td>
|
||||
<td>
|
||||
<form method="post" action="deleteCompanyHoliday.php" onsubmit="return confirm('Betriebsurlaub wirklich löschen?');">
|
||||
<input type="hidden" name="id" value="<?php echo intval($h['id']); ?>">
|
||||
<button class="btn btn-sm btn-danger">Löschen</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
<?php include 'footer.php';
|
||||
|
||||
?>
|
||||
<?php
|
||||
session_start();
|
||||
require_once('inc/config.inc.php');
|
||||
require_once('inc/functions.inc.php');
|
||||
require_once(__DIR__ . '/../inc/company_holiday_sync.inc.php');
|
||||
|
||||
$user = check_user();
|
||||
if (!is_admin_user()) {
|
||||
die('Zugriff verweigert. Nur Chefs duerfen Betriebsurlaub verwalten.');
|
||||
}
|
||||
|
||||
$error = '';
|
||||
$schemaError = '';
|
||||
|
||||
try {
|
||||
vacationSyncEnsureSchema($pdo);
|
||||
} catch (Throwable $e) {
|
||||
$schemaError = 'Die Seite konnte das Urlaubsschema nicht automatisch aktualisieren: ' . $e->getMessage();
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['start_date']) && isset($_POST['end_date'])) {
|
||||
$start = trim((string)($_POST['start_date'] ?? ''));
|
||||
$end = trim((string)($_POST['end_date'] ?? ''));
|
||||
$desc = trim((string)($_POST['description'] ?? 'Betriebsurlaub'));
|
||||
$vertretung = trim((string)($_POST['vertretung'] ?? ''));
|
||||
$vertretertelefon = trim((string)($_POST['vertretertelefon'] ?? ''));
|
||||
$vertreteradresse = trim((string)($_POST['vertreteradresse'] ?? ''));
|
||||
$vertreterurl = trim((string)($_POST['vertreterurl'] ?? ''));
|
||||
|
||||
if ($start === '' || $end === '') {
|
||||
$error = 'Bitte Start- und Enddatum angeben.';
|
||||
} elseif ($start > $end) {
|
||||
$error = 'Das Enddatum darf nicht vor dem Startdatum liegen.';
|
||||
} elseif ($vertretung === '' || $vertretertelefon === '' || $vertreteradresse === '' || $vertreterurl === '') {
|
||||
$error = 'Bitte alle Vertreterinformationen vollstaendig ausfuellen.';
|
||||
} elseif ($schemaError !== '') {
|
||||
$error = $schemaError;
|
||||
} else {
|
||||
$stmt = $pdo->prepare("
|
||||
INSERT INTO company_holidays (
|
||||
start_date, end_date, description, vertretung, vertretertelefon, vertreteradresse, vertreterurl, created_by
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
");
|
||||
$stmt->execute([
|
||||
$start,
|
||||
$end,
|
||||
$desc,
|
||||
$vertretung,
|
||||
$vertretertelefon,
|
||||
$vertreteradresse,
|
||||
$vertreterurl,
|
||||
$_SESSION['userid']
|
||||
]);
|
||||
vacationSyncUrlaubFromCompanyHoliday($pdo, (int)$pdo->lastInsertId());
|
||||
|
||||
header('Location: company_holidays.php');
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
include 'header.php';
|
||||
|
||||
$stmt = $pdo->prepare("SELECT * FROM company_holidays ORDER BY start_date DESC");
|
||||
$stmt->execute();
|
||||
$holidays = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
?>
|
||||
|
||||
<div class="container">
|
||||
<h2>Betriebsurlaub verwalten</h2>
|
||||
|
||||
<?php if ($error !== ''): ?>
|
||||
<div class="alert alert-danger"><?php echo htmlspecialchars($error); ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if ($schemaError !== ''): ?>
|
||||
<div class="alert alert-warning"><?php echo htmlspecialchars($schemaError); ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="post" class="mb-3">
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-3">
|
||||
<label>Von:</label>
|
||||
<input type="date" name="start_date" class="form-control" required>
|
||||
</div>
|
||||
<div class="form-group col-md-3">
|
||||
<label>Bis:</label>
|
||||
<input type="date" name="end_date" class="form-control" required>
|
||||
</div>
|
||||
<div class="form-group col-md-6">
|
||||
<label>Beschreibung:</label>
|
||||
<input type="text" name="description" class="form-control" placeholder="z. B. Betriebsurlaub Weihnachten">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-6">
|
||||
<label>Vertretung:</label>
|
||||
<input type="text" name="vertretung" class="form-control" required>
|
||||
</div>
|
||||
<div class="form-group col-md-6">
|
||||
<label>Vertretung Telefon:</label>
|
||||
<input type="text" name="vertretertelefon" class="form-control" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-8">
|
||||
<label>Vertretung Adresse:</label>
|
||||
<input type="text" name="vertreteradresse" class="form-control" required>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label>Vertretung Webseite:</label>
|
||||
<input type="text" name="vertreterurl" class="form-control" required>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-primary">Hinzufuegen</button>
|
||||
</form>
|
||||
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Von</th>
|
||||
<th>Bis</th>
|
||||
<th>Beschreibung</th>
|
||||
<th>Vertretung</th>
|
||||
<th>Kontakt</th>
|
||||
<th>Erstellt von</th>
|
||||
<th>Aktion</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($holidays as $h): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars((string)$h['start_date']); ?></td>
|
||||
<td><?php echo htmlspecialchars((string)$h['end_date']); ?></td>
|
||||
<td><?php echo htmlspecialchars((string)$h['description']); ?></td>
|
||||
<td><?php echo htmlspecialchars((string)$h['vertretung']); ?></td>
|
||||
<td>
|
||||
<?php echo htmlspecialchars((string)$h['vertretertelefon']); ?><br>
|
||||
<?php echo htmlspecialchars((string)$h['vertreteradresse']); ?><br>
|
||||
<?php echo htmlspecialchars((string)$h['vertreterurl']); ?>
|
||||
</td>
|
||||
<td><?php
|
||||
$s = $pdo->prepare("SELECT vorname, nachname FROM users WHERE id = ?");
|
||||
$s->execute([$h['created_by']]);
|
||||
$u = $s->fetch(PDO::FETCH_ASSOC);
|
||||
echo htmlspecialchars(trim(($u['vorname'] ?? '') . ' ' . ($u['nachname'] ?? '')));
|
||||
?></td>
|
||||
<td>
|
||||
<form method="post" action="deleteCompanyHoliday.php" onsubmit="return confirm('Betriebsurlaub wirklich loeschen?');">
|
||||
<input type="hidden" name="id" value="<?php echo (int)$h['id']; ?>">
|
||||
<button class="btn btn-sm btn-danger">Loeschen</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
session_start();
|
||||
require_once('inc/config.inc.php');
|
||||
require_once('inc/functions.inc.php');
|
||||
require_once(__DIR__ . '/../inc/company_holiday_sync.inc.php');
|
||||
|
||||
$user = check_user();
|
||||
if (!is_admin_user()) {
|
||||
@@ -18,9 +19,10 @@ if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['id'])) {
|
||||
|
||||
$id = intval($_POST['id']);
|
||||
|
||||
vacationSyncDeleteUrlaubByCompanyHoliday($pdo, $id);
|
||||
$stmt = $pdo->prepare("DELETE FROM company_holidays WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
|
||||
header('Location: company_holidays.php');
|
||||
exit;
|
||||
?>
|
||||
?>
|
||||
|
||||
@@ -1,38 +1,34 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once('inc/config.inc.php');
|
||||
require_once('inc/functions.inc.php');
|
||||
|
||||
$user = check_user();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['id'])) {
|
||||
http_response_code(400);
|
||||
die('Bad request');
|
||||
}
|
||||
|
||||
$id = (int)$_POST['id'];
|
||||
$referer = $_POST['referer'] ?? 'urlaubsantrag.php';
|
||||
|
||||
// Fetch vacation to verify ownership
|
||||
$stmt = $pdo->prepare("SELECT user_id, status FROM vacations WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$vac = $stmt->fetch();
|
||||
|
||||
if (!$vac) {
|
||||
die('Urlaubseintrag nicht gefunden.');
|
||||
}
|
||||
|
||||
$isAdmin = is_admin_user();
|
||||
|
||||
if (!$isAdmin && $vac['user_id'] != $_SESSION['userid']) {
|
||||
die('Zugriff verweigert.');
|
||||
}
|
||||
|
||||
// Allow deletion for admins or owner
|
||||
$del = $pdo->prepare("DELETE FROM vacations WHERE id = ?");
|
||||
$del->execute([$id]);
|
||||
|
||||
header('Location: ' . $referer);
|
||||
exit();
|
||||
|
||||
?>
|
||||
<?php
|
||||
session_start();
|
||||
require_once('inc/config.inc.php');
|
||||
require_once('inc/functions.inc.php');
|
||||
|
||||
$user = check_user();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['id'])) {
|
||||
http_response_code(400);
|
||||
die('Bad request');
|
||||
}
|
||||
|
||||
$id = (int)$_POST['id'];
|
||||
$referer = $_POST['referer'] ?? 'urlaubsantrag.php';
|
||||
|
||||
$stmt = $pdo->prepare("SELECT user_id, status FROM vacations WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$vac = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$vac) {
|
||||
die('Urlaubseintrag nicht gefunden.');
|
||||
}
|
||||
|
||||
$canManageTeamVacations = can_manage_team_vacations();
|
||||
if (!$canManageTeamVacations && (int)$vac['user_id'] !== (int)$_SESSION['userid']) {
|
||||
die('Zugriff verweigert.');
|
||||
}
|
||||
|
||||
$del = $pdo->prepare("DELETE FROM vacations WHERE id = ?");
|
||||
$del->execute([$id]);
|
||||
|
||||
header('Location: ' . $referer);
|
||||
exit();
|
||||
?>
|
||||
|
||||
@@ -145,6 +145,15 @@ function is_admin_user() {
|
||||
$statement->execute(array('id' => $_SESSION['userid']));
|
||||
return ($statement->rowCount() == 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true when the user may manage vacations for the team.
|
||||
* In the current Zeiterfassung, team leads are represented by the
|
||||
* existing admin role.
|
||||
*/
|
||||
function can_manage_team_vacations() {
|
||||
return is_admin_user();
|
||||
}
|
||||
/**
|
||||
* Prüft, ob der Benutzer Bearbeiter ist
|
||||
*/
|
||||
@@ -187,4 +196,4 @@ function isValidSequence($sequence) {
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
?>
|
||||
|
||||
+241
-178
@@ -1,178 +1,241 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once("inc/config.inc.php");
|
||||
require_once("inc/functions.inc.php");
|
||||
|
||||
$user = check_user();
|
||||
|
||||
if (!isset($_SESSION['userid'])) {
|
||||
die("Kein Benutzer angemeldet.");
|
||||
}
|
||||
|
||||
$user_id = $_SESSION['userid'];
|
||||
$message = "";
|
||||
$error = "";
|
||||
|
||||
function calculateWorkingDays($start, $end) {
|
||||
$start = new DateTime($start);
|
||||
$end = new DateTime($end);
|
||||
$end->modify('+1 day');
|
||||
|
||||
$interval = new DateInterval('P1D');
|
||||
$period = new DatePeriod($start, $interval, $end);
|
||||
|
||||
$workingDays = 0;
|
||||
|
||||
foreach ($period as $day) {
|
||||
if ($day->format('N') < 6) { // 1 (Mo) - 5 (Fr)
|
||||
$workingDays++;
|
||||
}
|
||||
}
|
||||
|
||||
return $workingDays;
|
||||
}
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] == "POST") {
|
||||
|
||||
$start_date = $_POST['start_date'];
|
||||
$end_date = $_POST['end_date'];
|
||||
$comment = trim($_POST['comment']);
|
||||
|
||||
if (empty($start_date) || empty($end_date)) {
|
||||
$error = "Bitte beide Datumsfelder ausfüllen.";
|
||||
} elseif ($start_date > $end_date) {
|
||||
$error = "Enddatum liegt vor dem Startdatum.";
|
||||
} elseif ($start_date < date("Y-m-d")) {
|
||||
$error = "Urlaub kann nicht in der Vergangenheit beantragt werden.";
|
||||
} else {
|
||||
|
||||
// Überschneidung prüfen
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT COUNT(*) FROM vacations
|
||||
WHERE user_id = ?
|
||||
AND status != 'abgelehnt'
|
||||
AND (
|
||||
(start_date BETWEEN ? AND ?)
|
||||
OR (end_date BETWEEN ? AND ?)
|
||||
OR (? BETWEEN start_date AND end_date)
|
||||
)
|
||||
");
|
||||
$stmt->execute([$user_id, $start_date, $end_date, $start_date, $end_date, $start_date]);
|
||||
$exists = $stmt->fetchColumn();
|
||||
|
||||
if ($exists > 0) {
|
||||
$error = "Der Zeitraum überschneidet sich mit einem bestehenden Antrag.";
|
||||
} else {
|
||||
|
||||
$days = calculateWorkingDays($start_date, $end_date);
|
||||
|
||||
$insert = $pdo->prepare("
|
||||
INSERT INTO vacations (user_id, start_date, end_date, days, comment_user)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
");
|
||||
|
||||
$insert->execute([$user_id, $start_date, $end_date, $days, $comment]);
|
||||
|
||||
$message = "Urlaubsantrag erfolgreich eingereicht ($days Werktage).";
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<?php include 'header.php'; ?>
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-8 offset-md-2">
|
||||
|
||||
<h2>Urlaubsantrag</h2>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<div class="alert alert-danger"><?php echo $error; ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($message): ?>
|
||||
<div class="alert alert-success"><?php echo $message; ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="post">
|
||||
|
||||
<div class="form-group">
|
||||
<label>Von:</label>
|
||||
<input type="date" name="start_date" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Bis:</label>
|
||||
<input type="date" name="end_date" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Kommentar (optional):</label>
|
||||
<textarea name="comment" class="form-control"></textarea>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
<button type="submit" class="btn btn-primary btn-block">
|
||||
Urlaub beantragen
|
||||
</button>
|
||||
|
||||
</form>
|
||||
|
||||
<hr>
|
||||
|
||||
<h4>Meine Anträge</h4>
|
||||
|
||||
<?php
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT * FROM vacations
|
||||
WHERE user_id = ?
|
||||
ORDER BY created_at DESC
|
||||
");
|
||||
$stmt->execute([$user_id]);
|
||||
$antraege = $stmt->fetchAll();
|
||||
?>
|
||||
|
||||
<table class="table table-bordered">
|
||||
<tr>
|
||||
<th>Von</th>
|
||||
<th>Bis</th>
|
||||
<th>Tage</th>
|
||||
<th>Status</th>
|
||||
<th>Aktion</th>
|
||||
</tr>
|
||||
|
||||
<?php foreach ($antraege as $a): ?>
|
||||
<tr>
|
||||
<td><?php echo $a['start_date']; ?></td>
|
||||
<td><?php echo $a['end_date']; ?></td>
|
||||
<td><?php echo $a['days']; ?></td>
|
||||
<td>
|
||||
<?php
|
||||
if ($a['status'] == 'beantragt') {
|
||||
echo '<span class="badge badge-warning">Beantragt</span>';
|
||||
} elseif ($a['status'] == 'genehmigt') {
|
||||
echo '<span class="badge badge-success">Genehmigt</span>';
|
||||
} else {
|
||||
echo '<span class="badge badge-danger">Abgelehnt</span>';
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
<td>
|
||||
<form method="post" action="deleteVacation.php" onsubmit="return confirm('Wirklich löschen?');">
|
||||
<input type="hidden" name="id" value="<?php echo $a['id']; ?>">
|
||||
<input type="hidden" name="referer" value="urlaubsantrag.php">
|
||||
<button type="submit" class="btn btn-sm btn-danger">Löschen</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
<?php
|
||||
session_start();
|
||||
require_once("inc/config.inc.php");
|
||||
require_once("inc/functions.inc.php");
|
||||
|
||||
$user = check_user();
|
||||
|
||||
if (!isset($_SESSION['userid'])) {
|
||||
die("Kein Benutzer angemeldet.");
|
||||
}
|
||||
|
||||
$user_id = (int)$_SESSION['userid'];
|
||||
$canManageTeamVacations = can_manage_team_vacations();
|
||||
$message = "";
|
||||
$error = "";
|
||||
$selected_user_id = $user_id;
|
||||
|
||||
$selectableUsers = [];
|
||||
if ($canManageTeamVacations) {
|
||||
$stmtUsers = $pdo->prepare("
|
||||
SELECT id, vorname, nachname, email
|
||||
FROM users
|
||||
WHERE zeiterfassung = 1
|
||||
ORDER BY nachname, vorname
|
||||
");
|
||||
$stmtUsers->execute();
|
||||
$selectableUsers = $stmtUsers->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
function calculateWorkingDays($start, $end) {
|
||||
$start = new DateTime($start);
|
||||
$end = new DateTime($end);
|
||||
$end->modify('+1 day');
|
||||
|
||||
$interval = new DateInterval('P1D');
|
||||
$period = new DatePeriod($start, $interval, $end);
|
||||
|
||||
$workingDays = 0;
|
||||
foreach ($period as $day) {
|
||||
if ($day->format('N') < 6) {
|
||||
$workingDays++;
|
||||
}
|
||||
}
|
||||
|
||||
return $workingDays;
|
||||
}
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$start_date = trim((string)($_POST['start_date'] ?? ''));
|
||||
$end_date = trim((string)($_POST['end_date'] ?? ''));
|
||||
$comment = trim((string)($_POST['comment'] ?? ''));
|
||||
$selected_user_id = $canManageTeamVacations ? (int)($_POST['user_id'] ?? $user_id) : $user_id;
|
||||
|
||||
$selectedUser = null;
|
||||
if ($selected_user_id <= 0) {
|
||||
$error = "Bitte einen Mitarbeiter auswaehlen.";
|
||||
} else {
|
||||
$stmtSelectedUser = $pdo->prepare("
|
||||
SELECT id, vorname, nachname
|
||||
FROM users
|
||||
WHERE id = ?
|
||||
AND zeiterfassung = 1
|
||||
LIMIT 1
|
||||
");
|
||||
$stmtSelectedUser->execute([$selected_user_id]);
|
||||
$selectedUser = $stmtSelectedUser->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$selectedUser) {
|
||||
$error = "Der ausgewaehlte Mitarbeiter wurde nicht gefunden.";
|
||||
}
|
||||
}
|
||||
|
||||
if ($error === "" && ($start_date === '' || $end_date === '')) {
|
||||
$error = "Bitte beide Datumsfelder ausfuellen.";
|
||||
} elseif ($error === "" && $start_date > $end_date) {
|
||||
$error = "Enddatum liegt vor dem Startdatum.";
|
||||
} elseif ($error === "" && $start_date < date("Y-m-d")) {
|
||||
$error = "Urlaub kann nicht in der Vergangenheit beantragt werden.";
|
||||
} elseif ($error === "") {
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT COUNT(*)
|
||||
FROM vacations
|
||||
WHERE user_id = ?
|
||||
AND status != 'abgelehnt'
|
||||
AND (
|
||||
(start_date BETWEEN ? AND ?)
|
||||
OR (end_date BETWEEN ? AND ?)
|
||||
OR (? BETWEEN start_date AND end_date)
|
||||
)
|
||||
");
|
||||
$stmt->execute([$selected_user_id, $start_date, $end_date, $start_date, $end_date, $start_date]);
|
||||
$exists = (int)$stmt->fetchColumn();
|
||||
|
||||
if ($exists > 0) {
|
||||
$error = "Der Zeitraum ueberschneidet sich mit einem bestehenden Antrag.";
|
||||
} else {
|
||||
$days = calculateWorkingDays($start_date, $end_date);
|
||||
|
||||
$insert = $pdo->prepare("
|
||||
INSERT INTO vacations (user_id, start_date, end_date, days, comment_user)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
");
|
||||
$insert->execute([$selected_user_id, $start_date, $end_date, $days, $comment]);
|
||||
|
||||
if ($selected_user_id !== $user_id && $selectedUser) {
|
||||
$message = "Urlaub fuer " . $selectedUser['vorname'] . " " . $selectedUser['nachname'] . " erfolgreich eingereicht ($days Werktage).";
|
||||
} else {
|
||||
$message = "Urlaubsantrag erfolgreich eingereicht ($days Werktage).";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<?php include 'header.php'; ?>
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-8 offset-md-2">
|
||||
|
||||
<h2>Urlaubsantrag</h2>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<div class="alert alert-danger"><?php echo htmlspecialchars($error); ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($message): ?>
|
||||
<div class="alert alert-success"><?php echo htmlspecialchars($message); ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="post">
|
||||
|
||||
<?php if ($canManageTeamVacations): ?>
|
||||
<div class="form-group">
|
||||
<label>Mitarbeiter:</label>
|
||||
<select name="user_id" class="form-control" required>
|
||||
<?php foreach ($selectableUsers as $employee): ?>
|
||||
<?php $employeeId = (int)$employee['id']; ?>
|
||||
<option value="<?php echo $employeeId; ?>" <?php echo ($selected_user_id === $employeeId) ? 'selected' : ''; ?>>
|
||||
<?php echo htmlspecialchars(trim($employee['nachname'] . ', ' . $employee['vorname'] . ' | ' . $employee['email'])); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Von:</label>
|
||||
<input type="date" name="start_date" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Bis:</label>
|
||||
<input type="date" name="end_date" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Kommentar (optional):</label>
|
||||
<textarea name="comment" class="form-control"></textarea>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
<button type="submit" class="btn btn-primary btn-block">
|
||||
<?php echo $canManageTeamVacations ? 'Urlaub eintragen' : 'Urlaub beantragen'; ?>
|
||||
</button>
|
||||
|
||||
</form>
|
||||
|
||||
<hr>
|
||||
|
||||
<h4><?php echo $canManageTeamVacations ? 'Urlaubseintraege' : 'Meine Antraege'; ?></h4>
|
||||
|
||||
<?php
|
||||
$listSql = "
|
||||
SELECT v.*, u.vorname, u.nachname
|
||||
FROM vacations v
|
||||
JOIN users u ON u.id = v.user_id
|
||||
";
|
||||
|
||||
if ($canManageTeamVacations) {
|
||||
$listSql .= " ORDER BY v.created_at DESC";
|
||||
$stmt = $pdo->prepare($listSql);
|
||||
$stmt->execute();
|
||||
} else {
|
||||
$listSql .= " WHERE v.user_id = ? ORDER BY v.created_at DESC";
|
||||
$stmt = $pdo->prepare($listSql);
|
||||
$stmt->execute([$user_id]);
|
||||
}
|
||||
|
||||
$antraege = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
?>
|
||||
|
||||
<table class="table table-bordered">
|
||||
<tr>
|
||||
<?php if ($canManageTeamVacations): ?>
|
||||
<th>Mitarbeiter</th>
|
||||
<?php endif; ?>
|
||||
<th>Von</th>
|
||||
<th>Bis</th>
|
||||
<th>Tage</th>
|
||||
<th>Status</th>
|
||||
<th>Aktion</th>
|
||||
</tr>
|
||||
|
||||
<?php foreach ($antraege as $a): ?>
|
||||
<tr>
|
||||
<?php if ($canManageTeamVacations): ?>
|
||||
<td><?php echo htmlspecialchars(trim($a['vorname'] . ' ' . $a['nachname'])); ?></td>
|
||||
<?php endif; ?>
|
||||
<td><?php echo htmlspecialchars((string)$a['start_date']); ?></td>
|
||||
<td><?php echo htmlspecialchars((string)$a['end_date']); ?></td>
|
||||
<td><?php echo (int)$a['days']; ?></td>
|
||||
<td>
|
||||
<?php
|
||||
if ($a['status'] === 'beantragt' || $a['status'] === null || $a['status'] === '') {
|
||||
echo '<span class="badge badge-warning">Beantragt</span>';
|
||||
} elseif ($a['status'] === 'genehmigt') {
|
||||
echo '<span class="badge badge-success">Genehmigt</span>';
|
||||
} else {
|
||||
echo '<span class="badge badge-danger">Abgelehnt</span>';
|
||||
}
|
||||
?>
|
||||
</td>
|
||||
<td>
|
||||
<form method="post" action="deleteVacation.php" onsubmit="return confirm('Wirklich loeschen?');">
|
||||
<input type="hidden" name="id" value="<?php echo (int)$a['id']; ?>">
|
||||
<input type="hidden" name="referer" value="urlaubsantrag.php">
|
||||
<button type="submit" class="btn btn-sm btn-danger">Loeschen</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
|
||||
@@ -41,16 +41,17 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
var url = 'api/vacations.php?start=' + info.startStr + '&end=' + info.endStr;
|
||||
fetch(url).then(function(res){ return res.json(); }).then(function(data){ successCallback(data); }).catch(function(err){ failureCallback(err); });
|
||||
},
|
||||
eventClick: function(info) {
|
||||
var ev = info.event;
|
||||
var props = ev.extendedProps;
|
||||
var html = '<strong>' + ev.title + '</strong><br>' + ev.start.toLocaleDateString() + ' - ' + (new Date(ev.end).toLocaleDateString()) + '<br>';
|
||||
if (props.type === 'user') {
|
||||
html += 'Status: ' + (props.status || '') + '<br>';
|
||||
html += 'Kommentar: ' + (props.comment || '') + '<br>';
|
||||
} else if (props.type === 'company') {
|
||||
html += 'Beschreibung: ' + (props.description || '') + '<br>';
|
||||
}
|
||||
eventClick: function(info) {
|
||||
var ev = info.event;
|
||||
var props = ev.extendedProps;
|
||||
var html = '<strong>' + ev.title + '</strong><br>' + ev.start.toLocaleDateString() + ' - ' + (new Date(ev.end).toLocaleDateString()) + '<br>';
|
||||
if (props.type === 'user') {
|
||||
html += 'Mitarbeiter: ' + (props.employee_name || '') + '<br>';
|
||||
html += 'Status: ' + (props.status || '') + '<br>';
|
||||
html += 'Kommentar: ' + (props.comment || '') + '<br>';
|
||||
} else if (props.type === 'company') {
|
||||
html += 'Beschreibung: ' + (props.description || '') + '<br>';
|
||||
}
|
||||
document.getElementById('detailsContent').innerHTML = html;
|
||||
document.getElementById('eventDetails').style.display = 'block';
|
||||
}
|
||||
|
||||
@@ -1,59 +1,63 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once('inc/config.inc.php');
|
||||
require_once('inc/functions.inc.php');
|
||||
|
||||
// allow any logged-in user to view the team calendar (read-only)
|
||||
$user = check_user();
|
||||
|
||||
include 'header.php';
|
||||
?>
|
||||
|
||||
<div class="container">
|
||||
<h2>Team Urlaubskalender</h2>
|
||||
<div id="calendar"></div>
|
||||
<br>
|
||||
<div>
|
||||
<span class="badge badge-success">genehmigt</span>
|
||||
<span class="badge badge-primary">Betriebsurlaub</span>
|
||||
</div>
|
||||
<br>
|
||||
<div id="eventDetails" style="display:none;">
|
||||
<h4>Details</h4>
|
||||
<div id="detailsContent"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<link href='https://cdn.jsdelivr.net/npm/fullcalendar@5.11.3/main.min.css' rel='stylesheet' />
|
||||
<script src='https://cdn.jsdelivr.net/npm/fullcalendar@5.11.3/main.min.js'></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var calendarEl = document.getElementById('calendar');
|
||||
|
||||
var calendar = new FullCalendar.Calendar(calendarEl, {
|
||||
initialView: 'dayGridMonth',
|
||||
firstDay: 1,
|
||||
height: 650,
|
||||
events: function(info, successCallback, failureCallback) {
|
||||
var url = 'api/vacations.php?start=' + info.startStr + '&end=' + info.endStr + '&public=1&public_all=1';
|
||||
fetch(url).then(function(res){ return res.json(); }).then(function(data){ successCallback(data); }).catch(function(err){ failureCallback(err); });
|
||||
},
|
||||
eventClick: function(info) {
|
||||
var ev = info.event;
|
||||
var props = ev.extendedProps;
|
||||
var html = '<strong>' + ev.title + '</strong><br>' + ev.start.toLocaleDateString() + ' - ' + (new Date(ev.end).toLocaleDateString()) + '<br>';
|
||||
if (props.type === 'user') {
|
||||
html += 'Mitarbeiter-ID: ' + (props.user_id || '') + '<br>';
|
||||
} else if (props.type === 'company') {
|
||||
html += 'Beschreibung: ' + (props.description || '') + '<br>';
|
||||
}
|
||||
document.getElementById('detailsContent').innerHTML = html;
|
||||
document.getElementById('eventDetails').style.display = 'block';
|
||||
}
|
||||
});
|
||||
|
||||
calendar.render();
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
<?php
|
||||
session_start();
|
||||
require_once('inc/config.inc.php');
|
||||
require_once('inc/functions.inc.php');
|
||||
|
||||
$user = check_user();
|
||||
|
||||
include 'header.php';
|
||||
?>
|
||||
|
||||
<div class="container">
|
||||
<h2>Team Urlaubskalender</h2>
|
||||
<div id="calendar"></div>
|
||||
<br>
|
||||
<div>
|
||||
<span class="badge badge-success">genehmigt</span>
|
||||
<span class="badge badge-warning">beantragt</span>
|
||||
<span class="badge badge-primary">Betriebsurlaub</span>
|
||||
</div>
|
||||
<br>
|
||||
<div id="eventDetails" style="display:none;">
|
||||
<h4>Details</h4>
|
||||
<div id="detailsContent"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<link href='https://cdn.jsdelivr.net/npm/fullcalendar@5.11.3/main.min.css' rel='stylesheet' />
|
||||
<script src='https://cdn.jsdelivr.net/npm/fullcalendar@5.11.3/main.min.js'></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var calendarEl = document.getElementById('calendar');
|
||||
|
||||
var calendar = new FullCalendar.Calendar(calendarEl, {
|
||||
initialView: 'dayGridMonth',
|
||||
firstDay: 1,
|
||||
height: 650,
|
||||
events: function(info, successCallback, failureCallback) {
|
||||
var url = 'api/vacations.php?start=' + info.startStr + '&end=' + info.endStr + '&public=1&public_all=1';
|
||||
fetch(url).then(function(res){ return res.json(); }).then(function(data){ successCallback(data); }).catch(function(err){ failureCallback(err); });
|
||||
},
|
||||
eventClick: function(info) {
|
||||
var ev = info.event;
|
||||
var props = ev.extendedProps;
|
||||
var html = '<strong>' + ev.title + '</strong><br>' + ev.start.toLocaleDateString() + ' - ' + (new Date(ev.end).toLocaleDateString()) + '<br>';
|
||||
if (props.type === 'user') {
|
||||
html += 'Mitarbeiter: ' + (props.employee_name || ev.title || '') + '<br>';
|
||||
html += 'Status: ' + (props.status || '') + '<br>';
|
||||
if (props.comment) {
|
||||
html += 'Kommentar: ' + props.comment + '<br>';
|
||||
}
|
||||
} else if (props.type === 'company') {
|
||||
html += 'Beschreibung: ' + (props.description || '') + '<br>';
|
||||
}
|
||||
document.getElementById('detailsContent').innerHTML = html;
|
||||
document.getElementById('eventDetails').style.display = 'block';
|
||||
}
|
||||
});
|
||||
|
||||
calendar.render();
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php include 'footer.php'; ?>
|
||||
|
||||
Reference in New Issue
Block a user