Commit graph

32 commits

Author SHA1 Message Date
Thomas Reitz
3f919340b5 feat: Stammdaten, CRM Reporting, Hilfesystem (hohe Priorität)
Stammdaten (Kapitel 14):
- 5 neue Prisma-Modelle: Department, Location, CostCenter, JobTitle, SkillCategory
- MasterDataModule (Core Service): vollständiges CRUD + öffentliche Dropdown-Endpoints
- Admin-UI /admin/master-data mit 5 Tabs, Inline-Edit, Farbwahl (Skill-Kategorien)

CRM Reporting (Kapitel 22.9):
- recharts ^2.12.0 installiert
- Deals: GET /deals/stats (Win/Loss-Rate, Umsatz, Trend, Verlustgründe)
- Aktivitäten: GET /activities/stats (nach Typ, Completion-Rate, offene Tasks)
- Reports-Seite /crm/reports: LineChart, PieChart, BarChart mit Zeitraum-Filter

Hilfesystem (Kapitel 16):
- @anthropic-ai/sdk installiert; ANTHROPIC_API_KEY optional in .env
- HelpModule (Core Service): POST /help/chat via Claude Haiku
- HelpTooltip-Komponente: Hover-Tooltip für Formularfelder
- HelpPanel: seitlicher Drawer mit Seitenkontext + KI-Chat
- -Button im Topbar (AppLayout), pageKey aus Route abgeleitet

Migration erforderlich: prisma migrate deploy (core-service)
Deployment: core rebuild, crm rebuild, frontend rebuild

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 10:39:30 +01:00
Thomas Reitz
3adca2de65 fix: VisibilityLevel Enum-Referenzen durch String-Literale ersetzen
Prisma Enums existieren als Runtime-Objekt nur nach prisma generate,
aber TypeScript im watch-mode erwartet importierbare Werte. Verwende
stattdessen direkte String-Vergleiche ('ALL', 'TEAM', 'OWN').

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 22:28:56 +01:00
Thomas Reitz
b484f4380f fix: VisibilityLevel als lokalen String-Type statt Prisma-Enum verwenden
Prisma-Enums sind zur Laufzeit nicht als Objekt verfuegbar wenn ts-node
den Import vor der Client-Generierung auflöst. Ersetzt durch eigenen
Type-Export aus build-visibility-filter.ts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 22:24:18 +01:00
Thomas Reitz
de4af77c5c feat: CRM Berechtigungsmodell — konfigurierbares Sichtbarkeitsmodell (OWN/TEAM/ALL)
Implementiert pro-Entity Sichtbarkeitssteuerung für Companies, Contacts, Deals
und Activities mit Rollen-basierter Zugriffskontrolle (ADMIN sieht alles,
TEAM_LEAD mindestens Team-Sicht, READONLY nur Lesezugriff).

- JWT Payload um tenantRole + department erweitert (Core + CRM)
- Team-Members-Endpoint im Core Service (GET /users/team-members)
- VisibilityModule mit Redis-Cache (CRM Service)
- ReadonlyGuard als globaler Guard (CRM Service)
- buildVisibilityFilter Utility für Prisma WHERE-Filterung
- Admin-Einstellungsseite /admin/crm-settings (Frontend)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 22:20:53 +01:00
Thomas Reitz
6c51eb5e83 feat(crm): Kontakt-Detailseite – Breite, Outlook Daten, Outlook-Push
- max-width 960px auf Kontakt-Detailseite
- M365-Sektion umbenannt zu "Outlook Daten", default eingeklappt
- Aufgaben-Tab entfernt (nur noch E-Mails + Kalender)
- "In Outlook speichern"-Button: pusht/aktualisiert Kontakt in Outlook-Kontakte via MS Graph POST/PATCH /me/contacts
- Kontaktdaten: Typ, Status immer sichtbar, Bundesland (state) in Adresse
- Backend: GraphService exportiert, pushContactToOutlook-Methode, POST /crm/contacts/:id/push-to-outlook

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 19:57:07 +01:00
Thomas Reitz
4c739945f0 feat(crm): Projektanfrage-Vorgangstyp – isProjectType + ProjectRequestDetails
Backend:
- DealType.isProjectType Boolean-Flag (Admin-konfigurierbar in CRM Settings)
- Neue 1:1-Tabelle project_request_details (ON DELETE CASCADE):
  notes, workload, startDate, duration, onsitePercent, rateRemote, rateOnsite
