From 9d633dc7e61b55dc83839cc7553aaf4762909b82 Mon Sep 17 00:00:00 2001 From: Youssef Yasser Date: Mon, 11 Nov 2024 19:38:44 +0200 Subject: [PATCH 1/2] feat: gain loyalty points upon booking - fix itinerary tests - refactor redeem points --- backend/src/constants/index.ts | 1 + backend/src/controllers/booking.controller.ts | 8 ++++++-- backend/src/controllers/loyalty.controller.ts | 19 +------------------ .../src/database/repositories/user.repo.ts | 18 ++++++++++++++++++ backend/tests/itinerary.test.ts | 4 +++- 5 files changed, 29 insertions(+), 21 deletions(-) create mode 100644 backend/src/constants/index.ts diff --git a/backend/src/constants/index.ts b/backend/src/constants/index.ts new file mode 100644 index 0000000..901b922 --- /dev/null +++ b/backend/src/constants/index.ts @@ -0,0 +1 @@ +export const LOYALTY_POINT_GAIN = 600000; diff --git a/backend/src/controllers/booking.controller.ts b/backend/src/controllers/booking.controller.ts index aa9806c..7862d37 100644 --- a/backend/src/controllers/booking.controller.ts +++ b/backend/src/controllers/booking.controller.ts @@ -4,6 +4,8 @@ import { logger } from '../middlewares/logger.middleware'; import Validator from '../utils/Validator.utils'; import bookingRepo from '../database/repositories/booking.repo'; import { checkUserLegalAge } from '../utils/AgeVerification.utils'; +import userRepo from '../database/repositories/user.repo'; +import { LOYALTY_POINT_GAIN } from '../constants'; class BookingController { async bookItinerary(req: Request, res: Response) { @@ -11,10 +13,11 @@ class BookingController { Validator.validateId(req.body.itinerary_id, 'incorrect itinerary id'); if (!(await checkUserLegalAge(req.user.userId))) { - res.status(403).json({ message: 'Cannot book as user is under 18' }); + res.status(ResponseStatusCodes.FORBIDDEN).json({ message: 'Cannot book as user is under 18' }); return; } const booking = await bookingRepo.bookItinerary(req.user.userId, req.body.itinerary_id); + await userRepo.updateUserLoyaltyPoints(req.user.userId, LOYALTY_POINT_GAIN); const response = { message: 'Booking successful', @@ -35,11 +38,12 @@ class BookingController { Validator.validateId(req.body.activity_id, 'incorrect activity id'); if (!(await checkUserLegalAge(req.user.userId))) { - res.status(403).json({ message: 'Cannot book as user is under 18' }); + res.status(ResponseStatusCodes.FORBIDDEN).json({ message: 'Cannot book as user is under 18' }); return; } const booking = await bookingRepo.bookActivity(req.user.userId, req.body.activity_id); + await userRepo.updateUserLoyaltyPoints(req.user.userId, LOYALTY_POINT_GAIN); const response = { message: 'Booking successful', diff --git a/backend/src/controllers/loyalty.controller.ts b/backend/src/controllers/loyalty.controller.ts index 3488c89..7721578 100644 --- a/backend/src/controllers/loyalty.controller.ts +++ b/backend/src/controllers/loyalty.controller.ts @@ -14,16 +14,11 @@ async function redeemPoints(req: Request, res: Response) { if (!user) throw new Error('User not found'); - if (user.loyalty_points < points) { - throw new Error('Insufficient points'); - } - - user.loyalty_points -= points; - user.loyalty_level = getLoyaltyLevel(user.loyalty_points); user.wallet += points * 0.01; // Update user await userRepo.updateUser(userId, user as UserType); + await userRepo.updateUserLoyaltyPoints(userId, -points); res.status(ResponseStatusCodes.OK).json({ message: 'Points redeemed successfully' }); } catch (error: any) { @@ -32,16 +27,4 @@ async function redeemPoints(req: Request, res: Response) { } } -function getLoyaltyLevel(points: number): number { - let level: number = 1; - - if (points >= 1000000) { - level = 3; - } else if (points >= 500000) { - level = 2; - } - - return level; -} - export { redeemPoints }; diff --git a/backend/src/database/repositories/user.repo.ts b/backend/src/database/repositories/user.repo.ts index 2435e08..becf475 100644 --- a/backend/src/database/repositories/user.repo.ts +++ b/backend/src/database/repositories/user.repo.ts @@ -2,6 +2,7 @@ import { ObjectId } from 'mongodb'; import { User } from '../models/user.model'; import { UserType } from '../../types/User.types'; import { accountType } from '../../types/User.types'; +import getUserLevel from '../../utils/UserLevel.util'; class UserRepository { async getUsers() { @@ -36,6 +37,23 @@ class UserRepository { return await User.updateOne({ _id: new ObjectId(id) }, user); } + async updateUserLoyaltyPoints(id: string, points: number) { + const user = await this.findUserById(id); + const oldPoints = user?.loyalty_points ?? 0; + const newPoints = oldPoints + points; + + if (newPoints < 0) { + throw new Error('Insufficient points'); + } + + return await User.findByIdAndUpdate(id, { + $set: { + loyalty_points: newPoints, + loyalty_level: getUserLevel(newPoints), + }, + }); + } + async acceptUser(id: string) { return await User.updateOne({ _id: new ObjectId(id) }, { accepted: true }); } diff --git a/backend/tests/itinerary.test.ts b/backend/tests/itinerary.test.ts index 2ae20eb..14ef033 100644 --- a/backend/tests/itinerary.test.ts +++ b/backend/tests/itinerary.test.ts @@ -40,7 +40,7 @@ let activityRef = { let newItinerary: ItineraryType = { name: 'Test Itinerary', - category: 'Test Category', + category: '', tags: [], activities: [], locations: [location], @@ -92,6 +92,8 @@ describe('Itinerary tests', () => { activityRef.activity = newActivity.body.data.activityId; newItinerary.tags.push(newTag.body.data.tagId); newItinerary.activities.push(activityRef); + newItinerary.category = newCategory.body.data.categoryId; + const response = await requestWithAuth('post', '/api/itineraries').send(newItinerary); itineraryId = response.body.data.itineraryId; From 40df4533b97e094e021965d280fdb36036ba5809 Mon Sep 17 00:00:00 2001 From: Youssef Yasser Date: Mon, 11 Nov 2024 20:03:59 +0200 Subject: [PATCH 2/2] feat: prevent user request account deletion if he has bookings --- .../src/controllers/users/user.controller.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/backend/src/controllers/users/user.controller.ts b/backend/src/controllers/users/user.controller.ts index b36d73d..9c1a3df 100644 --- a/backend/src/controllers/users/user.controller.ts +++ b/backend/src/controllers/users/user.controller.ts @@ -100,6 +100,24 @@ const updateUser = async (req: Request, res: Response) => { const requestAccountDeletion = async (req: Request, res: Response) => { try { const userId: string = req.user.userId; + + const userItineraryBookings = await userRepo.getItinerary(userId); + const unattendedItineraryBookings = + userItineraryBookings?.itinerary_bookings.filter((booking: any) => + ['pending', 'confirmed'].includes(booking.status) + ) || []; + + const userActivityBookings = await userRepo.getItinerary(userId); + const unattendedActivityBookings = + userActivityBookings?.activity_bookings.filter((booking: any) => + ['pending', 'confirmed'].includes(booking.status) + ) || []; + + if (unattendedItineraryBookings.length > 0 || unattendedActivityBookings.length > 0) { + res.status(ResponseStatusCodes.BAD_REQUEST).json({ message: 'You have unattended bookings' }); + return; + } + await userRepo.requestAccountDeletion(userId); const response = { message: 'Account deletion requested successfully',