Fix: Σ-Kennzeichen ließ sich beim Konto nicht abschalten
Ein nicht angehaktes Ankreuzfeld steht nicht im POST. KontenController:: formulardaten() reichte "in_summe" gar nicht durch, also griff im Model der Standardwert "zählt zur Summe" – die Anwendung meldete "Konto gespeichert", geändert hatte sich nichts. formulardaten() nimmt das POST-Array jetzt als Parameter und ist damit ohne HTTP prüfbar; die Ankreuzfelder werden ausdrücklich auf true/false abgebildet. Der Standard bleibt für Aufrufe ohne den Schlüssel bestehen – der QIF-Import gibt das Feld nicht an. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
1dbc848cd1
commit
92e15d54c1
5 changed files with 137 additions and 18 deletions
16
Changes.md
16
Changes.md
|
|
@ -1,5 +1,21 @@
|
|||
# Änderungen
|
||||
|
||||
## Korrektur – Σ-Kennzeichen ließ sich nicht abschalten (10.08.2026)
|
||||
|
||||
Das Kennzeichen *Zählt zur Summe der Kontostände* blieb beim Speichern eines Kontos
|
||||
unverändert stehen, obwohl die Anwendung „Konto gespeichert“ meldete.
|
||||
|
||||
- Ursache: `KontenController::formulardaten()` reichte `in_summe` gar nicht durch. Ein
|
||||
nicht angehaktes Ankreuzfeld steht nicht im POST, also griff im Model der Standardwert
|
||||
„zählt mit“ – gespeichert wurde ein Ja, das niemand angegeben hatte.
|
||||
- `formulardaten()` nimmt das POST-Array jetzt als Parameter und ist damit ohne HTTP
|
||||
prüfbar; die Ankreuzfelder werden ausdrücklich auf `true`/`false` abgebildet.
|
||||
- Der Standard „zählt zur Summe“ bleibt für Aufrufe ohne den Schlüssel bestehen – der
|
||||
QIF-Import gibt das Feld nicht an und soll es auch nicht.
|
||||
- `07_KontenTest` prüft die Formularabbildung und den Weg durch das Model; E2E
|
||||
`stammdaten.spec.js` hakt das Kennzeichen in der Oberfläche ab, setzt es wieder und
|
||||
sieht in beiden Fällen nach, statt der Erfolgsmeldung zu glauben.
|
||||
|
||||
## Phase 2 – Buchungen, Kontoblatt und Buchungsimport (10.08.2026)
|
||||
|
||||
**Erkenntnis aus dem Vollexport**
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ class KontenController extends BaseController
|
|||
$this->verlangeAnmeldung();
|
||||
|
||||
try {
|
||||
$id = Konto::anlegen($this->formulardaten());
|
||||
$id = Konto::anlegen(self::formulardaten($_POST));
|
||||
} catch (\Throwable $e) {
|
||||
View::redirect('/konten/neu', $e->getMessage(), 'fehler');
|
||||
return;
|
||||
|
|
@ -83,7 +83,7 @@ class KontenController extends BaseController
|
|||
}
|
||||
|
||||
try {
|
||||
Konto::speichern($id, $this->formulardaten());
|
||||
Konto::speichern($id, self::formulardaten($_POST));
|
||||
} catch (\Throwable $e) {
|
||||
View::redirect('/konten/' . $id . '/bearbeiten', $e->getMessage(), 'fehler');
|
||||
return;
|
||||
|
|
@ -107,23 +107,40 @@ class KontenController extends BaseController
|
|||
View::redirect('/konten', 'Konto „' . $konto['name'] . '“ gelöscht.');
|
||||
}
|
||||
|
||||
/** @return array<string,mixed> */
|
||||
private function formulardaten(): array
|
||||
/**
|
||||
* Bildet das Kontoformular auf die Modellfelder ab.
|
||||
*
|
||||
* Öffentlich und statisch, damit die Abbildung ohne HTTP prüfbar ist: Ein
|
||||
* nicht angehaktes Ankreuzfeld steht überhaupt nicht im POST, es muss hier
|
||||
* ausdrücklich zu `false` werden. Fehlt ein solches Feld in dieser Liste,
|
||||
* lässt es sich in der Oberfläche nie wieder abschalten – die Meldung sagt
|
||||
* „gespeichert“, der alte Wert bleibt stehen.
|
||||
*
|
||||
* @param array<string,mixed> $post
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
public static function formulardaten(array $post): array
|
||||
{
|
||||
$feld = static function (string $name, string $standard = '') use ($post): string {
|
||||
$wert = $post[$name] ?? $standard;
|
||||
return is_string($wert) ? trim($wert) : $standard;
|
||||
};
|
||||
|
||||
return [
|
||||
'name' => $this->post('name'),
|
||||
'kontoart' => $this->post('kontoart', 'bank'),
|
||||
'kontengruppe_id' => $this->post('kontengruppe_id'),
|
||||
'bankname' => $this->post('bankname'),
|
||||
'kontonummer' => $this->post('kontonummer'),
|
||||
'iban' => $this->post('iban'),
|
||||
'bic' => $this->post('bic'),
|
||||
'beschreibung' => $this->post('beschreibung'),
|
||||
'anfangssaldo' => self::zahl($this->post('anfangssaldo', '0')),
|
||||
'kreditlimit' => $this->post('kreditlimit') === '' ? null : self::zahl($this->post('kreditlimit')),
|
||||
'waehrung' => $this->post('waehrung', 'EUR'),
|
||||
'aktiv' => isset($_POST['aktiv']),
|
||||
'sortierung' => (int)$this->post('sortierung', '0'),
|
||||
'name' => $feld('name'),
|
||||
'kontoart' => $feld('kontoart', 'bank'),
|
||||
'kontengruppe_id' => $feld('kontengruppe_id'),
|
||||
'bankname' => $feld('bankname'),
|
||||
'kontonummer' => $feld('kontonummer'),
|
||||
'iban' => $feld('iban'),
|
||||
'bic' => $feld('bic'),
|
||||
'beschreibung' => $feld('beschreibung'),
|
||||
'anfangssaldo' => self::zahl($feld('anfangssaldo', '0')),
|
||||
'kreditlimit' => $feld('kreditlimit') === '' ? null : self::zahl($feld('kreditlimit')),
|
||||
'waehrung' => $feld('waehrung', 'EUR'),
|
||||
'aktiv' => isset($post['aktiv']),
|
||||
'in_summe' => isset($post['in_summe']),
|
||||
'sortierung' => (int)$feld('sortierung', '0'),
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -277,7 +277,10 @@ class Konto
|
|||
'kreditlimit' => $limit,
|
||||
'waehrung' => strtoupper(trim((string)($daten['waehrung'] ?? 'EUR'))) ?: 'EUR',
|
||||
'aktiv' => !empty($daten['aktiv']) ? 1 : 0,
|
||||
// Standard ist "zählt zur Summe"; nur ein ausdrückliches Nein schaltet es ab.
|
||||
// Standard ist "zählt zur Summe"; nur ein ausdrückliches Nein schaltet
|
||||
// es ab. Der QIF-Import lässt das Feld deshalb weg. Wer aus einem
|
||||
// Formular kommt, muss den Schlüssel dagegen immer mitgeben – siehe
|
||||
// KontenController::formulardaten().
|
||||
'in_summe' => array_key_exists('in_summe', $daten) && empty($daten['in_summe']) ? 0 : 1,
|
||||
'sortierung' => (int)($daten['sortierung'] ?? 0),
|
||||
];
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ class KontenTest extends TestCase
|
|||
$this->kontoarten();
|
||||
$this->uebersichtGruppiertUndSummiert();
|
||||
$this->betragsEingabe();
|
||||
$this->ankreuzfelderAusDemFormular();
|
||||
}
|
||||
|
||||
private function anlegenUndFinden(): void
|
||||
|
|
@ -155,4 +156,52 @@ class KontenTest extends TestCase
|
|||
$this->assertEquals(0.0, KontenController::zahl(''), 'Leere Eingabe ergibt null');
|
||||
$this->assertEquals(99.9, KontenController::zahl(' 99,90 € '), 'Währungszeichen und Leerzeichen stören nicht');
|
||||
}
|
||||
|
||||
/**
|
||||
* Ein nicht angehaktes Ankreuzfeld schickt der Browser gar nicht mit. Die
|
||||
* Formularabbildung muss daraus ein ausdrückliches Nein machen, sonst lässt
|
||||
* sich das Kennzeichen nie wieder abschalten – „Konto gespeichert“ steht
|
||||
* dann da, geändert hat sich nichts.
|
||||
*/
|
||||
private function ankreuzfelderAusDemFormular(): void
|
||||
{
|
||||
$angehakt = KontenController::formulardaten([
|
||||
'name' => 'Girokonto', 'kontoart' => 'bank', 'aktiv' => '1', 'in_summe' => '1',
|
||||
]);
|
||||
$this->assert($angehakt['aktiv'] === true, 'Angehaktes „aktiv“ kommt als Ja an');
|
||||
$this->assert($angehakt['in_summe'] === true, 'Angehaktes Σ-Kennzeichen kommt als Ja an');
|
||||
|
||||
$leer = KontenController::formulardaten(['name' => 'Girokonto', 'kontoart' => 'bank']);
|
||||
$this->assert($leer['aktiv'] === false, 'Fehlendes „aktiv“ heißt inaktiv');
|
||||
$this->assert($leer['in_summe'] === false, 'Fehlendes Σ-Kennzeichen heißt: zählt nicht zur Summe');
|
||||
|
||||
// Und derselbe Weg durch das Model hindurch: Abhaken muss ankommen.
|
||||
$id = Konto::anlegen(KontenController::formulardaten([
|
||||
'name' => 'Auslagen Eltern', 'kontoart' => 'sonstiges',
|
||||
'anfangssaldo' => '1.500,00', 'aktiv' => '1', 'in_summe' => '1',
|
||||
]));
|
||||
$this->assertEquals(1, (int)Konto::find($id)['in_summe'], 'Neues Konto zählt zunächst mit');
|
||||
$this->assertEquals(1500.0, (float)Konto::find($id)['anfangssaldo'], 'Anfangssaldo aus dem Formular');
|
||||
|
||||
Konto::speichern($id, KontenController::formulardaten([
|
||||
'name' => 'Auslagen Eltern', 'kontoart' => 'sonstiges',
|
||||
'anfangssaldo' => '1.500,00', 'aktiv' => '1',
|
||||
]));
|
||||
$this->assertEquals(0, (int)Konto::find($id)['in_summe'], 'Σ-Kennzeichen lässt sich entfernen');
|
||||
|
||||
$uebersicht = Konto::uebersicht();
|
||||
$gefunden = false;
|
||||
foreach ($uebersicht['gruppen'] as $gruppe) {
|
||||
foreach ($gruppe['konten'] as $konto) {
|
||||
if ((int)$konto['id'] === $id) {
|
||||
$gefunden = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->assert($gefunden, 'Das Konto bleibt in der Übersicht sichtbar');
|
||||
|
||||
// Der QIF-Import gibt das Feld nicht an – dort gilt weiter der Standard.
|
||||
$importiert = Konto::anlegen(['name' => 'Aus dem Export', 'kontoart' => 'bank', 'aktiv' => 1]);
|
||||
$this->assertEquals(1, (int)Konto::find($importiert)['in_summe'], 'Ohne Angabe zählt ein Konto mit');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,6 +55,40 @@ test.describe('Stammdaten', () => {
|
|||
await expect(page.locator('.summenzeile')).toContainText('Summe Kontostände')
|
||||
})
|
||||
|
||||
// Ein nicht angehaktes Ankreuzfeld schickt der Browser gar nicht mit. Bleibt
|
||||
// es serverseitig unbeachtet, meldet die Anwendung „Konto gespeichert“ und
|
||||
// das Kennzeichen steht unverändert da.
|
||||
test('Σ-Kennzeichen lässt sich abschalten und wieder setzen', async ({ page }) => {
|
||||
const name = `E2E Auslagen ${MARKE}`
|
||||
|
||||
await page.goto('/konten/neu')
|
||||
await page.fill('#name', name)
|
||||
await page.selectOption('#kontoart', 'sonstiges')
|
||||
await page.fill('#anfangssaldo', '500,00')
|
||||
await page.click('.formular button[type="submit"]')
|
||||
await expect(page.locator('.meldung-erfolg')).toBeVisible()
|
||||
|
||||
const kennzeichen = page.locator('input[name="in_summe"]')
|
||||
await expect(kennzeichen).toBeChecked()
|
||||
|
||||
await kennzeichen.uncheck()
|
||||
await page.locator('.formular button[type="submit"]').first().click()
|
||||
await expect(page.locator('.meldung-erfolg')).toBeVisible()
|
||||
|
||||
// In der Übersicht bleibt das Konto sichtbar, aber als „ohne Σ“ markiert.
|
||||
const zeile = page.locator('tr', { hasText: name })
|
||||
await expect(zeile).toContainText('ohne Σ')
|
||||
|
||||
// Und im Formular ist das Häkchen tatsächlich weg – nicht nur die Meldung da.
|
||||
await zeile.locator('a').first().click()
|
||||
await page.click('a.tab[href$="/bearbeiten"]')
|
||||
await expect(page.locator('input[name="in_summe"]')).not.toBeChecked()
|
||||
|
||||
await page.locator('input[name="in_summe"]').check()
|
||||
await page.locator('.formular button[type="submit"]').first().click()
|
||||
await expect(page.locator('tr', { hasText: name })).not.toContainText('ohne Σ')
|
||||
})
|
||||
|
||||
test('Kategorie mit Unterkategorie anlegen', async ({ page }) => {
|
||||
const oben = `E2E Wohnen ${MARKE}`
|
||||
const unten = `Strom ${MARKE}`
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue