payservedb 6.6.2 → 6.6.4

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 (141) hide show
  1. package/.env +2 -2
  2. package/index.js +277 -277
  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 +92 -92
  10. package/src/models/assetsAssignment.js +64 -64
  11. package/src/models/auditTrail.js +346 -346
  12. package/src/models/bankdetails.js +40 -40
  13. package/src/models/billerAddress.js +119 -119
  14. package/src/models/booking_invoice.js +151 -151
  15. package/src/models/bookinganalytics.js +63 -63
  16. package/src/models/bookingconfig.js +45 -45
  17. package/src/models/bookingproperty.js +122 -122
  18. package/src/models/bookingreservation.js +192 -192
  19. package/src/models/bookingrevenuerecord.js +84 -84
  20. package/src/models/budget.js +95 -95
  21. package/src/models/budgetCategory.js +19 -19
  22. package/src/models/campaigns.js +94 -94
  23. package/src/models/cashpayment.js +264 -264
  24. package/src/models/combinedUnits.js +62 -62
  25. package/src/models/common_area_electricity.js +38 -38
  26. package/src/models/common_area_generator.js +41 -41
  27. package/src/models/common_area_utility_alert.js +37 -37
  28. package/src/models/common_area_water.js +39 -39
  29. package/src/models/communication_status.js +33 -33
  30. package/src/models/company.js +53 -53
  31. package/src/models/country_tax.js +42 -42
  32. package/src/models/currency_settings.js +39 -39
  33. package/src/models/customer.js +208 -208
  34. package/src/models/dailyChecklist.js +312 -312
  35. package/src/models/default_payment_details.js +17 -17
  36. package/src/models/deliveryTimeMarks.js +18 -18
  37. package/src/models/dutyRosterChecklist.js +250 -250
  38. package/src/models/dutyroster.js +136 -136
  39. package/src/models/email.js +37 -37
  40. package/src/models/email_sms_queue.js +61 -61
  41. package/src/models/entry_exit.js +53 -53
  42. package/src/models/expense.js +99 -99
  43. package/src/models/expense_category.js +45 -45
  44. package/src/models/facility.js +61 -61
  45. package/src/models/facilityWalletTransactionsMetadata.js +236 -236
  46. package/src/models/facility_departements.js +20 -20
  47. package/src/models/facility_payment_details.js +20 -20
  48. package/src/models/facilityasset.js +25 -25
  49. package/src/models/faq.js +18 -18
  50. package/src/models/gl_account_double_entries.js +25 -25
  51. package/src/models/gl_accounts.js +56 -56
  52. package/src/models/gl_entries.js +49 -49
  53. package/src/models/goodsReceivedNotes.js +115 -115
  54. package/src/models/guard.js +47 -47
  55. package/src/models/handover.js +246 -246
  56. package/src/models/invoice.js +336 -336
  57. package/src/models/item_inspection.js +67 -67
  58. package/src/models/leaseagreement.js +226 -226
  59. package/src/models/leasetemplate.js +17 -17
  60. package/src/models/levy.js +206 -206
  61. package/src/models/levy_invoice_settings.js +26 -26
  62. package/src/models/levycontract.js +117 -117
  63. package/src/models/levytype.js +23 -23
  64. package/src/models/maintenance_service_vendor.js +38 -38
  65. package/src/models/maintenance_services.js +17 -17
  66. package/src/models/maintenancerequisition.js +31 -31
  67. package/src/models/master_workplan.js +32 -32
  68. package/src/models/master_workplan_child.js +34 -34
  69. package/src/models/message.js +38 -38
  70. package/src/models/module.js +21 -21
  71. package/src/models/notification.js +44 -44
  72. package/src/models/paymentTermsMarks.js +19 -19
  73. package/src/models/penalty.js +76 -76
  74. package/src/models/pendingCredentials.js +32 -32
  75. package/src/models/powerMeterCustomerAccount.js +78 -78
  76. package/src/models/powerMeterCustomerBand.js +14 -14
  77. package/src/models/powerMeterDailyReading.js +30 -30
  78. package/src/models/powerMeterGateways.js +37 -0
  79. package/src/models/powerMeterMonthlyReading.js +34 -34
  80. package/src/models/powerMeterPowerCharges.js +53 -53
  81. package/src/models/powerMeterSettings.js +138 -138
  82. package/src/models/powerMeterSingleDayReading.js +32 -32
  83. package/src/models/powerMeters.js +93 -65
  84. package/src/models/powerMetersManufacturer.js +14 -14
  85. package/src/models/propertyManagerContract.js +556 -556
  86. package/src/models/propertyManagerRevenue.js +195 -195
  87. package/src/models/purchaseOrderInvoice.js +74 -74
  88. package/src/models/purchase_order.js +213 -213
  89. package/src/models/purchase_request.js +110 -110
  90. package/src/models/refresh_token.js +23 -23
  91. package/src/models/reminder.js +197 -197
  92. package/src/models/report.js +13 -13
  93. package/src/models/resident.js +121 -121
  94. package/src/models/rfq_details.js +131 -131
  95. package/src/models/rfq_response.js +153 -153
  96. package/src/models/service_charge_invoice_upload.js +42 -42
  97. package/src/models/service_charge_payments.js +27 -27
  98. package/src/models/servicerequest.js +55 -55
  99. package/src/models/settings.js +62 -62
  100. package/src/models/smart_meter_daily_consumption.js +44 -44
  101. package/src/models/sms_africastalking.js +20 -20
  102. package/src/models/sms_balance_notification.js +26 -26
  103. package/src/models/sms_meliora.js +20 -20
  104. package/src/models/staff.js +36 -36
  105. package/src/models/stocksandspare.js +161 -161
  106. package/src/models/suppliers.js +74 -74
  107. package/src/models/tickets.js +121 -121
  108. package/src/models/unitManagementTemplate.js +44 -44
  109. package/src/models/unitasset.js +25 -25
  110. package/src/models/units.js +117 -117
  111. package/src/models/user.js +186 -186
  112. package/src/models/valueaddedservices.js +79 -79
  113. package/src/models/vas_invoices_upload.js +50 -50
  114. package/src/models/vas_payments.js +24 -24
  115. package/src/models/vasinvoice.js +192 -192
  116. package/src/models/vasvendor.js +57 -57
  117. package/src/models/visitLog.js +86 -86
  118. package/src/models/visitor.js +67 -67
  119. package/src/models/waitlist.js +45 -45
  120. package/src/models/wallet.js +44 -44
  121. package/src/models/wallet_transactions.js +50 -50
  122. package/src/models/water_invoice.js +305 -305
  123. package/src/models/water_meter_Command_Queue.js +33 -33
  124. package/src/models/water_meter_account.js +78 -78
  125. package/src/models/water_meter_billing.js +58 -58
  126. package/src/models/water_meter_communication.js +17 -17
  127. package/src/models/water_meter_communication_logs.js +30 -30
  128. package/src/models/water_meter_concentrator.js +66 -66
  129. package/src/models/water_meter_daily_history.js +32 -32
  130. package/src/models/water_meter_iot_cards.js +34 -34
  131. package/src/models/water_meter_manufacturer.js +35 -35
  132. package/src/models/water_meter_monthly_history.js +36 -36
  133. package/src/models/water_meter_negative_amounts.js +38 -38
  134. package/src/models/water_meter_settings.js +180 -180
  135. package/src/models/water_meter_single_day_history.js +34 -34
  136. package/src/models/water_meter_size.js +15 -15
  137. package/src/models/water_meters.js +134 -134
  138. package/src/models/water_meters_delivery.js +76 -76
  139. package/src/models/water_prepaid_credit.js +47 -47
  140. package/src/models/water_prepaid_debit.js +50 -50
  141. package/src/models/workorder.js +49 -49
