Files
Atlas/prisma/schema.prisma
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

554 lines
14 KiB
Plaintext

generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
enum UserRole {
ADMIN
DRIVER
}
enum BookingStatus {
RESERVED // Initial (nach E-Mail-Eingang)
CONFIRMED // Admin hat bestätigt & gesendet
READY_FOR_ASSIGNMENT // Vertrag unterschrieben → Admin-Freigabe
OPEN_FOR_DRIVERS // Admin hat freigegeben → Fahrer können sich melden
ASSIGNED // Admin hat Fahrer zugewiesen & Tour erstellt
COMPLETED // Event abgeschlossen
CANCELLED // Storniert
}
enum PhotoboxStatus {
AVAILABLE
IN_USE
MAINTENANCE
DAMAGED
}
enum PhotoboxModel {
VINTAGE_SMILE
VINTAGE_PHOTOS
NOSTALGIE
MAGIC_MIRROR
VINTAGE
}
enum InvoiceType {
PRIVATE
BUSINESS
}
enum EquipmentType {
PRINTER
CARPET
VIP_BARRIER
ACCESSORIES_KIT
PRINTER_PAPER
TRIPOD
OTHER
}
enum EquipmentStatus {
AVAILABLE
IN_USE
MAINTENANCE
DAMAGED
RESERVED
}
model User {
id String @id @default(cuid())
email String @unique
name String
password String
role UserRole @default(DRIVER)
phoneNumber String?
vehiclePlate String?
vehicleModel String?
active Boolean @default(true)
available Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
driverTours Tour[]
driverLocations DriverLocation[]
notifications Notification[]
driverAvailability DriverAvailability[]
}
model Location {
id String @id @default(cuid())
name String
city String
slug String @unique
websiteUrl String
contactEmail String
active Boolean @default(true)
warehouseAddress String?
warehouseZip String?
warehouseCity String?
imapHost String?
imapPort Int?
imapUser String?
imapPassword String?
imapSecure Boolean @default(true)
smtpHost String?
smtpPort Int?
smtpUser String?
smtpPassword String?
smtpSecure Boolean @default(true)
emailSyncEnabled Boolean @default(false)
lastEmailSync DateTime?
priceConfig PriceConfig[]
photoboxes Photobox[]
bookings Booking[]
equipment Equipment[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([slug])
}
model PriceConfig {
id String @id @default(cuid())
locationId String
location Location @relation(fields: [locationId], references: [id], onDelete: Cascade)
model PhotoboxModel
basePrice Float
kmFlatRate Float @default(0)
kmFlatRateUpTo Int @default(15)
pricePerKm Float @default(0)
kmMultiplier Int @default(4)
lexofficeArticleId String?
lexofficeArticleIdWithFlat String?
lexofficeKmFlatArticleId String?
lexofficeKmExtraArticleId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([locationId, model])
}
model Photobox {
id String @id @default(cuid())
locationId String
location Location @relation(fields: [locationId], references: [id], onDelete: Cascade)
model PhotoboxModel
serialNumber String @unique
status PhotoboxStatus @default(AVAILABLE)
active Boolean @default(true)
description String?
purchaseDate DateTime?
lastMaintenance DateTime?
bookings Booking[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([locationId, model])
@@index([status])
}
model Booking {
id String @id @default(cuid())
bookingNumber String @unique
locationId String
location Location @relation(fields: [locationId], references: [id])
photoboxId String?
photobox Photobox? @relation(fields: [photoboxId], references: [id])
status BookingStatus @default(RESERVED)
customerName String
customerEmail String
customerPhone String
customerAddress String?
customerCity String?
customerZip String?
invoiceType InvoiceType @default(PRIVATE)
companyName String?
eventDate DateTime
eventAddress String
eventCity String
eventZip String
eventLocation String?
setupTimeStart DateTime
setupTimeLatest DateTime
dismantleTimeEarliest DateTime?
dismantleTimeLatest DateTime?
withPrintFlat Boolean @default(true)
distance Float?
calculatedPrice Float?
// Contract Management
contractSigned Boolean @default(false)
contractSignedAt DateTime?
contractGenerated Boolean @default(false)
contractGeneratedAt DateTime?
contractSentAt DateTime?
contractSignedOnline Boolean @default(false)
contractPdfUrl String?
contractSignatureData String? @db.Text
contractSignedBy String?
contractSignedIp String?
contractUploadedBy String?
lexofficeOfferId String?
lexofficeInvoiceId String?
lexofficeContactId String?
lexofficeConfirmationId String?
confirmationSentAt DateTime?
// KI-Analyse
aiParsed Boolean @default(false)
aiResponseDraft String? @db.Text
aiProcessedAt DateTime?
// Freigabe-Status
readyForAssignment Boolean @default(false)
openForDrivers Boolean @default(false)
// Kalender-Sync (Nextcloud)
calendarEventId String?
calendarSynced Boolean @default(false)
calendarSyncedAt DateTime?
tourId String?
tour Tour? @relation(fields: [tourId], references: [id])
tourStops TourStop[]
notes String?
internalNotes String?
emails Email[]
bookingEquipment BookingEquipment[]
driverAvailability DriverAvailability[]
setupWindows SetupWindow[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([eventDate])
@@index([status])
@@index([locationId])
}
model SetupWindow {
id String @id @default(cuid())
bookingId String
booking Booking @relation(fields: [bookingId], references: [id], onDelete: Cascade)
setupDate DateTime
setupTimeStart DateTime
setupTimeEnd DateTime
preferred Boolean @default(false)
selected Boolean @default(false)
notes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([bookingId])
@@index([setupDate])
}
enum TourStatus {
PLANNED
IN_PROGRESS
COMPLETED
CANCELLED
}
enum StopStatus {
PENDING // Noch nicht erreicht
EN_ROUTE // Fahrer ist unterwegs
ARRIVED // Fahrer ist angekommen
SETUP_STARTED // Aufbau begonnen
SETUP_COMPLETE // Aufbau abgeschlossen
PICKUP_STARTED // Abbau/Abholung begonnen
PICKUP_COMPLETE // Abholung abgeschlossen
SKIPPED // Übersprungen
ISSUE // Problem aufgetreten
}
enum PhotoType {
SETUP_BEFORE // Vor dem Aufbau
SETUP_AFTER // Nach dem Aufbau
PICKUP_BEFORE // Vor dem Abbau
PICKUP_AFTER // Nach dem Abbau
ISSUE // Problem-Dokumentation
OTHER // Sonstiges
}
model Tour {
id String @id @default(cuid())
tourDate DateTime
tourNumber String @unique
driverId String?
driver User? @relation(fields: [driverId], references: [id])
bookings Booking[]
tourStops TourStop[]
routeOptimized Json?
totalDistance Float?
estimatedDuration Int?
status TourStatus @default(PLANNED)
startedAt DateTime?
completedAt DateTime?
notes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([tourDate])
@@index([driverId])
@@index([status])
}
model TourStop {
id String @id @default(cuid())
tourId String
tour Tour @relation(fields: [tourId], references: [id], onDelete: Cascade)
bookingId String
booking Booking @relation(fields: [bookingId], references: [id])
stopOrder Int // Reihenfolge der Stopps (1, 2, 3, ...)
stopType String @default("DELIVERY") // DELIVERY, PICKUP, BOTH
status StopStatus @default(PENDING)
// Timestamps für jeden Status
arrivedAt DateTime?
setupStartedAt DateTime?
setupCompleteAt DateTime?
pickupStartedAt DateTime?
pickupCompleteAt DateTime?
// Optional: GPS-Position bei Ankunft
arrivalLatitude Float?
arrivalLongitude Float?
// Notizen vom Fahrer
notes String? @db.Text
issueDescription String? @db.Text
photos DeliveryPhoto[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([tourId, bookingId])
@@index([tourId, stopOrder])
@@index([status])
}
model DriverLocation {
id String @id @default(cuid())
driverId String
driver User @relation(fields: [driverId], references: [id], onDelete: Cascade)
latitude Float
longitude Float
accuracy Float? // GPS-Genauigkeit in Metern
heading Float? // Richtung in Grad (0-360)
speed Float? // Geschwindigkeit in km/h
tourId String?
// Optional: Welcher Stopp ist aktuell?
currentStopId String?
createdAt DateTime @default(now())
@@index([driverId, createdAt])
@@index([tourId])
}
model DeliveryPhoto {
id String @id @default(cuid())
tourStopId String
tourStop TourStop @relation(fields: [tourStopId], references: [id], onDelete: Cascade)
photoType PhotoType
// Dateispeicherung (z.B. /uploads/photos/...)
fileName String
filePath String
fileSize Int?
mimeType String?
// Optional: GPS-Position wo das Foto aufgenommen wurde
latitude Float?
longitude Float?
caption String? @db.Text
uploadedAt DateTime @default(now())
@@index([tourStopId])
@@index([photoType])
}
model Notification {
id String @id @default(cuid())
userId String?
user User? @relation(fields: [userId], references: [id])
type String
title String
message String
read Boolean @default(false)
metadata Json?
createdAt DateTime @default(now())
@@index([userId, read])
}
model Email {
id String @id @default(cuid())
locationSlug String?
from String
to String
subject String
textBody String?
htmlBody String?
messageId String? @unique
inReplyTo String?
bookingId String?
booking Booking? @relation(fields: [bookingId], references: [id])
parsed Boolean @default(false)
parsedData Json?
direction String @default("INBOUND")
receivedAt DateTime @default(now())
createdAt DateTime @default(now())
@@index([locationSlug])
@@index([bookingId])
@@index([receivedAt])
}
model Project {
id String @id @default(cuid())
name String
description String?
active Boolean @default(true)
equipment Equipment[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Equipment {
id String @id @default(cuid())
name String
type EquipmentType
brand String?
model String?
serialNumber String? @unique
quantity Int @default(1)
status EquipmentStatus @default(AVAILABLE)
locationId String?
location Location? @relation(fields: [locationId], references: [id])
projectId String?
project Project? @relation(fields: [projectId], references: [id])
notes String?
purchaseDate DateTime?
purchasePrice Decimal?
price Float?
lexofficeArticleId String?
minStockLevel Int?
currentStock Int?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
bookingEquipment BookingEquipment[]
@@index([type])
@@index([status])
@@index([locationId])
}
model BookingEquipment {
id String @id @default(cuid())
bookingId String
booking Booking @relation(fields: [bookingId], references: [id], onDelete: Cascade)
equipmentId String
equipment Equipment @relation(fields: [equipmentId], references: [id])
quantity Int @default(1)
createdAt DateTime @default(now())
@@unique([bookingId, equipmentId])
@@index([bookingId])
@@index([equipmentId])
}
model DriverAvailability {
id String @id @default(cuid())
bookingId String
booking Booking @relation(fields: [bookingId], references: [id], onDelete: Cascade)
driverId String
driver User @relation(fields: [driverId], references: [id], onDelete: Cascade)
available Boolean @default(true)
message String? @db.Text
createdAt DateTime @default(now())
@@unique([bookingId, driverId])
@@index([bookingId])
@@index([driverId])
}