Bei einer monatlichen Praemie gibt es keinen Beleg - es gibt einen
Vertrag, und der wird einmal abgeschlossen, nicht monatlich quittiert.
Bisher blieb nur ein Freitextfeld pro Buchung: zwoelfmal im Jahr
derselbe Satz, mal Anzahl der Vertraege.
Buchung.vertragId mit onDelete: SetNull. Der Belegzustand kennt jetzt
drei Faelle statt zwei, getrennt ausgewiesen - ein Vertrag belegt, eine
Begruendung erklaert nur; zusammengeworfen verliert die Belegquote ihre
Aussage gegenueber dem Rechtspfleger.
Dazu: automatische Zuordnung beim Import bei eindeutiger Gegenstelle,
Sammelzuordnung ueber die Gruppen aus Issue #3, und die Rechnungslegung
nennt den Vertrag samt Nummer statt "ohne Beleg".
Die Sicherung dagegen: Weicht der Betrag um mehr als ein Fuenftel vom
hinterlegten Beitrag ab, faellt die Anerkennung weg und die Buchung wird
als "Betrag pruefen" markiert. Sonst waere die Zuordnung eine Hintertuer.
Zur Toleranz - ein Entwurf und seine Korrektur: Zuerst verglich ich den
Betrag gegen alle ueblichen Zahlweisen, damit es auch passt, wenn die
Bank anders abbucht als der Vertrag es fuehrt. Der eigene Test hat das
widerlegt: Bei 42,50 monatlich liegen 420 innerhalb von 20 Prozent des
Jahresbetrags von 510 - ausgerechnet der Fall aus der Aufgabenstellung
waere durchgerutscht. Jetzt wird stur gegen den hinterlegten Beitrag
verglichen. Bucht die Bank anders ab, stimmen die Vertragsdaten nicht,
und das soll auffallen.
Behoben: Die Buchungsliste hatte eine eigene, fast gleiche Datenabfrage
neben der aus berichte/gemeinsam.ts. Nur eine von beiden wurde
nachgezogen, in der Liste stand weiter "Beleg fehlt". Beide benutzen
jetzt BUCHUNG_EINBEZUG und zuBuchungEingabe - das kann nicht halb
vergessen werden. Gefunden hat es der Browsertest.
Nebenbei: Die Roadmap trug noch "Aktuelle Fassung 1.1.2" - Nachwirkung
der parallelen Bearbeitung von #3 und #10. Nachgezogen wie zugesagt.
Geprueft: 23 neue Einheitstests, die Haelfte davon darauf gerichtet, dass
die Anerkennung auch wirklich wegfaellt. Fuenf End-to-End-Tests auf
beiden Geraeteprofilen. 560 Unit-Tests, 63 e2e, null Typfehler.
Refs #11
Am Anfang einer Betreuung weiss niemand, welche Vertraege es gibt. Die
Antwort steht aber im Kontoauszug: Wer regelmaessig abbucht, hat einen
Vertrag. Neu unter Vermoegen & Kasse wird aus hunderten Buchungszeilen
eine Arbeitsliste.
Aufbau:
- Die Erkennung liegt als reine Funktionen in
src/lib/finanzen/wiederkehrend.ts, ohne Datenbank. Nur so laesst sich
jede Einordnung einzeln nachrechnen.
- Der Datenzugriff steht getrennt in wiederkehrend-laden.ts.
- Ein kleines Modell haelt fest, was bewusst KEIN Vertrag ist. Es haengt
am normalisierten Empfaengernamen, nicht an Buchungen - nur so
ueberlebt die Entscheidung den naechsten Import.
Zwei Haltungen liegen der Erkennung zugrunde:
Im Zweifel getrennt lassen. Zusammengefasst wird nur das Unstrittige:
Gross- und Kleinschreibung, Satzzeichen, Umlaute, Rechtsform am Ende.
Keine Aehnlichkeitssuche - "Stadtwerke Bamberg" und "Stadtwerke
Bayreuth" trennen drei Zeichen und zwei Rechnungssteller. Zwei Zeilen zu
viel sind harmlos; eine falsche Gruppe verbirgt einen Vertrag.
Nichts behaupten, was nicht belegt ist. Statt "monatlich" steht dort
"6 Zahlungen, etwa monatlich". Schwankt der Betrag, wird die Spanne
genannt. Ohne erkennbaren Abstand wird nicht hochgerechnet, sondern
gezeigt, was geflossen ist.
Dazu eine zweite Liste fuer einzelne groessere Zahlungen: Die
Gruppierung braucht zwei Zahlungen, ein Jahresbeitrag erscheint bei
einem importierten Jahr aber nur einmal - und ausgerechnet
Versicherungen zahlt man oft jaehrlich.
Geprueft: 37 Einheitstests, die Haelfte davon in der Gegenrichtung, plus
vier End-to-End-Tests auf beiden Geraeteprofilen. Der Browsertest hat
zwei Fehler gefunden, die Typpruefung und Einheitstests durchgelassen
haben - darunter ein verlorener Backslash, der die Vorbelegung des
Beitrags stillschweigend verschluckte.
Nebenbei: .claude/ ins .gitignore. Dort legen parallel arbeitende
Agenten ihre Worktrees ab; ein "git add -A" haette einen kompletten
zweiten Checkout eingecheckt.
Refs #3
Damit ist Etappe 1 fachlich vollstaendig.
Ersteinrichtung in drei Schritten, bewusst ausserhalb des
Anwendungsrahmens: der setzt Benutzer, zweiten Faktor UND eine betreute
Person voraus - beim allerersten Start gibt es nichts davon. Die Aktion
fuer den ersten Zugang sperrt sich selbst, sobald ein Benutzer existiert.
Zweiter Faktor: Das Geheimnis wird zuerst nur abgelegt, ohne
totpAktiviertAm. Erst ein echter Code aus der App schaltet ihn scharf.
Wer sich beim Abtippen vertut oder die falsche App nimmt, wuerde sich
sonst dauerhaft aussperren. Erst danach gibt es die
Wiederherstellungscodes - einmal angezeigt, danach nur noch als Hash.
Betreuungsjahr (src/lib/stammdaten/betreuungsjahr.ts): laeuft ab dem Tag
der Bestellung, nicht ab dem Kalenderjahr. Schrittweise vorwaerts
gerechnet statt ueber eine Differenz, damit Schaltjahre und der
29. Februar von selbst stimmen. Daran haengen Jahresbericht,
Rechnungslegung und die Ausschlussfrist der Aufwandspauschale.
Aendert sich das Bestellungsdatum, werden die Pflichtfristen neu
abgeleitet - ueber einen dynamischen Import, damit die Stammdaten auch
dann speicherbar bleiben, wenn das Fristenmodul fehlt.
Protokollansicht zeigt je Eintrag die geaenderten Felder als alt -> neu.
Die Aenderungen liegen verschluesselt, weil dort Diagnosen und
Kontostaende stehen koennen.
Seed legt bewusst keine echten Personendaten an, nur den ersten Zugang.
Mit SEED_DEMO=true kommt ein vollstaendiger Beispielfall dazu - Erika
Musterfrau, COPD GOLD III, Pflegegrad 3, Heimeinzug Januar 2026,
leerstehendes Haus, zwei Versicherungen, laufender Antrag beim Bezirk
mit acht Nachforderungen. Nur ausserhalb der Produktion.
Docker Compose ohne Reverse Proxy - der ist auf dem Zielserver
vorhanden. Die Datenbank hat keine Portfreigabe und ist nur ueber das
interne Netz erreichbar; die Anwendung horcht auf 127.0.0.1.
Etappe: 1
Phase: Fundament