@danielcok17/prisma-db 1.17.0 → 1.18.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danielcok17/prisma-db",
3
- "version": "1.17.0",
3
+ "version": "1.18.3",
4
4
  "description": "Shared Prisma schema for Legal AI applications",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/prisma/app.prisma CHANGED
@@ -30,112 +30,113 @@ model Account {
30
30
  }
31
31
 
32
32
  model User {
33
- id String @id @default(cuid())
34
- brand String @default("smartlex") // Multi-brand: "smartlex" | "justi"
35
- name String?
36
- email String?
37
- emailVerified DateTime?
38
- image String?
33
+ id String @id @default(cuid())
34
+ brand String @default("smartlex") // Multi-brand: "smartlex" | "justi"
35
+ name String?
36
+ email String?
37
+ emailVerified DateTime?
38
+ image String?
39
39
  // Credentials authentication fields
40
- password String? // Hashed password pre credentials login
41
- createdAt DateTime @default(now())
42
- agreedToTerms Boolean @default(false)
43
- practiceArea String[]
44
- lawFirm String?
45
- yearsOfExperience Int?
40
+ password String? // Hashed password pre credentials login
41
+ createdAt DateTime @default(now())
42
+ agreedToTerms Boolean @default(false)
43
+ practiceArea String[]
44
+ lawFirm String?
45
+ yearsOfExperience Int?
46
46
  // Nové polia pre schvalovanie používateľov
47
- isApproved Boolean @default(false) // Či je používateľ schválený
48
- isRejected Boolean @default(false) // Či je používateľ zamietnutý
49
- approvedAt DateTime? // Kedy bol schválený
50
- rejectedAt DateTime? // Kedy bol zamietnutý
51
- approvedBy String? // ID admina, ktorý schválil
52
- rejectedBy String? // ID admina, ktorý zamietol
53
- rejectionReason String? // Dôvod zamietnutia
47
+ isApproved Boolean @default(false) // Či je používateľ schválený
48
+ isRejected Boolean @default(false) // Či je používateľ zamietnutý
49
+ approvedAt DateTime? // Kedy bol schválený
50
+ rejectedAt DateTime? // Kedy bol zamietnutý
51
+ approvedBy String? // ID admina, ktorý schválil
52
+ rejectedBy String? // ID admina, ktorý zamietol
53
+ rejectionReason String? // Dôvod zamietnutia
54
54
  // Nové polia pre tracking a žiadosť
55
- referralSource String? // Odkiaľ sa o nás dozvedel (Google, Facebook, LinkedIn, etc.)
56
- applicationText String? // Text žiadosti o prihlásenie
55
+ referralSource String? // Odkiaľ sa o nás dozvedel (Google, Facebook, LinkedIn, etc.)
56
+ applicationText String? // Text žiadosti o prihlásenie
57
57
  // ✨ STRIPE FIELDS (B2C - len pre individuálnych používateľov)
58
- subscriptionTier SubscriptionTier @default(FREE) // Aktuálny subscription tier (cache)
59
- messageCount Int @default(4) // Free tier default
60
- messageCountResetAt DateTime? // Kedy sa má resetovať message count
61
- adminGrantExpiresAt DateTime? // Kedy admin grant expiruje
58
+ subscriptionTier SubscriptionTier @default(FREE) // Aktuálny subscription tier (cache)
59
+ messageCount Int @default(4) // Free tier default
60
+ messageCountResetAt DateTime? // Kedy sa má resetovať message count
61
+ adminGrantExpiresAt DateTime? // Kedy admin grant expiruje
62
62
  // ✨ COMPANY FIELDS (pre SZČO - samostatne zárobkovo činná osoba)
63
- customerType CustomerType @default(INDIVIDUAL) // Typ zákazníka: fyzická osoba alebo SZČO
64
- companyName String? // Obchodné meno (pre SZČO)
65
- companyNumber String? // IČO (povinné pre SZČO, 8 číslic)
66
- vatNumber String? // IČ DPH (SK + 10 číslic, nepovinné)
67
- taxNumber String? // DIČ (10 číslic, nepovinné)
68
- companyStreet String? // Ulica a číslo (pre SZČO)
69
- companyCity String? // Mesto (pre SZČO)
70
- companyPostalCode String? // PSČ (pre SZČO, formát: XXX XX)
71
- companyCountry String? @default("SK") // Krajina (ISO kód, default SK)
63
+ customerType CustomerType @default(INDIVIDUAL) // Typ zákazníka: fyzická osoba alebo SZČO
64
+ companyName String? // Obchodné meno (pre SZČO)
65
+ companyNumber String? // IČO (povinné pre SZČO, 8 číslic)
66
+ vatNumber String? // IČ DPH (SK + 10 číslic, nepovinné)
67
+ taxNumber String? // DIČ (10 číslic, nepovinné)
68
+ companyStreet String? // Ulica a číslo (pre SZČO)
69
+ companyCity String? // Mesto (pre SZČO)
70
+ companyPostalCode String? // PSČ (pre SZČO, formát: XXX XX)
71
+ companyCountry String? @default("SK") // Krajina (ISO kód, default SK)
72
72
  // ✨ UTM TRACKING & ATTRIBUTION FIELDS
73
73
  // First-visit attribution (celý lifecycle)
74
- firstUtmSource String? // Prvý UTM source
75
- firstUtmMedium String? // Prvý UTM medium
76
- firstUtmCampaign String? // Prvá kampaň
77
- firstUtmContent String? // Prvý content/variant
78
- firstUtmTerm String? // Prvé keywords
79
- firstVisitAt DateTime? // Kedy prvýkrát navštívil web
74
+ firstUtmSource String? // Prvý UTM source
75
+ firstUtmMedium String? // Prvý UTM medium
76
+ firstUtmCampaign String? // Prvá kampaň
77
+ firstUtmContent String? // Prvý content/variant
78
+ firstUtmTerm String? // Prvé keywords
79
+ firstVisitAt DateTime? // Kedy prvýkrát navštívil web
80
80
  // Registration attribution
81
- registrationUtmSource String? // UTM source pri registrácii
82
- registrationUtmMedium String? // UTM medium pri registrácii
83
- registrationUtmCampaign String? // Kampaň pri registrácii
84
- registrationUtmContent String? // Content/variant pri registrácii
85
- registrationUtmTerm String? // Keywords pri registrácii
81
+ registrationUtmSource String? // UTM source pri registrácii
82
+ registrationUtmMedium String? // UTM medium pri registrácii
83
+ registrationUtmCampaign String? // Kampaň pri registrácii
84
+ registrationUtmContent String? // Content/variant pri registrácii
85
+ registrationUtmTerm String? // Keywords pri registrácii
86
86
  // Profile fields
87
- phone String? // Mobilný telefón
88
- phoneVerified DateTime? // Kedy bol telefón overený cez OTP
89
- occupation String? // Povolanie (Právnik, Účtovník, etc.)
90
- preferredLanguage String? @default("sk") // Jazyk odpovedi (sk, cs, en)
87
+ phone String? // Mobilný telefón
88
+ phoneVerified DateTime? // Kedy bol telefón overený cez OTP
89
+ occupation String? // Povolanie (Právnik, Účtovník, etc.)
90
+ preferredLanguage String? @default("sk") // Jazyk odpovedi (sk, cs, en)
91
91
  // Email Lifecycle — 360° user view
92
92
  lastActiveAt DateTime?
93
93
  lastLoginAt DateTime?
94
94
  activatedAt DateTime?
95
- lifecycleStage LifecycleStage @default(REGISTERED)
96
- lifetimeValue Int @default(0)
95
+ lifecycleStage LifecycleStage @default(REGISTERED)
96
+ lifetimeValue Int @default(0)
97
97
  lastEmailSentAt DateTime?
98
98
  lastMarketingEmailSentAt DateTime?
99
- isEmailSuppressed Boolean @default(false)
100
- referralCode String? @unique
101
- churnRiskScore Int @default(0)
99
+ isEmailSuppressed Boolean @default(false)
100
+ referralCode String? @unique
101
+ churnRiskScore Int @default(0)
102
102
  churnRiskUpdatedAt DateTime?
103
103
  // Relations
104
- approvalRequest UserApprovalRequest?
105
- stripeCustomer StripeCustomer? // ✨ B2C Stripe customer
106
- ownedOrganizations Organization[] @relation("OrganizationOwner")
107
- organizationMembers OrganizationMember[]
108
- createdInvites OrganizationInvite[] @relation("InviteCreator")
109
- accounts Account[]
110
- answers Answer[]
111
- conversations Conversation[]
112
- feedbacks Feedback[]
113
- pageViews PageView[]
114
- sessions Session[]
115
- workflowLogs WorkflowLog[]
116
- verificationTokens VerificationToken[]
117
- passwordResetTokens PasswordResetToken[]
118
- phoneOtps PhoneOtp[]
119
- canvasDocuments CanvasDocument[]
120
- canvasDocumentVersions CanvasDocumentVersion[]
104
+ approvalRequest UserApprovalRequest?
105
+ stripeCustomer StripeCustomer? // ✨ B2C Stripe customer
106
+ ownedOrganizations Organization[] @relation("OrganizationOwner")
107
+ organizationMembers OrganizationMember[]
108
+ createdInvites OrganizationInvite[] @relation("InviteCreator")
109
+ accounts Account[]
110
+ answers Answer[]
111
+ conversations Conversation[]
112
+ feedbacks Feedback[]
113
+ pageViews PageView[]
114
+ sessions Session[]
115
+ workflowLogs WorkflowLog[]
116
+ verificationTokens VerificationToken[]
117
+ passwordResetTokens PasswordResetToken[]
118
+ phoneOtps PhoneOtp[]
119
+ canvasDocuments CanvasDocument[]
120
+ canvasDocumentVersions CanvasDocumentVersion[]
121
121
  // Folder system relations
122
- ownedFolders Folder[] @relation("FolderOwner")
123
- addedFolderItems FolderItem[] @relation("FolderItemAdder")
124
- folderActivities FolderActivity[] @relation("FolderActivityUser")
125
- folderSharesReceived FolderShare[] @relation("FolderShareUser")
126
- createdFolderShares FolderShare[] @relation("FolderShareCreator")
122
+ ownedFolders Folder[] @relation("FolderOwner")
123
+ addedFolderItems FolderItem[] @relation("FolderItemAdder")
124
+ folderActivities FolderActivity[] @relation("FolderActivityUser")
125
+ folderSharesReceived FolderShare[] @relation("FolderShareUser")
126
+ createdFolderShares FolderShare[] @relation("FolderShareCreator")
127
127
  // UTM tracking relations
128
- touchPoints TouchPoint[]
129
- conversions Conversion[]
128
+ touchPoints TouchPoint[]
129
+ conversions Conversion[]
130
130
  // Ingestion pipeline
131
- ingestedDocuments IngestedDocument[]
132
- adminGrants AdminGrant[]
133
- emailSends EmailSend[]
134
- emailConsent EmailConsent?
135
- userScore UserScore?
136
- sequenceEnrollments EmailSequenceEnrollment[]
137
- referralsSent Referral[] @relation("ReferralsSent")
138
- referralsReceived Referral[] @relation("ReferralsReceived")
131
+ ingestedDocuments IngestedDocument[]
132
+ adminGrants AdminGrant[]
133
+ invoiceGrants InvoiceGrant[]
134
+ emailSends EmailSend[]
135
+ emailConsent EmailConsent?
136
+ userScore UserScore?
137
+ sequenceEnrollments EmailSequenceEnrollment[]
138
+ referralsSent Referral[] @relation("ReferralsSent")
139
+ referralsReceived Referral[] @relation("ReferralsReceived")
139
140
 
140
141
  // Multi-brand: compound unique allows same email across brands
141
142
  @@unique([email, brand])
@@ -196,10 +197,10 @@ model Organization {
196
197
 
197
198
  // Subscription (B2B)
198
199
  subscriptionTier SubscriptionTier @default(FREE) // Tier organizácie (cache)
199
- messageCount Int @default(0) // Spoločný pool správ pre všetkých členov
200
- messageLimit Int @default(4) // Free tier default
201
- messageCountResetAt DateTime? // Kedy sa má resetovať pool
202
- adminGrantExpiresAt DateTime? // Kedy admin grant expiruje
200
+ messageCount Int @default(0) // Spoločný pool správ pre všetkých členov
201
+ messageLimit Int @default(4) // Free tier default
202
+ messageCountResetAt DateTime? // Kedy sa má resetovať pool
203
+ adminGrantExpiresAt DateTime? // Kedy admin grant expiruje
203
204
 
204
205
  // Settings
205
206
  isActive Boolean @default(true)
@@ -213,6 +214,7 @@ model Organization {
213
214
  stripeCustomer StripeCustomer?
214
215
  folderShares FolderShare[]
215
216
  adminGrants AdminGrant[]
217
+ invoiceGrants InvoiceGrant[]
216
218
 
217
219
  createdAt DateTime @default(now())
218
220
  updatedAt DateTime @updatedAt
@@ -285,7 +287,7 @@ model Conversation {
285
287
  updatedAt DateTime @updatedAt
286
288
  summary String?
287
289
  messagesSinceLastSummary Int? @default(0)
288
- integrityHash String? // HMAC-SHA256 hash chain - posledný chainHash pre verifikáciu integrity
290
+ integrityHash String? // HMAC-SHA256 hash chain - posledný chainHash pre verifikáciu integrity
289
291
  // Relácie
290
292
  answers Answer[]
291
293
  user User @relation(fields: [userId], references: [id])
@@ -310,8 +312,8 @@ model Answer {
310
312
  processingTime Int?
311
313
  model String?
312
314
  userId String?
313
- contentHash String? // HMAC-SHA256 hash obsahu správy (content + role + messageId)
314
- chainHash String? // Hash chain - zahŕňa predchádzajúci chainHash pre integritu sekvencie
315
+ contentHash String? // HMAC-SHA256 hash obsahu správy (content + role + messageId)
316
+ chainHash String? // Hash chain - zahŕňa predchádzajúci chainHash pre integritu sekvencie
315
317
  createdAt DateTime @default(now())
316
318
  updatedAt DateTime @updatedAt
317
319
  // Relácie
@@ -332,13 +334,13 @@ model Answer {
332
334
  }
333
335
 
334
336
  model MessageFile {
335
- id String @id @default(cuid())
336
- answerId String?
337
- fileName String
338
- fileType String
339
- base64Data String
340
- uploadedAt DateTime @default(now())
341
- answer Answer? @relation(fields: [answerId], references: [id], onDelete: Cascade)
337
+ id String @id @default(cuid())
338
+ answerId String?
339
+ fileName String
340
+ fileType String
341
+ base64Data String
342
+ uploadedAt DateTime @default(now())
343
+ answer Answer? @relation(fields: [answerId], references: [id], onDelete: Cascade)
342
344
 
343
345
  @@index([answerId])
344
346
  @@index([fileType])
@@ -725,7 +727,8 @@ model StripeSubscription {
725
727
  trialEnd DateTime?
726
728
 
727
729
  // Additional fields
728
- quantity Int @default(1) // Počet seats (pre LAW_FIRM/ENTERPRISE tier)
730
+ quantity Int @default(1) // Počet seats (pre LAW_FIRM/ENTERPRISE tier)
731
+ collectionMethod CollectionMethod @default(CHARGE_AUTOMATICALLY) // Karta vs faktúra
729
732
  defaultPaymentMethodId String? // Stripe payment method ID
730
733
 
731
734
  metadata Json? // Dodatočné Stripe metadata
@@ -844,11 +847,11 @@ enum CustomerType {
844
847
 
845
848
  // Stripe: Tier predplatného
846
849
  enum SubscriptionTier {
847
- FREE // Free tier (4 messages/month)
848
- LAWYER // Lawyer tier (1000 messages/month, €29/month, 1 user)
849
- LAWYER_PRO // Lawyer Pro tier (3000 messages/month, €49/month, 1 user) - RECOMMENDED
850
- LAW_FIRM // Law Firm tier (10000 messages/month, €129/month, up to 5 users)
851
- ENTERPRISE // Enterprise tier (unlimited messages, unlimited users, custom pricing)
850
+ FREE // Free tier (4 messages/month)
851
+ LAWYER // Lawyer tier (1000 messages/month, €29/month, 1 user)
852
+ LAWYER_PRO // Lawyer Pro tier (3000 messages/month, €49/month, 1 user) - RECOMMENDED
853
+ LAW_FIRM // Law Firm tier (10000 messages/month, €129/month, up to 5 users)
854
+ ENTERPRISE // Enterprise tier (unlimited messages, unlimited users, custom pricing)
852
855
  }
853
856
 
854
857
  // Stripe: Billing interval
@@ -1030,19 +1033,81 @@ model PasswordResetToken {
1030
1033
 
1031
1034
  // Phone OTP verification (BulkGate)
1032
1035
  model PhoneOtp {
1033
- id String @id @default(cuid())
1034
- userId String
1035
- phone String
1036
- bulkgateId String @unique // OTP ID from BulkGate API response
1037
- expiresAt DateTime
1038
- createdAt DateTime @default(now())
1039
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1036
+ id String @id @default(cuid())
1037
+ userId String
1038
+ phone String
1039
+ bulkgateId String @unique // OTP ID from BulkGate API response
1040
+ expiresAt DateTime
1041
+ createdAt DateTime @default(now())
1042
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1040
1043
 
1041
1044
  @@index([userId])
1042
1045
  @@index([bulkgateId])
1043
1046
  @@index([expiresAt])
1044
1047
  }
1045
1048
 
1049
+ model MeetingSlot {
1050
+ id String @id @default(cuid())
1051
+ startsAt DateTime
1052
+ durationMinutes Int @default(30)
1053
+ status MeetingSlotStatus @default(AVAILABLE)
1054
+ holdExpiresAt DateTime?
1055
+ note String?
1056
+ createdByAdminEmail String?
1057
+ createdAt DateTime @default(now())
1058
+ updatedAt DateTime @updatedAt
1059
+
1060
+ bookings MeetingBooking[]
1061
+
1062
+ @@index([status, startsAt])
1063
+ @@index([startsAt])
1064
+ }
1065
+
1066
+ model MeetingBooking {
1067
+ id String @id @default(cuid())
1068
+ slotId String
1069
+ status MeetingBookingStatus @default(OTP_PENDING)
1070
+ name String?
1071
+ email String
1072
+ phone String
1073
+ visitorNote String?
1074
+ publicToken String @unique
1075
+ bulkgateId String? @unique
1076
+ otpExpiresAt DateTime?
1077
+ holdExpiresAt DateTime?
1078
+ phoneVerifiedAt DateTime?
1079
+ slotStartAt DateTime
1080
+ slotEndAt DateTime
1081
+ adminNote String?
1082
+ adminActionBy String?
1083
+ acceptedAt DateTime?
1084
+ declinedAt DateTime?
1085
+ canceledAt DateTime?
1086
+ completedAt DateTime?
1087
+ noShowAt DateTime?
1088
+ createdAt DateTime @default(now())
1089
+ updatedAt DateTime @updatedAt
1090
+
1091
+ slot MeetingSlot @relation(fields: [slotId], references: [id], onDelete: Cascade)
1092
+
1093
+ @@index([slotId])
1094
+ @@index([status, createdAt])
1095
+ @@index([email])
1096
+ @@index([phone])
1097
+ @@index([slotStartAt])
1098
+ }
1099
+
1100
+ model MeetingBookingSettings {
1101
+ id String @id @default("default")
1102
+ bookingEnabled Boolean @default(true)
1103
+ timezone String @default("Europe/Bratislava")
1104
+ bookingHorizonDays Int @default(30)
1105
+ minLeadHours Int @default(12)
1106
+ adminNotificationEmail String @default("info@smartlex.sk")
1107
+ createdAt DateTime @default(now())
1108
+ updatedAt DateTime @updatedAt
1109
+ }
1110
+
1046
1111
  // ============================================
1047
1112
  // INGESTION PIPELINE MODELS
1048
1113
  // ============================================
@@ -1062,7 +1127,7 @@ model IngestedDocument {
1062
1127
  references String[] // Legal references from classification
1063
1128
  chunkCount Int?
1064
1129
  embeddingCost Float? // EUR
1065
- qdrantDocId String? @unique // doc_id used in Qdrant vector payloads
1130
+ qdrantDocId String? @unique // doc_id used in Qdrant vector payloads
1066
1131
  folderId String? // Optional folder association
1067
1132
  storagePath String? // Local filesystem path (dev/staging)
1068
1133
  storageUrl String? // Cloud storage URL (prod — S3/DigitalOcean Spaces)
@@ -1150,6 +1215,36 @@ enum ReferralStatus {
1150
1215
  REWARDED
1151
1216
  }
1152
1217
 
1218
+ enum InvoiceGrantStatus {
1219
+ PENDING // Faktúra odoslaná, čaká na platbu
1220
+ PAID // Zaplatené, grant aktívny
1221
+ VOID // Stornovaná (admin alebo Stripe)
1222
+ }
1223
+
1224
+ enum CollectionMethod {
1225
+ CHARGE_AUTOMATICALLY // Automatické strhnutie kartou
1226
+ SEND_INVOICE // Faktúra na email (bank transfer)
1227
+ }
1228
+
1229
+ enum MeetingSlotStatus {
1230
+ AVAILABLE
1231
+ HELD
1232
+ BOOKED
1233
+ BLOCKED
1234
+ ARCHIVED
1235
+ }
1236
+
1237
+ enum MeetingBookingStatus {
1238
+ OTP_PENDING
1239
+ PENDING_ADMIN_REVIEW
1240
+ ACCEPTED
1241
+ DECLINED
1242
+ CANCELED
1243
+ COMPLETED
1244
+ NO_SHOW
1245
+ EXPIRED
1246
+ }
1247
+
1153
1248
  // ============================================
1154
1249
  // CANVAS DOCUMENT MODELS
1155
1250
  // ============================================
@@ -1372,6 +1467,33 @@ model AdminGrant {
1372
1467
  @@index([isActive])
1373
1468
  }
1374
1469
 
1470
+ // One-off admin invoice — standalone faktúra pre špeciálne prípady (demo, trial, custom deal)
1471
+ // Pre recurring fakturáciu použiť StripeSubscription s collectionMethod=SEND_INVOICE
1472
+ model InvoiceGrant {
1473
+ id String @id @default(cuid())
1474
+ userId String?
1475
+ organizationId String?
1476
+ stripeInvoiceId String @unique
1477
+ tier SubscriptionTier
1478
+ durationDays Int // Na koľko dní sa aktivuje grant po platbe
1479
+ amount Int // Suma v centoch (EUR)
1480
+ status InvoiceGrantStatus @default(PENDING)
1481
+ grantId String? // ID AdminGrantu vytvoreného po platbe
1482
+ createdBy String // Admin email
1483
+ notes String?
1484
+ paidAt DateTime?
1485
+ createdAt DateTime @default(now())
1486
+ updatedAt DateTime @updatedAt
1487
+
1488
+ user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
1489
+ organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
1490
+
1491
+ @@index([userId])
1492
+ @@index([organizationId])
1493
+ @@index([status])
1494
+ @@index([stripeInvoiceId])
1495
+ }
1496
+
1375
1497
  model AppInvite {
1376
1498
  id String @id @default(cuid())
1377
1499
  email String
@@ -1399,39 +1521,39 @@ model AppInvite {
1399
1521
  // ============================================
1400
1522
 
1401
1523
  model EmailSend {
1402
- id String @id @default(cuid())
1403
- userId String?
1404
- templateSlug String
1405
- campaignId String?
1406
-
1407
- status EmailStatus @default(QUEUED)
1408
- sentAt DateTime?
1409
- deliveredAt DateTime?
1410
- toEmail String
1411
- fromEmail String @default("info@mail.smartlex.sk")
1412
-
1413
- openedAt DateTime?
1414
- openCount Int @default(0)
1415
- clickedAt DateTime?
1416
- clickCount Int @default(0)
1524
+ id String @id @default(cuid())
1525
+ userId String?
1526
+ templateSlug String
1527
+ campaignId String?
1528
+
1529
+ status EmailStatus @default(QUEUED)
1530
+ sentAt DateTime?
1531
+ deliveredAt DateTime?
1532
+ toEmail String
1533
+ fromEmail String @default("info@mail.smartlex.sk")
1534
+
1535
+ openedAt DateTime?
1536
+ openCount Int @default(0)
1537
+ clickedAt DateTime?
1538
+ clickCount Int @default(0)
1539
+
1540
+ bouncedAt DateTime?
1541
+ bounceType BounceType?
1542
+ complainedAt DateTime?
1543
+ unsubscribedAt DateTime?
1544
+
1545
+ errorMessage String?
1546
+ errorCode String?
1547
+
1548
+ subjectLine String?
1549
+ espMessageId String? @unique
1550
+ idempotencyKey String? @unique
1551
+ isTransactional Boolean @default(false)
1552
+ metadata Json?
1417
1553
 
1418
- bouncedAt DateTime?
1419
- bounceType BounceType?
1420
- complainedAt DateTime?
1421
- unsubscribedAt DateTime?
1422
-
1423
- errorMessage String?
1424
- errorCode String?
1425
-
1426
- subjectLine String?
1427
- espMessageId String? @unique
1428
- idempotencyKey String? @unique
1429
- isTransactional Boolean @default(false)
1430
- metadata Json?
1431
-
1432
- createdAt DateTime @default(now())
1554
+ createdAt DateTime @default(now())
1433
1555
 
1434
- user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
1556
+ user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
1435
1557
 
1436
1558
  @@index([userId])
1437
1559
  @@index([templateSlug])
@@ -1445,43 +1567,43 @@ model EmailSend {
1445
1567
  }
1446
1568
 
1447
1569
  model EmailSequence {
1448
- id String @id @default(cuid())
1449
- slug String @unique
1450
- name String
1451
- description String?
1452
- triggerEvent String
1453
- isActive Boolean @default(true)
1570
+ id String @id @default(cuid())
1571
+ slug String @unique
1572
+ name String
1573
+ description String?
1574
+ triggerEvent String
1575
+ isActive Boolean @default(true)
1454
1576
 
1455
- steps EmailSequenceStep[]
1456
- enrollments EmailSequenceEnrollment[]
1577
+ steps EmailSequenceStep[]
1578
+ enrollments EmailSequenceEnrollment[]
1457
1579
 
1458
- createdAt DateTime @default(now())
1459
- updatedAt DateTime @updatedAt
1580
+ createdAt DateTime @default(now())
1581
+ updatedAt DateTime @updatedAt
1460
1582
  }
1461
1583
 
1462
1584
  model EmailSequenceStep {
1463
- id String @id @default(cuid())
1585
+ id String @id @default(cuid())
1464
1586
  sequenceId String
1465
1587
  templateSlug String
1466
1588
  stepOrder Int
1467
- delayMinutes Int @default(0)
1589
+ delayMinutes Int @default(0)
1468
1590
  sendCondition Json?
1469
1591
  skipCondition Json?
1470
- isActive Boolean @default(true)
1592
+ isActive Boolean @default(true)
1471
1593
 
1472
- sequence EmailSequence @relation(fields: [sequenceId], references: [id], onDelete: Cascade)
1594
+ sequence EmailSequence @relation(fields: [sequenceId], references: [id], onDelete: Cascade)
1473
1595
 
1474
- createdAt DateTime @default(now())
1596
+ createdAt DateTime @default(now())
1475
1597
 
1476
1598
  @@unique([sequenceId, stepOrder])
1477
1599
  }
1478
1600
 
1479
1601
  model EmailSequenceEnrollment {
1480
- id String @id @default(cuid())
1481
- userId String
1482
- sequenceId String
1483
- currentStep Int @default(0)
1484
- status EnrollmentStatus @default(ACTIVE)
1602
+ id String @id @default(cuid())
1603
+ userId String
1604
+ sequenceId String
1605
+ currentStep Int @default(0)
1606
+ status EnrollmentStatus @default(ACTIVE)
1485
1607
 
1486
1608
  enrolledAt DateTime @default(now())
1487
1609
  completedAt DateTime?
@@ -1490,51 +1612,51 @@ model EmailSequenceEnrollment {
1490
1612
  nextSendAt DateTime?
1491
1613
  processingLockedAt DateTime?
1492
1614
 
1493
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1494
- sequence EmailSequence @relation(fields: [sequenceId], references: [id])
1615
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1616
+ sequence EmailSequence @relation(fields: [sequenceId], references: [id])
1495
1617
 
1496
1618
  @@index([status, nextSendAt])
1497
1619
  @@index([userId, sequenceId, status])
1498
1620
  }
1499
1621
 
1500
1622
  model EmailConsent {
1501
- id String @id @default(cuid())
1502
- userId String @unique
1623
+ id String @id @default(cuid())
1624
+ userId String @unique
1503
1625
 
1504
- marketingConsent Boolean @default(false)
1505
- productUpdatesConsent Boolean @default(false)
1506
- legalNewsConsent Boolean @default(false)
1626
+ marketingConsent Boolean @default(false)
1627
+ productUpdatesConsent Boolean @default(false)
1628
+ legalNewsConsent Boolean @default(false)
1507
1629
 
1508
- doubleOptinSentAt DateTime?
1509
- doubleOptinConfirmedAt DateTime?
1510
- doubleOptinToken String? @unique
1630
+ doubleOptinSentAt DateTime?
1631
+ doubleOptinConfirmedAt DateTime?
1632
+ doubleOptinToken String? @unique
1511
1633
 
1512
- user User @relation(fields: [userId], references: [id], onDelete: Restrict)
1513
- history EmailConsentHistory[]
1634
+ user User @relation(fields: [userId], references: [id], onDelete: Restrict)
1635
+ history EmailConsentHistory[]
1514
1636
 
1515
- createdAt DateTime @default(now())
1516
- updatedAt DateTime @updatedAt
1637
+ createdAt DateTime @default(now())
1638
+ updatedAt DateTime @updatedAt
1517
1639
  }
1518
1640
 
1519
1641
  model EmailConsentHistory {
1520
- id String @id @default(cuid())
1521
- consentId String
1522
- userId String
1642
+ id String @id @default(cuid())
1643
+ consentId String
1644
+ userId String
1523
1645
 
1524
- action ConsentAction
1525
- channel String
1526
- oldValue Boolean?
1527
- newValue Boolean
1646
+ action ConsentAction
1647
+ channel String
1648
+ oldValue Boolean?
1649
+ newValue Boolean
1528
1650
 
1529
- consentText String
1530
- consentVersion String
1531
- consentMethod String
1532
- ipAddress String?
1533
- userAgent String?
1651
+ consentText String
1652
+ consentVersion String
1653
+ consentMethod String
1654
+ ipAddress String?
1655
+ userAgent String?
1534
1656
 
1535
- consent EmailConsent @relation(fields: [consentId], references: [id], onDelete: Restrict)
1657
+ consent EmailConsent @relation(fields: [consentId], references: [id], onDelete: Restrict)
1536
1658
 
1537
- createdAt DateTime @default(now())
1659
+ createdAt DateTime @default(now())
1538
1660
 
1539
1661
  @@index([userId, createdAt])
1540
1662
  @@index([consentId])
@@ -1542,25 +1664,25 @@ model EmailConsentHistory {
1542
1664
  }
1543
1665
 
1544
1666
  model UserScore {
1545
- id String @id @default(cuid())
1546
- userId String @unique
1547
- score Int @default(0)
1548
- leadSegment LeadSegment @default(COLD)
1549
- churnRisk ChurnRisk @default(NONE)
1667
+ id String @id @default(cuid())
1668
+ userId String @unique
1669
+ score Int @default(0)
1670
+ leadSegment LeadSegment @default(COLD)
1671
+ churnRisk ChurnRisk @default(NONE)
1550
1672
 
1551
- signupScore Int @default(0)
1552
- engagementScore Int @default(0)
1553
- usageScore Int @default(0)
1554
- intentScore Int @default(0)
1555
- decayScore Int @default(0)
1673
+ signupScore Int @default(0)
1674
+ engagementScore Int @default(0)
1675
+ usageScore Int @default(0)
1676
+ intentScore Int @default(0)
1677
+ decayScore Int @default(0)
1556
1678
 
1557
- lastActivityAt DateTime?
1558
- scoreUpdatedAt DateTime @default(now())
1679
+ lastActivityAt DateTime?
1680
+ scoreUpdatedAt DateTime @default(now())
1559
1681
 
1560
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1561
- events UserScoreEvent[]
1682
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1683
+ events UserScoreEvent[]
1562
1684
 
1563
- createdAt DateTime @default(now())
1685
+ createdAt DateTime @default(now())
1564
1686
 
1565
1687
  @@index([score])
1566
1688
  @@index([leadSegment])
@@ -1568,18 +1690,18 @@ model UserScore {
1568
1690
  }
1569
1691
 
1570
1692
  model UserScoreEvent {
1571
- id String @id @default(cuid())
1572
- userScoreId String
1573
- userId String
1574
- eventType String
1575
- pointsDelta Int
1576
- scoreBefore Int
1577
- scoreAfter Int
1578
- metadata Json?
1693
+ id String @id @default(cuid())
1694
+ userScoreId String
1695
+ userId String
1696
+ eventType String
1697
+ pointsDelta Int
1698
+ scoreBefore Int
1699
+ scoreAfter Int
1700
+ metadata Json?
1579
1701
 
1580
- userScore UserScore @relation(fields: [userScoreId], references: [id], onDelete: Cascade)
1702
+ userScore UserScore @relation(fields: [userScoreId], references: [id], onDelete: Cascade)
1581
1703
 
1582
- createdAt DateTime @default(now())
1704
+ createdAt DateTime @default(now())
1583
1705
 
1584
1706
  @@index([userId, createdAt])
1585
1707
  @@index([userScoreId])
@@ -1588,35 +1710,35 @@ model UserScoreEvent {
1588
1710
  }
1589
1711
 
1590
1712
  model EmailSuppression {
1591
- id String @id @default(cuid())
1592
- email String
1593
- reason SuppressionReason
1594
- source String?
1713
+ id String @id @default(cuid())
1714
+ email String
1715
+ reason SuppressionReason
1716
+ source String?
1595
1717
 
1596
- suppressedAt DateTime @default(now())
1718
+ suppressedAt DateTime @default(now())
1597
1719
 
1598
1720
  @@unique([email, reason])
1599
1721
  @@index([email])
1600
1722
  }
1601
1723
 
1602
1724
  model Referral {
1603
- id String @id @default(cuid())
1604
- referrerId String?
1605
- referredUserId String?
1606
- referredEmail String?
1607
- status ReferralStatus @default(PENDING)
1608
- referralCode String @unique
1609
-
1610
- rewardType String?
1611
- rewardedAt DateTime?
1612
- clickCount Int @default(0)
1613
- registeredAt DateTime?
1614
- convertedAt DateTime?
1615
-
1616
- createdAt DateTime @default(now())
1617
-
1618
- referrer User? @relation("ReferralsSent", fields: [referrerId], references: [id], onDelete: SetNull)
1619
- referredUser User? @relation("ReferralsReceived", fields: [referredUserId], references: [id], onDelete: SetNull)
1725
+ id String @id @default(cuid())
1726
+ referrerId String?
1727
+ referredUserId String?
1728
+ referredEmail String?
1729
+ status ReferralStatus @default(PENDING)
1730
+ referralCode String @unique
1731
+
1732
+ rewardType String?
1733
+ rewardedAt DateTime?
1734
+ clickCount Int @default(0)
1735
+ registeredAt DateTime?
1736
+ convertedAt DateTime?
1737
+
1738
+ createdAt DateTime @default(now())
1739
+
1740
+ referrer User? @relation("ReferralsSent", fields: [referrerId], references: [id], onDelete: SetNull)
1741
+ referredUser User? @relation("ReferralsReceived", fields: [referredUserId], references: [id], onDelete: SetNull)
1620
1742
 
1621
1743
  @@index([referrerId])
1622
1744
  @@index([referredUserId])
@@ -0,0 +1,49 @@
1
+ -- CreateEnum
2
+ CREATE TYPE "InvoiceGrantStatus" AS ENUM ('PENDING', 'PAID', 'VOID');
3
+
4
+ -- CreateEnum
5
+ CREATE TYPE "CollectionMethod" AS ENUM ('CHARGE_AUTOMATICALLY', 'SEND_INVOICE');
6
+
7
+ -- AlterTable
8
+ ALTER TABLE "StripeSubscription" ADD COLUMN "collectionMethod" "CollectionMethod" NOT NULL DEFAULT 'CHARGE_AUTOMATICALLY';
9
+
10
+ -- CreateTable
11
+ CREATE TABLE "InvoiceGrant" (
12
+ "id" TEXT NOT NULL,
13
+ "userId" TEXT,
14
+ "organizationId" TEXT,
15
+ "stripeInvoiceId" TEXT NOT NULL,
16
+ "tier" "SubscriptionTier" NOT NULL,
17
+ "durationDays" INTEGER NOT NULL,
18
+ "amount" INTEGER NOT NULL,
19
+ "status" "InvoiceGrantStatus" NOT NULL DEFAULT 'PENDING',
20
+ "grantId" TEXT,
21
+ "createdBy" TEXT NOT NULL,
22
+ "notes" TEXT,
23
+ "paidAt" TIMESTAMP(3),
24
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
25
+ "updatedAt" TIMESTAMP(3) NOT NULL,
26
+
27
+ CONSTRAINT "InvoiceGrant_pkey" PRIMARY KEY ("id")
28
+ );
29
+
30
+ -- CreateIndex
31
+ CREATE UNIQUE INDEX "InvoiceGrant_stripeInvoiceId_key" ON "InvoiceGrant"("stripeInvoiceId");
32
+
33
+ -- CreateIndex
34
+ CREATE INDEX "InvoiceGrant_userId_idx" ON "InvoiceGrant"("userId");
35
+
36
+ -- CreateIndex
37
+ CREATE INDEX "InvoiceGrant_organizationId_idx" ON "InvoiceGrant"("organizationId");
38
+
39
+ -- CreateIndex
40
+ CREATE INDEX "InvoiceGrant_status_idx" ON "InvoiceGrant"("status");
41
+
42
+ -- CreateIndex
43
+ CREATE INDEX "InvoiceGrant_stripeInvoiceId_idx" ON "InvoiceGrant"("stripeInvoiceId");
44
+
45
+ -- AddForeignKey
46
+ ALTER TABLE "InvoiceGrant" ADD CONSTRAINT "InvoiceGrant_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
47
+
48
+ -- AddForeignKey
49
+ ALTER TABLE "InvoiceGrant" ADD CONSTRAINT "InvoiceGrant_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE CASCADE ON UPDATE CASCADE;
@@ -0,0 +1,102 @@
1
+ -- CreateEnum
2
+ CREATE TYPE "MeetingSlotStatus" AS ENUM ('AVAILABLE', 'HELD', 'BOOKED', 'BLOCKED', 'ARCHIVED');
3
+
4
+ -- CreateEnum
5
+ CREATE TYPE "MeetingBookingStatus" AS ENUM ('OTP_PENDING', 'PENDING_ADMIN_REVIEW', 'ACCEPTED', 'DECLINED', 'CANCELED', 'COMPLETED', 'NO_SHOW', 'EXPIRED');
6
+
7
+ -- CreateTable
8
+ CREATE TABLE "MeetingSlot" (
9
+ "id" TEXT NOT NULL,
10
+ "startsAt" TIMESTAMP(3) NOT NULL,
11
+ "durationMinutes" INTEGER NOT NULL DEFAULT 30,
12
+ "status" "MeetingSlotStatus" NOT NULL DEFAULT 'AVAILABLE',
13
+ "holdExpiresAt" TIMESTAMP(3),
14
+ "note" TEXT,
15
+ "createdByAdminEmail" TEXT,
16
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
17
+ "updatedAt" TIMESTAMP(3) NOT NULL,
18
+
19
+ CONSTRAINT "MeetingSlot_pkey" PRIMARY KEY ("id")
20
+ );
21
+
22
+ -- CreateTable
23
+ CREATE TABLE "MeetingBooking" (
24
+ "id" TEXT NOT NULL,
25
+ "slotId" TEXT NOT NULL,
26
+ "status" "MeetingBookingStatus" NOT NULL DEFAULT 'OTP_PENDING',
27
+ "name" TEXT,
28
+ "email" TEXT NOT NULL,
29
+ "phone" TEXT NOT NULL,
30
+ "visitorNote" TEXT,
31
+ "publicToken" TEXT NOT NULL,
32
+ "bulkgateId" TEXT,
33
+ "otpExpiresAt" TIMESTAMP(3),
34
+ "holdExpiresAt" TIMESTAMP(3),
35
+ "phoneVerifiedAt" TIMESTAMP(3),
36
+ "slotStartAt" TIMESTAMP(3) NOT NULL,
37
+ "slotEndAt" TIMESTAMP(3) NOT NULL,
38
+ "adminNote" TEXT,
39
+ "adminActionBy" TEXT,
40
+ "acceptedAt" TIMESTAMP(3),
41
+ "declinedAt" TIMESTAMP(3),
42
+ "canceledAt" TIMESTAMP(3),
43
+ "completedAt" TIMESTAMP(3),
44
+ "noShowAt" TIMESTAMP(3),
45
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
46
+ "updatedAt" TIMESTAMP(3) NOT NULL,
47
+
48
+ CONSTRAINT "MeetingBooking_pkey" PRIMARY KEY ("id")
49
+ );
50
+
51
+ -- CreateTable
52
+ CREATE TABLE "MeetingBookingSettings" (
53
+ "id" TEXT NOT NULL DEFAULT 'default',
54
+ "bookingEnabled" BOOLEAN NOT NULL DEFAULT true,
55
+ "timezone" TEXT NOT NULL DEFAULT 'Europe/Bratislava',
56
+ "bookingHorizonDays" INTEGER NOT NULL DEFAULT 30,
57
+ "minLeadHours" INTEGER NOT NULL DEFAULT 12,
58
+ "adminNotificationEmail" TEXT NOT NULL DEFAULT 'info@smartlex.sk',
59
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
60
+ "updatedAt" TIMESTAMP(3) NOT NULL,
61
+
62
+ CONSTRAINT "MeetingBookingSettings_pkey" PRIMARY KEY ("id")
63
+ );
64
+
65
+ -- CreateIndex
66
+ CREATE INDEX "MeetingSlot_status_startsAt_idx" ON "MeetingSlot"("status", "startsAt");
67
+
68
+ -- CreateIndex
69
+ CREATE INDEX "MeetingSlot_startsAt_idx" ON "MeetingSlot"("startsAt");
70
+
71
+ -- CreateIndex
72
+ CREATE UNIQUE INDEX IF NOT EXISTS "MeetingSlot_startsAt_duration_active_key"
73
+ ON "MeetingSlot"("startsAt", "durationMinutes")
74
+ WHERE "status" <> 'ARCHIVED';
75
+
76
+ -- CreateIndex
77
+ CREATE UNIQUE INDEX "MeetingBooking_publicToken_key" ON "MeetingBooking"("publicToken");
78
+
79
+ -- CreateIndex
80
+ CREATE UNIQUE INDEX "MeetingBooking_bulkgateId_key" ON "MeetingBooking"("bulkgateId");
81
+
82
+ -- CreateIndex
83
+ CREATE INDEX "MeetingBooking_slotId_idx" ON "MeetingBooking"("slotId");
84
+
85
+ -- CreateIndex
86
+ CREATE INDEX "MeetingBooking_status_createdAt_idx" ON "MeetingBooking"("status", "createdAt");
87
+
88
+ -- CreateIndex
89
+ CREATE INDEX "MeetingBooking_email_idx" ON "MeetingBooking"("email");
90
+
91
+ -- CreateIndex
92
+ CREATE INDEX "MeetingBooking_phone_idx" ON "MeetingBooking"("phone");
93
+
94
+ -- CreateIndex
95
+ CREATE INDEX "MeetingBooking_slotStartAt_idx" ON "MeetingBooking"("slotStartAt");
96
+
97
+ -- AddForeignKey
98
+ ALTER TABLE "MeetingBooking" ADD CONSTRAINT "MeetingBooking_slotId_fkey" FOREIGN KEY ("slotId") REFERENCES "MeetingSlot"("id") ON DELETE CASCADE ON UPDATE CASCADE;
99
+
100
+ INSERT INTO "MeetingBookingSettings" ("id", "updatedAt")
101
+ VALUES ('default', CURRENT_TIMESTAMP)
102
+ ON CONFLICT ("id") DO NOTHING;