HEX
Server: Apache/2.4.52 (Ubuntu)
System: Linux ip-10-0-8-47 6.8.0-1021-aws #23~22.04.1-Ubuntu SMP Tue Dec 10 16:31:58 UTC 2024 aarch64
User: ubuntu (1000)
PHP: 8.1.2-1ubuntu2.22
Disabled: NONE
Upload Files
File: //var/www/javago-nodeserver-hotfixes/src/services/order/order.services.js
import PredefinedItemImageDB from "../../models/pre_defined_item_images.model.js";
import AddonSizeCafeItemDB from "../../models/addon_size_cafe_items.model.js";
import CafeMenuItemSizeDB from "../../models/cafe_menu_item_sizes.model.js";
import UserFavouriteCafeDB from "../../models/user_favorite_cafes.model.js";
import LoyaltyStampDB from "../../models/manage_loyalty_stamps.model.js";
import GroupCoffeeRunDB from "../../models/group_coffee_run.model.js";
import NotificationDB from "../../models/notification_list.model.js";
import CafeMenuItemDB from "../../models/cafe_menu_items.model.js";
import CafeTimingDB from "../../models/cafe_timings.model.js";
import GroupMemberDB from "../../models/group_members.model.js";
import { messages } from "../../config/response.messages.js";
import AddonSizeDB from "../../models/addon_sizes.model.js";
import GroupDB from "../../models/groups.model.js";
import OrderDB from "../../models/orders.model.js";
import CafeDB from "../../models/cafes.model.js";
import UserDB from "../../models/users.model.js";
import InHouseOrderItems from "../../models/in_house_order_items.model.js";
import CafeGoToOrdersDB from "../../models/cafe_go_to_orders.model.js";
import * as config from "../../config/config.js";
import SizeDB from "../../models/sizes.model.js";
import * as Helper from "../../helper/index.js";
import Sequelize, { json } from "sequelize";
import { Op } from "sequelize";
import moment from "moment";
import coffeeRunInHouseOrders from "../../models/coffee_run_inhouse_order.model.js";
import schedule from "../../helper/schedule.js";
import notification from "../../helper/notification.js";
import CafeloyaltyStampExcludedItemsDB from "../../models/cafe_loyalty_stamp_excluded_items.model.js";

// create Group
export async function saveGroupCoffeeRun(data, loggedInUser) {
  try {
    let request_unique_id = (
      await Helper.generateRandomString(15)
    ).toUpperCase();

    let createRequestArr = [];
    if (data.type === 0) {
      data.users.forEach(async (element) => {
        createRequestArr.push({
          request_unique_id: request_unique_id,
          type: data.type,
          group_id: data.group_id,
          user_id: element.id,
          is_contributor_person: element.is_contributor_person,
          request_created_by: loggedInUser.id,
          request_endtime: data.request_endtime,
          created_at: moment().unix(),
          updated_at: moment().unix(),
        });
      });
    } else {
      data.users.forEach(async (element) => {
        createRequestArr.push({
          request_unique_id: request_unique_id,
          cafe_id: data.cafe_id,
          group_id: data.group_id,
          user_id: element.id,
          is_contributor_person: element.is_contributor_person,
          request_created_by: loggedInUser.id,
          request_endtime: data.request_endtime,
          created_at: moment().unix(),
          updated_at: moment().unix(),
        });
      });
    }

    var requestResults = await GroupCoffeeRunDB.bulkCreate(createRequestArr, {
      returning: true,
    });

    var notificationData = requestResults
      .filter((e) => e.dataValues.user_id != loggedInUser.id)
      .map((e) => {
        return {
          sender_id: loggedInUser.id,
          receiver_id: e.dataValues.user_id,
          reference_id: e.dataValues.id,
          notification_type: 2,
          is_read: 0,
          created_at: moment().unix(),
          updated_at: moment().unix(),
        };
      });

    await NotificationDB.bulkCreate(notificationData);

    const coffeRun = await GroupCoffeeRunDB.findOne({
      where: {
        request_unique_id: request_unique_id,
        user_id: loggedInUser.id,
      },
      include: [
        {
          model: CafeDB,
          required: false,
          attributes: ["id"],
          include: [
            {
              model: CafeGoToOrdersDB,
              required: false,
              attributes: ["id"],
              where: {
                user_id: loggedInUser.id,
              },
            },
          ],
        },
      ],
    });

    await notification.createCoffeeRunNotification({
      loggedInUser: loggedInUser.name,
      userIds: notificationData.map((e) => e.receiver_id),
      cafe_id: data.cafe_id,
      group_id: data.group_id,
      request_endtime: data.request_endtime,
    });

    await schedule.createSchedule({
      sender_id: loggedInUser.id,
      time: coffeRun.dataValues.request_endtime,
      receiver_id: notificationData[0].receiver_id,
      reference_id: coffeRun.dataValues.id,
      coffeeRunId: coffeRun.dataValues.request_unique_id,
    });

    return coffeRun;
  } catch (error) {
    throw new Error(error);
  }
}

//get cafe list by group id and edit time
export async function getGroupCoffeeRun(data) {
  try {
    if ([null, undefined, 0, "", "0"].includes(data.cafe_id)) {
      let check = await GroupCoffeeRunDB.findOne({
        where: {
          group_id: data.group_id,
          request_endtime: {
            [Op.gte]: moment().unix(),
          },
        },
      });
      if (check) {
        data.cafe_id = check.cafe_id;
      } else {
        return [];
      }
    }

    let result = await GroupCoffeeRunDB.findAll({
      order: [["id", "DESC"]],
      where: {
        group_id: parseInt(data.group_id),
        cafe_id: parseInt(data.cafe_id),
        request_endtime: {
          [Op.gte]: moment().unix(),
        },
        order_id: null,
        request_endtime: {
          [Op.gte]: moment().unix(),
        },
      },
      order: [["id", "DESC"]],
      include: [
        {
          model: UserDB,
          as: "creator_data",
          where: {
            is_deleted: 0,
            is_active: 1,
          },
          required: true,
          attributes: ["id", "name", "profile_picture", "email"],
        },
        {
          model: GroupDB,
          where: {
            is_active: 1,
          },
          include: [
            {
              model: GroupMemberDB,
              as: "group_details",
              attributes: ["id", "user_id"],
              include: [
                {
                  model: UserDB,
                  as: "user_data",
                  attributes: ["id", "name", "email", "profile_picture"],
                  where: { is_deleted: 0, is_active: 1, is_verified: 1 },
                },
              ],
            },
          ],
          required: true,
          attributes: [
            "id",
            "group_name",
            "group_profile",
            "created_at",
            "group_code",
          ],
        },
        {
          model: UserDB,
          as: "user_data",
          where: {
            is_deleted: 0,
            is_active: 1,
          },
          required: true,
          attributes: ["id", "name", "profile_picture", "email"],
        },
      ],
      attributes: [
        "id",
        "request_unique_id",
        "is_contributor_person",
        "is_order_place",
        "order_id",
        "request_endtime",
        "created_at",
        "cafe_id",
      ],
    });
    return result;
  } catch (error) {
    console.log(error);
    throw new Error(error);
  }
}

