Untitled

mail@pastecode.io avatar
unknown
plain_text
16 days ago
20 kB
1
Indexable
Never
// controllers/productController.js
const mongoose = require('mongoose');
const Product = require('../Models/productModel.js');
const SubCategory = require('../Models/subcategoryModel.js');
const Group = require('../Models/groupModel.js');
const Brand = require('../Models/brandModel.js');
const Category = require('../Models/categoryModel.js');
const User = require('../Models/userModel.js');
const Image = require('../Models/imageModel.js');
const catchAsync = require('../utils/catchAsync.js');
const AppError = require('../utils/appError.js');
const multer = require('multer');
const { isValidObjectId } = require('mongoose');

const defaultImage = '/img/products/default-product-image.png'; // Chemin vers l'image par défaut

const upload = multer();

exports.uploadImages = upload.array('images');

const isImage = (image) => {
  if (image == 'image/png' || image == 'image/jpeg' || image == 'image/jpg') {
    return true;
  }

  return false;
};

exports.addProduct = catchAsync(async (req, res, next) => {
  let {
    sku,
    quantity,
    name,
    description,
    categories,
    subcategories = [],
    groups = [],
    brand,
    tags,
    pricing,
    visibility
  } = req.body;

  quantity = parseInt(quantity);
  const skuRegex = /^[a-zA-Z0-9_-]{4,16}$/;

  visibility = visibility === 'true';

  // Validation checks
  if (!sku || !skuRegex.test(sku)) {
    return next(new AppError('Invalid SKU format', 400));
  }
  if (!quantity || typeof quantity !== 'number' || quantity < 0) {
    return next(new AppError('Invalid quantity format', 400));
  }
  if (!categories || !Array.isArray(categories) || !categories.length) {
    return next(new AppError('Categories array cannot be empty', 400));
  }

  const existingProduct = await Product.findOne({ sku });
  if (existingProduct) {
    return next(new AppError('Product with the same SKU already exists', 400));
  }

  // Processing images
  const imageDocuments = req.body.images.map((base64Str) => ({ image: new Buffer.from(base64Str, 'base64') }));
  const imageInsertionResults = await Image.insertMany(imageDocuments);
  const imgs = imageInsertionResults.map((doc) => doc._id);

  // Filter out any invalid ObjectIds from subcategories and groups
  const validSubcategories = subcategories.filter((subcatId) => isValidObjectId(subcatId));
  const validGroups = groups.filter((groupId) => isValidObjectId(groupId));

  // Validate brand if provided
  let validBrand;
  if (brand && isValidObjectId(brand)) {
    const existingBrand = await Brand.findById(brand);
    if (!existingBrand) {
      return next(new AppError('Brand not found', 404));
    }
    validBrand = brand;
  }

  // Create and save the new product
  const product = new Product({
    sku,
    quantity,
    name,
    description,
    images: imgs,
    categories,
    subcategories: validSubcategories,
    groups: validGroups,
    brand: validBrand,
    tags,
    pricing,
    visibility
  });

  await product.save();

  res.status(201).json({
    success: true,
    message: 'The product has been successfully added',
    product
  });
});

exports.updateProduct = catchAsync(async (req, res, next) => {
  let body = JSON.parse(JSON.stringify(req.body));
  const productId = req.params.id;
  console.log(productId);
  const session = await mongoose.startSession();
  session.startTransaction();

  try {
    if (!isValidObjectId(productId)) {
      return next(new AppError('Invalid product ID', 400));
    }

    const product = await Product.findById(productId);
    if (!product) {
      return next(new AppError('Product not found', 400));
    }

    let { sku, quantity, name, description, categories, subcategories, groups, brand, pricing, images } = body;

    // Update SKU if provided and unique
    if (sku && sku !== product.sku) {
      const existingProduct = await Product.findOne({ sku });
      if (existingProduct && existingProduct._id.toString() !== productId) {
        return next(new AppError('Product with the same SKU already exists', 400));
      }
      product.sku = sku;
    }

    if (quantity) {
      product.quantity = parseInt(quantity);
    }

    if (name) {
      product.name = name;
    }

    if (description) {
      product.description = description;
    }

    if (categories) {
      const ids = await checkCategories(categories, next);
      console.log(ids);
      product.categories = ids;
    }
    if (subcategories) {
      const ids = await checkSubCategories(subcategories, categories, next);
      product.subcategories = ids;
    }
    if (groups) {
      const ids = await checkGroups(groups, subcategories, next);
      product.groups = ids;
    }

    if (pricing) {
      product.pricing = pricing; // Make sure to validate or adjust pricing structure as needed
    }

    // Update brand if valid ObjectId
    if (brand && isValidObjectId(brand)) {
      const existingBrand = await Brand.findById(brand);
      if (!existingBrand) {
        return next(new AppError('Brand not found', 404));
      }
      product.brand = brand;
    }

    // Process images if provided
    if (images && images.length) {
      const imageDocuments = images.map((base64Str) => ({ image: new Buffer.from(base64Str, 'base64') }));
      await Image.deleteMany({ _id: { $in: product.images } }, { session });
      const imageInsertionResults = await Image.insertMany(imageDocuments, { session });
      product.images = imageInsertionResults.map((doc) => doc._id);
    }

    await product.save();
    await session.commitTransaction();
    session.endSession();

    res.status(200).json({
      message: 'Product updated successfully',
      product
    });
  } catch (error) {
    await session.abortTransaction();
    session.endSession();
    return next(error);
  }
});

