Files
opencrm/backend/factory-defaults
duffyduck 2c7a87ccd3 factory-defaults: HTML-Templates + Import über UI
Erweitert das bestehende Factory-Defaults-Bundle um vier HTML-Standardtexte
(Datenschutzerklärung, Impressum, Vollmacht-Vorlage, Website-Datenschutz)
und ergänzt den bisherigen CLI-Only-Import um einen Upload-Pfad in der UI.

Backend:
- collectFactoryDefaults() zieht jetzt auch die Whitelist-AppSettings
- exportFactoryDefaults() legt sie als app-settings/app-settings.json ins ZIP
- importFactoryDefaults(buffer) liest die ZIP idempotent ein – upserts pro
  Kategorie, Whitelist-Filter für AppSettings, Anti-Zip-Slip durch basename
  beim PDF-Lookup
- POST /api/factory-defaults/import (multer memoryStorage, max 50 MB,
  settings:update)
- seed-factory-defaults.ts (CLI) gleichermaßen um seedAppSettings() erweitert

Frontend:
- Import-Card in FactoryDefaults.tsx: Datei-Upload statt CLI-Anleitung
- Erfolgs-Box mit Counts pro Kategorie + Warnings (z.B. fehlende PDFs im ZIP)
- Preview zeigt jetzt auch die Anzahl HTML-Templates

Live verifiziert: Round-Trip Export → DELETE privacyPolicyHtml → Import →
Wert (13.6 KB) wieder vollständig hergestellt, Audit-Log zeigt EXPORT +
UPDATE-Eintrag mit Detail-Counts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 19:26:33 +02:00
..

Factory Defaults

Dieser Ordner enthält Stammdaten-Kataloge, die beim Initialisieren einer neuen OpenCRM-Installation automatisch eingespielt werden können.

Siehe auch den Abschnitt „Factory-Defaults" in der Haupt-README.md für einen Gesamtüberblick und die Abgrenzung zum Datenbank-Backup.


Inhalt

backend/factory-defaults/
├── providers/
│   └── providers.json              # Anbieter inkl. Tarife
├── contract-meta/
│   ├── cancellation-periods.json   # Kündigungsfristen
│   ├── contract-durations.json     # Vertragslaufzeiten
│   └── contract-categories.json    # Vertragskategorien (Strom/Gas/DSL/...)
├── pdf-templates/
│   ├── pdf-templates.json          # Metadaten + Feldzuordnungen
│   └── *.pdf                       # PDF-Vorlagen-Dateien
└── app-settings/
    └── app-settings.json           # HTML-Templates: Datenschutz / Impressum /
                                    # Vollmacht / Website-Datenschutz

Was NICHT enthalten ist: Kundendaten, Verträge, Dokumente, E-Mails, SMTP-Einstellungen, Secrets oder benutzerspezifische AppSettings. Dafür gibt es den separaten Datenbank-Backup-Export (Einstellungen → Datenbank & Zurücksetzen).

Bei den AppSettings ist nur eine Whitelist vorgesehen (HTML-Texte für rechtliche Standardpflichten) andere Keys werden beim Import ignoriert.


Export (aus einer bestehenden Installation)

  1. Im CRM als Admin einloggen
  2. EinstellungenFactory-Defaults
  3. Auf „Factory-Defaults exportieren" klicken
  4. Die heruntergeladene ZIP (factory-defaults-YYYY-MM-DD.zip) speichern

Inhalt der ZIP