@@ -1,337 +1,337 @@
1
- const mongoose = require("mongoose");
2
-
3
- const invoiceSchema = new mongoose.Schema(
4
- {
5
- invoiceNumber: {
6
- type: String,
7
- required: true,
8
- unique: true,
9
- },
10
- accountNumber: {
11
- type: String,
12
- required: true,
13
- unique: true,
14
- },
15
- client: {
16
- clientId: {
17
- type: mongoose.Schema.Types.ObjectId,
18
- ref: "Customer",
19
- required: true,
20
- },
21
- firstName: {
22
- type: String,
23
- required: true
24
- },
25
- lastName: {
26
- type: String,
27
- required: true
28
- }
29
- },
30
- facility: {
31
- id: {
32
- type: mongoose.Schema.Types.ObjectId,
33
- ref: "Facility",
34
- required: true,
35
- },
36
- name: {
37
- type: String,
38
- required: true
39
- }
40
- },
41
- unit: {
42
- id: { type: mongoose.Schema.Types.ObjectId, ref: "Unit", required: true },
43
- name: { type: String, required: true },
44
- },
45
- currency: {
46
- id: {
47
- type: mongoose.Schema.Types.ObjectId,
48
- ref: "Currency",
49
- required: true
50
- },
51
- name: {
52
- type: String,
53
- required: true
54
- },
55
- code: {
56
- type: String,
57
- required: true,
58
- uppercase: true,
59
- minlength: 3,
60
- maxlength: 3
61
- }
62
- },
63
- items: [
64
- {
65
- description: { type: String, required: true },
66
- quantity: { type: Number, required: true, min: 1 },
67
- unitPrice: { type: Number, required: true, min: 0 },
68
- },
69
- ],
70
- subTotal: {
71
- type: Number,
72
- required: true,
73
- },
74
- tax: {
75
- type: Number,
76
- required: true,
77
- },
78
- totalAmount: {
79
- type: Number,
80
- required: true,
81
- },
82
- amountPaid: {
83
- type: Number,
84
- default: 0,
85
- min: 0,
86
- },
87
- // Mark as deprecated, keep for backward compatibility
88
- overpay: {
89
- type: Number,
90
- default: 0,
91
- min: 0,
92
- deprecated: true
93
- },
94
- issueDate: {
95
- type: Date,
96
- required: true,
97
- },
98
- dueDate: {
99
- type: Date,
100
- required: true,
101
- },
102
- status: {
103
- type: String,
104
- required: true,
105
- enum: ["Unpaid", "Pending", "Paid", "Overdue", "Cancelled", "Partially Paid", "Void"],
106
- },
107
- penalty: {
108
- type: Number,
109
- default: 0,
110
- },
111
- whatFor: {
112
- invoiceType: { type: String, required: true },
113
- description: { type: String },
114
- },
115
- invoiceNote: {
116
- type: String,
117
- default: null,
118
- },
119
- balanceBroughtForward: {
120
- type: Number,
121
- default: 0,
122
- },
123
- voidMetadata: {
124
- voidedBy: {
125
- userId: { type: mongoose.Schema.Types.ObjectId, ref: "User" },
126
- name: { type: String },
127
- role: { type: String }
128
- },
129
- voidedAt: { type: Date },
130
- reason: { type: String }
131
- },
132
- cancelMetadata: {
133
- cancelledBy: {
134
- userId: { type: mongoose.Schema.Types.ObjectId, ref: "User" },
135
- name: { type: String },
136
- role: { type: String }
137
- },
138
- cancelledAt: { type: Date },
139
- reason: { type: String }
140
- },
141
- lastReminderSent: Date,
142
- reminderHistory: [
143
- {
144
- sentAt: Date,
145
- reminderId: mongoose.Schema.Types.ObjectId,
146
- notificationTypes: [String],
147
- },
148
- ],
149
- reconciliationHistory: [{
150
- date: { type: Date, required: true },
151
- amount: { type: Number, required: true },
152
- type: {
153
- type: String,
154
- enum: ['payment', 'cash', 'cheque', 'bank-transfer', 'mpesa-transfer', 'overpay-transfer', 'balance-deduction', 'overpay-received', 'credit-forward', 'debit-forward'],
155
- required: true
156
- },
157
- sourceInvoice: String,
158
- destinationInvoice: String,
159
- paymentReference: String,
160
- paymentCompletion: String,
161
- remainingBalance: Number,
162
- notes: String,
163
- exchangeRate: {
164
- type: Number,
165
- default: 1 // For cross-currency reconciliations
166
- },
167
- originalCurrency: {
168
- code: String, // Original currency code if different from invoice currency
169
- amount: Number // Amount in original currency
170
- }
171
- }],
172
- paymentDetails: {
173
- paymentStatus: { type: String, required: true },
174
- paymentMethod: { type: String },
175
- paymentDate: { type: Date },
176
- transactionId: { type: String },
177
- },
178
- // New field to track when an invoice has been viewed
179
- viewStatus: {
180
- isOpened: { type: Boolean, default: false },
181
- openedAt: { type: Date, default: null },
182
- openedBy: {
183
- facilityId: { type: mongoose.Schema.Types.ObjectId, ref: "Facility" },
184
- userId: { type: mongoose.Schema.Types.ObjectId, ref: "User" },
185
- userRole: { type: String }
186
- },
187
- viewHistory: [{
188
- viewedAt: { type: Date, required: true },
189
- facilityId: { type: mongoose.Schema.Types.ObjectId, ref: "Facility" },
190
- userId: { type: mongoose.Schema.Types.ObjectId, ref: "User" }
191
- }]
192
- },
193
- invoiceUrl: {
194
- type: String,
195
- default: null
196
- },
197
- // New fields for double entry accounts
198
- invoiceDoubleEntryAccount: {
199
- type: mongoose.Schema.Types.ObjectId,
200
- ref: "GLAccountDoubleEntries",
201
- required: false,
202
- },
203
- paymentDoubleEntryAccount: {
204
- type: mongoose.Schema.Types.ObjectId,
205
- ref: "GLAccountDoubleEntries",
206
- required: false,
207
- },
208
- // GL account details for invoice entries
209
- accountdebitedData: {
210
- amount: { type: Number },
211
- description: { type: String },
212
- isActive: { type: Boolean, default: true }
213
- },
214
- accountcreditedData: {
215
- amount: { type: Number },
216
- description: { type: String },
217
- isActive: { type: Boolean, default: true }
218
- }
219
- },
220
- {
221
- timestamps: true,
222
- }
223
- );
224
-
225
- // Add indexes for frequently queried fields
226
- invoiceSchema.index({ accountNumber: 1 });
227
- invoiceSchema.index({ status: 1 });
228
- invoiceSchema.index({ 'client.clientId': 1, status: 1 });
229
- invoiceSchema.index({ 'reconciliationHistory.paymentReference': 1 });
230
- invoiceSchema.index({ issueDate: -1 });
231
- invoiceSchema.index({ 'currency.code': 1 }); // Add index for currency code
232
- invoiceSchema.index({ 'currency.id': 1 }); // Add index for currency ID
233
- invoiceSchema.index({ 'currency.code': 1, 'client.clientId': 1, status: 1 }); // Compound index for currency-based queries
234
- invoiceSchema.index({ 'client.clientId': 1, balanceBroughtForward: 1 }); // Add index for finding invoices with credit balances
235
- invoiceSchema.index({ 'viewStatus.isOpened': 1 }); // Add index for view status
236
- invoiceSchema.index({ 'viewStatus.openedBy.facilityId': 1 }); // Add index for facility view tracking
237
- // Add index for double entry accounts
238
- invoiceSchema.index({ invoiceDoubleEntryAccount: 1 });
239
- invoiceSchema.index({ paymentDoubleEntryAccount: 1 });
240
-
241
- // Add virtual field for calculating balance
242
- invoiceSchema.virtual('calculatedBalance').get(function () {
243
- const baseBalance = this.totalAmount - (this.amountPaid || 0);
244
-
245
- // Add positive balanceBroughtForward (customer owes money)
246
- if (this.balanceBroughtForward > 0) {
247
- return baseBalance + this.balanceBroughtForward;
248
- }
249
-
250
- // Subtract negative balanceBroughtForward (credit)
251
- return baseBalance;
252
- });
253
-
254
- // Add virtual field for credit balance
255
- invoiceSchema.virtual('creditBalance').get(function () {
256
- return this.balanceBroughtForward < 0 ? Math.abs(this.balanceBroughtForward) : 0;
257
- });
258
-
259
- // Getter for compatible overpay field
260
- invoiceSchema.virtual('effectiveOverpay').get(function () {
261
- return this.balanceBroughtForward < 0 ? Math.abs(this.balanceBroughtForward) : 0;
262
- });
263
-
264
- // Add virtual populate for invoice double entry account
265
- invoiceSchema.virtual('invoiceDoubleEntry', {
266
- ref: 'GLAccountDoubleEntries',
267
- localField: 'invoiceDoubleEntryAccount',
268
- foreignField: '_id',
269
- justOne: true
270
- });
271
-
272
- // Add virtual populate for payment double entry account
273
- invoiceSchema.virtual('paymentDoubleEntry', {
274
- ref: 'GLAccountDoubleEntries',
275
- localField: 'paymentDoubleEntryAccount',
276
- foreignField: '_id',
277
- justOne: true
278
- });
279
-
280
- // Add method for currency conversion if needed
281
- invoiceSchema.methods.convertAmount = function (amount, fromCurrency, toCurrency, exchangeRate) {
282
- if (fromCurrency === toCurrency) {
283
- return amount;
284
- }
285
- return amount * exchangeRate;
286
- };
287
-
288
- // Add static method to find invoices by currency
289
- invoiceSchema.statics.findByCurrency = function (currencyCode) {
290
- return this.find({ 'currency.code': currencyCode.toUpperCase() });
291
- };
292
-
293
- // Add static method to find invoices with credit balance
294
- invoiceSchema.statics.findWithCreditBalance = function (clientId) {
295
- return this.find({
296
- 'client.clientId': clientId,
297
- 'balanceBroughtForward': { $lt: 0 }
298
- }).sort({ updatedAt: -1 });
299
- };
300
-
301
- // Add static method to calculate totals by currency
302
- invoiceSchema.statics.calculateTotalsByCurrency = function (query = {}) {
303
- return this.aggregate([
304
- { $match: query },
305
- {
306
- $group: {
307
- _id: '$currency.code',
308
- totalAmount: { $sum: '$totalAmount' },
309
- totalPaid: { $sum: '$amountPaid' },
310
- count: { $sum: 1 }
311
- }
312
- }
313
- ]);
314
- };
315
-
316
- // New static method to find all unviewed invoices
317
- invoiceSchema.statics.findUnviewedInvoices = function (facilityId) {
318
- return this.find({
319
- 'facility.id': facilityId,
320
- 'viewStatus.isOpened': false
321
- });
322
- };
323
-
324
- // Pre-save middleware to ensure overpay and balanceBroughtForward stay in sync during transition
325
- invoiceSchema.pre('save', function (next) {
326
- // If balanceBroughtForward is negative (credit), sync with overpay for backwards compatibility
327
- if (this.balanceBroughtForward < 0) {
328
- this.overpay = Math.abs(this.balanceBroughtForward);
329
- } else {
330
- this.overpay = 0; // No overpay if there's no negative balance
331
- }
332
- next();
333
- });
334
-
335
- const Invoice = mongoose.model('Invoice', invoiceSchema);
336
-
1
+ const mongoose = require("mongoose");
2
+
3
+ const invoiceSchema = new mongoose.Schema(
4
+ {
5
+ invoiceNumber: {
6
+ type: String,
7
+ required: true,
8
+ unique: true,
9
+ },
10
+ accountNumber: {
11
+ type: String,
12
+ required: true,
13
+ unique: true,
14
+ },
15
+ client: {
16
+ clientId: {
17
+ type: mongoose.Schema.Types.ObjectId,
18
+ ref: "Customer",
19
+ required: true,
20
+ },
21
+ firstName: {
22
+ type: String,
23
+ required: true
24
+ },
25
+ lastName: {
26
+ type: String,
27
+ required: true
28
+ }
29
+ },
30
+ facility: {
31
+ id: {
32
+ type: mongoose.Schema.Types.ObjectId,
33
+ ref: "Facility",
34
+ required: true,
35
+ },
36
+ name: {
37
+ type: String,
38
+ required: true
39
+ }
40
+ },
41
+ unit: {
42
+ id: { type: mongoose.Schema.Types.ObjectId, ref: "Unit", required: true },
43
+ name: { type: String, required: true },
44
+ },
45
+ currency: {
46
+ id: {
47
+ type: mongoose.Schema.Types.ObjectId,
48
+ ref: "Currency",
49
+ required: true
50
+ },
51
+ name: {
52
+ type: String,
53
+ required: true
54
+ },
55
+ code: {
56
+ type: String,
57
+ required: true,
58
+ uppercase: true,
59
+ minlength: 3,
60
+ maxlength: 3
61
+ }
62
+ },
63
+ items: [
64
+ {
65
+ description: { type: String, required: true },
66
+ quantity: { type: Number, required: true, min: 1 },
67
+ unitPrice: { type: Number, required: true, min: 0 },
68
+ },
69
+ ],
70
+ subTotal: {
71
+ type: Number,
72
+ required: true,
73
+ },
74
+ tax: {
75
+ type: Number,
76
+ required: true,
77
+ },
78
+ totalAmount: {
79
+ type: Number,
80
+ required: true,
81
+ },
82
+ amountPaid: {
83
+ type: Number,
84
+ default: 0,
85
+ min: 0,
86
+ },
87
+ // Mark as deprecated, keep for backward compatibility
88
+ overpay: {
89
+ type: Number,
90
+ default: 0,
91
+ min: 0,
92
+ deprecated: true
93
+ },
94
+ issueDate: {
95
+ type: Date,
96
+ required: true,
97
+ },
98
+ dueDate: {
99
+ type: Date,
100
+ required: true,
101
+ },
102
+ status: {
103
+ type: String,
104
+ required: true,
105
+ enum: ["Unpaid", "Pending", "Paid", "Overdue", "Cancelled", "Partially Paid", "Void"],
106
+ },
107
+ penalty: {
108
+ type: Number,
109
+ default: 0,
110
+ },
111
+ whatFor: {
112
+ invoiceType: { type: String, required: true },
113
+ description: { type: String },
114
+ },
115
+ invoiceNote: {
116
+ type: String,
117
+ default: null,
118
+ },
119
+ balanceBroughtForward: {
120
+ type: Number,
121
+ default: 0,
122
+ },
123
+ voidMetadata: {
124
+ voidedBy: {
125
+ userId: { type: mongoose.Schema.Types.ObjectId, ref: "User" },
126
+ name: { type: String },
127
+ role: { type: String }
128
+ },
129
+ voidedAt: { type: Date },
130
+ reason: { type: String }
131
+ },
132
+ cancelMetadata: {
133
+ cancelledBy: {
134
+ userId: { type: mongoose.Schema.Types.ObjectId, ref: "User" },
135
+ name: { type: String },
136
+ role: { type: String }
137
+ },
138
+ cancelledAt: { type: Date },
139
+ reason: { type: String }
140
+ },
141
+ lastReminderSent: Date,
142
+ reminderHistory: [
143
+ {
144
+ sentAt: Date,
145
+ reminderId: mongoose.Schema.Types.ObjectId,
146
+ notificationTypes: [String],
147
+ },
148
+ ],
149
+ reconciliationHistory: [{
150
+ date: { type: Date, required: true },
151
+ amount: { type: Number, required: true },
152
+ type: {
153
+ type: String,
154
+ enum: ['payment', 'cash', 'cheque', 'bank-transfer', 'mpesa-transfer', 'overpay-transfer', 'balance-deduction', 'overpay-received', 'credit-forward', 'debit-forward'],
155
+ required: true
156
+ },
157
+ sourceInvoice: String,
158
+ destinationInvoice: String,
159
+ paymentReference: String,
160
+ paymentCompletion: String,
161
+ remainingBalance: Number,
162
+ notes: String,
163
+ exchangeRate: {
164
+ type: Number,
165
+ default: 1 // For cross-currency reconciliations
166
+ },
167
+ originalCurrency: {
168
+ code: String, // Original currency code if different from invoice currency
169
+ amount: Number // Amount in original currency
170
+ }
171
+ }],
172
+ paymentDetails: {
173
+ paymentStatus: { type: String, required: true },
174
+ paymentMethod: { type: String },
175
+ paymentDate: { type: Date },
176
+ transactionId: { type: String },
177
+ },
178
+ // New field to track when an invoice has been viewed
179
+ viewStatus: {
180
+ isOpened: { type: Boolean, default: false },
181
+ openedAt: { type: Date, default: null },
182
+ openedBy: {
183
+ facilityId: { type: mongoose.Schema.Types.ObjectId, ref: "Facility" },
184
+ userId: { type: mongoose.Schema.Types.ObjectId, ref: "User" },
185
+ userRole: { type: String }
186
+ },
187
+ viewHistory: [{
188
+ viewedAt: { type: Date, required: true },
189
+ facilityId: { type: mongoose.Schema.Types.ObjectId, ref: "Facility" },
190
+ userId: { type: mongoose.Schema.Types.ObjectId, ref: "User" }
191
+ }]
192
+ },
193
+ invoiceUrl: {
194
+ type: String,
195
+ default: null
196
+ },
197
+ // New fields for double entry accounts
198
+ invoiceDoubleEntryAccount: {
199
+ type: mongoose.Schema.Types.ObjectId,
200
+ ref: "GLAccountDoubleEntries",
201
+ required: false,
202
+ },
203
+ paymentDoubleEntryAccount: {
204
+ type: mongoose.Schema.Types.ObjectId,
205
+ ref: "GLAccountDoubleEntries",
206
+ required: false,
207
+ },
208
+ // GL account details for invoice entries
209
+ accountdebitedData: {
210
+ amount: { type: Number },
211
+ description: { type: String },
212
+ isActive: { type: Boolean, default: true }
213
+ },
214
+ accountcreditedData: {
215
+ amount: { type: Number },
216
+ description: { type: String },
217
+ isActive: { type: Boolean, default: true }
218
+ }
219
+ },
220
+ {
221
+ timestamps: true,
222
+ }
223
+ );
224
+
225
+ // Add indexes for frequently queried fields
226
+ invoiceSchema.index({ accountNumber: 1 });
227
+ invoiceSchema.index({ status: 1 });
228
+ invoiceSchema.index({ 'client.clientId': 1, status: 1 });
229
+ invoiceSchema.index({ 'reconciliationHistory.paymentReference': 1 });
230
+ invoiceSchema.index({ issueDate: -1 });
231
+ invoiceSchema.index({ 'currency.code': 1 }); // Add index for currency code
232
+ invoiceSchema.index({ 'currency.id': 1 }); // Add index for currency ID
233
+ invoiceSchema.index({ 'currency.code': 1, 'client.clientId': 1, status: 1 }); // Compound index for currency-based queries
234
+ invoiceSchema.index({ 'client.clientId': 1, balanceBroughtForward: 1 }); // Add index for finding invoices with credit balances
235
+ invoiceSchema.index({ 'viewStatus.isOpened': 1 }); // Add index for view status
236
+ invoiceSchema.index({ 'viewStatus.openedBy.facilityId': 1 }); // Add index for facility view tracking
237
+ // Add index for double entry accounts
238
+ invoiceSchema.index({ invoiceDoubleEntryAccount: 1 });
239
+ invoiceSchema.index({ paymentDoubleEntryAccount: 1 });
240
+
241
+ // Add virtual field for calculating balance
242
+ invoiceSchema.virtual('calculatedBalance').get(function () {
243
+ const baseBalance = this.totalAmount - (this.amountPaid || 0);
244
+
245
+ // Add positive balanceBroughtForward (customer owes money)
246
+ if (this.balanceBroughtForward > 0) {
247
+ return baseBalance + this.balanceBroughtForward;
248
+ }
249
+
250
+ // Subtract negative balanceBroughtForward (credit)
251
+ return baseBalance;
252
+ });
253
+
254
+ // Add virtual field for credit balance
255
+ invoiceSchema.virtual('creditBalance').get(function () {
256
+ return this.balanceBroughtForward < 0 ? Math.abs(this.balanceBroughtForward) : 0;
257
+ });
258
+
259
+ // Getter for compatible overpay field
260
+ invoiceSchema.virtual('effectiveOverpay').get(function () {
261
+ return this.balanceBroughtForward < 0 ? Math.abs(this.balanceBroughtForward) : 0;
262
+ });
263
+
264
+ // Add virtual populate for invoice double entry account
265
+ invoiceSchema.virtual('invoiceDoubleEntry', {
266
+ ref: 'GLAccountDoubleEntries',
267
+ localField: 'invoiceDoubleEntryAccount',
268
+ foreignField: '_id',
269
+ justOne: true
270
+ });
271
+
272
+ // Add virtual populate for payment double entry account
273
+ invoiceSchema.virtual('paymentDoubleEntry', {
274
+ ref: 'GLAccountDoubleEntries',
275
+ localField: 'paymentDoubleEntryAccount',
276
+ foreignField: '_id',
277
+ justOne: true
278
+ });
279
+
280
+ // Add method for currency conversion if needed
281
+ invoiceSchema.methods.convertAmount = function (amount, fromCurrency, toCurrency, exchangeRate) {
282
+ if (fromCurrency === toCurrency) {
283
+ return amount;
284
+ }
285
+ return amount * exchangeRate;
286
+ };
287
+
288
+ // Add static method to find invoices by currency
289
+ invoiceSchema.statics.findByCurrency = function (currencyCode) {
290
+ return this.find({ 'currency.code': currencyCode.toUpperCase() });
291
+ };
292
+
293
+ // Add static method to find invoices with credit balance
294
+ invoiceSchema.statics.findWithCreditBalance = function (clientId) {
295
+ return this.find({
296
+ 'client.clientId': clientId,
297
+ 'balanceBroughtForward': { $lt: 0 }
298
+ }).sort({ updatedAt: -1 });
299
+ };
300
+
301
+ // Add static method to calculate totals by currency
302
+ invoiceSchema.statics.calculateTotalsByCurrency = function (query = {}) {
303
+ return this.aggregate([
304
+ { $match: query },
305
+ {
306
+ $group: {
307
+ _id: '$currency.code',
308
+ totalAmount: { $sum: '$totalAmount' },
309
+ totalPaid: { $sum: '$amountPaid' },
310
+ count: { $sum: 1 }
311
+ }
312
+ }
313
+ ]);
314
+ };
315
+
316
+ // New static method to find all unviewed invoices
317
+ invoiceSchema.statics.findUnviewedInvoices = function (facilityId) {
318
+ return this.find({
319
+ 'facility.id': facilityId,
320
+ 'viewStatus.isOpened': false
321
+ });
322
+ };
323
+
324
+ // Pre-save middleware to ensure overpay and balanceBroughtForward stay in sync during transition
325
+ invoiceSchema.pre('save', function (next) {
326
+ // If balanceBroughtForward is negative (credit), sync with overpay for backwards compatibility
327
+ if (this.balanceBroughtForward < 0) {
328
+ this.overpay = Math.abs(this.balanceBroughtForward);
329
+ } else {
330
+ this.overpay = 0; // No overpay if there's no negative balance
331
+ }
332
+ next();
333
+ });
334
+
335
+ const Invoice = mongoose.model('Invoice', invoiceSchema);
336
+
337
337
  module.exports = Invoice;