mirror of
http://172.20.10.11:3000/gitadmin/INSIGHT-MVP.git
synced 2026-06-25 09:06:40 +02:00
626 lines
26 KiB
Markdown
626 lines
26 KiB
Markdown
# 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):**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": [...],
|
|
"pagination": { "page": 1, "pageSize": 25, "total": 42, "totalPages": 2 },
|
|
"meta": { "timestamp": "..." }
|
|
}
|
|
```
|
|
|
|
**Einzelobjekt:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": { ... },
|
|
"meta": { "timestamp": "..." }
|
|
}
|
|
```
|
|
|
|
**Fehler:**
|
|
```json
|
|
{
|
|
"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
|
|
|
|
- [x] **Traefik HTTPS-Router**: `crm-secure` Router angelegt mit `entrypoints=websecure`, `tls=true`, Priority 100. Deployed in Commit `c9e2c4a`.
|
|
|
|
- [x] **Pipeline-Stages bearbeiten**: Neuer Endpoint `PATCH /crm/pipelines/:id/stages/:stageId` hinzugefuegt. Akzeptiert:
|
|
```json
|
|
{
|
|
"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`:
|
|
|
|
```json
|
|
{
|
|
"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
|
|
|
|
- [x] ~~Pipeline-Stages bearbeiten~~ — Frontend nutzt den neuen PATCH-Endpoint
|
|
- [x] ~~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)
|
|
|
|
```typescript
|
|
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):
|
|
```json
|
|
{
|
|
"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:
|
|
```json
|
|
{
|
|
"company": { "id": "...", "name": "Xinion GmbH", "industry": "Enterprise Software" }
|
|
}
|
|
```
|
|
|
|
**Contact-Detail** liefert:
|
|
```json
|
|
{
|
|
"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:
|
|
```json
|
|
{
|
|
"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
|
|
|
|
```typescript
|
|
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:
|
|
```json
|
|
{
|
|
"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[]`:
|
|
```json
|
|
{
|
|
"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
|
|
|
|
---
|
|
|
|
*Bitte neue Eintraege unten anfuegen. Format: `## YYYY-MM-DD | Absender: Betreff`*
|