diff --git a/backups/DoMiLi_Backup_2025-10.pdf b/backups/DoMiLi_Backup_2025-10.pdf new file mode 100755 index 0000000..168d891 Binary files /dev/null and b/backups/DoMiLi_Backup_2025-10.pdf differ diff --git a/export.php b/export.php new file mode 100755 index 0000000..0b394ca --- /dev/null +++ b/export.php @@ -0,0 +1,277 @@ +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 + 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 + ORDER BY m.meeting_date +"); +mysqli_stmt_bind_param($stmt, 's', $bisDatum); +mysqli_stmt_execute($stmt); +$result = mysqli_stmt_get_result($stmt); + +$alleMeetingsMitTeilnehmern = []; +while ($row = mysqli_fetch_assoc($result)) { + $alleMeetingsMitTeilnehmern[] = $row; +} +mysqli_stmt_close($stmt); + +// === 5. Meetings des Berichtsmonats mit Farben === +$stmtMeta = mysqli_prepare($conn, " + SELECT id, meeting_date, reason, color_id + FROM meetings + WHERE meeting_date >= ? AND meeting_date <= ? AND is_completed = 1 + ORDER BY meeting_date +"); +$startDate = $berichtsMonatBeginn->format('Y-m-d'); +$endDate = $berichtsMonatEnde->format('Y-m-d'); +mysqli_stmt_bind_param($stmtMeta, 'ss', $startDate, $endDate); +mysqli_stmt_execute($stmtMeta); +$resultMeta = mysqli_stmt_get_result($stmtMeta); + +$berichtsMeetingsMeta = []; +while ($row = mysqli_fetch_assoc($resultMeta)) { + $color_id = (int)$row['color_id']; + $colorStmt = mysqli_prepare($conn, "SELECT name FROM colors WHERE id = ?"); + mysqli_stmt_bind_param($colorStmt, 'i', $color_id); + mysqli_stmt_execute($colorStmt); + $colorRes = mysqli_stmt_get_result($colorStmt); + $colorName = '–'; + if ($cRow = mysqli_fetch_assoc($colorRes)) { + $colorName = $cRow['name']; + } + mysqli_stmt_close($colorStmt); + + $berichtsMeetingsMeta[] = [ + 'id' => $row['id'], + 'meeting_date' => $row['meeting_date'], + 'reason' => $row['reason'], + 'color_name' => $colorName + ]; +} +mysqli_stmt_close($stmtMeta); + +// === 6. Für jedes Meeting: alle Benutzer mit allen Werten === +$gruppiert = []; + +foreach ($berichtsMeetingsMeta as $meta) { + $mid = $meta['id']; + $meetingDatum = new DateTime($meta['meeting_date']); + + $gruppiert[$mid] = [ + 'datum' => $meta['meeting_date'], + 'reason' => $meta['reason'], + 'color_name' => $meta['color_name'], + 'teilnehmer' => [] + ]; + + foreach ($userIds as $uid) { + $username = $alleBenutzer[$uid]; + + $teilgenommen = false; + $wore_color = null; + $paid_this = false; + + 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']); + break; + } + } + + // Kumulierte Werte bis zu diesem Meeting + $strafenGesamt = 0; + $offeneStrafen = 0; + $teilnahmenGesamt = 0; + $rechnungenGesamt = 0; + + foreach ($alleMeetingsMitTeilnehmern as $mt) { + if ($mt['user_id'] != $uid || is_null($mt['user_id'])) continue; + $mDatum = new DateTime($mt['meeting_date']); + if ($mDatum > $meetingDatum) continue; + + $teilnahmenGesamt++; + if (!$mt['wore_color']) { + $strafenGesamt++; + if ($mt['meeting_date'] >= $last_closing) { + $offeneStrafen++; + } + } + if (!empty($mt['paid'])) { + $rechnungenGesamt++; + } + } + + $userNameAnzeige = htmlspecialchars($username); + if ($paid_this) { + $userNameAnzeige .= ' €'; + } + + if (!$teilgenommen) { + $farbeSymbol = '–'; + } else { + $farbeSymbol = $wore_color ? '✓' : '✗'; + } + + $gruppiert[$mid]['teilnehmer'][] = [ + 'username' => $userNameAnzeige, + 'farbe_symbol' => $farbeSymbol, + 'teilgenommen' => $teilgenommen, + 'offene_strafen' => $offeneStrafen, + 'strafen_gesamt' => $strafenGesamt, + 'teilnahmen_gesamt' => $teilnahmenGesamt, + 'rechnungen_gesamt' => $rechnungenGesamt + ]; + } +} + +// === 7. Gesamt offene Strafen === +$gesamtOffen = 0; +foreach ($alleMeetingsMitTeilnehmern as $mt) { + if (!is_null($mt['user_id']) && !$mt['wore_color'] && $mt['meeting_date'] >= $last_closing) { + $gesamtOffen++; + } +} + +// === 8. PDF-Pfad === +@mkdir(__DIR__ . '/backups', 0755, true); +$baseName = 'DoMiLi_Backup_' . $berichtsMonatBeginn->format('Y-m'); +$counter = 0; +$outputPath = __DIR__ . '/backups/' . $baseName . '.pdf'; +while (file_exists($outputPath)) { + $counter++; + $outputPath = __DIR__ . '/backups/' . $baseName . '_' . $counter . '.pdf'; +} + +// === 9. HTML mit allen Anforderungen === +$html = ' + + +
+ + + + + +Keine abgeschlossenen Meetings im Berichtsmonat.
'; +} else { + $meetingCount = 0; + foreach ($gruppiert as $meeting) { + if ($meetingCount > 0 && $meetingCount % 3 === 0) { + $html .= ''; + } + $meetingCount++; + + $meetingDatum = new DateTime($meeting['datum']); + $html .= '| Benutzer* | +Farbe getragen | +offene Strafen (in €) | +Strafen gesamt | +Teilnahmen gesamt | +Rechnung gesamt | +
|---|---|---|---|---|---|
| ' . $t['username'] . ' | +' . $farbeHtml . ' | +' . $t['offene_strafen'] . ' | +' . $t['strafen_gesamt'] . ' | +' . $t['teilnahmen_gesamt'] . ' | +' . $t['rechnungen_gesamt'] . ' | +
Erstellt am: ' . date('d.m.Y') . '
+* = "€" hat Restaurant-Rechnung bezahlt
+Settings -> Farben sehen.Hallo $recipient_name,
+der Jahresabschluss wurde am $today_formatted durchgeführt.
+Strafen der abgeschlossenen Periode (vom $last_closing_formatted bis $today_formatted):
+| Mitglied | +Anzahl Strafen | +Betrag | +
|---|
Dein DoMiLi-Admin.
"; + + $mail->isHTML(true); + $mail->Subject = "DoMiLi – Jahresabschluss abgeschlossen am $today_formatted"; + $mail->Body = $html_body; + $mail->AltBody = $text_body; + $mail->addAddress($recipient_email); + $mail->send(); + } catch (Exception $e) { + error_log("PHPMailer Fehler: " . $mail->ErrorInfo); + } + } + } + + $close_success = "Jahresabschluss erfolgreich durchgeführt!"; + } else { + $close_error = "Heute wurde bereits ein Abschluss durchgeführt."; + } +} + +// --- DATEN FÜR ANZEIGE --- +$last_closing_date = '2020-01-01'; +$result_last = mysqli_query($conn, "SELECT MAX(closing_date) AS last_date FROM penalty_closings"); +if ($result_last) { + $row = mysqli_fetch_assoc($result_last); + if ($row && !is_null($row['last_date'])) { + $last_closing_date = $row['last_date']; + } +} + +$cumulative_penalties = []; +$stmt_cum = mysqli_prepare($conn, " + SELECT u.username, COUNT(*) AS total_penalty_count + FROM meeting_teilnehmer mt + JOIN meetings m ON mt.meeting_id = m.id + JOIN users u ON mt.user_id = u.id + WHERE mt.attended = 1 AND mt.wore_color = 0 AND m.is_completed = 1 + GROUP BY u.username +"); +mysqli_stmt_execute($stmt_cum); +$result_cum = mysqli_stmt_get_result($stmt_cum); +while ($row = mysqli_fetch_assoc($result_cum)) { + $cumulative_penalties[$row['username']] = (int)$row['total_penalty_count']; +} +mysqli_stmt_close($stmt_cum); + +$open_penalties = []; +$total_open = 0; $penalty_amount = 1; -// SQL-Abfrage, um Strafen pro Benutzer seit dem letzten Reset-Datum zu zählen -// Strafe = anwesend (attended=1) aber Farbe nicht getragen (wore_color=0) -// NEUE BEDINGUNG: Nur Meetings einbeziehen, die als abgeschlossen markiert sind. -$sql_penalties = " - SELECT - u.username, - COUNT(mt.user_id) AS penalty_count +$stmt_open = mysqli_prepare($conn, " + SELECT u.username, COUNT(*) AS open_penalty_count FROM meeting_teilnehmer mt JOIN meetings m ON mt.meeting_id = m.id JOIN users u ON mt.user_id = u.id WHERE m.meeting_date >= ? AND mt.attended = 1 AND mt.wore_color = 0 AND m.is_completed = 1 GROUP BY u.username - ORDER BY penalty_count DESC -"; -$stmt = mysqli_prepare($conn, $sql_penalties); -// Überprüfen, ob das Statement erfolgreich vorbereitet wurde -if ($stmt === false) { - die("Fehler bei der SQL-Abfrage: " . mysqli_error($conn)); +"); +mysqli_stmt_bind_param($stmt_open, "s", $last_closing_date); +mysqli_stmt_execute($stmt_open); +$result_open = mysqli_stmt_get_result($stmt_open); +while ($row = mysqli_fetch_assoc($result_open)) { + $open_penalties[$row['username']] = (int)$row['open_penalty_count']; + $total_open += $row['open_penalty_count']; } -mysqli_stmt_bind_param($stmt, "s", $last_reset_date); -mysqli_stmt_execute($stmt); -$result = mysqli_stmt_get_result($stmt); +mysqli_stmt_close($stmt_open); -while ($row = mysqli_fetch_assoc($result)) { - $penalties_data[] = $row; - $total_penalties += $row['penalty_count']; - $total_due += $row['penalty_count'] * $penalty_amount; +$penalties_data = []; +$all_usernames = array_unique(array_merge(array_keys($cumulative_penalties), array_keys($open_penalties))); +foreach ($all_usernames as $username) { + $total = $cumulative_penalties[$username] ?? 0; + $open = $open_penalties[$username] ?? 0; + $penalties_data[] = [ + 'username' => $username, + 'total_penalty_count' => $total, + 'open_penalty_count' => $open + ]; +} +usort($penalties_data, fn($a, $b) => $b['open_penalty_count'] <=> $a['open_penalty_count']); + +$total_penalties = array_sum(array_column($penalties_data, 'total_penalty_count')); +$total_due = $total_open * $penalty_amount; + +$historical_table = []; +$closing_years = []; +$result_closings = mysqli_query($conn, "SELECT closing_date FROM penalty_closings ORDER BY closing_date"); +while ($row = mysqli_fetch_assoc($result_closings)) { + $closing_date = new DateTime($row['closing_date']); + $year = (int)$closing_date->format('Y'); + $month = (int)$closing_date->format('n'); + $closed_year = ($month >= 7) ? $year : ($year - 1); + $closing_years[] = $closed_year; +} +$closing_years = array_unique($closing_years); +sort($closing_years); + +if (!empty($closing_years)) { + foreach ($closing_years as $year) { + $year_start = "$year-01-01"; + $year_end = "$year-12-31"; + + $stmt_hist = mysqli_prepare($conn, " + SELECT u.username, COUNT(*) AS penalty_count + FROM meeting_teilnehmer mt + JOIN meetings m ON mt.meeting_id = m.id + JOIN users u ON mt.user_id = u.id + WHERE m.meeting_date BETWEEN ? AND ? + AND mt.attended = 1 AND mt.wore_color = 0 AND m.is_completed = 1 + GROUP BY u.username + "); + mysqli_stmt_bind_param($stmt_hist, "ss", $year_start, $year_end); + mysqli_stmt_execute($stmt_hist); + $result_hist = mysqli_stmt_get_result($stmt_hist); + while ($row = mysqli_fetch_assoc($result_hist)) { + $username = $row['username']; + $count = (int)$row['penalty_count']; + + if (!isset($historical_table[$username])) { + $historical_table[$username] = ['years' => [], 'total' => 0]; + } + $historical_table[$username]['years'][] = $year; + $historical_table[$username]['total'] += $count; + } + mysqli_stmt_close($stmt_hist); + } + + foreach ($historical_table as $username => $data) { + $historical_table[$username]['year_label'] = implode('/', $data['years']); + } } -mysqli_stmt_close($stmt); -// Neue Statistik: Ranking nach bezahlten Strafen -// NEUE BEDINGUNG: Auch hier nur abgeschlossene Meetings einbeziehen. $paid_stats = []; $sql_paid = " - SELECT - u.username, - COUNT(mt.user_id) AS paid_count + SELECT u.username, COUNT(mt.user_id) AS paid_count FROM meeting_teilnehmer mt JOIN meetings m ON mt.meeting_id = m.id JOIN users u ON mt.user_id = u.id @@ -66,38 +276,58 @@ require_once 'inc/header.php'; ?>
- Gesamtzahl der Strafen: = $total_penalties ?>
- Fälliger Gesamtbetrag: = number_format($total_due, 2, ',', '.'); ?> €
+ Strafen gesamt: = $total_penalties ?>
+ offener Betrag: = number_format($total_due, 2, ',', '.') ?> €
| Benutzer | -Anzahl Strafen | -Fälliger Betrag | +Strafen gesamt | +offener Betrag | ||
|---|---|---|---|---|---|---|
| = htmlspecialchars($penalty['username']); ?> | -= htmlspecialchars($penalty['penalty_count']); ?> | -= number_format($penalty['penalty_count'] * $penalty_amount, 2, ',', '.'); ?> € | += htmlspecialchars($p['username']) ?> | += htmlspecialchars($p['total_penalty_count']) ?> | += number_format($p['open_penalty_count'] * $penalty_amount, 2, ',', '.') ?> € | |
| Benutzer | +Jahr(e) | +Strafen | +
|---|---|---|
| = htmlspecialchars($username) ?> | += htmlspecialchars($data['year_label']) ?> | += htmlspecialchars($data['total']) ?> | +
| — | +||
Hallo ' . htmlspecialchars($u['username']) . ',
+' . htmlspecialchars($proposer) . ' hat einen neuen Vorschlag zur Verschiebung des Termins eingereicht.
+Neuer vorgeschlagener Termin:
+ ' . htmlspecialchars($new_fmt) . '
Grund:
+ ' . htmlspecialchars($reason) . '
Bitte stimme erneut ab, ob du teilnehmen möchtest.
+ ++ Dein DoMiLi-Team +
+Hallo ' . htmlspecialchars($u['username']) . ',
+Der Termin wurde vom Admin verschoben.
+Ursprünglicher Termin:
+ ' . htmlspecialchars($orig_fmt) . '
Neuer Termin:
+ ' . htmlspecialchars($new_fmt) . '
Grund:
+ ' . htmlspecialchars($new_reason) . '
Bitte stimme erneut ab, ob du teilnehmen möchtest.
+ ++ Dein DoMiLi-Team +
+Hallo ' . htmlspecialchars($u['username']) . ',
+Der Verschiebungsvorschlag von ' . htmlspecialchars($proposer) . ' wurde vom Admin abgelehnt.
+Der Termin bleibt daher unverändert beim ursprünglichen Termin am ' . htmlspecialchars($original_fmt) . '.
++ Dein DoMiLi-Team +
+