- 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
135 lines
4.0 KiB
TypeScript
135 lines
4.0 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server';
|
|
import { getServerSession } from 'next-auth';
|
|
import { authOptions } from '@/lib/auth';
|
|
import { prisma } from '@/lib/prisma';
|
|
import { nextcloudCalendar } from '@/lib/nextcloud-calendar';
|
|
|
|
function generateBookingNumber(): string {
|
|
const date = new Date();
|
|
const year = date.getFullYear().toString().slice(-2);
|
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
const random = Math.floor(Math.random() * 10000).toString().padStart(4, '0');
|
|
return `STM-${year}${month}-${random}`;
|
|
}
|
|
|
|
export async function POST(request: NextRequest) {
|
|
try {
|
|
const session = await getServerSession(authOptions);
|
|
|
|
if (!session || session.user.role !== 'ADMIN') {
|
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
}
|
|
|
|
const body = await request.json();
|
|
|
|
const eventDate = new Date(body.eventDate);
|
|
const startOfDay = new Date(eventDate);
|
|
startOfDay.setHours(0, 0, 0, 0);
|
|
const endOfDay = new Date(eventDate);
|
|
endOfDay.setHours(23, 59, 59, 999);
|
|
|
|
const availablePhotobox = await prisma.photobox.findFirst({
|
|
where: {
|
|
locationId: body.locationId,
|
|
model: body.model,
|
|
active: true,
|
|
NOT: {
|
|
bookings: {
|
|
some: {
|
|
eventDate: {
|
|
gte: startOfDay,
|
|
lte: endOfDay,
|
|
},
|
|
status: {
|
|
in: ['RESERVED', 'CONFIRMED'],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
if (!availablePhotobox) {
|
|
return NextResponse.json(
|
|
{ error: 'Keine Fotobox verfügbar für dieses Datum' },
|
|
{ status: 409 }
|
|
);
|
|
}
|
|
|
|
const booking = await prisma.booking.create({
|
|
data: {
|
|
bookingNumber: generateBookingNumber(),
|
|
locationId: body.locationId,
|
|
photoboxId: availablePhotobox.id,
|
|
status: 'RESERVED',
|
|
|
|
customerName: body.customerName,
|
|
customerEmail: body.customerEmail,
|
|
customerPhone: body.customerPhone,
|
|
customerAddress: body.customerAddress,
|
|
customerCity: body.customerCity,
|
|
customerZip: body.customerZip,
|
|
|
|
invoiceType: body.invoiceType,
|
|
companyName: body.companyName,
|
|
|
|
eventDate: new Date(body.eventDate),
|
|
eventAddress: body.eventAddress,
|
|
eventCity: body.eventCity,
|
|
eventZip: body.eventZip,
|
|
eventLocation: body.eventLocation,
|
|
|
|
setupTimeStart: new Date(body.setupTimeStart),
|
|
setupTimeLatest: new Date(body.setupTimeLatest),
|
|
dismantleTimeEarliest: body.dismantleTimeEarliest ? new Date(body.dismantleTimeEarliest) : null,
|
|
dismantleTimeLatest: body.dismantleTimeLatest ? new Date(body.dismantleTimeLatest) : null,
|
|
|
|
calculatedPrice: body.calculatedPrice,
|
|
notes: body.notes,
|
|
},
|
|
include: {
|
|
location: true,
|
|
photobox: true,
|
|
},
|
|
});
|
|
|
|
await prisma.notification.create({
|
|
data: {
|
|
type: 'NEW_BOOKING_MANUAL',
|
|
title: 'Neue manuelle Buchung',
|
|
message: `${body.customerName} - ${eventDate.toLocaleDateString('de-DE')}`,
|
|
metadata: {
|
|
bookingId: booking.id,
|
|
bookingNumber: booking.bookingNumber,
|
|
},
|
|
},
|
|
});
|
|
|
|
if (Array.isArray(body.equipmentIds) && body.equipmentIds.length > 0) {
|
|
await prisma.bookingEquipment.createMany({
|
|
data: body.equipmentIds.map((eqId: string) => ({
|
|
bookingId: booking.id,
|
|
equipmentId: eqId,
|
|
})),
|
|
});
|
|
}
|
|
|
|
try {
|
|
await nextcloudCalendar.syncBookingToCalendar(booking);
|
|
} catch (calError) {
|
|
console.error('Calendar sync error after booking creation:', calError);
|
|
}
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
booking,
|
|
});
|
|
} catch (error: any) {
|
|
console.error('Booking creation error:', error);
|
|
return NextResponse.json(
|
|
{ error: error.message || 'Internal server error' },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|