- Migration 20260313_project_request
- ProjectRequestDto mit Validierung (0-100% fuer Auslastung/Vorort-Anteil)
- Deals-Service: nested create + upsert fuer projectRequest

Frontend:
- DealFormModal: Vorgangsart-Dropdown an Anfang verschoben;
  Projektanfrage-Sektion erscheint conditional bei isProjectType=true
  (Beschreibung, Auslastung/Start, Laufzeit/Vorort-Anteil, Stundensaetze)
- CrmSettingsPage: DealTypesConfig mit Projektanfrage-Checkbox + Tabellenspalte
- types.ts: ProjectRequestDetails, CreateProjectRequestPayload, Deal.projectRequest

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 17:17:40 +01:00
Thomas Reitz
6bfce4af97 feat(crm): Vorgangsart (DealType) konfigurierbares Dropdown + Pipeline-Leerstate
- crm-service: Neues DealType-Model (deal_types Tabelle) mit name, color, sortOrder
  und Relation zu Deal.dealTypeId; Migration 20260313_deal_type
- crm-service: Vollstaendiger CRUD REST-Endpoint /crm/deal-types (TenantGuard)
- crm-service: CreateDealDto um optionales dealTypeId erweitert
- frontend: DealType Interface, API (dealTypesApi), Hooks (useDealTypes/...)
- frontend: CrmSettingsPage > Weitere Einstellungen > DealTypesConfig mit Farbpicker
- frontend: DealFormModal: Vorgangsart-Dropdown + Hinweis bei leerer Pipeline-Liste

Deployment: prisma migrate deploy && prisma generate im crm-service ausfuehren.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 16:15:02 +01:00
Thomas Reitz
2348602fb0 feat: Erweiterte Profilfelder (analog O365) + Profilbild-Sync aus Microsoft 365
Neue Felder im Benutzerprofil (analog Microsoft 365 /me):
- Stellenbezeichnung (jobTitle), Abteilung (department)
- Firma (companyName), Standort (officeLocation)

Changes:
- Core: Prisma-Migration + neue Felder in User-Model, UpdateUserDto,
  findById/update/updateProfile
- CRM: M365UserProfile-Interface + getM365Profile um neue Felder erweitert;
  neue Methode getM365Photo() lädt 96x96 JPEG als Base64 Data-URL;
  neuer Endpoint GET /crm/office365/photo
- Frontend: AuthContext User-Interface, M365UserProfile-Typ, office365Api.getM365Photo()
  ProfilePage: Neues Formular-Fieldset "Organisation" mit 4 Feldern;
  manueller Sync-Button übernimmt auch Profilbild (immer überschreiben);
  useO365ProfileSync: Auto-Sync lädt Foto nur wenn noch kein INSIGHT-Avatar

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 13:08:56 +01:00
Thomas Reitz
347a6ca418 feat: Profil mit Microsoft 365 Daten anreichern
- GraphService: getM365Profile() lädt givenName, surname, mobilePhone,
  businessPhones, city, streetAddress, postalCode, jobTitle via /me
- Office365Controller: GET /crm/office365/profile Endpunkt
- Frontend types: M365UserProfile Interface
- Frontend api: office365Api.getM365Profile()
- ProfilePage: O365 uebernehmen Button (nur wenn M365 verbunden)
  fuellt leere Felder: Vorname, Nachname, Telefon, Mobil, Ort, Strasse, PLZ
  Bestehende Werte werden NICHT ueberschrieben; Feedback zeigt welche
  Felder uebernommen wurden; User muss Speichern klicken

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 12:38:43 +01:00
Thomas Reitz
3b15c8ab9b feat: Aufgaben-Tab im Dashboard (O365 + CRM bidirektional)
- GraphService: graphPost/graphPatch Helfer; getAllTasksFlat (inkl.
  Body für CRM-Sync-Marker), createM365Task, completeM365Task
- Office365Controller: GET tasks/flat, POST tasks, PATCH tasks/:listId/:taskId/complete
- ActivitiesService/Controller: GET /crm/activities/open-tasks
  (TASK + FOLLOWUP, nicht erledigt)
