Fix #102: Offener Vorgang für fehlgeschlagene Bargeld-Tool-Prüfungen
Die Kachel "Offene Vorgänge" im Cockpit zeigt jetzt auch Buchungen, deren letzter Abgleich mit dem Bargeld-Tool an einem Abrufproblem gescheitert ist (bargeld_status = 'fehler'). Ein Klick führt auf die neue, kontoübergreifende Liste unter Verwaltung -> Bargeld-Tool -> fehlgeschlagene Prüfungen. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
bb6dd06f22
commit
ec72fd213a
10 changed files with 248 additions and 5 deletions
|
|
@ -1,5 +1,14 @@
|
|||
# Änderungen
|
||||
|
||||
## Offener Vorgang für fehlgeschlagene Bargeld-Tool-Prüfungen (30.09.2026)
|
||||
|
||||
- Die Kachel „Offene Vorgänge“ im Cockpit zählt jetzt auch Buchungen, deren letzter Abgleich
|
||||
mit dem Bargeld-Tool an einem Abrufproblem gescheitert ist (`bargeld_status = 'fehler'`) –
|
||||
nicht zu verwechseln mit „im Bargeld-Tool nicht gefunden“. Ein Klick führt auf die neue
|
||||
Liste unter Verwaltung → Bargeld-Tool → fehlgeschlagene Prüfungen
|
||||
(`/verwaltung/bargeld/fehler`), die diese Buchungen kontoübergreifend mit Konto, Betrag und
|
||||
Fehlermeldung auflistet (`Buchung::mitBargeldFehler()`). Fix #102.
|
||||
|
||||
## phpFinTS auf v4.1.6 (30.09.2026)
|
||||
|
||||
- Fork neu auf Upstream vom 22.09.2026 aufgebaut (psr/log und Depotaufstellung v6, #598, bleiben
|
||||
|
|
|
|||
|
|
@ -271,6 +271,12 @@ treffen kann. Der Tooltip nennt den Treffer bzw. die Meldung der Gegenstelle. Is
|
|||
Adresse hinterlegt, entfällt die Spalte ganz; sie zeigte sonst in jeder Zeile denselben
|
||||
grauen Kreis.
|
||||
|
||||
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
|
||||
`/verwaltung/bargeld/fehler`, die kontoübergreifende Liste dieser Buchungen
|
||||
(`Buchung::mitBargeldFehler()`). Der Status `fehlt` erscheint dort bewusst nicht: Er ist ein
|
||||
Ergebnis, kein offenes Problem – eine erneute Zuordnung fragt einfach wieder ohne Erfolg.
|
||||
|
||||
**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
|
||||
nach acht Sekunden ab. Null Treffer sind ausdrücklich kein Fehler – ein 404 dagegen schon,
|
||||
|
|
|
|||
|
|
@ -177,6 +177,11 @@ Was auf Bearbeitung wartet, mit Zahl und Weg dorthin:
|
|||
* **ohne Kategorie** – Buchungen ohne Kategorie, die auch keine Umbuchung und nicht
|
||||
gesplittet sind. Umbuchungen brauchen keine Kategorie und zählen deshalb nicht mit.
|
||||
* **nicht abgeglichen** – Buchungen aktiver Konten bis heute ohne Abgleich-Kennzeichen.
|
||||
* **Bargeld-Tool-Prüfung fehlgeschlagen** – Buchungen mit `bargeld_status = 'fehler'`, also
|
||||
einem gescheiterten Abruf beim Bargeld-Tool (siehe `docs/buchungen.md`), nicht zu
|
||||
verwechseln mit „im Bargeld-Tool nicht gefunden“. Führt auf
|
||||
`/verwaltung/bargeld/fehler`, die Liste dieser Buchungen kontoübergreifend
|
||||
(`Buchung::mitBargeldFehler()`), denn die Prüfung hängt an der Kategorie, nicht am Konto.
|
||||
* **Monatsabschluss steht aus** – der Vorschlag aus `Monatsabschluss::naechsterOffener()`,
|
||||
aber nur, wenn dieser Monat wirklich noch offen ist und Buchungen trägt.
|
||||
|
||||
|
|
@ -188,8 +193,11 @@ regelmäßigen Buchungen.
|
|||
|
||||
* `tests/15_CockpitTest.php` – Kachelauswahl (Reihenfolge, Unbekanntes, leere Auswahl),
|
||||
Monatsreihe mit Trennung von Einnahmen und Ausgaben, Umbuchungen bleiben draußen,
|
||||
Verteilung auf die Wurzelkategorie, Kürzung auf die Segmentgrenze, offene Vorgänge,
|
||||
Legendeneinträge führen zu Buchungslisten, deren Summe den Legendenwert trifft
|
||||
Verteilung auf die Wurzelkategorie, Kürzung auf die Segmentgrenze, offene Vorgänge
|
||||
(auch fehlgeschlagene Bargeld-Tool-Prüfungen), Legendeneinträge führen zu
|
||||
Buchungslisten, deren Summe den Legendenwert trifft
|
||||
* `tests/34_BargeldAbgleichTest.php` – die Liste der fehlgeschlagenen Prüfungen
|
||||
(`Buchung::mitBargeldFehler()`) und ihre Anzeige unter Verwaltung → Bargeld-Tool
|
||||
* `tests/13_VermoegensentwicklungTest.php` – Zeitraum, Fortschreibung der Stände,
|
||||
Umbuchungen, Σ-Kennzeichen, Schalter „nur aktive“, Zusammenfassen jenseits der Palette,
|
||||
Ebenen (jedes Konto genau einmal, letzte Ebene ohne Sammelstück, zu tiefe Ebene fällt
|
||||
|
|
|
|||
|
|
@ -139,6 +139,7 @@ $router->post('/verwaltung/kontengruppen/{id}/loeschen',
|
|||
$router->get('/verwaltung/bargeld', fn() => (new VerwaltungController())->bargeld());
|
||||
$router->post('/verwaltung/bargeld', fn() => (new VerwaltungController())->bargeldSpeichern());
|
||||
$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.
|
||||
$router->get('/verwaltung/bankinstitute', fn() => (new BankinstituteController())->index());
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ namespace Finanzmanager\Controllers;
|
|||
|
||||
use Finanzmanager\Models\Adresse;
|
||||
use Finanzmanager\Models\Bankinstitut;
|
||||
use Finanzmanager\Models\Buchung;
|
||||
use Finanzmanager\Models\Einstellung;
|
||||
use Finanzmanager\Models\Erinnerung;
|
||||
use Finanzmanager\Models\Kategorie;
|
||||
|
|
@ -275,4 +276,19 @@ class VerwaltungController extends BaseController
|
|||
$ergebnis = $zugang->test();
|
||||
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();
|
||||
|
||||
$this->render('verwaltung/bargeld_fehler.php', [
|
||||
'titel' => 'Bargeld-Tool: fehlgeschlagene Prüfungen',
|
||||
'buchungen' => Buchung::mitBargeldFehler(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -472,6 +472,28 @@ class Buchung
|
|||
return $zeilen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alle Buchungen, an denen die Bargeld-Tool-Prüfung zuletzt einen Fehler
|
||||
* festgestellt 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),
|
||||
* nicht für eine im Bargeld-Tool fehlende Buchung.
|
||||
*
|
||||
* @return list<array<string,mixed>>
|
||||
*/
|
||||
public static function mitBargeldFehler(): 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 = \'fehler\'
|
||||
ORDER BY b.datum DESC, b.id DESC',
|
||||
[Mandant::id()]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Salden aller Konten in einem Rutsch: Anfangssaldo plus Buchungen, plus die
|
||||
* Gegenseiten fremder Umbuchungen. Ohne Stichtag zählt alles (erwarteter
|
||||
|
|
|
|||
|
|
@ -271,6 +271,23 @@ class Cockpit
|
|||
];
|
||||
}
|
||||
|
||||
// Buchungen, deren letzter Abgleich mit dem Bargeld-Tool gescheitert
|
||||
// ist – ein Abrufproblem, keine Aussage über die Buchung selbst (siehe
|
||||
// BargeldAbgleich::FEHLER), aber solange offen, bis er erneut glückt.
|
||||
$bargeldFehler = (int)Database::fetchValue(
|
||||
'SELECT COUNT(*) FROM buchungen WHERE benutzer_id = ? AND bargeld_status = ?',
|
||||
[$benutzer, BargeldAbgleich::FEHLER],
|
||||
0
|
||||
);
|
||||
if ($bargeldFehler > 0) {
|
||||
$vorgaenge[] = [
|
||||
'schluessel' => 'bargeld_fehler',
|
||||
'text' => 'Bargeld-Tool-Prüfung fehlgeschlagen',
|
||||
'anzahl' => $bargeldFehler,
|
||||
'pfad' => '/verwaltung/bargeld/fehler',
|
||||
];
|
||||
}
|
||||
|
||||
// Fällige regelmäßige Buchungen – die automatischen zählen mit, denn
|
||||
// gebucht sind sie erst nach dem Fälligkeitslauf.
|
||||
$termine = Faelligkeitslauf::vorschau($heute);
|
||||
|
|
|
|||
62
src/Views/verwaltung/bargeld_fehler.php
Normal file
62
src/Views/verwaltung/bargeld_fehler.php
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
<?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 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">
|
||||
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.
|
||||
</p>
|
||||
|
||||
<?php if ($buchungen === []): ?>
|
||||
<p class="platzhalter">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'; ?>
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Finanzmanager\Database;
|
||||
use Finanzmanager\Models\Buchung;
|
||||
use Finanzmanager\Models\Kategorie;
|
||||
use Finanzmanager\Models\Konto;
|
||||
|
|
@ -35,6 +36,7 @@ class CockpitTest extends TestCase
|
|||
$this->verteilungFasstAufDerWurzelZusammen();
|
||||
$this->verteilungKuerztAufDieGrenze();
|
||||
$this->offeneVorgaengeZaehlenWasWartet();
|
||||
$this->bargeldFehlerErscheintAlsOffenerVorgang();
|
||||
$this->legendenFuehrenZuDenBuchungen();
|
||||
}
|
||||
|
||||
|
|
@ -251,6 +253,65 @@ class CockpitTest extends TestCase
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Eine Buchung, deren letzter Abgleich mit dem Bargeld-Tool gescheitert
|
||||
* ist, erscheint als eigener offener Vorgang – unabhängig vom Konto, denn
|
||||
* die Prüfung hängt an der Kategorie.
|
||||
*/
|
||||
private function bargeldFehlerErscheintAlsOffenerVorgang(): void
|
||||
{
|
||||
$schluessel = static fn(array $vorgaenge): array => array_column($vorgaenge, 'anzahl', 'schluessel');
|
||||
|
||||
$vorher = $schluessel(Cockpit::offeneVorgaenge(self::HEUTE));
|
||||
$this->assert(
|
||||
!isset($vorher['bargeld_fehler']),
|
||||
'Ohne betroffene Buchung fehlt der Vorgang ganz'
|
||||
);
|
||||
|
||||
$id = Buchung::anlegen([
|
||||
'konto_id' => $this->giro, 'datum' => '2026-08-09', 'betrag' => -15,
|
||||
'empfaenger' => 'Bargeldabhebung', 'kategorie_id' => $this->freizeit, 'abgeglichen' => 1,
|
||||
]);
|
||||
Database::execute(
|
||||
"UPDATE buchungen SET bargeld_status = 'fehler', bargeld_hinweis = 'Kasse nicht erreichbar.'
|
||||
WHERE id = ?",
|
||||
[$id]
|
||||
);
|
||||
|
||||
$nachher = Cockpit::offeneVorgaenge(self::HEUTE);
|
||||
$eintrag = null;
|
||||
foreach ($nachher as $vorgang) {
|
||||
if ($vorgang['schluessel'] === 'bargeld_fehler') {
|
||||
$eintrag = $vorgang;
|
||||
}
|
||||
}
|
||||
|
||||
$this->assertNotEmpty($eintrag, 'Der offene Vorgang erscheint jetzt');
|
||||
$this->assertEquals(1, $eintrag['anzahl'], 'Genau die eine betroffene Buchung wird gezählt');
|
||||
$this->assertEquals(
|
||||
'/verwaltung/bargeld/fehler',
|
||||
$eintrag['pfad'],
|
||||
'Der Weg führt zur Liste der fehlgeschlagenen Prüfungen'
|
||||
);
|
||||
|
||||
$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.
|
||||
Database::execute("UPDATE buchungen SET bargeld_status = 'gefunden' WHERE id = ?", [$id]);
|
||||
$ohne = $schluessel(Cockpit::offeneVorgaenge(self::HEUTE));
|
||||
$this->assert(!isset($ohne['bargeld_fehler']), 'Ein gefundener Treffer ist kein offener Vorgang mehr');
|
||||
|
||||
Database::execute("UPDATE buchungen SET bargeld_status = 'fehler' WHERE id = ?", [$id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Jeder Legendeneintrag führt zu einer Buchungsliste, deren Summe genau
|
||||
* seinen Wert trifft – auch „Nicht kategorisiert“ und das Sammelsegment.
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Finanzmanager\Database;
|
||||
use Finanzmanager\Models\Buchung;
|
||||
use Finanzmanager\Models\Einstellung;
|
||||
use Finanzmanager\Models\Kategorie;
|
||||
|
|
@ -51,6 +52,7 @@ class BargeldAbgleichTest extends TestCase
|
|||
$this->kontoblattFuehrtDenStatusMit();
|
||||
$this->kontoblattZeigtDieViervierSymbole();
|
||||
$this->konfigurationsseiteZeigtDieAuswahl();
|
||||
$this->fehlerlisteZeigtDieBuchungen();
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
|
|
@ -509,6 +511,45 @@ class BargeldAbgleichTest extends TestCase
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Die Liste unter „Verwaltung → Bargeld-Tool → fehlgeschlagene Prüfungen“,
|
||||
* das Ziel der Kachel „Offene Vorgänge“ im Cockpit. Buchung::mitBargeldFehler()
|
||||
* ist kontoübergreifend – geprüft am Datenmodell, nicht am Kontoblatt.
|
||||
*/
|
||||
private function fehlerlisteZeigtDieBuchungen(): void
|
||||
{
|
||||
$id = $this->buchung(-27.30, '2026-06-01');
|
||||
Buchung::zuordnen($id, $this->bargeldKategorie, null);
|
||||
Database::execute(
|
||||
"UPDATE buchungen SET bargeld_status = 'fehler', bargeld_hinweis = 'Kasse steht still.' WHERE id = ?",
|
||||
[$id]
|
||||
);
|
||||
|
||||
$liste = Buchung::mitBargeldFehler();
|
||||
$treffer = null;
|
||||
foreach ($liste as $zeile) {
|
||||
if ((int)$zeile['id'] === $id) {
|
||||
$treffer = $zeile;
|
||||
}
|
||||
}
|
||||
$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(
|
||||
str_contains($html, '/konten/' . $this->konto),
|
||||
'Der Kontoname führt ins Kontoblatt'
|
||||
);
|
||||
|
||||
$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 */
|
||||
private function kontoblattHtml(array $zeilen, bool $bargeld): string
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue