@valentine-efagene/qshelter-common 1.0.0

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 (179) hide show
  1. package/OpenApiHelper.ts +121 -0
  2. package/decorator/permission.decorator.ts +4 -0
  3. package/decorator/tenant.decorator.ts +16 -0
  4. package/dist/entities/BaseEntity.d.ts +11 -0
  5. package/dist/entities/BaseEntity.js +54 -0
  6. package/dist/entities/BaseEntity.js.map +1 -0
  7. package/dist/entities/TenantAwareEntity.d.ts +10 -0
  8. package/dist/entities/TenantAwareEntity.js +52 -0
  9. package/dist/entities/TenantAwareEntity.js.map +1 -0
  10. package/dist/entities/TenantAwareRepository.d.ts +13 -0
  11. package/dist/entities/TenantAwareRepository.js +65 -0
  12. package/dist/entities/TenantAwareRepository.js.map +1 -0
  13. package/dist/entities/amenity.entity.d.ts +4 -0
  14. package/dist/entities/amenity.entity.js +27 -0
  15. package/dist/entities/amenity.entity.js.map +1 -0
  16. package/dist/entities/common.entity.d.ts +17 -0
  17. package/dist/entities/common.entity.js +63 -0
  18. package/dist/entities/common.entity.js.map +1 -0
  19. package/dist/entities/common.pure.entity.d.ts +11 -0
  20. package/dist/entities/common.pure.entity.js +51 -0
  21. package/dist/entities/common.pure.entity.js.map +1 -0
  22. package/dist/entities/index.d.ts +27 -0
  23. package/dist/entities/index.js +44 -0
  24. package/dist/entities/index.js.map +1 -0
  25. package/dist/entities/mortgage-document.entity.d.ts +14 -0
  26. package/dist/entities/mortgage-document.entity.js +58 -0
  27. package/dist/entities/mortgage-document.entity.js.map +1 -0
  28. package/dist/entities/mortgage-downpayment-installment.entity.d.ts +19 -0
  29. package/dist/entities/mortgage-downpayment-installment.entity.js +63 -0
  30. package/dist/entities/mortgage-downpayment-installment.entity.js.map +1 -0
  31. package/dist/entities/mortgage-downpayment-payment.entity.d.ts +28 -0
  32. package/dist/entities/mortgage-downpayment-payment.entity.js +84 -0
  33. package/dist/entities/mortgage-downpayment-payment.entity.js.map +1 -0
  34. package/dist/entities/mortgage-downpayment.entity.d.ts +22 -0
  35. package/dist/entities/mortgage-downpayment.entity.js +66 -0
  36. package/dist/entities/mortgage-downpayment.entity.js.map +1 -0
  37. package/dist/entities/mortgage-step.entity.d.ts +12 -0
  38. package/dist/entities/mortgage-step.entity.js +52 -0
  39. package/dist/entities/mortgage-step.entity.js.map +1 -0
  40. package/dist/entities/mortgage-type.entity.d.ts +10 -0
  41. package/dist/entities/mortgage-type.entity.js +46 -0
  42. package/dist/entities/mortgage-type.entity.js.map +1 -0
  43. package/dist/entities/mortgage.entity.d.ts +37 -0
  44. package/dist/entities/mortgage.entity.js +124 -0
  45. package/dist/entities/mortgage.entity.js.map +1 -0
  46. package/dist/entities/password_reset_tokens.entity.d.ts +7 -0
  47. package/dist/entities/password_reset_tokens.entity.js +43 -0
  48. package/dist/entities/password_reset_tokens.entity.js.map +1 -0
  49. package/dist/entities/permission.entity.d.ts +6 -0
  50. package/dist/entities/permission.entity.js +30 -0
  51. package/dist/entities/permission.entity.js.map +1 -0
  52. package/dist/entities/property-document.entity.d.ts +6 -0
  53. package/dist/entities/property-document.entity.js +34 -0
  54. package/dist/entities/property-document.entity.js.map +1 -0
  55. package/dist/entities/property-media.entity.d.ts +6 -0
  56. package/dist/entities/property-media.entity.js +36 -0
  57. package/dist/entities/property-media.entity.js.map +1 -0
  58. package/dist/entities/property.entity.d.ts +36 -0
  59. package/dist/entities/property.entity.js +182 -0
  60. package/dist/entities/property.entity.js.map +1 -0
  61. package/dist/entities/refresh_token.entity.d.ts +7 -0
  62. package/dist/entities/refresh_token.entity.js +35 -0
  63. package/dist/entities/refresh_token.entity.js.map +1 -0
  64. package/dist/entities/role.entity.d.ts +8 -0
  65. package/dist/entities/role.entity.js +39 -0
  66. package/dist/entities/role.entity.js.map +1 -0
  67. package/dist/entities/settings.entity.d.ts +17 -0
  68. package/dist/entities/settings.entity.js +79 -0
  69. package/dist/entities/settings.entity.js.map +1 -0
  70. package/dist/entities/social.entity.d.ts +8 -0
  71. package/dist/entities/social.entity.js +46 -0
  72. package/dist/entities/social.entity.js.map +1 -0
  73. package/dist/entities/tenant.entity.d.ts +29 -0
  74. package/dist/entities/tenant.entity.js +82 -0
  75. package/dist/entities/tenant.entity.js.map +1 -0
  76. package/dist/entities/transaction.entity.d.ts +17 -0
  77. package/dist/entities/transaction.entity.js +84 -0
  78. package/dist/entities/transaction.entity.js.map +1 -0
  79. package/dist/entities/user.entity.d.ts +26 -0
  80. package/dist/entities/user.entity.js +103 -0
  81. package/dist/entities/user.entity.js.map +1 -0
  82. package/dist/entities/user_suspensions.entity.d.ts +7 -0
  83. package/dist/entities/user_suspensions.entity.js +42 -0
  84. package/dist/entities/user_suspensions.entity.js.map +1 -0
  85. package/dist/entities/wallet.entity.d.ts +17 -0
  86. package/dist/entities/wallet.entity.js +79 -0
  87. package/dist/entities/wallet.entity.js.map +1 -0
  88. package/dist/index.d.ts +3 -0
  89. package/dist/index.js +20 -0
  90. package/dist/index.js.map +1 -0
  91. package/dist/pagination/index.d.ts +2 -0
  92. package/dist/pagination/index.js +19 -0
  93. package/dist/pagination/index.js.map +1 -0
  94. package/dist/pagination/pagination.helper.d.ts +7 -0
  95. package/dist/pagination/pagination.helper.js +40 -0
  96. package/dist/pagination/pagination.helper.js.map +1 -0
  97. package/dist/pagination/pagination.types.d.ts +19 -0
  98. package/dist/pagination/pagination.types.js +3 -0
  99. package/dist/pagination/pagination.types.js.map +1 -0
  100. package/dist/standard-response.d.ts +7 -0
  101. package/dist/standard-response.js +27 -0
  102. package/dist/standard-response.js.map +1 -0
  103. package/dist/tsconfig.tsbuildinfo +1 -0
  104. package/dist/types/common.type.d.ts +26 -0
  105. package/dist/types/common.type.js +26 -0
  106. package/dist/types/common.type.js.map +1 -0
  107. package/dist/types/mortgage-fsm.types.d.ts +180 -0
  108. package/dist/types/mortgage-fsm.types.js +130 -0
  109. package/dist/types/mortgage-fsm.types.js.map +1 -0
  110. package/dist/types/policy.types.d.ts +18 -0
  111. package/dist/types/policy.types.js +3 -0
  112. package/dist/types/policy.types.js.map +1 -0
  113. package/dist/types/property.type.d.ts +9 -0
  114. package/dist/types/property.type.js +15 -0
  115. package/dist/types/property.type.js.map +1 -0
  116. package/dist/types/social.enums.d.ts +15 -0
  117. package/dist/types/social.enums.js +22 -0
  118. package/dist/types/social.enums.js.map +1 -0
  119. package/dist/types/tenant.enums.d.ts +13 -0
  120. package/dist/types/tenant.enums.js +19 -0
  121. package/dist/types/tenant.enums.js.map +1 -0
  122. package/dist/types/transaction.type.d.ts +8 -0
  123. package/dist/types/transaction.type.js +14 -0
  124. package/dist/types/transaction.type.js.map +1 -0
  125. package/dist/types/user.enums.d.ts +10 -0
  126. package/dist/types/user.enums.js +16 -0
  127. package/dist/types/user.enums.js.map +1 -0
  128. package/entities/BaseEntity.ts +34 -0
  129. package/entities/TenantAwareEntity.ts +34 -0
  130. package/entities/TenantAwareRepository.ts +100 -0
  131. package/entities/amenity.entity.ts +10 -0
  132. package/entities/common.entity.ts +46 -0
  133. package/entities/common.pure.entity.ts +36 -0
  134. package/entities/index.ts +27 -0
  135. package/entities/mortgage-document.entity.ts +37 -0
  136. package/entities/mortgage-downpayment-installment.entity.ts +40 -0
  137. package/entities/mortgage-downpayment-payment.entity.ts +61 -0
  138. package/entities/mortgage-downpayment.entity.ts +43 -0
  139. package/entities/mortgage-step.entity.ts +33 -0
  140. package/entities/mortgage-type.entity.ts +31 -0
  141. package/entities/mortgage.entity.ts +89 -0
  142. package/entities/password_reset_tokens.entity.ts +25 -0
  143. package/entities/permission.entity.ts +12 -0
  144. package/entities/property-document.entity.ts +21 -0
  145. package/entities/property-media.entity.ts +23 -0
  146. package/entities/property.entity.ts +147 -0
  147. package/entities/refresh_token.entity.ts +16 -0
  148. package/entities/role.entity.ts +20 -0
  149. package/entities/settings.entity.ts +56 -0
  150. package/entities/social.entity.ts +27 -0
  151. package/entities/tenant.entity.ts +65 -0
  152. package/entities/transaction.entity.ts +56 -0
  153. package/entities/user.entity.ts +89 -0
  154. package/entities/user_suspensions.entity.ts +24 -0
  155. package/entities/wallet.entity.ts +54 -0
  156. package/guard/permission.guard.ts +42 -0
  157. package/guard/swagger-auth.guard.ts +9 -0
  158. package/helpers/ArrayHelper.ts +1 -0
  159. package/helpers/ConstantHelper.ts +101 -0
  160. package/helpers/CustomNamingStrategy.ts +27 -0
  161. package/helpers/DateHelper.ts +21 -0
  162. package/helpers/EmailHelper.ts +38 -0
  163. package/helpers/FileSystemHelper.ts +101 -0
  164. package/index.ts +9 -0
  165. package/middleware/TenantMiddleware.ts +52 -0
  166. package/package.json +46 -0
  167. package/pagination/index.ts +2 -0
  168. package/pagination/pagination.helper.ts +57 -0
  169. package/pagination/pagination.types.ts +21 -0
  170. package/standard-response.ts +16 -0
  171. package/tsconfig.json +33 -0
  172. package/types/common.type.ts +32 -0
  173. package/types/mortgage-fsm.types.ts +279 -0
  174. package/types/policy.types.ts +21 -0
  175. package/types/property.type.ts +10 -0
  176. package/types/social.enums.ts +17 -0
  177. package/types/tenant.enums.ts +14 -0
  178. package/types/transaction.type.ts +9 -0
  179. package/types/user.enums.ts +11 -0
