M6: Mailversand als nachvollziehbaren Versandjob mit Dry-Run/Log
mailversenden.php hatte keine Zugriffskontrolle, versendete bei jedem GET-Request sofort echte Mails und nutzte PHPMailer, dessen Quelldateien im Repo gar nicht vorhanden waren (nur composer.json/Lizenz) - der Aufruf waere also ohnehin mit Fatal Error abgebrochen. Zusaetzlich waren SMTP- Host, Absender, PayPal-Link und FAQ-URL fest auf einen Alt-Kunden (AOK) codiert. - Ersetzt PHPMailer durch die bestehende saas_send_mail()-Abstraktion aus M3 (Transport log/mail je nach APP_MAIL_TRANSPORT) statt eine fehlende Abhaengigkeit nachzuvendoren. - Neue Tabelle outbound_emails protokolliert jeden Versandversuch: Mandant, Mitglied, Vorlage, Betreff, Status, Fehler - das Versandlog. - Formular hat eine standardmaessig aktive Dry-Run-Checkbox; im Dry-Run wird nur geloggt, saas_send_mail() nicht aufgerufen. - Mailtext ist jetzt tenant-generisch (Saldo, optionaler PayPal-Link nur wenn der Mandant PayPal aktiviert hat, eigener Dashboard-Link) statt hartcodierter Alt-Kunden-Inhalte. - Zugriffskontrolle ergaenzt (owner/admin/treasurer + Legacy-Fallback). - http-smoke.php: mailversenden.php ist jetzt reguel</EOF>
This commit is contained in:
+120
-109
@@ -1,6 +1,9 @@
|
||||
<?php
|
||||
|
||||
include "functions.php";
|
||||
require_once __DIR__ . "/app/ledger.php";
|
||||
require_once __DIR__ . "/app/saas-mail.php";
|
||||
app_require_csrf();
|
||||
include "header.php";
|
||||
include "headerline.php";
|
||||
include "nav.php";
|
||||
@@ -15,128 +18,136 @@ include "nav.php";
|
||||
<div class="content">
|
||||
|
||||
<?php
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
use PHPMailer\PHPMailer\SMTP;
|
||||
use PHPMailer\PHPMailer\Exception;
|
||||
|
||||
require 'PHPMailer/src/Exception.php';
|
||||
require 'PHPMailer/src/PHPMailer.php';
|
||||
require 'PHPMailer/src/SMTP.php';
|
||||
$pdo = app_db_pdo();
|
||||
$saasUser = saas_current_user($pdo);
|
||||
$tenantId = 0;
|
||||
$hasAccess = false;
|
||||
|
||||
|
||||
// Funktion zum Berechnen des Gesamtstands
|
||||
function berechneGesamtstand($mitarbeiterID, $conn) {
|
||||
// Kosten aus Kaffeeverbrauch abrufen
|
||||
$sqlKosten = "SELECT SUM(Kosten) AS GesamtKosten FROM kl_Kaffeeverbrauch WHERE MitarbeiterID = ?";
|
||||
$paramsKosten = array($mitarbeiterID);
|
||||
$stmtKosten = sqlsrv_query($conn, $sqlKosten, $paramsKosten);
|
||||
|
||||
if ($stmtKosten === false) {
|
||||
die(print_r(sqlsrv_errors(), true));
|
||||
}
|
||||
|
||||
$rowKosten = sqlsrv_fetch_array($stmtKosten, SQLSRV_FETCH_ASSOC);
|
||||
$gesamtKosten = $rowKosten['GesamtKosten'];
|
||||
|
||||
// Einzahlungen abrufen
|
||||
$sqlEinzahlungen = "SELECT SUM(Betrag) AS GesamtEinzahlungen FROM kl_Einzahlungen WHERE MitarbeiterID = ?";
|
||||
$paramsEinzahlungen = array($mitarbeiterID);
|
||||
$stmtEinzahlungen = sqlsrv_query($conn, $sqlEinzahlungen, $paramsEinzahlungen);
|
||||
|
||||
if ($stmtEinzahlungen === false) {
|
||||
die(print_r(sqlsrv_errors(), true));
|
||||
}
|
||||
|
||||
$rowEinzahlungen = sqlsrv_fetch_array($stmtEinzahlungen, SQLSRV_FETCH_ASSOC);
|
||||
$gesamtEinzahlungen = $rowEinzahlungen['GesamtEinzahlungen'];
|
||||
|
||||
// Gesamtstand berechnen (Kosten - Einzahlungen)
|
||||
$gesamtstand = $gesamtKosten - $gesamtEinzahlungen;
|
||||
|
||||
return $gesamtstand;
|
||||
if ($saasUser !== null && saas_user_has_role(['owner', 'admin', 'treasurer'], $saasUser)) {
|
||||
$tenantId = (int)$saasUser['tenant_id'];
|
||||
$hasAccess = true;
|
||||
}
|
||||
|
||||
// SQL-Abfrage für alle Mitarbeiter
|
||||
$sqlMitarbeiter = "SELECT MitarbeiterID, Name, Email FROM kl_Mitarbeiter WHERE aktiv='1'";
|
||||
$stmtMitarbeiter = sqlsrv_query($conn, $sqlMitarbeiter);
|
||||
|
||||
// E-Mail-Versand für jeden Mitarbeiter
|
||||
while ($row = sqlsrv_fetch_array($stmtMitarbeiter, SQLSRV_FETCH_ASSOC)) {
|
||||
$mitarbeiterID = $row['MitarbeiterID'];
|
||||
$name = trim($row['Name']);
|
||||
$email = $row['Email'];
|
||||
// Gesamtstand berechnen
|
||||
$gesamtstand = berechneGesamtstand($mitarbeiterID, $conn);
|
||||
$stand = abs($gesamtstand);
|
||||
$body = "Hallo $name,<br><br>";
|
||||
|
||||
if($gesamtstand > 0){
|
||||
$body .= "hier ist dein aktueller negativer Kaffeekassenstand. <br>
|
||||
Bitte zahle folgenden Betrag in die Kaffeekasse ein: {$stand} Euro.<br>
|
||||
<br>Es kann nur noch per Paypal bezahlt werden.<br>
|
||||
Dein Link: https://www.paypal.me/kaffeekassesb3/{$stand}<br>
|
||||
<b>Bezahle immer über die Freunde-Funktion von Paypal. Ansonsten stellen wir 20% des eingezahlten Betrags als Gebühr in Rechnung.</b><br>
|
||||
";
|
||||
|
||||
}else{
|
||||
$body .= "hier ist dein aktuelles Guthaben in der Kaffeeliste: {$stand} Euro.<br>
|
||||
<br>";
|
||||
if (!$hasAccess && $saasUser === null && checkKaffeelisteAdmin($conn, $mailadress)) {
|
||||
$tenant = ledger_fetch_default_tenant($pdo);
|
||||
if ($tenant !== null) {
|
||||
$tenantId = (int)$tenant['id'];
|
||||
$hasAccess = true;
|
||||
}
|
||||
|
||||
$body .= "
|
||||
Deinen aktuellen Stand findest du auch hier: <a href='https://kaffeeliste.arge.aok/'>https://kaffeeliste.arge.aok/</a><br>
|
||||
<br>
|
||||
<b>Hast du Fragen oder Probleme? Dann lies immer zu erst unsere <a href=https://kaffeeliste.arge.aok/faq.php>FAQs</a>. Danke! </b><br>
|
||||
<br>
|
||||
Vielen Dank!<br>
|
||||
Deine Kaffeekasse<br>
|
||||
<br>
|
||||
Dein Guthaben ist auf dem nächsten Ausdruck der Kaffeeliste einsehbar. Um die Umwelt zu schonen erfolgt ein neuer Ausdruck erst, wenn die Liste voll ist.<br>
|
||||
";
|
||||
|
||||
|
||||
|
||||
// PHPMailer initialisieren
|
||||
$mail = new PHPMailer(true);
|
||||
}
|
||||
|
||||
try {
|
||||
//Server settings
|
||||
$mail->SMTPDebug = SMTP::DEBUG_OFF; // Disable debugging
|
||||
$mail->Timeout = 120;
|
||||
|
||||
$mail->isSMTP();
|
||||
$mail->Host = 'smtpv.aoknds.aok'; // SMTP server
|
||||
|
||||
## Hier wird absichtlich keine Authentifizierung genutzt.
|
||||
|
||||
|
||||
#$mail->SMTPAuth = true;
|
||||
#$mail->Username = ''; // SMTP username
|
||||
#$mail->Password = ''; // SMTP password
|
||||
#$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; // Enable TLS encryption; `PHPMailer::ENCRYPTION_SMTPS` also accepted
|
||||
#$mail->Port = 587; // TCP port to connect to
|
||||
if (!$hasAccess) {
|
||||
echo "<h2>Kein Zugriff</h2>";
|
||||
include "footer.php";
|
||||
exit;
|
||||
}
|
||||
|
||||
//Recipients
|
||||
$mail->setFrom('kaffeelistesb3@nds.aok.de', 'Kaffeeliste ARGE');
|
||||
$mail->addAddress($email, $name); // Add a recipient
|
||||
$ergebnisse = null;
|
||||
$dryRun = true;
|
||||
|
||||
// Content
|
||||
$mail->isHTML(true); // Set email format to HTML
|
||||
$mail->Subject = "Kaffeeliste ARGE - Dein Stand";
|
||||
$mail->Body = utf8_decode($body);
|
||||
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$dryRun = !empty($_POST['dry_run']);
|
||||
$settings = saas_fetch_tenant_settings($pdo, $tenantId);
|
||||
$dashboardUrl = saas_app_url('index.php');
|
||||
$createdByUserId = $saasUser['user_id'] ?? null;
|
||||
|
||||
$mail->send();
|
||||
echo "E-Mail erfolgreich an $name gesendet.<br>";
|
||||
} catch (Exception $e) {
|
||||
echo "E-Mail konnte nicht gesendet werden. Fehler: {$mail->ErrorInfo}<br>";
|
||||
}
|
||||
$teilnehmer = ledger_fetch_participant_summaries($pdo, $tenantId, ['active_only' => true]);
|
||||
$ergebnisse = [];
|
||||
|
||||
|
||||
foreach ($teilnehmer as $person) {
|
||||
$email = trim((string)($person['email'] ?? ''));
|
||||
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
$ergebnisse[] = ['name' => $person['display_name'], 'email' => $email, 'status' => 'ohne_email'];
|
||||
continue;
|
||||
}
|
||||
|
||||
$subject = 'Kaffeeliste - Dein aktueller Stand';
|
||||
$body = saas_render_balance_mail_body(
|
||||
$person['display_name'],
|
||||
(int)$person['balance_cents'],
|
||||
$settings,
|
||||
$dashboardUrl
|
||||
);
|
||||
|
||||
if ($dryRun) {
|
||||
saas_log_outbound_email($pdo, $tenantId, $person['participant_id'], 'balance_notice', $subject, 'dry_run', null, $createdByUserId);
|
||||
$ergebnisse[] = ['name' => $person['display_name'], 'email' => $email, 'status' => 'dry_run'];
|
||||
continue;
|
||||
}
|
||||
|
||||
$sendResult = saas_send_mail($email, $subject, $body);
|
||||
if ($sendResult['ok']) {
|
||||
saas_log_outbound_email($pdo, $tenantId, $person['participant_id'], 'balance_notice', $subject, 'sent', null, $createdByUserId);
|
||||
$ergebnisse[] = ['name' => $person['display_name'], 'email' => $email, 'status' => 'sent'];
|
||||
} else {
|
||||
saas_log_outbound_email($pdo, $tenantId, $person['participant_id'], 'balance_notice', $subject, 'failed', $sendResult['error'] ?? 'unbekannter Fehler', $createdByUserId);
|
||||
$ergebnisse[] = ['name' => $person['display_name'], 'email' => $email, 'status' => 'failed'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$log = saas_fetch_outbound_email_log($pdo, $tenantId, 20);
|
||||
|
||||
function mailversenden_status_label(string $status): string
|
||||
{
|
||||
return match ($status) {
|
||||
'sent' => 'Versendet',
|
||||
'failed' => 'Fehlgeschlagen',
|
||||
'dry_run' => 'Dry-Run (nicht versendet)',
|
||||
'ohne_email' => 'Keine E-Mail-Adresse',
|
||||
default => $status,
|
||||
};
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
</div>
|
||||
<h2>Info-Mail versenden</h2>
|
||||
<p>Verschickt an alle aktiven Mitglieder eine E-Mail mit dem aktuellen
|
||||
Kaffeelisten-Stand. Im Dry-Run wird nichts wirklich versendet, aber jeder
|
||||
Empfänger wird im Versandlog unten protokolliert.</p>
|
||||
|
||||
<?php if ($ergebnisse !== null): ?>
|
||||
<h3>Ergebnis dieses Laufs (<?php echo $dryRun ? 'Dry-Run' : 'Live-Versand'; ?>)</h3>
|
||||
<table>
|
||||
<tr><th>Name</th><th>E-Mail</th><th>Status</th></tr>
|
||||
<?php foreach ($ergebnisse as $eintrag): ?>
|
||||
<tr>
|
||||
<td><?php echo saas_html($eintrag['name']); ?></td>
|
||||
<td><?php echo saas_html($eintrag['email']); ?></td>
|
||||
<td><?php echo saas_html(mailversenden_status_label($eintrag['status'])); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>">
|
||||
<?php echo app_csrf_field(); ?>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="dry_run" id="dry_run" checked>
|
||||
<label class="form-check-label" for="dry_run">Dry-Run (nichts wirklich versenden, nur protokollieren)</label>
|
||||
</div>
|
||||
<button type="submit">Info-Mail versenden</button>
|
||||
</form>
|
||||
|
||||
<h3>Versandlog (letzte 20)</h3>
|
||||
<table>
|
||||
<tr><th>Datum</th><th>Mitglied</th><th>Betreff</th><th>Status</th><th>Fehler</th></tr>
|
||||
<?php if ($log === []): ?>
|
||||
<tr><td colspan="5">Noch kein Versand protokolliert.</td></tr>
|
||||
<?php endif; ?>
|
||||
<?php foreach ($log as $eintrag): ?>
|
||||
<tr>
|
||||
<td><?php echo saas_html($eintrag['created_at']); ?></td>
|
||||
<td><?php echo saas_html($eintrag['display_name'] ?? '—'); ?></td>
|
||||
<td><?php echo saas_html($eintrag['subject']); ?></td>
|
||||
<td><?php echo saas_html(mailversenden_status_label($eintrag['status'])); ?></td>
|
||||
<td><?php echo saas_html($eintrag['error'] ?? ''); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<?php include "footer.php"; ?>
|
||||
<?php include "footer.php"; ?>
|
||||
|
||||
Reference in New Issue
Block a user