Refactored the whole app based on the requirements shared

This commit is contained in:
Stephanos A
2026-05-21 08:48:28 +03:00
parent 2dc3da9e74
commit 51bc906792
84 changed files with 6880 additions and 12659 deletions

View File

@@ -9,6 +9,8 @@ datasource db {
enum UserRole {
PASSENGER
AGENT
SUPERVISOR
ADMIN
STAFF
}
@@ -36,9 +38,12 @@ enum SeatStatus {
}
enum ServiceClass {
ECONOMY
BUSINESS
FIRST
ECONOMY_REGULAR
ECONOMY_BED_LOWER
ECONOMY_BED_MIDDLE
ECONOMY_BED_UPPER
VIP_BED_LOWER
VIP_BED_UPPER
}
enum BookingStatus {
@@ -48,6 +53,7 @@ enum BookingStatus {
CANCELLED
COMPLETED
NO_SHOW
REFUNDED
}
enum PaymentMethodType {
@@ -64,6 +70,7 @@ enum PaymentIntentStatus {
SUCCEEDED
FAILED
CANCELLED
REFUNDED
}
enum WalletLedgerType {
@@ -134,12 +141,21 @@ model User {
fullName String
passwordHash String
role UserRole @default(PASSENGER)
nationality String?
passportNumber String?
nationalId String?
failedLoginAttempts Int @default(0)
lockedUntil DateTime?
blockedUntil DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
passenger Passenger?
agent Agent?
sessions Session[]
devices Device[]
preferences UserPreferences?
auditLogs AuditLog[]
fraudAlerts FraudAlert[]
}
model Session {
@@ -147,6 +163,9 @@ model Session {
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)
}
@@ -267,8 +286,11 @@ model Seat {
kind SeatKind @default(STANDARD)
status SeatStatus @default(AVAILABLE)
heldUntil DateTime?
premiumFeeMinor Int @default(0)
eligibility String?
coach Coach @relation(fields: [coachId], references: [id])
bookingSeats BookingSeat[]
blocks SeatBlock[]
@@unique([coachId, row, col])
}
@@ -303,6 +325,7 @@ model Booking {
status BookingStatus @default(DRAFT)
currency String @default("ETB")
totalMinor Int
bookingType String @default("ONE_WAY")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
passenger Passenger @relation(fields: [passengerId], references: [id])
@@ -311,6 +334,10 @@ model Booking {
paymentIntent PaymentIntent?
ticket Ticket?
foodOrders FoodOrder[]
agentBooking AgentBooking?
modifications BookingModification[]
cancellation BookingCancellation?
baggage BaggageBooking[]
}
model BookingSeat {
@@ -392,10 +419,14 @@ model Ticket {
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[]
}
model LoyaltyAccount {
@@ -585,6 +616,7 @@ model UserPreferences {
dataSharing Boolean @default(false)
locale String @default("en")
darkMode Boolean @default(false)
language String @default("en")
user User @relation(fields: [userId], references: [id])
}
@@ -633,3 +665,253 @@ model JourneySegment {
journey Journey @relation(fields: [journeyId], references: [id])
trip Trip @relation(fields: [tripId], references: [id])
}
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])
}
model PasswordResetToken {
id String @id @default(uuid())
userId String
token String @unique
expiresAt DateTime
used Boolean @default(false)
createdAt DateTime @default(now())
@@index([userId])
}
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[]
}
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])
}
model RouteFareRule {
id String @id @default(uuid())
routeId String
serviceClass ServiceClass
passengerCategory String @default("ADULT")
baseFareMinor Int
discountPercent Int?
currency String @default("ETB")
validFrom DateTime
validUntil DateTime?
createdAt DateTime @default(now())
route Route @relation(fields: [routeId], references: [id], onDelete: Cascade)
@@index([routeId, serviceClass])
}
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[]
}
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])
}
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])
}
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])
}
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])
}
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])
}
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])
}
model BaggageAllowance {
id String @id @default(uuid())
serviceClass ServiceClass
maxWeightKg Int
maxPiecesCount Int
excessFeePerKg Int
currency String @default("ETB")
createdAt DateTime @default(now())
}
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])
}
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])
}
model NotificationTemplate {
id String @id @default(uuid())
code String @unique
channel String
subject String?
bodyTemplate String
active Boolean @default(true)
createdAt DateTime @default(now())
}
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])
}
model OperationalReport {
id String @id @default(uuid())
reportType String
dateFrom DateTime
dateTo DateTime
data Json
generatedBy String?
createdAt DateTime @default(now())
@@index([reportType, dateFrom])
}
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
}
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])
}