Files
Atlas/lib/booking-automation.ts
Julia Wehden a2c95c70e7 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
2026-03-19 16:21:55 +01:00

190 lines
6.1 KiB
TypeScript

import { prisma } from './prisma';
import { sendInitialBookingEmail } from './email-service';
import { nextcloudCalendar } from './nextcloud-calendar';
import { lexofficeService } from './lexoffice';
import { generateContractFromTemplate } from './pdf-template-service';
export class BookingAutomationService {
async runPostBookingActions(bookingId: string): Promise<{
emailSent: boolean;
calendarSynced: boolean;
lexofficeCreated: boolean;
contractGenerated: boolean;
errors: string[];
}> {
const errors: string[] = [];
let emailSent = false;
let calendarSynced = false;
let lexofficeCreated = false;
let contractGenerated = false;
try {
const booking = await prisma.booking.findUnique({
where: { id: bookingId },
include: {
location: true,
photobox: true,
bookingEquipment: {
include: {
equipment: true,
},
},
},
});
if (!booking) {
errors.push('Buchung nicht gefunden');
return { emailSent, calendarSynced, lexofficeCreated, contractGenerated, errors };
}
let priceConfig = null;
if (booking.photobox?.model && booking.locationId) {
priceConfig = await prisma.priceConfig.findUnique({
where: {
locationId_model: {
locationId: booking.locationId,
model: booking.photobox.model,
},
},
});
}
const bookingWithPriceConfig = {
...booking,
priceConfig,
};
console.log(`🤖 Automatische Aktionen für Buchung ${booking.bookingNumber}...`);
let quotationPdf: Buffer | null = null;
let contractPdf: Buffer | null = null;
// 1. LexOffice Contact + Quotation erstellen
try {
console.log(' 💼 Erstelle LexOffice-Kontakt und Angebot...');
const contactId = await lexofficeService.createContactFromBooking(bookingWithPriceConfig);
console.log(` ✅ LexOffice-Kontakt erstellt: ${contactId}`);
const quotationId = await lexofficeService.createQuotationFromBooking(bookingWithPriceConfig, contactId);
console.log(` ✅ LexOffice-Angebot erstellt: ${quotationId}`);
await prisma.booking.update({
where: { id: booking.id },
data: {
lexofficeContactId: contactId,
lexofficeOfferId: quotationId,
},
});
quotationPdf = await lexofficeService.getQuotationPDF(quotationId);
console.log(' ✅ Angebots-PDF heruntergeladen');
lexofficeCreated = true;
} catch (error: any) {
console.error(' ❌ LexOffice Fehler:', error.message);
errors.push(`LexOffice: ${error.message}`);
}
// 2. Mietvertrag-PDF generieren
try {
console.log(' 📄 Generiere Mietvertrag-PDF...');
contractPdf = await generateContractFromTemplate(
bookingWithPriceConfig,
booking.location,
booking.photobox
);
await prisma.booking.update({
where: { id: booking.id },
data: {
contractGenerated: true,
contractGeneratedAt: new Date(),
},
});
console.log(' ✅ Mietvertrag-PDF generiert');
contractGenerated = true;
} catch (error: any) {
console.error(' ❌ PDF-Generierung Fehler:', error.message);
errors.push(`PDF: ${error.message}`);
}
// 3. E-Mail mit Angebot + Vertrag versenden
if (quotationPdf && contractPdf) {
try {
console.log(' 📧 Sende E-Mail mit Angebot und Vertrag...');
await sendInitialBookingEmail(bookingWithPriceConfig, quotationPdf, contractPdf);
await prisma.booking.update({
where: { id: booking.id },
data: {
contractSentAt: new Date(),
},
});
emailSent = true;
console.log(' ✅ E-Mail gesendet');
} catch (error: any) {
console.error(' ❌ E-Mail Fehler:', error.message);
errors.push(`E-Mail: ${error.message}`);
}
} else {
console.warn(' ⚠️ E-Mail nicht gesendet - PDFs fehlen');
errors.push('E-Mail: PDFs nicht verfügbar');
}
// 4. Automatischer Nextcloud Kalender-Sync
try {
console.log(' 📅 Synchronisiere mit Nextcloud-Kalender...');
await nextcloudCalendar.syncBookingToCalendar(booking);
await prisma.booking.update({
where: { id: booking.id },
data: {
calendarSynced: true,
calendarSyncedAt: new Date(),
},
});
calendarSynced = true;
console.log(' ✅ Kalender synchronisiert');
} catch (error: any) {
console.error(' ❌ Kalender-Sync Fehler:', error.message);
errors.push(`Kalender: ${error.message}`);
}
// 5. Admin-Benachrichtigung erstellen
try {
await prisma.notification.create({
data: {
type: 'NEW_BOOKING',
title: 'Neue Buchungsanfrage',
message: `${booking.customerName} hat eine ${booking.photobox?.model || 'Fotobox'} für ${booking.eventCity} am ${new Date(booking.eventDate).toLocaleDateString('de-DE')} angefragt.`,
metadata: {
bookingId: booking.id,
bookingNumber: booking.bookingNumber,
},
},
});
console.log(' ✅ Admin-Benachrichtigung erstellt');
} catch (error: any) {
console.error(' ❌ Notification Fehler:', error.message);
}
console.log(`✅ Automatische Aktionen abgeschlossen (${errors.length} Fehler)`);
return { emailSent, calendarSynced, lexofficeCreated, contractGenerated, errors };
} catch (error: any) {
console.error('❌ Booking Automation Fehler:', error);
errors.push(error.message);
return { emailSent, calendarSynced, lexofficeCreated, contractGenerated, errors };
}
}
}
export const bookingAutomationService = new BookingAutomationService();