108 lines
3.3 KiB
TypeScript
108 lines
3.3 KiB
TypeScript
import { Injectable, NotFoundException } from '@nestjs/common';
|
|
import { Product } from '../../products/entities/product.entity';
|
|
import { Cart, CartItem } from '../interfaces/cart.interface';
|
|
import { CartValidationService } from './cart-validation.service';
|
|
import { CartCalculationService } from './cart-calculation.service';
|
|
import { CartMessage } from 'src/common/enums/message.enum';
|
|
|
|
@Injectable()
|
|
export class CartItemService {
|
|
constructor(
|
|
private readonly validationService: CartValidationService,
|
|
private readonly calculationService: CartCalculationService,
|
|
) {}
|
|
|
|
/**
|
|
* Create a new cart item from product
|
|
*/
|
|
createCartItem(product: Product, quantity: number): CartItem {
|
|
const itemPrice = product.price || 0;
|
|
const itemDiscount = product.discount || 0;
|
|
|
|
return {
|
|
foodId: product.id,
|
|
foodTitle: product.title,
|
|
quantity,
|
|
price: itemPrice,
|
|
discount: itemDiscount,
|
|
totalPrice: this.calculationService.calculateItemTotalPrice(itemPrice, itemDiscount, quantity),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
return {
|
|
...(previous ?? ({} as CartItem)),
|
|
foodId: product.id,
|
|
foodTitle: product.title,
|
|
quantity,
|
|
price: itemPrice,
|
|
discount: itemDiscount,
|
|
totalPrice: this.calculationService.calculateItemTotalPrice(itemPrice, itemDiscount, quantity),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Get item index in cart
|
|
*/
|
|
getItemIndex(cart: Cart, foodId: string): number {
|
|
return cart.items.findIndex(item => item.foodId === foodId);
|
|
}
|
|
|
|
/**
|
|
* Add or increment item in cart
|
|
*/
|
|
async addOrIncrementItem(cart: Cart, foodId: string, restaurantId: string, quantityToAdd: number): Promise<void> {
|
|
const product = await this.validationService.validateAndGetFood(foodId, restaurantId, quantityToAdd);
|
|
|
|
const index = this.getItemIndex(cart, product.id);
|
|
|
|
if (index < 0) {
|
|
cart.items.push(this.createCartItem(product, quantityToAdd));
|
|
return;
|
|
}
|
|
|
|
const existingItem = cart.items[index];
|
|
const newQuantity = existingItem.quantity + quantityToAdd;
|
|
this.validationService.validateStock(product, newQuantity);
|
|
cart.items[index] = this.buildCartItemFromFood(product, newQuantity, existingItem);
|
|
}
|
|
|
|
/**
|
|
* Remove item from cart
|
|
*/
|
|
removeItemOrFail(cart: Cart, foodId: string): void {
|
|
const itemIndex = this.getItemIndex(cart, foodId);
|
|
if (itemIndex < 0) {
|
|
throw new NotFoundException(CartMessage.ITEM_NOT_FOUND);
|
|
}
|
|
cart.items.splice(itemIndex, 1);
|
|
}
|
|
|
|
/**
|
|
* Decrement item quantity or remove if quantity reaches 0
|
|
*/
|
|
async decrementOrRemoveItem(cart: Cart, foodId: string): Promise<void> {
|
|
const itemIndex = this.getItemIndex(cart, foodId);
|
|
if (itemIndex < 0) {
|
|
throw new NotFoundException(CartMessage.ITEM_NOT_FOUND);
|
|
}
|
|
|
|
const existingItem = cart.items[itemIndex];
|
|
const newQuantity = existingItem.quantity - 1;
|
|
|
|
if (newQuantity <= 0) {
|
|
cart.items.splice(itemIndex, 1);
|
|
return;
|
|
}
|
|
|
|
const product = await this.validationService.getFoodOrFail(foodId);
|
|
cart.items[itemIndex] = this.buildCartItemFromFood(product, newQuantity, existingItem);
|
|
}
|
|
}
|
|
|