payservedb 8.4.6 → 8.4.7
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/.env +2 -2
- package/ZOHO_INTEGRATION_SCHEMA.md +644 -644
- package/index.js +312 -312
- package/package.json +17 -17
- package/src/models/account.js +52 -52
- package/src/models/agent_departments.js +59 -59
- package/src/models/agent_notifications.js +53 -53
- package/src/models/agent_performance.js +127 -127
- package/src/models/agent_roles.js +77 -77
- package/src/models/agents.js +154 -154
- package/src/models/apilog.js +18 -18
- package/src/models/approvalsWorkflows.js +49 -49
- package/src/models/archivedapilog.js +18 -18
- package/src/models/asset.js +92 -92
- package/src/models/assetsAssignment.js +64 -64
- package/src/models/auditTrail.js +346 -346
- package/src/models/bankdetails.js +47 -47
- package/src/models/billerAddress.js +124 -124
- package/src/models/booking_invoice.js +165 -165
- package/src/models/bookinganalytics.js +63 -63
- package/src/models/bookingconfig.js +45 -45
- package/src/models/bookingproperty.js +173 -173
- package/src/models/bookingreservation.js +209 -209
- package/src/models/bookingrevenuerecord.js +84 -84
- package/src/models/budget.js +95 -95
- package/src/models/budgetCategory.js +19 -19
- package/src/models/campaigns.js +108 -108
- package/src/models/cashpayment.js +264 -264
- package/src/models/combinedUnits.js +62 -62
- package/src/models/common_area_electricity.js +38 -38
- package/src/models/common_area_generator.js +41 -41
- package/src/models/common_area_utility_alert.js +37 -37
- package/src/models/common_area_water.js +39 -39
- package/src/models/communication_status.js +33 -33
- package/src/models/communication_user_opt.js +32 -32
- package/src/models/community_guidelines.js +24 -24
- package/src/models/company.js +53 -53
- package/src/models/coreBaseSettings.js +16 -16
- package/src/models/coreInvoiceSettings.js +100 -100
- package/src/models/counter_schema.js +21 -21
- package/src/models/country_tax.js +42 -42
- package/src/models/currency_settings.js +39 -39
- package/src/models/customer.js +210 -210
- package/src/models/customer_satisfaction_survey.js +278 -278
- package/src/models/customer_surveys.js +139 -139
- package/src/models/customer_tickets.js +239 -239
- package/src/models/dailyChecklist.js +312 -312
- package/src/models/default_payment_details.js +17 -17
- package/src/models/deliveryTimeMarks.js +18 -18
- package/src/models/document_type.js +19 -19
- package/src/models/dutyRosterChecklist.js +250 -250
- package/src/models/dutyroster.js +136 -136
- package/src/models/email.js +37 -37
- package/src/models/email_sms_queue.js +61 -61
- package/src/models/entry_exit.js +53 -53
- package/src/models/expense.js +99 -99
- package/src/models/expense_category.js +45 -45
- package/src/models/facility.js +71 -71
- package/src/models/facilityBillingPrices.js +29 -29
- package/src/models/facilityInvoice.js +240 -240
- package/src/models/facilityInvoicePayment.js +52 -52
- package/src/models/facilityInvoiceRecipient.js +32 -32
- package/src/models/facilityWalletTransactionsMetadata.js +236 -236
- package/src/models/facility_departements.js +20 -20
- package/src/models/facility_payment_details.js +20 -20
- package/src/models/facilityasset.js +25 -25
- package/src/models/faq.js +14 -14
- package/src/models/gl_account_double_entries.js +25 -25
- package/src/models/gl_accounts.js +56 -56
- package/src/models/gl_entries.js +49 -49
- package/src/models/goodsReceivedNotes.js +115 -115
- package/src/models/guard.js +47 -47
- package/src/models/handover.js +247 -247
- package/src/models/inspection_category.js +38 -38
- package/src/models/invoice.js +480 -480
- package/src/models/invoicing_schedule.js +41 -36
- package/src/models/item_inspection.js +96 -96
- package/src/models/knowledge_base.js +109 -109
- package/src/models/knowledge_base_rating.js +44 -44
- package/src/models/leaseagreement.js +236 -236
- package/src/models/leasetemplate.js +17 -17
- package/src/models/levy.js +223 -223
- package/src/models/levy_invoice_settings.js +26 -26
- package/src/models/levycontract.js +177 -177
- package/src/models/levytype.js +23 -23
- package/src/models/maintenance_service_vendor.js +38 -38
- package/src/models/maintenance_services.js +17 -17
- package/src/models/maintenancerequisition.js +31 -31
- package/src/models/master_workplan.js +32 -32
- package/src/models/master_workplan_child.js +34 -34
- package/src/models/message.js +38 -38
- package/src/models/module.js +21 -21
- package/src/models/notification.js +44 -44
- package/src/models/paymentTermsMarks.js +19 -19
- package/src/models/penalty.js +76 -76
- package/src/models/pendingCredentials.js +32 -32
- package/src/models/powerMeterCommunicationProtocol.js +17 -17
- package/src/models/powerMeterCustomerAccount.js +78 -78
- package/src/models/powerMeterCustomerBand.js +14 -14
- package/src/models/powerMeterDailyReading.js +30 -30
- package/src/models/powerMeterGateways.js +40 -40
- package/src/models/powerMeterMonthlyReading.js +34 -34
- package/src/models/powerMeterPowerCharges.js +85 -85
- package/src/models/powerMeterSettings.js +159 -159
- package/src/models/powerMeterSingleDayReading.js +32 -32
- package/src/models/powerMeters.js +116 -116
- package/src/models/powerMetersManufacturer.js +14 -14
- package/src/models/power_meter_account.js +81 -81
- package/src/models/power_meter_command_logs.js +30 -30
- package/src/models/power_meter_command_queue.js +33 -33
- package/src/models/power_meter_negative_balance.js +44 -44
- package/src/models/power_prepaid_credits.js +47 -47
- package/src/models/power_prepaid_debits.js +53 -53
- package/src/models/power_prepaid_orders.js +78 -78
- package/src/models/power_sms_notification.js +26 -26
- package/src/models/privacy_policy.js +19 -19
- package/src/models/propertyManagerContract.js +556 -556
- package/src/models/propertyManagerRevenue.js +195 -195
- package/src/models/purchaseOrderInvoice.js +74 -74
- package/src/models/purchase_order.js +213 -213
- package/src/models/purchase_request.js +110 -110
- package/src/models/refresh_token.js +23 -23
- package/src/models/reminder.js +197 -197
- package/src/models/report.js +13 -13
- package/src/models/resident.js +121 -121
- package/src/models/rfq_details.js +131 -131
- package/src/models/rfq_response.js +153 -153
- package/src/models/service_charge_invoice_upload.js +42 -42
- package/src/models/service_charge_payments.js +27 -27
- package/src/models/servicerequest.js +55 -55
- package/src/models/settings.js +62 -62
- package/src/models/short_urls.js +21 -21
- package/src/models/smart_meter_daily_consumption.js +44 -44
- package/src/models/sms_africastalking.js +20 -20
- package/src/models/sms_balance_notification.js +26 -26
- package/src/models/sms_meliora.js +20 -20
- package/src/models/staff.js +36 -36
- package/src/models/stocksandspare.js +161 -161
- package/src/models/suppliers.js +74 -74
- package/src/models/terms_and_conditions.js +19 -19
- package/src/models/tickets.js +186 -186
- package/src/models/tickets_category.js +72 -72
- package/src/models/unitManagementTemplate.js +44 -44
- package/src/models/unitasset.js +25 -25
- package/src/models/units.js +118 -118
- package/src/models/user.js +186 -186
- package/src/models/valueaddedservices.js +79 -79
- package/src/models/vas_invoices_upload.js +50 -50
- package/src/models/vas_payments.js +24 -24
- package/src/models/vasinvoice.js +192 -192
- package/src/models/vasvendor.js +57 -57
- package/src/models/visitLog.js +95 -95
- package/src/models/visitor.js +67 -67
- package/src/models/waitlist.js +45 -45
- package/src/models/wallet.js +44 -44
- package/src/models/wallet_transactions.js +50 -50
- package/src/models/water_invoice.js +351 -351
- package/src/models/water_meter_Command_Queue.js +33 -33
- package/src/models/water_meter_account.js +87 -87
- package/src/models/water_meter_billing.js +58 -58
- package/src/models/water_meter_communication.js +17 -17
- package/src/models/water_meter_communication_logs.js +39 -39
- package/src/models/water_meter_concentrator.js +70 -70
- package/src/models/water_meter_daily_history.js +32 -32
- package/src/models/water_meter_high_risk.js +36 -36
- package/src/models/water_meter_iot_cards.js +34 -34
- package/src/models/water_meter_manufacturer.js +35 -35
- package/src/models/water_meter_monthly_history.js +36 -36
- package/src/models/water_meter_negative_amounts.js +44 -44
- package/src/models/water_meter_settings.js +287 -287
- package/src/models/water_meter_single_day_history.js +34 -34
- package/src/models/water_meter_size.js +15 -15
- package/src/models/water_meters.js +133 -133
- package/src/models/water_meters_delivery.js +76 -76
- package/src/models/water_prepaid_credit.js +47 -47
- package/src/models/water_prepaid_debit.js +50 -50
- package/src/models/workorder.js +49 -49
- package/src/models/zohoAccount.js +453 -453
- package/src/models/zohoIntegration.js +262 -262
- package/src/models/zohoItem.js +504 -504
|
@@ -1,262 +1,262 @@
|
|
|
1
|
-
const mongoose = require("mongoose");
|
|
2
|
-
|
|
3
|
-
const ZohoIntegrationSchema = new mongoose.Schema(
|
|
4
|
-
{
|
|
5
|
-
facilityId: {
|
|
6
|
-
type: mongoose.Schema.Types.ObjectId,
|
|
7
|
-
ref: "Facility",
|
|
8
|
-
required: [true, "Facility ID is required"],
|
|
9
|
-
unique: true,
|
|
10
|
-
index: true,
|
|
11
|
-
},
|
|
12
|
-
|
|
13
|
-
// Zoho API Credentials
|
|
14
|
-
// NOTE: clientSecret, refreshToken, and accessToken should be encrypted before storage
|
|
15
|
-
clientId: {
|
|
16
|
-
type: String,
|
|
17
|
-
required: [true, "Zoho Client ID is required"],
|
|
18
|
-
trim: true,
|
|
19
|
-
},
|
|
20
|
-
clientSecret: {
|
|
21
|
-
type: String,
|
|
22
|
-
required: [true, "Zoho Client Secret is required"],
|
|
23
|
-
trim: true,
|
|
24
|
-
// This field should be encrypted in the application layer
|
|
25
|
-
},
|
|
26
|
-
refreshToken: {
|
|
27
|
-
type: String,
|
|
28
|
-
required: [true, "Zoho Refresh Token is required"],
|
|
29
|
-
trim: true,
|
|
30
|
-
// This field should be encrypted in the application layer
|
|
31
|
-
},
|
|
32
|
-
accessToken: {
|
|
33
|
-
type: String,
|
|
34
|
-
trim: true,
|
|
35
|
-
default: null,
|
|
36
|
-
// This field should be encrypted in the application layer
|
|
37
|
-
// Auto-refreshed by the system
|
|
38
|
-
},
|
|
39
|
-
organizationId: {
|
|
40
|
-
type: String,
|
|
41
|
-
required: [true, "Zoho Organization ID is required"],
|
|
42
|
-
trim: true,
|
|
43
|
-
},
|
|
44
|
-
|
|
45
|
-
// Integration Status
|
|
46
|
-
isActive: {
|
|
47
|
-
type: Boolean,
|
|
48
|
-
default: true,
|
|
49
|
-
},
|
|
50
|
-
connectionStatus: {
|
|
51
|
-
type: String,
|
|
52
|
-
enum: ["connected", "disconnected", "error", "pending"],
|
|
53
|
-
default: "pending",
|
|
54
|
-
},
|
|
55
|
-
connectionError: {
|
|
56
|
-
type: String,
|
|
57
|
-
trim: true,
|
|
58
|
-
default: null,
|
|
59
|
-
},
|
|
60
|
-
lastConnectionTest: {
|
|
61
|
-
type: Date,
|
|
62
|
-
default: null,
|
|
63
|
-
},
|
|
64
|
-
|
|
65
|
-
// Token Management
|
|
66
|
-
tokenExpiresAt: {
|
|
67
|
-
type: Date,
|
|
68
|
-
default: null,
|
|
69
|
-
},
|
|
70
|
-
lastTokenRefreshAt: {
|
|
71
|
-
type: Date,
|
|
72
|
-
default: null,
|
|
73
|
-
},
|
|
74
|
-
tokenRefreshCount: {
|
|
75
|
-
type: Number,
|
|
76
|
-
default: 0,
|
|
77
|
-
min: [0, "Token refresh count cannot be negative"],
|
|
78
|
-
},
|
|
79
|
-
|
|
80
|
-
// Sync Statistics
|
|
81
|
-
lastSyncedAt: {
|
|
82
|
-
type: Date,
|
|
83
|
-
default: null,
|
|
84
|
-
},
|
|
85
|
-
totalInvoicesSynced: {
|
|
86
|
-
type: Number,
|
|
87
|
-
default: 0,
|
|
88
|
-
min: [0, "Total invoices synced cannot be negative"],
|
|
89
|
-
},
|
|
90
|
-
totalPaymentsSynced: {
|
|
91
|
-
type: Number,
|
|
92
|
-
default: 0,
|
|
93
|
-
min: [0, "Total payments synced cannot be negative"],
|
|
94
|
-
},
|
|
95
|
-
totalCustomersSynced: {
|
|
96
|
-
type: Number,
|
|
97
|
-
default: 0,
|
|
98
|
-
min: [0, "Total customers synced cannot be negative"],
|
|
99
|
-
},
|
|
100
|
-
successfulSyncs: {
|
|
101
|
-
type: Number,
|
|
102
|
-
default: 0,
|
|
103
|
-
min: [0, "Successful syncs cannot be negative"],
|
|
104
|
-
},
|
|
105
|
-
failedSyncs: {
|
|
106
|
-
type: Number,
|
|
107
|
-
default: 0,
|
|
108
|
-
min: [0, "Failed syncs cannot be negative"],
|
|
109
|
-
},
|
|
110
|
-
|
|
111
|
-
// Sync Configuration
|
|
112
|
-
autoSyncEnabled: {
|
|
113
|
-
type: Boolean,
|
|
114
|
-
default: true,
|
|
115
|
-
},
|
|
116
|
-
syncFrequency: {
|
|
117
|
-
type: String,
|
|
118
|
-
enum: ["realtime", "hourly", "daily", "manual"],
|
|
119
|
-
default: "realtime",
|
|
120
|
-
},
|
|
121
|
-
lastSyncError: {
|
|
122
|
-
type: String,
|
|
123
|
-
trim: true,
|
|
124
|
-
default: null,
|
|
125
|
-
},
|
|
126
|
-
|
|
127
|
-
// Invoice Sync Settings
|
|
128
|
-
syncInvoicesOnCreation: {
|
|
129
|
-
type: Boolean,
|
|
130
|
-
default: true,
|
|
131
|
-
},
|
|
132
|
-
syncPaymentsOnReceipt: {
|
|
133
|
-
type: Boolean,
|
|
134
|
-
default: true,
|
|
135
|
-
},
|
|
136
|
-
markInvoicesAsSent: {
|
|
137
|
-
type: Boolean,
|
|
138
|
-
default: true,
|
|
139
|
-
},
|
|
140
|
-
|
|
141
|
-
// API Configuration
|
|
142
|
-
apiBaseUrl: {
|
|
143
|
-
type: String,
|
|
144
|
-
trim: true,
|
|
145
|
-
default: "https://www.zohoapis.com/books/v3",
|
|
146
|
-
},
|
|
147
|
-
requestTimeout: {
|
|
148
|
-
type: Number,
|
|
149
|
-
default: 30000,
|
|
150
|
-
min: [1000, "Request timeout must be at least 1000ms"],
|
|
151
|
-
},
|
|
152
|
-
maxRetries: {
|
|
153
|
-
type: Number,
|
|
154
|
-
default: 3,
|
|
155
|
-
min: [0, "Max retries cannot be negative"],
|
|
156
|
-
max: [10, "Max retries cannot exceed 10"],
|
|
157
|
-
},
|
|
158
|
-
|
|
159
|
-
// Audit Fields
|
|
160
|
-
createdBy: {
|
|
161
|
-
type: mongoose.Schema.Types.ObjectId,
|
|
162
|
-
ref: "User",
|
|
163
|
-
default: null,
|
|
164
|
-
},
|
|
165
|
-
updatedBy: {
|
|
166
|
-
type: mongoose.Schema.Types.ObjectId,
|
|
167
|
-
ref: "User",
|
|
168
|
-
default: null,
|
|
169
|
-
},
|
|
170
|
-
lastModifiedBy: {
|
|
171
|
-
type: mongoose.Schema.Types.ObjectId,
|
|
172
|
-
ref: "User",
|
|
173
|
-
default: null,
|
|
174
|
-
},
|
|
175
|
-
|
|
176
|
-
// Notes and metadata
|
|
177
|
-
notes: {
|
|
178
|
-
type: String,
|
|
179
|
-
trim: true,
|
|
180
|
-
default: null,
|
|
181
|
-
},
|
|
182
|
-
metadata: {
|
|
183
|
-
type: mongoose.Schema.Types.Mixed,
|
|
184
|
-
default: {},
|
|
185
|
-
},
|
|
186
|
-
},
|
|
187
|
-
{
|
|
188
|
-
timestamps: true,
|
|
189
|
-
}
|
|
190
|
-
);
|
|
191
|
-
|
|
192
|
-
// Indexes for better query performance
|
|
193
|
-
ZohoIntegrationSchema.index({ facilityId: 1, isActive: 1 });
|
|
194
|
-
ZohoIntegrationSchema.index({ connectionStatus: 1 });
|
|
195
|
-
ZohoIntegrationSchema.index({ lastSyncedAt: -1 });
|
|
196
|
-
|
|
197
|
-
// Virtual for success rate
|
|
198
|
-
ZohoIntegrationSchema.virtual("syncSuccessRate").get(function () {
|
|
199
|
-
const total = this.successfulSyncs + this.failedSyncs;
|
|
200
|
-
if (total === 0) return 0;
|
|
201
|
-
return ((this.successfulSyncs / total) * 100).toFixed(2);
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
// Method to mask sensitive fields for API responses
|
|
205
|
-
ZohoIntegrationSchema.methods.toSafeObject = function () {
|
|
206
|
-
const obj = this.toObject();
|
|
207
|
-
|
|
208
|
-
// Mask sensitive fields
|
|
209
|
-
if (obj.clientSecret) {
|
|
210
|
-
obj.clientSecret = "••••••••" + obj.clientSecret.slice(-4);
|
|
211
|
-
}
|
|
212
|
-
if (obj.refreshToken) {
|
|
213
|
-
obj.refreshToken = "••••••••" + obj.refreshToken.slice(-4);
|
|
214
|
-
}
|
|
215
|
-
if (obj.accessToken) {
|
|
216
|
-
obj.accessToken = "••••••••" + (obj.accessToken ? obj.accessToken.slice(-4) : "");
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
return obj;
|
|
220
|
-
};
|
|
221
|
-
|
|
222
|
-
// Method to check if token needs refresh (5 minutes buffer)
|
|
223
|
-
ZohoIntegrationSchema.methods.needsTokenRefresh = function () {
|
|
224
|
-
if (!this.tokenExpiresAt) return true;
|
|
225
|
-
const bufferTime = 5 * 60 * 1000; // 5 minutes in milliseconds
|
|
226
|
-
return new Date().getTime() >= (this.tokenExpiresAt.getTime() - bufferTime);
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
// Method to increment sync counters
|
|
230
|
-
ZohoIntegrationSchema.methods.incrementSyncCounters = function (type, success = true) {
|
|
231
|
-
this.lastSyncedAt = new Date();
|
|
232
|
-
|
|
233
|
-
if (type === "invoice") {
|
|
234
|
-
this.totalInvoicesSynced += 1;
|
|
235
|
-
} else if (type === "payment") {
|
|
236
|
-
this.totalPaymentsSynced += 1;
|
|
237
|
-
} else if (type === "customer") {
|
|
238
|
-
this.totalCustomersSynced += 1;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
if (success) {
|
|
242
|
-
this.successfulSyncs += 1;
|
|
243
|
-
this.lastSyncError = null;
|
|
244
|
-
} else {
|
|
245
|
-
this.failedSyncs += 1;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
return this.save();
|
|
249
|
-
};
|
|
250
|
-
|
|
251
|
-
// Pre-save hook to update timestamps
|
|
252
|
-
ZohoIntegrationSchema.pre("save", function (next) {
|
|
253
|
-
this.updatedAt = new Date();
|
|
254
|
-
next();
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
const ZohoIntegration = mongoose.model(
|
|
258
|
-
"ZohoIntegration",
|
|
259
|
-
ZohoIntegrationSchema
|
|
260
|
-
);
|
|
261
|
-
|
|
262
|
-
module.exports = ZohoIntegration;
|
|
1
|
+
const mongoose = require("mongoose");
|
|
2
|
+
|
|
3
|
+
const ZohoIntegrationSchema = new mongoose.Schema(
|
|
4
|
+
{
|
|
5
|
+
facilityId: {
|
|
6
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
7
|
+
ref: "Facility",
|
|
8
|
+
required: [true, "Facility ID is required"],
|
|
9
|
+
unique: true,
|
|
10
|
+
index: true,
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
// Zoho API Credentials
|
|
14
|
+
// NOTE: clientSecret, refreshToken, and accessToken should be encrypted before storage
|
|
15
|
+
clientId: {
|
|
16
|
+
type: String,
|
|
17
|
+
required: [true, "Zoho Client ID is required"],
|
|
18
|
+
trim: true,
|
|
19
|
+
},
|
|
20
|
+
clientSecret: {
|
|
21
|
+
type: String,
|
|
22
|
+
required: [true, "Zoho Client Secret is required"],
|
|
23
|
+
trim: true,
|
|
24
|
+
// This field should be encrypted in the application layer
|
|
25
|
+
},
|
|
26
|
+
refreshToken: {
|
|
27
|
+
type: String,
|
|
28
|
+
required: [true, "Zoho Refresh Token is required"],
|
|
29
|
+
trim: true,
|
|
30
|
+
// This field should be encrypted in the application layer
|
|
31
|
+
},
|
|
32
|
+
accessToken: {
|
|
33
|
+
type: String,
|
|
34
|
+
trim: true,
|
|
35
|
+
default: null,
|
|
36
|
+
// This field should be encrypted in the application layer
|
|
37
|
+
// Auto-refreshed by the system
|
|
38
|
+
},
|
|
39
|
+
organizationId: {
|
|
40
|
+
type: String,
|
|
41
|
+
required: [true, "Zoho Organization ID is required"],
|
|
42
|
+
trim: true,
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
// Integration Status
|
|
46
|
+
isActive: {
|
|
47
|
+
type: Boolean,
|
|
48
|
+
default: true,
|
|
49
|
+
},
|
|
50
|
+
connectionStatus: {
|
|
51
|
+
type: String,
|
|
52
|
+
enum: ["connected", "disconnected", "error", "pending"],
|
|
53
|
+
default: "pending",
|
|
54
|
+
},
|
|
55
|
+
connectionError: {
|
|
56
|
+
type: String,
|
|
57
|
+
trim: true,
|
|
58
|
+
default: null,
|
|
59
|
+
},
|
|
60
|
+
lastConnectionTest: {
|
|
61
|
+
type: Date,
|
|
62
|
+
default: null,
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
// Token Management
|
|
66
|
+
tokenExpiresAt: {
|
|
67
|
+
type: Date,
|
|
68
|
+
default: null,
|
|
69
|
+
},
|
|
70
|
+
lastTokenRefreshAt: {
|
|
71
|
+
type: Date,
|
|
72
|
+
default: null,
|
|
73
|
+
},
|
|
74
|
+
tokenRefreshCount: {
|
|
75
|
+
type: Number,
|
|
76
|
+
default: 0,
|
|
77
|
+
min: [0, "Token refresh count cannot be negative"],
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
// Sync Statistics
|
|
81
|
+
lastSyncedAt: {
|
|
82
|
+
type: Date,
|
|
83
|
+
default: null,
|
|
84
|
+
},
|
|
85
|
+
totalInvoicesSynced: {
|
|
86
|
+
type: Number,
|
|
87
|
+
default: 0,
|
|
88
|
+
min: [0, "Total invoices synced cannot be negative"],
|
|
89
|
+
},
|
|
90
|
+
totalPaymentsSynced: {
|
|
91
|
+
type: Number,
|
|
92
|
+
default: 0,
|
|
93
|
+
min: [0, "Total payments synced cannot be negative"],
|
|
94
|
+
},
|
|
95
|
+
totalCustomersSynced: {
|
|
96
|
+
type: Number,
|
|
97
|
+
default: 0,
|
|
98
|
+
min: [0, "Total customers synced cannot be negative"],
|
|
99
|
+
},
|
|
100
|
+
successfulSyncs: {
|
|
101
|
+
type: Number,
|
|
102
|
+
default: 0,
|
|
103
|
+
min: [0, "Successful syncs cannot be negative"],
|
|
104
|
+
},
|
|
105
|
+
failedSyncs: {
|
|
106
|
+
type: Number,
|
|
107
|
+
default: 0,
|
|
108
|
+
min: [0, "Failed syncs cannot be negative"],
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
// Sync Configuration
|
|
112
|
+
autoSyncEnabled: {
|
|
113
|
+
type: Boolean,
|
|
114
|
+
default: true,
|
|
115
|
+
},
|
|
116
|
+
syncFrequency: {
|
|
117
|
+
type: String,
|
|
118
|
+
enum: ["realtime", "hourly", "daily", "manual"],
|
|
119
|
+
default: "realtime",
|
|
120
|
+
},
|
|
121
|
+
lastSyncError: {
|
|
122
|
+
type: String,
|
|
123
|
+
trim: true,
|
|
124
|
+
default: null,
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
// Invoice Sync Settings
|
|
128
|
+
syncInvoicesOnCreation: {
|
|
129
|
+
type: Boolean,
|
|
130
|
+
default: true,
|
|
131
|
+
},
|
|
132
|
+
syncPaymentsOnReceipt: {
|
|
133
|
+
type: Boolean,
|
|
134
|
+
default: true,
|
|
135
|
+
},
|
|
136
|
+
markInvoicesAsSent: {
|
|
137
|
+
type: Boolean,
|
|
138
|
+
default: true,
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
// API Configuration
|
|
142
|
+
apiBaseUrl: {
|
|
143
|
+
type: String,
|
|
144
|
+
trim: true,
|
|
145
|
+
default: "https://www.zohoapis.com/books/v3",
|
|
146
|
+
},
|
|
147
|
+
requestTimeout: {
|
|
148
|
+
type: Number,
|
|
149
|
+
default: 30000,
|
|
150
|
+
min: [1000, "Request timeout must be at least 1000ms"],
|
|
151
|
+
},
|
|
152
|
+
maxRetries: {
|
|
153
|
+
type: Number,
|
|
154
|
+
default: 3,
|
|
155
|
+
min: [0, "Max retries cannot be negative"],
|
|
156
|
+
max: [10, "Max retries cannot exceed 10"],
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
// Audit Fields
|
|
160
|
+
createdBy: {
|
|
161
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
162
|
+
ref: "User",
|
|
163
|
+
default: null,
|
|
164
|
+
},
|
|
165
|
+
updatedBy: {
|
|
166
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
167
|
+
ref: "User",
|
|
168
|
+
default: null,
|
|
169
|
+
},
|
|
170
|
+
lastModifiedBy: {
|
|
171
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
172
|
+
ref: "User",
|
|
173
|
+
default: null,
|
|
174
|
+
},
|
|
175
|
+
|
|
176
|
+
// Notes and metadata
|
|
177
|
+
notes: {
|
|
178
|
+
type: String,
|
|
179
|
+
trim: true,
|
|
180
|
+
default: null,
|
|
181
|
+
},
|
|
182
|
+
metadata: {
|
|
183
|
+
type: mongoose.Schema.Types.Mixed,
|
|
184
|
+
default: {},
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
timestamps: true,
|
|
189
|
+
}
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
// Indexes for better query performance
|
|
193
|
+
ZohoIntegrationSchema.index({ facilityId: 1, isActive: 1 });
|
|
194
|
+
ZohoIntegrationSchema.index({ connectionStatus: 1 });
|
|
195
|
+
ZohoIntegrationSchema.index({ lastSyncedAt: -1 });
|
|
196
|
+
|
|
197
|
+
// Virtual for success rate
|
|
198
|
+
ZohoIntegrationSchema.virtual("syncSuccessRate").get(function () {
|
|
199
|
+
const total = this.successfulSyncs + this.failedSyncs;
|
|
200
|
+
if (total === 0) return 0;
|
|
201
|
+
return ((this.successfulSyncs / total) * 100).toFixed(2);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
// Method to mask sensitive fields for API responses
|
|
205
|
+
ZohoIntegrationSchema.methods.toSafeObject = function () {
|
|
206
|
+
const obj = this.toObject();
|
|
207
|
+
|
|
208
|
+
// Mask sensitive fields
|
|
209
|
+
if (obj.clientSecret) {
|
|
210
|
+
obj.clientSecret = "••••••••" + obj.clientSecret.slice(-4);
|
|
211
|
+
}
|
|
212
|
+
if (obj.refreshToken) {
|
|
213
|
+
obj.refreshToken = "••••••••" + obj.refreshToken.slice(-4);
|
|
214
|
+
}
|
|
215
|
+
if (obj.accessToken) {
|
|
216
|
+
obj.accessToken = "••••••••" + (obj.accessToken ? obj.accessToken.slice(-4) : "");
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return obj;
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
// Method to check if token needs refresh (5 minutes buffer)
|
|
223
|
+
ZohoIntegrationSchema.methods.needsTokenRefresh = function () {
|
|
224
|
+
if (!this.tokenExpiresAt) return true;
|
|
225
|
+
const bufferTime = 5 * 60 * 1000; // 5 minutes in milliseconds
|
|
226
|
+
return new Date().getTime() >= (this.tokenExpiresAt.getTime() - bufferTime);
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
// Method to increment sync counters
|
|
230
|
+
ZohoIntegrationSchema.methods.incrementSyncCounters = function (type, success = true) {
|
|
231
|
+
this.lastSyncedAt = new Date();
|
|
232
|
+
|
|
233
|
+
if (type === "invoice") {
|
|
234
|
+
this.totalInvoicesSynced += 1;
|
|
235
|
+
} else if (type === "payment") {
|
|
236
|
+
this.totalPaymentsSynced += 1;
|
|
237
|
+
} else if (type === "customer") {
|
|
238
|
+
this.totalCustomersSynced += 1;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (success) {
|
|
242
|
+
this.successfulSyncs += 1;
|
|
243
|
+
this.lastSyncError = null;
|
|
244
|
+
} else {
|
|
245
|
+
this.failedSyncs += 1;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return this.save();
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
// Pre-save hook to update timestamps
|
|
252
|
+
ZohoIntegrationSchema.pre("save", function (next) {
|
|
253
|
+
this.updatedAt = new Date();
|
|
254
|
+
next();
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
const ZohoIntegration = mongoose.model(
|
|
258
|
+
"ZohoIntegration",
|
|
259
|
+
ZohoIntegrationSchema
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
module.exports = ZohoIntegration;
|