feat: Equipment-System, Buchungsbearbeitung, Kundenadresse, LexOffice-Fix
- 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
This commit is contained in:
379
AUTOMATION-SYSTEM.md
Normal file
379
AUTOMATION-SYSTEM.md
Normal file
@@ -0,0 +1,379 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user