mirror of
https://github.com/Since-3/Kuma.git
synced 2026-10-12 20:51:18 +00:00
added cancellation worklfow
This commit is contained in:
parent
0fe79f4e9b
commit
cffca6b2dd
10 changed files with 620 additions and 148 deletions
|
|
@ -0,0 +1,2 @@
|
|||
-- AlterTable
|
||||
ALTER TABLE "Course" ADD COLUMN "cancelledAt" TIMESTAMP(3);
|
||||
|
|
@ -11,143 +11,145 @@ generator client {
|
|||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
directUrl = env("DIRECT_URL")
|
||||
}
|
||||
|
||||
model User {
|
||||
id String @id @default(uuid())
|
||||
name String?
|
||||
complaints String?
|
||||
birthday DateTime?
|
||||
plz String?
|
||||
city String?
|
||||
street String?
|
||||
houseNumber String?
|
||||
id String @id @default(uuid())
|
||||
name String?
|
||||
complaints String?
|
||||
birthday DateTime?
|
||||
plz String?
|
||||
city String?
|
||||
street String?
|
||||
houseNumber String?
|
||||
// @unique is Prisma-managed and stays case-sensitive at the schema level —
|
||||
// Prisma has no functional-index syntax. The actual enforced constraint is
|
||||
// a case-insensitive unique index on lower(email), added via raw SQL in
|
||||
// migration 20260817000000_case_insensitive_user_email. Do not remove
|
||||
// @unique here; it keeps Prisma's drift detection from trying to recreate
|
||||
// a duplicate case-sensitive constraint.
|
||||
email String @unique
|
||||
tel String?
|
||||
gender String?
|
||||
is2FA Boolean @default(false)
|
||||
isSendingMail Boolean @default(false)
|
||||
isNewsletter Boolean @default(false)
|
||||
pbSrc String?
|
||||
isValid Boolean @default(false)
|
||||
email String @unique
|
||||
tel String?
|
||||
gender String?
|
||||
is2FA Boolean @default(false)
|
||||
isSendingMail Boolean @default(false)
|
||||
isNewsletter Boolean @default(false)
|
||||
pbSrc String?
|
||||
isValid Boolean @default(false)
|
||||
trainings UserTraining[]
|
||||
courseBookings CourseBooking[]
|
||||
createdAt DateTime? @default(now())
|
||||
updatedAt DateTime? @updatedAt
|
||||
createdAt DateTime? @default(now())
|
||||
updatedAt DateTime? @updatedAt
|
||||
}
|
||||
|
||||
model Employee {
|
||||
id String @id @default(uuid())
|
||||
firstName String?
|
||||
lastName String?
|
||||
email String @unique
|
||||
is2FA Boolean @default(false)
|
||||
isSendingMail Boolean @default(false)
|
||||
pbSrc String?
|
||||
status String?
|
||||
roles String[] @default([])
|
||||
locations String[] @default([])
|
||||
permissions Json?
|
||||
affiliation String?
|
||||
label String?
|
||||
qualification String?
|
||||
authorization Json?
|
||||
onboardingToken String? @unique
|
||||
id String @id @default(uuid())
|
||||
firstName String?
|
||||
lastName String?
|
||||
email String @unique
|
||||
is2FA Boolean @default(false)
|
||||
isSendingMail Boolean @default(false)
|
||||
pbSrc String?
|
||||
status String?
|
||||
roles String[] @default([])
|
||||
locations String[] @default([])
|
||||
permissions Json?
|
||||
affiliation String?
|
||||
label String?
|
||||
qualification String?
|
||||
authorization Json?
|
||||
onboardingToken String? @unique
|
||||
onboardingTokenExpiry DateTime?
|
||||
isOnboarded Boolean @default(false)
|
||||
createdBy String? // Manager ID who created the employee
|
||||
trainings UserTraining[]
|
||||
createdAt DateTime? @default(now())
|
||||
updatedAt DateTime? @updatedAt
|
||||
isOnboarded Boolean @default(false)
|
||||
createdBy String? // Manager ID who created the employee
|
||||
trainings UserTraining[]
|
||||
createdAt DateTime? @default(now())
|
||||
updatedAt DateTime? @updatedAt
|
||||
|
||||
@@index([createdBy])
|
||||
}
|
||||
|
||||
model UserTraining {
|
||||
userId String
|
||||
userId String
|
||||
employeeId String
|
||||
employee Employee @relation(fields: [employeeId], references: [id])
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
|
||||
@@id([userId, employeeId])
|
||||
}
|
||||
|
||||
model Manager {
|
||||
id String @id @default(uuid())
|
||||
firstName String
|
||||
lastName String
|
||||
tel String?
|
||||
email String @unique
|
||||
is2FA Boolean @default(false)
|
||||
isSendingMail Boolean @default(false)
|
||||
pbSrc String?
|
||||
businesses Business[]
|
||||
createdAt DateTime? @default(now())
|
||||
updatedAt DateTime? @updatedAt
|
||||
id String @id @default(uuid())
|
||||
firstName String
|
||||
lastName String
|
||||
tel String?
|
||||
email String @unique
|
||||
is2FA Boolean @default(false)
|
||||
isSendingMail Boolean @default(false)
|
||||
pbSrc String?
|
||||
businesses Business[]
|
||||
createdAt DateTime? @default(now())
|
||||
updatedAt DateTime? @updatedAt
|
||||
}
|
||||
|
||||
model Business {
|
||||
id String @id @default(uuid())
|
||||
managerId String
|
||||
name String
|
||||
address String
|
||||
email String
|
||||
title String
|
||||
ustId String?
|
||||
banking String?
|
||||
plan String?
|
||||
subscription Json?
|
||||
slug String? @unique
|
||||
isPublic Boolean @default(false)
|
||||
id String @id @default(uuid())
|
||||
managerId String
|
||||
name String
|
||||
address String
|
||||
email String
|
||||
title String
|
||||
ustId String?
|
||||
banking String?
|
||||
plan String?
|
||||
subscription Json?
|
||||
slug String? @unique
|
||||
isPublic Boolean @default(false)
|
||||
stripeAccountId String? @unique // Stripe Connect Account ID
|
||||
stripeAccountStatus String? // "pending" | "active" | "restricted"
|
||||
stripeAccountStatus String? // "pending" | "active" | "restricted"
|
||||
invoiceCounter Int @default(0) // Fortlaufender Zähler für Rechnungsnummern
|
||||
cancellationPolicy Json? // { tiers: [{ hoursBeforeStart: number, refundPercent: number }, ...] }, absteigend sortiert. null = immer 100% Rückerstattung.
|
||||
manager Manager @relation(fields: [managerId], references: [id])
|
||||
courses Course[]
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
cancellationPolicy Json? // { tiers: [{ hoursBeforeStart: number, refundPercent: number }, ...] }, absteigend sortiert. null = immer 100% Rückerstattung.
|
||||
manager Manager @relation(fields: [managerId], references: [id])
|
||||
courses Course[]
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([managerId])
|
||||
}
|
||||
|
||||
model Course {
|
||||
id String @id @default(uuid())
|
||||
name String
|
||||
sport String[]
|
||||
level String @default("any")
|
||||
date DateTime
|
||||
timeFrom String // Anfangszeit
|
||||
timeTo String // Endzeit
|
||||
trainers String[] // Array of trainer IDs
|
||||
room String
|
||||
description String // Rich text HTML content
|
||||
id String @id @default(uuid())
|
||||
name String
|
||||
sport String[]
|
||||
level String @default("any")
|
||||
date DateTime
|
||||
timeFrom String // Anfangszeit
|
||||
timeTo String // Endzeit
|
||||
trainers String[] // Array of trainer IDs
|
||||
room String
|
||||
description String // Rich text HTML content
|
||||
maxParticipants Int // Maximum number of participants
|
||||
price Float @default(10.0) // Preis in Euro
|
||||
isStandingOrder Boolean @default(false)
|
||||
frequency String? // daily, weekly, biweekly, monthly, custom
|
||||
weekdays String[] // For custom frequency
|
||||
weekdayTimings Json? // { "monday": { "timeFrom": "08:00", "timeTo": "09:00" }, ... }
|
||||
endDate DateTime? // Required when isStandingOrder = true
|
||||
parentCourseId String? // Null on template; set on every child instance
|
||||
parentCourse Course? @relation("StandingOrderInstances", fields: [parentCourseId], references: [id], onDelete: Cascade)
|
||||
children Course[] @relation("StandingOrderInstances")
|
||||
status String @default("draft") // draft, published
|
||||
createdBy String // Manager ID who created the course
|
||||
businessId String?
|
||||
coverImage String?
|
||||
business Business? @relation(fields: [businessId], references: [id], onDelete: SetNull)
|
||||
bookings CourseBooking[]
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
price Float @default(10.0) // Preis in Euro
|
||||
isStandingOrder Boolean @default(false)
|
||||
frequency String? // daily, weekly, biweekly, monthly, custom
|
||||
weekdays String[] // For custom frequency
|
||||
weekdayTimings Json? // { "monday": { "timeFrom": "08:00", "timeTo": "09:00" }, ... }
|
||||
endDate DateTime? // Required when isStandingOrder = true
|
||||
parentCourseId String? // Null on template; set on every child instance
|
||||
parentCourse Course? @relation("StandingOrderInstances", fields: [parentCourseId], references: [id], onDelete: Cascade)
|
||||
children Course[] @relation("StandingOrderInstances")
|
||||
status String @default("draft") // draft, published, cancelled
|
||||
cancelledAt DateTime? // Zeitpunkt der Kursabsage (nur gesetzt wenn status = "cancelled")
|
||||
createdBy String // Manager ID who created the course
|
||||
businessId String?
|
||||
coverImage String?
|
||||
business Business? @relation(fields: [businessId], references: [id], onDelete: SetNull)
|
||||
bookings CourseBooking[]
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([createdBy])
|
||||
@@index([date])
|
||||
|
|
@ -158,8 +160,8 @@ model Course {
|
|||
}
|
||||
|
||||
model Room {
|
||||
id String @id @default(uuid())
|
||||
name String
|
||||
id String @id @default(uuid())
|
||||
name String
|
||||
createdBy String // Manager ID who created the room
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
|
@ -176,28 +178,28 @@ model GuestLead {
|
|||
}
|
||||
|
||||
model CourseBooking {
|
||||
id String @id @default(uuid())
|
||||
courseId String
|
||||
userId String?
|
||||
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
|
||||
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
|
||||
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
|
||||
invoiceIssuedAt DateTime? // Zeitpunkt der Rechnungsnummer-Vergabe, damit das Rechnungsdatum bei jedem Download gleich bleibt
|
||||
refundedAmount Int? // tatsächlich erstatteter Betrag in Cents
|
||||
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)
|
||||
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())
|
||||
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())
|
||||
|
||||
@@unique([courseId, userId]) // Ein User kann sich nur einmal pro Kurs anmelden
|
||||
@@index([courseId])
|
||||
@@index([userId])
|
||||
@@index([guestLeadId])
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue