Sindbad~EG File Manager

Current Path : /home/infinitibizsol/.trash/controllers.4/
Upload File :
Current File : /home/infinitibizsol/.trash/controllers.4/vehicleController.js

const { Op } = require("sequelize");
const db = require("../models");
const { col, literal, fn, transaction } = require("sequelize");
const {
  deleteImage,
  deleteBulkImages,
} = require("../controllers/imageUploadController copy");

const {
  formatTimeRange,
  calculateDateRange,
} = require("../constants/formatTimeRange");

const getAllUserVehicles = async (req, res) => {
  try {
    const user_id = req.params.id;
    let { timeRange = "currentweek", sortBy, orderDirection } = req.body;
    timeRange = formatTimeRange(timeRange);
    const weekRange = calculateDateRange(timeRange);

    const result = await db.tblVehicle.findAll({
      attributes: {
        exclude: ["createdAt", "updatedAt", "user_id"],
      },
      include: [
        {
          model: db.tblVehicleImage,
          as: "images",
          attributes: {
            exclude: ["createdAt", "updatedAt", "vehicle_id"],
          },
        },
      ],
      where: {
        user_id: user_id,
        createdAt: {
          [Op.between]: [weekRange.startDate, weekRange.endDate],
        },
      },
      order: [[sortBy || "vehicle_id", orderDirection || "ASC"]],
    });
    const updatedVehicles = result.map((vehicle) => {
      if (vehicle.dataValues.images && vehicle.dataValues.images.length > 0) {
        return {
          ...vehicle.dataValues,
          imageUrl: vehicle.dataValues.images[0].imageUrl,
        };
      } else {
        return { ...vehicle.dataValues, imageUrl: "preview.png" }; // or simply return vehicle for no imageUrl
      }
    });
    return res.json({
      status: 200,
      message: "Success",
      data: updatedVehicles,
    });
  } catch (error) {
    return res.status(500).json({
      status: 500,
      message: "Internal Server Error",
      error: error.message,
    });
  }
};

const filterByConditionLight = async (req, res) => {
  try {
    const { user_id, week_id } = req.body;
    let results;
    results = await db.sequelize.query(
      "CALL uFilterByConditionLight(:user_id,:week_id)",
      {
        replacements: { user_id, week_id },
        type: db.Sequelize.QueryTypes.SELECT,
      }
    );

    const data = results[0];
    const formattedData = Object.values(data)
      .flat()
      .filter(
        (item) => typeof item === "object" && !item.hasOwnProperty("fieldCount")
      );

    // return res.json(formattedData);
    const conditionLightValues = [0, 1, 2, 3, 4];
    // Check for missing condition_light values
    const missingValues = conditionLightValues.filter((value) => {
      return !formattedData.some((item) => item.condition_light === value);
    });

    // Add missing values to the existing data
    const newData = formattedData.concat(
      missingValues.map((value) => ({
        condition_light: value,
        units: 0,
      }))
    );
    const filteredData = newData.filter((item) => item.condition_light != 0);
    // # Sorting the list in-place in ascending order of 'condition_light'
    filteredData.sort((a, b) => a.condition_light - b.condition_light);

    return res.json({
      status: 200,
      Message: "Success",
      filter_type: "condition_light",
      data: filteredData,
    });
  } catch (error) {
    return res.status(500).json({
      status: 500,
      message: "Internal Server Error",
      error: error.message,
    });
  }
};

