@valentine-efagene/qshelter-common 2.0.44 → 2.0.46
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/internal/class.js +1 -1
- package/dist/generated/client/models/DocumentTemplate.d.ts +6 -4
- package/dist/generated/client/models/OfferLetter.d.ts +62 -44
- package/dist/src/prisma/tenant.js +21 -2
- package/package.json +1 -1
- package/prisma/migrations/20260104174626_make_offer_letter_template_optional/migration.sql +11 -0
- package/prisma/schema.prisma +3 -3
|
@@ -15,7 +15,7 @@ const config = {
|
|
|
15
15
|
"clientVersion": "7.2.0",
|
|
16
16
|
"engineVersion": "0c8ef2ce45c83248ab3df073180d5eda9e8be7a3",
|
|
17
17
|
"activeProvider": "mysql",
|
|
18
|
-
"inlineSchema": "// =============================================================================\n// QSHELTER UNIFIED DATABASE SCHEMA\n// =============================================================================\n// This schema contains all database models for the QShelter platform\n// Organized by domain for better readability\n// =============================================================================\n\ngenerator client {\n provider = \"prisma-client\"\n output = \"../generated/client\"\n engineType = \"client\"\n}\n\ndatasource db {\n provider = \"mysql\"\n}\n\n// =============================================================================\n// ENUMS - Database-enforced value constraints\n// =============================================================================\n\nenum PhaseCategory {\n DOCUMENTATION\n PAYMENT\n}\n\nenum PhaseType {\n KYC\n VERIFICATION\n DOWNPAYMENT\n MORTGAGE\n BALLOON\n CUSTOM\n}\n\nenum PaymentFrequency {\n MONTHLY\n BIWEEKLY\n WEEKLY\n ONE_TIME\n CUSTOM\n}\n\nenum ContractStatus {\n DRAFT\n PENDING\n ACTIVE\n COMPLETED\n CANCELLED\n TERMINATED\n}\n\nenum PhaseStatus {\n PENDING\n IN_PROGRESS\n AWAITING_APPROVAL\n ACTIVE\n COMPLETED\n SKIPPED\n FAILED\n}\n\nenum StepType {\n UPLOAD\n REVIEW\n SIGNATURE\n APPROVAL\n EXTERNAL_CHECK\n WAIT\n GENERATE_DOCUMENT // Triggers document generation (offer letters, contracts, etc.)\n}\n\nenum StepStatus {\n PENDING\n IN_PROGRESS\n COMPLETED\n FAILED\n SKIPPED\n}\n\nenum InstallmentStatus {\n PENDING\n PAID\n OVERDUE\n PARTIALLY_PAID\n WAIVED\n}\n\nenum PaymentStatus {\n INITIATED\n PENDING\n COMPLETED\n FAILED\n REFUNDED\n}\n\nenum ApprovalDecision {\n APPROVED\n REJECTED\n REQUEST_CHANGES\n}\n\n// =============================================================================\n// CONTRACT TERMINATION / CANCELLATION ENUMS\n// =============================================================================\n\nenum TerminationType {\n BUYER_WITHDRAWAL // Buyer wants to cancel (voluntary)\n SELLER_WITHDRAWAL // Seller/developer cancels\n MUTUAL_AGREEMENT // Both parties agree to terminate\n PAYMENT_DEFAULT // Buyer failed payment obligations\n DOCUMENT_FAILURE // Buyer failed to provide required documents\n FRAUD // Fraudulent activity detected\n FORCE_MAJEURE // External circumstances (disaster, etc.)\n PROPERTY_UNAVAILABLE // Property no longer available\n REGULATORY // Regulatory/legal requirement\n OTHER // Other reasons (with notes)\n}\n\nenum TerminationStatus {\n REQUESTED // Initial request submitted\n PENDING_REVIEW // Awaiting admin review\n PENDING_REFUND // Approved, awaiting refund processing\n REFUND_IN_PROGRESS // Refund being processed\n REFUND_COMPLETED // Refund completed\n COMPLETED // Termination fully executed (no refund or refund done)\n REJECTED // Termination request rejected\n CANCELLED // Termination request was cancelled\n}\n\nenum RefundStatus {\n NOT_APPLICABLE // No refund needed (no payments made)\n PENDING // Refund not yet initiated\n INITIATED // Refund request sent to payment gateway\n PROCESSING // Gateway processing refund\n PARTIAL_COMPLETED // Some refund completed (penalties deducted)\n COMPLETED // Full refund completed\n FAILED // Refund failed (needs manual intervention)\n}\n\nenum TerminationInitiator {\n BUYER\n SELLER\n ADMIN\n SYSTEM\n}\n\nenum CompletionCriterion {\n DOCUMENT_APPROVALS\n PAYMENT_AMOUNT\n STEPS_COMPLETED\n}\n\nenum DocumentStatus {\n DRAFT\n PENDING\n PENDING_SIGNATURE\n SENT\n VIEWED\n SIGNED\n APPROVED\n REJECTED\n EXPIRED\n CANCELLED\n}\n\nenum OfferLetterType {\n PROVISIONAL\n FINAL\n}\n\nenum OfferLetterStatus {\n DRAFT\n GENERATED\n SENT\n VIEWED\n SIGNED\n EXPIRED\n CANCELLED\n}\n\nenum UnderwritingDecisionKind {\n APPROVE\n REJECT\n CONDITIONAL\n}\n\n// =============================================================================\n// USER & AUTH DOMAIN\n// =============================================================================\n\nmodel User {\n id String @id @default(cuid())\n email String @unique\n password String?\n phone String? @unique\n firstName String?\n lastName String?\n isActive Boolean @default(true)\n isEmailVerified Boolean @default(false)\n googleId String?\n avatar String?\n tenantId String?\n tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: SetNull)\n // Support multiple roles via explicit join table `UserRole`\n userRoles UserRole[]\n walletId String? @unique\n wallet Wallet? @relation(fields: [walletId], references: [id])\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n emailVerifiedAt DateTime?\n emailVerificationToken String?\n lastLoginAt DateTime?\n refreshTokens RefreshToken[]\n passwordResets PasswordReset[]\n suspensions UserSuspension[]\n emailPreferences EmailPreference[]\n deviceEndpoints DeviceEndpoint[]\n socials Social[]\n\n // Relations to other domains\n properties Property[]\n contracts Contract[] @relation(\"ContractBuyer\")\n soldContracts Contract[] @relation(\"ContractSeller\")\n contractPayments ContractPayment[] @relation(\"ContractPayer\")\n\n // Phase step assignments and approvals\n assignedSteps ContractPhaseStep[] @relation(\"PhaseStepAssignee\")\n stepApprovals ContractPhaseStepApproval[] @relation(\"PhaseStepApprover\")\n uploadedDocs ContractDocument[] @relation(\"DocumentUploader\")\n\n // Prequalification and payment method changes\n prequalifications Prequalification[]\n paymentMethodChangeRequests PaymentMethodChangeRequest[] @relation(\"ChangeRequestor\")\n reviewedChangeRequests PaymentMethodChangeRequest[] @relation(\"ChangeReviewer\")\n\n // Contract terminations\n initiatedTerminations ContractTermination[] @relation(\"TerminationInitiator\")\n reviewedTerminations ContractTermination[] @relation(\"TerminationReviewer\")\n\n // Offer letters\n offerLettersGenerated OfferLetter[] @relation(\"OfferLetterGenerator\")\n offerLettersSent OfferLetter[] @relation(\"OfferLetterSender\")\n\n @@index([email])\n @@index([tenantId])\n @@map(\"users\")\n}\n\nmodel Role {\n id String @id @default(cuid())\n name String @unique\n description String?\n userRoles UserRole[]\n permissions RolePermission[]\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@map(\"roles\")\n}\n\nmodel Permission {\n id String @id @default(cuid())\n name String @unique\n description String?\n resource String\n action String\n roles RolePermission[]\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@unique([resource, action])\n @@index([resource])\n @@map(\"permissions\")\n}\n\nmodel RolePermission {\n roleId String\n permissionId String\n role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)\n permission Permission @relation(fields: [permissionId], references: [id], onDelete: Cascade)\n createdAt DateTime @default(now())\n\n @@id([roleId, permissionId])\n @@map(\"role_permissions\")\n}\n\nmodel UserRole {\n userId String\n roleId String\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)\n createdAt DateTime @default(now())\n\n @@id([userId, roleId])\n @@map(\"user_roles\")\n}\n\nmodel Tenant {\n id String @id @default(cuid())\n name String\n subdomain String @unique\n isActive Boolean @default(true)\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // Back-relations for multitenancy\n users User[]\n properties Property[]\n paymentPlans PaymentPlan[]\n paymentMethods PropertyPaymentMethod[]\n contracts Contract[]\n\n // Prequalification and payment method changes\n prequalifications Prequalification[]\n paymentMethodChangeRequests PaymentMethodChangeRequest[]\n documentRequirementRules DocumentRequirementRule[]\n\n // Contract terminations\n contractTerminations ContractTermination[]\n\n // Offer letters and templates\n documentTemplates DocumentTemplate[]\n offerLetters OfferLetter[]\n\n // Underwriting\n underwritingDecisions UnderwritingDecision[]\n\n @@index([subdomain])\n @@map(\"tenants\")\n}\n\nmodel RefreshToken {\n id String @id @default(cuid())\n // Use the JWT `jti` for indexed lookups and keep the raw JWT (optional)\n jti String? @unique @db.VarChar(255)\n token String? @db.LongText\n userId String\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n expiresAt DateTime\n createdAt DateTime @default(now())\n\n @@index([userId])\n @@index([expiresAt])\n @@map(\"refresh_tokens\")\n}\n\nmodel PasswordReset {\n id String @id @default(cuid())\n token String @unique\n userId String\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n expiresAt DateTime\n usedAt DateTime?\n createdAt DateTime @default(now())\n\n @@index([userId])\n @@index([expiresAt])\n @@map(\"password_resets\")\n}\n\nmodel UserSuspension {\n id String @id @default(cuid())\n userId String\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n reason String\n suspendedAt DateTime @default(now())\n expiresAt DateTime?\n liftedAt DateTime?\n\n @@index([userId])\n @@map(\"user_suspensions\")\n}\n\nmodel EmailPreference {\n id String @id @default(cuid())\n userId String\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n marketingEmails Boolean @default(true)\n transactionalEmails Boolean @default(true)\n propertyAlerts Boolean @default(true)\n paymentReminders Boolean @default(true)\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([userId])\n @@map(\"email_preferences\")\n}\n\nmodel DeviceEndpoint {\n id String @id @default(cuid())\n userId String\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n endpoint String // Push notification endpoint\n platform String // ios, android, web\n isActive Boolean @default(true)\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([userId])\n @@map(\"device_endpoints\")\n}\n\nmodel Social {\n id String @id @default(cuid())\n userId String\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n provider String // google, facebook, twitter, etc\n socialId String // ID from the social provider\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@unique([provider, socialId])\n @@index([userId])\n @@map(\"socials\")\n}\n\nmodel OAuthState {\n id String @id @default(cuid())\n state String @unique\n expiresAt DateTime\n createdAt DateTime @default(now())\n\n @@index([state])\n @@index([expiresAt])\n @@map(\"oauth_states\")\n}\n\nmodel Wallet {\n id String @id @default(cuid())\n balance Float @default(0)\n currency String @default(\"USD\")\n user User?\n transactions Transaction[]\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@map(\"wallets\")\n}\n\nmodel Transaction {\n id String @id @default(cuid())\n walletId String\n wallet Wallet @relation(fields: [walletId], references: [id], onDelete: Cascade)\n amount Float\n type String // CREDIT, DEBIT\n status String // PENDING, COMPLETED, FAILED\n reference String?\n description String?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([walletId])\n @@map(\"transactions\")\n}\n\nmodel Settings {\n id String @id @default(cuid())\n key String @unique\n value String @db.Text\n category String?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([category])\n @@map(\"settings\")\n}\n\n// =============================================================================\n// PROPERTY DOMAIN\n// =============================================================================\n// Property = listing/project (e.g., \"Sunrise Estate\")\n// PropertyVariant = configuration with specs & price (e.g., \"3-Bed Corner - Finished\")\n// PropertyUnit = individual sellable unit (e.g., \"Unit A1\")\n// =============================================================================\n\nmodel Property {\n id String @id @default(cuid())\n tenantId String\n tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)\n userId String\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n title String\n category String // SALE, RENT, LEASE\n propertyType String // APARTMENT, HOUSE, LAND, COMMERCIAL, ESTATE, TOWNHOUSE\n country String\n currency String // USD, NGN, etc\n city String\n district String?\n zipCode String?\n streetAddress String?\n longitude Float?\n latitude Float?\n status String @default(\"DRAFT\") // DRAFT, PUBLISHED, SOLD_OUT, ARCHIVED\n description String? @db.Text\n displayImageId String?\n displayImage PropertyMedia? @relation(\"DisplayImage\", fields: [displayImageId], references: [id], onDelete: SetNull)\n isPublished Boolean @default(false)\n publishedAt DateTime?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // Relations\n documents PropertyDocument[]\n media PropertyMedia[] @relation(\"PropertyMedia\")\n amenities PropertyAmenity[] // Shared amenities (gym, pool, security)\n paymentMethods PropertyPaymentMethodLink[]\n variants PropertyVariant[]\n prequalifications Prequalification[]\n\n @@index([tenantId])\n @@index([userId])\n @@index([category])\n @@index([propertyType])\n @@index([city])\n @@index([status])\n @@map(\"properties\")\n}\n\nmodel PropertyMedia {\n id String @id @default(cuid())\n propertyId String\n property Property @relation(\"PropertyMedia\", fields: [propertyId], references: [id], onDelete: Cascade)\n url String\n type String // IMAGE, VIDEO\n caption String?\n order Int @default(0)\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n displayForProperties Property[] @relation(\"DisplayImage\")\n\n @@index([propertyId])\n @@map(\"property_media\")\n}\n\nmodel PropertyDocument {\n id String @id @default(cuid())\n propertyId String\n property Property @relation(fields: [propertyId], references: [id], onDelete: Cascade)\n name String\n url String\n type String // TITLE_DEED, SURVEY_PLAN, etc\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([propertyId])\n @@map(\"property_documents\")\n}\n\nmodel Amenity {\n id String @id @default(cuid())\n name String @unique\n category String? // PROPERTY, VARIANT, BOTH - helps filter which amenities to show\n icon String? // Icon name/URL for UI\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n properties PropertyAmenity[]\n variants PropertyVariantAmenity[]\n\n @@index([category])\n @@map(\"amenities\")\n}\n\n// =============================================================================\n// PROPERTY VARIANT & UNIT MODELS\n// =============================================================================\n\n// PropertyVariant = specific configuration with its own price and amenities\n// e.g., \"3-Bedroom Corner Piece - Fully Finished\", \"2-Bedroom Standard - Carcass\"\nmodel PropertyVariant {\n id String @id @default(cuid())\n propertyId String\n property Property @relation(fields: [propertyId], references: [id], onDelete: Cascade)\n\n name String // \"Corner Piece - Finished\", \"Standard - Carcass\"\n description String? @db.Text\n\n // Specifications\n nBedrooms Int?\n nBathrooms Int?\n nParkingSpots Int?\n area Float? // Square meters/feet\n\n // Pricing\n price Float\n pricePerSqm Float? // Computed or set manually\n\n // Inventory counters (denormalized for performance, updated via triggers/service)\n totalUnits Int @default(1)\n availableUnits Int @default(1)\n reservedUnits Int @default(0)\n soldUnits Int @default(0)\n\n // Status\n status String @default(\"AVAILABLE\") // AVAILABLE, LOW_STOCK, SOLD_OUT, ARCHIVED\n isActive Boolean @default(true)\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // Relations\n amenities PropertyVariantAmenity[]\n units PropertyUnit[]\n media PropertyVariantMedia[]\n\n @@index([propertyId])\n @@index([status])\n @@index([price])\n @@map(\"property_variants\")\n}\n\n// PropertyVariantAmenity = amenities specific to a variant\n// e.g., \"Finished Kitchen\", \"Smart Home System\", \"Private Garden\"\nmodel PropertyVariantAmenity {\n variantId String\n amenityId String\n variant PropertyVariant @relation(fields: [variantId], references: [id], onDelete: Cascade)\n amenity Amenity @relation(fields: [amenityId], references: [id], onDelete: Cascade)\n createdAt DateTime @default(now())\n\n @@id([variantId, amenityId])\n @@map(\"property_variant_amenities\")\n}\n\n// PropertyVariantMedia = images/videos specific to a variant\nmodel PropertyVariantMedia {\n id String @id @default(cuid())\n variantId String\n variant PropertyVariant @relation(fields: [variantId], references: [id], onDelete: Cascade)\n url String\n type String // IMAGE, VIDEO, FLOOR_PLAN, 3D_TOUR\n caption String?\n order Int @default(0)\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([variantId])\n @@map(\"property_variant_media\")\n}\n\n// PropertyUnit = individual sellable/rentable unit within a variant\n// e.g., \"Unit A1\", \"Block B - Flat 3\", \"Plot 15\"\nmodel PropertyUnit {\n id String @id @default(cuid())\n variantId String\n variant PropertyVariant @relation(fields: [variantId], references: [id], onDelete: Cascade)\n\n unitNumber String // \"A1\", \"B-3\", \"Plot 15\"\n floorNumber Int? // For apartments\n blockName String? // \"Block A\", \"Tower 1\"\n\n // Unit-specific overrides (if different from variant)\n priceOverride Float? // If this specific unit has a different price\n areaOverride Float? // If this specific unit has a different area\n notes String? @db.Text // Internal notes about this unit\n\n // Status tracking\n status String @default(\"AVAILABLE\") // AVAILABLE, RESERVED, SOLD, RENTED, UNAVAILABLE\n\n // Reservation/hold\n reservedAt DateTime?\n reservedUntil DateTime?\n reservedById String?\n\n // Ownership tracking (once sold)\n ownerId String?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // Relations\n contracts Contract[]\n\n @@unique([variantId, unitNumber])\n @@index([variantId])\n @@index([status])\n @@map(\"property_units\")\n}\n\nmodel PropertyAmenity {\n propertyId String\n amenityId String\n property Property @relation(fields: [propertyId], references: [id], onDelete: Cascade)\n amenity Amenity @relation(fields: [amenityId], references: [id], onDelete: Cascade)\n createdAt DateTime @default(now())\n\n @@id([propertyId, amenityId])\n @@map(\"property_amenities\")\n}\n\n// =============================================================================\n// PAYMENT PLAN DOMAIN - Reusable installment structure templates\n// =============================================================================\n\n// PaymentPlan = reusable structure for how payments are scheduled\n// Examples: \"Monthly360\" (360 monthly payments), \"Weekly52\", \"OneTime\"\nmodel PaymentPlan {\n id String @id @default(cuid())\n tenantId String? // NULL = global template available to all tenants\n tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade)\n name String\n description String? @db.Text\n isActive Boolean @default(true)\n\n // Structure configuration\n paymentFrequency PaymentFrequency\n customFrequencyDays Int?\n numberOfInstallments Int // 1 for one-time, 360 for 30yr monthly, etc\n calculateInterestDaily Boolean @default(false)\n gracePeriodDays Int @default(0)\n\n // Fund collection behavior\n // true = we collect funds via wallet/gateway (e.g., downpayment)\n // false = external payment, we only track/reconcile (e.g., bank mortgage)\n collectFunds Boolean @default(true)\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // Used by property payment method phases (templates)\n methodPhases PropertyPaymentMethodPhase[]\n // Used by instantiated contract phases\n contractPhases ContractPhase[]\n\n @@unique([tenantId, name]) // Unique per tenant, or globally if tenantId is null\n @@index([tenantId])\n @@map(\"payment_plans\")\n}\n\n// =============================================================================\n// PROPERTY PAYMENT METHOD DOMAIN - Product offerings per property\n// =============================================================================\n\n// PropertyPaymentMethod = how a property can be purchased (e.g., \"Standard Mortgage\", \"Cash\", \"Rent-to-Own\")\nmodel PropertyPaymentMethod {\n id String @id @default(cuid())\n tenantId String\n tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)\n name String // \"Standard Mortgage\", \"Flexible Payment\", \"Cash Purchase\"\n description String? @db.Text\n isActive Boolean @default(true)\n\n // Global method configuration\n allowEarlyPayoff Boolean @default(true)\n earlyPayoffPenaltyRate Float?\n autoActivatePhases Boolean @default(true)\n requiresManualApproval Boolean @default(false)\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // Many-to-many with properties\n properties PropertyPaymentMethodLink[]\n // Phases that make up this method (templates)\n phases PropertyPaymentMethodPhase[]\n // Contracts using this method\n contracts Contract[]\n\n // Prequalifications for this payment method\n prequalifications Prequalification[]\n\n // Payment method change tracking\n changeRequestsFrom PaymentMethodChangeRequest[] @relation(\"ChangeFromMethod\")\n changeRequestsTo PaymentMethodChangeRequest[] @relation(\"ChangeToMethod\")\n\n // Document requirement rules\n documentRules DocumentRequirementRule[] @relation(\"RulePaymentMethod\")\n changeRulesFrom DocumentRequirementRule[] @relation(\"RuleFromMethod\")\n changeRulesTo DocumentRequirementRule[] @relation(\"RuleToMethod\")\n\n @@unique([tenantId, name]) // Unique per tenant\n @@index([tenantId])\n @@map(\"property_payment_methods\")\n}\n\n// Many-to-many link between Property and PaymentMethod\nmodel PropertyPaymentMethodLink {\n propertyId String\n property Property @relation(fields: [propertyId], references: [id], onDelete: Cascade)\n paymentMethodId String\n paymentMethod PropertyPaymentMethod @relation(fields: [paymentMethodId], references: [id], onDelete: Cascade)\n\n // Method-specific overrides for this property\n isDefault Boolean @default(false)\n isActive Boolean @default(true)\n createdAt DateTime @default(now())\n\n @@id([propertyId, paymentMethodId])\n @@map(\"property_payment_method_links\")\n}\n\n// Phase template within a PropertyPaymentMethod (e.g., documentation, downpayment, mortgage)\n// phaseCategory determines the FSM type: DOCUMENTATION or PAYMENT\nmodel PropertyPaymentMethodPhase {\n id String @id @default(cuid())\n paymentMethodId String\n paymentMethod PropertyPaymentMethod @relation(fields: [paymentMethodId], references: [id], onDelete: Cascade)\n paymentPlanId String? // Only for PAYMENT phases\n paymentPlan PaymentPlan? @relation(fields: [paymentPlanId], references: [id])\n\n name String\n description String? @db.Text\n\n // Phase classification (DB-enforced enums)\n phaseCategory PhaseCategory\n phaseType PhaseType\n order Int\n\n // Financial configuration (for PAYMENT phases)\n interestRate Float?\n percentOfPrice Float? // e.g., 10.0 for 10% downpayment\n\n // Fund collection behavior (inherited from PaymentPlan if not set)\n // true = we collect funds via wallet/gateway (e.g., downpayment)\n // false = external payment, we only track/reconcile (e.g., bank mortgage)\n collectFunds Boolean? // null = inherit from PaymentPlan\n\n // Activation rules\n requiresPreviousPhaseCompletion Boolean @default(true)\n minimumCompletionPercentage Float?\n completionCriterion CompletionCriterion?\n\n // Snapshots for audit (original config at creation time)\n stepDefinitionsSnapshot Json?\n requiredDocumentSnapshot Json?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // Normalized child tables (for DOCUMENTATION phases)\n steps PaymentMethodPhaseStep[]\n requiredDocuments PaymentMethodPhaseDocument[]\n\n @@index([paymentMethodId])\n @@index([paymentPlanId])\n @@index([phaseCategory])\n @@map(\"property_payment_method_phases\")\n}\n\n// Step template within a DOCUMENTATION phase\nmodel PaymentMethodPhaseStep {\n id String @id @default(cuid())\n phaseId String\n phase PropertyPaymentMethodPhase @relation(fields: [phaseId], references: [id], onDelete: Cascade)\n\n name String\n stepType StepType\n order Int\n\n metadata Json?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([phaseId])\n @@map(\"payment_method_phase_steps\")\n}\n\n// Required document within a DOCUMENTATION phase\nmodel PaymentMethodPhaseDocument {\n id String @id @default(cuid())\n phaseId String\n phase PropertyPaymentMethodPhase @relation(fields: [phaseId], references: [id], onDelete: Cascade)\n\n documentType String\n isRequired Boolean @default(true)\n description String? @db.Text\n allowedMimeTypes String? // CSV: application/pdf,image/jpeg\n maxSizeBytes Int?\n\n metadata Json?\n createdAt DateTime @default(now())\n\n @@index([phaseId, documentType])\n @@map(\"payment_method_phase_documents\")\n}\n\n// =============================================================================\n// CONTRACT DOMAIN - Unified agreement model (replaces Mortgage, PurchasePlan, etc.)\n// =============================================================================\n// Contract is the canonical agreement. \"Mortgage\" is just a product configuration\n// that creates a Contract with specific phases (documentation, downpayment, long-term payment).\n// Phases can be DOCUMENTATION (FSM for approvals) or PAYMENT (PaymentPlan-driven installments).\n// =============================================================================\n\nmodel Contract {\n id String @id @default(cuid())\n tenantId String\n tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)\n // Link to specific unit being purchased/rented\n propertyUnitId String\n propertyUnit PropertyUnit @relation(fields: [propertyUnitId], references: [id], onDelete: Cascade)\n buyerId String\n buyer User @relation(\"ContractBuyer\", fields: [buyerId], references: [id], onDelete: Cascade)\n sellerId String?\n seller User? @relation(\"ContractSeller\", fields: [sellerId], references: [id])\n paymentMethodId String? // PropertyPaymentMethod used to create this contract\n paymentMethod PropertyPaymentMethod? @relation(fields: [paymentMethodId], references: [id])\n\n // Contract identification\n contractNumber String @unique\n title String\n description String? @db.Text\n contractType String // Admin-defined: MORTGAGE, INSTALLMENT, RENT_TO_OWN, CASH, LEASE, etc.\n\n // Financial summary (computed from phases)\n totalAmount Float // Total contract value (from unit price or negotiated)\n downPayment Float @default(0)\n downPaymentPaid Float @default(0)\n principal Float? // Financed amount (if applicable)\n interestRate Float? // Overall interest rate (if applicable)\n termMonths Int? // Total term (if applicable)\n periodicPayment Float? // Computed periodic payment (if applicable)\n totalPaidToDate Float @default(0)\n totalInterestPaid Float @default(0)\n\n // FSM state (DB-enforced enums)\n status ContractStatus @default(DRAFT)\n state ContractStatus @default(DRAFT) // FSM state for workflow\n currentPhaseId String?\n\n // Timing\n nextPaymentDueDate DateTime?\n lastReminderSentAt DateTime?\n startDate DateTime?\n endDate DateTime?\n signedAt DateTime?\n terminatedAt DateTime?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // Relations\n phases ContractPhase[]\n documents ContractDocument[]\n payments ContractPayment[]\n transitions ContractTransition[]\n events ContractEvent[]\n terminations ContractTermination[]\n offerLetters OfferLetter[]\n\n // Prequalification that led to this contract (optional)\n prequalification Prequalification?\n // Payment method change requests for this contract\n paymentMethodChangeRequests PaymentMethodChangeRequest[]\n\n @@index([tenantId])\n @@index([propertyUnitId])\n @@index([buyerId])\n @@index([sellerId])\n @@index([paymentMethodId])\n @@index([status])\n @@index([state])\n @@map(\"contracts\")\n}\n\n// Phase within a contract - can be DOCUMENTATION or PAYMENT type\n// Admin names phases freely (e.g., \"KYC Documents\", \"Downpayment\", \"Monthly Mortgage\")\nmodel ContractPhase {\n id String @id @default(cuid())\n contractId String\n contract Contract @relation(fields: [contractId], references: [id], onDelete: Cascade)\n paymentPlanId String? // Only for PAYMENT phases\n paymentPlan PaymentPlan? @relation(fields: [paymentPlanId], references: [id])\n\n // Admin-defined naming\n name String\n description String? @db.Text\n\n // Phase classification (DB-enforced enums)\n phaseCategory PhaseCategory\n phaseType PhaseType\n order Int\n\n // FSM state for this phase (DB-enforced enum)\n status PhaseStatus @default(PENDING)\n\n // Financial details (for PAYMENT phases)\n totalAmount Float?\n paidAmount Float @default(0)\n remainingAmount Float?\n interestRate Float?\n\n // Fund collection behavior (snapshotted from template at contract creation)\n // true = we collect funds via wallet/gateway (e.g., downpayment)\n // false = external payment, we only track/reconcile (e.g., bank mortgage)\n collectFunds Boolean @default(true)\n\n // Progress counters (for efficient activation checks)\n approvedDocumentsCount Int @default(0)\n requiredDocumentsCount Int @default(0)\n completedStepsCount Int @default(0)\n totalStepsCount Int @default(0)\n\n // Timing\n dueDate DateTime?\n startDate DateTime?\n endDate DateTime?\n activatedAt DateTime?\n completedAt DateTime?\n\n // Activation rules\n requiresPreviousPhaseCompletion Boolean @default(true)\n minimumCompletionPercentage Float?\n completionCriterion CompletionCriterion?\n\n // Snapshots for audit (effective config at contract creation)\n paymentPlanSnapshot Json?\n stepDefinitionsSnapshot Json?\n requiredDocumentSnapshot Json?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // Relations\n installments ContractInstallment[]\n payments ContractPayment[]\n steps ContractPhaseStep[] // For DOCUMENTATION phases (FSM steps)\n\n @@index([contractId])\n @@index([paymentPlanId])\n @@index([phaseCategory])\n @@index([status])\n @@index([order])\n @@map(\"contract_phases\")\n}\n\n// Steps within a DOCUMENTATION phase (FSM for document collection/approval)\nmodel ContractPhaseStep {\n id String @id @default(cuid())\n phaseId String\n phase ContractPhase @relation(fields: [phaseId], references: [id], onDelete: Cascade)\n\n name String\n description String? @db.Text\n stepType StepType\n order Int\n\n status StepStatus @default(PENDING)\n\n // Configuration metadata (for GENERATE_DOCUMENT steps, etc.)\n metadata Json?\n\n // Assignment\n assigneeId String?\n assignee User? @relation(\"PhaseStepAssignee\", fields: [assigneeId], references: [id])\n\n // Required document types for UPLOAD steps (normalized)\n requiredDocuments ContractPhaseStepDocument[]\n\n // Timing\n dueDate DateTime?\n completedAt DateTime?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n approvals ContractPhaseStepApproval[]\n\n @@index([phaseId])\n @@index([status])\n @@index([order])\n @@map(\"contract_phase_steps\")\n}\n\n// Required documents for a step (normalized from CSV)\nmodel ContractPhaseStepDocument {\n id String @id @default(cuid())\n stepId String\n step ContractPhaseStep @relation(fields: [stepId], references: [id], onDelete: Cascade)\n\n documentType String\n isRequired Boolean @default(true)\n\n createdAt DateTime @default(now())\n\n @@index([stepId, documentType])\n @@map(\"contract_phase_step_documents\")\n}\n\n// Approvals for documentation steps\nmodel ContractPhaseStepApproval {\n id String @id @default(cuid())\n stepId String\n step ContractPhaseStep @relation(fields: [stepId], references: [id], onDelete: Cascade)\n approverId String?\n approver User? @relation(\"PhaseStepApprover\", fields: [approverId], references: [id])\n\n decision ApprovalDecision\n comment String? @db.Text\n decidedAt DateTime @default(now())\n\n createdAt DateTime @default(now())\n\n @@index([stepId])\n @@map(\"contract_phase_step_approvals\")\n}\n\n// Installments within a PAYMENT phase\nmodel ContractInstallment {\n id String @id @default(cuid())\n phaseId String\n phase ContractPhase @relation(fields: [phaseId], references: [id], onDelete: Cascade)\n\n installmentNumber Int\n\n amount Float\n principalAmount Float @default(0)\n interestAmount Float @default(0)\n\n dueDate DateTime\n status InstallmentStatus @default(PENDING)\n\n paidAmount Float @default(0)\n paidDate DateTime?\n\n lateFee Float @default(0)\n lateFeeWaived Boolean @default(false)\n gracePeriodDays Int @default(0)\n gracePeriodEndDate DateTime?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n payments ContractPayment[]\n\n @@index([phaseId])\n @@index([dueDate])\n @@index([status])\n @@map(\"contract_installments\")\n}\n\n// Unified payment record for contracts\nmodel ContractPayment {\n id String @id @default(cuid())\n contractId String\n contract Contract @relation(fields: [contractId], references: [id], onDelete: Cascade)\n phaseId String?\n phase ContractPhase? @relation(fields: [phaseId], references: [id])\n installmentId String?\n installment ContractInstallment? @relation(fields: [installmentId], references: [id])\n payerId String?\n payer User? @relation(\"ContractPayer\", fields: [payerId], references: [id])\n\n amount Float\n principalAmount Float @default(0)\n interestAmount Float @default(0)\n lateFeeAmount Float @default(0)\n\n paymentMethod String // BANK_TRANSFER, CREDIT_CARD, WALLET, CASH, CHECK\n status PaymentStatus @default(INITIATED)\n\n reference String? @unique\n gatewayResponse String? @db.Text // JSON\n\n processedAt DateTime?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([contractId])\n @@index([phaseId])\n @@index([installmentId])\n @@index([payerId])\n @@index([status])\n @@index([reference])\n @@map(\"contract_payments\")\n}\n\n// Contract documents (owned by contract, linked to phases/steps as needed)\nmodel ContractDocument {\n id String @id @default(cuid())\n contractId String\n contract Contract @relation(fields: [contractId], references: [id], onDelete: Cascade)\n phaseId String? // Optional link to specific phase\n stepId String? // Optional link to specific step\n\n name String\n url String\n type String // ID, BANK_STATEMENT, INCOME_PROOF, TITLE_DEED, SIGNATURE, etc.\n uploadedById String?\n uploadedBy User? @relation(\"DocumentUploader\", fields: [uploadedById], references: [id])\n\n status DocumentStatus @default(PENDING)\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([contractId])\n @@index([phaseId])\n @@index([stepId])\n @@index([type])\n @@index([status])\n @@map(\"contract_documents\")\n}\n\n// FSM transitions for audit\nmodel ContractTransition {\n id String @id @default(cuid())\n contractId String\n contract Contract @relation(fields: [contractId], references: [id], onDelete: Cascade)\n fromState String\n toState String\n trigger String\n metadata String? @db.Text // JSON\n transitionedAt DateTime @default(now())\n\n @@index([contractId])\n @@map(\"contract_transitions\")\n}\n\n// Domain events for audit and integration\nmodel ContractEvent {\n id String @id @default(cuid())\n contractId String\n contract Contract @relation(fields: [contractId], references: [id], onDelete: Cascade)\n event String\n data String? @db.Text // JSON\n createdAt DateTime @default(now())\n\n @@index([contractId])\n @@map(\"contract_events\")\n}\n\n// =============================================================================\n// OFFER LETTERS - Provisional and Final offer documents\n// =============================================================================\n\nmodel DocumentTemplate {\n id String @id @default(cuid())\n tenantId String\n tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)\n\n name String // \"Provisional Offer Letter\", \"Final Offer Letter\"\n code String // PROVISIONAL_OFFER, FINAL_OFFER\n description String?\n version Int @default(1)\n\n // Template content (Handlebars)\n htmlTemplate String @db.Text\n cssStyles String? @db.Text\n\n // Merge field definitions for UI\n mergeFields Json? // [{name, type, required, description}]\n\n isActive Boolean @default(true)\n isDefault Boolean @default(false)\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n offerLetters OfferLetter[]\n\n @@unique([tenantId, code, version])\n @@index([tenantId])\n @@index([code])\n @@map(\"document_templates\")\n}\n\nmodel OfferLetter {\n id String @id @default(cuid())\n tenantId String\n tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)\n contractId String\n contract Contract @relation(fields: [contractId], references: [id], onDelete: Cascade)\n\n // Template used\n templateId String\n template DocumentTemplate @relation(fields: [templateId], references: [id])\n\n // Letter details\n letterNumber String @unique // OL-XXXXXX\n type OfferLetterType\n status OfferLetterStatus @default(DRAFT)\n\n // Generated document\n htmlContent String? @db.Text // Rendered HTML\n pdfUrl String? // S3 URL of generated PDF\n pdfKey String? // S3 key for deletion/access\n\n // Merge data used (snapshot for audit)\n mergeData Json? // All data merged into template\n\n // Signing workflow\n sentAt DateTime?\n viewedAt DateTime?\n signedAt DateTime?\n signatureIp String?\n signatureData Json? // {method, timestamp, metadata}\n\n // Validity\n expiresAt DateTime?\n expiredAt DateTime?\n cancelledAt DateTime?\n cancelReason String?\n\n // Audit\n generatedById String?\n generatedBy User? @relation(\"OfferLetterGenerator\", fields: [generatedById], references: [id])\n sentById String?\n sentBy User? @relation(\"OfferLetterSender\", fields: [sentById], references: [id])\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([tenantId])\n @@index([contractId])\n @@index([type])\n @@index([status])\n @@map(\"offer_letters\")\n}\n\n// =============================================================================\n// CONTRACT TERMINATION - Full lifecycle for cancellation/termination\n// =============================================================================\n// Tracks termination requests from initiation through refund completion.\n// Industry-standard flow:\n// 1. Request created (by buyer/seller/admin/system)\n// 2. Admin reviews (if required by policy)\n// 3. Financial settlement calculated (refunds, penalties, forfeitures)\n// 4. Refund processed (if applicable)\n// 5. Contract marked terminated, unit released\n// =============================================================================\n\nmodel ContractTermination {\n id String @id @default(cuid())\n contractId String\n contract Contract @relation(fields: [contractId], references: [id], onDelete: Cascade)\n tenantId String\n tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)\n\n // Request identification\n requestNumber String @unique // TRM-XXXXXX\n\n // Who initiated and why\n initiatedBy TerminationInitiator\n initiatorId String? // userId if BUYER/SELLER/ADMIN\n initiator User? @relation(\"TerminationInitiator\", fields: [initiatorId], references: [id])\n type TerminationType\n reason String? @db.Text\n supportingDocs Json? // [{type, url, uploadedAt}]\n\n // Workflow status\n status TerminationStatus @default(REQUESTED)\n requiresApproval Boolean @default(true)\n autoApproveEligible Boolean @default(false) // Pre-signature, no payments\n\n // Admin review\n reviewedBy String?\n reviewer User? @relation(\"TerminationReviewer\", fields: [reviewedBy], references: [id])\n reviewedAt DateTime?\n reviewNotes String? @db.Text\n rejectionReason String? @db.Text\n\n // Financial snapshot at time of request\n contractSnapshot Json // Full contract state snapshot\n totalContractAmount Float\n totalPaidToDate Float\n outstandingBalance Float\n\n // Settlement calculation\n refundableAmount Float @default(0) // Amount eligible for refund\n penaltyAmount Float @default(0) // Penalties/fees to deduct\n forfeitedAmount Float @default(0) // Amount forfeited (non-refundable deposits)\n adminFeeAmount Float @default(0) // Processing fees\n netRefundAmount Float @default(0) // refundableAmount - penaltyAmount - adminFeeAmount\n settlementNotes String? @db.Text\n\n // Refund processing\n refundStatus RefundStatus @default(NOT_APPLICABLE)\n refundReference String? // Payment gateway reference\n refundMethod String? // ORIGINAL_METHOD, BANK_TRANSFER, CHECK, WALLET\n refundAccountDetails Json? // Encrypted bank details if needed\n refundInitiatedAt DateTime?\n refundCompletedAt DateTime?\n refundFailureReason String? @db.Text\n\n // Property unit handling\n unitReleasedAt DateTime?\n unitReservedForId String? // If unit being held for another buyer\n\n // Timing\n requestedAt DateTime @default(now())\n approvedAt DateTime?\n executedAt DateTime?\n completedAt DateTime?\n cancelledAt DateTime?\n\n // Idempotency and audit\n idempotencyKey String? @unique\n metadata Json?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([contractId])\n @@index([tenantId])\n @@index([status])\n @@index([type])\n @@index([initiatorId])\n @@index([requestedAt])\n @@map(\"contract_terminations\")\n}\n\n// =============================================================================\n// PREQUALIFICATION - Eligibility assessment before contract creation\n// =============================================================================\n// Prequalification is a separate aggregate that evaluates a user's eligibility\n// for a specific product (property + payment method). It captures their answers\n// to eligibility questions and calculates a score. Once approved, it can be\n// linked to a Contract to provide context about how the buyer qualified.\n// =============================================================================\n\nenum PrequalificationStatus {\n DRAFT\n SUBMITTED\n UNDER_REVIEW\n APPROVED\n REJECTED\n EXPIRED\n}\n\nmodel Prequalification {\n id String @id @default(cuid())\n tenantId String\n tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)\n userId String\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n // What they're applying for\n propertyId String\n property Property @relation(fields: [propertyId], references: [id], onDelete: Cascade)\n paymentMethodId String\n paymentMethod PropertyPaymentMethod @relation(fields: [paymentMethodId], references: [id])\n\n // Questionnaire responses and scoring\n answers Json // Array of {questionId, answer, weight, score}\n score Float? // Computed eligibility score (0-100)\n\n // Financial assessment\n requestedAmount Float? // How much they want to finance\n monthlyIncome Float? // Self-reported monthly income\n monthlyExpenses Float? // Self-reported monthly expenses\n debtToIncomeRatio Float? // Computed DTI\n suggestedTermMonths Int? // System-suggested term based on affordability\n\n // Status tracking\n status PrequalificationStatus @default(DRAFT)\n notes String? @db.Text // Admin notes\n reviewedBy String? // Admin who reviewed\n reviewedAt DateTime?\n expiresAt DateTime? // Prequalification validity period\n\n // If approved, may be linked to eventual contract\n contractId String? @unique\n contract Contract? @relation(fields: [contractId], references: [id])\n\n // Underwriting decisions for this prequalification\n underwritingDecisions UnderwritingDecision[]\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([tenantId])\n @@index([userId])\n @@index([propertyId])\n @@index([status])\n @@map(\"prequalifications\")\n}\n\n// =============================================================================\n// UNDERWRITING DECISION - Automated/manual credit decisions\n// =============================================================================\n// Records each underwriting decision for a prequalification. Multiple decisions\n// may exist if re-evaluation occurs (e.g., after document submission).\n// =============================================================================\n\nmodel UnderwritingDecision {\n id String @id @default(cuid())\n tenantId String\n tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)\n\n // Link to prequalification being evaluated\n prequalificationId String\n prequalification Prequalification @relation(fields: [prequalificationId], references: [id], onDelete: Cascade)\n\n // Decision outcome\n decision UnderwritingDecisionKind\n\n // Scoring and rationale\n score Float? // Computed risk score (0-100)\n reasons Json? // Array of reason codes/messages for decision\n conditions Json? // Array of conditions for CONDITIONAL decisions\n\n // Rule engine metadata\n rulesVersion String? // Version of rules/model used\n ruleResults Json? // Detailed rule evaluation results\n\n // External data references (credit bureau, verifications)\n externalChecks Json? // { creditBureau: {...}, incomeVerification: {...} }\n\n // Manual review (if escalated)\n isManualReview Boolean @default(false)\n reviewedBy String? // Admin who reviewed\n reviewedAt DateTime?\n reviewNotes String? @db.Text\n\n // Audit\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([tenantId])\n @@index([prequalificationId])\n @@index([decision])\n @@index([createdAt])\n @@map(\"underwriting_decisions\")\n}\n\n// =============================================================================\n// PAYMENT METHOD CHANGE REQUEST - Mid-contract payment method changes\n// =============================================================================\n// When a user wants to change their payment method after contract creation,\n// this aggregate tracks the request, required documentation, approvals, and\n// final execution. Different from-to combinations may require different docs.\n// =============================================================================\n\nenum PaymentMethodChangeStatus {\n PENDING_DOCUMENTS\n DOCUMENTS_SUBMITTED\n UNDER_REVIEW\n APPROVED\n REJECTED\n EXECUTED\n CANCELLED\n}\n\nmodel PaymentMethodChangeRequest {\n id String @id @default(cuid())\n tenantId String\n tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)\n contractId String\n contract Contract @relation(fields: [contractId], references: [id], onDelete: Cascade)\n\n // The change being requested\n fromPaymentMethodId String\n fromPaymentMethod PropertyPaymentMethod @relation(\"ChangeFromMethod\", fields: [fromPaymentMethodId], references: [id])\n toPaymentMethodId String\n toPaymentMethod PropertyPaymentMethod @relation(\"ChangeToMethod\", fields: [toPaymentMethodId], references: [id])\n\n // Who requested and why\n requestorId String\n requestor User @relation(\"ChangeRequestor\", fields: [requestorId], references: [id])\n reason String? @db.Text\n\n // Documentation requirements (determined by DocumentRequirementRule)\n requiredDocumentTypes String? // CSV: BANK_STATEMENT,INCOME_PROOF,NEW_EMPLOYER_LETTER\n submittedDocuments Json? // [{type, s3Key, uploadedAt, status}]\n\n // Financial impact assessment\n currentOutstanding Float? // Outstanding balance at time of request\n newTermMonths Int? // New term if applicable\n newInterestRate Float? // New rate if applicable\n newMonthlyPayment Float? // Projected new payment\n penaltyAmount Float? // Early change penalty if applicable\n financialImpactNotes String? @db.Text\n\n // Status and workflow\n status PaymentMethodChangeStatus @default(PENDING_DOCUMENTS)\n reviewerId String?\n reviewer User? @relation(\"ChangeReviewer\", fields: [reviewerId], references: [id])\n reviewNotes String? @db.Text\n reviewedAt DateTime?\n\n // Execution details\n executedAt DateTime?\n previousPhaseData Json? // Snapshot of phases before change\n newPhaseData Json? // New phases created after change\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([tenantId])\n @@index([contractId])\n @@index([status])\n @@index([requestorId])\n @@map(\"payment_method_change_requests\")\n}\n\n// =============================================================================\n// DOCUMENT REQUIREMENT RULES - Configurable document requirements\n// =============================================================================\n// Admins can configure which documents are required for specific scenarios:\n// - Prequalification for a payment method type\n// - Contract phases\n// - Payment method changes (from-to combinations)\n// This allows tenants to customize documentation workflows per product.\n// =============================================================================\n\nenum DocumentRequirementContext {\n PREQUALIFICATION // During prequalification\n CONTRACT_PHASE // During a contract phase\n PAYMENT_METHOD_CHANGE // When changing payment method mid-contract\n}\n\nmodel DocumentRequirementRule {\n id String @id @default(cuid())\n tenantId String\n tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)\n\n // Rule context\n context DocumentRequirementContext\n\n // Scoping (which situations this rule applies to)\n // For PREQUALIFICATION: paymentMethodId\n // For CONTRACT_PHASE: phaseType\n // For PAYMENT_METHOD_CHANGE: fromMethodId + toMethodId\n paymentMethodId String?\n paymentMethod PropertyPaymentMethod? @relation(\"RulePaymentMethod\", fields: [paymentMethodId], references: [id])\n phaseType String? // KYC, VERIFICATION, DOWNPAYMENT, etc.\n fromPaymentMethodId String?\n fromPaymentMethod PropertyPaymentMethod? @relation(\"RuleFromMethod\", fields: [fromPaymentMethodId], references: [id])\n toPaymentMethodId String?\n toPaymentMethod PropertyPaymentMethod? @relation(\"RuleToMethod\", fields: [toPaymentMethodId], references: [id])\n\n // Document requirements\n documentType String // ID_CARD, PASSPORT, BANK_STATEMENT, INCOME_PROOF, etc.\n isRequired Boolean @default(true)\n description String? // Instructions for the user\n maxSizeBytes Int? // Max file size allowed\n allowedMimeTypes String? // CSV: application/pdf,image/jpeg,image/png\n\n // Validation rules\n expiryDays Int? // Document must not be older than X days\n requiresManualReview Boolean @default(false)\n\n isActive Boolean @default(true)\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([tenantId])\n @@index([context])\n @@index([paymentMethodId])\n @@index([phaseType])\n @@index([fromPaymentMethodId, toPaymentMethodId])\n @@map(\"document_requirement_rules\")\n}\n\n// =============================================================================\n// EVENT OUTBOX - For guaranteed event delivery to SQS queues\n// =============================================================================\n\nmodel DomainEvent {\n id String @id @default(cuid())\n\n // Event identification\n eventType String // MORTGAGE.CREATED, PHASE.ACTIVATED, PAYMENT.COMPLETED, etc\n aggregateType String // Mortgage, MortgagePhase, MortgagePayment, Property, etc\n aggregateId String\n\n // Routing - which queue(s) should receive this\n queueName String // notifications, payments, mortgage-steps, accounting, etc\n\n // Event payload (all data needed by consumers)\n payload String @db.Text // JSON\n\n // Metadata\n occurredAt DateTime @default(now())\n actorId String? // User who triggered the event\n actorRole String? // Role of the actor\n\n // Processing status\n status String @default(\"PENDING\") // PENDING, PROCESSING, SENT, FAILED\n processedAt DateTime?\n sentAt DateTime?\n failureCount Int @default(0)\n lastError String? @db.Text\n nextRetryAt DateTime?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([status, nextRetryAt])\n @@index([eventType])\n @@index([aggregateType, aggregateId])\n @@index([queueName])\n @@index([occurredAt])\n @@map(\"domain_events\")\n}\n",
|
|
18
|
+
"inlineSchema": "// =============================================================================\n// QSHELTER UNIFIED DATABASE SCHEMA\n// =============================================================================\n// This schema contains all database models for the QShelter platform\n// Organized by domain for better readability\n// =============================================================================\n\ngenerator client {\n provider = \"prisma-client\"\n output = \"../generated/client\"\n engineType = \"client\"\n}\n\ndatasource db {\n provider = \"mysql\"\n}\n\n// =============================================================================\n// ENUMS - Database-enforced value constraints\n// =============================================================================\n\nenum PhaseCategory {\n DOCUMENTATION\n PAYMENT\n}\n\nenum PhaseType {\n KYC\n VERIFICATION\n DOWNPAYMENT\n MORTGAGE\n BALLOON\n CUSTOM\n}\n\nenum PaymentFrequency {\n MONTHLY\n BIWEEKLY\n WEEKLY\n ONE_TIME\n CUSTOM\n}\n\nenum ContractStatus {\n DRAFT\n PENDING\n ACTIVE\n COMPLETED\n CANCELLED\n TERMINATED\n}\n\nenum PhaseStatus {\n PENDING\n IN_PROGRESS\n AWAITING_APPROVAL\n ACTIVE\n COMPLETED\n SKIPPED\n FAILED\n}\n\nenum StepType {\n UPLOAD\n REVIEW\n SIGNATURE\n APPROVAL\n EXTERNAL_CHECK\n WAIT\n GENERATE_DOCUMENT // Triggers document generation (offer letters, contracts, etc.)\n}\n\nenum StepStatus {\n PENDING\n IN_PROGRESS\n COMPLETED\n FAILED\n SKIPPED\n}\n\nenum InstallmentStatus {\n PENDING\n PAID\n OVERDUE\n PARTIALLY_PAID\n WAIVED\n}\n\nenum PaymentStatus {\n INITIATED\n PENDING\n COMPLETED\n FAILED\n REFUNDED\n}\n\nenum ApprovalDecision {\n APPROVED\n REJECTED\n REQUEST_CHANGES\n}\n\n// =============================================================================\n// CONTRACT TERMINATION / CANCELLATION ENUMS\n// =============================================================================\n\nenum TerminationType {\n BUYER_WITHDRAWAL // Buyer wants to cancel (voluntary)\n SELLER_WITHDRAWAL // Seller/developer cancels\n MUTUAL_AGREEMENT // Both parties agree to terminate\n PAYMENT_DEFAULT // Buyer failed payment obligations\n DOCUMENT_FAILURE // Buyer failed to provide required documents\n FRAUD // Fraudulent activity detected\n FORCE_MAJEURE // External circumstances (disaster, etc.)\n PROPERTY_UNAVAILABLE // Property no longer available\n REGULATORY // Regulatory/legal requirement\n OTHER // Other reasons (with notes)\n}\n\nenum TerminationStatus {\n REQUESTED // Initial request submitted\n PENDING_REVIEW // Awaiting admin review\n PENDING_REFUND // Approved, awaiting refund processing\n REFUND_IN_PROGRESS // Refund being processed\n REFUND_COMPLETED // Refund completed\n COMPLETED // Termination fully executed (no refund or refund done)\n REJECTED // Termination request rejected\n CANCELLED // Termination request was cancelled\n}\n\nenum RefundStatus {\n NOT_APPLICABLE // No refund needed (no payments made)\n PENDING // Refund not yet initiated\n INITIATED // Refund request sent to payment gateway\n PROCESSING // Gateway processing refund\n PARTIAL_COMPLETED // Some refund completed (penalties deducted)\n COMPLETED // Full refund completed\n FAILED // Refund failed (needs manual intervention)\n}\n\nenum TerminationInitiator {\n BUYER\n SELLER\n ADMIN\n SYSTEM\n}\n\nenum CompletionCriterion {\n DOCUMENT_APPROVALS\n PAYMENT_AMOUNT\n STEPS_COMPLETED\n}\n\nenum DocumentStatus {\n DRAFT\n PENDING\n PENDING_SIGNATURE\n SENT\n VIEWED\n SIGNED\n APPROVED\n REJECTED\n EXPIRED\n CANCELLED\n}\n\nenum OfferLetterType {\n PROVISIONAL\n FINAL\n}\n\nenum OfferLetterStatus {\n DRAFT\n GENERATED\n SENT\n VIEWED\n SIGNED\n EXPIRED\n CANCELLED\n}\n\nenum UnderwritingDecisionKind {\n APPROVE\n REJECT\n CONDITIONAL\n}\n\n// =============================================================================\n// USER & AUTH DOMAIN\n// =============================================================================\n\nmodel User {\n id String @id @default(cuid())\n email String @unique\n password String?\n phone String? @unique\n firstName String?\n lastName String?\n isActive Boolean @default(true)\n isEmailVerified Boolean @default(false)\n googleId String?\n avatar String?\n tenantId String?\n tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: SetNull)\n // Support multiple roles via explicit join table `UserRole`\n userRoles UserRole[]\n walletId String? @unique\n wallet Wallet? @relation(fields: [walletId], references: [id])\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n emailVerifiedAt DateTime?\n emailVerificationToken String?\n lastLoginAt DateTime?\n refreshTokens RefreshToken[]\n passwordResets PasswordReset[]\n suspensions UserSuspension[]\n emailPreferences EmailPreference[]\n deviceEndpoints DeviceEndpoint[]\n socials Social[]\n\n // Relations to other domains\n properties Property[]\n contracts Contract[] @relation(\"ContractBuyer\")\n soldContracts Contract[] @relation(\"ContractSeller\")\n contractPayments ContractPayment[] @relation(\"ContractPayer\")\n\n // Phase step assignments and approvals\n assignedSteps ContractPhaseStep[] @relation(\"PhaseStepAssignee\")\n stepApprovals ContractPhaseStepApproval[] @relation(\"PhaseStepApprover\")\n uploadedDocs ContractDocument[] @relation(\"DocumentUploader\")\n\n // Prequalification and payment method changes\n prequalifications Prequalification[]\n paymentMethodChangeRequests PaymentMethodChangeRequest[] @relation(\"ChangeRequestor\")\n reviewedChangeRequests PaymentMethodChangeRequest[] @relation(\"ChangeReviewer\")\n\n // Contract terminations\n initiatedTerminations ContractTermination[] @relation(\"TerminationInitiator\")\n reviewedTerminations ContractTermination[] @relation(\"TerminationReviewer\")\n\n // Offer letters\n offerLettersGenerated OfferLetter[] @relation(\"OfferLetterGenerator\")\n offerLettersSent OfferLetter[] @relation(\"OfferLetterSender\")\n\n @@index([email])\n @@index([tenantId])\n @@map(\"users\")\n}\n\nmodel Role {\n id String @id @default(cuid())\n name String @unique\n description String?\n userRoles UserRole[]\n permissions RolePermission[]\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@map(\"roles\")\n}\n\nmodel Permission {\n id String @id @default(cuid())\n name String @unique\n description String?\n resource String\n action String\n roles RolePermission[]\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@unique([resource, action])\n @@index([resource])\n @@map(\"permissions\")\n}\n\nmodel RolePermission {\n roleId String\n permissionId String\n role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)\n permission Permission @relation(fields: [permissionId], references: [id], onDelete: Cascade)\n createdAt DateTime @default(now())\n\n @@id([roleId, permissionId])\n @@map(\"role_permissions\")\n}\n\nmodel UserRole {\n userId String\n roleId String\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)\n createdAt DateTime @default(now())\n\n @@id([userId, roleId])\n @@map(\"user_roles\")\n}\n\nmodel Tenant {\n id String @id @default(cuid())\n name String\n subdomain String @unique\n isActive Boolean @default(true)\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // Back-relations for multitenancy\n users User[]\n properties Property[]\n paymentPlans PaymentPlan[]\n paymentMethods PropertyPaymentMethod[]\n contracts Contract[]\n\n // Prequalification and payment method changes\n prequalifications Prequalification[]\n paymentMethodChangeRequests PaymentMethodChangeRequest[]\n documentRequirementRules DocumentRequirementRule[]\n\n // Contract terminations\n contractTerminations ContractTermination[]\n\n // Offer letters and templates\n documentTemplates DocumentTemplate[]\n offerLetters OfferLetter[]\n\n // Underwriting\n underwritingDecisions UnderwritingDecision[]\n\n @@index([subdomain])\n @@map(\"tenants\")\n}\n\nmodel RefreshToken {\n id String @id @default(cuid())\n // Use the JWT `jti` for indexed lookups and keep the raw JWT (optional)\n jti String? @unique @db.VarChar(255)\n token String? @db.LongText\n userId String\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n expiresAt DateTime\n createdAt DateTime @default(now())\n\n @@index([userId])\n @@index([expiresAt])\n @@map(\"refresh_tokens\")\n}\n\nmodel PasswordReset {\n id String @id @default(cuid())\n token String @unique\n userId String\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n expiresAt DateTime\n usedAt DateTime?\n createdAt DateTime @default(now())\n\n @@index([userId])\n @@index([expiresAt])\n @@map(\"password_resets\")\n}\n\nmodel UserSuspension {\n id String @id @default(cuid())\n userId String\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n reason String\n suspendedAt DateTime @default(now())\n expiresAt DateTime?\n liftedAt DateTime?\n\n @@index([userId])\n @@map(\"user_suspensions\")\n}\n\nmodel EmailPreference {\n id String @id @default(cuid())\n userId String\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n marketingEmails Boolean @default(true)\n transactionalEmails Boolean @default(true)\n propertyAlerts Boolean @default(true)\n paymentReminders Boolean @default(true)\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([userId])\n @@map(\"email_preferences\")\n}\n\nmodel DeviceEndpoint {\n id String @id @default(cuid())\n userId String\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n endpoint String // Push notification endpoint\n platform String // ios, android, web\n isActive Boolean @default(true)\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([userId])\n @@map(\"device_endpoints\")\n}\n\nmodel Social {\n id String @id @default(cuid())\n userId String\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n provider String // google, facebook, twitter, etc\n socialId String // ID from the social provider\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@unique([provider, socialId])\n @@index([userId])\n @@map(\"socials\")\n}\n\nmodel OAuthState {\n id String @id @default(cuid())\n state String @unique\n expiresAt DateTime\n createdAt DateTime @default(now())\n\n @@index([state])\n @@index([expiresAt])\n @@map(\"oauth_states\")\n}\n\nmodel Wallet {\n id String @id @default(cuid())\n balance Float @default(0)\n currency String @default(\"USD\")\n user User?\n transactions Transaction[]\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@map(\"wallets\")\n}\n\nmodel Transaction {\n id String @id @default(cuid())\n walletId String\n wallet Wallet @relation(fields: [walletId], references: [id], onDelete: Cascade)\n amount Float\n type String // CREDIT, DEBIT\n status String // PENDING, COMPLETED, FAILED\n reference String?\n description String?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([walletId])\n @@map(\"transactions\")\n}\n\nmodel Settings {\n id String @id @default(cuid())\n key String @unique\n value String @db.Text\n category String?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([category])\n @@map(\"settings\")\n}\n\n// =============================================================================\n// PROPERTY DOMAIN\n// =============================================================================\n// Property = listing/project (e.g., \"Sunrise Estate\")\n// PropertyVariant = configuration with specs & price (e.g., \"3-Bed Corner - Finished\")\n// PropertyUnit = individual sellable unit (e.g., \"Unit A1\")\n// =============================================================================\n\nmodel Property {\n id String @id @default(cuid())\n tenantId String\n tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)\n userId String\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n title String\n category String // SALE, RENT, LEASE\n propertyType String // APARTMENT, HOUSE, LAND, COMMERCIAL, ESTATE, TOWNHOUSE\n country String\n currency String // USD, NGN, etc\n city String\n district String?\n zipCode String?\n streetAddress String?\n longitude Float?\n latitude Float?\n status String @default(\"DRAFT\") // DRAFT, PUBLISHED, SOLD_OUT, ARCHIVED\n description String? @db.Text\n displayImageId String?\n displayImage PropertyMedia? @relation(\"DisplayImage\", fields: [displayImageId], references: [id], onDelete: SetNull)\n isPublished Boolean @default(false)\n publishedAt DateTime?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // Relations\n documents PropertyDocument[]\n media PropertyMedia[] @relation(\"PropertyMedia\")\n amenities PropertyAmenity[] // Shared amenities (gym, pool, security)\n paymentMethods PropertyPaymentMethodLink[]\n variants PropertyVariant[]\n prequalifications Prequalification[]\n\n @@index([tenantId])\n @@index([userId])\n @@index([category])\n @@index([propertyType])\n @@index([city])\n @@index([status])\n @@map(\"properties\")\n}\n\nmodel PropertyMedia {\n id String @id @default(cuid())\n propertyId String\n property Property @relation(\"PropertyMedia\", fields: [propertyId], references: [id], onDelete: Cascade)\n url String\n type String // IMAGE, VIDEO\n caption String?\n order Int @default(0)\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n displayForProperties Property[] @relation(\"DisplayImage\")\n\n @@index([propertyId])\n @@map(\"property_media\")\n}\n\nmodel PropertyDocument {\n id String @id @default(cuid())\n propertyId String\n property Property @relation(fields: [propertyId], references: [id], onDelete: Cascade)\n name String\n url String\n type String // TITLE_DEED, SURVEY_PLAN, etc\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([propertyId])\n @@map(\"property_documents\")\n}\n\nmodel Amenity {\n id String @id @default(cuid())\n name String @unique\n category String? // PROPERTY, VARIANT, BOTH - helps filter which amenities to show\n icon String? // Icon name/URL for UI\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n properties PropertyAmenity[]\n variants PropertyVariantAmenity[]\n\n @@index([category])\n @@map(\"amenities\")\n}\n\n// =============================================================================\n// PROPERTY VARIANT & UNIT MODELS\n// =============================================================================\n\n// PropertyVariant = specific configuration with its own price and amenities\n// e.g., \"3-Bedroom Corner Piece - Fully Finished\", \"2-Bedroom Standard - Carcass\"\nmodel PropertyVariant {\n id String @id @default(cuid())\n propertyId String\n property Property @relation(fields: [propertyId], references: [id], onDelete: Cascade)\n\n name String // \"Corner Piece - Finished\", \"Standard - Carcass\"\n description String? @db.Text\n\n // Specifications\n nBedrooms Int?\n nBathrooms Int?\n nParkingSpots Int?\n area Float? // Square meters/feet\n\n // Pricing\n price Float\n pricePerSqm Float? // Computed or set manually\n\n // Inventory counters (denormalized for performance, updated via triggers/service)\n totalUnits Int @default(1)\n availableUnits Int @default(1)\n reservedUnits Int @default(0)\n soldUnits Int @default(0)\n\n // Status\n status String @default(\"AVAILABLE\") // AVAILABLE, LOW_STOCK, SOLD_OUT, ARCHIVED\n isActive Boolean @default(true)\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // Relations\n amenities PropertyVariantAmenity[]\n units PropertyUnit[]\n media PropertyVariantMedia[]\n\n @@index([propertyId])\n @@index([status])\n @@index([price])\n @@map(\"property_variants\")\n}\n\n// PropertyVariantAmenity = amenities specific to a variant\n// e.g., \"Finished Kitchen\", \"Smart Home System\", \"Private Garden\"\nmodel PropertyVariantAmenity {\n variantId String\n amenityId String\n variant PropertyVariant @relation(fields: [variantId], references: [id], onDelete: Cascade)\n amenity Amenity @relation(fields: [amenityId], references: [id], onDelete: Cascade)\n createdAt DateTime @default(now())\n\n @@id([variantId, amenityId])\n @@map(\"property_variant_amenities\")\n}\n\n// PropertyVariantMedia = images/videos specific to a variant\nmodel PropertyVariantMedia {\n id String @id @default(cuid())\n variantId String\n variant PropertyVariant @relation(fields: [variantId], references: [id], onDelete: Cascade)\n url String\n type String // IMAGE, VIDEO, FLOOR_PLAN, 3D_TOUR\n caption String?\n order Int @default(0)\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([variantId])\n @@map(\"property_variant_media\")\n}\n\n// PropertyUnit = individual sellable/rentable unit within a variant\n// e.g., \"Unit A1\", \"Block B - Flat 3\", \"Plot 15\"\nmodel PropertyUnit {\n id String @id @default(cuid())\n variantId String\n variant PropertyVariant @relation(fields: [variantId], references: [id], onDelete: Cascade)\n\n unitNumber String // \"A1\", \"B-3\", \"Plot 15\"\n floorNumber Int? // For apartments\n blockName String? // \"Block A\", \"Tower 1\"\n\n // Unit-specific overrides (if different from variant)\n priceOverride Float? // If this specific unit has a different price\n areaOverride Float? // If this specific unit has a different area\n notes String? @db.Text // Internal notes about this unit\n\n // Status tracking\n status String @default(\"AVAILABLE\") // AVAILABLE, RESERVED, SOLD, RENTED, UNAVAILABLE\n\n // Reservation/hold\n reservedAt DateTime?\n reservedUntil DateTime?\n reservedById String?\n\n // Ownership tracking (once sold)\n ownerId String?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // Relations\n contracts Contract[]\n\n @@unique([variantId, unitNumber])\n @@index([variantId])\n @@index([status])\n @@map(\"property_units\")\n}\n\nmodel PropertyAmenity {\n propertyId String\n amenityId String\n property Property @relation(fields: [propertyId], references: [id], onDelete: Cascade)\n amenity Amenity @relation(fields: [amenityId], references: [id], onDelete: Cascade)\n createdAt DateTime @default(now())\n\n @@id([propertyId, amenityId])\n @@map(\"property_amenities\")\n}\n\n// =============================================================================\n// PAYMENT PLAN DOMAIN - Reusable installment structure templates\n// =============================================================================\n\n// PaymentPlan = reusable structure for how payments are scheduled\n// Examples: \"Monthly360\" (360 monthly payments), \"Weekly52\", \"OneTime\"\nmodel PaymentPlan {\n id String @id @default(cuid())\n tenantId String? // NULL = global template available to all tenants\n tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade)\n name String\n description String? @db.Text\n isActive Boolean @default(true)\n\n // Structure configuration\n paymentFrequency PaymentFrequency\n customFrequencyDays Int?\n numberOfInstallments Int // 1 for one-time, 360 for 30yr monthly, etc\n calculateInterestDaily Boolean @default(false)\n gracePeriodDays Int @default(0)\n\n // Fund collection behavior\n // true = we collect funds via wallet/gateway (e.g., downpayment)\n // false = external payment, we only track/reconcile (e.g., bank mortgage)\n collectFunds Boolean @default(true)\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // Used by property payment method phases (templates)\n methodPhases PropertyPaymentMethodPhase[]\n // Used by instantiated contract phases\n contractPhases ContractPhase[]\n\n @@unique([tenantId, name]) // Unique per tenant, or globally if tenantId is null\n @@index([tenantId])\n @@map(\"payment_plans\")\n}\n\n// =============================================================================\n// PROPERTY PAYMENT METHOD DOMAIN - Product offerings per property\n// =============================================================================\n\n// PropertyPaymentMethod = how a property can be purchased (e.g., \"Standard Mortgage\", \"Cash\", \"Rent-to-Own\")\nmodel PropertyPaymentMethod {\n id String @id @default(cuid())\n tenantId String\n tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)\n name String // \"Standard Mortgage\", \"Flexible Payment\", \"Cash Purchase\"\n description String? @db.Text\n isActive Boolean @default(true)\n\n // Global method configuration\n allowEarlyPayoff Boolean @default(true)\n earlyPayoffPenaltyRate Float?\n autoActivatePhases Boolean @default(true)\n requiresManualApproval Boolean @default(false)\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // Many-to-many with properties\n properties PropertyPaymentMethodLink[]\n // Phases that make up this method (templates)\n phases PropertyPaymentMethodPhase[]\n // Contracts using this method\n contracts Contract[]\n\n // Prequalifications for this payment method\n prequalifications Prequalification[]\n\n // Payment method change tracking\n changeRequestsFrom PaymentMethodChangeRequest[] @relation(\"ChangeFromMethod\")\n changeRequestsTo PaymentMethodChangeRequest[] @relation(\"ChangeToMethod\")\n\n // Document requirement rules\n documentRules DocumentRequirementRule[] @relation(\"RulePaymentMethod\")\n changeRulesFrom DocumentRequirementRule[] @relation(\"RuleFromMethod\")\n changeRulesTo DocumentRequirementRule[] @relation(\"RuleToMethod\")\n\n @@unique([tenantId, name]) // Unique per tenant\n @@index([tenantId])\n @@map(\"property_payment_methods\")\n}\n\n// Many-to-many link between Property and PaymentMethod\nmodel PropertyPaymentMethodLink {\n propertyId String\n property Property @relation(fields: [propertyId], references: [id], onDelete: Cascade)\n paymentMethodId String\n paymentMethod PropertyPaymentMethod @relation(fields: [paymentMethodId], references: [id], onDelete: Cascade)\n\n // Method-specific overrides for this property\n isDefault Boolean @default(false)\n isActive Boolean @default(true)\n createdAt DateTime @default(now())\n\n @@id([propertyId, paymentMethodId])\n @@map(\"property_payment_method_links\")\n}\n\n// Phase template within a PropertyPaymentMethod (e.g., documentation, downpayment, mortgage)\n// phaseCategory determines the FSM type: DOCUMENTATION or PAYMENT\nmodel PropertyPaymentMethodPhase {\n id String @id @default(cuid())\n paymentMethodId String\n paymentMethod PropertyPaymentMethod @relation(fields: [paymentMethodId], references: [id], onDelete: Cascade)\n paymentPlanId String? // Only for PAYMENT phases\n paymentPlan PaymentPlan? @relation(fields: [paymentPlanId], references: [id])\n\n name String\n description String? @db.Text\n\n // Phase classification (DB-enforced enums)\n phaseCategory PhaseCategory\n phaseType PhaseType\n order Int\n\n // Financial configuration (for PAYMENT phases)\n interestRate Float?\n percentOfPrice Float? // e.g., 10.0 for 10% downpayment\n\n // Fund collection behavior (inherited from PaymentPlan if not set)\n // true = we collect funds via wallet/gateway (e.g., downpayment)\n // false = external payment, we only track/reconcile (e.g., bank mortgage)\n collectFunds Boolean? // null = inherit from PaymentPlan\n\n // Activation rules\n requiresPreviousPhaseCompletion Boolean @default(true)\n minimumCompletionPercentage Float?\n completionCriterion CompletionCriterion?\n\n // Snapshots for audit (original config at creation time)\n stepDefinitionsSnapshot Json?\n requiredDocumentSnapshot Json?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // Normalized child tables (for DOCUMENTATION phases)\n steps PaymentMethodPhaseStep[]\n requiredDocuments PaymentMethodPhaseDocument[]\n\n @@index([paymentMethodId])\n @@index([paymentPlanId])\n @@index([phaseCategory])\n @@map(\"property_payment_method_phases\")\n}\n\n// Step template within a DOCUMENTATION phase\nmodel PaymentMethodPhaseStep {\n id String @id @default(cuid())\n phaseId String\n phase PropertyPaymentMethodPhase @relation(fields: [phaseId], references: [id], onDelete: Cascade)\n\n name String\n stepType StepType\n order Int\n\n metadata Json?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([phaseId])\n @@map(\"payment_method_phase_steps\")\n}\n\n// Required document within a DOCUMENTATION phase\nmodel PaymentMethodPhaseDocument {\n id String @id @default(cuid())\n phaseId String\n phase PropertyPaymentMethodPhase @relation(fields: [phaseId], references: [id], onDelete: Cascade)\n\n documentType String\n isRequired Boolean @default(true)\n description String? @db.Text\n allowedMimeTypes String? // CSV: application/pdf,image/jpeg\n maxSizeBytes Int?\n\n metadata Json?\n createdAt DateTime @default(now())\n\n @@index([phaseId, documentType])\n @@map(\"payment_method_phase_documents\")\n}\n\n// =============================================================================\n// CONTRACT DOMAIN - Unified agreement model (replaces Mortgage, PurchasePlan, etc.)\n// =============================================================================\n// Contract is the canonical agreement. \"Mortgage\" is just a product configuration\n// that creates a Contract with specific phases (documentation, downpayment, long-term payment).\n// Phases can be DOCUMENTATION (FSM for approvals) or PAYMENT (PaymentPlan-driven installments).\n// =============================================================================\n\nmodel Contract {\n id String @id @default(cuid())\n tenantId String\n tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)\n // Link to specific unit being purchased/rented\n propertyUnitId String\n propertyUnit PropertyUnit @relation(fields: [propertyUnitId], references: [id], onDelete: Cascade)\n buyerId String\n buyer User @relation(\"ContractBuyer\", fields: [buyerId], references: [id], onDelete: Cascade)\n sellerId String?\n seller User? @relation(\"ContractSeller\", fields: [sellerId], references: [id])\n paymentMethodId String? // PropertyPaymentMethod used to create this contract\n paymentMethod PropertyPaymentMethod? @relation(fields: [paymentMethodId], references: [id])\n\n // Contract identification\n contractNumber String @unique\n title String\n description String? @db.Text\n contractType String // Admin-defined: MORTGAGE, INSTALLMENT, RENT_TO_OWN, CASH, LEASE, etc.\n\n // Financial summary (computed from phases)\n totalAmount Float // Total contract value (from unit price or negotiated)\n downPayment Float @default(0)\n downPaymentPaid Float @default(0)\n principal Float? // Financed amount (if applicable)\n interestRate Float? // Overall interest rate (if applicable)\n termMonths Int? // Total term (if applicable)\n periodicPayment Float? // Computed periodic payment (if applicable)\n totalPaidToDate Float @default(0)\n totalInterestPaid Float @default(0)\n\n // FSM state (DB-enforced enums)\n status ContractStatus @default(DRAFT)\n state ContractStatus @default(DRAFT) // FSM state for workflow\n currentPhaseId String?\n\n // Timing\n nextPaymentDueDate DateTime?\n lastReminderSentAt DateTime?\n startDate DateTime?\n endDate DateTime?\n signedAt DateTime?\n terminatedAt DateTime?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // Relations\n phases ContractPhase[]\n documents ContractDocument[]\n payments ContractPayment[]\n transitions ContractTransition[]\n events ContractEvent[]\n terminations ContractTermination[]\n offerLetters OfferLetter[]\n\n // Prequalification that led to this contract (optional)\n prequalification Prequalification?\n // Payment method change requests for this contract\n paymentMethodChangeRequests PaymentMethodChangeRequest[]\n\n @@index([tenantId])\n @@index([propertyUnitId])\n @@index([buyerId])\n @@index([sellerId])\n @@index([paymentMethodId])\n @@index([status])\n @@index([state])\n @@map(\"contracts\")\n}\n\n// Phase within a contract - can be DOCUMENTATION or PAYMENT type\n// Admin names phases freely (e.g., \"KYC Documents\", \"Downpayment\", \"Monthly Mortgage\")\nmodel ContractPhase {\n id String @id @default(cuid())\n contractId String\n contract Contract @relation(fields: [contractId], references: [id], onDelete: Cascade)\n paymentPlanId String? // Only for PAYMENT phases\n paymentPlan PaymentPlan? @relation(fields: [paymentPlanId], references: [id])\n\n // Admin-defined naming\n name String\n description String? @db.Text\n\n // Phase classification (DB-enforced enums)\n phaseCategory PhaseCategory\n phaseType PhaseType\n order Int\n\n // FSM state for this phase (DB-enforced enum)\n status PhaseStatus @default(PENDING)\n\n // Financial details (for PAYMENT phases)\n totalAmount Float?\n paidAmount Float @default(0)\n remainingAmount Float?\n interestRate Float?\n\n // Fund collection behavior (snapshotted from template at contract creation)\n // true = we collect funds via wallet/gateway (e.g., downpayment)\n // false = external payment, we only track/reconcile (e.g., bank mortgage)\n collectFunds Boolean @default(true)\n\n // Progress counters (for efficient activation checks)\n approvedDocumentsCount Int @default(0)\n requiredDocumentsCount Int @default(0)\n completedStepsCount Int @default(0)\n totalStepsCount Int @default(0)\n\n // Timing\n dueDate DateTime?\n startDate DateTime?\n endDate DateTime?\n activatedAt DateTime?\n completedAt DateTime?\n\n // Activation rules\n requiresPreviousPhaseCompletion Boolean @default(true)\n minimumCompletionPercentage Float?\n completionCriterion CompletionCriterion?\n\n // Snapshots for audit (effective config at contract creation)\n paymentPlanSnapshot Json?\n stepDefinitionsSnapshot Json?\n requiredDocumentSnapshot Json?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // Relations\n installments ContractInstallment[]\n payments ContractPayment[]\n steps ContractPhaseStep[] // For DOCUMENTATION phases (FSM steps)\n\n @@index([contractId])\n @@index([paymentPlanId])\n @@index([phaseCategory])\n @@index([status])\n @@index([order])\n @@map(\"contract_phases\")\n}\n\n// Steps within a DOCUMENTATION phase (FSM for document collection/approval)\nmodel ContractPhaseStep {\n id String @id @default(cuid())\n phaseId String\n phase ContractPhase @relation(fields: [phaseId], references: [id], onDelete: Cascade)\n\n name String\n description String? @db.Text\n stepType StepType\n order Int\n\n status StepStatus @default(PENDING)\n\n // Configuration metadata (for GENERATE_DOCUMENT steps, etc.)\n metadata Json?\n\n // Assignment\n assigneeId String?\n assignee User? @relation(\"PhaseStepAssignee\", fields: [assigneeId], references: [id])\n\n // Required document types for UPLOAD steps (normalized)\n requiredDocuments ContractPhaseStepDocument[]\n\n // Timing\n dueDate DateTime?\n completedAt DateTime?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n approvals ContractPhaseStepApproval[]\n\n @@index([phaseId])\n @@index([status])\n @@index([order])\n @@map(\"contract_phase_steps\")\n}\n\n// Required documents for a step (normalized from CSV)\nmodel ContractPhaseStepDocument {\n id String @id @default(cuid())\n stepId String\n step ContractPhaseStep @relation(fields: [stepId], references: [id], onDelete: Cascade)\n\n documentType String\n isRequired Boolean @default(true)\n\n createdAt DateTime @default(now())\n\n @@index([stepId, documentType])\n @@map(\"contract_phase_step_documents\")\n}\n\n// Approvals for documentation steps\nmodel ContractPhaseStepApproval {\n id String @id @default(cuid())\n stepId String\n step ContractPhaseStep @relation(fields: [stepId], references: [id], onDelete: Cascade)\n approverId String?\n approver User? @relation(\"PhaseStepApprover\", fields: [approverId], references: [id])\n\n decision ApprovalDecision\n comment String? @db.Text\n decidedAt DateTime @default(now())\n\n createdAt DateTime @default(now())\n\n @@index([stepId])\n @@map(\"contract_phase_step_approvals\")\n}\n\n// Installments within a PAYMENT phase\nmodel ContractInstallment {\n id String @id @default(cuid())\n phaseId String\n phase ContractPhase @relation(fields: [phaseId], references: [id], onDelete: Cascade)\n\n installmentNumber Int\n\n amount Float\n principalAmount Float @default(0)\n interestAmount Float @default(0)\n\n dueDate DateTime\n status InstallmentStatus @default(PENDING)\n\n paidAmount Float @default(0)\n paidDate DateTime?\n\n lateFee Float @default(0)\n lateFeeWaived Boolean @default(false)\n gracePeriodDays Int @default(0)\n gracePeriodEndDate DateTime?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n payments ContractPayment[]\n\n @@index([phaseId])\n @@index([dueDate])\n @@index([status])\n @@map(\"contract_installments\")\n}\n\n// Unified payment record for contracts\nmodel ContractPayment {\n id String @id @default(cuid())\n contractId String\n contract Contract @relation(fields: [contractId], references: [id], onDelete: Cascade)\n phaseId String?\n phase ContractPhase? @relation(fields: [phaseId], references: [id])\n installmentId String?\n installment ContractInstallment? @relation(fields: [installmentId], references: [id])\n payerId String?\n payer User? @relation(\"ContractPayer\", fields: [payerId], references: [id])\n\n amount Float\n principalAmount Float @default(0)\n interestAmount Float @default(0)\n lateFeeAmount Float @default(0)\n\n paymentMethod String // BANK_TRANSFER, CREDIT_CARD, WALLET, CASH, CHECK\n status PaymentStatus @default(INITIATED)\n\n reference String? @unique\n gatewayResponse String? @db.Text // JSON\n\n processedAt DateTime?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([contractId])\n @@index([phaseId])\n @@index([installmentId])\n @@index([payerId])\n @@index([status])\n @@index([reference])\n @@map(\"contract_payments\")\n}\n\n// Contract documents (owned by contract, linked to phases/steps as needed)\nmodel ContractDocument {\n id String @id @default(cuid())\n contractId String\n contract Contract @relation(fields: [contractId], references: [id], onDelete: Cascade)\n phaseId String? // Optional link to specific phase\n stepId String? // Optional link to specific step\n\n name String\n url String\n type String // ID, BANK_STATEMENT, INCOME_PROOF, TITLE_DEED, SIGNATURE, etc.\n uploadedById String?\n uploadedBy User? @relation(\"DocumentUploader\", fields: [uploadedById], references: [id])\n\n status DocumentStatus @default(PENDING)\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([contractId])\n @@index([phaseId])\n @@index([stepId])\n @@index([type])\n @@index([status])\n @@map(\"contract_documents\")\n}\n\n// FSM transitions for audit\nmodel ContractTransition {\n id String @id @default(cuid())\n contractId String\n contract Contract @relation(fields: [contractId], references: [id], onDelete: Cascade)\n fromState String\n toState String\n trigger String\n metadata String? @db.Text // JSON\n transitionedAt DateTime @default(now())\n\n @@index([contractId])\n @@map(\"contract_transitions\")\n}\n\n// Domain events for audit and integration\nmodel ContractEvent {\n id String @id @default(cuid())\n contractId String\n contract Contract @relation(fields: [contractId], references: [id], onDelete: Cascade)\n event String\n data String? @db.Text // JSON\n createdAt DateTime @default(now())\n\n @@index([contractId])\n @@map(\"contract_events\")\n}\n\n// =============================================================================\n// OFFER LETTERS - Provisional and Final offer documents\n// =============================================================================\n\nmodel DocumentTemplate {\n id String @id @default(cuid())\n tenantId String\n tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)\n\n name String // \"Provisional Offer Letter\", \"Final Offer Letter\"\n code String // PROVISIONAL_OFFER, FINAL_OFFER\n description String?\n version Int @default(1)\n\n // Template content (Handlebars)\n htmlTemplate String @db.Text\n cssStyles String? @db.Text\n\n // Merge field definitions for UI\n mergeFields Json? // [{name, type, required, description}]\n\n isActive Boolean @default(true)\n isDefault Boolean @default(false)\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n offerLetters OfferLetter[]\n\n @@unique([tenantId, code, version])\n @@index([tenantId])\n @@index([code])\n @@map(\"document_templates\")\n}\n\nmodel OfferLetter {\n id String @id @default(cuid())\n tenantId String\n tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)\n contractId String\n contract Contract @relation(fields: [contractId], references: [id], onDelete: Cascade)\n\n // Template used (optional - documents-service may handle default selection)\n templateId String?\n template DocumentTemplate? @relation(fields: [templateId], references: [id])\n\n // Letter details\n letterNumber String @unique // OL-XXXXXX\n type OfferLetterType\n status OfferLetterStatus @default(DRAFT)\n\n // Generated document\n htmlContent String? @db.Text // Rendered HTML\n pdfUrl String? // S3 URL of generated PDF\n pdfKey String? // S3 key for deletion/access\n\n // Merge data used (snapshot for audit)\n mergeData Json? // All data merged into template\n\n // Signing workflow\n sentAt DateTime?\n viewedAt DateTime?\n signedAt DateTime?\n signatureIp String?\n signatureData Json? // {method, timestamp, metadata}\n\n // Validity\n expiresAt DateTime?\n expiredAt DateTime?\n cancelledAt DateTime?\n cancelReason String?\n\n // Audit\n generatedById String?\n generatedBy User? @relation(\"OfferLetterGenerator\", fields: [generatedById], references: [id])\n sentById String?\n sentBy User? @relation(\"OfferLetterSender\", fields: [sentById], references: [id])\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([tenantId])\n @@index([contractId])\n @@index([type])\n @@index([status])\n @@map(\"offer_letters\")\n}\n\n// =============================================================================\n// CONTRACT TERMINATION - Full lifecycle for cancellation/termination\n// =============================================================================\n// Tracks termination requests from initiation through refund completion.\n// Industry-standard flow:\n// 1. Request created (by buyer/seller/admin/system)\n// 2. Admin reviews (if required by policy)\n// 3. Financial settlement calculated (refunds, penalties, forfeitures)\n// 4. Refund processed (if applicable)\n// 5. Contract marked terminated, unit released\n// =============================================================================\n\nmodel ContractTermination {\n id String @id @default(cuid())\n contractId String\n contract Contract @relation(fields: [contractId], references: [id], onDelete: Cascade)\n tenantId String\n tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)\n\n // Request identification\n requestNumber String @unique // TRM-XXXXXX\n\n // Who initiated and why\n initiatedBy TerminationInitiator\n initiatorId String? // userId if BUYER/SELLER/ADMIN\n initiator User? @relation(\"TerminationInitiator\", fields: [initiatorId], references: [id])\n type TerminationType\n reason String? @db.Text\n supportingDocs Json? // [{type, url, uploadedAt}]\n\n // Workflow status\n status TerminationStatus @default(REQUESTED)\n requiresApproval Boolean @default(true)\n autoApproveEligible Boolean @default(false) // Pre-signature, no payments\n\n // Admin review\n reviewedBy String?\n reviewer User? @relation(\"TerminationReviewer\", fields: [reviewedBy], references: [id])\n reviewedAt DateTime?\n reviewNotes String? @db.Text\n rejectionReason String? @db.Text\n\n // Financial snapshot at time of request\n contractSnapshot Json // Full contract state snapshot\n totalContractAmount Float\n totalPaidToDate Float\n outstandingBalance Float\n\n // Settlement calculation\n refundableAmount Float @default(0) // Amount eligible for refund\n penaltyAmount Float @default(0) // Penalties/fees to deduct\n forfeitedAmount Float @default(0) // Amount forfeited (non-refundable deposits)\n adminFeeAmount Float @default(0) // Processing fees\n netRefundAmount Float @default(0) // refundableAmount - penaltyAmount - adminFeeAmount\n settlementNotes String? @db.Text\n\n // Refund processing\n refundStatus RefundStatus @default(NOT_APPLICABLE)\n refundReference String? // Payment gateway reference\n refundMethod String? // ORIGINAL_METHOD, BANK_TRANSFER, CHECK, WALLET\n refundAccountDetails Json? // Encrypted bank details if needed\n refundInitiatedAt DateTime?\n refundCompletedAt DateTime?\n refundFailureReason String? @db.Text\n\n // Property unit handling\n unitReleasedAt DateTime?\n unitReservedForId String? // If unit being held for another buyer\n\n // Timing\n requestedAt DateTime @default(now())\n approvedAt DateTime?\n executedAt DateTime?\n completedAt DateTime?\n cancelledAt DateTime?\n\n // Idempotency and audit\n idempotencyKey String? @unique\n metadata Json?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([contractId])\n @@index([tenantId])\n @@index([status])\n @@index([type])\n @@index([initiatorId])\n @@index([requestedAt])\n @@map(\"contract_terminations\")\n}\n\n// =============================================================================\n// PREQUALIFICATION - Eligibility assessment before contract creation\n// =============================================================================\n// Prequalification is a separate aggregate that evaluates a user's eligibility\n// for a specific product (property + payment method). It captures their answers\n// to eligibility questions and calculates a score. Once approved, it can be\n// linked to a Contract to provide context about how the buyer qualified.\n// =============================================================================\n\nenum PrequalificationStatus {\n DRAFT\n SUBMITTED\n UNDER_REVIEW\n APPROVED\n REJECTED\n EXPIRED\n}\n\nmodel Prequalification {\n id String @id @default(cuid())\n tenantId String\n tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)\n userId String\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n // What they're applying for\n propertyId String\n property Property @relation(fields: [propertyId], references: [id], onDelete: Cascade)\n paymentMethodId String\n paymentMethod PropertyPaymentMethod @relation(fields: [paymentMethodId], references: [id])\n\n // Questionnaire responses and scoring\n answers Json // Array of {questionId, answer, weight, score}\n score Float? // Computed eligibility score (0-100)\n\n // Financial assessment\n requestedAmount Float? // How much they want to finance\n monthlyIncome Float? // Self-reported monthly income\n monthlyExpenses Float? // Self-reported monthly expenses\n debtToIncomeRatio Float? // Computed DTI\n suggestedTermMonths Int? // System-suggested term based on affordability\n\n // Status tracking\n status PrequalificationStatus @default(DRAFT)\n notes String? @db.Text // Admin notes\n reviewedBy String? // Admin who reviewed\n reviewedAt DateTime?\n expiresAt DateTime? // Prequalification validity period\n\n // If approved, may be linked to eventual contract\n contractId String? @unique\n contract Contract? @relation(fields: [contractId], references: [id])\n\n // Underwriting decisions for this prequalification\n underwritingDecisions UnderwritingDecision[]\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([tenantId])\n @@index([userId])\n @@index([propertyId])\n @@index([status])\n @@map(\"prequalifications\")\n}\n\n// =============================================================================\n// UNDERWRITING DECISION - Automated/manual credit decisions\n// =============================================================================\n// Records each underwriting decision for a prequalification. Multiple decisions\n// may exist if re-evaluation occurs (e.g., after document submission).\n// =============================================================================\n\nmodel UnderwritingDecision {\n id String @id @default(cuid())\n tenantId String\n tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)\n\n // Link to prequalification being evaluated\n prequalificationId String\n prequalification Prequalification @relation(fields: [prequalificationId], references: [id], onDelete: Cascade)\n\n // Decision outcome\n decision UnderwritingDecisionKind\n\n // Scoring and rationale\n score Float? // Computed risk score (0-100)\n reasons Json? // Array of reason codes/messages for decision\n conditions Json? // Array of conditions for CONDITIONAL decisions\n\n // Rule engine metadata\n rulesVersion String? // Version of rules/model used\n ruleResults Json? // Detailed rule evaluation results\n\n // External data references (credit bureau, verifications)\n externalChecks Json? // { creditBureau: {...}, incomeVerification: {...} }\n\n // Manual review (if escalated)\n isManualReview Boolean @default(false)\n reviewedBy String? // Admin who reviewed\n reviewedAt DateTime?\n reviewNotes String? @db.Text\n\n // Audit\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([tenantId])\n @@index([prequalificationId])\n @@index([decision])\n @@index([createdAt])\n @@map(\"underwriting_decisions\")\n}\n\n// =============================================================================\n// PAYMENT METHOD CHANGE REQUEST - Mid-contract payment method changes\n// =============================================================================\n// When a user wants to change their payment method after contract creation,\n// this aggregate tracks the request, required documentation, approvals, and\n// final execution. Different from-to combinations may require different docs.\n// =============================================================================\n\nenum PaymentMethodChangeStatus {\n PENDING_DOCUMENTS\n DOCUMENTS_SUBMITTED\n UNDER_REVIEW\n APPROVED\n REJECTED\n EXECUTED\n CANCELLED\n}\n\nmodel PaymentMethodChangeRequest {\n id String @id @default(cuid())\n tenantId String\n tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)\n contractId String\n contract Contract @relation(fields: [contractId], references: [id], onDelete: Cascade)\n\n // The change being requested\n fromPaymentMethodId String\n fromPaymentMethod PropertyPaymentMethod @relation(\"ChangeFromMethod\", fields: [fromPaymentMethodId], references: [id])\n toPaymentMethodId String\n toPaymentMethod PropertyPaymentMethod @relation(\"ChangeToMethod\", fields: [toPaymentMethodId], references: [id])\n\n // Who requested and why\n requestorId String\n requestor User @relation(\"ChangeRequestor\", fields: [requestorId], references: [id])\n reason String? @db.Text\n\n // Documentation requirements (determined by DocumentRequirementRule)\n requiredDocumentTypes String? // CSV: BANK_STATEMENT,INCOME_PROOF,NEW_EMPLOYER_LETTER\n submittedDocuments Json? // [{type, s3Key, uploadedAt, status}]\n\n // Financial impact assessment\n currentOutstanding Float? // Outstanding balance at time of request\n newTermMonths Int? // New term if applicable\n newInterestRate Float? // New rate if applicable\n newMonthlyPayment Float? // Projected new payment\n penaltyAmount Float? // Early change penalty if applicable\n financialImpactNotes String? @db.Text\n\n // Status and workflow\n status PaymentMethodChangeStatus @default(PENDING_DOCUMENTS)\n reviewerId String?\n reviewer User? @relation(\"ChangeReviewer\", fields: [reviewerId], references: [id])\n reviewNotes String? @db.Text\n reviewedAt DateTime?\n\n // Execution details\n executedAt DateTime?\n previousPhaseData Json? // Snapshot of phases before change\n newPhaseData Json? // New phases created after change\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([tenantId])\n @@index([contractId])\n @@index([status])\n @@index([requestorId])\n @@map(\"payment_method_change_requests\")\n}\n\n// =============================================================================\n// DOCUMENT REQUIREMENT RULES - Configurable document requirements\n// =============================================================================\n// Admins can configure which documents are required for specific scenarios:\n// - Prequalification for a payment method type\n// - Contract phases\n// - Payment method changes (from-to combinations)\n// This allows tenants to customize documentation workflows per product.\n// =============================================================================\n\nenum DocumentRequirementContext {\n PREQUALIFICATION // During prequalification\n CONTRACT_PHASE // During a contract phase\n PAYMENT_METHOD_CHANGE // When changing payment method mid-contract\n}\n\nmodel DocumentRequirementRule {\n id String @id @default(cuid())\n tenantId String\n tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)\n\n // Rule context\n context DocumentRequirementContext\n\n // Scoping (which situations this rule applies to)\n // For PREQUALIFICATION: paymentMethodId\n // For CONTRACT_PHASE: phaseType\n // For PAYMENT_METHOD_CHANGE: fromMethodId + toMethodId\n paymentMethodId String?\n paymentMethod PropertyPaymentMethod? @relation(\"RulePaymentMethod\", fields: [paymentMethodId], references: [id])\n phaseType String? // KYC, VERIFICATION, DOWNPAYMENT, etc.\n fromPaymentMethodId String?\n fromPaymentMethod PropertyPaymentMethod? @relation(\"RuleFromMethod\", fields: [fromPaymentMethodId], references: [id])\n toPaymentMethodId String?\n toPaymentMethod PropertyPaymentMethod? @relation(\"RuleToMethod\", fields: [toPaymentMethodId], references: [id])\n\n // Document requirements\n documentType String // ID_CARD, PASSPORT, BANK_STATEMENT, INCOME_PROOF, etc.\n isRequired Boolean @default(true)\n description String? // Instructions for the user\n maxSizeBytes Int? // Max file size allowed\n allowedMimeTypes String? // CSV: application/pdf,image/jpeg,image/png\n\n // Validation rules\n expiryDays Int? // Document must not be older than X days\n requiresManualReview Boolean @default(false)\n\n isActive Boolean @default(true)\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([tenantId])\n @@index([context])\n @@index([paymentMethodId])\n @@index([phaseType])\n @@index([fromPaymentMethodId, toPaymentMethodId])\n @@map(\"document_requirement_rules\")\n}\n\n// =============================================================================\n// EVENT OUTBOX - For guaranteed event delivery to SQS queues\n// =============================================================================\n\nmodel DomainEvent {\n id String @id @default(cuid())\n\n // Event identification\n eventType String // MORTGAGE.CREATED, PHASE.ACTIVATED, PAYMENT.COMPLETED, etc\n aggregateType String // Mortgage, MortgagePhase, MortgagePayment, Property, etc\n aggregateId String\n\n // Routing - which queue(s) should receive this\n queueName String // notifications, payments, mortgage-steps, accounting, etc\n\n // Event payload (all data needed by consumers)\n payload String @db.Text // JSON\n\n // Metadata\n occurredAt DateTime @default(now())\n actorId String? // User who triggered the event\n actorRole String? // Role of the actor\n\n // Processing status\n status String @default(\"PENDING\") // PENDING, PROCESSING, SENT, FAILED\n processedAt DateTime?\n sentAt DateTime?\n failureCount Int @default(0)\n lastError String? @db.Text\n nextRetryAt DateTime?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([status, nextRetryAt])\n @@index([eventType])\n @@index([aggregateType, aggregateId])\n @@index([queueName])\n @@index([occurredAt])\n @@map(\"domain_events\")\n}\n",
|
|
19
19
|
"runtimeDataModel": {
|
|
20
20
|
"models": {},
|
|
21
21
|
"enums": {},
|
|
@@ -483,9 +483,9 @@ export type DocumentTemplateMinOrderByAggregateInput = {
|
|
|
483
483
|
export type DocumentTemplateSumOrderByAggregateInput = {
|
|
484
484
|
version?: Prisma.SortOrder;
|
|
485
485
|
};
|
|
486
|
-
export type
|
|
487
|
-
is?: Prisma.DocumentTemplateWhereInput;
|
|
488
|
-
isNot?: Prisma.DocumentTemplateWhereInput;
|
|
486
|
+
export type DocumentTemplateNullableScalarRelationFilter = {
|
|
487
|
+
is?: Prisma.DocumentTemplateWhereInput | null;
|
|
488
|
+
isNot?: Prisma.DocumentTemplateWhereInput | null;
|
|
489
489
|
};
|
|
490
490
|
export type DocumentTemplateCreateNestedManyWithoutTenantInput = {
|
|
491
491
|
create?: Prisma.XOR<Prisma.DocumentTemplateCreateWithoutTenantInput, Prisma.DocumentTemplateUncheckedCreateWithoutTenantInput> | Prisma.DocumentTemplateCreateWithoutTenantInput[] | Prisma.DocumentTemplateUncheckedCreateWithoutTenantInput[];
|
|
@@ -530,10 +530,12 @@ export type DocumentTemplateCreateNestedOneWithoutOfferLettersInput = {
|
|
|
530
530
|
connectOrCreate?: Prisma.DocumentTemplateCreateOrConnectWithoutOfferLettersInput;
|
|
531
531
|
connect?: Prisma.DocumentTemplateWhereUniqueInput;
|
|
532
532
|
};
|
|
533
|
-
export type
|
|
533
|
+
export type DocumentTemplateUpdateOneWithoutOfferLettersNestedInput = {
|
|
534
534
|
create?: Prisma.XOR<Prisma.DocumentTemplateCreateWithoutOfferLettersInput, Prisma.DocumentTemplateUncheckedCreateWithoutOfferLettersInput>;
|
|
535
535
|
connectOrCreate?: Prisma.DocumentTemplateCreateOrConnectWithoutOfferLettersInput;
|
|
536
536
|
upsert?: Prisma.DocumentTemplateUpsertWithoutOfferLettersInput;
|
|
537
|
+
disconnect?: Prisma.DocumentTemplateWhereInput | boolean;
|
|
538
|
+
delete?: Prisma.DocumentTemplateWhereInput | boolean;
|
|
537
539
|
connect?: Prisma.DocumentTemplateWhereUniqueInput;
|
|
538
540
|
update?: Prisma.XOR<Prisma.XOR<Prisma.DocumentTemplateUpdateToOneWithWhereWithoutOfferLettersInput, Prisma.DocumentTemplateUpdateWithoutOfferLettersInput>, Prisma.DocumentTemplateUncheckedUpdateWithoutOfferLettersInput>;
|
|
539
541
|
};
|
|
@@ -227,7 +227,7 @@ export type OfferLetterGroupByOutputType = {
|
|
|
227
227
|
id: string;
|
|
228
228
|
tenantId: string;
|
|
229
229
|
contractId: string;
|
|
230
|
-
templateId: string;
|
|
230
|
+
templateId: string | null;
|
|
231
231
|
letterNumber: string;
|
|
232
232
|
type: $Enums.OfferLetterType;
|
|
233
233
|
status: $Enums.OfferLetterStatus;
|
|
@@ -262,7 +262,7 @@ export type OfferLetterWhereInput = {
|
|
|
262
262
|
id?: Prisma.StringFilter<"OfferLetter"> | string;
|
|
263
263
|
tenantId?: Prisma.StringFilter<"OfferLetter"> | string;
|
|
264
264
|
contractId?: Prisma.StringFilter<"OfferLetter"> | string;
|
|
265
|
-
templateId?: Prisma.
|
|
265
|
+
templateId?: Prisma.StringNullableFilter<"OfferLetter"> | string | null;
|
|
266
266
|
letterNumber?: Prisma.StringFilter<"OfferLetter"> | string;
|
|
267
267
|
type?: Prisma.EnumOfferLetterTypeFilter<"OfferLetter"> | $Enums.OfferLetterType;
|
|
268
268
|
status?: Prisma.EnumOfferLetterStatusFilter<"OfferLetter"> | $Enums.OfferLetterStatus;
|
|
@@ -285,7 +285,7 @@ export type OfferLetterWhereInput = {
|
|
|
285
285
|
updatedAt?: Prisma.DateTimeFilter<"OfferLetter"> | Date | string;
|
|
286
286
|
tenant?: Prisma.XOR<Prisma.TenantScalarRelationFilter, Prisma.TenantWhereInput>;
|
|
287
287
|
contract?: Prisma.XOR<Prisma.ContractScalarRelationFilter, Prisma.ContractWhereInput>;
|
|
288
|
-
template?: Prisma.XOR<Prisma.
|
|
288
|
+
template?: Prisma.XOR<Prisma.DocumentTemplateNullableScalarRelationFilter, Prisma.DocumentTemplateWhereInput> | null;
|
|
289
289
|
generatedBy?: Prisma.XOR<Prisma.UserNullableScalarRelationFilter, Prisma.UserWhereInput> | null;
|
|
290
290
|
sentBy?: Prisma.XOR<Prisma.UserNullableScalarRelationFilter, Prisma.UserWhereInput> | null;
|
|
291
291
|
};
|
|
@@ -293,7 +293,7 @@ export type OfferLetterOrderByWithRelationInput = {
|
|
|
293
293
|
id?: Prisma.SortOrder;
|
|
294
294
|
tenantId?: Prisma.SortOrder;
|
|
295
295
|
contractId?: Prisma.SortOrder;
|
|
296
|
-
templateId?: Prisma.SortOrder;
|
|
296
|
+
templateId?: Prisma.SortOrderInput | Prisma.SortOrder;
|
|
297
297
|
letterNumber?: Prisma.SortOrder;
|
|
298
298
|
type?: Prisma.SortOrder;
|
|
299
299
|
status?: Prisma.SortOrder;
|
|
@@ -329,7 +329,7 @@ export type OfferLetterWhereUniqueInput = Prisma.AtLeast<{
|
|
|
329
329
|
NOT?: Prisma.OfferLetterWhereInput | Prisma.OfferLetterWhereInput[];
|
|
330
330
|
tenantId?: Prisma.StringFilter<"OfferLetter"> | string;
|
|
331
331
|
contractId?: Prisma.StringFilter<"OfferLetter"> | string;
|
|
332
|
-
templateId?: Prisma.
|
|
332
|
+
templateId?: Prisma.StringNullableFilter<"OfferLetter"> | string | null;
|
|
333
333
|
type?: Prisma.EnumOfferLetterTypeFilter<"OfferLetter"> | $Enums.OfferLetterType;
|
|
334
334
|
status?: Prisma.EnumOfferLetterStatusFilter<"OfferLetter"> | $Enums.OfferLetterStatus;
|
|
335
335
|
htmlContent?: Prisma.StringNullableFilter<"OfferLetter"> | string | null;
|
|
@@ -351,7 +351,7 @@ export type OfferLetterWhereUniqueInput = Prisma.AtLeast<{
|
|
|
351
351
|
updatedAt?: Prisma.DateTimeFilter<"OfferLetter"> | Date | string;
|
|
352
352
|
tenant?: Prisma.XOR<Prisma.TenantScalarRelationFilter, Prisma.TenantWhereInput>;
|
|
353
353
|
contract?: Prisma.XOR<Prisma.ContractScalarRelationFilter, Prisma.ContractWhereInput>;
|
|
354
|
-
template?: Prisma.XOR<Prisma.
|
|
354
|
+
template?: Prisma.XOR<Prisma.DocumentTemplateNullableScalarRelationFilter, Prisma.DocumentTemplateWhereInput> | null;
|
|
355
355
|
generatedBy?: Prisma.XOR<Prisma.UserNullableScalarRelationFilter, Prisma.UserWhereInput> | null;
|
|
356
356
|
sentBy?: Prisma.XOR<Prisma.UserNullableScalarRelationFilter, Prisma.UserWhereInput> | null;
|
|
357
357
|
}, "id" | "letterNumber">;
|
|
@@ -359,7 +359,7 @@ export type OfferLetterOrderByWithAggregationInput = {
|
|
|
359
359
|
id?: Prisma.SortOrder;
|
|
360
360
|
tenantId?: Prisma.SortOrder;
|
|
361
361
|
contractId?: Prisma.SortOrder;
|
|
362
|
-
templateId?: Prisma.SortOrder;
|
|
362
|
+
templateId?: Prisma.SortOrderInput | Prisma.SortOrder;
|
|
363
363
|
letterNumber?: Prisma.SortOrder;
|
|
364
364
|
type?: Prisma.SortOrder;
|
|
365
365
|
status?: Prisma.SortOrder;
|
|
@@ -391,7 +391,7 @@ export type OfferLetterScalarWhereWithAggregatesInput = {
|
|
|
391
391
|
id?: Prisma.StringWithAggregatesFilter<"OfferLetter"> | string;
|
|
392
392
|
tenantId?: Prisma.StringWithAggregatesFilter<"OfferLetter"> | string;
|
|
393
393
|
contractId?: Prisma.StringWithAggregatesFilter<"OfferLetter"> | string;
|
|
394
|
-
templateId?: Prisma.
|
|
394
|
+
templateId?: Prisma.StringNullableWithAggregatesFilter<"OfferLetter"> | string | null;
|
|
395
395
|
letterNumber?: Prisma.StringWithAggregatesFilter<"OfferLetter"> | string;
|
|
396
396
|
type?: Prisma.EnumOfferLetterTypeWithAggregatesFilter<"OfferLetter"> | $Enums.OfferLetterType;
|
|
397
397
|
status?: Prisma.EnumOfferLetterStatusWithAggregatesFilter<"OfferLetter"> | $Enums.OfferLetterStatus;
|
|
@@ -435,7 +435,7 @@ export type OfferLetterCreateInput = {
|
|
|
435
435
|
updatedAt?: Date | string;
|
|
436
436
|
tenant: Prisma.TenantCreateNestedOneWithoutOfferLettersInput;
|
|
437
437
|
contract: Prisma.ContractCreateNestedOneWithoutOfferLettersInput;
|
|
438
|
-
template
|
|
438
|
+
template?: Prisma.DocumentTemplateCreateNestedOneWithoutOfferLettersInput;
|
|
439
439
|
generatedBy?: Prisma.UserCreateNestedOneWithoutOfferLettersGeneratedInput;
|
|
440
440
|
sentBy?: Prisma.UserCreateNestedOneWithoutOfferLettersSentInput;
|
|
441
441
|
};
|
|
@@ -443,7 +443,7 @@ export type OfferLetterUncheckedCreateInput = {
|
|
|
443
443
|
id?: string;
|
|
444
444
|
tenantId: string;
|
|
445
445
|
contractId: string;
|
|
446
|
-
templateId
|
|
446
|
+
templateId?: string | null;
|
|
447
447
|
letterNumber: string;
|
|
448
448
|
type: $Enums.OfferLetterType;
|
|
449
449
|
status?: $Enums.OfferLetterStatus;
|
|
@@ -487,7 +487,7 @@ export type OfferLetterUpdateInput = {
|
|
|
487
487
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string;
|
|
488
488
|
tenant?: Prisma.TenantUpdateOneRequiredWithoutOfferLettersNestedInput;
|
|
489
489
|
contract?: Prisma.ContractUpdateOneRequiredWithoutOfferLettersNestedInput;
|
|
490
|
-
template?: Prisma.
|
|
490
|
+
template?: Prisma.DocumentTemplateUpdateOneWithoutOfferLettersNestedInput;
|
|
491
491
|
generatedBy?: Prisma.UserUpdateOneWithoutOfferLettersGeneratedNestedInput;
|
|
492
492
|
sentBy?: Prisma.UserUpdateOneWithoutOfferLettersSentNestedInput;
|
|
493
493
|
};
|
|
@@ -495,7 +495,7 @@ export type OfferLetterUncheckedUpdateInput = {
|
|
|
495
495
|
id?: Prisma.StringFieldUpdateOperationsInput | string;
|
|
496
496
|
tenantId?: Prisma.StringFieldUpdateOperationsInput | string;
|
|
497
497
|
contractId?: Prisma.StringFieldUpdateOperationsInput | string;
|
|
498
|
-
templateId?: Prisma.
|
|
498
|
+
templateId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null;
|
|
499
499
|
letterNumber?: Prisma.StringFieldUpdateOperationsInput | string;
|
|
500
500
|
type?: Prisma.EnumOfferLetterTypeFieldUpdateOperationsInput | $Enums.OfferLetterType;
|
|
501
501
|
status?: Prisma.EnumOfferLetterStatusFieldUpdateOperationsInput | $Enums.OfferLetterStatus;
|
|
@@ -521,7 +521,7 @@ export type OfferLetterCreateManyInput = {
|
|
|
521
521
|
id?: string;
|
|
522
522
|
tenantId: string;
|
|
523
523
|
contractId: string;
|
|
524
|
-
templateId
|
|
524
|
+
templateId?: string | null;
|
|
525
525
|
letterNumber: string;
|
|
526
526
|
type: $Enums.OfferLetterType;
|
|
527
527
|
status?: $Enums.OfferLetterStatus;
|
|
@@ -568,7 +568,7 @@ export type OfferLetterUncheckedUpdateManyInput = {
|
|
|
568
568
|
id?: Prisma.StringFieldUpdateOperationsInput | string;
|
|
569
569
|
tenantId?: Prisma.StringFieldUpdateOperationsInput | string;
|
|
570
570
|
contractId?: Prisma.StringFieldUpdateOperationsInput | string;
|
|
571
|
-
templateId?: Prisma.
|
|
571
|
+
templateId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null;
|
|
572
572
|
letterNumber?: Prisma.StringFieldUpdateOperationsInput | string;
|
|
573
573
|
type?: Prisma.EnumOfferLetterTypeFieldUpdateOperationsInput | $Enums.OfferLetterType;
|
|
574
574
|
status?: Prisma.EnumOfferLetterStatusFieldUpdateOperationsInput | $Enums.OfferLetterStatus;
|
|
@@ -895,14 +895,14 @@ export type OfferLetterCreateWithoutGeneratedByInput = {
|
|
|
895
895
|
updatedAt?: Date | string;
|
|
896
896
|
tenant: Prisma.TenantCreateNestedOneWithoutOfferLettersInput;
|
|
897
897
|
contract: Prisma.ContractCreateNestedOneWithoutOfferLettersInput;
|
|
898
|
-
template
|
|
898
|
+
template?: Prisma.DocumentTemplateCreateNestedOneWithoutOfferLettersInput;
|
|
899
899
|
sentBy?: Prisma.UserCreateNestedOneWithoutOfferLettersSentInput;
|
|
900
900
|
};
|
|
901
901
|
export type OfferLetterUncheckedCreateWithoutGeneratedByInput = {
|
|
902
902
|
id?: string;
|
|
903
903
|
tenantId: string;
|
|
904
904
|
contractId: string;
|
|
905
|
-
templateId
|
|
905
|
+
templateId?: string | null;
|
|
906
906
|
letterNumber: string;
|
|
907
907
|
type: $Enums.OfferLetterType;
|
|
908
908
|
status?: $Enums.OfferLetterStatus;
|
|
@@ -953,14 +953,14 @@ export type OfferLetterCreateWithoutSentByInput = {
|
|
|
953
953
|
updatedAt?: Date | string;
|
|
954
954
|
tenant: Prisma.TenantCreateNestedOneWithoutOfferLettersInput;
|
|
955
955
|
contract: Prisma.ContractCreateNestedOneWithoutOfferLettersInput;
|
|
956
|
-
template
|
|
956
|
+
template?: Prisma.DocumentTemplateCreateNestedOneWithoutOfferLettersInput;
|
|
957
957
|
generatedBy?: Prisma.UserCreateNestedOneWithoutOfferLettersGeneratedInput;
|
|
958
958
|
};
|
|
959
959
|
export type OfferLetterUncheckedCreateWithoutSentByInput = {
|
|
960
960
|
id?: string;
|
|
961
961
|
tenantId: string;
|
|
962
962
|
contractId: string;
|
|
963
|
-
templateId
|
|
963
|
+
templateId?: string | null;
|
|
964
964
|
letterNumber: string;
|
|
965
965
|
type: $Enums.OfferLetterType;
|
|
966
966
|
status?: $Enums.OfferLetterStatus;
|
|
@@ -1009,7 +1009,7 @@ export type OfferLetterScalarWhereInput = {
|
|
|
1009
1009
|
id?: Prisma.StringFilter<"OfferLetter"> | string;
|
|
1010
1010
|
tenantId?: Prisma.StringFilter<"OfferLetter"> | string;
|
|
1011
1011
|
contractId?: Prisma.StringFilter<"OfferLetter"> | string;
|
|
1012
|
-
templateId?: Prisma.
|
|
1012
|
+
templateId?: Prisma.StringNullableFilter<"OfferLetter"> | string | null;
|
|
1013
1013
|
letterNumber?: Prisma.StringFilter<"OfferLetter"> | string;
|
|
1014
1014
|
type?: Prisma.EnumOfferLetterTypeFilter<"OfferLetter"> | $Enums.OfferLetterType;
|
|
1015
1015
|
status?: Prisma.EnumOfferLetterStatusFilter<"OfferLetter"> | $Enums.OfferLetterStatus;
|
|
@@ -1065,14 +1065,14 @@ export type OfferLetterCreateWithoutTenantInput = {
|
|
|
1065
1065
|
createdAt?: Date | string;
|
|
1066
1066
|
updatedAt?: Date | string;
|
|
1067
1067
|
contract: Prisma.ContractCreateNestedOneWithoutOfferLettersInput;
|
|
1068
|
-
template
|
|
1068
|
+
template?: Prisma.DocumentTemplateCreateNestedOneWithoutOfferLettersInput;
|
|
1069
1069
|
generatedBy?: Prisma.UserCreateNestedOneWithoutOfferLettersGeneratedInput;
|
|
1070
1070
|
sentBy?: Prisma.UserCreateNestedOneWithoutOfferLettersSentInput;
|
|
1071
1071
|
};
|
|
1072
1072
|
export type OfferLetterUncheckedCreateWithoutTenantInput = {
|
|
1073
1073
|
id?: string;
|
|
1074
1074
|
contractId: string;
|
|
1075
|
-
templateId
|
|
1075
|
+
templateId?: string | null;
|
|
1076
1076
|
letterNumber: string;
|
|
1077
1077
|
type: $Enums.OfferLetterType;
|
|
1078
1078
|
status?: $Enums.OfferLetterStatus;
|
|
@@ -1136,14 +1136,14 @@ export type OfferLetterCreateWithoutContractInput = {
|
|
|
1136
1136
|
createdAt?: Date | string;
|
|
1137
1137
|
updatedAt?: Date | string;
|
|
1138
1138
|
tenant: Prisma.TenantCreateNestedOneWithoutOfferLettersInput;
|
|
1139
|
-
template
|
|
1139
|
+
template?: Prisma.DocumentTemplateCreateNestedOneWithoutOfferLettersInput;
|
|
1140
1140
|
generatedBy?: Prisma.UserCreateNestedOneWithoutOfferLettersGeneratedInput;
|
|
1141
1141
|
sentBy?: Prisma.UserCreateNestedOneWithoutOfferLettersSentInput;
|
|
1142
1142
|
};
|
|
1143
1143
|
export type OfferLetterUncheckedCreateWithoutContractInput = {
|
|
1144
1144
|
id?: string;
|
|
1145
1145
|
tenantId: string;
|
|
1146
|
-
templateId
|
|
1146
|
+
templateId?: string | null;
|
|
1147
1147
|
letterNumber: string;
|
|
1148
1148
|
type: $Enums.OfferLetterType;
|
|
1149
1149
|
status?: $Enums.OfferLetterStatus;
|
|
@@ -1261,7 +1261,7 @@ export type OfferLetterCreateManyGeneratedByInput = {
|
|
|
1261
1261
|
id?: string;
|
|
1262
1262
|
tenantId: string;
|
|
1263
1263
|
contractId: string;
|
|
1264
|
-
templateId
|
|
1264
|
+
templateId?: string | null;
|
|
1265
1265
|
letterNumber: string;
|
|
1266
1266
|
type: $Enums.OfferLetterType;
|
|
1267
1267
|
status?: $Enums.OfferLetterStatus;
|
|
@@ -1286,7 +1286,7 @@ export type OfferLetterCreateManySentByInput = {
|
|
|
1286
1286
|
id?: string;
|
|
1287
1287
|
tenantId: string;
|
|
1288
1288
|
contractId: string;
|
|
1289
|
-
templateId
|
|
1289
|
+
templateId?: string | null;
|
|
1290
1290
|
letterNumber: string;
|
|
1291
1291
|
type: $Enums.OfferLetterType;
|
|
1292
1292
|
status?: $Enums.OfferLetterStatus;
|
|
@@ -1329,14 +1329,14 @@ export type OfferLetterUpdateWithoutGeneratedByInput = {
|
|
|
1329
1329
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string;
|
|
1330
1330
|
tenant?: Prisma.TenantUpdateOneRequiredWithoutOfferLettersNestedInput;
|
|
1331
1331
|
contract?: Prisma.ContractUpdateOneRequiredWithoutOfferLettersNestedInput;
|
|
1332
|
-
template?: Prisma.
|
|
1332
|
+
template?: Prisma.DocumentTemplateUpdateOneWithoutOfferLettersNestedInput;
|
|
1333
1333
|
sentBy?: Prisma.UserUpdateOneWithoutOfferLettersSentNestedInput;
|
|
1334
1334
|
};
|
|
1335
1335
|
export type OfferLetterUncheckedUpdateWithoutGeneratedByInput = {
|
|
1336
1336
|
id?: Prisma.StringFieldUpdateOperationsInput | string;
|
|
1337
1337
|
tenantId?: Prisma.StringFieldUpdateOperationsInput | string;
|
|
1338
1338
|
contractId?: Prisma.StringFieldUpdateOperationsInput | string;
|
|
1339
|
-
templateId?: Prisma.
|
|
1339
|
+
templateId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null;
|
|
1340
1340
|
letterNumber?: Prisma.StringFieldUpdateOperationsInput | string;
|
|
1341
1341
|
type?: Prisma.EnumOfferLetterTypeFieldUpdateOperationsInput | $Enums.OfferLetterType;
|
|
1342
1342
|
status?: Prisma.EnumOfferLetterStatusFieldUpdateOperationsInput | $Enums.OfferLetterStatus;
|
|
@@ -1361,7 +1361,7 @@ export type OfferLetterUncheckedUpdateManyWithoutGeneratedByInput = {
|
|
|
1361
1361
|
id?: Prisma.StringFieldUpdateOperationsInput | string;
|
|
1362
1362
|
tenantId?: Prisma.StringFieldUpdateOperationsInput | string;
|
|
1363
1363
|
contractId?: Prisma.StringFieldUpdateOperationsInput | string;
|
|
1364
|
-
templateId?: Prisma.
|
|
1364
|
+
templateId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null;
|
|
1365
1365
|
letterNumber?: Prisma.StringFieldUpdateOperationsInput | string;
|
|
1366
1366
|
type?: Prisma.EnumOfferLetterTypeFieldUpdateOperationsInput | $Enums.OfferLetterType;
|
|
1367
1367
|
status?: Prisma.EnumOfferLetterStatusFieldUpdateOperationsInput | $Enums.OfferLetterStatus;
|
|
@@ -1404,14 +1404,14 @@ export type OfferLetterUpdateWithoutSentByInput = {
|
|
|
1404
1404
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string;
|
|
1405
1405
|
tenant?: Prisma.TenantUpdateOneRequiredWithoutOfferLettersNestedInput;
|
|
1406
1406
|
contract?: Prisma.ContractUpdateOneRequiredWithoutOfferLettersNestedInput;
|
|
1407
|
-
template?: Prisma.
|
|
1407
|
+
template?: Prisma.DocumentTemplateUpdateOneWithoutOfferLettersNestedInput;
|
|
1408
1408
|
generatedBy?: Prisma.UserUpdateOneWithoutOfferLettersGeneratedNestedInput;
|
|
1409
1409
|
};
|
|
1410
1410
|
export type OfferLetterUncheckedUpdateWithoutSentByInput = {
|
|
1411
1411
|
id?: Prisma.StringFieldUpdateOperationsInput | string;
|
|
1412
1412
|
tenantId?: Prisma.StringFieldUpdateOperationsInput | string;
|
|
1413
1413
|
contractId?: Prisma.StringFieldUpdateOperationsInput | string;
|
|
1414
|
-
templateId?: Prisma.
|
|
1414
|
+
templateId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null;
|
|
1415
1415
|
letterNumber?: Prisma.StringFieldUpdateOperationsInput | string;
|
|
1416
1416
|
type?: Prisma.EnumOfferLetterTypeFieldUpdateOperationsInput | $Enums.OfferLetterType;
|
|
1417
1417
|
status?: Prisma.EnumOfferLetterStatusFieldUpdateOperationsInput | $Enums.OfferLetterStatus;
|
|
@@ -1436,7 +1436,7 @@ export type OfferLetterUncheckedUpdateManyWithoutSentByInput = {
|
|
|
1436
1436
|
id?: Prisma.StringFieldUpdateOperationsInput | string;
|
|
1437
1437
|
tenantId?: Prisma.StringFieldUpdateOperationsInput | string;
|
|
1438
1438
|
contractId?: Prisma.StringFieldUpdateOperationsInput | string;
|
|
1439
|
-
templateId?: Prisma.
|
|
1439
|
+
templateId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null;
|
|
1440
1440
|
letterNumber?: Prisma.StringFieldUpdateOperationsInput | string;
|
|
1441
1441
|
type?: Prisma.EnumOfferLetterTypeFieldUpdateOperationsInput | $Enums.OfferLetterType;
|
|
1442
1442
|
status?: Prisma.EnumOfferLetterStatusFieldUpdateOperationsInput | $Enums.OfferLetterStatus;
|
|
@@ -1460,7 +1460,7 @@ export type OfferLetterUncheckedUpdateManyWithoutSentByInput = {
|
|
|
1460
1460
|
export type OfferLetterCreateManyTenantInput = {
|
|
1461
1461
|
id?: string;
|
|
1462
1462
|
contractId: string;
|
|
1463
|
-
templateId
|
|
1463
|
+
templateId?: string | null;
|
|
1464
1464
|
letterNumber: string;
|
|
1465
1465
|
type: $Enums.OfferLetterType;
|
|
1466
1466
|
status?: $Enums.OfferLetterStatus;
|
|
@@ -1503,14 +1503,14 @@ export type OfferLetterUpdateWithoutTenantInput = {
|
|
|
1503
1503
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string;
|
|
1504
1504
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string;
|
|
1505
1505
|
contract?: Prisma.ContractUpdateOneRequiredWithoutOfferLettersNestedInput;
|
|
1506
|
-
template?: Prisma.
|
|
1506
|
+
template?: Prisma.DocumentTemplateUpdateOneWithoutOfferLettersNestedInput;
|
|
1507
1507
|
generatedBy?: Prisma.UserUpdateOneWithoutOfferLettersGeneratedNestedInput;
|
|
1508
1508
|
sentBy?: Prisma.UserUpdateOneWithoutOfferLettersSentNestedInput;
|
|
1509
1509
|
};
|
|
1510
1510
|
export type OfferLetterUncheckedUpdateWithoutTenantInput = {
|
|
1511
1511
|
id?: Prisma.StringFieldUpdateOperationsInput | string;
|
|
1512
1512
|
contractId?: Prisma.StringFieldUpdateOperationsInput | string;
|
|
1513
|
-
templateId?: Prisma.
|
|
1513
|
+
templateId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null;
|
|
1514
1514
|
letterNumber?: Prisma.StringFieldUpdateOperationsInput | string;
|
|
1515
1515
|
type?: Prisma.EnumOfferLetterTypeFieldUpdateOperationsInput | $Enums.OfferLetterType;
|
|
1516
1516
|
status?: Prisma.EnumOfferLetterStatusFieldUpdateOperationsInput | $Enums.OfferLetterStatus;
|
|
@@ -1535,7 +1535,7 @@ export type OfferLetterUncheckedUpdateWithoutTenantInput = {
|
|
|
1535
1535
|
export type OfferLetterUncheckedUpdateManyWithoutTenantInput = {
|
|
1536
1536
|
id?: Prisma.StringFieldUpdateOperationsInput | string;
|
|
1537
1537
|
contractId?: Prisma.StringFieldUpdateOperationsInput | string;
|
|
1538
|
-
templateId?: Prisma.
|
|
1538
|
+
templateId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null;
|
|
1539
1539
|
letterNumber?: Prisma.StringFieldUpdateOperationsInput | string;
|
|
1540
1540
|
type?: Prisma.EnumOfferLetterTypeFieldUpdateOperationsInput | $Enums.OfferLetterType;
|
|
1541
1541
|
status?: Prisma.EnumOfferLetterStatusFieldUpdateOperationsInput | $Enums.OfferLetterStatus;
|
|
@@ -1560,7 +1560,7 @@ export type OfferLetterUncheckedUpdateManyWithoutTenantInput = {
|
|
|
1560
1560
|
export type OfferLetterCreateManyContractInput = {
|
|
1561
1561
|
id?: string;
|
|
1562
1562
|
tenantId: string;
|
|
1563
|
-
templateId
|
|
1563
|
+
templateId?: string | null;
|
|
1564
1564
|
letterNumber: string;
|
|
1565
1565
|
type: $Enums.OfferLetterType;
|
|
1566
1566
|
status?: $Enums.OfferLetterStatus;
|
|
@@ -1603,14 +1603,14 @@ export type OfferLetterUpdateWithoutContractInput = {
|
|
|
1603
1603
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string;
|
|
1604
1604
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string;
|
|
1605
1605
|
tenant?: Prisma.TenantUpdateOneRequiredWithoutOfferLettersNestedInput;
|
|
1606
|
-
template?: Prisma.
|
|
1606
|
+
template?: Prisma.DocumentTemplateUpdateOneWithoutOfferLettersNestedInput;
|
|
1607
1607
|
generatedBy?: Prisma.UserUpdateOneWithoutOfferLettersGeneratedNestedInput;
|
|
1608
1608
|
sentBy?: Prisma.UserUpdateOneWithoutOfferLettersSentNestedInput;
|
|
1609
1609
|
};
|
|
1610
1610
|
export type OfferLetterUncheckedUpdateWithoutContractInput = {
|
|
1611
1611
|
id?: Prisma.StringFieldUpdateOperationsInput | string;
|
|
1612
1612
|
tenantId?: Prisma.StringFieldUpdateOperationsInput | string;
|
|
1613
|
-
templateId?: Prisma.
|
|
1613
|
+
templateId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null;
|
|
1614
1614
|
letterNumber?: Prisma.StringFieldUpdateOperationsInput | string;
|
|
1615
1615
|
type?: Prisma.EnumOfferLetterTypeFieldUpdateOperationsInput | $Enums.OfferLetterType;
|
|
1616
1616
|
status?: Prisma.EnumOfferLetterStatusFieldUpdateOperationsInput | $Enums.OfferLetterStatus;
|
|
@@ -1635,7 +1635,7 @@ export type OfferLetterUncheckedUpdateWithoutContractInput = {
|
|
|
1635
1635
|
export type OfferLetterUncheckedUpdateManyWithoutContractInput = {
|
|
1636
1636
|
id?: Prisma.StringFieldUpdateOperationsInput | string;
|
|
1637
1637
|
tenantId?: Prisma.StringFieldUpdateOperationsInput | string;
|
|
1638
|
-
templateId?: Prisma.
|
|
1638
|
+
templateId?: Prisma.NullableStringFieldUpdateOperationsInput | string | null;
|
|
1639
1639
|
letterNumber?: Prisma.StringFieldUpdateOperationsInput | string;
|
|
1640
1640
|
type?: Prisma.EnumOfferLetterTypeFieldUpdateOperationsInput | $Enums.OfferLetterType;
|
|
1641
1641
|
status?: Prisma.EnumOfferLetterStatusFieldUpdateOperationsInput | $Enums.OfferLetterStatus;
|
|
@@ -1784,7 +1784,7 @@ export type OfferLetterSelect<ExtArgs extends runtime.Types.Extensions.InternalA
|
|
|
1784
1784
|
updatedAt?: boolean;
|
|
1785
1785
|
tenant?: boolean | Prisma.TenantDefaultArgs<ExtArgs>;
|
|
1786
1786
|
contract?: boolean | Prisma.ContractDefaultArgs<ExtArgs>;
|
|
1787
|
-
template?: boolean | Prisma.
|
|
1787
|
+
template?: boolean | Prisma.OfferLetter$templateArgs<ExtArgs>;
|
|
1788
1788
|
generatedBy?: boolean | Prisma.OfferLetter$generatedByArgs<ExtArgs>;
|
|
1789
1789
|
sentBy?: boolean | Prisma.OfferLetter$sentByArgs<ExtArgs>;
|
|
1790
1790
|
}, ExtArgs["result"]["offerLetter"]>;
|
|
@@ -1818,7 +1818,7 @@ export type OfferLetterOmit<ExtArgs extends runtime.Types.Extensions.InternalArg
|
|
|
1818
1818
|
export type OfferLetterInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
|
1819
1819
|
tenant?: boolean | Prisma.TenantDefaultArgs<ExtArgs>;
|
|
1820
1820
|
contract?: boolean | Prisma.ContractDefaultArgs<ExtArgs>;
|
|
1821
|
-
template?: boolean | Prisma.
|
|
1821
|
+
template?: boolean | Prisma.OfferLetter$templateArgs<ExtArgs>;
|
|
1822
1822
|
generatedBy?: boolean | Prisma.OfferLetter$generatedByArgs<ExtArgs>;
|
|
1823
1823
|
sentBy?: boolean | Prisma.OfferLetter$sentByArgs<ExtArgs>;
|
|
1824
1824
|
};
|
|
@@ -1827,7 +1827,7 @@ export type $OfferLetterPayload<ExtArgs extends runtime.Types.Extensions.Interna
|
|
|
1827
1827
|
objects: {
|
|
1828
1828
|
tenant: Prisma.$TenantPayload<ExtArgs>;
|
|
1829
1829
|
contract: Prisma.$ContractPayload<ExtArgs>;
|
|
1830
|
-
template: Prisma.$DocumentTemplatePayload<ExtArgs
|
|
1830
|
+
template: Prisma.$DocumentTemplatePayload<ExtArgs> | null;
|
|
1831
1831
|
generatedBy: Prisma.$UserPayload<ExtArgs> | null;
|
|
1832
1832
|
sentBy: Prisma.$UserPayload<ExtArgs> | null;
|
|
1833
1833
|
};
|
|
@@ -1835,7 +1835,7 @@ export type $OfferLetterPayload<ExtArgs extends runtime.Types.Extensions.Interna
|
|
|
1835
1835
|
id: string;
|
|
1836
1836
|
tenantId: string;
|
|
1837
1837
|
contractId: string;
|
|
1838
|
-
templateId: string;
|
|
1838
|
+
templateId: string | null;
|
|
1839
1839
|
letterNumber: string;
|
|
1840
1840
|
type: $Enums.OfferLetterType;
|
|
1841
1841
|
status: $Enums.OfferLetterStatus;
|
|
@@ -2135,7 +2135,7 @@ export interface Prisma__OfferLetterClient<T, Null = never, ExtArgs extends runt
|
|
|
2135
2135
|
readonly [Symbol.toStringTag]: "PrismaPromise";
|
|
2136
2136
|
tenant<T extends Prisma.TenantDefaultArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.TenantDefaultArgs<ExtArgs>>): Prisma.Prisma__TenantClient<runtime.Types.Result.GetResult<Prisma.$TenantPayload<ExtArgs>, T, "findUniqueOrThrow", GlobalOmitOptions> | Null, Null, ExtArgs, GlobalOmitOptions>;
|
|
2137
2137
|
contract<T extends Prisma.ContractDefaultArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.ContractDefaultArgs<ExtArgs>>): Prisma.Prisma__ContractClient<runtime.Types.Result.GetResult<Prisma.$ContractPayload<ExtArgs>, T, "findUniqueOrThrow", GlobalOmitOptions> | Null, Null, ExtArgs, GlobalOmitOptions>;
|
|
2138
|
-
template<T extends Prisma.
|
|
2138
|
+
template<T extends Prisma.OfferLetter$templateArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.OfferLetter$templateArgs<ExtArgs>>): Prisma.Prisma__DocumentTemplateClient<runtime.Types.Result.GetResult<Prisma.$DocumentTemplatePayload<ExtArgs>, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions>;
|
|
2139
2139
|
generatedBy<T extends Prisma.OfferLetter$generatedByArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.OfferLetter$generatedByArgs<ExtArgs>>): Prisma.Prisma__UserClient<runtime.Types.Result.GetResult<Prisma.$UserPayload<ExtArgs>, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions>;
|
|
2140
2140
|
sentBy<T extends Prisma.OfferLetter$sentByArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.OfferLetter$sentByArgs<ExtArgs>>): Prisma.Prisma__UserClient<runtime.Types.Result.GetResult<Prisma.$UserPayload<ExtArgs>, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions>;
|
|
2141
2141
|
/**
|
|
@@ -2514,6 +2514,24 @@ export type OfferLetterDeleteManyArgs<ExtArgs extends runtime.Types.Extensions.I
|
|
|
2514
2514
|
*/
|
|
2515
2515
|
limit?: number;
|
|
2516
2516
|
};
|
|
2517
|
+
/**
|
|
2518
|
+
* OfferLetter.template
|
|
2519
|
+
*/
|
|
2520
|
+
export type OfferLetter$templateArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
|
2521
|
+
/**
|
|
2522
|
+
* Select specific fields to fetch from the DocumentTemplate
|
|
2523
|
+
*/
|
|
2524
|
+
select?: Prisma.DocumentTemplateSelect<ExtArgs> | null;
|
|
2525
|
+
/**
|
|
2526
|
+
* Omit specific fields from the DocumentTemplate
|
|
2527
|
+
*/
|
|
2528
|
+
omit?: Prisma.DocumentTemplateOmit<ExtArgs> | null;
|
|
2529
|
+
/**
|
|
2530
|
+
* Choose, which related nodes to fetch as well
|
|
2531
|
+
*/
|
|
2532
|
+
include?: Prisma.DocumentTemplateInclude<ExtArgs> | null;
|
|
2533
|
+
where?: Prisma.DocumentTemplateWhereInput;
|
|
2534
|
+
};
|
|
2517
2535
|
/**
|
|
2518
2536
|
* OfferLetter.generatedBy
|
|
2519
2537
|
*/
|
|
@@ -1,11 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Models that require tenant scoping
|
|
2
|
+
* Models that require tenant scoping.
|
|
3
|
+
*
|
|
4
|
+
* IMPORTANT: When adding a new model with tenantId to the schema,
|
|
5
|
+
* add it to this list so queries are automatically filtered by tenant.
|
|
6
|
+
*
|
|
7
|
+
* Models with nullable tenantId (for global templates) should also be
|
|
8
|
+
* added to OPTIONAL_TENANT_MODELS below.
|
|
3
9
|
*/
|
|
4
10
|
const TENANT_SCOPED_MODELS = [
|
|
11
|
+
// Property domain
|
|
5
12
|
"property",
|
|
6
|
-
"paymentPlan",
|
|
7
13
|
"propertyPaymentMethod",
|
|
14
|
+
// Payment plan domain
|
|
15
|
+
"paymentPlan",
|
|
16
|
+
// Contract domain
|
|
8
17
|
"contract",
|
|
18
|
+
"contractTermination",
|
|
19
|
+
// Document domain
|
|
20
|
+
"documentTemplate",
|
|
21
|
+
"offerLetter",
|
|
22
|
+
"documentRequirementRule",
|
|
23
|
+
// Prequalification & underwriting domain
|
|
24
|
+
"prequalification",
|
|
25
|
+
"underwritingDecision",
|
|
26
|
+
// Payment method changes
|
|
27
|
+
"paymentMethodChangeRequest",
|
|
9
28
|
];
|
|
10
29
|
/**
|
|
11
30
|
* Models that can optionally have tenant scoping (nullable tenantId)
|
package/package.json
CHANGED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
-- DropForeignKey
|
|
2
|
+
ALTER TABLE `offer_letters` DROP FOREIGN KEY `offer_letters_templateId_fkey`;
|
|
3
|
+
|
|
4
|
+
-- DropIndex
|
|
5
|
+
DROP INDEX `offer_letters_templateId_fkey` ON `offer_letters`;
|
|
6
|
+
|
|
7
|
+
-- AlterTable
|
|
8
|
+
ALTER TABLE `offer_letters` MODIFY `templateId` VARCHAR(191) NULL;
|
|
9
|
+
|
|
10
|
+
-- AddForeignKey
|
|
11
|
+
ALTER TABLE `offer_letters` ADD CONSTRAINT `offer_letters_templateId_fkey` FOREIGN KEY (`templateId`) REFERENCES `document_templates`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
|
package/prisma/schema.prisma
CHANGED
|
@@ -1261,9 +1261,9 @@ model OfferLetter {
|
|
|
1261
1261
|
contractId String
|
|
1262
1262
|
contract Contract @relation(fields: [contractId], references: [id], onDelete: Cascade)
|
|
1263
1263
|
|
|
1264
|
-
// Template used
|
|
1265
|
-
templateId String
|
|
1266
|
-
template DocumentTemplate @relation(fields: [templateId], references: [id])
|
|
1264
|
+
// Template used (optional - documents-service may handle default selection)
|
|
1265
|
+
templateId String?
|
|
1266
|
+
template DocumentTemplate? @relation(fields: [templateId], references: [id])
|
|
1267
1267
|
|
|
1268
1268
|
// Letter details
|
|
1269
1269
|
letterNumber String @unique // OL-XXXXXX
|