cart refactor
This commit is contained in:
@@ -23,26 +23,23 @@ export class CartController {
|
||||
return this.cartService.getOrCreateCart(userId, shopId);
|
||||
}
|
||||
|
||||
@Post('product/:productId/increment')
|
||||
@Post('variant/:variantId/increment')
|
||||
@ApiOperation({ summary: 'Increment item quantity in cart by 1' })
|
||||
@ApiHeader(API_HEADER_SLUG)
|
||||
@ApiParam({ name: 'productId', description: 'Product ID to increment in cart' })
|
||||
async incrementItem(@UserId() userId: string, @ShopId() shopId: string, @Param('productId') productId: string) {
|
||||
return this.cartService.incrementItem(userId, shopId, productId, 1);
|
||||
async incrementItem(@UserId() userId: string, @ShopId() shopId: string, @Param('variantId') variantId: string) {
|
||||
return this.cartService.incrementItem(userId, shopId, variantId, 1);
|
||||
}
|
||||
|
||||
@Post('product/:productId/decrement')
|
||||
@Post('variant/:variantId/decrement')
|
||||
@ApiOperation({ summary: 'Decrement item quantity in cart by 1 (removes item if quantity reaches 0)' })
|
||||
@ApiHeader(API_HEADER_SLUG)
|
||||
@ApiParam({ name: 'productId', description: 'Product ID to decrement in cart' })
|
||||
async decrementItem(@UserId() userId: string, @ShopId() shopId: string, @Param('productId') productId: string) {
|
||||
return this.cartService.decrementItem(userId, shopId, productId);
|
||||
async decrementItem(@UserId() userId: string, @ShopId() shopId: string, @Param('variantId') variantId: string) {
|
||||
return this.cartService.decrementItem(userId, shopId, variantId);
|
||||
}
|
||||
|
||||
@Post('product/bulk')
|
||||
@Post('variant/bulk')
|
||||
@ApiOperation({ summary: 'Bulk add items to cart (increments quantity if items exist)' })
|
||||
@ApiHeader(API_HEADER_SLUG)
|
||||
@ApiBody({ type: BulkAddItemsToCartDto })
|
||||
bulkAddItems(
|
||||
@UserId() userId: string,
|
||||
@ShopId() shopId: string,
|
||||
@@ -51,12 +48,11 @@ export class CartController {
|
||||
return this.cartService.bulkAddItems(userId, shopId, bulkAddItemsDto);
|
||||
}
|
||||
|
||||
@Delete('product/:productId')
|
||||
@Delete('variant/:variantId')
|
||||
@ApiOperation({ summary: 'Remove item from cart' })
|
||||
@ApiHeader(API_HEADER_SLUG)
|
||||
@ApiParam({ name: 'productId', description: 'Product ID in the cart' })
|
||||
async removeItem(@UserId() userId: string, @ShopId() shopId: string, @Param('productId') productId: string) {
|
||||
return this.cartService.removeItem(userId, shopId, productId);
|
||||
async removeItem(@UserId() userId: string, @ShopId() shopId: string, @Param('variantId') variantId: string) {
|
||||
return this.cartService.removeItem(userId, shopId, variantId);
|
||||
}
|
||||
|
||||
@Delete()
|
||||
|
||||
@@ -5,6 +5,7 @@ import { CartValidationService } from './cart-validation.service';
|
||||
import { CartCalculationService } from './cart-calculation.service';
|
||||
import { CartMessage } from 'src/common/enums/message.enum';
|
||||
import { ProductService } from 'src/modules/products/providers/product.service';
|
||||
import { Variant } from 'src/modules/products/entities/variant.entity';
|
||||
|
||||
@Injectable()
|
||||
export class CartItemService {
|
||||
@@ -17,13 +18,13 @@ export class CartItemService {
|
||||
/**
|
||||
* Create a new cart item from product
|
||||
*/
|
||||
createCartItem(product: Product, quantity: number): CartItem {
|
||||
const itemPrice = product.price || 0;
|
||||
const itemDiscount = product.discount || 0;
|
||||
createCartItem(variant: Variant, quantity: number): CartItem {
|
||||
const itemPrice = variant.price || 0;
|
||||
const itemDiscount = variant.product.discount || 0;
|
||||
|
||||
return {
|
||||
variantId: product.id,
|
||||
productTitle: product.title,
|
||||
variantId: variant.id,
|
||||
productTitle: variant.product.title,
|
||||
quantity,
|
||||
price: itemPrice,
|
||||
discount: itemDiscount,
|
||||
@@ -34,13 +35,13 @@ export class CartItemService {
|
||||
/**
|
||||
* Build cart item from product (updating existing item if provided)
|
||||
*/
|
||||
buildCartItemFromFood(product: Product, quantity: number, previous?: CartItem): CartItem {
|
||||
const itemPrice = product.price || 0;
|
||||
const itemDiscount = product.discount || 0;
|
||||
buildCartItemFromFood(variant: Variant, quantity: number, previous?: CartItem): CartItem {
|
||||
const itemPrice = variant.price || 0;
|
||||
const itemDiscount = variant.product.discount || 0;
|
||||
return {
|
||||
...(previous ?? ({} as CartItem)),
|
||||
variantId: product.id,
|
||||
productTitle: product.title,
|
||||
variantId: variant.id,
|
||||
productTitle: variant.product.title,
|
||||
quantity,
|
||||
price: itemPrice,
|
||||
discount: itemDiscount,
|
||||
@@ -58,20 +59,20 @@ export class CartItemService {
|
||||
/**
|
||||
* Add or increment item in cart
|
||||
*/
|
||||
async addOrIncrementItem(cart: Cart, productId: string, shopId: string, quantityToAdd: number): Promise<void> {
|
||||
const product = await this.validationService.validateAndGetFood(productId, shopId, quantityToAdd);
|
||||
async addOrIncrementItem(cart: Cart, variantId: string, shopId: string, quantityToAdd: number): Promise<void> {
|
||||
const variant = await this.validationService.validateAndGetVariant(variantId, shopId, quantityToAdd);
|
||||
|
||||
const index = this.getItemIndex(cart, product.id);
|
||||
const index = this.getItemIndex(cart, variant.id);
|
||||
|
||||
if (index < 0) {
|
||||
cart.items.push(this.createCartItem(product, quantityToAdd));
|
||||
cart.items.push(this.createCartItem(variant, quantityToAdd));
|
||||
return;
|
||||
}
|
||||
|
||||
const existingItem = cart.items[index];
|
||||
const newQuantity = existingItem.quantity + quantityToAdd;
|
||||
|
||||
cart.items[index] = this.buildCartItemFromFood(product, newQuantity, existingItem);
|
||||
cart.items[index] = this.buildCartItemFromFood(variant, newQuantity, existingItem);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,8 +89,8 @@ export class CartItemService {
|
||||
/**
|
||||
* Decrement item quantity or remove if quantity reaches 0
|
||||
*/
|
||||
async decrementOrRemoveItem(cart: Cart, productId: string): Promise<void> {
|
||||
const itemIndex = this.getItemIndex(cart, productId);
|
||||
async decrementOrRemoveItem(cart: Cart, variantId: string): Promise<void> {
|
||||
const itemIndex = this.getItemIndex(cart, variantId);
|
||||
if (itemIndex < 0) {
|
||||
throw new NotFoundException(CartMessage.ITEM_NOT_FOUND);
|
||||
}
|
||||
@@ -102,8 +103,8 @@ export class CartItemService {
|
||||
return;
|
||||
}
|
||||
|
||||
const product = await this.productService.findOrFail(productId);
|
||||
cart.items[itemIndex] = this.buildCartItemFromFood(product, newQuantity, existingItem);
|
||||
const variant = await this.productService.findOrFailVariant(variantId);
|
||||
cart.items[itemIndex] = this.buildCartItemFromFood(variant, newQuantity, existingItem);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import { ProductService } from 'src/modules/products/providers/product.service';
|
||||
import { ShopService } from 'src/modules/shops/providers/shops.service';
|
||||
import { DeliveryService } from 'src/modules/delivery/providers/delivery.service';
|
||||
import { PaymentMethodService } from 'src/modules/payments/services/payment-method.service';
|
||||
import { Variant } from 'src/modules/products/entities/variant.entity';
|
||||
|
||||
@Injectable()
|
||||
export class CartValidationService {
|
||||
@@ -30,14 +31,14 @@ export class CartValidationService {
|
||||
/**
|
||||
* Validate product exists and belongs to shop
|
||||
*/
|
||||
async validateAndGetFood(productId: string, shopId: string, quantity: number): Promise<Product> {
|
||||
const product = await this.productService.findOrFail(productId)
|
||||
async validateAndGetVariant(variantId: string, shopId: string, quantity: number): Promise<Variant> {
|
||||
const variant = await this.productService.findOrFailVariant(variantId)
|
||||
|
||||
if (product.shop.id !== shopId) {
|
||||
if (variant.product.shop.id !== shopId) {
|
||||
throw new BadRequestException(CartMessage.PRODUCT_NOT_BELONGS_TO_SHOP);
|
||||
}
|
||||
|
||||
return product;
|
||||
return variant;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -156,18 +156,18 @@ export class CartService {
|
||||
/**
|
||||
* Increment item quantity in cart
|
||||
*/
|
||||
async incrementItem(userId: string, shopId: string, foodId: string, quantity: number): Promise<Cart> {
|
||||
async incrementItem(userId: string, shopId: string, variantId: string, quantity: number): Promise<Cart> {
|
||||
const cart = await this.getOrCreateCart(userId, shopId);
|
||||
await this.itemService.addOrIncrementItem(cart, foodId, shopId, quantity);
|
||||
await this.itemService.addOrIncrementItem(cart, variantId, shopId, quantity);
|
||||
return this.recalculateAndSaveCart(cart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrement item quantity in cart by 1 (removes item if quantity reaches 0)
|
||||
*/
|
||||
async decrementItem(userId: string, shopId: string, foodId: string): Promise<Cart> {
|
||||
async decrementItem(userId: string, shopId: string, variantId: string): Promise<Cart> {
|
||||
const cart = await this.findOneOrFail(userId, shopId);
|
||||
await this.itemService.decrementOrRemoveItem(cart, foodId);
|
||||
await this.itemService.decrementOrRemoveItem(cart, variantId);
|
||||
return this.recalculateAndSaveCart(cart);
|
||||
}
|
||||
|
||||
@@ -187,9 +187,9 @@ export class CartService {
|
||||
/**
|
||||
* Remove item from cart
|
||||
*/
|
||||
async removeItem(userId: string, shopId: string, foodId: string): Promise<Cart> {
|
||||
async removeItem(userId: string, shopId: string, variantId: string): Promise<Cart> {
|
||||
const cart = await this.findOneOrFail(userId, shopId);
|
||||
this.itemService.removeItemOrFail(cart, foodId);
|
||||
this.itemService.removeItemOrFail(cart, variantId);
|
||||
return this.recalculateAndSaveCart(cart);
|
||||
}
|
||||
|
||||
|
||||
@@ -222,4 +222,12 @@ export class ProductService {
|
||||
|
||||
return product
|
||||
}
|
||||
|
||||
async findOrFailVariant(variantId: string) {
|
||||
const variant = await this.em.findOne(Variant, { id: variantId } ,{ populate: ['product','product.shop',] });
|
||||
|
||||
if (!variant) throw new NotFoundException('Variant not found');
|
||||
|
||||
return variant
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user