Kundenakte: Tab "Geworben / angeworben" (Kundenempfehlungen)

Neuer Tab vor "Datenschutz", nur Mitarbeiter/Admin (nicht Portal),
ohne Consent-Pflicht. Zwei Abschnitte:
1. "<Kunde> wurde an Board geholt durch:" – max. 1 Werber
   (DB-Unique auf recruitedId).
2. "<Kunde> hat folgende Kunden an Board geholt:" – beliebig viele.

Jede Zeile: Kunde per Lupe-Such-Modal (breite Suche über Name/
Kundennr./Firma/E-Mail/Telefon) + Beziehungs-Dropdown. Löschen +
Externtab-Link zur Kundenakte pro Zeile.

Bidirektional aus EINEM Datensatz: "A geworben durch B" erscheint
automatisch bei B unter "hat geworben"; von beiden Akten
hinzufügbar/löschbar.

Backend: neues Model CustomerReferral (recruiter/recruited FKs,
recruitedId @unique, relationship) + Migration. Beziehungs-Whitelist
serverseitig; Self-Werbung + Doppel-Werber (409) abgefangen.
Portal-Token wird explizit geblockt (Defense-in-Depth, nicht nur
UI-Ausblendung). CREATE/DELETE auditiert.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-07-27 17:50:15 +02:00
co-authored by Claude Opus 4.7
parent 6b08762ff9
commit cda6d2814e
10 changed files with 744 additions and 1 deletions
+28
View File
@@ -25,6 +25,34 @@ export interface CustomerSummary {
portalEnabled?: boolean;
}
// Werbung ("Geworben / angeworben")
export const REFERRAL_RELATIONSHIPS = [
'Ehepartner/in',
'Lebenspartner/in',
'Kind',
'Enkelkind',
'Onkel/Tante',
'Neffe',
'Freund/Kumpel',
'Eltern',
'Bruder/Schwester',
'Bekannte',
'sonstige',
] as const;
export interface ReferralEntry {
id: number;
relationship: string;
customer: CustomerSummary;
}
export interface CustomerReferrals {
/** Von wem dieser Kunde geworben wurde (max. 1). */
recruitedBy: ReferralEntry | null;
/** Welche Kunden dieser Kunde geworben hat (beliebig viele). */
recruited: ReferralEntry[];
}
export interface RepresentativeAuthorization {
id: number;
customerId: number;