// check same time group cafe run can create or not
export async function checkSameGroupCafeRunEndTime(data) {
  try {
    return await GroupCoffeeRunDB.findOne({
      where: {
        group_id: data.group_id,
        user_id: data.loggedInUser.id,
        request_endtime: {
          [Op.gte]: moment().unix(),
        },
      },
      include: [
        {
          model: CafeDB,
          required: false,
          attributes: ["id"],
          include: [
            {
              model: CafeGoToOrdersDB,
              required: false,
              attributes: ["id"],
              where: {
                user_id: data.loggedInUser.id,
              },
            },
          ],
        },
      ],
    });
  } catch (error) {
    return null;
  }
}

// place order
export async function placeOrder(data) {
  try {
    let todayDayId = new Date().getDay();
    let currentTime = moment().format("HH:mm");
    if (data.group_coffee_run_id) {
      let checkRequestEndTime = await GroupCoffeeRunDB.findOne({
        where: {
          request_unique_id: data.group_coffee_run_id,
        },
      });
      if (!checkRequestEndTime) {
        return {
          status: 203,
          message: messages.group_coffee_run_wrong_passed,
          result: {},
        };
      } else if (
        checkRequestEndTime &&
        checkRequestEndTime.request_endtime < moment().unix()
      ) {
        return {
          status: 203,
          message: messages.group_coffee_run_end_time_up,
          result: {},
        };
      }
    }
    let itemsArray = data.order_item_array.map(function (currentValue) {
      return currentValue.item_id;
    });
    let result = await CafeDB.findOne({
      where: {
        id: data.cafe_id,
        is_active: 1,
        deleted_at: 0,
      },
      include: [
        {
          model: CafeTimingDB,
          where: {
            day: todayDayId,
            open_time: {
              [Op.lte]: currentTime,
            },
            close_time: {
              [Op.gte]: currentTime,
            },
          },
          required: false,
          attributes: ["id"],
          as: "time_sheet_data",
        },
        {
          model: CafeMenuItemDB,
          where: {
            id: {
              [Op.in]: itemsArray,
            },
          },
          include: [
            {
              model: CafeMenuItemSizeDB,
              required: false,
              include: [
                {
                  model: SizeDB,
                  required: false,
                  attributes: ["size_name"],
                },
              ],
              attributes: ["item_size_price"],
            },
            {
              model: AddonSizeCafeItemDB,
              required: false,
              include: [
                {
                  model: AddonSizeDB,
                  required: false,
                  attributes: ["addon_size_price"],
                },
              ],
              attributes: ["addon_size_id"],
            },
          ],
          required: false,
          attributes: [
            "id",
            "item_price",
            "status",
            "item_deleted_at",
            "item_name",
          ],
        },
      ],
      attributes: ["id"],
    });
    let final_result;
    if (!result) {
      final_result = {
        status: 203,
        message: messages.cafe_is_not_available,
      };
    } else if (result && result.time_sheet_data.length == 0) {
      final_result = {
        status: 203,
        message: messages.cafe_has_been_closed,
      };
    } else if (result && result.cafe_menu_items.length == 0) {
      final_result = {
        status: 203,
        message: messages.cafe_selected_items_not_avaialble,
      };
    } else if (result && result.cafe_menu_items.length) {
      let message = "";
      let items_prices = 0;
      for (let index = 0; index < result.cafe_menu_items.length; index++) {
        const element = result.cafe_menu_items[index];
        if (element.status == 0 || element.item_deleted_at > 0) {
          message += element.item_name;
          if (
            result.cafe_menu_items.length > 1 &&
            index != parseInt(result.cafe_menu_items.length) - 1
          ) {
            message += ", ";
          }
        }
        if (
          data.order_item_array.some(
            (children) =>
              children.item_size == "Regular" && children.item_id == element.id
          )
        ) {
          var qty = 1;
          data.order_item_array.map((children) => {
            if (children.item_id == element.id) {
              qty = children.item_quantity;
            }
          });
          items_prices = items_prices + Number(element.item_price) * qty;

          // now check addons prices
          for (let i = 0; i < data.order_item_array.length; i++) {
            const element1 = data.order_item_array[i];
            if (element1.item_id == element.id) {
              for (let j = 0; j < element1.addon_sizes.length; j++) {
                const element2 = element1.addon_sizes[j];
                let check = 0;
                element.addon_size_cafe_items.map((grandChildren) => {
                  if (
                    grandChildren.addon_size_id == element2.addon_size_id &&
                    element1.item_id == element.id
                  ) {
                    check = grandChildren.addon_size.addon_size_price;
                  }
                });
                items_prices += check;
              }
            }
          }
        } else if (
          data.order_item_array.some(
            (children) =>
              children.item_size != "Regular" && children.item_id == element.id
          )
        ) {
          for (let i = 0; i < data.order_item_array.length; i++) {
            const element1 = data.order_item_array[i];
            if (
              element1.item_size != "Regular" &&
              element.cafe_menu_item_sizes.some(
                (children) =>
                  children.size.size_name == element1.item_size &&
                  element1.item_id == element.id
              )
            ) {
              var tmp = 0;
              element.cafe_menu_item_sizes.map(function (childrenElement) {
                if (childrenElement.size.size_name == element1.item_size) {
                  tmp = childrenElement.item_size_price;
                }
              });
              items_prices += tmp ? tmp * element1.item_quantity : 0;
            }
            // now check addons prices
            if (element1.item_id == element.id) {
              for (let j = 0; j < element1.addon_sizes.length; j++) {
                const element2 = element1.addon_sizes[j];
                let check = 0;
                element.addon_size_cafe_items.map((grandChildren) => {
                  if (
                    grandChildren.addon_size_id == element2.addon_size_id &&
                    element1.item_id == element.id
                  ) {
                    check = grandChildren.addon_size.addon_size_price;
                  }
                });
                items_prices += check;
              }
            }
          }
        }
      }
      if (message) {
        final_result = {
          status: 203,
          message: message + " items not available",
          result: result,
        };
      } else if (
        message == "" &&
        Number(items_prices) != Number(data.items_amount)
      ) {
        final_result = {
          status: 203,
          message: messages.item_price_updated_error,
          result: result,
        };
      } else {
        if (data.loyalty_stamp_id) {
          let checkLoyaltyStamp = await LoyaltyStampDB.findOne({
            where: {
              id: parseInt(data.loyalty_stamp_id),
            },
          });
          if (checkLoyaltyStamp) {
            final_result = {
              status: 200,
              message: messages.data_found,
              result: result,
            };
          } else {
            final_result = {
              status: 203,
              message: messages.redeem_code_no_longer_available,
              result: result,
            };
          }
        } else {
          final_result = {
            status: 200,
            message: messages.data_found,
            result: result,
          };
        }
      }
      console.log(items_prices);
    }
    return final_result;
  } catch (error) {
    throw new Error(error);
  }
}

