Legacy-Abbau Schritt 1: Dual-Write in die kl_*-Tabellen entfernt

Die Migration in participants/ledger_entries ist abgeschlossen: kein
Teilnehmer traegt noch eine legacy_mitarbeiter_id, kein Journaleintrag eine
legacy_table. Damit war der jeweils zweite Zweig ("Default-Mandant schreibt
zusaetzlich nach kl_Einzahlungen/kl_Kaffeeverbrauch/kl_Mitarbeiter") in
sechs Dateien nicht mehr erreichbar - er musste aber bei jeder Aenderung
mitgepflegt werden, zuletzt bei der Bemerkung fuer Einzahlungen.

Entfernt:
- Dual-Write beim Buchen: einzahlung.php, stricheintragen.php, index.php,
  jahresauswertung.php, app/imports.php, app/paypal-inbox.php
- Dual-Write in der Mitgliederverwaltung: ledger_create_participant,
  ledger_update_participant, ledger_set_participant_active,
  ledger_anonymize_participant
- die verwaisten Spiegelfunktionen ledger_mirror_legacy_payment,
  ledger_mirror_legacy_consumption und ledger_void_entry_by_legacy_id

Nebenbei behoben: kaffeeliste.php hat die Teilnehmer-Detailseite nur fuer
Mitglieder mit legacy_mitarbeiter_id verlinkt - die hat seit der Migration
niemand mehr, die Seite war also fuer alle unerreichbar. Verlinkt und
adressiert wird jetzt ueber participant_id; "user_id" bleibt als Alias
erhalten. Der Mandanten-Isolationstest prueft jetzt ledger_void_entry, also
den Pfad, den letzteneintraege.php tatsaechlich nutzt.

Pruefskripte unveraendert gegenueber der Baseline vor dem Umbau
(http-smoke 27/6, role-matrix 55/0); die Isolationspruefung steigt von
9 auf 11 PASS bei gleichem vorbestehendem Fehler.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-21 20:57:49 +02:00
co-authored by Claude Opus 4.8
parent dd6e1e77b0
commit eec7a2fef2
11 changed files with 98 additions and 482 deletions
+12 -59
View File
@@ -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<array>
*/
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++;
}