import { Injectable, HttpException, HttpStatus, Inject, forwardRef } from "@nestjs/common"; import { InjectModel } from "@nestjs/sequelize"; import { Cart } from "./entities/cart.entity"; import { Product } from "src/products/entities/product.entity"; import { WalletService } from "src/wallet/wallet.service"; import { InvoiceService } from "src/invoice/invoice.service"; import { Invoice } from "src/invoice/entities/invoice.entity"; @Injectable() export class CartService { constructor( @InjectModel(Cart) private readonly cartModel: typeof Cart, @InjectModel(Invoice) private readonly invoiceModel: typeof Invoice, @InjectModel(Product) private readonly productModel: typeof Product, private readonly walletService: WalletService, @Inject(forwardRef(() => InvoiceService)) private invoiceService: InvoiceService, ) {} //create a cart and add item to cart async createAndAddItemToCart(addToCartDto: { userId: number; productId: number; quantity: number }): Promise<{ message: string; cartItem: Cart }> { const { userId, productId, quantity } = addToCartDto; if (!userId || !productId || !quantity || isNaN(Number(quantity)) || Number(quantity) <= 0) { throw new HttpException("Invalid parameters: userId, productId, and a positive quantity are required.", HttpStatus.BAD_REQUEST); } const product = await this.productModel.findByPk(productId); if (!product) { throw new HttpException("Product not found!", HttpStatus.NOT_FOUND); } let invoice = await this.invoiceModel.findOne({ where: { userId, status: "pending" } }); if (!invoice) { invoice = await this.invoiceService.createInvoiceFromCart(userId); } const invoiceId = invoice.id; let cart = await this.cartModel.findOne({ where: { userId, productId, status: "open" } }); console.log(cart); if (!cart) { cart = await this.cartModel.create({ userId, productId, invoiceId, quantity, productPrice: product.price, status: "open", }); await cart.save(); } else { cart.quantity += Number(quantity); await cart.save(); } await this.invoiceService.updateTotalPayment(userId); return { message: cart.id ? "Product quantity updated in cart successfully!" : "Product added to cart successfully!", cartItem: cart, }; } // Get user's cart async getUserCart(userId: number): Promise<{ cartItems: Cart[]; totalPrice: number }> { if (!userId) { throw new HttpException("User ID is required.", HttpStatus.BAD_REQUEST); } const cartItems = await this.cartModel.findAll({ where: { userId }, include: [ { model: Product, attributes: [], }, ], }); if (!cartItems || cartItems.length === 0) { throw new HttpException("No cart items found for the specified user.", HttpStatus.NOT_FOUND); } const totalPrice = cartItems.reduce((sum, item) => sum + (Number(item.productPrice * item.quantity) || 0), 0); return { cartItems, totalPrice }; } // Update cart item quantity async updateCart(userId: number, productId: number, quantity: number): Promise { const cartItem = await this.cartModel.findOne({ where: { userId, productId } }); if (!cartItem) { throw new HttpException("Product not found in the cart.", HttpStatus.NOT_FOUND); } cartItem.quantity = quantity; await cartItem.save(); await this.invoiceService.updateTotalPayment(userId); return cartItem; } // Remove an item from cart async removeFromCart(userId: number, productId: number): Promise<{ message: string }> { const cartItem = await this.cartModel.findOne({ where: { userId, productId } }); if (!cartItem) { throw new HttpException("Product not found in the cart.", HttpStatus.NOT_FOUND); } await cartItem.destroy(); await this.invoiceService.updateTotalPayment(userId); return { message: "Item deleted from your cart successfully." }; } //delete whole cart async clearCart(userId: number): Promise { await this.cartModel.destroy({ where: { userId } }); } //order(clearCart disable) async processOrder(userId: number, totalAmount: number): Promise<{ message: string; invoice: Invoice }> { try { const carts = await this.cartModel.findAll({ where: { userId, status: "open" } }); if (!carts || carts.length === 0) { throw new HttpException("No open carts found for this user.", HttpStatus.NOT_FOUND); } let invoice: Invoice | null = null; for (const cart of carts) { const invoiceId = cart.invoiceId; invoice = await this.invoiceModel.findOne({ where: { id: invoiceId, userId } }); if (invoice && invoice.status === "paid") { return { message: `Order for cart ID ${cart.id} has already been processed.`, invoice, }; } } await this.walletService.processPayment(userId, totalAmount); for (const cartItem of carts) { const { productId, quantity } = cartItem; const product = await this.productModel.findOne({ where: { id: productId } }); if (!product) { throw new HttpException(`Product with ID ${productId} not found.`, HttpStatus.NOT_FOUND); } if (product.quantity < quantity) { throw new HttpException(`Insufficient stock for product ID ${productId}.`, HttpStatus.BAD_REQUEST); } product.quantity -= quantity; await product.save(); } for (const cart of carts) { cart.status = "closed"; await cart.save(); } if (invoice) { invoice.status = "paid"; await invoice.save(); } return { message: "Order processed successfully!", invoice }; } catch (error) { console.error(error); if (error instanceof HttpException) { throw error; } else { throw new HttpException(`An error occurred while processing the order: ${error.message}`, HttpStatus.INTERNAL_SERVER_ERROR); } } } }