// save order
export async function saveOrder(data) {
  try {
    for (let index = 0; index < data.order_item_array.length; index++) {
      const element = data.order_item_array[index];
      let itemImageData = await CafeMenuItemDB.findByPk(element.item_id, {
        include: [
          {
            model: PredefinedItemImageDB,
            as: "image_data",
            required: true,
          },
        ],
      });
      element.item_image = itemImageData
        ? itemImageData.image_data.item_image
        : "";
    }

    let result = await OrderDB.create({
      order_number: await Helper.generateRandomString(6, true),
      user_id: data.loggedInUser,
      cafe_id: data.cafe_id,
      group_id: data.group_id ? data.group_id : null,
      group_coffee_run_id: data.group_coffee_run_id
        ? data.group_coffee_run_id
        : null,
      loyalty_stamp_id: data.loyalty_stamp_id ? data.loyalty_stamp_id : null,
      is_main_order: data.is_main_order,
      order_item_array: JSON.stringify(data.order_item_array),
      additional_note: data.additional_note,
      total_amount: data.total_amount ? Number(data.total_amount) : 0,
      tax: data.tax ? Number(data.tax) : 0,
      service_charge: data.service_charge ? Number(data.service_charge) : 0,
      other_charge: data.other_charge ? Number(data.other_charge) : 0,
      transaction_id: data.transaction_id,
      discount_amount: data.discount_amount ? Number(data.discount_amount) : 0,
      order_placed_at: moment().unix(),
      status: 1,
      created_at: moment().unix(),
      updated_at: moment().unix(),
    });
    if (data.group_coffee_run_id) {
      await GroupCoffeeRunDB.update(
        { order_id: result.id, updated_at: moment().unix() },
        {
          where: {
            request_unique_id: data.group_coffee_run_id,
            user_id: data.loggedInUser,
          },
          returning: true,
          plain: true,
        }
      );
      let returnDoc = await GroupCoffeeRunDB.findOne({
        where: {
          request_unique_id: data.group_coffee_run_id,
        },
      });
      await OrderDB.update(
        {
          request_unique_id: returnDoc.request_unique_id,
        },
        {
          where: {
            id: result.id,
          },
        }
      );
    }
    if (typeof result.order_item_array == "string")
      result.order_item_array = JSON.parse(result.order_item_array);
    let send_notification_exists = await GroupCoffeeRunDB.findOne({
      where: {
        request_unique_id: result.dataValues.group_coffee_run_id,
        user_id: result.dataValues.user_id,
        request_created_by: {
          [Op.ne]: result.dataValues.user_id,
        },
        is_contributor_person: 1,
      },
      attributes: ["id", "request_created_by", "order_id"],
    });
    console.log(send_notification_exists);
    if (send_notification_exists) {
      await NotificationDB.create({
        sender_id: result.dataValues.user_id,
        receiver_id: send_notification_exists.request_created_by,
        reference_id: send_notification_exists.id,
        notification_type: 5,
        is_read: 0,
        created_at: moment().unix(),
        updated_at: moment().unix(),
      });

      notification.payOrderNotification({
        loggedInUser: send_notification_exists.request_created_by,
        orderId: result.dataValues.id,
        groupId: data.group_id,
        senderId: result.dataValues.user_id,
      });

      console.log("Notification sent.");
    } else {
      console.log("Notification not sent because record does not exist.");
    }
    return result;
  } catch (error) {
    throw new Error(error);
  }
}