exports.viewProduct = catchAsync(async (req, res, next) => {
  const productId = req.params.id;

  // Validate the productId
  if (!isValidObjectId(productId)) {
    return res.status(400).json({ message: 'Invalid product ID' });
  }
  const pathsAndFields = [
    { path: 'subcategories', select: 'subcategory' },
    { path: 'categories', select: 'category' },
    { path: 'groups', select: 'group' },
    { path: 'brand', select: 'brand' },
    { path: 'offer', select: ['discount','status'] },
]; 

  const product = await Product.findById(productId).populate(pathsAndFields).exec();

  if (!product) {
    return res.status(404).json({ message: 'Product not found' });
  }

  res.status(200).json({ message: 'Product fetched successfully', product: product });
});
exports.deleteProduct = catchAsync(async (req, res, next) => {
  const productId = req.params.id;

  // First, find the product to get the image IDs
  const productToDelete = await Product.findById(productId);
  if (!productToDelete) {
    return res.status(404).json({ message: 'Product not found' });
  }

  // Delete the product
  await Product.findByIdAndDelete(productId);

  // Now delete each associated image
  if (productToDelete.images && productToDelete.images.length > 0) {
    for (const imageId of productToDelete.images) {
      // Assuming imageId is the actual ID, if it's a reference to an image object, you might need to adjust
      await Image.findByIdAndDelete(imageId);
    }
  }

  res.status(200).json({ message: 'Product and associated images deleted successfully' });
});

exports.getProducts = catchAsync(async (req, res, next) => {
  const requestedPage = parseInt(req.query.page) || 1; // Requested page number
  const productsPerPage = parseInt(req.query.limit) || 10; // Requested page number

  // Count total number of products
  const count = await Product.countDocuments();

  // Get products for the requested page
  const products = await Product.find().populate({ path: 'offer', select: ['discount','status'] })
    .skip((requestedPage - 1) * productsPerPage)
    .limit(productsPerPage)
    .select('-_v'); // Exclude the '_v' field from the JSON response

  res.status(200).json({
    count,
    currentPage: requestedPage,
    productsPerPage,
    products
  });
});

// Route pour la recherche SKU
exports.searchbySku = async (req, res) => {
  try {
    // Extract SKU and pagination parameters from query parameters
    const { sku } = req.query;
    let { page, limit } = req.query;

    // Check if SKU is provided
    if (!sku) {
      return res.status(400).json({ error: 'SKU is required' });
    }

    // Default to page 1 if not provided or invalid and limit to 10 if not provided or invalid
    page = page ? parseInt(page, 10) : 1;
    limit = limit ? parseInt(limit, 10) : 10;

    // Calculate the skip value
    const skip = (page - 1) * limit;

    // Search for products in the database based on SKU with pagination
    const products = await Product.find({ sku: { $regex: '^' + sku, $options: 'i' } })
      .populate({ path: 'offer', select: ['discount','status'] })
      .skip(skip)
      .limit(limit);

    // Find total number of products to calculate total pages
    const totalProducts = await Product.countDocuments({ sku: { $regex: '^' + sku, $options: 'i' } });
    const totalPages = Math.ceil(totalProducts / limit);

    // Handle case where no products are found with the specified SKU
    if (products.length === 0) {
      return res.status(404).json({ message: 'No products found with the specified SKU' });
    }

    // Return the found products along with pagination data
    res.json({
      products,
      pagination: {
        totalProducts,
        totalPages,
        currentPage: page,
        limit
      }
    });
  } catch (error) {
    console.error(error);
    res.status(500).json({ error: 'Internal Server Error' });
  }
};

