diff --git a/app/imports.php b/app/imports.php index 37bfdab..06e0c98 100644 --- a/app/imports.php +++ b/app/imports.php @@ -188,10 +188,8 @@ function imports_fetch_batch(PDO $pdo, int $tenantId, int $batchId): ?array } /** - * Commits every 'matched' row of a previewed batch into the ledger: for - * participants with a legacy_mitarbeiter_id (default tenant), a matching - * kl_Einzahlungen row is dual-written and mirrored; every other tenant is - * booked straight into the ledger. Already-committed batches are rejected. + * Commits every 'matched' row of a previewed batch straight into the ledger. + * Already-committed batches are rejected. * * @return array{imported: int} */ @@ -208,7 +206,7 @@ function imports_commit_batch(PDO $pdo, int $tenantId, int $batchId): array } $stmt = $pdo->prepare( - "SELECT r.id, r.participant_id, r.amount_cents, r.booked_at, p.legacy_mitarbeiter_id + "SELECT r.id, r.participant_id, r.amount_cents, r.booked_at FROM payment_import_rows r JOIN participants p ON p.id = r.participant_id WHERE r.batch_id = ? AND r.status = 'matched'" @@ -220,30 +218,18 @@ function imports_commit_batch(PDO $pdo, int $tenantId, int $batchId): array $pdo->beginTransaction(); try { - $insertLegacy = $pdo->prepare( - 'INSERT INTO kl_Einzahlungen (MitarbeiterID, Betrag, Datum) VALUES (?, ?, ?)' - ); $updateRow = $pdo->prepare( "UPDATE payment_import_rows SET status = 'imported', ledger_entry_id = ? WHERE id = ?" ); foreach ($rows as $row) { - $legacyMitarbeiterId = $row['legacy_mitarbeiter_id'] !== null ? (int)$row['legacy_mitarbeiter_id'] : null; - $amountCents = (int)$row['amount_cents']; - - if ($legacyMitarbeiterId !== null) { - $insertLegacy->execute([$legacyMitarbeiterId, $amountCents / 100, $row['booked_at']]); - $legacyPaymentId = (int)$pdo->lastInsertId(); - ledger_mirror_legacy_payment($pdo, $tenantId, $legacyPaymentId); - - $entryStmt = $pdo->prepare( - "SELECT id FROM ledger_entries WHERE tenant_id = ? AND legacy_table = 'kl_Einzahlungen' AND legacy_id = ?" - ); - $entryStmt->execute([$tenantId, $legacyPaymentId]); - $ledgerEntryId = (int)$entryStmt->fetchColumn(); - } else { - $ledgerEntryId = ledger_record_payment($pdo, $tenantId, (int)$row['participant_id'], $amountCents, 'csv_import'); - } + $ledgerEntryId = ledger_record_payment( + $pdo, + $tenantId, + (int)$row['participant_id'], + (int)$row['amount_cents'], + 'csv_import' + ); $updateRow->execute([$ledgerEntryId, $row['id']]); $imported++; diff --git a/app/ledger.php b/app/ledger.php index 1bcb3d2..5a89a3a 100644 --- a/app/ledger.php +++ b/app/ledger.php @@ -305,60 +305,6 @@ function ledger_fetch_participant_summary_by_legacy_id(PDO $pdo, int $tenantId, return $summaries[0] ?? null; } -function ledger_mirror_legacy_consumption(PDO $pdo, int $tenantId, int $legacyConsumptionId): void -{ - $stmt = $pdo->prepare( - "INSERT INTO ledger_entries - (tenant_id, participant_id, type, amount_cents, marks_count, - unit_price_cents, booked_at, source, note, legacy_table, legacy_id) - SELECT - p.tenant_id, - p.id, - 'consumption', - -CAST(ROUND(v.Kosten * 100) AS SIGNED), - v.AnzahlStriche, - CAST(ROUND(v.KostenproStrich * 100) AS SIGNED), - v.Datum, - CASE - WHEN v.Eintragsart = 2 THEN 'legacy_web' - ELSE 'legacy_manual' - END, - CASE - WHEN v.Eintragsart IS NULL THEN NULL - ELSE CONCAT('Legacy Eintragsart: ', v.Eintragsart) - END, - 'kl_Kaffeeverbrauch', - v.VerbrauchID - FROM kl_Kaffeeverbrauch v - JOIN participants p - ON p.tenant_id = ? - AND p.legacy_mitarbeiter_id = v.MitarbeiterID - WHERE v.VerbrauchID = ? - ON DUPLICATE KEY UPDATE - participant_id = VALUES(participant_id), - type = VALUES(type), - amount_cents = VALUES(amount_cents), - marks_count = VALUES(marks_count), - unit_price_cents = VALUES(unit_price_cents), - booked_at = VALUES(booked_at), - source = VALUES(source), - note = VALUES(note)" - ); - $stmt->execute([$tenantId, $legacyConsumptionId]); - - $check = $pdo->prepare( - "SELECT COUNT(*) - FROM ledger_entries - WHERE tenant_id = ? - AND legacy_table = 'kl_Kaffeeverbrauch' - AND legacy_id = ?" - ); - $check->execute([$tenantId, $legacyConsumptionId]); - if ((int)$check->fetchColumn() !== 1) { - throw new RuntimeException('Der Legacy-Strich konnte nicht ins Ledger gespiegelt werden.'); - } -} - function ledger_is_default_tenant(PDO $pdo, int $tenantId): bool { $tenant = ledger_fetch_default_tenant($pdo); @@ -412,10 +358,7 @@ function ledger_fetch_participants_for_admin(PDO $pdo, int $tenantId): array } /** - * Creates a tenant-scoped participant. For the default tenant, a matching - * kl_Mitarbeiter row is dual-written too, so the still-legacy bulk entry - * pages (stricheintragen.php, einzahlung.php) keep listing the member; other - * tenants have no legacy shadow table and get a participants-only row. + * Creates a tenant-scoped participant. * * @throws Throwable on constraint violations (e.g. duplicate email) */ @@ -427,35 +370,14 @@ function ledger_create_participant(PDO $pdo, int $tenantId, string $displayName, $paypalName = $paypalName !== null ? trim($paypalName) : null; $paypalName = $paypalName !== '' ? $paypalName : null; - $pdo->beginTransaction(); - try { - $legacyMitarbeiterId = null; - if (ledger_is_default_tenant($pdo, $tenantId)) { - $stmt = $pdo->prepare( - 'INSERT INTO kl_Mitarbeiter (Name, Email, paypalname, aktiv, admin) VALUES (?, ?, ?, ?, 0)' - ); - $stmt->execute([$displayName, $email, $paypalName, $active ? 1 : 0]); - $legacyMitarbeiterId = (int)$pdo->lastInsertId(); - } + $stmt = $pdo->prepare( + 'INSERT INTO participants + (tenant_id, display_name, email, email_norm, paypal_name, active) + VALUES (?, ?, ?, ?, ?, ?)' + ); + $stmt->execute([$tenantId, $displayName, $email, $emailNorm, $paypalName, $active ? 1 : 0]); - $stmt = $pdo->prepare( - 'INSERT INTO participants - (tenant_id, display_name, email, email_norm, paypal_name, active, legacy_mitarbeiter_id) - VALUES (?, ?, ?, ?, ?, ?, ?)' - ); - $stmt->execute([$tenantId, $displayName, $email, $emailNorm, $paypalName, $active ? 1 : 0, $legacyMitarbeiterId]); - $participantId = (int)$pdo->lastInsertId(); - - $pdo->commit(); - - return $participantId; - } catch (Throwable $e) { - if ($pdo->inTransaction()) { - $pdo->rollBack(); - } - - throw $e; - } + return (int)$pdo->lastInsertId(); } function ledger_update_participant(PDO $pdo, int $tenantId, int $participantId, string $displayName, string $email, ?string $paypalName, bool $active): bool @@ -466,168 +388,42 @@ function ledger_update_participant(PDO $pdo, int $tenantId, int $participantId, $paypalName = $paypalName !== null ? trim($paypalName) : null; $paypalName = $paypalName !== '' ? $paypalName : null; - $stmt = $pdo->prepare('SELECT legacy_mitarbeiter_id FROM participants WHERE id = ? AND tenant_id = ?'); - $stmt->execute([$participantId, $tenantId]); - $row = $stmt->fetch(); - if ($row === false) { - return false; - } - $legacyMitarbeiterId = $row['legacy_mitarbeiter_id'] !== null ? (int)$row['legacy_mitarbeiter_id'] : null; + $stmt = $pdo->prepare( + 'UPDATE participants + SET display_name = ?, email = ?, email_norm = ?, paypal_name = ?, active = ? + WHERE id = ? AND tenant_id = ?' + ); + $stmt->execute([$displayName, $email, $emailNorm, $paypalName, $active ? 1 : 0, $participantId, $tenantId]); - try { - $pdo->beginTransaction(); - - if ($legacyMitarbeiterId !== null) { - $stmt = $pdo->prepare( - 'UPDATE kl_Mitarbeiter SET Name = ?, Email = ?, paypalname = ?, aktiv = ? WHERE MitarbeiterID = ?' - ); - $stmt->execute([$displayName, $email, $paypalName, $active ? 1 : 0, $legacyMitarbeiterId]); - } - - $stmt = $pdo->prepare( - 'UPDATE participants - SET display_name = ?, email = ?, email_norm = ?, paypal_name = ?, active = ? - WHERE id = ? AND tenant_id = ?' - ); - $stmt->execute([$displayName, $email, $emailNorm, $paypalName, $active ? 1 : 0, $participantId, $tenantId]); - - $pdo->commit(); - - return true; - } catch (Throwable $e) { - if ($pdo->inTransaction()) { - $pdo->rollBack(); - } - - return false; - } + return $stmt->rowCount() >= 0; } function ledger_set_participant_active(PDO $pdo, int $tenantId, int $participantId, bool $active): bool { - $stmt = $pdo->prepare('SELECT legacy_mitarbeiter_id FROM participants WHERE id = ? AND tenant_id = ?'); - $stmt->execute([$participantId, $tenantId]); - $row = $stmt->fetch(); - if ($row === false) { - return false; - } - $legacyMitarbeiterId = $row['legacy_mitarbeiter_id'] !== null ? (int)$row['legacy_mitarbeiter_id'] : null; + $stmt = $pdo->prepare('UPDATE participants SET active = ? WHERE id = ? AND tenant_id = ?'); + $stmt->execute([$active ? 1 : 0, $participantId, $tenantId]); - try { - $pdo->beginTransaction(); - - if ($legacyMitarbeiterId !== null) { - $pdo->prepare('UPDATE kl_Mitarbeiter SET aktiv = ? WHERE MitarbeiterID = ?') - ->execute([$active ? 1 : 0, $legacyMitarbeiterId]); - } - - $pdo->prepare('UPDATE participants SET active = ? WHERE id = ? AND tenant_id = ?') - ->execute([$active ? 1 : 0, $participantId, $tenantId]); - - $pdo->commit(); - - return true; - } catch (Throwable $e) { - if ($pdo->inTransaction()) { - $pdo->rollBack(); - } - - return false; - } + return true; } /** * Anonymizes a participant (GDPR-style erasure) instead of deleting them: * name and email are replaced with a non-identifying placeholder and the * participant is deactivated, but their ledger history stays intact for - * the tenant's bookkeeping. For the default tenant, the linked - * kl_Mitarbeiter row is anonymized the same way (its email column is - * NOT NULL UNIQUE, so it gets a placeholder instead of NULL). + * the tenant's bookkeeping. */ function ledger_anonymize_participant(PDO $pdo, int $tenantId, int $participantId): bool { - $stmt = $pdo->prepare('SELECT legacy_mitarbeiter_id FROM participants WHERE id = ? AND tenant_id = ?'); - $stmt->execute([$participantId, $tenantId]); - $row = $stmt->fetch(); - if ($row === false) { - return false; - } - $legacyMitarbeiterId = $row['legacy_mitarbeiter_id'] !== null ? (int)$row['legacy_mitarbeiter_id'] : null; $placeholderName = 'Gelöschter Teilnehmer #' . $participantId; - $placeholderEmail = "deleted-participant-{$participantId}@invalid.local"; - try { - $pdo->beginTransaction(); - - if ($legacyMitarbeiterId !== null) { - $pdo->prepare('UPDATE kl_Mitarbeiter SET Name = ?, Email = ?, paypalname = NULL, aktiv = 0 WHERE MitarbeiterID = ?') - ->execute([$placeholderName, $placeholderEmail, $legacyMitarbeiterId]); - } - - $pdo->prepare( - 'UPDATE participants - SET display_name = ?, email = NULL, email_norm = NULL, paypal_name = NULL, active = 0, user_id = NULL - WHERE id = ? AND tenant_id = ?' - )->execute([$placeholderName, $participantId, $tenantId]); - - $pdo->commit(); - - return true; - } catch (Throwable $e) { - if ($pdo->inTransaction()) { - $pdo->rollBack(); - } - - return false; - } -} - -function ledger_mirror_legacy_payment(PDO $pdo, int $tenantId, int $legacyPaymentId): void -{ $stmt = $pdo->prepare( - "INSERT INTO ledger_entries - (tenant_id, participant_id, type, amount_cents, marks_count, - unit_price_cents, booked_at, source, note, legacy_table, legacy_id) - SELECT - p.tenant_id, - p.id, - 'payment', - CAST(ROUND(e.Betrag * 100) AS SIGNED), - NULL, - NULL, - e.Datum, - 'legacy_payment', - e.Bemerkung, - 'kl_Einzahlungen', - e.EinzahlungsID - FROM kl_Einzahlungen e - JOIN participants p - ON p.tenant_id = ? - AND p.legacy_mitarbeiter_id = e.MitarbeiterID - WHERE e.EinzahlungsID = ? - ON DUPLICATE KEY UPDATE - participant_id = VALUES(participant_id), - type = VALUES(type), - amount_cents = VALUES(amount_cents), - marks_count = VALUES(marks_count), - unit_price_cents = VALUES(unit_price_cents), - booked_at = VALUES(booked_at), - source = VALUES(source), - note = VALUES(note)" + 'UPDATE participants + SET display_name = ?, email = NULL, email_norm = NULL, paypal_name = NULL, active = 0, user_id = NULL + WHERE id = ? AND tenant_id = ?' ); - $stmt->execute([$tenantId, $legacyPaymentId]); + $stmt->execute([$placeholderName, $participantId, $tenantId]); - $check = $pdo->prepare( - "SELECT COUNT(*) - FROM ledger_entries - WHERE tenant_id = ? - AND legacy_table = 'kl_Einzahlungen' - AND legacy_id = ?" - ); - $check->execute([$tenantId, $legacyPaymentId]); - if ((int)$check->fetchColumn() !== 1) { - throw new RuntimeException('Die Legacy-Einzahlung konnte nicht ins Ledger gespiegelt werden.'); - } + return $stmt->rowCount() > 0; } /** @@ -695,21 +491,6 @@ function ledger_void_entry(PDO $pdo, int $tenantId, int $entryId, ?string $expec return true; } -function ledger_void_entry_by_legacy_id(PDO $pdo, int $tenantId, string $legacyTable, int $legacyId): bool -{ - $stmt = $pdo->prepare( - "UPDATE ledger_entries - SET voided_at = NOW() - WHERE tenant_id = ? - AND legacy_table = ? - AND legacy_id = ? - AND voided_at IS NULL" - ); - $stmt->execute([$tenantId, $legacyTable, $legacyId]); - - return $stmt->rowCount() > 0; -} - /** * Storniert den juengsten, noch nicht stornierten Web-Selbsteintrag eines * Teilnehmers (Quelle 'self_entry' bei SaaS-nativen Mandanten bzw. 'legacy_web' diff --git a/app/paypal-inbox.php b/app/paypal-inbox.php index 653b44e..6b20aff 100644 --- a/app/paypal-inbox.php +++ b/app/paypal-inbox.php @@ -102,24 +102,6 @@ function paypal_inbox_extract_token(string $address): ?string */ function paypal_book_payment(PDO $pdo, int $tenantId, array $participant, int $netCents, ?int $actorUserId, ?string $note = null): int { - $legacyMitarbeiterId = isset($participant['legacy_mitarbeiter_id']) && $participant['legacy_mitarbeiter_id'] !== null - ? (int) $participant['legacy_mitarbeiter_id'] - : 0; - - if ($legacyMitarbeiterId > 0) { - $stmt = $pdo->prepare('INSERT INTO kl_Einzahlungen (MitarbeiterID, Betrag, Bemerkung, Datum) VALUES (?, ?, ?, ?)'); - $stmt->execute([$legacyMitarbeiterId, $netCents / 100, $note, date('Y-m-d H:i:s')]); - $legacyPaymentId = (int) $pdo->lastInsertId(); - ledger_mirror_legacy_payment($pdo, $tenantId, $legacyPaymentId); - - $idStmt = $pdo->prepare( - "SELECT id FROM ledger_entries WHERE tenant_id = ? AND legacy_table = 'kl_Einzahlungen' AND legacy_id = ? LIMIT 1" - ); - $idStmt->execute([$tenantId, $legacyPaymentId]); - - return (int) $idStmt->fetchColumn(); - } - return ledger_record_payment($pdo, $tenantId, (int) $participant['participant_id'], $netCents, 'paypal_import', $actorUserId, $note); } diff --git a/einzahlung.php b/einzahlung.php index 18c54d6..babfa0d 100644 --- a/einzahlung.php +++ b/einzahlung.php @@ -43,34 +43,14 @@ if (!$hasAccess) { } /** - * Liefert die Mitarbeiter/Teilnehmerliste fuer die Vorder-/Rueckseiten- oder - * Alle-Auswahl. Siehe stricheintragen.php fuer dieselbe Logik: Default- - * Mandant nutzt die Legacy-Historie, andere Mandanten die Ledger-native - * Fensterpruefung mit dem konfigurierten Listenfenster. + * Liefert die Teilnehmerliste fuer die Vorder-/Rueckseiten- oder Alle-Auswahl. + * Siehe stricheintragen.php fuer dieselbe Logik. * * @return list */ function einzahlung_fetch_participants(PDO $pdo, int $tenantId, string $action): array { - $isDefault = ledger_is_default_tenant($pdo, $tenantId); - if ($action === 'vorderseite' || $action === 'rueckseite') { - if ($isDefault) { - $vergleich = $action === 'vorderseite' ? '>= 10' : '< 10'; - $join = $action === 'vorderseite' ? 'JOIN' : 'LEFT JOIN'; - $stmt = $pdo->query( - "SELECT M.MitarbeiterID - FROM kl_Mitarbeiter M - {$join} kl_Kaffeeverbrauch V ON M.MitarbeiterID = V.MitarbeiterID AND V.Datum >= DATE_SUB(NOW(), INTERVAL 100 DAY) - WHERE M.aktiv = 1 - GROUP BY M.MitarbeiterID - HAVING COALESCE(SUM(V.AnzahlStriche), 0) {$vergleich}" - ); - $legacyIds = array_map('intval', $stmt->fetchAll(PDO::FETCH_COLUMN)); - - return ledger_fetch_participant_summaries($pdo, $tenantId, ['legacy_mitarbeiter_ids' => $legacyIds]); - } - $settings = saas_fetch_tenant_settings($pdo, $tenantId); $windowDays = $settings['sheet_window_days'] ?? 100; @@ -94,24 +74,13 @@ const EINZAHLUNG_MAX_BETRAG = 1000.00; // Verarbeitung des Formulars, wenn es gesendet wurde if ($_SERVER["REQUEST_METHOD"] == "POST" ) { $hatGespeichert = true; - $datum = date("Y-m-d H:i:s"); // Das aktuelle Datum verwenden - // Legacy-Verknuepfung je Teilnehmer vorab laden, damit pro Zeile - // entschieden werden kann, ob (Default-Mandant) zusaetzlich in - // kl_Einzahlungen geschrieben oder (jeder andere Mandant) direkt ins - // Ledger gebucht wird. - $legacyMap = []; - $stmt = $pdo->prepare('SELECT id, legacy_mitarbeiter_id FROM participants WHERE tenant_id = ?'); + // Teilnehmer des Mandanten: dient zugleich der Zugehoerigkeitspruefung und + // liefert die Namen fuer verstaendliche Fehlermeldungen. + $namen = []; + $stmt = $pdo->prepare('SELECT id, display_name 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; - } - - // Namen fuer verstaendliche Fehlermeldungen. - $namen = []; - $stmtNamen = $pdo->prepare('SELECT id, display_name FROM participants WHERE tenant_id = ?'); - $stmtNamen->execute([$tenantId]); - foreach ($stmtNamen->fetchAll() as $row) { $namen[(int)$row['id']] = (string)$row['display_name']; } @@ -121,7 +90,7 @@ if ($_SERVER["REQUEST_METHOD"] == "POST" ) { $zuBuchen = []; foreach ($_POST["anzahlBetrag"] ?? [] as $participantId => $anzahlBetrag) { $participantId = (int)$participantId; - if ($participantId <= 0 || !array_key_exists($participantId, $legacyMap)) { + if ($participantId <= 0 || !array_key_exists($participantId, $namen)) { continue; } @@ -180,27 +149,17 @@ if ($_SERVER["REQUEST_METHOD"] == "POST" ) { } else { try { $pdo->beginTransaction(); - $insertLegacy = $pdo->prepare( - "INSERT INTO kl_Einzahlungen (MitarbeiterID, Betrag, Bemerkung, Datum) VALUES (?, ?, ?, ?)" - ); foreach ($zuBuchen as $participantId => $zeile) { - $legacyMitarbeiterId = $legacyMap[$participantId]; - if ($legacyMitarbeiterId !== null) { - $insertLegacy->execute([$legacyMitarbeiterId, $zeile['betrag'], $zeile['bemerkung'], $datum]); - $legacyPaymentId = (int)$pdo->lastInsertId(); - ledger_mirror_legacy_payment($pdo, $tenantId, $legacyPaymentId); - } else { - ledger_record_payment( - $pdo, - $tenantId, - $participantId, - (int)round($zeile['betrag'] * 100), - 'manual_bulk', - $saasUser['user_id'] ?? null, - $zeile['bemerkung'] - ); - } + ledger_record_payment( + $pdo, + $tenantId, + $participantId, + (int)round($zeile['betrag'] * 100), + 'manual_bulk', + $saasUser['user_id'] ?? null, + $zeile['bemerkung'] + ); $eingetragen++; } diff --git a/index.php b/index.php index e2b4110..65b5fa1 100644 --- a/index.php +++ b/index.php @@ -129,48 +129,16 @@ function dashboard_record_self_entry(PDO $pdo, array $participant, array $settin return ['ok' => false, 'error' => 'Der Preis pro Strich ist nicht gültig konfiguriert.']; } - $legacyMitarbeiterId = $participant['legacy_mitarbeiter_id'] !== null - ? (int)$participant['legacy_mitarbeiter_id'] - : 0; - try { - if ($legacyMitarbeiterId > 0) { - // Migrierter Default-Mandant: weiterhin in die Legacy-Tabelle - // schreiben, damit die noch-legacy Sammelseiten (stricheintragen.php) - // den Eintrag ebenfalls sehen; der Ledger-Eintrag entsteht per - // Spiegelung. - $bookedAt = date('Y-m-d H:i:s'); - $cost = (($marks * $unitPriceCents) / 100); - $unitPrice = ($unitPriceCents / 100); - - $pdo->beginTransaction(); - $stmt = $pdo->prepare( - 'INSERT INTO kl_Kaffeeverbrauch - (MitarbeiterID, AnzahlStriche, Kosten, KostenproStrich, Datum, Eintragsart) - VALUES (?, ?, ?, ?, ?, 2)' - ); - $stmt->execute([$legacyMitarbeiterId, $marks, $cost, $unitPrice, $bookedAt]); - $legacyConsumptionId = (int)$pdo->lastInsertId(); - if ($legacyConsumptionId <= 0) { - throw new RuntimeException('Legacy-Strich konnte nicht angelegt werden.'); - } - - ledger_mirror_legacy_consumption($pdo, (int)$participant['tenant_id'], $legacyConsumptionId); - $pdo->commit(); - } else { - // SaaS-native Mandanten (alle ausser dem migrierten Default- - // Mandanten) haben keine Legacy-Schattentabelle - direkt ins - // Ledger buchen. - ledger_record_consumption( - $pdo, - (int)$participant['tenant_id'], - (int)$participant['participant_id'], - $marks, - $unitPriceCents, - 'self_entry', - $actorUserId - ); - } + ledger_record_consumption( + $pdo, + (int)$participant['tenant_id'], + (int)$participant['participant_id'], + $marks, + $unitPriceCents, + 'self_entry', + $actorUserId + ); } catch (Throwable $e) { if ($pdo->inTransaction()) { $pdo->rollBack(); diff --git a/jahresauswertung.php b/jahresauswertung.php index 2b0dd79..7d073bd 100644 --- a/jahresauswertung.php +++ b/jahresauswertung.php @@ -101,22 +101,10 @@ if ($hasAccess && $_SERVER["REQUEST_METHOD"] === "POST") { ]; }, $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']; @@ -125,14 +113,7 @@ if ($hasAccess && $_SERVER["REQUEST_METHOD"] === "POST") { 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); - } + 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)) { diff --git a/kaffeeliste.php b/kaffeeliste.php index 27a1c3f..2bdb5ee 100644 --- a/kaffeeliste.php +++ b/kaffeeliste.php @@ -72,7 +72,7 @@ if ($hasAccess) { "; - if ($legacyId !== null) { - echo "{$name}"; - } else { - echo "{$name}"; - } + echo "{$name}"; echo "{$email}"; echo "{$balance}"; echo "{$totalCosts}"; diff --git a/scripts/check-m8-tenant-isolation.php b/scripts/check-m8-tenant-isolation.php index 9412d86..8598220 100644 --- a/scripts/check-m8-tenant-isolation.php +++ b/scripts/check-m8-tenant-isolation.php @@ -102,15 +102,20 @@ try { m8_assert('Zugangsvergabe auf fremden Teilnehmer schlaegt fehl', $grantResult['ok'] === false, $failures, $passes); // 7. Storno eines fremden Eintrags ueber den falschen Mandanten aendert nichts. - // ledger_void_entry_by_legacy_id() matcht ueber (tenant_id, legacy_table, - // legacy_id); dafuer braucht es einen Eintrag mit einer synthetischen - // Legacy-Referenz, unabhaengig von echten kl_*-Zeilen. - $pdo->prepare( - "UPDATE ledger_entries SET legacy_table = 'isolation_test', legacy_id = ? WHERE tenant_id = ? AND participant_id = ?" - )->execute([$participantA, $tenantA, $participantA]); - $voidedWrongTenant = ledger_void_entry_by_legacy_id($pdo, $tenantB, 'isolation_test', $participantA); + // Geprueft wird der echte Storno-Pfad ledger_void_entry(), den auch + // letzteneintraege.php nutzt. + $entryIdA = (int)$pdo->query( + "SELECT id FROM ledger_entries WHERE tenant_id = {$tenantA} AND participant_id = {$participantA} AND voided_at IS NULL LIMIT 1" + )->fetchColumn(); + m8_assert('Testeintrag fuer Storno vorhanden', $entryIdA > 0, $failures, $passes); + + $voidedWrongTenant = ledger_void_entry($pdo, $tenantB, $entryIdA); m8_assert('Storno ueber falschen Mandanten aendert nichts', $voidedWrongTenant === false, $failures, $passes); - $voidedRightTenant = ledger_void_entry_by_legacy_id($pdo, $tenantA, 'isolation_test', $participantA); + + $stillActive = (string)$pdo->query("SELECT COUNT(*) FROM ledger_entries WHERE id = {$entryIdA} AND voided_at IS NULL")->fetchColumn(); + m8_assert('fremder Eintrag ist nach abgelehntem Storno unveraendert', $stillActive === '1', $failures, $passes); + + $voidedRightTenant = ledger_void_entry($pdo, $tenantA, $entryIdA); m8_assert('Storno ueber den richtigen Mandanten funktioniert', $voidedRightTenant === true, $failures, $passes); // 8. Audit-Log ist mandantenscoped. diff --git a/scripts/http-smoke.php b/scripts/http-smoke.php index cdb5716..641dd8a 100644 --- a/scripts/http-smoke.php +++ b/scripts/http-smoke.php @@ -51,7 +51,7 @@ $checks = [ 'path' => 'kaffeeliste.php', 'contains' => [ 'Aktive Mitarbeiter', - 'teilnehmerauswertung.php?user_id=', + 'teilnehmerauswertung.php?participant_id=', 'GM Negativ', 'gm-negative@test.local', '-2,00 €', diff --git a/stricheintragen.php b/stricheintragen.php index b75ad7d..1653765 100644 --- a/stricheintragen.php +++ b/stricheintragen.php @@ -44,48 +44,15 @@ if (!$hasAccess) { } /** - * Liefert die Mitarbeiter/Teilnehmerliste fuer die Vorder-/Rueckseiten- oder - * Alle-Auswahl. Fuer den Default-Mandanten bleibt die Fensterlogik exakt wie - * im Legacy-Bestand (Anker ist das juengste Datum in kl_Kaffeeverbrauch); - * andere Mandanten haben keine Legacy-Historie und nutzen die - * Ledger-native Fensterpruefung mit dem konfigurierten Listenfenster. + * Liefert die Teilnehmerliste fuer die Vorder-/Rueckseiten- oder + * Alle-Auswahl. Vorderseite sind die Vieltrinker (>= 10 Striche im + * Listenfenster), Rueckseite der Rest. * * @return list */ function stricheintragen_fetch_participants(PDO $pdo, int $tenantId, string $action): array { - $isDefault = ledger_is_default_tenant($pdo, $tenantId); - - if ($isDefault && $action === 'vorderseite') { - $stmt = $pdo->query( - "SELECT M.MitarbeiterID - FROM kl_Mitarbeiter M - JOIN kl_Kaffeeverbrauch V ON M.MitarbeiterID = V.MitarbeiterID - WHERE V.Datum >= DATE_SUB((SELECT MAX(Datum) FROM kl_Kaffeeverbrauch WHERE Datum < CURDATE()), INTERVAL 100 DAY) - AND M.aktiv = 1 - GROUP BY M.MitarbeiterID - HAVING SUM(V.AnzahlStriche) >= 10" - ); - $legacyIds = array_map('intval', $stmt->fetchAll(PDO::FETCH_COLUMN)); - - return ledger_fetch_participant_summaries($pdo, $tenantId, ['legacy_mitarbeiter_ids' => $legacyIds]); - } - - if ($isDefault && $action === 'rueckseite') { - $stmt = $pdo->query( - "SELECT M.MitarbeiterID - FROM kl_Mitarbeiter M - LEFT JOIN kl_Kaffeeverbrauch V ON M.MitarbeiterID = V.MitarbeiterID AND V.Datum >= DATE_SUB((SELECT MAX(Datum) FROM kl_Kaffeeverbrauch), INTERVAL 100 DAY) - WHERE M.aktiv = 1 - GROUP BY M.MitarbeiterID - HAVING COALESCE(SUM(V.AnzahlStriche), 0) < 10" - ); - $legacyIds = array_map('intval', $stmt->fetchAll(PDO::FETCH_COLUMN)); - - return ledger_fetch_participant_summaries($pdo, $tenantId, ['legacy_mitarbeiter_ids' => $legacyIds]); - } - - if (!$isDefault && ($action === 'vorderseite' || $action === 'rueckseite')) { + if ($action === 'vorderseite' || $action === 'rueckseite') { $settings = saas_fetch_tenant_settings($pdo, $tenantId); $windowDays = $settings['sheet_window_days'] ?? 100; @@ -104,41 +71,27 @@ if ($_SERVER["REQUEST_METHOD"] == "POST") { $hatGespeichert = true; $kostenproStrich = floatval($_POST["kostenproStrich"] ?? 0); $unitPriceCents = (int)round($kostenproStrich * 100); - $datum = date("Y-m-d H:i:s"); - // Legacy-Verknuepfung je Teilnehmer vorab laden, damit pro Zeile - // entschieden werden kann, ob (Default-Mandant) zusaetzlich in - // kl_Kaffeeverbrauch geschrieben oder (jeder andere Mandant) direkt - // ins Ledger gebucht wird. - $legacyMap = []; - $stmt = $pdo->prepare('SELECT id, legacy_mitarbeiter_id FROM participants WHERE tenant_id = ?'); + // Teilnehmer des Mandanten - dient der Zugehoerigkeitspruefung, damit ueber + // das Formular keine fremden IDs bebucht werden koennen. + $eigeneTeilnehmer = []; + $stmt = $pdo->prepare('SELECT 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; + foreach ($stmt->fetchAll(PDO::FETCH_COLUMN) as $id) { + $eigeneTeilnehmer[(int)$id] = true; } try { $pdo->beginTransaction(); - $insertLegacy = $pdo->prepare( - "INSERT INTO kl_Kaffeeverbrauch (MitarbeiterID, AnzahlStriche, Kosten, KostenproStrich, Datum) VALUES (?, ?, ?, ?, ?)" - ); foreach ($_POST["anzahlStriche"] ?? [] as $participantId => $anzahlStriche) { $participantId = (int)$participantId; $anzahlStriche = (int)$anzahlStriche; - if ($participantId <= 0 || $anzahlStriche === 0 || !array_key_exists($participantId, $legacyMap)) { + if ($participantId <= 0 || $anzahlStriche === 0 || !isset($eigeneTeilnehmer[$participantId])) { continue; } - $legacyMitarbeiterId = $legacyMap[$participantId]; - if ($legacyMitarbeiterId !== null) { - $kosten = $anzahlStriche * $kostenproStrich; - $insertLegacy->execute([$legacyMitarbeiterId, $anzahlStriche, $kosten, $kostenproStrich, $datum]); - $legacyConsumptionId = (int)$pdo->lastInsertId(); - ledger_mirror_legacy_consumption($pdo, $tenantId, $legacyConsumptionId); - } else { - ledger_record_consumption($pdo, $tenantId, $participantId, $anzahlStriche, $unitPriceCents, 'manual_bulk'); - } + ledger_record_consumption($pdo, $tenantId, $participantId, $anzahlStriche, $unitPriceCents, 'manual_bulk'); $eingetragen++; } diff --git a/teilnehmerauswertung.php b/teilnehmerauswertung.php index d5f1dfb..57ba055 100644 --- a/teilnehmerauswertung.php +++ b/teilnehmerauswertung.php @@ -3,14 +3,19 @@ include "functions.php"; require_once __DIR__ . "/app/ledger.php"; -$legacyUserId = filter_input(INPUT_GET, 'user_id', FILTER_VALIDATE_INT); -if ($legacyUserId === null) { +// Teilnehmer-ID aus dem Journal. "user_id" bleibt als Alias erhalten, damit +// aeltere Links und Lesezeichen weiter funktionieren. +$participantId = filter_input(INPUT_GET, 'participant_id', FILTER_VALIDATE_INT); +if ($participantId === null || $participantId === false) { + $participantId = filter_input(INPUT_GET, 'user_id', FILTER_VALIDATE_INT); +} +if ($participantId === null) { http_response_code(400); exit('Fehlender Parameter'); } -if ($legacyUserId === false || $legacyUserId <= 0) { +if ($participantId === false || $participantId <= 0) { http_response_code(400); - exit('Ungültige Benutzer-ID'); + exit('Ungültige Teilnehmer-ID'); } $pdo = app_db_pdo(); @@ -32,7 +37,7 @@ if (!$hasAccess && $saasUser === null && checkKaffeelisteAdmin($conn, $mailadres } $participant = $hasAccess - ? ledger_fetch_participant_summary_by_legacy_id($pdo, $tenantId, $legacyUserId) + ? ledger_fetch_participant_summary($pdo, $tenantId, $participantId) : null; $settings = $hasAccess ? saas_fetch_tenant_settings($pdo, $tenantId) : null; $paymentEntries = $participant !== null