// get order list
export async function getOrder(userId, data) {
  try {
    if ([null, undefined, "", 0].includes(data.page)) data.page = 1;
    data.page = parseInt(data.page);
    let result = await OrderDB.paginate({
      page: data.page,
      paginate: parseInt(config.config.limit),
      where: {
        user_id: userId,
      },
      include: [
        {
          model: CafeDB,
          where: {
            is_active: 1,
            deleted_at: 0,
          },
          include: [
            {
              model: UserFavouriteCafeDB,
              as: "user_favorite_cafes",
              required: false,
              where: {
                is_favorite: 1,
                user_id: parseInt(userId),
              },
              attributes: ["is_favorite", "user_id"],
            },
          ],
          required: false,
          attributes: [
            "id",
            "cafe_name",
            "phone",
            "latitude",
            "bio",
            "email",
            "cafe_tax",
            "longitude",
            "address",
            "banner_image",
            "postcode",
          ],
        },
        {
          model: GroupDB,
          required: false,
          attributes: ["id", "group_name", "group_profile"],
          include: [
            {
              model: GroupMemberDB,
              as: "group_details",
              attributes: ["id", "user_id"],
              include: [
                {
                  model: UserDB,
                  as: "user_data",
                  attributes: ["id", "name", "email", "profile_picture"],
                  where: { is_deleted: 0, is_active: 1, is_verified: 1 },
                },
              ],
            },
          ],
        },
      ],
      attributes: [
        "id",
        "order_number",
        "created_at",
        "updated_at",
        "order_item_array",
        "additional_note",
        "total_amount",
        "tax",
        "discount_amount",
        "order_placed_at",
        "order_collected_at",
        "status",
      ],
      order: [["id", "DESC"]],
    });
    result = JSON.stringify(result);
    result = JSON.parse(result);
    for (let index = 0; index < result.docs.length; index++) {
      const element = result.docs[index];
      element.group_coffee_run = await GroupCoffeeRunDB.findAll({
        where: {
          order_id: element.id,
        },
        include: [
          {
            model: UserDB,
            attributes: ["id", "name", "profile_picture", "email"],
            required: true,
            where: {
              is_deleted: 0,
              is_active: 1,
            },
            as: "user_data",
          },
          {
            model: UserDB,
            attributes: ["id", "name", "profile_picture", "email"],
            required: true,
            where: {
              is_deleted: 0,
              is_active: 1,
            },
            as: "creator_data",
          },
        ],
        attributes: [
          "id",
          "request_unique_id",
          "is_contributor_person",
          "is_order_place",
          "request_endtime",
        ],
      });
      element.cafe.is_favorite =
        element.cafe.user_favorite_cafes.length > 0 ? 1 : 0;
      element.cafe.distance = 0;
      element.cafe.time_sheet_data = [];
      delete element.cafe.user_favorite_cafes;
    }
    return result;
  } catch (error) {
    throw new Error(error);
  }
}

export async function getOrderDetails(id) {
  try {
    let result = await OrderDB.findByPk(id, {
      attributes: [
        "id",
        "order_number",
        "group_id",
        "cafe_id",
        "total_amount",
        "tax",
        "service_charge",
        "other_charge",
        "order_item_array",
        "discount_amount",
        "order_placed_at",
        "transaction_id",
      ],
      include: [
        {
          model: UserDB,
          attributes: ["name", "profile_picture", "email"],
        },
      ],
    });
    return result;
  } catch (error) {
    throw new Error(error);
  }
}

export async function updateOrderDetails(id, transaction_id) {
  try {
    let result = await OrderDB.update(
      {
        transaction_id: transaction_id,
      },
      {
        where: { id: id },
      }
    );
    return result;
  } catch (error) {
    throw new Error(error);
  }
}

// get Individual order list
export async function getIndividualOrder(user_id, order_id, cafe_id) {
  try {
    let result = await OrderDB.findOne({
      where: {
        user_id: user_id,
        id: order_id,
        cafe_id: cafe_id,
      },
      include: [
        {
          model: CafeDB,
          where: {
            is_active: 1,
            deleted_at: 0,
          },
          include: [
            {
              model: UserFavouriteCafeDB,
              as: "user_favorite_cafes",
              required: false,
              where: {
                is_favorite: 1,
                user_id: parseInt(user_id),
              },
              attributes: ["is_favorite", "user_id"],
            },
          ],
          required: false,
          attributes: [
            "id",
            "cafe_name",
            "phone",
            "latitude",
            "longitude",
            "address",
            "banner_image",
            "postcode",
          ],
        },
        {
          model: GroupDB,
          required: false,
          attributes: ["id", "group_name", "group_profile"],
          include: [
            {
              model: GroupMemberDB,
              as: "group_details",
              attributes: ["id", "user_id"],
              include: [
                {
                  model: UserDB,
                  as: "user_data",
                  attributes: ["id", "name", "email", "profile_picture"],
                  where: { is_deleted: 0, is_active: 1, is_verified: 1 },
                },
              ],
            },
          ],
        },
      ],
      attributes: [
        "id",
        "order_number",
        "created_at",
        "updated_at",
        "order_item_array",
        "additional_note",
        "total_amount",
        "tax",
        "discount_amount",
        "order_placed_at",
        "order_collected_at",
        "status",
      ],
    });
    return result;
  } catch (error) {
    throw new Error(error);
  }
}

// change order status
export async function changeOrderStatus(orderId, data) {
  try {
    let check = await OrderDB.findByPk(orderId);
    if (!check) {
      return {
        status: 501,
        message: messages.wrong_order_id_applied,
      };
    }
    if (check.status == parseInt(data.flag)) {
      if (check.status == 1) {
        return {
          status: 409,
          message:
            messages.order_has_been_already_placed +
            " at " +
            check.order_placed_at,
          data: {
            cafe_id: check.cafe_id,
          },
        };
      }
      return {
        status: 409,
        message:
          messages.order_has_been_already_collected +
          " at " +
          check.order_collected_at,
        data: {
          cafe_id: check.cafe_id,
        },
      };
    }
    let updateObj = {
      status: parseInt(data.flag),
      updated_at: moment().unix(),
    };
    if (parseInt(data.flag) == 1) {
      updateObj.order_placed_at = moment().unix();
    } else {
      updateObj.order_collected_at = moment().unix();
    }
    let result = await OrderDB.update(updateObj, {
      where: {
        id: orderId,
      },
    });
    return {
      status: 202,
      message:
        parseInt(data.flag) == 1
          ? messages.order_has_been_placed
          : messages.order_has_been_collected,
      data: {
        cafe_id: check.cafe_id,
      },
    };
  } catch (error) {
    throw new Error(error);
  }
}

// Get Total Order cafe wise
export async function getAllCafeOrders(user_id, cafe_id) {
  try {
    return await OrderDB.count({
      where: {
        user_id: parseInt(user_id),
        cafe_id: cafe_id,
        status: 2,
        loyalty_stamp_id: null,
        // cafe_coupon_expired_time: 0
      },
    });
  } catch (error) {
    throw new Error(error);
  }
}