- Frontend types: M365TaskFlat + CrmOpenTask Interfaces
- Frontend api/hooks: getTasksFlat, createTask, completeTask,
  getOpenTasks; neue Hooks useOffice365TasksFlat, useCrmOpenTasks,
  usePushTaskToO365, useCompleteO365Task, useCompleteCrmTask
- DashboardTasksTab: vereinheitlichte Aufgabenliste mit Farbcodierung
  (O365 blau, CRM orange, Synced grün), Push-Button, Erledigen-Button
- Bidirektionaler Sync via [INSIGHT_CRM:{activityId}] Marker im O365
  Task Body; Erledigen eines Synced-Tasks aktualisiert beide Systeme
- DashboardPage: Tasks-Tab auf DashboardTasksTab umgestellt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 12:01:04 +01:00
Thomas Reitz
76e8dff577 feat(dashboard): Kalender-Tab mit Monats-/Wochenansicht und Tages-Agenda
- Graph API: getCalendarEventsForRange() für beliebigen Datumsbereich,
  GET /crm/office365/calendar/range?startDate=&endDate= Endpoint
  (vor bestehenden calendar-Route definiert um Routing-Konflikt zu vermeiden)
- Graph API: wellKnownName aus mailFolders $select entfernt (400-Fehler auf
  Exchange-Tenants die das OData-Property nicht unterstützen)
- Frontend: DashboardCalendarTab mit MonthView (6×7 Grid), WeekView (7 Spalten)
  und DayAgenda (rechts 1/3), Navigation vor/zurück + Heute-Button,
  deterministisches Event-Coloring, Klick öffnet Termin in Outlook Online
- Frontend: DashboardEmailTab Ordner-Sortierung auf Display-Name-Basis
  (wellKnownName optional, isInboxFolder() erkennt Posteingang/Inbox)
- Frontend: M365MailFolder.wellKnownName als optional markiert

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 10:54:32 +01:00
Thomas Reitz
01dc8bb41c feat(dashboard): E-Mail Tab mit Outlook-Postfach, Ordner-Navigation und Aktivitäten-Speicherung
- GraphService: getMailFolders() + getMailsByFolder(folderId, days) Methoden
- Office365Controller: GET /crm/office365/folders und /folders/:id/messages?days=X Endpoints
- ContactsController/Service: GET /crm/contacts/lookup?email=xxx für CRM-Kontakt-Abgleich
- Frontend types: M365MailFolder + CrmContactLookup Interfaces
- Frontend API: office365Api.getMailFolders/getMailsInFolder + contactsApi.lookupByEmail
- Frontend Hooks: useOffice365MailFolders, useOffice365MailsInFolder, useContactByEmail
- DashboardEmailTab: Ordner-Sidebar, Zeitfilter (1/7/14 Tage/alle), E-Mail-Liste
  mit Outlook-Link, CRM-Badge bei bekannten Absendern, Aktivitäten-Modal mit Kommentar

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 10:16:05 +01:00
Thomas Reitz
ad9c48bcb6 feat(crm): Office365-Übersichtsseite + Graph API Bugfixes
- Neuer Office365-Menüpunkt mit 4 Tabs (E-Mails, Kalender, Kontakte, Aufgaben)
- CRM-Service: Office365Controller mit globalen Graph-Endpoints
- Fix: $search + $orderby Kombination in Graph API nicht erlaubt
- M365Contact Interface + attendees/hasAttachments Typen ergänzt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 06:59:52 +01:00
Thomas Reitz
47b1938605 feat(crm): Microsoft 365 Graph-API Proxy (Phase 3.2)
- Neues GraphModule mit GraphService + GraphController
- GET /crm/contacts/:id/emails    → MS Graph Emails (Search nach Kontakt-E-Mail)
- GET /crm/contacts/:id/calendar  → Kalender-Termine (naechste 90 Tage)
- GET /crm/contacts/:id/tasks     → Microsoft To Do Listen + Aufgaben
- GraphService: JWT an Core-Service weiterleiten, M365-Token holen, Graph aufrufen
- Redis-Cache 5 Minuten fuer alle Graph-Responses
- CORE_SERVICE_URL env var + docker-compose.crm.yml Eintrag

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 22:40:31 +01:00
Thomas Reitz
63cb05d4d8 feat(crm): Phase 2.2-2.4 backend + contract files — vollständige CRM-Service Implementierung
- Phase 2.3 Forecast: probability-Feld in PipelineStage, GET /crm/deals/forecast Endpoint
- Phase 2.2 Import: ImportModule mit preview/execute/history Endpoints (CSV, XLSX, vCard)
- Phase 2.4 Enrichment: EnrichmentModule mit /enrich + /settings/integrations/north-data
- Contracts: ContractsModule mit CRUD + File-Upload Endpoints (Multer, max 25MB)
- Migrations: 20260312_contract_files, 20260312_phase23_forecast
- docker-compose.crm.yml: uploads Volume für Vertragsdokumente

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 22:06:58 +01:00
Thomas Reitz
aaedf68085 feat(crm): Phase 2.1 Custom Fields — backend + frontend integration
Backend (CRM expert): Custom field definitions CRUD, bulk value upsert,
7 endpoints, Prisma schema with CustomFieldDef + CustomFieldValue tables.