@@ -0,0 +1,100 @@
1
+ import { Repository, FindManyOptions, FindOneOptions, FindOptionsWhere, DeepPartial } from 'typeorm';
2
+
3
+ /**
4
+ * Base repository with automatic tenant scoping
5
+ * All queries automatically filter by tenantId
6
+ */
7
+ export class TenantAwareRepository<Entity extends { tenantId: number }> extends Repository<Entity> {
8
+ /**
9
+ * Find entities with automatic tenant filtering
10
+ */
11
+ async findByTenant(tenantId: number, options?: FindManyOptions<Entity>): Promise<Entity[]> {
12
+ return this.find({
13
+ ...options,
14
+ where: {
15
+ ...options?.where,
16
+ tenantId,
17
+ } as FindOptionsWhere<Entity>,
18
+ });
19
+ }
20
+
21
+ /**
22
+ * Find one entity with automatic tenant filtering
23
+ */
24
+ async findOneByTenant(
25
+ tenantId: number,
26
+ options?: FindOneOptions<Entity>
27
+ ): Promise<Entity | null> {
28
+ return this.findOne({
29
+ ...options,
30
+ where: {
31
+ ...options?.where,
32
+ tenantId,
33
+ } as FindOptionsWhere<Entity>,
34
+ });
35
+ }
36
+
37
+ /**
38
+ * Count entities with automatic tenant filtering
39
+ */
40
+ async countByTenant(tenantId: number, options?: FindManyOptions<Entity>): Promise<number> {
41
+ return this.count({
42
+ ...options,
43
+ where: {
44
+ ...options?.where,
45
+ tenantId,
46
+ } as FindOptionsWhere<Entity>,
47
+ });
48
+ }
49
+
50
+ /**
51
+ * Create and save entity with automatic tenantId
52
+ */
53
+ async createForTenant(tenantId: number, entityData: DeepPartial<Entity>): Promise<Entity> {
54
+ const entity = this.create({
55
+ ...entityData,
56
+ tenantId,
57
+ } as DeepPartial<Entity>);
58
+ return this.save(entity);
59
+ }
60
+
61
+ /**
62
+ * Update entity with tenant validation
63
+ */
64
+ async updateForTenant(
65
+ tenantId: number,
66
+ id: number,
67
+ entityData: DeepPartial<Entity>
68
+ ): Promise<Entity> {
69
+ const entity = await this.findOneByTenant(tenantId, { where: { id } as any });
70
+ if (!entity) {
71
+ throw new Error(`Entity with ID ${id} not found for tenant ${tenantId}`);
72
+ }
73
+ Object.assign(entity, entityData);
74
+ return this.save(entity);
75
+ }
76
+
77
+ /**
78
+ * Delete entity with tenant validation
79
+ */
80
+ async deleteForTenant(tenantId: number, id: number): Promise<void> {
81
+ const entity = await this.findOneByTenant(tenantId, { where: { id } as any });
82
+ if (!entity) {
83
+ throw new Error(`Entity with ID ${id} not found for tenant ${tenantId}`);
84
+ }
85
+ await this.remove(entity);
86
+ }
87
+
88
+ /**
89
+ * Soft delete entity with tenant validation
90
+ */
91
+ async softDeleteForTenant(tenantId: number, id: number): Promise<void> {
92
+ const entity = await this.findOneByTenant(tenantId, { where: { id } as any });
93
+ if (!entity) {
94
+ throw new Error(`Entity with ID ${id} not found for tenant ${tenantId}`);
95
+ }
96
+ await this.softRemove(entity);
97
+ }
98
+ }
99
+
100
+ export default TenantAwareRepository;
@@ -0,0 +1,10 @@
1
+ import { Column, Entity } from 'typeorm';
2
+ import { BaseEntity } from './BaseEntity';
3
+
4
+ @Entity({ name: 'amenity' })
5
+ export class Amenity extends BaseEntity {
6
+ @Column({
7
+ nullable: false
8
+ })
9
+ name: string
10
+ }
@@ -0,0 +1,46 @@
1
+ import { User } from './user.entity';
2
+ import {
3
+ ManyToOne,
4
+ JoinColumn,
5
+ Column,
6
+ } from 'typeorm';
7
+ import { DocumentStatus } from '../types/common.type';
8
+ import { AbstractTenantAwareEntity } from './common.pure.entity';
9
+
10
+ export abstract class AbstractBaseReviewableEntity extends AbstractTenantAwareEntity {
11
+ @ManyToOne(() => User, { eager: true })
12
+ @JoinColumn({ name: 'reviewed_by' })
13
+ reviewer: User;
14
+
15
+ @Column({ type: 'timestamp', nullable: true })
16
+ reviewedAt: string;
17
+ }
18
+
19
+ export abstract class AbstractBaseDocumentEntity extends AbstractBaseReviewableEntity {
20
+ @Column({
21
+ type: 'enum',
22
+ enum: DocumentStatus,
23
+ default: DocumentStatus.PENDING,
24
+ })
25
+ status: DocumentStatus;
26
+
27
+ @Column({ nullable: true })
28
+ comment: string;
29
+
30
+ @Column({ type: 'text', nullable: false })
31
+ url: string;
32
+
33
+ @Column({ nullable: true })
34
+ description: string;
35
+
36
+ @Column({ nullable: true })
37
+ name: string;
38
+
39
+ @Column({ nullable: false, comment: "In bytes" })
40
+ size: number
41
+ }
42
+
43
+ export abstract class AbstractBaseMediaEntity extends AbstractBaseDocumentEntity {
44
+ // @Column()
45
+ // mimeType: string;
46
+ }
@@ -0,0 +1,36 @@
1
+
2
+ import { CreateDateColumn, DeleteDateColumn, PrimaryGeneratedColumn, UpdateDateColumn, Column, Index, ManyToOne, JoinColumn } from "typeorm";
3
+ import { Tenant } from "./tenant.entity";
4
+
5
+
6
+ export abstract class AbstractBaseEntity {
7
+ @PrimaryGeneratedColumn()
8
+ id: number;
9
+
10
+ @CreateDateColumn({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
11
+ createdAt: Date;
12
+
13
+ @UpdateDateColumn({
14
+ type: 'timestamp',
15
+ default: () => 'CURRENT_TIMESTAMP',
16
+ onUpdate: 'CURRENT_TIMESTAMP',
17
+ })
18
+ updatedAt: Date;
19
+
20
+ @DeleteDateColumn({ nullable: true, default: null })
21
+ deletedAt: Date;
22
+ }
23
+
24
+ /**
25
+ * Tenant-aware base entity
26
+ * All entities that need tenant isolation should extend this
27
+ */
28
+ export abstract class AbstractTenantAwareEntity extends AbstractBaseEntity {
29
+ @ManyToOne(() => Tenant, { onDelete: 'CASCADE' })
30
+ @JoinColumn({ name: 'tenant_id' })
31
+ tenant: Tenant;
32
+
33
+ @Column({ name: 'tenant_id' })
34
+ @Index()
35
+ tenantId: number;
36
+ }
@@ -0,0 +1,27 @@
1
+ export * from './BaseEntity';
2
+ export * from './TenantAwareEntity';
3
+ export * from './TenantAwareRepository';
4
+ export * from './amenity.entity';
5
+ export * from './common.entity';
6
+ export * from './common.pure.entity';
7
+ export * from './mortgage-document.entity';
8
+ export * from './mortgage-downpayment-installment.entity';
9
+ export * from './mortgage-downpayment-payment.entity';
10
+ export * from './mortgage-downpayment.entity';
11
+ export * from './mortgage-step.entity';
12
+ export * from './mortgage-type.entity';
13
+ export * from './mortgage.entity';
14
+ export * from './password_reset_tokens.entity';
15
+ export * from './permission.entity';
16
+ export * from './property-document.entity';
17
+ export * from './property-media.entity';
18
+ export * from './property.entity';
19
+ export * from './refresh_token.entity';
20
+ export * from './role.entity';
21
+ export * from './settings.entity';
22
+ export * from './social.entity';
23
+ export * from './tenant.entity';
24
+ export * from './transaction.entity';
25
+ export * from './user.entity';
26
+ export * from './user_suspensions.entity';
27
+ export * from './wallet.entity';
@@ -0,0 +1,37 @@
1
+ import { Column, Entity, JoinColumn, ManyToOne } from 'typeorm';
2
+ import { AbstractBaseReviewableEntity } from './common.entity';
3
+ import { Mortgage } from './mortgage.entity';
4
+ import { User } from './user.entity';
5
+
6
+ @Entity({ name: 'mortgage_document' })
7
+ export class MortgageDocument extends AbstractBaseReviewableEntity {
8
+ @ManyToOne(() => Mortgage, (mortgage) => mortgage.documents, { onDelete: 'CASCADE' })
9
+ @JoinColumn({ name: 'mortgage_id' })
10
+ mortgage: Mortgage;
11
+
12
+ @Column({ nullable: true })
13
+ mortgageId: number;
14
+
15
+ @Column()
16
+ fileName: string;
17
+
18
+ // Allow null URLs for template/placeholder documents created from mortgage type templates.
19
+ @Column({ nullable: true })
20
+ url: string;
21
+
22
+ // Flag to mark this document as a template/placeholder (not yet uploaded). Template docs typically have a name but no URL.
23
+ @Column({ default: false })
24
+ isTemplate: boolean;
25
+
26
+ @Column({ nullable: true })
27
+ mimeType: string;
28
+
29
+ @ManyToOne(() => User, { nullable: true })
30
+ @JoinColumn({ name: 'uploaded_by' })
31
+ uploadedBy: User;
32
+
33
+ @Column({ nullable: true })
34
+ uploadedById: number;
35
+ }
36
+
37
+ export default MortgageDocument;
@@ -0,0 +1,40 @@
1
+ import { Column, Entity, JoinColumn, ManyToOne } from 'typeorm';
2
+ import { AbstractBaseEntity } from './common.pure.entity';
3
+ import { MortgageDownpaymentPlan } from './mortgage-downpayment.entity';
4
+
5
+ export enum InstallmentStatus {
6
+ PENDING = 'PENDING',
7
+ PARTIAL = 'PARTIAL',
8
+ PAID = 'PAID',
9
+ LATE = 'LATE',
10
+ }
11
+
12
+ @Entity({ name: 'mortgage_downpayment_installment' })
13
+ export class MortgageDownpaymentInstallment extends AbstractBaseEntity {
14
+ @ManyToOne(() => MortgageDownpaymentPlan, (p) => p.installments, { onDelete: 'CASCADE' })
15
+ @JoinColumn({ name: 'plan_id' })
16
+ plan: MortgageDownpaymentPlan;
17
+
18
+ @Column({ nullable: true })
19
+ planId: number;
20
+
21
+ @Column({ type: 'int' })
22
+ sequence: number;
23
+
24
+ @Column({ type: 'date' })
25
+ dueDate: Date;
26
+
27
+ @Column({ type: 'double precision' })
28
+ amountDue: number;
29
+
30
+ @Column({ type: 'double precision', default: 0 })
31
+ amountPaid: number;
32
+
33
+ @Column({ type: 'timestamp', nullable: true })
34
+ paidAt: Date;
35
+
36
+ @Column({ type: 'enum', enum: InstallmentStatus, default: InstallmentStatus.PENDING })
37
+ status: InstallmentStatus;
38
+ }
39
+
40
+ export default MortgageDownpaymentInstallment;
@@ -0,0 +1,61 @@
1
+ import { Column, Entity, JoinColumn, ManyToOne } from 'typeorm';
2
+ import { AbstractBaseEntity } from './common.pure.entity';
3
+ import { MortgageDownpaymentPlan } from './mortgage-downpayment.entity';
4
+ import { MortgageDownpaymentInstallment } from './mortgage-downpayment-installment.entity';
5
+ import { User } from './user.entity';
6
+
7
+ // Legacy enum - kept for backward compatibility
8
+ export enum DownpaymentPaymentStatus {
9
+ PENDING = 'PENDING',
10
+ COMPLETED = 'COMPLETED',
11
+ FAILED = 'FAILED',
12
+ }
13
+
14
+ // Payment state for FSM tracking
15
+ export enum PaymentState {
16
+ INITIATED = 'INITIATED',
17
+ COMPLETED = 'COMPLETED',
18
+ FAILED = 'FAILED',
19
+ }
20
+
21
+ @Entity({ name: 'mortgage_downpayment_payment' })
22
+ export class MortgageDownpaymentPayment extends AbstractBaseEntity {
23
+ @ManyToOne(() => MortgageDownpaymentPlan, { onDelete: 'CASCADE' })
24
+ @JoinColumn({ name: 'plan_id' })
25
+ plan: MortgageDownpaymentPlan;
26
+
27
+ @Column({ nullable: true })
28
+ planId: number;
29
+
30
+ @ManyToOne(() => MortgageDownpaymentInstallment, { nullable: true })
31
+ @JoinColumn({ name: 'installment_id' })
32
+ installment: MortgageDownpaymentInstallment;
33
+
34
+ @Column({ nullable: true })
35
+ installmentId: number;
36
+
37
+ @ManyToOne(() => User, { nullable: true })
38
+ @JoinColumn({ name: 'payer_id' })
39
+ payer: User;
40
+
41
+ @Column({ nullable: true })
42
+ payerId: number;
43
+
44
+ @Column({ type: 'double precision' })
45
+ amount: number;
46
+
47
+ @Column({ nullable: true, unique: true })
48
+ providerReference: string;
49
+
50
+ @Column({ type: 'enum', enum: DownpaymentPaymentStatus, default: DownpaymentPaymentStatus.PENDING })
51
+ status: DownpaymentPaymentStatus;
52
+
53
+ // FSM state tracking
54
+ @Column({ type: 'enum', enum: PaymentState, default: PaymentState.INITIATED })
55
+ state: PaymentState;
56
+
57
+ @Column({ type: 'text', nullable: true })
58
+ stateMetadata: string; // JSON metadata for state transitions
59
+ }
60
+
61
+ export default MortgageDownpaymentPayment;
@@ -0,0 +1,43 @@
1
+ import { Column, Entity, JoinColumn, ManyToOne, OneToMany } from 'typeorm';
2
+ import { AbstractBaseEntity } from './common.pure.entity';
3
+ import { Mortgage } from './mortgage.entity';
4
+ import { MortgageDownpaymentInstallment } from './mortgage-downpayment-installment.entity';
5
+ import { Frequency } from '../types/common.type';
6
+
7
+ export enum DownpaymentPlanStatus {
8
+ PENDING = 'PENDING',
9
+ ACTIVE = 'ACTIVE',
10
+ COMPLETED = 'COMPLETED',
11
+ DEFAULTED = 'DEFAULTED',
12
+ CANCELLED = 'CANCELLED',
13
+ }
14
+
15
+ @Entity({ name: 'mortgage_downpayment_plan' })
16
+ export class MortgageDownpaymentPlan extends AbstractBaseEntity {
17
+ @ManyToOne(() => Mortgage, { onDelete: 'CASCADE' })
18
+ @JoinColumn({ name: 'mortgage_id' })
19
+ mortgage: Mortgage;
20
+
21
+ @Column({ nullable: true })
22
+ mortgageId: number;
23
+
24
+ @Column({ type: 'double precision' })
25
+ totalAmount: number;
26
+
27
+ @Column({ type: 'int', nullable: true })
28
+ installmentCount: number;
29
+
30
+ @Column({ nullable: true, type: 'enum', enum: Frequency })
31
+ frequency: Frequency;
32
+
33
+ @Column({ type: 'date', nullable: true })
34
+ startDate: Date;
35
+
36
+ @Column({ type: 'enum', enum: DownpaymentPlanStatus, default: DownpaymentPlanStatus.PENDING })
37
+ status: DownpaymentPlanStatus;
38
+
39
+ @OneToMany(() => MortgageDownpaymentInstallment, (i) => i.plan, { cascade: true })
40
+ installments: MortgageDownpaymentInstallment[];
41
+ }
42
+
43
+ export default MortgageDownpaymentPlan;
@@ -0,0 +1,33 @@
1
+ import { Column, Entity, JoinColumn, ManyToOne } from 'typeorm';
2
+ import { AbstractBaseReviewableEntity } from './common.entity';
3
+ import { Mortgage } from './mortgage.entity';
4
+
5
+ @Entity({ name: 'mortgage_step' })
6
+ export class MortgageStep extends AbstractBaseReviewableEntity {
7
+ @ManyToOne(() => Mortgage, (mortgage) => mortgage.steps, { onDelete: 'CASCADE' })
8
+ @JoinColumn({ name: 'mortgage_id' })
9
+ mortgage: Mortgage;
10
+
11
+ @Column({ nullable: true })
12
+ mortgageId: number;
13
+
14
+ @Column()
15
+ title: string;
16
+
17
+ @Column({ type: 'text', nullable: true })
18
+ description: string;
19
+
20
+ // Sequence order - easier to query and reorder than relying purely on linked-list pointers
21
+ @Column({ type: 'int', default: 0 })
22
+ sequence: number;
23
+
24
+ // We use `sequence` for ordering. Removed linked-list pointer fields (nextStep/nextStepId) to reduce redundancy.
25
+
26
+ @Column({ default: false })
27
+ isOptional: boolean;
28
+
29
+ @Column({ nullable: true })
30
+ completedAt: Date;
31
+ }
32
+
33
+ export default MortgageStep;
@@ -0,0 +1,31 @@
1
+ import { Column, Entity } from 'typeorm';
2
+ import { AbstractBaseReviewableEntity } from './common.entity';
3
+
4
+ /**
5
+ * MortgageType stores named mortgage configurations. Examples: 'Standard Fixed', 'Interest Only', 'Buy-to-Let'.
6
+ * - defaultSteps: array of step templates { title, description?, sequence?, isOptional? }
7
+ * - requiredDocuments: array of document templates { name, required: boolean }
8
+ * - config: free-form JSON for additional settings
9
+ */
10
+ @Entity({ name: 'mortgage_type' })
11
+ export class MortgageType extends AbstractBaseReviewableEntity {
12
+ @Column()
13
+ name: string;
14
+
15
+ @Column({ nullable: true })
16
+ slug: string;
17
+
18
+ @Column({ type: 'text', nullable: true })
19
+ description?: string;
20
+
21
+ @Column({ type: 'json', nullable: true })
22
+ defaultSteps?: any[];
23
+
24
+ @Column({ type: 'json', nullable: true })
25
+ requiredDocuments?: any[];
26
+
27
+ @Column({ type: 'json', nullable: true })
28
+ config?: any;
29
+ }
30
+
31
+ export default MortgageType;
@@ -0,0 +1,89 @@
1
+ import { Column, Entity, JoinColumn, ManyToOne, OneToMany, OneToOne } from 'typeorm';
2
+ import { AbstractBaseReviewableEntity } from './common.entity';
3
+ import { Property } from './property.entity';
4
+ import { User } from './user.entity';
5
+ import MortgageDocument from './mortgage-document.entity';
6
+ import MortgageStep from './mortgage-step.entity';
7
+ import { MortgageType } from './mortgage-type.entity';
8
+ import { MortgageDownpaymentPlan } from './mortgage-downpayment.entity';
9
+ import { MortgageState } from '../types/mortgage-fsm.types';
10
+
11
+ // Legacy enum - kept for backward compatibility
12
+ export enum MortgageStatus {
13
+ DRAFT = 'DRAFT',
14
+ PENDING = 'PENDING',
15
+ ACTIVE = 'ACTIVE',
16
+ COMPLETED = 'COMPLETED',
17
+ CANCELLED = 'CANCELLED',
18
+ }
19
+
20
+ @Entity({ name: 'mortgage' })
21
+ export class Mortgage extends AbstractBaseReviewableEntity {
22
+ @ManyToOne(() => Property, (property) => property.mortgages, { onDelete: 'CASCADE' })
23
+ @JoinColumn({ name: 'property_id' })
24
+ property: Property;
25
+
26
+ @Column({ nullable: true })
27
+ propertyId: number;
28
+
29
+ @ManyToOne(() => User, { nullable: true })
30
+ @JoinColumn({ name: 'borrower_id' })
31
+ borrower: User;
32
+
33
+ @Column({ nullable: true })
34
+ borrowerId: number;
35
+
36
+ @Column({ type: 'double precision', nullable: true })
37
+ principal: number;
38
+
39
+ @Column({ type: 'double precision', nullable: true })
40
+ downPayment: number;
41
+
42
+ @Column({ type: 'int', nullable: true })
43
+ termMonths: number;
44
+
45
+ @Column({ type: 'double precision', nullable: true })
46
+ interestRate: number;
47
+
48
+ @Column({ type: 'double precision', nullable: true })
49
+ monthlyPayment: number;
50
+
51
+ @Column({ type: 'enum', enum: MortgageStatus, default: MortgageStatus.DRAFT })
52
+ status: MortgageStatus;
53
+
54
+ // FSM State - Primary state tracking
55
+ @Column({ type: 'varchar', default: MortgageState.DRAFT })
56
+ state: string; // Using string to store MortgageState enum values
57
+
58
+ // FSM Metadata - Stores transition history and context
59
+ @Column({ type: 'text', nullable: true })
60
+ stateMetadata: string; // JSON string containing last transition info
61
+
62
+ @OneToMany(() => MortgageDocument, (doc) => doc.mortgage)
63
+ documents: MortgageDocument[];
64
+
65
+ @OneToMany(() => MortgageStep, (step) => step.mortgage, { cascade: true })
66
+ steps: MortgageStep[];
67
+
68
+ @ManyToOne(() => MortgageType, { nullable: true })
69
+ @JoinColumn({ name: 'mortgage_type_id' })
70
+ mortgageType: MortgageType;
71
+
72
+ @Column({ nullable: true })
73
+ mortgageTypeId: number;
74
+
75
+ @Column({ type: 'timestamp', nullable: true })
76
+ lastReminderSentAt: Date;
77
+
78
+ @OneToOne(() => MortgageDownpaymentPlan, { nullable: true })
79
+ @JoinColumn({ name: 'downpayment_plan_id' })
80
+ downpaymentPlan: MortgageDownpaymentPlan;
81
+
82
+ @Column({ nullable: true })
83
+ downpaymentPlanId: number;
84
+
85
+ @Column({ type: 'double precision', nullable: true })
86
+ downPaymentPaid: number;
87
+ }
88
+
89
+ export default Mortgage;
@@ -0,0 +1,25 @@
1
+ import { Column, DeleteDateColumn, Entity, JoinColumn, OneToOne } from 'typeorm';
2
+ import { User } from './user.entity';
3
+ import { BaseEntity } from './BaseEntity';
4
+
5
+ @Entity({ name: 'password_reset_token' })
6
+ export class PasswordResetToken extends BaseEntity {
7
+ @OneToOne(() => User, {
8
+ //eager: true,
9
+ onDelete: 'CASCADE',
10
+ onUpdate: 'CASCADE',
11
+ })
12
+ @JoinColumn({ name: 'user_id' })
13
+ user: User;
14
+
15
+ @Column({
16
+ nullable: true
17
+ })
18
+ tokenHash: string
19
+
20
+ @DeleteDateColumn({
21
+ nullable: true,
22
+ default: null
23
+ })
24
+ expiresAt: Date;
25
+ }
@@ -0,0 +1,12 @@
1
+ import { Column, Entity, ManyToMany } from 'typeorm';
2
+ import { BaseEntity } from './BaseEntity';
3
+ import { Role } from './role.entity';
4
+
5
+ @Entity({ name: 'permissions' })
6
+ export class Permission extends BaseEntity {
7
+ @Column({ type: 'varchar', nullable: true })
8
+ name: string;
9
+
10
+ @ManyToMany(() => Role, role => role.permissions)
11
+ roles: Role[]
12
+ }
@@ -0,0 +1,21 @@
1
+ import {
2
+ Column,
3
+ Entity,
4
+ JoinColumn,
5
+ ManyToOne,
6
+ } from 'typeorm';
7
+ import { Property } from './property.entity';
8
+ import { AbstractBaseDocumentEntity } from './common.entity';
9
+
10
+ @Entity({ name: 'property-document' })
11
+ export class PropertyDocument extends AbstractBaseDocumentEntity {
12
+ @ManyToOne(() => Property, (property) => property.documents, {
13
+ onDelete: 'CASCADE',
14
+ onUpdate: 'CASCADE',
15
+ })
16
+ @JoinColumn({ name: 'property_id' })
17
+ property: Property;
18
+
19
+ @Column()
20
+ propertyId: number;
21
+ }
@@ -0,0 +1,23 @@
1
+ import {
2
+ Column,
3
+ Entity,
4
+ JoinColumn,
5
+ ManyToOne,
6
+ } from 'typeorm';
7
+ import { Property } from './property.entity';
8
+ import { AbstractBaseMediaEntity } from './common.entity';
9
+
10
+ @Entity({ name: 'property_media' })
11
+ export class PropertyMedia extends AbstractBaseMediaEntity {
12
+ @ManyToOne(() => Property, (property) => property.media, {
13
+ onDelete: 'CASCADE',
14
+ onUpdate: 'CASCADE',
15
+ })
16
+ @JoinColumn({ name: 'property_id' })
17
+ property: Property;
18
+
19
+ @Column({
20
+ nullable: false
21
+ })
22
+ propertyId: number
23
+ }