payservedb 9.0.7 → 9.0.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.
- package/package.json +1 -1
- package/src/models/powerMeterSettings.js +48 -7
- package/src/models/power_invoice.js +101 -212
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@ const powerMeterSettingsSchema = new mongoose.Schema({
|
|
|
5
5
|
type: mongoose.Schema.Types.ObjectId,
|
|
6
6
|
ref: 'Facility',
|
|
7
7
|
required: true,
|
|
8
|
-
unique: true
|
|
8
|
+
unique: true
|
|
9
9
|
},
|
|
10
10
|
|
|
11
11
|
// Billing Amounts
|
|
@@ -79,33 +79,72 @@ const powerMeterSettingsSchema = new mongoose.Schema({
|
|
|
79
79
|
min: 0
|
|
80
80
|
},
|
|
81
81
|
|
|
82
|
-
//
|
|
82
|
+
// Biller Address — stores the selected BillerAddress document ID
|
|
83
|
+
billerAddressId: {
|
|
84
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
85
|
+
ref: 'BillerAddress',
|
|
86
|
+
required: false,
|
|
87
|
+
default: null
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
// Payment Methods — mirrors the water meter settings pattern
|
|
91
|
+
paymentMethods: {
|
|
92
|
+
mobilePayment: {
|
|
93
|
+
status: {
|
|
94
|
+
type: Boolean,
|
|
95
|
+
default: false
|
|
96
|
+
},
|
|
97
|
+
paymentId: {
|
|
98
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
99
|
+
ref: 'FacilityPaymentDetails',
|
|
100
|
+
default: null
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
bankPayment: {
|
|
104
|
+
status: {
|
|
105
|
+
type: Boolean,
|
|
106
|
+
default: false
|
|
107
|
+
},
|
|
108
|
+
paymentId: {
|
|
109
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
110
|
+
ref: 'BankDetails',
|
|
111
|
+
default: null
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
// GL Account Configuration
|
|
83
117
|
glAccounts: {
|
|
84
118
|
invoice: {
|
|
85
119
|
debit: {
|
|
86
120
|
type: mongoose.Schema.Types.ObjectId,
|
|
87
|
-
ref:
|
|
121
|
+
ref: 'GLAccount',
|
|
88
122
|
required: false,
|
|
123
|
+
default: null
|
|
89
124
|
},
|
|
90
125
|
credit: {
|
|
91
126
|
type: mongoose.Schema.Types.ObjectId,
|
|
92
|
-
ref:
|
|
127
|
+
ref: 'GLAccount',
|
|
93
128
|
required: false,
|
|
129
|
+
default: null
|
|
94
130
|
}
|
|
95
131
|
},
|
|
96
132
|
payment: {
|
|
97
133
|
debit: {
|
|
98
134
|
type: mongoose.Schema.Types.ObjectId,
|
|
99
|
-
ref:
|
|
135
|
+
ref: 'GLAccount',
|
|
100
136
|
required: false,
|
|
137
|
+
default: null
|
|
101
138
|
},
|
|
102
139
|
credit: {
|
|
103
140
|
type: mongoose.Schema.Types.ObjectId,
|
|
104
|
-
ref:
|
|
141
|
+
ref: 'GLAccount',
|
|
105
142
|
required: false,
|
|
143
|
+
default: null
|
|
106
144
|
}
|
|
107
145
|
}
|
|
108
146
|
},
|
|
147
|
+
|
|
109
148
|
// Discounts
|
|
110
149
|
discounts: [{
|
|
111
150
|
name: {
|
|
@@ -128,12 +167,14 @@ const powerMeterSettingsSchema = new mongoose.Schema({
|
|
|
128
167
|
required: true
|
|
129
168
|
}
|
|
130
169
|
}],
|
|
131
|
-
|
|
170
|
+
|
|
171
|
+
// Other Charges (KPLC)
|
|
132
172
|
otherCharges: {
|
|
133
173
|
type: String,
|
|
134
174
|
enum: ['yes', 'no', ''],
|
|
135
175
|
default: ''
|
|
136
176
|
},
|
|
177
|
+
|
|
137
178
|
// Notifications
|
|
138
179
|
notifications: {
|
|
139
180
|
type: Boolean,
|
|
@@ -16,6 +16,12 @@ const powerInvoiceSchema = new mongoose.Schema(
|
|
|
16
16
|
type: String,
|
|
17
17
|
required: true,
|
|
18
18
|
},
|
|
19
|
+
// Unit reference — stored directly from PowerMeterAccount.unitId / PowerMeters.unitId
|
|
20
|
+
unitId: {
|
|
21
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
22
|
+
ref: 'Unit',
|
|
23
|
+
default: null,
|
|
24
|
+
},
|
|
19
25
|
yearMonth: {
|
|
20
26
|
type: String,
|
|
21
27
|
required: true,
|
|
@@ -40,42 +46,14 @@ const powerInvoiceSchema = new mongoose.Schema(
|
|
|
40
46
|
// Positive = arrears carried forward; Negative = credit from overpayment
|
|
41
47
|
},
|
|
42
48
|
|
|
43
|
-
// ──
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
},
|
|
50
|
-
paymentId: {
|
|
51
|
-
type: mongoose.Schema.Types.ObjectId,
|
|
52
|
-
ref: 'FacilityPaymentDetails',
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
bankPayment: {
|
|
56
|
-
status: {
|
|
57
|
-
type: Boolean,
|
|
58
|
-
default: false,
|
|
59
|
-
},
|
|
60
|
-
paymentId: {
|
|
61
|
-
type: mongoose.Schema.Types.ObjectId,
|
|
62
|
-
ref: 'BankDetails',
|
|
63
|
-
},
|
|
64
|
-
},
|
|
65
|
-
cashPayment: {
|
|
66
|
-
status: {
|
|
67
|
-
type: Boolean,
|
|
68
|
-
default: false,
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
},
|
|
72
|
-
paymentMethod: {
|
|
73
|
-
type: String,
|
|
74
|
-
enum: ['mobile', 'bank', 'cash', 'mixed'],
|
|
49
|
+
// ── Biller Address ─────────────────────────────────────────────────────────
|
|
50
|
+
// Reference to the BillerAddress document configured in PowerMeterSettings.
|
|
51
|
+
// The sub-document below is a denormalised snapshot captured at invoice time.
|
|
52
|
+
billerAddressId: {
|
|
53
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
54
|
+
ref: 'BillerAddress',
|
|
75
55
|
default: null,
|
|
76
56
|
},
|
|
77
|
-
|
|
78
|
-
// ── Biller Address ─────────────────────────────────────────────────────────
|
|
79
57
|
billerAddress: {
|
|
80
58
|
name: {
|
|
81
59
|
type: String,
|
|
@@ -110,6 +88,49 @@ const powerInvoiceSchema = new mongoose.Schema(
|
|
|
110
88
|
},
|
|
111
89
|
},
|
|
112
90
|
|
|
91
|
+
// ── Payment Methods ────────────────────────────────────────────────────────
|
|
92
|
+
// paymentMethods stores the IDs configured in PowerMeterSettings so the
|
|
93
|
+
// invoice knows which accounts/paybills are available for payment.
|
|
94
|
+
// paymentMethod (singular) records how this invoice was actually settled.
|
|
95
|
+
paymentMethods: {
|
|
96
|
+
mobilePayment: {
|
|
97
|
+
status: {
|
|
98
|
+
type: Boolean,
|
|
99
|
+
default: false,
|
|
100
|
+
},
|
|
101
|
+
// References the FacilityPaymentDetails (M-Pesa paybill) configured in settings
|
|
102
|
+
paymentId: {
|
|
103
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
104
|
+
ref: 'FacilityPaymentDetails',
|
|
105
|
+
default: null,
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
bankPayment: {
|
|
109
|
+
status: {
|
|
110
|
+
type: Boolean,
|
|
111
|
+
default: false,
|
|
112
|
+
},
|
|
113
|
+
// References the BankDetails configured in settings
|
|
114
|
+
paymentId: {
|
|
115
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
116
|
+
ref: 'BankDetails',
|
|
117
|
+
default: null,
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
cashPayment: {
|
|
121
|
+
status: {
|
|
122
|
+
type: Boolean,
|
|
123
|
+
default: false,
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
// How this invoice was actually paid (set during reconciliation)
|
|
128
|
+
paymentMethod: {
|
|
129
|
+
type: String,
|
|
130
|
+
enum: ['mobile', 'bank', 'cash', 'mixed'],
|
|
131
|
+
default: null,
|
|
132
|
+
},
|
|
133
|
+
|
|
113
134
|
// ── Dates ──────────────────────────────────────────────────────────────────
|
|
114
135
|
dateIssued: {
|
|
115
136
|
type: Date,
|
|
@@ -167,89 +188,21 @@ const powerInvoiceSchema = new mongoose.Schema(
|
|
|
167
188
|
},
|
|
168
189
|
|
|
169
190
|
// ── Power Charges ──────────────────────────────────────────────────────────
|
|
170
|
-
// All monetary values are in the invoice currency (default KES).
|
|
171
191
|
powerCharges: {
|
|
172
|
-
tariff: {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
},
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
},
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
},
|
|
187
|
-
|
|
188
|
-
// Fuel Cost Charge (FCC) – passed through from EPRA-approved fuel levy
|
|
189
|
-
fuelCostCharge: {
|
|
190
|
-
type: Number,
|
|
191
|
-
default: 0,
|
|
192
|
-
},
|
|
193
|
-
|
|
194
|
-
// Foreign Exchange Fluctuation Adjustment
|
|
195
|
-
forexAdjustment: {
|
|
196
|
-
type: Number,
|
|
197
|
-
default: 0,
|
|
198
|
-
},
|
|
199
|
-
|
|
200
|
-
// Inflation Adjustment Charge
|
|
201
|
-
inflationAdjustment: {
|
|
202
|
-
type: Number,
|
|
203
|
-
default: 0,
|
|
204
|
-
},
|
|
205
|
-
|
|
206
|
-
// Water Resource Management Levy (WRML)
|
|
207
|
-
waterResourceManagementLevy: {
|
|
208
|
-
type: Number,
|
|
209
|
-
default: 0,
|
|
210
|
-
},
|
|
211
|
-
|
|
212
|
-
// Energy Regulatory Levy (ERL)
|
|
213
|
-
energyRegulatoryLevy: {
|
|
214
|
-
type: Number,
|
|
215
|
-
default: 0,
|
|
216
|
-
},
|
|
217
|
-
|
|
218
|
-
// Rural Electrification & Renewable Energy Levy (REL)
|
|
219
|
-
ruralElectrificationLevy: {
|
|
220
|
-
type: Number,
|
|
221
|
-
default: 0,
|
|
222
|
-
},
|
|
223
|
-
|
|
224
|
-
// Power Factor Surcharge (applied to commercial/industrial customers only)
|
|
225
|
-
powerFactorSurcharge: {
|
|
226
|
-
type: Number,
|
|
227
|
-
default: 0,
|
|
228
|
-
},
|
|
229
|
-
|
|
230
|
-
// Value Added Tax amount
|
|
231
|
-
valueAddedTax: {
|
|
232
|
-
type: Number,
|
|
233
|
-
default: 0,
|
|
234
|
-
},
|
|
235
|
-
|
|
236
|
-
// VAT rate applied (%) – captured at time of billing; typically 16
|
|
237
|
-
vatPercentage: {
|
|
238
|
-
type: Number,
|
|
239
|
-
default: 16,
|
|
240
|
-
},
|
|
241
|
-
|
|
242
|
-
// Rural Electrification Programme levy percentage (%) – typically 5
|
|
243
|
-
repPercentage: {
|
|
244
|
-
type: Number,
|
|
245
|
-
default: 5,
|
|
246
|
-
},
|
|
247
|
-
|
|
248
|
-
// Grand total = sum of all charges above
|
|
249
|
-
totalCharge: {
|
|
250
|
-
type: Number,
|
|
251
|
-
required: true,
|
|
252
|
-
},
|
|
192
|
+
tariff: { type: String },
|
|
193
|
+
yearMonth: { type: String },
|
|
194
|
+
energyCharge: { type: Number, required: true, default: 0 },
|
|
195
|
+
fuelCostCharge: { type: Number, default: 0 },
|
|
196
|
+
forexAdjustment: { type: Number, default: 0 },
|
|
197
|
+
inflationAdjustment: { type: Number, default: 0 },
|
|
198
|
+
waterResourceManagementLevy: { type: Number, default: 0 },
|
|
199
|
+
energyRegulatoryLevy: { type: Number, default: 0 },
|
|
200
|
+
ruralElectrificationLevy: { type: Number, default: 0 },
|
|
201
|
+
powerFactorSurcharge: { type: Number, default: 0 },
|
|
202
|
+
valueAddedTax: { type: Number, default: 0 },
|
|
203
|
+
vatPercentage: { type: Number, default: 16 },
|
|
204
|
+
repPercentage: { type: Number, default: 5 },
|
|
205
|
+
totalCharge: { type: Number, required: true },
|
|
253
206
|
},
|
|
254
207
|
|
|
255
208
|
// ── Payment & Reconciliation ───────────────────────────────────────────────
|
|
@@ -270,60 +223,28 @@ const powerInvoiceSchema = new mongoose.Schema(
|
|
|
270
223
|
default: 'Unpaid',
|
|
271
224
|
},
|
|
272
225
|
|
|
273
|
-
// ── View / Read Status
|
|
226
|
+
// ── View / Read Status ─────────────────────────────────────────────────────
|
|
274
227
|
viewStatus: {
|
|
275
|
-
isOpened: {
|
|
276
|
-
|
|
277
|
-
default: false,
|
|
278
|
-
},
|
|
279
|
-
openedAt: {
|
|
280
|
-
type: Date,
|
|
281
|
-
default: null,
|
|
282
|
-
},
|
|
228
|
+
isOpened: { type: Boolean, default: false },
|
|
229
|
+
openedAt: { type: Date, default: null },
|
|
283
230
|
},
|
|
284
231
|
|
|
285
|
-
// ── Currency
|
|
232
|
+
// ── Currency ───────────────────────────────────────────────────────────────
|
|
286
233
|
currency: {
|
|
287
|
-
id: {
|
|
288
|
-
|
|
289
|
-
},
|
|
290
|
-
|
|
291
|
-
type: String,
|
|
292
|
-
default: 'Kenyan Shilling',
|
|
293
|
-
},
|
|
294
|
-
code: {
|
|
295
|
-
type: String,
|
|
296
|
-
default: 'KES',
|
|
297
|
-
},
|
|
298
|
-
symbol: {
|
|
299
|
-
type: String,
|
|
300
|
-
default: 'KSh',
|
|
301
|
-
},
|
|
234
|
+
id: { type: mongoose.Schema.Types.ObjectId },
|
|
235
|
+
name: { type: String, default: 'Kenyan Shilling' },
|
|
236
|
+
code: { type: String, default: 'KES' },
|
|
237
|
+
symbol: { type: String, default: 'KSh' },
|
|
302
238
|
},
|
|
303
239
|
|
|
304
|
-
// ── Notification Tracking
|
|
240
|
+
// ── Notification Tracking ──────────────────────────────────────────────────
|
|
305
241
|
notificationsSent: {
|
|
306
242
|
type: {
|
|
307
|
-
sms: {
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
},
|
|
311
|
-
|
|
312
|
-
type: Boolean,
|
|
313
|
-
default: false,
|
|
314
|
-
},
|
|
315
|
-
sentAt: {
|
|
316
|
-
type: Date,
|
|
317
|
-
default: null,
|
|
318
|
-
},
|
|
319
|
-
lastAttempt: {
|
|
320
|
-
type: Date,
|
|
321
|
-
default: null,
|
|
322
|
-
},
|
|
323
|
-
attempts: {
|
|
324
|
-
type: Number,
|
|
325
|
-
default: 0,
|
|
326
|
-
},
|
|
243
|
+
sms: { type: Boolean, default: false },
|
|
244
|
+
email: { type: Boolean, default: false },
|
|
245
|
+
sentAt: { type: Date, default: null },
|
|
246
|
+
lastAttempt: { type: Date, default: null },
|
|
247
|
+
attempts: { type: Number, default: 0 },
|
|
327
248
|
smsDetails: {
|
|
328
249
|
type: {
|
|
329
250
|
success: Boolean,
|
|
@@ -354,41 +275,18 @@ const powerInvoiceSchema = new mongoose.Schema(
|
|
|
354
275
|
},
|
|
355
276
|
},
|
|
356
277
|
|
|
357
|
-
//
|
|
278
|
+
// ── Reconciliation History ─────────────────────────────────────────────────
|
|
358
279
|
reconciliationHistory: [
|
|
359
280
|
{
|
|
360
|
-
date: {
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
},
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
},
|
|
368
|
-
type:
|
|
369
|
-
type: String,
|
|
370
|
-
default: 'Manual',
|
|
371
|
-
// e.g. 'Manual', 'M-Pesa', 'Bank Transfer', 'Cash'
|
|
372
|
-
},
|
|
373
|
-
paymentReference: {
|
|
374
|
-
type: String,
|
|
375
|
-
},
|
|
376
|
-
paymentCompletion: {
|
|
377
|
-
type: String,
|
|
378
|
-
default: 'Completed',
|
|
379
|
-
},
|
|
380
|
-
sourceInvoice: {
|
|
381
|
-
type: String,
|
|
382
|
-
},
|
|
383
|
-
destinationInvoice: {
|
|
384
|
-
type: String,
|
|
385
|
-
},
|
|
386
|
-
notes: {
|
|
387
|
-
type: String,
|
|
388
|
-
},
|
|
389
|
-
remainingBalance: {
|
|
390
|
-
type: Number,
|
|
391
|
-
},
|
|
281
|
+
date: { type: Date, default: Date.now },
|
|
282
|
+
amount: { type: Number, required: true },
|
|
283
|
+
type: { type: String, default: 'Manual' },
|
|
284
|
+
paymentReference: { type: String },
|
|
285
|
+
paymentCompletion: { type: String, default: 'Completed' },
|
|
286
|
+
sourceInvoice: { type: String },
|
|
287
|
+
destinationInvoice: { type: String },
|
|
288
|
+
notes: { type: String },
|
|
289
|
+
remainingBalance: { type: Number },
|
|
392
290
|
},
|
|
393
291
|
],
|
|
394
292
|
},
|
|
@@ -397,7 +295,7 @@ const powerInvoiceSchema = new mongoose.Schema(
|
|
|
397
295
|
}
|
|
398
296
|
);
|
|
399
297
|
|
|
400
|
-
//
|
|
298
|
+
// ── Pre-save middleware ────────────────────────────────────────────────────────
|
|
401
299
|
|
|
402
300
|
powerInvoiceSchema.pre('save', function (next) {
|
|
403
301
|
// 1. Auto-mark overdue
|
|
@@ -409,15 +307,11 @@ powerInvoiceSchema.pre('save', function (next) {
|
|
|
409
307
|
}
|
|
410
308
|
|
|
411
309
|
// 2. Sync powerCharges snapshot fields with top-level fields
|
|
412
|
-
if (this.isNew || this.isModified('tariff')) {
|
|
413
|
-
|
|
414
|
-
this.powerCharges.tariff = this.tariff;
|
|
415
|
-
}
|
|
310
|
+
if ((this.isNew || this.isModified('tariff')) && this.powerCharges && !this.powerCharges.tariff) {
|
|
311
|
+
this.powerCharges.tariff = this.tariff;
|
|
416
312
|
}
|
|
417
|
-
if (this.isNew || this.isModified('yearMonth')) {
|
|
418
|
-
|
|
419
|
-
this.powerCharges.yearMonth = this.yearMonth;
|
|
420
|
-
}
|
|
313
|
+
if ((this.isNew || this.isModified('yearMonth')) && this.powerCharges && !this.powerCharges.yearMonth) {
|
|
314
|
+
this.powerCharges.yearMonth = this.yearMonth;
|
|
421
315
|
}
|
|
422
316
|
|
|
423
317
|
// 3. Update payment method flags from reconciliation history
|
|
@@ -434,12 +328,7 @@ powerInvoiceSchema.pre('save', function (next) {
|
|
|
434
328
|
};
|
|
435
329
|
}
|
|
436
330
|
|
|
437
|
-
if (
|
|
438
|
-
paymentType &&
|
|
439
|
-
(paymentType.includes('mobile') ||
|
|
440
|
-
paymentType.includes('mpesa') ||
|
|
441
|
-
paymentType.includes('m-pesa'))
|
|
442
|
-
) {
|
|
331
|
+
if (paymentType && (paymentType.includes('mobile') || paymentType.includes('mpesa') || paymentType.includes('m-pesa'))) {
|
|
443
332
|
this.paymentMethods.mobilePayment.status = true;
|
|
444
333
|
this.paymentMethod = 'mobile';
|
|
445
334
|
} else if (paymentType && paymentType.includes('bank')) {
|
|
@@ -449,7 +338,6 @@ powerInvoiceSchema.pre('save', function (next) {
|
|
|
449
338
|
this.paymentMethods.cashPayment.status = true;
|
|
450
339
|
this.paymentMethod = 'cash';
|
|
451
340
|
} else {
|
|
452
|
-
// Default: treat unspecified / manual payments as cash
|
|
453
341
|
this.paymentMethods.cashPayment.status = true;
|
|
454
342
|
this.paymentMethod = 'cash';
|
|
455
343
|
}
|
|
@@ -459,12 +347,13 @@ powerInvoiceSchema.pre('save', function (next) {
|
|
|
459
347
|
next();
|
|
460
348
|
});
|
|
461
349
|
|
|
462
|
-
// Indexes
|
|
350
|
+
// ── Indexes ────────────────────────────────────────────────────────────────────
|
|
463
351
|
powerInvoiceSchema.index({ facilityId: 1, yearMonth: -1 });
|
|
464
352
|
powerInvoiceSchema.index({ customerId: 1 });
|
|
465
353
|
powerInvoiceSchema.index({ status: 1 });
|
|
466
354
|
powerInvoiceSchema.index({ meterNumber: 1 });
|
|
467
355
|
powerInvoiceSchema.index({ accountNumber: 1 });
|
|
356
|
+
powerInvoiceSchema.index({ unitId: 1 });
|
|
468
357
|
|
|
469
358
|
const PowerInvoice = mongoose.model('PowerInvoice', powerInvoiceSchema);
|
|
470
359
|
|