259 lines
7.2 KiB
JavaScript
259 lines
7.2 KiB
JavaScript
const cron = require("node-cron");
|
|
const {
|
|
Rental,
|
|
User,
|
|
Item,
|
|
ConditionCheck,
|
|
} = require("../models");
|
|
const { Op } = require("sequelize");
|
|
const emailServices = require("../services/email");
|
|
const logger = require("../utils/logger");
|
|
|
|
const reminderSchedule = "0 * * * *"; // Run every hour
|
|
|
|
class ConditionCheckReminderJob {
|
|
static startScheduledReminders() {
|
|
console.log("Starting automated condition check reminder job...");
|
|
|
|
const reminderJob = cron.schedule(
|
|
reminderSchedule,
|
|
async () => {
|
|
try {
|
|
await this.sendConditionCheckReminders();
|
|
} catch (error) {
|
|
logger.error("Error in scheduled condition check reminders", {
|
|
error: error.message,
|
|
stack: error.stack,
|
|
});
|
|
}
|
|
},
|
|
{
|
|
scheduled: false,
|
|
timezone: "America/New_York",
|
|
}
|
|
);
|
|
|
|
// Start the job
|
|
reminderJob.start();
|
|
|
|
console.log("Condition check reminder job scheduled:");
|
|
console.log("- Reminders every hour: " + reminderSchedule);
|
|
|
|
return {
|
|
reminderJob,
|
|
|
|
stop() {
|
|
reminderJob.stop();
|
|
console.log("Condition check reminder job stopped");
|
|
},
|
|
|
|
getStatus() {
|
|
return {
|
|
reminderJobRunning: reminderJob.getStatus() === "scheduled",
|
|
};
|
|
},
|
|
};
|
|
}
|
|
|
|
// Send reminders for upcoming condition check windows
|
|
static async sendConditionCheckReminders() {
|
|
try {
|
|
const now = new Date();
|
|
const reminderWindow = new Date(now.getTime() + 24 * 60 * 60 * 1000); // 24 hours ahead
|
|
|
|
// Find rentals with upcoming condition check windows
|
|
const rentals = await Rental.findAll({
|
|
where: {
|
|
status: {
|
|
[Op.in]: ["confirmed", "active", "completed"],
|
|
},
|
|
},
|
|
include: [
|
|
{ model: User, as: "owner" },
|
|
{ model: User, as: "renter" },
|
|
{ model: Item, as: "item" },
|
|
],
|
|
});
|
|
|
|
for (const rental of rentals) {
|
|
await this.checkAndSendConditionReminders(rental, now, reminderWindow);
|
|
}
|
|
|
|
console.log(
|
|
`Processed ${rentals.length} rentals for condition check reminders`
|
|
);
|
|
} catch (error) {
|
|
console.error("Error sending condition check reminders:", error);
|
|
}
|
|
}
|
|
|
|
// Check specific rental for reminder needs
|
|
static async checkAndSendConditionReminders(rental, now, reminderWindow) {
|
|
const rentalStart = new Date(rental.startDateTime);
|
|
const rentalEnd = new Date(rental.endDateTime);
|
|
|
|
// Pre-rental owner check (24 hours before rental start)
|
|
const preRentalWindow = new Date(
|
|
rentalStart.getTime() - 24 * 60 * 60 * 1000
|
|
);
|
|
if (now <= preRentalWindow && preRentalWindow <= reminderWindow) {
|
|
const existingCheck = await ConditionCheck.findOne({
|
|
where: {
|
|
rentalId: rental.id,
|
|
checkType: "pre_rental_owner",
|
|
},
|
|
});
|
|
|
|
if (!existingCheck) {
|
|
await this.sendPreRentalOwnerReminder(rental);
|
|
}
|
|
}
|
|
|
|
// Rental start renter check (within 24 hours of rental start)
|
|
if (now <= rentalStart && rentalStart <= reminderWindow) {
|
|
const existingCheck = await ConditionCheck.findOne({
|
|
where: {
|
|
rentalId: rental.id,
|
|
checkType: "rental_start_renter",
|
|
},
|
|
});
|
|
|
|
if (!existingCheck) {
|
|
await this.sendRentalStartRenterReminder(rental);
|
|
}
|
|
}
|
|
|
|
// Rental end renter check (within 24 hours of rental end)
|
|
if (now <= rentalEnd && rentalEnd <= reminderWindow) {
|
|
const existingCheck = await ConditionCheck.findOne({
|
|
where: {
|
|
rentalId: rental.id,
|
|
checkType: "rental_end_renter",
|
|
},
|
|
});
|
|
|
|
if (!existingCheck) {
|
|
await this.sendRentalEndRenterReminder(rental);
|
|
}
|
|
}
|
|
|
|
// Post-rental owner check (24 hours after rental end)
|
|
const postRentalWindow = new Date(
|
|
rentalEnd.getTime() + 24 * 60 * 60 * 1000
|
|
);
|
|
if (now <= postRentalWindow && postRentalWindow <= reminderWindow) {
|
|
const existingCheck = await ConditionCheck.findOne({
|
|
where: {
|
|
rentalId: rental.id,
|
|
checkType: "post_rental_owner",
|
|
},
|
|
});
|
|
|
|
if (!existingCheck) {
|
|
await this.sendPostRentalOwnerReminder(rental);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Individual email senders
|
|
static async sendPreRentalOwnerReminder(rental) {
|
|
const notificationData = {
|
|
type: "condition_check_reminder",
|
|
subtype: "pre_rental_owner",
|
|
title: "Condition Check Reminder",
|
|
message: `Please take photos of "${rental.item.name}" before the rental begins tomorrow.`,
|
|
rentalId: rental.id,
|
|
userId: rental.ownerId,
|
|
metadata: {
|
|
checkType: "pre_rental_owner",
|
|
deadline: new Date(rental.startDateTime).toISOString(),
|
|
},
|
|
};
|
|
|
|
await emailServices.rentalReminder.sendConditionCheckReminder(
|
|
rental.owner.email,
|
|
notificationData,
|
|
rental
|
|
);
|
|
|
|
console.log(`Pre-rental owner reminder sent for rental ${rental.id}`);
|
|
}
|
|
|
|
static async sendRentalStartRenterReminder(rental) {
|
|
const notificationData = {
|
|
type: "condition_check_reminder",
|
|
subtype: "rental_start_renter",
|
|
title: "Condition Check Reminder",
|
|
message: `Please take photos when you receive "${rental.item.name}" to document its condition.`,
|
|
rentalId: rental.id,
|
|
userId: rental.renterId,
|
|
metadata: {
|
|
checkType: "rental_start_renter",
|
|
deadline: new Date(
|
|
rental.startDateTime.getTime() + 24 * 60 * 60 * 1000
|
|
).toISOString(),
|
|
},
|
|
};
|
|
|
|
await emailServices.rentalReminder.sendConditionCheckReminder(
|
|
rental.renter.email,
|
|
notificationData,
|
|
rental
|
|
);
|
|
|
|
console.log(`Rental start renter reminder sent for rental ${rental.id}`);
|
|
}
|
|
|
|
static async sendRentalEndRenterReminder(rental) {
|
|
const notificationData = {
|
|
type: "condition_check_reminder",
|
|
subtype: "rental_end_renter",
|
|
title: "Condition Check Reminder",
|
|
message: `Please take photos when returning "${rental.item.name}" to document its condition.`,
|
|
rentalId: rental.id,
|
|
userId: rental.renterId,
|
|
metadata: {
|
|
checkType: "rental_end_renter",
|
|
deadline: new Date(
|
|
rental.endDateTime.getTime() + 24 * 60 * 60 * 1000
|
|
).toISOString(),
|
|
},
|
|
};
|
|
|
|
await emailServices.rentalReminder.sendConditionCheckReminder(
|
|
rental.renter.email,
|
|
notificationData,
|
|
rental
|
|
);
|
|
|
|
console.log(`Rental end renter reminder sent for rental ${rental.id}`);
|
|
}
|
|
|
|
static async sendPostRentalOwnerReminder(rental) {
|
|
const notificationData = {
|
|
type: "condition_check_reminder",
|
|
subtype: "post_rental_owner",
|
|
title: "Condition Check Reminder",
|
|
message: `Please take photos and mark the return status for "${rental.item.name}".`,
|
|
rentalId: rental.id,
|
|
userId: rental.ownerId,
|
|
metadata: {
|
|
checkType: "post_rental_owner",
|
|
deadline: new Date(
|
|
rental.endDateTime.getTime() + 48 * 60 * 60 * 1000
|
|
).toISOString(),
|
|
},
|
|
};
|
|
|
|
await emailServices.rentalReminder.sendConditionCheckReminder(
|
|
rental.owner.email,
|
|
notificationData,
|
|
rental
|
|
);
|
|
|
|
console.log(`Post-rental owner reminder sent for rental ${rental.id}`);
|
|
}
|
|
}
|
|
|
|
module.exports = ConditionCheckReminderJob;
|