docs: todo.md + README aktualisiert (Pentest-Fixes, Folgezähler,
SIM-cardUser, EmailProvider-Label-Override etc.) 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) <noreply@anthropic.com>
This commit is contained in:
@@ -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.
|
||||
|
||||
|
||||
+150
@@ -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: \<Name\>" 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 (`<script>`, `<img onerror>`)
|
||||
in der Vertragsliste nicht mehr als hässlicher Klartext
|
||||
aus, sondern verschwinden komplett.
|
||||
|
||||
- [x] **🆕 Vorvertrag-Verbrauch als Schätzwert im Folgevertrag**
|
||||
- **ContractForm** (Strom/Gas): Wenn ein `previousContractId`
|
||||
gesetzt ist, wird der Vorvertrag samt Readings nachgeladen und
|
||||
|
||||
Reference in New Issue
Block a user