factory-defaults-2026-04-23.zip
├── manifest.json                    # Version, Datum, Einträge pro Kategorie
├── providers/providers.json
├── contract-meta/cancellation-periods.json
├── contract-meta/contract-durations.json
├── contract-meta/contract-categories.json
├── pdf-templates/pdf-templates.json
├── pdf-templates/*.pdf
└── app-settings/app-settings.json

Die ZIP kann an andere Installationen weitergegeben werden z.B. für Test-Systeme, neue Installationen oder Partner-Setups.


Import (in eine andere Installation)

Variante A: Über die UI (empfohlen)

  1. Im Ziel-CRM als Admin einloggen
  2. Einstellungen → Factory-Defaults
  3. Im Bereich Import auf „ZIP hochladen" klicken
  4. Die exportierte ZIP wählen der Import läuft direkt
  5. Erfolgsmeldung zeigt Counts pro Kategorie an

Variante B: Über die CLI (für Bare-Metal / Migration / mehrere ZIPs zusammenführen)

  1. ZIP herunterladen (aus einer Export-Installation oder von einer Vorlage)
  2. Inhalt entpacken in diesen Ordner (backend/factory-defaults/), Unterordnerstruktur beibehalten
  3. Script ausführen:
    cd backend
    npm run seed:defaults
    
  4. Ausgabe prüfen bei Erfolg:
    📦 Factory-Defaults werden eingespielt...
    
      ✓ Anbieter: 7, Tarife: 12
      ✓ Kündigungsfristen: 5
      ✓ Laufzeiten: 4
      ✓ Vertragskategorien: 8
      ✓ PDF-Vorlagen: 3
    
    ✅ Factory-Defaults erfolgreich eingespielt.
    

Idempotenz

Das Script nutzt ausschließlich upsert:

  • Neue Einträge werden angelegt
  • Bestehende Einträge (match per unique key: name / code) werden aktualisiert
  • Nichts wird gelöscht

Du kannst npm run seed:defaults beliebig oft ausführen kein Datenverlust, keine Duplikate.

Was passiert mit den PDF-Dateien?

Die PDFs aus pdf-templates/*.pdf werden beim Import nach backend/uploads/pdf-templates/ kopiert (mit eindeutigem Zeitstempel-Suffix). Die Pfade in der DB werden automatisch auf die neue Kopie gesetzt.

Beim Re-Import einer bereits existierenden Vorlage wird die alte Datei in uploads/ entsorgt und durch die neue ersetzt.


Mehrere Exporte mergen

Wenn du mehrere ZIPs hast (z.B. "Verivox-Paket", "Check24-Paket", "eigene"), kannst du die JSON-Dateien frei benennen und in einen Ordner legen. Das Script liest alle *.json im jeweiligen Unterordner und merged den Inhalt zusammen.

Beispiel:

backend/factory-defaults/
  providers/
    verivox.json      # 40 Anbieter aus dem Verivox-Paket
    check24.json      # 30 Anbieter aus dem Check24-Paket
    eigene.json       # 5 eigene, firmenspezifische Anbieter
  contract-meta/
    standard.json     # Standard-Kündigungsfristen + Laufzeiten + Kategorien
  pdf-templates/
    ewe-paket.json    # EWE-Vorlage
    moon-paket.json   # Moon-Vorlage
    ewe-auftrag.pdf
    moon-formular.pdf

Bei gleichem Unique-Key (z.B. providers.name: "EWE" in mehreren Dateien) gewinnt der zuletzt gelesene Eintrag.


Teil-Import (nur Kategorien auswählen)

Falls du nur einen Teil importieren willst (z.B. nur PDF-Vorlagen ohne Anbieter), lösche oder verschiebe einfach die nicht gewünschten JSON-Dateien, bevor du das Script ausführst. Das Script überspringt Kategorien ohne Dateien ohne Fehler.

Beispiel: nur PDF-Vorlagen:

backend/factory-defaults/
  pdf-templates/         # nur diesen Ordner behalten
    pdf-templates.json
    *.pdf

Struktur-Referenz (für manuelle Pflege)

providers/providers.json

Array von Providern, jeweils inkl. zugehöriger Tarife:

[
  {
    "name": "EWE",
    "portalUrl": "https://www.ewe.de/privatkunden/meine-ewe/login",
    "usernameFieldName": "username",
    "passwordFieldName": "password",
    "isActive": true,
    "tariffs": [
      { "name": "EWE Zuhause Strom", "isActive": true },
      { "name": "EWE Zuhause Gas", "isActive": true }
    ]
  }
]

Unique Key: name

contract-meta/cancellation-periods.json

[
  { "code": "14T", "description": "14 Tage", "isActive": true },
  { "code": "1M", "description": "1 Monat", "isActive": true },
  { "code": "3M", "description": "3 Monate", "isActive": true }
]

Unique Key: code

contract-meta/contract-durations.json

[
  { "code": "12M", "description": "12 Monate", "isActive": true },
  { "code": "24M", "description": "24 Monate", "isActive": true }
]

Unique Key: code

contract-meta/contract-categories.json

[
  {
    "code": "ELECTRICITY",
    "name": "Strom",
    "icon": "Zap",
    "color": "#FFC107",
    "sortOrder": 1,
    "isActive": true
  }
]

Unique Key: code

pdf-templates/pdf-templates.json

[
  {
    "name": "EWE Auftragsformular",
    "description": "Auftrag für Glasfaser-Anschluss",
    "providerName": "EWE",
    "originalName": "EWE-Auftrag-Privat.pdf",
    "fieldMapping": {
      "Vorname": "customer.firstName",
      "Nachname": "customer.lastName",
      "Strasse": "address.streetFull",
      "PLZ": "address.postalCode",
      "Ort": "address.city"
    },
    "phoneFieldPrefix": "Rufnummer",
    "maxPhoneFields": 8,
    "isActive": true,
    "pdfFilename": "EWE_Auftragsformular.pdf"
  }
]

Unique Key: name Wichtig: Die pdfFilename muss zu einer PDF-Datei im selben Ordner passen.

app-settings/app-settings.json

HTML-Standardtexte als Werkseinstellung. Es ist eine Whitelist aktiv andere Keys werden beim Import ignoriert (Schutz vor versehentlichem Überschreiben von Secrets).

[
  { "key": "privacyPolicyHtml",        "value": "<h1>Datenschutzerklärung</h1>..." },
  { "key": "imprintHtml",              "value": "<h1>Impressum</h1>..." },
  { "key": "authorizationTemplateHtml","value": "<h1>Vollmacht</h1>..." },
  { "key": "websitePrivacyPolicyHtml", "value": "<h1>Website-Datenschutz</h1>..." }
]

Unique Key: key Erlaubte Keys: privacyPolicyHtml, imprintHtml, authorizationTemplateHtml, websitePrivacyPolicyHtml.


Berechtigungen

Aktion Berechtigung
Factory-Defaults Vorschau settings:read
Factory-Defaults Export (UI) settings:update
Factory-Defaults Import (UI) settings:update
Factory-Defaults Import (CLI) Server-Zugang (SSH/Shell)

Git & Versionierung

Dieser Ordner ist in .gitignore eingetragen (außer .gitkeep und README.md), damit firmen-spezifische Exporte nicht versehentlich ins Repo kommen.

Wenn du öffentlich teilbare Katalog-Pakete versionieren willst, lege sie außerhalb dieses Ordners ab (z.B. in einem eigenen Repository) und kopiere sie bei Bedarf hierher.