Extract and categorize new requirements from updated CLAUDE_BRIEFING.docx and INSIGHT_Konzept_v1.0.docx. Add comprehensive CRM task breakdown (Kap 22: full CRM spec, Kap 24: Office 365 integration) with prioritized action items for the CRM backend developer. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
64 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
-
Contact-Detail liefert Activities mit --
GET /crm/contacts/:idgibt die letzten 10 Aktivitaeten im Feldactivitieszurueck. Das Frontend zeigt diese in der Timeline an. -
Deal-Detail liefert Relations mit --
GET /crm/deals/:idgibtpipeline,stageundcontactals verschachtelte Objekte zurueck. -
Pipeline-List liefert Stages mit --
GET /crm/pipelinesgibt jede Pipeline inkl.stages[]Array zurueck. Das Frontend nutzt diese fuer die Stage-Selektoren im Deal-Formular. -
Deal.value ist ein String -- Decimal kommt als String vom Backend (z.B.
"24000.00"). Das Frontend parst mitparseFloat(). -
Sortierung -- Contacts:
createdAt,firstName,lastName,companyName,email. Deals:createdAt,title,value,expectedCloseDate. -
Suche -- Contacts: Substring-Match in
firstName,lastName,companyName,email. Deals: Substring-Match intitle.
Bekannte Offene Punkte
- Traefik HTTPS-Router fuer CRM: Aktuell hat der CRM-Service nur einen HTTP-Router (
webEntrypoint). Fuer HTTPS (websecure) muesste ein zweiter Router mittls=trueangelegt werden (wie beicore-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-secureRouter angelegt mitentrypoints=websecure,tls=true, Priority 100. Deployed in Commitc9e2c4a. -
Pipeline-Stages bearbeiten: Neuer Endpoint
PATCH /crm/pipelines/:id/stages/:stageIdhinzugefuegt. 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/:idmit{ 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-EndpointDealDetail 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-frontendneu 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,isActivedeals[]— Top 10 Vorgaenge mit: alle Deal-Felder +pipeline+stageObjekte_count— Zaehler fuercontactsunddeals
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
-
Neue Seiten/Routen:
/crm/companies— Unternehmensliste (wie Kontakte, mit Suche/Filter/Paginierung)/crm/companies/:id— Unternehmensdetail (2-Spalten: Info links, Kontakte+Vorgaenge rechts)
-
Sidebar: Neuer NavLink "Unternehmen" in der CRM-Sektion (zwischen Kontakte und Vorgaenge oder davor)
-
Contact-Formular:
companyIdDropdown (Unternehmen-Suche) +positionTextfeld hinzufuegen -
Contact-Liste: Company-Name als Spalte anzeigen (kommt aus
contact.company.name) -
Deal-Formular:
companyIdDropdown (Unternehmen-Suche) hinzufuegen -
Deal-Liste: Company-Name als Spalte anzeigen
-
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
companyIdwird aufnullgesetzt (SetNull) - Pipeline loeschen: Alle verknuepften Vorgaenge werden geloescht (Cascade)
- Kontakt loeschen: Vorgaenge behalten ihre Daten,
contactIdwirdnull(SetNull)
Deployment-Info
- Branch:
feature/crm-service, Commit:56a9ed9 - Prisma Migration
20260310183117_add_companiesangewendet - 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 umcompanyId,company-Relation - api.ts:
companiesApimit 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
- CompaniesPage: Tabelle mit Name, Branche, Stadt, E-Mail, Kontakte-Anzahl, Vorgaenge-Anzahl, Status, Aktionen; Suchfeld (debounced 300ms); Paginierung; Erstellen/Bearbeiten/Loeschen-Modals
- CompanyFormModal: Name*, Branche, E-Mail, Telefon, Website, Adresse (Strasse, PLZ/Stadt, Land), Notizen, Tags (kommasepariert), Aktiv-Checkbox
- 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:
- Kontakt-Verknuepfung: Lexware-Kontakte suchen und mit CRM Companies/Contacts verknuepfen oder importieren
- Beleg-Anzeige: Angebote, Auftragsbestaetigungen, Rechnungen und Gutschriften aus Lexware — anzeigbar am Unternehmen, Kontakt UND am Vorgang
- 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
-
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
-
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
- Verknuepfte Belege als Tabelle (aus
-
Tags-Integration: "ERP"-Tag in Company/Contact-Formularen hervorheben (z.B. besondere Farbe), da es den automatischen Push nach Lexware aktiviert
-
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 erreichbardown: API-Key konfiguriert aber API nicht erreichbarunconfigured: 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.sqlinprisma/migrations/20260310_add_lexware_integration/ - Neue Tabellen:
lexware_vouchers,deal_vouchers - Neue Felder:
lexware_contact_id,lexware_contact_version,lexware_synced_ataufcompaniesundcontacts
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
- Company/Contact Detail: Lexware-Card mit Status-Badge (Verknuepft/Nicht verknuepft), Such-Button, Sync/Push/Refresh-Buttons, Voucher-Tabelle mit Typ-Filter
- Deal Detail: Belege-Card mit verknuepften Vouchers, Link/Unlink-Funktion, Zugriff auf Company/Contact-Vouchers
- Lexware Search Modal: Debounced Suche (400ms), Anzeige von Name/Email/Adresse, Ein-Klick-Verknuepfung
- CRM Settings: Lexware-Toggle zum Ein-/Ausblenden aller Lexware-Sektionen
- 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 === nullzeigt 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:
- Branchen: CRUD-Tabelle mit Name, Farb-Badge + Color-Picker, Sortier-Pfeile
- Kontotypen: CRUD-Tabelle mit Name, Sortier-Pfeile
- 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 (nachprisma 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:
- Prisma-Migration
20260310_add_lexware_integration— War bereits manuell angewendet (VoucherType Enum existierte), wurde alsappliedmarkiert viaprisma migrate resolve - Prisma-Migration
20260311_add_company_detail_overhaul— Erfolgreich angewendet. Neue Tabellen:industries,account_types,relationship_types,company_relationships,contracts. Aenderungen ancompaniesundactivities. - Prisma-Client regeneriert —
prisma generateim Container ausgefuehrt - 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}
- Seed-Daten geladen fuer beide Tenants (
3fc0e74d-...und11111111-...):- 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 deployedSeed-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-crmContainer laeuft im Dev-Modus mit Volume-Mount. Code-Aenderungen werden automatisch erkannt. - Nach Schema-Aenderungen muss
prisma generateim Container ausgefuehrt werden. - Die
LexwareSyncContent-Komponente wurde als separater Export ausLexwareSyncPage.tsxextrahiert 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
pushundsyncMethoden, falls diese ebenfallstenantIdsetzen
Reproduktion
/crm/settings→ Tab "Lexoffice Sync" → "Import (Lexware → CRM)"- Beliebigen Lexware-Kontakt suchen (z.B. "team")
- Auf "Unternehmen" Button klicken
- → 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
tenantIdhaben, 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()undimportAsContact()pruefen zusaetzlichif (!tenantId)und werfenBadRequestExceptionmit 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
2026-03-12 | Architekt: Neue Anforderungen aus Konzeptdokument v1.0 + Briefing
Der Architekt hat das Konzeptdokument (INSIGHT_Konzept_v1.0.docx) und das Claude Briefing (CLAUDE_BRIEFING.docx) aktualisiert. Folgende Kapitel sind neu oder erweitert und betreffen den CRM-Service direkt:
- Kapitel 22 — CRM-Modul (vorher nur Platzhalter, jetzt vollstaendig spezifiziert)
- Kapitel 24 — Office 365 Integration (neu, CRM-relevante Teile)
- Kapitel 14 im Briefing — Office 365 Kurzreferenz (neu)
Hinweis: CORE vs CRM
Folgende Teile sind CORE-Aufgaben (NICHT fuer den CRM-Entwickler):
- Kap 24.1: OAuth-Flow +
user_integrationsTabelle inplatform_core - Kap 24.8: Azure App-Registrierung im Azure Portal
.env-Variablen:MS_CLIENT_ID,MS_CLIENT_SECRET,MS_REDIRECT_URI,MS_INTEGRATION_ENCRYPTION_KEY
Alles Folgende ist CRM-Arbeit.
A) CRM-Modul Spezifikation (Kap 22) — Neue/Erweiterte Anforderungen
A.1 Kontakttypen & Felder (Kap 22.1)
Die vollstaendige Felddefinition fuer Person und Unternehmen liegt jetzt vor:
Kontakttyp: Person
| Feld | Typ | Pflicht | Bemerkung |
|---|---|---|---|
| Vorname | String | Ja | |
| Nachname | String | Ja | |
| Jobtitel | String | Nein | |
| Unternehmen | Relation -> Unternehmen | Nein | |
| Abteilung | String | Nein | |
| Array (String) | Nein | Mehrere, Typ: Arbeit / Privat / Sonstige | |
| Telefon | Array (String) | Nein | Mehrere, Typ: Buero / Mobil / Fax |
| Adresse | Objekt | Nein | Strasse, PLZ, Stadt, Land |
| LinkedIn-URL | String (URL) | Nein | |
| Geburtsdatum | Date | Nein | Optional, kann ausgeblendet werden |
| Quelle | Enum | Nein | Messe, Empfehlung, Website, Kaltakquise, Import, Visitenkarte, Sonstige |
| Tags | Array (String) | Nein | Frei vergebbar, tenant-weit geteilt |
| Status | Enum | Ja | Aktiv / Inaktiv / Gesperrt (Default: Aktiv) |
| Notizen | Text (Markdown) | Nein | |
| Benutzerdefinierte Felder | Dynamisch | Nein | Siehe A.6 |
Kontakttyp: Unternehmen
| Feld | Typ | Pflicht | Bemerkung |
|---|---|---|---|
| Firmenname | String | Ja | |
| Branche | String | Nein | Freitext oder vordefinierte Kategorien |
| Website | String (URL) | Nein | |
| Telefon | Array (String) | Nein | Mehrere Nummern |
| Adresse Hauptsitz | Objekt | Nein | Strasse, PLZ, Stadt, Land |
| Adresse Lieferung | Objekt | Nein | Optional abweichende Lieferadresse |
| USt-IdNr. | String | Nein | |
| Steuernummer | String | Nein | |
| Handelsregisternummer | String | Nein | z.B. HRB 12345 — befuellbar via Datenanreicherung |
| Registergericht | String | Nein | z.B. Amtsgericht Muenchen — befuellbar via Datenanreicherung |
| Unternehmensgroesse | Enum | Nein | 1-10, 11-50, 51-200, 201-500, 500+ |
| Ansprechpartner | Relation -> Personen | Nein | Verknuepfte Personen |
| Tags | Array (String) | Nein | |
| Status | Enum | Ja | Aktiv / Inaktiv / Gesperrt |
| Notizen | Text (Markdown) | Nein | |
| Datenanreicherung | Automatisch | Nein | data_enriched_at + data_enriched_source |
| Benutzerdefinierte Felder | Dynamisch | Nein | Siehe A.6 |
Backend-Aufgabe: Abgleich mit bestehenden Prisma-Modellen. Fehlende Felder (LinkedIn, Geburtsdatum, Quelle, Unternehmensgroesse, USt-IdNr, Steuernummer, Handelsregisternummer, Registergericht, Adresse Lieferung, data_enriched_at/source) muessen ergaenzt werden.
A.2 Firmendaten-Anreicherung / Data Enrichment (Kap 22.2) — NEU
"Firmendaten laden"-Button im Unternehmenformular. Stammdaten aus externen Registern abrufen und als Vorschlag in einem Modal anzeigen.
Quellen:
- Unternehmensregister.de (kostenlos, oeffentlich) — HR-Nummer, Registergericht, Rechtsform, Sitz, Eintragsdatum
- North Data API (kommerziell, API-Key pro Tenant) — Adresse, Branche, Umsatz, Mitarbeiterzahl, Gesellschafter, Verflechtungen
Backend-Aufgaben:
POST /crm/companies/:id/enrichoderPOST /crm/data-enrichment/companyEndpoint- Paralleler Abruf: Unternehmensregister.de + North Data API (Timeout je 10 Sek.)
- Ergebnisse normalisieren und zusammenfuehren
data_enriched_at+data_enriched_sourcein Company speichern- Admin-Einstellung: North Data API-Key pro Tenant (Admin > CRM > Integrationen)
Frontend-Aufgaben:
- "Firmendaten laden" Button im Unternehmenformular
- Anreicherungs-Modal: Linke Spalte = aktueller Wert, Rechte Spalte = Vorschlag (Quelle), Checkbox pro Feld
- "Auswahl uebernehmen" -> Felder im Formular setzen (Speichern erst bei explizitem Speichern)
- CRM-Settings-Seite: North Data API-Key Konfiguration
A.3 Zustaendigkeit / Account Owner (Kap 22.3) — NEU
Jeder Kontakt/Unternehmen kann mit mehreren internen Mitarbeitern verknuepft werden (m:n).
| Aspekt | Entscheidung |
|---|---|
| Modell | m:n — ein Kontakt hat mehrere Owner, ein Mitarbeiter hat mehrere Kontakte |
| Rollen pro Zuweisung | OWNER, MEMBER, WATCHER (unterschiedliche Bearbeitungsrechte) |
| Mitarbeiter-Referenz | user_id aus platform_core (kein Kopieren von User-Daten) |
| Anzeige | Avatare der zugewiesenen Mitarbeiter in der Kontaktkarte |
| Pflicht-Owner | Mindestens 1 Owner pro Kontakt bei Erstellung (Default: erstellender User) |
DB-Tabelle: crm_contact_owners (contact_id, user_id, role: OWNER|MEMBER|WATCHER)
Backend-Aufgaben:
- Contact-Owner CRUD:
POST/DELETE /crm/contacts/:id/owners - Bei Kontakt-Erstellung automatisch erstellenden User als OWNER setzen
- Owner-Info in Contact-Detail-Response mitliefern
Frontend-Aufgaben:
- Avatare der Owner in der Kontaktkarte anzeigen
- Owner-Zuweisung UI (User suchen + Rolle waehlen)
A.4 Pipeline & Deal-Management — Erweiterte Spec (Kap 22.4)
Ergaenzungen zur bestehenden Implementierung:
- Forecast-Ansicht: Aggregierter Dealwert pro Stage gewichtet mit Wahrscheinlichkeit
- Lost-Grund: Enum + Freitext bei Status Lost: Preis, Timing, Wettbewerber, Kein Bedarf, Sonstige
- Pipeline-Sichtbarkeit: Enum — Alle Tenant-User / Nur zugewiesene Teams
- Deal-Owner: m:n analog zu Kontakt-Owner (crm_deal_owners)
Backend-Aufgaben:
lost_reasonEnum +lost_reason_textFreitext in Deals- Deal-Owner CRUD (analog Contact-Owner)
- Forecast-Endpoint:
GET /crm/deals/forecast(Wert x Wahrscheinlichkeit pro Stage)
Frontend-Aufgaben:
- Lost-Grund Modal bei Stage-Wechsel zu "Lost"
- Kanban-Board (Drag & Drop)
- Forecast-Ansicht
A.5 Aktivitaeten & Aufgaben (Kap 22.5) — Erweiterte Spec
6 Aktivitaetstypen sind definiert:
| Typ | Icon | Bemerkung |
|---|---|---|
| Anruf (Call) | Telefon | Kann Gespraechsnotizen enthalten |
| Meeting | Kalender | Datum, Uhrzeit, Ort / Videolink |
| Brief | Freitext-Notiz, KEINE echte E-Mail-Integration in MVP | |
| Aufgabe (Task) | Checkbox | Faelligkeitsdatum, Zuweisung an Mitarbeiter |
| Notiz | Stift | Freitext ohne Faelligkeitsdatum |
| Follow-Up | Pfeil | Erinnerung zu einem definierten Zeitpunkt |
Hinweis: E-Mail-Integration (Outlook) kommt erst mit Office 365 (Kap 24).
A.6 Benutzerdefinierte Felder / Custom Fields (Kap 22.6) — NEU
Pro Kontakttyp (Person, Unternehmen) und pro Deal koennen eigene Felder definiert werden.
Unterstuetzte Feldtypen:
| Typ | Beschreibung | Beispiel |
|---|---|---|
| Text | Einzeiliger Freitext | Kundennummer |
| Textarea | Mehrzeiliger Freitext | Besondere Hinweise |
| Zahl | Integer oder Decimal | Umsatz, Vertragslaufzeit |
| Datum | Datumspicker | Vertragsbeginn |
| Auswahl (Dropdown) | Vordefinierte Werte | Kundensegment: A / B / C |
| Mehrfachauswahl | Mehrere Werte waehlbar | Interessensgebiete |
| Checkbox | Boolean Ja/Nein | DSGVO-Einwilligung |
| URL | Validierter Link | Portal-Link |
DB-Tabellen:
crm_custom_field_defs (id, entity_type: PERSON|COMPANY|DEAL, name, label, field_type,
options jsonb, is_required, position)
crm_custom_field_values (id, field_def_id, entity_id, value_text, value_number,
value_date, value_boolean, value_json)
Backend-Aufgaben:
- CRUD fuer Field-Definitions:
GET/POST/PATCH/DELETE /crm/custom-fields - Wert-Speicherung bei Entity-Create/Update
- Custom Fields in Entity-Detail-Response mitliefern
Frontend-Aufgaben:
- Admin-Bereich: Custom Fields Verwaltung (Drag & Drop Reihenfolge, Pflichtfeld-Flag)
- Dynamische Formular-Felder in Contact/Deal-Formularen
- Custom Fields als optionale Spalten in Listen-Ansichten
- Custom Fields als Filter in der Suche
A.7 Kontakt-Import (Kap 22.7) — NEU
Import via Datei:
- CSV-Import mit visuellem Spalten-Mapper
- Excel-Import (.xlsx)
- vCard-Import (.vcf, einzeln und ZIP)
- Vorschau der ersten 10 Datensaetze + Validierungsfehler anzeigen
- Duplikat-Erkennung via E-Mail: ueberspringen / zusammenfuehren / als Duplikat markieren
Visitenkarten-Scan (Anthropic Vision API):
- "Visitenkarte scannen" — Kamera-/Datei-Dialog
- Bild base64-kodiert an CRM-Backend senden
- Backend ruft Anthropic Vision API auf (strukturierter Prompt)
- Vorausgefuelltes Kontaktformular zur Bestaetigung
- Rate Limit: max. 50 Scans pro Tenant pro Tag (konfigurierbar)
- Bild wird NICHT dauerhaft gespeichert
A.8 Berechtigungsmodell (Kap 22.8) — NEU
Ownership-basiertes Sichtbarkeitsmodell:
| Stufe | Beschreibung | Sieht was? |
|---|---|---|
| Eigene | User sieht nur eigene Datensaetze (Owner) | Eigene Kontakte, Deals, Aktivitaeten |
| Team | User sieht alle Datensaetze der eigenen Abteilung | Kontakte aller Kollegen |
| Alle | User sieht alle Datensaetze des Tenants | Vollzugriff (lesend) |
Berechtigungen nach Rolle:
| Rolle | Sichtbarkeit | Erstellen | Bearbeiten | Loeschen |
|---|---|---|---|---|
| tenant_admin | Alle | Ja | Alle | Alle |
| team_lead | Team | Ja | Team + Eigene | Eigene |
| tenant_member | Konfigurierbar | Ja | Eigene + zugewiesene | Eigene |
| tenant_readonly | Konfigurierbar | Nein | Nein | Nein |
Backend-Aufgaben:
- Sichtbarkeitsfilter in allen List-Queries (Contacts, Deals, Activities)
- Tenant-Admin Einstellung: Default-Sichtbarkeit pro Rolle
- Per-User Override moeglich
Frontend-Aufgaben:
- Admin > CRM-Einstellungen > Berechtigungen: Sichtbarkeitsstufe konfigurieren
A.9 Reporting & Dashboards (Kap 22.9) — NEU
5 Reports/Dashboards sind definiert:
| Dashboard | Inhalt | Aktualisierung |
|---|---|---|
| Pipeline-Uebersicht | Deals pro Stage, Gesamtvolumen, gewichteter Forecast | Echtzeit |
| Aktivitaeten-Uebersicht | Offene Aufgaben, ueberfaellige Aktivitaeten, Volumen/Woche | Echtzeit |
| Kontaktwachstum | Neue Kontakte/Monat, aufgeschluesselt nach Quelle | Taeglich |
| Win/Loss-Analyse | Won vs Lost Deals, Lost-Gruende als Torte, Durchschnittliche Deal-Dauer | Taeglich |
| Mitarbeiter-Performance | Deals pro Mitarbeiter, Aktivitaeten-Anzahl, Response-Zeit | Taeglich |
- Alle Reports als CSV exportierbar
- Datumsbereiche: letzte 7 / 30 / 90 Tage, benutzerdefiniert
- Mitarbeiter-Performance nur fuer tenant_admin und team_lead sichtbar
Backend-Aufgaben:
- Reporting-Endpoints:
GET /crm/reports/pipeline,/reports/activities,/reports/contacts-growth,/reports/win-loss,/reports/performance - CSV-Export fuer alle Reports
Frontend-Aufgaben:
- CRM Dashboard-Seite mit den 5 Report-Widgets
- Datumbereich-Selektor
- CSV-Export Buttons
A.10 CRM Datenbankschema (Kap 22.10)
Vollstaendiges Schema laut Architekt:
-- Kernentitaeten
crm_contacts (id, type: PERSON|COMPANY, status, source, created_by, created_at, updated_at)
crm_persons (id, contact_id, first_name, last_name, job_title, department, birthday, notes)
crm_companies (id, contact_id, name, industry, website, vat_id, tax_id, size_class, notes)
crm_contact_emails (id, contact_id, email, type: WORK|PERSONAL|OTHER, is_primary)
crm_contact_phones (id, contact_id, phone, type: OFFICE|MOBILE|FAX, is_primary)
crm_contact_addresses (id, contact_id, type: MAIN|DELIVERY, street, zip, city, country)
-- Beziehungen
crm_person_company (person_contact_id, company_contact_id)
crm_contact_owners (contact_id, user_id, role: OWNER|MEMBER|WATCHER)
crm_contact_tags (contact_id, tag)
-- Pipeline & Deals
crm_pipelines (id, name, is_default, visibility)
crm_pipeline_stages (id, pipeline_id, name, color, position, probability, is_won_stage, is_lost_stage)
crm_deals (id, contact_id, pipeline_id, stage_id, title, value, currency,
probability_override, expected_close_date, status: OPEN|WON|LOST,
lost_reason, lost_reason_text, notes, created_by)
crm_deal_owners (deal_id, user_id, role: OWNER|MEMBER|WATCHER)
-- Aktivitaeten
crm_activities (id, type: CALL|MEETING|EMAIL|TASK|NOTE|FOLLOWUP,
contact_id nullable, deal_id nullable,
title, body, due_date, completed_at, assigned_to_user_id, created_by)
-- Custom Fields
crm_custom_field_defs (id, entity_type: PERSON|COMPANY|DEAL, name, label, field_type,
options jsonb, is_required, position)
crm_custom_field_values (id, field_def_id, entity_id, value_text, value_number,
value_date, value_boolean, value_json)
Hinweis: Das aktuelle Prisma-Schema im CRM-Service weicht teilweise ab (z.B. app_crm Schema-Prefix statt crm_ Tabellen-Prefix, flaches Company/Contact-Modell statt Contact+Person+Company Split). Der Architekt hat das Ziel-Schema definiert — Abgleich und Migration sind Backend-Aufgaben.
A.11 CRM Events (Kap 22.11)
| Event | Ausgeloest von | Empfaenger/Zweck |
|---|---|---|
crm.contact.created |
CRM-Service | Zukuenftige Module (Marketing etc.) |
crm.contact.updated |
CRM-Service | Zukuenftige Module |
crm.deal.stage_changed |
CRM-Service | Reporting, Automatisierungen |
crm.deal.won |
CRM-Service | Reporting, Modul-Integrationen |
crm.deal.lost |
CRM-Service | Reporting |
crm.activity.due_soon |
CRM-Service (Scheduler) | Benachrichtigungs-Service |
core.user.deactivated |
Core-Service | CRM prueft Owner-Reassignment |
Backend-Aufgabe:
- Redis Pub/Sub Events bei Kontakt/Deal/Activity-Aenderungen publishen
B) Office 365 Integration — CRM-relevante Teile (Kap 24)
Die OAuth-Verbindung (Kap 24.1) und Azure App-Registrierung (Kap 24.8) sind CORE-Aufgaben. Der CRM-Service nutzt die bestehende MS-Verbindung des Users fuer folgende Features:
B.1 E-Mail Tab im CRM-Kontakt (Kap 24.2) — Read-only
Pro CRM-Kontakt wird ein "E-Mails"-Tab angezeigt mit allen Outlook-Mails von/an die Kontakt-E-Mail-Adresse.
Graph API Abfrage:
GET /me/messages
?$filter=from/emailAddress/address eq '{kontakt_email}'
or toRecipients/any(...)
&$select=id,subject,from,toRecipients,receivedDateTime,bodyPreview,isRead
&$orderby=receivedDateTime desc
&$top=25
| Feature | Detail |
|---|---|
| Anzeige | Absender, Betreff, Datum, Vorschautext (max. 255 Zeichen) |
| Sortierung | Neueste zuerst, 25 pro Seite |
| Caching | Redis 5 Min (Key: user:{id}:mails:contact:{id}) |
| Kein Volltext | Nur bodyPreview, kein vollstaendiger E-Mail-Body |
| Kein Senden | Read-only |
Backend-Aufgaben:
GET /crm/contacts/:id/emailsEndpoint (Proxy zu Graph API)- MS Access Token aus Redis / Refresh via Core
- Redis-Caching (5 Min TTL)
Frontend-Aufgaben:
- "E-Mails" Tab in ContactDetailPage (nur wenn MS-Verbindung aktiv)
- Ausgegraut wenn keine MS-Verbindung, Hinweis "Microsoft 365 verbinden"
B.2 Kalender Tab im CRM-Kontakt (Kap 24.3) — Read-only
Outlook-Termine bei denen der CRM-Kontakt als Teilnehmer eingetragen ist.
Graph API Abfrage:
GET /me/calendarView
?startDateTime={heute}T00:00:00Z
&endDateTime={heute+90Tage}T23:59:59Z
&$filter=attendees/any(a: a/emailAddress/address eq '{kontakt_email}')
&$select=id,subject,start,end,location,attendees,bodyPreview,onlineMeetingUrl
| Feature | Detail |
|---|---|
| Anzeige | Kommende 90 Tage + vergangene 90 Tage |
| Felder | Titel, Datum/Uhrzeit, Ort, Online-Meeting-Link, Teilnehmer |
| Caching | Redis 5 Min |
| Kein Schreiben | Read-only |
Backend-Aufgaben:
GET /crm/contacts/:id/calendarEndpoint- Redis-Caching
Frontend-Aufgaben:
- "Kalender" Tab in ContactDetailPage
B.3 Aufgaben Sync — Bidirektional mit Microsoft To Do (Kap 24.4)
CRM-Aufgaben koennen optional nach Microsoft To Do synchronisiert werden.
Richtung INSIGHT -> To Do:
- Trigger: Aufgabe erstellt/geaendert im CRM
- Action:
POST /me/todo/lists/{defaultListId}/tasks(oder PATCH wenn ms_task_id bekannt) ms_task_idwird incrm_activitiesgespeichert
Richtung To Do -> INSIGHT:
- NestJS
@CronPolling alle 5 Min fuer User mit aktiver MS-Verbindung GET /me/todo/lists/{listId}/tasks?$filter=lastModifiedDateTime gt {letzter_sync}- Status-Aenderungen (erledigt/offen) werden uebernommen
- Redis-Lock pro User verhindert parallele Sync-Jobs
- Nur INSIGHT-erstellte Aufgaben werden synchronisiert (die eine ms_task_id haben)
DB-Aenderungen an crm_activities:
+ ms_task_id VARCHAR(255) -- To Do Aufgaben ID
+ ms_task_list_id VARCHAR(255) -- To Do Listen ID
+ ms_synced_at TIMESTAMPTZ -- Letzter erfolgreicher Sync
Backend-Aufgaben:
- ms_task_id, ms_task_list_id, ms_synced_at Felder in Activity-Model
- Graph API Integration: POST/PATCH Tasks
- @Cron Scheduler (alle 5 Min) fuer To Do -> INSIGHT Sync
- Redis-Lock pro User
- Konfliktbehandlung: INSIGHT gewinnt (Last Write Wins mit Timestamp)
B.4 Kontakte Export nach Outlook (Kap 24.5) — Manuell
Button "Nach Outlook exportieren" im CRM-Kontakt (Person).
Ablauf:
- User klickt "Nach Outlook exportieren"
- Pruefung: MS-Verbindung aktiv? -> Wenn nein: Hinweis
- Existiert Kontakt in Outlook (ms_contact_id)? -> PATCH (Update) / POST (Neu)
- ms_contact_id wird in crm_persons gespeichert
Feldzuordnung CRM -> Outlook:
| CRM Feld | Outlook Contacts (Graph API) |
|---|---|
| Vorname + Nachname | givenName + surname |
| Jobtitel | jobTitle |
| Unternehmen | companyName |
| Abteilung | department |
| E-Mails | emailAddresses[] |
| Telefone | businessPhones[] / mobilePhone |
| Adresse | businessAddress |
| LinkedIn-URL | businessHomePage |
| Notizen | personalNotes |
DB-Aenderung an crm_persons:
+ ms_contact_id VARCHAR(255) -- Outlook Kontakt ID
Backend-Aufgaben:
POST /crm/contacts/:id/export-to-outlookEndpoint- ms_contact_id Feld in Person-Model
- Bei 404 (Kontakt in Outlook geloescht): neuen Kontakt erstellen, ms_contact_id aktualisieren
Frontend-Aufgaben:
- "Nach Outlook exportieren" Button in Kontakt-Header (nur fuer Personen)
- Ausgegraut wenn keine MS-Verbindung
C) Zusammenfassung: Priorisierte Aufgabenliste fuer CRM-Entwickler
Prio 1 — Kurzfristig (bestehende Features erweitern):
- Fehlende Kontakt-/Unternehmens-Felder im Prisma-Schema ergaenzen (LinkedIn, Geburtsdatum, Quelle, USt-IdNr etc.)
- Lost-Grund (Enum + Freitext) bei Deals
- Contact/Deal-Owner m:n Modell (crm_contact_owners, crm_deal_owners)
- Redis Pub/Sub Events bei Entity-Aenderungen
Prio 2 — Mittelfristig (neue Features):
- Custom Fields (Definition + Wert-Speicherung + Admin-UI)
- Firmendaten-Anreicherung (Unternehmensregister.de + North Data)
- Kontakt-Import (CSV, Excel, vCard) mit Spalten-Mapper + Duplikat-Erkennung
- Berechtigungsmodell (Eigene/Team/Alle Sichtbarkeit)
- Kanban-Board fuer Deals (Drag & Drop)
- Forecast-Ansicht
Prio 3 — Spaeter (abhaengig von Core-Vorarbeiten):
- Office 365: E-Mail Tab (benoetigt OAuth-Infrastruktur im Core)
- Office 365: Kalender Tab
- Office 365: Aufgaben Sync mit Microsoft To Do
- Office 365: Kontakte Export nach Outlook
- Visitenkarten-Scan (Anthropic Vision API)
- CRM Reporting & Dashboards (5 Reports + CSV-Export)
Bitte neue Eintraege unten anfuegen. Format: ## YYYY-MM-DD | Absender: Betreff