Skip to content

Commit

Permalink
[finishes #187713856] bug fix in adding product to Cart
Browse files Browse the repository at this point in the history
  • Loading branch information
JeanIrad committed Jun 3, 2024
1 parent 1d17fe7 commit 2a6c966
Show file tree
Hide file tree
Showing 10 changed files with 223 additions and 109 deletions.
222 changes: 124 additions & 98 deletions src/controllers/cartController.ts
Original file line number Diff line number Diff line change
@@ -1,110 +1,103 @@
import { Request, Response } from 'express';
import { Cart } from '../database/models/cart';
import { Product, ProductAttributes } from '../database/models/Product';
import { Product } from '../database/models/Product';
import User from '../database/models/user';
import { sendInternalErrorResponse, validateFields } from '../validations';
import { sendInternalErrorResponse } from '../validations';
import sequelize from '../database/models';
import logger from '../logs/config';
import { Size } from '../database/models/Size';
import CartsProducts from '../database/models/cartsProducts';
import { validateFields } from '../validations';
import { Transaction } from 'sequelize';
import { checkStockSize, updateStock } from '../helpers/stockSizeManagers';

const addCartItem = async (req: Request, res: Response): Promise<void> => {
const { productId, sizeId } = req.body;
const { productId, sizeId, quantity } = req.body;
const { id: userId } = req.user as User;
let transaction: Transaction | null = null;
// validate the required fields
const requiredFields = validateFields(req, ['productId', 'sizeId']);
if (requiredFields.length > 0) {
res.status(400).json({ ok: false, message: `Required fields: ${requiredFields} ` });
return;
}
try {
// Validate request body
const missingFields = validateFields(req, ['productId', 'sizeId']);
if (missingFields.length > 0) {
res.status(400).json({
ok: false,
message: `Following required fields are missing: ${missingFields.join(', ')}`,
});
// check if the product exist, given the productId
transaction = await sequelize.transaction();
const product = await Product.findByPk(productId, { transaction });
if (!product) {
res.status(404).json({ ok: false, message: 'Product not found' });
return;
}

// Start transaction
const transaction = await sequelize.transaction();

try {
// Check if the product exists
let size: Size | null = null;
// check if the product size exist
size = await Size.findOne({ where: { productId, id: sizeId }, transaction });
if (!size) {
res.status(404).json({ ok: false, message: 'Size not found for this product' });
return;
}
const stock = await checkStockSize(sizeId, productId, quantity);
if (stock <= 0) {
const product = await Product.findByPk(productId, {
include: {
model: Size,
as: 'sizes',
where: { id: sizeId },
attributes: ['quantity', 'price'],
},
transaction,
});
if (!product) {
res.status(404).json({ ok: false, message: 'Product was not found in stock!' });
return;
}

// Check if the item already exists in the cart
const cartItem: any = await Cart.findOne({
include: [
{
model: User,
as: 'user',
attributes: ['id', 'firstName', 'lastName', 'email', 'phoneNumber', 'gender'],
},
{
model: Product,
as: 'products',
through: { attributes: ['quantity'] },
},
],
where: { userId },
transaction: transaction,
include: [{ model: Size, as: 'sizes', where: { id: sizeId }, attributes: ['quantity'] }],
});

if (cartItem) {
// Check if the product being added is already in the cart
const existingProduct = cartItem.products.find((product: ProductAttributes) => product.id === productId);
if (existingProduct) {
// If the product already exists in the cart, increment the quantity in the join table
await sequelize.query(
'UPDATE "CartsProducts" SET quantity = quantity + 1 WHERE "cartId" =? AND "productId" =?',
{
replacements: [cartItem.id, product.id],
}
);
} else {
// If the product doesn't exist in the cart, add it to the cart
await cartItem.addProducts(product, {
through: { quantity: 1 },
transaction: transaction,
});
}
res
.status(404)
.json({ ok: false, message: `Our stock has ${product?.sizes[0].quantity} product(s) of this size only!` });
return;
}
await updateStock(sizeId, productId, stock);
// Find the cart for the current user
const cart = await Cart.findOne({ where: { userId }, transaction });
if (cart) {
// Check if product already exists in cart (optional for quantity update)
let existingCartItem: CartsProducts | null = null;
if (sizeId) {
existingCartItem = await CartsProducts.findOne({
where: { cartId: cart.id, productId: productId, sizeId: sizeId },
transaction,
});
} else {
// If the cart is empty, create a new cart item
const newCartItem: any = await Cart.create({ userId }, { transaction });
await newCartItem.addProducts(product, {
through: { quantity: 1 },
existingCartItem = await CartsProducts.findOne({
where: { cartId: cart.id, productId },
transaction,
});
}
// Update quantity if product already exists, otherwise create a new entry
if (existingCartItem) {
existingCartItem.quantity += quantity ?? 1;
await existingCartItem.save({ transaction });
} else {
await CartsProducts.create({
cartId: cart.id,
productId,
sizeId,
quantity: quantity ?? 1,
});
}
} else {
const newCart = await Cart.create(
{
userId,
},
{ transaction }
);

// Commit the transaction
await transaction.commit();
res.status(200).json({ ok: true, message: 'Product added to cart successfully' });
} catch (error) {
// Rollback the transaction if an error occurs
await transaction.rollback();
throw error;
await CartsProducts.create({ cartId: newCart.id, productId, sizeId, quantity: quantity ?? 1 }, { transaction });
}
await transaction.commit();
res.status(200).json({ message: 'Product added to cart successfully' });
return;
} catch (error) {
// Handle any unexpected errors
console.error(error);
// res.status(500).json({ ok: false, message: 'An unexpected error occurred' });
sendInternalErrorResponse(res, error);
await transaction?.rollback();
sendInternalErrorResponse(res, error instanceof Error ? error.message : error);
return;
}
};

//updating an item

const updateCartItem = async (req: Request, res: Response): Promise<void> => {
const { productId, quantity } = req.body;
const { productId, quantity, sizeId } = req.body;
const { id: userId } = req.user as User;

const transaction = await sequelize.transaction();
Expand All @@ -119,10 +112,13 @@ const updateCartItem = async (req: Request, res: Response): Promise<void> => {
return;
}
if (quantity >= 1) {
await sequelize.query('UPDATE "CartsProducts" SET quantity =? WHERE "cartId" =? AND "productId" =?', {
replacements: [quantity, cartItem.id, productId],
transaction,
});
await sequelize.query(
'UPDATE "CartsProducts" SET quantity =? WHERE "cartId" =? AND "productId" =? and "sizeId" =?',
{
replacements: [quantity, cartItem.id, productId, sizeId],
transaction,
}
);

await transaction.commit();
res.status(200).json({ ok: true, message: 'Cart updated successfully' });
Expand Down Expand Up @@ -154,7 +150,7 @@ const getCartItems = async (req: Request, res: Response): Promise<void> => {
{
model: Size,
as: 'sizes',
attributes: ['quantity', 'price'],
attributes: ['size', 'price'],
},
],
},
Expand All @@ -165,23 +161,52 @@ const getCartItems = async (req: Request, res: Response): Promise<void> => {
res.status(404).json({ ok: false, message: 'Cart not found' });
return;
}

const products = cart.products.map((product: any) => {
const cartsProducts = await CartsProducts.findAll({ where: { cartId: cart.id }, transaction });
const allProducts = Promise.all(
cartsProducts.map(async item => {
return {
product: await Product.findOne({
where: { id: item.productId },
attributes: ['id', 'name', 'sellerId', 'images'],
include: [
{
model: Size,
as: 'sizes',
where: {
id: item.sizeId,
},
attributes: ['price', 'size', 'id'],
},
],
transaction,
}),
quantity: item.quantity,
};
})
);
if ((await allProducts).length < 1) {
res.status(404).json({ ok: false, message: 'No Product in the Cart' });
return;
}
const sendProducts = [...(await allProducts)].map(item => {
if (!item.product) return null;
const { id, name, sellerId, images, sizes } = item.product;
return {
cartId: cart.id,
id: product.id,
name: product.name,
description: product.description,
quantity: product.CartsProducts,
image: product.images[0],
sellerId: product.sellerId,
createdAt: product.createdAt,
id,
name,
sizes,
sellerId,
image: images[0],
quantity: item.quantity,
};
});
res.status(200).json({ cart: products });
await transaction.commit();
res.status(200).json({ ok: true, cartId: cart.id, cartProducts: sendProducts });
return;
} catch (error) {
await transaction.rollback();
throw error;
logger.error(error);
sendInternalErrorResponse(res, error instanceof Error ? error.message : error);
}
};

Expand All @@ -205,6 +230,7 @@ const clearCart = async (req: Request, res: Response): Promise<void> => {
await sequelize.query('DELETE FROM "CartsProducts" WHERE "cartId" =?', { replacements: [cart.id], transaction });
await transaction.commit();
res.status(200).json({ ok: true, message: 'Cart cleared successfully' });
return;
} catch (error) {
await transaction.rollback();
logger.error(error);
Expand Down
2 changes: 1 addition & 1 deletion src/controllers/productsController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ export const getAllProduct = async (req: Request, res: Response) => {
);
return {
...product.toJSON(),
Sizes: validSizes,
// Sizes: validSizes,
};
});

Expand Down
2 changes: 2 additions & 0 deletions src/controllers/userController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ export const editUserRole = async (req: Request, res: Response) => {
res.status(404).json({ ok: false, error: 'User request not found' });
return;
}
const role = await Role.findByPk(roleId);
if (!role) return res.status(404).json({ ok: false, message: `There is no Role with this id: ${roleId}` });
await user.update({ RoleId: roleId });
await requestedUser.update({ status: 'approved' });

Expand Down
10 changes: 10 additions & 0 deletions src/database/migrations/20240514174638-cart-product.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ module.exports = {
onUpdate: 'CASCADE',
onDelete: 'CASCADE',
},
sizeId: {
type: Sequelize.UUID,
defaultValue: Sequelize.UUIDV4,
references: {
model: 'sizes',
key: 'id',
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE',
},
quantity: {
type: Sequelize.INTEGER,
allowNull: false,
Expand Down
2 changes: 0 additions & 2 deletions src/database/models/Product.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { UUIDV4 } from 'sequelize';
import { Category } from './Category';
import { Size } from './Size';
import User from './user';
import Cart from './cart';

export interface ProductAttributes {
id?: string;
Expand Down Expand Up @@ -79,4 +78,3 @@ Product.init(
Product.belongsTo(Category, { foreignKey: 'categoryId' });
Product.belongsTo(User, { foreignKey: 'sellerId', as: 'user' });
Product.hasMany(Size, { foreignKey: 'productId', as: 'sizes' });
// Product.hasMany(Cart, { foreignKey: 'productId', as: 'carts' });
2 changes: 0 additions & 2 deletions src/database/models/Size.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Model, Optional, DataTypes, UUIDV4 } from 'sequelize';
import sequelize from './index';

export interface SizeAttributes {
id: number;
size?: string;
Expand Down Expand Up @@ -31,7 +30,6 @@ Size.init(
type: DataTypes.UUID,
defaultValue: UUIDV4,
primaryKey: true,
autoIncrement: true,
},
size: {
type: DataTypes.STRING,
Expand Down
Loading

0 comments on commit 2a6c966

Please sign in to comment.