Files
Atlas/app/api/cron/process-pending-bookings/route.ts
2025-11-12 20:21:32 +01:00

174 lines
5.6 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server';
import { prisma } from '@/lib/prisma';
import { AIService } from '@/lib/ai-service';
import { LexOfficeService } from '@/lib/lexoffice';
import { getCalendarService } from '@/lib/nextcloud-calendar';
/**
* AUTO-WORKFLOW CRON-JOB
*
* Läuft alle 5 Minuten und:
* 1. Findet Buchungen mit `aiParsed=false` (neue E-Mails)
* 2. Startet KI-Analyse
* 3. Generiert Entwürfe (E-Mail, Angebot, Vertrag)
* 4. Setzt Status auf PENDING_REVIEW
*/
export async function GET(request: NextRequest) {
try {
// Cron-Secret validieren
const authHeader = request.headers.get('authorization');
if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
console.log('🔄 Auto-Workflow Cron-Job gestartet...');
// 1. Finde neue Buchungen (noch nicht von KI analysiert)
const pendingBookings = await prisma.booking.findMany({
where: {
aiParsed: false,
status: 'RESERVED', // Nur neue Reservierungen
},
include: {
location: true,
photobox: true,
},
take: 10, // Max 10 pro Lauf
});
if (pendingBookings.length === 0) {
console.log('✅ Keine neuen Buchungen gefunden');
return NextResponse.json({
message: 'No pending bookings',
processed: 0
});
}
console.log(`📋 ${pendingBookings.length} neue Buchungen gefunden`);
const aiService = new AIService();
const lexoffice = new LexOfficeService();
const calendar = getCalendarService();
let processed = 0;
let errors = 0;
for (const booking of pendingBookings) {
try {
console.log(`\n🤖 Verarbeite Buchung: ${booking.bookingNumber}`);
// 2. KI-Analyse (falls noch nicht vorhanden)
if (!booking.aiResponseDraft) {
console.log(' → Generiere E-Mail-Entwurf...');
// Generiere einfache Antwort (später kann man das erweitern)
const emailDraft = `Hallo ${booking.customerName},
vielen Dank für Ihre Anfrage für eine Fotobox-Vermietung am ${new Date(booking.eventDate).toLocaleDateString('de-DE')} in ${booking.eventCity}!
Wir freuen uns sehr über Ihr Interesse. Anbei finden Sie unser Angebot sowie den Mietvertrag zur Durchsicht.
Falls Sie Fragen haben, stehen wir Ihnen jederzeit gerne zur Verfügung!
Mit freundlichen Grüßen
Ihr ${booking.location?.name || 'SaveTheMoment'} Team`;
await prisma.booking.update({
where: { id: booking.id },
data: {
aiResponseDraft: emailDraft,
aiProcessedAt: new Date(),
},
});
}
// 3. LexOffice Kontakt & Angebot (Entwurf)
if (!booking.lexofficeContactId) {
console.log(' → Erstelle LexOffice Kontakt...');
try {
const contactId = await lexoffice.createContactFromBooking(booking);
await prisma.booking.update({
where: { id: booking.id },
data: { lexofficeContactId: contactId },
});
console.log(' → Erstelle LexOffice Angebot-Entwurf...');
const quotationId = await lexoffice.createQuotationFromBooking(booking, contactId);
await prisma.booking.update({
where: { id: booking.id },
data: { lexofficeOfferId: quotationId },
});
} catch (lexError) {
console.error(' ⚠️ LexOffice-Fehler (wird übersprungen):', lexError);
// Weiter machen, auch wenn LexOffice fehlschlägt
}
}
// 4. Kalender-Eintrag erstellen (Reservierung)
if (!booking.calendarEventId) {
console.log(' → Erstelle Kalender-Eintrag...');
try {
const eventId = await calendar.createBookingEvent(booking);
if (eventId) {
await prisma.booking.update({
where: { id: booking.id },
data: {
calendarEventId: eventId,
calendarSynced: true,
calendarSyncedAt: new Date(),
},
});
}
} catch (calError) {
console.error(' ⚠️ Kalender-Fehler (wird übersprungen):', calError);
}
}
// 5. Status aktualisieren: Bereit für Admin-Review
await prisma.booking.update({
where: { id: booking.id },
data: {
aiParsed: true,
readyForAssignment: true, // Admin kann jetzt prüfen
},
});
console.log(`✅ Buchung ${booking.bookingNumber} erfolgreich verarbeitet`);
processed++;
} catch (error) {
console.error(`❌ Fehler bei Buchung ${booking.bookingNumber}:`, error);
errors++;
// Markiere als fehlerhaft, damit es beim nächsten Lauf erneut versucht wird
await prisma.booking.update({
where: { id: booking.id },
data: {
internalNotes: `Auto-Workflow Fehler: ${error instanceof Error ? error.message : 'Unbekannter Fehler'}`,
},
});
}
}
console.log(`\n📊 Cron-Job abgeschlossen:`);
console.log(` ✅ Erfolgreich: ${processed}`);
console.log(` ❌ Fehler: ${errors}`);
return NextResponse.json({
success: true,
processed,
errors,
total: pendingBookings.length,
});
} catch (error: any) {
console.error('❌ Cron-Job Fehler:', error);
return NextResponse.json(
{ error: error.message || 'Cron job failed' },
{ status: 500 }
);
}
}