diff --git a/backups/DoMiLi_Backup_2025-10.pdf b/backups/DoMiLi_Backup_2025-10.pdf index 168d891..fd4761a 100755 Binary files a/backups/DoMiLi_Backup_2025-10.pdf and b/backups/DoMiLi_Backup_2025-10.pdf differ diff --git a/export.php b/export.php index 0b394ca..b5363c8 100755 --- a/export.php +++ b/export.php @@ -36,7 +36,7 @@ $userIds = array_keys($alleBenutzer); $bisDatum = $berichtsMonatEnde->format('Y-m-d'); $stmt = mysqli_prepare($conn, " SELECT m.id AS meeting_id, m.meeting_date, - mt.user_id, mt.wore_color, mt.paid + mt.user_id, mt.wore_color, mt.paid, mt.birthday_pay FROM meetings m LEFT JOIN meeting_teilnehmer mt ON m.id = mt.meeting_id AND mt.attended = 1 WHERE m.meeting_date <= ? AND m.is_completed = 1 @@ -107,12 +107,15 @@ foreach ($berichtsMeetingsMeta as $meta) { $teilgenommen = false; $wore_color = null; $paid_this = false; + $paid_this_birthday = false; // NEU + // Finde Teilnahme-Daten für dieses Meeting foreach ($alleMeetingsMitTeilnehmern as $mt) { if ($mt['meeting_id'] == $mid && $mt['user_id'] == $uid) { $teilgenommen = true; $wore_color = !empty($mt['wore_color']); - $paid_this = !empty($mt['paid']); + $paid_this = ($mt['paid'] == 1); + $paid_this_birthday = ($mt['birthday_pay'] == 1); // Explizit prüfen break; } } @@ -135,14 +138,22 @@ foreach ($berichtsMeetingsMeta as $meta) { $offeneStrafen++; } } - if (!empty($mt['paid'])) { + + // 🔹 NUR normale Zahlungen zählen (birthday_pay = 0) + $is_paid = ($mt['paid'] == 1); + $is_birthday = ($mt['birthday_pay'] == 1); + if ($is_paid && !$is_birthday) { $rechnungenGesamt++; } } + // Name mit Symbolen $userNameAnzeige = htmlspecialchars($username); if ($paid_this) { $userNameAnzeige .= ' '; + if ($paid_this_birthday) { + $userNameAnzeige .= ' (G)'; + } } if (!$teilgenommen) { @@ -196,6 +207,7 @@ $html = ' th, td { border: 1px solid #333; padding: 4pt 6pt; text-align: left; } th { background-color: #f0f0f0; font-weight: bold; } .paid-symbol { color: red; font-weight: bold; } + .birthday-symbol { color: #d63384; font-size: 0.85em; } .color-ok { color: green; } .color-fail { color: red; } .page-break { page-break-after: always; } @@ -260,6 +272,7 @@ $html .= '

Erstellt am: ' . date('d.m.Y') . '

* = "€" hat Restaurant-Rechnung bezahlt

+

(G) = Geburtstagszahlung (zählt nicht zur Rechnungsanzahl)

diff --git a/history.php b/history.php index 0ed034f..dede289 100755 --- a/history.php +++ b/history.php @@ -57,7 +57,8 @@ function get_all_meeting_details($conn) u.username, mt.attended, mt.wore_color, - mt.paid + mt.paid, + mt.birthday_pay FROM meetings m JOIN colors c ON m.color_id = c.id LEFT JOIN meeting_teilnehmer mt ON m.id = mt.meeting_id @@ -91,7 +92,8 @@ function get_all_meeting_details($conn) 'username' => $row['username'], 'attended' => $row['attended'], 'wore_color' => $row['wore_color'], - 'paid' => $row['paid'] + 'paid' => $row['paid'], + 'birthday_pay' => $row['birthday_pay'] ]; } } @@ -215,7 +217,13 @@ include('inc/header.php'); $status_icon = $participant['wore_color'] ? '✅' : '🔴'; $status_text = $participant['wore_color'] ? 'Farbe getragen' : 'Falsche Farbe'; } - $paid_icon = $participant['paid'] ? '💰' : ''; + $paid_icon = ''; + if ($participant['paid']) { + $paid_icon = '💰'; + if ($participant['birthday_pay']) { + $paid_icon .= ' 🎂'; + } + } ?> diff --git a/kasse.php b/kasse.php index b0e536e..eb5dc9a 100755 --- a/kasse.php +++ b/kasse.php @@ -263,10 +263,13 @@ $sql_paid = " FROM meeting_teilnehmer mt JOIN meetings m ON mt.meeting_id = m.id JOIN users u ON mt.user_id = u.id - WHERE mt.paid = 1 AND m.is_completed = 1 + WHERE mt.paid = 1 + AND mt.birthday_pay = 0 + AND m.is_completed = 1 GROUP BY u.username ORDER BY paid_count ASC, u.username ASC "; + $result_paid = mysqli_query($conn, $sql_paid); while ($row = mysqli_fetch_assoc($result_paid)) { $paid_stats[] = $row; diff --git a/participant.php b/participant.php index 499e425..bdb6ad5 100755 --- a/participant.php +++ b/participant.php @@ -38,7 +38,7 @@ while ($row = mysqli_fetch_assoc($users_result)) { } $existing_feedback = []; -$stmt = mysqli_prepare($conn, "SELECT user_id, attended, wore_color, paid FROM meeting_teilnehmer WHERE meeting_id = ?"); +$stmt = mysqli_prepare($conn, "SELECT user_id, attended, wore_color, paid, birthday_pay FROM meeting_teilnehmer WHERE meeting_id = ?"); mysqli_stmt_bind_param($stmt, "i", $meeting_id); mysqli_stmt_execute($stmt); $result = mysqli_stmt_get_result($stmt); @@ -72,68 +72,75 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") { // Neue Daten speichern if (isset($_POST['user_id']) && is_array($_POST['user_id'])) { - $stmt_insert = mysqli_prepare($conn, "INSERT INTO meeting_teilnehmer (meeting_id, user_id, attended, wore_color, paid) VALUES (?, ?, ?, ?, ?)"); + $stmt_insert = mysqli_prepare($conn, " + INSERT INTO meeting_teilnehmer + (meeting_id, user_id, attended, wore_color, paid, birthday_pay) + VALUES (?, ?, ?, ?, ?, ?) + "); + + $meeting_year = (int)date('Y', strtotime($meeting['meeting_date'])); + $meeting_month = (int)date('n', strtotime($meeting['meeting_date'])); + $meeting_day = (int)date('j', strtotime($meeting['meeting_date'])); foreach ($_POST['user_id'] as $user_id) { $user_id = intval($user_id); $attended = isset($_POST['attended'][$user_id]) ? 1 : 0; $wore_color = isset($_POST['wore_color'][$user_id]) ? 1 : 0; $paid = isset($_POST['paid'][$user_id]) ? 1 : 0; + $birthday_pay = 0; - mysqli_stmt_bind_param($stmt_insert, "iiiii", $meeting_id, $user_id, $attended, $wore_color, $paid); + if ($paid) { + // Hole Geburtstag des Users + $user_stmt = mysqli_prepare($conn, "SELECT birthday FROM users WHERE id = ?"); + mysqli_stmt_bind_param($user_stmt, "i", $user_id); + mysqli_stmt_execute($user_stmt); + $user_row = mysqli_fetch_assoc(mysqli_stmt_get_result($user_stmt)); + mysqli_stmt_close($user_stmt); + + if ($user_row && $user_row['birthday'] && $user_row['birthday'] !== '0000-00-00') { + $bday_month = (int)date('n', strtotime($user_row['birthday'])); + $bday_day = (int)date('j', strtotime($user_row['birthday'])); + + // War Geburtstag in diesem Jahr bereits? + $birthday_passed = ($bday_month < $meeting_month || + ($bday_month == $meeting_month && $bday_day <= $meeting_day)); + + if ($birthday_passed) { + // Prüfen: Hat er in DIESEM JAHR schon als Geburtstagszahler gezahlt? + $check_stmt = mysqli_prepare($conn, " + SELECT 1 FROM meeting_teilnehmer mt + JOIN meetings m ON mt.meeting_id = m.id + WHERE mt.user_id = ? + AND mt.birthday_pay = 1 + AND YEAR(m.meeting_date) = ? + LIMIT 1 + "); + mysqli_stmt_bind_param($check_stmt, "ii", $user_id, $meeting_year); + mysqli_stmt_execute($check_stmt); + $already_birthday_paid = mysqli_num_rows(mysqli_stmt_get_result($check_stmt)) > 0; + mysqli_stmt_close($check_stmt); + + if (!$already_birthday_paid) { + $birthday_pay = 1; + } + } + } + } + + mysqli_stmt_bind_param( + $stmt_insert, + "iiiiii", + $meeting_id, + $user_id, + $attended, + $wore_color, + $paid, + $birthday_pay + ); mysqli_stmt_execute($stmt_insert); } mysqli_stmt_close($stmt_insert); - // 🔹 GEBURTSTAGS-ZAHLUNG BEHANDELN – MIT last_birthday_year - $meeting_year = (int)date('Y', strtotime($meeting['meeting_date'])); - $meeting_month = (int)date('n', strtotime($meeting['meeting_date'])); - $meeting_day = (int)date('j', strtotime($meeting['meeting_date'])); - - foreach ($_POST['user_id'] as $user_id) { - $user_id = (int)$user_id; - $paid = isset($_POST['paid'][$user_id]) && $_POST['paid'][$user_id] == 1; - - if (!$paid) continue; - - $user_stmt = mysqli_prepare($conn, "SELECT birthday, last_birthday_year FROM users WHERE id = ?"); - mysqli_stmt_bind_param($user_stmt, "i", $user_id); - mysqli_stmt_execute($user_stmt); - $user_row = mysqli_fetch_assoc(mysqli_stmt_get_result($user_stmt)); - mysqli_stmt_close($user_stmt); - - if (!$user_row || !$user_row['birthday'] || $user_row['birthday'] === '0000-00-00') { - // Kein Geburtstag → normale Zahlung - $update = mysqli_prepare($conn, "UPDATE users SET regular_paid_count = regular_paid_count + 1 WHERE id = ?"); - mysqli_stmt_bind_param($update, "i", $user_id); - mysqli_stmt_execute($update); - mysqli_stmt_close($update); - continue; - } - - $last_bday_year = (int)($user_row['last_birthday_year'] ?? 0); - $bday_month = (int)date('n', strtotime($user_row['birthday'])); - $bday_day = (int)date('j', strtotime($user_row['birthday'])); - - $birthday_passed = ($bday_month < $meeting_month || - ($bday_month == $meeting_month && $bday_day <= $meeting_day)); - $birthday_not_paid_yet = ($last_bday_year < $meeting_year); - - if ($birthday_passed && $birthday_not_paid_yet) { - // 🎂 Geburtstags-Zahlung - $update = mysqli_prepare($conn, "UPDATE users SET last_birthday_year = ? WHERE id = ?"); - mysqli_stmt_bind_param($update, "ii", $meeting_year, $user_id); - mysqli_stmt_execute($update); - mysqli_stmt_close($update); - } else { - // 🔢 Normale Zahlung - $update = mysqli_prepare($conn, "UPDATE users SET regular_paid_count = regular_paid_count + 1 WHERE id = ?"); - mysqli_stmt_bind_param($update, "i", $user_id); - mysqli_stmt_execute($update); - mysqli_stmt_close($update); - } - } - // Meeting abschließen (nur im Index-Modus) if ($source_page === 'index') { $stmt_complete = mysqli_prepare($conn, "UPDATE meetings SET is_completed = 1 WHERE id = ?"); diff --git a/zahler.php b/zahler.php index a919003..d89269b 100755 --- a/zahler.php +++ b/zahler.php @@ -23,14 +23,12 @@ function get_next_payer_info($conn, $meeting_id) $meeting_month = (int)date('n', $meeting_ts); $meeting_day = (int)date('j', $meeting_ts); - // Alle ZUSAGENDEN Teilnehmer – mit last_birthday_year + // Alle ZUSAGENDEN Teilnehmer $sql = " SELECT u.id, u.username, - u.birthday, - u.last_birthday_year, - u.regular_paid_count + u.birthday FROM meeting_teilnehmer mt JOIN users u ON mt.user_id = u.id WHERE mt.meeting_id = ? AND mt.rsvp_status = 'accepted' @@ -47,23 +45,36 @@ function get_next_payer_info($conn, $meeting_id) return null; } - // 🔹 Geburtstagskandidaten: Geburtstag war + noch nicht in diesem Jahr gezahlt - $birthday_candidates = array_filter($candidates, function ($c) use ($meeting_year, $meeting_month, $meeting_day) { + // 🔹 Geburtstagskandidaten: Geburtstag war + noch nicht in diesem Jahr als Geburtstagszahler gezahlt + $birthday_candidates = array_filter($candidates, function ($c) use ($conn, $meeting_year, $meeting_month, $meeting_day) { if (!$c['birthday'] || $c['birthday'] === '0000-00-00') { return false; } - $last_bday_year = (int)($c['last_birthday_year'] ?? 0); - if ($last_bday_year >= $meeting_year) { - return false; // Bereits in diesem Jahr gezahlt - } - $bday_month = (int)date('n', strtotime($c['birthday'])); $bday_day = (int)date('j', strtotime($c['birthday'])); - // War der Geburtstag in diesem Jahr bereits? - return ($bday_month < $meeting_month || - ($bday_month == $meeting_month && $bday_day <= $meeting_day)); + // War Geburtstag in diesem Jahr bereits? + if (!($bday_month < $meeting_month || + ($bday_month == $meeting_month && $bday_day <= $meeting_day))) { + return false; + } + + // Hat er in diesem Jahr schon als Geburtstagszahler gezahlt? + $check_stmt = mysqli_prepare($conn, " + SELECT 1 FROM meeting_teilnehmer mt + JOIN meetings m ON mt.meeting_id = m.id + WHERE mt.user_id = ? + AND mt.birthday_pay = 1 + AND YEAR(m.meeting_date) = ? + LIMIT 1 + "); + mysqli_stmt_bind_param($check_stmt, "ii", $c['id'], $meeting_year); + mysqli_stmt_execute($check_stmt); + $already_paid = mysqli_num_rows(mysqli_stmt_get_result($check_stmt)) > 0; + mysqli_stmt_close($check_stmt); + + return !$already_paid; }); if (!empty($birthday_candidates)) { @@ -75,11 +86,28 @@ function get_next_payer_info($conn, $meeting_id) ]; } - // 🔹 Normale Rotation - $min_paid = min(array_column($candidates, 'regular_paid_count')); - $regular_candidates = array_filter($candidates, fn($c) => $c['regular_paid_count'] == $min_paid); + // 🔹 Normale Rotation: Zähle paid=1 UND birthday_pay=0 + $user_paid_counts = []; + foreach ($candidates as $c) { + $count_stmt = mysqli_prepare($conn, " + SELECT COUNT(*) + FROM meeting_teilnehmer + WHERE user_id = ? AND paid = 1 AND birthday_pay = 0 + "); + mysqli_stmt_bind_param($count_stmt, "i", $c['id']); + mysqli_stmt_execute($count_stmt); + $count = (int)mysqli_fetch_row(mysqli_stmt_get_result($count_stmt))[0]; + mysqli_stmt_close($count_stmt); + $user_paid_counts[$c['id']] = $count; + } + + $min_paid = min($user_paid_counts); + $regular_candidates = array_filter($candidates, function ($c) use ($user_paid_counts, $min_paid) { + return $user_paid_counts[$c['id']] === $min_paid; + }); + usort($regular_candidates, fn($a, $b) => strcmp($a['username'], $b['username'])); - $first = $regular_candidates[0]; + $first = reset($regular_candidates); return [ 'username' => $first['username'], 'is_birthday_payer' => false