// Gruperly - Prisma Schema (PostgreSQL) generator client { provider = "prisma-client" output = "./generated/prisma" } datasource db { provider = "postgresql" } // Auth (Better Auth) - required tables model User { id String @id @default(cuid()) name String email String @unique emailVerified Boolean @default(false) image String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt accounts Account[] sessions Session[] passkeys Passkey[] groupsMember GroupMember[] groupsOwner Group[] @relation("OwnerGroups") orgMemberships Member[] orgInvitations Invitation[] @relation("InvitedBy") waitlist WaitlistEntry[] @@map("users") } model Account { id String @id @default(cuid()) userId String providerId String accountId String issuer String accessToken String? refreshToken String? idToken String? accessTokenExpiresAt DateTime? refreshTokenExpiresAt DateTime? scope String? password String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([issuer, accountId]) @@index([userId]) @@map("accounts") } model Session { id String @id @default(cuid()) userId String token String @unique expiresAt DateTime ipAddress String? userAgent String? activeOrganizationId String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@map("sessions") } model Verification { id String @id @default(cuid()) identifier String value String expiresAt DateTime createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([identifier, value]) @@map("verifications") } // Better Auth - Organization plugin model Organization { id String @id @default(cuid()) name String slug String @unique logo String? metadata Json? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt members Member[] invitations Invitation[] @@map("organizations") } model Member { id String @id @default(cuid()) organizationId String userId String role String @default("member") createdAt DateTime @default(now()) organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([organizationId, userId]) @@map("members") } model Invitation { id String @id @default(cuid()) organizationId String email String role String status String @default("pending") inviterId String expiresAt DateTime createdAt DateTime @default(now()) organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) inviter User @relation("InvitedBy", fields: [inviterId], references: [id], onDelete: Cascade) @@index([organizationId]) @@map("invitations") } // Better Auth - Passkey plugin model Passkey { id String @id @default(cuid()) name String? publicKey String credentialID String @unique userId String counter Int deviceType String backedUp Boolean transports String? createdAt DateTime @default(now()) aaguid String? user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([userId]) @@map("passkeys") } // Domain models model Group { id String @id @default(cuid()) name String description String? createdById String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt owner User @relation("OwnerGroups", fields: [createdById], references: [id], onDelete: Cascade) members GroupMember[] students Student[] payments Payment[] @@map("groups") } model GroupMember { id String @id @default(cuid()) groupId String userId String role Role @default(MEMBER) // OWNER, ADMIN, MEMBER joinedAt DateTime @default(now()) group Group @relation(fields: [groupId], references: [id], onDelete: Cascade) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([groupId, userId]) @@map("group_members") } enum Role { OWNER ADMIN MEMBER } model Student { id String @id @default(cuid()) groupId String fullName String email String? phone String? guardianName String? guardianPhone String? notes String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt group Group @relation(fields: [groupId], references: [id], onDelete: Cascade) payments Payment[] @@index([groupId]) @@map("students") } model Payment { id String @id @default(cuid()) groupId String studentId String amount Decimal @db.Decimal(10, 2) currency String @default("MXN") status PaymentStatus @default(PENDING) // PENDING, PAID, OVERDUE, CANCELLED dueDate DateTime paidAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt group Group @relation(fields: [groupId], references: [id], onDelete: Cascade) student Student @relation(fields: [studentId], references: [id], onDelete: Cascade) @@index([groupId]) @@index([studentId]) @@map("payments") } enum PaymentStatus { PENDING PAID OVERDUE CANCELLED } model WaitlistEntry { id String @id @default(cuid()) email String name String? status WaitlistStatus @default(PENDING) // PENDING, INVITED, JOINED, DECLINED createdAt DateTime @default(now()) updatedAt DateTime @updatedAt userId String? @unique user User? @relation(fields: [userId], references: [id], onDelete: SetNull) @@unique([email]) @@map("waitlist_entries") } enum WaitlistStatus { PENDING INVITED JOINED DECLINED }