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:
@@ -213,10 +213,34 @@ model Customer {
|
||||
// Persönliche Anrede-Präferenzen aller Mitarbeiter zu diesem Kunden.
|
||||
userSalutations UserCustomerSalutation[]
|
||||
|
||||
// Werbung: wer hat wen an Board geholt.
|
||||
// referralAsRecruited = dieser Kunde WURDE geworben (max. 1 Werber).
|
||||
// referralsAsRecruiter = Kunden, die DIESER Kunde geworben hat (beliebig viele).
|
||||
referralAsRecruited CustomerReferral? @relation("ReferralRecruited")
|
||||
referralsAsRecruiter CustomerReferral[] @relation("ReferralRecruiter")
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
// Werbe-Beziehung zwischen zwei Kunden ("Geworben / angeworben").
|
||||
// Ein Datensatz = ein Werber (recruiter) hat einen Kunden (recruited) an
|
||||
// Board geholt, plus die Beziehungsart. `recruitedId` ist @unique, damit ein
|
||||
// Kunde nur von EINER Person geworben sein kann (Abschnitt 1 in der UI).
|
||||
// Ein Werber kann beliebig viele Kunden werben (Abschnitt 2).
|
||||
model CustomerReferral {
|
||||
id Int @id @default(autoincrement())
|
||||
recruiterId Int
|
||||
recruiter Customer @relation("ReferralRecruiter", fields: [recruiterId], references: [id], onDelete: Cascade)
|
||||
recruitedId Int @unique
|
||||
recruited Customer @relation("ReferralRecruited", fields: [recruitedId], references: [id], onDelete: Cascade)
|
||||
relationship String // Ehepartner/in, Kind, Freund/Kumpel, …
|
||||
createdAt DateTime @default(now())
|
||||
createdBy String?
|
||||
|
||||
@@index([recruiterId])
|
||||
}
|
||||
|
||||
// ==================== USER-CUSTOMER SALUTATION ====================
|
||||
// Per-User-Präferenz, ob der eingeloggte Mitarbeiter mit dem jeweiligen
|
||||
// Kunden per Du oder Sie ist. Fehlender Eintrag → fällt auf den
|
||||
|
||||
Reference in New Issue
Block a user