@valentine-efagene/qshelter-common 2.0.21 → 2.0.24
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/dist/generated/client/browser.d.ts +45 -30
- package/dist/generated/client/client.d.ts +45 -30
- package/dist/generated/client/commonInputTypes.d.ts +40 -0
- package/dist/generated/client/internal/class.d.ts +93 -60
- package/dist/generated/client/internal/class.js +2 -2
- package/dist/generated/client/internal/prismaNamespace.d.ts +1058 -720
- package/dist/generated/client/internal/prismaNamespace.js +321 -190
- package/dist/generated/client/internal/prismaNamespaceBrowser.d.ts +352 -215
- package/dist/generated/client/internal/prismaNamespaceBrowser.js +321 -190
- package/dist/generated/client/models/Amenity.d.ts +168 -1
- package/dist/generated/client/models/Contract.d.ts +2390 -309
- package/dist/generated/client/models/ContractDocument.d.ts +299 -12
- package/dist/generated/client/models/ContractEvent.d.ts +1052 -0
- package/dist/generated/client/models/ContractEvent.js +1 -0
- package/dist/generated/client/models/ContractInstallment.d.ts +1656 -0
- package/dist/generated/client/models/ContractInstallment.js +1 -0
- package/dist/generated/client/models/ContractPayment.d.ts +2026 -0
- package/dist/generated/client/models/ContractPayment.js +1 -0
- package/dist/generated/client/models/ContractPhase.d.ts +2467 -0
- package/dist/generated/client/models/ContractPhase.js +1 -0
- package/dist/generated/client/models/ContractPhaseStep.d.ts +1678 -0
- package/dist/generated/client/models/ContractPhaseStep.js +1 -0
- package/dist/generated/client/models/ContractPhaseStepApproval.d.ts +1249 -0
- package/dist/generated/client/models/ContractPhaseStepApproval.js +1 -0
- package/dist/generated/client/models/ContractTransition.d.ts +1118 -0
- package/dist/generated/client/models/ContractTransition.js +1 -0
- package/dist/generated/client/models/DomainEvent.d.ts +1240 -0
- package/dist/generated/client/models/DomainEvent.js +1 -0
- package/dist/generated/client/models/PaymentPlan.d.ts +467 -971
- package/dist/generated/client/models/Property.d.ts +372 -626
- package/dist/generated/client/models/PropertyPaymentMethod.d.ts +1714 -0
- package/dist/generated/client/models/PropertyPaymentMethod.js +1 -0
- package/dist/generated/client/models/PropertyPaymentMethodLink.d.ts +1158 -0
- package/dist/generated/client/models/PropertyPaymentMethodLink.js +1 -0
- package/dist/generated/client/models/PropertyPaymentMethodPhase.d.ts +1656 -0
- package/dist/generated/client/models/PropertyPaymentMethodPhase.js +1 -0
- package/dist/generated/client/models/PropertyUnit.d.ts +1598 -0
- package/dist/generated/client/models/PropertyUnit.js +1 -0
- package/dist/generated/client/models/PropertyVariant.d.ts +2079 -0
- package/dist/generated/client/models/PropertyVariant.js +1 -0
- package/dist/generated/client/models/PropertyVariantAmenity.d.ts +1080 -0
- package/dist/generated/client/models/PropertyVariantAmenity.js +1 -0
- package/dist/generated/client/models/PropertyVariantMedia.d.ts +1189 -0
- package/dist/generated/client/models/PropertyVariantMedia.js +1 -0
- package/dist/generated/client/models/Tenant.d.ts +482 -0
- package/dist/generated/client/models/User.d.ts +684 -427
- package/dist/generated/client/models/index.d.ts +15 -12
- package/dist/generated/client/models/index.js +15 -12
- package/dist/generated/client/models.d.ts +15 -12
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +2 -0
- package/dist/src/middleware/error-handler.d.ts +6 -0
- package/dist/src/middleware/error-handler.js +26 -0
- package/dist/src/middleware/index.d.ts +3 -0
- package/dist/src/middleware/index.js +3 -0
- package/dist/src/middleware/request-logger.d.ts +6 -0
- package/dist/src/middleware/request-logger.js +17 -0
- package/dist/src/middleware/tenant.d.ts +61 -0
- package/dist/src/middleware/tenant.js +85 -0
- package/dist/src/prisma/tenant.d.ts +51 -0
- package/dist/src/prisma/tenant.js +211 -0
- package/package.json +16 -2
- package/prisma/migrations/20251230104059_add_property_variants/migration.sql +622 -0
- package/prisma/migrations/20251230113413_add_multitenancy/migration.sql +54 -0
- package/prisma/schema.prisma +561 -267
package/prisma/schema.prisma
CHANGED
|
@@ -49,12 +49,15 @@ model User {
|
|
|
49
49
|
socials Social[]
|
|
50
50
|
|
|
51
51
|
// Relations to other domains
|
|
52
|
-
properties
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
52
|
+
properties Property[]
|
|
53
|
+
contracts Contract[] @relation("ContractBuyer")
|
|
54
|
+
soldContracts Contract[] @relation("ContractSeller")
|
|
55
|
+
contractPayments ContractPayment[] @relation("ContractPayer")
|
|
56
|
+
|
|
57
|
+
// Phase step assignments and approvals
|
|
58
|
+
assignedSteps ContractPhaseStep[] @relation("PhaseStepAssignee")
|
|
59
|
+
stepApprovals ContractPhaseStepApproval[] @relation("PhaseStepApprover")
|
|
60
|
+
uploadedDocs ContractDocument[] @relation("DocumentUploader")
|
|
58
61
|
|
|
59
62
|
@@index([email])
|
|
60
63
|
@@index([tenantId])
|
|
@@ -115,10 +118,16 @@ model Tenant {
|
|
|
115
118
|
name String
|
|
116
119
|
subdomain String @unique
|
|
117
120
|
isActive Boolean @default(true)
|
|
118
|
-
users User[]
|
|
119
121
|
createdAt DateTime @default(now())
|
|
120
122
|
updatedAt DateTime @updatedAt
|
|
121
123
|
|
|
124
|
+
// Back-relations for multitenancy
|
|
125
|
+
users User[]
|
|
126
|
+
properties Property[]
|
|
127
|
+
paymentPlans PaymentPlan[]
|
|
128
|
+
paymentMethods PropertyPaymentMethod[]
|
|
129
|
+
contracts Contract[]
|
|
130
|
+
|
|
122
131
|
@@index([subdomain])
|
|
123
132
|
@@map("tenants")
|
|
124
133
|
}
|
|
@@ -262,28 +271,29 @@ model Settings {
|
|
|
262
271
|
// =============================================================================
|
|
263
272
|
// PROPERTY DOMAIN
|
|
264
273
|
// =============================================================================
|
|
274
|
+
// Property = listing/project (e.g., "Sunrise Estate")
|
|
275
|
+
// PropertyVariant = configuration with specs & price (e.g., "3-Bed Corner - Finished")
|
|
276
|
+
// PropertyUnit = individual sellable unit (e.g., "Unit A1")
|
|
277
|
+
// =============================================================================
|
|
265
278
|
|
|
266
279
|
model Property {
|
|
267
280
|
id String @id @default(cuid())
|
|
281
|
+
tenantId String
|
|
282
|
+
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
268
283
|
userId String
|
|
269
284
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
270
285
|
title String
|
|
271
286
|
category String // SALE, RENT, LEASE
|
|
272
|
-
propertyType String // APARTMENT, HOUSE, LAND, COMMERCIAL
|
|
287
|
+
propertyType String // APARTMENT, HOUSE, LAND, COMMERCIAL, ESTATE, TOWNHOUSE
|
|
273
288
|
country String
|
|
274
289
|
currency String // USD, NGN, etc
|
|
275
290
|
city String
|
|
276
291
|
district String?
|
|
277
292
|
zipCode String?
|
|
278
293
|
streetAddress String?
|
|
279
|
-
nBedrooms String
|
|
280
|
-
nBathrooms String
|
|
281
|
-
nParkingSpots String
|
|
282
|
-
price Float
|
|
283
294
|
longitude Float?
|
|
284
295
|
latitude Float?
|
|
285
|
-
|
|
286
|
-
status String @default("DRAFT") // DRAFT, PUBLISHED, SOLD, RENTED
|
|
296
|
+
status String @default("DRAFT") // DRAFT, PUBLISHED, SOLD_OUT, ARCHIVED
|
|
287
297
|
description String? @db.Text
|
|
288
298
|
displayImageId String?
|
|
289
299
|
displayImage PropertyMedia? @relation("DisplayImage", fields: [displayImageId], references: [id], onDelete: SetNull)
|
|
@@ -292,17 +302,19 @@ model Property {
|
|
|
292
302
|
createdAt DateTime @default(now())
|
|
293
303
|
updatedAt DateTime @updatedAt
|
|
294
304
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
305
|
+
// Relations
|
|
306
|
+
documents PropertyDocument[]
|
|
307
|
+
media PropertyMedia[] @relation("PropertyMedia")
|
|
308
|
+
amenities PropertyAmenity[] // Shared amenities (gym, pool, security)
|
|
309
|
+
paymentMethods PropertyPaymentMethodLink[]
|
|
310
|
+
variants PropertyVariant[]
|
|
301
311
|
|
|
312
|
+
@@index([tenantId])
|
|
302
313
|
@@index([userId])
|
|
303
314
|
@@index([category])
|
|
304
315
|
@@index([propertyType])
|
|
305
316
|
@@index([city])
|
|
317
|
+
@@index([status])
|
|
306
318
|
@@map("properties")
|
|
307
319
|
}
|
|
308
320
|
|
|
@@ -338,15 +350,134 @@ model PropertyDocument {
|
|
|
338
350
|
}
|
|
339
351
|
|
|
340
352
|
model Amenity {
|
|
341
|
-
id String
|
|
342
|
-
name String
|
|
343
|
-
|
|
344
|
-
|
|
353
|
+
id String @id @default(cuid())
|
|
354
|
+
name String @unique
|
|
355
|
+
category String? // PROPERTY, VARIANT, BOTH - helps filter which amenities to show
|
|
356
|
+
icon String? // Icon name/URL for UI
|
|
357
|
+
createdAt DateTime @default(now())
|
|
358
|
+
updatedAt DateTime @updatedAt
|
|
345
359
|
properties PropertyAmenity[]
|
|
360
|
+
variants PropertyVariantAmenity[]
|
|
346
361
|
|
|
362
|
+
@@index([category])
|
|
347
363
|
@@map("amenities")
|
|
348
364
|
}
|
|
349
365
|
|
|
366
|
+
// =============================================================================
|
|
367
|
+
// PROPERTY VARIANT & UNIT MODELS
|
|
368
|
+
// =============================================================================
|
|
369
|
+
|
|
370
|
+
// PropertyVariant = specific configuration with its own price and amenities
|
|
371
|
+
// e.g., "3-Bedroom Corner Piece - Fully Finished", "2-Bedroom Standard - Carcass"
|
|
372
|
+
model PropertyVariant {
|
|
373
|
+
id String @id @default(cuid())
|
|
374
|
+
propertyId String
|
|
375
|
+
property Property @relation(fields: [propertyId], references: [id], onDelete: Cascade)
|
|
376
|
+
|
|
377
|
+
name String // "Corner Piece - Finished", "Standard - Carcass"
|
|
378
|
+
description String? @db.Text
|
|
379
|
+
|
|
380
|
+
// Specifications
|
|
381
|
+
nBedrooms Int?
|
|
382
|
+
nBathrooms Int?
|
|
383
|
+
nParkingSpots Int?
|
|
384
|
+
area Float? // Square meters/feet
|
|
385
|
+
|
|
386
|
+
// Pricing
|
|
387
|
+
price Float
|
|
388
|
+
pricePerSqm Float? // Computed or set manually
|
|
389
|
+
|
|
390
|
+
// Inventory counters (denormalized for performance, updated via triggers/service)
|
|
391
|
+
totalUnits Int @default(1)
|
|
392
|
+
availableUnits Int @default(1)
|
|
393
|
+
reservedUnits Int @default(0)
|
|
394
|
+
soldUnits Int @default(0)
|
|
395
|
+
|
|
396
|
+
// Status
|
|
397
|
+
status String @default("AVAILABLE") // AVAILABLE, LOW_STOCK, SOLD_OUT, ARCHIVED
|
|
398
|
+
isActive Boolean @default(true)
|
|
399
|
+
createdAt DateTime @default(now())
|
|
400
|
+
updatedAt DateTime @updatedAt
|
|
401
|
+
|
|
402
|
+
// Relations
|
|
403
|
+
amenities PropertyVariantAmenity[]
|
|
404
|
+
units PropertyUnit[]
|
|
405
|
+
media PropertyVariantMedia[]
|
|
406
|
+
|
|
407
|
+
@@index([propertyId])
|
|
408
|
+
@@index([status])
|
|
409
|
+
@@index([price])
|
|
410
|
+
@@map("property_variants")
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// PropertyVariantAmenity = amenities specific to a variant
|
|
414
|
+
// e.g., "Finished Kitchen", "Smart Home System", "Private Garden"
|
|
415
|
+
model PropertyVariantAmenity {
|
|
416
|
+
variantId String
|
|
417
|
+
amenityId String
|
|
418
|
+
variant PropertyVariant @relation(fields: [variantId], references: [id], onDelete: Cascade)
|
|
419
|
+
amenity Amenity @relation(fields: [amenityId], references: [id], onDelete: Cascade)
|
|
420
|
+
createdAt DateTime @default(now())
|
|
421
|
+
|
|
422
|
+
@@id([variantId, amenityId])
|
|
423
|
+
@@map("property_variant_amenities")
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// PropertyVariantMedia = images/videos specific to a variant
|
|
427
|
+
model PropertyVariantMedia {
|
|
428
|
+
id String @id @default(cuid())
|
|
429
|
+
variantId String
|
|
430
|
+
variant PropertyVariant @relation(fields: [variantId], references: [id], onDelete: Cascade)
|
|
431
|
+
url String
|
|
432
|
+
type String // IMAGE, VIDEO, FLOOR_PLAN, 3D_TOUR
|
|
433
|
+
caption String?
|
|
434
|
+
order Int @default(0)
|
|
435
|
+
createdAt DateTime @default(now())
|
|
436
|
+
updatedAt DateTime @updatedAt
|
|
437
|
+
|
|
438
|
+
@@index([variantId])
|
|
439
|
+
@@map("property_variant_media")
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// PropertyUnit = individual sellable/rentable unit within a variant
|
|
443
|
+
// e.g., "Unit A1", "Block B - Flat 3", "Plot 15"
|
|
444
|
+
model PropertyUnit {
|
|
445
|
+
id String @id @default(cuid())
|
|
446
|
+
variantId String
|
|
447
|
+
variant PropertyVariant @relation(fields: [variantId], references: [id], onDelete: Cascade)
|
|
448
|
+
|
|
449
|
+
unitNumber String // "A1", "B-3", "Plot 15"
|
|
450
|
+
floorNumber Int? // For apartments
|
|
451
|
+
blockName String? // "Block A", "Tower 1"
|
|
452
|
+
|
|
453
|
+
// Unit-specific overrides (if different from variant)
|
|
454
|
+
priceOverride Float? // If this specific unit has a different price
|
|
455
|
+
areaOverride Float? // If this specific unit has a different area
|
|
456
|
+
notes String? @db.Text // Internal notes about this unit
|
|
457
|
+
|
|
458
|
+
// Status tracking
|
|
459
|
+
status String @default("AVAILABLE") // AVAILABLE, RESERVED, SOLD, RENTED, UNAVAILABLE
|
|
460
|
+
|
|
461
|
+
// Reservation/hold
|
|
462
|
+
reservedAt DateTime?
|
|
463
|
+
reservedUntil DateTime?
|
|
464
|
+
reservedById String?
|
|
465
|
+
|
|
466
|
+
// Ownership tracking (once sold)
|
|
467
|
+
ownerId String?
|
|
468
|
+
|
|
469
|
+
createdAt DateTime @default(now())
|
|
470
|
+
updatedAt DateTime @updatedAt
|
|
471
|
+
|
|
472
|
+
// Relations
|
|
473
|
+
contracts Contract[]
|
|
474
|
+
|
|
475
|
+
@@unique([variantId, unitNumber])
|
|
476
|
+
@@index([variantId])
|
|
477
|
+
@@index([status])
|
|
478
|
+
@@map("property_units")
|
|
479
|
+
}
|
|
480
|
+
|
|
350
481
|
model PropertyAmenity {
|
|
351
482
|
propertyId String
|
|
352
483
|
amenityId String
|
|
@@ -359,305 +490,468 @@ model PropertyAmenity {
|
|
|
359
490
|
}
|
|
360
491
|
|
|
361
492
|
// =============================================================================
|
|
362
|
-
//
|
|
493
|
+
// PAYMENT PLAN DOMAIN - Reusable installment structure templates
|
|
363
494
|
// =============================================================================
|
|
364
495
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
downpaymentPlan MortgageDownpaymentPlan? @relation(fields: [downpaymentPlanId], references: [id])
|
|
375
|
-
principal Float
|
|
376
|
-
downPayment Float
|
|
377
|
-
downPaymentPaid Float @default(0)
|
|
378
|
-
termMonths Int
|
|
379
|
-
interestRate Float
|
|
380
|
-
monthlyPayment Float
|
|
381
|
-
status String @default("DRAFT") // DRAFT, PENDING, ACTIVE, COMPLETED, CANCELLED
|
|
382
|
-
state String @default("DRAFT") // FSM state
|
|
383
|
-
stateMetadata String? @db.Text // JSON metadata
|
|
384
|
-
lastReminderSentAt DateTime?
|
|
385
|
-
createdAt DateTime @default(now())
|
|
386
|
-
updatedAt DateTime @updatedAt
|
|
496
|
+
// PaymentPlan = reusable structure for how payments are scheduled
|
|
497
|
+
// Examples: "Monthly360" (360 monthly payments), "Weekly52", "OneTime"
|
|
498
|
+
model PaymentPlan {
|
|
499
|
+
id String @id @default(cuid())
|
|
500
|
+
tenantId String? // NULL = global template available to all tenants
|
|
501
|
+
tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
502
|
+
name String
|
|
503
|
+
description String? @db.Text
|
|
504
|
+
isActive Boolean @default(true)
|
|
387
505
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
506
|
+
// Structure configuration
|
|
507
|
+
paymentFrequency String // MONTHLY, BIWEEKLY, WEEKLY, ONE_TIME, CUSTOM
|
|
508
|
+
customFrequencyDays Int?
|
|
509
|
+
numberOfInstallments Int // 1 for one-time, 360 for 30yr monthly, etc
|
|
510
|
+
calculateInterestDaily Boolean @default(false)
|
|
511
|
+
gracePeriodDays Int @default(0)
|
|
392
512
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
@@index([status])
|
|
396
|
-
@@index([state])
|
|
397
|
-
@@map("mortgages")
|
|
398
|
-
}
|
|
513
|
+
createdAt DateTime @default(now())
|
|
514
|
+
updatedAt DateTime @updatedAt
|
|
399
515
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
createdAt DateTime @default(now())
|
|
405
|
-
updatedAt DateTime @updatedAt
|
|
406
|
-
mortgages Mortgage[]
|
|
516
|
+
// Used by property payment method phases (templates)
|
|
517
|
+
methodPhases PropertyPaymentMethodPhase[]
|
|
518
|
+
// Used by instantiated contract phases
|
|
519
|
+
contractPhases ContractPhase[]
|
|
407
520
|
|
|
408
|
-
@@
|
|
521
|
+
@@unique([tenantId, name]) // Unique per tenant, or globally if tenantId is null
|
|
522
|
+
@@index([tenantId])
|
|
523
|
+
@@map("payment_plans")
|
|
409
524
|
}
|
|
410
525
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
mortgage Mortgage @relation(fields: [mortgageId], references: [id], onDelete: Cascade)
|
|
415
|
-
name String
|
|
416
|
-
url String
|
|
417
|
-
type String // INCOME_PROOF, ID, etc
|
|
418
|
-
createdAt DateTime @default(now())
|
|
419
|
-
updatedAt DateTime @updatedAt
|
|
526
|
+
// =============================================================================
|
|
527
|
+
// PROPERTY PAYMENT METHOD DOMAIN - Product offerings per property
|
|
528
|
+
// =============================================================================
|
|
420
529
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
530
|
+
// PropertyPaymentMethod = how a property can be purchased (e.g., "Standard Mortgage", "Cash", "Rent-to-Own")
|
|
531
|
+
model PropertyPaymentMethod {
|
|
532
|
+
id String @id @default(cuid())
|
|
533
|
+
tenantId String
|
|
534
|
+
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
535
|
+
name String // "Standard Mortgage", "Flexible Payment", "Cash Purchase"
|
|
536
|
+
description String? @db.Text
|
|
537
|
+
isActive Boolean @default(true)
|
|
538
|
+
|
|
539
|
+
// Global method configuration
|
|
540
|
+
allowEarlyPayoff Boolean @default(true)
|
|
541
|
+
earlyPayoffPenaltyRate Float?
|
|
542
|
+
autoActivatePhases Boolean @default(true)
|
|
543
|
+
requiresManualApproval Boolean @default(false)
|
|
424
544
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
createdAt DateTime @default(now())
|
|
435
|
-
updatedAt DateTime @updatedAt
|
|
545
|
+
createdAt DateTime @default(now())
|
|
546
|
+
updatedAt DateTime @updatedAt
|
|
547
|
+
|
|
548
|
+
// Many-to-many with properties
|
|
549
|
+
properties PropertyPaymentMethodLink[]
|
|
550
|
+
// Phases that make up this method (templates)
|
|
551
|
+
phases PropertyPaymentMethodPhase[]
|
|
552
|
+
// Contracts using this method
|
|
553
|
+
contracts Contract[]
|
|
436
554
|
|
|
437
|
-
@@
|
|
438
|
-
@@
|
|
555
|
+
@@unique([tenantId, name]) // Unique per tenant
|
|
556
|
+
@@index([tenantId])
|
|
557
|
+
@@map("property_payment_methods")
|
|
439
558
|
}
|
|
440
559
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
endDate DateTime
|
|
448
|
-
createdAt DateTime @default(now())
|
|
449
|
-
updatedAt DateTime @updatedAt
|
|
560
|
+
// Many-to-many link between Property and PaymentMethod
|
|
561
|
+
model PropertyPaymentMethodLink {
|
|
562
|
+
propertyId String
|
|
563
|
+
property Property @relation(fields: [propertyId], references: [id], onDelete: Cascade)
|
|
564
|
+
paymentMethodId String
|
|
565
|
+
paymentMethod PropertyPaymentMethod @relation(fields: [paymentMethodId], references: [id], onDelete: Cascade)
|
|
450
566
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
567
|
+
// Method-specific overrides for this property
|
|
568
|
+
isDefault Boolean @default(false)
|
|
569
|
+
isActive Boolean @default(true)
|
|
570
|
+
createdAt DateTime @default(now())
|
|
454
571
|
|
|
455
|
-
@@
|
|
572
|
+
@@id([propertyId, paymentMethodId])
|
|
573
|
+
@@map("property_payment_method_links")
|
|
456
574
|
}
|
|
457
575
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
createdAt DateTime @default(now())
|
|
467
|
-
updatedAt DateTime @updatedAt
|
|
576
|
+
// Phase template within a PropertyPaymentMethod (e.g., documentation, downpayment, mortgage)
|
|
577
|
+
// phaseCategory determines the FSM type: DOCUMENTATION or PAYMENT
|
|
578
|
+
model PropertyPaymentMethodPhase {
|
|
579
|
+
id String @id @default(cuid())
|
|
580
|
+
paymentMethodId String
|
|
581
|
+
paymentMethod PropertyPaymentMethod @relation(fields: [paymentMethodId], references: [id], onDelete: Cascade)
|
|
582
|
+
paymentPlanId String? // Only for PAYMENT phases
|
|
583
|
+
paymentPlan PaymentPlan? @relation(fields: [paymentPlanId], references: [id])
|
|
468
584
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
}
|
|
585
|
+
name String
|
|
586
|
+
description String? @db.Text
|
|
472
587
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
amount Float
|
|
478
|
-
paymentMethod String
|
|
479
|
-
reference String?
|
|
480
|
-
status String @default("PENDING")
|
|
481
|
-
createdAt DateTime @default(now())
|
|
482
|
-
updatedAt DateTime @updatedAt
|
|
588
|
+
// Phase classification
|
|
589
|
+
phaseCategory String // DOCUMENTATION, PAYMENT
|
|
590
|
+
phaseType String // Admin-defined: KYC, VERIFICATION, DOWNPAYMENT, MORTGAGE, BALLOON, CUSTOM, etc.
|
|
591
|
+
order Int
|
|
483
592
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
593
|
+
// Financial configuration (for PAYMENT phases)
|
|
594
|
+
interestRate Float?
|
|
595
|
+
percentOfPrice Float? // e.g., 10.0 for 10% downpayment
|
|
487
596
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
mortgage Mortgage @relation(fields: [mortgageId], references: [id], onDelete: Cascade)
|
|
492
|
-
fromState String
|
|
493
|
-
toState String
|
|
494
|
-
trigger String
|
|
495
|
-
metadata String? @db.Text // JSON
|
|
496
|
-
transitionedAt DateTime @default(now())
|
|
597
|
+
// Activation rules
|
|
598
|
+
requiresPreviousPhaseCompletion Boolean @default(true)
|
|
599
|
+
minimumCompletionPercentage Float?
|
|
497
600
|
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
}
|
|
601
|
+
// For DOCUMENTATION phases: define required steps
|
|
602
|
+
requiredDocumentTypes String? // CSV: ID,BANK_STATEMENT,INCOME_PROOF
|
|
603
|
+
stepDefinitions String? @db.Text // JSON: [{name, stepType, order}]
|
|
501
604
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
mortgageId String
|
|
505
|
-
mortgage Mortgage @relation(fields: [mortgageId], references: [id], onDelete: Cascade)
|
|
506
|
-
event String
|
|
507
|
-
data String? @db.Text // JSON
|
|
508
|
-
createdAt DateTime @default(now())
|
|
605
|
+
createdAt DateTime @default(now())
|
|
606
|
+
updatedAt DateTime @updatedAt
|
|
509
607
|
|
|
510
|
-
@@index([
|
|
511
|
-
@@
|
|
608
|
+
@@index([paymentMethodId])
|
|
609
|
+
@@index([paymentPlanId])
|
|
610
|
+
@@index([phaseCategory])
|
|
611
|
+
@@map("property_payment_method_phases")
|
|
512
612
|
}
|
|
513
613
|
|
|
514
614
|
// =============================================================================
|
|
515
|
-
//
|
|
615
|
+
// CONTRACT DOMAIN - Unified agreement model (replaces Mortgage, PurchasePlan, etc.)
|
|
616
|
+
// =============================================================================
|
|
617
|
+
// Contract is the canonical agreement. "Mortgage" is just a product configuration
|
|
618
|
+
// that creates a Contract with specific phases (documentation, downpayment, long-term payment).
|
|
619
|
+
// Phases can be DOCUMENTATION (FSM for approvals) or PAYMENT (PaymentPlan-driven installments).
|
|
516
620
|
// =============================================================================
|
|
517
621
|
|
|
518
|
-
model
|
|
519
|
-
id
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
622
|
+
model Contract {
|
|
623
|
+
id String @id @default(cuid())
|
|
624
|
+
tenantId String
|
|
625
|
+
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
|
626
|
+
// Link to specific unit being purchased/rented
|
|
627
|
+
propertyUnitId String
|
|
628
|
+
propertyUnit PropertyUnit @relation(fields: [propertyUnitId], references: [id], onDelete: Cascade)
|
|
629
|
+
buyerId String
|
|
630
|
+
buyer User @relation("ContractBuyer", fields: [buyerId], references: [id], onDelete: Cascade)
|
|
631
|
+
sellerId String?
|
|
632
|
+
seller User? @relation("ContractSeller", fields: [sellerId], references: [id])
|
|
633
|
+
paymentMethodId String? // PropertyPaymentMethod used to create this contract
|
|
634
|
+
paymentMethod PropertyPaymentMethod? @relation(fields: [paymentMethodId], references: [id])
|
|
635
|
+
|
|
636
|
+
// Contract identification
|
|
637
|
+
contractNumber String @unique
|
|
638
|
+
title String
|
|
639
|
+
description String? @db.Text
|
|
640
|
+
contractType String // Admin-defined: MORTGAGE, INSTALLMENT, RENT_TO_OWN, CASH, LEASE, etc.
|
|
641
|
+
|
|
642
|
+
// Financial summary (computed from phases)
|
|
643
|
+
totalAmount Float // Total contract value (from unit price or negotiated)
|
|
644
|
+
downPayment Float @default(0)
|
|
645
|
+
downPaymentPaid Float @default(0)
|
|
646
|
+
principal Float? // Financed amount (if applicable)
|
|
647
|
+
interestRate Float? // Overall interest rate (if applicable)
|
|
648
|
+
termMonths Int? // Total term (if applicable)
|
|
649
|
+
periodicPayment Float? // Computed periodic payment (if applicable)
|
|
650
|
+
totalPaidToDate Float @default(0)
|
|
651
|
+
totalInterestPaid Float @default(0)
|
|
652
|
+
|
|
653
|
+
// FSM state
|
|
654
|
+
status String @default("DRAFT") // DRAFT, PENDING, ACTIVE, COMPLETED, CANCELLED, TERMINATED
|
|
655
|
+
state String @default("DRAFT") // FSM state for workflow
|
|
656
|
+
currentPhaseId String?
|
|
657
|
+
|
|
658
|
+
// Timing
|
|
659
|
+
nextPaymentDueDate DateTime?
|
|
660
|
+
lastReminderSentAt DateTime?
|
|
661
|
+
startDate DateTime?
|
|
662
|
+
endDate DateTime?
|
|
663
|
+
signedAt DateTime?
|
|
664
|
+
terminatedAt DateTime?
|
|
665
|
+
createdAt DateTime @default(now())
|
|
666
|
+
updatedAt DateTime @updatedAt
|
|
667
|
+
|
|
668
|
+
// Relations
|
|
669
|
+
phases ContractPhase[]
|
|
670
|
+
documents ContractDocument[]
|
|
671
|
+
payments ContractPayment[]
|
|
672
|
+
transitions ContractTransition[]
|
|
673
|
+
events ContractEvent[]
|
|
541
674
|
|
|
542
|
-
@@index([
|
|
675
|
+
@@index([tenantId])
|
|
676
|
+
@@index([propertyUnitId])
|
|
543
677
|
@@index([buyerId])
|
|
678
|
+
@@index([sellerId])
|
|
679
|
+
@@index([paymentMethodId])
|
|
680
|
+
@@index([status])
|
|
544
681
|
@@index([state])
|
|
545
|
-
@@map("
|
|
682
|
+
@@map("contracts")
|
|
546
683
|
}
|
|
547
684
|
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
685
|
+
// Phase within a contract - can be DOCUMENTATION or PAYMENT type
|
|
686
|
+
// Admin names phases freely (e.g., "KYC Documents", "Downpayment", "Monthly Mortgage")
|
|
687
|
+
model ContractPhase {
|
|
688
|
+
id String @id @default(cuid())
|
|
689
|
+
contractId String
|
|
690
|
+
contract Contract @relation(fields: [contractId], references: [id], onDelete: Cascade)
|
|
691
|
+
paymentPlanId String? // Only for PAYMENT phases
|
|
692
|
+
paymentPlan PaymentPlan? @relation(fields: [paymentPlanId], references: [id])
|
|
693
|
+
|
|
694
|
+
// Admin-defined naming
|
|
695
|
+
name String
|
|
696
|
+
description String? @db.Text
|
|
697
|
+
|
|
698
|
+
// Phase classification
|
|
699
|
+
phaseCategory String // DOCUMENTATION, PAYMENT
|
|
700
|
+
phaseType String // Admin-defined: DOWNPAYMENT, MORTGAGE, KYC, VERIFICATION, BALLOON, CUSTOM, etc.
|
|
701
|
+
order Int
|
|
702
|
+
|
|
703
|
+
// FSM state for this phase
|
|
704
|
+
status String @default("PENDING") // PENDING, IN_PROGRESS, AWAITING_APPROVAL, ACTIVE, COMPLETED, SKIPPED, FAILED
|
|
705
|
+
|
|
706
|
+
// Financial details (for PAYMENT phases)
|
|
707
|
+
totalAmount Float?
|
|
708
|
+
paidAmount Float @default(0)
|
|
709
|
+
remainingAmount Float?
|
|
710
|
+
interestRate Float?
|
|
711
|
+
|
|
712
|
+
// Timing
|
|
713
|
+
dueDate DateTime?
|
|
714
|
+
startDate DateTime?
|
|
715
|
+
endDate DateTime?
|
|
716
|
+
activatedAt DateTime?
|
|
717
|
+
completedAt DateTime?
|
|
559
718
|
|
|
560
|
-
|
|
561
|
-
|
|
719
|
+
// Activation rules
|
|
720
|
+
requiresPreviousPhaseCompletion Boolean @default(true)
|
|
721
|
+
minimumCompletionPercentage Float?
|
|
562
722
|
|
|
563
|
-
|
|
564
|
-
|
|
723
|
+
createdAt DateTime @default(now())
|
|
724
|
+
updatedAt DateTime @updatedAt
|
|
725
|
+
|
|
726
|
+
// Relations
|
|
727
|
+
installments ContractInstallment[]
|
|
728
|
+
payments ContractPayment[]
|
|
729
|
+
steps ContractPhaseStep[] // For DOCUMENTATION phases (FSM steps)
|
|
730
|
+
|
|
731
|
+
@@index([contractId])
|
|
732
|
+
@@index([paymentPlanId])
|
|
733
|
+
@@index([phaseCategory])
|
|
734
|
+
@@index([status])
|
|
735
|
+
@@index([order])
|
|
736
|
+
@@map("contract_phases")
|
|
565
737
|
}
|
|
566
738
|
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
739
|
+
// Steps within a DOCUMENTATION phase (FSM for document collection/approval)
|
|
740
|
+
model ContractPhaseStep {
|
|
741
|
+
id String @id @default(cuid())
|
|
742
|
+
phaseId String
|
|
743
|
+
phase ContractPhase @relation(fields: [phaseId], references: [id], onDelete: Cascade)
|
|
744
|
+
|
|
745
|
+
name String
|
|
746
|
+
description String? @db.Text
|
|
747
|
+
stepType String // UPLOAD, REVIEW, SIGNATURE, APPROVAL, EXTERNAL_CHECK, WAIT
|
|
748
|
+
order Int
|
|
749
|
+
|
|
750
|
+
status String @default("PENDING") // PENDING, IN_PROGRESS, COMPLETED, FAILED, SKIPPED
|
|
751
|
+
|
|
752
|
+
// Assignment
|
|
753
|
+
assigneeId String?
|
|
754
|
+
assignee User? @relation("PhaseStepAssignee", fields: [assigneeId], references: [id])
|
|
755
|
+
|
|
756
|
+
// Required document types for UPLOAD steps
|
|
757
|
+
requiredDocumentTypes String? // CSV: ID,BANK_STATEMENT,INCOME_PROOF
|
|
758
|
+
|
|
759
|
+
// Timing
|
|
760
|
+
dueDate DateTime?
|
|
761
|
+
completedAt DateTime?
|
|
762
|
+
|
|
763
|
+
createdAt DateTime @default(now())
|
|
764
|
+
updatedAt DateTime @updatedAt
|
|
765
|
+
|
|
766
|
+
approvals ContractPhaseStepApproval[]
|
|
767
|
+
|
|
768
|
+
@@index([phaseId])
|
|
769
|
+
@@index([status])
|
|
770
|
+
@@index([order])
|
|
771
|
+
@@map("contract_phase_steps")
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
// Approvals for documentation steps
|
|
775
|
+
model ContractPhaseStepApproval {
|
|
776
|
+
id String @id @default(cuid())
|
|
777
|
+
stepId String
|
|
778
|
+
step ContractPhaseStep @relation(fields: [stepId], references: [id], onDelete: Cascade)
|
|
779
|
+
approverId String?
|
|
780
|
+
approver User? @relation("PhaseStepApprover", fields: [approverId], references: [id])
|
|
781
|
+
|
|
782
|
+
decision String // APPROVED, REJECTED, REQUEST_CHANGES
|
|
783
|
+
comment String? @db.Text
|
|
784
|
+
decidedAt DateTime @default(now())
|
|
785
|
+
|
|
786
|
+
createdAt DateTime @default(now())
|
|
787
|
+
|
|
788
|
+
@@index([stepId])
|
|
789
|
+
@@map("contract_phase_step_approvals")
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
// Installments within a PAYMENT phase
|
|
793
|
+
model ContractInstallment {
|
|
794
|
+
id String @id @default(cuid())
|
|
795
|
+
phaseId String
|
|
796
|
+
phase ContractPhase @relation(fields: [phaseId], references: [id], onDelete: Cascade)
|
|
797
|
+
|
|
571
798
|
installmentNumber Int
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
799
|
+
|
|
800
|
+
amount Float
|
|
801
|
+
principalAmount Float @default(0)
|
|
802
|
+
interestAmount Float @default(0)
|
|
803
|
+
|
|
804
|
+
dueDate DateTime
|
|
805
|
+
status String @default("PENDING") // PENDING, PAID, OVERDUE, PARTIALLY_PAID, WAIVED
|
|
806
|
+
|
|
807
|
+
paidAmount Float @default(0)
|
|
808
|
+
paidDate DateTime?
|
|
809
|
+
|
|
810
|
+
lateFee Float @default(0)
|
|
811
|
+
lateFeeWaived Boolean @default(false)
|
|
812
|
+
gracePeriodDays Int @default(0)
|
|
813
|
+
gracePeriodEndDate DateTime?
|
|
814
|
+
|
|
815
|
+
createdAt DateTime @default(now())
|
|
816
|
+
updatedAt DateTime @updatedAt
|
|
817
|
+
|
|
818
|
+
payments ContractPayment[]
|
|
819
|
+
|
|
820
|
+
@@index([phaseId])
|
|
586
821
|
@@index([dueDate])
|
|
587
822
|
@@index([status])
|
|
588
|
-
@@map("
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
823
|
+
@@map("contract_installments")
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// Unified payment record for contracts
|
|
827
|
+
model ContractPayment {
|
|
828
|
+
id String @id @default(cuid())
|
|
829
|
+
contractId String
|
|
830
|
+
contract Contract @relation(fields: [contractId], references: [id], onDelete: Cascade)
|
|
831
|
+
phaseId String?
|
|
832
|
+
phase ContractPhase? @relation(fields: [phaseId], references: [id])
|
|
833
|
+
installmentId String?
|
|
834
|
+
installment ContractInstallment? @relation(fields: [installmentId], references: [id])
|
|
835
|
+
payerId String?
|
|
836
|
+
payer User? @relation("ContractPayer", fields: [payerId], references: [id])
|
|
837
|
+
|
|
601
838
|
amount Float
|
|
602
|
-
principalAmount Float
|
|
603
|
-
interestAmount Float
|
|
604
|
-
lateFeeAmount Float
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
839
|
+
principalAmount Float @default(0)
|
|
840
|
+
interestAmount Float @default(0)
|
|
841
|
+
lateFeeAmount Float @default(0)
|
|
842
|
+
|
|
843
|
+
paymentMethod String // BANK_TRANSFER, CREDIT_CARD, WALLET, CASH, CHECK
|
|
844
|
+
status String @default("INITIATED") // INITIATED, PENDING, COMPLETED, FAILED, REFUNDED
|
|
845
|
+
|
|
846
|
+
reference String? @unique
|
|
847
|
+
gatewayResponse String? @db.Text // JSON
|
|
848
|
+
|
|
849
|
+
processedAt DateTime?
|
|
850
|
+
createdAt DateTime @default(now())
|
|
851
|
+
updatedAt DateTime @updatedAt
|
|
852
|
+
|
|
853
|
+
@@index([contractId])
|
|
854
|
+
@@index([phaseId])
|
|
855
|
+
@@index([installmentId])
|
|
614
856
|
@@index([payerId])
|
|
615
857
|
@@index([status])
|
|
616
858
|
@@index([reference])
|
|
617
|
-
@@map("
|
|
859
|
+
@@map("contract_payments")
|
|
618
860
|
}
|
|
619
861
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
buyer User? @relation("ContractBuyer", fields: [buyerId], references: [id])
|
|
628
|
-
sellerId String?
|
|
629
|
-
seller User? @relation("ContractSeller", fields: [sellerId], references: [id])
|
|
630
|
-
contractType String // MORTGAGE, SALE_AGREEMENT, LEASE_AGREEMENT, etc
|
|
631
|
-
contractNumber String @unique
|
|
632
|
-
title String
|
|
633
|
-
description String? @db.Text
|
|
634
|
-
status String @default("DRAFT") // DRAFT, PENDING_SIGNATURE, ACTIVE, COMPLETED, TERMINATED
|
|
635
|
-
startDate DateTime?
|
|
636
|
-
endDate DateTime?
|
|
637
|
-
signedAt DateTime?
|
|
638
|
-
terminatedAt DateTime?
|
|
639
|
-
createdAt DateTime @default(now())
|
|
640
|
-
updatedAt DateTime @updatedAt
|
|
862
|
+
// Contract documents (owned by contract, linked to phases/steps as needed)
|
|
863
|
+
model ContractDocument {
|
|
864
|
+
id String @id @default(cuid())
|
|
865
|
+
contractId String
|
|
866
|
+
contract Contract @relation(fields: [contractId], references: [id], onDelete: Cascade)
|
|
867
|
+
phaseId String? // Optional link to specific phase
|
|
868
|
+
stepId String? // Optional link to specific step
|
|
641
869
|
|
|
642
|
-
|
|
870
|
+
name String
|
|
871
|
+
url String
|
|
872
|
+
type String // ID, BANK_STATEMENT, INCOME_PROOF, TITLE_DEED, SIGNATURE, etc.
|
|
873
|
+
uploadedById String?
|
|
874
|
+
uploadedBy User? @relation("DocumentUploader", fields: [uploadedById], references: [id])
|
|
643
875
|
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
876
|
+
status String @default("PENDING") // PENDING, APPROVED, REJECTED
|
|
877
|
+
|
|
878
|
+
createdAt DateTime @default(now())
|
|
879
|
+
updatedAt DateTime @updatedAt
|
|
880
|
+
|
|
881
|
+
@@index([contractId])
|
|
882
|
+
@@index([phaseId])
|
|
883
|
+
@@index([stepId])
|
|
884
|
+
@@index([type])
|
|
647
885
|
@@index([status])
|
|
648
|
-
@@map("
|
|
886
|
+
@@map("contract_documents")
|
|
649
887
|
}
|
|
650
888
|
|
|
651
|
-
|
|
889
|
+
// FSM transitions for audit
|
|
890
|
+
model ContractTransition {
|
|
891
|
+
id String @id @default(cuid())
|
|
892
|
+
contractId String
|
|
893
|
+
contract Contract @relation(fields: [contractId], references: [id], onDelete: Cascade)
|
|
894
|
+
fromState String
|
|
895
|
+
toState String
|
|
896
|
+
trigger String
|
|
897
|
+
metadata String? @db.Text // JSON
|
|
898
|
+
transitionedAt DateTime @default(now())
|
|
899
|
+
|
|
900
|
+
@@index([contractId])
|
|
901
|
+
@@map("contract_transitions")
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
// Domain events for audit and integration
|
|
905
|
+
model ContractEvent {
|
|
652
906
|
id String @id @default(cuid())
|
|
653
907
|
contractId String
|
|
654
908
|
contract Contract @relation(fields: [contractId], references: [id], onDelete: Cascade)
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
type String
|
|
909
|
+
event String
|
|
910
|
+
data String? @db.Text // JSON
|
|
658
911
|
createdAt DateTime @default(now())
|
|
659
|
-
updatedAt DateTime @updatedAt
|
|
660
912
|
|
|
661
913
|
@@index([contractId])
|
|
662
|
-
@@map("
|
|
914
|
+
@@map("contract_events")
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
// =============================================================================
|
|
918
|
+
// EVENT OUTBOX - For guaranteed event delivery to SQS queues
|
|
919
|
+
// =============================================================================
|
|
920
|
+
|
|
921
|
+
model DomainEvent {
|
|
922
|
+
id String @id @default(cuid())
|
|
923
|
+
|
|
924
|
+
// Event identification
|
|
925
|
+
eventType String // MORTGAGE.CREATED, PHASE.ACTIVATED, PAYMENT.COMPLETED, etc
|
|
926
|
+
aggregateType String // Mortgage, MortgagePhase, MortgagePayment, Property, etc
|
|
927
|
+
aggregateId String
|
|
928
|
+
|
|
929
|
+
// Routing - which queue(s) should receive this
|
|
930
|
+
queueName String // notifications, payments, mortgage-steps, accounting, etc
|
|
931
|
+
|
|
932
|
+
// Event payload (all data needed by consumers)
|
|
933
|
+
payload String @db.Text // JSON
|
|
934
|
+
|
|
935
|
+
// Metadata
|
|
936
|
+
occurredAt DateTime @default(now())
|
|
937
|
+
actorId String? // User who triggered the event
|
|
938
|
+
actorRole String? // Role of the actor
|
|
939
|
+
|
|
940
|
+
// Processing status
|
|
941
|
+
status String @default("PENDING") // PENDING, PROCESSING, SENT, FAILED
|
|
942
|
+
processedAt DateTime?
|
|
943
|
+
sentAt DateTime?
|
|
944
|
+
failureCount Int @default(0)
|
|
945
|
+
lastError String? @db.Text
|
|
946
|
+
nextRetryAt DateTime?
|
|
947
|
+
|
|
948
|
+
createdAt DateTime @default(now())
|
|
949
|
+
updatedAt DateTime @updatedAt
|
|
950
|
+
|
|
951
|
+
@@index([status, nextRetryAt])
|
|
952
|
+
@@index([eventType])
|
|
953
|
+
@@index([aggregateType, aggregateId])
|
|
954
|
+
@@index([queueName])
|
|
955
|
+
@@index([occurredAt])
|
|
956
|
+
@@map("domain_events")
|
|
663
957
|
}
|