generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } model Tenant { id String @id @default(cuid()) name String slug String @unique createdAt DateTime @default(now()) updatedAt DateTime @updatedAt users User[] roles Role[] events Event[] attendees Attendee[] registrations Registration[] invitees Invitee[] rsvps RSVP[] payments PaymentTransaction[] settings TenantSetting? integrations IntegrationSetting[] eventPages EventPage[] ticketTypes TicketType[] forms Form[] formSubmissions FormSubmission[] qrCodes QRCode[] checkIns CheckIn[] communicationTemplates CommunicationTemplate[] communicationLogs CommunicationLog[] paystackWebhookEvents PaystackWebhookEvent[] crmLeads CRMLead[] crmDeals CRMDeal[] crmActivities CRMActivity[] workflows Workflow[] workflowTriggers WorkflowTrigger[] workflowActions WorkflowAction[] calendarRoutingForms CalendarRoutingForm[] calendarSlots CalendarSlot[] bookings Booking[] formFields FormField[] } model User { id String @id @default(cuid()) tenantId String email String fullName String phone String? addressLine1 String? addressLine2 String? city String? state String? country String? avatarUrl String? mustChangePassword Boolean @default(false) passwordHash String refreshTokenId String? @unique refreshTokenHash String? refreshTokenExpiresAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) roles UserRole[] performedCheckIns CheckIn[] @relation("CheckInUser") crmActivities CRMActivity[] @relation("CRMActivityUser") @@unique([tenantId, email]) @@index([tenantId]) } model TenantSetting { id String @id @default(cuid()) tenantId String @unique appName String? logoUrl String? modules Json? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) } model IntegrationSetting { id String @id @default(cuid()) tenantId String key String value String? isSecret Boolean @default(false) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) @@unique([tenantId, key]) @@index([tenantId]) } model Role { id String @id @default(cuid()) tenantId String name String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) users UserRole[] permissions RolePermission[] @@unique([tenantId, name]) @@index([tenantId]) } model Permission { id String @id @default(cuid()) key String @unique createdAt DateTime @default(now()) updatedAt DateTime @updatedAt roles RolePermission[] } model UserRole { id String @id @default(cuid()) userId String roleId String createdAt DateTime @default(now()) user User @relation(fields: [userId], references: [id], onDelete: Cascade) role Role @relation(fields: [roleId], references: [id], onDelete: Cascade) @@unique([userId, roleId]) } model RolePermission { id String @id @default(cuid()) roleId String permissionId String createdAt DateTime @default(now()) role Role @relation(fields: [roleId], references: [id], onDelete: Cascade) permission Permission @relation(fields: [permissionId], references: [id], onDelete: Cascade) @@unique([roleId, permissionId]) } enum EventStatus { draft active closed } model Event { id String @id @default(cuid()) tenantId String name String slug String status EventStatus @default(draft) startsAt DateTime venue String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) attendees Attendee[] registrations Registration[] invitees Invitee[] rsvps RSVP[] payments PaymentTransaction[] eventPage EventPage? ticketTypes TicketType[] forms Form[] formSubmissions FormSubmission[] checkIns CheckIn[] crmLeads CRMLead[] crmDeals CRMDeal[] calendarRoutingForms CalendarRoutingForm[] bookings Booking[] @@unique([tenantId, slug]) @@index([tenantId]) @@index([tenantId, status]) } enum RegistrationStatus { pending confirmed cancelled } model Registration { id String @id @default(cuid()) tenantId String eventId String attendeeId String ticketTypeId String? status RegistrationStatus @default(pending) code String @unique source String @default("admin") checkedInAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) event Event @relation(fields: [eventId], references: [id], onDelete: Cascade) attendee Attendee @relation(fields: [attendeeId], references: [id], onDelete: Cascade) ticketType TicketType? @relation(fields: [ticketTypeId], references: [id], onDelete: SetNull) qrCode QRCode? checkIns CheckIn[] formSubmissions FormSubmission[] communicationLogs CommunicationLog[] payments PaymentTransaction[] @@unique([tenantId, eventId, attendeeId]) @@index([tenantId]) @@index([tenantId, eventId]) @@index([eventId]) @@index([ticketTypeId]) } enum InviteeStatus { invited delivered opened rsvped bounced } model Invitee { id String @id @default(cuid()) tenantId String eventId String fullName String email String phone String? status InviteeStatus @default(invited) code String @unique createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) event Event @relation(fields: [eventId], references: [id], onDelete: Cascade) rsvps RSVP[] communicationLogs CommunicationLog[] @@unique([tenantId, eventId, email]) @@index([tenantId]) @@index([tenantId, eventId]) } enum RSVPResponse { yes no maybe } model RSVP { id String @id @default(cuid()) tenantId String eventId String inviteeId String? response RSVPResponse note String? createdAt DateTime @default(now()) tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) event Event @relation(fields: [eventId], references: [id], onDelete: Cascade) invitee Invitee? @relation(fields: [inviteeId], references: [id], onDelete: SetNull) @@index([tenantId]) @@index([tenantId, eventId]) @@index([inviteeId]) } enum PaymentStatus { initialized success failed } model PaymentTransaction { id String @id @default(cuid()) tenantId String eventId String registrationId String? ticketTypeId String? email String amountKobo Int currency String @default("NGN") provider String @default("paystack") reference String @unique status PaymentStatus @default(initialized) raw Json? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) event Event @relation(fields: [eventId], references: [id], onDelete: Cascade) registration Registration? @relation(fields: [registrationId], references: [id], onDelete: SetNull) ticketType TicketType? @relation(fields: [ticketTypeId], references: [id], onDelete: SetNull) @@index([tenantId]) @@index([tenantId, eventId]) @@index([eventId]) @@index([registrationId]) @@index([ticketTypeId]) } model Attendee { id String @id @default(cuid()) tenantId String eventId String? fullName String email String phone String? tags String[] @default([]) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) event Event? @relation(fields: [eventId], references: [id], onDelete: SetNull) registrations Registration[] formSubmissions FormSubmission[] checkIns CheckIn[] crmLeads CRMLead[] bookings Booking[] @@unique([tenantId, email]) @@index([tenantId]) @@index([tenantId, eventId]) } model AuditLog { id String @id @default(cuid()) tenantId String? actorUserId String? action String entityType String? entityId String? ip String? userAgent String? metadata Json? createdAt DateTime @default(now()) @@index([tenantId]) @@index([actorUserId]) @@index([entityType, entityId]) } model EventPage { id String @id @default(cuid()) tenantId String eventId String @unique title String heroTitle String? description String? content Json? theme Json? publishedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) event Event @relation(fields: [eventId], references: [id], onDelete: Cascade) @@index([tenantId]) } model TicketType { id String @id @default(cuid()) tenantId String eventId String name String description String? priceKobo Int @default(0) currency String @default("NGN") capacity Int? soldCount Int @default(0) isActive Boolean @default(true) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) event Event @relation(fields: [eventId], references: [id], onDelete: Cascade) registrations Registration[] payments PaymentTransaction[] @@unique([tenantId, eventId, name]) @@index([tenantId]) @@index([tenantId, eventId]) } model Form { id String @id @default(cuid()) tenantId String eventId String? name String slug String description String? isPublic Boolean @default(false) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) event Event? @relation(fields: [eventId], references: [id], onDelete: SetNull) fields FormField[] submissions FormSubmission[] @@unique([tenantId, slug]) @@index([tenantId]) @@index([tenantId, eventId]) } model FormField { id String @id @default(cuid()) tenantId String formId String label String key String type String required Boolean @default(false) options Json? sortOrder Int @default(0) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) form Form @relation(fields: [formId], references: [id], onDelete: Cascade) @@unique([formId, key]) @@index([tenantId]) @@index([formId]) } model FormSubmission { id String @id @default(cuid()) tenantId String formId String eventId String? attendeeId String? registrationId String? data Json source String @default("admin") createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) form Form @relation(fields: [formId], references: [id], onDelete: Cascade) event Event? @relation(fields: [eventId], references: [id], onDelete: SetNull) attendee Attendee? @relation(fields: [attendeeId], references: [id], onDelete: SetNull) registration Registration? @relation(fields: [registrationId], references: [id], onDelete: SetNull) @@index([tenantId]) @@index([formId]) @@index([eventId]) @@index([attendeeId]) @@index([registrationId]) } model QRCode { id String @id @default(cuid()) tenantId String registrationId String @unique code String @unique payload Json status QRCodeStatus @default(active) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) registration Registration @relation(fields: [registrationId], references: [id], onDelete: Cascade) @@index([tenantId]) } enum QRCodeStatus { active revoked } model CheckIn { id String @id @default(cuid()) tenantId String eventId String registrationId String attendeeId String? checkedInByUserId String? code String result CheckInResult @default(checked_in) note String? createdAt DateTime @default(now()) tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) event Event @relation(fields: [eventId], references: [id], onDelete: Cascade) registration Registration @relation(fields: [registrationId], references: [id], onDelete: Cascade) attendee Attendee? @relation(fields: [attendeeId], references: [id], onDelete: SetNull) checkedInBy User? @relation("CheckInUser", fields: [checkedInByUserId], references: [id], onDelete: SetNull) @@index([tenantId]) @@index([tenantId, eventId]) @@index([registrationId]) @@index([attendeeId]) } enum CheckInResult { checked_in duplicate rejected } model CommunicationTemplate { id String @id @default(cuid()) tenantId String name String channel CommunicationChannel subject String? body String variables Json? isActive Boolean @default(true) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) logs CommunicationLog[] @@unique([tenantId, name, channel]) @@index([tenantId]) } model CommunicationLog { id String @id @default(cuid()) tenantId String templateId String? inviteeId String? registrationId String? channel CommunicationChannel to String subject String? body String status CommunicationStatus @default(queued) providerMessageId String? error String? raw Json? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) template CommunicationTemplate? @relation(fields: [templateId], references: [id], onDelete: SetNull) invitee Invitee? @relation(fields: [inviteeId], references: [id], onDelete: SetNull) registration Registration? @relation(fields: [registrationId], references: [id], onDelete: SetNull) @@index([tenantId]) @@index([templateId]) @@index([inviteeId]) @@index([registrationId]) } enum CommunicationChannel { email sms whatsapp } enum CommunicationStatus { queued sent failed } model PaystackWebhookEvent { id String @id @default(cuid()) tenantId String? event String reference String? status String @default("received") raw Json receivedAt DateTime @default(now()) processedAt DateTime? tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: SetNull) @@index([tenantId]) @@index([reference]) } model CRMLead { id String @id @default(cuid()) tenantId String eventId String? attendeeId String? fullName String email String phone String? source String @default("manual") status String @default("new") valueKobo Int @default(0) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) event Event? @relation(fields: [eventId], references: [id], onDelete: SetNull) attendee Attendee? @relation(fields: [attendeeId], references: [id], onDelete: SetNull) deals CRMDeal[] activities CRMActivity[] @@unique([tenantId, email]) @@index([tenantId]) @@index([eventId]) @@index([attendeeId]) } model CRMDeal { id String @id @default(cuid()) tenantId String leadId String? eventId String? title String stage String @default("new") valueKobo Int @default(0) probability Int @default(0) ownerUserId String? expectedCloseAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) lead CRMLead? @relation(fields: [leadId], references: [id], onDelete: SetNull) event Event? @relation(fields: [eventId], references: [id], onDelete: SetNull) activities CRMActivity[] @@index([tenantId]) @@index([leadId]) @@index([eventId]) } model CRMActivity { id String @id @default(cuid()) tenantId String leadId String? dealId String? userId String? type String @default("note") note String dueAt DateTime? completedAt DateTime? createdAt DateTime @default(now()) tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) lead CRMLead? @relation(fields: [leadId], references: [id], onDelete: SetNull) deal CRMDeal? @relation(fields: [dealId], references: [id], onDelete: SetNull) user User? @relation("CRMActivityUser", fields: [userId], references: [id], onDelete: SetNull) @@index([tenantId]) @@index([leadId]) @@index([dealId]) @@index([userId]) } model Workflow { id String @id @default(cuid()) tenantId String name String description String? enabled Boolean @default(true) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) triggers WorkflowTrigger[] actions WorkflowAction[] @@unique([tenantId, name]) @@index([tenantId]) } model WorkflowTrigger { id String @id @default(cuid()) tenantId String workflowId String type String config Json? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) workflow Workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) @@index([tenantId]) @@index([workflowId]) } model WorkflowAction { id String @id @default(cuid()) tenantId String workflowId String type String config Json? sortOrder Int @default(0) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) workflow Workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade) @@index([tenantId]) @@index([workflowId]) } model CalendarRoutingForm { id String @id @default(cuid()) tenantId String eventId String? name String slug String description String? durationMinutes Int @default(30) active Boolean @default(true) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) event Event? @relation(fields: [eventId], references: [id], onDelete: SetNull) slots CalendarSlot[] bookings Booking[] @@unique([tenantId, slug]) @@index([tenantId]) @@index([eventId]) } model CalendarSlot { id String @id @default(cuid()) tenantId String routingFormId String startsAt DateTime endsAt DateTime capacity Int @default(1) bookedCount Int @default(0) active Boolean @default(true) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) routingForm CalendarRoutingForm @relation(fields: [routingFormId], references: [id], onDelete: Cascade) @@index([tenantId]) @@index([routingFormId]) @@index([startsAt]) } model Booking { id String @id @default(cuid()) tenantId String routingFormId String eventId String? attendeeId String? fullName String email String phone String? startsAt DateTime endsAt DateTime status String @default("booked") notes String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade) routingForm CalendarRoutingForm @relation(fields: [routingFormId], references: [id], onDelete: Cascade) event Event? @relation(fields: [eventId], references: [id], onDelete: SetNull) attendee Attendee? @relation(fields: [attendeeId], references: [id], onDelete: SetNull) @@index([tenantId]) @@index([routingFormId]) @@index([eventId]) @@index([attendeeId]) @@index([startsAt]) }