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
125 lines
4.2 KiB
TypeScript
125 lines
4.2 KiB
TypeScript
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)
|
|
})
|
|
})
|