cart all
This commit is contained in:
@@ -12,6 +12,7 @@ import { ApiTags, ApiOperation, ApiBearerAuth, ApiBody, ApiParam, ApiHeader } fr
|
|||||||
import { AuthGuard } from '../../auth/guards/auth.guard';
|
import { AuthGuard } from '../../auth/guards/auth.guard';
|
||||||
import { UserId } from 'src/common/decorators/user-id.decorator';
|
import { UserId } from 'src/common/decorators/user-id.decorator';
|
||||||
import { RestId } from 'src/common/decorators/rest-id.decorator';
|
import { RestId } from 'src/common/decorators/rest-id.decorator';
|
||||||
|
import { SetAllCartParmsDto } from '../dto/set-all-cart-params.dto';
|
||||||
|
|
||||||
const API_HEADER_SLUG = {
|
const API_HEADER_SLUG = {
|
||||||
name: 'X-Slug',
|
name: 'X-Slug',
|
||||||
@@ -161,4 +162,15 @@ export class CartController {
|
|||||||
) {
|
) {
|
||||||
return this.cartService.setCarDelivery(userId, restaurantId, setCarDeliveryDto);
|
return this.cartService.setCarDelivery(userId, restaurantId, setCarDeliveryDto);
|
||||||
}
|
}
|
||||||
|
@Patch('all')
|
||||||
|
@ApiOperation({ summary: 'Set car delivery for cart' })
|
||||||
|
@ApiHeader(API_HEADER_SLUG)
|
||||||
|
@ApiBody({ type: SetAllCartParmsDto })
|
||||||
|
setAllCartParams(
|
||||||
|
@UserId() userId: string,
|
||||||
|
@RestId() restaurantId: string,
|
||||||
|
@Body() setCarDeliveryDto: SetCarDeliveryDto,
|
||||||
|
) {
|
||||||
|
return this.cartService.setCarDelivery(userId, restaurantId, setCarDeliveryDto);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { IsNotEmpty, IsOptional, IsString } from 'class-validator';
|
||||||
|
import { SetCarDeliveryDto } from './set-car-delivery.dto';
|
||||||
|
|
||||||
|
|
||||||
|
export class SetAllCartParmsDto {
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Cart description or notes',
|
||||||
|
required: false,
|
||||||
|
example: 'Please deliver to the back door',
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
description?: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: 'Delivery method ID', example: '01ARZ3NDEKTSV4RRFFQ69G5FAV' })
|
||||||
|
@IsNotEmpty()
|
||||||
|
@IsString()
|
||||||
|
deliveryMethodId!: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: 'User address ID', example: '01ARZ3NDEKTSV4RRFFQ69G5FAV' })
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
addressId?: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: 'Payment method ID', example: '01ARZ3NDEKTSV4RRFFQ69G5FAV' })
|
||||||
|
@IsNotEmpty()
|
||||||
|
@IsString()
|
||||||
|
paymentMethodId!: string;
|
||||||
|
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
carAddress?: SetCarDeliveryDto;
|
||||||
|
}
|
||||||
@@ -22,6 +22,7 @@ import { OrderCouponDetail } from 'src/modules/orders/interface/order.interface'
|
|||||||
import { CouponType } from 'src/modules/coupons/interface/coupon';
|
import { CouponType } from 'src/modules/coupons/interface/coupon';
|
||||||
import { UserWallet } from 'src/modules/users/entities/user-wallet.entity';
|
import { UserWallet } from 'src/modules/users/entities/user-wallet.entity';
|
||||||
import { PaymentMethodEnum } from 'src/modules/payments/interface/payment';
|
import { PaymentMethodEnum } from 'src/modules/payments/interface/payment';
|
||||||
|
import { SetAllCartParmsDto } from '../dto/set-all-cart-params.dto';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CartService {
|
export class CartService {
|
||||||
@@ -34,6 +35,92 @@ export class CartService {
|
|||||||
private readonly couponService: CouponService,
|
private readonly couponService: CouponService,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
|
async setAllCartParams(userId: string, restaurantId: string, params: SetAllCartParmsDto): Promise<Cart> {
|
||||||
|
const { deliveryMethodId, paymentMethodId, addressId, carAddress, description } = params;
|
||||||
|
|
||||||
|
// get existing cart (or throw)
|
||||||
|
const cart = await this.findOneOrFail(userId, restaurantId);
|
||||||
|
|
||||||
|
// If deliveryMethodId is provided -> validate and set it first
|
||||||
|
if (deliveryMethodId) {
|
||||||
|
const deliveryMethod = await this.getEnabledDeliveryMethodOrFail(restaurantId, deliveryMethodId);
|
||||||
|
cart.deliveryMethodId = deliveryMethodId;
|
||||||
|
// clear fields incompatible with the chosen delivery method
|
||||||
|
this.clearFieldsIncompatibleWithDeliveryMethod(cart, deliveryMethod.method);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If addressId is provided -> must have delivery method (either provided above or already on cart)
|
||||||
|
if (addressId) {
|
||||||
|
const effectiveDeliveryMethodId = deliveryMethodId ?? cart.deliveryMethodId;
|
||||||
|
if (!effectiveDeliveryMethodId) {
|
||||||
|
throw new BadRequestException('Delivery method must be set before setting address');
|
||||||
|
}
|
||||||
|
const deliveryMethod = await this.getDeliveryMethodForRestaurantOrFail(restaurantId, effectiveDeliveryMethodId);
|
||||||
|
this.assertDeliveryMethod(deliveryMethod.method, DeliveryMethodEnum.DeliveryCourier, 'Delivery method must be DeliveryCourier');
|
||||||
|
|
||||||
|
const address = await this.getUserAddressOrFail(addressId);
|
||||||
|
if (address.user.id !== userId) {
|
||||||
|
throw new BadRequestException('Address does not belong to the current user');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure cart fields are compatible and set address
|
||||||
|
this.clearFieldsIncompatibleWithDeliveryMethod(cart, DeliveryMethodEnum.DeliveryCourier);
|
||||||
|
cart.userAddress = {
|
||||||
|
address: address.address,
|
||||||
|
latitude: address.latitude,
|
||||||
|
longitude: address.longitude,
|
||||||
|
city: address.city,
|
||||||
|
province: address.province || '',
|
||||||
|
postalCode: address.postalCode || undefined,
|
||||||
|
fullName: `${address.user.firstName || ''} ${address.user.lastName || ''}`.trim(),
|
||||||
|
phone: address.user.phone,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// If carAddress is provided -> must have delivery method DeliveryCar
|
||||||
|
if (carAddress) {
|
||||||
|
const effectiveDeliveryMethodId = deliveryMethodId ?? cart.deliveryMethodId;
|
||||||
|
if (!effectiveDeliveryMethodId) {
|
||||||
|
throw new BadRequestException('Delivery method must be set before setting car delivery');
|
||||||
|
}
|
||||||
|
const deliveryMethod = await this.getDeliveryMethodForRestaurantOrFail(restaurantId, effectiveDeliveryMethodId);
|
||||||
|
this.assertDeliveryMethod(deliveryMethod.method, DeliveryMethodEnum.DeliveryCar, 'Delivery method must be DeliveryCar');
|
||||||
|
|
||||||
|
const user = await this.getUserOrFail(userId);
|
||||||
|
|
||||||
|
// make sure incompatible fields are cleared and set car address (phone comes from user)
|
||||||
|
this.clearFieldsIncompatibleWithDeliveryMethod(cart, DeliveryMethodEnum.DeliveryCar);
|
||||||
|
cart.carAddress = {
|
||||||
|
carModel: carAddress.carModel,
|
||||||
|
carColor: carAddress.carColor,
|
||||||
|
plateNumber: carAddress.plateNumber,
|
||||||
|
phone: user.phone,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// If payment method is provided -> validate and (for wallet) ensure enough balance.
|
||||||
|
if (paymentMethodId) {
|
||||||
|
const paymentMethod = await this.getEnabledPaymentMethodOrFail(restaurantId, paymentMethodId);
|
||||||
|
|
||||||
|
// Recalculate totals first so wallet check uses up-to-date total (delivery method may have changed above).
|
||||||
|
await this.recalculateCartTotals(cart);
|
||||||
|
|
||||||
|
if (paymentMethod.method === PaymentMethodEnum.Wallet) {
|
||||||
|
await this.assertWalletHasEnoughBalance(userId, restaurantId, cart.total);
|
||||||
|
}
|
||||||
|
|
||||||
|
cart.paymentMethodId = paymentMethodId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Description (if provided)
|
||||||
|
if (description !== undefined) {
|
||||||
|
cart.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Final recalculation + save and return cart
|
||||||
|
return this.recalculateAndSaveCart(cart);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get or create cart for user and restaurant
|
* Get or create cart for user and restaurant
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -240,9 +240,6 @@ export class PaymentsService {
|
|||||||
order.status = OrderStatus.PAID;
|
order.status = OrderStatus.PAID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private async getOrCreateLatestPendingPayment(
|
private async getOrCreateLatestPendingPayment(
|
||||||
orderId: string,
|
orderId: string,
|
||||||
params: {
|
params: {
|
||||||
|
|||||||
Reference in New Issue
Block a user