M5 abschliessen: Schreibseiten aufs Ledger umstellen, Storno statt Delete

- stricheintragen.php und einzahlung.php erhielten bisher gar keine
  Zugriffskontrolle (nur CSRF), jetzt Rollen-/Legacy-Fallback wie in
  kaffeeliste.php; Sammeleintraege spiegeln transaktional ins Ledger
  (ledger_mirror_legacy_payment neu ergaenzt).
- letzteneintraege.php war rein ueber Legacy-Admin gesperrt und haette
  neue SaaS-Mandanten ausgeschlossen; Loeschen markiert den gespiegelten
  Ledger-Eintrag jetzt per voided_at statt ihn zu entfernen
  (ledger_void_entry_by_legacy_id).
- check-m4-ledger-migration.php an das Storno-Modell angepasst: verwaiste
  Ledger-Zeilen sind nur noch ein Fehler, wenn sie nicht voided sind.
- Nebenbei: PHP-Warning bei Sammelerfassung behoben, toten Code entfernt,
  veraltete README (verwies auf nicht existierende saas-app/-Struktur)
  korrigiert.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-14 23:43:39 +02:00
co-authored by Claude Sonnet 5
parent aa9444f076
commit 968a55f442
7 changed files with 344 additions and 120 deletions
+68
View File
@@ -319,6 +319,74 @@ function ledger_mirror_legacy_consumption(PDO $pdo, int $tenantId, int $legacyCo
}
}
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',
NULL,
'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)"
);
$stmt->execute([$tenantId, $legacyPaymentId]);
$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.');
}
}
/**
* Marks the ledger entry mirrored from a legacy row as voided instead of
* deleting it, so corrections stay auditable. Idempotent: voiding an
* already-voided or missing entry is a no-op and returns false.
*/
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;
}
/**
* Options:
* - participant_id: int