payservedb 6.2.9 → 6.3.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 (136) hide show
  1. package/.env +2 -2
  2. package/index.js +272 -272
  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/assetsAssignment.js +64 -64
  11. package/src/models/auditlog.js +83 -83
  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 +112 -112
  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 +72 -72
  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/default_payment_details.js +17 -17
  35. package/src/models/deliveryTimeMarks.js +18 -18
  36. package/src/models/dutyroster.js +107 -107
  37. package/src/models/email.js +37 -37
  38. package/src/models/email_sms_queue.js +61 -61
  39. package/src/models/entry_exit.js +53 -53
  40. package/src/models/expense.js +99 -99
  41. package/src/models/expense_category.js +45 -45
  42. package/src/models/facility.js +61 -61
  43. package/src/models/facilityWalletTransactionsMetadata.js +239 -239
  44. package/src/models/facility_departements.js +20 -20
  45. package/src/models/facility_payment_details.js +20 -20
  46. package/src/models/facilityasset.js +25 -25
  47. package/src/models/faq.js +18 -18
  48. package/src/models/gl_account_double_entries.js +25 -25
  49. package/src/models/gl_accounts.js +56 -56
  50. package/src/models/gl_entries.js +49 -49
  51. package/src/models/goodsReceivedNotes.js +115 -115
  52. package/src/models/guard.js +47 -47
  53. package/src/models/handover.js +246 -246
  54. package/src/models/invoice.js +336 -336
  55. package/src/models/item_inspection.js +67 -67
  56. package/src/models/leaseagreement.js +221 -221
  57. package/src/models/leasetemplate.js +17 -17
  58. package/src/models/levy.js +206 -206
  59. package/src/models/levy_invoice_settings.js +26 -26
  60. package/src/models/levycontract.js +117 -117
  61. package/src/models/levytype.js +23 -23
  62. package/src/models/maintenance_service_vendor.js +38 -38
  63. package/src/models/maintenance_services.js +17 -17
  64. package/src/models/maintenancerequisition.js +31 -31
  65. package/src/models/message.js +38 -38
  66. package/src/models/module.js +21 -21
  67. package/src/models/notification.js +44 -44
  68. package/src/models/paymentTermsMarks.js +19 -19
  69. package/src/models/penalty.js +76 -76
  70. package/src/models/pendingCredentials.js +32 -32
  71. package/src/models/powerMeterCustomerAccount.js +78 -78
  72. package/src/models/powerMeterCustomerBand.js +14 -14
  73. package/src/models/powerMeterDailyReading.js +30 -30
  74. package/src/models/powerMeterMonthlyReading.js +34 -34
  75. package/src/models/powerMeterPowerCharges.js +53 -53
  76. package/src/models/powerMeterSettings.js +138 -138
  77. package/src/models/powerMeterSingleDayReading.js +32 -32
  78. package/src/models/powerMeters.js +65 -65
  79. package/src/models/powerMetersManufacturer.js +14 -14
  80. package/src/models/propertyManagerContract.js +556 -556
  81. package/src/models/propertyManagerRevenue.js +195 -195
  82. package/src/models/purchaseOrderInvoice.js +74 -74
  83. package/src/models/purchase_order.js +213 -202
  84. package/src/models/purchase_request.js +110 -110
  85. package/src/models/refresh_token.js +23 -23
  86. package/src/models/reminder.js +197 -197
  87. package/src/models/report.js +13 -13
  88. package/src/models/resident.js +121 -121
  89. package/src/models/rfq_details.js +131 -131
  90. package/src/models/rfq_response.js +153 -153
  91. package/src/models/service_charge_invoice_upload.js +42 -42
  92. package/src/models/service_charge_payments.js +27 -27
  93. package/src/models/servicerequest.js +55 -55
  94. package/src/models/settings.js +62 -62
  95. package/src/models/smart_meter_daily_consumption.js +44 -44
  96. package/src/models/sms_africastalking.js +20 -20
  97. package/src/models/sms_balance_notification.js +26 -26
  98. package/src/models/sms_meliora.js +20 -20
  99. package/src/models/staff.js +36 -36
  100. package/src/models/stocksandspare.js +34 -34
  101. package/src/models/suppliers.js +74 -74
  102. package/src/models/tickets.js +121 -121
  103. package/src/models/unitManagementTemplate.js +44 -44
  104. package/src/models/unitasset.js +25 -25
  105. package/src/models/units.js +112 -112
  106. package/src/models/user.js +186 -186
  107. package/src/models/valueaddedservices.js +79 -79
  108. package/src/models/vas_invoices_upload.js +50 -50
  109. package/src/models/vas_payments.js +24 -24
  110. package/src/models/vasinvoice.js +192 -192
  111. package/src/models/vasvendor.js +57 -57
  112. package/src/models/visitLog.js +86 -86
  113. package/src/models/visitor.js +67 -67
  114. package/src/models/waitlist.js +45 -45
  115. package/src/models/wallet.js +44 -44
  116. package/src/models/wallet_transactions.js +50 -50
  117. package/src/models/water_invoice.js +193 -193
  118. package/src/models/water_meter_Command_Queue.js +33 -33
  119. package/src/models/water_meter_account.js +78 -78
  120. package/src/models/water_meter_billing.js +58 -58
  121. package/src/models/water_meter_communication.js +17 -17
  122. package/src/models/water_meter_communication_logs.js +30 -30
  123. package/src/models/water_meter_concentrator.js +66 -66
  124. package/src/models/water_meter_daily_history.js +32 -32
  125. package/src/models/water_meter_iot_cards.js +34 -34
  126. package/src/models/water_meter_manufacturer.js +35 -35
  127. package/src/models/water_meter_monthly_history.js +36 -36
  128. package/src/models/water_meter_negative_amounts.js +38 -38
  129. package/src/models/water_meter_settings.js +150 -114
  130. package/src/models/water_meter_single_day_history.js +34 -34
  131. package/src/models/water_meter_size.js +15 -15
  132. package/src/models/water_meters.js +123 -123
  133. package/src/models/water_meters_delivery.js +76 -76
  134. package/src/models/water_prepaid_credit.js +47 -47
  135. package/src/models/water_prepaid_debit.js +50 -50
  136. 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;