payservedb 5.6.4 → 5.6.6

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 (113) hide show
  1. package/.env +2 -2
  2. package/index.js +193 -192
  3. package/package.json +17 -17
  4. package/src/models/account.js +35 -35
  5. package/src/models/apilog.js +18 -18
  6. package/src/models/approvalsWorkflows.js +49 -49
  7. package/src/models/archivedapilog.js +18 -18
  8. package/src/models/archivedauditlog.js +83 -83
  9. package/src/models/asset.js +34 -34
  10. package/src/models/auditlog.js +83 -83
  11. package/src/models/bankdetails.js +40 -40
  12. package/src/models/booking_invoice.js +151 -151
  13. package/src/models/bookinganalytics.js +63 -63
  14. package/src/models/bookingconfig.js +45 -45
  15. package/src/models/bookingproperty.js +112 -112
  16. package/src/models/bookingreservation.js +192 -192
  17. package/src/models/bookingrevenuerecord.js +84 -84
  18. package/src/models/budget.js +91 -91
  19. package/src/models/budgetCategory.js +19 -19
  20. package/src/models/campaigns.js +72 -72
  21. package/src/models/cashpayment.js +262 -262
  22. package/src/models/combinedUnits.js +62 -62
  23. package/src/models/common_area_electricity.js +38 -38
  24. package/src/models/common_area_generator.js +41 -41
  25. package/src/models/common_area_utility_alert.js +37 -37
  26. package/src/models/common_area_water.js +39 -39
  27. package/src/models/company.js +53 -53
  28. package/src/models/country_tax.js +42 -42
  29. package/src/models/currency_settings.js +39 -39
  30. package/src/models/customer.js +200 -200
  31. package/src/models/default_payment_details.js +17 -17
  32. package/src/models/dutyroster.js +107 -107
  33. package/src/models/email.js +37 -37
  34. package/src/models/entry_exit.js +53 -53
  35. package/src/models/expense.js +99 -99
  36. package/src/models/expense_category.js +19 -45
  37. package/src/models/facility.js +61 -61
  38. package/src/models/facility_payment_details.js +20 -20
  39. package/src/models/facilityasset.js +25 -25
  40. package/src/models/faq.js +18 -18
  41. package/src/models/gl_account_double_entries.js +25 -25
  42. package/src/models/gl_accounts.js +56 -56
  43. package/src/models/gl_entries.js +49 -49
  44. package/src/models/guard.js +47 -47
  45. package/src/models/handover.js +246 -246
  46. package/src/models/invoice.js +336 -336
  47. package/src/models/item_inspection.js +67 -67
  48. package/src/models/leaseagreement.js +221 -221
  49. package/src/models/leasetemplate.js +17 -17
  50. package/src/models/levy.js +152 -152
  51. package/src/models/levy_invoice_settings.js +26 -26
  52. package/src/models/levycontract.js +117 -117
  53. package/src/models/levytype.js +23 -23
  54. package/src/models/maintenance_service_vendor.js +38 -38
  55. package/src/models/maintenance_services.js +17 -17
  56. package/src/models/maintenancerequisition.js +31 -31
  57. package/src/models/message.js +38 -38
  58. package/src/models/module.js +21 -21
  59. package/src/models/notification.js +36 -24
  60. package/src/models/penalty.js +76 -76
  61. package/src/models/pendingCredentials.js +32 -32
  62. package/src/models/propertyManagerContract.js +358 -358
  63. package/src/models/propertyManagerRevenue.js +100 -100
  64. package/src/models/purchase_order.js +194 -194
  65. package/src/models/purchase_request.js +110 -110
  66. package/src/models/refresh_token.js +23 -23
  67. package/src/models/reminder.js +197 -197
  68. package/src/models/report.js +13 -13
  69. package/src/models/resident.js +121 -121
  70. package/src/models/rfq_details.js +131 -131
  71. package/src/models/rfq_response.js +105 -105
  72. package/src/models/service_charge_invoice_upload.js +42 -42
  73. package/src/models/service_charge_payments.js +27 -27
  74. package/src/models/servicerequest.js +55 -55
  75. package/src/models/settings.js +62 -62
  76. package/src/models/smart_meter_daily_consumption.js +44 -44
  77. package/src/models/sms_africastalking.js +20 -20
  78. package/src/models/sms_meliora.js +20 -20
  79. package/src/models/staff.js +36 -36
  80. package/src/models/stocksandspare.js +34 -34
  81. package/src/models/suppliers.js +74 -74
  82. package/src/models/tickets.js +109 -109
  83. package/src/models/unitManagementTemplate.js +44 -44
  84. package/src/models/unitasset.js +25 -25
  85. package/src/models/units.js +112 -112
  86. package/src/models/user.js +187 -187
  87. package/src/models/valueaddedservices.js +79 -79
  88. package/src/models/vas_invoices_upload.js +50 -50
  89. package/src/models/vas_payments.js +24 -24
  90. package/src/models/vasinvoice.js +192 -192
  91. package/src/models/vasvendor.js +57 -57
  92. package/src/models/visitLog.js +86 -86
  93. package/src/models/visitor.js +67 -67
  94. package/src/models/waitlist.js +45 -45
  95. package/src/models/water_invoice.js +193 -193
  96. package/src/models/water_meter_account.js +78 -78
  97. package/src/models/water_meter_billing.js +58 -58
  98. package/src/models/water_meter_communication.js +17 -17
  99. package/src/models/water_meter_communication_logs.js +30 -30
  100. package/src/models/water_meter_concentrator.js +59 -59
  101. package/src/models/water_meter_daily_history.js +32 -32
  102. package/src/models/water_meter_iot_cards.js +34 -34
  103. package/src/models/water_meter_manufacturer.js +35 -35
  104. package/src/models/water_meter_monthly_history.js +36 -36
  105. package/src/models/water_meter_settings.js +88 -114
  106. package/src/models/water_meter_single_day_history.js +34 -34
  107. package/src/models/water_meter_size.js +15 -15
  108. package/src/models/water_meters.js +117 -117
  109. package/src/models/water_meters_delivery.js +76 -76
  110. package/src/models/water_prepaid_credit.js +43 -43
  111. package/src/models/water_prepaid_debit.js +50 -50
  112. package/src/models/workorder.js +49 -49
  113. package/src/models/billerAddress.js +0 -117