Frontend: Types, API, hooks, admin settings page with field management,
CustomFieldsDisplay for detail pages, CustomFieldsForm for edit modals.
Also fix Vite allowedHosts for insight.xinion.lan.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 18:22:57 +01:00
Thomas Reitz
48df3c3144 feat(crm): Phase 1 backend schema expansion + frontend integration
Backend (CRM-Expert Phase 1):
- New enums: ContactSource, EntityStatus, CompanySize, OwnerRole,
  LostReason, EmailType, PhoneType
- Contact: add linkedinUrl, birthday, source, department, status
- Company: add vatId, taxId, tradeRegisterNumber, registerCourt,
  companySize, deliveryAddress, dataEnrichedAt/Source, status
- Deal: add lostReason + lostReasonText (required when status=LOST)
- Multi-value emails/phones tables (contact_emails, contact_phones)
- Owner m:n model (contact_owners, company_owners, deal_owners)
- Redis Pub/Sub CRM events (crm.contact.created, crm.deal.won, etc.)
- Activity due_soon scheduler (cron every 15 min)
- SQL migration with data migration for existing records

Frontend integration:
- types.ts: all new enums, interfaces, label maps
- api.ts: owner CRUD endpoints (add/remove for contacts/companies/deals)
- hooks.ts: 6 new owner mutation hooks
- ContactFormModal: LinkedIn, birthday, source, department, status fields
- ContactDetailPage: display new fields (LinkedIn, department, birthday,
  source, status badge)
- CompanyDetailPage: display vatId, taxId, trade register, company size,
  delivery address, data enrichment info
- DealFormModal: lost reason dropdown + text (shown when status=LOST)
- DealDetailPage: display lost reason with label
- CompaniesPage: EntityStatus-aware status dots (ACTIVE/INACTIVE/BLOCKED)
- ActivityType: add FOLLOWUP to all label maps

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 15:56:41 +01:00
Thomas Reitz
a85634a906 feat: add trade event (Messe-Timer) feature with admin CRUD and dashboard tiles
Backend: TradeEvent Prisma model, NestJS CRUD module with date validation
and tenant isolation. Frontend: Admin Events page with create/edit/delete
modals, dashboard countdown tiles showing upcoming/ongoing/ended events
with progress bars, and useEventCountdown hook for live timer updates.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 13:33:19 +01:00
Thomas Reitz
72fd57740b feat(crm): expandable contact sub-rows in companies table
Companies with linked contacts now show an expand arrow. Clicking it
lazy-loads and displays contacts as indented sub-rows with name,
position, email, phone, type badge and status. Backend gains companyId
filter on GET /contacts for efficient per-company querying.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 20:03:26 +01:00
Thomas Reitz
068a7b81d5 fix(crm): fix voucher ID mapping — Lexware API returns 'id' not 'voucherId'
The voucherlist endpoint returns items with 'id' field, but our interface
defined it as 'voucherId', causing undefined IDs when fetching voucher details.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 18:37:23 +01:00
Thomas Reitz
4924875e92 fix(crm): add mandatory voucherStatus=any to Lexware voucherlist API call
The Lexware Office API requires voucherStatus as a mandatory parameter
for the /v1/voucherlist endpoint. Without it, the API returns 400 Bad
Request. Using 'any' fetches vouchers regardless of status.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 12:00:29 +01:00
Thomas Reitz
b4d1f33862 chore(crm): log Lexware API error response body for debugging
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 11:57:52 +01:00
Thomas Reitz
ba4eec951a fix(crm): fix Lexware import 500 — tenantId validation in TenantGuard and service
- TenantGuard: remove PLATFORM_ADMIN bypass, require tenantId for all users
- lexware-contacts.service: add defensive tenantId check in importAsCompany
  and importAsContact with clear BadRequestException message

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 10:34:46 +01:00
Thomas Reitz
0ed1e77844 feat(crm): add company detail overhaul with industries, account types, relationship types
- Backend: new CRUD services/controllers for Industries, AccountTypes,
  RelationshipTypes, CompanyRelationships with Prisma schema migration
