payservedb 3.4.9 → 3.5.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.
- package/package.json +1 -1
- package/src/models/levy.js +1 -1
- package/src/models/reminder.js +131 -19
package/package.json
CHANGED
package/src/models/levy.js
CHANGED
package/src/models/reminder.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const mongoose = require("mongoose");
|
|
2
|
+
const moment = require("moment-timezone");
|
|
2
3
|
|
|
3
4
|
// Define the schema for Reminder
|
|
4
5
|
const reminderSchema = new mongoose.Schema(
|
|
@@ -10,28 +11,57 @@ const reminderSchema = new mongoose.Schema(
|
|
|
10
11
|
},
|
|
11
12
|
type: {
|
|
12
13
|
type: String,
|
|
13
|
-
enum: ['onetime', 'recurring'],
|
|
14
14
|
required: true,
|
|
15
|
+
enum: ['standard', 'custom'],
|
|
16
|
+
default: 'standard'
|
|
15
17
|
},
|
|
16
|
-
|
|
17
|
-
type: String,
|
|
18
|
-
enum: ['daily', 'weekly', 'biweekly', 'monthly'],
|
|
19
|
-
required: function() { return this.type === 'recurring'; },
|
|
20
|
-
},
|
|
21
|
-
scheduledDate: {
|
|
22
|
-
type: Date,
|
|
23
|
-
required: function() { return this.type === 'onetime'; },
|
|
24
|
-
},
|
|
25
|
-
time: {
|
|
18
|
+
module: {
|
|
26
19
|
type: String,
|
|
27
20
|
required: true,
|
|
21
|
+
enum: ['levy', 'lease'],
|
|
22
|
+
default: 'levy'
|
|
28
23
|
},
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
remindOn: {
|
|
25
|
+
invoiceDate: {
|
|
26
|
+
type: Boolean,
|
|
27
|
+
default: true,
|
|
28
|
+
},
|
|
29
|
+
dueDate: {
|
|
30
|
+
type: Boolean,
|
|
31
|
+
default: false,
|
|
32
|
+
},
|
|
33
|
+
afterOverdue: {
|
|
34
|
+
enabled: {
|
|
35
|
+
type: Boolean,
|
|
36
|
+
default: false,
|
|
37
|
+
},
|
|
38
|
+
days: {
|
|
39
|
+
type: [Number],
|
|
40
|
+
validate: {
|
|
41
|
+
validator: function (value) {
|
|
42
|
+
// If afterOverdue is not enabled, any value is valid
|
|
43
|
+
if (!this.remindOn.afterOverdue.enabled) return true;
|
|
44
|
+
|
|
45
|
+
// When enabled, days must be 1, 3, or 7
|
|
46
|
+
return Array.isArray(value) &&
|
|
47
|
+
value.length > 0 &&
|
|
48
|
+
value.every(day => [1, 3, 7].includes(day));
|
|
49
|
+
},
|
|
50
|
+
message: 'When overdue reminders are enabled, days can only be 1, 3, or 7'
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
31
54
|
},
|
|
32
|
-
|
|
55
|
+
time: {
|
|
33
56
|
type: String,
|
|
34
57
|
required: true,
|
|
58
|
+
default: "09:00",
|
|
59
|
+
validate: {
|
|
60
|
+
validator: function (value) {
|
|
61
|
+
return /^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$/.test(value);
|
|
62
|
+
},
|
|
63
|
+
message: 'Time must be in HH:mm format'
|
|
64
|
+
}
|
|
35
65
|
},
|
|
36
66
|
isActive: {
|
|
37
67
|
type: Boolean,
|
|
@@ -41,22 +71,104 @@ const reminderSchema = new mongoose.Schema(
|
|
|
41
71
|
processed: {
|
|
42
72
|
type: Boolean,
|
|
43
73
|
required: true,
|
|
44
|
-
default: false,
|
|
74
|
+
default: false,
|
|
75
|
+
},
|
|
76
|
+
lastProcessed: {
|
|
77
|
+
type: Date,
|
|
78
|
+
},
|
|
79
|
+
notificationTypes: {
|
|
80
|
+
type: [String],
|
|
81
|
+
required: true,
|
|
82
|
+
validate: {
|
|
83
|
+
validator: function (value) {
|
|
84
|
+
return value.length > 0 &&
|
|
85
|
+
value.every(type => ['SMS', 'EMAIL'].includes(type.toUpperCase()));
|
|
86
|
+
},
|
|
87
|
+
message: 'At least one valid notification type is required'
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
message: {
|
|
91
|
+
type: String,
|
|
92
|
+
maxLength: [500, 'Message cannot exceed 500 characters']
|
|
93
|
+
},
|
|
94
|
+
levyId: {
|
|
95
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
96
|
+
ref: "Levy",
|
|
97
|
+
required: true,
|
|
45
98
|
},
|
|
46
|
-
notificationTypes: [String],
|
|
47
|
-
message: String,
|
|
48
|
-
levyId: String,
|
|
49
99
|
facilityId: {
|
|
50
100
|
type: mongoose.Schema.Types.ObjectId,
|
|
51
101
|
ref: "Facility",
|
|
52
102
|
required: true,
|
|
103
|
+
index: true
|
|
53
104
|
},
|
|
105
|
+
timezone: {
|
|
106
|
+
type: String,
|
|
107
|
+
required: true,
|
|
108
|
+
default: 'UTC',
|
|
109
|
+
validate: {
|
|
110
|
+
validator: function (value) {
|
|
111
|
+
return moment.tz.names().includes(value);
|
|
112
|
+
},
|
|
113
|
+
message: 'Invalid timezone'
|
|
114
|
+
}
|
|
115
|
+
}
|
|
54
116
|
},
|
|
55
117
|
{
|
|
56
118
|
timestamps: true,
|
|
119
|
+
// Add indexes for frequently queried fields
|
|
120
|
+
indexes: [
|
|
121
|
+
{ facilityId: 1, isActive: 1 },
|
|
122
|
+
{ facilityId: 1, isActive: 1, 'remindOn.invoiceDate': 1 },
|
|
123
|
+
{ facilityId: 1, isActive: 1, 'remindOn.dueDate': 1 },
|
|
124
|
+
{ facilityId: 1, isActive: 1, 'remindOn.afterOverdue.enabled': 1 },
|
|
125
|
+
{ levyId: 1, isActive: 1 },
|
|
126
|
+
{ facilityId: 1, lastProcessed: 1 }
|
|
127
|
+
]
|
|
57
128
|
}
|
|
58
129
|
);
|
|
59
130
|
|
|
131
|
+
// Add method to check if reminder should be processed
|
|
132
|
+
reminderSchema.methods.shouldProcess = function (currentTime, invoice) {
|
|
133
|
+
if (!this.isActive || !invoice) return false;
|
|
134
|
+
|
|
135
|
+
const now = moment(currentTime);
|
|
136
|
+
const invoiceDate = moment(invoice.createdAt);
|
|
137
|
+
const dueDate = moment(invoice.dueDate);
|
|
138
|
+
const today = moment().startOf('day');
|
|
139
|
+
|
|
140
|
+
// Check if we should process based on time
|
|
141
|
+
if (!this.isTimeToProcess(now)) return false;
|
|
142
|
+
|
|
143
|
+
// Check invoice date reminder
|
|
144
|
+
if (this.remindOn.invoiceDate && invoiceDate.isSame(today, 'day')) {
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Check due date reminder
|
|
149
|
+
if (this.remindOn.dueDate && dueDate.isSame(today, 'day')) {
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Check overdue reminders
|
|
154
|
+
if (this.remindOn.afterOverdue.enabled && dueDate.isBefore(today, 'day')) {
|
|
155
|
+
const daysOverdue = today.diff(dueDate, 'days');
|
|
156
|
+
return this.remindOn.afterOverdue.days.includes(daysOverdue);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return false;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
// Helper method to check if current time matches reminder time
|
|
163
|
+
reminderSchema.methods.isTimeToProcess = function (currentTime) {
|
|
164
|
+
const reminderMoment = moment(this.time, 'HH:mm');
|
|
165
|
+
const currentMoment = moment(currentTime).format('HH:mm');
|
|
166
|
+
const diffMinutes = moment(currentMoment, 'HH:mm').diff(reminderMoment, 'minutes');
|
|
167
|
+
|
|
168
|
+
// Allow processing within 5-minute window
|
|
169
|
+
return Math.abs(diffMinutes) <= 5;
|
|
170
|
+
};
|
|
171
|
+
|
|
60
172
|
const Reminder = mongoose.model("Reminder", reminderSchema);
|
|
61
173
|
|
|
62
|
-
module.exports
|
|
174
|
+
module.exports = Reminder;
|