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,185 @@
|
||||
import { expect, test } from '@playwright/test'
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
import { anmelden } from './hilfen'
|
||||
|
||||
/**
|
||||
* Serienabfrage der Rückkaufswerte — gegen die echte Anwendung.
|
||||
*
|
||||
* Geprüft wird der ganze Lauf: Vorauswahl, ZIP-Download, die
|
||||
* Nebenwirkungen (Anfragedatum, Wiedervorlage, Ausgangsdokumente) und der
|
||||
* Dublettenschutz beim zweiten Lauf am selben Tag.
|
||||
*/
|
||||
|
||||
const KENNUNG = 'Prüfhausen Serie'
|
||||
|
||||
async function mitDb<T>(arbeit: (db: PrismaClient) => Promise<T>): Promise<T> {
|
||||
const db = new PrismaClient()
|
||||
try {
|
||||
return await arbeit(db)
|
||||
} finally {
|
||||
await db.$disconnect()
|
||||
}
|
||||
}
|
||||
|
||||
async function aufraeumen(): Promise<void> {
|
||||
await mitDb(async (db) => {
|
||||
await db.dokument.deleteMany({ where: { empfaenger: { contains: KENNUNG } } })
|
||||
await db.frist.deleteMany({ where: { titel: { contains: KENNUNG } } })
|
||||
await db.vertrag.deleteMany({ where: { bezeichnung: { contains: KENNUNG } } })
|
||||
})
|
||||
}
|
||||
|
||||
test.describe('Serienabfrage', () => {
|
||||
test.beforeAll(async () => {
|
||||
await aufraeumen()
|
||||
await mitDb(async (db) => {
|
||||
const betreute = await db.betreute.findMany({
|
||||
where: { geloeschtAm: null },
|
||||
select: { id: true },
|
||||
})
|
||||
for (const person of betreute) {
|
||||
// Zwei anfragbare, eine ohne Rueckkaufswert, eine ohne Gesellschaft.
|
||||
await db.vertrag.createMany({
|
||||
data: [
|
||||
{
|
||||
betreuteId: person.id,
|
||||
bezeichnung: `${KENNUNG} Leben`,
|
||||
gesellschaft: `${KENNUNG} Lebensversicherung`,
|
||||
art: 'LEBENSVERSICHERUNG',
|
||||
status: 'AKTIV',
|
||||
},
|
||||
{
|
||||
betreuteId: person.id,
|
||||
bezeichnung: `${KENNUNG} Sterbegeld`,
|
||||
gesellschaft: `${KENNUNG} Sterbekasse`,
|
||||
art: 'STERBEGELDVERSICHERUNG',
|
||||
status: 'AKTIV',
|
||||
},
|
||||
{
|
||||
betreuteId: person.id,
|
||||
bezeichnung: `${KENNUNG} Haftpflicht`,
|
||||
gesellschaft: `${KENNUNG} Allgemeine`,
|
||||
art: 'HAFTPFLICHT',
|
||||
status: 'AKTIV',
|
||||
},
|
||||
{
|
||||
betreuteId: person.id,
|
||||
bezeichnung: `${KENNUNG} Ohne Gesellschaft`,
|
||||
gesellschaft: null,
|
||||
art: 'LEBENSVERSICHERUNG',
|
||||
status: 'AKTIV',
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
test.afterAll(async () => {
|
||||
await aufraeumen()
|
||||
})
|
||||
|
||||
test('wählt richtig vor und benennt die Hindernisse', async ({ page }) => {
|
||||
await anmelden(page)
|
||||
await page.goto('/vertraege/serienabfrage/seite')
|
||||
|
||||
// Die beiden anfragbaren sind vorausgewählt …
|
||||
await expect(
|
||||
page.locator('li', { hasText: `${KENNUNG} Leben` }).locator('input[type="checkbox"]'),
|
||||
).toBeChecked()
|
||||
await expect(
|
||||
page.locator('li', { hasText: `${KENNUNG} Sterbegeld` }).locator('input[type="checkbox"]'),
|
||||
).toBeChecked()
|
||||
|
||||
// … die ohne Gesellschaft steht mit Begründung da, ohne Haken …
|
||||
const ohne = page.locator('li', { hasText: `${KENNUNG} Ohne Gesellschaft` })
|
||||
await expect(ohne).toContainText('nachtragen')
|
||||
await expect(ohne.locator('input[type="checkbox"]')).toHaveCount(0)
|
||||
|
||||
// … und die Haftpflicht taucht gar nicht erst auf.
|
||||
await expect(page.locator('li', { hasText: `${KENNUNG} Haftpflicht` })).toHaveCount(0)
|
||||
|
||||
// Die Zusammenfassung steht auf dem Knopf, BEVOR etwas entsteht -
|
||||
// und sie stimmt mit der Zahl der gesetzten Haken ueberein. Auf die
|
||||
// absolute Zahl laesst sich nicht bauen: Der Demofall bringt eigene
|
||||
// Vertraege mit.
|
||||
const haken = await page.locator('input[type="checkbox"]:checked').count()
|
||||
expect(haken).toBeGreaterThanOrEqual(2)
|
||||
await expect(
|
||||
page.getByRole('button', { name: new RegExp(`${haken} Schreiben erzeugen`) }),
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test('führt den Lauf durch: ZIP, Akte, Wiedervorlagen, Anfragedatum', async ({ page }) => {
|
||||
await anmelden(page)
|
||||
await page.goto('/vertraege/serienabfrage/seite')
|
||||
|
||||
const [download] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.getByRole('button', { name: /Schreiben erzeugen und herunterladen/ }).click(),
|
||||
])
|
||||
expect(download.suggestedFilename()).toMatch(/^\d{4}-\d{2}-\d{2}_Rueckkaufswert-Anfragen\.zip$/)
|
||||
|
||||
// Nebenwirkungen in der Datenbank: Anfragedatum, Dokumente, Fristen.
|
||||
const stand = await mitDb(async (db) => {
|
||||
const angefragt = await db.vertrag.count({
|
||||
where: {
|
||||
bezeichnung: { in: [`${KENNUNG} Leben`, `${KENNUNG} Sterbegeld`] },
|
||||
rueckkaufswertAngefragtAm: { not: null },
|
||||
},
|
||||
})
|
||||
const dokumente = await db.dokument.count({
|
||||
where: { empfaenger: { contains: KENNUNG }, richtung: 'AUSGANG' },
|
||||
})
|
||||
const fristen = await db.frist.count({
|
||||
where: { titel: { contains: KENNUNG }, typ: 'WIEDERVORLAGE' },
|
||||
})
|
||||
return { angefragt, dokumente, fristen }
|
||||
})
|
||||
|
||||
// Beide betreute Personen haben die Verträge - angefragt wird aber nur
|
||||
// bei der aktiven. Deshalb: genau 2 von 4.
|
||||
expect(stand.angefragt).toBe(2)
|
||||
expect(stand.dokumente).toBe(2)
|
||||
expect(stand.fristen).toBe(2)
|
||||
|
||||
// Auf der Seite stehen die beiden jetzt als frisch angefragt.
|
||||
await page.goto('/vertraege/serienabfrage/seite')
|
||||
await expect(page.getByText(/bereits angefragt/).first()).toBeVisible()
|
||||
})
|
||||
|
||||
test('erzeugt beim zweiten Lauf am selben Tag keine Dubletten', async ({ page }) => {
|
||||
await anmelden(page)
|
||||
await page.goto('/vertraege/serienabfrage/seite')
|
||||
|
||||
// Alles abwaehlen, dann NUR den frisch angefragten Vertrag anwaehlen.
|
||||
// So prueft der Lauf genau den Dublettenschutz und fasst die uebrigen
|
||||
// Vertraege nicht an.
|
||||
const angehakte = page.locator('input[type="checkbox"]:checked')
|
||||
while ((await angehakte.count()) > 0) {
|
||||
await angehakte.first().uncheck()
|
||||
}
|
||||
const leben = page.locator('li', { hasText: `${KENNUNG} Leben` })
|
||||
await leben.locator('input[type="checkbox"]').check()
|
||||
|
||||
const [download] = await Promise.all([
|
||||
page.waitForEvent('download'),
|
||||
page.getByRole('button', { name: /Schreiben erzeugen und herunterladen/ }).click(),
|
||||
])
|
||||
expect(download.suggestedFilename()).toContain('.zip')
|
||||
|
||||
// Der Dublettenschutz greift: weiterhin genau ein Dokument und eine
|
||||
// Frist für diesen Vertrag.
|
||||
const stand = await mitDb(async (db) => {
|
||||
const dokumente = await db.dokument.count({
|
||||
where: { empfaenger: `${KENNUNG} Lebensversicherung` },
|
||||
})
|
||||
const fristen = await db.frist.count({
|
||||
where: { titel: { contains: `${KENNUNG} Leben` }, typ: 'WIEDERVORLAGE' },
|
||||
})
|
||||
return { dokumente, fristen }
|
||||
})
|
||||
expect(stand.dokumente).toBe(1)
|
||||
expect(stand.fristen).toBe(1)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user