- Frontend: new hooks, API functions, and types for all config entities
- CompanyDetailPage redesign with ActivityFeed, RelationshipsCard
- CompanyFormModal extended with industry, account type, owner fields
- Activities service now supports companyId filter + includeContacts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 09:21:57 +01:00
Thomas Reitz
9d496d2e53 feat(crm): integrate Lexware Office for vouchers and contact sync
Adds complete Lexware Office integration to CRM service:
- Rate-limited HTTP client (Token Bucket, 2 req/s)
- Bidirectional contact sync (manual import + ERP-push)
- Voucher caching (quotes, orders, invoices, credit notes)
- Deal-voucher linking (m:n join table with audit)
- Cron jobs: voucher refresh (4h), ERP push (30min)
- Distributed locks via Redis for job deduplication
- Health check extended with Lexware status
- Prisma schema: LexwareVoucher, DealVoucher, VoucherType enum
- Companies/Contacts/Deals services extended with Lexware data

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 20:28:41 +01:00
Thomas Reitz
a13cca054b docs(crm): document Company entity for frontend developer
Update INSIGHT-CRM.md with complete Company API documentation,
Contact/Deal changes (companyId, position), and frontend integration
suggestions. Update Summarize.md with current test results.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 19:47:39 +01:00
Thomas Reitz
56a9ed9647 feat(crm): add Company entity + rename Deals to Vorgaenge
- New Company model with full CRUD under /api/v1/crm/companies
- Contact now has companyId relation + position field
- Deal now has companyId relation
- Company detail includes contacts (top 20) and deals (top 10)
- All endpoints include company in responses
- Swagger tags renamed: Deals -> Vorgaenge (Deals)
- Error messages use "Vorgang" instead of "Deal"

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 19:30:34 +01:00
Thomas Reitz
c9e2c4a44c feat(crm): add PATCH endpoint for pipeline stages + HTTPS router
- New PATCH /pipelines/:id/stages/:stageId endpoint to update
  stage name, color, and sortOrder
- Added HTTPS (websecure) Traefik router for CRM service
- Both requested by frontend developer via INSIGHT-CRM.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 19:22:33 +01:00
Thomas Reitz
43877bbb4a docs(crm): update Summarize.md with deployment status and test results
All CRM endpoints tested successfully on insight-dev-01.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 18:08:00 +01:00
Thomas Reitz
b3eb533fa7 chore(crm): add package-lock.json for Docker build
npm ci requires package-lock.json to ensure reproducible builds.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 16:20:35 +01:00
Thomas Reitz
094db465cb feat(crm): add standalone dev environment and test token generator
- docker-compose.crm-dev.yml: isolierte Testumgebung (PostgreSQL + Redis + CRM)
- scripts/generate-token.ts: generiert Test-JWTs fuer curl-basiertes API-Testing
- scripts/init-schema.sql: erstellt app_crm Schema beim DB-Start
- keys/: Test-RSA-Schluessel (nur fuer lokale Entwicklung!)
- Fix: multiSchema previewFeature entfernt (deprecated)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 16:03:59 +01:00
Thomas Reitz
8783d01fc0 feat(crm): scaffold CRM service with full CRUD modules
Eigenstaendiger NestJS-Service unter packages/crm-service/ mit:
- Prisma Schema (app_crm): Contact, Activity, Pipeline, PipelineStage, Deal
- JWT RS256 Auth mit shared Public Key und Token-Revocation
- Multi-Tenancy: TenantGuard + tenantId-Filter auf allen Queries
- CRUD-Module: Contacts, Activities, Pipelines, Deals
- Docker-Integration: docker-compose.crm.yml (Port 3100, Traefik-Route /api/v1/crm)
- Health-Check, Swagger, GlobalExceptionFilter, Pagination

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 15:54:13 +01:00