INSIGHT-MVP/docs/INSIGHT-CRM.md
Thomas Reitz 6e77bf43b0 feat(crm): prevent duplicate Lexware imports — show linked status in import list
Import tab now loads all CRM companies/contacts and cross-references
lexwareContactId to detect already-imported entries. Linked contacts show
a green badge and "Öffnen" link instead of import buttons.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 10:48:32 +01:00

45 KiB

INSIGHT CRM - Kommunikation Frontend <-> Backend

Dieses Dokument dient als Kommunikationskanal zwischen dem Frontend- und dem CRM-Backend-Entwickler.


2026-03-10 | Frontend: Erster Stand der CRM-Integration

Was wurde umgesetzt

Das komplette CRM-Frontend-Modul ist implementiert und auf dem Server deployed (feature/crm-service Branch, Commit c739dce).

Neue Dateien (16 Dateien, ~4.800 Zeilen)

packages/frontend/src/crm/
  types.ts                              -- Zentrale Interfaces (alle Entitaeten + API-Wrapper)
  api.ts                                -- API-Funktionen (Axios, baseURL /api/v1/crm/*)
  hooks.ts                              -- React Query Hooks + Query Key Factory
  contacts/
    ContactsPage.tsx + .module.css      -- Liste mit Suche, Typ-Filter, Paginierung
    ContactFormModal.tsx                -- Create/Edit Modal (Person/Organisation)
    ContactDetailPage.tsx + .module.css -- 2-Spalten: Info+Deals links, Aktivitaeten rechts
  deals/
    DealsPage.tsx + .module.css         -- Liste mit Pipeline/Stage/Status-Filter
    DealFormModal.tsx                   -- Create/Edit mit Kontakt-Suche + Pipeline/Stage-Selektor
    DealDetailPage.tsx + .module.css    -- Detail mit Stage-Fortschrittsbalken
  pipelines/
    PipelinesPage.tsx + .module.css     -- Verwaltung mit klappbaren Cards + Stage-Management
  activities/
    ActivityFormModal.tsx               -- Formular fuer Notiz/Anruf/E-Mail/Meeting/Aufgabe

Geaenderte Dateien (3)

Datei Aenderung
src/shell/App.tsx 5 CRM-Routen (/crm/contacts, /crm/contacts/:id, /crm/deals, /crm/deals/:id, /crm/pipelines)
src/shell/AppLayout.tsx CRM-Sektion in Sidebar (aufklappbar, 3 NavLinks mit SVG-Icons)
vite.config.ts Proxy /api/v1/crm -> localhost:3100 fuer lokale Entwicklung

Welche API-Endpoints werden genutzt

Modul Endpoints Methoden
Contacts /crm/contacts, /crm/contacts/:id GET (list+detail), POST, PATCH, DELETE
Deals /crm/deals, /crm/deals/:id GET (list+detail), POST, PATCH, DELETE
Pipelines /crm/pipelines, /crm/pipelines/:id, /crm/pipelines/:id/stages, /crm/pipelines/:id/stages/:stageId GET (list+detail), POST, PATCH, DELETE
Activities /crm/activities, /crm/activities/:id GET (list), POST, PATCH, DELETE

Erwartete Response-Formate

Liste (paginiert):

{
  "success": true,
  "data": [...],
  "pagination": { "page": 1, "pageSize": 25, "total": 42, "totalPages": 2 },
  "meta": { "timestamp": "..." }
}

Einzelobjekt:

{
  "success": true,
  "data": { ... },
  "meta": { "timestamp": "..." }
}

Fehler:

{
  "success": false,
  "error": { "code": "NOT_FOUND", "message": "...", "details": [] },
  "meta": { "timestamp": "..." }
}

Annahmen / Abhaengigkeiten ans Backend

  1. Contact-Detail liefert Activities mit -- GET /crm/contacts/:id gibt die letzten 10 Aktivitaeten im Feld activities zurueck. Das Frontend zeigt diese in der Timeline an.

  2. Deal-Detail liefert Relations mit -- GET /crm/deals/:id gibt pipeline, stage und contact als verschachtelte Objekte zurueck.

  3. Pipeline-List liefert Stages mit -- GET /crm/pipelines gibt jede Pipeline inkl. stages[] Array zurueck. Das Frontend nutzt diese fuer die Stage-Selektoren im Deal-Formular.

  4. Deal.value ist ein String -- Decimal kommt als String vom Backend (z.B. "24000.00"). Das Frontend parst mit parseFloat().

  5. Sortierung -- Contacts: createdAt, firstName, lastName, companyName, email. Deals: createdAt, title, value, expectedCloseDate.

  6. Suche -- Contacts: Substring-Match in firstName, lastName, companyName, email. Deals: Substring-Match in title.

Bekannte Offene Punkte

  • Traefik HTTPS-Router fuer CRM: Aktuell hat der CRM-Service nur einen HTTP-Router (web Entrypoint). Fuer HTTPS (websecure) muesste ein zweiter Router mit tls=true angelegt werden (wie bei core-api-secure).
  • Activity-Liste komplett laden: Die Contact-Detail-Seite zeigt nur die letzten 10 Aktivitaeten (aus dem Contact-Objekt). Fuer eine "Alle anzeigen"-Funktion wuerde ein separater GET /crm/activities?contactId=... Aufruf benoetigt (Hook existiert bereits).
  • Kanban-Board fuer Deals: Aktuell nur Tabellenansicht. Ein Drag-and-Drop Kanban-Board waere ein sinnvolles Feature fuer spaeter.
  • Pipeline-Stages bearbeiten: Derzeit kann man Stages nur hinzufuegen und loeschen, nicht den Namen/Farbe/Reihenfolge aendern (kein PATCH-Endpoint fuer Stages vorhanden).

2026-03-10 | Backend: Antwort auf Frontend-Integrationsbericht

Status der Annahmen

Alle 6 Annahmen des Frontend-Entwicklers sind korrekt und werden vom Backend unterstuetzt:

# Annahme Status Hinweise
1 Contact-Detail liefert Activities mit Bestaetigt GET /crm/contacts/:id liefert activities[] (letzte 10, sortiert nach createdAt desc)
2 Deal-Detail liefert Relations mit Bestaetigt GET /crm/deals/:id liefert pipeline (inkl. aller Stages!), stage, contact
3 Pipeline-List liefert Stages mit Bestaetigt GET /crm/pipelines liefert jede Pipeline inkl. stages[] + _count.deals
4 Deal.value ist ein String Bestaetigt Decimal wird als String geliefert (z.B. "24000")
5 Sortierung Bestaetigt Contacts: createdAt, firstName, lastName, companyName, email. Deals: createdAt, updatedAt, title, value, expectedCloseDate
6 Suche Bestaetigt Contacts: case-insensitive Substring in firstName, lastName, companyName, email. Deals: case-insensitive Substring in title

Erledigte Offene Punkte

  • Traefik HTTPS-Router: crm-secure Router angelegt mit entrypoints=websecure, tls=true, Priority 100. Deployed in Commit c9e2c4a.

  • Pipeline-Stages bearbeiten: Neuer Endpoint PATCH /crm/pipelines/:id/stages/:stageId hinzugefuegt. Akzeptiert:

    {
      "name": "Neuer Name",     // optional, max 200
      "sortOrder": 2,            // optional, int >= 0
      "color": "#EF4444"         // optional, Hex #RRGGBB
    }
    

Noch offene Punkte (Backend-Sicht)

  • Activity-Liste komplett laden: GET /crm/activities?contactId=... ist bereits implementiert und funktioniert. Das Frontend kann den bestehenden Hook direkt nutzen.
  • Kanban-Board: Backend-seitig kein Handlungsbedarf - PATCH /crm/deals/:id mit { stageId: "..." } genuegt fuer Drag & Drop.

Hinweis zu Pipeline-Detail bei Deals

GET /crm/deals/:id liefert die Pipeline inklusive aller Stages (nicht nur die aktuelle Stage). Das ist nuetzlich fuer den Stage-Fortschrittsbalken im DealDetailPage.tsx:

{
  "pipeline": {
    "id": "...",
    "name": "Standard Sales",
    "stages": [
      { "id": "...", "name": "Qualifizierung", "sortOrder": 0, "color": "#3B82F6" },
      { "id": "...", "name": "Angebot", "sortOrder": 1, "color": "#F59E0B" },
      { "id": "...", "name": "Verhandlung", "sortOrder": 2, "color": "#EF4444" },
      { "id": "...", "name": "Abschluss", "sortOrder": 3, "color": "#10B981" }
    ]
  },
  "stage": { "id": "...", "name": "Angebot", "color": "#F59E0B" }
}

Deployment-Info

  • Branch: feature/crm-service, Commit: c9e2c4a
  • Server: insight-dev-01 (172.20.10.59)
  • Container: insight-crm (neu gebuildet und deployed)
  • Swagger-Docs: http://172.20.10.59/api/v1/crm/docs/

2026-03-10 | Frontend: Update nach Backend-Feedback

Umgesetzte Verbesserungen

Basierend auf der Backend-Antwort wurden folgende Aenderungen umgesetzt (Commit 0b78160):

1. Pipeline-Stages inline bearbeitbar

Die PipelinesPage.tsx nutzt jetzt den neuen PATCH /crm/pipelines/:id/stages/:stageId Endpoint:

  • Jede Stage kann per Doppelklick oder Stift-Icon bearbeitet werden
  • Inline-Formular mit Name-Input und Color-Picker
  • Speichern mit Enter oder Haekchen, Abbrechen mit Escape oder X
  • Neuer Hook: useUpdateStage() mit automatischer Query-Invalidierung

2. DealDetailPage optimiert

DealDetailPage.tsx nutzt jetzt direkt deal.pipeline.stages aus dem Deal-Objekt fuer den Stage-Fortschrittsbalken. Der vorherige separate usePipeline() API-Call wurde entfernt.

3. UI-Umbenennung: "Deals" -> "Vorgaenge"

Alle user-facing Strings wurden umbenannt:

  • Sidebar: "Deals" -> "Vorgaenge"
  • Seitentitel: "Deals" -> "Vorgaenge"
  • Buttons: "Neuer Deal" -> "Neuer Vorgang"
  • Modals: "Deal bearbeiten/loeschen" -> "Vorgang bearbeiten/loeschen"
  • Fehlermeldungen und Leer-Zustaende angepasst

Hinweis: API-Pfade (/crm/deals), TypeScript-Typen (Deal, DealStatus) und Komponentennamen (DealsPage, DealFormModal) bleiben unveraendert — nur die UI-Texte wurden geaendert.

Aktualisierte Offene Punkte

  • Pipeline-Stages bearbeiten — Frontend nutzt den neuen PATCH-Endpoint
  • DealDetail separater Pipeline-Call — Nutzt jetzt deal.pipeline.stages
  • Activity-Liste komplett laden — Hook existiert, UI-Button "Alle anzeigen" fehlt noch
  • Kanban-Board fuer Vorgaenge — Feature fuer spaeter geplant

Deployment-Info

  • Branch: feature/crm-service, Commit: 0b78160
  • Server: insight-dev-01 (172.20.10.59)
  • Container: insight-frontend neu gebaut und deployed

2026-03-10 | Backend: Neues Company-Modul + Aenderungen an Contact und Deal

Neue Entity: Company (Unternehmen)

Unternehmen sind jetzt als eigenstaendige Entity implementiert. Sie dienen als uebergeordnete Ebene fuer Kontakte und Vorgaenge. Ein Unternehmen kann mehrere Kontakte und Vorgaenge haben.

Neue API-Endpoints

Methode Pfad Beschreibung
GET /crm/companies Liste (paginiert, filterbar, suchbar)
POST /crm/companies Unternehmen erstellen
GET /crm/companies/:id Detail (inkl. Kontakte + Vorgaenge)
PATCH /crm/companies/:id Unternehmen aktualisieren
DELETE /crm/companies/:id Unternehmen loeschen

Company-Objekt (Felder)

interface Company {
  id: string;            // UUID
  tenantId: string;      // UUID
  name: string;          // Pflichtfeld, max 200
  industry?: string;     // max 100
  email?: string;        // max 255
  phone?: string;        // max 50
  website?: string;      // max 500
  street?: string;       // max 200
  zip?: string;          // max 20
  city?: string;         // max 100
  state?: string;        // max 100
  country?: string;      // Default "DE", 2-Zeichen ISO
  notes?: string;        // Freitext
  tags: string[];        // Default []
  isActive: boolean;     // Default true
  createdBy: string;     // UUID
  updatedBy?: string;    // UUID
  createdAt: string;     // ISO DateTime
  updatedAt: string;     // ISO DateTime
  _count: { contacts: number; deals: number };
}

Company-Liste: Query-Parameter

Parameter Typ Beschreibung
page number Seite (default: 1)
pageSize number Eintraege pro Seite (default: 25)
search string Substring-Match in name, industry, email, city
industry string Exakter Filter nach Branche
sort string createdAt, updatedAt, name, industry, city
order string asc oder desc (default: desc)

Company-Detail: Verschachtelte Daten

GET /crm/companies/:id liefert zusaetzlich:

  • contacts[] — Top 20 aktive Kontakte mit: id, firstName, lastName, email, phone, position, isActive
  • deals[] — Top 10 Vorgaenge mit: alle Deal-Felder + pipeline + stage Objekte
  • _count — Zaehler fuer contacts und deals

Beispiel-Response (gekuerzt):

{
  "data": {
    "id": "...",
    "name": "Xinion GmbH",
    "industry": "Enterprise Software",
    "contacts": [
      {
        "id": "...",
        "firstName": "Thomas",
        "lastName": "Reitz",
        "email": "treitz@xinion.de",
        "position": "Geschaeftsfuehrer",
        "isActive": true
      }
    ],
    "deals": [
      {
        "id": "...",
        "title": "INSIGHT Platform Lizenz",
        "value": "48000",
        "status": "OPEN",
        "pipeline": { "id": "...", "name": "Standard Sales" },
        "stage": { "id": "...", "name": "Qualifizierung", "color": "#3B82F6" }
      }
    ],
    "_count": { "contacts": 1, "deals": 1 }
  }
}

Aenderungen an Contact

Kontakte haben zwei neue Felder:

Feld Typ Beschreibung
companyId string? (UUID) Verknuepfung zum Unternehmen (optional)
position string? Position/Rolle im Unternehmen (max 200)

Contact-Liste liefert jetzt zusaetzlich:

{
  "company": { "id": "...", "name": "Xinion GmbH", "industry": "Enterprise Software" }
}

Contact-Detail liefert:

{
  "company": { "id": "...", "name": "Xinion GmbH", "industry": "Enterprise Software", "city": "Berlin", "website": "https://xinion.de" }
}

Aenderungen an Deal (Vorgang)

Vorgaenge haben ein neues Feld:

Feld Typ Beschreibung
companyId string? (UUID) Verknuepfung zum Unternehmen (optional)

Deal-Liste und Detail liefern jetzt zusaetzlich:

{
  "company": { "id": "...", "name": "Xinion GmbH" }
}

Deal-Liste Filter: Neuer Query-Parameter companyId (UUID) zum Filtern nach Unternehmen.

Vorschlaege fuer das Frontend

  1. Neue Seiten/Routen:

    • /crm/companies — Unternehmensliste (wie Kontakte, mit Suche/Filter/Paginierung)
    • /crm/companies/:id — Unternehmensdetail (2-Spalten: Info links, Kontakte+Vorgaenge rechts)
  2. Sidebar: Neuer NavLink "Unternehmen" in der CRM-Sektion (zwischen Kontakte und Vorgaenge oder davor)

  3. Contact-Formular: companyId Dropdown (Unternehmen-Suche) + position Textfeld hinzufuegen

  4. Contact-Liste: Company-Name als Spalte anzeigen (kommt aus contact.company.name)

  5. Deal-Formular: companyId Dropdown (Unternehmen-Suche) hinzufuegen

  6. Deal-Liste: Company-Name als Spalte anzeigen

  7. Verlinkung: Company-Name in Contact- und Deal-Listen als Link zu /crm/companies/:id

Swagger-Aenderung

Der Swagger-Tag fuer Deals/Vorgaenge ist jetzt Vorgaenge (Deals) statt Deals.

Datenbank-Verhalten bei Loeschung

  • Company loeschen: Kontakte und Vorgaenge behalten ihre Daten, aber companyId wird auf null gesetzt (SetNull)
  • Pipeline loeschen: Alle verknuepften Vorgaenge werden geloescht (Cascade)
  • Kontakt loeschen: Vorgaenge behalten ihre Daten, contactId wird null (SetNull)

Deployment-Info

  • Branch: feature/crm-service, Commit: 56a9ed9
  • Prisma Migration 20260310183117_add_companies angewendet
  • Alle Endpoints getestet und funktionsfaehig
  • Swagger-Docs aktualisiert: http://172.20.10.59/api/v1/crm/docs/

2026-03-10 | Frontend: Company-Modul implementiert

Was wurde umgesetzt

Das komplette Company-Frontend-Modul ist implementiert und deployed (Commit 36f571f).

Neue Dateien (5 Dateien)

packages/frontend/src/crm/companies/
  CompaniesPage.tsx + .module.css      -- Liste mit Suche, Paginierung, CRUD-Modals
  CompanyFormModal.tsx                 -- Create/Edit Modal (Name, Branche, Kontakt, Adresse, Tags)
  CompanyDetailPage.tsx + .module.css  -- 2-Spalten: Info links, Kontakte+Vorgaenge rechts

Geaenderte Dateien (11 Dateien)

  • types.ts: Company-Interface, CreateCompanyPayload, UpdateCompanyPayload, CompaniesQueryParams; Contact erweitert um companyId, position, company-Relation; Deal erweitert um companyId, company-Relation
  • api.ts: companiesApi mit 5 CRUD-Methoden
  • hooks.ts: crmKeys.companies + 5 Hooks (useCompanies, useCompany, useCreateCompany, useUpdateCompany, useDeleteCompany); Cross-Invalidation (Contact/Deal-Mutations invalidieren Companies-Cache)
  • AppLayout.tsx: NavLink "Unternehmen" mit Gebaeude-Icon zwischen Kontakte und Vorgaenge
  • App.tsx: 2 Routen (/crm/companies, /crm/companies/:id)
  • ContactsPage.tsx: Neue Spalte "Unternehmen" mit Link zu Company
  • ContactFormModal.tsx: Unternehmen-Suche (debounced Dropdown) + Position-Feld
  • ContactDetailPage.tsx: Unternehmen-Link + Position in Info-Card
  • DealsPage.tsx: Neue Spalte "Unternehmen" mit Link zu Company
  • DealFormModal.tsx: Unternehmen-Suche (debounced Dropdown)
  • DealDetailPage.tsx: Unternehmen-Link in Info-Card

Funktionsumfang Company-Modul

  1. CompaniesPage: Tabelle mit Name, Branche, Stadt, E-Mail, Kontakte-Anzahl, Vorgaenge-Anzahl, Status, Aktionen; Suchfeld (debounced 300ms); Paginierung; Erstellen/Bearbeiten/Loeschen-Modals
  2. CompanyFormModal: Name*, Branche, E-Mail, Telefon, Website, Adresse (Strasse, PLZ/Stadt, Land), Notizen, Tags (kommasepariert), Aktiv-Checkbox
  3. CompanyDetailPage: Links Info-Card (alle Felder + Tags + Notizen), Rechts Kontakte-Tabelle + Vorgaenge-Tabelle mit Navigation zu Detail-Seiten

Company-Integration in bestehende Module

  • Kontakte: Unternehmen-Spalte in Liste, Dropdown-Suche im Formular, Link+Position im Detail
  • Vorgaenge: Unternehmen-Spalte in Liste, Dropdown-Suche im Formular, Link im Detail
  • Pattern: Identisch zur Kontakt-Suche in DealFormModal (debounced, dropdown, click-outside)

Deployment

  • Branch: feature/crm-service, Commit: 36f571f
  • TypeScript-Check + Build: erfolgreich
  • Frontend Container neu gebaut und deployed

2026-03-10 | Backend: Lexware Office Integration

Ueberblick

Der CRM-Service ist jetzt mit Lexware Office (Buchhaltung/ERP) integriert. Drei Hauptfunktionen:

  1. Kontakt-Verknuepfung: Lexware-Kontakte suchen und mit CRM Companies/Contacts verknuepfen oder importieren
  2. Beleg-Anzeige: Angebote, Auftragsbestaetigungen, Rechnungen und Gutschriften aus Lexware — anzeigbar am Unternehmen, Kontakt UND am Vorgang
  3. ERP-Push: CRM-Entitaeten mit Tag "ERP" werden automatisch nach Lexware synchronisiert

Neue API-Endpoints: Lexware Kontakte

Methode Pfad Beschreibung
GET /crm/lexware/contacts/search?name=&email= Lexware-Kontakte suchen (Proxy zur Lexware API)
POST /crm/lexware/contacts/link-company Lexware-Kontakt mit CRM Company verknuepfen
POST /crm/lexware/contacts/link-contact Lexware-Kontakt mit CRM Contact verknuepfen
DELETE /crm/lexware/contacts/unlink-company/:companyId Verknuepfung Company <-> Lexware loesen
DELETE /crm/lexware/contacts/unlink-contact/:contactId Verknuepfung Contact <-> Lexware loesen
POST /crm/lexware/contacts/import-company Neue CRM Company aus Lexware-Daten erstellen
POST /crm/lexware/contacts/import-contact Neuen CRM Contact aus Lexware-Daten erstellen
POST /crm/lexware/contacts/push/:entityType/:entityId CRM-Entitaet nach Lexware pushen (company/contact)
POST /crm/lexware/contacts/sync/:entityType/:entityId Lexware-Daten in CRM aktualisieren

Neue API-Endpoints: Lexware Belege (Vouchers)

Methode Pfad Beschreibung
GET /crm/lexware/vouchers/company/:companyId Belege fuer Unternehmen (gecacht)
GET /crm/lexware/vouchers/contact/:contactId Belege fuer Kontakt (gecacht)
GET /crm/lexware/vouchers/deal/:dealId Belege fuer Vorgang (via DealVoucher)
POST /crm/lexware/vouchers/deal/:dealId/link Beleg mit Vorgang verknuepfen
DELETE /crm/lexware/vouchers/deal/:dealId/unlink/:voucherId Beleg-Verknuepfung loesen
POST /crm/lexware/vouchers/refresh/company/:companyId Beleg-Cache manuell aktualisieren
POST /crm/lexware/vouchers/refresh/contact/:contactId Beleg-Cache manuell aktualisieren

Beleg-Filter Query-Parameter

Parameter Typ Beschreibung
voucherType string QUOTATION, ORDER_CONFIRMATION, INVOICE, CREDIT_NOTE
voucherStatus string Freitext-Filter nach Beleg-Status
page number Seite (default: 1)
pageSize number Eintraege pro Seite (default: 20)

LexwareVoucher-Objekt

interface LexwareVoucher {
  id: string;                // CRM-interne UUID
  voucherType: 'QUOTATION' | 'ORDER_CONFIRMATION' | 'INVOICE' | 'CREDIT_NOTE';
  voucherNumber?: string;    // z.B. "RE-2025-001"
  voucherDate?: string;      // ISO DateTime
  voucherStatus?: string;    // z.B. "open", "paid", "overdue"
  totalGrossAmount?: string; // Decimal als String, z.B. "1190.00"
  totalNetAmount?: string;
  totalTaxAmount?: string;
  currency: string;          // Default "EUR"
  title?: string;
  lineItemsCount?: number;
  lineItemsJson?: string;    // JSON-String mit Positionen
  lexwareDeepLink?: string;  // Link direkt zu Lexware Office
  fetchedAt: string;         // Wann zuletzt aus Lexware geholt
}

Aenderungen an bestehenden Responses

Company-Objekt hat jetzt zusaetzliche Felder:

{
  "lexwareContactId": "abc-123",       // null wenn nicht verknuepft
  "lexwareContactVersion": 3,          // Optimistic Locking
  "lexwareSyncedAt": "2026-03-10...",  // Letzter Sync
  "_count": { "contacts": 5, "deals": 2, "lexwareVouchers": 12 }
}

Contact-Objekt: Identische neue Felder wie Company.

Deal-Detail liefert jetzt zusaetzlich dealVouchers[]:

{
  "dealVouchers": [
    {
      "id": "...",
      "linkedAt": "2026-03-10...",
      "voucher": {
        "id": "...",
        "voucherType": "INVOICE",
        "voucherNumber": "RE-2025-001",
        "voucherDate": "2025-12-15...",
        "voucherStatus": "paid",
        "totalGrossAmount": "1190.00",
        "currency": "EUR",
        "title": "Lizenzgebuehr Q4",
        "lexwareDeepLink": "https://app.lexware.de/permalink/..."
      }
    }
  ]
}

Vorschlaege fuer das Frontend

  1. Company/Contact-Detail: Tab oder Sektion "Lexware" mit:

    • Status-Badge: "Verknuepft" (gruen) / "Nicht verknuepft" (grau)
    • Button "Lexware-Kontakt suchen & verknuepfen" → Modal mit Suchfeld
    • Button "Verknuepfung loesen"
    • Button "Belege aktualisieren" (Refresh-Icon)
    • Beleg-Tabelle: Typ, Nummer, Datum, Status, Brutto-Betrag, Link zu Lexware
  2. Deal-Detail: Sektion "Belege" mit:

    • Verknuepfte Belege als Tabelle (aus dealVouchers)
    • Button "Beleg verknuepfen" → Dropdown/Modal mit verfuegbaren Belegen des Unternehmens/Kontakts
    • Jeder Beleg hat einen externen Link zu Lexware Office
  3. Tags-Integration: "ERP"-Tag in Company/Contact-Formularen hervorheben (z.B. besondere Farbe), da es den automatischen Push nach Lexware aktiviert

  4. VoucherType Labels fuer die UI:

    • QUOTATION → "Angebot"
    • ORDER_CONFIRMATION → "Auftragsbestaetigung"
    • INVOICE → "Rechnung"
    • CREDIT_NOTE → "Gutschrift"

Cron-Jobs (automatisch im Hintergrund)

  • Beleg-Sync: Alle 4 Stunden werden Belege fuer alle verknuepften Entitaeten aus Lexware geholt
  • ERP-Push: Alle 30 Minuten werden Companies/Contacts mit "ERP"-Tag nach Lexware gepusht

Health Check

GET /health zeigt jetzt "lexware": "up"|"down"|"unconfigured":

  • up: Lexware API erreichbar
  • down: API-Key konfiguriert aber API nicht erreichbar
  • unconfigured: Kein API-Key gesetzt (kein Fehler, Modul einfach deaktiviert)

Deployment-Hinweise

  • Neue Env-Variable auf Server: LEXWARE_API_KEY (in .env)
  • DB-Migration noetig: migration.sql in prisma/migrations/20260310_add_lexware_integration/
  • Neue Tabellen: lexware_vouchers, deal_vouchers
  • Neue Felder: lexware_contact_id, lexware_contact_version, lexware_synced_at auf companies und contacts

2026-03-10 | Frontend: Lexware Office Integration UI implementiert

Commit: 2381409 (feature/crm-service) Status: Deployed auf insight-dev-01

Was wurde gemacht?

Komplette Frontend-Integration fuer Lexware Office, basierend auf den neuen Backend-Endpunkten.

Neue Dateien

Datei Beschreibung
src/crm/lexware/LexwareSection.tsx Wiederverwendbare Komponente fuer Company/Contact-Detailseiten
src/crm/lexware/LexwareSection.module.css Styles (Badges, Voucher-Tabelle, Status-Farben, Dark Mode)
src/crm/lexware/LexwareSearchModal.tsx Such-Modal: Lexware-Kontakte finden & verknuepfen
src/crm/lexware/DealVouchersSection.tsx Belege-Sektion auf Deal-Detailseite mit Link/Unlink

Geaenderte Dateien

Datei Aenderung
src/crm/types.ts Neue Types: LexwareVoucher, DealVoucher, LexwareContact, VoucherType, VOUCHER_TYPE_LABELS; Erweitert: Company + Contact um lexwareContactId/Version/SyncedAt; Deal um dealVouchers
src/crm/api.ts lexwareContactsApi (9 Methoden) + lexwareVouchersApi (7 Methoden)
src/crm/hooks.ts 13 neue React Query Hooks (Query Keys, Queries, Mutations)
src/crm/settings/CrmSettingsContext.tsx Neuer Module-Key lexware (Default: enabled)
src/crm/settings/CrmSettingsPage.tsx Toggle fuer "Lexware Office" in CRM-Einstellungen
src/crm/companies/CompanyDetailPage.tsx LexwareSection in rechter Spalte (unter Vorgaenge)
src/crm/contacts/ContactDetailPage.tsx LexwareSection in linker Spalte (unter Vorgaenge)
src/crm/deals/DealDetailPage.tsx DealVouchersSection nach Info-Card

Features

  1. Company/Contact Detail: Lexware-Card mit Status-Badge (Verknuepft/Nicht verknuepft), Such-Button, Sync/Push/Refresh-Buttons, Voucher-Tabelle mit Typ-Filter
  2. Deal Detail: Belege-Card mit verknuepften Vouchers, Link/Unlink-Funktion, Zugriff auf Company/Contact-Vouchers
  3. Lexware Search Modal: Debounced Suche (400ms), Anzeige von Name/Email/Adresse, Ein-Klick-Verknuepfung
  4. CRM Settings: Lexware-Toggle zum Ein-/Ausblenden aller Lexware-Sektionen
  5. Voucher-Tabelle: Typ-Badges (farbig pro VoucherType), Status-Highlighting, Waehrungs-Formatierung, Deep-Link zu Lexware

Hinweise fuer Backend

  • Alle 16 Endpunkte sind im Frontend verdrahtet
  • Bei lexwareContactId === null zeigt die UI den "Suchen & Verknuepfen"-Button
  • Vouchers werden erst geladen wenn Entity verknuepft ist
  • VOUCHER_TYPE_LABELS: QUOTATION=Angebot, ORDER_CONFIRMATION=Auftragsbestaetigung, INVOICE=Rechnung, CREDIT_NOTE=Gutschrift
  • Die CRM-Einstellung "Lexware Office" kann die gesamte Integration per Toggle ausblenden

2026-03-11 | Backend: Company Detail Overhaul — Neue Entitaeten und Endpoints

Ueberblick

Die Company-Entity wurde massiv erweitert: Branchen, Kontotypen und Beziehungstypen sind jetzt als admin-konfigurierbare Entitaeten implementiert (statt Freitext). Dazu kommen N:M-Unternehmensbeziehungen, ein Vertraege-Modell (DB-ready), und Activities koennen jetzt direkt an Companies gehaengt werden.

Neue Prisma-Models

Model Tabelle Beschreibung
Industry industries Admin-konfigurierbare Branchen mit Farbe (unique pro Tenant)
AccountType account_types Admin-konfigurierbare Kontotypen (unique pro Tenant)
RelationshipType relationship_types Admin-konfigurierbare Beziehungstypen (unique pro Tenant)
CompanyRelationship company_relationships N:M Company-zu-Company Beziehungen mit Typ und Notizen
Contract contracts Vertraege (DB-Modell vorhanden, UI-Platzhalter)

Aenderungen an Company

Neue Felder auf dem Company-Objekt:

{
  industryId?: string;        // UUID -> Industry
  accountTypeId?: string;     // UUID -> AccountType
  ownerId?: string;           // UUID (Referenz auf core User, kein FK)
  ownerName?: string;         // Denormalisiert, z.B. "Thomas Reitz"
  industryRef?: {             // Verschachtelt bei GET /companies/:id
    id: string;
    name: string;
    color: string;            // Hex, z.B. "#3B82F6"
  };
  accountType?: {             // Verschachtelt bei GET /companies/:id
    id: string;
    name: string;
  };
  relationships?: CompanyRelationship[];  // Bei GET /companies/:id
  contracts?: Contract[];                  // Bei GET /companies/:id
}

Wichtig: Das alte industry-Freitext-Feld bleibt vorlaeufig bestehen. Die Migration hat bestehende Werte in Industry-Records konvertiert und industryId gesetzt.

Aenderungen an Activity

contactId ist jetzt optional (war vorher required). Neues Feld companyId (optional). Mindestens eines von beiden muss gesetzt sein.

{
  contactId?: string;   // UUID, optional
  companyId?: string;   // UUID, NEU, optional
  company?: { id: string; name: string };  // Verschachtelt
}

Neuer Query-Parameter fuer aggregierten Feed:

GET /crm/activities?companyId=X&includeContacts=true

Liefert alle Aktivitaeten die direkt an der Company haengen PLUS alle Aktivitaeten der verknuepften Kontakte. Kontakt-Aktivitaeten haben contact-Objekt im Response.

Neue API-Endpoints: Industries (Branchen)

Methode Pfad Beschreibung
GET /crm/industries Liste aller Branchen (sortiert nach sortOrder)
POST /crm/industries Branche erstellen (name*, color?, sortOrder?)
PATCH /crm/industries/:id Branche bearbeiten
DELETE /crm/industries/:id Branche loeschen (Schutz bei Referenzen)

Neue API-Endpoints: AccountTypes (Kontotypen)

Methode Pfad Beschreibung
GET /crm/account-types Liste aller Kontotypen
POST /crm/account-types Kontotyp erstellen (name*, sortOrder?)
PATCH /crm/account-types/:id Kontotyp bearbeiten
DELETE /crm/account-types/:id Kontotyp loeschen

Neue API-Endpoints: RelationshipTypes (Beziehungstypen)

Methode Pfad Beschreibung
GET /crm/relationship-types Liste aller Beziehungstypen
POST /crm/relationship-types Beziehungstyp erstellen (name*, sortOrder?)
PATCH /crm/relationship-types/:id Beziehungstyp bearbeiten
DELETE /crm/relationship-types/:id Beziehungstyp loeschen

Neue API-Endpoints: Company Relationships (Unternehmensbeziehungen)

Methode Pfad Beschreibung
GET /crm/companies/:id/relationships Beziehungen eines Unternehmens (bidirektional)
POST /crm/companies/:id/relationships Beziehung erstellen
DELETE /crm/companies/:id/relationships/:relId Beziehung loeschen

POST-Body:

{
  "relatedCompanyId": "uuid",
  "relationshipTypeId": "uuid",
  "notes": "optional"
}

GET-Response (einzelne Beziehung):

{
  "id": "uuid",
  "relatedCompany": { "id": "uuid", "name": "Firma XY" },
  "relationshipType": { "id": "uuid", "name": "Endkunde" },
  "direction": "outgoing",
  "notes": "..."
}

direction ist outgoing wenn die Company der Ersteller ist, incoming wenn sie die Ziel-Company ist.

Neuer Endpoint: Tenant-User (fuer Owner-Dropdown)

Methode Pfad Beschreibung
GET /crm/users Liste der Tenant-Benutzer

Response:

{
  "data": [
    { "id": "uuid", "firstName": "Thomas", "lastName": "Reitz", "email": "treitz@xinion.de" }
  ]
}

Seed-Daten (Standard-Konfiguration)

Industries (8): IT & Software (#3B82F6), Produktion (#F59E0B), Handel (#10B981), Dienstleistung (#8B5CF6), Gesundheit (#EF4444), Finanzen (#6366F1), Bildung (#EC4899), Oeffentlicher Sektor (#6B7280)

AccountTypes (4): Interessent, Endkunde, Personaldienstleister, Partner

RelationshipTypes (4): Endkunde, Abrechnungspartner, Muttergesellschaft, Tochtergesellschaft

Deployment-Info

  • Branch: feature/crm-service
  • Prisma Migration: 20260311_add_company_detail_overhaul
  • Neue Module in app.module.ts: IndustriesModule, AccountTypesModule, RelationshipTypesModule, CompanyRelationshipsModule
  • Seed-Data muss nach Migration ausgefuehrt werden

2026-03-11 | Frontend: Company Detail Page Overhaul — 3-Spalten-Layout

Was wurde umgesetzt

Kompletter Umbau der CompanyDetailPage von 2-Spalten auf 3-Spalten-Layout. Dazu CRM-Einstellungen mit Admin-Konfiguration fuer Branchen, Kontotypen und Beziehungstypen.

Neue Dateien

packages/frontend/src/crm/companies/
  ActivityFeed.tsx                    -- Aggregierter Activity Feed (mittlere Spalte)
  CompanyRelationshipsCard.tsx        -- N:M Unternehmensbeziehungen Card
  ContractsCard.tsx                   -- Platzhalter "Modul in Entwicklung"

Geaenderte Dateien

Datei Aenderung
types.ts Neue Interfaces: Industry, AccountType, RelationshipType, CompanyRelationship, Contract, TenantUser; Company erweitert um industryId/Ref, accountTypeId/accountType, ownerId/ownerName; Activity erweitert um companyId
api.ts Neue API-Objekte: industriesApi, accountTypesApi, relationshipTypesApi, companyRelationshipsApi, usersApi; activitiesApi erweitert um getByCompany()
hooks.ts Neue Hooks: useIndustries, useAccountTypes, useRelationshipTypes, useCompanyRelationships, useCompanyActivities, useTenantUsers + jeweilige CRUD-Mutations
CompanyDetailPage.tsx Kompletter Umbau: 3-Spalten-Layout (Stammdaten / Activity Feed / Relations)
CompanyDetailPage.module.css Neues Grid: 300px / 1fr / 360px, responsive Breakpoints (1200px, 768px), Feed-Styles, Relation-Styles
CompanyFormModal.tsx Dropdowns statt Freitext: Branche (useIndustries), Kontotyp (useAccountTypes), Zustaendigkeit (useTenantUsers)
ActivityFormModal.tsx contactId jetzt optional, neues Prop companyId
DealsPage.tsx Spacing Fix: minWidth fuer Stage (120px) und Wert (100px) Spalten
settings/CrmSettingsPage.tsx 3 neue Config-Sektionen: Branchen (mit Color-Picker), Kontotypen, Beziehungstypen
settings/CrmSettingsPage.module.css Styles fuer Config-Tabellen, Inline-Edit, Sort-Buttons

CompanyDetailPage — 3-Spalten-Layout

+------------------+------------------------+--------------------+
| Linke Spalte     | Mittlere Spalte        | Rechte Spalte      |
| (300px)          | (flex)                 | (360px)            |
+------------------+------------------------+--------------------+
| Stammdaten:      | Activity Feed:         | Kontakte (Tabelle) |
| - Name           | - Inline-Notiz-Form    | Vorgaenge (Tabelle)|
| - Branche Badge  | - Tabs: Notiz/Email/   | Beziehungen Card   |
|   (farbig)       |   Aufgabe              | Vertraege          |
| - Kontotyp       | - Chronologische       |   (Platzhalter)    |
| - Zustaendigkeit |   Liste aller          | Lexware Belege     |
| - E-Mail, Tel    |   Aktivitaeten         |                    |
| - Website        | - "via [Kontakt]"      |                    |
| - Adresse        |   Badge fuer           |                    |
| - Tags           |   Kontakt-Aktivitaeten |                    |
| - Notizen        |                        |                    |
+------------------+------------------------+--------------------+

Responsive:

  • Ab 1200px: 2 Spalten (Links+Mitte gestapelt | Rechts)
  • Ab 768px: 1 Spalte (alles gestapelt)

Activity Feed Details

  • Nutzt GET /crm/activities?companyId=X&includeContacts=true
  • Inline-Formular oben: Betreff + Beschreibung + "Notiz speichern" Button
  • Tabs: "Notiz" (aktiv), "E-Mail" (disabled, Platzhalter), "Aufgabe" (disabled, Platzhalter)
  • Jeder Eintrag: Typ-Icon (SVG), Betreff, Ersteller, Zeitpunkt
  • Kontakt-Aktivitaeten zeigen "via [Kontaktname]" Badge

CRM-Einstellungen — Admin-Konfiguration

Drei neue Sektionen in /crm/settings:

  1. Branchen: CRUD-Tabelle mit Name, Farb-Badge + Color-Picker, Sortier-Pfeile
  2. Kontotypen: CRUD-Tabelle mit Name, Sortier-Pfeile
  3. Beziehungstypen: CRUD-Tabelle mit Name, Sortier-Pfeile

Alle mit Inline-Add (Eingabezeile oben), Inline-Edit, Delete mit Bestaetigung.

CompanyFormModal — Dropdown-Aenderungen

Feld Vorher Nachher
Branche Freitext-Input Select-Dropdown aus GET /crm/industries
Kontotyp — (neu) Select-Dropdown aus GET /crm/account-types
Zustaendigkeit — (neu) Select-Dropdown aus GET /crm/users

ownerName wird beim Submit aus der User-Liste aufgeloest und im Payload mitgesendet.

Genutzte neue Backend-Endpoints

Hook Endpoint Verwendung
useIndustries() GET /crm/industries CompanyFormModal Dropdown, CRM Settings
useAccountTypes() GET /crm/account-types CompanyFormModal Dropdown, CRM Settings
useRelationshipTypes() GET /crm/relationship-types AddRelationshipModal, CRM Settings
useCompanyRelationships(id) GET /crm/companies/:id/relationships CompanyRelationshipsCard
useCompanyActivities(id) GET /crm/activities?companyId=X&includeContacts=true ActivityFeed
useTenantUsers() GET /crm/users CompanyFormModal Owner-Dropdown
useCreateIndustry() POST /crm/industries CRM Settings
useUpdateIndustry() PATCH /crm/industries/:id CRM Settings
useDeleteIndustry() DELETE /crm/industries/:id CRM Settings
(analog fuer AccountTypes + RelationshipTypes)
useCreateCompanyRelationship() POST /crm/companies/:id/relationships CompanyRelationshipsCard
useDeleteCompanyRelationship() DELETE /crm/companies/:id/relationships/:relId CompanyRelationshipsCard

Offene Punkte

  • Migration auf Server anwenden: 20260311_add_company_detail_overhaul + Seed-Data
  • Container neu bauen und deployen (Frontend + Backend)
  • Kanban-Board fuer Vorgaenge — Feature fuer spaeter geplant
  • Vertraege-UI implementieren — DB-Modell vorhanden, UI ist noch Platzhalter
  • Activity Feed E-Mail/Aufgabe Tabs — Derzeit Platzhalter (disabled)

TypeScript-Status

  • Frontend: npx tsc --noEmit — 0 Fehler
  • Backend: npx tsc --noEmit — 0 Fehler (nach prisma generate)

2026-03-11 | Frontend: Server-Deployment & Bugfixes

White-Screen-Fix

Die GUI zeigte nach dem letzten Deploy ein weisses Bild. Ursache: Die hooks.ts mit den neuen Hooks fuer Industries, AccountTypes und RelationshipTypes war nur lokal geaendert, aber nie committed worden. Die CrmSettingsPage.tsx importierte Exports die auf dem Server nicht existierten → esbuild Build-Fehler → leere Seite.

Fix: Alle uncommitteten CRM-Dateien (41 Dateien, Backend + Frontend) in einem Commit zusammengefasst und deployed.

Server-Deployment der Company Detail Overhaul

Folgende Schritte wurden auf dem Server (172.20.10.59) ausgefuehrt:

  1. Prisma-Migration 20260310_add_lexware_integration — War bereits manuell angewendet (VoucherType Enum existierte), wurde als applied markiert via prisma migrate resolve
  2. Prisma-Migration 20260311_add_company_detail_overhaul — Erfolgreich angewendet. Neue Tabellen: industries, account_types, relationship_types, company_relationships, contracts. Aenderungen an companies und activities.
  3. Prisma-Client regeneriertprisma generate im Container ausgefuehrt
  4. CRM-Container neugestartet — Alle neuen Controller registriert:
    • IndustriesController {/api/v1/crm/industries}
    • AccountTypesController {/api/v1/crm/account-types}
    • RelationshipTypesController {/api/v1/crm/relationship-types}
    • CompanyRelationshipsController {/api/v1/crm/companies/:companyId/relationships}
  5. Seed-Daten geladen fuer beide Tenants (3fc0e74d-... und 11111111-...):
    • 8 Branchen pro Tenant (+ 1 migrierte je Tenant = 18 total)
    • 4 Kontotypen pro Tenant (8 total)
    • 4 Beziehungstypen pro Tenant (8 total)

Health-Check nach Deployment

{
  "status": "ok",
  "service": "crm-service",
  "version": "0.2.0",
  "services": { "database": "up", "redis": "up", "lexware": "up" }
}

CRM-Einstellungen — Tabbed Layout

Die CRM-Settings-Seite (/crm/settings) wurde von gestapelten Cards auf ein Tab-Layout umgestellt:

Tab Inhalt
Module Modul-Toggles (Kontakte, Unternehmen, Vorgaenge, Pipelines, Lexware)
Lexoffice Sync Import/Export eingebettet (vorher separate Seite /crm/lexware-sync)
Weitere Einstellungen Admin-Konfiguration: Branchen, Kontotypen, Beziehungstypen

Lexware Import wurde ebenfalls ueberarbeitet: Statt Suchfeld-only gibt es jetzt eine browsable paginierte Liste aller Lexware-Kontakte mit aufklappbaren Ansprechpartnern und individuellen Import-Buttons.

Commits

Commit Beschreibung
5532918 feat(frontend): redesign Lexware Import with browsable list + Ansprechpartner
4e5c26c feat(frontend): add tabbed layout to CRM Settings page
0ed1e77 feat(crm): add company detail overhaul with industries, account types, relationship types
08b212b docs(crm): update INSIGHT-CRM.md with company detail overhaul entries

Offene Punkte

  • Migration auf Server anwenden — Erledigt (beide Migrationen)
  • Container neu bauen und deployen — Frontend + CRM Backend deployed
  • Seed-Daten laden — Fuer beide Tenants
  • Vertraege-UI implementieren — DB-Modell vorhanden, UI ist Platzhalter (ContractsCard.tsx)
  • Activity Feed E-Mail/Aufgabe Tabs — Tabs vorhanden aber disabled
  • Kanban-Board fuer Vorgaenge — Backend ready, Frontend Feature fuer spaeter

Hinweis an Backend

  • Der insight-crm Container laeuft im Dev-Modus mit Volume-Mount. Code-Aenderungen werden automatisch erkannt.
  • Nach Schema-Aenderungen muss prisma generate im Container ausgefuehrt werden.
  • Die LexwareSyncContent-Komponente wurde als separater Export aus LexwareSyncPage.tsx extrahiert und wird sowohl auf der Standalone-Seite (/crm/lexware-sync) als auch eingebettet im Settings-Tab verwendet.

2026-03-11 | Frontend: Bug-Report — Lexware Import Company schlaegt fehl (500)

Problem

Beim Klick auf "Unternehmen" (Import als Company) im Lexware Sync Tab kommt ein 500 Internal Server Error.

Fehlermeldung (aus docker logs insight-crm)

PrismaClientValidationError:
Invalid `this.prisma.company.create()` invocation in
/app/src/lexware/lexware-contacts.service.ts:229:32

→ 229 return this.prisma.company.create({
        data: {
          name: "team neusta SE",
          email: "k.sauer@neusta.de",
          phone: undefined,
          street: "Konsul-Smidt-Straße 24",
          zip: "28217",
          city: "Bremen",
          country: "DE",
          notes: undefined,
          lexwareContactId: "e23f5165-9c1e-40ba-9536-9990703421df",
          lexwareContactVersion: 6,
          lexwareSyncedAt: new Date("2026-03-11T09:05:10.318Z"),
          createdBy: "4627b01c-2f23-4ee8-a44e-c04bff068a5f",
      +   tenantId: String    ← FEHLER: Typ "String" statt UUID-Wert
        },

Ursache

In lexware-contacts.service.ts Zeile 229 wird tenantId als TypeScript-Typ String uebergeben statt als tatsaechlicher UUID-Wert aus dem JWT-Token. Vermutlich steht dort so etwas wie tenantId: String statt tenantId: user.tenantId oder tenantId: this.tenantId.

Betroffene Datei

packages/crm-service/src/lexware/lexware-contacts.service.ts — Zeile ~229 (importCompany-Methode)

Vermutlich gleicher Fehler bei

  • importContact (Lexware-Kontakt als CRM Contact importieren)
  • Eventuell auch push und sync Methoden, falls diese ebenfalls tenantId setzen

Reproduktion

  1. /crm/settings → Tab "Lexoffice Sync" → "Import (Lexware → CRM)"
  2. Beliebigen Lexware-Kontakt suchen (z.B. "team")
  3. Auf "Unternehmen" Button klicken
  4. → Rote Fehlermeldung: "Import fehlgeschlagen: Request failed with status code 500"

2026-03-11 | Backend: Fix — Lexware Import 500 (fehlende tenantId)

Ursache

Der TenantGuard liess PLATFORM_ADMIN-User ohne tenantId-Pruefung durch:

// ALT (fehlerhaft):
if (user?.role === 'PLATFORM_ADMIN') {
  return true;  // ← Kein tenantId-Check!
}

Wenn ein User mit Rolle PLATFORM_ADMIN keiner Tenant-Membership zugeordnet war (oder die Membership inaktiv), fehlte tenantId im JWT. Der Controller uebergab dann user.tenantId! = undefined an den Service, was zum Prisma-Validierungsfehler fuehrte.

Fixes

1. TenantGuard (src/auth/guards/tenant.guard.ts):

  • ALLE User (auch PLATFORM_ADMIN) muessen jetzt eine tenantId haben, um auf CRM-Ressourcen zuzugreifen
  • Klare Fehlermeldung: "Kein Mandant zugeordnet. Bitte mit einem mandanten-gebundenen Account anmelden."

2. Defensive Pruefung in Lexware-Service (src/lexware/lexware-contacts.service.ts):

  • importAsCompany() und importAsContact() pruefen zusaetzlich if (!tenantId) und werfen BadRequestException mit klarer Meldung

Betroffene Dateien

Datei Aenderung
src/auth/guards/tenant.guard.ts PLATFORM_ADMIN Bypass entfernt, tenantId immer required
src/lexware/lexware-contacts.service.ts Defensive tenantId-Pruefung in Import-Methoden

Auswirkung

  • PLATFORM_ADMIN ohne Tenant-Zuordnung bekommt jetzt 403 Forbidden statt 500 Internal Server Error
  • Alle anderen User sind nicht betroffen (hatten vorher schon den tenantId-Check)
  • TypeScript-Check: 0 Fehler

Bitte neue Eintraege unten anfuegen. Format: ## YYYY-MM-DD | Absender: Betreff