Merge pull request 'Fix n.n.: Buchungsbericht – Bargeld-Abgleich, Gruppen-/Gesamtzeile, Filterbereich, Cockpit-Tooltip' (#110) from fds2610-bot/Finanzmanager:main into main
Reviewed-on: https://codeberg.org/fds2610/Finanzmanager/pulls/110
This commit is contained in:
commit
ed6e707c55
15 changed files with 224 additions and 26 deletions
25
Changes.md
25
Changes.md
|
|
@ -1,5 +1,30 @@
|
||||||
# Änderungen
|
# Änderungen
|
||||||
|
|
||||||
|
## Buchungsbericht: Gruppen- und Gesamtzeile (01.10.2026)
|
||||||
|
|
||||||
|
- Die Formatierung von Kategoriezeile und Gesamtzeile ist getauscht. Die Gesamtzeile zeigt nur noch „Gesamt“; die Anzahl der Buchungen steht im Tooltip („Anzahl n“).
|
||||||
|
|
||||||
|
## Buchungsbericht: Bargeld-Tool-Abgleich (01.10.2026)
|
||||||
|
|
||||||
|
- Ist ein Bargeld-Tool eingerichtet, zeigt der Buchungsbericht die Spalte „Bar“ (dasselbe Symbol wie im Kontoblatt) und bietet den Filter „Bargeld-Tool“: alle · gefunden · nicht gefunden · Abruf gescheitert · nicht abgeglichen.
|
||||||
|
- Der Filter wird mit dem Bericht gespeichert (`Auswertung::BARGELD_FILTER`) und wirkt nur im Buchungsbericht.
|
||||||
|
- Test: `14_AuswertungTest.php`.
|
||||||
|
|
||||||
|
## Berichte: Filterbereich (01.10.2026)
|
||||||
|
|
||||||
|
- Der Filterbereich erhält den hellblauen Bereichsrand samt Schatten.
|
||||||
|
- „Zeilen-Summe“ und „Spalten-Aufteilung“ stehen in der ersten Zeile hinter „Bericht anzeigen als“; das Suchfeld ist dafür breiter.
|
||||||
|
|
||||||
|
## Cockpit: Tooltip der Kachel „Offene Vorgänge“ (01.10.2026)
|
||||||
|
|
||||||
|
- Beim Überfahren der Überschrift nennt der Tooltip die Prüfungen, die gerade nichts melden und deshalb nicht in der Liste stehen (`Cockpit::VORGANGSPRUEFUNGEN`).
|
||||||
|
- Test: `15_CockpitTest.php`.
|
||||||
|
|
||||||
|
## Cockpit: Offene Vorgänge für nicht gefundene Bargeld-Buchungen (01.10.2026)
|
||||||
|
|
||||||
|
- Neuer offener Vorgang „im Bargeld-Tool nicht gefunden“ (Status `fehlt`) neben der fehlgeschlagenen Prüfung; die Liste unter `/verwaltung/bargeld/fehler?status=fehlt`.
|
||||||
|
- Test: `15_CockpitTest.php`.
|
||||||
|
|
||||||
## Rahmen, Schatten und Tabs im Design (01.10.2026)
|
## Rahmen, Schatten und Tabs im Design (01.10.2026)
|
||||||
|
|
||||||
- Tabellen (`table.liste`), Kacheln und breite Formulare (`.formular.breit`, u. a. Einstellungen) erhalten in beiden Themen einen 1px breiten hellblauen Rand (`--bereichsrand`) und einen leichten Schatten (`--bereichsschatten`) für mehr Kontrast zwischen den Bereichen.
|
- Tabellen (`table.liste`), Kacheln und breite Formulare (`.formular.breit`, u. a. Einstellungen) erhalten in beiden Themen einen 1px breiten hellblauen Rand (`--bereichsrand`) und einen leichten Schatten (`--bereichsschatten`) für mehr Kontrast zwischen den Bereichen.
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ Zwei Ausnahmen führen das Vorzeichen mit, weil sie keine Abschnitte haben: der
|
||||||
| Typ | Zeilen | Spalten |
|
| Typ | Zeilen | Spalten |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| **Summenbericht** | Zeilen-Summe, nach Einnahmen und Ausgaben getrennt | Zeitraum, je nach Spalten-Aufteilung |
|
| **Summenbericht** | Zeilen-Summe, nach Einnahmen und Ausgaben getrennt | Zeitraum, je nach Spalten-Aufteilung |
|
||||||
| **Buchungsbericht** | Einzelbuchungen, gruppiert nach Zeilen-Summe | feste Buchungsspalten |
|
| **Buchungsbericht** | Einzelbuchungen, gruppiert nach Zeilen-Summe; bei eingerichtetem Bargeld-Tool mit Spalte „Bar“ und Filter „Bargeld-Tool“ | feste Buchungsspalten |
|
||||||
| **Vergleichsbericht** | wie Summenbericht | Zeitraum, Vorjahreszeitraum, Differenz, Veränderung |
|
| **Vergleichsbericht** | wie Summenbericht | Zeitraum, Vorjahreszeitraum, Differenz, Veränderung |
|
||||||
| **Kontostandsbericht** | Konten | Salden zu den Enden der Spaltenzeiträume |
|
| **Kontostandsbericht** | Konten | Salden zu den Enden der Spaltenzeiträume |
|
||||||
| **Budgetbericht** | Kategoriebaum | Budget, Ist, Saldo |
|
| **Budgetbericht** | Kategoriebaum | Budget, Ist, Saldo |
|
||||||
|
|
|
||||||
|
|
@ -274,8 +274,9 @@ 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 auf
|
||||||
`/verwaltung/bargeld/fehler`, die kontoübergreifende Liste dieser Buchungen
|
`/verwaltung/bargeld/fehler`, die kontoübergreifende Liste dieser Buchungen
|
||||||
(`Buchung::mitBargeldFehler()`). Der Status `fehlt` erscheint dort bewusst nicht: Er ist ein
|
(`Buchung::mitBargeldFehler()`). Buchungen mit dem Status `fehlt` zählen als eigener offener
|
||||||
Ergebnis, kein offenes Problem – eine erneute Zuordnung fragt einfach wieder ohne Erfolg.
|
Vorgang „im Bargeld-Tool nicht gefunden“ und führen auf dieselbe Seite mit `?status=fehlt`
|
||||||
|
(`Buchung::mitBargeldFehler('fehlt')`).
|
||||||
|
|
||||||
**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
|
||||||
|
|
|
||||||
|
|
@ -172,7 +172,8 @@ zusammen und es entstünde keine Fläche – deshalb zwei Halbringe derselben Fa
|
||||||
|
|
||||||
## Offene Vorgänge
|
## Offene Vorgänge
|
||||||
|
|
||||||
Was auf Bearbeitung wartet, mit Zahl und Weg dorthin:
|
Was auf Bearbeitung wartet, mit Zahl und Weg dorthin. Der Tooltip der Überschrift nennt die
|
||||||
|
Prüfungen, die gerade nichts melden und daher nicht in der Liste stehen:
|
||||||
|
|
||||||
* **ohne Kategorie** – Buchungen ohne Kategorie, die auch keine Umbuchung und nicht
|
* **ohne Kategorie** – Buchungen ohne Kategorie, die auch keine Umbuchung und nicht
|
||||||
gesplittet sind. Umbuchungen brauchen keine Kategorie und zählen deshalb nicht mit.
|
gesplittet sind. Umbuchungen brauchen keine Kategorie und zählen deshalb nicht mit.
|
||||||
|
|
@ -182,6 +183,8 @@ Was auf Bearbeitung wartet, mit Zahl und Weg dorthin:
|
||||||
verwechseln mit „im Bargeld-Tool nicht gefunden“. Führt auf
|
verwechseln mit „im Bargeld-Tool nicht gefunden“. Führt auf
|
||||||
`/verwaltung/bargeld/fehler`, die Liste dieser Buchungen kontoübergreifend
|
`/verwaltung/bargeld/fehler`, die Liste dieser Buchungen kontoübergreifend
|
||||||
(`Buchung::mitBargeldFehler()`), denn die Prüfung hängt an der Kategorie, nicht am Konto.
|
(`Buchung::mitBargeldFehler()`), denn die Prüfung hängt an der Kategorie, nicht am Konto.
|
||||||
|
* **Im Bargeld-Tool nicht gefunden** – Buchungen mit `bargeld_status = 'fehlt'`. Führt auf
|
||||||
|
`/verwaltung/bargeld/fehler?status=fehlt` (`Buchung::mitBargeldFehler('fehlt')`).
|
||||||
* **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.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -826,6 +826,28 @@ table.liste tr.gruppensumme td {
|
||||||
background: color-mix(in srgb, var(--flaeche-gedaempft) 55%, var(--flaeche));
|
background: color-mix(in srgb, var(--flaeche-gedaempft) 55%, var(--flaeche));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Buchungsbericht: Kategoriezeile fett wie sonst die Summe, Gesamtzeile gedämpft wie sonst der Kopf. */
|
||||||
|
table.buchungsgruppen tr.gruppenzeile th {
|
||||||
|
background: color-mix(in srgb, var(--flaeche-gedaempft) 55%, var(--flaeche));
|
||||||
|
font-size: inherit;
|
||||||
|
letter-spacing: normal;
|
||||||
|
text-transform: none;
|
||||||
|
font-weight: 600;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.buchungsgruppen tr.gruppensumme td {
|
||||||
|
background: transparent;
|
||||||
|
border-top: 1px solid var(--rand);
|
||||||
|
font-size: .85rem;
|
||||||
|
letter-spacing: .03em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--text-gedaempft);
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.buchungsgruppen tr.gruppensumme td span { cursor: help; }
|
||||||
|
|
||||||
table.liste tfoot tr.summenzeile td {
|
table.liste tfoot tr.summenzeile td {
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
border-top: 2px solid var(--rand-kraeftig);
|
border-top: 2px solid var(--rand-kraeftig);
|
||||||
|
|
@ -1852,6 +1874,13 @@ body.schlicht {
|
||||||
gap: .6rem;
|
gap: .6rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.berichtsfilter {
|
||||||
|
border: var(--bereichsrand);
|
||||||
|
box-shadow: var(--bereichsschatten);
|
||||||
|
}
|
||||||
|
|
||||||
|
.berichtsfilter input[type="search"] { min-width: 24rem; }
|
||||||
|
|
||||||
.filterzeile {
|
.filterzeile {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|
|
||||||
|
|
@ -286,9 +286,14 @@ class VerwaltungController extends BaseController
|
||||||
{
|
{
|
||||||
$this->verlangeAnmeldung();
|
$this->verlangeAnmeldung();
|
||||||
|
|
||||||
|
$fehlt = ($_GET['status'] ?? '') === 'fehlt';
|
||||||
|
|
||||||
$this->render('verwaltung/bargeld_fehler.php', [
|
$this->render('verwaltung/bargeld_fehler.php', [
|
||||||
'titel' => 'Bargeld-Tool: fehlgeschlagene Prüfungen',
|
'titel' => $fehlt
|
||||||
'buchungen' => Buchung::mitBargeldFehler(),
|
? 'Bargeld-Tool: nicht gefundene Buchungen'
|
||||||
|
: 'Bargeld-Tool: fehlgeschlagene Prüfungen',
|
||||||
|
'fehlt' => $fehlt,
|
||||||
|
'buchungen' => Buchung::mitBargeldFehler($fehlt ? 'fehlt' : 'fehler'),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -504,23 +504,23 @@ class Buchung
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Alle Buchungen, an denen die Bargeld-Tool-Prüfung zuletzt einen Fehler
|
* Alle Buchungen, an denen die Bargeld-Tool-Prüfung zuletzt einen Fehler
|
||||||
* festgestellt hat – kontoübergreifend, denn die Prüfung hängt an der
|
* ('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
|
* Kategorie der Buchung, nicht an ihrem Konto. Der Status 'fehler' steht
|
||||||
* für einen gescheiterten Abruf (siehe Services\BargeldAbgleich::FEHLER),
|
* für einen gescheiterten Abruf (siehe Services\BargeldAbgleich::FEHLER),
|
||||||
* nicht für eine im Bargeld-Tool fehlende Buchung.
|
* 'fehlt' für eine im Bargeld-Tool nicht gefundene Buchung.
|
||||||
*
|
*
|
||||||
* @return list<array<string,mixed>>
|
* @return list<array<string,mixed>>
|
||||||
*/
|
*/
|
||||||
public static function mitBargeldFehler(): array
|
public static function mitBargeldFehler(string $status = 'fehler'): array
|
||||||
{
|
{
|
||||||
return Database::fetchAll(
|
return Database::fetchAll(
|
||||||
'SELECT b.id, b.datum, b.betrag, b.empfaenger, b.buchungstext,
|
'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
|
b.bargeld_hinweis, b.bargeld_geprueft_am, b.konto_id, k.name AS konto_name
|
||||||
FROM buchungen b
|
FROM buchungen b
|
||||||
JOIN konten k ON k.id = b.konto_id
|
JOIN konten k ON k.id = b.konto_id
|
||||||
WHERE b.benutzer_id = ? AND b.bargeld_status = \'fehler\'
|
WHERE b.benutzer_id = ? AND b.bargeld_status = ?
|
||||||
ORDER BY b.datum DESC, b.id DESC',
|
ORDER BY b.datum DESC, b.id DESC',
|
||||||
[Mandant::id()]
|
[Mandant::id(), $status === 'fehlt' ? 'fehlt' : 'fehler']
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,9 @@ class Auswertung
|
||||||
'budgetbericht' => 'Budgetbericht',
|
'budgetbericht' => 'Budgetbericht',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/** Zulässige Werte des Bargeld-Filters; 'keiner' = nicht abgeglichen. */
|
||||||
|
public const BARGELD_FILTER = ['gefunden', 'fehlt', 'fehler', 'keiner'];
|
||||||
|
|
||||||
/** Wonach die Zeilen zusammengefasst werden. */
|
/** Wonach die Zeilen zusammengefasst werden. */
|
||||||
public const ZEILENSUMMEN = [
|
public const ZEILENSUMMEN = [
|
||||||
'kategorie' => 'Kategorie',
|
'kategorie' => 'Kategorie',
|
||||||
|
|
@ -114,6 +117,10 @@ class Auswertung
|
||||||
'kategorien' => self::ids($roh['kategorien'] ?? []),
|
'kategorien' => self::ids($roh['kategorien'] ?? []),
|
||||||
'klassen' => self::ids($roh['klassen'] ?? []),
|
'klassen' => self::ids($roh['klassen'] ?? []),
|
||||||
'suche' => mb_substr(trim((string)($roh['suche'] ?? '')), 0, 120),
|
'suche' => mb_substr(trim((string)($roh['suche'] ?? '')), 0, 120),
|
||||||
|
// Nur der Buchungsbericht führt die Abgleichspalte und filtert danach.
|
||||||
|
'bargeld' => $typ === 'buchungsbericht' && in_array($roh['bargeld'] ?? '', self::BARGELD_FILTER, true)
|
||||||
|
? (string)$roh['bargeld']
|
||||||
|
: '',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -654,6 +661,7 @@ class Auswertung
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'darstellung' => 'buchungen',
|
'darstellung' => 'buchungen',
|
||||||
|
'bargeld' => BargeldAbgleich::eingerichtet(),
|
||||||
'gruppen' => array_values($gruppen),
|
'gruppen' => array_values($gruppen),
|
||||||
'summe' => round($summe, 2),
|
'summe' => round($summe, 2),
|
||||||
'anzahl' => $anzahl,
|
'anzahl' => $anzahl,
|
||||||
|
|
@ -825,6 +833,8 @@ class Auswertung
|
||||||
'gesplittet' => $zeile['split_id'] !== null,
|
'gesplittet' => $zeile['split_id'] !== null,
|
||||||
'split_gesamt' => !empty($zeile['split_zusammengefasst']),
|
'split_gesamt' => !empty($zeile['split_zusammengefasst']),
|
||||||
'betrag' => round((float)$zeile['betrag'], 2),
|
'betrag' => round((float)$zeile['betrag'], 2),
|
||||||
|
'bargeld_status' => $zeile['bargeld_status'] ?? null,
|
||||||
|
'bargeld_hinweis' => $zeile['bargeld_hinweis'] ?? null,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1177,13 +1187,13 @@ class Auswertung
|
||||||
$sql = 'SELECT * FROM (
|
$sql = 'SELECT * FROM (
|
||||||
SELECT b.id AS buchung_id, NULL AS split_id, b.datum, b.betrag, b.konto_id,
|
SELECT b.id AS buchung_id, NULL AS split_id, b.datum, b.betrag, b.konto_id,
|
||||||
b.kategorie_id, b.klasse_id, b.gegenkonto_id, b.empfaenger,
|
b.kategorie_id, b.klasse_id, b.gegenkonto_id, b.empfaenger,
|
||||||
b.buchungstext AS text
|
b.buchungstext AS text, b.bargeld_status, b.bargeld_hinweis
|
||||||
FROM buchungen b
|
FROM buchungen b
|
||||||
WHERE b.benutzer_id = ? AND b.gesplittet = 0 AND b.datum BETWEEN ? AND ?
|
WHERE b.benutzer_id = ? AND b.gesplittet = 0 AND b.datum BETWEEN ? AND ?
|
||||||
UNION ALL
|
UNION ALL
|
||||||
SELECT b.id AS buchung_id, s.id AS split_id, b.datum, s.betrag, b.konto_id,
|
SELECT b.id AS buchung_id, s.id AS split_id, b.datum, s.betrag, b.konto_id,
|
||||||
s.kategorie_id, s.klasse_id, s.gegenkonto_id, b.empfaenger,
|
s.kategorie_id, s.klasse_id, s.gegenkonto_id, b.empfaenger,
|
||||||
s.notiz AS text
|
s.notiz AS text, b.bargeld_status, b.bargeld_hinweis
|
||||||
FROM buchung_splits s
|
FROM buchung_splits s
|
||||||
JOIN buchungen b ON b.id = s.buchung_id
|
JOIN buchungen b ON b.id = s.buchung_id
|
||||||
WHERE b.benutzer_id = ? AND b.gesplittet = 1 AND b.datum BETWEEN ? AND ?
|
WHERE b.benutzer_id = ? AND b.gesplittet = 1 AND b.datum BETWEEN ? AND ?
|
||||||
|
|
@ -1213,6 +1223,14 @@ class Auswertung
|
||||||
$params = array_merge($params, $filter['klassen']);
|
$params = array_merge($params, $filter['klassen']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$bargeld = (string)($filter['bargeld'] ?? '');
|
||||||
|
if ($bargeld === 'keiner') {
|
||||||
|
$wo[] = 'bargeld_status IS NULL';
|
||||||
|
} elseif ($bargeld !== '') {
|
||||||
|
$wo[] = 'bargeld_status = ?';
|
||||||
|
$params[] = $bargeld;
|
||||||
|
}
|
||||||
|
|
||||||
if (($filter['suche'] ?? '') !== '') {
|
if (($filter['suche'] ?? '') !== '') {
|
||||||
$wo[] = '(empfaenger LIKE ? OR text LIKE ?)';
|
$wo[] = '(empfaenger LIKE ? OR text LIKE ?)';
|
||||||
$muster = '%' . $filter['suche'] . '%';
|
$muster = '%' . $filter['suche'] . '%';
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,22 @@ class Cockpit
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alle Prüfungen der Kachel „Offene Vorgänge“, in der Reihenfolge der Liste.
|
||||||
|
* Angezeigt wird nur, was etwas findet; der Tooltip der Überschrift nennt
|
||||||
|
* deshalb auch die, die gerade nichts melden.
|
||||||
|
*/
|
||||||
|
public const VORGANGSPRUEFUNGEN = [
|
||||||
|
'ohne_kategorie' => 'Buchungen ohne Kategorie',
|
||||||
|
'nicht_abgeglichen' => 'Nicht abgeglichene Buchungen',
|
||||||
|
'bankumsaetze' => 'Bankumsätze zum Abgleich',
|
||||||
|
'bargeld_fehler' => 'Fehlgeschlagene Bargeld-Tool-Prüfungen',
|
||||||
|
'bargeld_fehlt' => 'Im Bargeld-Tool nicht gefundene Buchungen',
|
||||||
|
'termine' => 'Fällige regelmäßige Buchungen',
|
||||||
|
'erinnerungen' => 'Fällige Erinnerungen',
|
||||||
|
'monatsabschluss' => 'Offener Monatsabschluss',
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Die sichtbaren Kacheln in ihrer Reihenfolge. Ohne gespeicherte Auswahl
|
* Die sichtbaren Kacheln in ihrer Reihenfolge. Ohne gespeicherte Auswahl
|
||||||
* sind alle sichtbar – ein leeres Cockpit beim ersten Aufruf wäre nur
|
* sind alle sichtbar – ein leeres Cockpit beim ersten Aufruf wäre nur
|
||||||
|
|
@ -288,6 +304,22 @@ class Cockpit
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Buchungen, die im Bargeld-Tool nicht gefunden wurden: der Abgleich
|
||||||
|
// lief, hat aber keinen Treffer ergeben.
|
||||||
|
$bargeldFehlt = (int)Database::fetchValue(
|
||||||
|
'SELECT COUNT(*) FROM buchungen WHERE benutzer_id = ? AND bargeld_status = ?',
|
||||||
|
[$benutzer, BargeldAbgleich::FEHLT],
|
||||||
|
0
|
||||||
|
);
|
||||||
|
if ($bargeldFehlt > 0) {
|
||||||
|
$vorgaenge[] = [
|
||||||
|
'schluessel' => 'bargeld_fehlt',
|
||||||
|
'text' => 'im Bargeld-Tool nicht gefunden',
|
||||||
|
'anzahl' => $bargeldFehlt,
|
||||||
|
'pfad' => '/verwaltung/bargeld/fehler?status=fehlt',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
// Fällige regelmäßige Buchungen – die automatischen zählen mit, denn
|
// Fällige regelmäßige Buchungen – die automatischen zählen mit, denn
|
||||||
// gebucht sind sie erst nach dem Fälligkeitslauf.
|
// gebucht sind sie erst nach dem Fälligkeitslauf.
|
||||||
$termine = Faelligkeitslauf::vorschau($heute);
|
$termine = Faelligkeitslauf::vorschau($heute);
|
||||||
|
|
|
||||||
|
|
@ -127,9 +127,7 @@ include __DIR__ . '/../layout/kopf.php';
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="filterzeile">
|
|
||||||
<label>Zeilen-Summe
|
<label>Zeilen-Summe
|
||||||
<select name="zeilensumme" <?= $zeilenWaehlbar ? '' : 'disabled' ?>>
|
<select name="zeilensumme" <?= $zeilenWaehlbar ? '' : 'disabled' ?>>
|
||||||
<?php foreach ($zeilensummen as $wert => $bezeichnung): ?>
|
<?php foreach ($zeilensummen as $wert => $bezeichnung): ?>
|
||||||
|
|
@ -151,7 +149,9 @@ include __DIR__ . '/../layout/kopf.php';
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="filterzeile">
|
||||||
<?php /* Vor jedem Ankreuzfeld ein verstecktes Feld gleichen Namens mit
|
<?php /* Vor jedem Ankreuzfeld ein verstecktes Feld gleichen Namens mit
|
||||||
„0“: Ein nicht angehaktes Feld sendet gar nichts, und der Wert
|
„0“: Ein nicht angehaktes Feld sendet gar nichts, und der Wert
|
||||||
fiele sonst auf seine Vorgabe zurück statt auf „aus“. Bei
|
fiele sonst auf seine Vorgabe zurück statt auf „aus“. Bei
|
||||||
|
|
@ -174,6 +174,19 @@ include __DIR__ . '/../layout/kopf.php';
|
||||||
</label>
|
</label>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if ($bericht['typ'] === 'buchungsbericht' && ($bericht['bargeld'] ?? false)): ?>
|
||||||
|
<label>Bargeld-Tool
|
||||||
|
<select name="bargeld">
|
||||||
|
<?php foreach (['' => 'alle', 'gefunden' => 'gefunden', 'fehlt' => 'nicht gefunden',
|
||||||
|
'fehler' => 'Abruf gescheitert', 'keiner' => 'nicht abgeglichen'] as $wert => $text): ?>
|
||||||
|
<option value="<?= View::h($wert) ?>" <?= $filter['bargeld'] === $wert ? 'selected' : '' ?>>
|
||||||
|
<?= View::h($text) ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
<label>Suche
|
<label>Suche
|
||||||
<input type="search" name="suche" value="<?= View::h($filter['suche']) ?>"
|
<input type="search" name="suche" value="<?= View::h($filter['suche']) ?>"
|
||||||
placeholder="Empfänger oder Text">
|
placeholder="Empfänger oder Text">
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,8 @@ use Finanzmanager\View;
|
||||||
*/
|
*/
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<table class="liste bericht">
|
<?php $spalten = $bericht['bargeld'] ? 8 : 7; ?>
|
||||||
|
<table class="liste bericht buchungsgruppen">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Datum</th>
|
<th>Datum</th>
|
||||||
|
|
@ -19,6 +20,9 @@ use Finanzmanager\View;
|
||||||
<th>Buchungstext</th>
|
<th>Buchungstext</th>
|
||||||
<th>Kategorie</th>
|
<th>Kategorie</th>
|
||||||
<th>Klasse</th>
|
<th>Klasse</th>
|
||||||
|
<?php if ($bericht['bargeld']): ?>
|
||||||
|
<th class="bargeldspalte" title="Abgleich mit dem Bargeld-Tool">Bar</th>
|
||||||
|
<?php endif; ?>
|
||||||
<th class="rechts">Betrag</th>
|
<th class="rechts">Betrag</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
@ -26,7 +30,7 @@ use Finanzmanager\View;
|
||||||
<?php foreach ($bericht['gruppen'] as $gruppe): ?>
|
<?php foreach ($bericht['gruppen'] as $gruppe): ?>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr class="gruppenzeile">
|
<tr class="gruppenzeile">
|
||||||
<th colspan="7"><?= View::h($gruppe['name']) ?></th>
|
<th colspan="<?= $spalten ?>"><?= View::h($gruppe['name']) ?></th>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<?php foreach ($gruppe['buchungen'] as $buchung): ?>
|
<?php foreach ($gruppe['buchungen'] as $buchung): ?>
|
||||||
|
|
@ -48,6 +52,9 @@ use Finanzmanager\View;
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</td>
|
</td>
|
||||||
<td><?= View::h($buchung['klasse']) ?></td>
|
<td><?= View::h($buchung['klasse']) ?></td>
|
||||||
|
<?php if ($bericht['bargeld']): ?>
|
||||||
|
<td class="bargeldspalte"><?= View::bargeldMarke($buchung['bargeld_status'], $buchung['bargeld_hinweis']) ?></td>
|
||||||
|
<?php endif; ?>
|
||||||
<td class="rechts <?= View::betragKlasse($buchung['betrag']) ?>">
|
<td class="rechts <?= View::betragKlasse($buchung['betrag']) ?>">
|
||||||
<?= View::betrag($buchung['betrag'], false) ?>
|
<?= View::betrag($buchung['betrag'], false) ?>
|
||||||
</td>
|
</td>
|
||||||
|
|
@ -55,8 +62,7 @@ use Finanzmanager\View;
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
|
|
||||||
<tr class="gruppensumme">
|
<tr class="gruppensumme">
|
||||||
<td colspan="6">Gesamt <?= View::h($gruppe['name']) ?>
|
<td colspan="<?= $spalten - 1 ?>"><span title="Anzahl <?= count($gruppe['buchungen']) ?>">Gesamt</span></td>
|
||||||
(<?= count($gruppe['buchungen']) ?>)</td>
|
|
||||||
<td class="rechts <?= View::betragKlasse($gruppe['summe']) ?>">
|
<td class="rechts <?= View::betragKlasse($gruppe['summe']) ?>">
|
||||||
<?= View::betrag($gruppe['summe'], false) ?>
|
<?= View::betrag($gruppe['summe'], false) ?>
|
||||||
</td>
|
</td>
|
||||||
|
|
@ -66,7 +72,7 @@ use Finanzmanager\View;
|
||||||
|
|
||||||
<tfoot>
|
<tfoot>
|
||||||
<tr class="summenzeile">
|
<tr class="summenzeile">
|
||||||
<td colspan="6">Gesamt (<?= (int)$bericht['anzahl'] ?> Buchungen)</td>
|
<td colspan="<?= $spalten - 1 ?>">Gesamt (<?= (int)$bericht['anzahl'] ?> Buchungen)</td>
|
||||||
<td class="rechts <?= View::betragKlasse($bericht['summe']) ?>">
|
<td class="rechts <?= View::betragKlasse($bericht['summe']) ?>">
|
||||||
<?= View::betrag($bericht['summe'], false) ?>
|
<?= View::betrag($bericht['summe'], false) ?>
|
||||||
</td>
|
</td>
|
||||||
|
|
|
||||||
|
|
@ -153,7 +153,19 @@ include __DIR__ . '/../layout/kopf.php';
|
||||||
|
|
||||||
<?php elseif ($kachel === 'offene_vorgaenge'): ?>
|
<?php elseif ($kachel === 'offene_vorgaenge'): ?>
|
||||||
<section class="kachel">
|
<section class="kachel">
|
||||||
<header><h2>Offene Vorgänge</h2></header>
|
<?php
|
||||||
|
$gefunden = array_column($vorgaenge ?? [], 'schluessel');
|
||||||
|
$nichtAngezeigt = [];
|
||||||
|
foreach (Cockpit::VORGANGSPRUEFUNGEN as $schluessel => $name) {
|
||||||
|
if (!in_array($schluessel, $gefunden, true)) {
|
||||||
|
$nichtAngezeigt[] = '• ' . $name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$tooltip = $nichtAngezeigt === []
|
||||||
|
? 'Alle Prüfungen melden gerade etwas.'
|
||||||
|
: "Nicht angezeigt, weil nichts offen ist:\n" . implode("\n", $nichtAngezeigt);
|
||||||
|
?>
|
||||||
|
<header><h2 title="<?= View::h($tooltip) ?>">Offene Vorgänge</h2></header>
|
||||||
<?php if (($vorgaenge ?? []) === []): ?>
|
<?php if (($vorgaenge ?? []) === []): ?>
|
||||||
<p class="platzhalter">
|
<p class="platzhalter">
|
||||||
Nichts offen: Alle Buchungen sind kategorisiert und abgeglichen,
|
Nichts offen: Alle Buchungen sind kategorisiert und abgeglichen,
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ use Finanzmanager\View;
|
||||||
* Alle Buchungen, an denen die Bargeld-Tool-Prüfung zuletzt einen Fehler
|
* Alle Buchungen, an denen die Bargeld-Tool-Prüfung zuletzt einen Fehler
|
||||||
* festgestellt hat – der Ziel der Kachel „Offene Vorgänge“ im Cockpit.
|
* 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
|
* @var list<array<string,mixed>> $buchungen
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
@ -18,14 +19,20 @@ include __DIR__ . '/../layout/kopf.php';
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="feldhinweis">
|
<p class="feldhinweis">
|
||||||
Bei diesen Buchungen ist der letzte Abruf beim Bargeld-Tool gescheitert – ein
|
<?php if ($fehlt): ?>
|
||||||
Abrufproblem, keine Aussage darüber, ob die Buchung dort steht. Eine erneute
|
Zu diesen Buchungen wurde im Bargeld-Tool keine Ausgabe mit demselben Betrag
|
||||||
Zuordnung der Kategorie in der Buchungsmaske oder im Kontoblatt stößt die
|
gefunden. Eine erneute Zuordnung der Kategorie in der Buchungsmaske oder im
|
||||||
Prüfung erneut an.
|
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>
|
</p>
|
||||||
|
|
||||||
<?php if ($buchungen === []): ?>
|
<?php if ($buchungen === []): ?>
|
||||||
<p class="platzhalter">Keine offenen Fehler.</p>
|
<p class="platzhalter"><?= $fehlt ? 'Keine nicht gefundenen Buchungen.' : 'Keine offenen Fehler.' ?></p>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<table class="liste">
|
<table class="liste">
|
||||||
<thead>
|
<thead>
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,14 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Finanzmanager\Database;
|
||||||
use Finanzmanager\Models\Bericht;
|
use Finanzmanager\Models\Bericht;
|
||||||
use Finanzmanager\Models\Berichtsgruppe;
|
use Finanzmanager\Models\Berichtsgruppe;
|
||||||
use Finanzmanager\Models\Buchung;
|
use Finanzmanager\Models\Buchung;
|
||||||
use Finanzmanager\Models\Budget;
|
use Finanzmanager\Models\Budget;
|
||||||
use Finanzmanager\Models\Kategorie;
|
use Finanzmanager\Models\Kategorie;
|
||||||
use Finanzmanager\Models\Klasse;
|
use Finanzmanager\Models\Klasse;
|
||||||
|
use Finanzmanager\Models\Einstellung;
|
||||||
use Finanzmanager\Models\Konto;
|
use Finanzmanager\Models\Konto;
|
||||||
use Finanzmanager\Services\Auswertung;
|
use Finanzmanager\Services\Auswertung;
|
||||||
use Finanzmanager\Services\BerichtExport;
|
use Finanzmanager\Services\BerichtExport;
|
||||||
|
|
@ -50,6 +52,7 @@ class AuswertungTest extends TestCase
|
||||||
$this->summenbericht_einschraenkungen();
|
$this->summenbericht_einschraenkungen();
|
||||||
|
|
||||||
$this->buchungsbericht();
|
$this->buchungsbericht();
|
||||||
|
$this->buchungsberichtBargeld();
|
||||||
$this->vergleichsbericht();
|
$this->vergleichsbericht();
|
||||||
$this->kontostandsbericht();
|
$this->kontostandsbericht();
|
||||||
$this->budgetbericht();
|
$this->budgetbericht();
|
||||||
|
|
@ -478,6 +481,30 @@ class AuswertungTest extends TestCase
|
||||||
|
|
||||||
// ── Weitere Berichtstypen ────────────────────────────────────────────────
|
// ── Weitere Berichtstypen ────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private function buchungsberichtBargeld(): void
|
||||||
|
{
|
||||||
|
$vorher = Einstellung::get('bargeld_url', '');
|
||||||
|
Einstellung::set('bargeld_url', '');
|
||||||
|
$this->assert(!$this->bericht(['typ' => 'buchungsbericht'])['bargeld'], 'Ohne Bargeld-Tool keine Abgleichspalte');
|
||||||
|
|
||||||
|
Einstellung::set('bargeld_url', 'http://bargeld.invalid');
|
||||||
|
Database::execute("UPDATE buchungen SET bargeld_status = 'fehlt' WHERE empfaenger = 'Rewe'");
|
||||||
|
|
||||||
|
$alle = $this->bericht(['typ' => 'buchungsbericht', 'zeilensumme' => 'konto']);
|
||||||
|
$this->assert($alle['bargeld'], 'Mit Bargeld-Tool erscheint die Abgleichspalte');
|
||||||
|
|
||||||
|
$fehlt = $this->bericht(['typ' => 'buchungsbericht', 'zeilensumme' => 'konto', 'bargeld' => 'fehlt']);
|
||||||
|
$this->assertEquals(1, $fehlt['anzahl'], 'Der Filter „nicht gefunden“ lässt nur die markierte Buchung übrig');
|
||||||
|
|
||||||
|
$keiner = $this->bericht(['typ' => 'buchungsbericht', 'zeilensumme' => 'konto', 'bargeld' => 'keiner']);
|
||||||
|
$this->assertEquals($alle['anzahl'] - 1, $keiner['anzahl'], 'Der Filter „nicht abgeglichen“ lässt die markierte Buchung weg');
|
||||||
|
|
||||||
|
$summe = $this->bericht(['typ' => 'summenbericht', 'bargeld' => 'fehlt']);
|
||||||
|
$this->assertEquals('', $summe['filter']['bargeld'], 'Außerhalb des Buchungsberichts wirkt der Filter nicht');
|
||||||
|
|
||||||
|
Einstellung::set('bargeld_url', (string)$vorher);
|
||||||
|
}
|
||||||
|
|
||||||
private function buchungsbericht(): void
|
private function buchungsbericht(): void
|
||||||
{
|
{
|
||||||
$bericht = $this->bericht(['typ' => 'buchungsbericht', 'zeilensumme' => 'konto']);
|
$bericht = $this->bericht(['typ' => 'buchungsbericht', 'zeilensumme' => 'konto']);
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,18 @@ class CockpitTest extends TestCase
|
||||||
$this->offeneVorgaengeZaehlenWasWartet();
|
$this->offeneVorgaengeZaehlenWasWartet();
|
||||||
$this->bargeldFehlerErscheintAlsOffenerVorgang();
|
$this->bargeldFehlerErscheintAlsOffenerVorgang();
|
||||||
$this->legendenFuehrenZuDenBuchungen();
|
$this->legendenFuehrenZuDenBuchungen();
|
||||||
|
$this->vorgangspruefungenKennenJedenVorgang();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function vorgangspruefungenKennenJedenVorgang(): void
|
||||||
|
{
|
||||||
|
foreach (Cockpit::offeneVorgaenge(self::HEUTE) as $vorgang) {
|
||||||
|
$this->assert(
|
||||||
|
isset(Cockpit::VORGANGSPRUEFUNGEN[$vorgang['schluessel']]),
|
||||||
|
'Der Tooltip kennt die Prüfung „' . $vorgang['schluessel'] . '“'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$this->assertEquals(8, count(Cockpit::VORGANGSPRUEFUNGEN), 'Acht Prüfungen sind aufgelistet');
|
||||||
}
|
}
|
||||||
|
|
||||||
private function stammdaten(): void
|
private function stammdaten(): void
|
||||||
|
|
@ -309,6 +321,14 @@ 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.
|
||||||
|
Database::execute("UPDATE buchungen SET bargeld_status = 'fehlt' WHERE id = ?", [$id]);
|
||||||
|
$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->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');
|
||||||
|
$this->assertEquals(0, count(Buchung::mitBargeldFehler()), 'Die Fehlerliste enthält sie nicht');
|
||||||
|
|
||||||
Database::execute("UPDATE buchungen SET bargeld_status = 'fehler' WHERE id = ?", [$id]);
|
Database::execute("UPDATE buchungen SET bargeld_status = 'fehler' WHERE id = ?", [$id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue