M4 Ledger-Preview als read-only App-Seite ergaenzen
- neue ledger-preview.php mit Tenant-Summen und letzten Ledger-Buchungen bauen - Admin-Navigation und HTTP-Smoke um Ledger-Preview erweitern - M4- und Meilenstein-Doku auf Preview-Stand aktualisieren
This commit is contained in:
@@ -140,9 +140,9 @@ Die Skripte erwarten die bekannten Dev-Umgebungsvariablen `DB_HOST`, `DB_NAME`,
|
|||||||
eine PayPal-Alias-Dublette und hinterlaesst keine Datei in `var/uploads`.
|
eine PayPal-Alias-Dublette und hinterlaesst keine Datei in `var/uploads`.
|
||||||
- CSV-Upload negative Tests: Nicht-CSV-Dateien werden abgewiesen.
|
- CSV-Upload negative Tests: Nicht-CSV-Dateien werden abgewiesen.
|
||||||
- Golden Master weiterhin gruen mit 104 Assertions.
|
- Golden Master weiterhin gruen mit 104 Assertions.
|
||||||
- HTTP-Smoke weiterhin gruen mit 22 sicheren Seiten inklusive Landingpage, Login,
|
- HTTP-Smoke weiterhin gruen mit 23 sicheren Seiten inklusive Landingpage, Login,
|
||||||
Registrierung, Passwort-Reset, E-Mail-Verifikation und geschuetzter
|
Registrierung, Passwort-Reset, E-Mail-Verifikation und geschuetzter
|
||||||
Mandant-Einstellungen sowie geschuetzter Mandantenauswahl.
|
Mandant-Einstellungen, geschuetzter Mandantenauswahl sowie Ledger-Preview.
|
||||||
|
|
||||||
## Bewusste Grenzen
|
## Bewusste Grenzen
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# M4 Datenmigration Fachlicher Kern
|
# M4 Datenmigration Fachlicher Kern
|
||||||
|
|
||||||
Stand: 2026-07-13
|
Stand: 2026-07-14
|
||||||
|
|
||||||
M4 uebernimmt die finanznahen Legacy-Buchungen additiv in das neue
|
M4 uebernimmt die finanznahen Legacy-Buchungen additiv in das neue
|
||||||
tenant-sichere Ledger-Modell. Die bestehenden Legacy-Seiten lesen und schreiben
|
tenant-sichere Ledger-Modell. Die bestehenden Legacy-Seiten lesen und schreiben
|
||||||
@@ -48,6 +48,7 @@ Ergaenzende App-Dateien:
|
|||||||
|
|
||||||
```text
|
```text
|
||||||
app/ledger.php
|
app/ledger.php
|
||||||
|
ledger-preview.php
|
||||||
```
|
```
|
||||||
|
|
||||||
Ausgefuehrter Dev-Stand:
|
Ausgefuehrter Dev-Stand:
|
||||||
@@ -58,6 +59,8 @@ Ausgefuehrter Dev-Stand:
|
|||||||
- Ein zweiter Backfill-Lauf blieb idempotent und erzeugte keine Dubletten.
|
- Ein zweiter Backfill-Lauf blieb idempotent und erzeugte keine Dubletten.
|
||||||
- `app/ledger.php` stellt zentrale Abfragen fuer Tenant-Summen,
|
- `app/ledger.php` stellt zentrale Abfragen fuer Tenant-Summen,
|
||||||
Teilnehmer-Summen, einzelne Teilnehmer und letzte Buchungen bereit.
|
Teilnehmer-Summen, einzelne Teilnehmer und letzte Buchungen bereit.
|
||||||
|
- `ledger-preview.php` zeigt eine read-only App-Vorschau auf Tenant-Summen,
|
||||||
|
aktive Teilnehmer und die letzten Ledger-Buchungen.
|
||||||
|
|
||||||
## Abbildungsregeln
|
## Abbildungsregeln
|
||||||
|
|
||||||
@@ -143,6 +146,7 @@ Installation aus `.local/` verwendet werden.
|
|||||||
Master: erfuellt.
|
Master: erfuellt.
|
||||||
- Tenant-Summen, aktive Teilnehmer, Einzelteilnehmer und letzte Buchungen sind
|
- Tenant-Summen, aktive Teilnehmer, Einzelteilnehmer und letzte Buchungen sind
|
||||||
ueber `app/ledger.php` abrufbar: erfuellt.
|
ueber `app/ledger.php` abrufbar: erfuellt.
|
||||||
|
- Read-only Ledger-Preview ist im Browser erreichbar: erfuellt.
|
||||||
|
|
||||||
## Aktueller Pruefstatus
|
## Aktueller Pruefstatus
|
||||||
|
|
||||||
@@ -155,11 +159,11 @@ Installation aus `.local/` verwendet werden.
|
|||||||
- M3 Settings-Flow: weiterhin gruen.
|
- M3 Settings-Flow: weiterhin gruen.
|
||||||
- M3 Mail-Flow: weiterhin gruen.
|
- M3 Mail-Flow: weiterhin gruen.
|
||||||
- Golden Master: weiterhin gruen mit 104 Assertions.
|
- Golden Master: weiterhin gruen mit 104 Assertions.
|
||||||
- HTTP-Smoke: weiterhin gruen mit 22 geprueften Seiten.
|
- HTTP-Smoke: weiterhin gruen mit 23 geprueften Seiten inklusive
|
||||||
|
`ledger-preview.php`.
|
||||||
|
|
||||||
## Noch offen in M4
|
## Noch offen in M4
|
||||||
|
|
||||||
- Erste read-only App-Seite oder Preview auf Basis von `app/ledger.php`.
|
|
||||||
- Tenant-sichere Dashboard-, Strich-, Einzahlungs- und Listenqueries schrittweise
|
- Tenant-sichere Dashboard-, Strich-, Einzahlungs- und Listenqueries schrittweise
|
||||||
von Legacy-Tabellen auf das Ledger umstellen.
|
von Legacy-Tabellen auf das Ledger umstellen.
|
||||||
- Storno-/Reversal-Modell in der UI statt harter Deletes.
|
- Storno-/Reversal-Modell in der UI statt harter Deletes.
|
||||||
|
|||||||
@@ -304,7 +304,7 @@ Uebersicht:
|
|||||||
| M1 | Golden Master | Legacy-Ergebnisse sind als Vergleichsbasis eingefroren; HTTP-Smoke prueft sichere Seiten |
|
| M1 | Golden Master | Legacy-Ergebnisse sind als Vergleichsbasis eingefroren; HTTP-Smoke prueft sichere Seiten |
|
||||||
| M2 | Technisches Fundament | Abgeschlossen: Migrationen, Bootstrap, Session, CSRF-Helper und Legacy-Schreibseitenschutz stehen |
|
| M2 | Technisches Fundament | Abgeschlossen: Migrationen, Bootstrap, Session, CSRF-Helper und Legacy-Schreibseitenschutz stehen |
|
||||||
| M3 | SaaS-Basis | Abgeschlossen: Tenants, User, Registrierung, Login, Rollen, Mail-Links und zentrale Mandantenauswahl funktionieren |
|
| M3 | SaaS-Basis | Abgeschlossen: Tenants, User, Registrierung, Login, Rollen, Mail-Links und zentrale Mandantenauswahl funktionieren |
|
||||||
| M4 | Datenmigration | Gestartet: Ledger-Tabelle, Legacy-Backfill, Paritaetscheck und Ledger-Service sind umgesetzt |
|
| M4 | Datenmigration | Gestartet: Ledger-Tabelle, Legacy-Backfill, Paritaetscheck, Ledger-Service und Preview sind umgesetzt |
|
||||||
| M5 | App-Kern | Dashboard, Striche, Einzahlungen, Mitglieder und Liste laufen |
|
| M5 | App-Kern | Dashboard, Striche, Einzahlungen, Mitglieder und Liste laufen |
|
||||||
| M6 | Betriebsflows | Import, Export, Mail und Jahresprozesse sind auditierbar |
|
| M6 | Betriebsflows | Import, Export, Mail und Jahresprozesse sind auditierbar |
|
||||||
| M7 | Landingpage | Public-Seite und Auth-Seiten sind im gemeinsamen Stil nutzbar; spaetere Ausbaustufen folgen |
|
| M7 | Landingpage | Public-Seite und Auth-Seiten sind im gemeinsamen Stil nutzbar; spaetere Ausbaustufen folgen |
|
||||||
@@ -482,6 +482,7 @@ Schritte:
|
|||||||
- Salden gegen Golden-Master vergleichen: Kontrollskript erledigt.
|
- Salden gegen Golden-Master vergleichen: Kontrollskript erledigt.
|
||||||
- Ledger-Service fuer Tenant-Summen, Teilnehmer-Summen und letzte Buchungen:
|
- Ledger-Service fuer Tenant-Summen, Teilnehmer-Summen und letzte Buchungen:
|
||||||
erledigt.
|
erledigt.
|
||||||
|
- Read-only Ledger-Preview fuer Browservergleich: erledigt.
|
||||||
|
|
||||||
Ergebnis:
|
Ergebnis:
|
||||||
|
|
||||||
@@ -490,6 +491,7 @@ Ergebnis:
|
|||||||
nachweisbar.
|
nachweisbar.
|
||||||
- Erste App-Abfragen koennen ueber `app/ledger.php` tenant-sicher aus dem neuen
|
- Erste App-Abfragen koennen ueber `app/ledger.php` tenant-sicher aus dem neuen
|
||||||
Modell lesen.
|
Modell lesen.
|
||||||
|
- `ledger-preview.php` zeigt die neue Ledger-Auswertung ohne Schreibzugriffe.
|
||||||
- Dokumentation: `docs/m4-data-migration.md`.
|
- Dokumentation: `docs/m4-data-migration.md`.
|
||||||
|
|
||||||
Abhaengigkeiten:
|
Abhaengigkeiten:
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ if(checkKaffeelisteAdmin($conn, $mailadress)){
|
|||||||
<li><a href="einzahlung.php">Einzahlung eintragen</a></li>
|
<li><a href="einzahlung.php">Einzahlung eintragen</a></li>
|
||||||
<li><a href="stricheintragen.php"> Striche eintragen</a></li>
|
<li><a href="stricheintragen.php"> Striche eintragen</a></li>
|
||||||
<li><a href="kaffeeliste.php">Kaffeeliste anzeigen</a></li>
|
<li><a href="kaffeeliste.php">Kaffeeliste anzeigen</a></li>
|
||||||
|
<li><a href="ledger-preview.php">Ledger Preview</a></li>
|
||||||
<li><a href="mitarbeiterverwalten.php">Mitglieder verwalten</a></li>
|
<li><a href="mitarbeiterverwalten.php">Mitglieder verwalten</a></li>
|
||||||
<li><a href="hinweise.php">Hinweise verwalten</a></li>
|
<li><a href="hinweise.php">Hinweise verwalten</a></li>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,160 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
require_once __DIR__ . '/functions.php';
|
||||||
|
require_once __DIR__ . '/app/ledger.php';
|
||||||
|
|
||||||
|
$pdo = app_db_pdo();
|
||||||
|
$saasUser = saas_current_user($pdo);
|
||||||
|
$hasAccess = false;
|
||||||
|
$tenantId = 0;
|
||||||
|
$tenantName = '';
|
||||||
|
$tenantSlug = '';
|
||||||
|
|
||||||
|
if ($saasUser !== null && saas_user_has_role(['owner', 'admin', 'treasurer'], $saasUser)) {
|
||||||
|
$hasAccess = true;
|
||||||
|
$tenantId = (int)$saasUser['tenant_id'];
|
||||||
|
$tenantName = (string)$saasUser['tenant_name'];
|
||||||
|
$tenantSlug = (string)$saasUser['tenant_slug'];
|
||||||
|
} elseif (checkKaffeelisteAdmin($conn, $mailadress)) {
|
||||||
|
$defaultTenantSlug = getenv('M3_DEFAULT_TENANT_SLUG');
|
||||||
|
$defaultTenantSlug = $defaultTenantSlug !== false && trim($defaultTenantSlug) !== ''
|
||||||
|
? trim($defaultTenantSlug)
|
||||||
|
: 'default';
|
||||||
|
$stmt = $pdo->prepare('SELECT id, name, slug FROM tenants WHERE slug = ? LIMIT 1');
|
||||||
|
$stmt->execute([$defaultTenantSlug]);
|
||||||
|
$tenant = $stmt->fetch();
|
||||||
|
|
||||||
|
if ($tenant !== false) {
|
||||||
|
$hasAccess = true;
|
||||||
|
$tenantId = (int)$tenant['id'];
|
||||||
|
$tenantName = (string)$tenant['name'];
|
||||||
|
$tenantSlug = (string)$tenant['slug'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$year = ledger_current_year();
|
||||||
|
$tenantTotals = $hasAccess ? ledger_fetch_tenant_totals($pdo, $tenantId, $year) : ledger_empty_totals();
|
||||||
|
$participantSummaries = $hasAccess ? ledger_fetch_participant_summaries($pdo, $tenantId, [
|
||||||
|
'active_only' => true,
|
||||||
|
'year' => $year,
|
||||||
|
]) : [];
|
||||||
|
$recentEntries = $hasAccess ? ledger_fetch_recent_entries($pdo, $tenantId, ['limit' => 25]) : [];
|
||||||
|
|
||||||
|
function ledger_preview_money(int $cents): string
|
||||||
|
{
|
||||||
|
return saas_format_money_cents($cents) . ' EUR';
|
||||||
|
}
|
||||||
|
|
||||||
|
function ledger_preview_entry_type(string $type): string
|
||||||
|
{
|
||||||
|
if ($type === 'payment') {
|
||||||
|
return 'Einzahlung';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($type === 'consumption') {
|
||||||
|
return 'Kaffeeverbrauch';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $type;
|
||||||
|
}
|
||||||
|
|
||||||
|
include 'header.php';
|
||||||
|
include 'headerline.php';
|
||||||
|
include 'nav.php';
|
||||||
|
?>
|
||||||
|
|
||||||
|
<section id="banner">
|
||||||
|
<div class="content">
|
||||||
|
<h2>Ledger Preview</h2>
|
||||||
|
|
||||||
|
<?php if (!$hasAccess): ?>
|
||||||
|
<div class="hint-box error"><p>Kein Zugriff.</p></div>
|
||||||
|
<?php else: ?>
|
||||||
|
<p>
|
||||||
|
Read-only Vorschau aus <code>ledger_entries</code> fuer
|
||||||
|
<?php echo saas_html($tenantName); ?>
|
||||||
|
(<code><?php echo saas_html($tenantSlug); ?></code>).
|
||||||
|
Die bestehenden App-Seiten schreiben weiterhin in die Legacy-Tabellen.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>Tenant-Summen</h3>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Gesamteinzahlungen</th>
|
||||||
|
<th>Gesamtausgaben</th>
|
||||||
|
<th>Gesamtstriche</th>
|
||||||
|
<th>Aktueller Saldo</th>
|
||||||
|
<th>Einzahlungen <?php echo $year; ?></th>
|
||||||
|
<th>Ausgaben <?php echo $year; ?></th>
|
||||||
|
<th>Striche <?php echo $year; ?></th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><?php echo ledger_preview_money($tenantTotals['total_payments_cents']); ?></td>
|
||||||
|
<td><?php echo ledger_preview_money($tenantTotals['total_costs_cents']); ?></td>
|
||||||
|
<td><?php echo number_format($tenantTotals['total_marks'], 0, ',', '.'); ?></td>
|
||||||
|
<td><?php echo ledger_preview_money($tenantTotals['balance_cents']); ?></td>
|
||||||
|
<td><?php echo ledger_preview_money($tenantTotals['year_payments_cents']); ?></td>
|
||||||
|
<td><?php echo ledger_preview_money($tenantTotals['year_costs_cents']); ?></td>
|
||||||
|
<td><?php echo number_format($tenantTotals['year_marks'], 0, ',', '.'); ?></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h3>Aktive Teilnehmer</h3>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>E-Mail</th>
|
||||||
|
<th>Aktueller Stand</th>
|
||||||
|
<th>Gesamtausgaben</th>
|
||||||
|
<th>Gesamtstriche</th>
|
||||||
|
<th>Gesamteinzahlungen</th>
|
||||||
|
<th>Striche <?php echo $year; ?></th>
|
||||||
|
</tr>
|
||||||
|
<?php foreach ($participantSummaries as $summary): ?>
|
||||||
|
<tr>
|
||||||
|
<td><?php echo saas_html($summary['display_name']); ?></td>
|
||||||
|
<td><?php echo saas_html($summary['email'] ?? ''); ?></td>
|
||||||
|
<td><?php echo ledger_preview_money($summary['balance_cents']); ?></td>
|
||||||
|
<td><?php echo ledger_preview_money($summary['total_costs_cents']); ?></td>
|
||||||
|
<td><?php echo number_format($summary['total_marks'], 0, ',', '.'); ?></td>
|
||||||
|
<td><?php echo ledger_preview_money($summary['total_payments_cents']); ?></td>
|
||||||
|
<td><?php echo number_format($summary['year_marks'], 0, ',', '.'); ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h3>Letzte Ledger-Buchungen</h3>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Datum</th>
|
||||||
|
<th>Teilnehmer</th>
|
||||||
|
<th>Typ</th>
|
||||||
|
<th>Betrag</th>
|
||||||
|
<th>Striche</th>
|
||||||
|
<th>Quelle</th>
|
||||||
|
<th>Legacy</th>
|
||||||
|
</tr>
|
||||||
|
<?php foreach ($recentEntries as $entry): ?>
|
||||||
|
<tr>
|
||||||
|
<td><?php echo saas_html($entry['booked_at']); ?></td>
|
||||||
|
<td><?php echo saas_html($entry['display_name']); ?></td>
|
||||||
|
<td><?php echo saas_html(ledger_preview_entry_type($entry['type'])); ?></td>
|
||||||
|
<td><?php echo ledger_preview_money($entry['amount_cents']); ?></td>
|
||||||
|
<td><?php echo $entry['marks_count'] !== null ? number_format($entry['marks_count'], 0, ',', '.') : ''; ?></td>
|
||||||
|
<td><?php echo saas_html($entry['source']); ?></td>
|
||||||
|
<td>
|
||||||
|
<?php
|
||||||
|
echo saas_html((string)($entry['legacy_table'] ?? ''));
|
||||||
|
if ($entry['legacy_id'] !== null) {
|
||||||
|
echo ' #' . (int)$entry['legacy_id'];
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</table>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<?php include 'footer.php'; ?>
|
||||||
@@ -31,6 +31,11 @@ $checks = [
|
|||||||
'path' => 'kaffeeliste.php',
|
'path' => 'kaffeeliste.php',
|
||||||
'contains' => ['Aktive Mitarbeiter'],
|
'contains' => ['Aktive Mitarbeiter'],
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'label' => 'Ledger Preview',
|
||||||
|
'path' => 'ledger-preview.php',
|
||||||
|
'contains' => ['Ledger Preview', 'Tenant-Summen', 'Letzte Ledger-Buchungen'],
|
||||||
|
],
|
||||||
[
|
[
|
||||||
'label' => 'Mitgliederverwaltung',
|
'label' => 'Mitgliederverwaltung',
|
||||||
'path' => 'mitarbeiterverwalten.php',
|
'path' => 'mitarbeiterverwalten.php',
|
||||||
|
|||||||
Reference in New Issue
Block a user