diff --git a/backend/controllers/rent/RentalController.js b/backend/controllers/rent/RentalController.js new file mode 100644 index 00000000..c49d45a8 --- /dev/null +++ b/backend/controllers/rent/RentalController.js @@ -0,0 +1,192 @@ +const RentProduct = require("../../model/rent/rentProduct"); +const User = require("../../model/user"); +const mongoose = require("mongoose"); + +exports.createRental = async (req, res) => { + const { userId, productId, rentalDuration, quantity } = req.body; + + try { + const user = await User.findById(userId); + const product = await RentProduct.findById(productId); + + if (!user || !product) { + console.error("User or Product not found", { userId, productId }); + return res.status(404).json({ message: "User or Product not found" }); + } + + if (product.availabilityStatus !== "available") { + console.error("Product not available for rent", { + availabilityStatus: product.availabilityStatus, + }); + return res + .status(400) + .json({ message: "Product not available for rent" }); + } + + switch (rentalDuration) { + case "hourly": + if (!product.rentalPricePerHour) { + return res + .status(400) + .json({ message: "Hourly rental price is not set" }); + } + rentalPrice = product.rentalPricePerHour * quantity; // Assuming hourly rate + break; + case "daily": + if (!product.rentalPricePerDay) { + return res + .status(400) + .json({ message: "Daily rental price is not set" }); + } + rentalPrice = product.rentalPricePerDay * quantity; + break; + case "weekly": + if (!product.rentalPricePerWeek) { + return res + .status(400) + .json({ message: "Weekly rental price is not set" }); + } + rentalPrice = product.rentalPricePerWeek * quantity; // Assuming weekly rate + break; + case "monthly": + if (!product.rentalPricePerMonth) { + return res + .status(400) + .json({ message: "Monthly rental price is not set" }); + } + rentalPrice = product.rentalPricePerMonth * quantity; // Assuming monthly rate + break; + default: + return res.status(400).json({ message: "Invalid rental duration" }); + } + + const rental = { + rentalId: new mongoose.Types.ObjectId(), + product: productId, + quantity, + rentalDuration, + rentalDate: new Date(), + status: "ongoing", + rentalPrice, + }; + + user.rentals.push(rental); + + product.rentedQuantity += quantity; + if (product.rentedQuantity > 0) product.availabilityStatus = "rented"; + + await product.save(); + await user.save(); + + res.status(201).json({ message: "Rental created successfully", rental }); + } catch (error) { + console.error("Error creating rental:", error); + res + .status(500) + .json({ + message: "Error creating rental", + error: error.message || error, + }); + } +}; + +exports.updateRental = async (req, res) => { + const { rentalId } = req.params; + const { rentalDuration, quantity } = req.body; + + try { + const user = await User.findOne({ "rentals.rentalId": rentalId }); + if (!user) return res.status(404).json({ message: "Rental not found" }); + + const rental = user.rentals.id(rentalId); + if (!rental) return res.status(404).json({ message: "Rental not found" }); + + rental.rentalDuration = rentalDuration; + rental.quantity = quantity; + + const product = await RentProduct.findById(rental.product); + + product.rentedQuantity += quantity; + + await product.save(); + await user.save(); + + res.status(200).json({ message: "Rental updated successfully", rental }); + } catch (error) { + res.status(500).json({ message: "Error updating rental", error }); + } +}; +exports.cancelRental = async (req, res) => { + const { rentalId } = req.params; + + try { + const user = await User.findOne({ "rentals.rentalId": rentalId }); + if (!user) return res.status(404).json({ message: "Rental not found" }); + + const rental = user.rentals.id(rentalId); + if (!rental || rental.status === "returned") + return res.status(400).json({ message: "Rental cannot be cancelled" }); + + rental.status = "cancelled"; + rental.returnDate = new Date(); + + const product = await RentProduct.findById(rental.product); + + product.rentedQuantity -= rental.quantity; + if (product.rentedQuantity === 0) product.availabilityStatus = "available"; + + await product.save(); + await user.save(); + + res.status(200).json({ message: "Rental cancelled successfully" }); + } catch (error) { + res.status(500).json({ message: "Error cancelling rental", error }); + } +}; + +exports.viewRental = async (req, res) => { + const { rentalId } = req.params; + + try { + const user = await User.findOne({ "rentals.rentalId": rentalId }).populate( + "rentals.product" + ); + if (!user) return res.status(404).json({ message: "Rental not found" }); + + const rental = user.rentals.id(rentalId); + res.status(200).json({ rental }); + } catch (error) { + res.status(500).json({ message: "Error retrieving rental details", error }); + } +}; + +exports.returnRental = async (req, res) => { + const { rentalId } = req.params; + + try { + const user = await User.findOne({ "rentals.rentalId": rentalId }); + if (!user) return res.status(404).json({ message: "Rental not found" }); + + const rental = user.rentals.id(rentalId); + if (!rental || rental.status === "returned") + return res + .status(400) + .json({ message: "Rental is already returned or invalid" }); + + rental.status = "returned"; + rental.returnDate = new Date(); + + const product = await RentProduct.findById(rental.product); + product.rentedQuantity -= rental.quantity; + if (product.rentedQuantity === 0) product.availabilityStatus = "available"; + + await product.save(); + await user.save(); + + res.status(200).json({ message: "Rental returned successfully", rental }); + } catch (error) { + res + .status(500) + .json({ message: "Error marking rental as returned", error }); + } +}; diff --git a/backend/index.js b/backend/index.js index 89afe20b..5f820ea0 100644 --- a/backend/index.js +++ b/backend/index.js @@ -16,6 +16,7 @@ const discussionRoutes = require('./routes/discussionRoutes'); const rentProductRoutes = require('./routes/rent/rentProductRoutes'); const rentWishlistRoutes = require('./routes/rent/rentWishlistRoutes'); const rentCartRoutes = require('./routes/rent/rentCartRoutes'); +const rentalRoutes = require('./routes/rent/rentalRoutes'); const ratingRoutes = require('./routes/rent/ratingRoutes'); @@ -51,6 +52,7 @@ app.use('/api', shopRoutes); app.use('/api', rentProductRoutes); app.use('/api', rentWishlistRoutes); app.use('/api', rentCartRoutes); +app.use('/api', rentalRoutes); app.use('/api', ratingRoutes); diff --git a/backend/model/rent/rentProduct.js b/backend/model/rent/rentProduct.js index 4e45c298..4ed8e838 100644 --- a/backend/model/rent/rentProduct.js +++ b/backend/model/rent/rentProduct.js @@ -1,20 +1,71 @@ -// models/Product.js const mongoose = require('mongoose'); const reviewSchema = new mongoose.Schema({ rentalId: String, - rating: { type: Number, required: true, min: 0, max: 5 }, - comment: String, + rating: { type: Number, required: true, min: 0, max: 5 }, + comment: String, }); const rentProductSchema = new mongoose.Schema({ name: { type: String, required: true }, description: { type: String, required: true }, - price: { type: Number, required: true }, + price: { type: Number, required: true }, // Base price for the product (purchase price) image: { type: String, required: true }, - rating: { type: Number, default: 0, min: 0, max: 5 }, category: { type: [String], required: true }, - reviews: [reviewSchema], // Nested schema for reviews + + + availabilityStatus: { + type: String, + enum: ['available', 'rented', 'maintenance'], + default: 'available', + }, + + rentalPricePerDay: { + type: Number, + required: true, + min: 0, // Minimum daily rental price + }, + + rentalDurationOptions: { + type: [String], + required: true, + enum: ['hourly', 'daily', 'weekly', 'monthly'], + }, // Available rental durations + + maxRentalDuration: { + type: Number, + required: true, + min: 1, // Minimum rental period (e.g., in days, weeks, etc.) + }, + + depositAmount: { + type: Number, + default: 0, + }, + + rentalTerms: { + type: String, + maxlength: 500, + }, + + rentedQuantity: { + type: Number, + default: 0, + }, + + + reviews: [reviewSchema], + + + rating: { type: Number, default: 0, min: 0, max: 5 }, }, { timestamps: true }); +// Method to update product rating based on reviews +rentProductSchema.methods.updateProductRating = function() { + const totalRating = this.reviews.reduce((sum, review) => sum + review.rating, 0); + const averageRating = this.reviews.length ? totalRating / this.reviews.length : 0; + this.rating = averageRating; + return this.save(); +}; + module.exports = mongoose.models.RentProduct || mongoose.model('RentProduct', rentProductSchema); diff --git a/backend/model/user.js b/backend/model/user.js index 85cdb380..1a3ed4de 100644 --- a/backend/model/user.js +++ b/backend/model/user.js @@ -1,5 +1,3 @@ -// models/User.js - const mongoose = require('mongoose'); const bcrypt = require('bcryptjs'); @@ -26,8 +24,24 @@ const userSchema = new mongoose.Schema({ }, otp: { type: String }, otpExpires: { type: Date }, - googleId: { type: String, sparse: true }, + googleId: { type: String, sparse: true }, + rentals: [ + { + rentalId: { type: String, required: true }, + product: { type: mongoose.Schema.Types.ObjectId, ref: 'RentProduct', required: true }, + quantity: { type: Number, default: 1 }, + rentalDuration: { type: String, required: true }, + rentalDate: { type: Date, default: Date.now }, + returnDate: { type: Date }, + status: { + type: String, + enum: ['ongoing', 'returned', 'cancelled'], + default: 'ongoing', + }, + } + ], + wishlist: [ { type: mongoose.Schema.Types.ObjectId, @@ -42,7 +56,6 @@ const userSchema = new mongoose.Schema({ ] }, { timestamps: true }); - // Hash password before saving userSchema.pre('save', async function(next) { if (!this.isModified('password')) return next(); diff --git a/backend/routes/rent/rentalRoutes.js b/backend/routes/rent/rentalRoutes.js new file mode 100644 index 00000000..aa47e78e --- /dev/null +++ b/backend/routes/rent/rentalRoutes.js @@ -0,0 +1,22 @@ +const express = require("express"); +const router = express.Router(); + +const { + createRental, + updateRental, + cancelRental, + viewRental, + returnRental, +} = require("../../controllers/rent/RentalController"); + +router.post("/rentals", createRental); + +router.put("/rentals/:rentalId", updateRental); + +router.delete("/rentals/:rentalId", cancelRental); + +router.get("/rentals/:rentalId", viewRental); + +router.post("/rentals/:rentalId/return", returnRental); + +module.exports = router;