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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "payservedb",
3
- "version": "3.4.9",
3
+ "version": "3.5.0",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -60,4 +60,4 @@ const levySchema = new mongoose.Schema(
60
60
  // Compile the model from the schema
61
61
  const Levy = mongoose.model("Levy", levySchema);
62
62
 
63
- module.exports = Levy;
63
+ module.exports = Levy;
@@ -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
- frequency: {
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
- day: {
30
- type: String,
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
- module: {
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 = Reminder;
174
+ module.exports = Reminder;