# Buchungen Kontoblatt, Buchungsmaske und die Saldenrechnung. ## Umbuchungen: ein Datensatz, zwei Sichten Eine Umbuchung wird **einmal** gespeichert – als Buchung auf dem Ausgangskonto mit `gegenkonto_id`. Das Kontoblatt des Gegenkontos zeigt sie mit umgekehrtem Vorzeichen. Der Grund ist nicht nur Sparsamkeit: Zwei gespiegelte Datensätze laufen früher oder später auseinander (einer wird geändert, der andere nicht). Und der QIF-Export macht es genauso – dort steht jede Umbuchung genau einmal, die Gegenseite nur als `L[Zielkonto]`. `Buchung::kontoblatt()` vereinigt deshalb drei Mengen: 1. Buchungen mit `konto_id = X` (eigene Sicht) 2. Buchungen mit `gegenkonto_id = X` ohne Splitzeilen, Betrag negiert 3. Splitzeilen mit `gegenkonto_id = X`, Betrag negiert Zeilen aus 2 und 3 tragen `gegensicht = true`. Sie sind nicht bearbeitbar – das geht auf dem Ursprungskonto –, damit eine Umbuchung nicht aus zwei Richtungen widersprüchlich geändert wird. **Beide Sichten nennen das Gegenüber.** In der Kategoriespalte steht das jeweils andere Konto in eckigen Klammern: auf dem Ausgangskonto das Ziel, auf dem Gegenkonto das Ursprungskonto. In der Gegensicht liefert die Abfrage dafür `b.konto_id` als `gegenkonto_id` – dieselbe Spalte, aus der anderen Richtung gelesen. Ohne diese Angabe stünde auf dem Gegenkonto eine Zeile, die sich „Umbuchung“ nennt und verschweigt, woher sie kommt. Dieselbe Dreiteilung gilt für die Salden (`Buchung::saldenJeKonto()`), die alle Konten mit sechs Abfragen auf einmal berechnet statt je Konto einzeln. ## Mehrfachauswahl im Kontoblatt Vor dem Datum steht je Zeile ein Ankreuzfeld, in der Titelzeile eines für alle gerade angezeigten Buchungen. *Auswahl löschen* in der Aktionsleiste schickt die Auswahl an `POST /konten/{id}/buchungen/loeschen`. Zwei Punkte, die sich nicht ändern dürfen: * Der Knopf steht **über** der Tabelle, die Ankreuzfelder stehen **in** ihr. Verbunden sind sie über `form="buchungsauswahl"`, nicht über Verschachtelung – ein Formular um die ganze Tabelle würde das Filterformular umschließen. * Zeilen mit `gegensicht = true` bekommen kein Ankreuzfeld, und der Controller prüft zusätzlich `konto_id`. Die Gegenseite einer fremden Umbuchung wird nur auf ihrem Ursprungskonto gelöscht – dieselbe Regel wie beim Bearbeiten. Filter und Seite hängen an der Abfragezeichenfolge des Formularziels. Der Controller baut den Weg zurück aus den bekannten Feldern neu zusammen, statt ein mitgeschicktes Ziel zu übernehmen; so lässt sich kein fremder Weiterleitungsort einschleusen. ## Kategorie und Gegenkonto in einem Feld Eine Buchung ist entweder kategorisiert **oder** eine Umbuchung – beides zugleich gibt es nicht. Deshalb stehen Kategorien und Konten in derselben Auswahlliste; die Konten unten, in eckigen Klammern, wie das Kategoriefeld im FinanzManager sie schreibt. Das eigene Konto fehlt in der Liste. Die Liste steht in `Views/buchungen/zuordnungsliste.php`. Sie wird nicht nur in der Buchungsmaske, deren Splitzeilen, der Schnellzuordnung im Kontoblatt und im Splitdialog eingebunden, sondern überall, wo eine Kategorie gewählt wird (#124): Regeln samt ihren Splitzeilen, Umsatzabgleich, Buchungsvorlagen, Regelmäßige Buchungen, Zahlungsverkehr und Verträge. Die Werte tragen ein Präfix, das `BuchungenController::zuordnungAus()` wieder auseinandernimmt (`zuordnungAusFormular()` nimmt ersatzweise die versteckten Einzelfelder `kategorie_id`/`gegenkonto_id` einer Tabellenzeile, `zuordnungswert()` baut den Wert für die Vorauswahl): | Wert | Bedeutung | |---|---| | `` (leer) | keine Zuordnung | | `kategorie:12` | Kategorie 12 | | `konto:7` | Umbuchung mit Gegenkonto 7 | | `split` | Splitdialog öffnen (nur im Kontoblatt) | | `depot:7` | Wertpapiergeschäft im Depot 7 (Kontoblatt und Buchungsmaske) | Die Auswahl *Ausgabe / Einnahme* bestimmt seither nur noch die Richtung des Betrags. Angeboten werden nur **aktive** Umbuchungskonten, je Kontengruppe in einer eigenen Gruppe („Umbuchungskonten – Liquide Mittel“, ohne Kontengruppen schlicht „Umbuchungskonten“) und in der Reihenfolge der Kontengruppen (`Konto::umbuchungsgruppen()`). Ein inaktives Konto, das schon gewählt ist, bleibt sichtbar stehen – sonst ginge die Zuordnung beim nächsten Speichern verloren. Alle übrigen inaktiven Konten stehen als verborgene Einträge (`