cart
This commit is contained in:
@@ -19,66 +19,65 @@ export class CartController {
|
|||||||
@Get()
|
@Get()
|
||||||
@ApiOperation({ summary: 'Get cart for current user and shop' })
|
@ApiOperation({ summary: 'Get cart for current user and shop' })
|
||||||
@ApiHeader(API_HEADER_SLUG)
|
@ApiHeader(API_HEADER_SLUG)
|
||||||
async findOne(@UserId() userId: string, @ShopId() restaurantId: string) {
|
async findOne(@UserId() userId: string, @ShopId() shopId: string) {
|
||||||
return this.cartService.getOrCreateCart(userId, restaurantId);
|
return this.cartService.getOrCreateCart(userId, shopId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('items/:foodId/increment')
|
@Post('product/:productId/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: 'foodId', description: 'Product ID to increment in cart' })
|
@ApiParam({ name: 'productId', description: 'Product ID to increment in cart' })
|
||||||
async incrementItem(@UserId() userId: string, @ShopId() restaurantId: string, @Param('foodId') foodId: string) {
|
async incrementItem(@UserId() userId: string, @ShopId() shopId: string, @Param('productId') productId: string) {
|
||||||
return this.cartService.incrementItem(userId, restaurantId, foodId, 1);
|
return this.cartService.incrementItem(userId, shopId, productId, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('items/:foodId/decrement')
|
@Post('product/:productId/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: 'foodId', description: 'Product ID to decrement in cart' })
|
@ApiParam({ name: 'productId', description: 'Product ID to decrement in cart' })
|
||||||
async decrementItem(@UserId() userId: string, @ShopId() restaurantId: string, @Param('foodId') foodId: string) {
|
async decrementItem(@UserId() userId: string, @ShopId() shopId: string, @Param('productId') productId: string) {
|
||||||
return this.cartService.decrementItem(userId, restaurantId, foodId);
|
return this.cartService.decrementItem(userId, shopId, productId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('items/bulk')
|
@Post('product/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 })
|
@ApiBody({ type: BulkAddItemsToCartDto })
|
||||||
bulkAddItems(
|
bulkAddItems(
|
||||||
@UserId() userId: string,
|
@UserId() userId: string,
|
||||||
@ShopId() restaurantId: string,
|
@ShopId() shopId: string,
|
||||||
@Body() bulkAddItemsDto: BulkAddItemsToCartDto,
|
@Body() bulkAddItemsDto: BulkAddItemsToCartDto,
|
||||||
) {
|
) {
|
||||||
return this.cartService.bulkAddItems(userId, restaurantId, bulkAddItemsDto);
|
return this.cartService.bulkAddItems(userId, shopId, bulkAddItemsDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Delete('items/:foodId')
|
@Delete('product/:productId')
|
||||||
@ApiOperation({ summary: 'Remove item from cart' })
|
@ApiOperation({ summary: 'Remove item from cart' })
|
||||||
@ApiHeader(API_HEADER_SLUG)
|
@ApiHeader(API_HEADER_SLUG)
|
||||||
@ApiParam({ name: 'foodId', description: 'Product ID in the cart' })
|
@ApiParam({ name: 'productId', description: 'Product ID in the cart' })
|
||||||
async removeItem(@UserId() userId: string, @ShopId() restaurantId: string, @Param('foodId') foodId: string) {
|
async removeItem(@UserId() userId: string, @ShopId() shopId: string, @Param('productId') productId: string) {
|
||||||
return this.cartService.removeItem(userId, restaurantId, foodId);
|
return this.cartService.removeItem(userId, shopId, productId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Delete()
|
@Delete()
|
||||||
@ApiOperation({ summary: 'Clear entire cart' })
|
@ApiOperation({ summary: 'Clear entire cart' })
|
||||||
@ApiHeader(API_HEADER_SLUG)
|
@ApiHeader(API_HEADER_SLUG)
|
||||||
async clearCart(@UserId() userId: string, @ShopId() restaurantId: string) {
|
async clearCart(@UserId() userId: string, @ShopId() shopId: string) {
|
||||||
return this.cartService.clearCart(userId, restaurantId);
|
return this.cartService.clearCart(userId, shopId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('apply-coupon')
|
@Post('coupon')
|
||||||
@ApiOperation({ summary: 'Apply coupon to cart' })
|
@ApiOperation({ summary: 'Apply coupon to cart' })
|
||||||
@ApiHeader(API_HEADER_SLUG)
|
@ApiHeader(API_HEADER_SLUG)
|
||||||
@ApiBody({ type: ApplyCouponDto })
|
async applyCoupon(@UserId() userId: string, @ShopId() shopId: string, @Body() applyCouponDto: ApplyCouponDto) {
|
||||||
async applyCoupon(@UserId() userId: string, @ShopId() restaurantId: string, @Body() applyCouponDto: ApplyCouponDto) {
|
return this.cartService.applyCoupon(userId, shopId, applyCouponDto);
|
||||||
return this.cartService.applyCoupon(userId, restaurantId, applyCouponDto);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Delete('coupon')
|
@Delete('coupon')
|
||||||
@ApiOperation({ summary: 'Remove coupon from cart' })
|
@ApiOperation({ summary: 'Remove coupon from cart' })
|
||||||
@ApiHeader(API_HEADER_SLUG)
|
@ApiHeader(API_HEADER_SLUG)
|
||||||
async removeCoupon(@UserId() userId: string, @ShopId() restaurantId: string) {
|
async removeCoupon(@UserId() userId: string, @ShopId() shopId: string) {
|
||||||
return this.cartService.removeCoupon(userId, restaurantId);
|
return this.cartService.removeCoupon(userId, shopId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -86,7 +85,7 @@ export class CartController {
|
|||||||
@ApiOperation({ summary: 'Set all cart params' })
|
@ApiOperation({ summary: 'Set all cart params' })
|
||||||
@ApiHeader(API_HEADER_SLUG)
|
@ApiHeader(API_HEADER_SLUG)
|
||||||
@ApiBody({ type: SetAllCartParmsDto })
|
@ApiBody({ type: SetAllCartParmsDto })
|
||||||
setAllCartParams(@UserId() userId: string, @ShopId() restaurantId: string, @Body() dto: SetAllCartParmsDto) {
|
setAllCartParams(@UserId() userId: string, @ShopId() shopId: string, @Body() dto: SetAllCartParmsDto) {
|
||||||
return this.cartService.setAllCartParams(userId, restaurantId, dto);
|
return this.cartService.setAllCartParams(userId, shopId, dto);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class AddItemToCartDto {
|
|||||||
@ApiProperty({ description: 'Product ID' })
|
@ApiProperty({ description: 'Product ID' })
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
@IsString()
|
@IsString()
|
||||||
foodId!: string;
|
productId!: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class BulkAddItemsToCartDto {
|
export class BulkAddItemsToCartDto {
|
||||||
@@ -21,8 +21,8 @@ export class BulkAddItemsToCartDto {
|
|||||||
description: 'Array of items to add to cart',
|
description: 'Array of items to add to cart',
|
||||||
type: [AddItemToCartDto],
|
type: [AddItemToCartDto],
|
||||||
example: [
|
example: [
|
||||||
{ foodId: 'product-123', quantity: 2 },
|
{ productId: 'product-123', quantity: 2 },
|
||||||
{ foodId: 'product-456', quantity: 1 },
|
{ productId: 'product-456', quantity: 1 },
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { IsNotEmpty, IsObject, IsOptional, IsString } from 'class-validator';
|
import { IsNotEmpty, IsObject, IsOptional, IsString } from 'class-validator';
|
||||||
import { SetCarDeliveryDto } from './set-car-delivery.dto';
|
|
||||||
|
|
||||||
|
|
||||||
export class SetAllCartParmsDto {
|
export class SetAllCartParmsDto {
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
description: 'Cart description or notes',
|
description: 'Cart description or notes',
|
||||||
@@ -27,14 +25,4 @@ export class SetAllCartParmsDto {
|
|||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
@IsString()
|
@IsString()
|
||||||
paymentMethodId!: string;
|
paymentMethodId!: string;
|
||||||
|
|
||||||
@ApiProperty({ description: 'Table number for dine-in orders', example: '5', required: false })
|
|
||||||
@IsOptional()
|
|
||||||
@IsString()
|
|
||||||
tableNumber?: string;
|
|
||||||
|
|
||||||
@ApiProperty()
|
|
||||||
@IsOptional()
|
|
||||||
@IsObject()
|
|
||||||
carAddress?: SetCarDeliveryDto;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
|
||||||
import { IsNotEmpty, IsString } from 'class-validator';
|
|
||||||
|
|
||||||
export class SetCarDeliveryDto {
|
|
||||||
@ApiProperty({ description: 'Car model', example: 'Toyota Camry' })
|
|
||||||
@IsNotEmpty()
|
|
||||||
@IsString()
|
|
||||||
carModel!: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: 'Car color', example: 'White' })
|
|
||||||
@IsNotEmpty()
|
|
||||||
@IsString()
|
|
||||||
carColor!: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: 'License plate number', example: '12ABC345' })
|
|
||||||
@IsNotEmpty()
|
|
||||||
@IsString()
|
|
||||||
plateNumber!: string;
|
|
||||||
}
|
|
||||||
@@ -11,8 +11,8 @@ export interface CartItem {
|
|||||||
|
|
||||||
export interface Cart {
|
export interface Cart {
|
||||||
userId: string;
|
userId: string;
|
||||||
restaurantId: string;
|
shopId: string;
|
||||||
restaurantName?: string;
|
shopName?: string;
|
||||||
|
|
||||||
items: CartItem[];
|
items: CartItem[];
|
||||||
coupon?: OrderCouponDetail;
|
coupon?: OrderCouponDetail;
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ export class CartCalculationService {
|
|||||||
cart.couponDiscount = couponDiscount;
|
cart.couponDiscount = couponDiscount;
|
||||||
cart.totalDiscount = couponDiscount + itemsDiscount;
|
cart.totalDiscount = couponDiscount + itemsDiscount;
|
||||||
|
|
||||||
cart.tax = await this.calculateTax(cart.restaurantId, Math.max(0, subTotal - cart.totalDiscount));
|
cart.tax = await this.calculateTax(cart.shopId, Math.max(0, subTotal - cart.totalDiscount));
|
||||||
cart.deliveryFee = await this.calculateDeliveryFee(cart);
|
cart.deliveryFee = await this.calculateDeliveryFee(cart);
|
||||||
|
|
||||||
// total = subtotal – totalDiscount + tax + deliveryFee
|
// total = subtotal – totalDiscount + tax + deliveryFee
|
||||||
|
|||||||
@@ -58,8 +58,8 @@ export class CartItemService {
|
|||||||
/**
|
/**
|
||||||
* Add or increment item in cart
|
* Add or increment item in cart
|
||||||
*/
|
*/
|
||||||
async addOrIncrementItem(cart: Cart, shopId: string, restaurantId: string, quantityToAdd: number): Promise<void> {
|
async addOrIncrementItem(cart: Cart, productId: string, shopId: string, quantityToAdd: number): Promise<void> {
|
||||||
const product = await this.validationService.validateAndGetFood(shopId, restaurantId, quantityToAdd);
|
const product = await this.validationService.validateAndGetFood(productId, shopId, quantityToAdd);
|
||||||
|
|
||||||
const index = this.getItemIndex(cart, product.id);
|
const index = this.getItemIndex(cart, product.id);
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import { WalletTransactionRepository } from 'src/modules/users/repositories/wall
|
|||||||
import { ProductService } from 'src/modules/products/providers/product.service';
|
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 { PaymentsService } from 'src/modules/payments/services/payments.service';
|
|
||||||
import { PaymentMethodService } from 'src/modules/payments/services/payment-method.service';
|
import { PaymentMethodService } from 'src/modules/payments/services/payment-method.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { OrderCouponDetail } from 'src/modules/orders/interface/order.interface'
|
|||||||
import { Cart } from '../interfaces/cart.interface';
|
import { Cart } from '../interfaces/cart.interface';
|
||||||
import { BulkAddItemsToCartDto } from '../dto/bulk-add-items.dto';
|
import { BulkAddItemsToCartDto } from '../dto/bulk-add-items.dto';
|
||||||
import { ApplyCouponDto } from '../dto/apply-coupon.dto';
|
import { ApplyCouponDto } from '../dto/apply-coupon.dto';
|
||||||
import { SetAllCartParmsDto } from '../dto/set-all-cart-params.dto';
|
import { SetAllCartParmsDto } from '../dto/set-all-cart-params.dto';
|
||||||
import { CouponService } from 'src/modules/coupons/providers/coupon.service';
|
import { CouponService } from 'src/modules/coupons/providers/coupon.service';
|
||||||
import { CartRepository } from '../repositories/cart.repository';
|
import { CartRepository } from '../repositories/cart.repository';
|
||||||
import { CartValidationService } from './cart-validation.service';
|
import { CartValidationService } from './cart-validation.service';
|
||||||
@@ -32,15 +32,15 @@ export class CartService {
|
|||||||
/**
|
/**
|
||||||
* Set all cart parameters at once
|
* Set all cart parameters at once
|
||||||
*/
|
*/
|
||||||
async setAllCartParams(userId: string, restaurantId: string, params: SetAllCartParmsDto): Promise<Cart> {
|
async setAllCartParams(userId: string, shopId: string, params: SetAllCartParmsDto): Promise<Cart> {
|
||||||
const { deliveryMethodId, paymentMethodId, addressId, carAddress, description, tableNumber } = params;
|
const { deliveryMethodId, paymentMethodId, addressId, description } = params;
|
||||||
|
|
||||||
// get existing cart (or throw)
|
// get existing cart (or throw)
|
||||||
const cart = await this.findOneOrFail(userId, restaurantId);
|
const cart = await this.findOneOrFail(userId, shopId);
|
||||||
|
|
||||||
// Validate and get delivery method
|
// Validate and get delivery method
|
||||||
const deliveryMethod = await this.validationService.getEnabledDeliveryMethodOrFail(
|
const deliveryMethod = await this.validationService.getEnabledDeliveryMethodOrFail(
|
||||||
restaurantId,
|
shopId,
|
||||||
deliveryMethodId,
|
deliveryMethodId,
|
||||||
);
|
);
|
||||||
cart.deliveryMethodId = deliveryMethodId;
|
cart.deliveryMethodId = deliveryMethodId;
|
||||||
@@ -65,7 +65,7 @@ export class CartService {
|
|||||||
|
|
||||||
// ensure address is within shop service area
|
// ensure address is within shop service area
|
||||||
await this.validationService.assertAddressInsideServiceArea(
|
await this.validationService.assertAddressInsideServiceArea(
|
||||||
restaurantId,
|
shopId,
|
||||||
address.latitude,
|
address.latitude,
|
||||||
address.longitude,
|
address.longitude,
|
||||||
);
|
);
|
||||||
@@ -85,7 +85,7 @@ export class CartService {
|
|||||||
|
|
||||||
// Validate and set payment method
|
// Validate and set payment method
|
||||||
const paymentMethod = await this.validationService.getEnabledPaymentMethodOrFail(
|
const paymentMethod = await this.validationService.getEnabledPaymentMethodOrFail(
|
||||||
restaurantId,
|
shopId,
|
||||||
paymentMethodId,
|
paymentMethodId,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -93,7 +93,7 @@ export class CartService {
|
|||||||
await this.calculationService.recalculateCartTotals(cart);
|
await this.calculationService.recalculateCartTotals(cart);
|
||||||
|
|
||||||
if (paymentMethod.method === PaymentMethodEnum.Wallet) {
|
if (paymentMethod.method === PaymentMethodEnum.Wallet) {
|
||||||
await this.validationService.assertWalletHasEnoughBalance(userId, restaurantId, cart.total);
|
await this.validationService.assertWalletHasEnoughBalance(userId, shopId, cart.total);
|
||||||
}
|
}
|
||||||
|
|
||||||
cart.paymentMethodId = paymentMethodId;
|
cart.paymentMethodId = paymentMethodId;
|
||||||
@@ -110,21 +110,21 @@ export class CartService {
|
|||||||
/**
|
/**
|
||||||
* Get or create cart for user and shop
|
* Get or create cart for user and shop
|
||||||
*/
|
*/
|
||||||
async getOrCreateCart(userId: string, restaurantId: string): Promise<Cart> {
|
async getOrCreateCart(userId: string, shopId: string): Promise<Cart> {
|
||||||
const existingCart = await this.cartRepository.findByUserAndRestaurant(userId, restaurantId);
|
const existingCart = await this.cartRepository.findByUserAndShop(userId, shopId);
|
||||||
if (existingCart) {
|
if (existingCart) {
|
||||||
return existingCart;
|
return existingCart;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find shop
|
// Find shop
|
||||||
const shop = await this.shopService.findOrFail(restaurantId);
|
const shop = await this.shopService.findOrFail(shopId);
|
||||||
|
|
||||||
// Create new cart
|
// Create new cart
|
||||||
const now = this.nowIso();
|
const now = this.nowIso();
|
||||||
const cart: Cart = {
|
const cart: Cart = {
|
||||||
userId,
|
userId,
|
||||||
restaurantId,
|
shopId: shop.id,
|
||||||
restaurantName: shop.name,
|
shopName: shop.name,
|
||||||
items: [],
|
items: [],
|
||||||
deliveryFee: 0,
|
deliveryFee: 0,
|
||||||
subTotal: 0,
|
subTotal: 0,
|
||||||
@@ -145,8 +145,8 @@ export class CartService {
|
|||||||
/**
|
/**
|
||||||
* Find cart by user and shop
|
* Find cart by user and shop
|
||||||
*/
|
*/
|
||||||
async findOneOrFail(userId: string, restaurantId: string): Promise<Cart> {
|
async findOneOrFail(userId: string, shopId: string): Promise<Cart> {
|
||||||
const cart = await this.cartRepository.findByUserAndRestaurant(userId, restaurantId);
|
const cart = await this.cartRepository.findByUserAndShop(userId, shopId);
|
||||||
if (!cart) {
|
if (!cart) {
|
||||||
throw new NotFoundException(CartMessage.NOT_FOUND);
|
throw new NotFoundException(CartMessage.NOT_FOUND);
|
||||||
}
|
}
|
||||||
@@ -156,17 +156,17 @@ export class CartService {
|
|||||||
/**
|
/**
|
||||||
* Increment item quantity in cart
|
* Increment item quantity in cart
|
||||||
*/
|
*/
|
||||||
async incrementItem(userId: string, restaurantId: string, foodId: string, quantity: number): Promise<Cart> {
|
async incrementItem(userId: string, shopId: string, foodId: string, quantity: number): Promise<Cart> {
|
||||||
const cart = await this.getOrCreateCart(userId, restaurantId);
|
const cart = await this.getOrCreateCart(userId, shopId);
|
||||||
await this.itemService.addOrIncrementItem(cart, foodId, restaurantId, quantity);
|
await this.itemService.addOrIncrementItem(cart, foodId, 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, restaurantId: string, foodId: string): Promise<Cart> {
|
async decrementItem(userId: string, shopId: string, foodId: string): Promise<Cart> {
|
||||||
const cart = await this.findOneOrFail(userId, restaurantId);
|
const cart = await this.findOneOrFail(userId, shopId);
|
||||||
await this.itemService.decrementOrRemoveItem(cart, foodId);
|
await this.itemService.decrementOrRemoveItem(cart, foodId);
|
||||||
return this.recalculateAndSaveCart(cart);
|
return this.recalculateAndSaveCart(cart);
|
||||||
}
|
}
|
||||||
@@ -174,11 +174,11 @@ export class CartService {
|
|||||||
/**
|
/**
|
||||||
* Bulk add items to cart (increments quantity if items exist)
|
* Bulk add items to cart (increments quantity if items exist)
|
||||||
*/
|
*/
|
||||||
async bulkAddItems(userId: string, restaurantId: string, bulkAddItemsDto: BulkAddItemsToCartDto): Promise<Cart> {
|
async bulkAddItems(userId: string, shopId: string, bulkAddItemsDto: BulkAddItemsToCartDto): Promise<Cart> {
|
||||||
const cart = await this.getOrCreateCart(userId, restaurantId);
|
const cart = await this.getOrCreateCart(userId, shopId);
|
||||||
|
|
||||||
for (const addItemDto of bulkAddItemsDto.items) {
|
for (const addItemDto of bulkAddItemsDto.items) {
|
||||||
await this.itemService.addOrIncrementItem(cart, addItemDto.foodId, restaurantId, addItemDto.quantity);
|
await this.itemService.addOrIncrementItem(cart, addItemDto.productId, shopId, addItemDto.quantity);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.recalculateAndSaveCart(cart);
|
return this.recalculateAndSaveCart(cart);
|
||||||
@@ -187,8 +187,8 @@ export class CartService {
|
|||||||
/**
|
/**
|
||||||
* Remove item from cart
|
* Remove item from cart
|
||||||
*/
|
*/
|
||||||
async removeItem(userId: string, restaurantId: string, foodId: string): Promise<Cart> {
|
async removeItem(userId: string, shopId: string, foodId: string): Promise<Cart> {
|
||||||
const cart = await this.findOneOrFail(userId, restaurantId);
|
const cart = await this.findOneOrFail(userId, shopId);
|
||||||
this.itemService.removeItemOrFail(cart, foodId);
|
this.itemService.removeItemOrFail(cart, foodId);
|
||||||
return this.recalculateAndSaveCart(cart);
|
return this.recalculateAndSaveCart(cart);
|
||||||
}
|
}
|
||||||
@@ -196,15 +196,15 @@ export class CartService {
|
|||||||
/**
|
/**
|
||||||
* Apply coupon to cart
|
* Apply coupon to cart
|
||||||
*/
|
*/
|
||||||
async applyCoupon(userId: string, restaurantId: string, applyCouponDto: ApplyCouponDto): Promise<Cart> {
|
async applyCoupon(userId: string, shopId: string, applyCouponDto: ApplyCouponDto): Promise<Cart> {
|
||||||
const cart = await this.findOneOrFail(userId, restaurantId);
|
const cart = await this.findOneOrFail(userId, shopId);
|
||||||
|
|
||||||
const orderAmount = cart.subTotal - cart.itemsDiscount;
|
const orderAmount = cart.subTotal - cart.itemsDiscount;
|
||||||
const user = await this.userService.findOneOrFail(userId);
|
const user = await this.userService.findOneOrFail(userId);
|
||||||
// check if coupon is valid and belong to the shop
|
// check if coupon is valid and belong to the shop
|
||||||
const { valid, coupon, message } = await this.couponService.validateCoupon(
|
const { valid, coupon, message } = await this.couponService.validateCoupon(
|
||||||
applyCouponDto.code,
|
applyCouponDto.code,
|
||||||
restaurantId,
|
shopId,
|
||||||
Number(orderAmount),
|
Number(orderAmount),
|
||||||
user.phone,
|
user.phone,
|
||||||
);
|
);
|
||||||
@@ -241,8 +241,8 @@ export class CartService {
|
|||||||
/**
|
/**
|
||||||
* Remove coupon from cart
|
* Remove coupon from cart
|
||||||
*/
|
*/
|
||||||
async removeCoupon(userId: string, restaurantId: string): Promise<Cart> {
|
async removeCoupon(userId: string, shopId: string): Promise<Cart> {
|
||||||
const cart = await this.findOneOrFail(userId, restaurantId);
|
const cart = await this.findOneOrFail(userId, shopId);
|
||||||
|
|
||||||
cart.coupon = undefined;
|
cart.coupon = undefined;
|
||||||
return this.recalculateAndSaveCart(cart);
|
return this.recalculateAndSaveCart(cart);
|
||||||
@@ -251,14 +251,14 @@ export class CartService {
|
|||||||
/**
|
/**
|
||||||
* Set address for cart
|
* Set address for cart
|
||||||
*/
|
*/
|
||||||
async setAddress(userId: string, restaurantId: string, addressId: string): Promise<Cart> {
|
async setAddress(userId: string, shopId: string, addressId: string): Promise<Cart> {
|
||||||
const cart = await this.findOneOrFail(userId, restaurantId);
|
const cart = await this.findOneOrFail(userId, shopId);
|
||||||
|
|
||||||
const deliveryMethodId = this.validationService.requireDeliveryMethodId(
|
const deliveryMethodId = this.validationService.requireDeliveryMethodId(
|
||||||
cart,
|
cart,
|
||||||
'Delivery method must be set before setting address',
|
'Delivery method must be set before setting address',
|
||||||
);
|
);
|
||||||
const deliveryMethod = await this.deliveryService.findOrFail(restaurantId, deliveryMethodId);
|
const deliveryMethod = await this.deliveryService.findOrFail(shopId, deliveryMethodId);
|
||||||
|
|
||||||
this.validationService.assertDeliveryMethod(
|
this.validationService.assertDeliveryMethod(
|
||||||
deliveryMethod.method,
|
deliveryMethod.method,
|
||||||
@@ -274,7 +274,7 @@ export class CartService {
|
|||||||
|
|
||||||
// ensure address is within shop service area
|
// ensure address is within shop service area
|
||||||
await this.validationService.assertAddressInsideServiceArea(
|
await this.validationService.assertAddressInsideServiceArea(
|
||||||
restaurantId,
|
shopId,
|
||||||
address.latitude,
|
address.latitude,
|
||||||
address.longitude,
|
address.longitude,
|
||||||
);
|
);
|
||||||
@@ -298,17 +298,17 @@ export class CartService {
|
|||||||
*/
|
*/
|
||||||
async setPaymentMethod(
|
async setPaymentMethod(
|
||||||
userId: string,
|
userId: string,
|
||||||
restaurantId: string,
|
shopId: string,
|
||||||
paymentMethodId: string,
|
paymentMethodId: string,
|
||||||
): Promise<Cart> {
|
): Promise<Cart> {
|
||||||
const cart = await this.findOneOrFail(userId, restaurantId);
|
const cart = await this.findOneOrFail(userId, shopId);
|
||||||
|
|
||||||
const paymentMethod = await this.validationService.getEnabledPaymentMethodOrFail(
|
const paymentMethod = await this.validationService.getEnabledPaymentMethodOrFail(
|
||||||
restaurantId,
|
shopId,
|
||||||
paymentMethodId,
|
paymentMethodId,
|
||||||
);
|
);
|
||||||
if (paymentMethod.method === PaymentMethodEnum.Wallet) {
|
if (paymentMethod.method === PaymentMethodEnum.Wallet) {
|
||||||
await this.validationService.assertWalletHasEnoughBalance(userId, restaurantId, cart.total);
|
await this.validationService.assertWalletHasEnoughBalance(userId, shopId, cart.total);
|
||||||
}
|
}
|
||||||
cart.paymentMethodId = paymentMethodId;
|
cart.paymentMethodId = paymentMethodId;
|
||||||
return this.recalculateAndSaveCart(cart);
|
return this.recalculateAndSaveCart(cart);
|
||||||
@@ -319,13 +319,13 @@ export class CartService {
|
|||||||
*/
|
*/
|
||||||
async setDeliveryMethod(
|
async setDeliveryMethod(
|
||||||
userId: string,
|
userId: string,
|
||||||
restaurantId: string,
|
shopId: string,
|
||||||
deliveryMethodId: string,
|
deliveryMethodId: string,
|
||||||
): Promise<Cart> {
|
): Promise<Cart> {
|
||||||
const cart = await this.findOneOrFail(userId, restaurantId);
|
const cart = await this.findOneOrFail(userId, shopId);
|
||||||
|
|
||||||
const deliveryMethod = await this.validationService.getEnabledDeliveryMethodOrFail(
|
const deliveryMethod = await this.validationService.getEnabledDeliveryMethodOrFail(
|
||||||
restaurantId,
|
shopId,
|
||||||
deliveryMethodId,
|
deliveryMethodId,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -338,8 +338,8 @@ export class CartService {
|
|||||||
/**
|
/**
|
||||||
* Set description for cart
|
* Set description for cart
|
||||||
*/
|
*/
|
||||||
async setDescription(userId: string, restaurantId: string, description: string): Promise<Cart> {
|
async setDescription(userId: string, shopId: string, description: string): Promise<Cart> {
|
||||||
const cart = await this.findOneOrFail(userId, restaurantId);
|
const cart = await this.findOneOrFail(userId, shopId);
|
||||||
|
|
||||||
cart.description = description;
|
cart.description = description;
|
||||||
return this.saveTouchedCart(cart);
|
return this.saveTouchedCart(cart);
|
||||||
@@ -349,8 +349,8 @@ export class CartService {
|
|||||||
/**
|
/**
|
||||||
* Clear cart from cache
|
* Clear cart from cache
|
||||||
*/
|
*/
|
||||||
async clearCart(userId: string, restaurantId: string): Promise<void> {
|
async clearCart(userId: string, shopId: string): Promise<void> {
|
||||||
return this.cartRepository.delete(userId, restaurantId);
|
return this.cartRepository.delete(userId, shopId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -7,19 +7,19 @@ export class CartRepository {
|
|||||||
private readonly CART_TTL = 3600; // 1 hour in seconds
|
private readonly CART_TTL = 3600; // 1 hour in seconds
|
||||||
private readonly CART_KEY_PREFIX = 'cart';
|
private readonly CART_KEY_PREFIX = 'cart';
|
||||||
|
|
||||||
constructor(private readonly cacheService: CacheService) {}
|
constructor(private readonly cacheService: CacheService) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get cart by user and shop
|
* Get cart by user and shop
|
||||||
*/
|
*/
|
||||||
async findByUserAndRestaurant(userId: string, restaurantId: string): Promise<Cart | null> {
|
async findByUserAndShop(userId: string, shopId: string): Promise<Cart | null> {
|
||||||
const cacheKey = this.getCacheKey(userId, restaurantId);
|
const cacheKey = this.getCacheKey(userId, shopId);
|
||||||
const cachedCart = await this.cacheService.get<string>(cacheKey);
|
const cachedCart = await this.cacheService.get<string>(cacheKey);
|
||||||
|
|
||||||
if (cachedCart) {
|
if (cachedCart) {
|
||||||
try {
|
try {
|
||||||
const parsed: unknown = JSON.parse(cachedCart);
|
const parsed: unknown = JSON.parse(cachedCart);
|
||||||
if (this.isCart(parsed) && parsed.userId === userId && parsed.restaurantId === restaurantId) {
|
if (this.isCart(parsed) && parsed.userId === userId && parsed.shopId === shopId) {
|
||||||
return parsed;
|
return parsed;
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
@@ -34,23 +34,23 @@ export class CartRepository {
|
|||||||
* Save cart to cache
|
* Save cart to cache
|
||||||
*/
|
*/
|
||||||
async save(cart: Cart): Promise<void> {
|
async save(cart: Cart): Promise<void> {
|
||||||
const cacheKey = this.getCacheKey(cart.userId, cart.restaurantId);
|
const cacheKey = this.getCacheKey(cart.userId, cart.shopId);
|
||||||
await this.cacheService.set(cacheKey, JSON.stringify(cart), this.CART_TTL);
|
await this.cacheService.set(cacheKey, JSON.stringify(cart), this.CART_TTL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete cart from cache
|
* Delete cart from cache
|
||||||
*/
|
*/
|
||||||
async delete(userId: string, restaurantId: string): Promise<void> {
|
async delete(userId: string, shopId: string): Promise<void> {
|
||||||
const cacheKey = this.getCacheKey(userId, restaurantId);
|
const cacheKey = this.getCacheKey(userId, shopId);
|
||||||
await this.cacheService.del(cacheKey);
|
await this.cacheService.del(cacheKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate cache key for cart
|
* Generate cache key for cart
|
||||||
*/
|
*/
|
||||||
private getCacheKey(userId: string, restaurantId: string): string {
|
private getCacheKey(userId: string, shopId: string): string {
|
||||||
return `${this.CART_KEY_PREFIX}:${userId}:${restaurantId}`;
|
return `${this.CART_KEY_PREFIX}:${userId}:${shopId}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -63,7 +63,7 @@ export class CartRepository {
|
|||||||
const cart = obj as Record<string, unknown>;
|
const cart = obj as Record<string, unknown>;
|
||||||
return (
|
return (
|
||||||
typeof cart.userId === 'string' &&
|
typeof cart.userId === 'string' &&
|
||||||
typeof cart.restaurantId === 'string' &&
|
typeof cart.shopId === 'string' &&
|
||||||
Array.isArray(cart.items) &&
|
Array.isArray(cart.items) &&
|
||||||
typeof cart.subTotal === 'number' &&
|
typeof cart.subTotal === 'number' &&
|
||||||
typeof cart.itemsDiscount === 'number' &&
|
typeof cart.itemsDiscount === 'number' &&
|
||||||
|
|||||||
Reference in New Issue
Block a user