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