@valentine-efagene/qshelter-common 2.0.124 → 2.0.126

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.
@@ -54,6 +54,7 @@ export type * from './models/DocumentationStepApproval.js';
54
54
  export type * from './models/PaymentInstallment.js';
55
55
  export type * from './models/ApplicationPayment.js';
56
56
  export type * from './models/ApplicationDocument.js';
57
+ export type * from './models/DocumentReview.js';
57
58
  export type * from './models/DocumentTemplate.js';
58
59
  export type * from './models/OfferLetter.js';
59
60
  export type * from './models/ApplicationTermination.js';
@@ -45,13 +45,19 @@ export type StepCondition = SimpleCondition | CompoundCondition;
45
45
  *
46
46
  * // Score 80 if income >= 2000000
47
47
  * { operator: ConditionOperator.GREATER_THAN_OR_EQUAL, value: 2000000, score: 80 }
48
+ *
49
+ * // Score 10 if married is true
50
+ * { operator: ConditionOperator.EQUALS, value: true, score: 10 }
51
+ *
52
+ * // Score 100 if employment_status is "employed"
53
+ * { operator: ConditionOperator.EQUALS, value: "employed", score: 100 }
48
54
  * ```
49
55
  */
50
56
  export interface ScoringRule {
51
57
  /** The comparison operator for the condition */
52
58
  operator: ConditionOperator;
53
- /** The value to compare against */
54
- value: number;
59
+ /** The value to compare against (number for numeric comparisons, boolean/string for EQUALS/NOT_EQUALS) */
60
+ value: number | boolean | string;
55
61
  /** The score to assign if the condition is met */
56
62
  score: number;
57
63
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@valentine-efagene/qshelter-common",
3
- "version": "2.0.124",
3
+ "version": "2.0.126",
4
4
  "description": "Shared database schemas and utilities for QShelter services",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",
@@ -262,6 +262,30 @@ enum CompletionCriterion {
262
262
  STEPS_COMPLETED
263
263
  }
264
264
 
265
+ // =============================================================================
266
+ // MULTI-PARTY DOCUMENT REVIEW ENUMS
267
+ // =============================================================================
268
+ // Enables different organizations to review the same document independently
269
+ // =============================================================================
270
+
271
+ enum ReviewParty {
272
+ INTERNAL // Your internal team (compliance, underwriting, etc.)
273
+ BANK // Partner bank/lender
274
+ DEVELOPER // Property developer
275
+ LEGAL // Legal team
276
+ GOVERNMENT // Government agencies (e.g., land registry)
277
+ INSURER // Insurance company
278
+ CUSTOMER // Customer acknowledgment/signature
279
+ }
280
+
281
+ enum ReviewDecision {
282
+ PENDING // Review not yet performed
283
+ APPROVED // Document approved by this party
284
+ REJECTED // Document rejected (permanent)
285
+ CHANGES_REQUESTED // Document needs modifications
286
+ WAIVED // Review not required by this party
287
+ }
288
+
265
289
  enum DocumentStatus {
266
290
  DRAFT
267
291
  PENDING
@@ -460,6 +484,9 @@ model User {
460
484
  approvedRefunds ApplicationRefund[] @relation("RefundApprover")
461
485
  processedRefunds ApplicationRefund[] @relation("RefundProcessor")
462
486
 
487
+ // Document reviews by this user
488
+ documentReviews DocumentReview[] @relation("DocumentReviewer")
489
+
463
490
  // Organization memberships (user can be employee of banks/developers)
464
491
  organizationMemberships OrganizationMember[]
465
492
 
@@ -747,6 +774,7 @@ model Tenant {
747
774
  domainEvents DomainEvent[]
748
775
  workflowBlockers WorkflowBlocker[]
749
776
  questionnairePlans QuestionnairePlan[]
777
+ documentReviews DocumentReview[]
750
778
 
751
779
  // Organizations (Banks, Developers) operating on this tenant
752
780
  organizations Organization[]
@@ -1271,6 +1299,17 @@ model DocumentationPlanStep {
1271
1299
  // { "all": [{ "questionKey": "has_spouse", "operator": "EQUALS", "value": "YES" }, { "questionKey": "spouse_employed", "operator": "EQUALS", "value": "YES" }] }
1272
1300
  condition Json?
1273
1301
 
1302
+ // =========================================================================
1303
+ // MULTI-PARTY REVIEW CONFIGURATION (for UPLOAD steps)
1304
+ // =========================================================================
1305
+ // Defines which parties must review documents uploaded in this step
1306
+ // Format: [{ party: "INTERNAL", required: true }, { party: "BANK", required: true }]
1307
+ // If null, defaults to single internal review (backward compatible)
1308
+ reviewRequirements Json?
1309
+
1310
+ // Review order: 'SEQUENTIAL' = parties review in order, 'PARALLEL' = all review simultaneously
1311
+ reviewOrder String? // 'SEQUENTIAL' | 'PARALLEL'
1312
+
1274
1313
  // =========================================================================
1275
1314
  // GATE STEP CONFIGURATION (only applicable when stepType = GATE)
1276
1315
  // =========================================================================
@@ -1357,7 +1396,11 @@ model QuestionnairePlanQuestion {
1357
1396
 
1358
1397
  // Scoring
1359
1398
  scoreWeight Int @default(1) // Multiplier for this question's score
1360
- scoringRules Json? // { "employed": 10, "self_employed": 7, "unemployed": 0 } or ranges
1399
+ // ScoringRules format: Array of { operator: ConditionOperator, value: number, score: number }
1400
+ // First matching rule wins. Example:
1401
+ // [{ operator: "GREATER_THAN_OR_EQUAL", value: 3000000, score: 100 },
1402
+ // { operator: "GREATER_THAN_OR_EQUAL", value: 2000000, score: 80 }]
1403
+ scoringRules Json?
1361
1404
 
1362
1405
  // Conditional logic (branching)
1363
1406
  // { "questionKey": "employment_status", "equals": "employed" }
@@ -2286,6 +2329,17 @@ model DocumentationStep {
2286
2329
  // Or: { "questionKey": "employment_status", "operator": "IN", "values": ["SELF_EMPLOYED", "BUSINESS_OWNER"] }
2287
2330
  condition Json?
2288
2331
 
2332
+ // =========================================================================
2333
+ // MULTI-PARTY REVIEW CONFIGURATION (for UPLOAD steps)
2334
+ // =========================================================================
2335
+ // Defines which parties must review documents uploaded in this step
2336
+ // Format: [{ party: "INTERNAL", required: true }, { party: "BANK", required: true }]
2337
+ // If null, defaults to single internal review (backward compatible)
2338
+ reviewRequirements Json?
2339
+
2340
+ // Review order: 'SEQUENTIAL' = parties review in order, 'PARALLEL' = all review simultaneously
2341
+ reviewOrder String? // 'SEQUENTIAL' | 'PARALLEL'
2342
+
2289
2343
  // Assignment
2290
2344
  assigneeId String?
2291
2345
  assignee User? @relation("DocumentationStepAssignee", fields: [assigneeId], references: [id])
@@ -2461,18 +2515,83 @@ model ApplicationDocument {
2461
2515
 
2462
2516
  status DocumentStatus @default(PENDING)
2463
2517
 
2518
+ // Document versioning (for re-uploads after changes requested)
2519
+ version Int @default(1)
2520
+ replacesDocumentId String? // If this is a revision, points to original
2521
+ replacesDocument ApplicationDocument? @relation("DocumentRevisions", fields: [replacesDocumentId], references: [id])
2522
+ revisions ApplicationDocument[] @relation("DocumentRevisions")
2523
+
2464
2524
  createdAt DateTime @default(now())
2465
2525
  updatedAt DateTime @updatedAt
2466
2526
 
2527
+ // Multi-party reviews for this document
2528
+ reviews DocumentReview[]
2529
+
2467
2530
  @@index([tenantId])
2468
2531
  @@index([applicationId])
2469
2532
  @@index([phaseId])
2470
2533
  @@index([stepId])
2471
2534
  @@index([type])
2472
2535
  @@index([status])
2536
+ @@index([replacesDocumentId])
2473
2537
  @@map("application_documents")
2474
2538
  }
2475
2539
 
2540
+ // =============================================================================
2541
+ // DOCUMENT REVIEW - Multi-party review tracking
2542
+ // =============================================================================
2543
+ // Tracks reviews by different organizations for the same document
2544
+ // Enables internal approval -> external approval workflows
2545
+ // =============================================================================
2546
+
2547
+ model DocumentReview {
2548
+ id String @id @default(cuid())
2549
+ tenantId String
2550
+ tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
2551
+ documentId String
2552
+ document ApplicationDocument @relation(fields: [documentId], references: [id], onDelete: Cascade)
2553
+
2554
+ // Who is reviewing
2555
+ reviewParty ReviewParty // INTERNAL, BANK, DEVELOPER, LEGAL, etc.
2556
+ organizationId String? // External organization ID if applicable
2557
+ reviewerId String? // User who performed the review
2558
+ reviewer User? @relation("DocumentReviewer", fields: [reviewerId], references: [id])
2559
+ reviewerName String? // Denormalized for audit trail
2560
+
2561
+ // Review state
2562
+ decision ReviewDecision @default(PENDING)
2563
+ comments String? @db.Text // General comments
2564
+
2565
+ // Structured concerns for change requests
2566
+ // Format: [{ field: "income_period", issue: "Missing months April-June" }]
2567
+ concerns Json?
2568
+
2569
+ // Review timing
2570
+ requestedAt DateTime @default(now())
2571
+ dueAt DateTime? // Optional deadline for this party
2572
+ reviewedAt DateTime? // When decision was made
2573
+
2574
+ // Review sequence (which order should parties review?)
2575
+ reviewOrder Int @default(0) // 0 = parallel, 1/2/3 = sequential
2576
+
2577
+ // For review chains (re-reviews after changes)
2578
+ parentReviewId String? // If this is a re-review after changes
2579
+ parentReview DocumentReview? @relation("ReviewChain", fields: [parentReviewId], references: [id])
2580
+ childReviews DocumentReview[] @relation("ReviewChain")
2581
+
2582
+ createdAt DateTime @default(now())
2583
+ updatedAt DateTime @updatedAt
2584
+
2585
+ @@unique([documentId, reviewParty, organizationId]) // One review per party per org per doc
2586
+ @@index([tenantId])
2587
+ @@index([documentId])
2588
+ @@index([reviewParty])
2589
+ @@index([decision])
2590
+ @@index([reviewerId])
2591
+ @@index([parentReviewId])
2592
+ @@map("document_reviews")
2593
+ }
2594
+
2476
2595
  // =============================================================================
2477
2596
  // OFFER LETTERS - Provisional and Final offer documents
2478
2597
  // =============================================================================