const getConditionLightVehicles = async (req, res) => {
  try {
    const {
      condition_light,
      user_id,
      week_id,
      sortBy = "vehicle_id",
      orderDirection = "ASC",
    } = req.body;

    let result;
    result = await db.tblOpportunity.findAll({
      attributes: ["opportunity_id", "auctioneer_id"],
      where: { user_id, week_id },
      include: [
        {
          model: db.tblVehicleRun,
          required: true, // Use this instead of raw to ensure an INNER JOIN
          where: {
            condition_light: condition_light,
          },
          include: [
            {
              model: db.tblVehicle,
              required: true, // Use this instead of raw to ensure an INNER JOIN
            },
            {
              model: db.tblAuctionLane,
              attributes: ["lane_id", "name"],
              // required: true, // Use this instead of raw to ensure an INNER JOIN
              include: [
                {
                  model: db.tblAuction,
                  attributes: ["auction_id", "name"],
                  required: true, // Use this instead of raw to ensure an INNER JOIN
                },
              ],
            },
          ],
        },
      ],
      order: [
        // Include the nested order here instead
        [db.tblVehicleRun, db.tblVehicle, sortBy, orderDirection],
      ],
    });

    // return res.json(result);
    if (!result.length) {
      return res.json({ status: 200, message: "Success", data: [] });
    }
    // // Format the result as per the required format
    const formattedResult = result.flatMap((item) =>
      item.tblVehicleRuns.map((vehicleRun) => {
        const vehicle = vehicleRun.tblVehicle || {};
        const auctionLane = vehicleRun.tblAuctionLane || {};
        const auction = auctionLane.tblAuction || {};

        return {
          auctioneer_id: item?.auctioneer_id || null,
          vehicle_id: vehicle.vehicle_id,
          year: vehicle.year,
          make: vehicle.make,
          model: vehicle.model,
          vin: vehicle.vin,
          mileage: vehicle.mileage,
          trim: vehicle.trim,
          color: vehicle.color,
          color_name: vehicle.color_name,
          details: vehicle.details,
          condition_light: vehicleRun?.condition_light || 4,
          sale_price: vehicleRun?.sale_price || 0,
          run_no: vehicleRun?.run_no || null,
          sale_status: vehicleRun?.sale_status || false,
          lane_id: vehicleRun?.lane_id || null,
          auction_fee: vehicleRun?.auction_fee || 300,
          net_proceeds: vehicleRun?.net_proceeds || null,
          vehicle_total_fee: vehicleRun?.vehicle_total_fee || 0,
          reserve: vehicleRun?.reserve || 0,
          lane_name: auctionLane?.name || null,
          lane_id: auctionLane?.lane_id || null,
          auction_id: auction?.auction_id || null,
          auction_name: auction?.name || null,
        };
      })
    );
    return res.json({ status: 200, message: "Success", data: formattedResult });
  } catch (error) {
    return res.status(500).json({
      status: 500,
      message: "Internal Server Error",
      error: error.message,
    });
  }
};

const filterBySaleStatus = async (req, res) => {
  try {
    const { user_id, week_id } = req.body;
    // Call the stored procedure and destructure the result.
    let results = await db.sequelize.query(
      "CALL uFilterBySaleStatus(:user_id, :week_id)",
      {
        replacements: { user_id, week_id },
        type: db.Sequelize.QueryTypes.SELECT,
      }
    );

    const data = results[0];
    const formattedData = Object.values(data)
      .flat()
      .filter(
        (item) => typeof item === "object" && !item.hasOwnProperty("fieldCount")
      );
    const saleStatusValues = ["true", "false"];
    // Check for missing sale_status values
    const missingValues = saleStatusValues.filter((value) => {
      return !formattedData.some((item) => item.sale_status === value);
    });
    // Add missing values to the existing data
    const newData = formattedData.concat(
      missingValues.map((value) => ({
        sale_status: value,
        units: 0,
      }))
    );
    return res.json({
      status: 200,
      message: "Success",
      filter_type: "sale_status",
      data: newData,
    });
  } catch (error) {
    // Send back a structured error response with proper status code.
    return res.status(500).json({
      status: 500,
      message: "Internal Server Error",
      error: error.message,
    });
  }
};

