feat: Reseller-Rolle + SUPER_ADMIN-Mandantenübersicht
Mandantenfähigkeit ausgebaut: - Neue Rolle RESELLER (company_id NULL); companies.reseller_id + is_active - RLS-Erweiterung (Migration 0034): companies/users zusätzlich auf app.reseller_id gefenced → Reseller sieht/verwaltet DB-seitig nur eigene Firmen, keine personenbezogenen Zeit-/Abwesenheitsdaten (DSGVO: nur Verwaltung) - get_current_user setzt app.reseller_id + Bypass aus für RESELLER - tenant_service: Firma + Erst-Admin (Einladung), Übersicht mit Kennzahlen - Router /reseller/* (Self-Service) und /admin/* (SUPER_ADMIN: Mandanten + Reseller) - Login-Sperre bei deaktiviertem Mandanten - Frontend: TenantsPage (/admin/tenants), eigene ResellerCompaniesPage (/reseller), rollenbasierte Login-Weiterleitung, Nav "Mandanten" für SUPER_ADMIN - 4 neue Tests inkl. Cross-Reseller-RLS-Isolation; 172/172 grün Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
"""Schemas für Reseller-Selbstverwaltung und SUPER_ADMIN-Mandantenübersicht.
|
||||
|
||||
Wichtig (DSGVO): Reseller sehen ausschließlich Firmen-Stammdaten und Kennzahlen
|
||||
(z.B. Nutzeranzahl) – KEINE personenbezogenen Zeit-/Abwesenheitsdaten.
|
||||
"""
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from pydantic import BaseModel, EmailStr, Field
|
||||
|
||||
|
||||
class TenantOut(BaseModel):
|
||||
"""Eine Firma als Mandant – für Reseller- und SUPER_ADMIN-Übersicht."""
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
id: uuid.UUID
|
||||
name: str
|
||||
slug: str
|
||||
plan: str
|
||||
is_active: bool
|
||||
country: str
|
||||
reseller_id: uuid.UUID | None = None
|
||||
reseller_name: str | None = None
|
||||
user_count: int = 0
|
||||
active_user_count: int = 0
|
||||
admin_email: str | None = None
|
||||
created_at: datetime | None = None
|
||||
|
||||
|
||||
class TenantCreate(BaseModel):
|
||||
"""Neue Firma anlegen (durch Reseller oder SUPER_ADMIN).
|
||||
|
||||
Es wird automatisch ein erster COMPANY_ADMIN per Einladung angelegt.
|
||||
"""
|
||||
name: str = Field(min_length=2, max_length=255)
|
||||
country: str = Field(default="DE", max_length=10)
|
||||
plan: str = Field(default="trial", max_length=50)
|
||||
admin_email: EmailStr
|
||||
admin_first_name: str = Field(min_length=1, max_length=100)
|
||||
admin_last_name: str = Field(min_length=1, max_length=100)
|
||||
|
||||
|
||||
class TenantUpdate(BaseModel):
|
||||
name: str | None = Field(None, min_length=2, max_length=255)
|
||||
plan: str | None = Field(None, max_length=50)
|
||||
is_active: bool | None = None
|
||||
|
||||
|
||||
class TenantAssignReseller(BaseModel):
|
||||
"""SUPER_ADMIN: Firma einem Reseller zuordnen (oder lösen mit null)."""
|
||||
reseller_id: uuid.UUID | None = None
|
||||
|
||||
|
||||
class ResellerOut(BaseModel):
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
id: uuid.UUID
|
||||
email: str
|
||||
first_name: str
|
||||
last_name: str
|
||||
is_active: bool
|
||||
created_at: datetime | None = None
|
||||
company_count: int = 0
|
||||
|
||||
|
||||
class ResellerCreate(BaseModel):
|
||||
email: EmailStr
|
||||
first_name: str = Field(min_length=1, max_length=100)
|
||||
last_name: str = Field(min_length=1, max_length=100)
|
||||
Reference in New Issue
Block a user