@valentine-efagene/qshelter-common 2.0.142 → 2.0.144

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 (37) hide show
  1. package/dist/generated/client/browser.d.ts +32 -0
  2. package/dist/generated/client/client.d.ts +32 -0
  3. package/dist/generated/client/commonInputTypes.d.ts +354 -144
  4. package/dist/generated/client/enums.d.ts +45 -0
  5. package/dist/generated/client/enums.js +39 -0
  6. package/dist/generated/client/internal/class.d.ts +66 -0
  7. package/dist/generated/client/internal/class.js +2 -2
  8. package/dist/generated/client/internal/prismaNamespace.d.ts +633 -3
  9. package/dist/generated/client/internal/prismaNamespace.js +193 -1
  10. package/dist/generated/client/internal/prismaNamespaceBrowser.d.ts +204 -0
  11. package/dist/generated/client/internal/prismaNamespaceBrowser.js +193 -1
  12. package/dist/generated/client/models/Application.d.ts +669 -1
  13. package/dist/generated/client/models/ApplicationDocument.d.ts +577 -1
  14. package/dist/generated/client/models/ApplicationOrganization.d.ts +2385 -0
  15. package/dist/generated/client/models/ApplicationOrganization.js +1 -0
  16. package/dist/generated/client/models/BankDocumentRequirement.d.ts +1932 -0
  17. package/dist/generated/client/models/BankDocumentRequirement.js +1 -0
  18. package/dist/generated/client/models/DocumentExpiryWarning.d.ts +1141 -0
  19. package/dist/generated/client/models/DocumentExpiryWarning.js +1 -0
  20. package/dist/generated/client/models/Organization.d.ts +390 -0
  21. package/dist/generated/client/models/PropertyMedia.d.ts +0 -7
  22. package/dist/generated/client/models/PropertyPaymentMethod.d.ts +192 -3
  23. package/dist/generated/client/models/PropertyVariant.d.ts +0 -7
  24. package/dist/generated/client/models/ScheduledJob.d.ts +1317 -0
  25. package/dist/generated/client/models/ScheduledJob.js +1 -0
  26. package/dist/generated/client/models/StateTransitionDefinition.d.ts +1104 -0
  27. package/dist/generated/client/models/StateTransitionDefinition.js +1 -0
  28. package/dist/generated/client/models/StateTransitionLog.d.ts +1383 -0
  29. package/dist/generated/client/models/StateTransitionLog.js +1 -0
  30. package/dist/generated/client/models/Tenant.d.ts +2535 -798
  31. package/dist/generated/client/models/index.d.ts +6 -0
  32. package/dist/generated/client/models/index.js +6 -0
  33. package/dist/generated/client/models.d.ts +6 -0
  34. package/dist/src/prisma/tenant.js +4 -0
  35. package/package.json +1 -1
  36. package/prisma/migrations/20260120024650_add_questionnaire_phase_review/migration.sql +102 -0
  37. package/prisma/schema.prisma +423 -2
