@danielcok17/prisma-db 1.16.1 → 1.18.0

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.16.1",
3
+ "version": "1.18.0",
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
@@ -88,6 +88,18 @@ model User {
88
88
  phoneVerified DateTime? // Kedy bol telefón overený cez OTP
89
89
  occupation String? // Povolanie (Právnik, Účtovník, etc.)
90
90
  preferredLanguage String? @default("sk") // Jazyk odpovedi (sk, cs, en)
91
+ // Email Lifecycle — 360° user view
92
+ lastActiveAt DateTime?
93
+ lastLoginAt DateTime?
94
+ activatedAt DateTime?
95
+ lifecycleStage LifecycleStage @default(REGISTERED)
96
+ lifetimeValue Int @default(0)
97
+ lastEmailSentAt DateTime?
98
+ lastMarketingEmailSentAt DateTime?
99
+ isEmailSuppressed Boolean @default(false)
100
+ referralCode String? @unique
101
+ churnRiskScore Int @default(0)
102
+ churnRiskUpdatedAt DateTime?
91
103
  // Relations
92
104
  approvalRequest UserApprovalRequest?
93
105
  stripeCustomer StripeCustomer? // ✨ B2C Stripe customer
@@ -118,6 +130,13 @@ model User {
118
130
  // Ingestion pipeline
119
131
  ingestedDocuments IngestedDocument[]
120
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")
121
140
 
122
141
  // Multi-brand: compound unique allows same email across brands
123
142
  @@unique([email, brand])
@@ -133,6 +152,9 @@ model User {
133
152
  @@index([registrationUtmSource])
134
153
  @@index([registrationUtmCampaign])
135
154
  @@index([firstVisitAt])
155
+ @@index([lifecycleStage])
156
+ @@index([lastActiveAt])
157
+ @@index([isEmailSuppressed])
136
158
  }
137
159
 
138
160
  // Nový model pre žiadosti o schválenie
@@ -192,6 +214,7 @@ model Organization {
192
214
  stripeCustomer StripeCustomer?
193
215
  folderShares FolderShare[]
194
216
  adminGrants AdminGrant[]
217
+ invoiceGrants InvoiceGrant[]
195
218
 
196
219
  createdAt DateTime @default(now())
197
220
  updatedAt DateTime @updatedAt
@@ -704,7 +727,8 @@ model StripeSubscription {
704
727
  trialEnd DateTime?
705
728
 
706
729
  // Additional fields
707
- 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
708
732
  defaultPaymentMethodId String? // Stripe payment method ID
709
733
 
710
734
  metadata Json? // Dodatočné Stripe metadata
@@ -1065,6 +1089,81 @@ enum IngestionStatus {
1065
1089
  ERROR
1066
1090
  }
1067
1091
 
1092
+ enum LifecycleStage {
1093
+ REGISTERED
1094
+ ACTIVATED
1095
+ PAYING
1096
+ SUSPENDED
1097
+ CHURNED
1098
+ REACTIVATED
1099
+ }
1100
+
1101
+ enum EmailStatus {
1102
+ QUEUED
1103
+ SENT
1104
+ DELIVERED
1105
+ BOUNCED
1106
+ FAILED
1107
+ COMPLAINED
1108
+ }
1109
+
1110
+ enum EnrollmentStatus {
1111
+ ACTIVE
1112
+ COMPLETED
1113
+ EXITED
1114
+ PAUSED
1115
+ }
1116
+
1117
+ enum LeadSegment {
1118
+ COLD
1119
+ WARM
1120
+ HOT
1121
+ CONVERTING
1122
+ }
1123
+
1124
+ enum ChurnRisk {
1125
+ NONE
1126
+ LOW
1127
+ MEDIUM
1128
+ HIGH
1129
+ }
1130
+
1131
+ enum BounceType {
1132
+ HARD
1133
+ SOFT
1134
+ }
1135
+
1136
+ enum ConsentAction {
1137
+ GRANTED
1138
+ WITHDRAWN
1139
+ MODIFIED
1140
+ }
1141
+
1142
+ enum SuppressionReason {
1143
+ HARD_BOUNCE
1144
+ COMPLAINT
1145
+ MANUAL
1146
+ SUNSET
1147
+ }
1148
+
1149
+ enum ReferralStatus {
1150
+ PENDING
1151
+ REGISTERED
1152
+ CONVERTED
1153
+ REWARDED
1154
+ }
1155
+
1156
+ enum InvoiceGrantStatus {
1157
+ PENDING // Faktúra odoslaná, čaká na platbu
1158
+ PAID // Zaplatené, grant aktívny
1159
+ VOID // Stornovaná (admin alebo Stripe)
1160
+ }
1161
+
1162
+ enum CollectionMethod {
1163
+ CHARGE_AUTOMATICALLY // Automatické strhnutie kartou
1164
+ SEND_INVOICE // Faktúra na email (bank transfer)
1165
+ }
1166
+
1068
1167
  // ============================================
1069
1168
  // CANVAS DOCUMENT MODELS
1070
1169
  // ============================================
@@ -1287,6 +1386,33 @@ model AdminGrant {
1287
1386
  @@index([isActive])
1288
1387
  }
1289
1388
 
1389
+ // One-off admin invoice — standalone faktúra pre špeciálne prípady (demo, trial, custom deal)
1390
+ // Pre recurring fakturáciu použiť StripeSubscription s collectionMethod=SEND_INVOICE
1391
+ model InvoiceGrant {
1392
+ id String @id @default(cuid())
1393
+ userId String?
1394
+ organizationId String?
1395
+ stripeInvoiceId String @unique
1396
+ tier SubscriptionTier
1397
+ durationDays Int // Na koľko dní sa aktivuje grant po platbe
1398
+ amount Int // Suma v centoch (EUR)
1399
+ status InvoiceGrantStatus @default(PENDING)
1400
+ grantId String? // ID AdminGrantu vytvoreného po platbe
1401
+ createdBy String // Admin email
1402
+ notes String?
1403
+ paidAt DateTime?
1404
+ createdAt DateTime @default(now())
1405
+ updatedAt DateTime @updatedAt
1406
+
1407
+ user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
1408
+ organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
1409
+
1410
+ @@index([userId])
1411
+ @@index([organizationId])
1412
+ @@index([status])
1413
+ @@index([stripeInvoiceId])
1414
+ }
1415
+
1290
1416
  model AppInvite {
1291
1417
  id String @id @default(cuid())
1292
1418
  email String
@@ -1308,3 +1434,233 @@ model AppInvite {
1308
1434
  @@index([token])
1309
1435
  @@index([isActive])
1310
1436
  }
1437
+
1438
+ // ============================================
1439
+ // EMAIL LIFECYCLE MODELS
1440
+ // ============================================
1441
+
1442
+ model EmailSend {
1443
+ id String @id @default(cuid())
1444
+ userId String?
1445
+ templateSlug String
1446
+ campaignId String?
1447
+
1448
+ status EmailStatus @default(QUEUED)
1449
+ sentAt DateTime?
1450
+ deliveredAt DateTime?
1451
+ toEmail String
1452
+ fromEmail String @default("info@mail.smartlex.sk")
1453
+
1454
+ openedAt DateTime?
1455
+ openCount Int @default(0)
1456
+ clickedAt DateTime?
1457
+ clickCount Int @default(0)
1458
+
1459
+ bouncedAt DateTime?
1460
+ bounceType BounceType?
1461
+ complainedAt DateTime?
1462
+ unsubscribedAt DateTime?
1463
+
1464
+ errorMessage String?
1465
+ errorCode String?
1466
+
1467
+ subjectLine String?
1468
+ espMessageId String? @unique
1469
+ idempotencyKey String? @unique
1470
+ isTransactional Boolean @default(false)
1471
+ metadata Json?
1472
+
1473
+ createdAt DateTime @default(now())
1474
+
1475
+ user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
1476
+
1477
+ @@index([userId])
1478
+ @@index([templateSlug])
1479
+ @@index([status])
1480
+ @@index([sentAt])
1481
+ @@index([campaignId])
1482
+ @@index([toEmail])
1483
+ @@index([userId, sentAt])
1484
+ @@index([status, sentAt])
1485
+ @@index([userId, isTransactional, sentAt])
1486
+ }
1487
+
1488
+ model EmailSequence {
1489
+ id String @id @default(cuid())
1490
+ slug String @unique
1491
+ name String
1492
+ description String?
1493
+ triggerEvent String
1494
+ isActive Boolean @default(true)
1495
+
1496
+ steps EmailSequenceStep[]
1497
+ enrollments EmailSequenceEnrollment[]
1498
+
1499
+ createdAt DateTime @default(now())
1500
+ updatedAt DateTime @updatedAt
1501
+ }
1502
+
1503
+ model EmailSequenceStep {
1504
+ id String @id @default(cuid())
1505
+ sequenceId String
1506
+ templateSlug String
1507
+ stepOrder Int
1508
+ delayMinutes Int @default(0)
1509
+ sendCondition Json?
1510
+ skipCondition Json?
1511
+ isActive Boolean @default(true)
1512
+
1513
+ sequence EmailSequence @relation(fields: [sequenceId], references: [id], onDelete: Cascade)
1514
+
1515
+ createdAt DateTime @default(now())
1516
+
1517
+ @@unique([sequenceId, stepOrder])
1518
+ }
1519
+
1520
+ model EmailSequenceEnrollment {
1521
+ id String @id @default(cuid())
1522
+ userId String
1523
+ sequenceId String
1524
+ currentStep Int @default(0)
1525
+ status EnrollmentStatus @default(ACTIVE)
1526
+
1527
+ enrolledAt DateTime @default(now())
1528
+ completedAt DateTime?
1529
+ exitedAt DateTime?
1530
+ exitReason String?
1531
+ nextSendAt DateTime?
1532
+ processingLockedAt DateTime?
1533
+
1534
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1535
+ sequence EmailSequence @relation(fields: [sequenceId], references: [id])
1536
+
1537
+ @@index([status, nextSendAt])
1538
+ @@index([userId, sequenceId, status])
1539
+ }
1540
+
1541
+ model EmailConsent {
1542
+ id String @id @default(cuid())
1543
+ userId String @unique
1544
+
1545
+ marketingConsent Boolean @default(false)
1546
+ productUpdatesConsent Boolean @default(false)
1547
+ legalNewsConsent Boolean @default(false)
1548
+
1549
+ doubleOptinSentAt DateTime?
1550
+ doubleOptinConfirmedAt DateTime?
1551
+ doubleOptinToken String? @unique
1552
+
1553
+ user User @relation(fields: [userId], references: [id], onDelete: Restrict)
1554
+ history EmailConsentHistory[]
1555
+
1556
+ createdAt DateTime @default(now())
1557
+ updatedAt DateTime @updatedAt
1558
+ }
1559
+
1560
+ model EmailConsentHistory {
1561
+ id String @id @default(cuid())
1562
+ consentId String
1563
+ userId String
1564
+
1565
+ action ConsentAction
1566
+ channel String
1567
+ oldValue Boolean?
1568
+ newValue Boolean
1569
+
1570
+ consentText String
1571
+ consentVersion String
1572
+ consentMethod String
1573
+ ipAddress String?
1574
+ userAgent String?
1575
+
1576
+ consent EmailConsent @relation(fields: [consentId], references: [id], onDelete: Restrict)
1577
+
1578
+ createdAt DateTime @default(now())
1579
+
1580
+ @@index([userId, createdAt])
1581
+ @@index([consentId])
1582
+ @@index([createdAt])
1583
+ }
1584
+
1585
+ model UserScore {
1586
+ id String @id @default(cuid())
1587
+ userId String @unique
1588
+ score Int @default(0)
1589
+ leadSegment LeadSegment @default(COLD)
1590
+ churnRisk ChurnRisk @default(NONE)
1591
+
1592
+ signupScore Int @default(0)
1593
+ engagementScore Int @default(0)
1594
+ usageScore Int @default(0)
1595
+ intentScore Int @default(0)
1596
+ decayScore Int @default(0)
1597
+
1598
+ lastActivityAt DateTime?
1599
+ scoreUpdatedAt DateTime @default(now())
1600
+
1601
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
1602
+ events UserScoreEvent[]
1603
+
1604
+ createdAt DateTime @default(now())
1605
+
1606
+ @@index([score])
1607
+ @@index([leadSegment])
1608
+ @@index([churnRisk])
1609
+ }
1610
+
1611
+ model UserScoreEvent {
1612
+ id String @id @default(cuid())
1613
+ userScoreId String
1614
+ userId String
1615
+ eventType String
1616
+ pointsDelta Int
1617
+ scoreBefore Int
1618
+ scoreAfter Int
1619
+ metadata Json?
1620
+
1621
+ userScore UserScore @relation(fields: [userScoreId], references: [id], onDelete: Cascade)
1622
+
1623
+ createdAt DateTime @default(now())
1624
+
1625
+ @@index([userId, createdAt])
1626
+ @@index([userScoreId])
1627
+ @@index([eventType])
1628
+ @@index([createdAt])
1629
+ }
1630
+
1631
+ model EmailSuppression {
1632
+ id String @id @default(cuid())
1633
+ email String
1634
+ reason SuppressionReason
1635
+ source String?
1636
+
1637
+ suppressedAt DateTime @default(now())
1638
+
1639
+ @@unique([email, reason])
1640
+ @@index([email])
1641
+ }
1642
+
1643
+ model Referral {
1644
+ id String @id @default(cuid())
1645
+ referrerId String?
1646
+ referredUserId String?
1647
+ referredEmail String?
1648
+ status ReferralStatus @default(PENDING)
1649
+ referralCode String @unique
1650
+
1651
+ rewardType String?
1652
+ rewardedAt DateTime?
1653
+ clickCount Int @default(0)
1654
+ registeredAt DateTime?
1655
+ convertedAt DateTime?
1656
+
1657
+ createdAt DateTime @default(now())
1658
+
1659
+ referrer User? @relation("ReferralsSent", fields: [referrerId], references: [id], onDelete: SetNull)
1660
+ referredUser User? @relation("ReferralsReceived", fields: [referredUserId], references: [id], onDelete: SetNull)
1661
+
1662
+ @@index([referrerId])
1663
+ @@index([referredUserId])
1664
+ @@index([referralCode])
1665
+ @@index([status])
1666
+ }
@@ -0,0 +1,372 @@
1
+ /*
2
+ Warnings:
3
+
4
+ - A unique constraint covering the columns `[referralCode]` on the table `User` will be added. If there are existing duplicate values, this will fail.
5
+
6
+ */
7
+ -- CreateEnum
8
+ CREATE TYPE "LifecycleStage" AS ENUM ('REGISTERED', 'ACTIVATED', 'PAYING', 'SUSPENDED', 'CHURNED', 'REACTIVATED');
9
+
10
+ -- CreateEnum
11
+ CREATE TYPE "EmailStatus" AS ENUM ('QUEUED', 'SENT', 'DELIVERED', 'BOUNCED', 'FAILED', 'COMPLAINED');
12
+
13
+ -- CreateEnum
14
+ CREATE TYPE "EnrollmentStatus" AS ENUM ('ACTIVE', 'COMPLETED', 'EXITED', 'PAUSED');
15
+
16
+ -- CreateEnum
17
+ CREATE TYPE "LeadSegment" AS ENUM ('COLD', 'WARM', 'HOT', 'CONVERTING');
18
+
19
+ -- CreateEnum
20
+ CREATE TYPE "ChurnRisk" AS ENUM ('NONE', 'LOW', 'MEDIUM', 'HIGH');
21
+
22
+ -- CreateEnum
23
+ CREATE TYPE "BounceType" AS ENUM ('HARD', 'SOFT');
24
+
25
+ -- CreateEnum
26
+ CREATE TYPE "ConsentAction" AS ENUM ('GRANTED', 'WITHDRAWN', 'MODIFIED');
27
+
28
+ -- CreateEnum
29
+ CREATE TYPE "SuppressionReason" AS ENUM ('HARD_BOUNCE', 'COMPLAINT', 'MANUAL', 'SUNSET');
30
+
31
+ -- CreateEnum
32
+ CREATE TYPE "ReferralStatus" AS ENUM ('PENDING', 'REGISTERED', 'CONVERTED', 'REWARDED');
33
+
34
+ -- AlterTable
35
+ ALTER TABLE "User" ADD COLUMN "activatedAt" TIMESTAMP(3),
36
+ ADD COLUMN "churnRiskScore" INTEGER NOT NULL DEFAULT 0,
37
+ ADD COLUMN "churnRiskUpdatedAt" TIMESTAMP(3),
38
+ ADD COLUMN "isEmailSuppressed" BOOLEAN NOT NULL DEFAULT false,
39
+ ADD COLUMN "lastActiveAt" TIMESTAMP(3),
40
+ ADD COLUMN "lastEmailSentAt" TIMESTAMP(3),
41
+ ADD COLUMN "lastLoginAt" TIMESTAMP(3),
42
+ ADD COLUMN "lastMarketingEmailSentAt" TIMESTAMP(3),
43
+ ADD COLUMN "lifecycleStage" "LifecycleStage" NOT NULL DEFAULT 'REGISTERED',
44
+ ADD COLUMN "lifetimeValue" INTEGER NOT NULL DEFAULT 0,
45
+ ADD COLUMN "referralCode" TEXT;
46
+
47
+ -- CreateTable
48
+ CREATE TABLE "EmailSend" (
49
+ "id" TEXT NOT NULL,
50
+ "userId" TEXT,
51
+ "templateSlug" TEXT NOT NULL,
52
+ "campaignId" TEXT,
53
+ "status" "EmailStatus" NOT NULL DEFAULT 'QUEUED',
54
+ "sentAt" TIMESTAMP(3),
55
+ "deliveredAt" TIMESTAMP(3),
56
+ "toEmail" TEXT NOT NULL,
57
+ "fromEmail" TEXT NOT NULL DEFAULT 'info@mail.smartlex.sk',
58
+ "openedAt" TIMESTAMP(3),
59
+ "openCount" INTEGER NOT NULL DEFAULT 0,
60
+ "clickedAt" TIMESTAMP(3),
61
+ "clickCount" INTEGER NOT NULL DEFAULT 0,
62
+ "bouncedAt" TIMESTAMP(3),
63
+ "bounceType" "BounceType",
64
+ "complainedAt" TIMESTAMP(3),
65
+ "unsubscribedAt" TIMESTAMP(3),
66
+ "errorMessage" TEXT,
67
+ "errorCode" TEXT,
68
+ "subjectLine" TEXT,
69
+ "espMessageId" TEXT,
70
+ "idempotencyKey" TEXT,
71
+ "isTransactional" BOOLEAN NOT NULL DEFAULT false,
72
+ "metadata" JSONB,
73
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
74
+
75
+ CONSTRAINT "EmailSend_pkey" PRIMARY KEY ("id")
76
+ );
77
+
78
+ -- CreateTable
79
+ CREATE TABLE "EmailSequence" (
80
+ "id" TEXT NOT NULL,
81
+ "slug" TEXT NOT NULL,
82
+ "name" TEXT NOT NULL,
83
+ "description" TEXT,
84
+ "triggerEvent" TEXT NOT NULL,
85
+ "isActive" BOOLEAN NOT NULL DEFAULT true,
86
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
87
+ "updatedAt" TIMESTAMP(3) NOT NULL,
88
+
89
+ CONSTRAINT "EmailSequence_pkey" PRIMARY KEY ("id")
90
+ );
91
+
92
+ -- CreateTable
93
+ CREATE TABLE "EmailSequenceStep" (
94
+ "id" TEXT NOT NULL,
95
+ "sequenceId" TEXT NOT NULL,
96
+ "templateSlug" TEXT NOT NULL,
97
+ "stepOrder" INTEGER NOT NULL,
98
+ "delayMinutes" INTEGER NOT NULL DEFAULT 0,
99
+ "sendCondition" JSONB,
100
+ "skipCondition" JSONB,
101
+ "isActive" BOOLEAN NOT NULL DEFAULT true,
102
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
103
+
104
+ CONSTRAINT "EmailSequenceStep_pkey" PRIMARY KEY ("id")
105
+ );
106
+
107
+ -- CreateTable
108
+ CREATE TABLE "EmailSequenceEnrollment" (
109
+ "id" TEXT NOT NULL,
110
+ "userId" TEXT NOT NULL,
111
+ "sequenceId" TEXT NOT NULL,
112
+ "currentStep" INTEGER NOT NULL DEFAULT 0,
113
+ "status" "EnrollmentStatus" NOT NULL DEFAULT 'ACTIVE',
114
+ "enrolledAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
115
+ "completedAt" TIMESTAMP(3),
116
+ "exitedAt" TIMESTAMP(3),
117
+ "exitReason" TEXT,
118
+ "nextSendAt" TIMESTAMP(3),
119
+ "processingLockedAt" TIMESTAMP(3),
120
+
121
+ CONSTRAINT "EmailSequenceEnrollment_pkey" PRIMARY KEY ("id")
122
+ );
123
+
124
+ -- CreateTable
125
+ CREATE TABLE "EmailConsent" (
126
+ "id" TEXT NOT NULL,
127
+ "userId" TEXT NOT NULL,
128
+ "marketingConsent" BOOLEAN NOT NULL DEFAULT false,
129
+ "productUpdatesConsent" BOOLEAN NOT NULL DEFAULT false,
130
+ "legalNewsConsent" BOOLEAN NOT NULL DEFAULT false,
131
+ "doubleOptinSentAt" TIMESTAMP(3),
132
+ "doubleOptinConfirmedAt" TIMESTAMP(3),
133
+ "doubleOptinToken" TEXT,
134
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
135
+ "updatedAt" TIMESTAMP(3) NOT NULL,
136
+
137
+ CONSTRAINT "EmailConsent_pkey" PRIMARY KEY ("id")
138
+ );
139
+
140
+ -- CreateTable
141
+ CREATE TABLE "EmailConsentHistory" (
142
+ "id" TEXT NOT NULL,
143
+ "consentId" TEXT NOT NULL,
144
+ "userId" TEXT NOT NULL,
145
+ "action" "ConsentAction" NOT NULL,
146
+ "channel" TEXT NOT NULL,
147
+ "oldValue" BOOLEAN,
148
+ "newValue" BOOLEAN NOT NULL,
149
+ "consentText" TEXT NOT NULL,
150
+ "consentVersion" TEXT NOT NULL,
151
+ "consentMethod" TEXT NOT NULL,
152
+ "ipAddress" TEXT,
153
+ "userAgent" TEXT,
154
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
155
+
156
+ CONSTRAINT "EmailConsentHistory_pkey" PRIMARY KEY ("id")
157
+ );
158
+
159
+ -- CreateTable
160
+ CREATE TABLE "UserScore" (
161
+ "id" TEXT NOT NULL,
162
+ "userId" TEXT NOT NULL,
163
+ "score" INTEGER NOT NULL DEFAULT 0,
164
+ "leadSegment" "LeadSegment" NOT NULL DEFAULT 'COLD',
165
+ "churnRisk" "ChurnRisk" NOT NULL DEFAULT 'NONE',
166
+ "signupScore" INTEGER NOT NULL DEFAULT 0,
167
+ "engagementScore" INTEGER NOT NULL DEFAULT 0,
168
+ "usageScore" INTEGER NOT NULL DEFAULT 0,
169
+ "intentScore" INTEGER NOT NULL DEFAULT 0,
170
+ "decayScore" INTEGER NOT NULL DEFAULT 0,
171
+ "lastActivityAt" TIMESTAMP(3),
172
+ "scoreUpdatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
173
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
174
+
175
+ CONSTRAINT "UserScore_pkey" PRIMARY KEY ("id")
176
+ );
177
+
178
+ -- CreateTable
179
+ CREATE TABLE "UserScoreEvent" (
180
+ "id" TEXT NOT NULL,
181
+ "userScoreId" TEXT NOT NULL,
182
+ "userId" TEXT NOT NULL,
183
+ "eventType" TEXT NOT NULL,
184
+ "pointsDelta" INTEGER NOT NULL,
185
+ "scoreBefore" INTEGER NOT NULL,
186
+ "scoreAfter" INTEGER NOT NULL,
187
+ "metadata" JSONB,
188
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
189
+
190
+ CONSTRAINT "UserScoreEvent_pkey" PRIMARY KEY ("id")
191
+ );
192
+
193
+ -- CreateTable
194
+ CREATE TABLE "EmailSuppression" (
195
+ "id" TEXT NOT NULL,
196
+ "email" TEXT NOT NULL,
197
+ "reason" "SuppressionReason" NOT NULL,
198
+ "source" TEXT,
199
+ "suppressedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
200
+
201
+ CONSTRAINT "EmailSuppression_pkey" PRIMARY KEY ("id")
202
+ );
203
+
204
+ -- CreateTable
205
+ CREATE TABLE "Referral" (
206
+ "id" TEXT NOT NULL,
207
+ "referrerId" TEXT,
208
+ "referredUserId" TEXT,
209
+ "referredEmail" TEXT,
210
+ "status" "ReferralStatus" NOT NULL DEFAULT 'PENDING',
211
+ "referralCode" TEXT NOT NULL,
212
+ "rewardType" TEXT,
213
+ "rewardedAt" TIMESTAMP(3),
214
+ "clickCount" INTEGER NOT NULL DEFAULT 0,
215
+ "registeredAt" TIMESTAMP(3),
216
+ "convertedAt" TIMESTAMP(3),
217
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
218
+
219
+ CONSTRAINT "Referral_pkey" PRIMARY KEY ("id")
220
+ );
221
+
222
+ -- CreateIndex
223
+ CREATE UNIQUE INDEX "EmailSend_espMessageId_key" ON "EmailSend"("espMessageId");
224
+
225
+ -- CreateIndex
226
+ CREATE UNIQUE INDEX "EmailSend_idempotencyKey_key" ON "EmailSend"("idempotencyKey");
227
+
228
+ -- CreateIndex
229
+ CREATE INDEX "EmailSend_userId_idx" ON "EmailSend"("userId");
230
+
231
+ -- CreateIndex
232
+ CREATE INDEX "EmailSend_templateSlug_idx" ON "EmailSend"("templateSlug");
233
+
234
+ -- CreateIndex
235
+ CREATE INDEX "EmailSend_status_idx" ON "EmailSend"("status");
236
+
237
+ -- CreateIndex
238
+ CREATE INDEX "EmailSend_sentAt_idx" ON "EmailSend"("sentAt");
239
+
240
+ -- CreateIndex
241
+ CREATE INDEX "EmailSend_campaignId_idx" ON "EmailSend"("campaignId");
242
+
243
+ -- CreateIndex
244
+ CREATE INDEX "EmailSend_toEmail_idx" ON "EmailSend"("toEmail");
245
+
246
+ -- CreateIndex
247
+ CREATE INDEX "EmailSend_userId_sentAt_idx" ON "EmailSend"("userId", "sentAt");
248
+
249
+ -- CreateIndex
250
+ CREATE INDEX "EmailSend_status_sentAt_idx" ON "EmailSend"("status", "sentAt");
251
+
252
+ -- CreateIndex
253
+ CREATE INDEX "EmailSend_userId_isTransactional_sentAt_idx" ON "EmailSend"("userId", "isTransactional", "sentAt");
254
+
255
+ -- CreateIndex
256
+ CREATE UNIQUE INDEX "EmailSequence_slug_key" ON "EmailSequence"("slug");
257
+
258
+ -- CreateIndex
259
+ CREATE UNIQUE INDEX "EmailSequenceStep_sequenceId_stepOrder_key" ON "EmailSequenceStep"("sequenceId", "stepOrder");
260
+
261
+ -- CreateIndex
262
+ CREATE INDEX "EmailSequenceEnrollment_status_nextSendAt_idx" ON "EmailSequenceEnrollment"("status", "nextSendAt");
263
+
264
+ -- CreateIndex
265
+ CREATE INDEX "EmailSequenceEnrollment_userId_sequenceId_status_idx" ON "EmailSequenceEnrollment"("userId", "sequenceId", "status");
266
+
267
+ -- CreateIndex
268
+ CREATE UNIQUE INDEX "EmailConsent_userId_key" ON "EmailConsent"("userId");
269
+
270
+ -- CreateIndex
271
+ CREATE UNIQUE INDEX "EmailConsent_doubleOptinToken_key" ON "EmailConsent"("doubleOptinToken");
272
+
273
+ -- CreateIndex
274
+ CREATE INDEX "EmailConsentHistory_userId_createdAt_idx" ON "EmailConsentHistory"("userId", "createdAt");
275
+
276
+ -- CreateIndex
277
+ CREATE INDEX "EmailConsentHistory_consentId_idx" ON "EmailConsentHistory"("consentId");
278
+
279
+ -- CreateIndex
280
+ CREATE INDEX "EmailConsentHistory_createdAt_idx" ON "EmailConsentHistory"("createdAt");
281
+
282
+ -- CreateIndex
283
+ CREATE UNIQUE INDEX "UserScore_userId_key" ON "UserScore"("userId");
284
+
285
+ -- CreateIndex
286
+ CREATE INDEX "UserScore_score_idx" ON "UserScore"("score");
287
+
288
+ -- CreateIndex
289
+ CREATE INDEX "UserScore_leadSegment_idx" ON "UserScore"("leadSegment");
290
+
291
+ -- CreateIndex
292
+ CREATE INDEX "UserScore_churnRisk_idx" ON "UserScore"("churnRisk");
293
+
294
+ -- CreateIndex
295
+ CREATE INDEX "UserScoreEvent_userId_createdAt_idx" ON "UserScoreEvent"("userId", "createdAt");
296
+
297
+ -- CreateIndex
298
+ CREATE INDEX "UserScoreEvent_userScoreId_idx" ON "UserScoreEvent"("userScoreId");
299
+
300
+ -- CreateIndex
301
+ CREATE INDEX "UserScoreEvent_eventType_idx" ON "UserScoreEvent"("eventType");
302
+
303
+ -- CreateIndex
304
+ CREATE INDEX "UserScoreEvent_createdAt_idx" ON "UserScoreEvent"("createdAt");
305
+
306
+ -- CreateIndex
307
+ CREATE INDEX "EmailSuppression_email_idx" ON "EmailSuppression"("email");
308
+
309
+ -- CreateIndex
310
+ CREATE UNIQUE INDEX "EmailSuppression_email_reason_key" ON "EmailSuppression"("email", "reason");
311
+
312
+ -- CreateIndex
313
+ CREATE UNIQUE INDEX "Referral_referralCode_key" ON "Referral"("referralCode");
314
+
315
+ -- CreateIndex
316
+ CREATE INDEX "Referral_referrerId_idx" ON "Referral"("referrerId");
317
+
318
+ -- CreateIndex
319
+ CREATE INDEX "Referral_referredUserId_idx" ON "Referral"("referredUserId");
320
+
321
+ -- CreateIndex
322
+ CREATE INDEX "Referral_referralCode_idx" ON "Referral"("referralCode");
323
+
324
+ -- CreateIndex
325
+ CREATE INDEX "Referral_status_idx" ON "Referral"("status");
326
+
327
+ -- CreateIndex
328
+ CREATE UNIQUE INDEX "User_referralCode_key" ON "User"("referralCode");
329
+
330
+ -- CreateIndex
331
+ CREATE INDEX "User_lifecycleStage_idx" ON "User"("lifecycleStage");
332
+
333
+ -- CreateIndex
334
+ CREATE INDEX "User_lastActiveAt_idx" ON "User"("lastActiveAt");
335
+
336
+ -- CreateIndex
337
+ CREATE INDEX "User_isEmailSuppressed_idx" ON "User"("isEmailSuppressed");
338
+
339
+ -- AddForeignKey
340
+ ALTER TABLE "EmailSend" ADD CONSTRAINT "EmailSend_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
341
+
342
+ -- AddForeignKey
343
+ ALTER TABLE "EmailSequenceStep" ADD CONSTRAINT "EmailSequenceStep_sequenceId_fkey" FOREIGN KEY ("sequenceId") REFERENCES "EmailSequence"("id") ON DELETE CASCADE ON UPDATE CASCADE;
344
+
345
+ -- AddForeignKey
346
+ ALTER TABLE "EmailSequenceEnrollment" ADD CONSTRAINT "EmailSequenceEnrollment_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
347
+
348
+ -- AddForeignKey
349
+ ALTER TABLE "EmailSequenceEnrollment" ADD CONSTRAINT "EmailSequenceEnrollment_sequenceId_fkey" FOREIGN KEY ("sequenceId") REFERENCES "EmailSequence"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
350
+
351
+ -- AddForeignKey
352
+ ALTER TABLE "EmailConsent" ADD CONSTRAINT "EmailConsent_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
353
+
354
+ -- AddForeignKey
355
+ ALTER TABLE "EmailConsentHistory" ADD CONSTRAINT "EmailConsentHistory_consentId_fkey" FOREIGN KEY ("consentId") REFERENCES "EmailConsent"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
356
+
357
+ -- AddForeignKey
358
+ ALTER TABLE "UserScore" ADD CONSTRAINT "UserScore_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
359
+
360
+ -- AddForeignKey
361
+ ALTER TABLE "UserScoreEvent" ADD CONSTRAINT "UserScoreEvent_userScoreId_fkey" FOREIGN KEY ("userScoreId") REFERENCES "UserScore"("id") ON DELETE CASCADE ON UPDATE CASCADE;
362
+
363
+ -- AddForeignKey
364
+ ALTER TABLE "Referral" ADD CONSTRAINT "Referral_referrerId_fkey" FOREIGN KEY ("referrerId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
365
+
366
+ -- AddForeignKey
367
+ ALTER TABLE "Referral" ADD CONSTRAINT "Referral_referredUserId_fkey" FOREIGN KEY ("referredUserId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
368
+
369
+ -- Partial unique index: max 1 ACTIVE enrollment per user per sequence
370
+ CREATE UNIQUE INDEX IF NOT EXISTS "enrollment_active_unique"
371
+ ON "EmailSequenceEnrollment" ("userId", "sequenceId")
372
+ WHERE status = 'ACTIVE';
@@ -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;