Enhance waitlist functionality: update queries to count distinct users, add new impfwarteliste.php page, and improve form handling in functions.impfen.inc.php

This commit is contained in:
2026-03-21 17:04:37 +01:00
parent 347188bd0c
commit 70a78c9586
6 changed files with 451 additions and 11 deletions
+18 -7
View File
@@ -235,7 +235,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
} else {
$dosen = (int)$rule['dosen_pro_flasche'];
$stCount = $pdo->prepare("SELECT COUNT(*)
$stCount = $pdo->prepare("SELECT COUNT(DISTINCT userid)
FROM warteliste
WHERE checked = 1
AND (impfstoff = :iid OR impfstoff = 0)
@@ -291,16 +291,27 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
WHERE checked = 1
AND (impfstoff = :iid OR impfstoff = 0)
AND (zeitraum_id = :zid OR zeitraum_id IS NULL)
ORDER BY date_created ASC, warteid ASC
LIMIT :lim");
ORDER BY date_created ASC, warteid ASC");
$stW->bindValue(':iid', $impfstoffId, PDO::PARAM_INT);
$stW->bindValue(':zid', $planId, PDO::PARAM_INT);
$stW->bindValue(':lim', $dosen, PDO::PARAM_INT);
$stW->execute();
$warteRows = $stW->fetchAll(PDO::FETCH_ASSOC);
$warteRowsRaw = $stW->fetchAll(PDO::FETCH_ASSOC);
$warteRows = [];
$seenUserIds = [];
foreach ($warteRowsRaw as $warteRow) {
$warteUserId = (int)$warteRow['userid'];
if (isset($seenUserIds[$warteUserId])) {
continue;
}
$seenUserIds[$warteUserId] = true;
$warteRows[] = $warteRow;
if (count($warteRows) >= $dosen) {
break;
}
}
if (count($warteRows) < $dosen) {
throw new RuntimeException("Warteliste während Verarbeitung zu klein geworden.");
throw new RuntimeException("Warteliste waehrend Verarbeitung zu klein geworden.");
}
$stInsTermin = $pdo->prepare("INSERT INTO impftermin
@@ -540,7 +551,7 @@ $eventOverview = [];
try {
$stRules = $pdo->prepare("SELECT r.impfstoff_id, r.dosen_pro_flasche, i.impfname,
COALESCE((SELECT COUNT(*) FROM warteliste w WHERE w.checked = 1 AND (w.impfstoff = r.impfstoff_id OR w.impfstoff = 0)),0) AS wartende
COALESCE((SELECT COUNT(DISTINCT w.userid) FROM warteliste w WHERE w.checked = 1 AND (w.impfstoff = r.impfstoff_id OR w.impfstoff = 0)),0) AS wartende
FROM impfstoff_workflow r
INNER JOIN impfstoff i ON i.impfid = r.impfstoff_id
WHERE (i.aktiv = 1 OR i.aktivwarteliste = 1 OR i.aktivtermin = 1 OR i.aktivgrippe = 1)
+1 -1
View File
@@ -411,7 +411,7 @@ $allImpforte = getImpforte($pdo, false);
$zeitraeume = impfGetZeitraumRows($pdo, true);
$stRules = $pdo->prepare("SELECT r.impfstoff_id, r.dosen_pro_flasche, i.impfname,
COALESCE((SELECT COUNT(*) FROM warteliste w WHERE w.checked = 1 AND (w.impfstoff = r.impfstoff_id OR w.impfstoff = 0)),0) AS wartende
COALESCE((SELECT COUNT(DISTINCT w.userid) FROM warteliste w WHERE w.checked = 1 AND (w.impfstoff = r.impfstoff_id OR w.impfstoff = 0)),0) AS wartende
FROM impfstoff_workflow r
INNER JOIN impfstoff i ON i.impfid = r.impfstoff_id
WHERE (i.aktiv = 1 OR i.aktivwarteliste = 1 OR i.aktivtermin = 1 OR i.aktivgrippe = 1)
+142
View File
@@ -358,3 +358,145 @@ if (!function_exists('impfGetZeitraeumeByImpfstoff')) {
return $result;
}
}
if (!function_exists('impfGetWartelistenFormOptions')) {
function impfGetWartelistenFormOptions(PDO $pdo): array
{
$impfstoffe = [];
$zeitfenster = [];
$stImpfstoffe = $pdo->prepare("SELECT r.impfstoff_id, i.impfname
FROM impfstoff_workflow r
INNER JOIN impfstoff i ON i.impfid = r.impfstoff_id
WHERE (i.aktiv = 1 OR i.aktivwarteliste = 1 OR i.aktivtermin = 1 OR i.aktivgrippe = 1)
AND r.dosen_pro_flasche > 0
ORDER BY i.impfname");
$stImpfstoffe->execute();
foreach ($stImpfstoffe->fetchAll(PDO::FETCH_ASSOC) as $row) {
$impfstoffe[(int)$row['impfstoff_id']] = (string)$row['impfname'];
}
$zeitraeumeByImpfstoff = impfGetZeitraeumeByImpfstoff($pdo, true);
foreach ($zeitraeumeByImpfstoff as $impfstoffId => $zeitraeume) {
if (!isset($impfstoffe[$impfstoffId])) {
continue;
}
foreach ($zeitraeume as $zeitraum) {
if (!isset($zeitfenster[$impfstoffId])) {
$zeitfenster[$impfstoffId] = [];
}
$zeitfenster[$impfstoffId][] = [
'id' => (int)$zeitraum['zeitraum_id'],
'label' => (string)$zeitraum['label'],
];
}
}
foreach (array_keys($impfstoffe) as $impfstoffId) {
if (empty($zeitfenster[$impfstoffId])) {
unset($impfstoffe[$impfstoffId]);
}
}
return [
'impfstoffe' => $impfstoffe,
'zeitfenster' => $zeitfenster,
];
}
}
if (!function_exists('impfCreateWaitlistEntryForPerson')) {
function impfCreateWaitlistEntryForPerson(
PDO $pdo,
int $personId,
int $impfstoffId,
int $zeitraumId,
int $impfart,
?string $letzteImpfung = null,
int $checked = 1
): array {
if ($personId <= 0) {
return [false, 'Keine gueltige Person uebergeben.', null];
}
if ($impfstoffId <= 0) {
return [false, 'Bitte einen Impfstoff auswaehlen.', null];
}
if ($zeitraumId <= 0) {
return [false, 'Bitte ein Zeitfenster auswaehlen.', null];
}
if ($impfart < 1 || $impfart > 4) {
return [false, 'Bitte eine gueltige Impfungsart auswaehlen.', null];
}
$letzteImpfung = $letzteImpfung !== null ? trim($letzteImpfung) : null;
if ($impfart === 1) {
$letzteImpfung = null;
} elseif ($letzteImpfung === null || $letzteImpfung === '') {
return [false, 'Bitte das Datum der letzten Impfung angeben.', null];
} elseif (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $letzteImpfung)) {
return [false, 'Das Datum der letzten Impfung ist ungueltig.', null];
}
$stPerson = $pdo->prepare("SELECT person_id, patientenart, vorname, nachname
FROM persons
WHERE person_id = :pid
LIMIT 1");
$stPerson->execute(['pid' => $personId]);
$person = $stPerson->fetch(PDO::FETCH_ASSOC);
if (!$person) {
return [false, 'Die Person wurde nicht gefunden.', null];
}
$zeitraum = impfLoadZeitraumById($pdo, $zeitraumId, true);
if (!$zeitraum) {
return [false, 'Das ausgewaehlte Zeitfenster ist nicht mehr verfuegbar.', null];
}
if (!in_array($impfstoffId, $zeitraum['impfstoff_id_list'] ?? [], true)) {
return [false, 'Impfstoff und Zeitfenster passen nicht zusammen.', null];
}
$stDup = $pdo->prepare("SELECT warteid
FROM warteliste
WHERE userid = :uid
AND checked IN (0, 1)
AND impfstoff = :impfstoff
AND COALESCE(zeitraum_id, 0) = :zeitraum_id
AND impfart = :impfart
LIMIT 1");
$stDup->execute([
'uid' => $personId,
'impfstoff' => $impfstoffId,
'zeitraum_id' => $zeitraumId,
'impfart' => $impfart,
]);
if ($stDup->fetchColumn()) {
return [false, 'Diese Anfrage ist fuer diese Person bereits auf der aktiven Warteliste vorhanden.', null];
}
$patientenart = ((int)($person['patientenart'] ?? 0) === 1) ? 1 : 0;
$hash = bin2hex(random_bytes(16));
$checkedValue = ($checked === 0) ? 0 : 1;
$stInsert = $pdo->prepare("INSERT INTO warteliste
(userid, checked, hash, impfenangebot, impfstoff, Patientenart, Impfaufklaerung, WeitereFragen, impfart, impfenmit, letzteimpfung, impfenzeitraum, zeitraum_id, date_created)
VALUES
(:userid, :checked, :hash, 1, :impfstoff, :patientenart, 0, 0, :impfart, '', :letzteimpfung, :impfenzeitraum, :zeitraum_id, NOW())");
$stInsert->execute([
'userid' => $personId,
'checked' => $checkedValue,
'hash' => $hash,
'impfstoff' => $impfstoffId,
'patientenart' => $patientenart,
'impfart' => $impfart,
'letzteimpfung' => $letzteImpfung,
'impfenzeitraum' => (string)$zeitraum['label'],
'zeitraum_id' => $zeitraumId,
]);
$warteid = (int)$pdo->lastInsertId();
$personName = trim((string)$person['vorname'] . ' ' . (string)$person['nachname']);
return [true, 'Wartelistenplatz fuer ' . $personName . ' wurde gespeichert.', $warteid];
}
}
+283
View File
@@ -0,0 +1,283 @@
<?php
require_once(__DIR__ . "/../inc/config.inc.php");
require_once(__DIR__ . "/../inc/functions.inc.php");
require_once(__DIR__ . "/../inc/functions.impfen.inc.php");
ini_set('display_errors', '1');
error_reporting(E_ALL);
$user = check_intern_user();
if (!$user) {
header("Location: login.php");
exit;
}
include(__DIR__ . "/templates/header.inc.php");
echo "</header>";
echo "<div class='jumbotron'><div class='container'>";
echo "<h1>Impfwarteliste</h1>";
echo "<p>Hallo " . e((string)($user['vorname'] ?? '')) . ",<br></p>";
if (!check_mailreg()) {
echo "<br><br>";
echo "Es fehlt die Authentifizierung Ihres Kontos per E-Mail. Bitte authentifizieren Sie Ihre E-Mail-Adresse.<br>";
echo "<form action='authmeldung.php' method='POST'>";
echo "<input name='aktion' type='hidden' value='1'>";
echo "<input type='submit' class='btn btn-primary' value='E-Mail Authentifizierung'><br>";
echo "</form>";
}
if (!check_userdatenvorhanden()) {
echo "<br><br>";
echo "Es fehlen noch Informationen in Ihren Stammdaten. Bitte pflegen Sie die Daten nach.<br>";
echo "<form action='settings.php' method='POST'>";
echo "<input name='aktion' type='hidden' value='1'>";
echo "<input type='submit' class='btn btn-primary' value='Stammdaten pflegen'><br>";
echo "</form>";
}
if (!(check_mailreg() && check_userdatenvorhanden())) {
echo "<br><br><br><form action='index.php' method='POST'>";
echo "<input type='submit' class='btn btn-primary' value='Zum Hauptmenue'>";
echo "</form>";
echo "</div></div>";
include(__DIR__ . "/templates/footer.inc.php");
exit;
}
try {
impfWorkflowEnsureTables($pdo);
$internUserId = isset($_SESSION['userid']) ? (int)$_SESSION['userid'] : null;
$personId = ensurePersonFromInternUsersByEmail($pdo, (string)($user['email'] ?? ''), $internUserId);
} catch (Throwable $t) {
echo "<div class='alert alert-danger'>Fehler: " . e($t->getMessage()) . "</div>";
echo "</div></div>";
include(__DIR__ . "/templates/footer.inc.php");
exit;
}
$options = impfGetWartelistenFormOptions($pdo);
$verfuegbareImpfstoffe = $options['impfstoffe'];
$zeitOptionenByImpfstoff = $options['zeitfenster'];
$zeitOptionenJson = json_encode($zeitOptionenByImpfstoff, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$form = [
'impfstoff_id' => (int)($_POST['impfstoff_id'] ?? 0),
'zeitraum_id' => (int)($_POST['zeitraum_id'] ?? 0),
'impfart' => (int)($_POST['impfart'] ?? 0),
'letzteimpfung' => trim((string)($_POST['letzteimpfung'] ?? '')),
];
$successMessage = '';
$errorMessage = '';
$stActive = $pdo->prepare("SELECT w.warteid, w.checked, w.impfart, w.letzteimpfung, w.impfenzeitraum, i.impfname
FROM warteliste w
LEFT JOIN impfstoff i ON i.impfid = w.impfstoff
WHERE w.userid = :pid
AND w.checked IN (0, 1)
ORDER BY w.date_created DESC, w.warteid DESC");
$stActive->execute(['pid' => $personId]);
$activeWaitRows = $stActive->fetchAll(PDO::FETCH_ASSOC);
if (($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'POST' && (string)($_POST['aktion'] ?? '') === 'create_waitlist') {
try {
[$ok, $message, $warteid] = impfCreateWaitlistEntryForPerson(
$pdo,
$personId,
(int)$form['impfstoff_id'],
(int)$form['zeitraum_id'],
(int)$form['impfart'],
$form['letzteimpfung'] !== '' ? $form['letzteimpfung'] : null,
1
);
if ($ok) {
try {
SendMailMessageVorlage($pdo, "2", (int)$warteid, "9");
$successMessage = $message . ' Eine Bestaetigung wurde per E-Mail versendet.';
} catch (Throwable $mailError) {
$successMessage = $message . ' Der Eintrag wurde gespeichert, aber die E-Mail konnte nicht automatisch versendet werden.';
error_log('Intern waitlist mail failed: ' . $mailError->getMessage());
}
$stActive->execute(['pid' => $personId]);
$activeWaitRows = $stActive->fetchAll(PDO::FETCH_ASSOC);
} else {
$errorMessage = (string)$message;
}
} catch (Throwable $t) {
$errorMessage = 'Der Wartelisteneintrag konnte nicht gespeichert werden: ' . $t->getMessage();
}
}
echo "<p>Hier koennen Sie sich direkt aus dem internen Bereich in die Impfwarteliste eintragen lassen. Ihre Stammdaten werden aus Ihrem Kundenkonto uebernommen.</p>";
echo "<h4>Ihre hinterlegten Stammdaten</h4>";
echo "Name: " . e(trim((string)($user['vorname'] ?? '') . ' ' . (string)($user['nachname'] ?? ''))) . "<br>";
echo "Geburtstag: " . e((string)($user['geburtstag'] ?? '')) . "<br>";
echo "Adresse: " . e(trim((string)($user['strasse'] ?? '') . ', ' . (string)($user['plz'] ?? '') . ', ' . (string)($user['ort'] ?? ''))) . "<br>";
echo "E-Mail: " . e((string)($user['email'] ?? '')) . "<br><br>";
if ($successMessage !== '') {
echo "<div class='alert alert-success'>" . e($successMessage) . "</div>";
}
if ($errorMessage !== '') {
echo "<div class='alert alert-danger'>" . e($errorMessage) . "</div>";
}
if (!empty($activeWaitRows)) {
$impfartLabels = [
1 => 'Erstimpfung',
2 => 'Zweitimpfung',
3 => 'Drittimpfung',
4 => 'Viertimpfung',
];
echo "<div class='alert alert-info'>";
echo "<strong>Ihre aktiven Wartelisteneintraege.</strong><br>";
echo "Sie koennen im internen Bereich mehrere verschiedene Wartelistenanfragen anlegen. Exakte Duplikate werden weiterhin geblockt.";
echo "</div>";
echo "<table class='table table-bordered table-striped'>";
echo "<thead><tr><th>Impfstoff</th><th>Zeitraum</th><th>Impfungsart</th><th>Status</th><th>Letzte Impfung</th></tr></thead><tbody>";
foreach ($activeWaitRows as $activeWaitRow) {
$statusText = ((int)$activeWaitRow['checked'] === 1) ? 'Bestaetigt' : 'Unbestaetigt';
echo "<tr>";
echo "<td>" . e((string)($activeWaitRow['impfname'] ?? 'Unbekannt')) . "</td>";
echo "<td>" . e((string)($activeWaitRow['impfenzeitraum'] ?? '')) . "</td>";
echo "<td>" . e((string)($impfartLabels[(int)$activeWaitRow['impfart']] ?? ('Status ' . (int)$activeWaitRow['impfart']))) . "</td>";
echo "<td>" . e($statusText) . "</td>";
echo "<td>" . e((string)($activeWaitRow['letzteimpfung'] ?? '')) . "</td>";
echo "</tr>";
}
echo "</tbody></table>";
}
if (empty($verfuegbareImpfstoffe)) {
echo "<div class='alert alert-warning'>Aktuell stehen im internen Bereich keine konfigurierten Impfstoffe mit Zeitfenstern zur Verfuegung.</div>";
} else {
?>
<form action="<?php echo e($_SERVER['PHP_SELF']); ?>" method="POST">
<input type="hidden" name="aktion" value="create_waitlist">
<div class="row">
<div class="col-sm-8">
<label for="impfstoff_id">Ich moechte mich mit folgendem Impfstoff auf die Warteliste setzen lassen:</label>
<select class="form-control" name="impfstoff_id" id="impfstoff_id" required onchange="updateZeitfenster()">
<option value="">- Bitte auswaehlen -</option>
<?php foreach ($verfuegbareImpfstoffe as $impfstoffId => $impfstoffName): ?>
<option value="<?php echo (int)$impfstoffId; ?>" <?php echo ($form['impfstoff_id'] === (int)$impfstoffId) ? 'selected' : ''; ?>>
<?php echo e((string)$impfstoffName); ?>
</option>
<?php endforeach; ?>
</select>
</div>
</div>
<div class="row" style="margin-top:12px;">
<div class="col-sm-10">
<label for="zeitraum_id">Welcher Zeitbereich passt fuer Sie?</label>
<select class="form-control" name="zeitraum_id" id="zeitraum_id" required disabled>
<option value="">- Bitte zuerst Impfstoff auswaehlen -</option>
</select>
</div>
</div>
<div class="row" style="margin-top:12px;">
<div class="col-sm-6">
<label for="impfart">Welche Impfungsart benoetigen Sie?</label>
<select class="form-control" name="impfart" id="impfart" required onchange="checkLetzteImpfung()">
<option value="">- Bitte auswaehlen -</option>
<option value="1" <?php echo ($form['impfart'] === 1) ? 'selected' : ''; ?>>Erstimpfung</option>
<option value="2" <?php echo ($form['impfart'] === 2) ? 'selected' : ''; ?>>Zweitimpfung</option>
<option value="3" <?php echo ($form['impfart'] === 3) ? 'selected' : ''; ?>>Drittimpfung</option>
<option value="4" <?php echo ($form['impfart'] === 4) ? 'selected' : ''; ?>>Viertimpfung</option>
</select>
</div>
</div>
<div class="row" id="letzteimpfung_box" style="margin-top:12px; display:none;">
<div class="col-sm-6">
<label for="letzteimpfung">Wann war Ihre letzte Impfung?</label>
<input class="form-control" type="date" name="letzteimpfung" id="letzteimpfung" value="<?php echo e($form['letzteimpfung']); ?>">
</div>
</div>
<div class="row" style="margin-top:18px;">
<div class="col-sm-12">
<button class="btn btn-primary" type="submit" id="submit_waitlist">In Warteliste eintragen</button>
<a class="btn btn-default" href="index.php">Zum Hauptmenue</a>
</div>
</div>
</form>
<script>
const zeitfensterByImpfstoff = <?php echo $zeitOptionenJson ?: '{}'; ?>;
const initialZeitraumId = <?php echo (int)$form['zeitraum_id']; ?>;
function updateZeitfenster() {
const impfstoff = document.getElementById('impfstoff_id');
const zeitraum = document.getElementById('zeitraum_id');
const submit = document.getElementById('submit_waitlist');
const impfstoffId = impfstoff.value;
const optionen = zeitfensterByImpfstoff[impfstoffId] || [];
zeitraum.innerHTML = '';
if (!impfstoffId || optionen.length === 0) {
const opt = document.createElement('option');
opt.value = '';
opt.textContent = '- Kein Zeitfenster verfuegbar -';
zeitraum.appendChild(opt);
zeitraum.disabled = true;
submit.disabled = true;
return;
}
const optDefault = document.createElement('option');
optDefault.value = '';
optDefault.textContent = '- Bitte auswaehlen -';
zeitraum.appendChild(optDefault);
optionen.forEach((eintrag) => {
const opt = document.createElement('option');
opt.value = String(eintrag.id || '');
opt.textContent = eintrag.label || '';
if (initialZeitraumId > 0 && Number(eintrag.id) === initialZeitraumId) {
opt.selected = true;
}
zeitraum.appendChild(opt);
});
zeitraum.disabled = false;
submit.disabled = false;
}
function checkLetzteImpfung() {
const impfart = document.getElementById('impfart');
const box = document.getElementById('letzteimpfung_box');
const input = document.getElementById('letzteimpfung');
if (impfart.value === '' || impfart.value === '1') {
box.style.display = 'none';
input.required = false;
if (impfart.value === '1') {
input.value = '';
}
} else {
box.style.display = 'block';
input.required = true;
}
}
updateZeitfenster();
checkLetzteImpfung();
</script>
<?php
}
echo "</div></div>";
include(__DIR__ . "/templates/footer.inc.php");
?>
+5 -2
View File
@@ -53,7 +53,10 @@ if( is_checked_in_index() ){
?>
<p>Neue Anfragen können Sie über diesen Button einreichen:<br><br></p>
<p><a class="btn btn-primary btn-lg" href="neueanfrage.php" role="button">Neue Anfragen erstellen</a></p><br><br>
<p>Wenn Sie sich für eine Impfung vormerken lassen möchten, können Sie sich hier direkt in die Warteliste eintragen:<br><br></p>
<p><a class="btn btn-primary btn-lg" href="impfwarteliste.php" role="button">Zur Impfwarteliste</a></p><br><br>
<p>Hier können Sie Ihre Anfragen einsehen. Die Antwort erhalten Sie per E-Mail.<br><br></p>
<p><a class="btn btn-primary btn-lg" href="meineanfragen.php" role="button">Meine Anfragen einsehen</a></p><br><br>
@@ -120,4 +123,4 @@ if( is_checked_in_index() ){
<?php
include("templates/footer.inc.php")
?>
?>
+2 -1
View File
@@ -68,6 +68,7 @@
<ul class="nav col-12 col-lg-auto me-lg-auto mb-2 justify-content-center mb-md-0">
<li ><a href="meineanfragen.php" class="nav-link px-2 text-white">Meine Anfragen</a></li>
<li ><a href="neueanfrage.php" class="nav-link px-2 text-white">Neue Anfrage</a></li>
<li ><a href="impfwarteliste.php" class="nav-link px-2 text-white">Impfwarteliste</a></li>
<li ><a href="settings.php" class="nav-link px-2 text-white">Einstellungen</a></li>
</ul>
@@ -93,4 +94,4 @@
?>
?>