16 KiB
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
- 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/expresszur 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
git clone <repository-url>
cd opencrm
2. MariaDB-Datenbank starten
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
cd backend
# Dependencies installieren
npm install
# .env-Datei erstellen (falls noch nicht vorhanden)
cp .env.example .env
Die .env-Datei sollte folgende Werte enthalten:
# 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
# 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
cd ../frontend
# Dependencies installieren
npm install
Anwendung starten
Backend starten (Terminal 1)
cd backend
npm run dev
Das Backend läuft auf http://localhost:3001
Frontend starten (Terminal 2)
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:
- Einmalig im Browser-Console ausführen:
fetch('/api/developer/setup', { method: 'POST' }) - 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
- Einstellungen → Email-Provisionierung öffnen
- 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)
- Provider als "Standard" und "Aktiv" markieren
- 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:
- Geprüft, ob die E-Mail-Adresse bereits existiert
- Falls nicht: E-Mail-Adresse beim Provider angelegt mit Weiterleitung an:
- Kunden-E-Mail-Adresse
- Standard-Weiterleitungsadresse (falls konfiguriert)
Befehle
Backend
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
npm run dev # Entwicklungsserver starten
npm run build # Produktions-Build erstellen
npm run preview # Build-Vorschau
Docker
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
│ │ ├── 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
- Prüfe ob Container läuft:
docker ps - Prüfe die DATABASE_URL in
.env - Warte nach Container-Start ca. 10 Sekunden
Prisma-Fehler
# Prisma Client neu generieren
npx prisma generate
# Schema zur Datenbank pushen (ohne Migration)
npx prisma db push
Port bereits belegt
- Backend:
PORTin.envändern - Frontend: In
vite.config.tsanpassen
Developer-Menü fehlt
# 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.
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
Datenbank-Modell
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 |
| 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 |
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 ausgeblendetisAutoAssigned = false: E-Mail wurde manuell dem Vertrag zugeordnet → X-Button sichtbar
Lizenz
MIT