@danielcok17/prisma-db 1.12.0 → 1.13.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.
Files changed (30) hide show
  1. package/README.md +129 -129
  2. package/package.json +47 -47
  3. package/prisma/app.prisma +1186 -1185
  4. package/prisma/law.prisma +82 -82
  5. package/prisma/migrations/20250818134929_init/migration.sql +374 -374
  6. package/prisma/migrations/20250903104817_add_workflow_log/migration.sql +103 -103
  7. package/prisma/migrations/20251009213916_add_email_authentication/migration.sql +54 -54
  8. package/prisma/migrations/20251031112719_change_default_message_count_to_10/migration.sql +2 -2
  9. package/prisma/migrations/20251111182842_add_stripe_integration_and_organizations/migration.sql +246 -246
  10. package/prisma/migrations/20260102162551_add_billing_interval/migration.sql +8 -8
  11. package/prisma/migrations/20260102220947_add_lawyer_pro_tier/migration.sql +2 -2
  12. package/prisma/migrations/20260110200252_add_canvas_documents/migration.sql +80 -80
  13. package/prisma/migrations/20260113150746_add_admin_grant_expiration/migration.sql +5 -5
  14. package/prisma/migrations/20260113205726_add_organization_invite/migration.sql +40 -40
  15. package/prisma/migrations/20260120172343_adjust_message_file/migration.sql +2 -0
  16. package/prisma/migrations/20260120212010_add_user_company_fields/migration.sql +22 -22
  17. package/prisma/migrations/20260129094625_add_utm_tracking_attribution/migration.sql +198 -198
  18. package/dist/index.d.ts +0 -5
  19. package/dist/index.d.ts.map +0 -1
  20. package/dist/index.js +0 -24
  21. package/dist/index.js.map +0 -1
  22. package/dist/paths.d.ts +0 -4
  23. package/dist/paths.d.ts.map +0 -1
  24. package/dist/paths.js +0 -12
  25. package/dist/paths.js.map +0 -1
  26. package/dist/utils.d.ts +0 -5
  27. package/dist/utils.d.ts.map +0 -1
  28. package/dist/utils.js +0 -13
  29. package/dist/utils.js.map +0 -1
  30. package/prisma/migrations/20260215120000_add_multi_brand_support/migration.sql +0 -23
