b6b579a04de931fb92a84b5300dc8d5c94b47ab5
23
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
9e68d10787 |
Ansprechpartner am Vorgang hinterlegen
Ein namentlich adressiertes Schreiben landet bei der Person, die den Vorgang kennt, statt in der Poststelle. Am Vorgang laesst sich jetzt ein Kontakt aus dem Verzeichnis als Ansprechpartner benennen; die Sachstandsanfrage redet ihn an, und sein Name steht ueber der Behoerde. Die Sachstandsanfrage konnte das schon - sie bekam den Namen nur nie uebergeben. entwurf.ts lud einen der verknuepften Kontakte und verwarf ihn wieder. Wer angeredet wird, ist jetzt eine Entscheidung und kein Zufall. Aufbau: - Migration: ansprechpartnerId am Vorgang (SetNull - ein Vorgang darf nicht daran haengen, dass es den Kontakt noch gibt) und anrede am Kontakt. - Die Anrede als reine Funktion in kontakte/anrede.ts. Geraten wird nie: ohne hinterlegte Anrede wird mit vollem Namen angeredet. Ein leerer Name mit gesetzter Anrede faellt auf die allgemeine Anrede zurueck - "Sehr geehrte Frau ," darf nie herausgehen. - Der Wechsel des Ansprechpartners steht mit NAMEN im Protokoll. Zwei cuids nebeneinander waeren protokolliert, aber nicht nachvollziehbar, und nachvollziehbar war die Anforderung. - Der Ansprechpartner wird gegen die Akte geprueft, bevor er gespeichert wird - sein Name steht anschliessend im Briefkopf. Nebenbei behoben: Der Schreiben-Editor verwarf die empfaengerAnschrift aus dem Entwurf, sein State startete immer leer. Jetzt kommt die Anschrift des Ansprechpartners im Formular an. Geprueft: neun Einheitstests fuer die Anrede, fuenf End-to-End-Tests von der Auswahl bis zum Protokolleintrag. Refs #9 |
||
|
|
09a4528c4b |
Rueckkaufswert am Vertrag sichtbar machen
Am Telefon mit der Gesellschaft braucht man die Zahl dort, wo man sie gefragt hat. Auf der Vertragsseite steht jetzt oben der zuletzt mitgeteilte Wert mit Stichtag und Quelle, darunter die Historie; in der Uebersicht traegt ein Wert, der aelter als ein Jahr ist, ein Abzeichen. Ohne Migration, und das ist keine Einsparung, sondern der Punkt: Die Werthistorie gibt es schon als Wertstand am Vermoegensposten, und der kann auf einen Vertrag zeigen. Ein Feld am Vertrag waere genau die zweite Wahrheit, die das Issue ausschliesst - dieselbe Zahl steht im Vermoegensverzeichnis, weil es dieselbe Zahl ist. Aufbau: - Die Ein-Jahres-Grenze als eine getestete Regel in finanzen/wertalter.ts. Sie stand bisher als "> 365" mitten in der Vermoegensseite; jetzt gilt sie am Vertrag genauso. - wertHistorie() fuehrt die Wertstaende aller mit dem Vertrag verknuepften Posten zu einer Reihe zusammen. - Wo ein Rueckkaufswert zu erwarten waere, aber keiner erfasst ist, steht "noch nicht erfasst" statt einer leeren Zelle. Zwei Fehler dabei behoben: - Vertrag und Vermoegensposten wurden ueber die ART des Postens zusammengefuehrt. Ein von Hand als "Sonstiges" angelegter Posten wurde nicht gefunden, und beim ersten Erfassen am Vertrag entstand still ein zweiter - Bericht und Vertrag haetten verschiedene Zahlen gezeigt. Massgeblich ist jetzt die Verknuepfung. - Beim Speichern eines Vermoegenspostens wurde geprueft, ob das verknuepfte Konto zur betreuten Person gehoert; bei Vertrag und Immobilie fehlte die Pruefung, beim Vertragsverweis einer Buchung ebenso. Bei mehreren betreuten Personen haette ein manipuliertes Formular ueber Aktengrenzen hinweg verknuepfen koennen. Geprueft: zehn Einheitstests, vier End-to-End-Tests - darunter der neu eingetragene Wert, der unveraendert im Vermoegensverzeichnis wiederauftaucht, und der Posten mit abweichender Art. Refs #8 |
||
|
|
f8d7752c27 |
Eigene Vorlagen fuer Anschreiben
Wer zum dritten Mal denselben Brief tippt, tippt ihn beim vierten Mal
falsch. Neu unter Einstellungen -> Schreibvorlagen: eigene Texte
hinterlegen, beim freien Schreiben auswaehlen, Platzhalter fuellen sich
aus den Daten, die ohnehin im System stehen.
Aufbau:
- Die Ersetzung als reine Funktionen in schreiben/platzhalter.ts. Drei
Faelle, keiner davon still: Wert einsetzen, sichtbare Luecke
[Aktenzeichen des Gerichts], unbekannter Platzhalter markiert. Ein
Tippfehler in der Vorlage darf das Schreiben nicht verhindern - aber
er muss ins Auge springen.
- Die Bestellungsabsatz-Warnung ist zweistufig: Der erste Speicherversuch
ohne ihn kommt mit einer Rueckfrage zurueck, erst die Bestaetigung
speichert. Ohne den Nachweis der Vertretungsmacht kommt keine Auskunft,
und das faellt erst nach vier Wochen auf. Ein ausformulierter Absatz
zaehlt so viel wie der Platzhalter, ein halber Nachweis ("Beschluss der
Eigentuemerversammlung") nicht.
- Vier mitgelieferte Vorlagen bleiben Code und damit unveraenderlich;
"Als Kopie uebernehmen" legt eine eigene Fassung an. Fuer Rueckkaufswert
und Kuendigung fuehrt die Oberflaeche weiter zuerst zu den
fallbezogenen Erzeugern - die rechnen Kuendigungstermine aus, was eine
Vorlage nicht kann.
- Vorlagen haengen an der betreuten Person, nicht am Benutzer: Sie
beziehen sich auf ihren Fall, liegen im Papierkorb wie alles andere und
sind im Vollexport ihrer Akte enthalten.
Der Editor haelt Betreff und Text als eigenen State. Beim Wechsel der
Vorlage ueberlebte der alte Entwurf die Navigation, die Vorlage kam nie
im Textfeld an - ein key auf dem Editor setzt ihn neu auf. Gefunden hat
das erst der End-to-End-Test.
Geprueft: dreizehn Einheitstests fuer die Ersetzung, sechs
End-to-End-Tests fuer Warnung, Kopie, Aufloesung und Papierkorb.
Refs #7
|
||
|
|
9fe6f10f25 |
Rueckkaufswerte aller Versicherungen in einem Zug anfragen
Vor Vermoegensverzeichnis und Jahresbericht braucht es von jeder Lebens- und Rentenversicherung den Rueckkaufswert zum Stichtag - jedes Mal dieselben fuenf bis zehn Briefe. Neu unter Vertraege: Stichtag waehlen, Liste pruefen, ein Klick. Heraus kommt ein ZIP mit einer Word-Datei je Gesellschaft plus Begleitblatt; je Vertrag entstehen nebenbei die PDF-Fassung in der Akte, die Wiedervorlage nach 28 Tagen und der Anfragevermerk. Aufbau: - Auswahllogik als reine Funktionen in vertraege/serienabfrage.ts: Vorauswahl, Hindernisse mit Begruendung statt stummem Ausblenden, 28-Tage-Sperre im Gleichschritt mit der Wiedervorlage. - Der Lauf in vertraege/serienlauf.ts: je Vertrag das volle Paket aus Issue #4, aber in einem Durchgang. Kein Alles-oder-nichts - ein gescheiterter Vertrag steht mit Grund im Begleitblatt, die uebrigen laufen durch. Eine Transaktion ueber alle waere die falsche Zusage. - Dublettenschutz tagesgenau: Der Doppelklick erzeugt nichts Doppeltes, die bewusste zweite Anfrage nach einer Woche bleibt moeglich. - Die Anzahl steht vor dem Klick auf dem Knopf. Gekuendigte Vertraege bleiben anfragbar: Bis zum Wirksamwerden besteht der Vertrag, und genau dann will der Sozialhilfetraeger den Wert sehen. Geprueft: zwoelf Einheitstests, drei End-to-End-Tests inklusive der Nebenwirkungen in der Datenbank und des Dublettenschutzes. 602 Unit-Tests, 87 e2e, null Typfehler. Refs #6 |
||
|
|
b59d11c9b4 |
Schreiben als E-Mail uebernehmen
Im Schreiben-Editor gibt es jetzt den Umschalter Brief/E-Mail. Die E-Mail-Fassung wird aus dem - womoeglich bearbeiteten - Brieftext abgeleitet: ohne Absenderblock, Anschrift und Datumszeile, aber mit dem Bestellungsabsatz. Betreff und Text einzeln kopierbar. Die offene Entscheidung aus dem Issue (mailto oder .eml) ist nach Rueckfrage mit BEIDES beantwortet: - mailto fuer kurze Texte. Der Knopf verschwindet, sobald der kodierte Link 1800 Zeichen uebersteigt - mailto uebergibt den Text in der Adresszeile, deren Laenge nirgends zugesichert ist, und ein halbierter Brief faellt erst beim Empfaenger auf. Die Grenze wird NACH der URL-Kodierung gerechnet; 900 Umlaute sind roh harmlos und kodiert weit drueber. - .eml immer: RFC 5322 mit X-Unsent: 1, quoted-printable fuer den Rumpf, RFC-2047-Kodierung fuer Umlaute im Betreff. Bewusst ohne From-Kopf - ein erfundener Absender wuerde je nach Programm uebernommen. Beide Knoepfe erscheinen nur mit E-Mail-Adresse (aus dem Kontakt uebernommen oder von Hand). Ohne Adresse bleibt das Kopieren. Die Ablage kennt den Unterschied: Eine E-Mail heisst in der Akte "E-Mail: ...", und abgelegt wird die E-Mail-Fassung samt Empfaengeradresse im Kopf der PDF - in der Akte soll stehen, was tatsaechlich hinausging. Sicherung in emailFassung(): Findet sich keine Anrede (frei umgebauter Text), wird NICHTS entfernt. Lieber ein Briefkopf zu viel in der Mail als stillschweigend verlorener Inhalt. Geprueft: 20 Einheitstests, vier End-to-End-Tests auf beiden Geraeteprofilen. 590 Unit-Tests, 81 e2e, null Typfehler. Refs #5 |
||
|
|
c21d03abf6 |
Schreiben erzeugen, ausgeben und ablegen
Die Anschreiben gab es bisher nur als Text zum Kopieren. Danach war
Handarbeit: in Word nachbauen, ausdrucken, abschicken - und noch einmal
von Hand als Dokument in die Akte legen. Der letzte Schritt wurde am
haeufigsten vergessen, und dann fehlt der Nachweis im Bericht.
Neu unter /schreiben/neu: bearbeiten, als Word-Datei herunterladen, in
die Akte legen, Wiedervorlage setzen - in einem Zug. Eine Seite fuer alle
Arten und alle drei Empfaengerwege, statt die Bearbeitung dreimal an
Vertrag, Vorgang und Freitext nachzubauen.
Der dritte Weg ist der eigentliche Zugewinn: Schreiben an eine Stelle,
zu der es noch keinen Datensatz gibt. Am Anfang einer Betreuung ist das
der Normalfall - man fragt ja an, um zu erfahren, ob dort ueberhaupt
etwas besteht. Aus der freien Anschrift laesst sich mit einem Klick ein
Kontakt anlegen.
Zwei Entscheidungen, die erklaert gehoeren:
Die Akte bekommt ein PDF, heruntergeladen wird .docx. Der
Objektspeicher nimmt nur PDF und Bilder an, geprueft an den Magic Bytes.
Eine .docx ist ein ZIP-Archiv; sie durchzulassen hiesse jedes ZIP
durchzulassen, und diese Pruefung ist eine bewusste Zusage der
Anwendung. Sachlich ist PDF fuer den Nachweis ohnehin richtiger: Es
aendert sich nicht mehr. Die Oberflaeche sagt dazu, dass eine spaeter in
Word geaenderte Fassung von der Akte abweicht.
Der Berichtssatz in berichte/docx.ts bleibt unangetastet. Er setzt einen
Bericht mit Kopfzeilen und Unterschriftenblock; ein Brief braucht
Absender, Anschriftenfeld, Datumszeile und Grussformel. Beides in eine
Funktion zu zwingen haette beide Ausgaben schlechter gemacht. Der
Umsetzungshinweis im Issue ("muss nur angebunden werden") war insoweit
falsch - von mir selbst geschrieben.
Die vorhandenen Erzeuger (Rueckkaufswert, Kuendigung, Sachstandsanfrage)
bleiben unveraendert; ihr Wortlaut ist getestet und erprobt.
Geprueft: zehn Einheitstests, darunter der Gleichschritt der
Wiedervorlagefristen ueber zwei Module hinweg. Fuenf End-to-End-Tests
auf beiden Geraeteprofilen: Entwurf, Vertragsbezug, Word-Download,
Ablage samt Wiedervorlage, Kontakt aus freier Anschrift.
Refs #4
|
||
|
|
fa994350d6 |
Dauerzahlungen zu einem Vertrag gelten als belegt
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 |
||
|
|
aa0cc66292 |
Wiederkehrende Zahlungen aufspueren
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 |
||
|
|
0128bf71e7 |
Seitenleiste bleibt stehen, Benutzermenue wieder erreichbar
Am Schreibtisch war "Mein Konto" praktisch nicht zu finden - und damit auch nicht Passwortwechsel, zweiter Faktor und Passkey-Verwaltung. Die Seitenleiste hatte keine feste Hoehe und wuchs mit der Seite mit. Ihr Fuss mit dem Benutzermenue landete deshalb am Ende der SEITE statt am unteren Bildschirmrand: auf /heute bei 1632 Pixel in einem 900 Pixel hohen Fenster. Nachgemessen, nicht geraten - im Browser auf fuenf Seiten. Der Fehler sah nach nichts aus. Kein Element fehlte, nichts war kaputt, die Seite sah richtig aus. Man musste nur ganz nach unten scrollen. Behoben mit lg:sticky, lg:top-0, lg:h-dvh und lg:self-start. Das self-start gehoert dazu, weil ein Flex-Kind sonst auf die volle Zeilenhoehe gestreckt wird und die feste Hoehe wieder verliert. Die Navigation dazwischen hat bereits overflow-y-auto und scrollt bei Bedarf in sich selbst. Am Handy war es nie ein Problem - dort sitzt das Menue in der ohnehin festen Kopfzeile. Neu: e2e/benutzermenue.spec.ts fragt nicht, ob das Menue im DOM ist, sondern ob es im sichtbaren Bereich steht - auf fuenf Seiten, an drei Scrollpositionen, auf einem flachen Laptopfenster und am Handy. Die Position in der Mitte ist Absicht: Am Seitenende wird eine mitwachsende Leiste zufaellig doch sichtbar, der Test waere gruen und der Fehler trotzdem da. Beide Sichtbarkeitstests sind gegen den kaputten Zustand gegengeprueft und schlagen dort an. Refs #1 |
||
|
|
c38c988c3c |
feat: Anmelden mit Passkey
Passkeys (WebAuthn/FIDO2) als zweiter Anmeldeweg - auf der Anmeldeseite und bei der erneuten Bestaetigung vor sensiblen Aktionen. Verwaltet unter "Mein Konto". Der Passkey ERSETZT den zweiten Faktor, statt zu ihm hinzuzukommen. Das ist tragbar, weil er zwei Faktoren in sich vereint: Besitz des Geraets und dessen Entsperrung. Damit das keine Annahme bleibt, wird die Nutzerverifikation angefordert (userVerification: 'required') und serverseitig nachgeprueft - eine Antwort ohne sie wird abgewiesen. TOTP muss vorher eingerichtet sein. Ein Passkey haengt an einem Geraet; waere er der einzige Weg hinein, koennte ein verlorenes Handy die Akte schliessen. Aufbau: - Die Pruefung laeuft vollstaendig im Credentials-Provider "passkey", nicht in der Server Action davor. Ein "war schon geprueft" ueber die Grenze zu reichen waere die Stelle, an der sich Anmeldungen faelschen lassen. - Challenges liegen serverseitig und werden per DELETE genau einmal verbraucht - die Datenbank entscheidet, wer sie bekommt. - Bereichskennung und Herkunft kommen aus AUTH_URL, nicht aus einer zweiten Konfigurationsquelle. - Ohne https wird der Knopf gar nicht erst angeboten. - Rate-Limiting, Kontosperre und Audit-Log gelten unveraendert; die Anmeldung nennt den verwendeten Passkey namentlich. Zwei Anmerkungen zur Migration: - "prisma migrate diff" wollte zusaetzlich die Trigramm-Indizes aus prisma/sql/suche.sql loeschen, die Prisma nicht kennt. Die Zeilen sind entfernt und im Migrationskopf vermerkt - ein DROP haette die unscharfe Suche stillgelegt, ohne dass etwas fehlgeschlagen waere. - "prisma format" hat das Schema durchgaengig neu ausgerichtet. Ohne Whitespace betrachtet sind es 88 hinzugefuegte Zeilen und keine geloeschte. Geprueft: 21 Einheitstests, drei End-to-End-Tests gegen Chromiums virtuellen Authenticator (einrichten, anmelden, bestaetigen, entfernen, Geraet ohne Verifikation abgewiesen), beide Geraeteprofile. 496 Tests, null Typfehler, Produktionsbau steht. Nebenbei: Die Zahlen im README waren veraltet (24 statt 33 Modelle, 46 statt 79 Seiten). Sie stehen jetzt mit dem Befehl daneben, mit dem man sie nachzaehlt. |
||
|
|
0e7da42f27 |
docs: Bedienungsanleitung und Roadmap
Bis jetzt gab es Code und Betriebsdokumentation, aber nichts, was einem beim Arbeiten hilft. Die Anleitung ist kein Handbuch zum Durchlesen, sondern die zwoelf Handgriffe, die im Alltag vorkommen - vom Behoerdenschreiben aufnehmen bis zum Vertrag kuendigen. Dazu der Jahresrhythmus, die Berichte, was am Handy anders ist, und ein Abschnitt "wenn etwas schiefgeht" mit den Faellen, die wirklich passieren: Handy verloren, Code wird nicht angenommen, Frist stimmt nicht, Bericht zeigt eine Differenz. Ein eigener Abschnitt sagt, was die Anwendung NICHT tut. Wer sich auf etwas verlaesst, das sie nicht leistet, faellt sonst genau dann darauf herein, wenn es darauf ankommt. Sie ist unter /anleitung auch in der Anwendung lesbar - dieselbe Datei, kein zweiter Text, der auseinanderlaeuft. Wer im Heim steht und nicht weiterweiss, hat sie auf dem Telefon dabei, und der Druckknopf legt sie zu den Betreuungsunterlagen. Dafuer ein schlichter Markdown-Baustein statt einer Bibliothek: die Anwendung zeigt genau zwei solche Dokumente, beide schreibe ich selbst. Rohes HTML wird nicht durchgereicht. Die Datenschutzseite benutzt ihn jetzt ebenfalls, statt eine zweite Umsetzung zu pflegen. Beide Dateien werden ueber outputFileTracingIncludes in den Standalone-Build gezogen - der Pfad entsteht erst zur Laufzeit, die Dateiverfolgung von Next.js kann ihn nicht erkennen. Ohne den Eintrag waeren beide Seiten im Behaelter leer. Die Roadmap sortiert die 44 Schema-Wuensche der Etappen nach einer einzigen Frage zuerst: Kann daraus ein falscher Wert in einem Bericht ans Gericht werden? Was das kann, steht in 1.1. Dazu ein Abschnitt "bewusst nicht geplant" mit Begruendung - damit klar ist, worauf niemand wartet. Etappe: 8 Phase: Haertung |
||
|
|
b2ac13e128 |
feat: Versionierung mit Changelog, Version 1.0.0
Bis jetzt stand nirgends, welcher Stand laeuft. Beim Aktualisieren auf einem Server ist das die erste Frage. CHANGELOG.md nach Keep a Changelog, Versionsnummern nach SemVer. Der Changelog sagt, was sich beim Arbeiten aendert - nicht, welche Datei angefasst wurde. Dazu eine Tabelle, was Major, Minor und Patch fuer den Betreiber praktisch bedeuten, und der Hinweis, dass sich eine Datenbank-Migration nicht zurueckrollen laesst. Die Version steht bewusst an zwei Stellen: package.json und src/lib/version.ts. Ein Import der package.json waere im Standalone-Build eine andere Datei als die des Projekts - die Zahl haenge dann an der Laufzeit. Ein Test haelt beide zusammen, und "npm run version" setzt sie gemeinsam samt Changelog-Abschnitt. Committet und getaggt wird nicht automatisch. Ein Changelog-Eintrag, den niemand gegengelesen hat, ist keinen Deut besser als keiner. Sichtbar wird die Version unten in der Seitenleiste und unter /api/gesundheit - dort mit Git-Stand, den der Docker-Build hineinreicht. Auf der Anmeldeseite steht sie nicht: wer nicht angemeldet ist, muss nicht wissen, welche Fassung hier laeuft. Etappe: 8 Phase: Haertung |
||
|
|
468202249a |
fix: Doppelte Wurzelroute entfernt, Produktionsbuild geprueft
Zwei Dateien loesten denselben Pfad "/" auf: src/app/page.tsx und src/app/(app)/page.tsx. Next.js uebersetzt das noch, erzeugt beim Standalone-Build aber ein unvollstaendiges Client-Manifest - und genau dieses Verzeichnis kopiert das Docker-Abbild. Im Behaelter waere die Anwendung damit erst beim Start umgefallen. Die Weiterleitung liegt jetzt nur noch ausserhalb der Routengruppe. Dazu vier ungenutzte Importe und Konstanten, die den Build blockierten, und eine ESLint-Regel, die mit Unterstrich benannte Parameter durchgehen laesst - _gemeldeterMimeTyp in der Dateiablage wird absichtlich nicht benutzt, weil der vom Browser gemeldete MIME-Typ nichts beweist. Der Name sagt das; ihn zu entfernen wuerde die Schnittstelle verschlechtern. Geprueft am fertigen Standalone-Build, also genau so, wie der Behaelter startet: - strenge CSP greift (nonce plus strict-dynamic, kein unsafe-inline fuer Skripte) und blockiert dabei nichts Eigenes - Anmeldung, alle Fachseiten, Benutzermenue und Schnellerfassung funktionieren - kein Konsolenfehler, keine blockierte Anfrage - keine einzige Anfrage an einen fremden Host Etappe: 8 Phase: Haertung |
||
|
|
5cc7f5c140 |
feat: Jahresbericht, Vollexport, Wissensbasis und Mitleser-Freigaben
Etappe 7. Die Ausgabeschicht - alles, was diese Anwendung nach aussen abgibt. Der Jahresbericht nach § 1863 BGB ist ein ENTWURF zum Ueberarbeiten, kein fertiger Text. Er zieht sich die fuenf Gliederungspunkte aus Kontaktprotokollen, Gesundheitsereignissen und Vorgangsstatus zusammen; was davor abgegeben wird, entscheidet der Betreuer. Das sagt auch die Oberflaeche. Ausgabe als PDF und als DOCX, damit sich weiterschreiben laesst. Vollexport als ZIP-Strom mit Gegendruck, damit auch grosse Bestaende ohne Speicherausbruch durchgehen: Originaldateien nach Jahren sortiert, alle Tabellen als entschluesseltes JSON, die erzeugten Berichte, ein LIESMICH und SHA-256 je Datei. Damit laesst sich die Anwendung verlassen - kein Datensilo. Der Export ist eine sensible Aktion: nur BETREUER und ADMIN, nur nach frischer Anmeldung, immer mit Audit-Eintrag. Ein Mitleser kommt nicht heran. 37 Hinweiskarten in daten/hinweise.json, Zod-geprueft und ohne Neustart pflegbar. Jede mit Rechtsgrundlage, Link und dem stehenden Vermerk, dass sie keine Rechtsberatung ersetzt. Modulweise Freigaben fuer Mitleser, mit ausdruecklicher Warnung bei Gesundheit, Finanzen und Sozialhilfe. Rollen- und Freigabeaenderungen erfordern eine frische Anmeldung und stehen im Audit-Log. Nebenbei geloest: Webpack loest .tsx vor .ts auf, TypeScript und Vitest umgekehrt. Ein Modul, das beide Endungen traegt, importiert sich unter einem der beiden Werkzeuge selbst. Deshalb heissen die Bausteine jetzt eindeutig (jahresbericht.entwurf.ts, .inhalt.tsx, .ausgabe.ts) und werden ueber eine gemeinsame Fassade weitergereicht. 41 Tests. Etappe: 7 Phase: Ausgabe |
||
|
|
b69a12da8f |
feat: Immobilie, Versicherungen und Heim mit COPD-Verlauf
Etappe 6. Das leerstehende Haus, die Vertraege und der Gesundheitsverlauf - die drei Bereiche, in denen fuer diesen Fall das meiste Geld und das meiste Risiko liegt. Leerstands-Checkliste mit zehn Punkten, jeder mit Begruendung. Der wichtigste: den Gebaeudeversicherer ueber den Leerstand informieren - sonst droht Leistungsfreiheit, und zwar genau dann, wenn es brennt oder eine Leitung platzt. Dazu Verkehrssicherungspflicht, Frostschutz zum Oktober, Kontrollgaenge, Zaehlerstaende, Schluesselverzeichnis. Der Kuendigungsrechner nennt den spaetesten ABSENDETAG, nicht den Fristablauf: eine Kuendigung wirkt mit dem Zugang, nicht mit dem Abschicken. Drei Tage Postlaufpuffer sind einkalkuliert. Der Kuendigungs-Assistent beantwortet die Genehmigungsfrage nach § 1850 BGB nicht - er stellt sie und verlangt den Gang zum Rechtspfleger. Der Versandnachweis ist Pflichtfeld: ohne ihn kommt die Kuendigung nicht durch die Pruefung. Leistungszuschlag nach § 43c SGB XI mit Stand 01.01.2024 (15/30/50/75 Prozent nach Verweildauer, erst ab Pflegegrad 2). Die Oberflaeche zeigt, ab wann die naechste Stufe greift - das ist bares Geld und wird regelmaessig uebersehen. COPD-Schnellerfassung fuer Exazerbation, Sauerstoff, Sturz, Krankenhaus, Hilfsmittel und Impfungen, mit STIKO-Plan fuer COPD. Beschreibungen verschluesselt. Die Jahresauswertung ueber Exazerbationen und Krankenhaustage wandert in den Jahresbericht und begruendet eine Hoeherstufung. 74 Tests. Etappe: 6 Phase: Substanz |
||
|
|
d35e24f830 |
feat: Sozialhilfe-Modul mit Nachforderungs-Tracker und Fristpruefung
Etappe 5. Bildet das laufende Verfahren beim Bezirk Oberfranken ab. Rechtsbehelfsfrist: § 187 Abs. 1, § 188 Abs. 2 und 3 BGB, danach § 64 Abs. 3 SGG - hier verschieben Wochenende und Feiertag das Fristende auf den naechsten Werktag. Das ist genau umgekehrt zu den Betreuerfristen, wo bewusst nicht verschoben wird, und deshalb liegt die Feiertagsliste getrennt. Zwei bayerische Sonderfaelle, die haeufig falsch gemacht werden: Mariae Himmelfahrt verschiebt NICHT (Art. 1 Abs. 1 S. 2 BayFTG - der Feiertag gilt nur in ueberwiegend katholischen Gemeinden, Bayreuth ist keine), und der Buss- und Bettag ist in Bayern seit 1995 kein Feiertag mehr. Beide erzeugen stattdessen einen ausdruecklichen Hinweis, damit man die Frist im Zweifel selbst nachrechnet. Der Rechenweg wird als Schrittliste ausgegeben. Heimkosten: Der Dashboard-Agent hatte einen fachlichen Widerspruch gefunden - der einrichtungseinheitliche Eigenanteil auf der Heimrechnung ist bereits um die Leistung der Pflegekasse gekuerzt. Wird sie zusaetzlich als Deckung erfasst, entsteht eine Doppelzaehlung von ueber tausend Euro im Monat. Das Modul rechnet jetzt ausdruecklich nach der Lesart EIGENANTEIL_NETTO, fuehrt die Pflegekassenleistung nur nachrichtlich mit und warnt, wenn sie doch belegt ist. Die Oberflaeche sagt auf einer eigenen Karte, was hier gegen was gerechnet wird. Die Schonvermoegens-Bewertung liefert Argumentationshilfen zum Uebernehmen, keine Automatik - die Einordnung obliegt dem Traeger und gegebenenfalls dem Sozialgericht. Das steht an jeder dieser Karten. Alle jaehrlich veraenderlichen Betraege stehen an einer Stelle mit Stand-Datum und Prueftermin. Die Bekleidungspauschale bewusst ohne Zahl: sie ist regional und steht im Bescheid. 46 Tests. Etappe: 5 Phase: Behoerde |
||
|
|
3833a9dd8f |
docs: README, Datenschutztexte, Dockerfile und Sicherungsskripte
Damit ist Etappe 1 abgeschlossen und betriebsfertig dokumentiert. README mit beiden Betriebswegen. Docker Compose auf eigenem Server ist der Hauptweg; Vercel und Neon sind vollstaendig beschrieben, aber mit einer klaren Begruendung, warum ich davon abrate: beide Anbieter sind US-Unternehmen und unterliegen auch bei EU-Region potenziell dem CLOUD-Act. Bei Gesundheitsdaten einer nicht entscheidungsfaehigen Person ist das eine Abwaegung, die man bewusst treffen muss. Reverse-Proxy-Beispiele fuer Caddy, nginx und Traefik - der Proxy selbst ist nicht Teil des Compose-Setups, weil er auf dem Zielserver schon steht. Die Datenbank hat keine Portfreigabe. Sicherung: pg_dump schreibt direkt in die Verschluesselung, der Klartext landet nie auf der Platte. Danach eine Selbstpruefung - laesst sich die Datei nicht wieder entschluesseln, endet der Lauf mit Fehler statt mit einem stillen Scheinerfolg. Der jeweils erste Stand eines Monats bleibt dauerhaft stehen. Die README sagt ausdruecklich, was das Backup NICHT enthaelt: den Schluessel ENC_KEY_V1 und die Dateien im Objektspeicher. Dazu eine Anleitung, die Wiederherstellung einmal im Jahr zu ueben - mit dem letzten Schritt, in der Anwendung nachzusehen, ob die Diagnosen im Klartext erscheinen. Erst dann stimmt nicht nur die Datenbank, sondern auch der Schluessel. Verarbeitungsverzeichnis und Datenschutzhinweise als ausfuellbare Textbausteine. Die Hinweise sind in einfacher Sprache geschrieben - sie sollen der betreuten Person vorgelesen werden koennen. Die Seite /datenschutz liest denselben Text aus docs/, damit es nicht zwei Fassungen gibt, die auseinanderlaufen. Dockerfile in drei Stufen mit Tesseract, deutschen Sprachdaten und poppler-utils. Zeitzone Europe/Berlin ist gesetzt - ohne sie rechnet der Server in UTC und verschiebt Fristen um einen Tag. Etappe: 1 Phase: Fundament |
||
|
|
6b4df4c9cb |
feat: Dashboard Heute, Kontakte und Kontaktprotokoll
Das Dashboard beantwortet die Frage, was heute ansteht - nicht, welche Daten es gibt. Jede Kachel endet in einer Handlung: Fristen-Ampel, wartet auf andere (mit Tagen seit dem letzten Kontakt), offene Nachforderungen des Bezirks mit Fortschrittsbalken, ungepruefter Posteingang, unverbuchte Belege, Kennzahlen. Zwoelf Abfragen in einem Promise.all, kein N+1. Cent-Betraege werden beim Verlassen der Serverseite gewandelt. Kacheln zu Modulen, die ein Mitleser nicht freigegeben bekommen hat, entfallen ganz - sie zeigen nicht "0", denn das waere eine Aussage ueber Daten, die er nicht sehen darf. Kontaktprotokoll als Schnellerfassung: Datum vorbelegt, Art als grosse Knopfreihe, Dauer als Schnellwahl, ein Freitextfeld. Alles andere eingeklappt. Ziel sind unter 20 Sekunden am Handy. Das Haekchen "persoenlicher Kontakt zur betreuten Person" ist bewusst NICHT vorbelegt - es wandert in den Jahresbericht nach § 1863 BGB, und ein versehentlich gesetztes Haekchen waere dort eine falsche Angabe gegenueber dem Gericht. Aus demselben Grund gilt der Textbaustein "regelmaessiger persoenlicher Kontakt" erst ab persoenlichem Kontakt in mindestens 75 Prozent der Monate. Sonst schreibt die Auswertung ausdruecklich, dass kein persoenlicher Kontakt dokumentiert ist. Ein geschoenter Bericht hilft niemandem. Protokolldaten werden als 12:00 UTC gespeichert und durchgaengig in UTC gerechnet, sonst kippt die Zeitzone einzelne Besuchstage. 51 Tests. Etappe: 2 Phase: Alltag |
||
|
|
0cdd4edfdc |
feat: Fristen, Pflichtfristen aus dem Bestellungsdatum und ICS-Feed
Die gesetzlichen Fristen entstehen von selbst, sobald das Bestellungsdatum in den Stammdaten steht - Vermoegensverzeichnis und Anfangsbericht nach drei Monaten, Jahresbericht und Vermoegensuebersicht zum Ende jedes Betreuungsjahres, Aufwandspauschale, Landespflegegeld, Zuzahlungsbefreiung, Rueckkaufswerte. Zwei Punkte, an denen es sonst schiefgeht: Das Betreuungsjahr endet am Tag VOR dem Jahrestag (§ 188 Abs. 2 BGB), bei Bestellung am 29. Februar am 28. (§ 188 Abs. 3). Ohne diese Regel verrutscht die ganze Kette um einen Tag. Faellt eine Frist auf Samstag, Sonntag oder Feiertag, wird sie NICHT verschoben. § 193 BGB verschiebt zwar den Ablauf, aber als Betreuer will ich frueher fertig sein, nicht spaeter. Stattdessen steht ein Hinweis in der Beschreibung. Bayerische Feiertage inklusive, ueber die Osterformel berechnet. Pflichtfristen tragen bewusst keine RRULE: sonst entstuende beim Erledigen eine Dublette neben der ohnehin abgeleiteten Folgefrist. Beim erneuten Ableiten werden nur offene Fristen nachgezogen, Erledigtes bleibt unangetastet. Der ICS-Feed kommt ohne Token-Feld im Schema aus - die Signatur ist ein HMAC ueber betreuteId und userId. VALARM je Vorwarnstufe, Zeilenfaltung nach RFC 5545. 51 Tests. Etappe: 2 Phase: Alltag |
||
|
|
1217725076 |
feat: Vorgaenge als Fallakte mit Genehmigungskette und Vorlagen
Jeder Vorgang ist eine kleine Akte: Kopfdaten, Aufgabenliste zum Abhaken, Genehmigungsstufen und ein Zeitstrahl, der Dokumente, Kontaktprotokolle, Fristen, Buchungen und Statuswechsel zusammenfuehrt. Die abnahmerelevante Regel: Ein genehmigungspflichtiger Vorgang laesst sich nicht auf VOLLZOGEN setzen, solange kein Beschlussdokument hinterlegt ist. Geprueft wird in darfStatusWechseln() UND in der Server Action - nicht nur im UI, wo das Auswahlfeld die Stufe ohnehin gar nicht erst anbietet. Hintergrund ist § 1858 BGB: die Genehmigung muss vor dem Rechtsgeschaeft vorliegen, nicht danach. Neun Vorlagen mit durchdachter Aufgabenreihenfolge. Bei Wohnungs- aufloesung und Hausverkauf erzwingen sie die Kette Mitteilung -> Genehmigung -> Rechtsgeschaeft; ein Test prueft das. Kanban ohne Drag-and-Drop: Statuswechsel laufen ueber Auswahlfeld und Knopf. Am Handy ist das verlaesslicher, und am Schreibtisch kostet es einen Klick mehr statt einer verlorenen Karte. Zeitzonen: alsTag() bildet den oertlichen Kalendertag auf Mitternacht UTC ab. Ohne das rutscht in Deutschland jede Faelligkeit einen Tag zurueck. 33 Tests. Etappe: 2 Phase: Alltag |
||
|
|
63352272b8 |
fix: Navigationsstruktur aus der Client-Komponente herausloesen
Das Server-Layout filtert die Navigation danach, welche Module der angemeldete Benutzer sehen darf. Es importierte NAV_STRUKTUR aber aus einer Datei mit "use client" - dort kommt auf dem Server kein Wert an, sondern nur ein Verweis. NAV_STRUKTUR.filter war entsprechend keine Funktion, und jede Fachseite lief in einen Serverfehler. Zweiter Fehler in derselben Zeile: die Eintraege trugen die Icons als React-Komponenten. Die lassen sich nicht ueber die Server/Client-Grenze reichen. Beides behoben, indem die Struktur nach src/lib/navigation-struktur.ts wandert und dort nur noch ein Kuerzel je Icon steht. Die Client- Komponente uebersetzt das Kuerzel. Die Wiederausfuhr aus navigation.tsx wurde bewusst weggelassen, sonst stellt sich dieselbe Falle wieder auf. Dazu scripts/screenshots.mjs: meldet sich mit dem Entwicklungszugang an und fotografiert Seiten am Schreibtisch und am Handy, hell und dunkel. Etappe: 1 Phase: Fundament |
||
|
|
22b3a9b9e9 |
feat: Stammdaten, Ersteinrichtung, Zweitfaktor und Protokollansicht
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 |
||
|
|
dcd3a8ce71 |
feat: Oberflaechenbausteine, Anwendungsrahmen und Konventionen
Basisbausteine im shadcn/ui-Stil, aber mit drei bewussten Abweichungen: - Bedienelemente sind 44px hoch statt 36px. Ich bediene die App oft am Handy im Heim oder auf dem Amt; kleiner ist dort nicht treffsicher. - Auswahlfelder benutzen das native <select>. Android und iOS oeffnen dafuer ihre eigene Liste - schneller als jeder Nachbau, und mit Screenreader und Tastatur funktioniert es ohne Zutun. - Eingabefelder haben mindestens 16px Schrift, sonst zoomt iOS-Safari beim Hineintippen das Formular heran. <Feld> verdrahtet Beschriftung, Erklaerung und Fehler ueber aria-describedby. Der Erklaerungstext ist kein Beiwerk: die Anwendung benutzt Begriffe aus dem Betreuungsrecht, die niemand nachschlagen soll. <Rechtshinweis> ist der stehende Vermerk, dass hier keine Rechtsberatung stattfindet. Er steht an jeder Stelle mit Paragraphenbezug. Der Anwendungsrahmen fuehrt drei Bedingungen zusammen, ohne die niemand an Fachdaten kommt: angemeldet, zweiter Faktor eingerichtet, mindestens eine betreute Person zugeordnet. Die Navigation zeigt Zaehler fuer ueberfaellige Fristen und neuen Posteingang - die Frage "wo muss ich hin?" wird schon im Menue beantwortet. Das Farbschema wird per Inline-Skript mit CSP-Nonce gesetzt, bevor die Seite gezeichnet wird - sonst blitzt beim Laden das helle Thema auf. docs/KONVENTIONEN.md haelt fest, was in allen weiteren Etappen gilt: Verzeichnisaufbau, Cent-Betraege, Umgang mit verschluesselten Feldern, Zugriffspruefung, Audit-Pflicht, Ampelfarben, Commit-Trailer. Etappe: 1 Phase: Fundament |