Kunden verwalten
[admin] [billing]
Kundenverwaltung im Admin: Einladen, Freischalten, Pakete zuweisen, Vertragsdaten, Kündigung, Löschung. Inkl. DSGVO-konformes Archiv.
Für wen
Admins, die ihre Lizenz-Kunden manuell oder per Self-Service-Link aufnehmen, Pakete zuordnen, Verträge verwalten.
Übersicht
Sidebar → Kunden: Liste aller Kunden. Spalten: Kundennummer, Name/Firma, Paket, Status, Erstellt. Zwei Filter-Reihen:
- Status-Filter: alle / aktiv / pending / wartet auf Zahlung / pausiert / inaktiv / gekündigt.
- Branchen-Filter (NACE-Sektionen): zeigt nur Kunden, deren Branche in einer der gewählten NACE-Sektionen liegt (Mehrfachauswahl, ODER). Leer = alle. Das Kunden-Detail zeigt die zugeordnete Branche jedes Kunden.
Status-Lifecycle
- pending — eingeladen / registriert, noch nicht freigeschaltet
- pending_payment (wartet auf Zahlung) — vom Admin freigeschaltet: Es wurde eine Rechnung gestellt und auf Zahlung gewartet. Wird automatisch active, sobald die Zahlung eingeht (
invoice.paid). - active — freigeschaltet und bezahlt, hat Portal-Zugang
- paused — vorübergehend ausgesetzt (reaktivierbar)
- inactive — deaktiviert
- cancelled — Abonnement gekündigt (Enddatum erreicht / gesetzt)
- Gelöscht (
deleted_customers-Archiv): DSGVO-konform anonymisiert
Schritt für Schritt — Kunden einladen
1. + Kunde einladen
Kunden → + Kunde einladen.
2. Felder
- E-Mail (Pflicht) — Empfänger der Einladungs-Mail
- Vorname / Nachname / Firma
- Paket auswählen (Dropdown aller aktiven Pakete des Anbieter)
- Add-ons optional zuweisen
- Notizen (intern, für dich)
3. Versenden
licensio generiert einen Registrierungs-Token, schickt eine Mail mit dem Link /<tenant>.licensio.io/register/<token>. Der Kunde klickt den Link und setzt ein Passwort — der Account wird mit Status pending angelegt.
4. Freischalten (immer per Rechnung)
Du schaltest den Kunden manuell im Kunden-Detail frei (Sektion Status → Freischalten). Die Freischaltung eines kostenpflichtigen, buchbaren Pakets erzeugt IMMER eine Stripe-Rechnung (send_invoice, 14 Tage Fälligkeit) im Connected-Account des Anbieter — unabhängig davon, ob ein Zahlungsmittel hinterlegt ist. Auf dem Admin-Pfad gibt es keinen Checkout (Checkout nur beim Self-Service /join). Der Kunde geht in den Status pending_payment und erhält die Rechnungs-Mail mit Hosted-Payment-Link. Der Status wechselt erst dann automatisch auf active, wenn invoice.paid eingeht.
- Zahlungslink erneut senden: Ist die Rechnung/Subscription abgelaufen oder storniert („totes" Abo), zeigt das Kunden-Detail einen Button „Zahlungslink erneut senden", der eine frische Rechnung erzeugt.
- Kostenlos / kein Paket: Ein Paket mit Preis 0 (oder kein Paket) wird direkt aktiviert, ohne Rechnung.
- Ein zukünftiger Vertragsstart wird im Rechnungs-Flow nicht abgebildet (mit Hinweis geblockt).
- Self-Service-Auto-Freischaltung: Ein Paket mit
auto_activate=trueschaltet den Kunden bei der Self-Service-Registrierung über/joindirekt frei (dieser Pfad nutzt Checkout und Sofortzahlung) — ohne manuelle Freischaltung.
Kunden-Detail
Klick auf Kunde in der Liste. Das Layout besteht aus einer fixen Kopf-Kachel und aufklappbaren Sektionen (Collapsibles). Der Auf-/Zu-Zustand jeder Sektion und der aktive Logs-Tab werden pro Kunde im Browser gespeichert (localStorage). Standardmäßig sind alle Collapsibles zugeklappt.
Fixe Kachel "Kontakt & Abonnement"
Immer geöffnet, oben. Bündelt Stammdaten + Abonnement. Über den Bearbeiten-Button (oben rechts) werden die Stammdaten inline editierbar (Felder werden zu Eingaben, Speichern / Abbrechen):
- Name, Firma, E-Mail, Kundennummer (
customer_number, z.B. PT-2026-0001), Status-Badge, externe ID - Adresse (Straße, PLZ, Stadt, Land), USt-ID, Steuernummer
- Ansprechpartner, Zahlungsart, interne Nr. (
customer_number_int, sekundär) - Branche (NACE): die dem Kunden zugeordneten NACE-Rev.-2.1-Abteilungen (bis zu 3, die erste ist die Hauptbranche), als lesbare Labels (
customers.industry_codes, Migration 093) - Account-Typ:
Whitelabel(Default) oderConnect, abgeleitet auscustomers.connect_user_id(NULL = Whitelabel, gesetzt = Connect) - Zahlungsstatus (Stripe-Subscription live: Status, Rechnungsstatus wie „bezahlt" / „offen bis <Datum>" / „storniert", Betrag, nächste/letzte Belastung, Link zur Hosted-Rechnung)
- Rechnungen: die Stripe-Rechnungen des Abos (Nummer, Betrag, Status, Link) — so sind Proration-Rechnungen für Zusatzpakete ohne Stripe-Login einsehbar
- Pakete & Zahlung: Basis-Paket (wechselbar) + Add-ons, Add-Package-Button (Payment-Link erzeugen / per Mail senden)
Bearbeiten (Admin): PATCH /api/customers/[id] schreibt first_name, last_name, company, responsible_person, street, zip, city, country, vat_id, tax_number, payment_method. Best-effort Stripe-Sync wie im Portal-Self-Edit: Adresse + Name auf den Stripe-Customer, vat_id als eu_vat-Tax-ID. tax_number bleibt nur in der DB (Stripe kennt keine nationale Steuernummer). country wird als 2-stelliger ISO-Code validiert. E-Mail bleibt ausgenommen (eigener OTP-Flow via „E-Mail ändern").
Aufklappbare Sektionen (Standard: zugeklappt)
- Logs — Tab-Bar mit drei Tabs (jeweils letzte 10):
- Datei-Downloads — aus
download_logs(Datum, Produkt, Dateityp) - Logins — aus
customer_activity(activity_type='login', Datum + gekürzter IP-Hash) - Feed-Exporte — aus
customer_activity(activity_type='feed_export', Datum + Format + via token/session) - Leerer Tab zeigt „Noch keine Daten". Tab-Auswahl wird pro Kunde gemerkt.
- Datei-Downloads — aus
- Support-Verlauf — letzte 3 Tickets (
support_tickets, sortiert nachupdated_at, mit Status-Badge + Link zur Ticket-Detailseite) + E-Mail-Verlauf (email_logs) - Einwilligungen — DSGVO-Nachweis (
customer_consents, nur wenn vorhanden) - Dokumente — Vertrags-Uploads (
customer_documents) + Vertragsstart-Datum - Interne Notizen
- Status — Freischalten (pending → pending_payment per Rechnung → active bei
invoice.paid; „Zahlungslink erneut senden" solange pending_payment) / Deaktivieren / Aktivierung ablehnen (rejected_at,rejection_reason) / Pausieren-Reaktivieren. Kündigung ist hier integriert (nur beistatus='active'): „Abonnement kündigen" (Datum-Modal) bzw. Kündigung zurücknehmen. - Aktionen — Registrierungslink neu senden, Passwort-Reset, Stripe-Billing-Portal öffnen
- Gefahrenzone — Löschen (DSGVO Art. 17): Kunde wird in
deleted_customersarchiviert (nurcustomer_number,tenant_id,deleted_at,deleted_by— KEIN PII), Auth-User + Customer-Record werden gelöscht.
Hinweis: Logins + Feed-Exporte stammen aus dem Customer-Activity-Logging (Migration 060). Downloads bleiben separat in
download_logs. Account-Typ-Ankercustomers.connect_user_id(Migration 061, nullable, noch ohne FK) — licensio Connect ist Phase 2.
Kundennummern
Auto-Trigger generiert: PT-2026-0001, PT-2026-0002, ... pro Anbieter + Jahr. Nach Kunden-Löschung wird die Nummer NICHT wiederverwendet — sie bleibt im deleted_customers-Archiv reserviert (Migration 013).
Häufige Fragen
Wie lange dauert die Stripe-Synchronisation?
Sofort beim Speichern. Bei Stripe-Express-Accounts werden manche Felder (z.B. company.address) nicht akzeptiert — dann silent best-effort, der Rest wird gespeichert.
Woher kommt die Kundenadresse?
Beim Self-Service-Checkout unter /join fragt Stripe eine Pflicht-Adresse (und optional die USt-ID) ab. Nach Abschluss übernimmt der Webhook (checkout.session.completed) Straße/PLZ/Stadt + USt-ID automatisch in den Kundendatensatz — aber nur, wenn die Felder noch leer sind. Der Admin-Freischaltungs-Pfad erzeugt eine send_invoice-Rechnung (kein Checkout) — dort wird die Adresse nicht automatisch erfasst; pflege sie im Admin oder lass sie den Kunden im Portal eintragen. Eine später im Portal oder Admin gepflegte Adresse wird nie überschrieben. country wird nicht aus dem Checkout übernommen (bleibt beim Registrierungs-Wert). Hinweis: Der Setup-Checkout (Pakete ohne sofortige Abbuchung) sammelt keine Adresse — dort bleibt das Feld leer, bis es im Portal/Admin gepflegt wird. Bestandskunden lassen sich per Backfill-Script (apps/portal/scripts/backfill-stripe-addresses.ts) nachziehen.
Was wenn ein Kunde sich selbst registriert hat?
Self-Service-Pakete erstellen den Customer mit status='pending'. Du siehst sie in der Liste mit Filter "Pending".
Kann ich Kunden CSV-importieren?
Phase 2 — Bulk-Import noch nicht implementiert (product_imports-Tabelle ist Skeleton dafür).
DSGVO-Auskunft für einen Kunden? Aktuell manuell aus DB exportierbar. Self-Service-Auskunft (Phase 2 / nice-to-have).
Was wenn der Stripe-Customer-Update fehlschlägt? licensio-Customer wird trotzdem gespeichert. Du siehst eine Warnung im UI. Stripe-Sync kannst du via "Erneut synchronisieren"-Button wiederholen.
Verwandt
- Pakete — Pakete erstellen + zuweisen
- Rechnungsstellung — Stripe-Setup
- Account — Kundensicht