mirror of
https://github.com/Since-3/Kuma.git
synced 2026-10-12 21:41:17 +00:00
added discount codes
This commit is contained in:
parent
d045ee1600
commit
db514248a2
13 changed files with 1408 additions and 43 deletions
|
|
@ -42,6 +42,7 @@ model User {
|
|||
trainings UserTraining[]
|
||||
courseBookings CourseBooking[]
|
||||
courseWaitlistEntries CourseWaitlistEntry[]
|
||||
discountRedemptions DiscountRedemption[]
|
||||
createdAt DateTime? @default(now())
|
||||
updatedAt DateTime? @updatedAt
|
||||
}
|
||||
|
|
@ -128,10 +129,11 @@ model Business {
|
|||
onboardingStartedAt DateTime? // Wizard erstmals geoeffnet, ab dann kein Redirect mehr
|
||||
onboardingDismissedAt DateTime? // Checkliste im Dashboard ausgeblendet
|
||||
onboardingCompletedAt DateTime? // Zeitpunkt, an dem erstmals alle Pflichtschritte erledigt waren
|
||||
manager Manager @relation(fields: [managerId], references: [id])
|
||||
manager Manager @relation(fields: [managerId], references: [id])
|
||||
courses Course[]
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
discountCodes DiscountCode[]
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([managerId])
|
||||
}
|
||||
|
|
@ -195,30 +197,35 @@ model GuestLead {
|
|||
}
|
||||
|
||||
model CourseBooking {
|
||||
id String @id @default(uuid())
|
||||
id String @id @default(uuid())
|
||||
courseId String
|
||||
userId String?
|
||||
guestLeadId String?
|
||||
paymentStatus String @default("pending") // "pending" | "paid" | "failed" | "refunded" | "partially_refunded" | "refunding" | "cancelled" (cancelled = storniert ohne Geldbewegung, 0% Erstattung)
|
||||
stripeSessionId String? @unique
|
||||
stripePaymentIntentId String? @unique
|
||||
paymentStatus String @default("pending") // "pending" | "paid" | "failed" | "refunded" | "partially_refunded" | "refunding" | "cancelled" (cancelled = storniert ohne Geldbewegung, 0% Erstattung)
|
||||
stripeSessionId String? @unique
|
||||
stripePaymentIntentId String? @unique
|
||||
amountPaid Int? // tatsächlich gezahlter Betrag in Cents
|
||||
platformFee Int? // unsere Service-Fee in Cents
|
||||
invoiceNumber String? @unique // Rechnungsnummer, wird lazy beim ersten PDF-Download vergeben
|
||||
invoiceNumber String? @unique // Rechnungsnummer, wird lazy beim ersten PDF-Download vergeben
|
||||
invoiceIssuedAt DateTime? // Zeitpunkt der Rechnungsnummer-Vergabe, damit das Rechnungsdatum bei jedem Download gleich bleibt
|
||||
refundedAmount Int? // tatsächlich erstatteter Betrag in Cents
|
||||
refundedAt DateTime? // Zeitpunkt der erfolgreichen Stripe-Rückerstattung
|
||||
cancelledBy String? // "user" | "manager" | "system" (system = automatischer Überbuchungs-Refund)
|
||||
cancellationReason String?
|
||||
course Course @relation(fields: [courseId], references: [id], onDelete: Cascade)
|
||||
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
guestLead GuestLead? @relation(fields: [guestLeadId], references: [id], onDelete: SetNull)
|
||||
createdAt DateTime @default(now())
|
||||
discountCodeId String? // Gesetzt, sobald bei der Buchung ein gültiger Rabattcode eingelöst wurde
|
||||
discountAmount Int? // Tatsächlich gewährter Rabatt in Cents (Snapshot, unabhängig von späteren Code-Änderungen)
|
||||
course Course @relation(fields: [courseId], references: [id], onDelete: Cascade)
|
||||
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
guestLead GuestLead? @relation(fields: [guestLeadId], references: [id], onDelete: SetNull)
|
||||
discountCode DiscountCode? @relation(fields: [discountCodeId], references: [id], onDelete: SetNull)
|
||||
discountRedemption DiscountRedemption?
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@unique([courseId, userId]) // Ein User kann sich nur einmal pro Kurs anmelden
|
||||
@@index([courseId])
|
||||
@@index([userId])
|
||||
@@index([guestLeadId])
|
||||
@@index([discountCodeId])
|
||||
}
|
||||
|
||||
// Bewusst nur für registrierte User (kein guestEmail-Feld): ein
|
||||
|
|
@ -258,3 +265,49 @@ model Notification {
|
|||
@@index([recipientId, readAt])
|
||||
@@index([recipientId, createdAt])
|
||||
}
|
||||
|
||||
// Rabattcode eines Business. scope = "specific_courses" schränkt die Gültigkeit
|
||||
// über courseIds ein (Array statt Join-Tabelle, analog Course.trainers/sport —
|
||||
// hier reicht eine Handvoll IDs pro Code, kein eigenes Modell nötig).
|
||||
// redeemedCount wird ausschließlich im Stripe-Webhook per {increment: 1} erhöht,
|
||||
// niemals beim Checkout-Start (siehe DiscountRedemption).
|
||||
model DiscountCode {
|
||||
id String @id @default(uuid())
|
||||
businessId String
|
||||
code String // Anzeigeform, z.B. "SOMMER25" — Eindeutigkeit nur pro Business (siehe @@unique)
|
||||
type String // "percent" | "fixed"
|
||||
value Int // percent: 1-100, fixed: Cents
|
||||
scope String // "all_courses" | "specific_courses"
|
||||
courseIds String[] // Nur relevant wenn scope = "specific_courses"
|
||||
maxRedemptions Int? // null = unbegrenzt
|
||||
redeemedCount Int @default(0)
|
||||
startsAt DateTime? // null = sofort gültig
|
||||
expiresAt DateTime? // null = kein Ablauf
|
||||
isActive Boolean @default(true) // Manuelles An/Aus, unabhängig vom Zeitfenster
|
||||
business Business @relation(fields: [businessId], references: [id], onDelete: Cascade)
|
||||
bookings CourseBooking[]
|
||||
redemptions DiscountRedemption[]
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@unique([businessId, code])
|
||||
@@index([businessId])
|
||||
}
|
||||
|
||||
// Ein Eintrag pro tatsächlich eingelöstem Rabattcode (nur bei bezahlter Buchung,
|
||||
// siehe Webhook handleCheckoutCompleted). Der @@unique-Constraint erzwingt
|
||||
// "1x pro User" race-condition-sicher auf DB-Ebene, unabhängig von Anwendungslogik.
|
||||
model DiscountRedemption {
|
||||
id String @id @default(uuid())
|
||||
discountCodeId String
|
||||
userId String
|
||||
bookingId String @unique
|
||||
discountCode DiscountCode @relation(fields: [discountCodeId], references: [id], onDelete: Cascade)
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
booking CourseBooking @relation(fields: [bookingId], references: [id], onDelete: Cascade)
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@unique([discountCodeId, userId])
|
||||
@@index([discountCodeId])
|
||||
@@index([userId])
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue