- Vintage Modell hinzugefuegt - Equipment Multi-Select (Neue Buchung + Bearbeitung) - Kundenadresse in Formularen - Bearbeiten-Seite fuer Buchungen - Abbau-Zeiten in Formularen und Uebersicht - Vertrag PDF nur bei Privatkunden - LexOffice Kontakt-Erstellung Fix (BUSINESS) - Zurueck-Pfeil auf Touren-Seite
380 lines
9.3 KiB
Markdown
380 lines
9.3 KiB
Markdown
# SaveTheMoment Automatisierungs-System
|
|
|
|
## Übersicht
|
|
|
|
Das vollständig automatisierte Buchungs- und Verwaltungssystem für SaveTheMoment verarbeitet Anfragen von der E-Mail-Eingang bis zur Admin-Bestätigung.
|
|
|
|
## Workflow-Phasen
|
|
|
|
### Phase 1: ANFRAGE (Automatisch bei neuer Buchung)
|
|
|
|
**Trigger:** Neue Buchung via E-Mail-Sync oder API
|
|
|
|
**Automatische Aktionen (`lib/booking-automation.ts::runPostBookingActions()`):**
|
|
|
|
1. ✅ **LexOffice Contact & Quotation erstellen**
|
|
- Contact aus Kundendaten erstellen
|
|
- Angebot mit allen Positionen generieren (Fotobox, KM-Pauschale, Extras)
|
|
- Angebots-PDF von LexOffice herunterladen
|
|
- Speichert `lexofficeContactId` und `lexofficeOfferId`
|
|
|
|
2. ✅ **Mietvertrag-PDF generieren**
|
|
- PDF aus Template (`mietvertrag-vorlage.pdf`) erstellen
|
|
- Kundendaten, Event-Details, Preis einfügen
|
|
- Setzt `contractGenerated: true` und `contractGeneratedAt`
|
|
|
|
3. ✅ **E-Mail mit Angebot + Vertrag versenden**
|
|
- Beide PDFs als Anhänge
|
|
- Online-Signatur-Link enthalten
|
|
- Übersichtliche Buchungsdetails und Gesamtpreis
|
|
- Setzt `contractSentAt`
|
|
|
|
4. ✅ **Nextcloud Kalender-Sync**
|
|
- Event im Buchungskalender erstellen/aktualisieren
|
|
- Setzt `calendarSynced: true` und `calendarSyncedAt`
|
|
|
|
5. ✅ **Admin-Benachrichtigung**
|
|
- Notification mit Typ `NEW_BOOKING`
|
|
|
|
**Dateien:**
|
|
- `lib/booking-automation.ts` - Hauptlogik
|
|
- `lib/lexoffice.ts` - LexOffice API (Contact, Quotation, PDF-Download)
|
|
- `lib/pdf-template-service.ts` - Contract PDF-Generierung
|
|
- `lib/email-service.ts` - `sendInitialBookingEmail()`
|
|
- `lib/nextcloud-calendar.ts` - CalDAV-Synchronisation
|
|
|
|
---
|
|
|
|
### Phase 2: ONLINE-UNTERSCHRIFT (Kunde)
|
|
|
|
**Endpoint:** `POST /api/bookings/[id]/sign`
|
|
|
|
**Ablauf:**
|
|
1. Kunde öffnet Link: `/contract/sign/{token}`
|
|
2. Canvas-Signatur zeichnen
|
|
3. POST an `/api/bookings/[id]/sign` mit Base64-Signatur
|
|
4. PDF mit Signatur neu generieren
|
|
5. Datenbank-Update:
|
|
- `contractSigned: true`
|
|
- `contractSignedAt: DateTime`
|
|
- `contractSignedOnline: true`
|
|
- `contractSignatureData: String` (Base64)
|
|
- `contractSignedBy: String` (Kundenname)
|
|
- `contractSignedIp: String`
|
|
6. Notification `CONTRACT_SIGNED` für Admin
|
|
|
|
**Dateien:**
|
|
- `app/api/bookings/[id]/sign/route.ts`
|
|
|
|
---
|
|
|
|
### Phase 3: ADMIN-BESTÄTIGUNG
|
|
|
|
**Endpoint:** `POST /api/bookings/[id]/confirm`
|
|
|
|
**Voraussetzungen:**
|
|
- ✅ `contractSigned === true`
|
|
- ✅ `status !== 'CONFIRMED'`
|
|
|
|
**Ablauf:**
|
|
1. Admin klickt auf "Buchung bestätigen" im Dashboard
|
|
2. LexOffice Auftragsbestätigung erstellen
|
|
- `lexofficeService.createConfirmationFromBooking()`
|
|
- Speichert `lexofficeConfirmationId`
|
|
3. Status-Update: `RESERVED → CONFIRMED`
|
|
4. Setzt `confirmationSentAt`
|
|
5. Nextcloud Kalender aktualisieren (Status: CONFIRMED)
|
|
6. Notification `BOOKING_CONFIRMED`
|
|
|
|
**Dateien:**
|
|
- `app/api/bookings/[id]/confirm/route.ts`
|
|
|
|
---
|
|
|
|
### Phase 4: RECHNUNG (Zeitgesteuert - TODO)
|
|
|
|
**Trigger:** Cron-Job
|
|
|
|
**Regeln:**
|
|
- **Privatkunde:** 2 Wochen vor Event-Datum
|
|
- **Geschäftskunde:** Nach Event-Datum
|
|
|
|
**Ablauf:**
|
|
1. LexOffice Rechnung erstellen
|
|
2. Rechnung finalisieren (freigeben)
|
|
3. E-Mail an Kunde mit Rechnungs-PDF
|
|
4. Setzt `lexofficeInvoiceId`
|
|
|
|
---
|
|
|
|
## API-Endpunkte
|
|
|
|
### Admin-Endpoints
|
|
|
|
#### `POST/GET /api/admin/test-automation`
|
|
Testet die automatischen Aktionen für die neueste Buchung.
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"success": true,
|
|
"emailSent": true,
|
|
"calendarSynced": true,
|
|
"lexofficeCreated": true,
|
|
"contractGenerated": true,
|
|
"errors": []
|
|
}
|
|
```
|
|
|
|
#### `POST /api/bookings/[id]/confirm`
|
|
Bestätigt eine Buchung (RESERVED → CONFIRMED) und erstellt LexOffice Auftragsbestätigung.
|
|
|
|
**Auth:** Admin required
|
|
|
|
---
|
|
|
|
### Public-Endpoints
|
|
|
|
#### `POST /api/bookings/[id]/sign`
|
|
Speichert die Online-Signatur des Kunden.
|
|
|
|
**Body:**
|
|
```json
|
|
{
|
|
"signatureData": "data:image/png;base64,..."
|
|
}
|
|
```
|
|
|
|
#### `GET /api/bookings/[id]/sign`
|
|
Ruft Buchungsdetails für Signatur-Seite ab.
|
|
|
|
---
|
|
|
|
## E-Mail-Templates
|
|
|
|
### 1. Initiale Buchungsanfrage (`sendInitialBookingEmail`)
|
|
**An:** Kunde
|
|
**Anhänge:**
|
|
- `Angebot_{bookingNumber}.pdf`
|
|
- `Mietvertrag_{bookingNumber}.pdf`
|
|
|
|
**Inhalt:**
|
|
- Buchungsdetails (Nummer, Datum, Location, Fotobox)
|
|
- Gesamtpreis (Highlight)
|
|
- Online-Signatur-Button
|
|
- Nächste Schritte
|
|
|
|
### 2. Buchungsbestätigung (`sendBookingConfirmationEmail`)
|
|
**An:** Kunde
|
|
**Anhänge:** Keine (aktuell - TODO: Auftragsbestätigung anhängen)
|
|
|
|
**Inhalt:**
|
|
- Bestätigung der verbindlichen Buchung
|
|
- Buchungsdetails
|
|
|
|
### 3. Vertragsversand (`sendContractEmail`)
|
|
**An:** Kunde
|
|
**Anhänge:**
|
|
- `Mietvertrag_{bookingNumber}.pdf`
|
|
|
|
**Inhalt:**
|
|
- Vertrag als PDF
|
|
- Online-Signatur-Link
|
|
- Hinweis auf Signatur-Möglichkeiten
|
|
|
|
---
|
|
|
|
## Datenbank-Felder (Booking)
|
|
|
|
### LexOffice-Integration
|
|
- `lexofficeContactId` - LexOffice Kontakt-ID
|
|
- `lexofficeOfferId` - LexOffice Angebots-ID
|
|
- `lexofficeConfirmationId` - LexOffice Auftragsbestätigungs-ID
|
|
- `lexofficeInvoiceId` - LexOffice Rechnungs-ID (TODO)
|
|
- `confirmationSentAt` - Zeitpunkt Admin-Bestätigung
|
|
|
|
### Contract Management
|
|
- `contractGenerated` - PDF wurde generiert
|
|
- `contractGeneratedAt` - Zeitpunkt Generierung
|
|
- `contractSentAt` - Zeitpunkt Versand
|
|
- `contractSigned` - Vertrag unterschrieben
|
|
- `contractSignedAt` - Zeitpunkt Unterschrift
|
|
- `contractSignedOnline` - Online vs. Upload
|
|
- `contractSignatureData` - Base64 Signatur-Bild
|
|
- `contractSignedBy` - Name des Unterzeichners
|
|
- `contractSignedIp` - IP-Adresse bei Online-Signatur
|
|
- `contractPdfUrl` - URL zum finalen PDF (optional)
|
|
|
|
### Kalender-Sync
|
|
- `calendarEventId` - Nextcloud Event-UID
|
|
- `calendarSynced` - Sync erfolgreich
|
|
- `calendarSyncedAt` - Zeitpunkt letzter Sync
|
|
|
|
---
|
|
|
|
## Konfiguration
|
|
|
|
### Umgebungsvariablen
|
|
|
|
```env
|
|
# LexOffice
|
|
LEXOFFICE_API_KEY=your_api_key
|
|
|
|
# Nextcloud
|
|
NEXTCLOUD_URL=https://your-nextcloud.com
|
|
NEXTCLOUD_USERNAME=username
|
|
NEXTCLOUD_PASSWORD=app_password
|
|
|
|
# E-Mail Test-Modus
|
|
TEST_MODE=true
|
|
TEST_EMAIL_RECIPIENT=test@example.com
|
|
EMAIL_ENABLED=true
|
|
|
|
# Base URL
|
|
NEXTAUTH_URL=https://your-domain.com
|
|
```
|
|
|
|
### LexOffice-Artikel-IDs
|
|
|
|
Artikel-IDs sind in `PriceConfig` hinterlegt:
|
|
- `lexofficeArticleId` - Fotobox ohne Druckflatrate
|
|
- `lexofficeArticleIdWithFlat` - Fotobox mit Druckflatrate
|
|
- `lexofficeKmFlatArticleId` - Kilometerpauschale (optional)
|
|
- `lexofficeKmExtraArticleId` - Zusatzkilometer (optional)
|
|
|
|
**Setup:** `scripts/setup-lexoffice-mapping.ts`
|
|
|
|
---
|
|
|
|
## Testing
|
|
|
|
### Manuelle Tests
|
|
|
|
1. **Test Automation:**
|
|
```bash
|
|
curl -X GET http://localhost:3000/api/admin/test-automation \
|
|
-H "Cookie: next-auth.session-token=YOUR_TOKEN"
|
|
```
|
|
|
|
2. **Test Signatur:**
|
|
```bash
|
|
curl -X POST http://localhost:3000/api/bookings/{id}/sign \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"signatureData": "data:image/png;base64,..."}'
|
|
```
|
|
|
|
3. **Test Bestätigung:**
|
|
```bash
|
|
curl -X POST http://localhost:3000/api/bookings/{id}/confirm \
|
|
-H "Cookie: next-auth.session-token=YOUR_TOKEN"
|
|
```
|
|
|
|
### E-Mail-Sync Test
|
|
|
|
```bash
|
|
npm run sync-emails
|
|
# oder
|
|
node scripts/manual-email-sync.ts
|
|
```
|
|
|
|
---
|
|
|
|
## Fehlerbehandlung
|
|
|
|
### Automatisierung (Non-Blocking)
|
|
|
|
Alle Aktionen in `runPostBookingActions()` sind try-catch geschützt:
|
|
- LexOffice-Fehler → logged, aber kein Abbruch
|
|
- PDF-Fehler → logged, aber kein Abbruch
|
|
- E-Mail-Fehler → logged, nur wenn beide PDFs verfügbar
|
|
- Kalender-Fehler → logged, aber kein Abbruch
|
|
|
|
**Return-Objekt:**
|
|
```typescript
|
|
{
|
|
emailSent: boolean;
|
|
calendarSynced: boolean;
|
|
lexofficeCreated: boolean;
|
|
contractGenerated: boolean;
|
|
errors: string[];
|
|
}
|
|
```
|
|
|
|
### Error-Logs
|
|
|
|
Alle Fehler werden in Console geloggt mit Emoji-Präfix:
|
|
- ✅ Erfolg
|
|
- ❌ Fehler
|
|
- ⚠️ Warnung
|
|
- 🤖 Automation Start
|
|
- 💼 LexOffice
|
|
- 📄 PDF
|
|
- 📧 E-Mail
|
|
- 📅 Kalender
|
|
|
|
---
|
|
|
|
## Nächste Schritte
|
|
|
|
### Kurzfristig
|
|
1. ✅ Frontend für Online-Signatur erstellen (`/contract/sign/{token}`)
|
|
2. ✅ Dashboard-Button für Admin-Bestätigung
|
|
3. ✅ Testing mit echter Buchung
|
|
|
|
### Mittelfristig
|
|
1. Rechnung-Scheduler implementieren (Cron-Job)
|
|
2. E-Mail-Template für Rechnung
|
|
3. Zahlungsstatus-Tracking
|
|
|
|
### Langfristig
|
|
1. Webhook-Integration für LexOffice (statt Polling)
|
|
2. SMS-Benachrichtigungen
|
|
3. Customer-Portal für Buchungsübersicht
|
|
|
|
---
|
|
|
|
## Architektur-Entscheidungen
|
|
|
|
### Warum Custom LineItems statt nur LexOffice-Artikel?
|
|
|
|
**Problem:** Kilometerpreise sind standortabhängig (Lübeck: 60€/15km, Hamburg: 100€/60km)
|
|
|
|
**Lösung:** Hybrid-Ansatz
|
|
- Fotobox: LexOffice-Artikel (wenn ID vorhanden)
|
|
- KM-Pauschale: Custom LineItem mit dynamischem Preis
|
|
- Extras: LexOffice-Artikel oder Custom
|
|
|
|
### Warum Promise-basiertes Singleton für Nextcloud?
|
|
|
|
**Problem:** Race-Conditions bei parallelen Initialisierungen
|
|
|
|
**Lösung:** `initPromise`-Pattern
|
|
- Erste Initialisierung erstellt Promise
|
|
- Alle weiteren Aufrufe warten auf gleiche Promise
|
|
- Bei Fehler: Reset für erneuten Versuch
|
|
|
|
### Warum Non-Blocking Automation?
|
|
|
|
**Problem:** Ein Fehler (z.B. LexOffice down) sollte nicht den gesamten Prozess stoppen
|
|
|
|
**Lösung:** Granulare Error-Handling
|
|
- Jede Aktion in eigenem try-catch
|
|
- Fehler werden gesammelt, aber nicht propagiert
|
|
- Partial Success möglich (z.B. E-Mail ja, Kalender nein)
|
|
|
|
---
|
|
|
|
## Support
|
|
|
|
Bei Fragen oder Problemen:
|
|
1. Logs prüfen (Console-Output mit Emojis)
|
|
2. `.env` Konfiguration validieren
|
|
3. Nextcloud-Credentials testen: `node test-nextcloud-connection.js`
|
|
4. LexOffice-Artikel-IDs prüfen: `node scripts/setup-lexoffice-mapping.ts`
|
|
|
|
---
|
|
|
|
**Version:** 1.0
|
|
**Letzte Aktualisierung:** 2025-11-12
|
|
**Entwickler:** Dennis Forte mit KI-Unterstützung
|