Skip to content

Commit

Permalink
add rental APIs
Browse files Browse the repository at this point in the history
  • Loading branch information
haseebzaki-07 committed Nov 9, 2024
1 parent 5fdc27a commit 7860059
Show file tree
Hide file tree
Showing 5 changed files with 290 additions and 10 deletions.
192 changes: 192 additions & 0 deletions backend/controllers/rent/RentalController.js
Original file line number Diff line number Diff line change
@@ -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 });
}
};
2 changes: 2 additions & 0 deletions backend/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');

Expand Down Expand Up @@ -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);

Expand Down
63 changes: 57 additions & 6 deletions backend/model/rent/rentProduct.js
Original file line number Diff line number Diff line change
@@ -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);
21 changes: 17 additions & 4 deletions backend/model/user.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// models/User.js

const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');

Expand All @@ -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,
Expand All @@ -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();
Expand Down
22 changes: 22 additions & 0 deletions backend/routes/rent/rentalRoutes.js
Original file line number Diff line number Diff line change
@@ -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;

0 comments on commit 7860059

Please sign in to comment.