diff --git a/backend/src/services/customer.service.ts b/backend/src/services/customer.service.ts index 45ec1d82..07ebf89c 100644 --- a/backend/src/services/customer.service.ts +++ b/backend/src/services/customer.service.ts @@ -900,7 +900,10 @@ export async function getCustomerRepresentatives(customerId: number) { } export async function getRepresentedByList(customerId: number) { - // Holt alle Kunden, die den angegebenen Kunden vertreten können + // Holt alle Kunden, die den angegebenen Kunden vertreten können. + // `portalEnabled` mit rausgeben, damit die UI ein Badge zeigen kann, + // falls der Vertreter noch keinen Portal-Zugang hat (Vertretung ist + // dann formal eingetragen, greift aber erst mit aktivem Portal). return prisma.customerRepresentative.findMany({ where: { customerId: customerId, isActive: true }, include: { @@ -912,6 +915,7 @@ export async function getRepresentedByList(customerId: number) { lastName: true, companyName: true, type: true, + portalEnabled: true, }, }, }, @@ -940,10 +944,10 @@ export async function addRepresentative( throw new Error('Ein Kunde kann sich nicht selbst vertreten'); } - // Prüfen ob der Vertreter ein Portal-Konto hat - if (!representative.portalEnabled) { - throw new Error('Der Vertreter-Kunde muss ein aktiviertes Portal-Konto haben'); - } + // Portal-Zwang entfernt (2026-07-03): der Vertreter darf ohne Portal + // eingetragen werden; die Beziehung ist erst dann effektiv aktiv, + // wenn der Vertreter einen Portal-Zugang bekommt (die Frontend-UI + // zeigt in dem Fall ein Warn-Badge). return prisma.customerRepresentative.upsert({ where: { @@ -985,12 +989,15 @@ export async function removeRepresentative(customerId: number, representativeId: } export async function searchCustomersForRepresentative(search: string, excludeCustomerId: number) { - // Sucht Kunden, die als Vertreter hinzugefügt werden können - // Nur Kunden mit aktiviertem Portal + // Sucht Kunden, die als Vertreter hinzugefügt werden können. + // 2026-07-03: Portal-Filter entfernt – der Admin soll auch Kunden ohne + // Portal-Zugang als Vertreter markieren können; das Portal wird dann + // typischerweise nachträglich aktiviert. Der Zugriff wird ohnehin erst + // aktiv, sobald das Portal für den Vertreter angeschaltet ist – die UI + // zeigt jetzt ein Warn-Badge, wenn der Vertreter noch kein Portal hat. return prisma.customer.findMany({ where: { id: { not: excludeCustomerId }, - portalEnabled: true, OR: [ { firstName: { contains: search } }, { lastName: { contains: search } }, @@ -1005,6 +1012,7 @@ export async function searchCustomersForRepresentative(search: string, excludeCu lastName: true, companyName: true, type: true, + portalEnabled: true, }, take: 10, }); diff --git a/frontend/src/pages/customers/CustomerDetail.tsx b/frontend/src/pages/customers/CustomerDetail.tsx index 43877ee5..a6577e02 100644 --- a/frontend/src/pages/customers/CustomerDetail.tsx +++ b/frontend/src/pages/customers/CustomerDetail.tsx @@ -2327,7 +2327,8 @@ function PortalTab({

- Nur Kunden mit aktiviertem Portal können als Vertreter hinzugefügt werden. + Vertreter ohne Portal-Zugang können bereits hinterlegt werden – der Zugriff + wird erst wirksam, sobald das Portal aktiviert ist.

{/* Suchergebnisse */} @@ -2336,8 +2337,13 @@ function PortalTab({ {searchResults.map((customer) => (
-

+

{customer.companyName || `${customer.firstName} ${customer.lastName}`} + {!customer.portalEnabled && ( + + Portal inaktiv + + )}

{customer.customerNumber}

@@ -2362,9 +2368,14 @@ function PortalTab({ {representatives.map((rep: CustomerRepresentative) => (
-

+

{rep.representative?.companyName || `${rep.representative?.firstName} ${rep.representative?.lastName}`} + {rep.representative && rep.representative.portalEnabled === false && ( + + Portal inaktiv + + )}

{rep.representative?.customerNumber}

diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts index 43a839ff..1084105f 100644 --- a/frontend/src/types/index.ts +++ b/frontend/src/types/index.ts @@ -22,6 +22,7 @@ export interface CustomerSummary { lastName: string; companyName?: string; type: 'PRIVATE' | 'BUSINESS'; + portalEnabled?: boolean; } export interface RepresentativeAuthorization {