bulk add
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import { Controller, Get, Post, Body, Patch, Delete, UseGuards, Param } from '@nestjs/common';
|
import { Controller, Get, Post, Body, Patch, Delete, UseGuards, Param } from '@nestjs/common';
|
||||||
import { CartService } from '../providers/cart.service';
|
import { CartService } from '../providers/cart.service';
|
||||||
import { AddItemToCartDto } from '../dto/add-item.dto';
|
import { AddItemToCartDto } from '../dto/add-item.dto';
|
||||||
|
import { BulkAddItemsToCartDto } from '../dto/bulk-add-items.dto';
|
||||||
import { UpdateItemQuantityDto } from '../dto/update-item.dto';
|
import { UpdateItemQuantityDto } from '../dto/update-item.dto';
|
||||||
import { ApplyCouponDto } from '../dto/apply-coupon.dto';
|
import { ApplyCouponDto } from '../dto/apply-coupon.dto';
|
||||||
import { SetAddressDto } from '../dto/set-address.dto';
|
import { SetAddressDto } from '../dto/set-address.dto';
|
||||||
@@ -35,6 +36,20 @@ export class CartController {
|
|||||||
return this.cartService.addItem(userId, restaurantId, addItemDto);
|
return this.cartService.addItem(userId, restaurantId, addItemDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Post('items/bulk')
|
||||||
|
@ApiOperation({ summary: 'Bulk add items to cart (increments quantity if items exist)' })
|
||||||
|
@ApiBody({ type: BulkAddItemsToCartDto })
|
||||||
|
@ApiResponse({ status: 201, description: 'Items added to cart successfully' })
|
||||||
|
@ApiResponse({ status: 400, description: 'Bad request (e.g., insufficient stock, invalid items)' })
|
||||||
|
@ApiResponse({ status: 404, description: 'Food not found' })
|
||||||
|
bulkAddItems(
|
||||||
|
@UserId() userId: string,
|
||||||
|
@RestId() restaurantId: string,
|
||||||
|
@Body() bulkAddItemsDto: BulkAddItemsToCartDto,
|
||||||
|
) {
|
||||||
|
return this.cartService.bulkAddItems(userId, restaurantId, bulkAddItemsDto);
|
||||||
|
}
|
||||||
|
|
||||||
@Patch('items/:itemId')
|
@Patch('items/:itemId')
|
||||||
@ApiOperation({ summary: 'Update item quantity in cart' })
|
@ApiOperation({ summary: 'Update item quantity in cart' })
|
||||||
@ApiParam({ name: 'itemId', description: 'Item ID in the cart' })
|
@ApiParam({ name: 'itemId', description: 'Item ID in the cart' })
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { IsNotEmpty, IsArray, ValidateNested, ArrayMinSize } from 'class-validator';
|
||||||
|
import { Type } from 'class-transformer';
|
||||||
|
import { AddItemToCartDto } from './add-item.dto';
|
||||||
|
|
||||||
|
export class BulkAddItemsToCartDto {
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Array of items to add to cart',
|
||||||
|
type: [AddItemToCartDto],
|
||||||
|
example: [
|
||||||
|
{ foodId: 'food-123', quantity: 2 },
|
||||||
|
{ foodId: 'food-456', quantity: 1 },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
@IsNotEmpty()
|
||||||
|
@IsArray()
|
||||||
|
@ArrayMinSize(1, { message: 'At least one item is required' })
|
||||||
|
@ValidateNested({ each: true })
|
||||||
|
@Type(() => AddItemToCartDto)
|
||||||
|
items!: AddItemToCartDto[];
|
||||||
|
}
|
||||||
|
|
||||||
@@ -5,6 +5,7 @@ import { EntityManager } from '@mikro-orm/postgresql';
|
|||||||
import { CacheService } from '../../utils/cache.service';
|
import { CacheService } from '../../utils/cache.service';
|
||||||
import { ulid } from 'ulid';
|
import { ulid } from 'ulid';
|
||||||
import { AddItemToCartDto } from '../dto/add-item.dto';
|
import { AddItemToCartDto } from '../dto/add-item.dto';
|
||||||
|
import { BulkAddItemsToCartDto } from '../dto/bulk-add-items.dto';
|
||||||
import { UpdateItemQuantityDto } from '../dto/update-item.dto';
|
import { UpdateItemQuantityDto } from '../dto/update-item.dto';
|
||||||
import { ApplyCouponDto } from '../dto/apply-coupon.dto';
|
import { ApplyCouponDto } from '../dto/apply-coupon.dto';
|
||||||
import { SetAddressDto } from '../dto/set-address.dto';
|
import { SetAddressDto } from '../dto/set-address.dto';
|
||||||
@@ -145,6 +146,83 @@ export class CartService {
|
|||||||
return cart;
|
return cart;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bulk add items to cart (increments quantity if items exist)
|
||||||
|
*/
|
||||||
|
async bulkAddItems(userId: string, restaurantId: string, bulkAddItemsDto: BulkAddItemsToCartDto): Promise<Cart> {
|
||||||
|
const cart = await this.getOrCreateCart(userId, restaurantId);
|
||||||
|
|
||||||
|
// Process each item
|
||||||
|
for (const addItemDto of bulkAddItemsDto.items) {
|
||||||
|
// Find food
|
||||||
|
const food = await this.em.findOne(Food, { id: addItemDto.foodId }, { populate: ['restaurant'] });
|
||||||
|
if (!food) {
|
||||||
|
throw new NotFoundException(`Food with ID ${addItemDto.foodId} not found`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if food belongs to the restaurant
|
||||||
|
if (food.restaurant.id !== restaurantId) {
|
||||||
|
throw new BadRequestException(`Food ${food.title || food.id} does not belong to restaurant ${restaurantId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check stock availability
|
||||||
|
if (food.stock < addItemDto.quantity) {
|
||||||
|
throw new BadRequestException(`Insufficient stock for food ${food.title}. Available: ${food.stock}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if item already exists in cart
|
||||||
|
const existingItemIndex = cart.items.findIndex(item => item.foodId === addItemDto.foodId);
|
||||||
|
|
||||||
|
if (existingItemIndex >= 0) {
|
||||||
|
// Update existing item quantity
|
||||||
|
const existingItem = cart.items[existingItemIndex];
|
||||||
|
const newQuantity = existingItem.quantity + addItemDto.quantity;
|
||||||
|
|
||||||
|
// Check stock for new total quantity
|
||||||
|
if (food.stock < newQuantity) {
|
||||||
|
throw new BadRequestException(`Insufficient stock for food ${food.title}. Available: ${food.stock}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const itemPrice = food.price || 0;
|
||||||
|
const itemDiscount = food.discount || 0;
|
||||||
|
const itemTotalPrice = itemPrice * newQuantity;
|
||||||
|
const itemTotalDiscount = itemDiscount * newQuantity;
|
||||||
|
const itemFinalPrice = itemTotalPrice - itemTotalDiscount;
|
||||||
|
|
||||||
|
cart.items[existingItemIndex] = {
|
||||||
|
...existingItem,
|
||||||
|
quantity: newQuantity,
|
||||||
|
totalPrice: itemFinalPrice,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// Create new item
|
||||||
|
const itemPrice = food.price || 0;
|
||||||
|
const itemDiscount = food.discount || 0;
|
||||||
|
const itemTotalPrice = itemPrice * addItemDto.quantity;
|
||||||
|
const itemTotalDiscount = itemDiscount * addItemDto.quantity;
|
||||||
|
const itemFinalPrice = itemTotalPrice - itemTotalDiscount;
|
||||||
|
|
||||||
|
const cartItem: CartItem = {
|
||||||
|
itemId: ulid(),
|
||||||
|
foodId: food.id,
|
||||||
|
foodTitle: food.title,
|
||||||
|
quantity: addItemDto.quantity,
|
||||||
|
price: itemPrice,
|
||||||
|
discount: itemDiscount,
|
||||||
|
totalPrice: itemFinalPrice,
|
||||||
|
};
|
||||||
|
|
||||||
|
cart.items.push(cartItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recalculate cart totals once after all items are added
|
||||||
|
await this.recalculateCartTotals(cart);
|
||||||
|
await this.saveCart(cart);
|
||||||
|
|
||||||
|
return cart;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update item quantity in cart
|
* Update item quantity in cart
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user