// Get Total Order cafe wise
export async function getcouponOrder(user_id, cafe_id) {
  try {
    const orders = await OrderDB.findAll({
      where: {
        user_id: parseInt(user_id),
        cafe_id: cafe_id,
        status: 2,
        loyalty_stamp_id: null,
      },
    });

    const LoyltyStamp = await LoyaltyStampDB.findOne({
      where: {
        cafe_id: cafe_id,
      },
    });
    // Get excluded items for this cafe's loyalty stamp
    const excludedItems = await CafeloyaltyStampExcludedItemsDB.findAll({
      where: {
        stamp_id: LoyltyStamp.id,
      },
    });

    const excludedItemsId = excludedItems.map((item) => item.item_id);

    // Process orders and count
    const processedOrderCount = await Promise.all(
      orders.map(async (order) => {
        // Parse the order item array
        const orderItemArray = JSON.parse(order.order_item_array);

        // Get details for each item in the order
        const itemDetailsPromises = orderItemArray.map(async (orderItem) => {
          const itemDetails = await CafeMenuItemDB.findOne({
            where: {
              id: orderItem.item_id,
              cafe_id: cafe_id,
            },
          });

          return itemDetails ? itemDetails.dataValues : null;
        });

        // Resolve all item details
        const itemDetailsArray = await Promise.all(itemDetailsPromises);

        // Check if all items are excluded
        const allItemsExcluded = itemDetailsArray.every(
          (itemDetails) =>
            itemDetails && excludedItemsId.includes(itemDetails.id)
        );

        // Return true if at least one item is not excluded
        return !allItemsExcluded;
      })
    );

    // Count the number of orders with at least one non-excluded item
    const orderCount = processedOrderCount.filter(Boolean).length;
    return orderCount;
  } catch (error) {
    throw new Error(error);
  }
}

// Get Total Order
export async function getUniversalOrders(user_id) {
  try {
    return await OrderDB.count({
      where: {
        user_id: parseInt(user_id),
        status: 2,
        universal_coupon_expired_time: 0,
      },
    });
  } catch (error) {
    throw new Error(error);
  }
}

// Get last universal expires time
export async function getLastExpireDateUniversal(user_id) {
  try {
    return await OrderDB.findOne({
      where: {
        user_id: parseInt(user_id),
        status: 2,
        universal_coupon_expired_time: {
          [Op.ne]: 0,
        },
      },
      order: [["id", "DESC"]],
    });
  } catch (error) {
    throw new Error(error);
  }
}

// Get last universal expires time
export async function getLastExpireCafeDate(user_id, cafe_id) {
  try {
    return await OrderDB.findOne({
      where: {
        user_id: parseInt(user_id),
        status: 2,
        cafe_id: cafe_id,
        cafe_coupon_expired_time: {
          [Op.ne]: 0,
        },
      },
      order: [["id", "DESC"]],
    });
  } catch (error) {
    throw new Error(error);
  }
}

//Get cafe universal stamp
export async function getUniversalStamp() {
  try {
    let nonUniversal = await LoyaltyStampDB.findOne({
      where: {
        cafe_id: null,
        is_universal: 1,
      },
    });
    return nonUniversal;
  } catch (error) {
    throw new Error(error);
  }
}

//Get cafe stemp by cafe ID
export async function getStampsByCafe(cafe_id) {
  try {
    let nonUniversal = await LoyaltyStampDB.findOne({
      where: {
        cafe_id: parseInt(cafe_id),
        is_universal: 0,
      },
    });
    return nonUniversal;
  } catch (error) {
    throw new Error(error);
  }
}

//update coupons by cafe
export async function updateCouponByCafeId(
  user_id,
  coupon_id,
  cafe_id,
  stamp_no,
  stamp_expires_in
) {
  try {
    let orderIds = [];
    let expireDay = moment().unix();
    let orderDetails = await OrderDB.findAll({
      where: {
        user_id: parseInt(user_id),
        status: 2,
        cafe_coupon_expired_time: 0,
        cafe_id: cafe_id,
      },
      attributes: ["id"],
      order: [["id", "ASC"]],
      limit: stamp_no,
    });
    for (let i = 0; i < orderDetails.length; i++) {
      orderIds.push(orderDetails[i].id);
    }
    return await OrderDB.update(
      {
        cafe_coupon_order_id: coupon_id,
        cafe_coupon_expired_time: moment(expireDay, "X")
          .add(stamp_expires_in, "days")
          .unix(),
      },
      {
        where: {
          cafe_id: parseInt(cafe_id),
          user_id: user_id,
          id: {
            [Op.in]: orderIds,
          },
        },
      }
    );
  } catch (error) {
    console.log("error: ", error);
    throw new Error(error);
  }
}

//update universal coupons
export async function updateUniversalCoupon(
  user_id,
  coupon_id,
  stamp_no,
  stamp_expires_in
) {
  try {
    let orderIds = [];
    let expireDay = moment().unix();
    let orderDetails = await OrderDB.findAll({
      where: {
        user_id: parseInt(user_id),
        status: 2,
        universal_coupon_expired_time: 0,
      },
      attributes: ["id"],
      order: [["id", "ASC"]],
      limit: stamp_no,
    });
    for (let i = 0; i < orderDetails.length; i++) {
      orderIds.push(orderDetails[i].id);
    }
    return await OrderDB.update(
      {
        universal_coupon_order_id: coupon_id,
        universal_coupon_expired_time: moment(expireDay, "X")
          .add(stamp_expires_in, "days")
          .unix(),
      },
      {
        where: {
          user_id: user_id,
          id: {
            [Op.in]: orderIds,
          },
        },
      }
    );
  } catch (error) {
    throw new Error(error);
  }
}

