Untitled

mail@pastecode.io avatar
unknown
plain_text
a month ago
19 kB
3
Indexable
Never
const mongoose = require('mongoose');
const User = require('../Models/userModel.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 Product = require('../Models/productModel.js');
const AppError = require('../utils/appError.js');
const catchAsync = require('../utils/catchAsync.js');
const { isDecimal } = require('validator');
const Client = require('../Models/clientModel.js');
const Cart = require('../Models/cartModel.js');
const Order = require('../Models/orderModel.js');
const Retailer = require('../Models/retailerModel.js');
const Notification = require('../Models/notificationModel.js');
const Offer = require('../Models/offerModel.js');
const Socket = require('../socket/socketIndex.js');
const valid = require('../utils/validationfunctions.js');
const XLSX = require('xlsx');
///////////////

exports.AddOrder = catchAsync(async (req, res, next) => {
  const { productDetails, billingDetails, userId } = req.body;
  const session = await mongoose.startSession();

  try {
    session.startTransaction();
    let total = 0;
    const user = await checkUser(userId, next);
    // await checkProductDetails(productDetails,session,next);
    if (!Array.isArray(productDetails)) {
      return next(new AppError('products Details not array', 400));
    }
    if (productDetails.length < 1) {
      return next(new AppError('no products in this order', 400));
    }
    for (const prod of productDetails) {
      const product = await Product.findById(toObjectId(prod.product, next));

      if (!product) {
        return next(new AppError('Product not found', 400));
      }
      if (prod.quantity < 1) {
        return next(new AppError('invalid Quantity value', 400));
      }
      if (product.quantity < prod.quantity) {
        return next(new AppError('Quantity for this Product not found', 400));
      }
      product.quantity = product.quantity - prod.quantity;
      prod.initialPrice = product.pricing.initialPrice;
      if (user.role == 'client') {
        prod.price = product.pricing.publicPrice;
      } else if (user.role == 'retailer') {
        const retailer = await Retailer.findById({ _id: user.profile }).select('pricingType');
        prod.price = product.pricing.discountPrices[retailer.pricingType - 1];
      } else {
        return next(new AppError("this user isn't client or retailer", 400));
      }
      if (product.offer) {
        const offer = await Offer.findById(product.offer);
        if (offer.status == true) {
          prod.price = Number.parseFloat(prod.price * (1 - offer.discount / 100)).toFixed(2);
        }
      }
      total += prod.price * prod.quantity;
      await product.save({ session: session });
    }
    await checkbillingDetails(billingDetails, next);
    const order = await Order.create(
      [
        {
          products: productDetails,
          cart_address: billingDetails,
          totalPrice: Number.parseFloat(total).toFixed(2),
          ordered_by: user._id
        }
      ],
      { session }
    );
    if (user.role == 'client') {
      await Client.updateOne({ _id: user.profile }, { $push: { orders: order[0]._id } }, { session });
    } else if (user.role == 'retailer') {
      await Retailer.updateOne({ _id: user.profile }, { $push: { orders: order[0]._id } }, { session });
    }
    const users = await User.find({ $or: [{ role: 'manager' }, { role: 'admin' }, { role: 'subadmin' }] });
    const usersIds = [];
    for (let i = 0; i < users.length; i++) {
      const notification = await Notification.create([{ content: 'new order', type: 'order' }], { session });
      await User.updateOne({ _id: users[i]._id }, { $push: { notifications: notification[0]._id } }, { session });
      usersIds.push({ id: users[i]._id.toString(), data: notification });
    }
    Socket.SocketConnectedList(usersIds);
    await session.commitTransaction();
    session.endSession();
    res.status(200).json({ message: 'Order added successfully', order: order });
  } catch (error) {
    await session.abortTransaction();
    session.endSession();
    return next(error);
  }
});

///////////////

const checkUser = async (userId, next) => {
  const user = await User.findById(toObjectId(userId, next));

  if (!user) {
    return next(new AppError('User not found', 400));
  }

  switch (user.role) {
    case 'client':
      const client = await Client.findById(user.profile);
      if (!client) {
        return next(new AppError('Client profile not found', 400));
      }
      break;
    case 'retailer':
      const retailer = await Retailer.findById(user.profile);
      if (!retailer) {
        return next(new AppError('Retailer profile not found', 400));
      }
      break;
    default:
      return next(new AppError('this role does not have access for this function', 401));
  }
  return user;
};

const checkbillingDetails = async (billingDetails, next) => {
  if (!valid.isValidPhoneNumber(billingDetails.phone)) {
    return next(new AppError('Phone is required', 400));
  }
  if (!billingDetails.city) {
    return next(new AppError('city is required', 400));
  }
  if (!billingDetails.zipCode) {
    return next(new AppError('zip_code is required', 400));
  }
  if (!billingDetails.address) {
    return next(new AppError('adresse is required', 400));
  }
  if (!billingDetails.first_name) {
    return next(new AppError('first name is required', 400));
  }
  if (!billingDetails.last_name) {
    return next(new AppError('last name is required', 400));
  }
};

exports.getOrders = catchAsync(async (req, res, next) => {
  const { id } = req.params;
  let orders = null;
  const user = await checkUser(id, next);
  if (user.role == 'client') {
    const pathsAndFields = { path: 'orders', populate: { path: 'products.product', select: ['name'] } };

    orders = await Client.find({ _id: user.profile }).populate(pathsAndFields);
  }
  if (user.role == 'retailer') {
    const pathsAndFields = {
      path: 'orders',
      select: ['products'],
      populate: { path: 'products.product', select: ['name'] }
    };

    orders = await Retailer.find({ _id: user.profile }).populate(pathsAndFields);
  }

  const data = orders[0].orders.reduce((result, order) => {
    let obj = {};
    obj.item_count = order.products.length;
    obj.id = order._id;
    obj.totalPrice = order.totalPrice;
    obj.status = order.status;
    obj.createdAt = order.createdAt;
    result.push(obj);
    return result;
  }, []);

  res.status(200).json({ message: 'success', orders: data });
});

exports.getOrderDetails = catchAsync(async (req, res, next) => {
  const { id } = req.params;
  const pathsAndFields = { path: 'products.product', select: ['name', 'images'] };

  const order = await Order.findById(toObjectId(id, next)).populate(pathsAndFields);

  res.status(200).json({ message: 'success', order: order });
});

exports.changeOrderStatus = catchAsync(async (req, res, next) => {
  const { id, status } = req.body;
  const order_id = toObjectId(id, next);
  const session = await mongoose.startSession();

  try {
    session.startTransaction();
    const order = await Order.updateOne({ _id: order_id }, { status: status }, { runValidators: true }, { session });
    if (order.matchedCount > 0) {
      const users = await Order.aggregate([
        { $match: { _id: order_id } },
        {
          $lookup: {
            from: 'users',
            localField: 'ordered_by',
            foreignField: '_id',
            pipeline: [{ $project: { name: 1 } }],
            as: 'user'
          }
        }
      ]);

      const usersIds = [];
      for (let i = 0; i < users[0].user.length; i++) {
        const notification = await Notification.create(
          [{ content: 'Your order status has been changed, new status :' + status + '', type: 'order' }],
          { session }
        );

        await User.updateOne({ _id: users[0].user[i]._id }, { $push: { notifications: notification[0]._id } });
        usersIds.push({ id: users[0].user[i]._id.toString(), data: notification });
      }
      Socket.SocketConnectedList(usersIds);
      await session.commitTransaction();
      session.endSession();
      res.status(200).json({ status: 'success', order: order });
    } else {
      res.status(400).json({ status: 'error', message: 'order id invalid' });
    }
  } catch (error) {
    await session.abortTransaction();
    session.endSession();
    return next(error);
  }
});

exports.AddTrackingAndColis = catchAsync(async (req, res, next) => {
  const { tracking_number, colis_number, orderId } = req.query;
  console.log('****, ', orderId);

  const order = await Order.findById(toObjectId(orderId, next));
  if (!order) {
    return next(new AppError('Order not found', 400));
  }
  if (tracking_number) {
    order.nb_tracking = tracking_number;
  }
  if (colis_number) {
    order.nb_colis = colis_number;
  }
  await order.save();
  res.status(200).json({ message: 'success', data: order });
});

exports.filterOrders = catchAsync(async (req, res, next) => {
  const { category, status, startDate, endDate, limitDate } = req.body;
  const page = parseInt(req.query.page);
  const limit = parseInt(req.query.limit);
  if (!page || !limit) {
    return next(new AppError('page and limit required', 400));
  }
  let category_filter = {};
  let filter = {};

  if (category) {
    const Id = toObjectId(category, next);
    let id_category = await Category.findById(Id);
    if (id_category) {
      //arrayAND.push({ categories: Id });
      category_filter.categories = Id;
    } else {
      return next(new AppError('invalid categorie value', 400));
    }
  }

  if (status == 'pending' || status == 'processing' || status == 'cancel' || status == 'delivered') {
    filter.status = status;
  }
  if (limitDate) {
    var currentDate = new Date();
    let days = parseInt(limitDate);
    currentDate.setDate(currentDate.getDate() - days);
    filter.createdAt = { $gte: currentDate };
  }
  if (startDate && !endDate) {
    let date = new Date(startDate);
    if (date == 'Invalid Date') {
      return next(new AppError('Invalid Date', 400));
    }
    filter.createdAt = { $gte: date };
  }
  if (!startDate && endDate) {
    let date = new Date(endDate);
    date.setDate(date.getDate() + 1);
    if (date == 'Invalid Date') {
      return next(new AppError('Invalid Date', 400));
    }
    filter.createdAt = { $lte: date };
  }
  if (startDate && endDate) {
    let start_date = new Date(startDate);
    let end_date = new Date(endDate);
    end_date.setDate(end_date.getDate() + 1);
    if (start_date == 'Invalid Date' || end_date == 'Invalid Date') {
      return next(new AppError('Invalid Date', 400));
    }
    filter.createdAt = { $gte: start_date, $lte: end_date };
  }

  const results = await Order.aggregate([
    { $match: filter },
    { $sort: { createdAt: -1 } },
    {
      $lookup: {
        from: 'products',
        localField: 'products.product',
        foreignField: '_id',
        pipeline: [{ $match: category_filter }],
        as: 'product'
      }
    },
    {
      $lookup: {
        from: 'users',
        localField: 'ordered_by',
        foreignField: '_id',
        pipeline: [{ $project: { name: 1 } }],
        as: 'clientName'
      }
    },

    { $limit: (page - 1) * limit + limit },
    { $skip: (page - 1) * limit }
  ]);

  const data = results.reduce((result, order) => {
    if (order.product.length > 0) {
      let obj = {};
      obj.units = order.products.length;
      obj.id = order._id;
      obj.product = {};
      obj.product.name = order.product[0]?.name;
      obj.product.image = order.product[0]?.images[0];
      obj.price = order.totalPrice;
      obj.status = order.status;
      obj.nb_tracking = order?.nb_tracking || '';
      obj.nb_colis = order?.nb_colis || '';
      obj.clientName = order.clientName[0]?.name;
      result.push(obj);
    }
    return result;
  }, []);
  const count = await Order.find(filter).count();
  res.status(200).json({ status: 'success', count, data });
});

exports.OrdersToExcel = catchAsync(async (req, res, next) => {
  const { category, status, startDate, endDate, limitDate } = req.query;
  let category_filter = {};
  let filter = {};

  if (category) {
    const Id = toObjectId(category, next);
    let id_category = await Category.findById(Id);
    if (id_category) {
      //arrayAND.push({ categories: Id });
      category_filter.categories = Id;
    } else {
      return next(new AppError('invalid categorie value', 400));
    }
  }

  if (status == 'pending' || status == 'processing' || status == 'cancel' || status == 'delivered') {
    filter.status = status;
  }
  if (limitDate) {
    var currentDate = new Date();
    let days = parseInt(limitDate);
    currentDate.setDate(currentDate.getDate() - days);
    filter.createdAt = { $gte: currentDate };
  }
  if (startDate && !endDate) {
    let date = new Date(startDate);
    if (date == 'Invalid Date') {
      return next(new AppError('Invalid Date', 400));
    }
    filter.createdAt = { $gte: date };
  }
  if (!startDate && endDate) {
    let date = new Date(endDate);
    date.setDate(date.getDate() + 1);
    if (date == 'Invalid Date') {
      return next(new AppError('Invalid Date', 400));
    }
    filter.createdAt = { $lte: date };
  }
  if (startDate && endDate) {
    let start_date = new Date(startDate);
    let end_date = new Date(endDate);
    end_date.setDate(end_date.getDate() + 1);
    if (start_date == 'Invalid Date' || end_date == 'Invalid Date') {
      return next(new AppError('Invalid Date', 400));
    }
    filter.createdAt = { $gte: start_date, $lte: end_date };
    console.log('date :', end_date);
  }

  const results = await Order.aggregate([
    { $match: filter },
    { $sort: { createdAt: -1 } },
    {
      $lookup: {
        from: 'products',
        localField: 'products.product',
        foreignField: '_id',
        pipeline: [{ $match: category_filter }],
        as: 'product'
      }
    },
    {
      $lookup: {
        from: 'users',
        localField: 'ordered_by',
        foreignField: '_id',
        pipeline: [{ $project: { name: 1 } }],
        as: 'clientName'
      }
    }
  ]);

  const flatData = results
    .map((order) => {
      return {
        'Order ID': order._id.toString(),
        'First Name': order?.cart_address?.first_name,
        'Last Name': order?.cart_address?.last_name,
        Phone: order?.cart_address?.phone,
        City: order?.cart_address?.city,
        'Zip Code': order?.cart_address?.zipCode,
        Address: order?.cart_address?.address,
        Units: order?.products.length,
        'Total Price': order?.totalPrice,
        Status: order.status,
        'Created At': formatDateToYYYYMMDD(order?.createdAt),
        'Updated At': formatDateToYYYYMMDD(order?.updatedAt),
        'Tracking Number': order?.nb_tracking,
        'Number of Parcels': order?.nb_colis
      };
    })
    .flat();

  const worksheet = XLSX.utils.json_to_sheet(flatData);
  const workbook = XLSX.utils.book_new();
  XLSX.utils.book_append_sheet(workbook, worksheet, 'Orders');

  // Generate Excel file buffer

  const excelBuffer = XLSX.write(workbook, { bookType: 'xlsx', type: 'buffer' });

  res.setHeader('Content-Disposition', 'attachment; filename="OrdersData.xlsx"');
  res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');

  // Send the buffer as a response
  return res.send(excelBuffer);
});

function formatDateToYYYYMMDD(date) {
  if (date) {
    if (!(date instanceof Date)) {
      throw new Error('Invalid date object');
    }

    const year = date.getFullYear();
    const month = String(date.getMonth() + 1).padStart(2, '0'); // Months are 0-based, so add 1
    const day = String(date.getDate()).padStart(2, '0');

    return `${year}-${month}-${day}`;
  }
}

exports.getListOrders = catchAsync(async (req, res, next) => {
  const results = await Order.aggregate([
    {
      $lookup: {
        from: 'products',
        localField: 'products.0.product',
        foreignField: '_id',
        pipeline: [{ $project: { name: 1, images: 1 } }],
        as: 'product'
      }
    },
    {
      $lookup: {
        from: 'users',
        localField: 'ordered_by',
        foreignField: '_id',
        pipeline: [{ $project: { name: 1 } }],
        as: 'clientName'
      }
    }
  ]);
  const data = results.reduce((result, order) => {
    let obj = {};
    obj.units = order.products.length;
    obj.id = order._id;
    obj.product = {};
    obj.product.name = order.product[0]?.name;
    obj.product.image = order.product[0]?.images[0];
    obj.price = order.totalPrice;
    obj.status = order.status;
    obj.clientName = order.clientName[0]?.name;
    result.push(obj);
    return result;
  }, []);

  res.status(200).json({ status: 'success', data: data });
});

exports.getListOrdersByUser = catchAsync(async (req, res, next) => {
  const { id } = req.params;
  let orders = null;
  const user = await checkUser(id, next);
  if (user.role == 'client') {
    const pathsAndFields = { path: 'orders', populate: { path: 'products.0.product', select: ['name', 'images'] } };

    orders = await Client.find({ _id: user.profile }).populate(pathsAndFields);
  }
  if (user.role == 'retailer') {
    const pathsAndFields = { path: 'orders', populate: { path: 'products.0.product', select: ['name', 'images'] } };
    orders = await Retailer.find({ _id: user.profile }).populate(pathsAndFields);
  }

  const data = orders[0].orders.reduce((result, order) => {
    let obj = {};
    obj.item_count = order.products.length;
    obj.id = order._id;
    obj.totalPrice = order.totalPrice;
    obj.status = order.status;
    obj.createdAt = order.createdAt;
    obj.name = order.products[0].product?.name;
    obj.image = order.products[0]?.product?.images[0];
    result.push(obj);
    return result;
  }, []);

  res.status(200).json({ message: 'success', orders: data });
});

exports.getOrderInvoce = catchAsync(async (req, res, next) => {
  const { id } = req.params;
  const pathsAndFields = [
    { path: 'products.product', select: ['name', 'images'] },
    { path: 'ordered_by', select: ['name', 'phone,email'] }
  ];
  const order = await Order.findById(toObjectId(id, next)).populate(pathsAndFields);

  res.status(200).json({ message: 'success', order: order });
});

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