Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7780739a1a |
+57
-1
@@ -26,6 +26,61 @@ Wiederherstellung*.
|
|||||||
|
|
||||||
_Noch nichts._
|
_Noch nichts._
|
||||||
|
|
||||||
|
## [1.0.4] – 2026-08-22
|
||||||
|
|
||||||
|
### Behoben
|
||||||
|
|
||||||
|
- **Die Ersteinrichtung ließ sich nicht abschließen.** Schritt 3 meldete
|
||||||
|
„Bitte prüfe die markierten Felder" — markierte aber keines. Man kam an
|
||||||
|
dieser Stelle nicht weiter.
|
||||||
|
|
||||||
|
Ursache: Das kurze Formular der Ersteinrichtung schickt neun Felder, das
|
||||||
|
vollständige unter *Stammdaten* alle. Beide gehen durch dasselbe Schema,
|
||||||
|
und die optionalen Felder waren zwar `nullable`, aber nicht `optional` —
|
||||||
|
ein Feld, das gar nicht abgeschickt wurde, ist `undefined`, nicht
|
||||||
|
`null`. Beanstandet wurden also zwei Dutzend Felder, die auf dem
|
||||||
|
Bildschirm nie standen.
|
||||||
|
|
||||||
|
- **Eine Meldung ohne zugehöriges Feld kann nicht mehr unsichtbar
|
||||||
|
bleiben.** Beanstandet die Prüfung ein Feld, das das Formular gar nicht
|
||||||
|
gesendet hat, sagt die Anwendung das jetzt ausdrücklich und schreibt es
|
||||||
|
ins Serverlog — statt es als Eingabefehler auszugeben, den niemand
|
||||||
|
finden kann. Das ist ein Fehler im Programm, und er soll auch so
|
||||||
|
aussehen.
|
||||||
|
|
||||||
|
- **Eine betreute Person ließ sich nicht endgültig löschen.** Beim Löschen
|
||||||
|
setzt Postgres den Verweis im Audit-Log auf NULL (`ON DELETE SET NULL`),
|
||||||
|
und genau dieses UPDATE hat der Unveränderlichkeits-Trigger abgewiesen.
|
||||||
|
Betroffen war damit auch das Leeren des Papierkorbs nach 30 Tagen und
|
||||||
|
das Entfernen des Demofalls.
|
||||||
|
|
||||||
|
Der Trigger erlaubt jetzt genau eine Ausnahme: das Nullen der beiden
|
||||||
|
Fremdschlüssel `betreuteId` und `userId`. Jede andere Änderung bleibt
|
||||||
|
verboten, auch das Setzen eines Fremdschlüssels auf einen **anderen**
|
||||||
|
Wert. Der Nachweis bleibt vollständig — Zeitpunkt, Aktion, Bezeichnung,
|
||||||
|
die verschlüsselte Änderung und die Momentaufnahmen von Name und
|
||||||
|
E-Mail sind unberührt. Es verschwindet nur der Verweis auf einen
|
||||||
|
Datensatz, den es nicht mehr gibt.
|
||||||
|
|
||||||
|
**Beim Update nötig:** das SQL-Skript erneut ausführen, sonst bleibt der
|
||||||
|
alte Trigger stehen.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose exec app node node_modules/prisma/build/index.js db execute --schema prisma/schema.prisma --file prisma/sql/audit-schutz.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
### Geprüft
|
||||||
|
|
||||||
|
- Sieben neue Tests (`tests/stammdaten.test.ts`) prüfen genau die
|
||||||
|
Formularinhalte der Ersteinrichtung, dazu das vollständige Formular mit
|
||||||
|
leeren Feldern und die weiterhin verlangten Pflichtangaben.
|
||||||
|
- Der Ablauf ist zusätzlich im Browser durchgespielt worden: Anmeldung,
|
||||||
|
zweiter Faktor, Schritt 3, Landung auf dem Dashboard — und die daraus
|
||||||
|
abgeleiteten Pflichtfristen samt Ausschlussfrist für die
|
||||||
|
Aufwandspauschale.
|
||||||
|
- Der geänderte Trigger ist gegen eine laufende Datenbank geprüft:
|
||||||
|
Löschen gelingt, Fälschen und Löschen von Einträgen scheitert weiterhin.
|
||||||
|
|
||||||
## [1.0.3] – 2026-08-22
|
## [1.0.3] – 2026-08-22
|
||||||
|
|
||||||
Keine Änderung am Programm — nur an der Konfigurationsvorlage und der
|
Keine Änderung am Programm — nur an der Konfigurationsvorlage und der
|
||||||
@@ -296,7 +351,8 @@ Was davon wann angegangen wird, steht in [`docs/ROADMAP.md`](docs/ROADMAP.md).
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
[Unveröffentlicht]: https://git.wilde-server.de/florian.wilde/betreuungs-cockpit/compare/v1.0.3...HEAD
|
[Unveröffentlicht]: https://git.wilde-server.de/florian.wilde/betreuungs-cockpit/compare/v1.0.4...HEAD
|
||||||
|
[1.0.4]: https://git.wilde-server.de/florian.wilde/betreuungs-cockpit/compare/v1.0.3...v1.0.4
|
||||||
[1.0.3]: https://git.wilde-server.de/florian.wilde/betreuungs-cockpit/compare/v1.0.2...v1.0.3
|
[1.0.3]: https://git.wilde-server.de/florian.wilde/betreuungs-cockpit/compare/v1.0.2...v1.0.3
|
||||||
[1.0.2]: https://git.wilde-server.de/florian.wilde/betreuungs-cockpit/compare/v1.0.1...v1.0.2
|
[1.0.2]: https://git.wilde-server.de/florian.wilde/betreuungs-cockpit/compare/v1.0.1...v1.0.2
|
||||||
[1.0.1]: https://git.wilde-server.de/florian.wilde/betreuungs-cockpit/compare/v1.0.0...v1.0.1
|
[1.0.1]: https://git.wilde-server.de/florian.wilde/betreuungs-cockpit/compare/v1.0.0...v1.0.1
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "betreuungs-cockpit",
|
"name": "betreuungs-cockpit",
|
||||||
"version": "1.0.3",
|
"version": "1.0.4",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "Arbeits- und Dokumentationswerkzeug fuer die ehrenamtliche rechtliche Betreuung",
|
"description": "Arbeits- und Dokumentationswerkzeug fuer die ehrenamtliche rechtliche Betreuung",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
@@ -25,6 +25,42 @@ BEGIN
|
|||||||
RETURN OLD;
|
RETURN OLD;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Eine einzige Ausnahme beim UPDATE: das Nullen der beiden
|
||||||
|
* Fremdschluessel.
|
||||||
|
*
|
||||||
|
* Wird eine betreute Person oder ein Benutzer endgueltig geloescht
|
||||||
|
* (Papierkorb nach 30 Tagen), setzt Postgres wegen ON DELETE SET NULL
|
||||||
|
* die Spalten betreuteId bzw. userId auf NULL. Ohne diese Ausnahme
|
||||||
|
* scheitert das Loeschen - und der Papierkorb liesse sich nie leeren.
|
||||||
|
*
|
||||||
|
* Der Nachweis bleibt dabei vollstaendig: Zeitpunkt, Aktion, Entitaet,
|
||||||
|
* Bezeichnung und die verschluesselte Aenderung sind unberuehrt, ebenso
|
||||||
|
* die Momentaufnahmen userEmail und userName. Es verschwindet nur der
|
||||||
|
* Verweis auf einen Datensatz, den es nicht mehr gibt.
|
||||||
|
*
|
||||||
|
* Jede andere Aenderung wird abgewiesen - auch das Zurueckschreiben
|
||||||
|
* eines Fremdschluessels auf einen anderen Wert.
|
||||||
|
*/
|
||||||
|
IF TG_OP = 'UPDATE'
|
||||||
|
AND NEW.id = OLD.id
|
||||||
|
AND NEW.zeitpunkt = OLD.zeitpunkt
|
||||||
|
AND NEW.aktion = OLD.aktion
|
||||||
|
AND NEW.entitaet = OLD.entitaet
|
||||||
|
AND NEW."entitaetId" IS NOT DISTINCT FROM OLD."entitaetId"
|
||||||
|
AND NEW.bezeichnung IS NOT DISTINCT FROM OLD.bezeichnung
|
||||||
|
AND NEW."userEmail" IS NOT DISTINCT FROM OLD."userEmail"
|
||||||
|
AND NEW."userName" IS NOT DISTINCT FROM OLD."userName"
|
||||||
|
AND NEW."aenderungVerschluesselt" IS NOT DISTINCT FROM OLD."aenderungVerschluesselt"
|
||||||
|
AND NEW."ipHash" IS NOT DISTINCT FROM OLD."ipHash"
|
||||||
|
AND NEW."userAgent" IS NOT DISTINCT FROM OLD."userAgent"
|
||||||
|
-- Die beiden Fremdschluessel duerfen sich aendern, aber NUR nach NULL.
|
||||||
|
AND (NEW."betreuteId" IS NULL OR NEW."betreuteId" IS NOT DISTINCT FROM OLD."betreuteId")
|
||||||
|
AND (NEW."userId" IS NULL OR NEW."userId" IS NOT DISTINCT FROM OLD."userId")
|
||||||
|
THEN
|
||||||
|
RETURN NEW;
|
||||||
|
END IF;
|
||||||
|
|
||||||
RAISE EXCEPTION
|
RAISE EXCEPTION
|
||||||
'Das Audit-Log ist unveraenderlich. % auf audit_log ist nicht zulaessig.',
|
'Das Audit-Log ist unveraenderlich. % auf audit_log ist nicht zulaessig.',
|
||||||
TG_OP
|
TG_OP
|
||||||
|
|||||||
@@ -8,21 +8,36 @@ import { z } from 'zod'
|
|||||||
* ist, nicht was falsch war.
|
* ist, nicht was falsch war.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** Leere Zeichenketten aus einem Formular sollen zu null werden, nicht zu "". */
|
/**
|
||||||
|
* Optionales Textfeld.
|
||||||
|
*
|
||||||
|
* Drei Eingaben muessen zu null werden:
|
||||||
|
* "" das Feld war da, blieb aber leer
|
||||||
|
* null ausdruecklich geleert
|
||||||
|
* undefined das Feld war im Formular ueberhaupt nicht dabei
|
||||||
|
*
|
||||||
|
* Der letzte Fall ist der wichtige: Das kurze Formular der Ersteinrichtung
|
||||||
|
* schickt nur eine Handvoll Felder, das vollstaendige unter Stammdaten
|
||||||
|
* alle. Beide gehen durch dasselbe Schema. Ohne .optional() wuerde die
|
||||||
|
* Ersteinrichtung an zwei Dutzend Feldern scheitern, die gar nicht auf dem
|
||||||
|
* Bildschirm stehen - und die Meldung liesse sich keinem Feld zuordnen.
|
||||||
|
*/
|
||||||
const optionalerText = (max = 200) =>
|
const optionalerText = (max = 200) =>
|
||||||
z
|
z
|
||||||
.string()
|
.string()
|
||||||
.trim()
|
.trim()
|
||||||
.max(max, `Bitte höchstens ${max} Zeichen.`)
|
.max(max, `Bitte höchstens ${max} Zeichen.`)
|
||||||
.transform((wert) => (wert === '' ? null : wert))
|
.optional()
|
||||||
.nullable()
|
.nullable()
|
||||||
|
.transform((wert) => (wert === undefined || wert === '' ? null : wert))
|
||||||
|
|
||||||
/** HTML-Datumsfelder liefern "" oder "2026-01-15". */
|
/** HTML-Datumsfelder liefern "" oder "2026-01-15" - oder gar nichts. */
|
||||||
const optionalesDatum = z
|
const optionalesDatum = z
|
||||||
.string()
|
.string()
|
||||||
.trim()
|
.trim()
|
||||||
.transform((wert) => (wert === '' ? null : wert))
|
.optional()
|
||||||
.nullable()
|
.nullable()
|
||||||
|
.transform((wert) => (wert === undefined || wert === '' ? null : wert))
|
||||||
.refine((wert) => wert === null || /^\d{4}-\d{2}-\d{2}$/.test(wert), {
|
.refine((wert) => wert === null || /^\d{4}-\d{2}-\d{2}$/.test(wert), {
|
||||||
message: 'Bitte ein Datum im Format TT.MM.JJJJ wählen.',
|
message: 'Bitte ein Datum im Format TT.MM.JJJJ wählen.',
|
||||||
})
|
})
|
||||||
|
|||||||
+1
-1
@@ -17,7 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/** Muss zur "version" in der package.json passen. */
|
/** Muss zur "version" in der package.json passen. */
|
||||||
export const VERSION = '1.0.3'
|
export const VERSION = '1.0.4'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Kurzer Git-Stand, beim Bauen hineingereicht:
|
* Kurzer Git-Stand, beim Bauen hineingereicht:
|
||||||
|
|||||||
@@ -53,6 +53,49 @@ function felderAusFehler(fehler: z.ZodError): Record<string, string> {
|
|||||||
return ergebnis
|
return ergebnis
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Baut die Antwort auf einen Pruefungsfehler.
|
||||||
|
*
|
||||||
|
* Der wichtige Teil ist die Unterscheidung: Beanstandet die Pruefung ein
|
||||||
|
* Feld, das gar nicht abgeschickt wurde, dann passt das Formular nicht zum
|
||||||
|
* Schema - und die Meldung liesse sich auf dem Bildschirm keinem Feld
|
||||||
|
* zuordnen. Der Nutzer saehe "Bitte pruefe die markierten Felder", ohne
|
||||||
|
* dass irgendetwas markiert waere, und kaeme nie weiter.
|
||||||
|
*
|
||||||
|
* Das ist ein Fehler im Programm, keiner in der Eingabe. Er wird deshalb
|
||||||
|
* benannt und ins Serverlog geschrieben, statt ihn dem Nutzer als
|
||||||
|
* Eingabefehler unterzuschieben.
|
||||||
|
*/
|
||||||
|
function antwortAufFehler(fehler: z.ZodError, formular: FormData): BetreuteZustand {
|
||||||
|
const felder = felderAusFehler(fehler)
|
||||||
|
const gesendet = new Set(formular.keys())
|
||||||
|
const nichtZuordenbar = Object.entries(felder).filter(([feld]) => !gesendet.has(feld))
|
||||||
|
|
||||||
|
if (nichtZuordenbar.length > 0) {
|
||||||
|
const liste = nichtZuordenbar.map(([feld, meldung]) => `${feld}: ${meldung}`).join('; ')
|
||||||
|
console.error(
|
||||||
|
`[stammdaten] Das Formular passt nicht zum Schema. Beanstandet wurden ` +
|
||||||
|
`Felder, die es gar nicht abgeschickt hat: ${liste}`,
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
fehler:
|
||||||
|
'Das Formular passt nicht zur Prüfung — betroffen sind Felder, die ' +
|
||||||
|
'hier gar nicht stehen: ' +
|
||||||
|
nichtZuordenbar.map(([feld]) => feld).join(', ') +
|
||||||
|
'. Das ist ein Fehler in der Anwendung, nicht in deiner Eingabe. ' +
|
||||||
|
'Einzelheiten stehen im Serverlog.',
|
||||||
|
felder,
|
||||||
|
werte: werteMerken(formular),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
fehler: 'Bitte prüfe die markierten Felder.',
|
||||||
|
felder,
|
||||||
|
werte: werteMerken(formular),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function werteMerken(formular: FormData): Record<string, string> {
|
function werteMerken(formular: FormData): Record<string, string> {
|
||||||
const werte: Record<string, string> = {}
|
const werte: Record<string, string> = {}
|
||||||
for (const [name, wert] of formular.entries()) {
|
for (const [name, wert] of formular.entries()) {
|
||||||
@@ -77,13 +120,7 @@ export async function betreuteAnlegen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const gelesen = BetreuteSchema.safeParse(formularLesen(formular))
|
const gelesen = BetreuteSchema.safeParse(formularLesen(formular))
|
||||||
if (!gelesen.success) {
|
if (!gelesen.success) return antwortAufFehler(gelesen.error, formular)
|
||||||
return {
|
|
||||||
fehler: 'Bitte prüfe die markierten Felder.',
|
|
||||||
felder: felderAusFehler(gelesen.error),
|
|
||||||
werte: werteMerken(formular),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const { offen, verschluesselt } = aufteilen(gelesen.data)
|
const { offen, verschluesselt } = aufteilen(gelesen.data)
|
||||||
|
|
||||||
@@ -134,13 +171,7 @@ export async function betreuteAendern(
|
|||||||
const { benutzer, betreute } = await erfordereZugang(betreuteId, 'STAMMDATEN', 'schreiben')
|
const { benutzer, betreute } = await erfordereZugang(betreuteId, 'STAMMDATEN', 'schreiben')
|
||||||
|
|
||||||
const gelesen = BetreuteSchema.safeParse(formularLesen(formular))
|
const gelesen = BetreuteSchema.safeParse(formularLesen(formular))
|
||||||
if (!gelesen.success) {
|
if (!gelesen.success) return antwortAufFehler(gelesen.error, formular)
|
||||||
return {
|
|
||||||
fehler: 'Bitte prüfe die markierten Felder.',
|
|
||||||
felder: felderAusFehler(gelesen.error),
|
|
||||||
werte: werteMerken(formular),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const { offen, verschluesselt } = aufteilen(gelesen.data)
|
const { offen, verschluesselt } = aufteilen(gelesen.data)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,137 @@
|
|||||||
|
import { describe, expect, it } from 'vitest'
|
||||||
|
import { BetreuteSchema, formularLesen } from '@/lib/validierung/betreute'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Eingabepruefung der Stammdaten.
|
||||||
|
*
|
||||||
|
* Anlass für diese Datei war ein Fehler im Betrieb: Die Ersteinrichtung
|
||||||
|
* ließ sich nicht abschließen. Das Formular meldete „Bitte prüfe die
|
||||||
|
* markierten Felder", markierte aber keines — weil die Meldungen zu
|
||||||
|
* Feldern gehörten, die dort gar nicht stehen.
|
||||||
|
*
|
||||||
|
* Ursache: Das kurze Formular der Ersteinrichtung schickt neun Felder,
|
||||||
|
* das vollständige unter Stammdaten alle. Beide gehen durch dasselbe
|
||||||
|
* Schema, und die optionalen Felder waren zwar `nullable`, aber nicht
|
||||||
|
* `optional` — ein fehlendes Feld ist `undefined`, nicht `null`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** Die Felder, die das kurze Formular der Ersteinrichtung tatsächlich schickt. */
|
||||||
|
function ersteinrichtungsFormular(): FormData {
|
||||||
|
const f = new FormData()
|
||||||
|
f.set('vorname', 'Franziska')
|
||||||
|
f.set('nachname', 'Wilde')
|
||||||
|
f.set('geburtsdatum', '1963-09-23')
|
||||||
|
f.set('betreuungsgericht', 'Lichtenfels')
|
||||||
|
f.set('aktenzeichenGericht', 'XVII 355/25')
|
||||||
|
f.set('bestellungAm', '2025-11-07')
|
||||||
|
f.append('aufgabenkreise', 'VERMOEGENSSORGE')
|
||||||
|
f.append('aufgabenkreise', 'GESUNDHEITSSORGE')
|
||||||
|
f.set('befreiterBetreuer', 'on')
|
||||||
|
f.set('ehrenamtlich', 'on')
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Ersteinrichtung', () => {
|
||||||
|
it('nimmt das kurze Formular an, obwohl es die meisten Felder nicht schickt', () => {
|
||||||
|
const gelesen = BetreuteSchema.safeParse(formularLesen(ersteinrichtungsFormular()))
|
||||||
|
|
||||||
|
if (!gelesen.success) {
|
||||||
|
const felder = Object.keys(gelesen.error.flatten().fieldErrors).join(', ')
|
||||||
|
throw new Error(`Abgelehnt wegen: ${felder}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(gelesen.data.vorname).toBe('Franziska')
|
||||||
|
expect(gelesen.data.nachname).toBe('Wilde')
|
||||||
|
expect(gelesen.data.geburtsdatum.toISOString()).toBe('1963-09-23T00:00:00.000Z')
|
||||||
|
expect(gelesen.data.bestellungAm?.toISOString()).toBe('2025-11-07T00:00:00.000Z')
|
||||||
|
expect(gelesen.data.aufgabenkreise).toEqual(['VERMOEGENSSORGE', 'GESUNDHEITSSORGE'])
|
||||||
|
expect(gelesen.data.befreiterBetreuer).toBe(true)
|
||||||
|
expect(gelesen.data.ehrenamtlich).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('macht aus nicht gesendeten Feldern null, nicht undefined', () => {
|
||||||
|
const gelesen = BetreuteSchema.parse(formularLesen(ersteinrichtungsFormular()))
|
||||||
|
|
||||||
|
// Diese Felder standen nie auf dem Bildschirm. Sie muessen als null in
|
||||||
|
// der Datenbank landen, sonst schreibt Prisma sie gar nicht.
|
||||||
|
expect(gelesen.heimName).toBeNull()
|
||||||
|
expect(gelesen.diagnosen).toBeNull()
|
||||||
|
expect(gelesen.geburtsname).toBeNull()
|
||||||
|
expect(gelesen.heimEinzugAm).toBeNull()
|
||||||
|
expect(gelesen.bestellungBis).toBeNull()
|
||||||
|
expect(gelesen.aktenzeichenBezirk).toBeNull()
|
||||||
|
expect(gelesen.pflegegrad).toBeNull()
|
||||||
|
expect(gelesen.schwerbehinderungGdB).toBeNull()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('haelt auch das vollstaendige Formular aus', () => {
|
||||||
|
const f = ersteinrichtungsFormular()
|
||||||
|
// So kommt es aus dem Bearbeitungsformular: alle Felder da, viele leer.
|
||||||
|
for (const feld of [
|
||||||
|
'anrede', 'geburtsname', 'geburtsort',
|
||||||
|
'heimName', 'heimStrasse', 'heimPlz', 'heimOrt', 'heimZimmer', 'heimTelefon',
|
||||||
|
'fruehereStrasse', 'frueherePlz', 'fruehererOrt',
|
||||||
|
'krankenkasse', 'krankenversichertennummer', 'pflegekasse',
|
||||||
|
'schwerbehinderungMerkzeichen', 'diagnosen', 'ermittelterWille',
|
||||||
|
'aufgabenkreiseFreitext', 'aktenzeichenBezirk', 'aktenzeichenSozialamt',
|
||||||
|
'rentenversicherungsnummer', 'steuerId', 'notizen',
|
||||||
|
]) {
|
||||||
|
f.set(feld, '')
|
||||||
|
}
|
||||||
|
for (const feld of ['heimEinzugAm', 'pflegegradSeit', 'pflegegradBescheidVom', 'bestellungBis']) {
|
||||||
|
f.set(feld, '')
|
||||||
|
}
|
||||||
|
f.set('pflegegrad', '')
|
||||||
|
f.set('schwerbehinderungGdB', '')
|
||||||
|
|
||||||
|
const gelesen = BetreuteSchema.safeParse(formularLesen(f))
|
||||||
|
expect(gelesen.success, 'Leere Felder duerfen nicht abgelehnt werden').toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('nimmt ausgefuellte Angaben unveraendert an', () => {
|
||||||
|
const f = ersteinrichtungsFormular()
|
||||||
|
f.set('heimName', ' Seniorenzentrum St. Katharina ')
|
||||||
|
f.set('pflegegrad', '3')
|
||||||
|
f.set('diagnosen', 'COPD GOLD III')
|
||||||
|
|
||||||
|
const gelesen = BetreuteSchema.parse(formularLesen(f))
|
||||||
|
// Fuehrende und nachlaufende Leerzeichen fliegen raus.
|
||||||
|
expect(gelesen.heimName).toBe('Seniorenzentrum St. Katharina')
|
||||||
|
expect(gelesen.pflegegrad).toBe(3)
|
||||||
|
expect(gelesen.diagnosen).toBe('COPD GOLD III')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('besteht weiterhin auf Vorname, Nachname und Geburtsdatum', () => {
|
||||||
|
const f = new FormData()
|
||||||
|
f.set('vorname', '')
|
||||||
|
f.set('nachname', '')
|
||||||
|
f.set('geburtsdatum', '')
|
||||||
|
|
||||||
|
const gelesen = BetreuteSchema.safeParse(formularLesen(f))
|
||||||
|
expect(gelesen.success).toBe(false)
|
||||||
|
|
||||||
|
if (!gelesen.success) {
|
||||||
|
const felder = gelesen.error.flatten().fieldErrors
|
||||||
|
expect(Object.keys(felder).sort()).toEqual(['geburtsdatum', 'nachname', 'vorname'])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('weist ein Geburtsdatum in der Zukunft ab', () => {
|
||||||
|
const f = ersteinrichtungsFormular()
|
||||||
|
f.set('geburtsdatum', '2099-01-01')
|
||||||
|
|
||||||
|
const gelesen = BetreuteSchema.safeParse(formularLesen(f))
|
||||||
|
expect(gelesen.success).toBe(false)
|
||||||
|
if (!gelesen.success) {
|
||||||
|
expect(gelesen.error.flatten().fieldErrors.geburtsdatum?.[0]).toMatch(/Zukunft/)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('weist ein unfertiges Datum ab, statt es stillschweigend zu verwerfen', () => {
|
||||||
|
const f = ersteinrichtungsFormular()
|
||||||
|
f.set('bestellungAm', '2025-11')
|
||||||
|
|
||||||
|
const gelesen = BetreuteSchema.safeParse(formularLesen(f))
|
||||||
|
expect(gelesen.success).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user