true, 'year' => $jahr]) : []; $gesamtJahresstriche = array_sum(array_column($teilnehmer, 'year_marks')); if ($hasAccess && $_SERVER["REQUEST_METHOD"] === "POST") { $dryRun = !empty($_POST['dry_run']); $gesamtbetragEingabe = (string)($_POST['gesamtbetrag'] ?? ''); $gesamtbetrag = filter_var(str_replace(',', '.', $gesamtbetragEingabe), FILTER_VALIDATE_FLOAT); $totalCents = $gesamtbetrag !== false ? (int)round($gesamtbetrag * 100) : 0; if ($totalCents <= 0) { $fehler = 'Bitte einen Gesamtbetrag größer 0 eingeben.'; } elseif ($gesamtJahresstriche <= 0) { $fehler = "Für {$jahr} gibt es noch keine Striche, daher kann nichts verteilt werden."; } else { $createdByUserId = $saasUser['user_id'] ?? null; // Nur Teilnehmer mit Strichen in diesem Jahr erhalten einen Anteil. $empfaenger = array_values(array_filter( $teilnehmer, static fn(array $p): bool => (int)$p['year_marks'] > 0 )); $anzahlEmpfaenger = count($empfaenger); // Bonus proportional zu den Strichen. Damit die Summe exakt dem // eingegebenen Gesamtbetrag entspricht, erhaelt der letzte Empfaenger // den verbleibenden Restcent statt einer eigenen Rundung. $zuteilung = []; $bereitsVerteilt = 0; foreach ($empfaenger as $index => $person) { $yearMarks = (int)$person['year_marks']; if ($index === $anzahlEmpfaenger - 1) { $bonusCents = $totalCents - $bereitsVerteilt; } else { $bonusCents = (int)round($totalCents * $yearMarks / $gesamtJahresstriche); $bereitsVerteilt += $bonusCents; } $zuteilung[] = [ 'person' => $person, 'year_marks' => $yearMarks, 'anteil' => $yearMarks / $gesamtJahresstriche, 'bonus_cents' => $bonusCents, ]; } if ($dryRun) { $ergebnisse = array_map(static function (array $z): array { return [ 'name' => $z['person']['display_name'], 'year_marks' => $z['year_marks'], 'anteil' => $z['anteil'], 'bonus_cents' => $z['bonus_cents'], 'status' => 'vorschau', ]; }, $zuteilung); } else { // Legacy-Verknuepfung je Teilnehmer vorab laden (gleiches Zweig-Muster // wie bei Sammelerfassung und CSV-Import: Default-Mandant bucht // zusaetzlich in kl_Einzahlungen, alle anderen Mandanten rein Ledger-nativ). $legacyMap = []; $stmt = $pdo->prepare('SELECT id, legacy_mitarbeiter_id FROM participants WHERE tenant_id = ?'); $stmt->execute([$tenantId]); foreach ($stmt->fetchAll() as $row) { $legacyMap[(int)$row['id']] = $row['legacy_mitarbeiter_id'] !== null ? (int)$row['legacy_mitarbeiter_id'] : null; } $ergebnisse = []; $zuBenachrichtigen = []; try { $pdo->beginTransaction(); $insertLegacy = $pdo->prepare('INSERT INTO kl_Einzahlungen (MitarbeiterID, Betrag, Datum) VALUES (?, ?, ?)'); $jetzt = date('Y-m-d H:i:s'); foreach ($zuteilung as $z) { $person = $z['person']; $bonusCents = $z['bonus_cents']; if ($bonusCents <= 0) { continue; } $legacyMitarbeiterId = $legacyMap[$person['participant_id']] ?? null; if ($legacyMitarbeiterId !== null) { $insertLegacy->execute([$legacyMitarbeiterId, $bonusCents / 100, $jetzt]); $legacyPaymentId = (int)$pdo->lastInsertId(); ledger_mirror_legacy_payment($pdo, $tenantId, $legacyPaymentId); } else { ledger_record_payment($pdo, $tenantId, (int)$person['participant_id'], $bonusCents, 'year_end_bonus', $createdByUserId); } $email = trim((string)($person['email'] ?? '')); if (filter_var($email, FILTER_VALIDATE_EMAIL)) { $zuBenachrichtigen[] = [ 'participant_id' => (int)$person['participant_id'], 'email' => $email, 'name' => (string)$person['display_name'], 'year_marks' => $z['year_marks'], 'bonus_cents' => $bonusCents, ]; } $ergebnisse[] = [ 'name' => $person['display_name'], 'year_marks' => $z['year_marks'], 'anteil' => $z['anteil'], 'bonus_cents' => $bonusCents, 'status' => 'gebucht', ]; } $pdo->commit(); } catch (Throwable $e) { if ($pdo->inTransaction()) { $pdo->rollBack(); } $fehler = 'Die Verteilung konnte nicht gespeichert werden.'; $ergebnisse = null; } if ($fehler === null) { // Mails erst nach erfolgreichem Commit versenden - sonst waeren // bei einem spaeteren Rollback bereits Mails draussen, deren // Buchung nie stattgefunden hat. $settings = saas_fetch_tenant_settings($pdo, $tenantId); $dashboardUrl = saas_app_url('index.php'); foreach ($zuBenachrichtigen as $mail) { $subject = "Kaffeeliste - Dein Jahresbonus {$jahr}"; $body = saas_render_year_end_bonus_mail_body($mail['name'], $mail['year_marks'], $mail['bonus_cents'], $dashboardUrl); $sendResult = saas_send_mail($mail['email'], $subject, $body); saas_log_outbound_email( $pdo, $tenantId, $mail['participant_id'], 'year_end_bonus', $subject, $sendResult['ok'] ? 'sent' : 'failed', $sendResult['error'] ?? null, $createdByUserId ); } app_audit_log($pdo, $tenantId, $createdByUserId, 'year_end_bonus.distributed', 'tenant', $tenantId, ['jahr' => $jahr, 'gesamtbetrag_cents' => $totalCents, 'empfaenger' => count($ergebnisse)]); // Post-Redirect-Get: Ergebnis in die Session legen und per GET // erneut anzeigen, damit ein Refresh nicht doppelt bucht. $_SESSION['flash_year_end'] = ['tenant_id' => $tenantId, 'ergebnisse' => $ergebnisse]; header('Location: jahresauswertung.php'); exit; } } } } include "header.php"; include "headerline.php"; include "nav.php"; ?>