package/prisma/app.prisma CHANGED
@@ -1,1185 +1,1186 @@
1
- generator client {
2
- provider = "prisma-client-js"
3
- output = "./generated/app"
4
- binaryTargets = ["native", "linux-musl-openssl-3.0.x"]
5
- }
6
-
7
- datasource db {
8
- provider = "postgresql"
9
- url = env("POSTGRES_PRISMA_URL")
10
- directUrl = env("POSTGRES_URL_NON_POOLING")
11
- }
12
-
13
- model Account {
14
- id String @id @default(cuid())
15
- userId String
16
- type String
17
- provider String
18
- providerAccountId String
19
- refresh_token String?
20
- access_token String?
21
- expires_at Int?
22
- token_type String?
23
- scope String?
24
- id_token String?
25
- session_state String?
26
- brand String @default("smartlex") // Multi-brand: "smartlex" | "justi"
27
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
28
-
29
- @@unique([provider, providerAccountId, brand])
30
- }
31
-
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?
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?
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
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
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(10) // OPRAVENÉ z 100 na 10
60
- messageCountResetAt DateTime? // Kedy sa resetovať message count
61
- adminGrantExpiresAt DateTime? // Kedy admin grant expiruje
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? // 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
- // UTM TRACKING & ATTRIBUTION FIELDS
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
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
86
- // Relations
87
- approvalRequest UserApprovalRequest?
88
- stripeCustomer StripeCustomer? // ✨ B2C Stripe customer
89
- ownedOrganizations Organization[] @relation("OrganizationOwner")
90
- organizationMembers OrganizationMember[]
91
- createdInvites OrganizationInvite[] @relation("InviteCreator")
92
- accounts Account[]
93
- answers Answer[]
94
- conversations Conversation[]
95
- feedbacks Feedback[]
96
- pageViews PageView[]
97
- sessions Session[]
98
- workflowLogs WorkflowLog[]
99
- verificationTokens VerificationToken[]
100
- passwordResetTokens PasswordResetToken[]
101
- canvasDocuments CanvasDocument[]
102
- canvasDocumentVersions CanvasDocumentVersion[]
103
- // Folder system relations
104
- ownedFolders Folder[] @relation("FolderOwner")
105
- addedFolderItems FolderItem[] @relation("FolderItemAdder")
106
- folderActivities FolderActivity[] @relation("FolderActivityUser")
107
- folderSharesReceived FolderShare[] @relation("FolderShareUser")
108
- createdFolderShares FolderShare[] @relation("FolderShareCreator")
109
- // UTM tracking relations
110
- touchPoints TouchPoint[]
111
- conversions Conversion[]
112
-
113
- // Multi-brand: compound unique allows same email across brands
114
- @@unique([email, brand])
115
- @@index([brand])
116
- @@index([customerType])
117
- @@index([companyNumber])
118
- @@index([email, customerType])
119
- // UTM tracking indexes
120
- @@index([firstUtmSource])
121
- @@index([firstUtmCampaign])
122
- @@index([registrationUtmSource])
123
- @@index([registrationUtmCampaign])
124
- @@index([firstVisitAt])
125
- }
126
-
127
- // Nový model pre žiadosti o schválenie
128
- model UserApprovalRequest {
129
- id String @id @default(cuid())
130
- userId String @unique
131
- status ApprovalStatus @default(PENDING)
132
- submittedAt DateTime @default(now())
133
- reviewedAt DateTime?
134
- reviewedBy String? // ID admina, ktorý preskúmal žiadosť
135
- notes String? // Poznámky admina
136
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
137
-
138
- @@index([status])
139
- @@index([submittedAt])
140
- @@index([reviewedAt])
141
- }
142
-
143
- // ============================================
144
- // ORGANIZATION MODELS (B2B)
145
- // ============================================
146
-
147
- // Organization = Právna kancelária / Firma
148
- model Organization {
149
- id String @id @default(cuid())
150
- name String // "Advokátska kancelária XYZ s.r.o."
151
-
152
- // Billing & Legal Information
153
- companyNumber String? // IČO (Identifikačné číslo organizácie)
154
- vatNumber String? // IČ DPH (VAT Number)
155
- taxNumber String? // DIČ (Daňové identifikačné číslo)
156
- legalForm String? // "s.r.o.", "a.s.", "v.o.s.", "k.s.", "SZČO"
157
- billingEmail String
158
-
159
- // Address
160
- street String?
161
- city String?
162
- postalCode String?
163
- country String @default("SK")
164
-
165
- // Subscription (B2B)
166
- subscriptionTier SubscriptionTier @default(FREE) // Tier organizácie (cache)
167
- messageCount Int @default(0) // Spoločný pool správ pre všetkých členov
168
- messageLimit Int @default(100) // Limit správ podľa tieru
169
- messageCountResetAt DateTime? // Kedy sa resetovať pool
170
- adminGrantExpiresAt DateTime? // Kedy admin grant expiruje
171
-
172
- // Settings
173
- isActive Boolean @default(true)
174
- maxMembers Int @default(5) // Max počet členov podľa tieru
175
-
176
- // Relations
177
- owner User @relation("OrganizationOwner", fields: [ownerId], references: [id])
178
- ownerId String
179
- members OrganizationMember[]
180
- invites OrganizationInvite[]
181
- stripeCustomer StripeCustomer?
182
- folderShares FolderShare[]
183
-
184
- createdAt DateTime @default(now())
185
- updatedAt DateTime @updatedAt
186
-
187
- @@index([ownerId])
188
- @@index([vatNumber])
189
- @@index([companyNumber])
190
- @@index([subscriptionTier])
191
- @@index([billingEmail])
192
- }
193
-
194
- // Many-to-Many: User ↔ Organization
195
- model OrganizationMember {
196
- id String @id @default(cuid())
197
- organizationId String
198
- userId String
199
- role MemberRole @default(MEMBER)
200
- joinedAt DateTime @default(now())
201
-
202
- organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
203
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
204
-
205
- @@unique([organizationId, userId])
206
- @@index([organizationId])
207
- @@index([userId])
208
- @@index([role])
209
- }
210
-
211
- // Organization Invite System
212
- model OrganizationInvite {
213
- id String @id @default(cuid())
214
- organizationId String
215
- token String @unique
216
- email String? // Optional: specific email invite
217
- role MemberRole @default(MEMBER)
218
- createdBy String
219
- expiresAt DateTime
220
- maxUses Int @default(1) // For multi-use invites
221
- currentUses Int @default(0)
222
- isActive Boolean @default(true)
223
- usedAt DateTime? // Last usage timestamp
224
- usedBy String? // Last user who used it
225
- createdAt DateTime @default(now())
226
- updatedAt DateTime @updatedAt
227
-
228
- organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
229
- creator User @relation("InviteCreator", fields: [createdBy], references: [id])
230
-
231
- @@index([token])
232
- @@index([organizationId])
233
- @@index([isActive])
234
- @@index([expiresAt])
235
- }
236
-
237
- enum MemberRole {
238
- OWNER // Zakladateľ/vlastník, plný prístup k billing a settings
239
- ADMIN // Administrátor, môže pridávať/odoberať členov
240
- MEMBER // Bežný člen, len používa systém
241
- }
242
-
243
- // ZJEDNODUŠENÝ Conversation - len metadata, bez duplikátov
244
- model Conversation {
245
- id String @id @default(cuid())
246
- name String
247
- userId String
248
- isShareable Boolean @default(false)
249
- shareUrl String? @unique
250
- sharedAt DateTime?
251
- createdAt DateTime @default(now())
252
- updatedAt DateTime @updatedAt
253
- summary String?
254
- messagesSinceLastSummary Int? @default(0)
255
- // Relácie
256
- answers Answer[]
257
- user User @relation(fields: [userId], references: [id])
258
- workflowLogs WorkflowLog[]
259
- canvasDocuments CanvasDocument[]
260
-
261
- @@index([userId])
262
- @@index([createdAt])
263
- }
264
-
265
- // Hlavný model - všetky správy, bez duplikátov
266
- model Answer {
267
- id String @id @default(cuid())
268
- conversationId String
269
- messageId String @unique
270
- role Role
271
- content String
272
- question String?
273
- answer String?
274
- evaluation String?
275
- isWelcome Boolean @default(false)
276
- processingTime Int?
277
- model String?
278
- userId String?
279
- createdAt DateTime @default(now())
280
- updatedAt DateTime @updatedAt
281
- // Relácie
282
- conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
283
- user User? @relation(fields: [userId], references: [id])
284
- feedback Feedback?
285
- references Reference[]
286
- metrics AnswerMetrics?
287
- WorkflowLog WorkflowLog[]
288
- files MessageFile[]
289
- canvasDocumentId String? // No FK constraint - flexible document management
290
-
291
- @@index([conversationId])
292
- @@index([messageId])
293
- @@index([role])
294
- @@index([userId])
295
- @@index([createdAt])
296
- }
297
-
298
- model MessageFile {
299
- id String @id @default(cuid())
300
- answerId String
301
- fileName String
302
- fileType String
303
- base64Data String
304
- uploadedAt DateTime @default(now())
305
- answer Answer @relation(fields: [answerId], references: [id], onDelete: Cascade)
306
-
307
- @@index([answerId])
308
- @@index([fileType])
309
- @@index([uploadedAt])
310
- }
311
-
312
- // Nová tabuľka pre metriky odpovedí
313
- model AnswerMetrics {
314
- id String @id @default(cuid())
315
- answerId String @unique
316
- // Náklady
317
- apiCost Float? // Celkové náklady (ai_cost + rag_cost)
318
- aiCost Float? // Náklady len na AI provider volania
319
- ragCost Float? // Náklady na RAG operácie
320
- // Volania
321
- apiCalls Int? // Celkový počet volaní
322
- aiCalls Int? // Počet AI provider volaní
323
- ragCalls Int? // Počet RAG operácií
324
- // Čas a výkon
325
- apiDuration Int? // Doba spracovania v ms
326
- // AI Provider a Model
327
- aiProvider String? // Hlavný AI provider
328
- aiModel String? // Hlavný AI model
329
- aiProvidersUsed String[] // Všetky použité AI providery
330
- aiModelsUsed String[] // Všetky použité AI modely
331
- // Timestamps
332
- createdAt DateTime @default(now())
333
- updatedAt DateTime @updatedAt
334
- // Relations
335
- answer Answer @relation(fields: [answerId], references: [id], onDelete: Cascade)
336
-
337
- @@index([answerId])
338
- @@index([apiCost])
339
- @@index([apiDuration])
340
- @@index([aiProvider])
341
- @@index([createdAt])
342
- }
343
-
344
- model Feedback {
345
- id String @id @default(cuid())
346
- answerId String @unique
347
- rating FeedbackRating
348
- feedbackTypes String[]
349
- customFeedback String?
350
- userId String
351
- messageContent String?
352
- createdAt DateTime @default(now())
353
- answer Answer @relation(fields: [answerId], references: [id], onDelete: Cascade)
354
- user User @relation(fields: [userId], references: [id])
355
-
356
- @@index([rating])
357
- @@index([userId])
358
- @@index([answerId])
359
- }
360
-
361
- // ZJEDNODUŠENÝ Reference - len na Answer, bez duplikátov
362
- model Reference {
363
- id String @id @default(cuid())
364
- answerId String // Len answerId - konzistentné
365
- type ReferenceType
366
- title String
367
- reference String
368
- summary String?
369
- url String?
370
- uuid String?
371
- relevance Float?
372
- citationNumber Int?
373
- metadata Json?
374
- userId String?
375
- createdAt DateTime @default(now())
376
- // Relácie
377
- answer Answer @relation(fields: [answerId], references: [id], onDelete: Cascade)
378
-
379
- @@index([answerId])
380
- @@index([type])
381
- @@index([reference])
382
- @@index([userId])
383
- @@index([createdAt])
384
- }
385
-
386
- model PageView {
387
- id String @id @default(cuid())
388
- userId String?
389
- sessionId String?
390
- page String
391
- path String
392
- referrer String?
393
- userAgent String?
394
- ipAddress String?
395
- country String?
396
- city String?
397
- deviceType String?
398
- browser String?
399
- os String?
400
- screenSize String?
401
- language String?
402
- timeOnPage Int?
403
- isBounce Boolean @default(true)
404
- createdAt DateTime @default(now())
405
- session Session? @relation(fields: [sessionId], references: [sessionId], onDelete: SetNull)
406
- user User? @relation(fields: [userId], references: [id])
407
-
408
- @@index([userId])
409
- @@index([sessionId])
410
- @@index([page])
411
- @@index([createdAt])
412
- @@index([country])
413
- @@index([deviceType])
414
- }
415
-
416
- model Session {
417
- id String @id @default(cuid())
418
- sessionId String @unique
419
- userId String?
420
- startedAt DateTime @default(now())
421
- endedAt DateTime?
422
- duration Int?
423
-
424
- // ============================================
425
- // FIRST-TOUCH ATTRIBUTION
426
- // ============================================
427
- // Prvý UTM touchpoint v tejto session
428
- // Automaticky naplnené pri prvom TouchPoint s UTM
429
-
430
- firstUtmSource String?
431
- firstUtmMedium String?
432
- firstUtmCampaign String?
433
- firstUtmContent String?
434
- firstUtmTerm String?
435
- firstTouchAt DateTime? // Timestamp prvého UTM touchpoint
436
-
437
- // ============================================
438
- // LAST-TOUCH ATTRIBUTION
439
- // ============================================
440
- // Posledný UTM touchpoint v tejto session
441
- // Aktualizované pri každom novom TouchPoint s UTM
442
-
443
- lastUtmSource String?
444
- lastUtmMedium String?
445
- lastUtmCampaign String?
446
- lastUtmContent String?
447
- lastUtmTerm String?
448
- lastTouchAt DateTime? // Timestamp posledného UTM touchpoint
449
-
450
- // ============================================
451
- // RELATIONS
452
- // ============================================
453
-
454
- touchPoints TouchPoint[] // Všetky touchpointy v session
455
- conversions Conversion[] // Všetky konverzie v session
456
-
457
- // Existujúce relations
458
- pageViews PageView[]
459
- user User? @relation(fields: [userId], references: [id])
460
- workflowLogs WorkflowLog[]
461
-
462
- // ============================================
463
- // INDEXES
464
- // ============================================
465
-
466
- @@index([firstUtmCampaign])
467
- @@index([lastUtmCampaign])
468
- @@index([firstUtmSource, firstUtmMedium])
469
- @@index([lastUtmSource, lastUtmMedium])
470
- // Existujúce indexes
471
- @@index([userId])
472
- @@index([startedAt])
473
- }
474
-
475
- // ============================================
476
- // UTM TRACKING & ATTRIBUTION MODELS
477
- // ============================================
478
-
479
- // TouchPoint - každý click s UTM parametrami
480
- // Umožňuje multi-touch attribution a customer journey analysis
481
- model TouchPoint {
482
- id String @id @default(cuid())
483
- userId String? // Pre prihlásených používateľov
484
- sessionId String // Session tracking (required)
485
-
486
- // ============================================
487
- // UTM PARAMETERS (všetky lowercase!)
488
- // ============================================
489
- // Best practice: Vždy lowercase, validované pred uložením
490
-
491
- utmSource String? // Odkiaľ: "email", "facebook", "google", "linkedin"
492
- utmMedium String? // Typ média: "email", "cpc", "social", "organic", "paid-social"
493
- utmCampaign String? // Názov kampane: "smizany-2026-01", "spring-sale-2026"
494
- utmContent String? // Variant/link identifier: "cta-primary", "banner-top", "hero-button"
495
- utmTerm String? // Keywords (pre paid search): "pravny-asistent", "advokat-ai"
496
-
497
- // ============================================
498
- // CONTEXT & METADATA
499
- // ============================================
500
-
501
- page String // Ktorá stránka: "/", "/pricing", "/contact"
502
- path String // Celý path s params: "/?utm_source=email&utm_campaign=..."
503
- referrer String? // HTTP Referrer (odkiaľ prišiel)
504
-
505
- // Device & Browser info (denormalizované pre rýchle queries)
506
- deviceType String? // "mobile", "tablet", "desktop"
507
- browser String? // "Chrome", "Firefox", "Safari"
508
- os String? // "Windows", "macOS", "iOS", "Android"
509
-
510
- // Geolocation (anonymizované)
511
- country String? // "SK", "CZ", "US"
512
- city String? // "Bratislava", "Prague"
513
-
514
- // Timestamp
515
- timestamp DateTime @default(now())
516
-
517
- // ============================================
518
- // RELATIONS
519
- // ============================================
520
-
521
- session Session @relation(fields: [sessionId], references: [sessionId], onDelete: Cascade)
522
- user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
523
-
524
- // ============================================
525
- // INDEXES (optimalizované pre analytics queries)
526
- // ============================================
527
-
528
- @@index([userId])
529
- @@index([sessionId])
530
- @@index([utmSource])
531
- @@index([utmMedium])
532
- @@index([utmCampaign])
533
- @@index([utmSource, utmMedium])
534
- @@index([utmCampaign, timestamp])
535
- @@index([timestamp])
536
- @@index([sessionId, timestamp])
537
- @@index([userId, timestamp])
538
- @@map("touch_points")
539
- }
540
-
541
- // Conversion - sledovanie konverzií s attribution
542
- // Podporuje first-touch, last-touch aj multi-touch attribution
543
- model Conversion {
544
- id String @id @default(cuid())
545
- userId String? // Kto konvertoval (nullable pre anonymous conversions)
546
- sessionId String // V ktorej session
547
-
548
- // ============================================
549
- // CONVERSION METADATA
550
- // ============================================
551
-
552
- type ConversionType // Typ konverzie
553
- value Decimal? // Revenue (pre e-commerce/subscriptions)
554
- currency String? @default("EUR")
555
-
556
- // Optional metadata
557
- metadata Json? // Stripe subscription ID, product info, etc.
558
-
559
- // ============================================
560
- // FIRST-TOUCH ATTRIBUTION (40% credit)
561
- // ============================================
562
- // Prvý UTM touchpoint v session = awareness channel
563
-
564
- firstUtmSource String? // Odkiaľ prišiel prvýkrát
565
- firstUtmMedium String? // Akým médiom prvýkrát
566
- firstUtmCampaign String? // Ktorá kampaň priniesla awareness
567
- firstUtmContent String? // Ktorý konkrétny link/banner
568
- firstUtmTerm String? // Ktoré keywords (pre paid search)
569
- firstTouchAt DateTime? // Kedy bol prvý touchpoint
570
-
571
- // ============================================
572
- // LAST-TOUCH ATTRIBUTION (40% credit)
573
- // ============================================
574
- // Posledný UTM touchpoint pred konverziou = conversion channel
575
-
576
- lastUtmSource String? // Odkiaľ prišiel naposledy
577
- lastUtmMedium String? // Akým médiom naposledy
578
- lastUtmCampaign String? // Ktorá kampaň priniesla konverziu
579
- lastUtmContent String? // Ktorý konkrétny link/banner
580
- lastUtmTerm String? // Ktoré keywords (pre paid search)
581
- lastTouchAt DateTime? // Kedy bol posledný touchpoint
582
-
583
- // ============================================
584
- // MULTI-TOUCH ATTRIBUTION (20% credit)
585
- // ============================================
586
- // Všetky touchpointy medzi prvým a posledným
587
-
588
- touchPointIds String[] // Array of TouchPoint IDs (pre detailnú analýzu)
589
- touchPointCount Int? // Počet touchpoints v customer journey
590
-
591
- // ============================================
592
- // CALCULATED FIELDS
593
- // ============================================
594
-
595
- journeyDuration Int? // Čas od prvého touchpoint po konverziu (sekundy)
596
-
597
- // Timestamp
598
- timestamp DateTime @default(now())
599
-
600
- // ============================================
601
- // RELATIONS
602
- // ============================================
603
-
604
- session Session @relation(fields: [sessionId], references: [sessionId], onDelete: Cascade)
605
- user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
606
-
607
- // ============================================
608
- // INDEXES
609
- // ============================================
610
-
611
- @@index([userId])
612
- @@index([sessionId])
613
- @@index([type])
614
- @@index([timestamp])
615
- // First-touch attribution indexes
616
- @@index([firstUtmSource])
617
- @@index([firstUtmMedium])
618
- @@index([firstUtmCampaign])
619
- @@index([firstUtmCampaign, type])
620
- @@index([firstUtmSource, firstUtmMedium])
621
- // Last-touch attribution indexes
622
- @@index([lastUtmSource])
623
- @@index([lastUtmMedium])
624
- @@index([lastUtmCampaign])
625
- @@index([lastUtmCampaign, type])
626
- @@index([lastUtmSource, lastUtmMedium])
627
- // Analytics indexes
628
- @@index([type, timestamp])
629
- @@index([value])
630
- @@map("conversions")
631
- }
632
-
633
- // ============================================
634
- // STRIPE INTEGRATION MODELS
635
- // ============================================
636
-
637
- // Stripe Customer - polymorphic relation (User OR Organization)
638
- model StripeCustomer {
639
- id String @id @default(cuid())
640
-
641
- // ✅ Polymorphic relation - buď User (B2C) alebo Organization (B2B)
642
- userId String? @unique // B2C: Individuálny používateľ
643
- organizationId String? @unique // B2B: Organizácia
644
-
645
- stripeCustomerId String @unique
646
- email String
647
- name String?
648
-
649
- // Billing details (Stripe Address & Tax IDs)
650
- billingAddress Json? // Stripe Address object
651
- taxIds Json? // Stripe Tax IDs array (VAT, etc.)
652
-
653
- createdAt DateTime @default(now())
654
- updatedAt DateTime @updatedAt
655
-
656
- // Relations
657
- user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
658
- organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
659
- subscriptions StripeSubscription[]
660
- payments StripePayment[]
661
-
662
- @@index([stripeCustomerId])
663
- @@index([userId])
664
- @@index([organizationId])
665
- @@index([email])
666
- }
667
-
668
- // Stripe Subscription - sledovanie predplatného (User alebo Organization)
669
- model StripeSubscription {
670
- id String @id @default(cuid())
671
-
672
- // ✅ FIX: customerId = FK na StripeCustomer.id (nie stripeCustomerId!)
673
- customerId String // FK na StripeCustomer.id
674
- stripeCustomerId String // Stripe ID (pre logging/redundancia)
675
- stripeSubscriptionId String @unique
676
- stripePriceId String
677
- stripeProductId String
678
-
679
- status SubscriptionStatus
680
- tier SubscriptionTier @default(FREE)
681
- billingInterval BillingInterval @default(MONTHLY) // Monthly or Yearly billing
682
-
683
- currentPeriodStart DateTime
684
- currentPeriodEnd DateTime
685
- cancelAtPeriodEnd Boolean @default(false)
686
- canceledAt DateTime?
687
-
688
- trialStart DateTime?
689
- trialEnd DateTime?
690
-
691
- // Additional fields
692
- quantity Int @default(1) // Počet seats (pre LAW_FIRM/ENTERPRISE tier)
693
- defaultPaymentMethodId String? // Stripe payment method ID
694
-
695
- metadata Json? // Dodatočné Stripe metadata
696
- createdAt DateTime @default(now())
697
- updatedAt DateTime @updatedAt
698
-
699
- // ✅ FIX: Relácia na customerId (interné ID), nie stripeCustomerId (Stripe ID)
700
- customer StripeCustomer @relation(fields: [customerId], references: [id], onDelete: Cascade)
701
-
702
- @@index([stripeSubscriptionId])
703
- @@index([customerId])
704
- @@index([stripeCustomerId])
705
- @@index([status])
706
- @@index([tier])
707
- @@index([billingInterval])
708
- @@index([currentPeriodEnd])
709
- }
710
-
711
- // Stripe Event - prevencia duplicitného spracovania webhookov (idempotencia)
712
- model StripeEvent {
713
- id String @id @default(cuid())
714
- eventId String @unique // Stripe event ID
715
- type String // Typ eventu (napr. 'checkout.session.completed')
716
- data Json // Úplné event data pre debugging
717
- processed Boolean @default(false)
718
- processedAt DateTime?
719
- error String? // Uloženie chýb pri spracovaní
720
- createdAt DateTime @default(now())
721
-
722
- @@index([eventId])
723
- @@index([processed])
724
- @@index([type])
725
- @@index([createdAt])
726
- }
727
-
728
- // Stripe Payment - sledovanie jednotlivých platieb (voliteľné, pre detailnú analytiku)
729
- model StripePayment {
730
- id String @id @default(cuid())
731
-
732
- // ✅ FIX: Pridaná relácia na StripeCustomer
733
- customerId String
734
- stripePaymentId String @unique
735
- stripeCustomerId String // Redundantné, ale OK pre logging
736
-
737
- amount Int // Suma v centoch
738
- currency String @default("eur")
739
- status String // succeeded, failed, pending
740
- paymentMethod String? // card, sepa_debit, atď.
741
- description String?
742
-
743
- // Invoice info
744
- invoiceId String? // Stripe Invoice ID
745
- invoiceUrl String? // URL na faktúru
746
-
747
- metadata Json?
748
- createdAt DateTime @default(now())
749
-
750
- // ✅ Relácia na StripeCustomer
751
- customer StripeCustomer @relation(fields: [customerId], references: [id], onDelete: Cascade)
752
-
753
- @@index([stripePaymentId])
754
- @@index([customerId])
755
- @@index([stripeCustomerId])
756
- @@index([status])
757
- @@index([createdAt])
758
- }
759
-
760
- // ============================================
761
- // ENUMS
762
- // ============================================
763
-
764
- enum Role {
765
- USER
766
- ASSISTANT
767
- SYSTEM
768
- }
769
-
770
- enum FeedbackRating {
771
- LIKE
772
- DISLIKE
773
- NEUTRAL
774
- }
775
-
776
- enum ReferenceType {
777
- LAW
778
- CASE
779
- REGULATION
780
- DOCUMENT
781
- OTHER
782
- }
783
-
784
- enum ApprovalStatus {
785
- PENDING
786
- APPROVED
787
- REJECTED
788
- }
789
-
790
- // Stripe: Status predplatného
791
- enum SubscriptionStatus {
792
- ACTIVE // Predplatné je aktívne
793
- CANCELED // Predplatné zrušené
794
- INCOMPLETE // Iniciálna platba zlyhala
795
- INCOMPLETE_EXPIRED // Neúplné predplatné expirované
796
- PAST_DUE // Platba zlyhala, retry prebieha
797
- TRIALING // V skúšobnom období
798
- UNPAID // Platba zlyhala, žiadny retry
799
- PAUSED // Predplatné pozastavené (Stripe feature)
800
- }
801
-
802
- // Customer type: Individuálny vs SZČO
803
- enum CustomerType {
804
- INDIVIDUAL // Fyzická osoba (bez IČO)
805
- SELF_EMPLOYED // SZČO - Samostatne zárobkovo činná osoba (s IČO)
806
- }
807
-
808
- // Stripe: Tier predplatného
809
- enum SubscriptionTier {
810
- FREE // Free tier (10 messages/month)
811
- LAWYER // Lawyer tier (1000 messages/month, €29/month, 1 user)
812
- LAWYER_PRO // Lawyer Pro tier (3000 messages/month, €49/month, 1 user) - RECOMMENDED
813
- LAW_FIRM // Law Firm tier (10000 messages/month, €129/month, up to 5 users)
814
- ENTERPRISE // Enterprise tier (unlimited messages, unlimited users, custom pricing)
815
- }
816
-
817
- // Stripe: Billing interval
818
- enum BillingInterval {
819
- MONTHLY // Monthly billing
820
- YEARLY // Yearly billing (17% discount)
821
- }
822
-
823
- // Canvas Document Status
824
- enum DocumentStatus {
825
- DRAFT
826
- SAVED
827
- ARCHIVED
828
- }
829
-
830
- // Conversion types for UTM attribution tracking
831
- enum ConversionType {
832
- REGISTRATION // Nová registrácia
833
- SUBSCRIPTION // Začiatok predplatného (FREE → PAID)
834
- UPGRADE // Upgrade tieru (LAWYERLAWYER_PRO)
835
- RENEWAL // Obnovenie predplatného
836
- TRIAL_START // Začiatok trial periodu
837
- PURCHASE // Jednorazový nákup
838
- LEAD // Vyplnenie kontaktného formulára
839
- DEMO_REQUEST // Žiadosť o demo
840
- DOWNLOAD // Stiahnutie resourceu
841
- FORM_SUBMIT // Iné formuláre
842
- }
843
-
844
- // ============================================
845
- // FOLDER SYSTEM ENUMS
846
- // ============================================
847
-
848
- // Folder item types (what can be stored in folders)
849
- enum FolderItemType {
850
- CONVERSATION
851
- CANVAS_DOCUMENT
852
- REFERENCE
853
- ATTACHMENT
854
- }
855
-
856
- // Folder activity types (for timeline)
857
- enum FolderActivityType {
858
- FOLDER_CREATED
859
- FOLDER_UPDATED
860
- FOLDER_MOVED
861
- FOLDER_ARCHIVED
862
- ITEM_ADDED
863
- ITEM_REMOVED
864
- ITEM_UPDATED
865
- SHARE_ADDED
866
- SHARE_REMOVED
867
- USER_MESSAGE
868
- USER_NOTE
869
- }
870
-
871
- // Folder permission levels
872
- enum FolderPermission {
873
- VIEW
874
- EDIT
875
- ADMIN
876
- }
877
-
878
- // Nový model pre logovanie admin akcií
879
- model AdminActionLog {
880
- id String @id @default(cuid())
881
- action String // APPROVE_USER, REJECT_USER, etc.
882
- targetUserId String // ID používateľa, na ktorého sa akcia vzťahuje
883
- adminEmail String // E-mail admina, ktorý vykonal akciu
884
- details Json? // Ďalšie detaily akcie (reason, notes, etc.)
885
- createdAt DateTime @default(now())
886
-
887
- @@index([action])
888
- @@index([targetUserId])
889
- @@index([adminEmail])
890
- @@index([createdAt])
891
- }
892
-
893
- model WorkflowLog {
894
- id String @id @default(cuid()) // Added @default(cuid())
895
- conversationId String
896
- messageId String?
897
- userId String? // Made optional to preserve logs when users are deleted
898
- sessionId String? // Removed @unique to allow multiple workflows per session
899
- startedAt DateTime @default(now())
900
- completedAt DateTime?
901
- status WorkflowStatus @default(IN_PROGRESS)
902
- totalDuration Int? // milliseconds
903
- error String?
904
- metadata Json?
905
- steps WorkflowStep[] // Renamed from WorkflowStep to steps (lowerCamel, plural)
906
- conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
907
- user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
908
- session Session? @relation(fields: [sessionId], references: [sessionId], onDelete: SetNull)
909
- answer Answer? @relation(fields: [messageId], references: [messageId])
910
-
911
- @@index([conversationId])
912
- @@index([userId])
913
- @@index([sessionId])
914
- @@index([startedAt])
915
- @@index([status])
916
- @@index([conversationId, startedAt]) // Composite index for timeline queries
917
- @@index([messageId])
918
- }
919
-
920
- model WorkflowStep {
921
- id String @id @default(cuid()) // Added @default(cuid())
922
- workflowId String
923
- stepName String // 'classify_question', 'rag_search', 'ai_processing', 'generate_response'
924
- stepType StepType
925
- startedAt DateTime @default(now())
926
- completedAt DateTime?
927
- duration Int? // milliseconds
928
- status StepStatus @default(IN_PROGRESS)
929
- inputData Json? // Sanitized input data
930
- outputData Json? // Sanitized output data
931
- error String?
932
- metadata Json? // Provider used, model used, tokens, costs, etc.
933
- workflow WorkflowLog @relation(fields: [workflowId], references: [id], onDelete: Cascade)
934
-
935
- @@index([workflowId])
936
- @@index([stepName])
937
- @@index([stepType])
938
- @@index([startedAt])
939
- @@index([status])
940
- @@index([workflowId, startedAt]) // Composite index for timeline queries
941
- }
942
-
943
- enum WorkflowStatus {
944
- IN_PROGRESS
945
- COMPLETED
946
- FAILED
947
- CANCELLED
948
- }
949
-
950
- enum StepStatus {
951
- IN_PROGRESS
952
- COMPLETED
953
- FAILED
954
- SKIPPED
955
- }
956
-
957
- enum StepType {
958
- CLASSIFICATION
959
- RAG_RETRIEVAL
960
- AI_PROCESSING
961
- RESPONSE_GENERATION
962
- DATA_PERSISTENCE
963
- ERROR_HANDLING
964
- }
965
-
966
- // Email verification tokens
967
- model VerificationToken {
968
- id String @id @default(cuid())
969
- userId String
970
- token String @unique
971
- expires DateTime
972
- createdAt DateTime @default(now())
973
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
974
-
975
- @@index([token])
976
- @@index([userId])
977
- @@index([expires])
978
- }
979
-
980
- // Password reset tokens
981
- model PasswordResetToken {
982
- id String @id @default(cuid())
983
- userId String
984
- token String @unique
985
- expires DateTime
986
- createdAt DateTime @default(now())
987
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
988
-
989
- @@index([token])
990
- @@index([userId])
991
- @@index([expires])
992
- }
993
-
994
- // ============================================
995
- // CANVAS DOCUMENT MODELS
996
- // ============================================
997
-
998
- model CanvasDocument {
999
- id String @id @default(cuid())
1000
- userId String
1001
- title String
1002
- status DocumentStatus @default(DRAFT)
1003
- createdAt DateTime @default(now())
1004
- updatedAt DateTime @updatedAt
1005
- originConversationId String?
1006
- originAnswerId String? // No FK constraint - async save reference
1007
- currentVersionId String? @unique
1008
-
1009
- // Relations
1010
- user User @relation(fields: [userId], references: [id], onDelete: Restrict)
1011
- originConversation Conversation? @relation(fields: [originConversationId], references: [id], onDelete: SetNull)
1012
- currentVersion CanvasDocumentVersion? @relation("CurrentVersion", fields: [currentVersionId], references: [id], onDelete: SetNull)
1013
- versions CanvasDocumentVersion[] @relation("DocumentVersions")
1014
-
1015
- @@index([userId])
1016
- @@index([originConversationId])
1017
- @@index([status])
1018
- @@index([createdAt])
1019
- }
1020
-
1021
- model CanvasDocumentVersion {
1022
- id String @id @default(cuid())
1023
- documentId String
1024
- versionNumber Int
1025
- title String
1026
- markdownContent String
1027
- createdAt DateTime @default(now())
1028
- createdBy String
1029
- regenerationPrompt String?
1030
- parentVersionId String?
1031
-
1032
- // Relations
1033
- document CanvasDocument @relation("DocumentVersions", fields: [documentId], references: [id], onDelete: Cascade)
1034
- creator User @relation(fields: [createdBy], references: [id], onDelete: Restrict)
1035
- parentVersion CanvasDocumentVersion? @relation("VersionHistory", fields: [parentVersionId], references: [id], onDelete: SetNull)
1036
- childVersions CanvasDocumentVersion[] @relation("VersionHistory")
1037
- currentForDocument CanvasDocument? @relation("CurrentVersion")
1038
-
1039
- @@unique([documentId, versionNumber])
1040
- @@index([documentId])
1041
- @@index([createdBy])
1042
- @@index([createdAt])
1043
- }
1044
-
1045
- // ============================================
1046
- // FOLDER SYSTEM MODELS
1047
- // ============================================
1048
-
1049
- // Main folder entity with hierarchy support via parentId
1050
- // Uses materialized path pattern for efficient hierarchy queries
1051
- model Folder {
1052
- id String @id @default(cuid())
1053
- name String
1054
- description String?
1055
- color String? @default("#6366f1")
1056
- icon String? @default("folder")
1057
-
1058
- // Ownership (always user-owned)
1059
- ownerId String
1060
-
1061
- // Hierarchy
1062
- parentId String?
1063
- rootFolderId String? // NULL for root folders, points to top-level ancestor
1064
- path String @default("/") // Materialized path: "/parentId/grandparentId/..."
1065
- depth Int @default(0)
1066
-
1067
- // Metadata
1068
- sortOrder Int @default(0)
1069
- isArchived Boolean @default(false)
1070
- createdAt DateTime @default(now())
1071
- updatedAt DateTime @updatedAt
1072
-
1073
- // Relations
1074
- owner User @relation("FolderOwner", fields: [ownerId], references: [id], onDelete: Cascade)
1075
- parent Folder? @relation("FolderHierarchy", fields: [parentId], references: [id], onDelete: Cascade)
1076
- children Folder[] @relation("FolderHierarchy")
1077
- items FolderItem[]
1078
- activities FolderActivity[]
1079
- shares FolderShare[]
1080
-
1081
- @@index([ownerId])
1082
- @@index([parentId])
1083
- @@index([rootFolderId])
1084
- @@index([path])
1085
- @@index([isArchived])
1086
- @@index([ownerId, isArchived])
1087
- }
1088
-
1089
- // Links entities (conversations, documents, etc.) to folders
1090
- // Polymorphic: entityType + entityId point to any supported entity
1091
- model FolderItem {
1092
- id String @id @default(cuid())
1093
- folderId String
1094
-
1095
- // Polymorphic reference
1096
- entityType FolderItemType
1097
- entityId String
1098
-
1099
- // Optional metadata override
1100
- displayName String?
1101
- notes String?
1102
-
1103
- // Denormalized for efficient hierarchy queries
1104
- rootFolderId String?
1105
-
1106
- // Tracking
1107
- addedById String
1108
- sortOrder Int @default(0)
1109
- createdAt DateTime @default(now())
1110
- updatedAt DateTime @updatedAt
1111
-
1112
- // Relations
1113
- folder Folder @relation(fields: [folderId], references: [id], onDelete: Cascade)
1114
- addedBy User @relation("FolderItemAdder", fields: [addedById], references: [id], onDelete: Restrict)
1115
-
1116
- @@unique([folderId, entityType, entityId])
1117
- @@index([folderId])
1118
- @@index([entityType, entityId])
1119
- @@index([rootFolderId])
1120
- @@index([addedById])
1121
- }
1122
-
1123
- // Timeline entries for folders (system events + user messages)
1124
- // Opening ANY folder shows ALL activities from the entire folder tree
1125
- model FolderActivity {
1126
- id String @id @default(cuid())
1127
- folderId String
1128
-
1129
- // Denormalized for cross-folder timeline queries
1130
- rootFolderId String?
1131
-
1132
- // Activity details
1133
- activityType FolderActivityType
1134
- userId String
1135
- content String? // For USER_MESSAGE, USER_NOTE types
1136
- metadata Json? // For system event details
1137
- relatedItemId String? // Optional reference to FolderItem
1138
-
1139
- // Timestamp
1140
- createdAt DateTime @default(now())
1141
-
1142
- // Relations
1143
- folder Folder @relation(fields: [folderId], references: [id], onDelete: Cascade)
1144
- user User @relation("FolderActivityUser", fields: [userId], references: [id], onDelete: Restrict)
1145
-
1146
- @@index([folderId])
1147
- @@index([rootFolderId])
1148
- @@index([userId])
1149
- @@index([activityType])
1150
- @@index([createdAt])
1151
- @@index([rootFolderId, createdAt(sort: Desc)])
1152
- }
1153
-
1154
- // Sharing permissions for folders
1155
- // Can share with individual user OR entire organization (not both)
1156
- model FolderShare {
1157
- id String @id @default(cuid())
1158
- folderId String
1159
-
1160
- // Share target (either user OR organization, enforced by application logic)
1161
- userId String?
1162
- organizationId String?
1163
-
1164
- // Permission level
1165
- permission FolderPermission @default(VIEW)
1166
-
1167
- // Sharing metadata
1168
- sharedById String
1169
- sharedAt DateTime @default(now())
1170
- expiresAt DateTime?
1171
-
1172
- // Relations
1173
- folder Folder @relation(fields: [folderId], references: [id], onDelete: Cascade)
1174
- user User? @relation("FolderShareUser", fields: [userId], references: [id], onDelete: Cascade)
1175
- organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
1176
- sharedBy User @relation("FolderShareCreator", fields: [sharedById], references: [id], onDelete: Restrict)
1177
-
1178
- // Partial unique indexes for nullable columns
1179
- @@unique([folderId, userId])
1180
- @@unique([folderId, organizationId])
1181
- @@index([folderId])
1182
- @@index([userId])
1183
- @@index([organizationId])
1184
- @@index([sharedById])
1185
- }
1
+ generator client {
2
+ provider = "prisma-client-js"
3
+ output = "./generated/app"
4
+ binaryTargets = ["native", "linux-musl-openssl-3.0.x"]
5
+ }
6
+
7
+ datasource db {
8
+ provider = "postgresql"
9
+ url = env("POSTGRES_PRISMA_URL")
10
+ directUrl = env("POSTGRES_URL_NON_POOLING")
11
+ }
12
+
13
+ model Account {
14
+ id String @id @default(cuid())
15
+ userId String
16
+ type String
17
+ provider String
18
+ providerAccountId String
19
+ refresh_token String?
20
+ access_token String?
21
+ expires_at Int?
22
+ token_type String?
23
+ scope String?
24
+ id_token String?
25
+ session_state String?
26
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
27
+
28
+ @@unique([provider, providerAccountId])
29
+ }
30
+
31
+ model User {
32
+ id String @id @default(cuid())
33
+ name String?
34
+ email String? @unique
35
+ emailVerified DateTime?
36
+ image String?
37
+ // Credentials authentication fields
38
+ password String? // Hashed password pre credentials login
39
+ createdAt DateTime @default(now())
40
+ agreedToTerms Boolean @default(false)
41
+ practiceArea String[]
42
+ lawFirm String?
43
+ yearsOfExperience Int?
44
+ // Nové polia pre schvalovanie používateľov
45
+ isApproved Boolean @default(false) // Či je používateľ schválený
46
+ isRejected Boolean @default(false) // Či je používateľ zamietnutý
47
+ approvedAt DateTime? // Kedy bol schválený
48
+ rejectedAt DateTime? // Kedy bol zamietnutý
49
+ approvedBy String? // ID admina, ktorý schválil
50
+ rejectedBy String? // ID admina, ktorý zamietol
51
+ rejectionReason String? // Dôvod zamietnutia
52
+ // Nové polia pre tracking a žiadosť
53
+ referralSource String? // Odkiaľ sa o nás dozvedel (Google, Facebook, LinkedIn, etc.)
54
+ applicationText String? // Text žiadosti o prihlásenie
55
+ // STRIPE FIELDS (B2C - len pre individuálnych používateľov)
56
+ subscriptionTier SubscriptionTier @default(FREE) // Aktuálny subscription tier (cache)
57
+ messageCount Int @default(10) // OPRAVENÉ z 100 na 10
58
+ messageCountResetAt DateTime? // Kedy sa resetovať message count
59
+ adminGrantExpiresAt DateTime? // Kedy admin grant expiruje
60
+ // COMPANY FIELDS (pre SZČO - samostatne zárobkovo činná osoba)
61
+ customerType CustomerType @default(INDIVIDUAL) // Typ zákazníka: fyzická osoba alebo SZČO
62
+ companyName String? // Obchodné meno (pre SZČO)
63
+ companyNumber String? // IČO (povinné pre SZČO, 8 číslic)
64
+ vatNumber String? // DPH (SK + 10 číslic, nepovinné)
65
+ taxNumber String? // DIČ (10 číslic, nepovinné)
66
+ companyStreet String? // Ulica a číslo (pre SZČO)
67
+ companyCity String? // Mesto (pre SZČO)
68
+ companyPostalCode String? // PSČ (pre SZČO, formát: XXX XX)
69
+ companyCountry String? @default("SK") // Krajina (ISO kód, default SK)
70
+ // UTM TRACKING & ATTRIBUTION FIELDS
71
+ // First-visit attribution (celý lifecycle)
72
+ firstUtmSource String? // Prvý UTM source
73
+ firstUtmMedium String? // Prvý UTM medium
74
+ firstUtmCampaign String? // Prvá kampaň
75
+ firstUtmContent String? // Prvý content/variant
76
+ firstUtmTerm String? // Prvé keywords
77
+ firstVisitAt DateTime? // Kedy prvýkrát navštívil web
78
+ // Registration attribution
79
+ registrationUtmSource String? // UTM source pri registrácii
80
+ registrationUtmMedium String? // UTM medium pri registrácii
81
+ registrationUtmCampaign String? // Kampaň pri registrácii
82
+ registrationUtmContent String? // Content/variant pri registrácii
83
+ registrationUtmTerm String? // Keywords pri registrácii
84
+ // Relations
85
+ approvalRequest UserApprovalRequest?
86
+ stripeCustomer StripeCustomer? // ✨ B2C Stripe customer
87
+ ownedOrganizations Organization[] @relation("OrganizationOwner")
88
+ organizationMembers OrganizationMember[]
89
+ createdInvites OrganizationInvite[] @relation("InviteCreator")
90
+ accounts Account[]
91
+ answers Answer[]
92
+ conversations Conversation[]
93
+ feedbacks Feedback[]
94
+ pageViews PageView[]
95
+ sessions Session[]
96
+ workflowLogs WorkflowLog[]
97
+ verificationTokens VerificationToken[]
98
+ passwordResetTokens PasswordResetToken[]
99
+ canvasDocuments CanvasDocument[]
100
+ canvasDocumentVersions CanvasDocumentVersion[]
101
+ // Folder system relations
102
+ ownedFolders Folder[] @relation("FolderOwner")
103
+ addedFolderItems FolderItem[] @relation("FolderItemAdder")
104
+ folderActivities FolderActivity[] @relation("FolderActivityUser")
105
+ folderSharesReceived FolderShare[] @relation("FolderShareUser")
106
+ createdFolderShares FolderShare[] @relation("FolderShareCreator")
107
+ // UTM tracking relations
108
+ touchPoints TouchPoint[]
109
+ conversions Conversion[]
110
+
111
+ @@index([customerType])
112
+ @@index([companyNumber])
113
+ @@index([email, customerType])
114
+ // UTM tracking indexes
115
+ @@index([firstUtmSource])
116
+ @@index([firstUtmCampaign])
117
+ @@index([registrationUtmSource])
118
+ @@index([registrationUtmCampaign])
119
+ @@index([firstVisitAt])
120
+ }
121
+
122
+ // Nový model pre žiadosti o schválenie
123
+ model UserApprovalRequest {
124
+ id String @id @default(cuid())
125
+ userId String @unique
126
+ status ApprovalStatus @default(PENDING)
127
+ submittedAt DateTime @default(now())
128
+ reviewedAt DateTime?
129
+ reviewedBy String? // ID admina, ktorý preskúmal žiadosť
130
+ notes String? // Poznámky admina
131
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
132
+
133
+ @@index([status])
134
+ @@index([submittedAt])
135
+ @@index([reviewedAt])
136
+ }
137
+
138
+ // ============================================
139
+ // ORGANIZATION MODELS (B2B)
140
+ // ============================================
141
+
142
+ // Organization = Právna kancelária / Firma
143
+ model Organization {
144
+ id String @id @default(cuid())
145
+ name String // "Advokátska kancelária XYZ s.r.o."
146
+
147
+ // Billing & Legal Information
148
+ companyNumber String? // IČO (Identifikačné číslo organizácie)
149
+ vatNumber String? // DPH (VAT Number)
150
+ taxNumber String? // DIČ (Daňové identifikačné číslo)
151
+ legalForm String? // "s.r.o.", "a.s.", "v.o.s.", "k.s.", "SZČO"
152
+ billingEmail String
153
+
154
+ // Address
155
+ street String?
156
+ city String?
157
+ postalCode String?
158
+ country String @default("SK")
159
+
160
+ // Subscription (B2B)
161
+ subscriptionTier SubscriptionTier @default(FREE) // Tier organizácie (cache)
162
+ messageCount Int @default(0) // Spoločný pool správ pre všetkých členov
163
+ messageLimit Int @default(100) // Limit správ podľa tieru
164
+ messageCountResetAt DateTime? // Kedy sa má resetovať pool
165
+ adminGrantExpiresAt DateTime? // Kedy admin grant expiruje
166
+
167
+ // Settings
168
+ isActive Boolean @default(true)
169
+ maxMembers Int @default(5) // Max počet členov podľa tieru
170
+
171
+ // Relations
172
+ owner User @relation("OrganizationOwner", fields: [ownerId], references: [id])
173
+ ownerId String
174
+ members OrganizationMember[]
175
+ invites OrganizationInvite[]
176
+ stripeCustomer StripeCustomer?
177
+ folderShares FolderShare[]
178
+
179
+ createdAt DateTime @default(now())
180
+ updatedAt DateTime @updatedAt
181
+
182
+ @@index([ownerId])
183
+ @@index([vatNumber])
184
+ @@index([companyNumber])
185
+ @@index([subscriptionTier])
186
+ @@index([billingEmail])
187
+ }
188
+
189
+ // Many-to-Many: User ↔ Organization
190
+ model OrganizationMember {
191
+ id String @id @default(cuid())
192
+ organizationId String
193
+ userId String
194
+ role MemberRole @default(MEMBER)
195
+ joinedAt DateTime @default(now())
196
+
197
+ organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
198
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
199
+
200
+ @@unique([organizationId, userId])
201
+ @@index([organizationId])
202
+ @@index([userId])
203
+ @@index([role])
204
+ }
205
+
206
+ // Organization Invite System
207
+ model OrganizationInvite {
208
+ id String @id @default(cuid())
209
+ organizationId String
210
+ token String @unique
211
+ email String? // Optional: specific email invite
212
+ role MemberRole @default(MEMBER)
213
+ createdBy String
214
+ expiresAt DateTime
215
+ maxUses Int @default(1) // For multi-use invites
216
+ currentUses Int @default(0)
217
+ isActive Boolean @default(true)
218
+ usedAt DateTime? // Last usage timestamp
219
+ usedBy String? // Last user who used it
220
+ createdAt DateTime @default(now())
221
+ updatedAt DateTime @updatedAt
222
+
223
+ organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
224
+ creator User @relation("InviteCreator", fields: [createdBy], references: [id])
225
+
226
+ @@index([token])
227
+ @@index([organizationId])
228
+ @@index([isActive])
229
+ @@index([expiresAt])
230
+ }
231
+
232
+ enum MemberRole {
233
+ OWNER // Zakladateľ/vlastník, plný prístup k billing a settings
234
+ ADMIN // Administrátor, môže pridávať/odoberať členov
235
+ MEMBER // Bežný člen, len používa systém
236
+ }
237
+
238
+ // ZJEDNODUŠENÝ Conversation - len metadata, bez duplikátov
239
+ model Conversation {
240
+ id String @id @default(cuid())
241
+ name String
242
+ userId String
243
+ isShareable Boolean @default(false)
244
+ shareUrl String? @unique
245
+ sharedAt DateTime?
246
+ createdAt DateTime @default(now())
247
+ updatedAt DateTime @updatedAt
248
+ summary String?
249
+ messagesSinceLastSummary Int? @default(0)
250
+ // Relácie
251
+ answers Answer[]
252
+ user User @relation(fields: [userId], references: [id])
253
+ workflowLogs WorkflowLog[]
254
+ canvasDocuments CanvasDocument[]
255
+
256
+ @@index([userId])
257
+ @@index([createdAt])
258
+ }
259
+
260
+ // Hlavný model - všetky správy, bez duplikátov
261
+ model Answer {
262
+ id String @id @default(cuid())
263
+ conversationId String
264
+ messageId String @unique
265
+ role Role
266
+ content String
267
+ question String?
268
+ answer String?
269
+ evaluation String?
270
+ isWelcome Boolean @default(false)
271
+ processingTime Int?
272
+ model String?
273
+ userId String?
274
+ createdAt DateTime @default(now())
275
+ updatedAt DateTime @updatedAt
276
+ // Relácie
277
+ conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
278
+ user User? @relation(fields: [userId], references: [id])
279
+ feedback Feedback?
280
+ references Reference[]
281
+ metrics AnswerMetrics?
282
+ WorkflowLog WorkflowLog[]
283
+ files MessageFile[]
284
+ canvasDocumentId String? // No FK constraint - flexible document management
285
+
286
+ @@index([conversationId])
287
+ @@index([messageId])
288
+ @@index([role])
289
+ @@index([userId])
290
+ @@index([createdAt])
291
+ }
292
+
293
+ model MessageFile {
294
+ id String @id @default(cuid())
295
+ answerId String?
296
+ fileName String
297
+ fileType String
298
+ base64Data String
299
+ uploadedAt DateTime @default(now())
300
+ answer Answer? @relation(fields: [answerId], references: [id], onDelete: Cascade)
301
+
302
+ @@index([answerId])
303
+ @@index([fileType])
304
+ @@index([uploadedAt])
305
+ }
306
+
307
+ // Nová tabuľka pre metriky odpovedí
308
+ model AnswerMetrics {
309
+ id String @id @default(cuid())
310
+ answerId String @unique
311
+ // Náklady
312
+ apiCost Float? // Celkové náklady (ai_cost + rag_cost)
313
+ aiCost Float? // Náklady len na AI provider volania
314
+ ragCost Float? // Náklady na RAG operácie
315
+ // Volania
316
+ apiCalls Int? // Celkový počet volaní
317
+ aiCalls Int? // Počet AI provider volaní
318
+ ragCalls Int? // Počet RAG operácií
319
+ // Čas a výkon
320
+ apiDuration Int? // Doba spracovania v ms
321
+ // AI Provider a Model
322
+ aiProvider String? // Hlavný AI provider
323
+ aiModel String? // Hlavný AI model
324
+ aiProvidersUsed String[] // Všetky použité AI providery
325
+ aiModelsUsed String[] // Všetky použité AI modely
326
+ // Timestamps
327
+ createdAt DateTime @default(now())
328
+ updatedAt DateTime @updatedAt
329
+ // Relations
330
+ answer Answer @relation(fields: [answerId], references: [id], onDelete: Cascade)
331
+
332
+ @@index([answerId])
333
+ @@index([apiCost])
334
+ @@index([apiDuration])
335
+ @@index([aiProvider])
336
+ @@index([createdAt])
337
+ }
338
+
339
+ model Feedback {
340
+ id String @id @default(cuid())
341
+ answerId String @unique
342
+ rating FeedbackRating
343
+ feedbackTypes String[]
344
+ customFeedback String?
345
+ userId String
346
+ messageContent String?
347
+ createdAt DateTime @default(now())
348
+ answer Answer @relation(fields: [answerId], references: [id], onDelete: Cascade)
349
+ user User @relation(fields: [userId], references: [id])
350
+
351
+ @@index([rating])
352
+ @@index([userId])
353
+ @@index([answerId])
354
+ }
355
+
356
+ // ZJEDNODUŠENÝ Reference - len na Answer, bez duplikátov
357
+ model Reference {
358
+ id String @id @default(cuid())
359
+ answerId String // Len answerId - konzistentné
360
+ type ReferenceType
361
+ title String
362
+ reference String
363
+ summary String?
364
+ url String?
365
+ uuid String?
366
+ relevance Float?
367
+ citationNumber Int?
368
+ metadata Json?
369
+ userId String?
370
+ createdAt DateTime @default(now())
371
+ // Relácie
372
+ answer Answer @relation(fields: [answerId], references: [id], onDelete: Cascade)
373
+
374
+ @@index([answerId])
375
+ @@index([type])
376
+ @@index([reference])
377
+ @@index([userId])
378
+ @@index([createdAt])
379
+ }
380
+
381
+ model PageView {
382
+ id String @id @default(cuid())
383
+ userId String?
384
+ sessionId String?
385
+ page String
386
+ path String
387
+ referrer String?
388
+ userAgent String?
389
+ ipAddress String?
390
+ country String?
391
+ city String?
392
+ deviceType String?
393
+ browser String?
394
+ os String?
395
+ screenSize String?
396
+ language String?
397
+ timeOnPage Int?
398
+ isBounce Boolean @default(true)
399
+ createdAt DateTime @default(now())
400
+ session Session? @relation(fields: [sessionId], references: [sessionId], onDelete: SetNull)
401
+ user User? @relation(fields: [userId], references: [id])
402
+
403
+ @@index([userId])
404
+ @@index([sessionId])
405
+ @@index([page])
406
+ @@index([createdAt])
407
+ @@index([country])
408
+ @@index([deviceType])
409
+ }
410
+
411
+ model Session {
412
+ id String @id @default(cuid())
413
+ sessionId String @unique
414
+ userId String?
415
+ startedAt DateTime @default(now())
416
+ endedAt DateTime?
417
+ duration Int?
418
+
419
+ // ============================================
420
+ // FIRST-TOUCH ATTRIBUTION
421
+ // ============================================
422
+ // Prvý UTM touchpoint v tejto session
423
+ // Automaticky naplnené pri prvom TouchPoint s UTM
424
+
425
+ firstUtmSource String?
426
+ firstUtmMedium String?
427
+ firstUtmCampaign String?
428
+ firstUtmContent String?
429
+ firstUtmTerm String?
430
+ firstTouchAt DateTime? // Timestamp prvého UTM touchpoint
431
+
432
+ // ============================================
433
+ // LAST-TOUCH ATTRIBUTION
434
+ // ============================================
435
+ // Posledný UTM touchpoint v tejto session
436
+ // Aktualizované pri každom novom TouchPoint s UTM
437
+
438
+ lastUtmSource String?
439
+ lastUtmMedium String?
440
+ lastUtmCampaign String?
441
+ lastUtmContent String?
442
+ lastUtmTerm String?
443
+ lastTouchAt DateTime? // Timestamp posledného UTM touchpoint
444
+
445
+ // ============================================
446
+ // RELATIONS
447
+ // ============================================
448
+
449
+ touchPoints TouchPoint[] // Všetky touchpointy v session
450
+ conversions Conversion[] // Všetky konverzie v session
451
+
452
+ // Existujúce relations
453
+ pageViews PageView[]
454
+ user User? @relation(fields: [userId], references: [id])
455
+ workflowLogs WorkflowLog[]
456
+
457
+ // ============================================
458
+ // INDEXES
459
+ // ============================================
460
+
461
+ @@index([firstUtmCampaign])
462
+ @@index([lastUtmCampaign])
463
+ @@index([firstUtmSource, firstUtmMedium])
464
+ @@index([lastUtmSource, lastUtmMedium])
465
+
466
+ // Existujúce indexes
467
+ @@index([userId])
468
+ @@index([startedAt])
469
+ }
470
+
471
+ // ============================================
472
+ // UTM TRACKING & ATTRIBUTION MODELS
473
+ // ============================================
474
+
475
+ // TouchPoint - každý click s UTM parametrami
476
+ // Umožňuje multi-touch attribution a customer journey analysis
477
+ model TouchPoint {
478
+ id String @id @default(cuid())
479
+ userId String? // Pre prihlásených používateľov
480
+ sessionId String // Session tracking (required)
481
+
482
+ // ============================================
483
+ // UTM PARAMETERS (všetky lowercase!)
484
+ // ============================================
485
+ // Best practice: Vždy lowercase, validované pred uložením
486
+
487
+ utmSource String? // Odkiaľ: "email", "facebook", "google", "linkedin"
488
+ utmMedium String? // Typ média: "email", "cpc", "social", "organic", "paid-social"
489
+ utmCampaign String? // Názov kampane: "smizany-2026-01", "spring-sale-2026"
490
+ utmContent String? // Variant/link identifier: "cta-primary", "banner-top", "hero-button"
491
+ utmTerm String? // Keywords (pre paid search): "pravny-asistent", "advokat-ai"
492
+
493
+ // ============================================
494
+ // CONTEXT & METADATA
495
+ // ============================================
496
+
497
+ page String // Ktorá stránka: "/", "/pricing", "/contact"
498
+ path String // Celý path s params: "/?utm_source=email&utm_campaign=..."
499
+ referrer String? // HTTP Referrer (odkiaľ prišiel)
500
+
501
+ // Device & Browser info (denormalizované pre rýchle queries)
502
+ deviceType String? // "mobile", "tablet", "desktop"
503
+ browser String? // "Chrome", "Firefox", "Safari"
504
+ os String? // "Windows", "macOS", "iOS", "Android"
505
+
506
+ // Geolocation (anonymizované)
507
+ country String? // "SK", "CZ", "US"
508
+ city String? // "Bratislava", "Prague"
509
+
510
+ // Timestamp
511
+ timestamp DateTime @default(now())
512
+
513
+ // ============================================
514
+ // RELATIONS
515
+ // ============================================
516
+
517
+ session Session @relation(fields: [sessionId], references: [sessionId], onDelete: Cascade)
518
+ user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
519
+
520
+ // ============================================
521
+ // INDEXES (optimalizované pre analytics queries)
522
+ // ============================================
523
+
524
+ @@index([userId])
525
+ @@index([sessionId])
526
+ @@index([utmSource])
527
+ @@index([utmMedium])
528
+ @@index([utmCampaign])
529
+ @@index([utmSource, utmMedium])
530
+ @@index([utmCampaign, timestamp])
531
+ @@index([timestamp])
532
+ @@index([sessionId, timestamp])
533
+ @@index([userId, timestamp])
534
+
535
+ @@map("touch_points")
536
+ }
537
+
538
+ // Conversion - sledovanie konverzií s attribution
539
+ // Podporuje first-touch, last-touch aj multi-touch attribution
540
+ model Conversion {
541
+ id String @id @default(cuid())
542
+ userId String? // Kto konvertoval (nullable pre anonymous conversions)
543
+ sessionId String // V ktorej session
544
+
545
+ // ============================================
546
+ // CONVERSION METADATA
547
+ // ============================================
548
+
549
+ type ConversionType // Typ konverzie
550
+ value Decimal? // Revenue (pre e-commerce/subscriptions)
551
+ currency String? @default("EUR")
552
+
553
+ // Optional metadata
554
+ metadata Json? // Stripe subscription ID, product info, etc.
555
+
556
+ // ============================================
557
+ // FIRST-TOUCH ATTRIBUTION (40% credit)
558
+ // ============================================
559
+ // Prvý UTM touchpoint v session = awareness channel
560
+
561
+ firstUtmSource String? // Odkiaľ prišiel prvýkrát
562
+ firstUtmMedium String? // Akým médiom prvýkrát
563
+ firstUtmCampaign String? // Ktorá kampaň priniesla awareness
564
+ firstUtmContent String? // Ktorý konkrétny link/banner
565
+ firstUtmTerm String? // Ktoré keywords (pre paid search)
566
+ firstTouchAt DateTime? // Kedy bol prvý touchpoint
567
+
568
+ // ============================================
569
+ // LAST-TOUCH ATTRIBUTION (40% credit)
570
+ // ============================================
571
+ // Posledný UTM touchpoint pred konverziou = conversion channel
572
+
573
+ lastUtmSource String? // Odkiaľ prišiel naposledy
574
+ lastUtmMedium String? // Akým médiom naposledy
575
+ lastUtmCampaign String? // Ktorá kampaň priniesla konverziu
576
+ lastUtmContent String? // Ktorý konkrétny link/banner
577
+ lastUtmTerm String? // Ktoré keywords (pre paid search)
578
+ lastTouchAt DateTime? // Kedy bol posledný touchpoint
579
+
580
+ // ============================================
581
+ // MULTI-TOUCH ATTRIBUTION (20% credit)
582
+ // ============================================
583
+ // Všetky touchpointy medzi prvým a posledným
584
+
585
+ touchPointIds String[] // Array of TouchPoint IDs (pre detailnú analýzu)
586
+ touchPointCount Int? // Počet touchpoints v customer journey
587
+
588
+ // ============================================
589
+ // CALCULATED FIELDS
590
+ // ============================================
591
+
592
+ journeyDuration Int? // Čas od prvého touchpoint po konverziu (sekundy)
593
+
594
+ // Timestamp
595
+ timestamp DateTime @default(now())
596
+
597
+ // ============================================
598
+ // RELATIONS
599
+ // ============================================
600
+
601
+ session Session @relation(fields: [sessionId], references: [sessionId], onDelete: Cascade)
602
+ user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
603
+
604
+ // ============================================
605
+ // INDEXES
606
+ // ============================================
607
+
608
+ @@index([userId])
609
+ @@index([sessionId])
610
+ @@index([type])
611
+ @@index([timestamp])
612
+
613
+ // First-touch attribution indexes
614
+ @@index([firstUtmSource])
615
+ @@index([firstUtmMedium])
616
+ @@index([firstUtmCampaign])
617
+ @@index([firstUtmCampaign, type])
618
+ @@index([firstUtmSource, firstUtmMedium])
619
+
620
+ // Last-touch attribution indexes
621
+ @@index([lastUtmSource])
622
+ @@index([lastUtmMedium])
623
+ @@index([lastUtmCampaign])
624
+ @@index([lastUtmCampaign, type])
625
+ @@index([lastUtmSource, lastUtmMedium])
626
+
627
+ // Analytics indexes
628
+ @@index([type, timestamp])
629
+ @@index([value])
630
+
631
+ @@map("conversions")
632
+ }
633
+
634
+ // ============================================
635
+ // STRIPE INTEGRATION MODELS
636
+ // ============================================
637
+
638
+ // Stripe Customer - polymorphic relation (User OR Organization)
639
+ model StripeCustomer {
640
+ id String @id @default(cuid())
641
+
642
+ // Polymorphic relation - buď User (B2C) alebo Organization (B2B)
643
+ userId String? @unique // B2C: Individuálny používateľ
644
+ organizationId String? @unique // B2B: Organizácia
645
+
646
+ stripeCustomerId String @unique
647
+ email String
648
+ name String?
649
+
650
+ // Billing details (Stripe Address & Tax IDs)
651
+ billingAddress Json? // Stripe Address object
652
+ taxIds Json? // Stripe Tax IDs array (VAT, etc.)
653
+
654
+ createdAt DateTime @default(now())
655
+ updatedAt DateTime @updatedAt
656
+
657
+ // Relations
658
+ user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
659
+ organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
660
+ subscriptions StripeSubscription[]
661
+ payments StripePayment[]
662
+
663
+ @@index([stripeCustomerId])
664
+ @@index([userId])
665
+ @@index([organizationId])
666
+ @@index([email])
667
+ }
668
+
669
+ // Stripe Subscription - sledovanie predplatného (User alebo Organization)
670
+ model StripeSubscription {
671
+ id String @id @default(cuid())
672
+
673
+ // ✅ FIX: customerId = FK na StripeCustomer.id (nie stripeCustomerId!)
674
+ customerId String // FK na StripeCustomer.id
675
+ stripeCustomerId String // Stripe ID (pre logging/redundancia)
676
+ stripeSubscriptionId String @unique
677
+ stripePriceId String
678
+ stripeProductId String
679
+
680
+ status SubscriptionStatus
681
+ tier SubscriptionTier @default(FREE)
682
+ billingInterval BillingInterval @default(MONTHLY) // Monthly or Yearly billing
683
+
684
+ currentPeriodStart DateTime
685
+ currentPeriodEnd DateTime
686
+ cancelAtPeriodEnd Boolean @default(false)
687
+ canceledAt DateTime?
688
+
689
+ trialStart DateTime?
690
+ trialEnd DateTime?
691
+
692
+ // Additional fields
693
+ quantity Int @default(1) // Počet seats (pre LAW_FIRM/ENTERPRISE tier)
694
+ defaultPaymentMethodId String? // Stripe payment method ID
695
+
696
+ metadata Json? // Dodatočné Stripe metadata
697
+ createdAt DateTime @default(now())
698
+ updatedAt DateTime @updatedAt
699
+
700
+ // FIX: Relácia na customerId (interné ID), nie stripeCustomerId (Stripe ID)
701
+ customer StripeCustomer @relation(fields: [customerId], references: [id], onDelete: Cascade)
702
+
703
+ @@index([stripeSubscriptionId])
704
+ @@index([customerId])
705
+ @@index([stripeCustomerId])
706
+ @@index([status])
707
+ @@index([tier])
708
+ @@index([billingInterval])
709
+ @@index([currentPeriodEnd])
710
+ }
711
+
712
+ // Stripe Event - prevencia duplicitného spracovania webhookov (idempotencia)
713
+ model StripeEvent {
714
+ id String @id @default(cuid())
715
+ eventId String @unique // Stripe event ID
716
+ type String // Typ eventu (napr. 'checkout.session.completed')
717
+ data Json // Úplné event data pre debugging
718
+ processed Boolean @default(false)
719
+ processedAt DateTime?
720
+ error String? // Uloženie chýb pri spracovaní
721
+ createdAt DateTime @default(now())
722
+
723
+ @@index([eventId])
724
+ @@index([processed])
725
+ @@index([type])
726
+ @@index([createdAt])
727
+ }
728
+
729
+ // Stripe Payment - sledovanie jednotlivých platieb (voliteľné, pre detailnú analytiku)
730
+ model StripePayment {
731
+ id String @id @default(cuid())
732
+
733
+ // ✅ FIX: Pridaná relácia na StripeCustomer
734
+ customerId String
735
+ stripePaymentId String @unique
736
+ stripeCustomerId String // Redundantné, ale OK pre logging
737
+
738
+ amount Int // Suma v centoch
739
+ currency String @default("eur")
740
+ status String // succeeded, failed, pending
741
+ paymentMethod String? // card, sepa_debit, atď.
742
+ description String?
743
+
744
+ // Invoice info
745
+ invoiceId String? // Stripe Invoice ID
746
+ invoiceUrl String? // URL na faktúru
747
+
748
+ metadata Json?
749
+ createdAt DateTime @default(now())
750
+
751
+ // Relácia na StripeCustomer
752
+ customer StripeCustomer @relation(fields: [customerId], references: [id], onDelete: Cascade)
753
+
754
+ @@index([stripePaymentId])
755
+ @@index([customerId])
756
+ @@index([stripeCustomerId])
757
+ @@index([status])
758
+ @@index([createdAt])
759
+ }
760
+
761
+ // ============================================
762
+ // ENUMS
763
+ // ============================================
764
+
765
+ enum Role {
766
+ USER
767
+ ASSISTANT
768
+ SYSTEM
769
+ }
770
+
771
+ enum FeedbackRating {
772
+ LIKE
773
+ DISLIKE
774
+ NEUTRAL
775
+ }
776
+
777
+ enum ReferenceType {
778
+ LAW
779
+ CASE
780
+ REGULATION
781
+ DOCUMENT
782
+ OTHER
783
+ }
784
+
785
+ enum ApprovalStatus {
786
+ PENDING
787
+ APPROVED
788
+ REJECTED
789
+ }
790
+
791
+ // Stripe: Status predplatného
792
+ enum SubscriptionStatus {
793
+ ACTIVE // Predplatné je aktívne
794
+ CANCELED // Predplatné zrušené
795
+ INCOMPLETE // Iniciálna platba zlyhala
796
+ INCOMPLETE_EXPIRED // Neúplné predplatné expirované
797
+ PAST_DUE // Platba zlyhala, retry prebieha
798
+ TRIALING // V skúšobnom období
799
+ UNPAID // Platba zlyhala, žiadny retry
800
+ PAUSED // Predplatné pozastavené (Stripe feature)
801
+ }
802
+
803
+ // Customer type: Individuálny vs SZČO
804
+ enum CustomerType {
805
+ INDIVIDUAL // Fyzická osoba (bez IČO)
806
+ SELF_EMPLOYED // SZČO - Samostatne zárobkovo činná osoba (s IČO)
807
+ }
808
+
809
+ // Stripe: Tier predplatného
810
+ enum SubscriptionTier {
811
+ FREE // Free tier (10 messages/month)
812
+ LAWYER // Lawyer tier (1000 messages/month, €29/month, 1 user)
813
+ LAWYER_PRO // Lawyer Pro tier (3000 messages/month, €49/month, 1 user) - RECOMMENDED
814
+ LAW_FIRM // Law Firm tier (10000 messages/month, €129/month, up to 5 users)
815
+ ENTERPRISE // Enterprise tier (unlimited messages, unlimited users, custom pricing)
816
+ }
817
+
818
+ // Stripe: Billing interval
819
+ enum BillingInterval {
820
+ MONTHLY // Monthly billing
821
+ YEARLY // Yearly billing (17% discount)
822
+ }
823
+
824
+ // Canvas Document Status
825
+ enum DocumentStatus {
826
+ DRAFT
827
+ SAVED
828
+ ARCHIVED
829
+ }
830
+
831
+ // Conversion types for UTM attribution tracking
832
+ enum ConversionType {
833
+ REGISTRATION // Nová registrácia
834
+ SUBSCRIPTION // Začiatok predplatného (FREEPAID)
835
+ UPGRADE // Upgrade tieru (LAWYER → LAWYER_PRO)
836
+ RENEWAL // Obnovenie predplatného
837
+ TRIAL_START // Začiatok trial periodu
838
+ PURCHASE // Jednorazový nákup
839
+ LEAD // Vyplnenie kontaktného formulára
840
+ DEMO_REQUEST // Žiadosť o demo
841
+ DOWNLOAD // Stiahnutie resourceu
842
+ FORM_SUBMIT // Iné formuláre
843
+ }
844
+
845
+ // ============================================
846
+ // FOLDER SYSTEM ENUMS
847
+ // ============================================
848
+
849
+ // Folder item types (what can be stored in folders)
850
+ enum FolderItemType {
851
+ CONVERSATION
852
+ CANVAS_DOCUMENT
853
+ REFERENCE
854
+ ATTACHMENT
855
+ }
856
+
857
+ // Folder activity types (for timeline)
858
+ enum FolderActivityType {
859
+ FOLDER_CREATED
860
+ FOLDER_UPDATED
861
+ FOLDER_MOVED
862
+ FOLDER_ARCHIVED
863
+ ITEM_ADDED
864
+ ITEM_REMOVED
865
+ ITEM_UPDATED
866
+ SHARE_ADDED
867
+ SHARE_REMOVED
868
+ USER_MESSAGE
869
+ USER_NOTE
870
+ }
871
+
872
+ // Folder permission levels
873
+ enum FolderPermission {
874
+ VIEW
875
+ EDIT
876
+ ADMIN
877
+ }
878
+
879
+ // Nový model pre logovanie admin akcií
880
+ model AdminActionLog {
881
+ id String @id @default(cuid())
882
+ action String // APPROVE_USER, REJECT_USER, etc.
883
+ targetUserId String // ID používateľa, na ktorého sa akcia vzťahuje
884
+ adminEmail String // E-mail admina, ktorý vykonal akciu
885
+ details Json? // Ďalšie detaily akcie (reason, notes, etc.)
886
+ createdAt DateTime @default(now())
887
+
888
+ @@index([action])
889
+ @@index([targetUserId])
890
+ @@index([adminEmail])
891
+ @@index([createdAt])
892
+ }
893
+
894
+ model WorkflowLog {
895
+ id String @id @default(cuid()) // Added @default(cuid())
896
+ conversationId String
897
+ messageId String?
898
+ userId String? // Made optional to preserve logs when users are deleted
899
+ sessionId String? // Removed @unique to allow multiple workflows per session
900
+ startedAt DateTime @default(now())
901
+ completedAt DateTime?
902
+ status WorkflowStatus @default(IN_PROGRESS)
903
+ totalDuration Int? // milliseconds
904
+ error String?
905
+ metadata Json?
906
+ steps WorkflowStep[] // Renamed from WorkflowStep to steps (lowerCamel, plural)
907
+ conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
908
+ user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
909
+ session Session? @relation(fields: [sessionId], references: [sessionId], onDelete: SetNull)
910
+ answer Answer? @relation(fields: [messageId], references: [messageId])
911
+
912
+ @@index([conversationId])
913
+ @@index([userId])
914
+ @@index([sessionId])
915
+ @@index([startedAt])
916
+ @@index([status])
917
+ @@index([conversationId, startedAt]) // Composite index for timeline queries
918
+ @@index([messageId])
919
+ }
920
+
921
+ model WorkflowStep {
922
+ id String @id @default(cuid()) // Added @default(cuid())
923
+ workflowId String
924
+ stepName String // 'classify_question', 'rag_search', 'ai_processing', 'generate_response'
925
+ stepType StepType
926
+ startedAt DateTime @default(now())
927
+ completedAt DateTime?
928
+ duration Int? // milliseconds
929
+ status StepStatus @default(IN_PROGRESS)
930
+ inputData Json? // Sanitized input data
931
+ outputData Json? // Sanitized output data
932
+ error String?
933
+ metadata Json? // Provider used, model used, tokens, costs, etc.
934
+ workflow WorkflowLog @relation(fields: [workflowId], references: [id], onDelete: Cascade)
935
+
936
+ @@index([workflowId])
937
+ @@index([stepName])
938
+ @@index([stepType])
939
+ @@index([startedAt])
940
+ @@index([status])
941
+ @@index([workflowId, startedAt]) // Composite index for timeline queries
942
+ }
943
+
944
+ enum WorkflowStatus {
945
+ IN_PROGRESS
946
+ COMPLETED
947
+ FAILED
948
+ CANCELLED
949
+ }
950
+
951
+ enum StepStatus {
952
+ IN_PROGRESS
953
+ COMPLETED
954
+ FAILED
955
+ SKIPPED
956
+ }
957
+
958
+ enum StepType {
959
+ CLASSIFICATION
960
+ RAG_RETRIEVAL
961
+ AI_PROCESSING
962
+ RESPONSE_GENERATION
963
+ DATA_PERSISTENCE
964
+ ERROR_HANDLING
965
+ }
966
+
967
+ // Email verification tokens
968
+ model VerificationToken {
969
+ id String @id @default(cuid())
970
+ userId String
971
+ token String @unique
972
+ expires DateTime
973
+ createdAt DateTime @default(now())
974
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
975
+
976
+ @@index([token])
977
+ @@index([userId])
978
+ @@index([expires])
979
+ }
980
+
981
+ // Password reset tokens
982
+ model PasswordResetToken {
983
+ id String @id @default(cuid())
984
+ userId String
985
+ token String @unique
986
+ expires DateTime
987
+ createdAt DateTime @default(now())
988
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
989
+
990
+ @@index([token])
991
+ @@index([userId])
992
+ @@index([expires])
993
+ }
994
+
995
+ // ============================================
996
+ // CANVAS DOCUMENT MODELS
997
+ // ============================================
998
+
999
+ model CanvasDocument {
1000
+ id String @id @default(cuid())
1001
+ userId String
1002
+ title String
1003
+ status DocumentStatus @default(DRAFT)
1004
+ createdAt DateTime @default(now())
1005
+ updatedAt DateTime @updatedAt
1006
+ originConversationId String?
1007
+ originAnswerId String? // No FK constraint - async save reference
1008
+ currentVersionId String? @unique
1009
+
1010
+ // Relations
1011
+ user User @relation(fields: [userId], references: [id], onDelete: Restrict)
1012
+ originConversation Conversation? @relation(fields: [originConversationId], references: [id], onDelete: SetNull)
1013
+ currentVersion CanvasDocumentVersion? @relation("CurrentVersion", fields: [currentVersionId], references: [id], onDelete: SetNull)
1014
+ versions CanvasDocumentVersion[] @relation("DocumentVersions")
1015
+
1016
+ @@index([userId])
1017
+ @@index([originConversationId])
1018
+ @@index([status])
1019
+ @@index([createdAt])
1020
+ }
1021
+
1022
+ model CanvasDocumentVersion {
1023
+ id String @id @default(cuid())
1024
+ documentId String
1025
+ versionNumber Int
1026
+ title String
1027
+ markdownContent String
1028
+ createdAt DateTime @default(now())
1029
+ createdBy String
1030
+ regenerationPrompt String?
1031
+ parentVersionId String?
1032
+
1033
+ // Relations
1034
+ document CanvasDocument @relation("DocumentVersions", fields: [documentId], references: [id], onDelete: Cascade)
1035
+ creator User @relation(fields: [createdBy], references: [id], onDelete: Restrict)
1036
+ parentVersion CanvasDocumentVersion? @relation("VersionHistory", fields: [parentVersionId], references: [id], onDelete: SetNull)
1037
+ childVersions CanvasDocumentVersion[] @relation("VersionHistory")
1038
+ currentForDocument CanvasDocument? @relation("CurrentVersion")
1039
+
1040
+ @@unique([documentId, versionNumber])
1041
+ @@index([documentId])
1042
+ @@index([createdBy])
1043
+ @@index([createdAt])
1044
+ }
1045
+
1046
+ // ============================================
1047
+ // FOLDER SYSTEM MODELS
1048
+ // ============================================
1049
+
1050
+ // Main folder entity with hierarchy support via parentId
1051
+ // Uses materialized path pattern for efficient hierarchy queries
1052
+ model Folder {
1053
+ id String @id @default(cuid())
1054
+ name String
1055
+ description String?
1056
+ color String? @default("#6366f1")
1057
+ icon String? @default("folder")
1058
+
1059
+ // Ownership (always user-owned)
1060
+ ownerId String
1061
+
1062
+ // Hierarchy
1063
+ parentId String?
1064
+ rootFolderId String? // NULL for root folders, points to top-level ancestor
1065
+ path String @default("/") // Materialized path: "/parentId/grandparentId/..."
1066
+ depth Int @default(0)
1067
+
1068
+ // Metadata
1069
+ sortOrder Int @default(0)
1070
+ isArchived Boolean @default(false)
1071
+ createdAt DateTime @default(now())
1072
+ updatedAt DateTime @updatedAt
1073
+
1074
+ // Relations
1075
+ owner User @relation("FolderOwner", fields: [ownerId], references: [id], onDelete: Cascade)
1076
+ parent Folder? @relation("FolderHierarchy", fields: [parentId], references: [id], onDelete: Cascade)
1077
+ children Folder[] @relation("FolderHierarchy")
1078
+ items FolderItem[]
1079
+ activities FolderActivity[]
1080
+ shares FolderShare[]
1081
+
1082
+ @@index([ownerId])
1083
+ @@index([parentId])
1084
+ @@index([rootFolderId])
1085
+ @@index([path])
1086
+ @@index([isArchived])
1087
+ @@index([ownerId, isArchived])
1088
+ }
1089
+
1090
+ // Links entities (conversations, documents, etc.) to folders
1091
+ // Polymorphic: entityType + entityId point to any supported entity
1092
+ model FolderItem {
1093
+ id String @id @default(cuid())
1094
+ folderId String
1095
+
1096
+ // Polymorphic reference
1097
+ entityType FolderItemType
1098
+ entityId String
1099
+
1100
+ // Optional metadata override
1101
+ displayName String?
1102
+ notes String?
1103
+
1104
+ // Denormalized for efficient hierarchy queries
1105
+ rootFolderId String?
1106
+
1107
+ // Tracking
1108
+ addedById String
1109
+ sortOrder Int @default(0)
1110
+ createdAt DateTime @default(now())
1111
+ updatedAt DateTime @updatedAt
1112
+
1113
+ // Relations
1114
+ folder Folder @relation(fields: [folderId], references: [id], onDelete: Cascade)
1115
+ addedBy User @relation("FolderItemAdder", fields: [addedById], references: [id], onDelete: Restrict)
1116
+
1117
+ @@unique([folderId, entityType, entityId])
1118
+ @@index([folderId])
1119
+ @@index([entityType, entityId])
1120
+ @@index([rootFolderId])
1121
+ @@index([addedById])
1122
+ }
1123
+
1124
+ // Timeline entries for folders (system events + user messages)
1125
+ // Opening ANY folder shows ALL activities from the entire folder tree
1126
+ model FolderActivity {
1127
+ id String @id @default(cuid())
1128
+ folderId String
1129
+
1130
+ // Denormalized for cross-folder timeline queries
1131
+ rootFolderId String?
1132
+
1133
+ // Activity details
1134
+ activityType FolderActivityType
1135
+ userId String
1136
+ content String? // For USER_MESSAGE, USER_NOTE types
1137
+ metadata Json? // For system event details
1138
+ relatedItemId String? // Optional reference to FolderItem
1139
+
1140
+ // Timestamp
1141
+ createdAt DateTime @default(now())
1142
+
1143
+ // Relations
1144
+ folder Folder @relation(fields: [folderId], references: [id], onDelete: Cascade)
1145
+ user User @relation("FolderActivityUser", fields: [userId], references: [id], onDelete: Restrict)
1146
+
1147
+ @@index([folderId])
1148
+ @@index([rootFolderId])
1149
+ @@index([userId])
1150
+ @@index([activityType])
1151
+ @@index([createdAt])
1152
+ @@index([rootFolderId, createdAt(sort: Desc)])
1153
+ }
1154
+
1155
+ // Sharing permissions for folders
1156
+ // Can share with individual user OR entire organization (not both)
1157
+ model FolderShare {
1158
+ id String @id @default(cuid())
1159
+ folderId String
1160
+
1161
+ // Share target (either user OR organization, enforced by application logic)
1162
+ userId String?
1163
+ organizationId String?
1164
+
1165
+ // Permission level
1166
+ permission FolderPermission @default(VIEW)
1167
+
1168
+ // Sharing metadata
1169
+ sharedById String
1170
+ sharedAt DateTime @default(now())
1171
+ expiresAt DateTime?
1172
+
1173
+ // Relations
1174
+ folder Folder @relation(fields: [folderId], references: [id], onDelete: Cascade)
1175
+ user User? @relation("FolderShareUser", fields: [userId], references: [id], onDelete: Cascade)
1176
+ organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
1177
+ sharedBy User @relation("FolderShareCreator", fields: [sharedById], references: [id], onDelete: Restrict)
1178
+
1179
+ // Partial unique indexes for nullable columns
1180
+ @@unique([folderId, userId])
1181
+ @@unique([folderId, organizationId])
1182
+ @@index([folderId])
1183
+ @@index([userId])
1184
+ @@index([organizationId])
1185
+ @@index([sharedById])
1186
+ }