From c58a60db23aa1abd446960e7340e244e337a01f0 Mon Sep 17 00:00:00 2001 From: duffyduck Date: Mon, 1 Jun 2026 11:10:23 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20todo.md=20+=20README=20aktualisiert=20(?= =?UTF-8?q?Pentest-Fixes,=20Folgez=C3=A4hler,=20SIM-cardUser,=20EmailProvi?= =?UTF-8?q?der-Label-Override=20etc.)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit todo.md: rund 14 neue Erledigt-Einträge seit dem letzten Stand – gruppiert nach Feature (Folgezähler-Workflow, Anzeige-/UX-Polishing, Pentest 42.5/43.5/43.6) und im klassischen "kompakter Header + Bullets"-Stil. README.md: - Zähler-Bullet um Lieferadress-Pflicht + Folgezähler-Kette ergänzt - Strom/Gas-Vertragsfelder um Verbrauchs-Schätzwert aus Vorvertrag, HT/NT, Sofort-/Neukunden-Bonus, Folgezähler-Wechseldatum + Endstand - SIM-Karten-Liste um "Kartennutzer" für Firmen-/Familienverträge Co-Authored-By: Claude Opus 4.7 (1M context) --- README.md | 18 +++++-- docs/todo.md | 150 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 163 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 61887fc8..db275bf6 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,9 @@ Web-basiertes CRM-System für Kundenverwaltung mit Verträgen (Energie, Telekomm - **Adressen**: Mehrere Liefer-/Melde- und Rechnungsadressen pro Kunde - **Bankkarten**: Mit Ablaufdatum, Aktiv-Status und Dokument-Upload (PDF) - **Ausweise**: Personalausweis, Reisepass, etc. mit Ablaufdatum und Dokument-Upload (PDF) -- **Zähler**: Strom-/Gaszähler mit Zählerstandhistorie +- **Zähler**: Strom-/Gaszähler mit Zählerstandhistorie, + Pflicht-Zuordnung zur Lieferadresse und Folgezähler-Kette + (Auto-Propagation auf alle Verträge mit dem Vorgänger) - **Rechnungen**: Rechnungsverwaltung für Energieverträge mit Dokumenten-Upload - **Vertrags-Cockpit**: Dashboard zur Überwachung offener Aufgaben (fehlende Dokumente, Rechnungen) - **Auto-Vertragsstatus**: Lieferbestätigung-Upload setzt `DRAFT` → `ACTIVE` (mit Vertragsbeginn), @@ -724,11 +726,15 @@ Je nach Vertragstyp werden unterschiedliche Felder im Formular angezeigt: #### Strom & Gas (ELECTRICITY, GAS) -- Zähler-Auswahl -- Jahresverbrauch (kWh/m³) -- Grundpreis, Arbeitspreis -- Bonus +- Zähler-Auswahl (gefiltert auf die Lieferadresse des Vertrags) +- Jahresverbrauch (kWh/m³) – bei Folgeverträgen mit Schätzwert + aus dem Vorvertrag und 1-Klick-Übernahme +- Grundpreis, Arbeitspreis (HT/NT bei Zweitarif) +- Sofort-Bonus, Neukunden-Bonus - Vorversorger, Kundennummer beim Vorversorger +- Folgezähler-Workflow (Zählerwechsel): Wechseldatum + optionaler + Endstand des alten Zählers, der automatisch als Zählerstand + erfasst wird und in die Verbrauchsberechnung einfließt #### Internet (DSL, CABLE, FIBER) @@ -752,6 +758,8 @@ Je nach Vertragstyp werden unterschiedliche Felder im Formular angezeigt: - Rufnummer, SIM-Kartennummer (ICCID) - PIN, PUK (verschlüsselt) - Multisim-Flag, Hauptkarte-Flag + - Kartennutzer (optional) – tatsächlicher Nutzer bei Firmen-/ + Familienverträgen, kann vom Vertragsinhaber abweichen > **Hinweis Multisim:** Nicht buchbar bei Klarmobil, Congstar, Otelo. Benötigt Freenet oder vergleichbar. diff --git a/docs/todo.md b/docs/todo.md index 983bbc85..acc3e72f 100644 --- a/docs/todo.md +++ b/docs/todo.md @@ -97,6 +97,156 @@ isolierte Instanz (keine Multi-Tenancy im Code), Provisioning + Abrechnung ## ✅ Erledigt +- [x] **🆕 EmailProvider-Settings: Override-Feld „Bezeichnung im UI"** + - `customerEmailLabel` existierte im Backend (Schema + + Update-Logik + Public-Endpoint), war im UI aber nicht + erreichbar – das Label kam ausschließlich aus + `deriveLabelFromDomain`. Jetzt neuer optionaler Input + „Bezeichnung im UI" unter dem Domain-Block in + Einstellungen → E-Mail-Provider. Leer = Auto-Ableitung, + befüllt = überschreibt das Tab-Label und alle anderen + Stellen, die `customerEmailLabel` nutzen (max 60 Zeichen). + +- [x] **🆕 SIM-Karten: Feld „Kartennutzer"** + - Bei Firmen- und Familienverträgen weicht der + Vertragsinhaber (Firma/Eltern) vom tatsächlichen Nutzer + (Mitarbeiter/Kind) ab. Neuer optionaler `SimCard.cardUser` + (String), Migration `20260601100000_sim_card_user` mit + `IF NOT EXISTS`. + - ContractForm: eigene Zeile „Kartennutzer" oberhalb der + technischen SIM-Felder mit erklärendem Placeholder. + - ContractDetail: zeigt „Nutzer: \" neben den + Hauptkarte/Multisim-Badges (nur wenn gefüllt). + +- [x] **🆕 Vorgängervertrag-Modal: Kundennr./Vertragsnr. sichtbar machen** + - Die „Anbieter & Tarif"-Card im ContractDetailModal war + nur sichtbar, wenn Provider oder Tarif gesetzt waren – + Bei Entwürfen ohne Anbieter wurden so auch + `customerNumberAtProvider` + `contractNumberAtProvider` + versteckt, obwohl sie gepflegt sein können. Fix: + Sichtbarkeitsbedingung um die beiden Felder erweitert. + +- [x] **🆕 ContractEmails-Card: Quicklinks auch ohne Postfach** + - „Postfach öffnen" + „Stressfrei wechseln Adressen" waren + nur im Normal-Zweig sichtbar. Jetzt in einer gemeinsamen + `cardTitle`-Variable extrahiert und auch im + „Kein-Mailbox"-State sichtbar. + +- [x] **🆕 Vertrags-Forms: Mini-Links zu Stammdaten** + - In ContractEmailsSection neben „Postfach öffnen" jetzt + zusätzlich „Stressfrei wechseln Adressen" → Kundenakte-Tab. + - In ContractForm kleine ExternalLink-Icons neben Select-Labels: + Lieferadresse/Rechnungsadresse → `?tab=addresses`, + Bankkarte → `?tab=bankcards`, Ausweis → `?tab=documents`, + Anbieter + Tarif → `/settings/providers`, + Vertriebsplattform → `/settings/platforms`. + - Select-Komponente nimmt jetzt `ReactNode` als label + (statt nur string), rückwärtskompatibel. + - In ContractDetail (Strom/Gas Card-Header) zusätzlich + permanenter „Zähler verwalten"-Link in neuem Tab. + +- [x] **🆕 Vertragshistorie: Vertragsnummern als Link** + - Erkennt Vertragsnummern (`PREFIX-RANDOM`) in `title` und + `description` via Regex, löst sie gegen + `previousContract` + `followUpContract` des aktuellen + Vertrags auf und rendert sie als Link in neuem Tab. + Nicht aufgelöste Nummern bleiben als Text. + +- [x] **🆕 UI-Bug-Fix: „Wurde sondergekündigt?"-Checkbox-Label** + - Label-Klasse war `flex` → Block-Layout über volle + `col-span-2`-Breite. Klick rechts neben dem Text + triggerte ungewollt die Checkbox. Fix: `inline-flex`. + +- [x] **🆕 „Zähler verwalten"-Link im Folgezähler-Form** + - In der SuccessorMeterForm (Vertragsansicht) im Header + rechts neben „Folgezähler hinzufügen (Zählerwechsel)" + ein Link in neuem Tab zur Zähler-Verwaltung des Kunden, + damit man bei „Kein passender Zähler verfügbar" direkt + rüberspringen kann. + +- [x] **🆕 Folgezähler-Forms: Checkbox „Alten Zähler deaktivieren"** + - Beide Folgezähler-Forms (Kundenakte MeterModal + + Vertragsansicht SuccessorMeterForm) bekommen eine + Checkbox, die standardmäßig angehakt ist. Beim Speichern + wird der Vorgänger automatisch auf `isActive=false` + gesetzt – ein-klick-fähiger Zählerwechsel. + - Backend: `createMeter` mit `successorOf` und + `addSuccessorMeter` akzeptieren `deactivatePredecessor` + (Default true). + +- [x] **🆕 Kundenakte → Zähler: Verträge-Aufklappliste + Filter** + - Pro Zähler ein „Verträge (N)"-Aufklapp, listet alle + Verträge auf, die diesen Zähler nutzen – als Hauptzähler + (`energyDetails.meterId`) oder über Folgezähler-Kette + (`ContractMeter`). Dedupliziert. Jeder Eintrag ist Link + in neuen Tab mit Vertragsnummer, Anbieter, Status-Badge. + - Neue Checkbox „Zähler ohne Verträge anzeigen" neben + „Inaktive anzeigen". Filtert die Liste auf Orphans + (Zähler ohne Vertragszuordnung): + | Inaktive | ohne Verträge | Ergebnis | + |----------|---------------|----------| + | ☐ | ☐ | nur aktive Zähler (Default) | + | ☑ | ☐ | alle Zähler | + | ☐ | ☑ | aktive Zähler ohne Vertrag | + | ☑ | ☑ | alle Zähler ohne Vertrag | + +- [x] **🆕 Folgezähler-Button auch bei Single-Meter-Verträgen** + - Folgeverträge ohne ContractMeter-Eintrag (alte Daten oder + pure Single-Meter-Verträge) bekamen den Button nie zu + Gesicht. Fix: Button wird jetzt aus dem if/else gerendert, + sobald entweder ein Single-Meter oder ContractMeter- + Einträge vorhanden sind. Im Backend wird der bisherige + `energyDetails.meterId` bei Single-Meter-Verträgen + automatisch als ContractMeter (position 0, + `removedAt` = Wechseldatum) backfillt, damit der alte + Zähler in der Historie bleibt. + +- [x] **🆕 Multi-Meter-Verbrauch auf Vertragslaufzeit clampen** + - Bei Verträgen, die Vorgänger einer Folgevertrags-Kette + sind, hängen über `ContractMeter` auch Folgezähler dran, + die nach Vertragsende installiert wurden. Die Berechnung + nahm `cm.installedAt..cm.removedAt` 1:1 ohne Clamp gegen + `contract.startDate/endDate` – damit flossen Zählerstände + aus der Folgevertrags-Phase in den Verbrauch des + Vorvertrags ein. Fix: + `meterStart = max(installedAt, contractStart)`, + `meterEnd = min(removedAt, contractEnd)`, + Zähler komplett außerhalb der Laufzeit werden übersprungen. + +- [x] **🆕 Vertragsansicht: Standort + Inaktiv-Badge beim Zähler** + - Strom/Gas-Card zeigt jetzt neben der Zählernummer ein + rotes „Inaktiv"-Badge (falls deaktiviert) und darunter + eine kleine Zeile „Standort: …" (falls gepflegt). + +- [x] **🛡️ Pentest 43.5 (INFO) + 43.6 (MEDIUM): History-XSS + blocked:-Marker** + - 43.6: `ContractHistoryEntry.title` + `.description` waren + auf beiden Pfaden ungestrippt – Admin konnte HTML/Script- + Tags einschreiben, Portal-User las sie roh zurück. Fix: + `stripHtml()` auf Create + Update (Write-Pfad) und + `sanitizeEntry()` im List + Get (Read-Pfad). + - 43.5: `stripHtml` ersetzt `javascript:` → `blocked:` – + sinnvoll bei URL-Feldern, hässlich in Tarif-/Preis-Namen. + Neuer `stripForDisplay`-Wrapper entfernt den Marker + zusätzlich in den Display-Feldern. + +- [x] **🛡️ Pentest 42.5 (MEDIUM): priceFirst12Months XSS** + - Drei Preisfelder sind im Schema `String?` (freitextliche + Tarifangaben). `sanitizeContract` strippte sie auf dem + Read-Pfad nicht – Alt-Daten mit XSS-Payloads kamen 1:1 + raus. Fix: `priceFirst12Months`, `priceFrom13Months`, + `priceAfter24Months` in `CONTRACT_DISPLAY_STRING_FIELDS` + aufgenommen. + +- [x] **🛡️ Anzeige-Defense: HTML in display-relevanten Strings strippen** + - `sanitizeContract` und `sanitizeCustomer` strippen jetzt + zusätzlich HTML in den definierten Display-Feldern + (`providerName`, `tariffName`, `customerNumberAtProvider`, + `firstName`, `lastName`, `companyName`, etc.). Wirkt auch + auf nested `previousContract` + `energyDetails`. Damit + sehen Pentester-Payloads (`