coupon
This commit is contained in:
@@ -11,6 +11,7 @@ import { SetAddressDto } from '../dto/set-address.dto';
|
||||
import { SetPaymentMethodDto } from '../dto/set-payment-method.dto';
|
||||
import { SetDeliveryMethodDto } from '../dto/set-delivery-method.dto';
|
||||
import { UserAddress } from '../../users/entities/user-address.entity';
|
||||
import { User } from '../../users/entities/user.entity';
|
||||
import { PaymentMethod } from '../../payments/entities/payment-method.entity';
|
||||
import { Delivery } from '../../delivery/entities/delivery.entity';
|
||||
import { Cart, CartItem } from '../interfaces/cart.interface';
|
||||
@@ -366,20 +367,35 @@ export class CartService {
|
||||
|
||||
const orderAmount = cart.subTotal - cart.itemsDiscount;
|
||||
|
||||
const { valid, coupon } = await this.couponService.validateCoupon(
|
||||
// check if coupon is valid and belong to the restaurant
|
||||
const { valid, coupon, message } = await this.couponService.validateCoupon(
|
||||
applyCouponDto.code,
|
||||
restaurantId,
|
||||
Number(orderAmount),
|
||||
);
|
||||
|
||||
if (!valid) {
|
||||
throw new BadRequestException('Invalid coupon code');
|
||||
throw new BadRequestException(message || 'Invalid coupon code');
|
||||
}
|
||||
|
||||
if (!coupon) {
|
||||
throw new BadRequestException('Coupon not found');
|
||||
}
|
||||
|
||||
// Validate user phone if coupon is restricted to a specific user
|
||||
if (coupon.userPhone) {
|
||||
const user = await this.em.findOne(User, { id: userId });
|
||||
if (!user) {
|
||||
throw new NotFoundException('User not found');
|
||||
}
|
||||
|
||||
if (user.phone !== coupon.userPhone) {
|
||||
throw new BadRequestException(
|
||||
'This coupon is only available for a specific user and cannot be applied to your account.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Check maxUsesPerUser limit by counting how many orders this user has with this coupon
|
||||
if (coupon.maxUsesPerUser) {
|
||||
const result = await this.em.getConnection().execute<{ count: string }>(
|
||||
@@ -401,6 +417,40 @@ export class CartService {
|
||||
}
|
||||
}
|
||||
|
||||
// Validate that cart has items matching coupon criteria (if restrictions exist)
|
||||
const hasCategoryRestriction = coupon.foodCategories && coupon.foodCategories.length > 0;
|
||||
const hasFoodRestriction = coupon.foods && coupon.foods.length > 0;
|
||||
|
||||
if (hasCategoryRestriction || hasFoodRestriction) {
|
||||
// Fetch all foods in cart with their categories
|
||||
const foodIds = cart.items.map(item => item.foodId);
|
||||
const foods = await this.em.find(Food, { id: { $in: foodIds } }, { populate: ['category'] });
|
||||
const foodMap = new Map(foods.map(f => [f.id, f]));
|
||||
|
||||
let hasMatchingItem = false;
|
||||
for (const item of cart.items) {
|
||||
const food = foodMap.get(item.foodId);
|
||||
if (!food) continue;
|
||||
|
||||
// Check if item matches coupon criteria
|
||||
const matchesCategory =
|
||||
hasCategoryRestriction && food.category && coupon.foodCategories!.includes(food.category.id);
|
||||
const matchesFood = hasFoodRestriction && coupon.foods!.includes(food.id);
|
||||
|
||||
// Item is eligible if it matches category OR food criteria
|
||||
if (matchesCategory || matchesFood) {
|
||||
hasMatchingItem = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasMatchingItem) {
|
||||
throw new BadRequestException(
|
||||
'This coupon cannot be applied to the items in your cart. Please add items from the specified categories or foods.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const couponDetail: OrderCouponDetail = {
|
||||
couponId: coupon.id,
|
||||
couponName: coupon.name,
|
||||
@@ -562,7 +612,48 @@ export class CartService {
|
||||
let couponDiscount = 0;
|
||||
|
||||
if (cart.coupon) {
|
||||
const priceAfterItemDiscount = subTotal - itemsDiscount;
|
||||
// Get the full coupon entity to check foodCategories and foods
|
||||
const coupon = await this.couponService.findById(cart.coupon.couponId);
|
||||
|
||||
// Determine which items are eligible for the coupon
|
||||
let eligibleItemsTotal = 0;
|
||||
let eligibleItemsDiscount = 0;
|
||||
|
||||
// If coupon has foodCategories or foods restrictions, filter items
|
||||
const hasCategoryRestriction = coupon.foodCategories && coupon.foodCategories.length > 0;
|
||||
const hasFoodRestriction = coupon.foods && coupon.foods.length > 0;
|
||||
|
||||
if (hasCategoryRestriction || hasFoodRestriction) {
|
||||
// Fetch all foods in cart with their categories
|
||||
const foodIds = cart.items.map(item => item.foodId);
|
||||
const foods = await this.em.find(Food, { id: { $in: foodIds } }, { populate: ['category'] });
|
||||
const foodMap = new Map(foods.map(f => [f.id, f]));
|
||||
|
||||
for (const item of cart.items) {
|
||||
const food = foodMap.get(item.foodId);
|
||||
if (!food) continue;
|
||||
|
||||
// Check if item matches coupon criteria
|
||||
const matchesCategory =
|
||||
hasCategoryRestriction && food.category && coupon.foodCategories!.includes(food.category.id);
|
||||
const matchesFood = hasFoodRestriction && coupon.foods!.includes(food.id);
|
||||
|
||||
// Item is eligible if it matches category OR food criteria
|
||||
if (matchesCategory || matchesFood) {
|
||||
const itemPrice = item.price * item.quantity;
|
||||
const itemDiscount = item.discount * item.quantity;
|
||||
eligibleItemsTotal += itemPrice;
|
||||
eligibleItemsDiscount += itemDiscount;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No restrictions, apply to all items
|
||||
eligibleItemsTotal = subTotal;
|
||||
eligibleItemsDiscount = itemsDiscount;
|
||||
}
|
||||
|
||||
const priceAfterItemDiscount = eligibleItemsTotal - eligibleItemsDiscount;
|
||||
|
||||
if (cart.coupon.type === CouponType.PERCENTAGE) {
|
||||
let discount = (priceAfterItemDiscount * cart.coupon.value) / 100;
|
||||
// Apply maxDiscount limit if set
|
||||
|
||||
Reference in New Issue
Block a user