From ed58490fc286fb74fbdda9d64b6554d1247cee95 Mon Sep 17 00:00:00 2001 From: fds2610-bot Date: Sun, 2 Aug 2026 20:21:57 +0200 Subject: [PATCH] Fix #166: Buchungszeilen ohne Anlage-Spalte, Typ in der ersten Spalte --- Changes.md | 6 ++-- docs/anlagevermögen.md | 4 ++- src/View.php | 4 ++- src/Views/belege/detail.php | 19 +++------- tests/e2e/specs/beleg-buchung.spec.js | 51 +++++++++++++++++++++------ 5 files changed, 55 insertions(+), 29 deletions(-) diff --git a/Changes.md b/Changes.md index cf1e49c..255e002 100644 --- a/Changes.md +++ b/Changes.md @@ -23,8 +23,10 @@ ### Buchungszeilen brauchen weniger Breite (#166) -- **`src/Views/belege/detail.php`:** Buchungsnummer und Buchungsdatum teilen sich in der Belegansicht eine Spalte (Nummer oben, Datum darunter) – wie bereits bei Kategorie- und Zahlungskonto. Die Tabelle ist damit eine Spalte schmaler und ohne waagerechtes Scrollen lesbar. -- **`tests/e2e/specs/beleg-buchung.spec.js`:** Neuer Test auf die zusammengelegte Spalte; die Betragsprüfungen der bestehenden Tests greifen jetzt auf die verschobene Zelle zu. +- **`src/Views/belege/detail.php`:** Buchungsnummer, Buchungsdatum und Buchungstyp teilen sich in der Belegansicht eine Spalte (Nummer oben, Datum darunter, Typ-Badge zuunterst) – wie bereits bei Kategorie- und Zahlungskonto. Die Spalte **Anlage** entfällt ersatzlos; das Wirtschaftsgut ist weiterhin über Journal, UStVA- und EÜR-Drill-down sowie die Anlagenliste erreichbar. Von acht Spalten bleiben sechs, die Tabelle ist ohne waagerechtes Scrollen lesbar. +- **`src/View.php`:** `formatMoney()` setzt zwischen Betrag und `€` ein geschütztes Leerzeichen. In schmalen Spalten rutschte das Währungszeichen sonst allein in die nächste Zeile. Wirkt in allen HTML-Ansichten; die PDF-Dienste haben eigene Formatierer und bleiben unberührt. +- **`docs/anlagevermögen.md`:** Der Abschnitt „Anzeige in Buchungstabellen" nennt die Beleg-Detailansicht nicht mehr unter den Übersichten mit Anlage-Spalte. +- **`tests/e2e/specs/beleg-buchung.spec.js`:** Tests auf die zusammengelegte Spalte (inklusive Typ und entfallener Anlage-Spalte) und darauf, dass Betrag und Währungszeichen in einer Zeile bleiben; die Betragsprüfungen der bestehenden Tests greifen jetzt auf die verschobene Zelle zu. ### Nebenbei diff --git a/docs/anlagevermögen.md b/docs/anlagevermögen.md index 6ab83ba..00f7750 100644 --- a/docs/anlagevermögen.md +++ b/docs/anlagevermögen.md @@ -52,7 +52,9 @@ In beiden Fällen wird die `asset_id` in der Buchungszeile gespeichert. ## Anzeige in Buchungstabellen -In allen Buchungsübersichten (Beleg-Detail, Journal, UStVA-Drill-down, EÜR-Drill-down) erscheint eine **Anlage**-Spalte. Sie zeigt die Anlagen-Nummer als direkten Link zur Bearbeitungsmaske des Wirtschaftsguts. Falls für ein älteres Wirtschaftsgut keine Anlagen-Nummer hinterlegt ist, wird stattdessen die Bezeichnung angezeigt. +In den Buchungsübersichten Journal, UStVA-Drill-down und EÜR-Drill-down erscheint eine **Anlage**-Spalte. Sie zeigt die Anlagen-Nummer als direkten Link zur Bearbeitungsmaske des Wirtschaftsguts. Falls für ein älteres Wirtschaftsgut keine Anlagen-Nummer hinterlegt ist, wird stattdessen die Bezeichnung angezeigt. + +In der **Beleg-Detailansicht** entfällt die Spalte zugunsten einer schmaleren Tabelle; das zugehörige Wirtschaftsgut ist dort über das Journal oder die Anlagenliste erreichbar. --- diff --git a/src/View.php b/src/View.php index ec0d51c..94207a4 100644 --- a/src/View.php +++ b/src/View.php @@ -76,7 +76,9 @@ class View public static function formatMoney(mixed $value): string { - return number_format((float)$value, 2, ',', '.') . ' €'; + // Geschütztes Leerzeichen: in schmalen Tabellenspalten rutscht das + // Währungszeichen sonst allein in die nächste Zeile. + return number_format((float)$value, 2, ',', '.') . "\u{00A0}€"; } public static function formatDate(?string $date): string diff --git a/src/Views/belege/detail.php b/src/Views/belege/detail.php index f67d976..ad8cb62 100644 --- a/src/Views/belege/detail.php +++ b/src/Views/belege/detail.php @@ -218,13 +218,11 @@ $suppliers = $suppliers ?? []; - - + - @@ -236,9 +234,9 @@ $suppliers = $suppliers ?? []; - @@ -250,15 +248,6 @@ $suppliers = $suppliers ?? []; Storniert - - +
Buchungsnr.
Datum
TypBuchungsnr.
Datum
Typ
Kategorie Zahlungskonto Wert TextAnlage

- +
+


