const { Rental, Item, User } = require("../models"); const emailServices = require("./email"); const { isActive } = require("../utils/rentalStatus"); const logger = require("../utils/logger"); 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 * @returns {Object} - Updated rental with late fee information */ static async processLateReturn(rentalId, actualReturnDateTime) { const rental = await Rental.findByPk(rentalId, { include: [{ model: Item, as: "item" }], }); if (!rental) { throw new Error("Rental not found"); } if (!isActive(rental)) { 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", payoutStatus: "pending", }; 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 ); } // Trigger immediate payout if rental is verified to be actually completed not late if (!lateCalculation.isLate) { // Import here to avoid circular dependency const PayoutService = require("./payoutService"); PayoutService.triggerPayoutOnCompletion(rentalId).catch((err) => { logger.error("Error triggering payout on late return processing", { rentalId, error: err.message, }); }); } return { rental: updatedRental, lateCalculation, }; } } module.exports = LateReturnService;