exports.productInStock = catchAsync(async (req, res, next) => {
  if (req.body.stock == 1) {
    let products = await Product.find({ quantity: { $gt: 0 } })
    .populate({ path: 'offer', select: ['discount','status'] });
    if (products) {
      res.status(200).json({
        products
      });
    } else {
      return next(new AppError('There are no products with this filter', 400));
    }
  } else if (req.body.stock == 0) {
    let products = await Product.find({ quantity: { $eq: 0 } })
        .populate({ path: 'offer', select: ['discount','status'] });
    if (products) {
      res.status(200).json({
        products
      });
    } else {
      return next(new AppError('There are no products with this filter', 400));
    }
  } else {
    return next(new AppError('invalad value', 400));
  }
});

exports.productVisibility = catchAsync(async (req, res, next) => {
  if (req.body.visibility == 1) {
    let products = await Product.find({ visibility: true })
    .populate({ path: 'offer', select: ['discount','status'] });;
    if (products) {
      res.status(200).json({
        products
      });
    } else {
      return next(new AppError('There are no products with this filter', 400));
    }
  }
  if (req.body.visibility == 0) {
    let products = await Product.find({ visibility: false })
    .populate({ path: 'offer', select: ['discount','status'] });
    if (products) {
      res.status(200).json({
        products
      });
    } else {
      return next(new AppError('There are no products with this filter', 400));
    }
  }
});

exports.productByCategory = catchAsync(async (req, res, next) => {
  let category = await Category.findById(req.body.id_category);
  if (category) {
    const products = await Product.find({ categories: req.body.id_category })
                       .populate({ path: 'offer', select: ['discount','status'] });

    if (products) {
      res.status(200).json({
        products
      });
    } else {
      return next(new AppError('this product no existe', 400));
    }
  } else {
    return next(new AppError('this category no existe', 400));
  }
});

exports.deleteProducts = catchAsync(async (req, res, next) => {
  const productIds = req.body.products;

  if (!Array.isArray(productIds) || productIds.length === 0) {
    return next(new AppError('Invalid or empty product IDs array', 400));
  }

  // Convert string IDs to mongoose ObjectId
  const ids = productIds.map((id) => mongoose.Types.ObjectId(id));

  // Find products to retrieve their images before deletion
  const products = await Product.find({ _id: { $in: ids } });

  if (products.length === 0) {
    return next(new AppError('No products found for deletion', 404));
  }

  // Collect all image IDs to delete
  const imagesToDelete = products.reduce((acc, product) => {
    if (product.images && product.images.length) {
      acc.push(...product.images.map((imageId) => mongoose.Types.ObjectId(imageId)));
    }
    return acc;
  }, []);

  // Delete images if there are any to delete
  if (imagesToDelete.length > 0) {
    await Image.deleteMany({ _id: { $in: imagesToDelete } });
  }

  // Finally, delete the products
  await Product.deleteMany({ _id: { $in: ids } });

  res.status(200).json({
    message: 'Products and associated images deleted successfully',
    deletedCount: products.length
  });
});

exports.productsVisibilityChange = catchAsync(async (req, res, next) => {
  const productIds = req.body.products;
  let visibility = req.body.visibility;
  let obj = {};
  if (visibility) {
    if (visibility == 'true') {
      obj.visibility = true;
    } else if (visibility == 'false') {
      obj.visibility = false;
    } else {
      return next(new AppError('invalid visibility value', 400));
    }
  } else {
    return next(new AppError('visiility required', 400));
  }
  if (productIds) {
    if (Array.isArray(productIds)) {
      let ids = new Array();
      for (let j = 0; j < productIds.length; j++) {
        try {
          ids.push(new mongoose.Types.ObjectId(productIds[j]));
        } catch (e) {
          return next(new AppError('Invalid id.', 401));
        }
      }
      const productsapdated = await Product.updateMany({ _id: { $in: ids } }, obj);
      if (productsapdated) {
        res.status(200).json({
          message: 'Product visibility updated successfully',
          productsapdated
        });
      } else {
        return next(new AppError('no products', 400));
      }
    } else {
      return next(new AppError('invalid format for products array', 400));
    }
  }
});

