Files
edr-platform/apps/edr-passenger-api/prisma/schema.prisma
2026-06-16 08:49:41 +03:00

1347 lines
35 KiB
Plaintext

generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
schemas = ["passenger"]
}
enum UserRole {
PASSENGER
AGENT
SUPERVISOR
ADMIN
STAFF
@@schema("passenger")
}
enum TripStatus {
SCHEDULED
BOARDING
EN_ROUTE
ARRIVED
CANCELLED
DELAYED
@@schema("passenger")
}
enum SeatKind {
STANDARD
PREMIUM
ACCESSIBLE
@@schema("passenger")
}
enum SeatStatus {
AVAILABLE
HELD
BOOKED
BLOCKED
@@schema("passenger")
}
enum PassengerCategory {
ADULT
CHILD
@@schema("passenger")
}
enum IdDocumentType {
NATIONAL_ID
PASSPORT
DRIVING_LICENSE
OTHER
@@schema("passenger")
}
enum Currency {
ETB
DJF
USD
@@schema("passenger")
}
model CoachType {
id String @id @default(uuid())
code String
name String
type String @default("passenger") // 'passenger', 'sleeper', 'dining', 'baggage'
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
coaches Coach[]
seatClasses SeatClass[]
@@schema("passenger")
}
model SeatClass {
id String @id @default(uuid())
coachTypeId String
name String
description String?
baseFareMinor Int
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
coachType CoachType @relation(fields: [coachTypeId], references: [id])
fareRules FareRule[]
routeFareRules RouteFareRule[]
segmentFares SegmentFareRule[]
@@unique([coachTypeId, name])
@@index([coachTypeId])
@@schema("passenger")
}
enum BookingStatus {
DRAFT
PENDING_PAYMENT
CONFIRMED
CANCELLED
COMPLETED
NO_SHOW
REFUNDED
@@schema("passenger")
}
enum PaymentRegion {
ETHIOPIA
DJIBOUTI
INTERNATIONAL
GLOBAL
@@schema("passenger")
}
enum PaymentMethodType {
TELEBIRR
CBE_BIRR
EBIRR
CARD
WALLET
WAAFI
DMONEY
@@schema("passenger")
}
enum PaymentIntentStatus {
REQUIRES_ACTION
PROCESSING
SUCCEEDED
FAILED
CANCELLED
REFUNDED
@@schema("passenger")
}
enum WalletLedgerType {
CREDIT
DEBIT
@@schema("passenger")
}
enum NotificationCategory {
BOOKING
PAYMENT
DISRUPTION
PROMOTION
SYSTEM
@@schema("passenger")
}
enum StopStatus {
COMPLETED
APPROACHING
CURRENT
UPCOMING
@@schema("passenger")
}
enum SupportConversationStatus {
OPEN
RESOLVED
CLOSED
@@schema("passenger")
}
enum SupportSender {
USER
BOT
AGENT
@@schema("passenger")
}
enum LoyaltyTier {
BRONZE
SILVER
GOLD
PLATINUM
@@schema("passenger")
}
enum LoyaltyLedgerReason {
TRIP_COMPLETED
REWARD_REDEEMED
PROMO_BONUS
MANUAL_ADJUSTMENT
EXPIRY
@@schema("passenger")
}
enum FoodOrderStatus {
PENDING
PREPARING
READY
DELIVERED
CANCELLED
@@schema("passenger")
}
enum DevicePlatform {
IOS
ANDROID
WEB
@@schema("passenger")
}
model User {
id String @id @default(uuid())
email String @unique
phone String @unique
fullName String
passwordHash String
role UserRole @default(PASSENGER)
nationality String?
nationalityCode String?
passportNumber String?
nationalId String?
failedLoginAttempts Int @default(0)
lockedUntil DateTime?
blockedUntil DateTime?
lastLoginAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
faydaVerified Boolean @default(false)
faydaVerifiedAt DateTime?
faydaSub String? @unique
passenger Passenger?
agent Agent?
sessions Session[]
devices Device[]
preferences UserPreferences?
auditLogs AuditLog[]
fraudAlerts FraudAlert[]
faydaVerificationSessions FaydaVerificationSession[]
@@schema("passenger")
}
model Session {
id String @id @default(uuid())
userId String
token String @unique
expiresAt DateTime
ipAddress String?
userAgent String?
lastActivityAt DateTime @default(now())
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@schema("passenger")
}
model Passenger {
id String @id @default(uuid())
userId String @unique
defaultTravelerProfileId String?
preferredLanguage String?
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id])
bookings Booking[]
loyalty LoyaltyAccount?
wallet WalletAccount?
notifications Notification[]
travelerProfiles TravelerProfile[]
savedRoutes SavedRoute[]
@@index([userId])
@@schema("passenger")
}
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])
@@schema("passenger")
}
model Station {
id String @id @default(uuid())
code String @unique
name String
city String
countryCode String?
isOperational Boolean @default(true)
timezone String @default("Africa/Addis_Ababa")
lat Decimal @db.Decimal(9, 6)
lng Decimal @db.Decimal(9, 6)
originSchedules TrainSchedule[] @relation("OriginTrips")
destinationSchedules TrainSchedule[] @relation("DestinationTrips")
stopTimes TripStopTime[]
crowdSignals StationCrowdSignal[]
@@index([city, countryCode])
@@schema("passenger")
}
model Train {
id String @id @default(uuid())
number String @unique
name String
operatorId String @default("op_edr")
operatorName String?
description String?
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
schedules TrainSchedule[]
@@schema("passenger")
}
model TrainSchedule {
id String @id @default(uuid())
trainId String
routeId String?
originStationId String
destinationStationId String
departureAt DateTime
arrivalAt DateTime
durationMinutes Int
status TripStatus @default(SCHEDULED)
stopsCount Int @default(0)
reservedCount Int @default(0)
onTimePercent Int @default(100)
carbonRating String @default("A")
notes String?
train Train @relation(fields: [trainId], references: [id])
route Route? @relation(fields: [routeId], references: [id])
originStation Station @relation("OriginTrips", fields: [originStationId], references: [id])
destinationStation Station @relation("DestinationTrips", fields: [destinationStationId], references: [id])
coachAssignments CoachAssignment[]
bookings Booking[]
stopTimes TripStopTime[]
liveStatus TripLiveStatus?
menuItems MenuItem[]
journeySegments JourneySegment[]
@@index([departureAt, originStationId])
@@schema("passenger")
}
model TripStopTime {
id String @id @default(uuid())
scheduleId String
stationId String
sequence Int
plannedArrivalAt DateTime?
plannedDepartureAt DateTime?
actualArrivalAt DateTime?
status StopStatus @default(UPCOMING)
schedule TrainSchedule @relation(fields: [scheduleId], references: [id])
station Station @relation(fields: [stationId], references: [id])
@@unique([scheduleId, sequence])
@@schema("passenger")
}
model TripLiveStatus {
id String @id @default(uuid())
scheduleId String @unique
state String
currentLocationLabel String?
progressPercent Int @default(0)
delayMinutes Int @default(0)
currentSpeedKph Int?
platformLabel String?
updatedAt DateTime @updatedAt
schedule TrainSchedule @relation(fields: [scheduleId], references: [id])
@@schema("passenger")
}
model Coach {
id String @id @default(uuid())
coachTypeId String
number String @unique
arrangement String @default("2+2") // e.g., '2+2', '3+2', '2+2+2'
capacity Int @default(0) // Total seats/beds
status String @default("ACTIVE") // 'ACTIVE', 'MAINTENANCE', 'INACTIVE'
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
coachType CoachType @relation(fields: [coachTypeId], references: [id])
seats Seat[]
assignments CoachAssignment[]
@@index([coachTypeId])
@@schema("passenger")
}
model CoachAssignment {
id String @id @default(uuid())
scheduleId String
coachId String
positionNumber Int
isOperational Boolean @default(true)
createdAt DateTime @default(now())
schedule TrainSchedule @relation(fields: [scheduleId], references: [id])
coach Coach @relation(fields: [coachId], references: [id])
@@unique([scheduleId, positionNumber])
@@index([scheduleId])
@@schema("passenger")
}
model Seat {
id String @id @default(uuid())
coachId String
seatNumber String // Auto-generated: e.g., '1', '2', '3' (unique per coach)
row Int
col String
kind SeatKind @default(STANDARD)
status SeatStatus @default(AVAILABLE)
heldUntil DateTime?
isWindow Boolean @default(false)
isAisle Boolean @default(false)
bedPosition String? // 'lower', 'middle', 'upper'
premiumFeeMinor Int @default(0)
coach Coach @relation(fields: [coachId], references: [id])
bookingSeats BookingSeat[]
blocks SeatBlock[]
ticketSeats TicketSeat[]
@@unique([coachId, seatNumber])
@@unique([coachId, row, col])
@@index([coachId])
@@schema("passenger")
}
model SeatHold {
id String @id @default(uuid())
scheduleId String
seatIds String[]
fareQuoteId String?
passengerId String
createdBy String?
expiresAt DateTime
createdAt DateTime @default(now())
@@index([expiresAt])
@@schema("passenger")
}
model FareRule {
id String @id @default(uuid())
tripId String?
route String?
nationality String? // Ethiopian, Djiboutian, Other
seatClassId String
baseFareMinor Int
seatClass SeatClass @relation(fields: [seatClassId], references: [id])
currency String @default("ETB")
refundable Boolean @default(true)
validFrom DateTime
validUntil DateTime?
createdAt DateTime @default(now())
@@schema("passenger")
}
model Booking {
id String @id @default(uuid())
bookingRef String @unique
passengerId String
scheduleId String
status BookingStatus @default(DRAFT)
currency String @default("ETB")
totalMinor Int
adultCount Int @default(1)
childCount Int @default(0)
displayCurrency Currency?
displayTotalMinor Int?
bookingType String @default("ONE_WAY")
contactEmail String?
contactPhone String?
userAgent String?
source String @default("WEB")
promoCode String?
paidAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
passenger Passenger @relation(fields: [passengerId], references: [id])
schedule TrainSchedule @relation(fields: [scheduleId], references: [id])
seats BookingSeat[]
paymentIntent PaymentIntent?
ticket Ticket?
foodOrders FoodOrder[]
agentBooking AgentBooking?
modifications BookingModification[]
cancellation BookingCancellation?
baggage BaggageBooking[]
@@index([passengerId, status])
@@schema("passenger")
}
model BookingSeat {
id String @id @default(uuid())
bookingId String
seatId String
passengerName String
dateOfBirth DateTime?
passengerCategory PassengerCategory @default(ADULT)
idDocumentType IdDocumentType?
idDocumentNumber String?
passportNumber String?
passportCountry String?
verifaydaVerified Boolean @default(false)
verifaydaData Json?
faydaVerifiedAt DateTime?
faydaSub String?
faydaVerifiedName String?
seatLabelSnapshot String?
fareMinor Int?
displayCurrency Currency?
displayFareMinor Int?
booking Booking @relation(fields: [bookingId], references: [id])
seat Seat @relation(fields: [seatId], references: [id])
@@schema("passenger")
}
model PaymentMethod {
id String @id @default(uuid())
type PaymentMethodType @unique
displayName String
region PaymentRegion @default(GLOBAL)
currency String @default("ETB")
providerId String?
isDefault Boolean @default(false)
enabled Boolean @default(true)
sortOrder Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@schema("passenger")
}
model PaymentIntent {
id String @id @default(uuid())
bookingId String @unique
amountMinor Int
currency String @default("ETB")
method PaymentMethodType
provider String?
status PaymentIntentStatus @default(REQUIRES_ACTION)
providerRef String?
clientAction Json?
merchantOrderId String? @unique
providerOrderId String?
providerTxnId String?
rawInitiation Json?
paidAt DateTime?
refundedAt DateTime?
captureMethod String?
failureCode String?
failureMessage String?
expiresAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
booking Booking @relation(fields: [bookingId], references: [id])
refunds PaymentRefund[]
@@index([providerOrderId])
@@index([providerTxnId])
@@schema("passenger")
}
model PaymentWebhookEvent {
id String @id @default(uuid())
provider PaymentMethodType
externalEventId String
merchantOrderId String?
providerTxnId String?
signatureValid Boolean
status String
payload Json
receivedAt DateTime @default(now())
processedAt DateTime?
processingError String?
@@unique([provider, externalEventId])
@@index([merchantOrderId])
@@schema("passenger")
}
model PaymentRefund {
id String @id @default(uuid())
paymentIntentId String
amountMinor Int
reason String?
providerRefundId String?
status String
createdAt DateTime @default(now())
paymentIntent PaymentIntent @relation(fields: [paymentIntentId], references: [id])
@@schema("passenger")
}
model Ticket {
id String @id @default(uuid())
bookingId String @unique
bookingRef String
status String @default("CONFIRMED")
qrPayload String
barcodePayload String?
pdfUrl String?
deliveryChannel String @default("EMAIL")
issuedAt DateTime @default(now())
validatedAt DateTime?
validatorId String?
booking Booking @relation(fields: [bookingId], references: [id])
validationLogs GateValidationLog[]
seats TicketSeat[]
@@schema("passenger")
}
model TicketSeat {
id String @id @default(uuid())
ticketId String
seatId String
seatIndex Int @default(0)
ticket Ticket @relation(fields: [ticketId], references: [id], onDelete: Cascade)
seat Seat @relation(fields: [seatId], references: [id])
@@index([ticketId])
@@index([seatId])
@@schema("passenger")
}
model LoyaltyAccount {
id String @id @default(uuid())
passengerId String @unique
pointsBalance Int @default(0)
lifetimePoints Int @default(0)
tier LoyaltyTier @default(BRONZE)
tierUpdatedAt DateTime?
updatedAt DateTime @updatedAt
passenger Passenger @relation(fields: [passengerId], references: [id])
ledger LoyaltyLedgerEntry[]
rewards LoyaltyReward[]
@@schema("passenger")
}
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])
@@schema("passenger")
}
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])
@@schema("passenger")
}
model WalletAccount {
id String @id @default(uuid())
passengerId String @unique
balanceMinor Int @default(0)
status String @default("ACTIVE")
holdMinor Int @default(0)
currency String @default("ETB")
updatedAt DateTime @updatedAt
passenger Passenger @relation(fields: [passengerId], references: [id])
ledger WalletLedgerEntry[]
@@index([passengerId])
@@schema("passenger")
}
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])
@@schema("passenger")
}
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])
@@schema("passenger")
}
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())
@@schema("passenger")
}
model StationCrowdSignal {
id String @id @default(uuid())
stationId String
level String
label String
statusLabel String
confidence Int?
observedAt DateTime?
updatedAt DateTime @updatedAt
station Station @relation(fields: [stationId], references: [id])
@@schema("passenger")
}
model WeatherAlert {
id String @id @default(uuid())
region String
severity String
title String
message String
validUntil DateTime
createdAt DateTime @default(now())
@@schema("passenger")
}
model MenuCategory {
id String @id @default(uuid())
name String
items MenuItem[]
@@schema("passenger")
}
model MenuItem {
id String @id @default(uuid())
scheduleId String
categoryId String
name String
priceMinor Int
currency String @default("ETB")
available Boolean @default(true)
availableUntil DateTime?
schedule TrainSchedule @relation(fields: [scheduleId], references: [id])
category MenuCategory @relation(fields: [categoryId], references: [id])
@@schema("passenger")
}
model FoodOrder {
id String @id @default(uuid())
bookingId String
status FoodOrderStatus @default(PENDING)
totalMinor Int
currency String @default("ETB")
specialInstructions String?
estimatedReadyAt DateTime?
createdAt DateTime @default(now())
booking Booking @relation(fields: [bookingId], references: [id])
items FoodOrderItem[]
@@schema("passenger")
}
model FoodOrderItem {
id String @id @default(uuid())
orderId String
menuItemId String
name String
quantity Int
unitPriceMinor Int?
lineTotalMinor Int
order FoodOrder @relation(fields: [orderId], references: [id])
@@schema("passenger")
}
model FaqCategory {
id String @id @default(uuid())
title String
iconKey String?
articles FaqArticle[]
@@schema("passenger")
}
model FaqArticle {
id String @id @default(uuid())
categoryId String
question String
answerMarkdown String
rank Int @default(0)
category FaqCategory @relation(fields: [categoryId], references: [id])
@@schema("passenger")
}
model SupportConversation {
id String @id @default(uuid())
userId String
assignedAgentId String?
status SupportConversationStatus @default(OPEN)
createdAt DateTime @default(now())
messages SupportMessage[]
@@schema("passenger")
}
model SupportMessage {
id String @id @default(uuid())
conversationId String
sender SupportSender
text String
attachments Json?
createdAt DateTime @default(now())
conversation SupportConversation @relation(fields: [conversationId], references: [id])
@@schema("passenger")
}
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)
language String @default("en")
user User @relation(fields: [userId], references: [id])
@@schema("passenger")
}
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])
@@schema("passenger")
}
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])
@@schema("passenger")
}
model Journey {
id String @id @default(uuid())
passengerId String
status String
totalMinor Int
currency String @default("ETB")
createdAt DateTime @default(now())
journeySegments JourneySegment[]
@@schema("passenger")
}
model JourneySegment {
id String @id @default(uuid())
journeyId String
scheduleId String
segmentOrder Int
seatId String?
coachId String?
departureStationId String
arrivalStationId String
journey Journey @relation(fields: [journeyId], references: [id])
schedule TrainSchedule @relation(fields: [scheduleId], references: [id])
@@schema("passenger")
}
model OtpCode {
id String @id @default(uuid())
userId String?
email String?
phone String?
code String
purpose String
expiresAt DateTime
verified Boolean @default(false)
createdAt DateTime @default(now())
@@index([email, phone])
@@schema("passenger")
}
model PasswordResetToken {
id String @id @default(uuid())
userId String
token String @unique
expiresAt DateTime
used Boolean @default(false)
createdAt DateTime @default(now())
@@index([userId])
@@schema("passenger")
}
model Route {
id String @id @default(uuid())
code String @unique
name String
description String?
effectiveFrom DateTime
effectiveUntil DateTime?
active Boolean @default(true)
createdAt DateTime @default(now())
stops RouteStop[]
fareRules RouteFareRule[]
segmentFares SegmentFareRule[]
schedules TrainSchedule[]
@@schema("passenger")
}
model RouteStop {
id String @id @default(uuid())
routeId String
stationId String
sequence Int
distanceKm Int?
createdAt DateTime @default(now())
route Route @relation(fields: [routeId], references: [id], onDelete: Cascade)
@@unique([routeId, sequence])
@@index([routeId, stationId])
@@schema("passenger")
}
model RouteFareRule {
id String @id @default(uuid())
routeId String
seatClassId String
passengerCategory PassengerCategory @default(ADULT)
baseFareMinor Int
discountPercent Int?
taxPercent Int?
surchargeMinor Int?
currency String @default("ETB")
validFrom DateTime
validUntil DateTime?
createdAt DateTime @default(now())
route Route @relation(fields: [routeId], references: [id], onDelete: Cascade)
seatClass SeatClass @relation(fields: [seatClassId], references: [id])
@@index([routeId, seatClassId])
@@schema("passenger")
}
model SegmentFareRule {
id String @id @default(uuid())
routeId String
originStopSequence Int
destinationStopSequence Int
seatClassId String
baseFareMinor Int
nationality String? // Optional: Ethiopian, Djiboutian, Other
currency String @default("ETB")
validFrom DateTime
validUntil DateTime?
createdAt DateTime @default(now())
route Route @relation(fields: [routeId], references: [id], onDelete: Cascade)
seatClass SeatClass @relation(fields: [seatClassId], references: [id])
@@unique([routeId, originStopSequence, destinationStopSequence, seatClassId, nationality])
@@index([routeId, seatClassId])
@@schema("passenger")
}
model Agent {
id String @id @default(uuid())
userId String @unique
agentCode String @unique
stationId String?
commissionRate Int @default(5)
active Boolean @default(true)
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id])
bookings AgentBooking[]
shifts AgentShift[]
commissions AgentCommission[]
@@schema("passenger")
}
model AgentBooking {
id String @id @default(uuid())
agentId String
bookingId String @unique
paymentMethod String
cashReceived Int?
changeGiven Int?
paperTicket Boolean @default(false)
createdAt DateTime @default(now())
agent Agent @relation(fields: [agentId], references: [id])
booking Booking @relation(fields: [bookingId], references: [id])
@@schema("passenger")
}
model AgentShift {
id String @id @default(uuid())
agentId String
openedAt DateTime @default(now())
closedAt DateTime?
openingBalance Int @default(0)
closingBalance Int?
reconciled Boolean @default(false)
notes String?
agent Agent @relation(fields: [agentId], references: [id])
@@index([agentId, openedAt])
@@schema("passenger")
}
model AgentCommission {
id String @id @default(uuid())
agentId String
bookingId String
amountMinor Int
rate Int
paidAt DateTime?
createdAt DateTime @default(now())
agent Agent @relation(fields: [agentId], references: [id])
@@index([agentId, paidAt])
@@schema("passenger")
}
model BookingModification {
id String @id @default(uuid())
bookingId String
modifiedBy String
modificationType String
oldData Json
newData Json
fareAdjustment Int @default(0)
reason String?
createdAt DateTime @default(now())
booking Booking @relation(fields: [bookingId], references: [id])
@@index([bookingId])
@@schema("passenger")
}
model BookingCancellation {
id String @id @default(uuid())
bookingId String @unique
cancelledBy String
reason String?
refundAmount Int
refundMethod String
refundStatus String
processedAt DateTime?
createdAt DateTime @default(now())
booking Booking @relation(fields: [bookingId], references: [id])
@@schema("passenger")
}
model GateValidationLog {
id String @id @default(uuid())
ticketId String
validatorId String
gateId String?
status String
reason String?
validatedAt DateTime @default(now())
ticket Ticket @relation(fields: [ticketId], references: [id])
@@index([ticketId])
@@index([validatorId])
@@schema("passenger")
}
model BaggageAllowance {
id String @id @default(uuid())
seatClassId String
maxWeightKg Int
maxPiecesCount Int
excessFeePerKg Int
currency String @default("ETB")
createdAt DateTime @default(now())
@@schema("passenger")
}
model BaggageBooking {
id String @id @default(uuid())
bookingId String
weightKg Int
piecesCount Int
excessFeeMinor Int @default(0)
paid Boolean @default(false)
createdAt DateTime @default(now())
booking Booking @relation(fields: [bookingId], references: [id])
@@index([bookingId])
@@schema("passenger")
}
model AuditLog {
id String @id @default(uuid())
userId String?
action String
entityType String
entityId String?
oldData Json?
newData Json?
ipAddress String?
userAgent String?
createdAt DateTime @default(now())
user User? @relation(fields: [userId], references: [id])
@@index([userId, createdAt])
@@index([entityType, entityId])
@@schema("passenger")
}
model NotificationTemplate {
id String @id @default(uuid())
code String @unique
channel String
subject String?
bodyTemplate String
active Boolean @default(true)
createdAt DateTime @default(now())
@@schema("passenger")
}
model SeatBlock {
id String @id @default(uuid())
seatId String
reason String
blockedBy String
approvedBy String?
blockedAt DateTime @default(now())
unblockAt DateTime?
seat Seat @relation(fields: [seatId], references: [id])
@@index([seatId])
@@schema("passenger")
}
model OperationalReport {
id String @id @default(uuid())
reportType String
dateFrom DateTime
dateTo DateTime
data Json
generatedBy String?
createdAt DateTime @default(now())
@@index([reportType, dateFrom])
@@schema("passenger")
}
model FraudRule {
id String @id @default(uuid())
type String @unique
enabled Boolean @default(true)
threshold Float
config Json?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@schema("passenger")
}
model FraudAlert {
id String @id @default(uuid())
userId String
eventType String
triggeredRules String[]
context Json
severity String @default("MEDIUM")
acknowledged Boolean @default(false)
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId, createdAt])
@@index([acknowledged])
@@schema("passenger")
}
model CurrencyExchangeRate {
id String @id @default(uuid())
fromCurrency Currency
toCurrency Currency
rate Decimal @db.Decimal(18, 6)
effectiveDate DateTime @default(now())
source String @default("MANUAL")
createdAt DateTime @default(now())
@@unique([fromCurrency, toCurrency, effectiveDate])
@@index([fromCurrency, toCurrency])
@@schema("passenger")
}
model VerifaydaVerification {
id String @id @default(uuid())
bookingId String?
nationalId String
requestPayload Json
responsePayload Json?
verified Boolean @default(false)
failureReason String?
verifiedAt DateTime?
createdAt DateTime @default(now())
@@index([nationalId])
@@index([bookingId])
@@schema("passenger")
}
model SavedPassengerProfile {
id String @id @default(uuid())
userId String?
deviceId String?
passengerName String
dateOfBirth DateTime
idDocumentType IdDocumentType
passportNumber String?
passportCountry String?
nationality String?
phone String?
email String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([userId])
@@index([deviceId])
@@schema("passenger")
}
model FaydaVerificationSession {
id String @id @default(uuid())
state String @unique
codeVerifier String
purpose String @default("PURCHASE")
platform String @default("WEB") // WEB | MOBILE — recorded for audit
saveToAccount Boolean @default(false)
status String @default("PENDING")
errorCode String?
errorDescription String?
authCode String?
createdAt DateTime @default(now())
expiresAt DateTime
completedAt DateTime?
userId String?
bookingId String?
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
@@index([bookingId])
@@index([state])
@@index([expiresAt])
@@schema("passenger")
}