@@ -679,6 +679,12 @@ model Organization {
679
679
  tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
680
680
  members OrganizationMember[]
681
681
 
682
+ // Applications where this organization is involved
683
+ applicationAssignments ApplicationOrganization[]
684
+
685
+ // Bank-specific document requirements (for BANK type organizations)
686
+ documentRequirements BankDocumentRequirement[]
687
+
682
688
  // Properties developed by this organization (for DEVELOPERs)
683
689
  // developedProperties Property[] @relation("PropertyDeveloper")
684
690
 
@@ -728,6 +734,83 @@ model OrganizationMember {
728
734
  @@map("organization_members")
729
735
  }
730
736
 
737
+ // =============================================================================
738
+ // BANK DOCUMENT REQUIREMENTS - Bank-specific document overlays
739
+ // =============================================================================
740
+ // Banks have different risk appetites and compliance requirements.
741
+ // For a standardized product (e.g., NHF Mortgage), the payment structure is fixed
742
+ // but document requirements vary by bank.
743
+ //
744
+ // This model defines OVERLAY rules:
745
+ // - Base documents come from DocumentationPlan
746
+ // - Bank adds/modifies requirements via BankDocumentRequirement
747
+ //
748
+ // When switching banks mid-application:
749
+ // 1. Keep all existing approved documents
750
+ // 2. Query new bank's requirements
751
+ // 3. Calculate delta (what's missing)
752
+ // 4. Customer uploads only the delta
753
+ // =============================================================================
754
+
755
+ /// How the bank's requirement modifies the base requirement
756
+ enum BankDocumentModifier {
757
+ REQUIRED // Bank requires this document (add if not in base)
758
+ OPTIONAL // Bank makes this optional (override base if required)
759
+ NOT_REQUIRED // Bank doesn't need this (skip even if in base)
760
+ STRICTER // Bank has stricter version (e.g., 12 months instead of 6)
761
+ }
762
+
763
+ /// Bank Document Requirement - Bank-specific document rules
764
+ model BankDocumentRequirement {
765
+ id String @id @default(cuid())
766
+ tenantId String
767
+ tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
768
+
769
+ // Which bank this applies to
770
+ organizationId String
771
+ organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
772
+
773
+ // Which phase type this applies to (e.g., KYC, VERIFICATION)
774
+ phaseType String
775
+
776
+ // Optional: specific payment method (NULL = applies to all)
777
+ paymentMethodId String?
778
+ paymentMethod PropertyPaymentMethod? @relation(fields: [paymentMethodId], references: [id])
779
+
780
+ // Document identification
781
+ documentType String // ID_CARD, BANK_STATEMENT, EMPLOYMENT_LETTER, etc.
782
+ documentName String // Human-readable override (e.g., "12 Months Bank Statement")
783
+
784
+ // How this modifies the base requirement
785
+ modifier BankDocumentModifier @default(REQUIRED)
786
+
787
+ // Bank-specific rules
788
+ description String? @db.Text // Bank's specific instructions
789
+ expiryDays Int? // Bank's validity period (may be stricter than base)
790
+ minFiles Int? // Bank may require more files
791
+ maxFiles Int?
792
+ allowedMimeTypes String? // Bank may restrict file types
793
+
794
+ // Bank-specific validation (JSON schema)
795
+ // Example: { "bankStatementMonths": 12, "minimumBalance": 500000 }
796
+ validationRules Json?
797
+
798
+ // Priority for conflict resolution (higher = takes precedence)
799
+ priority Int @default(100)
800
+
801
+ isActive Boolean @default(true)
802
+ createdAt DateTime @default(now())
803
+ updatedAt DateTime @updatedAt
804
+
805
+ @@unique([organizationId, phaseType, documentType, paymentMethodId])
806
+ @@index([tenantId])
807
+ @@index([organizationId])
808
+ @@index([phaseType])
809
+ @@index([documentType])
810
+ @@index([paymentMethodId])
811
+ @@map("bank_document_requirements")
812
+ }
813
+
731
814
  model Tenant {
732
815
  id String @id @default(cuid())
733
816
  name String
@@ -815,9 +898,18 @@ model Tenant {
815
898
  questionnairePhaseReviews QuestionnairePhaseReview[]
816
899
  documentReviews DocumentReview[]
817
900
 
901
+ // Bank-specific document requirements
902
+ bankDocumentRequirements BankDocumentRequirement[]
903
+
818
904
  // Organizations (Banks, Developers) operating on this tenant
819
905
  organizations Organization[]
820
906
 
907
+ // Application organization assignments
908
+ applicationOrganizations ApplicationOrganization[]
909
+
910
+ // State machine audit
911
+ stateTransitionLogs StateTransitionLog[]
912
+
821
913
  @@index([subdomain])
822
914
  @@map("tenants")
823
915
  }
@@ -1570,6 +1662,9 @@ model PropertyPaymentMethod {
1570
1662
  changeRulesFrom DocumentRequirementRule[] @relation("RuleFromMethod")
1571
1663
  changeRulesTo DocumentRequirementRule[] @relation("RuleToMethod")
1572
1664
 
1665
+ // Bank-specific document requirements for this payment method
1666
+ bankDocumentRequirements BankDocumentRequirement[]
1667
+
1573
1668
  @@unique([tenantId, name]) // Unique per tenant
1574
1669
  @@index([tenantId])
1575
1670
  @@map("property_payment_methods")
@@ -1914,6 +2009,17 @@ model Application {
1914
2009
  paymentMethodId String? // PropertyPaymentMethod used to create this contract
1915
2010
  paymentMethod PropertyPaymentMethod? @relation(fields: [paymentMethodId], references: [id])
1916
2011
 
2012
+ // =========================================================================
2013
+ // PAYMENT METHOD VERSION SNAPSHOT
2014
+ // =========================================================================
2015
+ // Captures the payment method configuration at application creation time.
2016
+ // This ensures existing applications continue with original terms even if
2017
+ // the payment method template is later modified.
2018
+ // =========================================================================
2019
+ paymentMethodSnapshot Json? // Full snapshot of PropertyPaymentMethod + phases at creation
2020
+ paymentMethodSnapshotAt DateTime? // When the snapshot was taken
2021
+ paymentMethodSnapshotHash String? // Hash to detect if template changed since snapshot
2022
+
1917
2023
  // Contract identification
1918
2024
  applicationNumber String @unique
1919
2025
  title String
@@ -1971,6 +2077,9 @@ model Application {
1971
2077
  // Refund requests
1972
2078
  refunds ApplicationRefund[]
1973
2079
 
2080
+ // Organization assignments - which orgs are involved in this application
2081
+ organizations ApplicationOrganization[]
2082
+
1974
2083
  @@index([tenantId])
1975
2084
  @@index([propertyUnitId])
1976
2085
  @@index([buyerId])
@@ -1982,6 +2091,104 @@ model Application {
1982
2091
  @@map("applications")
1983
2092
  }
1984
2093
 
2094
+ // =============================================================================
2095
+ // APPLICATION ORGANIZATION - Binds organizations to specific applications
2096
+ // =============================================================================
2097
+ // Tracks which organizations (banks, developers, legal firms) are involved
2098
+ // in a specific application. This enables:
2099
+ // 1. Validation that only assigned developers can upload sales offers
2100
+ // 2. Only assigned banks can upload preapproval/mortgage offer letters
2101
+ // 3. Multi-bank application support (future: apply to multiple banks)
2102
+ // =============================================================================
2103
+
2104
+ /// Role that an organization plays in an application
2105
+ enum ApplicationOrganizationRole {
2106
+ DEVELOPER // Property developer - uploads sales offer letters
2107
+ LENDER // Bank/financial institution - provides mortgage
2108
+ LEGAL // Legal firm - handles conveyancing
2109
+ INSURER // Insurance company
2110
+ GOVERNMENT // Government agency (land registry, etc.)
2111
+ }
2112
+
2113
+ /// Status of organization's involvement in the application
2114
+ enum ApplicationOrganizationStatus {
2115
+ PENDING // Awaiting organization's response/engagement
2116
+ ACTIVE // Organization is actively participating
2117
+ COMPLETED // Organization's role is complete
2118
+ DECLINED // Organization declined to participate
2119
+ WITHDRAWN // Organization withdrew from the application
2120
+ }
2121
+
2122
+ model ApplicationOrganization {
2123
+ id String @id @default(cuid())
2124
+ tenantId String
2125
+ tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
2126
+ applicationId String
2127
+ application Application @relation(fields: [applicationId], references: [id], onDelete: Cascade)
2128
+
2129
+ // Which organization is involved
2130
+ organizationId String
2131
+ organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
2132
+
2133
+ // What role does this organization play
2134
+ role ApplicationOrganizationRole
2135
+ status ApplicationOrganizationStatus @default(PENDING)
2136
+
2137
+ // Who assigned this organization (admin or system)
2138
+ assignedById String?
2139
+ assignedAt DateTime @default(now())
2140
+
2141
+ // For multi-bank applications: track if this is the selected lender
2142
+ // Multiple banks can be PENDING, but only one should be ACTIVE for LENDER role
2143
+ isPrimary Boolean @default(false)
2144
+
2145
+ // Organization-specific terms (e.g., bank's offered interest rate)
2146
+ offeredTerms Json? // { interestRate, termMonths, conditions, etc. }
2147
+ termsOfferedAt DateTime?
2148
+ termsAcceptedAt DateTime?
2149
+ termsDeclinedAt DateTime?
2150
+ declineReason String? @db.Text
2151
+
2152
+ // Tracking
2153
+ activatedAt DateTime?
2154
+ completedAt DateTime?
2155
+ withdrawnAt DateTime?
2156
+
2157
+ // =========================================================================
2158
+ // SLA TRACKING - Monitor bank responsiveness
2159
+ // =========================================================================
2160
+ // When documentation is complete, clock starts for bank to respond.
2161
+ // If they're slow, admin can reassign to another bank.
2162
+ // =========================================================================
2163
+ slaHours Int? // Expected response time (e.g., 48 hours)
2164
+ slaStartedAt DateTime? // When clock started (e.g., when docs were complete)
2165
+ slaBreachedAt DateTime? // When SLA was breached (set by cron job)
2166
+ slaBreachNotified Boolean @default(false) // Was admin notified of breach?
2167
+
2168
+ // Reminder tracking
2169
+ reminderCount Int @default(0)
2170
+ lastReminderSentAt DateTime?
2171
+ nextReminderAt DateTime?
2172
+
2173
+ // Escalation
2174
+ escalatedAt DateTime?
2175
+ escalatedToUserId String? // Admin handling the escalation
2176
+ escalationNotes String? @db.Text
2177
+
2178
+ createdAt DateTime @default(now())
2179
+ updatedAt DateTime @updatedAt
2180
+
2181
+ @@unique([applicationId, organizationId, role]) // One org per role per application
2182
+ @@index([tenantId])
2183
+ @@index([applicationId])
2184
+ @@index([organizationId])
2185
+ @@index([role])
2186
+ @@index([status])
2187
+ @@index([isPrimary])
2188
+ @@index([slaBreachedAt])
2189
+ @@map("application_organizations")
2190
+ }
2191
+
1985
2192
  // =============================================================================
1986
2193
  // CONTRACT REFUNDS - Track refund requests for overpayments or cancellations
1987
2194
  // =============================================================================
@@ -2182,8 +2389,8 @@ model QuestionnairePhaseReview {
2182
2389
  notes String? @db.Text
2183
2390
 
2184
2391
  // Snapshot of scores at time of review (for audit)
2185
- scoreAtReview Int? // The totalScore when this review was made
2186
- passedAtReview Boolean? // The passed flag when this review was made
2392
+ scoreAtReview Int? // The totalScore when this review was made
2393
+ passedAtReview Boolean? // The passed flag when this review was made
2187
2394
 
2188
2395
  createdAt DateTime @default(now())
2189
2396
 
@@ -2478,6 +2685,29 @@ model ApplicationDocument {
2478
2685
  uploadedById String?
2479
2686
  uploadedBy User? @relation("DocumentUploader", fields: [uploadedById], references: [id])
2480
2687
 
2688
+ // =========================================================================
2689
+ // UPLOADER VALIDATION - Enforces who can upload this document
2690
+ // =========================================================================
2691
+ // expectedUploader is set from DocumentDefinition.uploadedBy at phase creation
2692
+ // Allows validation that correct party is uploading (e.g., developer uploads sales offer)
2693
+ expectedUploader UploadedBy? // CUSTOMER, LENDER, DEVELOPER, LEGAL, PLATFORM, etc.
2694
+ // If expectedUploader is LENDER/DEVELOPER, track which org should upload
2695
+ expectedOrganizationId String?
2696
+
2697
+ // =========================================================================
2698
+ // DOCUMENT EXPIRY - Time-sensitive document tracking
2699
+ // =========================================================================
2700
+ // Documents like bank statements have a shelf life. If application takes too long,
2701
+ // documents may need to be re-uploaded.
2702
+ // =========================================================================
2703
+ documentDate DateTime? // When the document was issued/created (e.g., bank statement date)
2704
+ expiresAt DateTime? // When this document expires (computed from documentDate + expiryDays)
2705
+ expiryDays Int? // Number of days this document type is valid (copied from DocumentDefinition)
2706
+ isExpired Boolean @default(false) // Denormalized flag, updated by cron job
2707
+ expiredAt DateTime? // When the document was marked expired
2708
+ expiryWarningAt DateTime? // When expiry warning was sent
2709
+ revalidatedAt DateTime? // If document was re-uploaded after expiry
2710
+
2481
2711
  status DocumentStatus @default(PENDING)
2482
2712
 
2483
2713
  // NO versioning - document is replaced on re-upload (url is updated)
@@ -2504,6 +2734,8 @@ model ApplicationDocument {
2504
2734
  @@index([documentType])
2505
2735
  @@index([status])
2506
2736
  @@index([replacesDocumentId])
2737
+ @@index([isExpired])
2738
+ @@index([expiresAt])
2507
2739
  @@map("application_documents")
2508
2740
  }
2509
2741
 
@@ -3504,3 +3736,192 @@ model WorkflowBlocker {
3504
3736
  @@index([tenantId, isOverdue, blockerActor]) // For SLA violation queries
3505
3737
  @@map("workflow_blockers")
3506
3738
  }
3739
+
3740
+ // =============================================================================
3741
+ // STATE MACHINE DEFINITION - Explicit state transitions
3742
+ // =============================================================================
3743
+ // Defines valid state transitions for applications and phases.
3744
+ // This makes the state machine configurable and auditable, rather than
3745
+ // being hard-coded in service logic.
3746
+ // =============================================================================
3747
+
3748
+ /// Entity type that has state machine behavior
3749
+ enum StateMachineEntity {
3750
+ APPLICATION
3751
+ APPLICATION_PHASE
3752
+ DOCUMENTATION_STAGE
3753
+ DOCUMENT
3754
+ }
3755
+
3756
+ /// State Machine Transition Definition - Configurable state transitions
3757
+ /// Allows admins to customize workflows without code changes
3758
+ model StateTransitionDefinition {
3759
+ id String @id @default(cuid())
3760
+ tenantId String? // NULL = global template, set = tenant-specific override
3761
+
3762
+ // What entity this transition applies to
3763
+ entityType StateMachineEntity
3764
+
3765
+ // The transition
3766
+ fromState String // e.g., "DRAFT", "PENDING", "IN_PROGRESS"
3767
+ toState String // e.g., "ACTIVE", "COMPLETED"
3768
+ trigger String // e.g., "SUBMIT", "APPROVE", "COMPLETE"
3769
+
3770
+ // Transition constraints
3771
+ isEnabled Boolean @default(true)
3772
+ requiresRole String? // Role required to trigger this transition
3773
+ requiresPhase String? // Phase type required (for APPLICATION transitions)
3774
+
3775
+ // Side effects configuration
3776
+ sideEffects Json? // [{ type: "SEND_EMAIL", config: {...} }, { type: "LOCK_UNIT" }]
3777
+
3778
+ // Audit
3779
+ description String? @db.Text
3780
+ createdAt DateTime @default(now())
3781
+ updatedAt DateTime @updatedAt
3782
+
3783
+ @@unique([tenantId, entityType, fromState, trigger])
3784
+ @@index([entityType])
3785
+ @@index([fromState])
3786
+ @@index([trigger])
3787
+ @@map("state_transition_definitions")
3788
+ }
3789
+
3790
+ /// State Transition Log - Audit trail for all state changes
3791
+ /// Records every state transition for compliance and debugging
3792
+ model StateTransitionLog {
3793
+ id String @id @default(cuid())
3794
+ tenantId String
3795
+ tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
3796
+
3797
+ // What entity changed state
3798
+ entityType StateMachineEntity
3799
+ entityId String // Application ID, Phase ID, Document ID, etc.
3800
+
3801
+ // The transition that occurred
3802
+ fromState String
3803
+ toState String
3804
+ trigger String
3805
+
3806
+ // Who triggered the transition
3807
+ actorId String?
3808
+ actorType EventActorType?
3809
+
3810
+ // Context at time of transition
3811
+ contextSnapshot Json? // Relevant data at time of transition (phase data, payment amounts, etc.)
3812
+
3813
+ // Timing
3814
+ occurredAt DateTime @default(now())
3815
+ durationMs Int? // How long the entity was in fromState
3816
+
3817
+ // Validation
3818
+ wasValid Boolean @default(true) // Was this a valid transition per state machine?
3819
+ errorMessage String? @db.Text // If invalid, why?
3820
+
3821
+ @@index([tenantId])
3822
+ @@index([entityType, entityId])
3823
+ @@index([entityType, fromState])
3824
+ @@index([entityType, toState])
3825
+ @@index([occurredAt])
3826
+ @@index([actorId])
3827
+ @@map("state_transition_logs")
3828
+ }
3829
+
3830
+ // =============================================================================
3831
+ // DOCUMENT EXPIRY JOB - Scheduled job tracking for document expiration checks
3832
+ // =============================================================================
3833
+ // Tracks scheduled jobs for checking document expiration across applications.
3834
+ // This supports a cron-based approach where a Lambda runs periodically to:
3835
+ // 1. Find documents nearing expiry (warning)
3836
+ // 2. Mark expired documents
3837
+ // 3. Notify customers to re-upload
3838
+ // 4. Optionally block phase progression if critical documents expired
3839
+ // =============================================================================
3840
+
3841
+ /// Job status for scheduled background jobs
3842
+ enum ScheduledJobStatus {
3843
+ PENDING
3844
+ RUNNING
3845
+ COMPLETED
3846
+ FAILED
3847
+ CANCELLED
3848
+ }
3849
+
3850
+ /// Type of scheduled job
3851
+ enum ScheduledJobType {
3852
+ DOCUMENT_EXPIRY_CHECK // Check for expired documents
3853
+ SLA_BREACH_CHECK // Check for SLA breaches
3854
+ PAYMENT_REMINDER // Send payment reminders
3855
+ DOCUMENT_EXPIRY_WARNING // Warn about documents nearing expiry
3856
+ }
3857
+
3858
+ /// Scheduled Job - Tracks execution of background jobs
3859
+ model ScheduledJob {
3860
+ id String @id @default(cuid())
3861
+ tenantId String? // NULL = system-wide job, set = tenant-specific
3862
+
3863
+ jobType ScheduledJobType
3864
+ status ScheduledJobStatus @default(PENDING)
3865
+
3866
+ // Scheduling
3867
+ scheduledAt DateTime // When the job should run
3868
+ startedAt DateTime? // When job started executing
3869
+ completedAt DateTime? // When job finished
3870
+ durationMs Int? // Execution time
3871
+
3872
+ // Job parameters
3873
+ parameters Json? // Job-specific configuration
3874
+
3875
+ // Results
3876
+ itemsProcessed Int @default(0)
3877
+ itemsAffected Int @default(0) // Documents expired, SLAs breached, etc.
3878
+ errorCount Int @default(0)
3879
+ errors Json? // Array of error details
3880
+ summary String? @db.Text // Human-readable summary
3881
+
3882
+ // Retry handling
3883
+ attemptNumber Int @default(1)
3884
+ maxAttempts Int @default(3)
3885
+ nextRetryAt DateTime?
3886
+
3887
+ createdAt DateTime @default(now())
3888
+ updatedAt DateTime @updatedAt
3889
+
3890
+ @@index([tenantId])
3891
+ @@index([jobType])
3892
+ @@index([status])
3893
+ @@index([scheduledAt])
3894
+ @@index([jobType, status, scheduledAt])
3895
+ @@map("scheduled_jobs")
3896
+ }
3897
+
3898
+ /// Document Expiry Warning - Track warnings sent for expiring documents
3899
+ model DocumentExpiryWarning {
3900
+ id String @id @default(cuid())
3901
+ tenantId String
3902
+ documentId String
3903
+
3904
+ // Warning details
3905
+ expiresAt DateTime // When document will expire
3906
+ daysUntil Int // Days until expiry at time of warning
3907
+ warningSent DateTime @default(now())
3908
+
3909
+ // Customer notification tracking
3910
+ notificationSent Boolean @default(false)
3911
+ notificationId String? // Reference to notification service
3912
+
3913
+ // Resolution tracking
3914
+ resolved Boolean @default(false)
3915
+ resolvedAt DateTime?
3916
+ resolvedBy String? // User who resolved (re-uploaded document)
3917
+ newDocumentId String? // ID of the replacement document
3918
+
3919
+ createdAt DateTime @default(now())
3920
+
3921
+ @@unique([documentId, daysUntil]) // One warning per document per threshold
3922
+ @@index([tenantId])
3923
+ @@index([documentId])
3924
+ @@index([expiresAt])
3925
+ @@index([resolved])
3926
+ @@map("document_expiry_warnings")
3927
+ }