exports.productsStockStatusChange = catchAsync(async (req, res, next) => {
  const productIds = req.body.products;
  let status = req.body.stockstatus;
  let obj = {};
  if (status) {
    if (status == true) {
      obj.quantity = 0;
    } else {
      return next(new AppError('invalid status value', 400));
    }
  } else {
    return next(new AppError('status required', 400));
  }
  console.log(productIds);
  if (productIds) {
    if (Array.isArray(productIds)) {
      let ids = new Array();
      for (let j = 0; j < productIds.length; j++) {
        try {
          ids.push(new mongoose.Types.ObjectId(productIds[j]));
        } catch (e) {
          return next(new AppError('Invalid id.', 401));
        }
      }
      const productsapdated = await Product.updateMany({ _id: { $in: ids } }, obj);
      if (productsapdated) {
        res.status(200).json({
          message: 'Products stockstatus changed successfully',
          productsapdated
        });
      } else {
        return next(new AppError('no products', 400));
      }
    } else {
      return next(new AppError('invalid format for products array', 400));
    }
  }
});

exports.productFilter = catchAsync(async (req, res, next) => {
  let { visibility, status, id_category } = req.body;
  const page = parseInt(req.query.page);
  const limit = parseInt(req.query.limit);

  try {
    var Id = new mongoose.Types.ObjectId(id_category);
  } catch (e) {
    return next(new AppError('Invalid id.', 401));
  }

  let obj = {};
  if (visibility) {
    if (visibility == 'true') {
      obj.visibility = true;
    } else if (visibility == 'false') {
      obj.visibility = false;
    } else {
      return next(new AppError('invalid visibility value', 400));
    }
  }

  if (status) {
    if (status == 'true') {
      obj.quantity = { $gt: 0 };
    } else if (status == 'false') {
      obj.quantity = 0;
    } else {
      return next(new AppError('invalid visibility value', 400));
    }
  }
  if (id_category) {
    let category = await Category.findById(Id);
    if (category) {
      obj.categories = Id;
    } else {
      return next(new AppError('invalid categorie value', 400));
    }
  }

  const paths = ['subcategories', 'categories', 'groups', 'brand'];
  const pathsAndFields = [
    { path: 'subcategories', select: 'subcategory' },
    { path: 'categories', select: 'category' },
    { path: 'groups', select: 'group' },
    { path: 'brand', select: 'brand' },
    { path: 'offer', select: ['discount','status'] },
  ];
  const count = await Product.find(obj).count();
  console.log(count);
  let countPages = count % limit == 0 ? parseInt(count / limit) : parseInt(count / limit) + 1;
  const products = await Product.find(obj)
    .populate(pathsAndFields)
    .skip((page - 1) * limit)
    .limit(limit);

  if (products) {
    res.status(200).json({
      products
    });
  } else {
    return next(new AppError('this product no existe', 400));
  }
});

const checkCategories = async (categories, next) => {
  let ids = new Array();
  if (categories.length > 0) {
    for (const category of categories) {
      if (!category) {
        return next(new AppError('id is required', 400));
      }
      let id = toObjectId(category, next);
      const existingCategory = await Category.find({ _id: id });
      ids.push(id);
      if (existingCategory?.length < 1) {
        return next(new AppError('Category not found', 400));
      }
    }
  }
  return ids;
};

const checkSubCategories = async (subcategories, categories, next) => {
  let ids = new Array();
  if (subcategories.length > 0) {
    for (const subcategory of subcategories) {
      if (!subcategory) {
        return next(new AppError('id is required', 400));
      }
      let id = toObjectId(subcategory, next);
      const existingSubCategory = await SubCategory.find({ _id: id, category: { $in: categories } });
      ids.push(id);
      if (existingSubCategory?.length < 1) {
        return next(new AppError('SubCategory not found in this category', 400));
      }
    }
  }
  return ids;
};

const checkGroups = async (groups, subcategories, next) => {
  let ids = new Array();
  if (groups.length > 0) {
    for (const group of groups) {
      if (!group) {
        return next(new AppError('id is required', 400));
      }
      let id = toObjectId(group, next);

      const existingGroup = await Group.find({ _id: id, subcategory: { $in: subcategories } });
      ids.push(id);
      if (existingGroup?.length < 1) {
        return next(new AppError('group not found in this category', 400));
      }
    }
  }
  return ids;
};

const toObjectId = (id, next) => {
  try {
    return new mongoose.Types.ObjectId(id);
  } catch (e) {
    return next(new AppError('Invalid id.', 401));
  }
};
Leave a Comment