Files
praxis-creutzburg-web/admin/impfworkflow.php
T
2026-03-23 17:14:09 +01:00

1184 lines
56 KiB
PHP

<?php
session_start();
require_once __DIR__ . "/../inc/config.inc.php";
require_once __DIR__ . "/../inc/functions.inc.php";
require_once __DIR__ . "/../inc/functions.impfen.inc.php";
require_once __DIR__ . "/../inc/impfworkflow_notifications.inc.php";
$user = check_admin_user();
include __DIR__ . "/templates/header.inc.php";
if (!$user) {
echo "<div class='container main-container'><h3>Erst anmelden: <a href='login.php'>Login</a></h3></div>";
include __DIR__ . "/templates/footer.inc.php";
exit;
}
if (!check_worker()) {
echo "<div class='container main-container'>Dieser Bereich ist nur für Bearbeiter freigeschaltet.</div>";
include __DIR__ . "/templates/footer.inc.php";
exit;
}
function esc($v): string
{
return htmlspecialchars((string)$v, ENT_QUOTES, 'UTF-8');
}
function weekdayName(int $d): string
{
$map = [1 => 'Montag', 2 => 'Dienstag', 3 => 'Mittwoch', 4 => 'Donnerstag', 5 => 'Freitag', 6 => 'Samstag', 7 => 'Sonntag'];
return $map[$d] ?? 'Unbekannt';
}
function workflowImpfartName(int $impfart): string
{
$map = [
1 => 'Erstimpfung',
2 => 'Zweitimpfung',
3 => 'Drittimpfung',
4 => 'Viertimpfung',
];
return $map[$impfart] ?? 'Unbekannt';
}
function workflowWarteStatus(int $checked): string
{
if ($checked === 1) {
return 'Bestätigt';
}
if ($checked === 0) {
return 'Unbestätigt';
}
return 'Status ' . $checked;
}
function workflowTerminStatus(int $checked, int $behandelt): string
{
if ($behandelt === 1) {
return 'Behandelt';
}
$map = [
0 => 'Anfrage offen',
1 => 'Bestätigt',
2 => 'Abgesagt',
3 => 'Vom Praxisteam abgesagt',
4 => 'Systemstorno',
];
return $map[$checked] ?? ('Status ' . $checked);
}
function workflowPlanLabel(array $plan): string
{
return impfZeitraumLabel($plan);
}
function ensureWorkflowTables(PDO $pdo): void
{
impfWorkflowEnsureTables($pdo);
}
function workflowDeleteWaitlistEntry(PDO $pdo, int $warteid): void
{
if ($warteid <= 0) {
return;
}
$stDeleteMap = $pdo->prepare("DELETE FROM warteliste_zeitraum WHERE warteid = :wid");
$stDeleteMap->execute(['wid' => $warteid]);
$stDelete = $pdo->prepare("DELETE FROM warteliste WHERE warteid = :wid");
$stDelete->execute(['wid' => $warteid]);
}
function workflowCountWaitersForPlan(PDO $pdo, int $impfstoffId, int $planId): int
{
return impfWorkflowNotificationCountWaitersForPlan($pdo, $impfstoffId, $planId);
}
function workflowLoadWaitRowsForPlan(PDO $pdo, int $impfstoffId, int $planId): array
{
$stW = $pdo->prepare("SELECT w.warteid, w.userid, w.hash, w.impfart, w.Impfaufklaerung, w.WeitereFragen, w.letzteimpfung, w.date_created
FROM warteliste w
WHERE w.checked = 1
AND (w.impfstoff = :iid OR w.impfstoff = 0)
AND (
EXISTS (
SELECT 1
FROM warteliste_zeitraum wz
WHERE wz.warteid = w.warteid
AND wz.zeitraum_id = :zid
)
OR (
NOT EXISTS (
SELECT 1
FROM warteliste_zeitraum wz_none
WHERE wz_none.warteid = w.warteid
)
AND (w.zeitraum_id = :zid OR w.zeitraum_id IS NULL)
)
)
ORDER BY w.date_created ASC, w.warteid ASC");
$stW->execute([
'iid' => $impfstoffId,
'zid' => $planId,
]);
return $stW->fetchAll(PDO::FETCH_ASSOC);
}
function workflowAddWartelisteEntry(
PDO $pdo,
int $personId,
int $impfstoffId,
$planIds,
int $impfart,
?string $letzteImpfung,
int $checked
): array {
if ($personId <= 0) {
return [false, "Bitte eine Person auswaehlen."];
}
$impfstoffId = max(0, $impfstoffId);
$planIds = impfNormalizeZeitraumIds($planIds);
$impfart = ($impfart >= 1 && $impfart <= 4) ? $impfart : 1;
if ($impfart > 1 && !$letzteImpfung) {
$impfart = 1;
}
$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, "Ausgewaehlte Person wurde nicht gefunden."];
}
$impfstoffValue = $impfstoffId;
$impfstoffName = 'ohne Vorgabe';
$zeitraum = 'Flexibel';
if (!empty($planIds)) {
$zeitraumLabels = [];
foreach ($planIds as $planId) {
$plan = impfLoadZeitraumById($pdo, $planId, true);
if (!$plan) {
return [false, "Mindestens ein ausgewaehltes Zeitfenster ist nicht mehr verfuegbar."];
}
$zugeordneteImpfstoffe = $plan['impfstoff_id_list'] ?? [];
if ($impfstoffId > 0 && !in_array($impfstoffId, $zugeordneteImpfstoffe, true)) {
return [false, "Impfstoff und Zeitfenster passen nicht zusammen."];
}
if ($impfstoffId <= 0) {
if (count($zugeordneteImpfstoffe) !== 1) {
return [false, "Bitte einen Impfstoff auswaehlen, der allen Zeitfenstern eindeutig zugeordnet ist."];
}
$currentImpfstoffId = (int)$zugeordneteImpfstoffe[0];
if ($impfstoffValue > 0 && $impfstoffValue !== $currentImpfstoffId) {
return [false, "Die ausgewaehlten Zeitfenster gehoeren zu unterschiedlichen Impfstoffen."];
}
$impfstoffValue = $currentImpfstoffId;
}
$zeitraumLabels[] = workflowPlanLabel($plan);
}
if ($impfstoffId <= 0) {
$impfstoffId = $impfstoffValue;
}
}
if ($impfstoffId > 0) {
$stImpfstoff = $pdo->prepare("SELECT impfid, impfname
FROM impfstoff
WHERE impfid = :iid
LIMIT 1");
$stImpfstoff->execute(['iid' => $impfstoffId]);
$impfstoff = $stImpfstoff->fetch(PDO::FETCH_ASSOC);
if (!$impfstoff) {
return [false, "Der ausgewaehlte Impfstoff wurde nicht gefunden."];
}
$impfstoffName = (string)$impfstoff['impfname'];
} else {
$impfstoffValue = 0;
}
if (!empty($planIds)) {
$zeitraum = implode(' | ', $zeitraumLabels);
}
$stDup = $pdo->prepare("SELECT warteid
FROM warteliste
WHERE userid = :uid
AND checked IN (0, 1)
LIMIT 1");
$stDup->execute(['uid' => $personId]);
if ($stDup->fetch()) {
return [false, "Die Person ist bereits auf der aktiven Warteliste eingetragen."];
}
$patientenart = ((int)($person['patientenart'] ?? 0) === 1) ? 1 : 0;
$hash = md5('admin-warte-' . $personId . '-' . microtime(true) . '-' . random_int(1000, 9999));
$checkedValue = ($checked === 0) ? 0 : 1;
$letzteValue = ($impfart === 1) ? null : ($letzteImpfung ?: null);
$zeitraumIdValue = !empty($planIds) ? (int)$planIds[0] : null;
$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' => $impfstoffValue,
'patientenart' => $patientenart,
'impfart' => $impfart,
'letzteimpfung' => $letzteValue,
'impfenzeitraum' => $zeitraum,
'zeitraum_id' => $zeitraumIdValue,
]);
$warteid = (int)$pdo->lastInsertId();
if (!empty($planIds)) {
impfSetWartelistenZeitraeume($pdo, $warteid, $planIds);
}
$personName = trim((string)$person['vorname'] . ' ' . (string)$person['nachname']);
return [true, "Wartelistenplatz fuer {$personName} ({$impfstoffName}) gespeichert."];
}
$message = "";
$error = "";
$personSearch = trim((string)($_POST['person_search'] ?? ''));
$view = (string)($_GET['view'] ?? 'event-create');
$validViews = ['warteliste', 'teilnehmer', 'event-create', 'event-teilnehmer'];
if (!in_array($view, $validViews, true)) {
$view = 'event-create';
}
try {
ensureWorkflowTables($pdo);
} catch (Throwable $e) {
$error = "Workflow-Tabellen konnten nicht erstellt werden: " . $e->getMessage();
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$aktion = (string)($_POST['aktion'] ?? '');
if ($aktion === 'create_event') {
$impfstoffId = (int)($_POST['impfstoff_id'] ?? 0);
$planId = (int)($_POST['plan_id'] ?? 0);
$datum = trim((string)($_POST['event_date'] ?? ''));
if ($impfstoffId <= 0 || $planId <= 0 || $datum === '') {
$error = "Bitte Impfstoff, Zeitraum und Datum auswählen.";
} else {
$stRule = $pdo->prepare("SELECT dosen_pro_flasche FROM impfstoff_workflow WHERE impfstoff_id = :iid LIMIT 1");
$stRule->execute(['iid' => $impfstoffId]);
$rule = $stRule->fetch(PDO::FETCH_ASSOC);
$plan = impfLoadZeitraumById($pdo, $planId, true);
if (!$rule || !$plan || !in_array($impfstoffId, $plan['impfstoff_id_list'] ?? [], true)) {
$error = "Bitte zuerst Stammdaten und Zeitraeume fuer den Impfstoff pflegen.";
} else {
$dow = (int)date('N', strtotime($datum));
if ($dow !== (int)$plan['wochentag']) {
$error = "Datum passt nicht zum gewaehlten Zeitraum (" . weekdayName((int)$plan['wochentag']) . ").";
} else {
$dosen = (int)$rule['dosen_pro_flasche'];
$wartende = workflowCountWaitersForPlan($pdo, $impfstoffId, $planId);
if ($wartende < $dosen) {
$error = "Nicht genug bestätigte Warteteilnehmer: {$wartende} von {$dosen}.";
} else {
$stDup = $pdo->prepare("SELECT timeid
FROM timeslots
WHERE date = :date
AND start = :start
AND ende = :ende
AND impfortid = :oid
AND impfstoff = :iid
LIMIT 1");
$stDup->execute([
'date' => $datum,
'start' => $plan['start'],
'ende' => $plan['ende'],
'oid' => (int)$plan['impfortid'],
'iid' => $impfstoffId,
]);
if ($stDup->fetch()) {
$error = "Für diesen Impfstoff existiert am gewählten Datum/Zeit bereits ein Event.";
} else {
$terminIds = [];
$mailTemplateId = ($impfstoffId === 5) ? '39' : '6';
try {
$pdo->beginTransaction();
$stInsSlot = $pdo->prepare("INSERT INTO timeslots (date, start, ende, aktiv, impfdosen, impfstoff, terminart, impfortid)
VALUES (:date, :start, :ende, 1, :dosen, :iid, 1, :oid)");
$stInsSlot->execute([
'date' => $datum,
'start' => $plan['start'],
'ende' => $plan['ende'],
'dosen' => $dosen,
'iid' => $impfstoffId,
'oid' => (int)$plan['impfortid'],
]);
$timeid = (int)$pdo->lastInsertId();
$warteRowsRaw = workflowLoadWaitRowsForPlan($pdo, $impfstoffId, $planId);
$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 waehrend Verarbeitung zu klein geworden.");
}
$stInsTermin = $pdo->prepare("INSERT INTO impftermin
(userid, timeid, hash, checked, behandelt, coronafragen, astraok, impfart, Impfaufklaerung, WeitereFragen, letzteimpfung)
VALUES
(:userid, :timeid, :hash, 0, 0, 0, 1, :impfart, :aufkl, :fragen, :letzte)");
$stDelW = $pdo->prepare("DELETE FROM warteliste WHERE warteid = :wid");
foreach ($warteRows as $w) {
$terminHash = md5((string)$w['hash'] . microtime(true) . random_int(100, 999));
$stInsTermin->execute([
'userid' => (int)$w['userid'],
'timeid' => $timeid,
'hash' => $terminHash,
'impfart' => max(1, (int)$w['impfart']),
'aufkl' => (int)($w['Impfaufklaerung'] ?? 0),
'fragen' => (int)($w['WeitereFragen'] ?? 0),
'letzte' => $w['letzteimpfung'] ?: null,
]);
$terminIds[] = (int)$pdo->lastInsertId();
$stDelW->execute(['wid' => (int)$w['warteid']]);
$pdo->prepare("DELETE FROM warteliste_zeitraum WHERE warteid = :wid")
->execute(['wid' => (int)$w['warteid']]);
}
$stReduce = $pdo->prepare("UPDATE timeslots SET impfdosen = GREATEST(impfdosen - :cnt, 0) WHERE timeid = :timeid");
$stReduce->execute(['cnt' => count($terminIds), 'timeid' => $timeid]);
$pdo->commit();
foreach ($terminIds as $tid) {
SendMailMessageVorlage($pdo, '1', $tid, $mailTemplateId);
}
$message = count($terminIds) . " Terminanfragen wurden erstellt und versendet.";
} catch (Throwable $e) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
$error = "Fehler beim Erstellen des Impfevents: " . $e->getMessage();
}
}
}
}
}
}
} elseif ($aktion === 'search_person') {
if ($personSearch === '') {
$error = "Bitte einen Suchbegriff für den Patientenstamm eingeben.";
}
} elseif ($aktion === 'add_waitlist_existing') {
$personId = (int)($_POST['wl_person_id'] ?? 0);
$impfstoffId = (int)($_POST['wl_impfstoff_id'] ?? 0);
$planIds = impfNormalizeZeitraumIds($_POST['wl_plan_ids'] ?? ($_POST['wl_plan_id'] ?? []));
[$ok, $msg] = workflowAddWartelisteEntry(
$pdo,
$personId,
$impfstoffId,
$planIds,
1,
null,
1
);
if ($ok) {
$message = $msg;
} else {
$error = $msg;
}
} elseif ($aktion === 'add_waitlist_new') {
$vorname = trim((string)($_POST['new_vorname'] ?? ''));
$nachname = trim((string)($_POST['new_nachname'] ?? ''));
$geburtstag = trim((string)($_POST['new_geburtstag'] ?? ''));
$email = trim((string)($_POST['new_email'] ?? ''));
$tele = trim((string)($_POST['new_tele'] ?? ''));
$plz = trim((string)($_POST['new_plz'] ?? ''));
$ort = trim((string)($_POST['new_ort'] ?? ''));
$strasse = trim((string)($_POST['new_strasse'] ?? ''));
$patientenart = ((int)($_POST['new_patientenart'] ?? 0) === 1) ? 1 : 0;
$impfstoffId = (int)($_POST['new_impfstoff_id'] ?? 0);
$planIds = impfNormalizeZeitraumIds($_POST['new_plan_ids'] ?? ($_POST['new_plan_id'] ?? []));
if ($vorname === '' || $nachname === '' || $geburtstag === '') {
$error = "Für neue Patienten sind Vorname, Nachname und Geburtstag erforderlich.";
} else {
try {
$personId = (int)Userspeichern(
$vorname,
$nachname,
$geburtstag,
$email,
$tele,
$ort,
$plz,
$strasse,
0,
$patientenart,
0
);
[$ok, $msg] = workflowAddWartelisteEntry(
$pdo,
$personId,
$impfstoffId,
$planIds,
1,
null,
1
);
if ($ok) {
$message = $msg;
} else {
$error = $msg;
}
} catch (Throwable $e) {
$error = "Neuer Patient konnte nicht gespeichert werden: " . $e->getMessage();
}
}
} elseif ($aktion === 'cancel_event_participant') {
$terminId = (int)($_POST['terminid'] ?? 0);
if ($terminId <= 0) {
$error = "Ungültiger Termin.";
} else {
try {
$pdo->beginTransaction();
$stTermin = $pdo->prepare("SELECT it.terminid, it.timeid, it.checked, it.behandelt, p.vorname, p.nachname
FROM impftermin it
LEFT JOIN persons p ON p.person_id = it.userid
WHERE it.terminid = :tid
LIMIT 1");
$stTermin->execute(['tid' => $terminId]);
$termin = $stTermin->fetch(PDO::FETCH_ASSOC);
if (!$termin) {
throw new RuntimeException("Termin nicht gefunden.");
}
if ((int)$termin['behandelt'] === 1) {
throw new RuntimeException("Behandelte Termine können nicht abgesagt werden.");
}
$altStatus = (int)$termin['checked'];
if (in_array($altStatus, [2, 3, 4], true)) {
throw new RuntimeException("Der Termin ist bereits abgesagt.");
}
$stUpdate = $pdo->prepare("UPDATE impftermin
SET checked = 3
WHERE terminid = :tid");
$stUpdate->execute(['tid' => $terminId]);
if (in_array($altStatus, [0, 1], true)) {
$stDosen = $pdo->prepare("UPDATE timeslots
SET impfdosen = impfdosen + 1
WHERE timeid = :timeid");
$stDosen->execute(['timeid' => (int)$termin['timeid']]);
}
$pdo->commit();
$personName = trim((string)($termin['vorname'] ?? '') . ' ' . (string)($termin['nachname'] ?? ''));
$message = "Termin für " . trim($personName) . " wurde vom Praxisteam abgesagt.";
} catch (Throwable $e) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
$error = "Termin konnte nicht abgesagt werden: " . $e->getMessage();
}
}
} elseif ($aktion === 'delete_event_participant') {
$terminId = (int)($_POST['terminid'] ?? 0);
if ($terminId <= 0) {
$error = "Ungültiger Termin.";
} else {
try {
$pdo->beginTransaction();
$stTermin = $pdo->prepare("SELECT it.terminid, it.timeid, it.checked, it.behandelt, p.vorname, p.nachname
FROM impftermin it
LEFT JOIN persons p ON p.person_id = it.userid
WHERE it.terminid = :tid
LIMIT 1");
$stTermin->execute(['tid' => $terminId]);
$termin = $stTermin->fetch(PDO::FETCH_ASSOC);
if (!$termin) {
throw new RuntimeException("Termin nicht gefunden.");
}
if ((int)$termin['behandelt'] === 1) {
throw new RuntimeException("Behandelte Termine können nicht gelöscht werden.");
}
$altStatus = (int)$termin['checked'];
if (in_array($altStatus, [0, 1], true)) {
$stDosen = $pdo->prepare("UPDATE timeslots
SET impfdosen = impfdosen + 1
WHERE timeid = :timeid");
$stDosen->execute(['timeid' => (int)$termin['timeid']]);
}
$stDelete = $pdo->prepare("DELETE FROM impftermin WHERE terminid = :tid");
$stDelete->execute(['tid' => $terminId]);
$pdo->commit();
$personName = trim((string)($termin['vorname'] ?? '') . ' ' . (string)($termin['nachname'] ?? ''));
$message = "Termin für " . trim($personName) . " wurde gelöscht.";
} catch (Throwable $e) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
$error = "Termin konnte nicht gelöscht werden: " . $e->getMessage();
}
}
} elseif ($aktion === 'delete_waitlist') {
$warteid = (int)($_POST['warteid'] ?? 0);
if ($warteid <= 0) {
$error = "Ungültiger Wartelisten-Eintrag.";
} else {
$stDelete = $pdo->prepare("SELECT warteid FROM warteliste WHERE warteid = :wid");
$stDelete->execute(['wid' => $warteid]);
$exists = (bool)$stDelete->fetch(PDO::FETCH_ASSOC);
workflowDeleteWaitlistEntry($pdo, $warteid);
if ($exists) {
$message = "Wartelisten-Eintrag wurde gelöscht.";
} else {
$error = "Wartelisten-Eintrag nicht gefunden.";
}
}
}
}
$rules = [];
$plans = [];
$configuredImpfstoffe = [];
$eligible = [];
$personResults = [];
$waitRows = [];
$upcomingRows = [];
$eventOverview = [];
$planWaitCounts = [];
try {
$stRules = $pdo->prepare("SELECT r.impfstoff_id, r.dosen_pro_flasche, i.impfname,
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)
ORDER BY i.impfname");
$stRules->execute();
$rules = $stRules->fetchAll(PDO::FETCH_ASSOC);
$plans = impfGetZeitraumRows($pdo, true);
$planExistsForImpfstoff = [];
foreach ($plans as $p) {
foreach ($p['impfstoff_id_list'] as $impfstoffId) {
$planExistsForImpfstoff[(int)$impfstoffId] = true;
}
}
foreach ($rules as $r) {
$iid = (int)$r['impfstoff_id'];
$dosen = (int)$r['dosen_pro_flasche'];
if ($dosen <= 0 || !isset($planExistsForImpfstoff[$iid])) {
continue;
}
$configuredImpfstoffe[] = $r;
$hasEligiblePlan = false;
foreach ($plans as $plan) {
if (!in_array($iid, $plan['impfstoff_id_list'] ?? [], true)) {
continue;
}
$planId = (int)$plan['zeitraum_id'];
$planWaitCounts[$iid][$planId] = workflowCountWaitersForPlan($pdo, $iid, $planId);
if ($planWaitCounts[$iid][$planId] >= $dosen) {
$hasEligiblePlan = true;
}
}
if ($hasEligiblePlan) {
$eligible[] = $r;
}
}
if ($personSearch !== '') {
$searchLike = '%' . $personSearch . '%';
$searchExactId = ctype_digit($personSearch) ? (int)$personSearch : -1;
$stPersons = $pdo->prepare("SELECT person_id, vorname, nachname, geburtstag, email, tele, ort, plz, strasse
FROM persons
WHERE person_id = :pid
OR vorname LIKE :q
OR nachname LIKE :q
OR email LIKE :q
OR tele LIKE :q
ORDER BY nachname, vorname
LIMIT 30");
$stPersons->execute([
'pid' => $searchExactId,
'q' => $searchLike,
]);
$personResults = $stPersons->fetchAll(PDO::FETCH_ASSOC);
}
$stWait = $pdo->prepare("SELECT w.warteid, w.userid, w.checked, w.impfstoff, w.impfart, w.impfenzeitraum, w.zeitraum_id, w.letzteimpfung, w.date_created,
p.vorname, p.nachname, p.geburtstag, p.email, p.tele,
i.impfname
FROM warteliste w
INNER JOIN persons p ON p.person_id = w.userid
LEFT JOIN impfstoff i ON i.impfid = w.impfstoff
WHERE w.checked IN (0, 1)
ORDER BY w.checked DESC, w.date_created ASC
LIMIT 500");
$stWait->execute();
$waitRows = $stWait->fetchAll(PDO::FETCH_ASSOC);
foreach ($waitRows as &$waitRow) {
$waitRow['zeitraum_labels'] = impfGetWartelistenZeitraeumeLabels($pdo, (int)$waitRow['warteid'], false);
if (!empty($waitRow['zeitraum_labels'])) {
$waitRow['impfenzeitraum'] = implode(' | ', $waitRow['zeitraum_labels']);
}
}
unset($waitRow);
$notificationEvents = impfWorkflowNotificationProcess($pdo);
if (!empty($notificationEvents)) {
$notificationText = count($notificationEvents) . " Impfworkflow-Benachrichtigung(en) wurden versendet.";
$message = ($message === '') ? $notificationText : ($message . ' ' . $notificationText);
}
$stUpcoming = $pdo->prepare("SELECT ts.timeid, ts.date, ts.start, ts.ende, ts.impfdosen,
i.impfname, o.anzeigename, o.adresse,
it.terminid, it.checked, it.behandelt, it.impfart,
p.vorname, p.nachname, p.geburtstag, p.email, p.tele
FROM timeslots ts
INNER JOIN impfstoff i ON i.impfid = ts.impfstoff
LEFT JOIN impfort o ON o.ortid = ts.impfortid
LEFT JOIN impftermin it ON it.timeid = ts.timeid
LEFT JOIN persons p ON p.person_id = it.userid
WHERE ts.date >= :today
AND ts.aktiv = 1
ORDER BY ts.date, ts.start, ts.ende, i.impfname, p.nachname, p.vorname");
$stUpcoming->execute(['today' => date('Y-m-d')]);
$upcomingRows = $stUpcoming->fetchAll(PDO::FETCH_ASSOC);
foreach ($upcomingRows as $row) {
$timeid = (int)$row['timeid'];
if (!isset($eventOverview[$timeid])) {
$eventOverview[$timeid] = [
'timeid' => $timeid,
'date' => $row['date'],
'start' => $row['start'],
'ende' => $row['ende'],
'impfdosen' => (int)$row['impfdosen'],
'impfname' => $row['impfname'],
'anzeigename' => $row['anzeigename'],
'adresse' => $row['adresse'],
'teilnehmer' => [],
];
}
if (!empty($row['terminid'])) {
$eventOverview[$timeid]['teilnehmer'][] = [
'terminid' => (int)$row['terminid'],
'checked' => (int)($row['checked'] ?? 0),
'behandelt' => (int)($row['behandelt'] ?? 0),
'impfart' => (int)($row['impfart'] ?? 1),
'vorname' => (string)($row['vorname'] ?? ''),
'nachname' => (string)($row['nachname'] ?? ''),
'geburtstag' => (string)($row['geburtstag'] ?? ''),
'email' => (string)($row['email'] ?? ''),
'tele' => (string)($row['tele'] ?? ''),
];
}
}
} catch (Throwable $e) {
$error = trim($error . ' Fehler beim Laden der Impfverwaltung: ' . $e->getMessage());
}
?>
<div class="container main-container">
<h2>Impfverwaltung - Impfevent</h2>
<div class="row" style="margin-bottom: 15px;">
<div class="col-sm-12">
<a class="btn <?php echo ($view === 'warteliste') ? 'btn-primary' : 'btn-default'; ?>" href="impfworkflow_warteliste.php">Warteliste</a>
<a class="btn <?php echo ($view === 'teilnehmer') ? 'btn-primary' : 'btn-default'; ?>" href="impfworkflow_teilnehmer.php">Teilnehmer pflegen</a>
<a class="btn <?php echo ($view === 'event-create') ? 'btn-success' : 'btn-default'; ?>" href="impfworkflow_event_create.php">Impfevent erstellen</a>
<a class="btn <?php echo ($view === 'event-teilnehmer') ? 'btn-primary' : 'btn-default'; ?>" href="impfworkflow_event_teilnehmer.php">Geplante Event-Teilnehmer</a>
<a class="btn btn-default" href="impfworkflow_stammdaten.php">Stammdaten</a>
</div>
</div>
<?php if ($message): ?>
<div class="alert alert-success"><?php echo esc($message); ?></div>
<?php endif; ?>
<?php if ($error): ?>
<div class="alert alert-danger"><?php echo esc($error); ?></div>
<?php endif; ?>
<style>
.workflow-action-row {
margin-top: 12px;
text-align: left;
}
</style>
<?php if ($view === 'warteliste'): ?>
<div class="panel panel-default">
<div class="panel-heading"><strong>Aktive Impfwarteliste</strong></div>
<div class="panel-body">
<?php if (empty($waitRows)): ?>
<p>Aktuell gibt es keine aktiven Wartelisten-Einträge.</p>
<?php else: ?>
<table class="table table-bordered table-striped table-condensed">
<thead>
<tr>
<th>Patient</th>
<th>Kontakt</th>
<th>Impfstoff</th>
<th>Zeitraum</th>
<th>Impfungsart</th>
<th>Status</th>
<th>Eingetragen</th>
<th>Aktion</th>
</tr>
</thead>
<tbody>
<?php foreach ($waitRows as $w): ?>
<tr>
<td>
<?php echo esc(trim((string)$w['vorname'] . ' ' . (string)$w['nachname'])); ?><br>
<small><?php echo esc((string)$w['geburtstag']); ?></small>
</td>
<td>
<?php echo esc((string)$w['email']); ?><br>
<?php echo esc((string)$w['tele']); ?>
</td>
<td><?php echo esc((string)($w['impfname'] ?: 'Unbekannt')); ?></td>
<td>
<?php if (!empty($w['zeitraum_labels'])): ?>
<?php echo implode('<br>', array_map('esc', $w['zeitraum_labels'])); ?>
<?php else: ?>
<?php echo esc((string)$w['impfenzeitraum']); ?>
<?php endif; ?>
</td>
<td>
<?php echo esc(workflowImpfartName((int)$w['impfart'])); ?>
<?php if (!empty($w['letzteimpfung'])): ?>
<br><small>Letzte: <?php echo esc((string)$w['letzteimpfung']); ?></small>
<?php endif; ?>
</td>
<td><?php echo esc(workflowWarteStatus((int)$w['checked'])); ?></td>
<td><?php echo esc((string)$w['date_created']); ?></td>
<td>
<form method="post" onsubmit="return confirm('Eintrag wirklich löschen?');">
<input type="hidden" name="aktion" value="delete_waitlist">
<input type="hidden" name="warteid" value="<?php echo (int)$w['warteid']; ?>">
<button class="btn btn-danger btn-xs" type="submit">Löschen</button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
<?php if ($view === 'teilnehmer'): ?>
<div class="panel panel-default">
<div class="panel-heading"><strong>Patient aus Kundenstamm zur Warteliste hinzufügen</strong></div>
<div class="panel-body">
<form method="post" class="form-inline" style="margin-bottom:12px;">
<input type="hidden" name="aktion" value="search_person">
<label>Patientensuche</label>
<input class="form-control" type="text" name="person_search" value="<?php echo esc($personSearch); ?>" placeholder="Name, E-Mail, Telefon oder ID">
<div class="workflow-action-row">
<button class="btn btn-default" type="submit">Suchen</button>
</div>
</form>
<?php if ($personSearch !== ''): ?>
<?php if (empty($personResults)): ?>
<div class="alert alert-warning">Keine Person im Kundenstamm gefunden.</div>
<?php else: ?>
<div class="alert alert-info"><?php echo count($personResults); ?> Treffer gefunden. Impfangaben sind optional, neue Eintraege werden automatisch als bestaetigt gespeichert.</div>
<?php endif; ?>
<?php endif; ?>
<form method="post" class="form-inline">
<input type="hidden" name="aktion" value="add_waitlist_existing">
<input type="hidden" name="person_search" value="<?php echo esc($personSearch); ?>">
<label>Patient</label>
<select class="form-control" name="wl_person_id" required <?php echo empty($personResults) ? 'disabled' : ''; ?>>
<option value="">Bitte wählen</option>
<?php foreach ($personResults as $p): ?>
<?php
$pid = (int)$p['person_id'];
$selected = ((int)($_POST['wl_person_id'] ?? 0) === $pid) ? 'selected' : '';
$personLabel = trim((string)$p['nachname'] . ', ' . (string)$p['vorname']) . ' | ' . (string)$p['geburtstag'] . ' | ' . (string)$p['email'];
?>
<option value="<?php echo $pid; ?>" <?php echo $selected; ?>><?php echo esc($personLabel); ?></option>
<?php endforeach; ?>
</select>
<label style="margin-left:10px;">Impfstoff</label>
<select class="form-control" name="wl_impfstoff_id" id="existing_impfstoff">
<option value="0" <?php echo ((int)($_POST['wl_impfstoff_id'] ?? 0) === 0) ? 'selected' : ''; ?>>Keine Vorgabe</option>
<?php foreach ($configuredImpfstoffe as $r): ?>
<?php
$iid = (int)$r['impfstoff_id'];
$selected = ((int)($_POST['wl_impfstoff_id'] ?? 0) === $iid) ? 'selected' : '';
?>
<option value="<?php echo $iid; ?>" <?php echo $selected; ?>>
<?php echo esc($r['impfname'] . ' (Dosen: ' . $r['dosen_pro_flasche'] . ')'); ?>
</option>
<?php endforeach; ?>
</select>
<label style="margin-left:10px;">Zeitfenster</label>
<?php $selectedExistingPlanIds = impfNormalizeZeitraumIds($_POST['wl_plan_ids'] ?? ($_POST['wl_plan_id'] ?? [])); ?>
<select class="form-control" name="wl_plan_ids[]" id="existing_plan" multiple size="6">
<?php foreach ($plans as $p): ?>
<?php
$planId = (int)$p['zeitraum_id'];
$selected = in_array($planId, $selectedExistingPlanIds, true) ? 'selected' : '';
$impfstoffeCsv = implode(',', $p['impfstoff_id_list']);
$impfstoffeText = empty($p['impfstoff_name_list']) ? 'ohne Impfstoff' : implode(', ', $p['impfstoff_name_list']);
?>
<option value="<?php echo $planId; ?>" data-impfstoffe="<?php echo esc($impfstoffeCsv); ?>" <?php echo $selected; ?>>
<?php echo esc(workflowPlanLabel($p) . ' | Impfstoffe: ' . $impfstoffeText); ?>
</option>
<?php endforeach; ?>
</select>
<div class="workflow-action-row">
<button class="btn btn-primary" type="submit" <?php echo empty($personResults) ? 'disabled' : ''; ?>>
Zur Warteliste hinzufügen
</button>
</div>
</form>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading"><strong>Neuen Patienten anlegen und zur Warteliste hinzufügen</strong></div>
<div class="panel-body">
<form method="post">
<input type="hidden" name="aktion" value="add_waitlist_new">
<div class="row">
<div class="col-sm-3">
<label>Vorname</label>
<input class="form-control" type="text" name="new_vorname" value="<?php echo esc((string)($_POST['new_vorname'] ?? '')); ?>" required>
</div>
<div class="col-sm-3">
<label>Nachname</label>
<input class="form-control" type="text" name="new_nachname" value="<?php echo esc((string)($_POST['new_nachname'] ?? '')); ?>" required>
</div>
<div class="col-sm-3">
<label>Geburtstag</label>
<input class="form-control" type="date" name="new_geburtstag" value="<?php echo esc((string)($_POST['new_geburtstag'] ?? '')); ?>" required>
</div>
<div class="col-sm-3">
<label>E-Mail</label>
<input class="form-control" type="email" name="new_email" value="<?php echo esc((string)($_POST['new_email'] ?? '')); ?>">
</div>
</div>
<div class="row" style="margin-top:10px;">
<div class="col-sm-3">
<label>Telefon</label>
<input class="form-control" type="text" name="new_tele" value="<?php echo esc((string)($_POST['new_tele'] ?? '')); ?>">
</div>
<div class="col-sm-2">
<label>PLZ</label>
<input class="form-control" type="text" name="new_plz" value="<?php echo esc((string)($_POST['new_plz'] ?? '')); ?>">
</div>
<div class="col-sm-3">
<label>Ort</label>
<input class="form-control" type="text" name="new_ort" value="<?php echo esc((string)($_POST['new_ort'] ?? '')); ?>">
</div>
<div class="col-sm-4">
<label>Straße</label>
<input class="form-control" type="text" name="new_strasse" value="<?php echo esc((string)($_POST['new_strasse'] ?? '')); ?>">
</div>
</div>
<div class="row" style="margin-top:10px;">
<div class="col-sm-3">
<label>Schon Patient in der Praxis</label>
<select class="form-control" name="new_patientenart" required>
<option value="">Bitte wählen</option>
<option value="1" <?php echo ((string)($_POST['new_patientenart'] ?? '') === '1') ? 'selected' : ''; ?>>Ja</option>
<option value="0" <?php echo ((string)($_POST['new_patientenart'] ?? '') === '0') ? 'selected' : ''; ?>>Nein</option>
</select>
</div>
<div class="col-sm-3">
<label>Impfstoff</label>
<select class="form-control" name="new_impfstoff_id" id="new_impfstoff">
<option value="0" <?php echo ((int)($_POST['new_impfstoff_id'] ?? 0) === 0) ? 'selected' : ''; ?>>Keine Vorgabe</option>
<?php foreach ($configuredImpfstoffe as $r): ?>
<?php
$iid = (int)$r['impfstoff_id'];
$selected = ((int)($_POST['new_impfstoff_id'] ?? 0) === $iid) ? 'selected' : '';
?>
<option value="<?php echo $iid; ?>" <?php echo $selected; ?>>
<?php echo esc($r['impfname'] . ' (Dosen: ' . $r['dosen_pro_flasche'] . ')'); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-sm-3">
<label>Zeitfenster</label>
<?php $selectedNewPlanIds = impfNormalizeZeitraumIds($_POST['new_plan_ids'] ?? ($_POST['new_plan_id'] ?? [])); ?>
<select class="form-control" name="new_plan_ids[]" id="new_plan" multiple size="6">
<?php foreach ($plans as $p): ?>
<?php
$planId = (int)$p['zeitraum_id'];
$selected = in_array($planId, $selectedNewPlanIds, true) ? 'selected' : '';
$impfstoffeCsv = implode(',', $p['impfstoff_id_list']);
$impfstoffeText = empty($p['impfstoff_name_list']) ? 'ohne Impfstoff' : implode(', ', $p['impfstoff_name_list']);
?>
<option value="<?php echo $planId; ?>" data-impfstoffe="<?php echo esc($impfstoffeCsv); ?>" <?php echo $selected; ?>>
<?php echo esc(workflowPlanLabel($p) . ' | Impfstoffe: ' . $impfstoffeText); ?>
</option>
<?php endforeach; ?>
</select>
</div>
</div>
<div class="row" style="margin-top:10px;">
<div class="col-sm-12">
<p class="help-block" style="margin-bottom:0;">
Der Eintrag wird automatisch als bestaetigt gespeichert.
</p>
<div class="workflow-action-row">
<button class="btn btn-primary" type="submit">
Patienten speichern und zur Warteliste hinzufügen
</button>
</div>
</div>
</div>
</form>
</div>
</div>
<?php endif; ?>
<?php if ($view === 'event-create'): ?>
<div class="panel panel-default">
<div class="panel-heading"><strong>Konkretes Impfevent erstellen</strong></div>
<div class="panel-body">
<p>Es werden nur Impfstoffe angeboten, bei denen die bestätigte Warteliste mindestens eine Flasche füllt.</p>
<form method="post" class="form-inline" style="margin-bottom:14px;">
<input type="hidden" name="aktion" value="create_event">
<label>Impfstoff (bereit)</label>
<select class="form-control" name="impfstoff_id" id="event_impfstoff" required>
<option value="">Bitte wählen</option>
<?php foreach ($eligible as $e): ?>
<option value="<?php echo (int)$e['impfstoff_id']; ?>"><?php echo esc($e['impfname'] . ' (Wartende: ' . $e['wartende'] . ', Dosen: ' . $e['dosen_pro_flasche'] . ')'); ?></option>
<?php endforeach; ?>
</select>
<label style="margin-left:10px;">Zeitraum</label>
<select class="form-control" name="plan_id" id="event_plan" required>
<option value="">Bitte wählen</option>
<?php foreach ($plans as $p): ?>
<?php
$impfstoffeCsv = implode(',', $p['impfstoff_id_list']);
$impfstoffeText = empty($p['impfstoff_name_list']) ? 'ohne Impfstoff' : implode(', ', $p['impfstoff_name_list']);
?>
<option value="<?php echo (int)$p['zeitraum_id']; ?>" data-impfstoffe="<?php echo esc($impfstoffeCsv); ?>">
<?php echo esc(workflowPlanLabel($p) . ' | Impfstoffe: ' . $impfstoffeText); ?>
</option>
<?php endforeach; ?>
</select>
<label style="margin-left:10px;">Konkretes Datum</label>
<input class="form-control" type="date" name="event_date" required>
<div class="workflow-action-row">
<button class="btn btn-success" type="submit">Impfevent erstellen</button>
</div>
</form>
</div>
</div>
<?php endif; ?>
<?php if ($view === 'event-teilnehmer'): ?>
<div class="panel panel-default">
<div class="panel-heading"><strong>Teilnehmer geplanter Impfevents</strong></div>
<div class="panel-body">
<?php if (empty($eventOverview)): ?>
<p>Aktuell sind keine zukünftigen Impfevents vorhanden.</p>
<?php else: ?>
<?php foreach ($eventOverview as $event): ?>
<div class="panel panel-info">
<div class="panel-heading">
<strong><?php echo esc((string)$event['impfname']); ?></strong>
| <?php echo esc((string)$event['date']); ?> <?php echo esc(substr((string)$event['start'], 0, 5)); ?>-<?php echo esc(substr((string)$event['ende'], 0, 5)); ?>
| <?php echo esc((string)($event['anzeigename'] ?? '')); ?>
</div>
<div class="panel-body">
<?php if (!empty($event['adresse'])): ?>
<p><small><?php echo esc((string)$event['adresse']); ?></small></p>
<?php endif; ?>
<p><strong>Geplante Dosen:</strong> <?php echo (int)$event['impfdosen']; ?> |
<strong>Teilnehmer:</strong> <?php echo count($event['teilnehmer']); ?></p>
<?php if (empty($event['teilnehmer'])): ?>
<p>Für dieses Event sind noch keine Teilnehmer angelegt.</p>
<?php else: ?>
<table class="table table-bordered table-striped table-condensed">
<thead>
<tr>
<th>Patient</th>
<th>Kontakt</th>
<th>Impfungsart</th>
<th>Status</th>
<th>Aktion</th>
</tr>
</thead>
<tbody>
<?php foreach ($event['teilnehmer'] as $t): ?>
<tr>
<td>
<?php echo esc(trim((string)$t['vorname'] . ' ' . (string)$t['nachname'])); ?><br>
<small><?php echo esc((string)$t['geburtstag']); ?></small>
</td>
<td>
<?php echo esc((string)$t['email']); ?><br>
<?php echo esc((string)$t['tele']); ?>
</td>
<td><?php echo esc(workflowImpfartName((int)$t['impfart'])); ?></td>
<td><?php echo esc(workflowTerminStatus((int)$t['checked'], (int)$t['behandelt'])); ?></td>
<td>
<?php
$terminId = (int)$t['terminid'];
$behandelt = (int)$t['behandelt'];
$checked = (int)$t['checked'];
$kannAbsagen = ($behandelt === 0) && !in_array($checked, [2, 3, 4], true);
$kannLoeschen = ($behandelt === 0);
?>
<form method="post" style="display:inline-block; margin-right:6px;" onsubmit="return confirm('Termin wirklich absagen?');">
<input type="hidden" name="aktion" value="cancel_event_participant">
<input type="hidden" name="terminid" value="<?php echo $terminId; ?>">
<button class="btn btn-warning btn-xs" type="submit" <?php echo $kannAbsagen ? '' : 'disabled'; ?>>Absagen</button>
</form>
<form method="post" style="display:inline-block;" onsubmit="return confirm('Termin wirklich löschen?');">
<input type="hidden" name="aktion" value="delete_event_participant">
<input type="hidden" name="terminid" value="<?php echo $terminId; ?>">
<button class="btn btn-danger btn-xs" type="submit" <?php echo $kannLoeschen ? '' : 'disabled'; ?>>Löschen</button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
<script>
(function () {
function bindPlanFilter(impfstoffId, planId) {
var impfstoff = document.getElementById(impfstoffId);
var plan = document.getElementById(planId);
if (!impfstoff || !plan) {
return;
}
function filterPlans() {
var val = impfstoff.value;
var needsFilter = (val !== "" && val !== "0");
var selectedValues = [];
for (var s = 0; s < plan.options.length; s++) {
if (plan.options[s].selected && plan.options[s].value !== '') {
selectedValues.push(plan.options[s].value);
}
}
var selectedValid = 0;
for (var i = 0; i < plan.options.length; i++) {
var opt = plan.options[i];
var optionImpfstoffe = opt.getAttribute('data-impfstoffe');
if (!optionImpfstoffe) {
opt.hidden = false;
continue;
}
var ids = optionImpfstoffe.split(',');
var visible = !needsFilter;
if (needsFilter) {
for (var j = 0; j < ids.length; j++) {
if (ids[j] === val) {
visible = true;
break;
}
}
}
opt.hidden = !visible;
if (!visible) {
opt.selected = false;
} else if (selectedValues.indexOf(opt.value) !== -1) {
selectedValid++;
}
}
if (selectedValues.length > 0 && selectedValid === 0) {
plan.value = '';
}
}
impfstoff.addEventListener('change', filterPlans);
filterPlans();
}
bindPlanFilter('event_impfstoff', 'event_plan');
bindPlanFilter('existing_impfstoff', 'existing_plan');
bindPlanFilter('new_impfstoff', 'new_plan');
})();
</script>
</div>
<?php include __DIR__ . "/templates/footer.inc.php"; ?>