generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } enum UserRole { PASSENGER ADMIN STAFF } enum TripStatus { SCHEDULED BOARDING EN_ROUTE ARRIVED CANCELLED DELAYED } enum SeatKind { STANDARD PREMIUM ACCESSIBLE } enum SeatStatus { AVAILABLE HELD BOOKED BLOCKED } enum ServiceClass { ECONOMY BUSINESS FIRST } enum BookingStatus { DRAFT PENDING_PAYMENT CONFIRMED CANCELLED COMPLETED NO_SHOW } enum PaymentMethodType { TELEBIRR CBE_BIRR EBIRR CARD WALLET } enum PaymentIntentStatus { REQUIRES_ACTION PROCESSING SUCCEEDED FAILED CANCELLED } enum WalletLedgerType { CREDIT DEBIT } enum NotificationCategory { BOOKING PAYMENT DISRUPTION PROMOTION SYSTEM } enum StopStatus { COMPLETED APPROACHING CURRENT UPCOMING } enum SupportConversationStatus { OPEN RESOLVED CLOSED } enum SupportSender { USER BOT AGENT } enum LoyaltyTier { BRONZE SILVER GOLD PLATINUM } enum LoyaltyLedgerReason { TRIP_COMPLETED REWARD_REDEEMED PROMO_BONUS MANUAL_ADJUSTMENT EXPIRY } enum FoodOrderStatus { PENDING PREPARING READY DELIVERED CANCELLED } enum DevicePlatform { IOS ANDROID WEB } model User { id String @id @default(uuid()) email String @unique phone String @unique fullName String passwordHash String role UserRole @default(PASSENGER) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt passenger Passenger? sessions Session[] devices Device[] preferences UserPreferences? } model Session { id String @id @default(uuid()) userId String token String @unique expiresAt DateTime createdAt DateTime @default(now()) user User @relation(fields: [userId], references: [id], onDelete: Cascade) } model Passenger { id String @id @default(uuid()) userId String @unique createdAt DateTime @default(now()) user User @relation(fields: [userId], references: [id]) bookings Booking[] loyalty LoyaltyAccount? wallet WalletAccount? notifications Notification[] travelerProfiles TravelerProfile[] savedRoutes SavedRoute[] } model TravelerProfile { id String @id @default(uuid()) passengerId String fullName String relationship String dateOfBirth DateTime? nationalId String? notes String? createdAt DateTime @default(now()) passenger Passenger @relation(fields: [passengerId], references: [id]) } model Station { id String @id @default(uuid()) code String @unique name String city String timezone String @default("Africa/Addis_Ababa") lat Decimal @db.Decimal(9, 6) lng Decimal @db.Decimal(9, 6) originTrips Trip[] @relation("OriginTrips") destinationTrips Trip[] @relation("DestinationTrips") stopTimes TripStopTime[] crowdSignals StationCrowdSignal[] } model TrainService { id String @id @default(uuid()) number String @unique name String operatorId String @default("op_edr") trips Trip[] } model Trip { id String @id @default(uuid()) serviceId String originStationId String destinationStationId String departureAt DateTime arrivalAt DateTime durationMinutes Int status TripStatus @default(SCHEDULED) stopsCount Int @default(0) onTimePercent Int @default(100) carbonRating String @default("A") service TrainService @relation(fields: [serviceId], references: [id]) originStation Station @relation("OriginTrips", fields: [originStationId], references: [id]) destinationStation Station @relation("DestinationTrips", fields: [destinationStationId], references: [id]) coaches Coach[] bookings Booking[] stopTimes TripStopTime[] liveStatus TripLiveStatus? menuItems MenuItem[] } model TripStopTime { id String @id @default(uuid()) tripId String stationId String sequence Int plannedArrivalAt DateTime? plannedDepartureAt DateTime? actualArrivalAt DateTime? status StopStatus @default(UPCOMING) trip Trip @relation(fields: [tripId], references: [id]) station Station @relation(fields: [stationId], references: [id]) @@unique([tripId, sequence]) } model TripLiveStatus { id String @id @default(uuid()) tripId String @unique state String currentLocationLabel String? progressPercent Int @default(0) delayMinutes Int @default(0) currentSpeedKph Int? platformLabel String? updatedAt DateTime @updatedAt trip Trip @relation(fields: [tripId], references: [id]) } model Coach { id String @id @default(uuid()) tripId String label String serviceClass ServiceClass trip Trip @relation(fields: [tripId], references: [id]) seats Seat[] @@unique([tripId, label]) } model Seat { id String @id @default(uuid()) coachId String row Int col String label String kind SeatKind @default(STANDARD) status SeatStatus @default(AVAILABLE) heldUntil DateTime? coach Coach @relation(fields: [coachId], references: [id]) bookingSeats BookingSeat[] @@unique([coachId, row, col]) } model SeatHold { id String @id @default(uuid()) tripId String seatIds String[] fareQuoteId String? passengerId String expiresAt DateTime createdAt DateTime @default(now()) } model FareRule { id String @id @default(uuid()) tripId String? route String? serviceClass ServiceClass baseFareMinor Int currency String @default("ETB") refundable Boolean @default(true) validFrom DateTime validUntil DateTime? createdAt DateTime @default(now()) } model Booking { id String @id @default(uuid()) bookingRef String @unique passengerId String tripId String status BookingStatus @default(DRAFT) currency String @default("ETB") totalMinor Int createdAt DateTime @default(now()) updatedAt DateTime @updatedAt passenger Passenger @relation(fields: [passengerId], references: [id]) trip Trip @relation(fields: [tripId], references: [id]) seats BookingSeat[] paymentIntent PaymentIntent? ticket Ticket? foodOrders FoodOrder[] } model BookingSeat { id String @id @default(uuid()) bookingId String seatId String passengerName String idDocumentType String? idDocumentNumber String? booking Booking @relation(fields: [bookingId], references: [id]) seat Seat @relation(fields: [seatId], references: [id]) } model PaymentMethod { id String @id @default(uuid()) userId String type PaymentMethodType displayName String maskedHint String? isDefault Boolean @default(false) createdAt DateTime @default(now()) } model PaymentIntent { id String @id @default(uuid()) bookingId String @unique amountMinor Int currency String @default("ETB") method PaymentMethodType status PaymentIntentStatus @default(REQUIRES_ACTION) providerRef String? clientAction Json? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt booking Booking @relation(fields: [bookingId], references: [id]) } model Ticket { id String @id @default(uuid()) bookingId String @unique bookingRef String status String @default("CONFIRMED") qrPayload String issuedAt DateTime @default(now()) validatedAt DateTime? validatorId String? booking Booking @relation(fields: [bookingId], references: [id]) } model LoyaltyAccount { id String @id @default(uuid()) passengerId String @unique pointsBalance Int @default(0) tier LoyaltyTier @default(BRONZE) updatedAt DateTime @updatedAt passenger Passenger @relation(fields: [passengerId], references: [id]) ledger LoyaltyLedgerEntry[] rewards LoyaltyReward[] } model LoyaltyLedgerEntry { id String @id @default(uuid()) accountId String delta Int reason LoyaltyLedgerReason bookingId String? balanceAfter Int createdAt DateTime @default(now()) account LoyaltyAccount @relation(fields: [accountId], references: [id]) } model LoyaltyReward { id String @id @default(uuid()) accountId String title String costPoints Int available Boolean @default(true) description String? account LoyaltyAccount @relation(fields: [accountId], references: [id]) } model WalletAccount { id String @id @default(uuid()) passengerId String @unique balanceMinor Int @default(0) currency String @default("ETB") updatedAt DateTime @updatedAt passenger Passenger @relation(fields: [passengerId], references: [id]) ledger WalletLedgerEntry[] } model WalletLedgerEntry { id String @id @default(uuid()) walletId String type WalletLedgerType amountMinor Int balanceAfterMinor Int description String relatedBookingId String? createdAt DateTime @default(now()) wallet WalletAccount @relation(fields: [walletId], references: [id]) } model Notification { id String @id @default(uuid()) passengerId String title String body String category NotificationCategory read Boolean @default(false) deepLink String? metadata Json? createdAt DateTime @default(now()) passenger Passenger @relation(fields: [passengerId], references: [id]) } model Promotion { id String @id @default(uuid()) title String subtitle String? code String @unique percentOff Int? amountOffMinor Int? validUntil DateTime ctaLabel String? deepLink String? active Boolean @default(true) createdAt DateTime @default(now()) } model StationCrowdSignal { id String @id @default(uuid()) stationId String level String label String statusLabel String updatedAt DateTime @updatedAt station Station @relation(fields: [stationId], references: [id]) } model WeatherAlert { id String @id @default(uuid()) region String severity String title String message String validUntil DateTime createdAt DateTime @default(now()) } model MenuCategory { id String @id @default(uuid()) name String items MenuItem[] } model MenuItem { id String @id @default(uuid()) tripId String categoryId String name String priceMinor Int currency String @default("ETB") available Boolean @default(true) trip Trip @relation(fields: [tripId], references: [id]) category MenuCategory @relation(fields: [categoryId], references: [id]) } model FoodOrder { id String @id @default(uuid()) bookingId String status FoodOrderStatus @default(PENDING) totalMinor Int currency String @default("ETB") createdAt DateTime @default(now()) booking Booking @relation(fields: [bookingId], references: [id]) items FoodOrderItem[] } model FoodOrderItem { id String @id @default(uuid()) orderId String menuItemId String name String quantity Int lineTotalMinor Int order FoodOrder @relation(fields: [orderId], references: [id]) } model FaqCategory { id String @id @default(uuid()) title String iconKey String? articles FaqArticle[] } model FaqArticle { id String @id @default(uuid()) categoryId String question String answerMarkdown String rank Int @default(0) category FaqCategory @relation(fields: [categoryId], references: [id]) } model SupportConversation { id String @id @default(uuid()) userId String status SupportConversationStatus @default(OPEN) createdAt DateTime @default(now()) messages SupportMessage[] } model SupportMessage { id String @id @default(uuid()) conversationId String sender SupportSender text String createdAt DateTime @default(now()) conversation SupportConversation @relation(fields: [conversationId], references: [id]) } model UserPreferences { id String @id @default(uuid()) userId String @unique pushEnabled Boolean @default(true) emailEnabled Boolean @default(true) smsEnabled Boolean @default(false) promosEnabled Boolean @default(true) biometricEnabled Boolean @default(false) twoFactorEnabled Boolean @default(false) defaultPaymentMethodId String? autoDownloadTickets Boolean @default(true) dataSharing Boolean @default(false) locale String @default("en") darkMode Boolean @default(false) user User @relation(fields: [userId], references: [id]) } model Device { id String @id @default(uuid()) userId String platform DevicePlatform name String pushToken String? trusted Boolean @default(false) lastSeenAt DateTime @default(now()) user User @relation(fields: [userId], references: [id]) } model SavedRoute { id String @id @default(uuid()) passengerId String fromStationId String toStationId String fromName String toName String tripCount Int @default(0) createdAt DateTime @default(now()) passenger Passenger @relation(fields: [passengerId], references: [id]) }