payservedb 7.9.1 → 7.9.2

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 (159) hide show
  1. package/.env +2 -2
  2. package/.idea/material_theme_project_new.xml +11 -11
  3. package/.idea/modules.xml +7 -7
  4. package/.idea/psdb.iml +11 -11
  5. package/.idea/vcs.xml +5 -5
  6. package/index.js +288 -287
  7. package/package.json +17 -17
  8. package/src/models/account.js +35 -35
  9. package/src/models/apilog.js +18 -18
  10. package/src/models/approvalsWorkflows.js +49 -49
  11. package/src/models/archivedapilog.js +18 -18
  12. package/src/models/asset.js +92 -92
  13. package/src/models/assetsAssignment.js +64 -64
  14. package/src/models/auditTrail.js +346 -346
  15. package/src/models/bankdetails.js +43 -43
  16. package/src/models/billerAddress.js +124 -124
  17. package/src/models/booking_invoice.js +151 -151
  18. package/src/models/bookinganalytics.js +63 -63
  19. package/src/models/bookingconfig.js +45 -45
  20. package/src/models/bookingproperty.js +122 -122
  21. package/src/models/bookingreservation.js +192 -192
  22. package/src/models/bookingrevenuerecord.js +84 -84
  23. package/src/models/budget.js +95 -95
  24. package/src/models/budgetCategory.js +19 -19
  25. package/src/models/campaigns.js +94 -94
  26. package/src/models/cashpayment.js +264 -264
  27. package/src/models/combinedUnits.js +62 -62
  28. package/src/models/common_area_electricity.js +38 -38
  29. package/src/models/common_area_generator.js +41 -41
  30. package/src/models/common_area_utility_alert.js +37 -37
  31. package/src/models/common_area_water.js +39 -39
  32. package/src/models/communication_status.js +33 -33
  33. package/src/models/company.js +53 -53
  34. package/src/models/coreBaseSettings.js +16 -16
  35. package/src/models/coreInvoiceSettings.js +100 -100
  36. package/src/models/country_tax.js +42 -42
  37. package/src/models/currency_settings.js +39 -39
  38. package/src/models/customer.js +208 -208
  39. package/src/models/dailyChecklist.js +312 -312
  40. package/src/models/default_payment_details.js +17 -17
  41. package/src/models/deliveryTimeMarks.js +18 -18
  42. package/src/models/dutyRosterChecklist.js +250 -250
  43. package/src/models/dutyroster.js +136 -136
  44. package/src/models/email.js +37 -37
  45. package/src/models/email_sms_queue.js +61 -61
  46. package/src/models/entry_exit.js +53 -53
  47. package/src/models/expense.js +99 -99
  48. package/src/models/expense_category.js +45 -45
  49. package/src/models/facility.js +62 -62
  50. package/src/models/facilityBillingPrices.js +30 -0
  51. package/src/models/facilityInvoice.js +223 -223
  52. package/src/models/facilityInvoiceRecipient.js +32 -32
  53. package/src/models/facilityWalletTransactionsMetadata.js +236 -236
  54. package/src/models/facility_departements.js +20 -20
  55. package/src/models/facility_payment_details.js +20 -20
  56. package/src/models/facilityasset.js +25 -25
  57. package/src/models/faq.js +18 -18
  58. package/src/models/gl_account_double_entries.js +25 -25
  59. package/src/models/gl_accounts.js +56 -56
  60. package/src/models/gl_entries.js +49 -49
  61. package/src/models/goodsReceivedNotes.js +115 -115
  62. package/src/models/guard.js +47 -47
  63. package/src/models/handover.js +246 -246
  64. package/src/models/invoice.js +336 -336
  65. package/src/models/item_inspection.js +67 -67
  66. package/src/models/leaseagreement.js +226 -226
  67. package/src/models/leasetemplate.js +17 -17
  68. package/src/models/levy.js +206 -206
  69. package/src/models/levy_invoice_settings.js +26 -26
  70. package/src/models/levycontract.js +168 -168
  71. package/src/models/levytype.js +23 -23
  72. package/src/models/maintenance_service_vendor.js +38 -38
  73. package/src/models/maintenance_services.js +17 -17
  74. package/src/models/maintenancerequisition.js +31 -31
  75. package/src/models/master_workplan.js +32 -32
  76. package/src/models/master_workplan_child.js +34 -34
  77. package/src/models/message.js +38 -38
  78. package/src/models/module.js +21 -21
  79. package/src/models/notification.js +44 -44
  80. package/src/models/paymentTermsMarks.js +19 -19
  81. package/src/models/penalty.js +76 -76
  82. package/src/models/pendingCredentials.js +32 -32
  83. package/src/models/powerMeterCommunicationProtocol.js +17 -17
  84. package/src/models/powerMeterCustomerAccount.js +78 -78
  85. package/src/models/powerMeterCustomerBand.js +14 -14
  86. package/src/models/powerMeterDailyReading.js +30 -30
  87. package/src/models/powerMeterGateways.js +40 -40
  88. package/src/models/powerMeterMonthlyReading.js +34 -34
  89. package/src/models/powerMeterPowerCharges.js +85 -85
  90. package/src/models/powerMeterSettings.js +159 -159
  91. package/src/models/powerMeterSingleDayReading.js +32 -32
  92. package/src/models/powerMeters.js +116 -116
  93. package/src/models/powerMetersManufacturer.js +14 -14
  94. package/src/models/power_meter_account.js +81 -81
  95. package/src/models/power_meter_command_logs.js +30 -30
  96. package/src/models/power_meter_command_queue.js +33 -33
  97. package/src/models/power_meter_negative_balance.js +44 -44
  98. package/src/models/power_prepaid_credits.js +47 -47
  99. package/src/models/power_prepaid_debits.js +50 -50
  100. package/src/models/power_prepaid_orders.js +78 -78
  101. package/src/models/power_sms_notification.js +26 -26
  102. package/src/models/propertyManagerContract.js +556 -556
  103. package/src/models/propertyManagerRevenue.js +195 -195
  104. package/src/models/purchaseOrderInvoice.js +74 -74
  105. package/src/models/purchase_order.js +213 -213
  106. package/src/models/purchase_request.js +110 -110
  107. package/src/models/refresh_token.js +23 -23
  108. package/src/models/reminder.js +197 -197
  109. package/src/models/report.js +13 -13
  110. package/src/models/resident.js +121 -121
  111. package/src/models/rfq_details.js +131 -131
  112. package/src/models/rfq_response.js +153 -153
  113. package/src/models/service_charge_invoice_upload.js +42 -42
  114. package/src/models/service_charge_payments.js +27 -27
  115. package/src/models/servicerequest.js +55 -55
  116. package/src/models/settings.js +62 -62
  117. package/src/models/smart_meter_daily_consumption.js +44 -44
  118. package/src/models/sms_africastalking.js +20 -20
  119. package/src/models/sms_balance_notification.js +26 -26
  120. package/src/models/sms_meliora.js +20 -20
  121. package/src/models/staff.js +36 -36
  122. package/src/models/stocksandspare.js +161 -161
  123. package/src/models/suppliers.js +74 -74
  124. package/src/models/tickets.js +121 -121
  125. package/src/models/unitManagementTemplate.js +44 -44
  126. package/src/models/unitasset.js +25 -25
  127. package/src/models/units.js +117 -117
  128. package/src/models/user.js +186 -186
  129. package/src/models/valueaddedservices.js +79 -79
  130. package/src/models/vas_invoices_upload.js +50 -50
  131. package/src/models/vas_payments.js +24 -24
  132. package/src/models/vasinvoice.js +192 -192
  133. package/src/models/vasvendor.js +57 -57
  134. package/src/models/visitLog.js +95 -95
  135. package/src/models/visitor.js +67 -67
  136. package/src/models/waitlist.js +45 -45
  137. package/src/models/wallet.js +44 -44
  138. package/src/models/wallet_transactions.js +50 -50
  139. package/src/models/water_invoice.js +351 -351
  140. package/src/models/water_meter_Command_Queue.js +33 -33
  141. package/src/models/water_meter_account.js +82 -82
  142. package/src/models/water_meter_billing.js +58 -58
  143. package/src/models/water_meter_communication.js +17 -17
  144. package/src/models/water_meter_communication_logs.js +39 -39
  145. package/src/models/water_meter_concentrator.js +66 -66
  146. package/src/models/water_meter_daily_history.js +32 -32
  147. package/src/models/water_meter_high_risk.js +36 -36
  148. package/src/models/water_meter_iot_cards.js +34 -34
  149. package/src/models/water_meter_manufacturer.js +35 -35
  150. package/src/models/water_meter_monthly_history.js +36 -36
  151. package/src/models/water_meter_negative_amounts.js +44 -44
  152. package/src/models/water_meter_settings.js +261 -261
  153. package/src/models/water_meter_single_day_history.js +34 -34
  154. package/src/models/water_meter_size.js +15 -15
  155. package/src/models/water_meters.js +133 -133
  156. package/src/models/water_meters_delivery.js +76 -76
  157. package/src/models/water_prepaid_credit.js +47 -47
  158. package/src/models/water_prepaid_debit.js +50 -50
  159. 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;