const getSaleStatusVehicles = async (req, res) => {
  try {
    const { sale_status, user_id, week_id, sortBy, orderDirection } = req.body;
    let result;
    result = await db.tblOpportunity.findAll({
      attributes: ["opportunity_id", "auctioneer_id"],
      where: { user_id, week_id },
      include: [
        {
          model: db.tblVehicleRun,
          required: true, // Use this instead of raw to ensure an INNER JOIN
          where: {
            sale_status: sale_status,
          },
          include: [
            {
              model: db.tblVehicle,
              required: true, // Use this instead of raw to ensure an INNER JOIN
            },
            {
              model: db.tblAuctionLane,
              attributes: ["lane_id", "name"],
              // required: true, // Use this instead of raw to ensure an INNER JOIN
              include: [
                {
                  model: db.tblAuction,
                  attributes: ["auction_id", "name"],
                  required: true, // Use this instead of raw to ensure an INNER JOIN
                },
              ],
            },
          ],
        },
      ],
      order: [
        // Include the nested order here instead
        [db.tblVehicleRun, db.tblVehicle, sortBy, orderDirection],
      ],
    });

    // return res.json(result);
    if (!result.length) {
      return res.json({ status: 200, message: "Success", data: [] });
    }
    // // Format the result as per the required format
    const formattedResult = result.flatMap((item) =>
      item.tblVehicleRuns.map((vehicleRun) => {
        const vehicle = vehicleRun.tblVehicle || {};
        const auctionLane = vehicleRun.tblAuctionLane || {};
        const auction = auctionLane.tblAuction || {};

        return {
          auctioneer_id: item?.auctioneer_id || null,
          vehicle_id: vehicle.vehicle_id,
          year: vehicle.year,
          make: vehicle.make,
          model: vehicle.model,
          vin: vehicle.vin,
          mileage: vehicle.mileage,
          trim: vehicle.trim,
          color: vehicle.color,
          color_name: vehicle.color_name,
          details: vehicle.details,
          condition_light: vehicleRun?.condition_light || 4,
          sale_price: vehicleRun?.sale_price || 0,
          run_no: vehicleRun?.run_no || null,
          sale_status: vehicleRun?.sale_status || false,
          lane_id: vehicleRun?.lane_id || null,
          auction_fee: vehicleRun?.auction_fee || 300,
          net_proceeds: vehicleRun?.net_proceeds || null,
          vehicle_total_fee: vehicleRun?.vehicle_total_fee || 0,
          reserve: vehicleRun?.reserve || 0,
          lane_name: auctionLane?.name || null,
          lane_id: auctionLane?.lane_id || null,
          auction_id: auction?.auction_id || null,
          auction_name: auction?.name || null,
        };
      })
    );
    return res.json({ status: 200, message: "Success", data: formattedResult });
  } catch (error) {
    return res.status(500).json({
      status: 500,
      message: "Internal Server Error",
      error: error.message,
    });
  }
};
const assignVehicleToAuctioneerCtrl = async (req, res) => {
  try {
    const { car_ids, auctioneerId, userId, weekId } = req.body;

    // Find the opportunity_id just once outside of the loop
    const opportunity = await db.tblOpportunity.findOne({
      where: {
        week_id: parseInt(weekId),
        auctioneer_id: auctioneerId,
        user_id: userId,
      },
      attributes: ["opportunity_id"],
    });

    if (!opportunity) {
      return res
        .status(404)
        .json({ status: 404, message: "Opportunity not found." });
    }

    const data = {
      opportunity_id: opportunity.opportunity_id,
    };

    // Create or update vehicles in parallel
    const promises = car_ids.map((item) => {
      return db.tblVehicleRun
        .findOrCreate({
          where: { vehicle_id: item },
          defaults: {
            // Used only if a new instance is created
            vehicle_id: item,
            ...data,
          },
        })
        .then(([vehicleRun, created]) => {
          if (!created) {
            // Update the record only if it's necessary.
            // You might want to add logic to check if an update is required
            return db.tblVehicleRun.update(data, {
              where: {
                vehicle_id: item,
              },
            });
          }
        });
    });

    await Promise.all(promises);

    return res
      .status(200)
      .json({ status: 200, message: "Assigned vehicles successfully" });
  } catch (error) {
    console.error(error);
    return res.status(500).json({
      status: 500,
      message: "Internal Server Error",
      error: error.message,
    });
  }
};

const createVehicleController = async (req, res) => {
  try {
    const { user_id } = req.body;
    const userId = parseInt(user_id);
    let imageFiles = req.imageIds;
    req.body.user_id = userId;
    let result = await db.tblVehicle.create(req.body);

    const car_id = result.vehicle_id;
    if (req.imageIds.length > 0) {
      await Promise.all(
        imageFiles.map(async (item) =>
          db.tblVehicleImage.create({
            vehicle_id: car_id,
            imageUrl: item,
          })
        )
      );
    }

    if (!result) {
      return res.status(500).json({ status: 500, message: "Operation Failed" });
    }

    return res.status(200).json({ status: 200, message: "Success" });
  } catch (error) {
    console.log(error);
    return res.status(500).json({
      status: 500,
      message: "Internal Server Error",
      error: error.message,
    });
  }
};

const createVehicleControllerWithoutImages = async (req, res) => {
  try {
    const body = req.body;
    const result = await db.tblVehicle.create(body);

    return res.json({ status: 200, message: "Success", data: result });
  } catch (error) {
    return res.status(500).json({
      status: 500,
      message: "Internal Server Error",
      error: error.message,
    });
  }
};

const getVehicleDetailsById = async (req, res) => {
  try {
    const vehicle_id = req.params.id;
    let result = await db.tblVehicle.findAll({
      attributes: {
        include: ["vin", "mileage", "color", "color_name", "details"],
      },
      where: {
        vehicle_id: vehicle_id,
      },
    });

    return res
      .status(200)
      .json({ status: 200, message: "success", data: result });
  } catch (error) {
    return res.status(500).json({
      status: 500,
      message: "Internal Server Error",
      error: error.message,
    });
  }
};

