diff --git a/Changes.md b/Changes.md index 7b0a24c..44d5929 100644 --- a/Changes.md +++ b/Changes.md @@ -5,11 +5,13 @@ - Die Kachel *Vermögensentwicklung* war ein Platzhalter und zeigt jetzt die Kontostände der letzten zwölf Monate je Monatsende – ein Balken pro Monat, ein Farbton pro Konto (`Vermoegensentwicklung::reihe()`, `src/Views/cockpit/vermoegensdiagramm.php`). -- Gezählt wird, was auch in der Kontenübersicht in die Summe zählt: Konten mit - Σ-Kennzeichen. Negative Stände stapeln unter der Nulllinie. +- Negative Stände stapeln unter der Nulllinie. - Schalter *nur aktive* oben rechts an der Kachel, im Standard eingeschaltet: Er blendet - stillgelegte Konten aus und merkt sich seinen Zustand über die Einstellungen - (`cockpit_vermoegen_nur_aktive`) – auch über das Abmelden hinaus. + stillgelegte Konten samt Legendeneintrag aus und merkt sich seinen Zustand über die + Einstellungen (`cockpit_vermoegen_nur_aktive`) – auch über das Abmelden hinaus. Er ist + das einzige Kriterium; das Σ-Kennzeichen filtert hier **nicht** mit, weil stillgelegte + Konten es meist nicht tragen und beim Ausschalten sonst genau das fehlte, was man sehen + will. - `Buchung::bewegungenJeKontoUndMonat()` liefert die Monatsbewegungen in einer Abfrage je Quelle – zwölf Stichtagsabfragen wären es sonst. - Mehr Konten als Farbtöne: Die größten behalten ihre Farbe, der Rest wird zu *Übrige diff --git a/docs/cockpit.md b/docs/cockpit.md index bb9fbb0..3257d89 100644 --- a/docs/cockpit.md +++ b/docs/cockpit.md @@ -10,9 +10,13 @@ Monate – ein Balken je Monat, ein Farbton je Konto. ### Woher die Zahlen kommen -`Vermoegensentwicklung::reihe()` zählt dieselben Konten wie die Summe der Kontenübersicht: -die mit gesetztem Σ-Kennzeichen (`in_summe`). Der Stand eines Monats ist der Stand des -Vormonats plus dessen Bewegungen: +Welche Konten zählen, entscheidet allein der Schalter „nur aktive“. Das Σ-Kennzeichen +(`in_summe`) spielt hier **keine** Rolle – anders als in der Kontenübersicht. Stillgelegte +Konten tragen es meist nicht; würde es mitfiltern, fiele beim Ausschalten des Schalters +genau das weg, was man sehen will. Die Summe der Kachel kann bei ausgeschaltetem Schalter +deshalb von der Summe der Kontenübersicht abweichen. + +Der Stand eines Monats ist der Stand des Vormonats plus dessen Bewegungen: * `Buchung::saldenJeKonto()` liefert den Stand am Tag vor dem Zeitraum – alles Ältere steckt damit im ersten Balken. @@ -63,11 +67,17 @@ Die Achse rundet über `Vermoegensentwicklung::skala()` auf Schritte von 1, 2, 2 mal einer Zehnerpotenz und schließt die Null immer ein. Jedes Balkenstück trägt ein `` mit Konto, Monat und Betrag – das ist der Tooltip, ohne eine Zeile JavaScript. -Die Legende führt nur Farbe und Kontoname. Die Beträge stehen am Balken im Tooltip und in +Die Maße stehen in echten Bildpunkten: Ein Balken ist 18 Punkte breit, zwischen zwei +Monaten bleiben 3. Damit das auch auf dem Schirm so ankommt, trägt das `<svg>` Breite und +Höhe als Attribute und wird **nicht** auf die Kachel gestreckt; `max-width: 100%` lässt es +erst mitschrumpfen, wenn die Kachel schmaler wird als die Zeichnung. + +Die Legende führt nur Farbe und Kontoname und steht neben der Zeichnung – die schmale +Zeichnung ließe sonst die halbe Kachel leer. Die Beträge stehen am Balken im Tooltip und in der Kachel *Kontostände*; in der Legende kosteten sie nur Breite. Die Kachel greift über -zwei Spalten des Cockpit-Rasters (`.kachelraster.dicht`) – schmal genug, um die übrigen -Kacheln danebenstehen zu lassen, breit genug für zwölf Balken. Unterhalb von 820 px fällt -der Ausgriff weg, sonst erzwänge er im einspaltigen Raster einen waagerechten Rollbalken. +zwei Spalten des Cockpit-Rasters (`.kachelraster.dicht`). Unterhalb von 820 px fällt der +Ausgriff weg, sonst erzwänge er im einspaltigen Raster einen waagerechten Rollbalken; dort +rutscht auch die Legende unter die Zeichnung. ## Tests diff --git a/public/static/app.css b/public/static/app.css index d5401d4..f7dea5e 100644 --- a/public/static/app.css +++ b/public/static/app.css @@ -371,11 +371,22 @@ form.inline { display: inline; margin: 0; } .serie-11 { --farbe: #e07b91; } .serie-12 { --farbe: #7b8a99; } +/* Das SVG bringt seine Maße als Attribute mit und wird nicht auf die Kachel + gestreckt: Nur so sind die 18 Punkte Balkenbreite auch 18 Bildpunkte. Erst + wenn die Kachel schmaler wird als die Zeichnung, skaliert sie mit. */ +.diagrammbereich { + display: flex; + flex-wrap: wrap; + align-items: flex-start; + gap: .5rem 1.5rem; +} + .diagramm { - width: 100%; + max-width: 100%; height: auto; display: block; overflow: visible; + flex: 0 1 auto; } .diagramm rect { fill: var(--farbe); } @@ -390,27 +401,34 @@ form.inline { display: inline; margin: 0; } stroke-width: 1.5; } -/* Die Schrift skaliert mit dem viewBox: In der schmalen Kachel schrumpft sie um - ein Viertel, deshalb hier größer als im Fließtext angesetzt. */ .diagramm-achse, .diagramm-beschriftung { fill: var(--text-gedaempft); font-family: var(--schrift); - font-size: 14px; + font-size: 10px; } -.diagramm-achse { font-family: var(--schrift-zahl); } +/* Ziffernschrift für die Achse, aber schmal genug für 21 Punkte Spaltenabstand + bleiben die Monatskürzel darunter. */ +.diagramm-achse { + font-family: var(--schrift-zahl); + font-size: 11px; +} .diagramm-beschriftung.schwach { opacity: .7; } +/* Neben der Zeichnung statt darunter: Die schmale Zeichnung ließe sonst die + halbe Kachel leer. */ .diagrammlegende { list-style: none; - margin: .9rem 0 0; + margin: 0; padding: 0; + flex: 1 1 11rem; display: grid; - grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr)); + grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr)); gap: .1rem 1.2rem; font-size: .88rem; + align-content: start; } .diagrammlegende li { diff --git a/src/Services/Vermoegensentwicklung.php b/src/Services/Vermoegensentwicklung.php index 977592c..cf790e5 100644 --- a/src/Services/Vermoegensentwicklung.php +++ b/src/Services/Vermoegensentwicklung.php @@ -11,10 +11,10 @@ use Finanzmanager\Models\Konto; * Die Kontostände der letzten zwölf Monate, je Konto und Monatsende – Grundlage * der Cockpit-Kachel *Vermögensentwicklung*. * - * Gezählt wird, was auch in der Kontenübersicht in die Summe zählt: Konten mit - * gesetztem Kennzeichen `in_summe`. Der Schalter „nur aktive“ an der Kachel - * entscheidet, ob stillgelegte Konten dazugehören: ausgeschaltet zeigt er auch - * das Vermögen, das damals auf inzwischen aufgelösten Konten lag. + * Einziges Kriterium ist der Schalter „nur aktive“ an der Kachel: eingeschaltet + * zählen die geführten Konten, ausgeschaltet alle. Das Σ-Kennzeichen (`in_summe`) + * bleibt hier ausdrücklich außen vor – stillgelegte Konten tragen es meist nicht, + * und dann fiele beim Ausschalten genau das weg, was man sehen will. */ class Vermoegensentwicklung { @@ -54,10 +54,6 @@ class Vermoegensentwicklung $summen = array_fill(0, count($monate), 0.0); foreach (Konto::alle($nurAktive) as $konto) { - if ((int)$konto['in_summe'] !== 1) { - continue; - } - $id = (int)$konto['id']; $stand = (float)$konto['anfangssaldo'] + ($anfang[$id] ?? 0.0); $werte = []; diff --git a/src/Views/cockpit/vermoegensdiagramm.php b/src/Views/cockpit/vermoegensdiagramm.php index e77b49d..e343d8c 100644 --- a/src/Views/cockpit/vermoegensdiagramm.php +++ b/src/Views/cockpit/vermoegensdiagramm.php @@ -44,17 +44,22 @@ foreach ($monate as $nummer => $monat) { $skala = Vermoegensentwicklung::skala($grenzwerte); -$breite = 760; -$hoehe = 250; -$links = 82; -$rechts = 10; -$oben = 12; -$unten = 40; +// Maße in echten Bildpunkten: Das SVG trägt Breite und Höhe als Attribute und +// wird deshalb nicht auf die Kachel gestreckt. Ein Balken ist 18 Punkte breit, +// zwischen zwei Monaten bleiben 3 – erst unterhalb der eigenen Breite skaliert +// die Zeichnung mit (max-width in app.css). +$balken = 18; +$luecke = 3; +$links = 52; +$rechts = 6; +$oben = 10; +$unten = 32; -$plotB = $breite - $links - $rechts; -$plotH = $hoehe - $oben - $unten; -$spalte = $plotB / max(1, $anzahl); -$balken = min(40.0, $spalte * 0.62); +$spalte = $balken + $luecke; +$plotB = $spalte * $anzahl; +$plotH = 150; +$breite = $links + $plotB + $rechts; +$hoehe = $oben + $plotH + $unten; $y = static function (float $wert) use ($skala, $oben, $plotH): float { return round($oben + $plotH * ($skala['max'] - $wert) / ($skala['max'] - $skala['min']), 2); @@ -76,7 +81,9 @@ $zeitraum = $monate[0]['kuerzel'] . ' ' . $monate[0]['jahrKurz'] . ' bis ' . $monate[$anzahl - 1]['kuerzel'] . ' ' . $monate[$anzahl - 1]['jahrKurz']; ?> -<svg class="diagramm" viewBox="0 0 <?= $breite ?> <?= $hoehe ?>" role="img" +<div class="diagrammbereich"> +<svg class="diagramm" width="<?= $breite ?>" height="<?= $hoehe ?>" + viewBox="0 0 <?= $breite ?> <?= $hoehe ?>" role="img" preserveAspectRatio="xMidYMid meet" aria-label="Kontostände je Monatsende, <?= View::h($zeitraum) ?>"> <title>Vermögensentwicklung <?= View::h($zeitraum) ?> @@ -121,10 +128,10 @@ $zeitraum = $monate[0]['kuerzel'] . ' ' . $monate[0]['jahrKurz'] - - @@ -140,3 +147,4 @@ $zeitraum = $monate[0]['kuerzel'] . ' ' . $monate[0]['jahrKurz'] + diff --git a/tests/13_VermoegensentwicklungTest.php b/tests/13_VermoegensentwicklungTest.php index 24a0825..4b0a130 100644 --- a/tests/13_VermoegensentwicklungTest.php +++ b/tests/13_VermoegensentwicklungTest.php @@ -28,7 +28,7 @@ class VermoegensentwicklungTest extends TestCase $this->zwoelfMonateBisHeute(); $this->staendeWachsenMonatFuerMonat(); $this->umbuchungVerschiebtZwischenKonten(); - $this->ohneSummenkennzeichenKeinBalken(); + $this->summenkennzeichenFiltertNicht(); $this->schalterFiltertStillgelegteKonten(); $this->mehrKontenAlsFarbenWerdenZusammengefasst(); $this->skalaRundetAufLesbareSchritte(); @@ -119,8 +119,10 @@ class VermoegensentwicklungTest extends TestCase // Die Kreditkarte steht im Minus – sie zieht die Summe herunter. $karte = $this->konto($reihe, $this->kreditkarte); $this->assertEquals(-200.0, $karte['werte'][11], 'Negativer Stand bleibt negativ'); - $this->assertEquals(1600.0, $reihe['summen'][11], 'Summe ist Giro minus Kreditkarte'); $this->assert($reihe['hatWerte'], 'Es gibt etwas zu zeichnen'); + + // Giro 1800, Kreditkarte -200, Auslagen 5000, Sparbuch 0. + $this->assertEquals(6600.0, $reihe['summen'][11], 'Die Summe ist der Stapel aller Konten'); } private function umbuchungVerschiebtZwischenKonten(): void @@ -144,13 +146,18 @@ class VermoegensentwicklungTest extends TestCase ); } - private function ohneSummenkennzeichenKeinBalken(): void + /** + * Das Σ-Kennzeichen entscheidet hier nichts: Stillgelegte Konten tragen es + * meist nicht, und dann fiele beim Ausschalten des Schalters genau das weg, + * was man sehen will. + */ + private function summenkennzeichenFiltertNicht(): void { $reihe = $this->reihe(); - $this->assertNull( + $this->assertNotEmpty( $this->konto($reihe, $this->auslagen), - 'Ein Konto ohne Σ-Kennzeichen zeichnet keinen Balken' + 'Ein aktives Konto ohne Σ-Kennzeichen zeichnet trotzdem einen Balken' ); // Ein Konto, das im ganzen Zeitraum auf null steht, bläht nur die Legende auf.