@valentine-efagene/qshelter-common 2.0.151 → 2.0.153

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/dist/generated/client/browser.d.ts +13 -2
  2. package/dist/generated/client/client.d.ts +15 -4
  3. package/dist/generated/client/client.js +2 -2
  4. package/dist/generated/client/commonInputTypes.d.ts +20 -190
  5. package/dist/generated/client/enums.d.ts +0 -34
  6. package/dist/generated/client/enums.js +0 -30
  7. package/dist/generated/client/internal/class.d.ts +29 -7
  8. package/dist/generated/client/internal/class.js +2 -2
  9. package/dist/generated/client/internal/prismaNamespace.d.ts +181 -32
  10. package/dist/generated/client/internal/prismaNamespace.js +42 -11
  11. package/dist/generated/client/internal/prismaNamespaceBrowser.d.ts +46 -11
  12. package/dist/generated/client/internal/prismaNamespaceBrowser.js +42 -11
  13. package/dist/generated/client/models/ApplicationOrganization.d.ts +308 -67
  14. package/dist/generated/client/models/ApprovalStage.d.ts +210 -91
  15. package/dist/generated/client/models/ApprovalStageProgress.d.ts +258 -69
  16. package/dist/generated/client/models/DocumentApproval.d.ts +196 -65
  17. package/dist/generated/client/models/DocumentReview.d.ts +475 -86
  18. package/dist/generated/client/models/Organization.d.ts +424 -52
  19. package/dist/generated/client/models/OrganizationMember.d.ts +42 -169
  20. package/dist/generated/client/models/OrganizationType.d.ts +1982 -0
  21. package/dist/generated/client/models/OrganizationType.js +1 -0
  22. package/dist/generated/client/models/OrganizationTypeAssignment.d.ts +1159 -0
  23. package/dist/generated/client/models/OrganizationTypeAssignment.js +1 -0
  24. package/dist/generated/client/models/Tenant.d.ts +575 -4
  25. package/dist/generated/client/models/User.d.ts +0 -12
  26. package/dist/generated/client/models/index.d.ts +2 -0
  27. package/dist/generated/client/models/index.js +2 -0
  28. package/dist/generated/client/models.d.ts +2 -0
  29. package/package.json +1 -1
  30. package/prisma/migrations/20260125102448_org_types_many_to_many/migration.sql +153 -0
  31. package/prisma/migrations/20260125103700_20260125102448_org_types_many_to_many_fix/migration.sql +5 -0
  32. package/prisma/schema.prisma +113 -79
@@ -760,18 +760,6 @@ export type UserNullableScalarRelationFilter = {
760
760
  is?: Prisma.UserWhereInput | null;
761
761
  isNot?: Prisma.UserWhereInput | null;
762
762
  };
763
- export type StringFieldUpdateOperationsInput = {
764
- set?: string;
765
- };
766
- export type NullableStringFieldUpdateOperationsInput = {
767
- set?: string | null;
768
- };
769
- export type BoolFieldUpdateOperationsInput = {
770
- set?: boolean;
771
- };
772
- export type DateTimeFieldUpdateOperationsInput = {
773
- set?: Date | string;
774
- };
775
763
  export type NullableDateTimeFieldUpdateOperationsInput = {
776
764
  set?: Date | string | null;
777
765
  };
@@ -30,6 +30,8 @@ export * from './OAuthState';
30
30
  export * from './OfferLetter';
31
31
  export * from './Organization';
32
32
  export * from './OrganizationMember';
33
+ export * from './OrganizationType';
34
+ export * from './OrganizationTypeAssignment';
33
35
  export * from './PasswordReset';
34
36
  export * from './PaymentInstallment';
35
37
  export * from './PaymentMethodChangeRequest';
@@ -30,6 +30,8 @@ export * from './OAuthState';
30
30
  export * from './OfferLetter';
31
31
  export * from './Organization';
32
32
  export * from './OrganizationMember';
33
+ export * from './OrganizationType';
34
+ export * from './OrganizationTypeAssignment';
33
35
  export * from './PasswordReset';
34
36
  export * from './PaymentInstallment';
