Fix #131: Offene Vorgänge des Bargeld-Tools im Buchungsbericht

Die offenen Vorgänge „im Bargeld-Tool nicht gefunden“ und „Bargeld-Tool-
Prüfung fehlgeschlagen“ führen in den Buchungsbericht mit dem Filter
„Bargeld-Tool“ – dort steht die Spalte Kategorie/Gegenkonto mit. Die
eigene Liste unter /verwaltung/bargeld/fehler entfällt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
fds2610-bot 2026-10-12 21:51:41 +02:00
parent a2867bd806
commit c6d5babb41
13 changed files with 132 additions and 167 deletions

View file

@ -31,6 +31,10 @@ Das Format folgt [Keep a Changelog](https://keepachangelog.com/de/1.1.0/).
### Fixed ### Fixed
- Offene Vorgänge: „Im Bargeld-Tool nicht gefunden“ und „Bargeld-Tool-Prüfung fehlgeschlagen“
führen jetzt in den Buchungsbericht mit dem passenden Filter „Bargeld-Tool“ – dort steht die
Spalte Kategorie (bzw. das Gegenkonto) mit; die eigene Liste unter
`/verwaltung/bargeld/fehler` entfällt (#131)
- Anmeldeseite: Die Marke zeigt wie die Seitenleiste nach dem Anmelden das Coinstack-Symbol - Anmeldeseite: Die Marke zeigt wie die Seitenleiste nach dem Anmelden das Coinstack-Symbol
und den fetten, zweifarbigen Schriftzug „Finanzmanager“ statt einer grauen Statusmarke (#130) und den fetten, zweifarbigen Schriftzug „Finanzmanager“ statt einer grauen Statusmarke (#130)
- Buchungsmaske: Eine falsche Splitsumme zeigte zwar die Fehlermeldung, riss aber über die - Buchungsmaske: Eine falsche Splitsumme zeigte zwar die Fehlermeldung, riss aber über die

View file

@ -1,5 +1,13 @@
# Änderungen # Änderungen
## Offene Vorgänge des Bargeld-Tools im Buchungsbericht (12.10.2026)
- Anlass (#131): Der Liste „Im Bargeld-Tool nicht gefunden“, aufgerufen aus *Offene Vorgänge* im Cockpit, fehlte die Spalte Kategorie/Gegenkonto. Gewünscht war statt einer eigenen Liste ein Filter im Buchungsbericht.
- Den Filter „Bargeld-Tool“ hat der Buchungsbericht schon; die beiden offenen Vorgänge „Bargeld-Tool-Prüfung fehlgeschlagen“ und „im Bargeld-Tool nicht gefunden“ führen jetzt dorthin (`Cockpit::bargeldBericht()`): gesamter Zeitraum, Umbuchungen eingeschlossen, Filter „Abruf gescheitert“ bzw. „nicht gefunden“. Der Bericht zeigt Konto, Kategorie bzw. Gegenkonto in eckigen Klammern und Klasse; die Meldung der Gegenstelle steht im Tooltip der Spalte „Bar“.
- Ist einer dieser beiden Filter gesetzt, erklärt der Hinweis unter dem Bericht den Status und dass eine erneute Zuordnung der Kategorie die Prüfung erneut anstößt – der Text der bisherigen Liste.
- Entfernt: die Seite `/verwaltung/bargeld/fehler` (`VerwaltungController::bargeldFehler()`, `Views/verwaltung/bargeld_fehler.php`) und `Buchung::mitBargeldFehler()`.
- Tests: `tests/34_BargeldAbgleichTest.php`, `tests/15_CockpitTest.php`, `tests/e2e/specs/bargeld.spec.js`.
## Anmeldeseite: Marke wie in der Seitenleiste (12.10.2026) ## Anmeldeseite: Marke wie in der Seitenleiste (12.10.2026)
- Anlass (#130): Icon und Schrift des Logos auf der Anmeldeseite wichen von der Marke im angemeldeten Zustand ab. - Anlass (#130): Icon und Schrift des Logos auf der Anmeldeseite wichen von der Marke im angemeldeten Zustand ab.

View file

@ -311,11 +311,12 @@ Adresse hinterlegt, entfällt die Spalte ganz; sie zeigte sonst in jeder Zeile d
grauen Kreis. grauen Kreis.
Buchungen mit dem Status `fehler` zählen im Cockpit als offener Vorgang „Bargeld-Tool-Prüfung Buchungen mit dem Status `fehler` zählen im Cockpit als offener Vorgang „Bargeld-Tool-Prüfung
fehlgeschlagen“ (siehe `docs/cockpit.md`) – ein Klick führt auf fehlgeschlagen“ (siehe `docs/cockpit.md`) – ein Klick führt in den Buchungsbericht über den
`/verwaltung/bargeld/fehler`, die kontoübergreifende Liste dieser Buchungen gesamten Zeitraum, gefiltert auf „Bargeld-Tool: Abruf gescheitert“. Buchungen mit dem Status
(`Buchung::mitBargeldFehler()`). Buchungen mit dem Status `fehlt` zählen als eigener offener `fehlt` zählen als eigener offener Vorgang „im Bargeld-Tool nicht gefunden“ und führen in
Vorgang „im Bargeld-Tool nicht gefunden“ und führen auf dieselbe Seite mit `?status=fehlt` denselben Bericht mit dem Filter „nicht gefunden“. Der Bericht zeigt Konto, Kategorie bzw.
(`Buchung::mitBargeldFehler('fehlt')`). Gegenkonto und Klasse; die Meldung der Gegenstelle steht im Tooltip der Spalte „Bar“. Eine
eigene Liste dafür gibt es nicht mehr (#131).
**Ein Fehlschlag ist ein Ergebnis, kein Abbruch.** Weder ein abgeschaltetes Bargeld-Tool **Ein Fehlschlag ist ein Ergebnis, kein Abbruch.** Weder ein abgeschaltetes Bargeld-Tool
noch ein Netzfehler darf verhindern, dass die Kategorie gespeichert wird; der Abruf bricht noch ein Netzfehler darf verhindern, dass die Kategorie gespeichert wird; der Abruf bricht

View file

@ -181,11 +181,12 @@ Prüfungen, die gerade nichts melden und daher nicht in der Liste stehen:
* **nicht abgeglichen** – Buchungen aktiver Konten bis heute ohne Abgleich-Kennzeichen. * **nicht abgeglichen** – Buchungen aktiver Konten bis heute ohne Abgleich-Kennzeichen.
* **Bargeld-Tool-Prüfung fehlgeschlagen** – Buchungen mit `bargeld_status = 'fehler'`, also * **Bargeld-Tool-Prüfung fehlgeschlagen** – Buchungen mit `bargeld_status = 'fehler'`, also
einem gescheiterten Abruf beim Bargeld-Tool (siehe `docs/buchungen.md`), nicht zu einem gescheiterten Abruf beim Bargeld-Tool (siehe `docs/buchungen.md`), nicht zu
verwechseln mit „im Bargeld-Tool nicht gefunden“. Führt auf verwechseln mit „im Bargeld-Tool nicht gefunden“. Führt in den Buchungsbericht über den
`/verwaltung/bargeld/fehler`, die Liste dieser Buchungen kontoübergreifend gesamten Zeitraum mit dem Filter „Bargeld-Tool: Abruf gescheitert“ – kontoübergreifend,
(`Buchung::mitBargeldFehler()`), denn die Prüfung hängt an der Kategorie, nicht am Konto. denn die Prüfung hängt an der Kategorie, nicht am Konto, und samt Spalte Kategorie (#131).
* **Im Bargeld-Tool nicht gefunden** – Buchungen mit `bargeld_status = 'fehlt'`. Führt auf * **Im Bargeld-Tool nicht gefunden** – Buchungen mit `bargeld_status = 'fehlt'`. Führt in
`/verwaltung/bargeld/fehler?status=fehlt` (`Buchung::mitBargeldFehler('fehlt')`). denselben Buchungsbericht mit dem Filter „Bargeld-Tool: nicht gefunden“
(`Cockpit::bargeldBericht()`).
* **Monatsabschluss steht aus** – der Vorschlag aus `Monatsabschluss::naechsterOffener()`, * **Monatsabschluss steht aus** – der Vorschlag aus `Monatsabschluss::naechsterOffener()`,
aber nur, wenn dieser Monat wirklich noch offen ist und Buchungen trägt. aber nur, wenn dieser Monat wirklich noch offen ist und Buchungen trägt.

View file

@ -139,7 +139,6 @@ $router->post('/verwaltung/kontengruppen/{id}/loeschen',
$router->get('/verwaltung/bargeld', fn() => (new VerwaltungController())->bargeld()); $router->get('/verwaltung/bargeld', fn() => (new VerwaltungController())->bargeld());
$router->post('/verwaltung/bargeld', fn() => (new VerwaltungController())->bargeldSpeichern()); $router->post('/verwaltung/bargeld', fn() => (new VerwaltungController())->bargeldSpeichern());
$router->post('/verwaltung/bargeld/test', fn() => (new VerwaltungController())->bargeldTest()); $router->post('/verwaltung/bargeld/test', fn() => (new VerwaltungController())->bargeldTest());
$router->get('/verwaltung/bargeld/fehler', fn() => (new VerwaltungController())->bargeldFehler());
// Das Bankleitzahlen-Verzeichnis: nachschlagen und die Institutsliste einlesen. // Das Bankleitzahlen-Verzeichnis: nachschlagen und die Institutsliste einlesen.
$router->get('/verwaltung/bankinstitute', fn() => (new BankinstituteController())->index()); $router->get('/verwaltung/bankinstitute', fn() => (new BankinstituteController())->index());

View file

@ -6,7 +6,6 @@ namespace Finanzmanager\Controllers;
use Finanzmanager\Models\Adresse; use Finanzmanager\Models\Adresse;
use Finanzmanager\Models\Bankinstitut; use Finanzmanager\Models\Bankinstitut;
use Finanzmanager\Models\Buchung;
use Finanzmanager\Models\Einstellung; use Finanzmanager\Models\Einstellung;
use Finanzmanager\Models\Erinnerung; use Finanzmanager\Models\Erinnerung;
use Finanzmanager\Models\Kategorie; use Finanzmanager\Models\Kategorie;
@ -278,26 +277,6 @@ class VerwaltungController extends BaseController
View::redirect('/verwaltung/bargeld', $ergebnis['meldung'], $ergebnis['erfolg'] ? 'erfolg' : 'fehler'); View::redirect('/verwaltung/bargeld', $ergebnis['meldung'], $ergebnis['erfolg'] ? 'erfolg' : 'fehler');
} }
/**
* Alle Buchungen, an denen die Bargeld-Tool-Prüfung zuletzt einen Fehler
* festgestellt hat – der Weg, den die Kachel „Offene Vorgänge“ des
* Cockpit anbietet, sobald es solche Buchungen gibt.
*/
public function bargeldFehler(): void
{
$this->verlangeAnmeldung();
$fehlt = ($_GET['status'] ?? '') === 'fehlt';
$this->render('verwaltung/bargeld_fehler.php', [
'titel' => $fehlt
? 'Bargeld-Tool: nicht gefundene Buchungen'
: 'Bargeld-Tool: fehlgeschlagene Prüfungen',
'fehlt' => $fehlt,
'buchungen' => Buchung::mitBargeldFehler($fehlt ? 'fehlt' : 'fehler'),
]);
}
/** Die gesamte Datenbank als Download. */ /** Die gesamte Datenbank als Download. */
public function sicherungHerunterladen(): void public function sicherungHerunterladen(): void
{ {

View file

@ -510,28 +510,6 @@ class Buchung
return $zeilen; return $zeilen;
} }
/**
* Alle Buchungen, an denen die Bargeld-Tool-Prüfung zuletzt einen Fehler
* ('fehler') oder keinen Treffer ('fehlt') ergeben hat – kontoübergreifend, denn die Prüfung hängt an der
* Kategorie der Buchung, nicht an ihrem Konto. Der Status 'fehler' steht
* für einen gescheiterten Abruf (siehe Services\BargeldAbgleich::FEHLER),
* 'fehlt' für eine im Bargeld-Tool nicht gefundene Buchung.
*
* @return list<array<string,mixed>>
*/
public static function mitBargeldFehler(string $status = 'fehler'): array
{
return Database::fetchAll(
'SELECT b.id, b.datum, b.betrag, b.empfaenger, b.buchungstext,
b.bargeld_hinweis, b.bargeld_geprueft_am, b.konto_id, k.name AS konto_name
FROM buchungen b
JOIN konten k ON k.id = b.konto_id
WHERE b.benutzer_id = ? AND b.bargeld_status = ?
ORDER BY b.datum DESC, b.id DESC',
[Mandant::id(), $status === 'fehlt' ? 'fehlt' : 'fehler']
);
}
/** /**
* Salden aller Konten in einem Rutsch: Anfangssaldo plus Buchungen, plus die * Salden aller Konten in einem Rutsch: Anfangssaldo plus Buchungen, plus die
* Gegenseiten fremder Umbuchungen. Ohne Stichtag zählt alles (erwarteter * Gegenseiten fremder Umbuchungen. Ohne Stichtag zählt alles (erwarteter

View file

@ -301,7 +301,7 @@ class Cockpit
'schluessel' => 'bargeld_fehler', 'schluessel' => 'bargeld_fehler',
'text' => 'Bargeld-Tool-Prüfung fehlgeschlagen', 'text' => 'Bargeld-Tool-Prüfung fehlgeschlagen',
'anzahl' => $bargeldFehler, 'anzahl' => $bargeldFehler,
'pfad' => '/verwaltung/bargeld/fehler', 'pfad' => self::bargeldBericht(BargeldAbgleich::FEHLER),
]; ];
} }
@ -317,7 +317,7 @@ class Cockpit
'schluessel' => 'bargeld_fehlt', 'schluessel' => 'bargeld_fehlt',
'text' => 'im Bargeld-Tool nicht gefunden', 'text' => 'im Bargeld-Tool nicht gefunden',
'anzahl' => $bargeldFehlt, 'anzahl' => $bargeldFehlt,
'pfad' => '/verwaltung/bargeld/fehler?status=fehlt', 'pfad' => self::bargeldBericht(BargeldAbgleich::FEHLT),
]; ];
} }
@ -370,4 +370,21 @@ class Cockpit
return $vorgaenge; return $vorgaenge;
} }
/**
* Der Buchungsbericht über den gesamten Zeitraum, gefiltert auf einen
* Status des Bargeld-Tools – mit Kategorie bzw. Gegenkonto in eckigen
* Klammern (#131). Umbuchungen zählen mit, damit die Liste genau die
* Buchungen zeigt, die der offene Vorgang zählt.
*/
private static function bargeldBericht(string $status): string
{
return '/auswertungen/bericht?' . http_build_query([
'typ' => 'buchungsbericht',
'zeitraum' => 'alles',
'umbuchungen' => '1',
'bargeld' => $status,
'angepasst' => '1',
]);
}
} }

View file

@ -89,4 +89,14 @@ use Finanzmanager\View;
dem Summenbericht überein. dem Summenbericht überein.
<?php endif; ?> <?php endif; ?>
Der Empfänger führt in die Buchungsmaske. Der Empfänger führt in die Buchungsmaske.
<?php if (in_array($bericht['filter']['bargeld'], ['fehlt', 'fehler'], true)): ?>
<?php if ($bericht['filter']['bargeld'] === 'fehlt'): ?>
Zu diesen Buchungen wurde im Bargeld-Tool keine Ausgabe mit demselben Betrag gefunden.
<?php else: ?>
Bei diesen Buchungen ist der letzte Abruf beim Bargeld-Tool gescheitert – ein
Abrufproblem, keine Aussage darüber, ob die Buchung dort steht.
<?php endif; ?>
Die Meldung steht am Symbol in der Spalte „Bar“; eine erneute Zuordnung der Kategorie
in der Buchungsmaske oder im Kontoblatt stößt die Prüfung erneut an.
<?php endif; ?>
</p> </p>

View file

@ -1,69 +0,0 @@
<?php
use Finanzmanager\View;
/**
* Alle Buchungen, an denen die Bargeld-Tool-Prüfung zuletzt einen Fehler
* festgestellt hat – der Ziel der Kachel „Offene Vorgänge“ im Cockpit.
*
* @var bool $fehlt true: im Bargeld-Tool nicht gefunden, sonst Abruf gescheitert
* @var list<array<string,mixed>> $buchungen
*/
include __DIR__ . '/../layout/kopf.php';
?>
<div class="seitenaktionen">
<a href="/verwaltung/bargeld" class="knopf">Bargeld-Tool</a>
<a href="/" class="knopf">Cockpit</a>
</div>
<p class="feldhinweis">
<?php if ($fehlt): ?>
Zu diesen Buchungen wurde im Bargeld-Tool keine Ausgabe mit demselben Betrag
gefunden. Eine erneute Zuordnung der Kategorie in der Buchungsmaske oder im
Kontoblatt stößt die Prüfung erneut an.
<?php else: ?>
Bei diesen Buchungen ist der letzte Abruf beim Bargeld-Tool gescheitert – ein
Abrufproblem, keine Aussage darüber, ob die Buchung dort steht. Eine erneute
Zuordnung der Kategorie in der Buchungsmaske oder im Kontoblatt stößt die
Prüfung erneut an.
<?php endif; ?>
</p>
<?php if ($buchungen === []): ?>
<p class="platzhalter"><?= $fehlt ? 'Keine nicht gefundenen Buchungen.' : 'Keine offenen Fehler.' ?></p>
<?php else: ?>
<table class="liste">
<thead>
<tr>
<th>Datum</th>
<th>Konto</th>
<th>Empfänger / Buchungstext</th>
<th class="rechts">Betrag</th>
<th>Meldung</th>
</tr>
</thead>
<tbody>
<?php foreach ($buchungen as $buchung): ?>
<tr>
<td><?= View::h(View::datum($buchung['datum'])) ?></td>
<td>
<a href="/konten/<?= (int)$buchung['konto_id'] ?>">
<?= View::h($buchung['konto_name']) ?>
</a>
</td>
<td>
<?= View::h($buchung['empfaenger'] !== '' ? $buchung['empfaenger'] : $buchung['buchungstext']) ?>
</td>
<td class="rechts <?= View::betragKlasse($buchung['betrag']) ?>">
<?= View::betrag($buchung['betrag']) ?>
</td>
<td class="klein"><?= View::h($buchung['bargeld_hinweis'] ?? '') ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
<?php include __DIR__ . '/../layout/fuss.php'; ?>

View file

@ -301,19 +301,9 @@ class CockpitTest extends TestCase
$this->assertNotEmpty($eintrag, 'Der offene Vorgang erscheint jetzt'); $this->assertNotEmpty($eintrag, 'Der offene Vorgang erscheint jetzt');
$this->assertEquals(1, $eintrag['anzahl'], 'Genau die eine betroffene Buchung wird gezählt'); $this->assertEquals(1, $eintrag['anzahl'], 'Genau die eine betroffene Buchung wird gezählt');
$this->assertEquals( $this->assertEquals(
'/verwaltung/bargeld/fehler', '/auswertungen/bericht?typ=buchungsbericht&zeitraum=alles&umbuchungen=1&bargeld=fehler&angepasst=1',
$eintrag['pfad'], $eintrag['pfad'],
'Der Weg führt zur Liste der fehlgeschlagenen Prüfungen' 'Der Weg führt in den Buchungsbericht, gefiltert auf fehlgeschlagene Prüfungen (#131)'
);
$liste = Buchung::mitBargeldFehler();
$this->assertEquals(1, count($liste), 'Die Liste enthält genau diese Buchung');
$this->assertEquals($id, $liste[0]['id'], 'Es ist dieselbe Buchung');
$this->assertEquals('Cockpitgiro', $liste[0]['konto_name'], 'Der Kontoname kommt mit');
$this->assertEquals(
'Kasse nicht erreichbar.',
$liste[0]['bargeld_hinweis'],
'Die Meldung der Gegenstelle steht dabei'
); );
// Ein Status ungleich 'fehler' (z. B. 'gefunden') zählt nicht mit. // Ein Status ungleich 'fehler' (z. B. 'gefunden') zählt nicht mit.
@ -321,13 +311,16 @@ class CockpitTest extends TestCase
$ohne = $schluessel(Cockpit::offeneVorgaenge(self::HEUTE)); $ohne = $schluessel(Cockpit::offeneVorgaenge(self::HEUTE));
$this->assert(!isset($ohne['bargeld_fehler']), 'Ein gefundener Treffer ist kein offener Vorgang mehr'); $this->assert(!isset($ohne['bargeld_fehler']), 'Ein gefundener Treffer ist kein offener Vorgang mehr');
// Nicht gefunden: eigener Vorgang mit eigener Liste. // Nicht gefunden: eigener Vorgang mit eigenem Filter im Buchungsbericht.
Database::execute("UPDATE buchungen SET bargeld_status = 'fehlt' WHERE id = ?", [$id]); Database::execute("UPDATE buchungen SET bargeld_status = 'fehlt' WHERE id = ?", [$id]);
$fehlt = $schluessel(Cockpit::offeneVorgaenge(self::HEUTE)); $fehlt = $schluessel(Cockpit::offeneVorgaenge(self::HEUTE));
$this->assertEquals(1, $fehlt['bargeld_fehlt'] ?? 0, 'Eine im Bargeld-Tool nicht gefundene Buchung ist ein offener Vorgang'); $this->assertEquals(1, $fehlt['bargeld_fehlt'] ?? 0, 'Eine im Bargeld-Tool nicht gefundene Buchung ist ein offener Vorgang');
$this->assert(!isset($fehlt['bargeld_fehler']), 'Nicht gefunden zählt nicht als gescheiterter Abruf'); $this->assert(!isset($fehlt['bargeld_fehler']), 'Nicht gefunden zählt nicht als gescheiterter Abruf');
$this->assertEquals(1, count(Buchung::mitBargeldFehler('fehlt')), 'Die Liste „nicht gefunden“ enthält die Buchung'); $pfade = array_column(Cockpit::offeneVorgaenge(self::HEUTE), 'pfad', 'schluessel');
$this->assertEquals(0, count(Buchung::mitBargeldFehler()), 'Die Fehlerliste enthält sie nicht'); $this->assert(
str_contains($pfade['bargeld_fehlt'] ?? '', 'typ=buchungsbericht') && str_contains($pfade['bargeld_fehlt'] ?? '', 'bargeld=fehlt'),
'Der Weg führt in den Buchungsbericht, gefiltert auf „nicht gefunden“ (#131)'
);
Database::execute("UPDATE buchungen SET bargeld_status = 'fehler' WHERE id = ?", [$id]); Database::execute("UPDATE buchungen SET bargeld_status = 'fehler' WHERE id = ?", [$id]);
} }

View file

@ -7,8 +7,10 @@ use Finanzmanager\Models\Buchung;
use Finanzmanager\Models\Einstellung; use Finanzmanager\Models\Einstellung;
use Finanzmanager\Models\Kategorie; use Finanzmanager\Models\Kategorie;
use Finanzmanager\Models\Konto; use Finanzmanager\Models\Konto;
use Finanzmanager\Services\Auswertung;
use Finanzmanager\Services\BargeldAbgleich; use Finanzmanager\Services\BargeldAbgleich;
use Finanzmanager\Services\BargeldTool; use Finanzmanager\Services\BargeldTool;
use Finanzmanager\Services\Cockpit;
use Finanzmanager\Services\Crypto; use Finanzmanager\Services\Crypto;
/** /**
@ -52,7 +54,7 @@ class BargeldAbgleichTest extends TestCase
$this->kontoblattFuehrtDenStatusMit(); $this->kontoblattFuehrtDenStatusMit();
$this->kontoblattZeigtDieViervierSymbole(); $this->kontoblattZeigtDieViervierSymbole();
$this->konfigurationsseiteZeigtDieAuswahl(); $this->konfigurationsseiteZeigtDieAuswahl();
$this->fehlerlisteZeigtDieBuchungen(); $this->buchungsberichtFiltertNachBargeldStatus();
} }
protected function tearDown(): void protected function tearDown(): void
@ -512,42 +514,72 @@ class BargeldAbgleichTest extends TestCase
} }
/** /**
* Die Liste unter „Verwaltung → Bargeld-Tool → fehlgeschlagene Prüfungen“, * Statt einer eigenen Liste führt der offene Vorgang des Cockpit in den
* das Ziel der Kachel „Offene Vorgänge“ im Cockpit. Buchung::mitBargeldFehler() * Buchungsbericht, gefiltert auf den Status des Bargeld-Tools (#131).
* ist kontoübergreifend – geprüft am Datenmodell, nicht am Kontoblatt. * Dort steht – anders als in der früheren Liste – die Kategorie dabei.
*/ */
private function fehlerlisteZeigtDieBuchungen(): void private function buchungsberichtFiltertNachBargeldStatus(): void
{ {
$id = $this->buchung(-27.30, '2026-06-01'); Einstellung::set('bargeld_url', 'https://bargeld.example.org', 'bargeld');
Buchung::zuordnen($id, $this->bargeldKategorie, null);
$fehler = $this->buchung(-27.30, '2026-06-01');
Buchung::zuordnen($fehler, $this->bargeldKategorie, null);
$fehlt = $this->buchung(-8.40, '2026-06-02');
Buchung::zuordnen($fehlt, $this->bargeldKategorie, null);
Database::execute( Database::execute(
"UPDATE buchungen SET bargeld_status = 'fehler', bargeld_hinweis = 'Kasse steht still.' WHERE id = ?", "UPDATE buchungen SET bargeld_status = 'fehler', bargeld_hinweis = 'Kasse steht still.' WHERE id = ?",
[$id] [$fehler]
);
Database::execute(
"UPDATE buchungen SET bargeld_status = 'fehlt', bargeld_hinweis = 'Kein Treffer.' WHERE id = ?",
[$fehlt]
); );
$liste = Buchung::mitBargeldFehler(); $pfade = array_column(Cockpit::offeneVorgaenge('2026-06-30'), 'pfad', 'schluessel');
$treffer = null; foreach (['bargeld_fehler' => 'fehler', 'bargeld_fehlt' => 'fehlt'] as $schluessel => $status) {
foreach ($liste as $zeile) { $this->assertNotEmpty($pfade[$schluessel] ?? '', "Der offene Vorgang $schluessel erscheint");
if ((int)$zeile['id'] === $id) { parse_str((string)parse_url($pfade[$schluessel] ?? '', PHP_URL_QUERY), $query);
$treffer = $zeile; $this->assertEquals(
} '/auswertungen/bericht',
parse_url($pfade[$schluessel] ?? '', PHP_URL_PATH),
"Der Vorgang $schluessel führt in die Auswertung"
);
$this->assertEquals('buchungsbericht', $query['typ'] ?? '', "Der Vorgang $schluessel öffnet den Buchungsbericht");
$this->assertEquals('alles', $query['zeitraum'] ?? '', 'Über den gesamten Zeitraum');
$this->assertEquals($status, $query['bargeld'] ?? '', "Gefiltert auf den Status $status");
$bericht = Auswertung::bericht(Auswertung::normalisiere($query), '2026-06-30');
$buchungen = array_merge(...array_column($bericht['gruppen'], 'buchungen'));
$ids = array_column($buchungen, 'buchung_id');
$this->assert(
in_array($status === 'fehler' ? $fehler : $fehlt, $ids, true),
"Der Bericht enthält die Buchung mit Status $status"
);
$this->assert(
!in_array($status === 'fehler' ? $fehlt : $fehler, $ids, true),
'Die Buchung mit dem anderen Status fehlt'
);
$this->assertEquals(
['Bargeld Haushalt'],
array_values(array_unique(array_column($buchungen, 'kategorie'))),
'Die Kategorie steht an jeder Buchung'
);
$html = $this->rendere('auswertungen/teil-buchungen.php', ['bericht' => $bericht]);
$this->assert(str_contains($html, '<th>Kategorie</th>'), 'Die Spalte Kategorie steht im Bericht');
$this->assert(str_contains($html, 'Bargeld Haushalt'), 'Die Kategorie der Buchung steht im Bericht');
$this->assert(str_contains($html, 'Bargeld Giro'), 'Das Konto steht im Bericht');
$this->assert(
str_contains($html, $status === 'fehler' ? 'Kasse steht still.' : 'Kein Treffer.'),
'Die Meldung der Gegenstelle steht am Symbol'
);
$this->assert(str_contains($html, 'stößt die Prüfung erneut an'), 'Der Hinweis zur erneuten Prüfung steht dabei');
} }
$this->assertNotEmpty($treffer, 'Die Buchung mit Status „fehler“ steht in der Liste');
$this->assertEquals('Bargeld Giro', $treffer['konto_name'], 'Der Kontoname kommt mit – die Liste ist kontoübergreifend');
$this->assertEquals('Kasse steht still.', $treffer['bargeld_hinweis'], 'Die Meldung steht dabei');
$html = $this->rendere('verwaltung/bargeld_fehler.php', ['buchungen' => $liste]);
$this->assert(str_contains($html, 'Bargeld Giro'), 'Der Kontoname steht in der Liste');
$this->assert(str_contains($html, 'Bargeldabhebung'), 'Der Empfänger steht in der Liste');
$this->assert(str_contains($html, 'Kasse steht still.'), 'Die Meldung der Gegenstelle steht dabei');
$this->assert( $this->assert(
str_contains($html, '/konten/' . $this->konto), !is_file(dirname(__DIR__) . '/src/Views/verwaltung/bargeld_fehler.php'),
'Der Kontoname führt ins Kontoblatt' 'Die frühere eigene Liste ist entfernt'
); );
$leer = $this->rendere('verwaltung/bargeld_fehler.php', ['buchungen' => []]);
$this->assert(str_contains($leer, 'Keine offenen Fehler.'), 'Eine leere Liste sagt das ausdrücklich');
} }
/** @param list<array<string,mixed>> $zeilen */ /** @param list<array<string,mixed>> $zeilen */

View file

@ -183,5 +183,17 @@ test.describe('Bargeld-Tool', () => {
const danach = page.locator('tr', { hasText: empfaenger }) const danach = page.locator('tr', { hasText: empfaenger })
await expect(danach).toContainText(KATEGORIE) await expect(danach).toContainText(KATEGORIE)
await expect(danach.locator('td.bargeldspalte .bargeld-fehler')).toBeVisible() await expect(danach.locator('td.bargeldspalte .bargeld-fehler')).toBeVisible()
// Der offene Vorgang im Cockpit führt in den Buchungsbericht, gefiltert
// auf fehlgeschlagene Prüfungen – samt Kategorie (#131).
await page.goto('/')
await page.locator('.vorgangsliste a', { hasText: 'Bargeld-Tool-Prüfung fehlgeschlagen' }).click()
await expect(page).toHaveURL(/\/auswertungen\/bericht\?.*typ=buchungsbericht/)
await expect(page.locator('select[name="bargeld"]')).toHaveValue('fehler')
await expect(page.locator('table.buchungsgruppen thead')).toContainText('Kategorie')
const imBericht = page.locator('table.buchungsgruppen tr', { hasText: empfaenger })
await expect(imBericht).toContainText(KATEGORIE)
await expect(imBericht).toContainText(GIRO)
await expect(imBericht.locator('td.bargeldspalte .bargeld-fehler')).toBeVisible()
}) })
}) })