@@ -1,359 +1,359 @@
1
- const mongoose = require('mongoose');
2
- const Schema = mongoose.Schema;
3
-
4
- const PropertyManagerContractSchema = new Schema(
5
- {
6
- contractName: {
7
- type: String,
8
- required: [true, 'Contract name is required']
9
- },
10
- propertyManager: {
11
- type: mongoose.Schema.Types.ObjectId,
12
- ref: 'User',
13
- required: [true, 'Property manager is required']
14
- },
15
- units: [{
16
- type: mongoose.Schema.Types.ObjectId,
17
- ref: 'Unit',
18
- required: [true, 'At least one unit is required']
19
- }],
20
- customerId: {
21
- type: mongoose.Schema.Types.ObjectId,
22
- ref: 'Customer',
23
- required: [true, 'Customer ID is required']
24
- },
25
- // Optional fields - only required when status is "Active"
26
- startDate: {
27
- type: Date,
28
- required: function() {
29
- return this.status === 'Active';
30
- }
31
- },
32
- endDate: {
33
- type: Date,
34
- required: function() {
35
- return this.status === 'Active';
36
- }
37
- },
38
- invoiceDay: {
39
- type: Number,
40
- required: function() {
41
- return this.status === 'Active';
42
- },
43
- min: [1, 'Invoice day must be between 1 and 31'],
44
- max: [31, 'Invoice day must be between 1 and 31']
45
- },
46
- dueDate: {
47
- type: String, // Store as string like "5th", "10th", etc.
48
- required: function() {
49
- return this.status === 'Active';
50
- }
51
- },
52
- balanceBroughtForward: {
53
- type: Number,
54
- default: 0
55
- },
56
- collectionFrequency: {
57
- type: String,
58
- enum: ['Daily', 'Weekly', 'Bi-Weekly', 'Monthly', 'Quarterly', 'Semi-Annually', 'Annually'],
59
- default: 'Monthly',
60
- required: function() {
61
- return this.status === 'Active';
62
- }
63
- },
64
- // Always required fields regardless of status
65
- managementFee: {
66
- type: {
67
- type: String,
68
- enum: ['percentage', 'amount'],
69
- required: [true, 'Management fee type is required']
70
- },
71
- value: {
72
- type: Number,
73
- required: [true, 'Management fee value is required'],
74
- min: [0, 'Management fee value cannot be negative']
75
- }
76
- },
77
- // GL Account configurations - matching LeaseAgreement pattern
78
- invoiceDoubleEntryAccount: {
79
- type: mongoose.Schema.Types.ObjectId,
80
- ref: 'GLAccountDoubleEntries'
81
- },
82
- paymentDoubleEntryAccount: {
83
- type: mongoose.Schema.Types.ObjectId,
84
- ref: 'GLAccountDoubleEntries'
85
- },
86
- // GL Account direct configurations (used when creating double entry records)
87
- glAccounts: {
88
- invoice: {
89
- debit: {
90
- type: mongoose.Schema.Types.ObjectId,
91
- ref: 'GLAccount'
92
- },
93
- credit: {
94
- type: mongoose.Schema.Types.ObjectId,
95
- ref: 'GLAccount'
96
- }
97
- },
98
- payment: {
99
- debit: {
100
- type: mongoose.Schema.Types.ObjectId,
101
- ref: 'GLAccount'
102
- },
103
- credit: {
104
- type: mongoose.Schema.Types.ObjectId,
105
- ref: 'GLAccount'
106
- }
107
- }
108
- },
109
- status: {
110
- type: String,
111
- enum: ['Active', 'Inactive', 'Completed', 'Suspended', 'Terminated'],
112
- default: 'Inactive',
113
- required: [true, 'Status is required']
114
- },
115
- facilityId: {
116
- type: mongoose.Schema.Types.ObjectId,
117
- ref: 'Facility',
118
- required: [true, 'Facility ID is required']
119
- },
120
- lastInvoiceDate: {
121
- type: Date
122
- },
123
- nextInvoiceDate: {
124
- type: Date
125
- },
126
- autoSend: {
127
- type: Boolean,
128
- default: false
129
- },
130
- // Track data source and sync information
131
- leaseDataSource: {
132
- type: String,
133
- enum: ['lease', 'manual'],
134
- default: 'manual'
135
- },
136
- lastSyncedAt: {
137
- type: Date
138
- },
139
- // Track which units have lease agreements
140
- unitsWithLeases: [{
141
- unitId: {
142
- type: mongoose.Schema.Types.ObjectId,
143
- ref: 'Unit'
144
- },
145
- leaseId: {
146
- type: mongoose.Schema.Types.ObjectId,
147
- ref: 'LeaseAgreement'
148
- },
149
- syncedAt: {
150
- type: Date,
151
- default: Date.now
152
- }
153
- }],
154
- createdBy: {
155
- type: mongoose.Schema.Types.ObjectId,
156
- ref: 'User'
157
- },
158
- updatedBy: {
159
- type: mongoose.Schema.Types.ObjectId,
160
- ref: 'User'
161
- },
162
- // Track contract edit history
163
- editHistory: [{
164
- editedBy: {
165
- type: mongoose.Schema.Types.Mixed,
166
- ref: 'User'
167
- },
168
- editedAt: {
169
- type: Date,
170
- default: Date.now
171
- },
172
- reason: {
173
- type: String,
174
- required: true
175
- },
176
- changes: {
177
- type: Object
178
- }
179
- }]
180
- },
181
- {
182
- timestamps: true,
183
- toJSON: { virtuals: true },
184
- toObject: { virtuals: true }
185
- }
186
- );
187
-
188
- // Virtual populate for Property Manager details
189
- PropertyManagerContractSchema.virtual('propertyManagerDetails', {
190
- ref: 'User',
191
- localField: 'propertyManager',
192
- foreignField: '_id',
193
- justOne: true
194
- });
195
-
196
- // Virtual populate for Customer details
197
- PropertyManagerContractSchema.virtual('customer', {
198
- ref: 'Customer',
199
- localField: 'customerId',
200
- foreignField: '_id',
201
- justOne: true
202
- });
203
-
204
- // Virtual populate for Units details
205
- PropertyManagerContractSchema.virtual('unitDetails', {
206
- ref: 'Unit',
207
- localField: 'units',
208
- foreignField: '_id'
209
- });
210
-
211
- // Virtual populate for invoice double entry account
212
- PropertyManagerContractSchema.virtual('invoiceDoubleEntry', {
213
- ref: 'GLAccountDoubleEntries',
214
- localField: 'invoiceDoubleEntryAccount',
215
- foreignField: '_id',
216
- justOne: true
217
- });
218
-
219
- // Virtual populate for payment double entry account
220
- PropertyManagerContractSchema.virtual('paymentDoubleEntry', {
221
- ref: 'GLAccountDoubleEntries',
222
- localField: 'paymentDoubleEntryAccount',
223
- foreignField: '_id',
224
- justOne: true
225
- });
226
-
227
- // Virtual populate for Facility details
228
- PropertyManagerContractSchema.virtual('facility', {
229
- ref: 'Facility',
230
- localField: 'facilityId',
231
- foreignField: '_id',
232
- justOne: true
233
- });
234
-
235
- // Virtual to check if contract is complete (has all lease-dependent fields)
236
- PropertyManagerContractSchema.virtual('isComplete').get(function() {
237
- return this.startDate && this.endDate && this.invoiceDay && this.dueDate && this.collectionFrequency;
238
- });
239
-
240
- // Virtual to get completion percentage
241
- PropertyManagerContractSchema.virtual('completionPercentage').get(function() {
242
- const requiredFields = ['startDate', 'endDate', 'invoiceDay', 'dueDate', 'collectionFrequency'];
243
- const completedFields = requiredFields.filter(field => this[field] != null).length;
244
- return Math.round((completedFields / requiredFields.length) * 100);
245
- });
246
-
247
- // Pre-save middleware to ensure endDate is after startDate (only when both exist)
248
- PropertyManagerContractSchema.pre('save', function (next) {
249
- if (this.startDate && this.endDate && this.startDate >= this.endDate) {
250
- next(new Error('End date must be after start date'));
251
- } else {
252
- next();
253
- }
254
- });
255
-
256
- // Pre-save middleware to validate units array is not empty
257
- PropertyManagerContractSchema.pre('save', function (next) {
258
- if (!this.units || this.units.length === 0) {
259
- next(new Error('At least one unit must be specified'));
260
- } else {
261
- next();
262
- }
263
- });
264
-
265
- // Pre-save middleware to validate management fee
266
- PropertyManagerContractSchema.pre('save', function (next) {
267
- if (this.managementFee.type === 'percentage' && this.managementFee.value > 100) {
268
- next(new Error('Management fee percentage cannot exceed 100%'));
269
- } else {
270
- next();
271
- }
272
- });
273
-
274
- // Pre-save middleware to auto-update status based on completion
275
- PropertyManagerContractSchema.pre('save', function (next) {
276
- // If contract has all required lease fields and is currently Inactive, make it Active
277
- if (this.status === 'Inactive' && this.isComplete) {
278
- this.status = 'Active';
279
- this.leaseDataSource = 'lease';
280
- this.lastSyncedAt = new Date();
281
- }
282
-
283
- // If contract is missing required fields and is currently Active, make it Inactive
284
- if (this.status === 'Active' && !this.isComplete) {
285
- this.status = 'Inactive';
286
- }
287
-
288
- next();
289
- });
290
-
291
- // Pre-save middleware to set next invoice date based on collection frequency (only for active contracts)
292
- PropertyManagerContractSchema.pre('save', function (next) {
293
- if (this.isNew && this.status === 'Active' && this.startDate && this.invoiceDay && !this.nextInvoiceDate) {
294
- const baseDate = new Date(this.startDate);
295
- baseDate.setDate(this.invoiceDay);
296
-
297
- // If the invoice day has passed this month, set for next month
298
- if (baseDate < this.startDate) {
299
- baseDate.setMonth(baseDate.getMonth() + 1);
300
- }
301
-
302
- this.nextInvoiceDate = baseDate;
303
- }
304
- next();
305
- });
306
-
307
- // Method to sync with lease data
308
- PropertyManagerContractSchema.methods.syncWithLeaseData = async function(leaseData) {
309
- if (!leaseData) return;
310
-
311
- this.startDate = leaseData.leaseTerms?.startDate || this.startDate;
312
- this.endDate = leaseData.leaseTerms?.endDate || this.endDate;
313
- this.invoiceDay = leaseData.financialTerms?.paymentDueDate || this.invoiceDay;
314
- this.balanceBroughtForward = leaseData.financialTerms?.balanceBroughtForward || this.balanceBroughtForward;
315
- this.collectionFrequency = leaseData.billingCycle?.frequency || this.collectionFrequency;
316
- this.autoSend = leaseData.billingCycle?.autoSend !== undefined ? leaseData.billingCycle.autoSend : this.autoSend;
317
-
318
- // Copy GL accounts if available
319
- if (leaseData.glAccounts) {
320
- this.glAccounts = leaseData.glAccounts;
321
- }
322
- if (leaseData.invoiceDoubleEntryAccount) {
323
- this.invoiceDoubleEntryAccount = leaseData.invoiceDoubleEntryAccount;
324
- }
325
- if (leaseData.paymentDoubleEntryAccount) {
326
- this.paymentDoubleEntryAccount = leaseData.paymentDoubleEntryAccount;
327
- }
328
-
329
- this.leaseDataSource = 'lease';
330
- this.lastSyncedAt = new Date();
331
-
332
- // Auto-calculate due date from payment due date if not set
333
- if (this.invoiceDay && !this.dueDate) {
334
- const dueDateMap = {
335
- 1: "1st", 5: "5th", 10: "10th", 15: "15th"
336
- };
337
- this.dueDate = dueDateMap[this.invoiceDay] || `${this.invoiceDay}th`;
338
- }
339
-
340
- return this.save();
341
- };
342
-
343
- // Indexes for efficient queries
344
- PropertyManagerContractSchema.index({ customerId: 1, status: 1 });
345
- PropertyManagerContractSchema.index({ facilityId: 1 });
346
- PropertyManagerContractSchema.index({ units: 1 });
347
- PropertyManagerContractSchema.index({ propertyManager: 1 }); // New index for property manager
348
- PropertyManagerContractSchema.index({ invoiceDoubleEntryAccount: 1 });
349
- PropertyManagerContractSchema.index({ paymentDoubleEntryAccount: 1 });
350
- PropertyManagerContractSchema.index({ startDate: 1, endDate: 1 });
351
- PropertyManagerContractSchema.index({ nextInvoiceDate: 1, status: 1 });
352
- PropertyManagerContractSchema.index({ leaseDataSource: 1 });
353
- PropertyManagerContractSchema.index({ 'unitsWithLeases.unitId': 1 });
354
-
355
-
356
- // Compile the model from the schema
357
- const PropertyManagerContract = mongoose.model("PropertyManagerContract", PropertyManagerContractSchema);
358
-
1
+ const mongoose = require('mongoose');
2
+ const Schema = mongoose.Schema;
3
+
4
+ const PropertyManagerContractSchema = new Schema(
5
+ {
6
+ contractName: {
7
+ type: String,
8
+ required: [true, 'Contract name is required']
9
+ },
10
+ propertyManager: {
11
+ type: mongoose.Schema.Types.ObjectId,
12
+ ref: 'User',
13
+ required: [true, 'Property manager is required']
14
+ },
15
+ units: [{
16
+ type: mongoose.Schema.Types.ObjectId,
17
+ ref: 'Unit',
18
+ required: [true, 'At least one unit is required']
19
+ }],
20
+ customerId: {
21
+ type: mongoose.Schema.Types.ObjectId,
22
+ ref: 'Customer',
23
+ required: [true, 'Customer ID is required']
24
+ },
25
+ // Optional fields - only required when status is "Active"
26
+ startDate: {
27
+ type: Date,
28
+ required: function() {
29
+ return this.status === 'Active';
30
+ }
31
+ },
32
+ endDate: {
33
+ type: Date,
34
+ required: function() {
35
+ return this.status === 'Active';
36
+ }
37
+ },
38
+ invoiceDay: {
39
+ type: Number,
40
+ required: function() {
41
+ return this.status === 'Active';
42
+ },
43
+ min: [1, 'Invoice day must be between 1 and 31'],
44
+ max: [31, 'Invoice day must be between 1 and 31']
45
+ },
46
+ dueDate: {
47
+ type: String, // Store as string like "5th", "10th", etc.
48
+ required: function() {
49
+ return this.status === 'Active';
50
+ }
51
+ },
52
+ balanceBroughtForward: {
53
+ type: Number,
54
+ default: 0
55
+ },
56
+ collectionFrequency: {
57
+ type: String,
58
+ enum: ['Daily', 'Weekly', 'Bi-Weekly', 'Monthly', 'Quarterly', 'Semi-Annually', 'Annually'],
59
+ default: 'Monthly',
60
+ required: function() {
61
+ return this.status === 'Active';
62
+ }
63
+ },
64
+ // Always required fields regardless of status
65
+ managementFee: {
66
+ type: {
67
+ type: String,
68
+ enum: ['percentage', 'amount'],
69
+ required: [true, 'Management fee type is required']
70
+ },
71
+ value: {
72
+ type: Number,
73
+ required: [true, 'Management fee value is required'],
74
+ min: [0, 'Management fee value cannot be negative']
75
+ }
76
+ },
77
+ // GL Account configurations - matching LeaseAgreement pattern
78
+ invoiceDoubleEntryAccount: {
79
+ type: mongoose.Schema.Types.ObjectId,
80
+ ref: 'GLAccountDoubleEntries'
81
+ },
82
+ paymentDoubleEntryAccount: {
83
+ type: mongoose.Schema.Types.ObjectId,
84
+ ref: 'GLAccountDoubleEntries'
85
+ },
86
+ // GL Account direct configurations (used when creating double entry records)
87
+ glAccounts: {
88
+ invoice: {
89
+ debit: {
90
+ type: mongoose.Schema.Types.ObjectId,
91
+ ref: 'GLAccount'
92
+ },
93
+ credit: {
94
+ type: mongoose.Schema.Types.ObjectId,
95
+ ref: 'GLAccount'
96
+ }
97
+ },
98
+ payment: {
99
+ debit: {
100
+ type: mongoose.Schema.Types.ObjectId,
101
+ ref: 'GLAccount'
102
+ },
103
+ credit: {
104
+ type: mongoose.Schema.Types.ObjectId,
105
+ ref: 'GLAccount'
106
+ }
107
+ }
108
+ },
109
+ status: {
110
+ type: String,
111
+ enum: ['Active', 'Inactive', 'Completed', 'Suspended', 'Terminated'],
112
+ default: 'Inactive',
113
+ required: [true, 'Status is required']
114
+ },
115
+ facilityId: {
116
+ type: mongoose.Schema.Types.ObjectId,
117
+ ref: 'Facility',
118
+ required: [true, 'Facility ID is required']
119
+ },
120
+ lastInvoiceDate: {
121
+ type: Date
122
+ },
123
+ nextInvoiceDate: {
124
+ type: Date
125
+ },
126
+ autoSend: {
127
+ type: Boolean,
128
+ default: false
129
+ },
130
+ // Track data source and sync information
131
+ leaseDataSource: {
132
+ type: String,
133
+ enum: ['lease', 'manual'],
134
+ default: 'manual'
135
+ },
136
+ lastSyncedAt: {
137
+ type: Date
138
+ },
139
+ // Track which units have lease agreements
140
+ unitsWithLeases: [{
141
+ unitId: {
142
+ type: mongoose.Schema.Types.ObjectId,
143
+ ref: 'Unit'
144
+ },
145
+ leaseId: {
146
+ type: mongoose.Schema.Types.ObjectId,
147
+ ref: 'LeaseAgreement'
148
+ },
149
+ syncedAt: {
150
+ type: Date,
151
+ default: Date.now
152
+ }
153
+ }],
154
+ createdBy: {
155
+ type: mongoose.Schema.Types.ObjectId,
156
+ ref: 'User'
157
+ },
158
+ updatedBy: {
159
+ type: mongoose.Schema.Types.ObjectId,
160
+ ref: 'User'
161
+ },
162
+ // Track contract edit history
163
+ editHistory: [{
164
+ editedBy: {
165
+ type: mongoose.Schema.Types.Mixed,
166
+ ref: 'User'
167
+ },
168
+ editedAt: {
169
+ type: Date,
170
+ default: Date.now
171
+ },
172
+ reason: {
173
+ type: String,
174
+ required: true
175
+ },
176
+ changes: {
177
+ type: Object
178
+ }
179
+ }]
180
+ },
181
+ {
182
+ timestamps: true,
183
+ toJSON: { virtuals: true },
184
+ toObject: { virtuals: true }
185
+ }
186
+ );
187
+
188
+ // Virtual populate for Property Manager details
189
+ PropertyManagerContractSchema.virtual('propertyManagerDetails', {
190
+ ref: 'User',
191
+ localField: 'propertyManager',
192
+ foreignField: '_id',
193
+ justOne: true
194
+ });
195
+
196
+ // Virtual populate for Customer details
197
+ PropertyManagerContractSchema.virtual('customer', {
198
+ ref: 'Customer',
199
+ localField: 'customerId',
200
+ foreignField: '_id',
201
+ justOne: true
202
+ });
203
+
204
+ // Virtual populate for Units details
205
+ PropertyManagerContractSchema.virtual('unitDetails', {
206
+ ref: 'Unit',
207
+ localField: 'units',
208
+ foreignField: '_id'
209
+ });
210
+
211
+ // Virtual populate for invoice double entry account
212
+ PropertyManagerContractSchema.virtual('invoiceDoubleEntry', {
213
+ ref: 'GLAccountDoubleEntries',
214
+ localField: 'invoiceDoubleEntryAccount',
215
+ foreignField: '_id',
216
+ justOne: true
217
+ });
218
+
219
+ // Virtual populate for payment double entry account
220
+ PropertyManagerContractSchema.virtual('paymentDoubleEntry', {
221
+ ref: 'GLAccountDoubleEntries',
222
+ localField: 'paymentDoubleEntryAccount',
223
+ foreignField: '_id',
224
+ justOne: true
225
+ });
226
+
227
+ // Virtual populate for Facility details
228
+ PropertyManagerContractSchema.virtual('facility', {
229
+ ref: 'Facility',
230
+ localField: 'facilityId',
231
+ foreignField: '_id',
232
+ justOne: true
233
+ });
234
+
235
+ // Virtual to check if contract is complete (has all lease-dependent fields)
236
+ PropertyManagerContractSchema.virtual('isComplete').get(function() {
237
+ return this.startDate && this.endDate && this.invoiceDay && this.dueDate && this.collectionFrequency;
238
+ });
239
+
240
+ // Virtual to get completion percentage
241
+ PropertyManagerContractSchema.virtual('completionPercentage').get(function() {
242
+ const requiredFields = ['startDate', 'endDate', 'invoiceDay', 'dueDate', 'collectionFrequency'];
243
+ const completedFields = requiredFields.filter(field => this[field] != null).length;
244
+ return Math.round((completedFields / requiredFields.length) * 100);
245
+ });
246
+
247
+ // Pre-save middleware to ensure endDate is after startDate (only when both exist)
248
+ PropertyManagerContractSchema.pre('save', function (next) {
249
+ if (this.startDate && this.endDate && this.startDate >= this.endDate) {
250
+ next(new Error('End date must be after start date'));
251
+ } else {
252
+ next();
253
+ }
254
+ });
255
+
256
+ // Pre-save middleware to validate units array is not empty
257
+ PropertyManagerContractSchema.pre('save', function (next) {
258
+ if (!this.units || this.units.length === 0) {
259
+ next(new Error('At least one unit must be specified'));
260
+ } else {
261
+ next();
262
+ }
263
+ });
264
+
265
+ // Pre-save middleware to validate management fee
266
+ PropertyManagerContractSchema.pre('save', function (next) {
267
+ if (this.managementFee.type === 'percentage' && this.managementFee.value > 100) {
268
+ next(new Error('Management fee percentage cannot exceed 100%'));
269
+ } else {
270
+ next();
271
+ }
272
+ });
273
+
274
+ // Pre-save middleware to auto-update status based on completion
275
+ PropertyManagerContractSchema.pre('save', function (next) {
276
+ // If contract has all required lease fields and is currently Inactive, make it Active
277
+ if (this.status === 'Inactive' && this.isComplete) {
278
+ this.status = 'Active';
279
+ this.leaseDataSource = 'lease';
280
+ this.lastSyncedAt = new Date();
281
+ }
282
+
283
+ // If contract is missing required fields and is currently Active, make it Inactive
284
+ if (this.status === 'Active' && !this.isComplete) {
285
+ this.status = 'Inactive';
286
+ }
287
+
288
+ next();
289
+ });
290
+
291
+ // Pre-save middleware to set next invoice date based on collection frequency (only for active contracts)
292
+ PropertyManagerContractSchema.pre('save', function (next) {
293
+ if (this.isNew && this.status === 'Active' && this.startDate && this.invoiceDay && !this.nextInvoiceDate) {
294
+ const baseDate = new Date(this.startDate);
295
+ baseDate.setDate(this.invoiceDay);
296
+
297
+ // If the invoice day has passed this month, set for next month
298
+ if (baseDate < this.startDate) {
299
+ baseDate.setMonth(baseDate.getMonth() + 1);
300
+ }
301
+
302
+ this.nextInvoiceDate = baseDate;
303
+ }
304
+ next();
305
+ });
306
+
307
+ // Method to sync with lease data
308
+ PropertyManagerContractSchema.methods.syncWithLeaseData = async function(leaseData) {
309
+ if (!leaseData) return;
310
+
311
+ this.startDate = leaseData.leaseTerms?.startDate || this.startDate;
312
+ this.endDate = leaseData.leaseTerms?.endDate || this.endDate;
313
+ this.invoiceDay = leaseData.financialTerms?.paymentDueDate || this.invoiceDay;
314
+ this.balanceBroughtForward = leaseData.financialTerms?.balanceBroughtForward || this.balanceBroughtForward;
315
+ this.collectionFrequency = leaseData.billingCycle?.frequency || this.collectionFrequency;
316
+ this.autoSend = leaseData.billingCycle?.autoSend !== undefined ? leaseData.billingCycle.autoSend : this.autoSend;
317
+
318
+ // Copy GL accounts if available
319
+ if (leaseData.glAccounts) {
320
+ this.glAccounts = leaseData.glAccounts;
321
+ }
322
+ if (leaseData.invoiceDoubleEntryAccount) {
323
+ this.invoiceDoubleEntryAccount = leaseData.invoiceDoubleEntryAccount;
324
+ }
325
+ if (leaseData.paymentDoubleEntryAccount) {
326
+ this.paymentDoubleEntryAccount = leaseData.paymentDoubleEntryAccount;
327
+ }
328
+
329
+ this.leaseDataSource = 'lease';
330
+ this.lastSyncedAt = new Date();
331
+
332
+ // Auto-calculate due date from payment due date if not set
333
+ if (this.invoiceDay && !this.dueDate) {
334
+ const dueDateMap = {
335
+ 1: "1st", 5: "5th", 10: "10th", 15: "15th"
336
+ };
337
+ this.dueDate = dueDateMap[this.invoiceDay] || `${this.invoiceDay}th`;
338
+ }
339
+
340
+ return this.save();
341
+ };
342
+
343
+ // Indexes for efficient queries
344
+ PropertyManagerContractSchema.index({ customerId: 1, status: 1 });
345
+ PropertyManagerContractSchema.index({ facilityId: 1 });
346
+ PropertyManagerContractSchema.index({ units: 1 });
347
+ PropertyManagerContractSchema.index({ propertyManager: 1 }); // New index for property manager
348
+ PropertyManagerContractSchema.index({ invoiceDoubleEntryAccount: 1 });
349
+ PropertyManagerContractSchema.index({ paymentDoubleEntryAccount: 1 });
350
+ PropertyManagerContractSchema.index({ startDate: 1, endDate: 1 });
351
+ PropertyManagerContractSchema.index({ nextInvoiceDate: 1, status: 1 });
352
+ PropertyManagerContractSchema.index({ leaseDataSource: 1 });
353
+ PropertyManagerContractSchema.index({ 'unitsWithLeases.unitId': 1 });
354
+
355
+
356
+ // Compile the model from the schema
357
+ const PropertyManagerContract = mongoose.model("PropertyManagerContract", PropertyManagerContractSchema);
358
+
359
359
  module.exports = PropertyManagerContract;