payservedb 6.1.6 → 6.1.8

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