// Get Loyalty list
export async function loyaltyList(data, query) {
  try {
    // Ensure page is a number and defaults to 1 if not provided
    const page = query.page ? parseInt(query.page, 10) : 1;
    const limit = 10;
    const offset = (page - 1) * limit;

    // Group orders by cafe
    let loyaltyDetails = await OrderDB.findAndCountAll({
      limit: limit,
      offset: offset,
      order: [["id", "ASC"]],
      attributes: ["id", "order_item_array", "user_id", "cafe_id"],
      include: [
        {
          model: CafeDB,
          attributes: ["id", "cafe_name"],
          include: [
            {
              model: LoyaltyStampDB,
              attributes: [
                "id",
                "stamp_no",
                "cafe_id",
                "offer_text",
                "stamp_color",
              ],
            },
          ],
        },
      ],
      where: {
        user_id: data,
        status: 2,
        cafe_coupon_expired_time: 0,
        cafe_coupon_order_id: null,
      },
    });

    // Group orders by cafe
    const cafeGroupedOrders = loyaltyDetails.rows.reduce((acc, order) => {
      const cafeId = order.cafe_id;
      console.log(cafeId);
      if (!acc[cafeId]) {
        acc[cafeId] = {
          user_id: order.user_id,
          cafe_id: cafeId,
          cafe: {
            ...order.cafe.dataValues,
            order_items: [],
          },
          Count: 0,
        };
      }

      // Add order items
      acc[cafeId].cafe.order_items.push({
        id: order.id,
        order_item_array: order.order_item_array,
      });

      return acc;
    }, {});

    // Process orders for counting
    const processedCafeOrders = await Promise.all(
      Object.values(cafeGroupedOrders).map(async (cafeOrder) => {
        let orderC = 0;
        // Process order items to determine if order should be counted
        const processedOrderItems = await Promise.all(
          cafeOrder.cafe.order_items.map(async (orderItem) => {
            const itemArray = JSON.parse(orderItem.order_item_array);

            const itemDetailsPromises = itemArray.map(async (item) => {
              const itemDetails = await CafeMenuItemDB.findOne({
                where: {
                  id: item.item_id,
                  cafe_id: cafeOrder.cafe_id,
                },
              });

              return itemDetails ? itemDetails.dataValues : null;
            });

            const itemDetailsArray = await Promise.all(itemDetailsPromises);

            const excludedItems = await CafeloyaltyStampExcludedItemsDB.findAll(
              {
                where: {
                  stamp_id: cafeOrder.cafe.manage_loyalty_stamps[0].id,
                },
              }
            );

            const excludedItemsId = excludedItems.map((item) => item.item_id);
            console.log(excludedItemsId);
            console.log(itemDetailsArray);
            // Check if all items are excluded
            const allItemsExcluded = itemDetailsArray.every(
              (itemDetails) =>
                itemDetails && excludedItemsId.includes(itemDetails.id)
            );
            if (!allItemsExcluded) {
              orderC++;
            }
            return !allItemsExcluded;
          })
        );

        // Determine if any order items should be counted
        //cafeOrder.Count = processedOrderItems.some(Boolean) ? 1 : 0;
        cafeOrder.Count = orderC;
        return cafeOrder;
      })
    );

    // Calculate total processed count
    const totalProcessedCount = processedCafeOrders.reduce(
      (total, order) => total + order.Count,
      0
    );

    // Calculate total pages
    const totalPages = Math.ceil(loyaltyDetails.count / limit);

    return {
      docs: processedCafeOrders,
      pages: totalPages,
      total: loyaltyDetails.count,
      totalProcessedCount: totalProcessedCount,
      currentPage: page,
    };
  } catch (error) {
    console.error("Error in loyaltyList:", error);
    throw new Error(`Failed to retrieve loyalty details: ${error.message}`);
  }
}

export async function loyaltyListold(data, query) {
  try {
    let loyaltyDetails = await OrderDB.paginate({
      paginate: 10,
      page: query.page,
      order: [["id", "ASC"]],
      attributes: [
        "user_id",
        "cafe_id",
        [Sequelize.fn("COUNT", Sequelize.col("orders.id")), "Count"],
      ],
      include: [
        {
          model: CafeDB,
          attributes: ["id", "cafe_name"],
          include: [
            {
              model: LoyaltyStampDB,
              attributes: ["stamp_no", "cafe_id", "offer_text", "stamp_color"],
            },
          ],
        },
      ],
      distinct: false,
      col: "status",
      group: ["cafe_id"],
      where: {
        user_id: data,
        status: 2,
        cafe_coupon_expired_time: 0,
        cafe_coupon_order_id: null,
      },
    });
    return loyaltyDetails;
  } catch (error) {
    throw new Error(error);
  }
}

// Get universal Loyalty list
export async function universalCardList(user_id) {
  try {
    return await OrderDB.count({
      where: {
        user_id: parseInt(user_id),
        // universal_coupon_order_id: null,
        universal_coupon_expired_time: 0,
        status: 2,
      },
    });
  } catch (error) {
    throw new Error(error);
  }
}

// get redeem code
export async function getRedeemCode(data) {
  try {
    let final_result = {
      universal: {},
      nonUniversal: {},
    };
    let checkTotalOrder = await OrderDB.count({
      where: {
        user_id: data.userId,
        status: 2,
      },
    });
    let nonUniversal = await LoyaltyStampDB.findOne({
      where: {
        cafe_id: parseInt(data.cafe_id),
        is_universal: 0,
      },
    });
    let universal = await LoyaltyStampDB.findOne({
      where: {
        cafe_id: null,
        is_universal: 1,
      },
    });
    let checkTotalRedeem = await OrderDB.count({
      where: {
        user_id: parseInt(data.userId),
        loyalty_stamp_id: universal.id,
        status: 2,
      },
    });
    let checkTotalCafeRedeem = await OrderDB.count({
      where: {
        user_id: parseInt(data.userId),
        loyalty_stamp_id: parseInt(data.cafe_id),
        status: 2,
      },
    });
    if (universal.stamp_no <= checkTotalOrder) {
      let deno =
        checkTotalRedeem > 0
          ? checkTotalOrder - checkTotalRedeem * universal.stamp_no
          : checkTotalOrder;
      final_result.universal = deno >= universal.stamp_no ? universal : {};
    }
    if (nonUniversal.stamp_no <= checkTotalOrder) {
      let deno =
        checkTotalCafeRedeem > 0
          ? checkTotalOrder - checkTotalCafeRedeem * nonUniversal.stamp_no
          : checkTotalOrder;
      final_result.nonUniversal =
        deno >= nonUniversal.stamp_no ? nonUniversal : {};
    }
    return final_result;
  } catch (error) {
    throw new Error(error);
  }
}

