From 83a90f2d3dda63f7ae39eb2897394a9aabf26ec3 Mon Sep 17 00:00:00 2001 From: fds2610-bot Date: Mon, 12 Oct 2026 16:07:42 +0200 Subject: [PATCH] =?UTF-8?q?Fix=20#219:=20Bank=20buchen=20standardm=C3=A4?= =?UTF-8?q?=C3=9Fig=20=C3=A4lteste=20Ums=C3=A4tze=20zuerst?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Changes.md | 5 ++ docs/funktionen.md | 1 + src/Models/BankTransaction.php | 2 +- src/Views/bank/index.php | 4 +- .../104_BankSortierungAeltesteZuerstTest.php | 69 +++++++++++++++++++ tests/e2e/specs/bank-sortierung.spec.js | 18 +++++ 6 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 tests/104_BankSortierungAeltesteZuerstTest.php create mode 100644 tests/e2e/specs/bank-sortierung.spec.js diff --git a/Changes.md b/Changes.md index 86929db..9707574 100644 --- a/Changes.md +++ b/Changes.md @@ -2,6 +2,11 @@ --- +## 2026-10-12 – Bank buchen: älteste Umsätze zuerst (#219) + +- **`src/Models/BankTransaction.php`** (`all()`) **und `src/Views/bank/index.php`:** Die Standard-Sortierrichtung ist jetzt aufsteigend statt absteigend – ohne `dir`-Parameter erscheinen die ältesten Umsätze oben, damit datumsgetreu gebucht wird und die Buchungsnummern zeitlich fortlaufend entstehen. Im Filter ist „↑ Aufsteigend“ vorbelegt; „↓ Absteigend“ bleibt über `dir=desc` wählbar, unbekannte Werte fallen auf aufsteigend zurück. +- **`tests/104_BankSortierungAeltesteZuerstTest.php`** (neu) und **`tests/e2e/specs/bank-sortierung.spec.js`** (neu): prüfen die Standardreihenfolge, `dir=desc` und die Vorbelegung im Filter. + ## 2026-10-06 – Header-Injection über Dateinamen im Content-Disposition-Header behoben (#217) - **`src/View.php`:** neue Methode `View::contentDispositionHeader(string $disposition, string $filename): string`. `PATHINFO_BASENAME` entfernt beim Beleg-Upload nur Verzeichnisanteile, nicht aber Anführungszeichen oder Backslashes im client-seitig gewählten Dateinamen – ein präparierter Name wie `legit"; filename*=UTF-8''%2e%2e%2fevil.exe` brach beim Download aus dem `filename="..."`-Parameter aus und schleuste einen zusätzlichen, von vielen Browsern bevorzugt ausgewerteten `filename*=`-Parameter ein. Damit ließ sich der im Download-Dialog angezeigte Dateiname/die Endung gegenüber anderen Nutzern fälschen (Dateityp-Spoofing). Die neue Methode maskiert Anführungszeichen, Backslashes und Steuerzeichen im ASCII-`filename`-Parameter und setzt zusätzlich RFC-6266-konform `filename*=UTF-8''...` mit dem unveränderten, `rawurlencode()`-kodierten Namen. diff --git a/docs/funktionen.md b/docs/funktionen.md index 6b0e39f..a7909d0 100644 --- a/docs/funktionen.md +++ b/docs/funktionen.md @@ -106,6 +106,7 @@ Anlagenspiegel für abnutzbare Wirtschaftsgüter – siehe [anlagevermögen.md]( - **Beleg-Zuordnung**: jede Banktransaktion kann einem oder mehreren Belegen zugeordnet werden; der zugeordnete Betrag wird separat erfasst - **Teilzuordnung**: eine Transaktion kann schrittweise mehreren Belegen anteilig zugeordnet werden - **Status-Logik**: `offen` → keine Zuordnung, `teilweise` → Restsaldo > 0,01 €, `gebucht` → vollständig zugeordnet +- **Sortierung**: Die Liste unter `/bank` zeigt standardmäßig die ältesten Umsätze zuerst (Wertstellung aufsteigend), damit datumsgetreu gebucht wird und die Buchungsnummern zeitlich fortlaufend vergeben werden; Sortierfeld und Richtung (*Absteigend*) sind im Filter umstellbar - Direkter Beleg-Upload aus der Transaktionsansicht heraus (erzeugt neuen Beleg und verknüpft ihn) - **Verschlüsselung**: Der `APP_KEY` in `.env` ist Pflicht für Direktverbindungen – im Backup mitsichern, Verlust = Verlust aller gespeicherten Credentials diff --git a/src/Models/BankTransaction.php b/src/Models/BankTransaction.php index a464b8a..6065cd4 100644 --- a/src/Models/BankTransaction.php +++ b/src/Models/BankTransaction.php @@ -55,7 +55,7 @@ class BankTransaction 'status' => 'bt.status', ]; $sortCol = $sortMap[$filter['sort'] ?? ''] ?? 'bt.wertstellung'; - $sortDir = ($filter['dir'] ?? 'desc') === 'asc' ? 'ASC' : 'DESC'; + $sortDir = ($filter['dir'] ?? 'asc') === 'desc' ? 'DESC' : 'ASC'; $whereStr = implode(' AND ', $where); $offset = ($page - 1) * $perPage; diff --git a/src/Views/bank/index.php b/src/Views/bank/index.php index 8d2fe93..6dc12fe 100644 --- a/src/Views/bank/index.php +++ b/src/Views/bank/index.php @@ -86,8 +86,8 @@ $months = ['Jan','Feb','Mär','Apr','Mai','Jun','Jul','Aug','Sep','Okt','Nov','D diff --git a/tests/104_BankSortierungAeltesteZuerstTest.php b/tests/104_BankSortierungAeltesteZuerstTest.php new file mode 100644 index 0000000..9d1815d --- /dev/null +++ b/tests/104_BankSortierungAeltesteZuerstTest.php @@ -0,0 +1,69 @@ + array_column($result['rows'], 'wertstellung'); + + $standard = BankTransaction::all(['search' => $marker]); + $this->assertEquals( + ['2026-01-05', '2026-02-10', '2026-03-15'], + $daten($standard), + 'Ohne dir-Parameter: älteste Wertstellung zuerst' + ); + + $absteigend = BankTransaction::all(['search' => $marker, 'dir' => 'desc']); + $this->assertEquals( + ['2026-03-15', '2026-02-10', '2026-01-05'], + $daten($absteigend), + 'dir=desc: neueste Wertstellung zuerst' + ); + + $ungueltig = BankTransaction::all(['search' => $marker, 'dir' => 'quatsch']); + $this->assertEquals( + ['2026-01-05', '2026-02-10', '2026-03-15'], + $daten($ungueltig), + 'Ungültiger dir-Wert fällt auf aufsteigend zurück' + ); + + $view = (string)file_get_contents(dirname(__DIR__) . '/src/Views/bank/index.php'); + $this->assert( + str_contains($view, "(\$filter['dir'] ?? 'asc') === 'asc'"), + 'Filterauswahl zeigt „Aufsteigend“ als Vorbelegung' + ); + $this->assert( + !str_contains($view, "(\$filter['dir'] ?? 'desc')"), + 'Filterauswahl belegt nicht mehr „Absteigend“ vor' + ); + } +} diff --git a/tests/e2e/specs/bank-sortierung.spec.js b/tests/e2e/specs/bank-sortierung.spec.js new file mode 100644 index 0000000..a04dd6f --- /dev/null +++ b/tests/e2e/specs/bank-sortierung.spec.js @@ -0,0 +1,18 @@ +// @ts-check +// „Bank buchen“ sortiert standardmäßig älteste zuerst (Issue #219): Ohne +// Parameter ist im Filter „Aufsteigend“ vorbelegt, „Absteigend“ bleibt wählbar. + +const { test, expect } = require('@playwright/test') +const { gotoOk } = require('../helpers/app') + +test.describe('Bank buchen – Sortierrichtung', () => { + test('ohne Parameter ist „Aufsteigend“ vorbelegt', async ({ page }) => { + await gotoOk(page, '/bank') + await expect(page.locator('select[name="dir"]')).toHaveValue('asc') + }) + + test('dir=desc bleibt ausgewählt', async ({ page }) => { + await gotoOk(page, '/bank?dir=desc') + await expect(page.locator('select[name="dir"]')).toHaveValue('desc') + }) +})