35
37
  export * from './PaymentMethodChangeRequest';
@@ -1,3 +1,5 @@
1
+ export type * from './models/OrganizationType.js';
2
+ export type * from './models/OrganizationTypeAssignment.js';
1
3
  export type * from './models/User.js';
2
4
  export type * from './models/Role.js';
3
5
  export type * from './models/Permission.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@valentine-efagene/qshelter-common",
3
- "version": "2.0.151",
3
+ "version": "2.0.153",
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",
@@ -0,0 +1,153 @@
1
+ /*
2
+ Warnings:
3
+
4
+ - You are about to drop the column `role` on the `application_organizations` table. All the data in the column will be lost.
5
+ - You are about to drop the column `reviewParty` on the `approval_stage_progress` table. All the data in the column will be lost.
6
+ - You are about to drop the column `organizationId` on the `approval_stages` table. All the data in the column will be lost.
7
+ - You are about to drop the column `reviewParty` on the `approval_stages` table. All the data in the column will be lost.
8
+ - You are about to drop the column `reviewParty` on the `document_approvals` table. All the data in the column will be lost.
9
+ - You are about to drop the column `reviewParty` on the `document_reviews` table. All the data in the column will be lost.
10
+ - You are about to drop the column `approvalLimit` on the `organization_members` table. All the data in the column will be lost.
11
+ - You are about to drop the column `canApprove` on the `organization_members` table. All the data in the column will be lost.
12
+ - You are about to drop the column `role` on the `organization_members` table. All the data in the column will be lost.
13
+ - You are about to drop the column `type` on the `organizations` table. All the data in the column will be lost.
14
+ - A unique constraint covering the columns `[applicationId,assignedAsTypeId]` on the table `application_organizations` will be added. If there are existing duplicate values, this will fail.
15
+ - A unique constraint covering the columns `[documentId,organizationId]` on the table `document_reviews` will be added. If there are existing duplicate values, this will fail.
16
+ - Added the required column `assignedAsTypeId` to the `application_organizations` table without a default value. This is not possible if the table is not empty.
17
+ - Added the required column `organizationTypeId` to the `approval_stage_progress` table without a default value. This is not possible if the table is not empty.
18
+ - Added the required column `organizationTypeId` to the `approval_stages` table without a default value. This is not possible if the table is not empty.
19
+ - Added the required column `organizationTypeId` to the `document_approvals` table without a default value. This is not possible if the table is not empty.
20
+
21
+ */
22
+ -- DropForeignKey
23
+ ALTER TABLE `application_organizations` DROP FOREIGN KEY `application_organizations_applicationId_fkey`;
24
+
25
+ -- DropForeignKey
26
+ ALTER TABLE `document_reviews` DROP FOREIGN KEY `document_reviews_documentId_fkey`;
27
+
28
+ -- DropIndex
29
+ DROP INDEX `application_organizations_applicationId_organizationId_role_key` ON `application_organizations`;
30
+
31
+ -- DropIndex
32
+ DROP INDEX `application_organizations_role_idx` ON `application_organizations`;
33
+
34
+ -- DropIndex
35
+ DROP INDEX `document_reviews_documentId_reviewParty_organizationId_key` ON `document_reviews`;
36
+
37
+ -- DropIndex
38
+ DROP INDEX `document_reviews_reviewParty_idx` ON `document_reviews`;
39
+
40
+ -- DropIndex
41
+ DROP INDEX `organization_members_role_idx` ON `organization_members`;
42
+
43
+ -- DropIndex
44
+ DROP INDEX `organizations_type_idx` ON `organizations`;
45
+
46
+ -- AlterTable
47
+ ALTER TABLE `application_organizations` DROP COLUMN `role`,
48
+ ADD COLUMN `assignedAsTypeId` VARCHAR(191) NOT NULL;
49
+
50
+ -- AlterTable
51
+ ALTER TABLE `approval_stage_progress` DROP COLUMN `reviewParty`,
52
+ ADD COLUMN `organizationTypeId` VARCHAR(191) NOT NULL;
53
+
54
+ -- AlterTable
55
+ ALTER TABLE `approval_stages` DROP COLUMN `organizationId`,
56
+ DROP COLUMN `reviewParty`,
57
+ ADD COLUMN `organizationTypeId` VARCHAR(191) NOT NULL;
58
+
59
+ -- AlterTable
60
+ ALTER TABLE `document_approvals` DROP COLUMN `reviewParty`,
61
+ ADD COLUMN `organizationTypeId` VARCHAR(191) NOT NULL;
62
+
63
+ -- AlterTable
64
+ ALTER TABLE `document_reviews` DROP COLUMN `reviewParty`,
65
+ ADD COLUMN `organizationTypeId` VARCHAR(191) NULL;
66
+
67
+ -- AlterTable
68
+ ALTER TABLE `organization_members` DROP COLUMN `approvalLimit`,
69
+ DROP COLUMN `canApprove`,
70
+ DROP COLUMN `role`,
71
+ ADD COLUMN `joinedAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3);
72
+
73
+ -- AlterTable
74
+ ALTER TABLE `organizations` DROP COLUMN `type`;
75
+
76
+ -- CreateTable
77
+ CREATE TABLE `organization_types` (
78
+ `id` VARCHAR(191) NOT NULL,
79
+ `tenantId` VARCHAR(191) NOT NULL,
80
+ `code` VARCHAR(191) NOT NULL,
81
+ `name` VARCHAR(191) NOT NULL,
82
+ `description` TEXT NULL,
83
+ `isSystemType` BOOLEAN NOT NULL DEFAULT false,
84
+ `createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
85
+ `updatedAt` DATETIME(3) NOT NULL,
86
+
87
+ INDEX `organization_types_tenantId_idx`(`tenantId`),
88
+ INDEX `organization_types_code_idx`(`code`),
89
+ UNIQUE INDEX `organization_types_tenantId_code_key`(`tenantId`, `code`),
90
+ PRIMARY KEY (`id`)
91
+ ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
92
+
93
+ -- CreateTable
94
+ CREATE TABLE `organization_type_assignments` (
95
+ `id` VARCHAR(191) NOT NULL,
96
+ `organizationId` VARCHAR(191) NOT NULL,
97
+ `typeId` VARCHAR(191) NOT NULL,
98
+ `isPrimary` BOOLEAN NOT NULL DEFAULT false,
99
+ `createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
100
+
101
+ INDEX `organization_type_assignments_organizationId_idx`(`organizationId`),
102
+ INDEX `organization_type_assignments_typeId_idx`(`typeId`),
103
+ UNIQUE INDEX `organization_type_assignments_organizationId_typeId_key`(`organizationId`, `typeId`),
104
+ PRIMARY KEY (`id`)
105
+ ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
106
+
107
+ -- CreateIndex
108
+ CREATE INDEX `application_organizations_assignedAsTypeId_idx` ON `application_organizations`(`assignedAsTypeId`);
109
+
110
+ -- CreateIndex
111
+ CREATE UNIQUE INDEX `application_organizations_applicationId_assignedAsTypeId_key` ON `application_organizations`(`applicationId`, `assignedAsTypeId`);
112
+
113
+ -- CreateIndex
114
+ CREATE INDEX `approval_stages_organizationTypeId_idx` ON `approval_stages`(`organizationTypeId`);
115
+
116
+ -- CreateIndex
117
+ CREATE INDEX `document_approvals_organizationTypeId_idx` ON `document_approvals`(`organizationTypeId`);
118
+
119
+ -- CreateIndex
120
+ CREATE INDEX `document_reviews_organizationId_idx` ON `document_reviews`(`organizationId`);
121
+
122
+ -- CreateIndex
123
+ CREATE INDEX `document_reviews_organizationTypeId_idx` ON `document_reviews`(`organizationTypeId`);
124
+
125
+ -- CreateIndex
126
+ CREATE UNIQUE INDEX `document_reviews_documentId_organizationId_key` ON `document_reviews`(`documentId`, `organizationId`);
127
+
128
+ -- AddForeignKey
129
+ ALTER TABLE `organization_types` ADD CONSTRAINT `organization_types_tenantId_fkey` FOREIGN KEY (`tenantId`) REFERENCES `tenants`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
130
+
131
+ -- AddForeignKey
132
+ ALTER TABLE `organization_type_assignments` ADD CONSTRAINT `organization_type_assignments_organizationId_fkey` FOREIGN KEY (`organizationId`) REFERENCES `organizations`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
133
+
134
+ -- AddForeignKey
135
+ ALTER TABLE `organization_type_assignments` ADD CONSTRAINT `organization_type_assignments_typeId_fkey` FOREIGN KEY (`typeId`) REFERENCES `organization_types`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
136
+
137
+ -- AddForeignKey
138
+ ALTER TABLE `approval_stages` ADD CONSTRAINT `approval_stages_organizationTypeId_fkey` FOREIGN KEY (`organizationTypeId`) REFERENCES `organization_types`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
139
+
140
+ -- AddForeignKey
141
+ ALTER TABLE `application_organizations` ADD CONSTRAINT `application_organizations_assignedAsTypeId_fkey` FOREIGN KEY (`assignedAsTypeId`) REFERENCES `organization_types`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
142
+
143
+ -- AddForeignKey
144
+ ALTER TABLE `document_reviews` ADD CONSTRAINT `document_reviews_organizationId_fkey` FOREIGN KEY (`organizationId`) REFERENCES `organizations`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
145
+
146
+ -- AddForeignKey
147
+ ALTER TABLE `document_reviews` ADD CONSTRAINT `document_reviews_organizationTypeId_fkey` FOREIGN KEY (`organizationTypeId`) REFERENCES `organization_types`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
148
+
149
+ -- AddForeignKey
150
+ ALTER TABLE `approval_stage_progress` ADD CONSTRAINT `approval_stage_progress_organizationTypeId_fkey` FOREIGN KEY (`organizationTypeId`) REFERENCES `organization_types`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
151
+
152
+ -- AddForeignKey
153
+ ALTER TABLE `document_approvals` ADD CONSTRAINT `document_approvals_organizationTypeId_fkey` FOREIGN KEY (`organizationTypeId`) REFERENCES `organization_types`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
@@ -0,0 +1,5 @@
1
+ -- AddForeignKey
2
+ ALTER TABLE `application_organizations` ADD CONSTRAINT `application_organizations_applicationId_fkey` FOREIGN KEY (`applicationId`) REFERENCES `applications`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
3
+
4
+ -- AddForeignKey
5
+ ALTER TABLE `document_reviews` ADD CONSTRAINT `document_reviews_documentId_fkey` FOREIGN KEY (`documentId`) REFERENCES `application_documents`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
@@ -203,17 +203,69 @@ enum ApprovalDecision {
203
203
  }
204
204
 
205
205
  // =============================================================================
206
- // ORGANIZATION ENUMS (Banks, Developers, etc.)
206
+ // ORGANIZATION TYPE - Lookup table for organization types (replaces enum)
207
207
  // =============================================================================
208
+ // Organizations can have multiple types via OrganizationTypeAssignment.
209
+ // Example: QShelter can be both PLATFORM and DEVELOPER.
210
+ // System types are seeded during tenant bootstrap and cannot be deleted.
211
+ // =============================================================================
212
+
213
+ model OrganizationType {
214
+ id String @id @default(cuid())
215
+ tenantId String
216
+ tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
217
+ code String // "PLATFORM", "BANK", "DEVELOPER", "LEGAL", "INSURER", "GOVERNMENT"
218
+ name String // Human-readable: "Platform Operator", "Bank/Lender", etc.
219
+ description String? @db.Text
220
+
221
+ // System types are seeded during bootstrap and cannot be deleted
222
+ isSystemType Boolean @default(false)
223
+
224
+ createdAt DateTime @default(now())
225
+ updatedAt DateTime @updatedAt
226
+
227
+ // Organizations with this type
228
+ organizations OrganizationTypeAssignment[]
229
+
230
+ // Applications assigned to orgs acting as this type
231
+ applicationOrganizations ApplicationOrganization[]
232
+
233
+ // Approval stages that require this type of organization
234
+ approvalStages ApprovalStage[]
235
+
236
+ // Document reviews performed by orgs acting as this type (audit snapshot)
237
+ documentReviews DocumentReview[]
238
+
239
+ // Approval stage progress instances
240
+ approvalStageProgress ApprovalStageProgress[]
241
+
242
+ // Document approvals
243
+ documentApprovals DocumentApproval[]
244
+
245
+ @@unique([tenantId, code])
246
+ @@index([tenantId])
247
+ @@index([code])
248
+ @@map("organization_types")
249
+ }
208
250
 
209
- /// Type of organization on the platform
210
- enum OrganizationType {
211
- PLATFORM // The platform operator (e.g., QShelter) - the tenant's own organization
212
- BANK // Financial institution providing mortgages (e.g., Access Bank, GTBank)
213
- DEVELOPER // Property developer building and selling properties
214
- LEGAL // Legal firms handling conveyancing and documentation
215
- INSURER // Insurance companies providing property or mortgage insurance
216
- GOVERNMENT // Government agencies (e.g., land registry, tax authorities)
251
+ /// Links organizations to their types (many-to-many)
252
+ /// An organization can have multiple types (e.g., QShelter is PLATFORM + DEVELOPER)
253
+ model OrganizationTypeAssignment {
254
+ id String @id @default(cuid())
255
+ organizationId String
256
+ organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
257
+ typeId String
258
+ orgType OrganizationType @relation(fields: [typeId], references: [id], onDelete: Cascade)
259
+
260
+ // Which type is the primary one for display purposes
261
+ isPrimary Boolean @default(false)
262
+
263
+ createdAt DateTime @default(now())
264
+
265
+ @@unique([organizationId, typeId])
266
+ @@index([organizationId])
267
+ @@index([typeId])
268
+ @@map("organization_type_assignments")
217
269
  }
218
270
 
219
271
  /// Status of an organization
@@ -224,14 +276,6 @@ enum OrganizationStatus {
224
276
  INACTIVE // No longer active
225
277
  }
226
278
 
227
- /// Role of a member within an organization
228
- enum OrganizationRole {
229
- ADMIN // Can manage org settings, members, and full operations
230
- MANAGER // Can approve transactions within limits (checker)
231
- OFFICER // Regular employee, can initiate actions (maker)
232
- VIEWER // Read-only access to organization data
233
- }
234
-
235
279
  // =============================================================================
236
280
  // CONTRACT TERMINATION / CANCELLATION ENUMS
237
281
  // =============================================================================
@@ -274,21 +318,9 @@ enum CompletionCriterion {
274
318
  }
275
319
 
276
320
  // =============================================================================
277
- // MULTI-PARTY DOCUMENT REVIEW ENUMS
278
- // =============================================================================
279
- // Enables different organizations to review the same document independently
321
+ // DOCUMENT REVIEW ENUMS
280
322
  // =============================================================================
281
323
 
282
- enum ReviewParty {
283
- INTERNAL // Your internal team (compliance, underwriting, etc.)
284
- BANK // Partner bank/lender
285
- DEVELOPER // Property developer
286
- LEGAL // Legal team
287
- GOVERNMENT // Government agencies (e.g., land registry)
288
- INSURER // Insurance company
289
- CUSTOMER // Customer acknowledgment/signature
290
- }
291
-
292
324
  enum ReviewDecision {
293
325
  PENDING // Review not yet performed
294
326
  APPROVED // Document approved by this party
@@ -623,9 +655,12 @@ model Organization {
623
655
  id String @id @default(cuid())
624
656
  tenantId String
625
657
  name String // e.g., "Access Bank PLC", "Lekki Gardens Ltd"
626
- type OrganizationType // PLATFORM, BANK, DEVELOPER, LEGAL, INSURER, GOVERNMENT
627
658
  status OrganizationStatus @default(PENDING)
628
659
 
660
+ // Organization types (many-to-many via OrganizationTypeAssignment)
661
+ // An org can have multiple types (e.g., QShelter is PLATFORM + DEVELOPER)
662
+ types OrganizationTypeAssignment[]
663
+
629
664
  // Platform organization flag - marks the tenant's own organization
630
665
  isPlatformOrg Boolean @default(false)
631
666
 
@@ -667,41 +702,39 @@ model Organization {
667
702
  // Bank-specific document requirements (for BANK type organizations)
668
703
  documentRequirements BankDocumentRequirement[]
669
704
 
705
+ // Document reviews performed by this organization
706
+ documentReviews DocumentReview[]
707
+
670
708
  // Properties developed by this organization (for DEVELOPERs)
671
709
  // developedProperties Property[] @relation("PropertyDeveloper")
672
710
 
673
711
  @@unique([tenantId, bankCode]) // Bank codes unique within tenant
674
712
  @@unique([tenantId, cacNumber]) // CAC numbers unique within tenant
675
713
  @@index([tenantId])
676
- @@index([type])
677
714
  @@index([status])
678
715
  @@map("organizations")
679
716
  }
680
717
 
681
- /// OrganizationMember: Links users to organizations with roles and permissions
682
- /// Supports maker-checker workflows via canApprove and approvalLimit
718
+ /// OrganizationMember: Links users to organizations
719
+ /// User's own roles (via UserRole) determine their abilities within the org
683
720
  model OrganizationMember {
684
- id String @id @default(cuid())
721
+ id String @id @default(cuid())
685
722
  organizationId String
686
723
  userId String
687
- role OrganizationRole @default(OFFICER)
688
724
 
689
725
  // Employee details
690
- title String? // Job title (e.g., "Loan Officer", "Project Manager")
726
+ title String? // Job title (e.g., "Loan Officer", "Mortgage Operations Officer")
691
727
  department String? // Department within organization
692
728
  employeeId String? // Internal employee ID
693
729
 
694
730
  // Status
695
731
  isActive Boolean @default(true)
696
732
 
697
- // Maker-Checker workflow permissions
698
- canApprove Boolean @default(false) // Can this member approve transactions?
699
- approvalLimit Decimal? // Maximum amount this member can approve (null = unlimited)
700
-
701
733
  // Invitation/onboarding tracking
702
734
  invitedAt DateTime?
703
735
  acceptedAt DateTime?
704
736
  invitedBy String? // User ID who invited this member
737
+ joinedAt DateTime @default(now())
705
738
 
706
739
  createdAt DateTime @default(now())
707
740
  updatedAt DateTime @updatedAt
@@ -712,7 +745,6 @@ model OrganizationMember {
712
745
  @@unique([organizationId, userId]) // User can only be member once per org
713
746
  @@index([userId])
714
747
  @@index([organizationId])
715
- @@index([role])
716
748
  @@map("organization_members")
717
749
  }
718
750
 
@@ -886,6 +918,7 @@ model Tenant {
886
918
 
887
919
  // Application organization assignments
888
920
  applicationOrganizations ApplicationOrganization[]
921
+ organizationTypes OrganizationType[]
889
922
 
890
923
  @@index([subdomain])
891
924
  @@map("tenants")
@@ -1425,7 +1458,7 @@ model DocumentDefinition {
1425
1458
  // APPROVAL STAGE - Sequential workflow stages for document review (template)
1426
1459
  // =============================================================================
1427
1460
  // Defines a stage in the approval workflow. Documents flow through stages
1428
- // sequentially. Each stage can have different reviewers (INTERNAL, BANK, etc.)
1461
+ // sequentially. Each stage requires a specific organization type to review.
1429
1462
  // =============================================================================
1430
1463
 
1431
1464
  model ApprovalStage {
@@ -1433,9 +1466,10 @@ model ApprovalStage {
1433
1466
  planId String
1434
1467
  plan DocumentationPlan @relation(fields: [planId], references: [id], onDelete: Cascade)
1435
1468
 
1436
- name String // "QShelter Staff Review", "Bank Review"
1437
- order Int // 1, 2, 3 - sequential order
1438
- reviewParty ReviewParty // INTERNAL, BANK, DEVELOPER, LEGAL, etc.
1469
+ name String // "QShelter Review", "Bank Review"
1470
+ order Int // 1, 2, 3 - sequential order
1471
+ organizationTypeId String // Which type of org should review at this stage
1472
+ organizationType OrganizationType @relation(fields: [organizationTypeId], references: [id])
1439
1473
 
1440
1474
  // Stage behavior flags
1441
1475
  autoTransition Boolean @default(false) // Auto-complete when all docs approved? Default: require explicit approval
@@ -1446,9 +1480,6 @@ model ApprovalStage {
1446
1480
  onRejection RejectionBehavior @default(CASCADE_BACK)
1447
1481
  restartFromStageOrder Int? // If onRejection = RESTART_FROM_STAGE, which stage order to restart from
1448
1482
 
1449
- // Optional: specific organization (e.g., which bank)
1450
- organizationId String?
1451
-
1452
1483
  // SLA (optional)
1453
1484
  slaHours Int? // Escalate if not completed within X hours
1454
1485
 
@@ -1462,6 +1493,7 @@ model ApprovalStage {
1462
1493
 
1463
1494
  @@unique([planId, order])
1464
1495
  @@index([planId])
1496
+ @@index([organizationTypeId])
1465
1497
  @@map("approval_stages")
1466
1498
  }
1467
1499
 
@@ -2079,18 +2111,9 @@ model Application {
2079
2111
  // in a specific application. This enables:
2080
2112
  // 1. Validation that only assigned developers can upload sales offers
2081
2113
  // 2. Only assigned banks can upload preapproval/mortgage offer letters
2082
- // 3. Multi-bank application support (future: apply to multiple banks)
2114
+ // 3. Flexible type-based assignment (org assigned acting as specific type)
2083
2115
  // =============================================================================
2084
2116
 
2085
- /// Role that an organization plays in an application
2086
- enum ApplicationOrganizationRole {
2087
- DEVELOPER // Property developer - uploads sales offer letters
2088
- LENDER // Bank/financial institution - provides mortgage
2089
- LEGAL // Legal firm - handles conveyancing
2090
- INSURER // Insurance company
2091
- GOVERNMENT // Government agency (land registry, etc.)
2092
- }
2093
-
2094
2117
  /// Status of organization's involvement in the application
2095
2118
  enum ApplicationOrganizationStatus {
2096
2119
  PENDING // Awaiting organization's response/engagement
@@ -2111,8 +2134,11 @@ model ApplicationOrganization {
2111
2134
  organizationId String
2112
2135
  organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
2113
2136
 
2114
- // What role does this organization play
2115
- role ApplicationOrganizationRole
2137
+ // Which type the org is acting as on this application
2138
+ // (org must have this type in their OrganizationTypeAssignment)
2139
+ assignedAsTypeId String
2140
+ assignedAsType OrganizationType @relation(fields: [assignedAsTypeId], references: [id])
2141
+
2116
2142
  status ApplicationOrganizationStatus @default(PENDING)
2117
2143
 
2118
2144
  // Who assigned this organization (admin or system)
@@ -2159,11 +2185,11 @@ model ApplicationOrganization {
2159
2185
  createdAt DateTime @default(now())
2160
2186
  updatedAt DateTime @updatedAt
2161
2187
 
2162
- @@unique([applicationId, organizationId, role]) // One org per role per application
2188
+ @@unique([applicationId, assignedAsTypeId]) // One org per type per application
2163
2189
  @@index([tenantId])
2164
2190
  @@index([applicationId])
2165
2191
  @@index([organizationId])
2166
- @@index([role])
2192
+ @@index([assignedAsTypeId])
2167
2193
  @@index([status])
2168
2194
  @@index([isPrimary])
2169
2195
  @@index([slaBreachedAt])
@@ -2729,8 +2755,9 @@ model ApplicationDocument {
2729
2755
  // =============================================================================
2730
2756
  // DOCUMENT REVIEW - Multi-party review tracking
2731
2757
  // =============================================================================
2732
- // Tracks reviews by different organizations for the same document
2733
- // Enables internal approval -> external approval workflows
2758
+ // Tracks reviews by different organizations for the same document.
2759
+ // organizationId is NULL for customer self-attestations/signatures.
2760
+ // For org reviews, organizationTypeId captures which type the org was acting as.
2734
2761
  // =============================================================================
2735
2762
 
2736
2763
  model DocumentReview {
@@ -2740,12 +2767,14 @@ model DocumentReview {
2740
2767
  documentId String
2741
2768
  document ApplicationDocument @relation(fields: [documentId], references: [id], onDelete: Cascade)
2742
2769
 
2743
- // Who is reviewing
2744
- reviewParty ReviewParty // INTERNAL, BANK, DEVELOPER, LEGAL, etc.
2745
- organizationId String? // External organization ID if applicable
2746
- reviewerId String? // User who performed the review
2747
- reviewer User? @relation("DocumentReviewer", fields: [reviewerId], references: [id])
2748
- reviewerName String? // Denormalized for audit trail
2770
+ // Who is reviewing - NULL organizationId means customer self-review
2771
+ organizationId String? // Organization performing the review (null = customer)
2772
+ organization Organization? @relation(fields: [organizationId], references: [id])
2773
+ organizationTypeId String? // Which type the org was acting as (audit snapshot)
2774
+ organizationType OrganizationType? @relation(fields: [organizationTypeId], references: [id])
2775
+ reviewerId String? // User who performed the review
2776
+ reviewer User? @relation("DocumentReviewer", fields: [reviewerId], references: [id])
2777
+ reviewerName String? // Denormalized for audit trail
2749
2778
 
2750
2779
  // Review state
2751
2780
  decision ReviewDecision @default(PENDING)
@@ -2771,10 +2800,12 @@ model DocumentReview {
2771
2800
  createdAt DateTime @default(now())
2772
2801
  updatedAt DateTime @updatedAt
2773
2802
 
2774
- @@unique([documentId, reviewParty, organizationId]) // One review per party per org per doc
2803
+ // Unique: one review per org per doc (null org = customer review)
2804
+ @@unique([documentId, organizationId])
2775
2805
  @@index([tenantId])
2776
2806
  @@index([documentId])
2777
- @@index([reviewParty])
2807
+ @@index([organizationId])
2808
+ @@index([organizationTypeId])
2778
2809
  @@index([decision])
2779
2810
  @@index([reviewerId])
2780
2811
  @@index([parentReviewId])
@@ -2800,9 +2831,10 @@ model ApprovalStageProgress {
2800
2831
  approvalStage ApprovalStage @relation(fields: [approvalStageId], references: [id])
2801
2832
 
2802
2833
  // Stage info (denormalized for convenience)
2803
- name String
2804
- order Int
2805
- reviewParty ReviewParty
2834
+ name String
2835
+ order Int
2836
+ organizationTypeId String // Which type of org should review at this stage
2837
+ organizationType OrganizationType @relation(fields: [organizationTypeId], references: [id])
2806
2838
 
2807
2839
  // Behavior flags (copied from template)
2808
2840
  autoTransition Boolean @default(false)
@@ -2856,9 +2888,10 @@ model DocumentApproval {
2856
2888
  stageProgress ApprovalStageProgress @relation(fields: [stageProgressId], references: [id], onDelete: Cascade)
2857
2889
 
2858
2890
  // Reviewer info
2859
- reviewerId String
2860
- reviewer User @relation("DocumentApprovalReviewer", fields: [reviewerId], references: [id])
2861
- reviewParty ReviewParty // INTERNAL, BANK, etc.
2891
+ reviewerId String
2892
+ reviewer User @relation("DocumentApprovalReviewer", fields: [reviewerId], references: [id])
2893
+ organizationTypeId String // Which type the reviewer's org was acting as
2894
+ organizationType OrganizationType @relation(fields: [organizationTypeId], references: [id])
2862
2895
 
2863
2896
  // Decision
2864
2897
  decision ReviewDecision // APPROVED, REJECTED, CHANGES_REQUESTED
@@ -2871,6 +2904,7 @@ model DocumentApproval {
2871
2904
  @@index([documentId])
2872
2905
  @@index([stageProgressId])
2873
2906
  @@index([reviewerId])
2907
+ @@index([organizationTypeId])
2874
2908
  @@index([decision])
2875
2909
  @@map("document_approvals")
2876
2910
  }