Members Modul
Zweck
Das Members Modul bietet eine umfassende Mitarbeiterverwaltung mit Rollen (Admin, Planer, Mitarbeiter), Berechtigungen, Einladungssystem per E-Mail, Profilverwaltung und Übersicht aller Organisationsmitglieder.
Erweitert zu einem vollständigen Personalwesen-Modul mit: - Dokumentenverwaltung (Verträge, Zeugnisse, Qualifikationen, Ausweise) - Vertragsverwaltung (Arbeitsverträge mit Laufzeiten) - Qualifikationsverwaltung (mit Ablaufdaten und Warnungen) - Abwesenheitsverwaltung (Urlaub, Krankheit, etc.) - Notizenverwaltung (Personalnotizen) - Team-Verwaltung (Teams erstellen, bearbeiten, löschen, Mitglieder zuweisen) - ERP-Integration (DATEV, SAP, Custom) - Erweiterte Personalwesen-Felder (Geburtsdatum, Steuer-ID, Bankverbindung, etc.)
Verantwortlichkeiten
Was gehört zum Modul
- Mitarbeiter-Verwaltung: Übersicht aller Organisationsmitglieder
- Rollen-Management: Rollen zuweisen (Admin, Planer, Mitarbeiter)
- Berechtigungen: Berechtigungen pro Rolle verwalten
- Einladungssystem: Mitarbeiter per E-Mail einladen
- Profilverwaltung: Profil-Informationen verwalten
- Mitglieder-Status: Aktiv/Inaktiv verwalten
- Dokumentenverwaltung: Dokumente hochladen, verwalten und herunterladen
- Vertragsverwaltung: Arbeitsverträge erstellen, verwalten und verfolgen
- Qualifikationsverwaltung: Qualifikationen mit Ablaufdaten verwalten
- Abwesenheitsverwaltung: Urlaub, Krankheit und andere Abwesenheiten verwalten
- Notizenverwaltung: Personalnotizen erstellen und verwalten
- Team-Verwaltung: Teams erstellen, bearbeiten und löschen, Mitglieder zu Teams hinzufügen/entfernen, Team-Leader zuweisen
- ERP-Integration: Export für DATEV, SAP und andere ERP-Systeme
Was gehört nicht zum Modul
- Authentifizierung: Siehe Core Auth
- Tenant-Verwaltung: Siehe Core Tenancy
- Benachrichtigungen: Siehe notifications Modul
Implementierung
Backend- und Frontend-API sind modular aufgebaut; Details siehe interne Entwickler-Dokumentation.
Konfiguration
Entitlement
Das Modul ist ein Core-Modul und erfordert kein spezielles Entitlement. Es ist immer aktiv.
Entitlement-Key: Keiner (Core-Modul)
Environment Variables
Keine modulspezifischen ENV-Variablen erforderlich.
Defaults
- Standard-Rolle:
MEMBER(Mitarbeiter) - Einladungs-E-Mail: Wird automatisch versendet
Abhängigkeiten
Optionale Abhängigkeiten
Keine.
Core-Abhängigkeiten
- notifications: Für Benachrichtigungen bei Einladungen
API-Endpoints
Authentifizierung
Alle Endpoints erfordern Authentifizierung. Zwei Methoden werden unterstützt:
- Firebase ID Token (Standard für Web-App)
- API-Token (für externe Nutzung)
Header: Authorization: Bearer <token>
Mitglieder auflisten
GET /api/members
Lädt alle Mitglieder des Tenants.
Response:
{
"members": [
{
"id": "member123",
"uid": "user123",
"email": "user@example.com",
"role": "ADMIN",
"status": "active",
"displayName": "Max Mustermann"
}
],
"count": 1
}
Eigenes Profil abrufen
GET /api/members/me
Lädt das eigene Profil (für Admins/Manager).
Response:
{
"member": {
"id": "member123",
"uid": "user123",
"email": "user@example.com",
"role": "ADMIN",
"status": "active",
"displayName": "Max Mustermann"
}
}
Fehler:
- 404 NOT_FOUND: Profil nicht gefunden
- 403 ACCESS_DENIED: Keine Berechtigung
Mitglied-Details abrufen
GET /api/members/:memberId
Lädt Details eines Mitglieds.
Response:
{
"member": {
"id": "member123",
"uid": "user123",
"email": "user@example.com",
"role": "MEMBER",
"status": "active",
"displayName": "Max Mustermann",
"firstName": "Max",
"lastName": "Mustermann",
"phone": "+49 123 456789",
"department": "Sicherheit",
"position": "Wachmann"
}
}
Fehler:
- 404 NOT_FOUND: Mitglied nicht gefunden
- 403 ACCESS_DENIED: Keine Berechtigung
Mitglied einladen
POST /api/members
Lädt ein Mitglied per E-Mail ein. Erstellt einen Firebase Auth User und gibt optional einen Password Reset Link zurück.
Request Body:
{
"email": "newuser@example.com",
"role": "MEMBER",
"displayName": "Optional: Anzeigename",
"firstName": "Optional: Vorname",
"lastName": "Optional: Nachname"
}
Response:
{
"member": {
"id": "member123",
"uid": "user123",
"email": "newuser@example.com",
"role": "MEMBER",
"status": "active"
},
"passwordResetLink": "https://...",
"message": "Member invited successfully. User can set their password via the reset link."
}
Fehler:
- 422 VALIDATION_ERROR: Ungültige Eingabedaten (z.B. ungültige E-Mail)
- 409 DUPLICATE: Mitglied existiert bereits
- 500 AUTH_ERROR: Fehler beim Erstellen des User-Accounts
- 403 SUBSCRIPTION_USER_LIMIT_REACHED: Abonnement-Limit erreicht
Eigenes Profil aktualisieren
PATCH /api/members/me
Aktualisiert das eigene Profil (für Admins/Manager). Nur bestimmte Felder können aktualisiert werden (keine Rolle/Status-Änderung).
Request Body:
{
"displayName": "Max Mustermann",
"firstName": "Max",
"lastName": "Mustermann",
"phone": "+49 123 456789",
"address": {
"street": "Musterstraße 1",
"zip": "12345",
"city": "Berlin"
},
"employeeNumber": "EMP001",
"department": "Sicherheit",
"position": "Wachmann",
"hourlyRate": 15.50,
"skills": ["Sicherheit", "Erste Hilfe"],
"notes": "Optional: Notizen",
"hasSachkunde": true,
"hasFuehrerschein": true,
"hasUnterweisung": true,
"securityQualifications": ["Sachkunde §34a"]
}
Response:
{
"member": {
"id": "member123",
"displayName": "Max Mustermann",
"firstName": "Max",
"lastName": "Mustermann"
},
"message": "Profile updated successfully"
}
Fehler:
- 404 NOT_FOUND: Profil nicht gefunden
- 403 ACCESS_DENIED: Keine Berechtigung
Mitglied aktualisieren
PUT /api/members/:memberId
Aktualisiert ein Mitglied (nur Admin/Manager).
Request Body:
{
"role": "PLANNER",
"displayName": "Max Mustermann",
"firstName": "Max",
"lastName": "Mustermann",
"phone": "+49 123 456789",
"department": "Sicherheit",
"position": "Wachmann",
"hourlyRate": 15.50,
"skills": ["Sicherheit"],
"status": "active"
}
Response:
{
"member": {
"id": "member123",
"role": "PLANNER",
"displayName": "Max Mustermann",
"status": "active"
},
"message": "Member updated successfully"
}
Fehler:
- 404 NOT_FOUND: Mitglied nicht gefunden
- 403 ACCESS_DENIED: Keine Berechtigung
Mitglied löschen
DELETE /api/members/:memberId
Löscht ein Mitglied (nur Admin).
Response:
Fehler:
- 404 NOT_FOUND: Mitglied nicht gefunden
- 403 FORBIDDEN: Kann sich nicht selbst löschen
Mitglied aktivieren
POST /api/members/:memberId/activate
Aktiviert ein deaktiviertes Mitglied (nur Admin/Manager).
Response:
Fehler:
- 404 NOT_FOUND: Mitglied nicht gefunden
- 403 ACCESS_DENIED: Keine Berechtigung
Mitglied deaktivieren
POST /api/members/:memberId/deactivate
Deaktiviert ein Mitglied (nur Admin/Manager).
Response:
{
"member": {
"id": "member123",
"status": "inactive"
},
"message": "Member deactivated successfully"
}
Fehler:
- 404 NOT_FOUND: Mitglied nicht gefunden
- 403 ACCESS_DENIED: Keine Berechtigung
Schichten eines Mitglieds abrufen
GET /api/members/:memberId/shifts?includeCompleted=false
Lädt alle Schichten eines Mitglieds (angenommen + zugewiesen).
Query-Parameter:
- includeCompleted (optional): Auch abgeschlossene Schichten einbeziehen (Standard: false)
Response:
{
"shifts": [
{
"id": "shift123",
"title": "Schicht-Titel",
"status": "PUBLISHED",
"startsAt": "2024-01-15T08:00:00Z",
"endsAt": "2024-01-15T17:00:00Z"
}
],
"count": 1
}
Fehler:
- 404 NOT_FOUND: Mitglied nicht gefunden
- 403 ACCESS_DENIED: Keine Berechtigung
Password Reset Link generieren
POST /api/members/:memberId/generate-invite-link
Sendet eine Password Reset E-Mail an einen Mitarbeiter. Die E-Mail wird automatisch gesendet (kein Link wird zurückgegeben aus Sicherheitsgründen).
Response:
Fehler:
- 404 NOT_FOUND: Mitglied nicht gefunden
- 403 ACCESS_DENIED: Keine Berechtigung
- 500 EMAIL_SEND_FAILED: Fehler beim E-Mail-Versand
Personalwesen-Erweiterungen
Dokumentenverwaltung
Dokumente abrufen
GET /api/members/:memberId/documents?type=contract
Lädt alle Dokumente eines Mitarbeiters.
Query-Parameter:
- type (optional): Filter nach Dokumententyp (contract, certificate, qualification, identification, other)
Response:
{
"documents": [
{
"id": "doc123",
"memberId": "member123",
"type": "contract",
"fileName": "arbeitsvertrag.pdf",
"fileSize": 245678,
"fileType": "application/pdf",
"filePath": "[storage-path]/[filename]",
"uploadedByUid": "user123",
"uploadedAt": "2024-01-15T10:00:00Z",
"description": "Arbeitsvertrag vom 01.01.2024"
}
],
"count": 1
}
Dokument hochladen
POST /api/members/:memberId/documents
Lädt ein Dokument hoch (multipart/form-data).
Request Body (FormData):
- file: Datei (PDF, JPG, PNG, max. 10MB)
- type: Dokumententyp (contract, certificate, qualification, identification, other)
- description (optional): Beschreibung
- metadata (optional): JSON-String mit Metadaten
Response:
{
"document": {
"id": "doc123",
"memberId": "member123",
"type": "contract",
"fileName": "arbeitsvertrag.pdf",
"fileSize": 245678,
"fileType": "application/pdf",
"uploadedAt": "2024-01-15T10:00:00Z"
},
"message": "Document uploaded successfully"
}
Fehler:
- 404 NOT_FOUND: Mitglied nicht gefunden
- 422 VALIDATION_ERROR: Ungültiger Dateityp oder Dateigröße überschritten
- 403 ACCESS_DENIED: Keine Berechtigung
Dokument-Download-URL abrufen
GET /api/members/:memberId/documents/:documentId/download
Generiert eine temporäre Download-URL (1 Stunde gültig).
Response:
Dokument löschen
DELETE /api/members/:memberId/documents/:documentId
Löscht ein Dokument (nur Admin/Manager).
Response:
Vertragsverwaltung
Verträge abrufen
GET /api/members/:memberId/contracts
Lädt alle Verträge eines Mitarbeiters.
Response:
{
"contracts": [
{
"id": "contract123",
"memberId": "member123",
"type": "permanent",
"startDate": "2024-01-01",
"endDate": null,
"employmentModel": "full_time",
"salary": 3500.00,
"hourlyRate": 20.00,
"weeklyHours": 40,
"noticePeriod": 30,
"createdAt": "2024-01-01T10:00:00Z"
}
],
"count": 1
}
Aktiven Vertrag abrufen
GET /api/members/:memberId/contracts/active
Lädt den aktuell aktiven Vertrag eines Mitarbeiters.
Response:
{
"contract": {
"id": "contract123",
"type": "permanent",
"startDate": "2024-01-01",
"endDate": null,
"employmentModel": "full_time"
}
}
Vertrag erstellen
POST /api/members/:memberId/contracts
Erstellt einen neuen Vertrag.
Request Body:
{
"type": "permanent",
"startDate": "2024-01-01",
"endDate": null,
"employmentModel": "full_time",
"salary": 3500.00,
"hourlyRate": 20.00,
"weeklyHours": 40,
"noticePeriod": 30,
"documentId": "doc123",
"notes": "Unbefristeter Arbeitsvertrag"
}
Vertrag aktualisieren
PUT /api/members/:memberId/contracts/:contractId
Aktualisiert einen Vertrag.
Vertrag löschen
DELETE /api/members/:memberId/contracts/:contractId
Löscht einen Vertrag.
Qualifikationsverwaltung
Qualifikationen abrufen
GET /api/members/:memberId/qualifications
Lädt alle Qualifikationen eines Mitarbeiters.
Response:
{
"qualifications": [
{
"id": "qual123",
"memberId": "member123",
"name": "Führerschein Klasse B",
"type": "Führerschein",
"issuer": "Straßenverkehrsamt",
"issueDate": "2020-01-15",
"expiryDate": null,
"documentId": "doc456",
"notes": "Gültig unbefristet"
}
],
"count": 1
}
Ablaufende Qualifikationen abrufen
GET /api/members/:memberId/qualifications/expiring?daysAhead=30
Lädt Qualifikationen, die in den nächsten Tagen ablaufen.
Query-Parameter:
- daysAhead (optional): Anzahl der Tage im Voraus (Standard: 30)
Response:
{
"qualifications": [
{
"id": "qual123",
"name": "Erste-Hilfe-Kurs",
"expiryDate": "2024-02-15"
}
],
"count": 1,
"daysAhead": 30
}
Qualifikation erstellen
POST /api/members/:memberId/qualifications
Request Body:
{
"name": "Erste-Hilfe-Kurs",
"type": "Zertifikat",
"issuer": "DRK",
"issueDate": "2023-01-15",
"expiryDate": "2025-01-15",
"documentId": "doc789",
"notes": "Jährliche Auffrischung erforderlich"
}
Abwesenheitsverwaltung
Abwesenheiten abrufen
GET /api/members/:memberId/absences?startDate=2024-01-01&endDate=2024-12-31
Lädt alle Abwesenheiten eines Mitarbeiters.
Query-Parameter:
- startDate (optional): Startdatum für Filter
- endDate (optional): Enddatum für Filter
Response:
{
"absences": [
{
"id": "absence123",
"memberId": "member123",
"type": "vacation",
"startDate": "2024-07-01",
"endDate": "2024-07-14",
"reason": "Sommerurlaub",
"approvalStatus": "approved",
"approvedByUid": "admin123",
"approvedAt": "2024-06-15T10:00:00Z",
"createdAt": "2024-06-01T10:00:00Z"
}
],
"count": 1
}
Abwesenheit erstellen
POST /api/members/:memberId/absences
Request Body:
{
"type": "vacation",
"startDate": "2024-07-01",
"endDate": "2024-07-14",
"reason": "Sommerurlaub",
"notes": "Genehmigt durch Vorgesetzten"
}
Abwesenheit aktualisieren
PUT /api/members/:memberId/absences/:absenceId
Kann auch zur Genehmigung/Ablehnung verwendet werden:
Notizenverwaltung
Notizen abrufen
GET /api/members/:memberId/notes
Lädt alle Notizen eines Mitarbeiters.
Response:
{
"notes": [
{
"id": "note123",
"memberId": "member123",
"title": "Mitarbeitergespräch",
"content": "Sehr gute Leistung, Weiterentwicklung geplant",
"category": "Mitarbeitergespräch",
"isConfidential": false,
"createdAt": "2024-01-15T10:00:00Z",
"createdByUid": "admin123"
}
],
"count": 1
}
Notiz erstellen
POST /api/members/:memberId/notes
Request Body:
{
"title": "Mitarbeitergespräch",
"content": "Sehr gute Leistung",
"category": "Mitarbeitergespräch",
"isConfidential": false
}
ERP-Export
Vollständige Mitarbeiterdaten für ERP-Export
GET /api/members/:memberId/erp-export?format=json
Lädt vollständige Mitarbeiterdaten für ERP-Export (DATEV, SAP, etc.).
Query-Parameter:
- format (optional): Export-Format (json oder csv, Standard: json)
Response:
{
"export": {
"member": {
"id": "member123",
"email": "user@example.com",
"firstName": "Max",
"lastName": "Mustermann",
"externalEmployeeId": "EMP001",
"erpSystem": "datev",
"erpSyncStatus": "synced",
"dateOfBirth": "1990-01-15",
"taxId": "12345678901",
"bankAccount": {
"iban": "DE89370400440532013000",
"bic": "COBADEFFXXX"
}
},
"contracts": [...],
"qualifications": [...],
"absences": [...],
"documents": [...],
"notes": [...],
"exportedAt": "2024-01-15T10:00:00Z",
"exportedBy": "admin123"
},
"format": "json"
}
Verwendung für DATEV-Integration:
// Beispiel: DATEV-Export
const exportData = await getMemberERPExport(memberId, 'json');
// Konvertiere zu DATEV-Format
const datevFormat = convertToDatevFormat(exportData.export);
Verwendung für SAP-Integration:
// Beispiel: SAP-Export
const exportData = await getMemberERPExport(memberId, 'json');
// Sende an SAP-System
await sendToSAP(exportData.export);
Team-Verwaltung
Teams auflisten
GET /api/members/teams
Lädt alle Teams des Tenants.
Response:
{
"teams": [
{
"id": "team123",
"name": "Vertriebsteam",
"description": "Team für Vertrieb und Kundenbetreuung",
"leaderId": "member123",
"memberIds": ["member123", "member456"],
"createdAt": "2024-01-15T10:00:00Z",
"updatedAt": "2024-01-15T10:00:00Z",
"createdByUid": "user123"
}
],
"count": 1
}
Fehler:
- 403 ACCESS_DENIED: Keine Berechtigung
Team-Details abrufen
GET /api/members/teams/:teamId
Lädt Details eines Teams.
Response:
{
"team": {
"id": "team123",
"name": "Vertriebsteam",
"description": "Team für Vertrieb und Kundenbetreuung",
"leaderId": "member123",
"memberIds": ["member123", "member456"],
"createdAt": "2024-01-15T10:00:00Z",
"updatedAt": "2024-01-15T10:00:00Z",
"createdByUid": "user123"
}
}
Fehler:
- 404 NOT_FOUND: Team nicht gefunden
- 403 ACCESS_DENIED: Keine Berechtigung
Team erstellen
POST /api/members/teams
Erstellt ein neues Team.
Request Body:
{
"name": "Vertriebsteam",
"description": "Team für Vertrieb und Kundenbetreuung",
"leaderId": "member123",
"memberIds": ["member123", "member456"]
}
Response:
{
"team": {
"id": "team123",
"name": "Vertriebsteam",
"description": "Team für Vertrieb und Kundenbetreuung",
"leaderId": "member123",
"memberIds": ["member123", "member456"],
"createdAt": "2024-01-15T10:00:00Z",
"updatedAt": "2024-01-15T10:00:00Z",
"createdByUid": "user123"
},
"message": "Team created successfully"
}
Fehler:
- 422 VALIDATION_ERROR: Validierungsfehler (z.B. fehlender Name)
- 409 DUPLICATE: Team existiert bereits
- 403 ACCESS_DENIED: Keine Berechtigung
Team aktualisieren
PUT /api/members/teams/:teamId
Aktualisiert ein bestehendes Team.
Request Body:
{
"name": "Vertriebsteam (Aktualisiert)",
"description": "Aktualisierte Beschreibung",
"leaderId": "member789",
"memberIds": ["member123", "member456", "member789"]
}
Alle Felder sind optional.
Response:
{
"team": {
"id": "team123",
"name": "Vertriebsteam (Aktualisiert)",
"description": "Aktualisierte Beschreibung",
"leaderId": "member789",
"memberIds": ["member123", "member456", "member789"],
"createdAt": "2024-01-15T10:00:00Z",
"updatedAt": "2024-01-16T10:00:00Z",
"createdByUid": "user123"
},
"message": "Team updated successfully"
}
Fehler:
- 404 NOT_FOUND: Team nicht gefunden
- 409 DUPLICATE: Team-Name existiert bereits
- 403 ACCESS_DENIED: Keine Berechtigung
Team löschen
DELETE /api/members/teams/:teamId
Löscht ein Team.
Response:
Fehler:
- 404 NOT_FOUND: Team nicht gefunden
- 403 ACCESS_DENIED: Keine Berechtigung
Mitglied zu Team hinzufügen
POST /api/members/teams/:teamId/members/:memberId
Fügt ein Mitglied zu einem Team hinzu.
Response:
{
"team": {
"id": "team123",
"name": "Vertriebsteam",
"memberIds": ["member123", "member456", "member789"],
"updatedAt": "2024-01-16T10:00:00Z"
},
"message": "Member added to team successfully"
}
Fehler:
- 404 NOT_FOUND: Team nicht gefunden
- 409 DUPLICATE: Mitglied ist bereits im Team
- 403 ACCESS_DENIED: Keine Berechtigung
Mitglied aus Team entfernen
DELETE /api/members/teams/:teamId/members/:memberId
Entfernt ein Mitglied aus einem Team.
Response:
{
"team": {
"id": "team123",
"name": "Vertriebsteam",
"memberIds": ["member123", "member456"],
"updatedAt": "2024-01-16T10:00:00Z"
},
"message": "Member removed from team successfully"
}
Fehler:
- 404 NOT_FOUND: Team nicht gefunden oder Mitglied nicht im Team
- 403 ACCESS_DENIED: Keine Berechtigung
Team-Mitglieder abrufen
GET /api/members/teams/:teamId/members
Lädt alle Mitglieder eines Teams.
Response:
{
"members": [
{
"id": "member123",
"uid": "user123",
"email": "user@example.com",
"displayName": "Max Mustermann",
"role": "MANAGER",
"status": "ACTIVE"
}
],
"count": 1
}
Fehler:
- 404 NOT_FOUND: Team nicht gefunden
- 403 ACCESS_DENIED: Keine Berechtigung
Team-Statistiken abrufen
GET /api/members/teams/:teamId/stats
Lädt Statistiken für ein Team.
Response:
{
"stats": {
"totalMembers": 5,
"activeMembers": 4,
"inactiveMembers": 1,
"pendingMembers": 0,
"adminCount": 1,
"managerCount": 2,
"employeeCount": 2
}
}
Fehler:
- 404 NOT_FOUND: Team nicht gefunden
- 403 ACCESS_DENIED: Keine Berechtigung
Firestore Collections
Members
Pfad: /tenants/{tenantId}/members/{memberId}
{
uid: string; // Firebase Auth UID
email: string;
role: 'ADMIN' | 'MANAGER' | 'EMPLOYEE';
status: 'ACTIVE' | 'INACTIVE' | 'PENDING';
displayName?: string;
firstName?: string;
lastName?: string;
address?: string; // Legacy: einfache Adresse
employeeNumber?: string;
phone?: string;
department?: string;
position?: string;
hourlyRate?: number;
skills?: string[];
notes?: string;
hasSachkunde?: boolean;
hasFuehrerschein?: boolean;
hasUnterweisung?: boolean;
securityQualifications?: string[];
customFields?: Record<string, CustomFieldValue>;
// Personalwesen-Erweiterungen
dateOfBirth?: string; // ISO 8601 Date
nationality?: string;
maritalStatus?: 'single' | 'married' | 'divorced' | 'widowed' | 'partnership';
placeOfBirth?: string;
residentialAddress?: {
street: string;
zipCode: string;
city: string;
country?: string;
state?: string;
};
postalAddress?: {
street: string;
zipCode: string;
city: string;
country?: string;
state?: string;
};
emergencyContact?: {
name: string;
phone: string;
relationship?: string;
email?: string;
};
relatives?: string[];
bankAccount?: {
iban: string;
bic?: string;
bankName?: string;
accountHolder?: string;
};
taxClass?: number; // 1-6
taxId?: string;
socialSecurityNumber?: string;
employmentModel?: 'full_time' | 'part_time' | 'mini_job' | 'internship' | 'freelance';
weeklyHours?: number;
workingTimeDistribution?: string;
externalEmployeeId?: string; // Für DATEV/SAP
erpSyncStatus?: 'not_synced' | 'synced' | 'error';
erpLastSyncAt?: Timestamp;
erpSystem?: 'datev' | 'sap' | 'custom';
teamId?: string;
supervisorId?: string; // Member-ID
costCenter?: string;
costCenterCode?: string;
invitedByUid?: string;
createdAt: Timestamp;
updatedAt: Timestamp;
lastActiveAt?: Timestamp;
lastLoginAt?: Timestamp;
ssoConfigId?: string;
}
Member Documents
Pfad: /tenants/{tenantId}/members/{memberId}/documents/{documentId}
{
memberId: string;
type: 'contract' | 'certificate' | 'qualification' | 'identification' | 'other';
fileName: string;
fileSize: number;
fileType: string;
filePath: string; // Storage-Pfad
uploadedByUid: string;
uploadedAt: Timestamp;
description?: string;
metadata?: Record<string, string | number | boolean>;
}
Member Contracts
Pfad: /tenants/{tenantId}/members/{memberId}/contracts/{contractId}
{
memberId: string;
type: 'permanent' | 'temporary' | 'internship' | 'freelance';
startDate: Timestamp;
endDate?: Timestamp;
employmentModel: 'full_time' | 'part_time' | 'mini_job' | 'internship' | 'freelance';
salary?: number; // Bruttogehalt (monatlich)
hourlyRate?: number;
weeklyHours?: number;
noticePeriod?: number; // Kündigungsfrist in Tagen
documentId?: string; // Referenz zu MemberDocument
notes?: string;
createdAt: Timestamp;
updatedAt: Timestamp;
createdByUid: string;
}
Member Qualifications
Pfad: /tenants/{tenantId}/members/{memberId}/qualifications/{qualificationId}
{
memberId: string;
name: string;
type: string; // z.B. "Führerschein", "Sachkunde", "Zertifikat"
issuer?: string;
issueDate?: Timestamp;
expiryDate?: Timestamp;
documentId?: string;
notes?: string;
createdAt: Timestamp;
updatedAt: Timestamp;
createdByUid: string;
}
Member Absences
Pfad: /tenants/{tenantId}/members/{memberId}/absences/{absenceId}
{
memberId: string;
type: 'vacation' | 'sick' | 'special_leave' | 'unpaid_leave' | 'other';
startDate: Timestamp;
endDate: Timestamp;
reason?: string;
approvalStatus: 'pending' | 'approved' | 'rejected';
approvedByUid?: string;
approvedAt?: Timestamp;
notes?: string;
createdAt: Timestamp;
updatedAt: Timestamp;
createdByUid: string;
}
Member Notes
Pfad: /tenants/{tenantId}/members/{memberId}/notes/{noteId}
{
memberId: string;
title: string;
content: string;
category?: string; // z.B. "Mitarbeitergespräch", "Leistungsbewertung"
isConfidential?: boolean; // Nur für Admin sichtbar
createdAt: Timestamp;
updatedAt: Timestamp;
createdByUid: string;
}
Teams
Pfad: /tenants/{tenantId}/teams/{teamId}
{
name: string;
description?: string;
leaderId?: string; // Member-ID des Team-Leaders
memberIds: string[]; // Array von Member-IDs
createdAt: Timestamp;
updatedAt: Timestamp;
createdByUid: string;
}
Firebase Storage Struktur
Dokumente werden in Firebase Storage gespeichert:
Dokumente werden in einer sicheren, hierarchischen Struktur organisiert nach Dokumenttyp gespeichert.
Unterstützte Typen: contract, certificate, qualification, identification, other
Unterstützte Dateitypen: PDF, JPG, PNG (max. 10MB)
Fehlerfälle
HTTP-Status-Codes
- 200 OK: Erfolgreich
- 201 Created: Ressource erstellt
- 400 Bad Request: Validierungsfehler
- 401 Unauthorized: Nicht authentifiziert
- 403 Forbidden: Keine Berechtigung (nur Admin)
- 404 Not Found: Ressource nicht gefunden
- 500 Internal Server Error: Server-Fehler
Fehlercodes
Die folgenden Fehlercodes werden von den API-Endpunkten zurückgegeben:
NOT_FOUND: Ressource nicht gefunden (Mitglied, Team, etc.)ACCESS_DENIED: Keine Berechtigung für die OperationVALIDATION_ERROR: Validierungsfehler bei EingabedatenDUPLICATE: Ressource existiert bereits (Mitglied, Team-Name) oder Mitglied bereits im TeamAUTH_ERROR: Fehler beim Erstellen des User-AccountsSUBSCRIPTION_USER_LIMIT_REACHED: Abonnement-Limit erreicht (mitcurrentCountundmaxCount)FORBIDDEN: Operation nicht erlaubt (z.B. sich selbst löschen)EMAIL_SEND_FAILED: Fehler beim E-Mail-Versand
ERP-Integration
DATEV-Integration
Das Modul unterstützt den Export von Mitarbeiterdaten für DATEV:
// Beispiel: DATEV-Export
const exportData = await getMemberERPExport(memberId, 'json');
// Konvertiere zu DATEV-Format
const datevData = {
Personalnummer: exportData.export.member.externalEmployeeId || exportData.export.member.employeeNumber,
Vorname: exportData.export.member.firstName,
Nachname: exportData.export.member.lastName,
Geburtsdatum: exportData.export.member.dateOfBirth,
Steuerklasse: exportData.export.member.taxClass,
SteuerID: exportData.export.member.taxId,
IBAN: exportData.export.member.bankAccount?.iban,
BIC: exportData.export.member.bankAccount?.bic,
// ... weitere Felder
};
// Sende an DATEV
await sendToDatev(datevData);
SAP-Integration
Für SAP-Integration kann der JSON-Export verwendet werden:
// Beispiel: SAP-Export
const exportData = await getMemberERPExport(memberId, 'json');
// Konvertiere zu SAP-Format
const sapData = {
PERNR: exportData.export.member.externalEmployeeId,
VORNA: exportData.export.member.firstName,
NACHN: exportData.export.member.lastName,
GBDAT: exportData.export.member.dateOfBirth,
// ... weitere Felder
};
// Sende an SAP
await sendToSAP(sapData);
Custom ERP-Integration
Für andere ERP-Systeme kann der generische JSON-Export verwendet werden:
const exportData = await getMemberERPExport(memberId, 'json');
// Verarbeite exportData.export nach Bedarf
Rückwärtskompatibilität
Alle Erweiterungen sind rückwärtskompatibel:
- ✅ Alle neuen Felder sind optional
- ✅ Bestehende API-Endpoints bleiben unverändert
- ✅ Bestehende Daten werden automatisch unterstützt
- ✅ Alte Clients können weiterhin ohne neue Felder arbeiten
- ✅ Neue Sub-Collections werden nur erstellt, wenn benötigt
Sicherheit
Berechtigungen
- Dokumente: Nur Admin/Manager können hochladen/löschen
- Verträge: Nur Admin/Manager können erstellen/bearbeiten
- Qualifikationen: Nur Admin/Manager können erstellen/bearbeiten
- Abwesenheiten: Admin/Manager können alle sehen, Mitarbeiter nur eigene
- Notizen: Nur Admin/Manager können erstellen/bearbeiten
- ERP-Export: Nur Admin/Manager können exportieren
Validierung
- Alle Uploads werden validiert (Dateityp, Größe)
- Alle Eingaben werden mit Zod-Schemas validiert
- Tenant-Scope wird bei allen Operationen geprüft
- Alle Sub-Collections sind tenant-scoped
FAQ / Troubleshooting
Wie lade ich ein Dokument hoch?
- Öffne die Mitglieder-Detailansicht
- Wechsle zum Tab "Dokumente"
- Klicke auf "Dokument hochladen"
- Wähle Datei und Dokumententyp
- Optional: Beschreibung hinzufügen
- Klicke auf "Hochladen"
Wie exportiere ich Daten für DATEV?
- Öffne die Mitglieder-Detailansicht
- Klicke auf "ERP-Export"
- Die JSON-Datei wird heruntergeladen
- Konvertiere die Daten nach DATEV-Format (siehe ERP-Integration)
Wie verwalte ich ablaufende Qualifikationen?
- Öffne die Mitglieder-Detailansicht
- Wechsle zum Tab "Qualifikationen"
- Ablaufende Qualifikationen werden automatisch hervorgehoben
- Verwende den Endpoint
/api/members/:memberId/qualifications/expiringfür automatisierte Warnungen
Wie genehmige ich eine Abwesenheit?
- Öffne die Mitglieder-Detailansicht
- Wechsle zum Tab "Abwesenheiten"
- Klicke auf "Genehmigen" oder "Ablehnen" bei ausstehenden Abwesenheiten