const getUserVehicleData = async (req, res) => {
  try {
    const userId = req.params.id;
    const result = await db.tblVehicle.findAll({
      where: {
        user_id: userId,
      },
    });
    return res
      .status(200)
      .json({ status: 200, message: "Success", data: result });
  } catch (error) {
    return res.status(500).json({
      status: 500,
      message: "Internal Server Error",
      error: error.message,
    });
  }
};
const getVehicleImagesByCarId = async (req, res) => {
  try {
    const vehicle_id = req.params.id;
    let result = await db.tblVehicleImage.findAll({
      where: {
        vehicle_id: vehicle_id,
      },
    });
    return res
      .status(200)
      .json({ status: 200, message: "success", data: result });
  } catch (error) {
    return res.status(500).json({ status: 500, message: `${error.message}` });
  }
};

const updateVehicleDetails = async (req, res) => {
  try {
    const vehicleId = req.params.id;
    const { rmvImagesListIds: rmvImagesListIdsStr } = req.body;

    // Update vehicle details except images
    await updateVehicle(vehicleId, req.body);

    // Handle new image files upload
    if (req.imageIds && req.imageIds.length > 0) {
      await uploadVehicleImages(vehicleId, req.imageIds);
    }

    // Remove images based on provided IDs
    const rmvImagesListIds = JSON.parse(rmvImagesListIdsStr);
    if (Array.isArray(rmvImagesListIds) && rmvImagesListIds.length > 0) {
      await removeVehicleImages(rmvImagesListIds, res);
    }

    return res.json({ status: 200, message: "Success" });
  } catch (error) {
    console.error(error);
    return res.status(500).json({
      status: 500,
      message: "Internal Server Error",
      error: error.message,
    });
  }
};

async function updateVehicle(vehicleId, vehicleData) {
  return db.tblVehicle.update(vehicleData, {
    where: { vehicle_id: vehicleId },
  });
}

async function uploadVehicleImages(vehicleId, imageFiles) {
  const promises = imageFiles.map((item) =>
    db.tblVehicleImage.create({
      vehicle_id: vehicleId,
      imageUrl: item,
    })
  );
  await Promise.all(promises);
}

async function removeVehicleImages(imageIds, res) {
  // Assuming deleteImage is defined as provided earlier
  try {
    const destroyPromises = imageIds.map(async (id) => {
      // Await the result from the database query
      const record = await db.tblVehicleImage.findOne({
        attributes: ["imageUrl"],
        where: { image_id: id },
      });

      if (record) {
        // Extract the public ID for Cloudinary from the imageUrl
        const publicId = record.imageUrl; // This function needs to be defined based on your imageUrl format
        // Await the cloudinary deletion operation
        await deleteImage(publicId);
      }

      // Destroy the record in the database and return its promise
      return db.tblVehicleImage.destroy({ where: { image_id: id } });
    });

    // Wait for all promises (both Cloudinary deletions and database deletions) to resolve
    await Promise.all(destroyPromises);
  } catch (error) {
    throw error; // Rethrow to be caught by the outer try...catch
  }
}

const removeVehicleFromAuctioneer = async (req, res) => {
  try {
    const { carIds } = req.body;
    const results = await Promise.all(
      carIds.map(async (vehicle_id) => {
        let result;
        await db.sequelize.transaction(async (t) => {
          // Update tblVehicleRunFee
          const vehicle_run_id = await db.tblVehicleRun.findOne({
            where: { vehicle_id: vehicle_id },
            transaction: t,
          });
          await db.tblVehicleRunFee.destroy({
            where: {
              vehicle_run_id: vehicle_run_id.vehicle_run_id,
            },
            transaction: t,
          });
          // Update tblVehicleRun
          result = await db.tblVehicleRun.destroy({
            where: {
              vehicle_run_id: vehicle_run_id.vehicle_run_id,
            },
            transaction: t,
          });
        });

        return result;
      })
    );

    return res.json({ status: 200, message: "Success", data: results });
  } catch (error) {
    return res.status(500).json({
      status: 500,
      message: "Internal Server Error",
      error: error.message,
    });
  }
};

const updateVehicleStatus = async (req, res) => {
  try {
    let result;
    const vehicle_id = req.body.vehicle_id;
    [result] = await db.tblVehicleRun.findAll({
      where: {
        vehicle_id: vehicle_id,
      },
    });

    if (!result) {
      await db.tblVehicleRun.create(req.body);
    } else {
      await db.tblVehicleRun.update(req.body, {
        where: {
          vehicle_id: vehicle_id,
        },
      });
    }
    return res.json({ status: 200, message: "Success" });
  } catch (error) {
    return res.status(500).json({
      status: 500,
      message: "Internal Server Error",
      error: error.message,
    });
  }
};