// apply redeem code
export async function applyRedeemCode(order_id) {
  try {
    let final_result = await OrderDB.update(
      {
        cafe_coupon_order_id: order_id,
      },
      {
        where: {
          id: order_id,
        },
      }
    );
    return final_result;
  } catch (error) {
    throw new Error(error);
  }
}

export async function orderListByGroup(data) {
  try {
    if ([null, undefined, "", 0].includes(data.page)) data.page = 1;
    data.page = parseInt(data.page);
    let result = await OrderDB.paginate({
      page: data.page,
      paginate: parseInt(config.config.limit),
      where: {
        group_id: parseInt(data.group_id),
      },
      include: [
        {
          model: CafeDB,
          where: {
            is_active: 1,
            deleted_at: 0,
          },
          include: [
            {
              model: UserFavouriteCafeDB,
              as: "user_favorite_cafes",
              required: false,
              where: {
                is_favorite: 1,
                user_id: parseInt(data.userId),
              },
              attributes: ["is_favorite", "user_id"],
            },
          ],
          required: false,
          attributes: [
            "id",
            "cafe_name",
            "phone",
            "latitude",
            "bio",
            "email",
            "cafe_tax",
            "longitude",
            "address",
            "banner_image",
            "postcode",
          ],
        },
        {
          model: GroupDB,
          required: false,
          attributes: ["id", "group_name", "group_profile"],
          include: [
            {
              model: GroupMemberDB,
              as: "group_details",
              attributes: ["id", "user_id"],
              include: [
                {
                  model: UserDB,
                  as: "user_data",
                  attributes: ["id", "name", "email", "profile_picture"],
                  where: { is_deleted: 0, is_active: 1, is_verified: 1 },
                },
              ],
            },
          ],
        },
      ],
      attributes: [
        "id",
        "order_number",
        "created_at",
        "updated_at",
        "order_item_array",
        "additional_note",
        "total_amount",
        "tax",
        "discount_amount",
        "order_placed_at",
        "order_collected_at",
        "status",
      ],
      order: [["id", "DESC"]],
    });
    result = JSON.stringify(result);
    result = JSON.parse(result);
    for (let index = 0; index < result.docs.length; index++) {
      const element = result.docs[index];
      element.group_coffee_run = await GroupCoffeeRunDB.findAll({
        where: {
          order_id: element.id,
        },
        include: [
          {
            model: UserDB,
            attributes: ["id", "name", "profile_picture", "email"],
            required: true,
            where: {
              is_deleted: 0,
              is_active: 1,
            },
            as: "user_data",
          },
          {
            model: UserDB,
            attributes: ["id", "name", "profile_picture", "email"],
            required: true,
            where: {
              is_deleted: 0,
              is_active: 1,
            },
            as: "creator_data",
          },
        ],
        attributes: [
          "id",
          "request_unique_id",
          "is_contributor_person",
          "is_order_place",
          "request_endtime",
        ],
      });
      element.cafe.is_favorite =
        element.cafe.user_favorite_cafes.length > 0 ? 1 : 0;
      element.cafe.distance = 0;
      element.cafe.time_sheet_data = [];
      if (typeof element.order_item_array == "string")
        element.order_item_array = JSON.parse(element.order_item_array);
      delete element.cafe.user_favorite_cafes;
    }
    return result;
  } catch (error) {
    throw new Error(error);
  }
}

//get cafe list by group id and edit time
export async function getGroupCoffeeRunOrders(coffeeRunId, groupId, user_id) {
  try {
    let groupCoffeeRun;
    groupCoffeeRun = await GroupCoffeeRunDB.findOne({
      where: {
        group_id: groupId,
      },
      order: [["id", "DESC"]],
      attributes: [
        "request_unique_id",
        "cafe_id",
        "group_id",
        "request_endtime",
        "type",
        "request_created_by",
      ],
    });

    if (groupCoffeeRun == null) {
      return null;
    }

    var inHouseOrders = [];
    var orderDetails = [];
    if (groupCoffeeRun.dataValues.type == 0) {
      inHouseOrders = await coffeeRunInHouseOrders.findAll({
        where: {
          coffee_run_id: groupCoffeeRun.dataValues.request_unique_id,
        },
        include: [
          {
            model: UserDB,
            attributes: ["id", "name"],
          },
        ],
      });
    } else {
      orderDetails = await OrderDB.findAll({
        where: {
          group_coffee_run_id: groupCoffeeRun.dataValues.request_unique_id,
        },
        attributes: [
          "order_number",
          "order_item_array",
          "total_amount",
          "tax",
          "discount_amount",
          "service_charge",
          "other_charge",
          "created_at",
          "order_collected_at",
          "transaction_id",
        ],
        include: [
          {
            model: UserDB,
            attributes: ["id", "name"],
          },
        ],
      });
    }

    const orderCollected = orderDetails.find(
      (e) => e.dataValues.order_collected_at != 0
    );
    const inHouseOrderCollected = inHouseOrders.find(
      (e) => e.dataValues.order_collected_at != 0
    );
    if (orderCollected || inHouseOrderCollected) {
      return null;
    }

    var cafeDetails = null;
    if (groupCoffeeRun.dataValues.type == 1) {
      cafeDetails = await CafeDB.findOne({
        where: {
          id: groupCoffeeRun.dataValues.cafe_id,
        },
        include: [
          {
            model: UserFavouriteCafeDB,
            as: "user_favorite_cafes",
            required: false,
            where: {
              is_favorite: 1,
              user_id: parseInt(user_id),
            },
            attributes: ["is_favorite"],
          },
        ],
        attributes: [
          "id",
          "cafe_name",
          "banner_image",
          "address",
          "latitude",
          "longitude",
        ],
      });
    }

    const groupDetails = await GroupDB.findOne({
      where: {
        id: groupCoffeeRun.dataValues.group_id,
      },
      attributes: ["group_name", "group_profile"],
      include: [
        {
          model: GroupMemberDB,
          as: "group_details",
          attributes: ["user_id"],
          include: [
            {
              model: UserDB,
              as: "user_data",
              attributes: ["name", "profile_picture", "email"],
            },
          ],
        },
      ],
    });

    return {
      cafe_details: cafeDetails,
      group_details: groupDetails,
      coffe_run: groupCoffeeRun,
      order_details: orderDetails,
      in_house_orders: inHouseOrders,
    };
  } catch (error) {
    throw new Error(error);
  }
}

