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
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
SPERRFRIST_TAGE,
|
||||
bewerten,
|
||||
heuteSchonAngefragt,
|
||||
kandidatenBewerten,
|
||||
type SerienKandidat,
|
||||
} from '@/lib/vertraege/serienabfrage'
|
||||
import { RUECKKAUFSWERT_WIEDERVORLAGE_TAGE } from '@/lib/vertraege/rueckkaufswert'
|
||||
|
||||
/**
|
||||
* Serienabfrage der Rückkaufswerte — die Auswahllogik.
|
||||
*
|
||||
* Der teuerste Fehler wäre hier ein Brief zu viel an der falschen Stelle
|
||||
* (die Gesellschaft antwortet auf die zweite Anfrage mit Verwirrung) oder
|
||||
* ein stumm fehlender Vertrag (der Wert fehlt dann im Vermögensverzeichnis).
|
||||
* Beides wird geprüft.
|
||||
*/
|
||||
|
||||
const HEUTE = new Date('2026-08-23T00:00:00.000Z')
|
||||
|
||||
function vertrag(teile: Partial<SerienKandidat> = {}): SerienKandidat {
|
||||
return {
|
||||
id: 'v1',
|
||||
bezeichnung: 'Lebensversicherung',
|
||||
gesellschaft: 'Allianz',
|
||||
art: 'LEBENSVERSICHERUNG',
|
||||
status: 'AKTIV',
|
||||
rueckkaufswertAngefragtAm: null,
|
||||
...teile,
|
||||
}
|
||||
}
|
||||
|
||||
function vorTagen(tage: number): Date {
|
||||
const d = new Date(HEUTE)
|
||||
d.setUTCDate(d.getUTCDate() - tage)
|
||||
return d
|
||||
}
|
||||
|
||||
describe('Bewertung eines Vertrags', () => {
|
||||
it('wählt eine unbefragte Lebensversicherung vor', () => {
|
||||
const b = bewerten(vertrag(), HEUTE)
|
||||
expect(b.moeglich).toBe(true)
|
||||
expect(b.vorausgewaehlt).toBe(true)
|
||||
expect(b.hindernis).toBeNull()
|
||||
})
|
||||
|
||||
it('schließt Vertragsarten ohne Rückkaufswert aus — mit Begründung', () => {
|
||||
const b = bewerten(vertrag({ art: 'HAFTPFLICHT' }), HEUTE)
|
||||
expect(b.moeglich).toBe(false)
|
||||
expect(b.hindernis).toContain('keinen Rückkaufswert')
|
||||
})
|
||||
|
||||
it('schließt verwertete und abgelaufene Verträge aus', () => {
|
||||
expect(bewerten(vertrag({ status: 'VERWERTET' }), HEUTE).moeglich).toBe(false)
|
||||
expect(bewerten(vertrag({ status: 'ABGELAUFEN' }), HEUTE).moeglich).toBe(false)
|
||||
})
|
||||
|
||||
it('lässt einen gekündigten Vertrag zu', () => {
|
||||
// Bis zum Wirksamwerden besteht der Vertrag - und genau dann will der
|
||||
// Sozialhilfeträger den Wert sehen.
|
||||
const b = bewerten(vertrag({ status: 'GEKUENDIGT' }), HEUTE)
|
||||
expect(b.moeglich).toBe(true)
|
||||
})
|
||||
|
||||
it('verlangt eine Gesellschaft und sagt, was zu tun ist', () => {
|
||||
const b = bewerten(vertrag({ gesellschaft: null }), HEUTE)
|
||||
expect(b.moeglich).toBe(false)
|
||||
expect(b.hindernis).toContain('nachtragen')
|
||||
})
|
||||
|
||||
it('wählt einen frisch angefragten Vertrag NICHT vor, lässt ihn aber wählbar', () => {
|
||||
// Der Brief könnte verloren gegangen sein - dann wählt man von Hand an.
|
||||
const b = bewerten(vertrag({ rueckkaufswertAngefragtAm: vorTagen(10) }), HEUTE)
|
||||
expect(b.moeglich).toBe(true)
|
||||
expect(b.vorausgewaehlt).toBe(false)
|
||||
expect(b.hindernis).toContain('bereits angefragt')
|
||||
expect(b.tageSeitAnfrage).toBe(10)
|
||||
})
|
||||
|
||||
it('wählt nach Ablauf der Sperrfrist wieder vor', () => {
|
||||
const b = bewerten(vertrag({ rueckkaufswertAngefragtAm: vorTagen(SPERRFRIST_TAGE) }), HEUTE)
|
||||
expect(b.vorausgewaehlt).toBe(true)
|
||||
})
|
||||
|
||||
it('hält die Sperrfrist mit der Wiedervorlage im Gleichschritt', () => {
|
||||
// Beide 28 Tage: Solange die Wiedervorlage läuft, ist die Antwort
|
||||
// unterwegs - eine zweite Anfrage davor wäre nur Verwirrung.
|
||||
expect(SPERRFRIST_TAGE).toBe(RUECKKAUFSWERT_WIEDERVORLAGE_TAGE)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Sortierung der Kandidaten', () => {
|
||||
it('stellt Vorausgewählte nach oben, Unmögliche ans Ende', () => {
|
||||
const bewertet = kandidatenBewerten(
|
||||
[
|
||||
vertrag({ id: 'a', bezeichnung: 'Zulassung', art: 'HAFTPFLICHT' }),
|
||||
vertrag({ id: 'b', bezeichnung: 'Beitragsfrei' }),
|
||||
vertrag({ id: 'c', bezeichnung: 'Angefragt', rueckkaufswertAngefragtAm: vorTagen(5) }),
|
||||
],
|
||||
HEUTE,
|
||||
)
|
||||
|
||||
expect(bewertet[0]!.vertrag.id).toBe('b')
|
||||
expect(bewertet[1]!.vertrag.id).toBe('c')
|
||||
expect(bewertet[2]!.vertrag.id).toBe('a')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Dublettenschutz', () => {
|
||||
it('erkennt die Anfrage vom selben Tag', () => {
|
||||
expect(heuteSchonAngefragt(HEUTE, HEUTE)).toBe(true)
|
||||
})
|
||||
|
||||
it('lässt eine bewusste zweite Anfrage nach einer Woche zu', () => {
|
||||
// Tagesgenau, nicht sperrfristgenau: Der Doppelklick soll abgefangen
|
||||
// werden, die bewusste Wiederholung (Brief verloren) nicht.
|
||||
expect(heuteSchonAngefragt(vorTagen(7), HEUTE)).toBe(false)
|
||||
})
|
||||
|
||||
it('meldet ohne Anfrage nichts', () => {
|
||||
expect(heuteSchonAngefragt(null, HEUTE)).toBe(false)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user