|
|
|
@ -1,15 +1,25 @@ |
|
|
|
|
import { Injectable } from "@nestjs/common"; |
|
|
|
|
import { InjectModel } from "@nestjs/sequelize"; |
|
|
|
|
import { Product } from "./entities/product.entity"; |
|
|
|
|
import { CreateProductDto } from "./dto/create-product.dto"; |
|
|
|
|
import { UpdateProductDto } from "./dto/update-product.dto"; |
|
|
|
|
import { CreateProductDto } from "./dto/products/create-product.dto"; |
|
|
|
|
import { UpdateProductDto } from "./dto/products/update-product.dto"; |
|
|
|
|
import { Op } from "sequelize"; |
|
|
|
|
import { HttpException, HttpStatus } from "@nestjs/common"; |
|
|
|
|
import { Cart } from "./entities/cart.entity"; |
|
|
|
|
import { Invoice } from "src/invoice/entities/invoice.entity"; |
|
|
|
|
import { InvoiceService } from "src/invoice/invoice.service"; |
|
|
|
|
import { WalletService } from "src/wallet/WalletService"; |
|
|
|
|
|
|
|
|
|
@Injectable() |
|
|
|
|
export class ProductsService { |
|
|
|
|
constructor(@InjectModel(Product) private readonly productModel: typeof Product) {} |
|
|
|
|
|
|
|
|
|
constructor( |
|
|
|
|
@InjectModel(Product) private readonly productModel: typeof Product, |
|
|
|
|
@InjectModel(Cart) private readonly cartModel: typeof Cart, |
|
|
|
|
@InjectModel(Invoice) private readonly invoiceModel: typeof Invoice, |
|
|
|
|
private invoiceService: InvoiceService, |
|
|
|
|
private walletService: WalletService, |
|
|
|
|
) {} |
|
|
|
|
///////////////////////////////////////////products//////////////////////////////////////////////
|
|
|
|
|
// create a new product
|
|
|
|
|
async create(createProductDto: CreateProductDto): Promise<Product> { |
|
|
|
|
try { |
|
|
|
@ -27,7 +37,7 @@ export class ProductsService { |
|
|
|
|
return newProduct; |
|
|
|
|
} catch (error) { |
|
|
|
|
if (error instanceof HttpException) { |
|
|
|
|
throw error |
|
|
|
|
throw error; |
|
|
|
|
} |
|
|
|
|
throw new HttpException("An error occurred while creating or updating the product.", HttpStatus.INTERNAL_SERVER_ERROR); |
|
|
|
|
} |
|
|
|
@ -161,7 +171,7 @@ export class ProductsService { |
|
|
|
|
return product; |
|
|
|
|
} catch (error) { |
|
|
|
|
if (error instanceof HttpException) { |
|
|
|
|
throw error |
|
|
|
|
throw error; |
|
|
|
|
} |
|
|
|
|
throw new HttpException("An error occurred while updating the product.", HttpStatus.INTERNAL_SERVER_ERROR); |
|
|
|
|
} |
|
|
|
@ -187,4 +197,203 @@ export class ProductsService { |
|
|
|
|
throw new HttpException("An unexpected error occurred while deleting the product.", HttpStatus.INTERNAL_SERVER_ERROR); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
////////////////////////////////////////////cart/////////////////////////////////////////////////
|
|
|
|
|
//create and add item to a 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); |
|
|
|
|
} |
|
|
|
|
if (product.quantity < quantity) { |
|
|
|
|
throw new HttpException("Product quantity insufficient!", HttpStatus.CONFLICT); |
|
|
|
|
} |
|
|
|
|
try { |
|
|
|
|
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" } }); |
|
|
|
|
|
|
|
|
|
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, |
|
|
|
|
}; |
|
|
|
|
} catch (error) { |
|
|
|
|
if (error instanceof HttpException) { |
|
|
|
|
throw error; |
|
|
|
|
} |
|
|
|
|
throw new HttpException("An unexpected error occurred while adding the product to cart. Please try again later.", HttpStatus.INTERNAL_SERVER_ERROR); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// Get user's cart
|
|
|
|
|
async getUserOpenCart(userId: number): Promise<{ cartItems: Cart[]; totalPrice: number }> { |
|
|
|
|
if (!userId) { |
|
|
|
|
throw new HttpException("User ID is required.", HttpStatus.BAD_REQUEST); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
const cartItems = await this.cartModel.findAll({ |
|
|
|
|
where: { userId, status: "open" }, |
|
|
|
|
include: [ |
|
|
|
|
{ |
|
|
|
|
model: Product, |
|
|
|
|
attributes: ["name", "price"], |
|
|
|
|
}, |
|
|
|
|
], |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
if (!cartItems || cartItems.length === 0) { |
|
|
|
|
return { cartItems: [], totalPrice: 0 }; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const totalPrice = cartItems.reduce((sum, item) => { |
|
|
|
|
return sum + (Number(item.productPrice) * item.quantity || 0); |
|
|
|
|
}, 0); |
|
|
|
|
|
|
|
|
|
return { cartItems, totalPrice }; |
|
|
|
|
} catch (error) { |
|
|
|
|
if (error instanceof HttpException) { |
|
|
|
|
throw error; |
|
|
|
|
} |
|
|
|
|
throw new HttpException("An unexpected error occurred while fetching the cart. Please try again later.", HttpStatus.INTERNAL_SERVER_ERROR); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// Update cart item quantity
|
|
|
|
|
async updateCart(userId: number, productId: number, quantity: number): Promise<Cart> { |
|
|
|
|
const cartItem = await this.cartModel.findOne({ where: { userId, productId, status: "open" } }); |
|
|
|
|
|
|
|
|
|
if (!cartItem) { |
|
|
|
|
throw new HttpException("Product not found in the cart.", HttpStatus.NOT_FOUND); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const product = await this.productModel.findByPk(productId); |
|
|
|
|
|
|
|
|
|
if (!product) { |
|
|
|
|
throw new HttpException("Product not found.", HttpStatus.NOT_FOUND); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (product.quantity < quantity) { |
|
|
|
|
throw new HttpException("Insufficient product quantity.", HttpStatus.CONFLICT); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
cartItem.quantity = quantity; |
|
|
|
|
await cartItem.save(); |
|
|
|
|
await this.invoiceService.updateTotalPayment(userId); |
|
|
|
|
return cartItem; |
|
|
|
|
} catch (error) { |
|
|
|
|
if (error instanceof HttpException) { |
|
|
|
|
throw error; |
|
|
|
|
} |
|
|
|
|
throw new HttpException("An unexpected error occurred while updating the cart. Please try again later.", HttpStatus.INTERNAL_SERVER_ERROR); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// Remove an item from cart
|
|
|
|
|
async removeFromCart(userId: number, productId: number): Promise<{ message: string; cartItem: Cart }> { |
|
|
|
|
const cartItem = await this.cartModel.findOne({ where: { userId, productId, status: "open" } }); |
|
|
|
|
if (!cartItem) { |
|
|
|
|
throw new HttpException("Product not found in the cart.", HttpStatus.NOT_FOUND); |
|
|
|
|
} |
|
|
|
|
try { |
|
|
|
|
await cartItem.destroy(); |
|
|
|
|
await this.invoiceService.updateTotalPayment(userId); |
|
|
|
|
return { message: "Item deleted from your cart successfully.", cartItem }; |
|
|
|
|
} catch (error) { |
|
|
|
|
if (error instanceof HttpException) { |
|
|
|
|
throw error; |
|
|
|
|
} |
|
|
|
|
throw new HttpException("An unexpected error occurred while removing the item from the cart. Please try again later.", HttpStatus.INTERNAL_SERVER_ERROR); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
//delete whole cart by user
|
|
|
|
|
async clearCart(userId: number) { |
|
|
|
|
await this.cartModel.destroy({ |
|
|
|
|
where: { userId, status: "open" }, |
|
|
|
|
}); |
|
|
|
|
return { message: "Cart cleared successfully" }; |
|
|
|
|
}//order
|
|
|
|
|
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); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|