const deleteInventoryVehicle = async (req, res) => {
  const vehicleId = parseInt(req.params.id, 10);

  // Validate if vehicle ID is a number
  if (!Number.isInteger(vehicleId)) {
    return res.status(400).json({
      message: "Invalid vehicle ID",
    });
  }

  try {
    // Start a transaction
    const transaction = await db.sequelize.transaction();

    // Check if associated vehicle run exists
    const vehicleRun = await db.tblVehicleRun.findOne({
      attributes: ["vehicle_run_id"],
      where: { vehicle_id: vehicleId },
      transaction,
    });

    if (vehicleRun) {
      // If a vehicle run exists, remove associated data within the same transaction
      const { vehicle_run_id } = vehicleRun;

      const [vehicleRunFeeRemoveCount, vehicleRunRemoveCount] =
        await Promise.all([
          db.tblVehicleRunFee.destroy({
            where: { vehicle_run_id },
            transaction,
          }),
          db.tblVehicleRun.destroy({ where: { vehicle_run_id }, transaction }),
        ]);

      const vehicleRemovedData = await removeVehicleAndImages(
        vehicleId,
        transaction
      );

      // Commit all changes
      await transaction.commit();

      // Respond with the result counts
      res.json({
        status: 200,
        message: "Vehicle and associated data deleted successfully.",
        ...vehicleRemovedData,
        vehicleRunRemoveCount: vehicleRunRemoveCount || 0,
        vehicleRunFeeRemoveCount: vehicleRunFeeRemoveCount || 0,
      });
    } else {
      const vehicleRemovedData = await removeVehicleAndImages(
        vehicleId,
        transaction
      );
      // No associated vehicle run, commit transaction for vehicle and its images only
      await transaction.commit();
      res.json({
        status: 200,
        message: "Vehicle deleted successfully.",
        ...vehicleRemovedData,
      });
    }
  } catch (error) {
    // Rollback the transaction on error
    if (transaction) await transaction.rollback();
    res.status(500).json({
      message: "Internal Server Error",
      error: error.message,
    });
  }
};

async function removeVehicleAndImages(vehicleId, transaction) {
  let cloudinaryDeletedCount;
  const vehicleImagesUrl = await db.tblVehicleImage.findAll({
    attributes: ["imageUrl"],
    where: { vehicle_id: vehicleId },
    transaction,
  });
  if (vehicleImagesUrl.length > 0) {
    cloudinaryDeletedCount = await deleteBulkImages(vehicleImagesUrl);
  }
  const vehicleImageRemovedCount = await db.tblVehicleImage.destroy({
    where: { vehicle_id: vehicleId },
    transaction,
  });
  const vehicleRemovedCount = await db.tblVehicle.destroy({
    where: { vehicle_id: vehicleId },
    transaction,
  });
  return {
    cloudinaryDeletedCount,
    vehicleRemovedCount: vehicleRemovedCount || 0,
    vehicleImageRemovedCount: vehicleImageRemovedCount || 0,
  };
}

const insertYears = async (req, res) => {
  try {
    const startingYear = 1990;
    const currentYear = new Date().getFullYear();
    let result;
    for (let year = startingYear; year <= currentYear; year++) {
      result = await db.tblYear.create({ vehicle_year: year });
    }

    return res.json({ status: 200, message: "Success", data: result });
  } catch (error) {
    return res.status(500).json({
      status: 500,
      message: "Internal Server Error",
      error: error.message,
    });
  }
};
const getYears = async (req, res) => {
  try {
    let result;
    result = await db.tblYear.findAll();
    const convertedData = result.map((entry) => parseInt(entry.vehicle_year));

    return res.json({ status: 200, message: "Success", data: convertedData });
  } catch (error) {
    return res.status(500).json({
      status: 500,
      message: "Internal Server Error",
      error: error.message,
    });
  }
};

module.exports = {
  getAllUserVehicles,
  createVehicleControllerWithoutImages,
  createVehicleController,
  getVehicleImagesByCarId,
  getVehicleDetailsById,
  updateVehicleDetails,
  assignVehicleToAuctioneerCtrl,
  getUserVehicleData,
  removeVehicleFromAuctioneer,
  updateVehicleStatus,
  filterByConditionLight,
  filterBySaleStatus,
  deleteInventoryVehicle,
  getConditionLightVehicles,
  getSaleStatusVehicles,
  insertYears,
  getYears,
};

Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists