# OpenCRM Web-basiertes CRM-System für Kundenverwaltung mit Verträgen (Energie, Telekommunikation, KFZ-Versicherung). ## Features - **Kundenverwaltung**: Privat- und Geschäftskunden mit Stammdaten - **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 - **Rechnungen**: Rechnungsverwaltung für Energieverträge mit Dokumenten-Upload - **Vertrags-Cockpit**: Dashboard zur Überwachung offener Aufgaben (fehlende Dokumente, Rechnungen) - **Verträge**: - Energie (Strom, Gas) - Telekommunikation (DSL, Glasfaser, Mobilfunk, TV) - KFZ-Versicherung - **Folgeverträge**: Automatische Datenübernahme bei Anbieterwechsel - **Vertriebsplattformen**: Verwaltbar über WebUI - **Email-Provisionierung**: Automatische E-Mail-Weiterleitung bei Plesk/cPanel/DirectAdmin - **Berechtigungssystem**: Admin, Mitarbeiter, Nur-Lesen, Kundenportal - **Verschlüsselte Zugangsdaten**: Portal-Passwörter AES-256-GCM verschlüsselt - **Developer-Tools**: Datenbank-Browser und interaktives ER-Diagramm ## Tech Stack - **Frontend**: React 18, TypeScript, Tailwind CSS, React Query - **Backend**: Node.js, Express 4.x, TypeScript - **Datenbank**: MariaDB - **ORM**: Prisma - **Auth**: JWT mit Rollen-basierter Zugriffskontrolle > **Hinweis zu Express 5:** Das Projekt verwendet bewusst Express 4.x (nicht 5.x). Express 5 ist seit Jahren in der Beta-Phase und noch nicht offiziell stable. Bei der Installation darauf achten, dass `@types/express` zur Express-Version passt: > - Express 4.x → `@types/express@^4.17.x` > - Express 5.x → `@types/express@^5.x` (erst bei offiziellem Release empfohlen) ## Voraussetzungen - Node.js 18+ (empfohlen: 20+) - Docker & Docker Compose - npm ## Installation ### 1. Repository klonen ```bash git clone cd opencrm ``` ### 2. MariaDB-Datenbank starten ```bash docker-compose up -d ``` Dies startet einen MariaDB-Container mit: - **Port:** 3306 - **Datenbank:** opencrm - **Root-Passwort:** rootpassword - **Benutzer:** opencrm / opencrm123 Warte ca. 10 Sekunden bis die Datenbank bereit ist. ### 3. Backend einrichten ```bash cd backend # Dependencies installieren npm install # .env-Datei erstellen (falls noch nicht vorhanden) cp .env.example .env ``` Die `.env`-Datei sollte folgende Werte enthalten: ```env # Database DATABASE_URL="mysql://root:rootpassword@localhost:3306/opencrm" # JWT JWT_SECRET="change-this-to-a-very-long-random-secret-in-production" JWT_EXPIRES_IN="7d" # Encryption (for portal credentials) - generate with: openssl rand -hex 32 ENCRYPTION_KEY="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" # Server PORT=3001 NODE_ENV=development ``` ### 4. Datenbank initialisieren ```bash # Prisma Client generieren und Migrationen ausführen npx prisma migrate dev # Seed-Daten einspielen (Admin-User, Rollen, Berechtigungen) npm run db:seed ``` ### 5. Frontend einrichten ```bash cd ../frontend # Dependencies installieren npm install ``` ## Anwendung starten ### Backend starten (Terminal 1) ```bash cd backend npm run dev ``` Das Backend läuft auf `http://localhost:3001` ### Frontend starten (Terminal 2) ```bash cd frontend npm run dev ``` Das Frontend läuft auf `http://localhost:5173` ## Erster Login Nach dem Seed sind folgende Zugangsdaten verfügbar: - **E-Mail:** admin@admin.com - **Passwort:** admin ## Developer-Tools aktivieren Die Developer-Tools (Datenbankstruktur, ER-Diagramm) sind standardmäßig für Admins verfügbar. Falls der Menüpunkt nicht erscheint: 1. Einmalig im Browser-Console ausführen: ```javascript fetch('/api/developer/setup', { method: 'POST' }) ``` 2. Ausloggen und neu einloggen Alternativ können Developer-Rechte pro Benutzer vergeben werden: - Benutzer bearbeiten > "Entwicklerzugriff" aktivieren ## Email-Provisionierung Das System unterstützt die automatische Erstellung von E-Mail-Weiterleitungen auf Hosting-Servern für Stressfrei-Wechseln Adressen. ### Unterstützte Provider - **Plesk** (implementiert) - **cPanel** (vorbereitet) - **DirectAdmin** (vorbereitet) ### Konfiguration 1. **Einstellungen** → **Email-Provisionierung** öffnen 2. Neuen Provider hinzufügen: - **Name**: Bezeichnung (z.B. "Plesk Hauptserver") - **Typ**: Plesk/cPanel/DirectAdmin - **API-URL**: Server-URL (z.B. `https://server.de:8443`) - **Benutzername/Passwort**: API-Zugangsdaten - **Domain**: E-Mail-Domain (z.B. `stressfrei-wechseln.de`) - **Standard-Weiterleitung**: Zusätzliche Weiterleitungsadresse (optional) 3. Provider als "Standard" und "Aktiv" markieren 4. Verbindung testen ### Verwendung Beim Anlegen einer Stressfrei-Wechseln Adresse im Kundenbereich erscheint die Checkbox **"Beim E-Mail-Provider anlegen"**, wenn: - Ein aktiver Standard-Provider konfiguriert ist - Der Kunde eine E-Mail-Adresse hat Bei aktivierter Checkbox wird automatisch: 1. Geprüft, ob die E-Mail-Adresse bereits existiert 2. Falls nicht: E-Mail-Adresse beim Provider angelegt mit Weiterleitung an: - Kunden-E-Mail-Adresse - Standard-Weiterleitungsadresse (falls konfiguriert) ## Befehle ### Backend ```bash npm run dev # Entwicklungsserver starten npm run build # Produktions-Build erstellen npm run db:studio # Prisma Studio (Datenbank-GUI) npm run db:migrate # Neue Migration erstellen npm run db:seed # Seed erneut ausführen ``` ### Frontend ```bash npm run dev # Entwicklungsserver starten npm run build # Produktions-Build erstellen npm run preview # Build-Vorschau ``` ### Docker ```bash docker-compose up -d # Container starten docker-compose down # Container stoppen docker-compose down -v # Container stoppen + Daten löschen docker-compose logs -f # Logs anzeigen ``` ## Projektstruktur ``` opencrm/ ├── backend/ │ ├── src/ │ │ ├── controllers/ # Request-Handler │ │ ├── middleware/ # Auth, Validation │ │ ├── routes/ # API-Endpunkte │ │ ├── services/ # Business-Logik │ │ ├── types/ # TypeScript-Typen │ │ └── index.ts # Server-Einstiegspunkt │ ├── prisma/ │ │ ├── schema.prisma # Datenbank-Schema │ │ └── seed.ts # Seed-Daten │ ├── uploads/ # Hochgeladene Dokumente │ │ ├── bank-cards/ # Bankkarten-Dokumente │ │ ├── documents/ # Ausweis-Scans │ │ ├── invoices/ # Rechnungsdokumente (Strom/Gas) │ │ ├── business-registrations/ # Gewerbeanmeldungen │ │ ├── commercial-registers/ # Handelsregisterauszüge │ │ ├── privacy-policies/ # Datenschutzerklärungen │ │ ├── cancellation-letters/ # Kündigungsschreiben │ │ ├── cancellation-confirmations/ # Kündigungsbestätigungen │ │ └── cancellation-*-options/ # Kündigungsdokumente Optionen │ └── package.json ├── frontend/ │ ├── src/ │ │ ├── components/ # UI-Komponenten │ │ ├── pages/ # Seiten │ │ ├── hooks/ # Custom Hooks │ │ ├── services/ # API-Client │ │ ├── types/ # TypeScript-Typen │ │ └── App.tsx # Haupt-Komponente │ └── package.json ├── docker-compose.yml # MariaDB-Container └── README.md ``` ## Berechtigungen | Rolle | Kunden | Verträge | Benutzer | Plattformen | Developer | |-------|--------|----------|----------|-------------|-----------| | Admin | CRUD | CRUD | CRUD | CRUD | Optional | | Mitarbeiter | CRUD | CRUD | - | Lesen | - | | Mitarbeiter (Lesen) | Lesen | Lesen | - | Lesen | - | | Kunde | Eigene | Eigene | - | - | - | ## Troubleshooting ### Datenbank-Verbindungsfehler 1. Prüfe ob Container läuft: `docker ps` 2. Prüfe die DATABASE_URL in `.env` 3. Warte nach Container-Start ca. 10 Sekunden ### Prisma-Fehler ```bash # Prisma Client neu generieren npx prisma generate # Schema zur Datenbank pushen (ohne Migration) npx prisma db push ``` ### Port bereits belegt - Backend: `PORT` in `.env` ändern - Frontend: In `vite.config.ts` anpassen ### Developer-Menü fehlt ```bash # In der Browser-Console: fetch('/api/developer/setup', { method: 'POST' }).then(r => r.json()).then(console.log) # Danach ausloggen und neu einloggen ``` ## Vertragstypen ### Standard-Vertragstypen Folgende Vertragstypen werden bei Installation/Factory-Reset automatisch angelegt: | Code | Name | Icon | Farbe | |------|------|------|-------| | ELECTRICITY | Strom | Zap | #FFC107 | | GAS | Gas | Flame | #FF5722 | | DSL | DSL | Wifi | #2196F3 | | FIBER | Glasfaser | Cable | #9C27B0 | | CABLE | Kabel Internet (Coax) | Cable | #00BCD4 | | MOBILE | Mobilfunk | Smartphone | #4CAF50 | | TV | TV | Tv | #E91E63 | | CAR_INSURANCE | KFZ-Versicherung | Car | #607D8B | > **Hinweis:** Vertragstypen können nur von Benutzern mit **Entwicklerzugriff** geändert werden, da Änderungen auch Anpassungen an den Formularen erfordern. ### Vertragstyp-spezifische Felder Je nach Vertragstyp werden unterschiedliche Felder im Formular angezeigt: #### Strom & Gas (ELECTRICITY, GAS) - Zähler-Auswahl - Jahresverbrauch (kWh/m³) - Grundpreis, Arbeitspreis - Bonus - Vorversorger, Kundennummer beim Vorversorger #### Internet (DSL, CABLE, FIBER) - Download/Upload (Mbit/s) - Router Modell, Seriennummer - Installationsdatum - Benutzername, Passwort - Rufnummern mit SIP-Zugangsdaten | Vertragstyp | Zusatzfeld | |-------------|------------| | **Glasfaser (FIBER)** | Home-ID | #### Mobilfunk (MOBILE) - Datenvolumen (GB) - Inklusiv-Minuten, Inklusiv-SMS - Gerät-Modell, IMEI - Multisim-Checkbox - SIM-Karten (dynamisch erweiterbar): - Rufnummer, SIM-Kartennummer (ICCID) - PIN, PUK (verschlüsselt) - Multisim-Flag, Hauptkarte-Flag > **Hinweis Multisim:** Nicht buchbar bei Klarmobil, Congstar, Otelo. Benötigt Freenet oder vergleichbar. #### TV - Receiver Modell - Smartcard-Nummer - Paket/Angebot #### KFZ-Versicherung (CAR_INSURANCE) - Kennzeichen, HSN, TSN, FIN/VIN - Fahrzeugtyp, Erstzulassung - SF-Klasse (Schadenfreiheitsklasse) - Versicherungsart (Haftpflicht/Teilkasko/Vollkasko) - Selbstbeteiligungen (Teilkasko, Vollkasko) - Versicherungsscheinnummer - Vorversicherer ### Standard-Anbieter Folgende Anbieter werden bei Installation/Factory-Reset automatisch angelegt: | Anbieter | Portal-URL | |----------|------------| | Vodafone | https://www.vodafone.de/meinvodafone/account/login | | Klarmobil | https://www.klarmobil.de/login | | Otelo | https://www.otelo.de/mein-otelo/login | | Congstar | https://www.congstar.de/login/ | | Telekom | https://www.telekom.de/kundencenter/startseite | | O2 | https://www.o2online.de/ecare/selfcare | | 1&1 | https://control-center.1und1.de/ | ### Anbieter-spezifische Felder Einige Felder werden nur bei bestimmten Anbietern angezeigt: | Anbieter | Vertragstyp | Zusatzfeld | |----------|-------------|------------| | **Vodafone** | DSL, Kabel Internet | Aktivierungscode | > **Hinweis Multisim:** Bei Klarmobil, Congstar und Otelo ist Multisim **nicht** buchbar. Dafür wird Freenet oder ein vergleichbarer Anbieter benötigt. ## Rechnungsverwaltung (Energieverträge) Für Strom- und Gas-Verträge können Rechnungen verwaltet werden, um den Abrechnungsstatus zu tracken. ### Rechnungstypen | Typ | Beschreibung | |-----|--------------| | **Zwischenrechnung** (INTERIM) | Reguläre Jahresrechnung während der Vertragslaufzeit | | **Schlussrechnung** (FINAL) | Endabrechnung nach Vertragskündigung/Deaktivierung | | **Nicht verfügbar** (NOT_AVAILABLE) | Rechnung ist nicht mehr zu bekommen (z.B. Anbieter existiert nicht mehr) | ### Funktionen - **Rechnungen hinzufügen/bearbeiten/löschen** in der Vertragsdetailansicht - **Dokument-Upload** (PDF) - Pflicht, außer bei Typ "Nicht verfügbar" - **Statusanzeige**: Grünes Badge bei Schlussrechnung, Orange bei fehlender Schlussrechnung - **E-Mail-Anhänge als Rechnung speichern**: Direkt aus dem E-Mail-Client ### E-Mail-Integration Bei E-Mails, die einem Energievertrag zugeordnet sind: 1. Toggle zwischen "Als Dokument" und "Als Rechnung" im Speichern-Dialog 2. Rechnungsdatum und Typ auswählen 3. Anhang wird automatisch als Rechnungsdokument gespeichert ## Vertrags-Cockpit Dashboard zur Überwachung offener Aufgaben und fehlender Dokumente. ### Kategorien | Kategorie | Prüfungen | |-----------|-----------| | **Fehlende Dokumente** | Kündigungsschreiben, Kündigungsbestätigung (wenn Kündigung markiert) | | **Fehlende Rechnungen** | Schluss-/Zwischenrechnungen für Energieverträge | | **Ablaufende Dokumente** | Ausweise, Bankkarten (nächste 30 Tage) | ### Rechnungsprüfung für Energieverträge #### Schlussrechnung (gekündigte/deaktivierte Verträge) | Status | Prüfung | |--------|---------| | **CANCELLED** / **DEACTIVATED** | Schlussrechnung oder "Nicht verfügbar" erforderlich | #### Zwischenrechnung (laufende Verträge) | Bedingung | Warnung | |-----------|---------| | Vertrag > 12 Monate alt, keine Rechnung | "Zwischenrechnung fehlt" | | Letzte Rechnung > 12 Monate her | "Zwischenrechnung überfällig" | > **Hinweis Status-Logik:** > - **EXPIRED** = Laufzeit abgelaufen, aber Vertrag läuft ohne Kündigung weiter → Zwischenrechnung prüfen > - **CANCELLED** = Aktive Kündigung → Schlussrechnung prüfen > - **DEACTIVATED** = Manuell beendet → Schlussrechnung prüfen ### Vertrag zurückstellen (Snooze) Wenn ein Vertrag EXPIRED ist (Laufzeit vorbei, läuft aber weiter), erscheint er im Cockpit. Manchmal ist Bleiben günstiger als Wechseln - aber der Vertrag soll nicht dauerhaft im Cockpit erscheinen. **Lösung:** Vertrag temporär zurückstellen mit Datum für erneute Prüfung. #### Snooze aktivieren Im Cockpit hat jeder Vertrag einen Snooze-Button (Glocke mit Uhr). Optionen: - **+3 Monate** - **+6 Monate** (Empfohlen) - **+12 Monate** - **Eigenes Datum** (Datepicker) #### Unterdrückte Warnungen (bei aktivem Snooze) | Warnung | Beschreibung | |---------|--------------| | Kündigungsfrist | Frist läuft ab | | Vertrag läuft ab | Enddatum naht | | Kündigungsschreiben fehlt | Bei markierter Kündigung | | Kündigungsbestätigung fehlt | Bei markierter Kündigung | #### NICHT unterdrückte Warnungen Diese Warnungen werden auch bei aktivem Snooze angezeigt: | Warnung | Beschreibung | |---------|--------------| | Schlussrechnung fehlt | Beendeter Vertrag ohne Endabrechnung | | Zwischenrechnung fehlt/überfällig | Laufender Vertrag ohne Jahresrechnung | | Zählerstand fehlt | Fehlende Zählerstände bei Energieverträgen | | Zugangsdaten fehlen | Portal-Passwort, SIP-Daten, etc. | #### Snooze aufheben **Im Cockpit:** Bei Verträgen in der Kategorie "Erneute Prüfung fällig" erscheint ein "Snooze aufheben"-Button. **In Vertragsdetails:** Bei zurückgestellten Verträgen erscheint ein bernsteinfarbenes Badge "Zurückgestellt bis [Datum]" mit X-Button zum Aufheben. #### Erneute Prüfung fällig Wenn das Snooze-Datum in der Vergangenheit liegt, erscheint der Vertrag in der neuen Kategorie "Erneute Prüfung fällig" mit Angabe, seit wie vielen Tagen die Prüfung fällig ist. ### Status-Info Modal An verschiedenen Stellen (Vertragsformular, Vertragsdetails, Vertragsübersicht, Kundenansicht) zeigt ein ℹ-Icon neben dem Status eine Erklärung aller Vertragsstatus: | Status | Bedeutung | |--------|-----------| | **Entwurf** | Vertrag wird noch vorbereitet | | **Ausstehend** | Wartet auf Aktivierung | | **Aktiv** | Vertrag läuft normal | | **Abgelaufen** | Laufzeit vorbei, läuft aber ohne Kündigung weiter | | **Gekündigt** | Aktive Kündigung eingereicht, Vertrag endet | | **Deaktiviert** | Manuell beendet/archiviert | ## E-Mail-Client Ein vollständig integrierter E-Mail-Client pro Kunde mit IMAP-Empfang und SMTP-Versand. ### Funktionen #### E-Mails lesen & verwalten - **E-Mail-Tab in Kundenansicht** mit Ordnern: Posteingang, Gesendet, Papierkorb - **Mehrere E-Mail-Konten** pro Kunde (Dropdown zur Auswahl) - **E-Mail-Detailansicht** mit HTML/Text-Body, Absender, Empfänger, CC, Datum - **Gelesen/Ungelesen** markieren - **Favoriten** (Stern) für wichtige E-Mails - **Papierkorb** mit Wiederherstellen und endgültigem Löschen #### E-Mails schreiben - **Neue E-Mail verfassen** mit An, CC, Betreff, Text - **Antworten** mit zitiertem Originaltext - **Dateianhänge** (max. 10 MB pro Datei, 25 MB gesamt) - **SMTP-Versand** über die gewählte StressfreiEmail-Adresse #### Vertragszuordnung - **E-Mails zu Verträgen zuordnen** für bessere Nachverfolgung - **E-Mail-Tab in Vertragsansicht** zeigt nur zugeordnete E-Mails - **Automatische Zuordnung** bei Versand aus Vertragskontext - **Manuelle Zuordnung** über Suchfeld und Vertragsauswahl #### Anhänge - **Anhangsliste** in E-Mail-Detail - **Download** einzelner Anhänge - **Inline-Ansicht** (im Browser öffnen) ### Technische Details #### Backend-Services | Service | Beschreibung | |---------|--------------| | `imapService.ts` | IMAP-Client (ImapFlow) für E-Mail-Empfang | | `smtpService.ts` | SMTP-Client (Nodemailer) für E-Mail-Versand | | `cachedEmail.service.ts` | E-Mail-Caching, Synchronisation, Zuordnung | #### API-Endpunkte ``` GET /api/customers/:id/emails # E-Mails für Kunde GET /api/contracts/:id/emails # E-Mails für Vertrag GET /api/emails/:id # Einzelne E-Mail mit Body POST /api/stressfrei-emails/:id/sync # IMAP-Synchronisation POST /api/stressfrei-emails/:id/send # E-Mail senden POST /api/emails/:id/assign # Vertrag zuordnen DELETE /api/emails/:id/assign # Zuordnung aufheben PATCH /api/emails/:id/read # Gelesen/Ungelesen POST /api/emails/:id/star # Favorit umschalten DELETE /api/emails/:id # In Papierkorb POST /api/emails/:id/restore # Aus Papierkorb wiederherstellen DELETE /api/emails/:id/permanent # Endgültig löschen GET /api/emails/:id/attachments/:filename # Anhang herunterladen # Rechnungen (Invoices) GET /api/invoices/ecd/:ecdId # Rechnungen für EnergyContractDetails POST /api/invoices/ecd/:ecdId # Rechnung hinzufügen PUT /api/invoices/ecd/:ecdId/:invoiceId # Rechnung bearbeiten DELETE /api/invoices/ecd/:ecdId/:invoiceId # Rechnung löschen POST /api/invoices/:invoiceId/upload # Rechnungsdokument hochladen # Cockpit GET /api/contracts/cockpit # Offene Aufgaben abrufen ``` #### Datenbank-Modell ```prisma model CachedEmail { id Int @id @default(autoincrement()) stressfreiEmailId Int stressfreiEmail StressfreiEmail @relation(...) folder String // INBOX, SENT messageId String // RFC 5322 Message-ID uid Int // IMAP UID subject String? fromAddress String fromName String? toAddresses String @db.Text // JSON Array ccAddresses String? @db.Text receivedAt DateTime textBody String? @db.LongText htmlBody String? @db.LongText hasAttachments Boolean @default(false) attachmentNames String? @db.Text // JSON Array contractId Int? // Vertragszuordnung assignedAt DateTime? assignedBy Int? isAutoAssigned Boolean @default(false) isRead Boolean @default(false) isStarred Boolean @default(false) isDeleted Boolean @default(false) deletedAt DateTime? @@unique([stressfreiEmailId, messageId, folder]) } ``` #### Sicherheit - **Passwort-Verschlüsselung**: AES-256-GCM für Mailbox-Passwörter - **Passwort-Reset**: Neues Passwort generieren und beim Provider setzen - **Verschlüsselungsmodi**: SSL, STARTTLS, oder unverschlüsselt - **Selbstsignierte Zertifikate**: Konfigurierbar pro Provider #### Berechtigungen | Aktion | Berechtigung | |--------|--------------| | E-Mails lesen | `customers:read` | | E-Mails senden, markieren | `customers:update` | | Anhänge in Dokumente speichern | `customers:update` | | Vertrag zuordnen | `contracts:update` | | Löschen, Papierkorb | `emails:delete` | ### Frontend-Komponenten | Komponente | Beschreibung | |------------|--------------| | `EmailClientTab.tsx` | Haupt-Tab mit Konto-Auswahl und Ordnern | | `EmailList.tsx` | E-Mail-Liste mit Aktionen | | `EmailDetail.tsx` | E-Mail-Ansicht mit Anhängen | | `ComposeEmailModal.tsx` | Neue E-Mail / Antworten | | `TrashEmailList.tsx` | Papierkorb-Verwaltung | | `AssignToContractModal.tsx` | Vertragszuordnung | | `ContractEmailsSection.tsx` | E-Mails in Vertragsansicht | | `SaveAttachmentModal.tsx` | Anhänge in Dokumentfelder speichern | | `SaveEmailAsPdfModal.tsx` | E-Mail als PDF in Dokumentfelder speichern | | `InvoicesSection.tsx` | Rechnungsverwaltung in Vertragsdetails | ### Anhänge als Dokumente speichern E-Mail-Anhänge können direkt in Dokumentfelder des CRM gespeichert werden. Über den blauen Speichern-Button (💾) neben jedem Anhang öffnet sich ein Modal mit allen verfügbaren Zielen. #### Verfügbare Ziele | Kategorie | Dokumentfelder | |-----------|----------------| | **Kunde** | Datenschutzerklärung | | **Kunde (Gewerbe)** | + Gewerbeanmeldung, Handelsregisterauszug | | **Ausweisdokumente** | Dokumentscan (pro Ausweis) | | **Bankkarten** | Kartenscan (pro Karte) | | **Vertrag** | Kündigungsschreiben, Kündigungsbestätigung, Kündigungsschreiben (Optionen), Kündigungsbestätigung (Optionen) | > **Hinweis:** Vertragsdokumente sind nur verfügbar, wenn die E-Mail einem Vertrag zugeordnet ist. #### Dynamische Konfiguration Die Dokumentziele werden zentral in `backend/src/config/documentTargets.config.ts` konfiguriert. Neue Dokumentfelder werden automatisch im Modal angezeigt, ohne Frontend-Änderungen. ```typescript // Beispiel: Neues Feld hinzufügen { key: 'newDocument', label: 'Neues Dokument', field: 'newDocumentPath', // Prisma-Feld condition: null, // oder 'BUSINESS' für Geschäftskunden directory: 'new-documents' // Upload-Verzeichnis } ``` #### Warnung bei Überschreiben Wenn bereits ein Dokument im Zielfeld vorhanden ist, wird eine Warnung angezeigt. Das vorhandene Dokument wird beim Speichern automatisch ersetzt und die alte Datei gelöscht. ### Vertragszuordnung aufheben (X-Button) Der X-Button zum Aufheben der Vertragszuordnung erscheint nur bei **manuell zugeordneten** E-Mails. E-Mails, die direkt aus dem Vertragskontext gesendet wurden (`isAutoAssigned = true`), bleiben dauerhaft mit dem Vertrag verknüpft. | Ansicht | Vertrags-Badge | X-Button sichtbar | |---------|----------------|-------------------| | **Kundenakte - Posteingang** | ✅ | ✅ immer | | **Kundenakte - Gesendet** | ✅ | nur manuell zugeordnet | | **Kundenakte - Papierkorb** | ✅ | je nach Original-Ordner | | **Vertrag - Posteingang** | ✅ | ✅ immer | | **Vertrag - Gesendet** | ✅ | nur manuell zugeordnet | | **Vertrag - Papierkorb** | ✅ | je nach Original-Ordner | | **Detail-Ansicht (beide)** | ✅ | nur manuell zugeordnet | > **Hinweis:** Bei gesendeten E-Mails gilt: > - `isAutoAssigned = true`: E-Mail wurde direkt aus dem Vertragskontext gesendet → X-Button ausgeblendet > - `isAutoAssigned = false`: E-Mail wurde manuell dem Vertrag zugeordnet → X-Button sichtbar ### Vertragsbaum in Kundenansicht In der Kundendetailansicht werden Verträge als **Baumstruktur** mit Vorgänger-Verknüpfung dargestellt: ``` ▼ GAS-ML781A4FYXU │ Gas │ ACTIVE │ 01.01.2025 - 31.12.2026 └─ GAS-ML24GKR...│ Gas │ EXPIRED│ 05.05.2023 - 05.05.2025 (Vorgänger) └─ GAS-OLD123 │ Gas │ EXPIRED│ 01.01.2021 - 04.05.2023 (Vorgänger) ▶ MOB-ML77W560A73 │ Mobil│ DRAFT │ 02.01.2024 - 02.01.2026 ``` **Funktionsweise:** - **Aktuellste Verträge oben** - Verträge ohne Nachfolger werden als Wurzelknoten angezeigt - **Standardmäßig eingeklappt** - Klick auf ▶ zeigt die Vorgängerkette - **Vorgänger eingerückt** - Mit grauem Rand und "(Vorgänger)" Label - **Verknüpfung über `previousContractId`** - Wird beim Erstellen eines Folgevertrags automatisch gesetzt > **Hinweis:** In der Hauptvertragsliste (`/contracts`) wird weiterhin die flache Ansicht ohne Baumstruktur verwendet. ## Lizenz MIT