//mark order collected
export async function markOrderCollected(coffeeRunId, type) {
  try {
    let updatedOrders;
    if (type == 1) {
      updatedOrders = await OrderDB.update(
        {
          order_collected_at: moment().unix(),
          status: 2,
        },
        {
          where: {
            group_coffee_run_id: coffeeRunId,
            status: 1,
          },
        }
      );
    } else {
      updatedOrders = await coffeeRunInHouseOrders.update(
        {
          order_collected_at: moment().unix(),
        },
        {
          where: {
            coffee_run_id: coffeeRunId,
          },
        }
      );
    }

    if (updatedOrders[0] === 0) {
      return null;
    }
    //Update endtime if it is greater than current time
    const currentTimeUnix = moment().unix();
    await GroupCoffeeRunDB.update(
      { request_endtime: currentTimeUnix },
      {
        where: {
          request_unique_id: coffeeRunId,
          request_endtime: { [Op.gt]: currentTimeUnix },
        },
      }
    );
    return updatedOrders[0];
  } catch (error) {
    throw new Error(error);
  }
}

export async function getPastCoffeeRunOrders(user_id) {
  try {
    const cafeOrders = await GroupCoffeeRunDB.findAll({
      where: {
        user_id: user_id,
        [Op.or]: [
          {
            in_house_order_id: {
              [Op.ne]: null,
            },
          },
          {
            order_id: {
              [Op.ne]: null,
            },
          },
        ],
      },
      attributes: ["id", "user_id", "order_id", "in_house_order_id"],
      order: [["id", "DESC"]],
      include: [
        {
          model: GroupDB,
          attributes: ["group_name"],
        },
        {
          model: CafeDB,
          attributes: ["cafe_name"],
        },
        {
          model: OrderDB,
          required: false,
          attributes: [
            "id",
            "order_item_array",
            "total_amount",
            "tax",
            "discount_amount",
            "created_at",
            "order_collected_at",
          ],
        },
        {
          model: coffeeRunInHouseOrders,
          required: false,
          attributes: [
            "id",
            "in_house_order_items",
            "customized_order",
            "created_at",
            "order_collected_at",
          ],
        },
      ],
    });
    return cafeOrders;
  } catch (error) {
    throw new Error(error);
  }
}

//Non Universal Card List
export async function nonuniversalCardList(user_id, cafe_id) {
  try {
    // Initialize the processed orders counter
    let processedOrdersCount = 0;

    // Fetch the loyalty stamp details
    const stamp = await LoyaltyStampDB.findOne({
      where: {
        cafe_id: parseInt(cafe_id),
      },
    });

    // Fetch the excluded items for this stamp
    const excludedItems = await CafeloyaltyStampExcludedItemsDB.findAll({
      where: {
        stamp_id: stamp.id,
      },
    });

    // Extract all item_ids from the results (make sure it's an array)
    const excludedItemsId = excludedItems.map((item) => item.item_id); // Use item_id instead of cafe_menu_id

    // Fetch all orders based on the given criteria
    const orders = await OrderDB.findAll({
      where: {
        user_id: parseInt(user_id),
        cafe_id: parseInt(cafe_id),
        universal_coupon_expired_time: 0,
        // status: 2,
      },
    });

    // Loop through each order to get item details
    for (const order of orders) {
      // Parse the order's item array
      const orderItemArray = JSON.parse(order.order_item_array);
      const itemDetailsPromises = orderItemArray.map(async (orderItem) => {
        const itemDetails = await CafeMenuItemDB.findOne({
          where: {
            id: orderItem.item_id, // Directly use item_id
            cafe_id: parseInt(cafe_id), // Optional: include cafe_id for more precise matching
          },
        });

        return itemDetails.dataValues; // Extract item details
      });

      // Wait for all item detail queries to complete
      const itemDetailsArray = await Promise.all(itemDetailsPromises);

      // Now check if all items in the order belong to the excludedItemsId
      const allItemsExcluded = itemDetailsArray.every(
        (itemDetails) => excludedItemsId.includes(itemDetails.id) // Use item_id from item details
      );

      if (allItemsExcluded) {
        continue; // Skip this order
      }

      // If order is not skipped, increment the processed orders counter
      processedOrdersCount++;
    }

    // Return the count of processed orders
    console.log(processedOrdersCount);
    return processedOrdersCount;
  } catch (error) {
    console.log(error);
    throw new Error(error);
  }
}

export async function nonuniversalCardListold(user_id, cafe_id) {
  try {
    return await OrderDB.count({
      where: {
        user_id: parseInt(user_id),
        cafe_id: parseInt(cafe_id),
        // universal_coupon_order_id: null,
        universal_coupon_expired_time: 0,
        status: 2,
      },
    });
  } catch (error) {
    throw new Error(error);
  }
}

//Get cafe universal stamp
export async function getnonUniversalStamp(cafe_id) {
  try {
    let nonUniversal = await LoyaltyStampDB.findOne({
      where: {
        cafe_id: cafe_id,
        is_universal: 0,
      },
    });
    return nonUniversal;
  } catch (error) {
    throw new Error(error);
  }
}

//Categories coupon can be applied
export async function getcategoriesForCoupon(cafe_id) {
  try {
    let cafeStamp = await LoyaltyStampDB.findOne({
      where: {
        cafe_id: cafe_id,
      },
    });
    let categories;
    categories = cafeStamp.stamp_applicable_to_categories;
    return categories;
  } catch (error) {
    throw new Error(error);
  }
}