- - - - - - – - -
@@ -300,7 +289,7 @@ $suppliers = $suppliers ?? [];
Noch keine Buchungen.
Noch keine Buchungen.
diff --git a/tests/e2e/specs/beleg-buchung.spec.js b/tests/e2e/specs/beleg-buchung.spec.js index ccc0856..6aba4dd 100644 --- a/tests/e2e/specs/beleg-buchung.spec.js +++ b/tests/e2e/specs/beleg-buchung.spec.js @@ -73,7 +73,7 @@ test.describe('Beleg-Upload und Buchung', () => { const zeile = page.locator('table tbody tr', { hasText: tag }).first() await expect(zeile).toHaveCount(1) - expect(parseMoney(await zeile.locator('td').nth(4).textContent())).toBeCloseTo(100.0, 2) + expect(parseMoney(await zeile.locator('td').nth(3).textContent())).toBeCloseTo(100.0, 2) }) test('Buchung im BRUTTO-Modus wird vor dem Absenden in Netto umgerechnet', async ({ page }) => { @@ -98,7 +98,7 @@ test.describe('Beleg-Upload und Buchung', () => { // Kern der Regression: Gespeichert werden muss der Nettobetrag 100,00 — // nicht die eingegebenen 119,00. - const gespeichert = parseMoney(await zeile.locator('td').nth(4).textContent()) + const gespeichert = parseMoney(await zeile.locator('td').nth(3).textContent()) expect(gespeichert, 'Bruttoeingabe wurde nicht in Netto umgerechnet').toBeCloseTo(100.0, 2) }) @@ -131,13 +131,14 @@ test.describe('Beleg-Upload und Buchung', () => { const zeile = page.locator('table tbody tr', { hasText: tag }).first() await expect(zeile).toHaveCount(1) - expect(parseMoney(await zeile.locator('td').nth(4).textContent())).toBeCloseTo(100.0, 2) + expect(parseMoney(await zeile.locator('td').nth(3).textContent())).toBeCloseTo(100.0, 2) }) - // Issue #166: Buchungsnummer und Buchungsdatum teilen sich eine Spalte, - // damit die Tabelle ohne waagerechtes Scrollen lesbar bleibt. Prüfbar nur - // am gerenderten DOM — die Zellaufteilung existiert nur in der View. - test('Buchungsnummer und Datum stehen in einer gemeinsamen Spalte', async ({ page }) => { + // Issue #166: Buchungsnummer, Buchungsdatum und Typ teilen sich eine Spalte, + // die Spalte „Anlage" entfällt — damit die Tabelle ohne waagerechtes Scrollen + // lesbar bleibt. Prüfbar nur am gerenderten DOM: die Zellaufteilung existiert + // nur in der View. + test('Buchungsnummer, Datum und Typ stehen in einer gemeinsamen Spalte', async ({ page }) => { const { id, tag } = await uploadBeleg(page) belegId = id @@ -149,17 +150,47 @@ test.describe('Beleg-Upload und Buchung', () => { const zeile = page.locator('table tbody tr', { hasText: tag }).first() await expect(zeile).toHaveCount(1) - // Erste Zelle trägt beides: Buchungsnummer (BU-JAHR-NNNNNN) und Datum. + // Erste Zelle trägt alle drei Angaben: Buchungsnummer (BU-JAHR-NNNNNN), + // Datum und Buchungstyp. const ersteZelle = await zeile.locator('td').nth(0).textContent() expect(ersteZelle).toMatch(/BU-\d{4}-\d+/) expect(ersteZelle).toMatch(/\d{2}\.\d{2}\.\d{4}/) + expect(ersteZelle).toMatch(/Einnahme|Ausgabe/) - // Die Kopfzeile führt beide Bezeichnungen in einer einzigen Spalte. + // Die Kopfzeile führt alle drei Bezeichnungen in einer einzigen Spalte; + // „Anlage" gibt es nicht mehr. const kopf = page.locator('table thead tr').first() const ersteKopfzelle = await kopf.locator('th').nth(0).textContent() expect(ersteKopfzelle).toContain('Buchungsnr.') expect(ersteKopfzelle).toContain('Datum') - await expect(kopf.locator('th')).toHaveCount(8) + expect(ersteKopfzelle).toContain('Typ') + await expect(kopf.locator('th')).toHaveCount(6) + await expect(kopf.locator('th', { hasText: 'Anlage' })).toHaveCount(0) + }) + + // Der Betrag darf nicht zwischen Zahl und Währungszeichen umbrechen. + test('Betrag und Währungszeichen bleiben zusammen', async ({ page }) => { + const { id, tag } = await uploadBeleg(page) + belegId = id + + await buchungsmaskeVorbereiten(page, { buchungstext: tag }) + await page.fill('#betrag_netto_input', '1234.56') + await page.click('#buchung_form button[type="submit"]') + await page.waitForLoadState('networkidle') + + const wert = page.locator('table tbody tr', { hasText: tag }).first().locator('td').nth(3) + const text = await wert.textContent() + // Geschütztes Leerzeichen (U+00A0) statt eines gewöhnlichen Leerzeichens. + expect(text).toBe('1.234,56\u00A0€') + + // Und der Betrag steht auch gerendert auf einer einzigen Zeile — ein + // Umbruch zwischen Zahl und Währungszeichen ergäbe zwei Rechtecke. + const fragmente = await wert.evaluate((el) => { + const r = document.createRange() + r.selectNodeContents(el) + return r.getClientRects().length + }) + expect(fragmente).toBe(1) }) // Hinweis: Dass ein Beleg mit Buchungen nicht löschbar ist, ist reine