payservedb 9.1.8 → 9.1.9
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/index.js +85 -73
- package/package.json +1 -1
- package/src/models/levycontract.js +25 -0
- package/src/models/movein_amenity.js +14 -0
- package/src/models/movein_application.js +2 -2
- package/src/models/movein_disbursement.js +37 -0
- package/src/models/movein_email_thread.js +20 -0
- package/src/models/movein_enquiry.js +17 -0
- package/src/models/movein_featured_package.js +17 -0
- package/src/models/movein_invoice.js +25 -0
- package/src/models/movein_lease.js +38 -0
- package/src/models/movein_settings.js +26 -0
- package/src/models/movein_tenancy.js +56 -0
- package/src/models/movein_want_unit.js +21 -0
- package/src/models/power_invoice.js +4 -9
package/index.js
CHANGED
|
@@ -94,11 +94,10 @@ const models = {
|
|
|
94
94
|
Levy: require("./src/models/levy"),
|
|
95
95
|
LevyType: require("./src/models/levytype"),
|
|
96
96
|
LevyContract: require("./src/models/levycontract"),
|
|
97
|
-
DepositSchema: require("./src/models/levy_deposits"),
|
|
98
97
|
Invoice: require("./src/models/invoice"),
|
|
99
|
-
InvoiceEditLog: require("./src/models/invoice_edit_log"),
|
|
100
98
|
CombinedInvoice: require("./src/models/combined_invoice"),
|
|
101
99
|
InvoiceGeneration: require("./src/models/invoice_generation_approval"),
|
|
100
|
+
InvoiceEditLog: require("./src/models/invoice_edit_log"),
|
|
102
101
|
ShortUrl: require("./src/models/short_urls"),
|
|
103
102
|
InvoicingSchedule: require("./src/models/invoicing_schedule"),
|
|
104
103
|
Reminder: require("./src/models/reminder"),
|
|
@@ -111,8 +110,8 @@ const models = {
|
|
|
111
110
|
Report: require("./src/models/report"),
|
|
112
111
|
Ticket: require("./src/models/tickets"),
|
|
113
112
|
Rating: require("./src/models/facility_rating"),
|
|
114
|
-
FacilityEtimsConfig: require("./src/models/facility_etims_config"),
|
|
115
113
|
Stocksandspare: require("./src/models/stocksandspare"),
|
|
114
|
+
FacilityEtimsConfig: require("./src/models/facility_etims_config"),
|
|
116
115
|
ServiceVendor: require("./src/models/maintenance_service_vendor"),
|
|
117
116
|
MaintenanceServices: require("./src/models/maintenance_services"),
|
|
118
117
|
StockRequisition: require("./src/models/maintenancerequisition"),
|
|
@@ -199,7 +198,6 @@ const models = {
|
|
|
199
198
|
FacilityDepartment: require("./src/models/facility_departements"),
|
|
200
199
|
EmailSmsQueue: require("./src/models/email_sms_queue"),
|
|
201
200
|
PurchaseOrderInvoice: require("./src/models/purchaseOrderInvoice"),
|
|
202
|
-
PowerInvoice: require("./src/models/power_invoice"),
|
|
203
201
|
PowerMeterCustomerBand: require("./src/models/powerMeterCustomerBand"),
|
|
204
202
|
PowerMeterCommunicationProtocol: require("./src/models/powerMeterCommunicationProtocol"),
|
|
205
203
|
PowerMeterDailyReading: require("./src/models/powerMeterDailyReading"),
|
|
@@ -211,6 +209,7 @@ const models = {
|
|
|
211
209
|
PowerMeterLog: require("./src/models/power_meter_command_logs"),
|
|
212
210
|
PowerMeterManufacturer: require("./src/models/powerMetersManufacturer"),
|
|
213
211
|
PowerMeterMonthlyReading: require("./src/models/powerMeterMonthlyReading"),
|
|
212
|
+
PowerInvoice: require("./src/models/power_invoice"),
|
|
214
213
|
PowerMeterSettings: require("./src/models/powerMeterSettings"),
|
|
215
214
|
PowerPrepaidDebit: require("./src/models/power_prepaid_debits"),
|
|
216
215
|
PowerPrepaidCredit: require("./src/models/power_prepaid_credits"),
|
|
@@ -253,85 +252,21 @@ const models = {
|
|
|
253
252
|
PrivacyPolicy: require("./src/models/privacy_policy"),
|
|
254
253
|
TermsAndConditions: require("./src/models/terms_and_conditions"),
|
|
255
254
|
CommunityGuidelines: require("./src/models/community_guidelines"),
|
|
256
|
-
WhatsappConversation: require("./src/models/whatsapp_conversation"),
|
|
255
|
+
WhatsappConversation : require("./src/models/whatsapp_conversation"),
|
|
257
256
|
CustomerPreference: require("./src/models/customer_preference"),
|
|
258
257
|
MoveinApplication: require("./src/models/movein_application"),
|
|
259
258
|
EmailThread: require("./src/models/email_thread"),
|
|
260
259
|
MoveinLandlord: require("./src/models/movein_landlord"),
|
|
261
260
|
MoveinUser: require("./src/models/movein_user"),
|
|
262
|
-
|
|
263
|
-
InvoiceCreditAdjustment: require("./src/models/invoiceCreditAdjustment"),
|
|
264
|
-
// Customer Obsession admin-managed config
|
|
261
|
+
// Customer Obsession admin-managed config (PR3/4/5)
|
|
265
262
|
EmailCcConfig: require("./src/models/email_cc_config"),
|
|
266
263
|
AutoReplyRule: require("./src/models/auto_reply_rule"),
|
|
267
264
|
RecipientGroup: require("./src/models/recipient_group"),
|
|
268
265
|
RecipientGroupMember: require("./src/models/recipient_group_member"),
|
|
266
|
+
InvoiceWithholdingTax: require("./src/models/InvoiceWithholdingTax"),
|
|
267
|
+
InvoiceCreditAdjustment: require("./src/models/invoiceCreditAdjustment"),
|
|
269
268
|
};
|
|
270
269
|
|
|
271
|
-
// ── Move-In platform model schema sources (keyed by model name) ─────────────
|
|
272
|
-
const moveInModelDefs = {
|
|
273
|
-
MoveInUser: { file: "./src/models/movein_user", name: "MoveInUser" },
|
|
274
|
-
MoveInUnit: { file: "./src/models/movein_unit", name: "MoveInUnit" },
|
|
275
|
-
MoveInLandlord: { file: "./src/models/movein_landlord", name: "MoveInLandlord" },
|
|
276
|
-
MoveInApplication: { file: "./src/models/movein_application", name: "MoveInApplication" },
|
|
277
|
-
CustomerPreference: { file: "./src/models/customer_preference", name: "CustomerPreference" },
|
|
278
|
-
MoveInViewingSlot: { file: "./src/models/movein_viewing_slot", name: "MoveInViewingSlot" },
|
|
279
|
-
MoveInBooking: { file: "./src/models/movein_booking", name: "MoveInBooking" },
|
|
280
|
-
MoveInReservation: { file: "./src/models/movein_reservation", name: "MoveInReservation" },
|
|
281
|
-
MoveInPayment: { file: "./src/models/movein_payment", name: "MoveInPayment" },
|
|
282
|
-
MoveInDeal: { file: "./src/models/movein_deal", name: "MoveInDeal" },
|
|
283
|
-
MoveInCommission: { file: "./src/models/movein_commission", name: "MoveInCommission" },
|
|
284
|
-
MoveInNotification: { file: "./src/models/movein_notification", name: "MoveInNotification" },
|
|
285
|
-
MoveInAuditLog: { file: "./src/models/movein_audit_log", name: "MoveInAuditLog" },
|
|
286
|
-
MoveInOtp: { file: "./src/models/movein_otp", name: "MoveInOtp" },
|
|
287
|
-
MoveInConversation: { file: "./src/models/movein_conversation", name: "MoveInConversation" },
|
|
288
|
-
MoveInMessage: { file: "./src/models/movein_message", name: "MoveInMessage" },
|
|
289
|
-
MoveInLandlordUser: { file: "./src/models/movein_landlord_user", name: "MoveInLandlordUser" },
|
|
290
|
-
MoveInHandoffToken: { file: "./src/models/movein_handoff_token", name: "MoveInHandoffToken" },
|
|
291
|
-
MoveInReminder: { file: "./src/models/movein_reminder", name: "MoveInReminder" },
|
|
292
|
-
};
|
|
293
|
-
|
|
294
|
-
// Initial moveIn namespace — models on default connection (payserve_property).
|
|
295
|
-
// connectToMoveInDB replaces these with models bound to payserve_movein.
|
|
296
|
-
const moveInModels = {};
|
|
297
|
-
for (const [key, def] of Object.entries(moveInModelDefs)) {
|
|
298
|
-
moveInModels[key] = require(def.file);
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
// Opens a dedicated connection to payserve_movein and re-registers all
|
|
302
|
-
// Move-In models on that connection so queries hit the correct database.
|
|
303
|
-
async function connectToMoveInDB(dbName, secured, username, password, url, port) {
|
|
304
|
-
try {
|
|
305
|
-
let connectionString;
|
|
306
|
-
if (secured === false) {
|
|
307
|
-
connectionString = `mongodb://${url}:${port}/${dbName}`;
|
|
308
|
-
} else {
|
|
309
|
-
const source = "?authSource=admin";
|
|
310
|
-
connectionString = `mongodb://${username}:${password}@${url}:${port}/${dbName}${source}`;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
const conn = await mongoose.createConnection(connectionString, {
|
|
314
|
-
useNewUrlParser: true,
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
console.log(`Connected to Move-In DB: ${dbName}`);
|
|
318
|
-
|
|
319
|
-
for (const [key, def] of Object.entries(moveInModelDefs)) {
|
|
320
|
-
const baseModel = require(def.file);
|
|
321
|
-
const schema = baseModel.schema;
|
|
322
|
-
moveInModels[key] = conn.models[def.name]
|
|
323
|
-
? conn.models[def.name]
|
|
324
|
-
: conn.model(def.name, schema);
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
module.exports.moveInConnection = conn;
|
|
328
|
-
return conn;
|
|
329
|
-
} catch (err) {
|
|
330
|
-
console.error("Error connecting to Move-In DB:", err);
|
|
331
|
-
throw err;
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
|
|
335
270
|
// Function to get models dynamically from a specific database connection
|
|
336
271
|
async function getModelFromDB(dbConnection, modelName, schema) {
|
|
337
272
|
if (!dbConnection.models[modelName]) {
|
|
@@ -392,6 +327,82 @@ function initializeService(modelNames = []) {
|
|
|
392
327
|
};
|
|
393
328
|
}
|
|
394
329
|
|
|
330
|
+
// Move-In platform model schema sources (keyed by model name)
|
|
331
|
+
const moveInModelDefs = {
|
|
332
|
+
MoveInUser: { file: "./src/models/movein_user", name: "MoveInUser" },
|
|
333
|
+
MoveInUnit: { file: "./src/models/movein_unit", name: "MoveInUnit" },
|
|
334
|
+
MoveInLandlord: { file: "./src/models/movein_landlord", name: "MoveInLandlord" },
|
|
335
|
+
MoveInApplication: { file: "./src/models/movein_application", name: "MoveInApplication" },
|
|
336
|
+
CustomerPreference: { file: "./src/models/customer_preference", name: "CustomerPreference" },
|
|
337
|
+
MoveInViewingSlot: { file: "./src/models/movein_viewing_slot", name: "MoveInViewingSlot" },
|
|
338
|
+
MoveInBooking: { file: "./src/models/movein_booking", name: "MoveInBooking" },
|
|
339
|
+
MoveInReservation: { file: "./src/models/movein_reservation", name: "MoveInReservation" },
|
|
340
|
+
MoveInPayment: { file: "./src/models/movein_payment", name: "MoveInPayment" },
|
|
341
|
+
MoveInDeal: { file: "./src/models/movein_deal", name: "MoveInDeal" },
|
|
342
|
+
MoveInCommission: { file: "./src/models/movein_commission", name: "MoveInCommission" },
|
|
343
|
+
MoveInNotification: { file: "./src/models/movein_notification", name: "MoveInNotification" },
|
|
344
|
+
MoveInReminder: { file: "./src/models/movein_reminder", name: "MoveInReminder" },
|
|
345
|
+
MoveInAuditLog: { file: "./src/models/movein_audit_log", name: "MoveInAuditLog" },
|
|
346
|
+
MoveInOtp: { file: "./src/models/movein_otp", name: "MoveInOtp" },
|
|
347
|
+
MoveInConversation: { file: "./src/models/movein_conversation", name: "MoveInConversation" },
|
|
348
|
+
MoveInMessage: { file: "./src/models/movein_message", name: "MoveInMessage" },
|
|
349
|
+
MoveInLandlordUser: { file: "./src/models/movein_landlord_user", name: "MoveInLandlordUser" },
|
|
350
|
+
MoveInHandoffToken: { file: "./src/models/movein_handoff_token", name: "MoveInHandoffToken" },
|
|
351
|
+
MoveInTenancy: { file: "./src/models/movein_tenancy", name: "MoveInTenancy" },
|
|
352
|
+
MoveInDisbursement: { file: "./src/models/movein_disbursement", name: "MoveInDisbursement" },
|
|
353
|
+
MoveInLease: { file: "./src/models/movein_lease", name: "MoveInLease" },
|
|
354
|
+
MoveInAmenity: { file: "./src/models/movein_amenity", name: "MoveInAmenity" },
|
|
355
|
+
MoveInEnquiry: { file: "./src/models/movein_enquiry", name: "MoveInEnquiry" },
|
|
356
|
+
MoveInInvoice: { file: "./src/models/movein_invoice", name: "MoveInInvoice" },
|
|
357
|
+
MoveInSettings: { file: "./src/models/movein_settings", name: "MoveInSettings" },
|
|
358
|
+
MoveInWantUnit: { file: "./src/models/movein_want_unit", name: "MoveInWantUnit" },
|
|
359
|
+
MoveInFeaturedPackage: { file: "./src/models/movein_featured_package", name: "MoveInFeaturedPackage" },
|
|
360
|
+
MoveInEmailThread: { file: "./src/models/movein_email_thread", name: "MoveInEmailThread" },
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
// Initial moveIn namespace — models on default connection (payserve_property).
|
|
364
|
+
// connectToMoveInDB replaces these with models bound to payserve_movein.
|
|
365
|
+
const moveInModels = {};
|
|
366
|
+
for (const [key, def] of Object.entries(moveInModelDefs)) {
|
|
367
|
+
moveInModels[key] = require(def.file);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Opens a dedicated connection to payserve_movein and re-registers all
|
|
371
|
+
// moveIn models on that connection so queries hit the correct database.
|
|
372
|
+
async function connectToMoveInDB(dbName, secured, username, password, url, port) {
|
|
373
|
+
try {
|
|
374
|
+
let connectionString;
|
|
375
|
+
if (secured === false) {
|
|
376
|
+
connectionString = `mongodb://${url}:${port}/${dbName}`;
|
|
377
|
+
} else {
|
|
378
|
+
const source = "?authSource=admin";
|
|
379
|
+
connectionString = `mongodb://${username}:${password}@${url}:${port}/${dbName}${source}`;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const conn = await mongoose.createConnection(connectionString, {
|
|
383
|
+
useNewUrlParser: true,
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
console.log(`Connected to Move-In DB: ${dbName}`);
|
|
387
|
+
|
|
388
|
+
// Re-register each moveIn model on the new connection
|
|
389
|
+
for (const [key, def] of Object.entries(moveInModelDefs)) {
|
|
390
|
+
const baseModel = require(def.file);
|
|
391
|
+
const schema = baseModel.schema;
|
|
392
|
+
moveInModels[key] = conn.models[def.name]
|
|
393
|
+
? conn.models[def.name]
|
|
394
|
+
: conn.model(def.name, schema);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Expose the raw connection for advanced use
|
|
398
|
+
module.exports.moveInConnection = conn;
|
|
399
|
+
return conn;
|
|
400
|
+
} catch (err) {
|
|
401
|
+
console.error("Error connecting to Move-In DB:", err);
|
|
402
|
+
throw err;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
395
406
|
module.exports = {
|
|
396
407
|
connectToMongoDB,
|
|
397
408
|
connectToMoveInDB,
|
|
@@ -399,4 +410,5 @@ module.exports = {
|
|
|
399
410
|
getModelFromDB,
|
|
400
411
|
initializeService,
|
|
401
412
|
...models,
|
|
402
|
-
|
|
413
|
+
moveIn: moveInModels,
|
|
414
|
+
};
|
package/package.json
CHANGED
|
@@ -118,6 +118,31 @@ const LevyContractSchema = new Schema(
|
|
|
118
118
|
default: 0,
|
|
119
119
|
min: [0, 'Upfront amount cannot be negative']
|
|
120
120
|
},
|
|
121
|
+
// How many months of billing the upfront amount covers. Drives
|
|
122
|
+
// how many period invoices get generated (combined with the
|
|
123
|
+
// contract's paymentFrequency/collectionFrequency).
|
|
124
|
+
numberOfMonths: {
|
|
125
|
+
type: Number,
|
|
126
|
+
min: [1, 'Number of months must be at least 1']
|
|
127
|
+
},
|
|
128
|
+
// The levy's per-month rate, used to compute each period
|
|
129
|
+
// invoice's amount (amountPerMonth * monthsInPeriod, with the
|
|
130
|
+
// first period prorated by days if the contract starts mid-month).
|
|
131
|
+
amountPerMonth: {
|
|
132
|
+
type: Number,
|
|
133
|
+
min: [0, 'Amount per month cannot be negative']
|
|
134
|
+
},
|
|
135
|
+
// How the upfront amount was derived on the frontend ('auto'
|
|
136
|
+
// = amountPerMonth x numberOfMonths, 'custom' = manually entered
|
|
137
|
+
// total). Stored for reference/audit only.
|
|
138
|
+
calculationMode: {
|
|
139
|
+
type: String,
|
|
140
|
+
enum: ['auto', 'custom'],
|
|
141
|
+
default: 'auto'
|
|
142
|
+
},
|
|
143
|
+
// Legacy fields — superseded by numberOfMonths + the date-range
|
|
144
|
+
// period builder, but kept in place since other code may still
|
|
145
|
+
// reference them.
|
|
121
146
|
billingPeriods: {
|
|
122
147
|
type: [String],
|
|
123
148
|
default: []
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const mongoose = require('mongoose');
|
|
2
|
+
|
|
3
|
+
const moveInAmenitySchema = new mongoose.Schema(
|
|
4
|
+
{
|
|
5
|
+
name: { type: String, required: true, trim: true },
|
|
6
|
+
icon: { type: String, default: null },
|
|
7
|
+
category: { type: String, default: 'general', trim: true },
|
|
8
|
+
isActive: { type: Boolean, default: true },
|
|
9
|
+
},
|
|
10
|
+
{ timestamps: true }
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
const MoveInAmenity = mongoose.model('MoveInAmenity', moveInAmenitySchema);
|
|
14
|
+
module.exports = MoveInAmenity;
|
|
@@ -3,7 +3,7 @@ const mongoose = require('mongoose');
|
|
|
3
3
|
const moveInApplicationSchema = new mongoose.Schema(
|
|
4
4
|
{
|
|
5
5
|
unitId: { type: mongoose.Schema.Types.ObjectId, required: true, index: true },
|
|
6
|
-
facilityId: { type: mongoose.Schema.Types.ObjectId, ref: 'Facility',
|
|
6
|
+
facilityId: { type: mongoose.Schema.Types.ObjectId, ref: 'Facility', default: null, index: true },
|
|
7
7
|
unitName: { type: String, default: null },
|
|
8
8
|
facilityName: { type: String, default: null },
|
|
9
9
|
tenantId: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true, index: true },
|
|
@@ -26,4 +26,4 @@ const moveInApplicationSchema = new mongoose.Schema(
|
|
|
26
26
|
);
|
|
27
27
|
|
|
28
28
|
const MoveInApplication = mongoose.model('MoveInApplication', moveInApplicationSchema);
|
|
29
|
-
module.exports = MoveInApplication;
|
|
29
|
+
module.exports = MoveInApplication;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const mongoose = require('mongoose');
|
|
2
|
+
|
|
3
|
+
const moveInDisbursementSchema = new mongoose.Schema(
|
|
4
|
+
{
|
|
5
|
+
tenancyId: { type: mongoose.Schema.Types.ObjectId, required: true, index: true },
|
|
6
|
+
unitId: { type: mongoose.Schema.Types.ObjectId, required: true, index: true },
|
|
7
|
+
landlordId: { type: mongoose.Schema.Types.ObjectId, required: true, index: true },
|
|
8
|
+
tenantId: { type: mongoose.Schema.Types.ObjectId, required: true, index: true },
|
|
9
|
+
|
|
10
|
+
// Amounts
|
|
11
|
+
totalCollected: { type: Number, required: true },
|
|
12
|
+
commissionAmount:{ type: Number, required: true },
|
|
13
|
+
netAmount: { type: Number, required: true }, // totalCollected - commissionAmount
|
|
14
|
+
currency: { type: String, default: 'KES' },
|
|
15
|
+
|
|
16
|
+
// Disbursement details
|
|
17
|
+
status: { type: String, enum: ['pending', 'processing', 'settled', 'failed'], default: 'pending', index: true },
|
|
18
|
+
method: { type: String, enum: ['mpesa', 'bank', null], default: null },
|
|
19
|
+
reference: { type: String, default: null }, // M-Pesa code or bank ref
|
|
20
|
+
disbursedAt: { type: Date, default: null },
|
|
21
|
+
disbursedBy: { type: mongoose.Schema.Types.ObjectId, default: null },
|
|
22
|
+
note: { type: String, default: null },
|
|
23
|
+
|
|
24
|
+
// Bank details snapshot at time of disbursement
|
|
25
|
+
bankName: { type: String, default: null },
|
|
26
|
+
bankAccount: { type: String, default: null },
|
|
27
|
+
bankBranch: { type: String, default: null },
|
|
28
|
+
|
|
29
|
+
// Invoice references
|
|
30
|
+
landlordInvoiceId: { type: mongoose.Schema.Types.ObjectId, default: null },
|
|
31
|
+
landlordReceiptId: { type: mongoose.Schema.Types.ObjectId, default: null },
|
|
32
|
+
},
|
|
33
|
+
{ timestamps: true }
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const MoveInDisbursement = mongoose.model('MoveInDisbursement', moveInDisbursementSchema);
|
|
37
|
+
module.exports = MoveInDisbursement;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const mongoose = require('mongoose');
|
|
2
|
+
|
|
3
|
+
const moveInEmailThreadSchema = new mongoose.Schema(
|
|
4
|
+
{
|
|
5
|
+
messageId: { type: String, required: true, unique: true, index: true },
|
|
6
|
+
from: { type: String, default: '' },
|
|
7
|
+
fromName: { type: String, default: '' },
|
|
8
|
+
subject: { type: String, default: '(no subject)' },
|
|
9
|
+
bodyText: { type: String, default: '' },
|
|
10
|
+
bodyHtml: { type: String, default: '' },
|
|
11
|
+
date: { type: Date, default: Date.now },
|
|
12
|
+
uid: { type: Number, default: null },
|
|
13
|
+
isRead: { type: Boolean, default: false },
|
|
14
|
+
isReplied: { type: Boolean, default: false },
|
|
15
|
+
},
|
|
16
|
+
{ timestamps: true }
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
const MoveInEmailThread = mongoose.model('MoveInEmailThread', moveInEmailThreadSchema);
|
|
20
|
+
module.exports = MoveInEmailThread;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const mongoose = require('mongoose');
|
|
2
|
+
|
|
3
|
+
const moveInEnquirySchema = new mongoose.Schema(
|
|
4
|
+
{
|
|
5
|
+
unitId: { type: mongoose.Schema.Types.ObjectId, required: true, index: true },
|
|
6
|
+
landlordId: { type: mongoose.Schema.Types.ObjectId, default: null, index: true },
|
|
7
|
+
fullName: { type: String, required: true, trim: true },
|
|
8
|
+
phone: { type: String, default: null },
|
|
9
|
+
email: { type: String, required: true, trim: true, lowercase: true },
|
|
10
|
+
message: { type: String, default: null },
|
|
11
|
+
isRead: { type: Boolean, default: false },
|
|
12
|
+
},
|
|
13
|
+
{ timestamps: true }
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const MoveInEnquiry = mongoose.model('MoveInEnquiry', moveInEnquirySchema);
|
|
17
|
+
module.exports = MoveInEnquiry;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const mongoose = require('mongoose');
|
|
2
|
+
|
|
3
|
+
const moveInFeaturedPackageSchema = new mongoose.Schema(
|
|
4
|
+
{
|
|
5
|
+
name: { type: String, required: true, trim: true, maxlength: 80 },
|
|
6
|
+
description: { type: String, default: '', maxlength: 500 },
|
|
7
|
+
durationDays: { type: Number, required: true, min: 1, max: 365 },
|
|
8
|
+
price: { type: Number, required: true, min: 0 },
|
|
9
|
+
sortOrder: { type: Number, default: 0 },
|
|
10
|
+
isActive: { type: Boolean, default: true },
|
|
11
|
+
createdBy: { type: mongoose.Schema.Types.ObjectId, default: null },
|
|
12
|
+
},
|
|
13
|
+
{ timestamps: true }
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const MoveInFeaturedPackage = mongoose.model('MoveInFeaturedPackage', moveInFeaturedPackageSchema);
|
|
17
|
+
module.exports = MoveInFeaturedPackage;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const mongoose = require('mongoose');
|
|
2
|
+
|
|
3
|
+
const moveInInvoiceSchema = new mongoose.Schema(
|
|
4
|
+
{
|
|
5
|
+
invoiceNumber: { type: String, required: true, unique: true, index: true },
|
|
6
|
+
tenantName: { type: String, required: true, trim: true },
|
|
7
|
+
tenantEmail: { type: String, required: true, trim: true, lowercase: true },
|
|
8
|
+
tenantPhone: { type: String, default: null },
|
|
9
|
+
unitId: { type: mongoose.Schema.Types.ObjectId, required: true, index: true },
|
|
10
|
+
landlordId: { type: mongoose.Schema.Types.ObjectId, default: null, index: true },
|
|
11
|
+
wantUnitApplicationId: { type: mongoose.Schema.Types.ObjectId, default: null },
|
|
12
|
+
type: { type: String, default: 'deposit' },
|
|
13
|
+
amount: { type: Number, required: true },
|
|
14
|
+
currency: { type: String, default: 'KES' },
|
|
15
|
+
status: { type: String, enum: ['pending', 'paid', 'cancelled', 'expired'], default: 'pending', index: true },
|
|
16
|
+
deadlineAt: { type: Date, default: null },
|
|
17
|
+
paidAt: { type: Date, default: null },
|
|
18
|
+
mpesaTransactionId: { type: String, default: null },
|
|
19
|
+
notes: { type: String, default: null },
|
|
20
|
+
},
|
|
21
|
+
{ timestamps: true }
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
const MoveInInvoice = mongoose.model('MoveInInvoice', moveInInvoiceSchema);
|
|
25
|
+
module.exports = MoveInInvoice;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const mongoose = require('mongoose');
|
|
2
|
+
|
|
3
|
+
const moveInLeaseSchema = new mongoose.Schema(
|
|
4
|
+
{
|
|
5
|
+
// 'default' = platform-wide template; 'unit' = unit-specific override
|
|
6
|
+
type: { type: String, enum: ['default', 'unit'], required: true, index: true },
|
|
7
|
+
unitId: { type: mongoose.Schema.Types.ObjectId, default: null, index: true },
|
|
8
|
+
uploadedBy: { type: mongoose.Schema.Types.ObjectId, required: true },
|
|
9
|
+
|
|
10
|
+
// File storage
|
|
11
|
+
originalName: { type: String, required: true },
|
|
12
|
+
filePath: { type: String, required: true },
|
|
13
|
+
mimeType: { type: String, default: 'application/pdf' },
|
|
14
|
+
fileSizeBytes: { type: Number, default: null },
|
|
15
|
+
|
|
16
|
+
status: { type: String, enum: ['active', 'superseded'], default: 'active', index: true },
|
|
17
|
+
supersededAt: { type: Date, default: null },
|
|
18
|
+
|
|
19
|
+
// Signed copy (per tenancy — embedded subdoc array)
|
|
20
|
+
signedCopies: [
|
|
21
|
+
{
|
|
22
|
+
tenancyId: { type: mongoose.Schema.Types.ObjectId, required: true },
|
|
23
|
+
tenantId: { type: mongoose.Schema.Types.ObjectId, required: true },
|
|
24
|
+
filePath: { type: String, required: true },
|
|
25
|
+
originalName: { type: String, default: null },
|
|
26
|
+
uploadedAt: { type: Date, default: Date.now },
|
|
27
|
+
status: { type: String, enum: ['pending_review', 'approved', 'rejected'], default: 'pending_review' },
|
|
28
|
+
reviewedAt: { type: Date, default: null },
|
|
29
|
+
reviewedBy: { type: mongoose.Schema.Types.ObjectId, default: null },
|
|
30
|
+
rejectionNote: { type: String, default: null },
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
},
|
|
34
|
+
{ timestamps: true }
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
const MoveInLease = mongoose.model('MoveInLease', moveInLeaseSchema);
|
|
38
|
+
module.exports = MoveInLease;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const mongoose = require('mongoose');
|
|
2
|
+
|
|
3
|
+
const feeRuleSchema = new mongoose.Schema(
|
|
4
|
+
{
|
|
5
|
+
rule: { type: String, default: 'same_as_rent' },
|
|
6
|
+
value: { type: Number, default: 0 },
|
|
7
|
+
},
|
|
8
|
+
{ _id: false }
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
const moveInSettingsSchema = new mongoose.Schema(
|
|
12
|
+
{
|
|
13
|
+
singletonKey: { type: String, default: 'global', unique: true },
|
|
14
|
+
reservation: { type: feeRuleSchema, default: () => ({ rule: 'same_as_rent', value: 0 }) },
|
|
15
|
+
viewing: { type: feeRuleSchema, default: () => ({ rule: 'fixed_amount', value: 500 }) },
|
|
16
|
+
commission: { type: feeRuleSchema, default: () => ({ rule: 'fixed_amount', value: 0 }) },
|
|
17
|
+
paymentDeadlineHours: { type: Number, default: 24 },
|
|
18
|
+
featuredEnabled: { type: Boolean, default: false },
|
|
19
|
+
updatedBy: { type: mongoose.Schema.Types.ObjectId, default: null },
|
|
20
|
+
updatedAt: { type: Date, default: null },
|
|
21
|
+
},
|
|
22
|
+
{ timestamps: false }
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const MoveInSettings = mongoose.model('MoveInSettings', moveInSettingsSchema);
|
|
26
|
+
module.exports = MoveInSettings;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
const mongoose = require('mongoose');
|
|
2
|
+
|
|
3
|
+
const moveInTenancySchema = new mongoose.Schema(
|
|
4
|
+
{
|
|
5
|
+
unitId: { type: mongoose.Schema.Types.ObjectId, required: true, index: true },
|
|
6
|
+
unitName: { type: String, default: null },
|
|
7
|
+
applicationId: { type: mongoose.Schema.Types.ObjectId, required: true, index: true },
|
|
8
|
+
tenantId: { type: mongoose.Schema.Types.ObjectId, required: true, index: true },
|
|
9
|
+
tenantName: { type: String, default: null },
|
|
10
|
+
tenantEmail: { type: String, default: null },
|
|
11
|
+
tenantPhone: { type: String, default: null },
|
|
12
|
+
landlordId: { type: mongoose.Schema.Types.ObjectId, default: null, index: true },
|
|
13
|
+
|
|
14
|
+
// Payment leg
|
|
15
|
+
paymentId: { type: mongoose.Schema.Types.ObjectId, default: null },
|
|
16
|
+
paymentStatus: { type: String, enum: ['pending', 'paid'], default: 'pending' },
|
|
17
|
+
amountPaid: { type: Number, default: null },
|
|
18
|
+
accountNumber: { type: String, default: null }, // MVR...
|
|
19
|
+
commissionAccountNumber: { type: String, default: null }, // MVC...
|
|
20
|
+
commissionType: { type: String, enum: ['percent', 'flat'], default: 'percent' },
|
|
21
|
+
commissionValue: { type: Number, default: 15 },
|
|
22
|
+
commissionAmount:{ type: Number, default: null },
|
|
23
|
+
netToLandlord: { type: Number, default: null },
|
|
24
|
+
paidAt: { type: Date, default: null },
|
|
25
|
+
|
|
26
|
+
// Lease leg
|
|
27
|
+
leaseId: { type: mongoose.Schema.Types.ObjectId, default: null },
|
|
28
|
+
leaseStatus: { type: String, enum: ['pending', 'downloaded', 'uploaded', 'approved', 'rejected'], default: 'pending' },
|
|
29
|
+
leaseSignedAt: { type: Date, default: null },
|
|
30
|
+
leaseApprovedAt: { type: Date, default: null },
|
|
31
|
+
leaseApprovedBy: { type: mongoose.Schema.Types.ObjectId, default: null },
|
|
32
|
+
|
|
33
|
+
// Overall tenancy status
|
|
34
|
+
status: {
|
|
35
|
+
type: String,
|
|
36
|
+
enum: ['awaiting_payment', 'awaiting_lease', 'awaiting_both', 'active', 'cancelled', 'ended'],
|
|
37
|
+
default: 'awaiting_both',
|
|
38
|
+
index: true,
|
|
39
|
+
},
|
|
40
|
+
activatedAt: { type: Date, default: null },
|
|
41
|
+
|
|
42
|
+
// Disbursement leg
|
|
43
|
+
disbursementId: { type: mongoose.Schema.Types.ObjectId, default: null },
|
|
44
|
+
disbursedAt: { type: Date, default: null },
|
|
45
|
+
|
|
46
|
+
// Invoice references
|
|
47
|
+
tenantInvoiceId: { type: mongoose.Schema.Types.ObjectId, default: null },
|
|
48
|
+
tenantReceiptId: { type: mongoose.Schema.Types.ObjectId, default: null },
|
|
49
|
+
|
|
50
|
+
notes: { type: String, default: null },
|
|
51
|
+
},
|
|
52
|
+
{ timestamps: true }
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const MoveInTenancy = mongoose.model('MoveInTenancy', moveInTenancySchema);
|
|
56
|
+
module.exports = MoveInTenancy;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const mongoose = require('mongoose');
|
|
2
|
+
|
|
3
|
+
const moveInWantUnitSchema = new mongoose.Schema(
|
|
4
|
+
{
|
|
5
|
+
unitId: { type: mongoose.Schema.Types.ObjectId, required: true, index: true },
|
|
6
|
+
landlordId: { type: mongoose.Schema.Types.ObjectId, default: null, index: true },
|
|
7
|
+
fullName: { type: String, required: true, trim: true },
|
|
8
|
+
phone: { type: String, required: true, trim: true },
|
|
9
|
+
email: { type: String, required: true, trim: true, lowercase: true },
|
|
10
|
+
occupation: { type: String, default: null },
|
|
11
|
+
evidencePath: { type: String, default: null },
|
|
12
|
+
status: { type: String, enum: ['pending', 'approved', 'rejected'], default: 'pending', index: true },
|
|
13
|
+
reason: { type: String, default: null },
|
|
14
|
+
respondedAt: { type: Date, default: null },
|
|
15
|
+
invoiceId: { type: mongoose.Schema.Types.ObjectId, default: null },
|
|
16
|
+
},
|
|
17
|
+
{ timestamps: true }
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
const MoveInWantUnit = mongoose.model('MoveInWantUnit', moveInWantUnitSchema);
|
|
21
|
+
module.exports = MoveInWantUnit;
|
|
@@ -46,16 +46,11 @@ const powerInvoiceSchema = new mongoose.Schema(
|
|
|
46
46
|
// Positive = arrears carried forward; Negative = credit from overpayment
|
|
47
47
|
},
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
{
|
|
51
|
-
name: { type: String, required: true }, // e.g. "withholding"
|
|
52
|
-
percentage: { type: Number, required: true }, // e.g. 5
|
|
53
|
-
amount: { type: Number, required: true }, // e.g. 50 (subTotal * percentage / 100)
|
|
54
|
-
},
|
|
55
|
-
],
|
|
56
|
-
totalWithholdingTax: {
|
|
49
|
+
withholdingTaxPercentage: {
|
|
57
50
|
type: Number,
|
|
58
|
-
|
|
51
|
+
min: 0,
|
|
52
|
+
max: 100,
|
|
53
|
+
default: 0
|
|
59
54
|
},
|
|
60
55
|
|
|
61
56
|
// ── Biller Address ─────────────────────────────────────────────────────────
|