114 lines
3.7 KiB
JavaScript
114 lines
3.7 KiB
JavaScript
const { Rental, Item, User } = require("../models");
|
|
const emailServices = require("./email");
|
|
|
|
class LateReturnService {
|
|
/**
|
|
* Calculate late fees based on actual return time vs scheduled end time
|
|
* @param {Object} rental - Rental instance with populated item data
|
|
* @param {Date} actualReturnDateTime - When the item was actually returned
|
|
* @returns {Object} - { lateHours, lateFee, isLate }
|
|
*/
|
|
static calculateLateFee(rental, actualReturnDateTime) {
|
|
const scheduledEnd = new Date(rental.endDateTime);
|
|
const actualReturn = new Date(actualReturnDateTime);
|
|
|
|
// Calculate hours late
|
|
const hoursLate = (actualReturn - scheduledEnd) / (1000 * 60 * 60);
|
|
|
|
if (hoursLate <= 0) {
|
|
return {
|
|
lateHours: 0,
|
|
lateFee: 0.0,
|
|
isLate: false,
|
|
};
|
|
}
|
|
|
|
let lateFee = 0;
|
|
let pricingType = "daily";
|
|
const billableDays = Math.ceil(hoursLate / 24);
|
|
|
|
// Calculate late fees per day, deriving daily rate from available pricing tiers
|
|
if (rental.item?.pricePerDay && rental.item.pricePerDay > 0) {
|
|
// Daily pricing - charge per day late
|
|
lateFee = billableDays * parseFloat(rental.item.pricePerDay);
|
|
} else if (rental.item?.pricePerWeek && rental.item.pricePerWeek > 0) {
|
|
// Weekly pricing - derive daily rate and charge per day late
|
|
const dailyRate = parseFloat(rental.item.pricePerWeek) / 7;
|
|
lateFee = billableDays * dailyRate;
|
|
} else if (rental.item?.pricePerMonth && rental.item.pricePerMonth > 0) {
|
|
// Monthly pricing - derive daily rate and charge per day late
|
|
const dailyRate = parseFloat(rental.item.pricePerMonth) / 30;
|
|
lateFee = billableDays * dailyRate;
|
|
} else if (rental.item?.pricePerHour && rental.item.pricePerHour > 0) {
|
|
// Hourly pricing - derive daily rate and charge per day late
|
|
const dailyRate = parseFloat(rental.item.pricePerHour) * 24;
|
|
lateFee = billableDays * dailyRate;
|
|
} else {
|
|
// Free borrows - charge $10 per day late
|
|
lateFee = billableDays * 10.0;
|
|
}
|
|
|
|
return {
|
|
lateHours: hoursLate,
|
|
lateFee: parseFloat(lateFee.toFixed(2)),
|
|
isLate: true,
|
|
pricingType,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Process late return and update rental with fees
|
|
* @param {string} rentalId - Rental ID
|
|
* @param {Date} actualReturnDateTime - When item was returned
|
|
* @param {string} notes - Optional notes about the return
|
|
* @returns {Object} - Updated rental with late fee information
|
|
*/
|
|
static async processLateReturn(rentalId, actualReturnDateTime, notes = null) {
|
|
const rental = await Rental.findByPk(rentalId, {
|
|
include: [{ model: Item, as: "item" }],
|
|
});
|
|
|
|
if (!rental) {
|
|
throw new Error("Rental not found");
|
|
}
|
|
|
|
if (rental.status !== "active") {
|
|
throw new Error("Can only process late returns for active rentals");
|
|
}
|
|
|
|
const lateCalculation = this.calculateLateFee(rental, actualReturnDateTime);
|
|
|
|
const updates = {
|
|
actualReturnDateTime: new Date(actualReturnDateTime),
|
|
status: lateCalculation.isLate ? "returned_late" : "completed",
|
|
};
|
|
|
|
if (notes) {
|
|
updates.notes = notes;
|
|
}
|
|
|
|
const updatedRental = await rental.update(updates);
|
|
|
|
// Send notification to customer service if late return detected
|
|
if (lateCalculation.isLate && lateCalculation.lateFee > 0) {
|
|
// Fetch owner and renter user data for email
|
|
const owner = await User.findByPk(updatedRental.ownerId);
|
|
const renter = await User.findByPk(updatedRental.renterId);
|
|
|
|
await emailServices.customerService.sendLateReturnToCustomerService(
|
|
updatedRental,
|
|
owner,
|
|
renter,
|
|
lateCalculation
|
|
);
|
|
}
|
|
|
|
return {
|
|
rental: updatedRental,
|
|
lateCalculation,
|
|
};
|
|
}
|
|
}
|
|
|
|
module.exports = LateReturnService;
|