const moment = require('moment')
const uniqid = require('uniqid');
//packages
const Request = require('../models/request')
const { getAllWorkflow } = require('../models/staffmember')
const Settings = require("../models/settings");
const Route = require('../models/routes')
const ClientChat = require('../models/clientChat')
const Product = require('../models/product')
const Project = require('../models/Projects')
const SplitManagementController = require('./SplitManagementController')
const ProjectTask = require('../models/projectTask')
const CustomerOrder = require('../models/customerOrder')
const CustomerOrderProduct = require('../models/customerOrderProduct')
const RequestPartner = require('../models/requestPartners')
const RequestUpdates = require('../models/requestUpdates')
const Admin = require('../models/admin')
const RequestActivity = require('../models/RequestActivity')
const Community = require('../models/community')
const { empty } = require('../Helpers/Common')
const { filteredSignwiseInstalations } = require('../Helpers/SplitManagementHelper')
const Client = require("../models/client")
const OnboardWorkFlowFields = require("../models/onboardWorkflowFields")
const OnboardWorkFlowStages = require("../models/onboardWorkFlowStages")
const OnboardWorkFlowQuestionnaires = require("../models/onboardWorkflowQuestionnaires")
const sgMail = require('@sendgrid/mail')
const { SENDGRID_API_KEY, BASE_URL, portalName, SEND_EMAIL, TWILIO_PHONE, DOMAIN } = require("../constants/constant")
const { rfqEmail, designersEmail, existingDesignerEmail, removedDesignerEmail, remainingDesignerInfoEmail, yourDesignAcceptedEmailTemplate, yourDesignRejectedEmailTemplate, newEstimateCreatedEmailTemplate, newJobCreatedEmailTemplate, finalInvoiceApprovedEmailTemplate, deletedItemEmailTemplate, designOrderCompletionEmail } = require('../constants/emailTemplates');
const RequestProposals = require('../models/requestProposals')
const Teams = require("../models/teams");
const SplitEstimate = require("../models/SplitEstimate");
const JobCost = require("../models/jobCost")
const Recipe = require("../models/recipe")
const Proposals=require("../models/proposal")
const Partners = require('../models/client.js')
const Routes = require('../models/routes.js')
const crypto = require("crypto");
const Installation = require(`../models/installation`);
const SplitManagementModel = require('../models/SplitManagementModel');
let u_id = uniqid()
const {
  clientId,
  clientSecret,
  redirectUri,
  authCode,
  companyId,
  accessToken,
  refreshToken,
  getNewAccessToken,
  realmId,
  ensureValidAccessToken,
  PRODUCTION,
  discountAccRefId,
  getAccounts,
  materialExpAccId,
  materialAssetAccId,
  materialIncAccId,
  serviceIncAccId,
  serviceExpAccId,
  catalogIncAccId,
  catalogExpAccId,
  AVALARA_ACCOUNT_ID,
  AVALARA_LICENSE_KEY,
  AVALARA_BASE_URL,
  AVALARA_COMPANY_ID,
  getAccountIdByName,
  findAccountIdByAcctNum,
  locationInventoryCode,
  departmentId
} = require('../quickbook/quickbook.js');
const axios = require('axios')
const QuickBooks = require('node-quickbooks');
const { values } = require('@babel/runtime/regenerator');
const { io: ioClient } = require('socket.io-client');
const REST_API_IO = DOMAIN === "localhost" ? "http://localhost:4000" : 'https://socket.signwisesolutions.com';
const fs = require('fs');
const path = require('path');
const { exec } = require('child_process');
const { execSync } = require('child_process');
const { promisify } = require('util');
const execAsync = promisify(exec);
const { PDFDocument } = require('pdf-lib');
const { RequestAttachmentImages, RequestAttachments, addRequestAttachmentImage, getRequestAttachmentByIdForConversion, getDesignOrderAttachmentByIdForConversion, getRequestAttachmentImagesByDocumentId, deleteRequestAttachmentImagesByDocumentId, updateRequestAttachmentImagesStatusByDocumentId, DesignOrderAttachments, getEditedPdfAttachmentBySource, updateEditedPdfAttachmentBySource } = require('../models/documentRepository');
// const UNITS = [
//   { id: 1, name: 'Square feet', short_name: 'Sq ft', unit_id: 1 },
//   { id: 2, name: 'Linear feet', short_name: 'Lin ft', unit_id: 1 },
//   { id: 3, name: 'Square inches', short_name: 'Sq in', unit_id: 1 },
//   { id: 4, name: 'Unit', short_name: 'Unit', unit_id: 2 },
//   { id: 5, name: 'Gallons', short_name: 'Gal', unit_id: 3 },
//   { id: 6, name: 'Quarts', short_name: 'Qt', unit_id: 3 },
//   { id: 7, name: 'Milliliter', short_name: 'ml', unit_id: 3 },
//   { id: 8, name: 'Ounce', short_name: 'oz', unit_id: 3 },
//   { id: 9, name: 'Kilogram', short_name: 'kg', unit_id: 4 },
//   { id: 10, name: 'Gram', short_name: 'g', unit_id: 4 },
//   { id: 11, name: 'Milligram', short_name: 'mg', unit_id: 4 },
// ];

const UNITS = [
  { id: 1, name: 'Square feet', short_name: 'Sq ft', unit_id: 1 },
  { id: 2, name: 'Linear feet', short_name: 'Lin ft', unit_id: 1 },
  { id: 3, name: 'Square inches', short_name: 'Sq in', unit_id: 1 },
  { id: 4, name: 'Unit', short_name: 'Unit', unit_id: 2 },
  { id: 5, name: 'Gallons', short_name: 'Gal', unit_id: 3 },
  { id: 6, name: 'Quarts', short_name: 'Qt', unit_id: 3 },
  { id: 7, name: 'Milliliter', short_name: 'ml', unit_id: 3 },
  { id: 8, name: 'Liter', short_name: 'L', unit_id: 3 },
  { id: 9, name: 'Pint', short_name: 'pt', unit_id: 3 },
  { id: 10, name: 'Half Pint', short_name: 'half-pt', unit_id: 3 },
  { id: 11, name: 'Ounce', short_name: 'oz', unit_id: 3 },
  { id: 12, name: 'Kilogram', short_name: 'kg', unit_id: 4 },
  { id: 13, name: 'Gram', short_name: 'g', unit_id: 4 },
  { id: 14, name: 'Milligram', short_name: 'mg', unit_id: 4 },
  { id: 15, name: 'Each', short_name: 'EA', unit_id: 2 },

];

// ✅ Function to Get Dynamic Conversion Factor Based on Units & Width
const getConversionFactor = (toUnitId, fromUnitId, width = 1) => {
  if (!toUnitId) {
    toUnitId = fromUnitId;
  }
  if (!fromUnitId) {
    fromUnitId = toUnitId;
  }
  const conversionFactors = {
      1: { 1: 1, 2: width, 3: 1 / 144 }, // Square Feet → (itself, Linear Feet by width, Square Inches)
      2: { 1: 1 / width, 2: 1, 3: 1 / (width * 144) }, // Linear Feet → (Square Feet by width, itself, Square Inches)
      3: { 1: 144, 2: width * 144, 3: 1 }, // Square Inches → (Square Feet, Linear Feet, itself)
      4: { 4: 1 }, // Unit → itself (for standalone items)
      5: { 5: 1, 6: 1 / 4, 7: 1 / 3785.41, 8: 1 / 128 }, // Gallons → (itself, Quarts, Milliliters, Ounces)
      6: { 5: 4, 6: 1, 7: 1 / 946.35, 8: 1 / 32 }, // Quarts → (Gallons, itself, Milliliters, Ounces)
      7: { 5: 3785.41, 6: 946.35, 7: 1, 8: 29.5735 }, // Milliliters → (Gallons, Quarts, itself, Ounces)
      8: { 5: 128, 6: 32, 7: 1 / 29.5735, 8: 1 }, // Ounces → (Gallons, Quarts, Milliliters, itself)
      9: { 9: 1, 10: 1 / 1000, 11: 1 / 1000000 }, // Kilograms → (itself, Grams, Milligrams)
      10: { 9: 1000, 10: 1, 11: 1 / 1000 }, // Grams → (Kilograms, itself, Milligrams)
      11: { 9: 1000000, 10: 1000, 11: 1 }, // Milligrams → (Kilograms, Grams, itself)
  };

  return conversionFactors[fromUnitId]?.[toUnitId] || 1; // Default to 1 if invalid
};

// ✅ Function to Convert Unit Cost Based on Unit Type
const getConvertedUnitCost = (originalCost, fromUnitId, toUnitId, width = 1) => {
  const conversionFactor = getConversionFactor(fromUnitId, toUnitId, width);
  if (conversionFactor === 0) {
      console.error("Conversion factor is zero, invalid conversion.");
      return originalCost; // Default to original cost if invalid
  }
  return originalCost / conversionFactor; // Adjust cost based on conversionF
};

const nodemailer = require('nodemailer');
const { sendMessageToPhoneNumber } = require('./teams.js');
const { FACILITY_ID, getOtherInstallationDatabases } = require('../config/SplitManagementDb.js');
const { addBuildOrderSalesOrderToOtherInstallation, addBuildOrderSalesOrderToCurrentInstallation, deleteBuildOrderFromOtherInstallations, transferProductStockToOtherInstallation, addMirrorOrderSalesOrderToOtherInstallation, updateMirrorOrderSalesOrderToOtherInstallation, deleteMirrorOrderFromOtherInstallations, updateMirrorOrderStatusFromOtherInstallations } = require('./SplitManagementController.js');
const { validateInventoryItemsInQbForPo, validateClientInQb, fetchTaxAmountFromEstimateById, findItemByName, findItemById, findItemIdByName } = require('./qbController.js');

const transporter = nodemailer.createTransport({
  host: 'mail.signwisesolutions.com', 
  port: 465, 
  secure: true, 
  auth: {
    user: 'noreply@signwisesolutions.com', 
    pass: 'signwise@2024', 
  },
});


let UniqueEstimateId =null
 let doc_number = null

const getAllRequests = async (req, res) => {
  try {
    let userId = req.body.userId
    let userType = req.body.userType
    let page = req.body.page
    let search = req.body.search
    let rowPerpage = req.body.rowsPerPage
    let where=req.body.where
    let sort = req.body.sort
    let fromDate = req.body.from
    let toDate = req.body.to
    let dueData = req.body.dueData
    let filterCommunity = req.body.filterCommunity
    let filterClient = req.body?.filterClient
    let filterProjectType = req.body?.filterProjectType
    let filterProjectCategory = req.body?.filterProjectCategory

    let data = await Request.getAllRequests(null, userId, userType, page, rowPerpage, search,where,sort,fromDate,toDate, filterCommunity, dueData ,filterClient, filterProjectType, filterProjectCategory)
   
    let materials = await Product.getAllMaterials(true);
    let requestTeam = await Request.getAllTeamBatch()
    let services = await Request.getServices(null)
    let products = await Product.getAllProducts(false);
        products = products.filter(row=>row.inhouse!==6 && (row.inhouse===1 || row.inhouse===4 || row.inhouse === 5 || row.inhouse === 0))
        // products = products.filter(row=>row.inhouse===1)  // uncomment this line if it causes any other problem
    let labourCostServices = await Product.getAllLabourCostServices();
        // labourCostServices = labourCostServices.filter(row=>row.inhouse===1)
    let community = [];
    if(userType != 3){
      community = await Community.getAllCommunities();
    }else{
      let clientDetails = await Client.getPartnerByUserId(userId);
      console.log('clientDetails----',clientDetails)
      if(clientDetails){
        community = await Community.getAllCommunitiesByClient(false,clientDetails?.id);
      }
    }
    let workflows = await Client.getAllWorkflows();
    for (const data of products) {
      let result = await Product.getServiceByProduct(data.id);
      data.services = result
    }
    for (const data of products){
      let result = await Product.getMaterialByProduct(data.id)
        data.materials = result || []
        if (data?.inhouse == 0) {
          const price = Number(data?.sub_contract_price) || 0;
          const markup = Number(data?.markup) || 0;
          data.actual_retail_price = price * (1 + markup / 100);
        }
        data.facility_id = FACILITY_ID
    }
    let allProposals = await Proposals.getAllAwardedProposals()
    let clients = await Client.getAllClientsDataFilters();
    const staffMembers = await getAllWorkflow();
    // const otherInstallationProducts = await SplitManagementModel.getAllProductsFromMultipleDatabase(true);
    // const currentFacilitySplitManagementSettings = await SplitManagementModel.getAllSplitManagementSettings();
    // const otherInstallationSplitManagementSettings = await SplitManagementModel.getAllSplitManagementSettingsFromOtherInstallations();
   
    res.send({
      status: 1,
      data: data,
      staffMembers: staffMembers || [],
      requestTeam: requestTeam,
      services: services,
      products: products,
      community: community,
      clients:clients,
      labourCostServices,
      workflows,
      allProposals,
      materials,
      // otherInstallationProducts,
      // currentFacilitySplitManagementSettings,
      // otherInstallationSplitManagementSettings,
      message: 'Request fetched successful'
    })
  } catch (error) {
    console.log(error)
    res.send({ status: 0, message: 'Request fetched failed' });
  }
}

const getAllRequestFilters = async (req, res) => {
  try {
    const userId = req.body.userId
    const userType = req.body.userType
    const isQuotation = req.body.isQuotation
    const From = req.body.From
    const teamId = req.body.teamId
    let community = []
    if (userType != 3) {
      community = await Community.getAllCommunities(null,null,null,null,null,null,isQuotation,From,teamId,{userType:userType,filterType:true})
    } else {
      const clientDetails = await Client.getPartnerByUserId(userId)
      if (clientDetails) {
        community = await Community.getAllCommunitiesByClient(false, clientDetails?.id,userType,From,teamId)
      }
    }

    const clients = await Client.getAllClientsDataFilters(From,teamId)
    const staffMembers = await getAllWorkflow()

    res.send({
      status: 1,
      staffMembers: staffMembers || [],
      community: community,
      clients: clients,
      message: 'Request filters fetched successful'
    })
  } catch (error) {
    console.log(error)
    res.send({ status: 0, message: 'Request filters fetched failed' })
  }
}

// Quotation: filters endpoint (clients and communities only)
const getAllQuotationFilters = async (req, res) => {
  try {
    const userId = req.body.userId
    const userType = req.body.userType

    let community = []
    if (userType != 3) {
      community = await Community.getAllCommunities()
    } else {
      const clientDetails = await Client.getPartnerByUserId(userId)
      if (clientDetails) {
        community = await Community.getAllCommunitiesByClient(false, clientDetails?.id)
      }
    }

    const clients = await Client.getAllClientsDataFilters()

    res.send({
      status: 1,
      community: community,
      clients: clients,
      message: 'Quotation filters fetched successful'
    })
  } catch (error) {
    console.log(error)
    res.send({ status: 0, message: 'Quotation filters fetched failed' })
  }
}

const getAllRequestsForAdd = async (req, res) => {
  try {
    let userId = req.body.userId
    let userType = req.body.userType
    let page = req.body.page
    let search = req.body.search
    let rowPerpage = req.body.rowsPerPage
    let where=req.body.where
    let sort = req.body.sort
    let fromDate = req.body.from
    let toDate = req.body.to
    let dueData = req.body.dueData
    let filterCommunity = req.body.filterCommunity
    let filterClient = req.body?.filterClient
    let filterProjectType = req.body?.filterProjectType
    let isSplitOrder = req.body?.isSplitOrder
    let materials = await Product.getAllMaterials(false);
    let requestTeam = await Request.getAllTeamBatch()
    let services = await Request.getServices(null)
    let products = await Product.getAllProductsNew(false);
    products = products.filter(row=>row.inhouse===1 || row.inhouse===4 || row.inhouse === 5 || row.inhouse === 0 || row.inhouse === 7)
    let labourCostServices = await Product.getAllLabourCostServices();
    // for (const data of labourCostServices) {
    //     data.facility_id = FACILITY_ID
    // }
    // for (const data of products) {
    //   let result = await Product.getServiceByProduct(data.id);
    //   data.services = result

    //   let result1 = await Product.getMaterialByProduct(data.id)
    //     data.materials = result1 || []
    //     data.facility_id = FACILITY_ID
    //     if (data?.inhouse == 0) {
    //       const price = Number(data?.sub_contract_price) || 0;
    //       const markup = Number(data?.markup) || 0;
    //       data.actual_retail_price = price * (1 + markup / 100);
    //     }
    // }
    products.forEach((p) => {
      // Process in-house products
      if (p?.inhouse === 0) {
        const price = Number(p?.sub_contract_price) || 0;
        const markup = Number(p?.markup) || 0;
        p.actual_retail_price = price * (1 + markup / 100);
      }
    
      p.facility_id = FACILITY_ID;
    });
    let allProposals = await Proposals.getAllAwardedProposals()

    let otherInstallationProducts = []
    let otherInstallationServices = []
    if(isSplitOrder){
      otherInstallationProducts = await SplitManagementModel.getAllProductsFromMultipleDatabase(true);
      otherInstallationServices = await SplitManagementModel.getAllLabourCostServicesFromOtherInstallations();
    }
    let currentFacilitySplitManagementSettings = await SplitManagementModel.getAllSplitManagementSettings();
    let otherInstallationSplitManagementSettings = await SplitManagementModel.getAllSplitManagementSettingsFromOtherInstallations();

    res.send({
      status: 1,
      requestTeam: requestTeam,
      services: services,
      products: products,
      labourCostServices,
      allProposals,
      materials,
      otherInstallationProducts,
      otherInstallationServices,
      currentFacilitySplitManagementSettings,
      otherInstallationSplitManagementSettings,
      message: 'Request fetched successful'
    })
  } catch (error) {
    console.log(error)
    res.send({ status: 0, message: 'Request fetched failed' });
  }
}

const getAllRequestsNew = async (req, res) => {
  try {
    let userId = req.body.userId
    let userType = req.body.userType
    let page = req.body.page
    let search = req.body.search
    let rowPerpage = req.body.rowsPerPage
    let where=req.body.where
    let sort = req.body.sort
    let fromDate = req.body.from
    let toDate = req.body.to
    let dueData = req.body.dueData
    let filterCommunity = req.body.filterCommunity
    let filterClient = req.body?.filterClient
    let filterProjectType = req.body?.filterProjectType
    let filterUser = req.body?.filterUser
    let splitOrder = req.body?.splitOrder
    let filterProjectCategory = req.body?.filterProjectCategory
    
    // ORIGINAL CODE - BACKUP (Slow performance due to N+1 queries)
    // let data = await Request.getAllRequests(null, userId, userType, page, rowPerpage, search,where,sort,fromDate,toDate, filterCommunity, dueData,filterClient, filterProjectType, filterUser)
    
    // NEW OPTIMIZED VERSION - Uses bulk fetching and parallel processing
    let data = await Request.getAllRequestsOptimized(null, userId, userType, page, rowPerpage, search,where,sort,fromDate,toDate, filterCommunity, dueData,filterClient, filterProjectType, filterUser,splitOrder,filterProjectCategory)
   
    let result = data.data
    
    // ORIGINAL CODE - BACKUP (N+1 Query Problem)
    // This code was causing performance issues by executing individual queries for each request
    // for (const data of result) {
    //   let attachment = await Request.getAllRequestAttachmentsForCards(data.id,data.isFromDesignOrder)
    //   data.attachment=attachment || []
    // }

    // OPTIMIZED VERSION - Using new optimized model with bulk fetching
    // All attachments, designs, and partners are now fetched in parallel within the model
    
    // Fetch staff members for the filter
    // const staffMembers = await getAllWorkflow();
    
    res.send({
      status: 1,
      data: data,
      // staffMembers: staffMembers || [],
      message: 'Requests fetched successfully'
    })
  } catch (error) {
    console.log(error)
    res.send({ status: 0, message: 'Failed to fetch requests' });
  }
}

const getAllRequestsTotalCount = async (req, res) => {
  try {
    let userId = req.body.userId
    let userType = req.body.userType
    let page = req.body.page
    let search = req.body.search
    let rowPerpage = req.body.rowsPerPage
    let where = req.body.where
    let sort = req.body.sort
    let fromDate = req.body.from
    let toDate = req.body.to
    let dueData = req.body.dueData
    let filterCommunity = req.body.filterCommunity
    let filterClient = req.body?.filterClient
    let filterProjectType = req.body?.filterProjectType
    let filterUser = req.body?.filterUser
    let filterProjectCategory = req.body?.filterProjectCategory

    const countRes = await Request.getAllRequestsTotalCount(null, userId, userType, page, rowPerpage, search, where, sort, fromDate, toDate, filterCommunity, dueData, filterClient, filterProjectType, filterUser, filterProjectCategory)
    res.send({ status: 1, data: countRes })
  } catch (error) {
    console.log(error)
    res.send({ status: 0, message: 'Failed to fetch total count' });
  }
}

const updateSalesOrderHoldStatus = async (req, reply) => {
    try {
        let { request_id, userId, reason = '' } = req.body
        let currentDate = moment().unix();
        let project = {}
        const request = await Request.getRequestById(request_id)
        const isSplitOrder = request[0]?.is_split_order
        const originRequestId = request_id || request[0]?.id
        const facilityTypes = (request[0]?.facility_type || "")
        .split(",")
        .map(v => v.trim())
        .filter(v => v !== "");
      const targetFacilityId = facilityTypes.filter(id => id !== String(FACILITY_ID));
      const project_category = request[0]?.project_category !== 15 ? true : false;

        console.log('requestrequest', request)
        if(request[0]?.hold==0){
            project['hold'] = 1;
            project['hold_reason'] = reason
        }else{
            project['hold'] = 0;
            project['hold_reason'] = reason

        }
        let jobHoldData = {
            hold: project?.hold,
            reason: project?.hold_reason,
        }
        project['timestamp'] = currentDate;
        let project_id = request[0]?.project_id
        console.log('projecttttt', project)
        let updateRequest = await Request.updateRequestById(request_id,project)
        let updateProject = null
        if(project_id){
          updateProject = await Project.updateProject(project_id,jobHoldData)
        }else{
          updateProject = true
        }

        if(isSplitOrder && originRequestId && targetFacilityId && project_category){
          const updateHoldStatus = await SplitManagementController.updateTargetedFacilityHoldStatusFromOtherInstallations(originRequestId, targetFacilityId, project,jobHoldData);
          console.log('updateHoldStatus32312312', updateHoldStatus)
        }
        
        console.log('updateRequestupdateRequest', updateRequest)
        if (updateRequest && updateProject) {
            let notificationMsg = ""
            if(project?.hold==1){
            notificationMsg = `The sales order job "${request[0]?.name}" has been moved to Hold`
            } else{
            notificationMsg = `The sales order job "${request[0]?.name}" has been removed from Hold`
            }
            console.log('notificationMsg', notificationMsg)
            let requestActivity = await RequestActivity.addRequestUpdate({
              request_id: request_id,
              activity: notificationMsg,
              added_by: userId,
              timestamp: currentDate
            });
            let notificationUrl = "sales-order-job/view-project/" + request_id;
            let notificationData = {};
            const incrementResult_User = await Admin.incrementField(userId, "noti_count", 1);
            let notificationData_user = {
                user_id: userId,
                url: notificationUrl,
                timestamp: currentDate,
                message: notificationMsg,
                status: 1,
                from_id: userId,
            };
            const addNotificationResult_user = await Admin.addNotification(notificationData_user);
            let adminIds = await Admin.getAllAdminIds();

            for (const adminId of adminIds) {
                if (adminId != userId) {
                    const incrementResult = await Admin.incrementField(adminId, "noti_count", 1);
                    notificationData = {
                        user_id: adminId,
                        url: notificationUrl,
                        timestamp: currentDate,
                        message: notificationMsg,
                        status: 1,
                        from_id: userId,
                    };
                    const addNotificationResult = await Admin.addNotification(notificationData);
                }
            }
            reply.send({ status: 1, message: notificationMsg });
        }
        else {
            reply.send({ status: 0, message: "Failed to update Sales order status " });
        }
    }
    catch (error) {
        reply.status(500).send({ message: "Server error:" + error })
    }
}

//previos
// const getAllQuotations= async (req, res) => {
//   try {
//     let userId = req.body.userId
//     let userType = req.body.userType
//     let page = req.body.page
//     let search = req.body.search
//     let rowPerpage = req.body.rowsPerPage
//     let where=req.body.where
//     let sort = req.body.sort
//     let fromDate = req.body.from
//     let toDate = req.body.to
//     let data = await Request.getAllQuotations(null, userId, userType, page, rowPerpage, search,where,sort, fromDate, toDate)
//     let allProposals = await Proposals.getAllAwardedProposals()
//     let requestTeam = await Request.getAllTeamBatch()
//     let services = await Request.getServices(null)
//     let products = await Product.getAllProducts(false);
//     products = products.filter(row => row.inhouse === 1)
//     let labourCostServices = await Product.getAllLabourCostServices();
//     labourCostServices = labourCostServices.filter(row => row.inhouse === 1)
//     let community = await Community.getAllCommunities();
//     let workflows = await Client.getAllWorkflows();
//     for (const data of products) {
//       let result = await Product.getServiceByProduct(data.id);

//       data.services = result
//     }
//     let materials = await Product.getAllMaterials(true);
//     res.send({
//       status: 1,
//       data: data,
//       requestTeam: requestTeam,
//       services: services,
//       products: products,
//       community: community,
//       labourCostServices,
//       materials,
//       workflows,
//       allProposals,
//       message: 'Request fetched successful'
//     })
//   } catch (error) {
//     console.log(error)
//     res.send({ status: 0, message: 'Request fetched failed' });
//   }
// }

const getAllQuotations= async (req, res) => {
  try {
    let userId = req.body.userId
    let userType = req.body.userType
    let page = req.body.page
    let search = req.body.search
    let rowPerpage = req.body.rowsPerPage
    let where=req.body.where
    let sort = req.body.sort
    let fromDate = req.body.from
    let toDate = req.body.to
    let filterClient = req.body.filterClient
    let filterProjectType = req.body.filterProjectType
    let filterUser = req.body.filterUser
    let dueData = req.body.dueData
    let filterCommunity = req.body.filterCommunity
    let data = await Request.getAllQuotations(null, userId, userType, page, rowPerpage, search,where,sort, fromDate, toDate,filterClient, filterProjectType, filterUser, dueData, filterCommunity)
    let allProposals = await Proposals.getAllAwardedProposals()
    let requestTeam = await Request.getAllTeamBatch()
    let services = await Request.getServices(null)
    let products = await Product.getAllProducts(false);
    products = products.filter(row => row.inhouse!==6 && (row.inhouse === 1 || row.inhouse === 5 || row.inhouse === 4 || row.inhouse === 0))
    let labourCostServices = await Product.getAllLabourCostServices();
    // labourCostServices = labourCostServices.filter(row => row.inhouse === 1)
    let community = await Community.getAllCommunities();
    let workflows = await Client.getAllWorkflows();
    for (const data of products) {
      let result = await Product.getServiceByProduct(data.id);

      data.services = result
    }
    for (const data of products){
      let result = await Product.getMaterialByProduct(data.id)
        data.materials = result || []
        if (data?.inhouse == 0) {
          const price = Number(data?.sub_contract_price) || 0;
          const markup = Number(data?.markup) || 0;
          data.actual_retail_price = price * (1 + markup / 100);
        }
    }
    let materials = await Product.getAllMaterials(true);
    let clients = await Client.getAllClientsDataFilters();
    res.send({
      status: 1,
      data: data,
      requestTeam: requestTeam,
      services: services,
      products: products,
      community: community,
      clients:clients,
      labourCostServices,
      materials,
      workflows,
      allProposals,
      message: 'Request fetched successful'
    })
  } catch (error) {
    console.log(error)
    res.send({ status: 0, message: 'Request fetched failed' });
  }
}

// const getAllQuotationsAdd= async (req, res) => {
//   try {
//     let userId = req.body.userId
//     let userType = req.body.userType
//     let page = req.body.page
//     let search = req.body.search
//     let rowPerpage = req.body.rowsPerPage
//     let where=req.body.where
//     let sort = req.body.sort
//     let fromDate = req.body.from
//     let toDate = req.body.to
//     let filterClient = req.body.filterClient
//     let filterProjectType = req.body.filterProjectType
//     let allProposals = await Proposals.getAllAwardedProposals()
//     let requestTeam = await Request.getAllTeamBatch()
//     let services = await Request.getServices(null)
//     let products = await Product.getAllProducts(false);
//     products = products.filter(row => row.inhouse === 1 || row.inhouse === 5 || row.inhouse === 4 || row.inhouse === 0 || row.inhouse === 7)
//     let labourCostServices = await Product.getAllLabourCostServices();
//     // let community = await Community.getAllCommunities();
//     for (const data of products) {
//       let result = await Product.getServiceByProduct(data.id);
//       data.services = result

//       let result1 = await Product.getMaterialByProduct(data.id)
//         data.materials = result1 || []
//         data.facility_id = FACILITY_ID
//         if (data?.inhouse == 0) {
//           const price = Number(data?.sub_contract_price) || 0;
//           const markup = Number(data?.markup) || 0;
//           data.actual_retail_price = price * (1 + markup / 100);
//         }
//     }
//     let materials = await Product.getAllMaterials(false);
//     // let clients = await Client.getAllClientsDataFilters();
//     res.send({
//       status: 1,
//       requestTeam: requestTeam,
//       services: services,
//       products: products,
//       // community: community,
//       // clients:clients,
//       labourCostServices,
//       materials,
//       allProposals,
//       message: 'Request fetched successful'
//     })
//   } catch (error) {
//     console.log(error)
//     res.send({ status: 0, message: 'Request fetched failed' });
//   }
// }
const getAllQuotationsAdd = async (req, res) => {
  try {
    const {
      userId,
      userType,
      page,
      search,
      rowsPerPage,
      where,
      sort,
      from: fromDate,
      to: toDate,
      filterClient,
      filterProjectType
    } = req.body;

    /* ==========================================
       1️⃣ Fetch Independent Data In Parallel
       ========================================== */

    const [
      allProposals,
      requestTeam,
      services,
      rawProducts,
      labourCostServices,
      materials
    ] = await Promise.all([
      Proposals.getAllAwardedProposals(),
      Request.getAllTeamBatch(),
      Request.getServices(null),
      // Product.getAllProducts(false),
      Product.getAllProductsNew(false),
      Product.getAllLabourCostServices(),
      Product.getAllMaterials(false)
    ]);

    /* ==========================================
       2️⃣ Filter Products
       ========================================== */

    let products = rawProducts.filter(row =>
      row.inhouse === 1 ||
      row.inhouse === 5 ||
      row.inhouse === 4 ||
      row.inhouse === 0 ||
      row.inhouse === 7
    );

    /* ==========================================
       3️⃣ BULK Fetch Services + Materials
       ========================================== */

    // if (products.length) {
    //   const productIds = products.map(p => p.id);

    //   const [allServices, allMaterials] = await Promise.all([
    //     Product.getServicesForBulkProductsByIds(productIds),
    //     Product.getMaterialsForBulkProductsByIds(productIds)
    //   ]);

    //   // Create fast lookup maps
    //   const serviceMap = {};
    //   const materialMap = {};

    //   for (const s of allServices) {
    //     if (!serviceMap[s.product_id]) serviceMap[s.product_id] = [];
    //     serviceMap[s.product_id].push(s);
    //   }

    //   for (const m of allMaterials) {
    //     if (!materialMap[m.product_id]) materialMap[m.product_id] = [];
    //     materialMap[m.product_id].push(m);
    //   }

    //   products = products.map(product => {
    //     product.services = serviceMap[product.id] || [];
    //     product.materials = materialMap[product.id] || [];
    //     product.facility_id = FACILITY_ID;

    //     if (product?.inhouse == 0) {
    //       const price = Number(product?.sub_contract_price) || 0;
    //       const markup = Number(product?.markup) || 0;
    //       product.actual_retail_price = price * (1 + markup / 100);
    //     }

    //     return product;
    //   });
    // }

    products.forEach((p) => {
      // Process in-house products
      if (p?.inhouse === 0) {
        const price = Number(p?.sub_contract_price) || 0;
        const markup = Number(p?.markup) || 0;
        p.actual_retail_price = price * (1 + markup / 100);
      }
    
      p.facility_id = FACILITY_ID;
    });
    /* ==========================================
       4️⃣ Return Response
       ========================================== */

    return res.send({
      status: 1,
      requestTeam,
      services,
      products,
      labourCostServices,
      materials,
      allProposals,
      message: 'Request fetched successful'
    });

  } catch (error) {
    console.error(error);
    return res.send({ status: 0, message: 'Request fetched failed' });
  }
};

const getAllQuotationsCards= async (req, res) => {
  try {
    let userId = req.body.userId
    let userType = req.body.userType
    let page = req.body.page
    let search = req.body.search
    let rowPerpage = req.body.rowsPerPage
    let where=req.body.where
    let sort = req.body.sort
    let fromDate = req.body.from
    let toDate = req.body.to
    let filterClient = req.body.filterClient
    let filterUser = req.body.filterUser
    let dueData = req.body.dueData
    let filterProjectType = req.body.filterProjectType
    let filterCommunity = req.body.filterCommunity
    let projectEstimate = req.body.projectEstimate
    let data = await Request.getAllQuotations(null, userId, userType, page, rowPerpage, search,where,sort, fromDate, toDate ,filterClient, filterProjectType, filterUser, dueData, filterCommunity,projectEstimate)
    // let allProposals = await Proposals.getAllAwardedProposals()
    // let requestTeam = await Request.getAllTeamBatch()
    // let services = await Request.getServices(null)
    // let products = await Product.getAllProducts(false);
    // products = products.filter(row => row.inhouse === 1)
    // let labourCostServices = await Product.getAllLabourCostServices();
    // labourCostServices = labourCostServices.filter(row => row.inhouse === 1)
    // let community = await Community.getAllCommunities();
    // let workflows = await Client.getAllWorkflows();
    // for (const data of products) {
    //   let result = await Product.getServiceByProduct(data.id);

    //   data.services = result
    // }
    // let materials = await Product.getAllMaterials(true);

    // let result = data.data
    let result = Array.isArray(data.data) ? data.data : [];
    
    // ORIGINAL CODE - BACKUP (N+1 Query Problem)
    // for (const data of result) {
    //   let attachment = await Request.getAllRequestAttachmentsForCards(data.id,data.isFromDesignOrder)
    //   data.attachment=attachment || []
    // }

    // OPTIMIZED VERSION - All attachments are now fetched within the optimized model
    res.send({
      status: 1,
      data: data,
      // requestTeam: requestTeam,
      // services: services,
      // products: products,
      // community: community,
      // labourCostServices,
      // materials,
      // workflows,
      // allProposals,
      message: 'Request fetched successful'
    })
  } catch (error) {
    console.log(error)
    res.send({ status: 0, message: 'Request fetched failed' });
  }
}

const getAllQuotationsCount= async (req, res) => {
  try {
    let userId = req.body.userId
    let userType = req.body.userType
    let page = req.body.page
    let search = req.body.search
    let rowPerpage = req.body.rowsPerPage
    let where=req.body.where
    let sort = req.body.sort
    let fromDate = req.body.from
    let toDate = req.body.to
    let data = await Request.getAllQuotationsCount(null, userId, userType, page, rowPerpage, search,where,sort, fromDate, toDate)
    
    res.send({
      status: 1,
      data: data,
      message: 'Request fetched successful'
    })
  } catch (error) {
    console.log(error)
    res.send({ status: 0, message: 'Request fetched failed' });
  }
}
let currentDate = moment().unix()
const getCommunityByClient = async (req, res) => {
  let { clientId,userType,From,teamId,noAllCommunity } = req.body
  let [community,allCommunity] = await Promise.all([
    Community.getAllCommunitiesByClient(false, clientId,userType,From,teamId),
    noAllCommunity ? Promise.resolve([]) : Community.getAllCommunities(null,null,null,null,null,userType,From,teamId)
  ])
  // let community = await Community.getAllCommunitiesByClient(false, clientId,userType,From,teamId);
  // const allCommunity = await Community.getAllCommunities(null,null,null,null,null,userType,From,teamId);
  res.send({ status: 1, community,allCommunity })
}

// New API endpoint for client-specific project autocomplete
const getCommunitiesForClientAutocomplete = async (req, res) => {
  try {
    const { userId, userType, isClient, clientId } = req.body;
    console.log('Backend API called with:', { userId, userType, isClient, clientId });
    
    if (!userId) {
      console.log('No userId provided');
      return res.send({ status: 0, message: 'User ID is required' });
    }
    
    const communities = await Community.getCommunitiesForClientAutocomplete(userId, userType, isClient, clientId);
    console.log('Backend returning communities:', communities);
    
    res.send({ 
      status: 1, 
      data: communities, 
      message: 'Communities fetched successfully' 
    });
  } catch (error) {
    console.log('Error in getCommunitiesForClientAutocomplete:', error);
    res.send({ 
      status: 0, 
      message: 'Failed to fetch communities', 
      error: error.message 
    });
  }
}

////////////////////////////////////////////////quickbook



async function createAvalaraEstimate (estimateData, attempt = 0) {
  const MAX_RETRIES = 5;
  const isNoCharge  = !!(estimateData && estimateData.isNoCharge);

  /* 1. Ship-From & Ship-To addresses -------------------------------------- */
  // const shipFrom = {
  //   line1      : '1600 Amphitheatre Parkway',
  //   city       : 'Mountain View',
  //   region     : 'CA',
  //   postalCode : '94043',
  //   country    : 'US'
  // };

  const shipFrom = {
  line1      : '680 Columbia Ave',
  city       : 'Riverside',
  region     : 'CA',
  postalCode : '92507',
  country    : 'US'
};


  const client  = estimateData.clientDetails || {};
  const project  = estimateData.projectData || {};
  const shipTo  = {
    line1      : project?.address    || '',
    city       : project.city       || '',
    region     : project.state      || '',
    postalCode : project.zip   || '',
    country    : 'US'
  };

//   shipTo= {
//   line1: '1600 Amphitheatre Parkway',
//   city: 'Mountain View',
//   region: 'CA',
//   postalCode: '94043',
//   country: 'US'
// }

  /* 2. Build Avalara line array ------------------------------------------ */
  const lines    = [];
  const discount = Number(estimateData.discount || 0);

  function pushLine (name, qty, unit, taxCode) {
    const amount = isNoCharge ? 0 : +(unit * qty).toFixed(2);
    lines.push({
      number      : String(lines.length + 1),
      quantity    : qty,
      amount,
      itemCode    : String(name).slice(0, 50),
      description : name,
      taxCode     : taxCode || "	NT",                   // || "P0000000",
      discounted  : discount > 0
    });
  }

  /* Catalog products */
  (estimateData.catalogs || []).forEach(p => {
    const qty   = (p?.quantity || 1) * (p?.locationQuantity || 1);
    const price = isNoCharge
      ? 0
      : p.isAdditionalProduct
        ? Number(p.price || 0)
        : Number(p?.actual_retail_price || p?.retail_price  || 0);
        const taxCode = p.taxCode 
    pushLine(p.name, qty, price,taxCode);
  });

    /* Catalog material */
  // (estimateData.produtFormulaMaterial || []).forEach(p => {
  //   const qty   = (p.quantity || 1) 
  //   let cost =Number(p.unit_cost) 
  //   let retailMarkup =Number(p.retail_price)
  //   let markupAmount = parseFloat((cost * retailMarkup) / 100).toFixed(2)
  //   const price = isNoCharge
  //     ? 0
  //       : Number((p.price + markupAmount ) || 0);
  //       const taxCode = p.taxCode 
  //   pushLine(p.name, qty, price,taxCode);
  // });

  /* Additional materials */
  (estimateData.materials || []).forEach(m => {
    const qty   = Number(m.quantity || 1);
    const price = isNoCharge ? 0 : Number(m.markUpCost || 0);
    const taxCode = m.taxCode
    pushLine(m.name, qty, price,taxCode);
  });

  /* Services */
  // (estimateData.services || []).filter(service => service.quantityDifference !== 0).forEach(s => {
  (estimateData.services || []).forEach(s => {
    const diff  = Number(s.quantityDifference || 0);
    const qty   = diff !== 0 ? diff : Number(s.quantity || 1);
    if (qty === 0) return;
    const price = isNoCharge ? 0 : Number(s.total_cost || 0);
    const taxCode = s.taxCode

    pushLine(s.name, qty, price,taxCode);
  });

  /* RFP services */
  (estimateData.rfpServices || []).forEach(rs => {
    const price = isNoCharge
      ? 0
      : (Number(rs.originalMarkup || 0) / 100) * rs.total_cost + rs.total_cost;
    const taxCode = rs.taxCode

    pushLine(rs.name, 1, price,taxCode);
  });

  /* RFP products */
  (estimateData.rfpProducts || []).forEach(rp => {
    const qty   = (rp.quantity || 1) * (rp.location_quantity || 1);
    const base  = rp.sellingPrice != null ? rp.sellingPrice : rp.product_retail_price;
    const price = isNoCharge ? 0 : (
      (estimateData.markup_type === 1
        ? rp.WholesaleMarkup
        : rp.product_markup) / 100 * base + base
    );
    const taxCode = rp.taxCode

    pushLine(rp.product_name, qty, price,taxCode);
  });

  if (!lines.length) {
    // throw new Error('No sellable line-items found.');
    return {}
  }

  /* 3. Build payload ------------------------------------------------------ */
  const payload = {
    type        : 'SalesInvoice',               // estimate = quote
    commit      : false,
    code        : `${Date.now()}`,
    date        : moment().format('YYYY-MM-DD'),
    customerCode: String(client.user_id || 'CUST2001'),
    entityUseCode:client?.avalaraCustomerId ? client?.avalaraTaxType == 1 ?'N' : "A" :"N",  //"A non tax, N taxable"
    companyCode : 'DEFAULT',
    discount,
    addresses   : { shipFrom, shipTo },
    lines
    // If you ever want to hard-override tax:
    // taxOverride: {
    //   type     : 'TaxAmount',
    //   reason   : 'Pre-calculated total',
    //   taxAmount: 119.78
    // }
  };

  // console.log('📤 Ava payload', JSON.stringify(payload, null, 2));

  /* 4. POST with retry ---------------------------------------------------- */
  const authHeader = 'Basic ' + Buffer.from(
    `${AVALARA_ACCOUNT_ID}:${AVALARA_LICENSE_KEY}`
  ).toString('base64');
  const baseURL = (AVALARA_BASE_URL || 'https://sandbox-rest.avatax.com') + '/api/v2';

  try {
    const { data } = await axios.post(
      `${baseURL}/transactions/create`,
      payload,
      { headers: { Authorization: authHeader, 'Content-Type': 'application/json' }, timeout: 30000 }
    );
    // console.log('✅ Ava response', JSON.stringify(data, null, 2));
    // console.log('🧾 Ava Total Tax Amount:', data.totalTax);
    // console.log('🆔 Ava Transaction ID:', data.id);
    let result ={
      avalara_transaction_id: data.id,
      avalara_code: data.code,
      totalTax: data.totalTax,
    }
    return result;

  } catch (err) {
    const code = err.code || (err.response && err.response.status);
    const transient = ['ENOTFOUND', 'ECONNREFUSED', 'ETIMEDOUT', 502, 503, 504];
    if (transient.includes(code) && attempt < MAX_RETRIES) {
      const wait = Math.pow(2, attempt) * 1000;
      console.warn(`🔄 Retry in ${wait / 1000}s…`);
      await new Promise(r => setTimeout(r, wait));
      return createAvalaraEstimate(estimateData, attempt + 1);
    }
    return {}
    // throw err;
  }
}


async function updateAvalaraTransaction(estimateData, transactionCode, adjustmentReason = 6, attempt = 0) {
  console.log('estimateData', estimateData)
  const MAX_RETRIES = 5;
  const isNoCharge = !!(estimateData && estimateData.isNoCharge);

  if (!transactionCode) throw new Error('Transaction code is required to update Avalara');

  /* Addresses */
  // const shipFrom = {
  //   line1: '1600 Amphitheatre Parkway',
  //   city: 'Mountain View',
  //   region: 'CA',
  //   postalCode: '94043',
  //   country: 'US'
  // };
  const shipFrom = {
  line1      : '680 Columbia Ave',
  city       : 'Riverside',
  region     : 'CA',
  postalCode : '92507',
  country    : 'US'
};


  const client = estimateData.clientDetails || {};
  const project  = estimateData.projectData || {};
  const shipTo  = {
    line1      : project?.address    || '',
    city       : project.city       || '',
    region     : project.state      || '',
    postalCode : project.zip   || '',
    country    : 'US'
  };
  // const shipTo = {
  //   line1: '1600 Amphitheatre Parkway',
  //   city: 'Mountain View',
  //   region: 'CA',
  //   postalCode: '94043',
  //   country: 'US'
  // };

  /* Lines */
  const lines = [];
  const discount = Number(estimateData.discount || 0);

  function pushLine(name, qty, unit, taxCode) {
    const amount = isNoCharge ? 0 : +(unit * qty).toFixed(2);
    lines.push({
      number: String(lines.length + 1),
      quantity: qty,
      amount,
      itemCode: String(name).slice(0, 50),
      description: name,
      taxCode: taxCode || "NT",
      discounted: discount > 0
    });
  }

  ;(estimateData.catalogs || []).forEach(p => {
    const qty = (p.quantity || 1) * (p.locationQuantity || 1);
    const price = isNoCharge ? 0 : (p.isAdditionalProduct ? Number(p.price || 0) : Number(p.actual_retail_price || p?.retail_price || 0));
    pushLine(p.name, qty, price, p.taxCode);
  });

      /* Catalog material */
  // (estimateData.produtFormulaMaterial || []).forEach(p => {
  //   const qty   = (p.quantity || 1) 
  //   const price = isNoCharge
  //     ? 0
  //       : Number(p.price  || 0);
  //       const taxCode = p.taxCode 
  //   pushLine(p.name, qty, price,taxCode);
  // });

  //   (estimateData.produtFormulaMaterial || []).forEach(p => {
  //   const qty   = (p.quantity || 1) 
  //   let cost =Number(p.unit_cost) 
  //   let retailMarkup =Number(p.retail_price)
  //   let markupAmount = parseFloat((cost * retailMarkup) / 100).toFixed(2)
  //   const price = isNoCharge
  //     ? 0
  //       : Number((p.price + markupAmount ) || 0);
  //       const taxCode = p.taxCode 
  //   pushLine(p.name, qty, price,taxCode);
  // });


  ;(estimateData.materials || []).forEach(m => {
  const name = m.name || m.product_name || 'Unnamed Material';
  const qty = Number(m.quantity || 1);
  const price = isNoCharge ? 0 : Number(m.markUpCost || 0);
  pushLine(name, qty, price, m.taxCode);
});

  // ;(estimateData.services || []).filter(s => s.quantityDifference !== 0).forEach(s => {
  ;(estimateData.services || []).forEach(s => {
    // const qty = Number(s.quantityDifference );
    const diff  = Number(s.quantityDifference || 0);
    const qty   = diff !== 0 ? diff : Number(s.quantity || 1);
    const price = isNoCharge? 0 : s.unit_cost?s.unit_cost : s.total_cost
  
      pushLine(s.name, qty, isNoCharge ? 0 : Number(price || 0), s.taxCode);
  
  });

  ;(estimateData.rfpServices || []).forEach(rs => {
    let quantity =rs.quantity || 1;
    // const price = isNoCharge ? 0 : (Number(rs.originalMarkup || 0) / 100) * rs.total_cost + rs.total_cost;
     let price= rs.originalMarkup/100 * rs.unit_cost + rs.unit_cost
     console.log('price', price)
    pushLine(rs.name, quantity, price, rs.taxCode);
  });

  (estimateData.rfpProducts || []).forEach(rp => {
    const qty = (rp.quantity || 1) * (rp.location_quantity || 1);
    const base = rp.sellingPrice != null ? rp.sellingPrice : rp.product_retail_price;
    // const price = isNoCharge ? 0 : (
    //   (estimateData.markup_type === 1
    //     ? rp.WholesaleMarkup
    //     : rp.product_markup) / 100 * base + base
    // );
     let price =   rp.product_markup / 100 * rp.product_price + rp.product_price
     console.log('price', price)
    pushLine(rp.product_name, qty, price, rp.taxCode);
  });

  if (!lines.length) {
    // throw new Error('No sellable line-items found.');
    return {}
  }

  /* New transaction model for adjustment */
  const newTransaction = {
    type: 'SalesInvoice',
    commit: false,
    code: transactionCode,
    date: moment().format('YYYY-MM-DD'),
    // customerCode: String(client.id || 'UNKNOWN'),
    // companyCode: 'DEFAULT',
    customerCode: String(client.user_id || 'CUST2001'),
    entityUseCode:client?.avalaraCustomerId ? client?.avalaraTaxType == 1 ?'N' : "A" :"N",  //"A non tax, N taxable"
    companyCode : 'DEFAULT',
    discount,
    addresses: { shipFrom, shipTo },
    lines
  };

  const payload = {
    adjustmentReason,
    adjustmentDescription: 'Updating transaction from estimate data',
    newTransaction
  };

  const authHeader = 'Basic ' + Buffer.from(
    `${AVALARA_ACCOUNT_ID}:${AVALARA_LICENSE_KEY}`
  ).toString('base64');
  const baseURL = (AVALARA_BASE_URL || 'https://sandbox-rest.avatax.com') + '/api/v2';

  try {
    const { data } = await axios.post(
      `${baseURL}/companies/DEFAULT/transactions/${transactionCode}/adjust`,
      payload,
      { headers: { Authorization: authHeader, 'Content-Type': 'application/json' }, timeout: 30000 }
    );

    console.log('✅ Ava Transaction Updated:', JSON.stringify(data, null, 2));
    return {
      avalara_transaction_id: data.id,
      avalara_code: data.code,
      totalTax: data.totalTax
    };

  } catch (err) {
    const code = err.code || (err.response && err.response.status);
    const transient = ['ENOTFOUND', 'ECONNREFUSED', 'ETIMEDOUT', 502, 503, 504];
    if (transient.includes(code) && attempt < MAX_RETRIES) {
      const wait = Math.pow(2, attempt) * 1000;
      console.warn(`🔄 Retry in ${wait / 1000}s…`);
      await new Promise(r => setTimeout(r, wait));
      return updateAvalaraTransaction(estimateData, transactionCode, adjustmentReason, attempt + 1);
    }
    const msg = err?.response?.data || err.message;
    console.error('❌ Error adjusting Avalara transaction:', msg);
    // throw new Error(`Avalara update failed: ${JSON.stringify(msg)}`);
    return {}
  }
}




// async function voidAvalaraTransaction(transactionCode, companyCode) {
//   if (!transactionCode || !companyCode) {
//     throw new Error('Both transactionCode and companyCode are required to void a transaction');
//   }

//   const authHeader = 'Basic ' + Buffer.from(
//     `${AVALARA_ACCOUNT_ID}:${AVALARA_LICENSE_KEY}`
//   ).toString('base64');

//   const url = `${AVALARA_BASE_URL}/api/v2/companies/${companyCode}/transactions/${transactionCode}/void`;
//   console.log('♻️ Voiding Avalara transaction:', url);

//   try {
//     const { data } = await axios.post(url, {
//       code: "DocVoided", // Required by Avalara
//       reason: "Voiding the transaction as part of cleanup" // Customize your reason
//     }, {
//       headers: {
//         Authorization: authHeader,
//         'Content-Type': 'application/json'
//       },
//       timeout: 20000
//     });

//     console.log('✅ Successfully voided Avalara transaction:', data);
//     return data;

//   } catch (err) {
//     const msg = err?.response?.data || err.message;
//     console.error('❌ Error voiding transaction:', msg);
//     throw new Error(`Avalara void failed: ${JSON.stringify(msg)}`);
//   }
// }

// const transactionCode = '1752252860087';
// const companyCode = 'DEFAULT';

// voidAvalaraTransaction(transactionCode, companyCode)
//   .then(data => console.log('✅ Transaction deleted:', data))
//   .catch(err => console.error('❌ Delete error:', err));


async function getAvalaraTransactionById(transactionId) {
  if (!transactionId) throw new Error('Avalara transaction ID is required');

  const authHeader = 'Basic ' + Buffer.from(
    `${AVALARA_ACCOUNT_ID}:${AVALARA_LICENSE_KEY}`
  ).toString('base64');

  const url = `${AVALARA_BASE_URL}/api/v2/transactions/${transactionId}`;
  console.log('📡 Fetching Avalara transaction by ID:', url);

  try {
    const { data } = await axios.get(url, {
      headers: {
        Authorization: authHeader,
        'Content-Type': 'application/json'
      },
      timeout: 20000
    });

    console.log('✅ Fetched Avalara transaction:', data);
    return data;

  } catch (err) {
    const msg = err?.response?.data || err.message;
    console.error('❌ Error fetching transaction by ID:', msg);
    throw new Error(`Avalara fetch by ID failed: ${JSON.stringify(msg)}`);
  }
}

// const transactionId = 85093400082064;

// getAvalaraTransactionById(transactionId)
//   .then(data => console.log('✅ Transaction:', data))
//   .catch(err => console.error('❌ Error:', err));

const addEstimateToQuickBooks = async (estimateData) => {
  console.log("Attempting to add estimate to QuickBooks...");
  let token = await getNewAccessToken();

  const postEstimate = async (useNewToken = false, attempt = 0) => {
    if (useNewToken) {
      console.log('Refreshing access token...');
      token = await getNewAccessToken();
    }

    try {
      // Ensure all items have valid inventory start dates
      // for (let item of estimateData.items) {
      //   // const itemDetails = await findItemByName(item.name);
      //   const itemDetails = await findItemById(item.itemId);
      //   item.inventoryStartDate = itemDetails.InventoryStartDate;
      // }

      // Use the latest inventory start date to set the transaction date
      // const txnDate = moment.max(estimateData.items.map(item => moment(item.inventoryStartDate))).add(10, 'days').format('YYYY-MM-DD');
      const txnDate = moment().format('YYYY-MM-DD');

      const discountAccounts = await getAccounts('Income', 'DiscountsRefundsGiven');
      const dynamicDiscountAccRefId = discountAccounts?.length > 0 ? discountAccounts[0].Id : null;

      const discountAccId= dynamicDiscountAccRefId || await getAccountIdByName("Discounts") 

      // Prepare the QuickBooks estimate data
      let quickBooksEstimateData = {
        CustomerRef: { value: estimateData.customerId },
        TxnDate: txnDate,
        DepartmentRef: { value: departmentId },
        Line: [
          ...estimateData.items.map(item => ({
            Amount: item.amount,
            DetailType: "SalesItemLineDetail",
            SalesItemLineDetail: {
              ItemRef: { value: item.itemId },
              Qty: item.quantity,
              UnitPrice: item.unitPrice,
              TaxCodeRef: { value: 'TAX' } 
            }
          })),
          {
            Amount: estimateData.discount,
            DetailType: "DiscountLineDetail",
            DiscountLineDetail: {
              DiscountAccountRef: { value: discountAccId } // Your Discount Account ID
            }
          }
        ],
        PrivateNote: estimateData.name || estimateData.description,
        // TxnTaxDetail: {
        //   TxnTaxCodeRef: { value: 'TAX' },
        //   TotalTax: estimateData.taxAmount // Set the total tax amount here
        // },
        CustomField: [
          {
            DefinitionId: "1", // Replace this with your actual custom field DefinitionId
            Name: "P.O. Number", // The name of your custom field
            Type: "StringType",
            StringValue: (estimateData.poNumber || "")?.slice(0, 30) // The value you want to set in the custom field
          },
          {
            DefinitionId: "2", // Replace this with your actual custom field DefinitionId
            Name: "Project", // The name of your custom field
            Type: "StringType",
            StringValue: (estimateData.projectName || "")?.slice(0, 30)// The value you want to set in the custom field
          },
          {
            DefinitionId: "3", // Replace this with your actual custom field DefinitionId
            Name: "Sales Rep Name", // The name of your custom field
            Type: "StringType",
            StringValue: (estimateData?.salesRepName || "")?.slice(0, 30)// The value you want to set in the custom field
          },
        ],
      };

      if(estimateData.jobClass){
        quickBooksEstimateData.ClassRef = { value: estimateData.jobClass }
      }
        if(estimateData?.clientData){
          quickBooksEstimateData.BillEmail = {
            Address: estimateData?.clientData?.email || ""
          }
          quickBooksEstimateData.BillAddr = {
            Line1: estimateData.clientData?.quickbookCustomerName || "",
            Line2: estimateData.clientData?.address || "",
            City: estimateData.clientData?.city || "",
            CountrySubDivisionCode: estimateData.clientData?.state || "",
            PostalCode: estimateData.clientData?.zip_code || "",
            // Country: estimateData.clientData?.country || ""
          }
        }
        if(estimateData?.ProjectData){
          quickBooksEstimateData.ShipAddr = {
            Line1: estimateData.ProjectData?.name || "",
            Line2: estimateData.ProjectData?.address || "",
            City: estimateData.ProjectData?.city || "",
            CountrySubDivisionCode: estimateData.ProjectData?.state || "",
            PostalCode: estimateData.ProjectData?.zip || estimateData.ProjectData?.zip_code || "",
            // Country: estimateData.ProjectData?.country || ""
          }
        }

      console.log("QuickBooks Estimate Data:", JSON.stringify(quickBooksEstimateData, null, 2));

      const apiUrl = PRODUCTION
      ? `https://quickbooks.api.intuit.com/v3/company/${realmId}`
      : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}`;
  

      const response = await axios.post(
        // `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/estimate`,
        `${apiUrl}/estimate`,
        quickBooksEstimateData,
        {
          headers: {
            Authorization: `Bearer ${token}`,
            'Content-Type': 'application/json',
            Accept: 'application/json'
          },
          // timeout: 30000 // 30 seconds
        }
      );

      if (response.status === 200) {
        console.log("Estimate has been successfully added to QuickBooks:", response.data);
        console.log("Unique Estimate ID:", response.data.Estimate.Id);
        UniqueEstimateId = response.data.Estimate.Id;
        doc_number = response.data.Estimate.DocNumber;
        // createAvalaraSalesOrder(response.data)
        return response.data;
      } else {
        console.error("Failed to add the estimate to QuickBooks:", response.data);
        return null;
      }

    } catch (error) {
      if (error.code === 'ENOTFOUND' && attempt < 5) {
        // Retry logic with exponential backoff in case of DNS lookup failure
        const backoffTime = Math.pow(2, attempt) * 1000;
        console.log(`DNS lookup failed, retrying in ${backoffTime / 1000} seconds... (Attempt ${attempt + 1})`);
        await new Promise(resolve => setTimeout(resolve, backoffTime));
        return postEstimate(useNewToken, attempt + 1); // Retry
      }

      // Other errors
      console.error("Error adding estimate to QuickBooks:", error.response ? error.response.data : error.message);
      const errs = error.response?.data?.Fault?.Error || [];
      errs.forEach((e, i) => {
      console.log(`--- QBO Error #${i + 1} ---`);
      console.log("Message :", e.Message);
      console.log("Detail  :", e.Detail);
      console.log("Code    :", e.code);
      console.log("Element :", e.element); // <-- THIS often includes Line[xx]
      });
      const faultType = error.response?.data?.Fault?.type;
      if (faultType === 'AUTHENTICATION') {
        console.log('Access token expired, refreshing token...');
        return postEstimate(true); // Retry with new access token
      } else {
        throw error; // Rethrow error if not related to DNS or authentication
      }
    }
  };

  return postEstimate();
};


const editEstimateInQuickBooks = async (estimateData, estimateId) => {
  const fetchLatestEstimate = async () => {
    const token = await getNewAccessToken();
    // const url = `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/estimate/${estimateId}?minorversion=65`;

    const apiUrl = PRODUCTION
    ? `https://quickbooks.api.intuit.com/v3/company/${realmId}`
    : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}`;

    const url = `${apiUrl}/estimate/${estimateId}?minorversion=65`;
    try {
      const response = await axios.get(url, {
        headers: {
          Authorization: `Bearer ${token}`,
          Accept: 'application/json'
        },
        timeout: 10000 
      });
      console.log("Fetched latest estimate:", response.data);
      return response.data;
    } catch (error) {
      console.error("Error fetching latest estimate data:", error.response ? error.response.data : error.message);
      throw error;
    }
  };

  const updateEstimate = async (retryCount = 0) => {
    const maxRetries = 1;
    const backoffFactor = 2;
    const baseDelay = 1000; // 1 second

    try {
      const token = await getNewAccessToken();
      const latestEstimate = await fetchLatestEstimate();
      const latestSyncToken = latestEstimate.Estimate.SyncToken;

      // Ensure all items have valid inventory start dates
      // for (let item of estimateData.items) {
      //   // const itemDetails = await findItemByName(item.name);
      //   const itemDetails = await findItemById(item.itemId);

      //   item.inventoryStartDate = itemDetails.InventoryStartDate;
      // }

      // const txnDate = moment.max(estimateData.items.map(item => moment(item.inventoryStartDate)))
      //   .add(10, 'days')
      //   .format('YYYY-MM-DD');

      const txnDate = moment().format('YYYY-MM-DD');

      const discountAccounts = await getAccounts('Income', 'DiscountsRefundsGiven');
      const dynamicDiscountAccRefId = discountAccounts?.length > 0 ? discountAccounts[0].Id : null;

      const discountAccId=await getAccountIdByName("Discounts") || dynamicDiscountAccRefId || discountAccRefId

      
      let quickBooksEstimateData = {
        sparse: false, 
        Id: estimateId,
        SyncToken: latestSyncToken,
        CustomerRef: {
          value: estimateData.customerId || latestEstimate.Estimate.CustomerRef.value
        },
        TxnDate: txnDate,
        DepartmentRef: { value: departmentId },
        Line: [
          ...estimateData.items.map(item => ({
            Amount: item.amount,
            DetailType: "SalesItemLineDetail",
            SalesItemLineDetail: {
              ItemRef: { value: item.itemId },
              Qty: item.quantity,
              UnitPrice: item.unitPrice,
              TaxCodeRef: { value: 'TAX' } 

            },
          })), 
          {
            Amount: estimateData.discount || 0, // Apply a fixed discount amount
            DetailType: "DiscountLineDetail",
            DiscountLineDetail: {
              DiscountAccountRef: {
                value: discountAccId, // Your Discount Account ID
              },
            },
          },
        ],
        BillEmail: {
          Address: estimateData?.clientData?.email || ""
        },
        BillAddr: {
          Line1: estimateData.clientData?.quickbookCustomerName || "",
          Line2: estimateData.clientData?.address || "",
          City: estimateData.clientData?.city || "",
          CountrySubDivisionCode: estimateData.clientData?.state || "",
          PostalCode: estimateData.clientData?.zip_code || "",
          // Country: estimateData.clientData?.country || ""
        },
      
        ShipAddr: {
          Line1: estimateData.ProjectData?.name || "",
          Line2: estimateData.ProjectData?.address || "",
          City: estimateData.ProjectData?.city || "",
          CountrySubDivisionCode: estimateData.ProjectData?.state || "",
          PostalCode: estimateData.ProjectData?.zip || estimateData.ProjectData?.zip_code || "",
          // Country: estimateData.ProjectData?.country || ""
        },
        PrivateNote: estimateData.name || estimateData.description,
        // TxnTaxDetail: { no need tax from  here in qb already the client linked qb account with avalara
        //   TxnTaxCodeRef: { value: 'TAX' },
        //   TotalTax: estimateData.taxAmount // Set the total tax amount here
        // },
        CustomField: [
          {
            DefinitionId: "1", // Replace this with your actual custom field DefinitionId
            Name: "P.O. Number", // The name of your custom field
            Type: "StringType",
            StringValue: (estimateData.poNumber || "")?.slice(0, 30) // The value you want to set in the custom field
          },
          {
            DefinitionId: "2", // Replace this with your actual custom field DefinitionId
            Name: "Project", // The name of your custom field
            Type: "StringType",
            StringValue: (estimateData.projectName || "")?.slice(0, 30)// The value you want to set in the custom field
          },
          {
            DefinitionId: "3", // Replace this with your actual custom field DefinitionId
            Name: "Sales Rep Name", // The name of your custom field
            Type: "StringType",
            StringValue: (estimateData.salesRepName || "")?.slice(0, 30)// The value you want to set in the custom field
          },
          // {
          //   DefinitionId: "4", // Replace this with your actual custom field DefinitionId
          //   Name: "Sales Rep", // The name of your custom field
          //   Type: "ReferenceType",
          //   Value: 400000033 || "" // The value you want to set in the custom field
          // },
        ],
      };
      if(estimateData.jobClass){
        quickBooksEstimateData.ClassRef = { value: estimateData.jobClass }
        // quickBooksEstimateData.SalesRepRef = {value: "400000033"};
      }

      

      const apiUrlUpdate = PRODUCTION
      ? `https://quickbooks.api.intuit.com/v3/company/${realmId}`
      : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}`;
  

      console.log("QuickBooks Estimate Data:", JSON.stringify(quickBooksEstimateData, null, 2));
      const response = await axios.post(
        // `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/estimate?operation=update`,
        `${apiUrlUpdate}/estimate?operation=update`,
        quickBooksEstimateData,
        {
          headers: {
            Authorization: `Bearer ${token}`,
            'Content-Type': 'application/json',
            Accept: 'application/json'
          },
          timeout: 10000
        }
      );

      if (response.status === 200) {
        console.log("Estimate has been successfully updated in QuickBooks:", response.data);
        doc_number = response.data.Estimate.DocNumber;
        UniqueEstimateId = response.data.Estimate.Id;
        return response.data;
      } else {
        console.error("Failed to update the estimate in QuickBooks:", response.data);
        return null;
      }

    } catch (error) {
      // DNS resolution error handling with exponential backoff
      if (error.code === 'ENOTFOUND' && retryCount < maxRetries) {
        const delay = baseDelay * Math.pow(backoffFactor, retryCount); // Exponential backoff
        console.log(`DNS lookup failed, retrying in ${delay / 1000} seconds... (Attempt ${retryCount + 1})`);
        await new Promise(resolve => setTimeout(resolve, delay));
        return updateEstimate(retryCount + 1); // Retry
      }

      console.error("Error updating estimate in QuickBooks:", error.response ? error.response.data : error.message);
      const faultType = error.response?.data?.Fault?.type;

      if (faultType === 'ValidationFault') {
        if (retryCount < maxRetries) {
          const delay = baseDelay * Math.pow(backoffFactor, retryCount); // Exponential backoff for validation faults
          console.log(`Validation fault, retrying update in ${delay / 1000} seconds... (Attempt ${retryCount + 1})`);
          await new Promise(resolve => setTimeout(resolve, delay));
          await updateEstimate(retryCount + 1); // Retry with incremented count
        } else {
          console.error('Max retry attempts reached.');
        }
      } else {
        console.error("Other error updating estimate in QuickBooks:", error.response?.data || error.message);
      }
    }
  };

  await updateEstimate();
};


const axiosRetry = async (axiosRequestFn, retries = 3, delay = 1000) => {
  try {
    return await axiosRequestFn();
  } catch (error) {
    if (retries > 0 && error.code === 'ENOTFOUND') {
      console.log(`Retrying request... attempts left: ${retries}`);
      await new Promise(res => setTimeout(res, delay));
      return axiosRetry(axiosRequestFn, retries - 1, delay * 2); // Exponential backoff
    } else {
      throw error;
    }
  }
};


///////////////////invoice quickbook

const addInvoiceToQuickBooks = async (invoiceData) => {
  let token = await getNewAccessToken();

  const postInvoice = async (useNewToken = false, retryCount = 0) => {
    const maxRetries = 5; // Maximum number of retries
    const backoffFactor = 2; // Exponential backoff factor
    const baseDelay = 1000; // Initial delay of 1 second

    if (useNewToken) {
      console.log('Refreshing access token...');
      token = await getNewAccessToken();
    }

    try {
      // Ensure all items have valid inventory start dates
      for (let item of invoiceData.items) {
        // const itemDetails = await findItemByName(item.name);
        const itemDetails = await findItemById(item.itemId);
        item.inventoryStartDate = itemDetails.InventoryStartDate;
      }

      // Use the latest inventory start date to set the transaction date
      // const txnDate = moment.max(invoiceData.items.map(item => moment(item.inventoryStartDate)))
      //   .add(10, 'days')
      //   .format('YYYY-MM-DD');
      const txnDate = moment().format('YYYY-MM-DD');

      const discountAccounts = await getAccounts('Income', 'DiscountsRefundsGiven');
      const dynamicDiscountAccRefId = discountAccounts?.length > 0 ? discountAccounts[0].Id : null;

      const discountAccId= dynamicDiscountAccRefId || discountAccRefId
      const quickBooksInvoiceData = {
        CustomerRef: { value: invoiceData.customerId },
        TxnDate: txnDate,
        DepartmentRef: { value: departmentId },
        Line: [
          ...invoiceData.items.map(item => ({
            Amount: parseFloat(item.amount.toFixed(2)), // Ensure proper precision
            DetailType: "SalesItemLineDetail",
            SalesItemLineDetail: {
              ItemRef: { value: item.itemId },
              Qty: item.quantity,
              UnitPrice: parseFloat(item.unitPrice), // Ensure proper precision
              TaxCodeRef: { value: 'TAX' } 

            }
          })),
          {
            Amount: invoiceData.discount, // Apply a fixed discount amount
            DetailType: "DiscountLineDetail",
            DiscountLineDetail: {
              DiscountAccountRef: { value: discountAccId }, // Your Discount Account ID
            },
          },
        ],
        PrivateNote: invoiceData.note,
        TxnTaxDetail: {
          TxnTaxCodeRef: { value: 'TAX' },
          TotalTax: invoiceData.taxAmount // Set the total tax amount here
        },
        CustomField: [
          {
            DefinitionId: "2", // Replace this with your actual custom field DefinitionId
            Name: "Job Type", // The name of your custom field
            Type: "StringType",
            StringValue: invoiceData.jobType // The value you want to set in the custom field
          },
          {
            DefinitionId: "1", // Replace this with your actual custom field DefinitionId
            Name: "No Charge Grand Total", // The name of your custom field
            Type: "StringType",
            StringValue: invoiceData.customGrandTotal // The value you want to set in the custom field
          },
        
        ],
        ...(invoiceData.email ? { BillEmail: { Address: invoiceData.email } } : {}),
        ...(invoiceData.termsId ? { TermsRef: { value: invoiceData.termsId } } : {}),
        DueDate: moment(txnDate).add(invoiceData.termsDays, 'days').format('YYYY-MM-DD')
      };

      console.log("QuickBooks Invoice Data:", JSON.stringify(quickBooksInvoiceData, null, 2));

      const apiUrl = PRODUCTION
      ? `https://quickbooks.api.intuit.com/v3/company/${realmId}/invoice`
      : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/invoice`;

      const response = await axios.post(
        apiUrl,
        quickBooksInvoiceData,
        {
          headers: {
            Authorization: `Bearer ${token}`,
            'Content-Type': 'application/json',
            Accept: 'application/json',
          },
          timeout: 30000, // Set a 30 seconds timeout
        }
      );

      if (response.status === 200) {
        console.log("Invoice has been successfully added to QuickBooks:", response.data);
        console.log("Unique Invoice ID:", response.data.Invoice.Id);
        UniqueEstimateId = response.data.Invoice.Id;
        doc_number = response.data.Invoice.DocNumber;

        return response.data;
      } else {
        console.error("Failed to add the invoice to QuickBooks:", response.data);
        return null;
      }
    } catch (error) {
      // Retry logic on DNS resolution errors
      if (error.code === 'ENOTFOUND' && retryCount < maxRetries) {
        const delay = baseDelay * Math.pow(backoffFactor, retryCount); // Exponential backoff
        console.log(`DNS lookup failed, retrying in ${delay / 1000} seconds... (Attempt ${retryCount + 1})`);
        await new Promise(resolve => setTimeout(resolve, delay));
        return postInvoice(useNewToken, retryCount + 1); // Retry with incremented count
      }

      console.error("Error adding invoice to QuickBooks:", error.response ? error.response.data : error.message);
      const faultType = error.response?.data?.Fault?.type;
      if (faultType === 'AUTHENTICATION') {
        console.log('Access token expired, refreshing token...');
        return postInvoice(true); // Retry with a new access token
      } else if (faultType === 'ValidationFault') {
        console.error("Validation error adding invoice to QuickBooks:", JSON.stringify(error.response?.data, null, 2));
      } else {
        console.error("Other error adding invoice to QuickBooks:", error.response?.data || error.message);
      }
    }
  };

  await postInvoice();
};




const editInvoiceInQuickBooks = async (invoiceData, invoiceId) => {
  const fetchLatestInvoice = async () => {
    if (!invoiceId) {
      console.error("Error: Missing or undefined invoiceId.");
      throw new Error("Missing or undefined invoiceId.");
    }

    const url = PRODUCTION
    ? `https://quickbooks.api.intuit.com/v3/company/${realmId}/invoice/${invoiceId}?minorversion=65`
    : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/invoice/${invoiceId}?minorversion=65`;



    const token = await getNewAccessToken();
    // const url = `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/invoice/${invoiceId}?minorversion=65`;

    try {
      const response = await axios.get(url, {
        headers: {
          Authorization: `Bearer ${token}`,
          Accept: 'application/json'
        },
        timeout: 10000 
      });
      console.log("Fetched latest invoice data:", JSON.stringify(response.data, null, 2));
      return response.data;
    } catch (error) {
      console.error("Error fetching latest invoice data:", JSON.stringify(error.response ? error.response.data : error.message, null, 2));
      throw error;
    }
  };

  const updateInvoice = async (retryCount = 0) => {
    const maxRetries = 5;  // Increased retries to handle DNS issues better
    const backoffFactor = 2;
    const baseDelay = 1000; // Initial delay of 1 second

    try {
      const token = await getNewAccessToken();
      const latestInvoice = await fetchLatestInvoice();
      const latestSyncToken = latestInvoice.Invoice.SyncToken;

      // Determine the correct CustomerRef
      const customerRef = latestInvoice.Invoice.CustomerRef || { value: invoiceData.customerId };

      // Check if the invoiceId is valid
      if (!invoiceId || isNaN(Number(invoiceId))) {
        throw new Error("Invalid invoiceId: " + invoiceId);
      }

      // const txnDate = moment.max(invoiceData.items.map(item => moment(item.inventoryStartDate)))
      //   .add(10, 'days')
      //   .format('YYYY-MM-DD');
      const txnDate = moment().format('YYYY-MM-DD');

      const discountAccounts = await getAccounts('Income', 'DiscountsRefundsGiven');
      const dynamicDiscountAccRefId = discountAccounts?.length > 0 ? discountAccounts[0].Id : null;

      const discountAccId= dynamicDiscountAccRefId || discountAccRefId

      const quickBooksInvoiceData = {
        sparse: false, 
        Id: invoiceId,
        SyncToken: latestSyncToken,
        CustomerRef: customerRef, // Use the fetched CustomerRef or fallback to invoiceData.customerId
        TxnDate: txnDate,
        DepartmentRef: { value: departmentId },
        Line: [
          ...invoiceData.items.map(item => ({
            Amount: parseFloat(item.amount.toFixed(2)), // Ensure proper precision
            DetailType: "SalesItemLineDetail",
            SalesItemLineDetail: {
              ItemRef: { value: item.itemId },
              Qty: item.quantity,
              UnitPrice: parseFloat(item.unitPrice), // Ensure proper precision
              TaxCodeRef: { value: 'TAX' } 
            }
          })),
          {
            Amount: invoiceData.discount, // Apply a fixed discount amount
            DetailType: "DiscountLineDetail",
            DiscountLineDetail: {
              DiscountAccountRef: {
                value: discountAccId, // Your Discount Account ID
              },
            },
          },
        ],
        PrivateNote: invoiceData.note,
        TxnTaxDetail: {
          TxnTaxCodeRef: { value: 'TAX' },
          TotalTax: invoiceData.taxAmount // Set the total tax amount here
        },
        CustomField: [
          {
            DefinitionId: "2", 
            Name: "Job Type", 
            Type: "StringType",
            StringValue: invoiceData.jobType 
          },
          {
            DefinitionId: "1", // Replace this with your actual custom field DefinitionId
            Name: "No Charge Grand Total", // The name of your custom field
            Type: "StringType",
            StringValue: invoiceData.customGrandTotal // The value you want to set in the custom field
          },
        ],
        ...(invoiceData.email ? { BillEmail: { Address: invoiceData.email } } : {}),
        ...(invoiceData.termsId ? { TermsRef: { value: invoiceData.termsId } } : {}),
        DueDate: moment(txnDate).add(invoiceData.termsDays, 'days').format('YYYY-MM-DD')
      };

      console.log("QuickBooks Invoice Data:", JSON.stringify(quickBooksInvoiceData, null, 2));

      const apiUrl = PRODUCTION
      ? `https://quickbooks.api.intuit.com/v3/company/${realmId}/invoice?operation=update`
      : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/invoice?operation=update`;

      const response = await axios.post(
        apiUrl,
        quickBooksInvoiceData,
        {
          headers: {
            Authorization: `Bearer ${token}`,
            'Content-Type': 'application/json',
            Accept: 'application/json'
          },
          timeout: 10000 
        }
      );

      if (response.status === 200) {
        console.log("Invoice has been successfully updated in QuickBooks:", response.data);
        doc_number = response.data.Invoice.DocNumber;
        return response.data;
      } else {
        console.error("Failed to update the invoice in QuickBooks:", JSON.stringify(response.data, null, 2));
        return null;
      }
    } catch (error) {
      // Retry logic on DNS resolution errors
      if (error.code === 'ENOTFOUND' && retryCount < maxRetries) {
        const delay = baseDelay * Math.pow(backoffFactor, retryCount); // Exponential backoff
        console.log(`DNS lookup failed, retrying in ${delay / 1000} seconds... (Attempt ${retryCount + 1})`);
        await new Promise(resolve => setTimeout(resolve, delay));
        return await updateInvoice(retryCount + 1); // Retry with incremented count
      }

      console.error("Error updating invoice in QuickBooks:", JSON.stringify(error.response ? error.response.data : error.message, null, 2));
      const faultType = error.response?.data?.Fault?.type;
      if (faultType === 'ValidationFault') {
        const validationErrors = error.response.data.Fault.Error;
        validationErrors.forEach(err => {
          console.error(`Validation Error: ${err.Message} (Code: ${err.code})`);
        });

        if (retryCount < maxRetries) {
          const delay = baseDelay * Math.pow(backoffFactor, retryCount); // Exponential backoff
          console.log(`Retrying update in ${delay}ms...`);
          await new Promise(resolve => setTimeout(resolve, delay));
          return await updateInvoice(retryCount + 1); // Retry with incremented count
        } else {
          console.error('Max retry attempts reached.');
        }
      } else {
        console.error("Other error updating invoice in QuickBooks:", JSON.stringify(error.response?.data || error.message, null, 2));
      }
    }
  };

  const dataInv = await updateInvoice();
  return dataInv
};

const editInvoiceInQuickBooksFinal = async (invoiceData, invoiceId) => {
  console.log('combined-invoiceData----',invoiceData)
  const fetchLatestInvoice = async () => {
    if (!invoiceId) {
      console.error("Error: Missing or undefined invoiceId.");
      throw new Error("Missing or undefined invoiceId.");
    }

    const url = PRODUCTION
    ? `https://quickbooks.api.intuit.com/v3/company/${realmId}/invoice/${invoiceId}?minorversion=65`
    : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/invoice/${invoiceId}?minorversion=65`;



    const token = await getNewAccessToken();
    // const url = `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/invoice/${invoiceId}?minorversion=65`;

    try {
      const response = await axios.get(url, {
        headers: {
          Authorization: `Bearer ${token}`,
          Accept: 'application/json'
        },
        timeout: 10000 
      });
      console.log("Fetched latest invoice data:", JSON.stringify(response.data, null, 2));
      return response.data;
    } catch (error) {
      console.error("Error fetching latest invoice data:", JSON.stringify(error.response ? error.response.data : error.message, null, 2));
      throw error;
    }
  };

  const updateInvoice = async (retryCount = 0) => {
    const maxRetries = 5;  // Increased retries to handle DNS issues better
    const backoffFactor = 2;
    const baseDelay = 1000; // Initial delay of 1 second

    try {
      const token = await getNewAccessToken();
      const latestInvoice = await fetchLatestInvoice();
      const latestSyncToken = latestInvoice.Invoice.SyncToken;

      // Determine the correct CustomerRef
      const customerRef = latestInvoice.Invoice.CustomerRef || { value: invoiceData.customerId };

      // Check if the invoiceId is valid
      if (!invoiceId || isNaN(Number(invoiceId))) {
        throw new Error("Invalid invoiceId: " + invoiceId);
      }

      // const txnDate = moment.max(invoiceData.items.map(item => moment(item.inventoryStartDate)))
      //   .add(10, 'days')
      //   .format('YYYY-MM-DD');
      const txnDate = moment().format('YYYY-MM-DD');
  
      // // Filter out invalid items and ensure valid ItemRef and UnitPrice
      // const validItems = invoiceData.items
      //   .filter(item => item.itemId && !isNaN(item.unitPrice) && !isNaN(item.amount))
      //   .map(item => ({
      //     Amount: parseFloat(item.amount).toFixed(2),
      //     DetailType: "SalesItemLineDetail",
      //     SalesItemLineDetail: {
      //       ItemRef: { value: item.itemId },
      //       Qty: item.quantity || 1,
      //       UnitPrice: parseFloat(item.unitPrice),
      //       TaxCodeRef: { value: item.name === 'Additional amount' ? 'NON' : 'TAX' },
      //     },
      //   }));

      const discountAccounts = await getAccounts('Income', 'DiscountsRefundsGiven');
      const dynamicDiscountAccRefId = discountAccounts?.length > 0 ? discountAccounts[0].Id : null;

      const discountAccId= dynamicDiscountAccRefId || discountAccRefId

      console.log("Original invoiceData items:", invoiceData.items);

      const validItems = invoiceData.items
        .filter(item => {
          const itemId = item.SalesItemLineDetail?.ItemRef?.value;
          const unitPrice = item.SalesItemLineDetail?.UnitPrice;
          const amount = item.Amount;

          console.log(`Checking item: ${JSON.stringify(item)}`);
          const isValid = itemId && !isNaN(unitPrice) && !isNaN(amount);
          console.log(`Is valid item: ${isValid}`);
          return isValid;
        })
        .map(item => ({
          Amount: parseFloat(item.Amount).toFixed(2),
          DetailType: "SalesItemLineDetail",
          SalesItemLineDetail: {
            ItemRef: { value: item.SalesItemLineDetail.ItemRef.value },
            Qty: item.SalesItemLineDetail.Qty || 1,
            UnitPrice: parseFloat(item.SalesItemLineDetail.UnitPrice),
            TaxCodeRef: { value: item.SalesItemLineDetail.TaxCodeRef?.value || 'TAX' },
          },
        }));

      console.log("Valid items after filtering:", validItems);

  
      // Add discount line only if discount is valid
      if (invoiceData.discount && !isNaN(invoiceData.discount)) {
        validItems.push({
          Amount: parseFloat(invoiceData.discount).toFixed(2),
          DetailType: "DiscountLineDetail",
          DiscountLineDetail: {
            DiscountAccountRef: {
              value: discountAccId,
            },
          },
        });
      }
  
      const quickBooksInvoiceData = {
        sparse: false,
        Id: invoiceId,
        SyncToken: latestSyncToken,
        CustomerRef: customerRef,
        TxnDate: txnDate,
        Line: validItems,
        PrivateNote: invoiceData.note,
        TxnTaxDetail: {
          TxnTaxCodeRef: { value: 'TAX' },
          TotalTax: parseFloat(invoiceData.taxAmount) || 0,
        },
        CustomField: [
          {
            DefinitionId: "2",
            Name: "Job Type",
            Type: "StringType",
            StringValue: invoiceData.jobType || "",
          },
        ],
        ...(invoiceData.email ? { BillEmail: { Address: invoiceData.email } } : {}),
        ...(invoiceData.termsId ? { TermsRef: { value: invoiceData.termsId } } : {}),
        DueDate: moment(txnDate).add(invoiceData.termsDays || 0, 'days').format('YYYY-MM-DD'),
      };
  
      console.log("QuickBooks Invoice Data:", JSON.stringify(quickBooksInvoiceData, null, 2));
  
      const apiUrl = PRODUCTION
        ? `https://quickbooks.api.intuit.com/v3/company/${realmId}/invoice?operation=update`
        : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/invoice?operation=update`;
  
      const response = await axios.post(apiUrl, quickBooksInvoiceData, {
        headers: {
          Authorization: `Bearer ${token}`,
          'Content-Type': 'application/json',
          Accept: 'application/json',
        },
        timeout: 10000,
      });
  
      if (response.status === 200) {
        console.log("Invoice has been successfully updated in QuickBooks:", response.data);
        return response.data;
      } else {
        console.error("Failed to update the invoice in QuickBooks:", JSON.stringify(response.data, null, 2));
        return null;
      }
    } catch (error) {
      console.error("Error updating invoice in QuickBooks:", JSON.stringify(error.response ? error.response.data : error.message, null, 2));
      throw error;
    }
  };

  const dataInv = await updateInvoice();
  return dataInv
};


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



//////////////////////////////////////////////quickbook add product
let quickbookId=null

const addProductToQuickBooks = async (productData) => {
  let token = getNewAccessToken();

  const postProduct = async (useNewToken = true) => {
    if (useNewToken) {
      console.log('Refreshing access token...');
      token = await getNewAccessToken();
    }

    try {
      const currentDate = moment().toISOString();
      const asOfDate = moment().format('YYYY-MM-DD'); // As of date in yyyy-mm-dd format

      const incomeAccounts = await getAccounts('Income');
      const expenseAccounts = await getAccounts('Expense');

      // Assuming you want to use the first account from each type
      const dynamicIncomeAccountRef = incomeAccounts.length > 0 ? incomeAccounts[0].Id : null;
      const dynamicExpenseAccountRef = expenseAccounts.length > 0 ? expenseAccounts[0].Id : null;

      // let description = productData?.wholeSalePrice === 0 ? "" : `Wholesale Price: ${productData?.wholeSalePrice}, Retail Price: ${productData.retailPrice}`;
      const quickBooksProductData = {
        Name: productData.name + locationInventoryCode,
        Type: "NonInventory",
        IncomeAccountRef: {
          value:dynamicIncomeAccountRef || catalogIncAccId
        },
        ExpenseAccountRef: {
          value: dynamicExpenseAccountRef || catalogExpAccId
        },
        UnitPrice: Number(productData?.retail_price || 0),
        // Description: description,
      };

      console.log("QuickBooks Product Data:", JSON.stringify(quickBooksProductData, null, 2));

      const searchLink = PRODUCTION? `https://quickbooks.api.intuit.com/v3/company/${realmId}/item?minorversion=4` 
                            : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/item?minorversion=4`
      const response = await axios.post(
        searchLink,
        quickBooksProductData,
        {
          headers: {
            Authorization: `Bearer ${token}`,
            'Content-Type': 'application/json',
            Accept: 'application/json'
          }
        }
      );

      if (response.status === 200) {
        console.log("Product has been successfully added to QuickBooks:", response.data);
        console.log("quickbook id:", response.data.Item.Id);

        quickbookId = response.data.Item.Id;

        return { success: true, data: response.data ,quickbookId : quickbookId };
      } else {
    quickbookId=null
        console.error("Failed to add the product to QuickBooks:", response.data);
        return { success: false, error: response.data };
      }
    } catch (error) {
    quickbookId=null

      console.error("Error adding product to QuickBooks:", error.response ? error.response.data : error.message);
      const faultType = error.response?.data?.Fault?.type;
      if (faultType === 'AUTHENTICATION') {
        console.log('Access token expired, refreshing token...');
        try {
          // Retry the API call with a new access token
          return await postProduct(true);
        } catch (refreshError) {
    quickbookId=null
          console.error('Error refreshing token:', refreshError.response?.data || refreshError.message);
          return { success: false, error: refreshError.response?.data || refreshError.message };
        }
      } else if (faultType === 'ValidationFault') {
    quickbookId=null
        console.error("Validation error adding product to QuickBooks:", JSON.stringify(error.response?.data, null, 2));
        return { success: false, error: error.response?.data || error.message };
      } else {
    quickbookId=null
        console.error("Other error adding product to QuickBooks:", error.response?.data || error.message);
        return { success: false, error: error.response?.data || error.message };
      }
    }
  };

  return await postProduct();
};

const updateProductInQuickBooks = async (productData) => {
  let token = accessToken;

  const postUpdate = async (useNewToken = true) => {
    if (useNewToken) {
      console.log('Refreshing access token...');
      token = await getNewAccessToken();
    }

    try {
      // Step 1: Search for the product by name
      const searchResponse = await axios.get(
        `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/query?query=select * from Item where Name='${productData.existingName + locationInventoryCode}'`,
        {
          headers: {
            Authorization: `Bearer ${token}`,
            'Content-Type': 'application/json',
            Accept: 'application/json'
          }
        }
      );

      if (searchResponse.status === 200 && searchResponse.data.QueryResponse.Item && searchResponse.data.QueryResponse.Item.length > 0) {
        const existingProduct = searchResponse.data.QueryResponse.Item[0];

      //  let description = productData.wholeSalePrice ===0? "" :`Wholesale Price: ${productData.wholeSalePrice}, Retail Price: ${productData.retailPrice}`
        const quickBooksProductData = {
          ...existingProduct,
          Name: productData.name + locationInventoryCode,
          UnitPrice: Number(productData.retail_price || 0),
          // Description: `Wholesale Price: ${productData.wholeSalePrice}, Retail Price: ${productData.retailPrice}`,
          Type: "NonInventory",
          // Description: description,
          sparse: true 
        };

        console.log("QuickBooks Product Data for Update:", JSON.stringify(quickBooksProductData, null, 2));

        // Step 3: Make the update request
        const updateResponse = await axios.post(
          `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/item?operation=update&minorversion=4`,
          quickBooksProductData,
          {
            headers: {
              Authorization: `Bearer ${token}`,
              'Content-Type': 'application/json',
              Accept: 'application/json'
            }
          }
        );

        if (updateResponse.status === 200) {
          console.log("Product has been successfully updated in QuickBooks:", updateResponse.data);
          quickbookId = updateResponse.data.Item.Id;
        } else {
          console.error("Failed to update the product in QuickBooks:", updateResponse.data);
        }
      } else {
        console.log(`Product with name '${productData.name}' not found in QuickBooks.`);
      }
    } catch (error) {
      console.error("Error updating product in QuickBooks:", error.response ? error.response.data : error.message);
      const faultType = error.response?.data?.Fault?.type;
      if (faultType === 'AUTHENTICATION') {
        console.log('Access token expired, refreshing token...');
        try {
          // Retry the API call with a new access token
          await postUpdate(true);
        } catch (refreshError) {
          console.error('Error refreshing token:', refreshError.response?.data || refreshError.message);
        }
      } else if (faultType === 'ValidationFault') {
        console.error("Validation error updating product in QuickBooks:", JSON.stringify(error.response?.data, null, 2));
      } else {
        console.error("Other error updating product in QuickBooks:", error.response?.data || error.message);
      }
    }
  };

  await postUpdate();
};

let serviceUniqueId;

const addServiceToQuickBooks = async (serviceData) => {
  let token = accessToken;

  const postService = async (useNewToken = true) => {
    if (useNewToken) {
      console.log('Refreshing access token...');
      token = await getNewAccessToken();
    }

    const incomeAccounts = await getAccounts('Income', 'ServiceFeeIncome');
    const expenseAccounts = await getAccounts('Cost of Goods Sold'); // Fetch expense accounts
    const dynamicServiceExpAccIdNew = expenseAccounts?.length > 0 ? expenseAccounts[0].Id : serviceExpAccId;
    const dynamicServiceIncAccId = incomeAccounts?.length > 0 ? incomeAccounts[0].Id : serviceIncAccId; 
    try {
      const quickBooksServiceData = {
        Name: serviceData.name + locationInventoryCode,
        Type: "Service",
        IncomeAccountRef: {
          value: dynamicServiceIncAccId   || serviceIncAccId
        },
        ExpenseAccountRef: {
          value:dynamicServiceExpAccIdNew || serviceExpAccId
        },
        SalesTaxIncluded: false,
        PurchaseTaxIncluded: false,
        UnitPrice: serviceData.total_cost,
        QtyOnHand: 1, 
      };

      console.log("QuickBooks Service Data:", JSON.stringify(quickBooksServiceData, null, 2));

      const apiUrl = PRODUCTION
      ? `https://quickbooks.api.intuit.com/v3/company/${realmId}/item?minorversion=63`
      : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/item?minorversion=63`;


      const response = await axios.post(
        apiUrl,
        quickBooksServiceData,
        {
          headers: {
            Authorization: `Bearer ${token}`,
            'Content-Type': 'application/json',
            Accept: 'application/json'
          }
        }
      );

      if (response.status === 200) {
        console.log("Service has been successfully added to QuickBooks:", response.data);
        serviceUniqueId = response.data.Item.Id;
      } else {
        console.error("Failed to add the service to QuickBooks:", response.data);
        serviceUniqueId = null;
      }
    } catch (error) {
      serviceUniqueId = null;
      console.error("Error adding service to QuickBooks:", error.response ? error.response.data : error.message);
      const faultType = error.response?.data?.Fault?.type;
      if (faultType === 'AUTHENTICATION') {
        console.log('Access token expired, refreshing token...');
        try {
          await postService(true);
        } catch (refreshError) {
        serviceUniqueId = null;
          console.error('Error refreshing token:', refreshError.response?.data || refreshError.message);
        }
      } else if (faultType === 'ValidationFault') {
        serviceUniqueId = null;
        console.error("Validation error adding service to QuickBooks:", JSON.stringify(error.response?.data, null, 2));
      } else {
        serviceUniqueId = null;
        console.error("Other error adding service to QuickBooks:", error.response?.data || error.message);
      }
    }
  };

  await postService();
};

const updateServiceInQuickBooks = async (serviceData) => {
  let token = accessToken;

  const postUpdate = async (useNewToken = true) => {
    if (useNewToken) {
      console.log('Refreshing access token...');
      token = await getNewAccessToken();
    }

    try {
      const searchLink = PRODUCTION 
        ? `https://quickbooks.api.intuit.com/v3/company/${realmId}/query?query=select * from Item where Name='${serviceData.name + locationInventoryCode}'`
        : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/query?query=select * from Item where Name='${serviceData.name + locationInventoryCode}'`;

      // Step 1: Search for the service by name
      const searchResponse = await axios.get(
        searchLink,
        {
          headers: {
            Authorization: `Bearer ${token}`,
            'Content-Type': 'application/json',
            Accept: 'application/json'
          }
        }
      );

      if (searchResponse.status === 200 && searchResponse.data.QueryResponse.Item && searchResponse.data.QueryResponse.Item.length > 0) {
        const existingService = searchResponse.data.QueryResponse.Item[0];

        // Step 2: Update the service data (without changing account refs)
        const quickBooksServiceData = {
          ...existingService, // Keep existing data
          Name: serviceData.name + locationInventoryCode,
          Type: "Service",
          UnitPrice: serviceData.total_cost,
          SalesTaxIncluded: false,
          PurchaseTaxIncluded: false,
          sparse: true // Perform a partial update
        };

        console.log("QuickBooks Service Data for Update:", JSON.stringify(quickBooksServiceData, null, 2));

        const updateLink = PRODUCTION 
          ? `https://quickbooks.api.intuit.com/v3/company/${realmId}/item?operation=update&minorversion=63`
          : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/item?operation=update&minorversion=63`;

        // Step 3: Make the update request
        const updateResponse = await axios.post(
          updateLink,
          quickBooksServiceData,
          {
            headers: {
              Authorization: `Bearer ${token}`,
              'Content-Type': 'application/json',
              Accept: 'application/json'
            }
          }
        );

        if (updateResponse.status === 200) {
          console.log("Service has been successfully updated in QuickBooks:", updateResponse.data);
          serviceUniqueId = updateResponse.data.Item.Id;
        } else {
          console.error("Failed to update the service in QuickBooks:", updateResponse.data);
        }
      } else {
        console.log(`Service with name '${serviceData.existingName}' not found in QuickBooks.`);
      }
    } catch (error) {
      console.error("Error updating service in QuickBooks:", error.response ? error.response.data : error.message);
      const faultType = error.response?.data?.Fault?.type;
      if (faultType === 'AUTHENTICATION') {
        console.log('Access token expired, refreshing token...');
        try {
          await postUpdate(true);
        } catch (refreshError) {
          console.error('Error refreshing token:', refreshError.response?.data || refreshError.message);
        }
      } else if (faultType === 'ValidationFault') {
        console.error("Validation error updating service in QuickBooks:", JSON.stringify(error.response?.data, null, 2));
      } else {
        console.error("Other error updating service in QuickBooks:", error.response?.data || error.message);
      }
    }
  };

  await postUpdate();
};

let materialQbId=null;

const findAccountByGlCode = async (glCode) => {
  if (!glCode) return null; // Return null if no GL Code is provided

  try {
      const allAccounts = await getAllGLCodes(); // Fetch all accounts from QuickBooks
      
      const matchedAccount = allAccounts.find(account => account.glCode == glCode);
      // console.log('matchedAccount',matchedAccount,allAccounts)
      return matchedAccount ? matchedAccount.id : null; // Return ID if found, else null
  } catch (error) {
      console.error(`Error finding account for GL Code (${glCode}):`, error.message);
      return null;
  }
};

const addMaterialToQuickBooks = async (productData) => {
  try {
    console.log('Refreshing access token...');
    const token = await getNewAccessToken();

    const currentDate = moment().toISOString();
    const asOfDate = moment().format('YYYY-MM-DD');

        // Fetch dynamic account IDs based on account types
        const assetAccounts = await getAccounts('Other Current Asset');   // Fetch asset accounts
        const expenseAccounts = await getAccounts('Cost of Goods Sold'); 
        const incomeAccounts = await getAccounts('Income', 'SalesOfProductIncome'); 

    
        // Use the first account for each type (if available)
        // const materialIncAccIdNew = incomeAccounts?.length > 0 ? incomeAccounts[0].Id : null;
        const dynamicMaterialAssetAccId = assetAccounts?.length > 0 ? assetAccounts[0].Id : null;
        let dynamicMaterialExpAccId = expenseAccounts?.length > 0 ? expenseAccounts[0].Id : null;
        let dynamicMaterialIncAccId = incomeAccounts?.length > 0 ? incomeAccounts[0].Id : null;

        if(productData?.incomeGlCode){
          dynamicMaterialIncAccId =await findAccountByGlCode(productData?.incomeGlCode)
        }
        if(productData?.expenseGlCode){
          dynamicMaterialExpAccId = await findAccountByGlCode(productData?.expenseGlCode)
        }

            console.log('dynamicMaterialAssetAccId',dynamicMaterialIncAccId)
             let itemTypeMap = {
               1: "Inventory",      // Inventory
               2: "NonInventory",   // Non-Inventory
               3: "Service",        // Service
               4: "Group"           // Bundle (QuickBooks uses "Group" for bundles)
             };

            let qbType = productData?.itemType && itemTypeMap[productData?.itemType] || "Inventory"; 

         let track=true
         if(qbType=="Inventory" || productData?.itemType == 1){
          track=true
         }else{
          track=false
          
         }
    // QuickBooks product data structure
    let quickBooksProductData = {
      Name: productData?.name + locationInventoryCode,
      Description: productData?.description || '',
      UnitPrice: productData?.price,
      Type: qbType || "Inventory",
      Sku: productData?.sku,
      ReorderPoint: productData?.ReorderPoint,
      IncomeAccountRef: {
      value:  dynamicMaterialIncAccId || materialIncAccId
      },
      AssetAccountRef: {
        value:dynamicMaterialAssetAccId || materialAssetAccId
      },
      ExpenseAccountRef: {
         value: dynamicMaterialExpAccId || materialExpAccId
      },
      TrackQtyOnHand: track,
      QtyOnHand: productData?.unit_qty || 0,
      InvStartDate: asOfDate,
    };

    console.log("datttttttttttttttttt",productData?.qb_sub_category,productData?.qb_category)
   // ✅ Handle Category & Subcategory Assignments Correctly
   if (productData?.qb_sub_category && productData?.qb_sub_category !== "null") {
    quickBooksProductData.SubItem = true;
    quickBooksProductData.ParentRef = { value: String(productData?.qb_sub_category) };
  } else if (productData?.qb_category && productData?.qb_category !== "null") {
    quickBooksProductData.SubItem = true; // 🔹 Required when assigning a category
    quickBooksProductData.ParentRef = { value: String(productData.qb_category) };
  } else {
    // If no category or subcategory, do not set ParentRef or SubItem
    delete quickBooksProductData?.SubItem;
    delete quickBooksProductData?.ParentRef;
  }

    console.log("QuickBooks Product Data:", JSON.stringify(quickBooksProductData, null, 2));

    const apiUrl = PRODUCTION 
    ? `https://quickbooks.api.intuit.com/v3/company/${realmId}/item` 
    : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/item`;

    const response = await axios.post(
      apiUrl,
      quickBooksProductData,
      {
        headers: {
          Authorization: `Bearer ${token}`,
          'Content-Type': 'application/json',
          Accept: 'application/json'
        }
      }
    );

    if (response.status === 200) {
      materialQbId = response.data.Item.Id;
      console.log("Material has been successfully added to QuickBooks:", response.data);
    } else {
      console.error("Failed to add the material to QuickBooks:", response.data);
      materialQbId=null
    }
  } catch (error) {
    materialQbId=null
    console.error("Error adding material to QuickBooks:", error.response ? error.response.data : error.message);
    const faultType = error.response?.data?.Fault?.type;
    if (faultType === 'ValidationFault') {
      materialQbId=null
      console.error("Validation error adding material to QuickBooks:", JSON.stringify(error.response?.data, null, 2));
    } else {
      materialQbId=null
      console.error("Other error adding material to QuickBooks:", error.response?.data || error.message);
    }
  }
};

const updateMaterialInQuickBooks = async (materialProductData) => {
  let token = accessToken;

  const postUpdateMaterial = async (useNewToken = true) => {
    if (useNewToken) {
      console.log('Refreshing access token...');
      token = await getNewAccessToken();
    }

    try {
      const searchUrl = PRODUCTION 
      ? `https://quickbooks.api.intuit.com/v3/company/${realmId}/query?query=select * from Item where Name='${materialProductData.name + locationInventoryCode}'`
      : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/query?query=select * from Item where Name='${materialProductData.name + locationInventoryCode}'`;
      // Step 1: Search for the material by name
      const searchResponse = await axios.get(
        searchUrl,
        {
          headers: {
            Authorization: `Bearer ${token}`,
            'Content-Type': 'application/json',
            Accept: 'application/json',
          },
        }
      );

      if (searchResponse.status === 200 && searchResponse.data.QueryResponse.Item && searchResponse.data.QueryResponse.Item.length > 0) {
        const existingMaterial = searchResponse.data.QueryResponse.Item[0];



                // Fetch dynamic account IDs based on account types
                const assetAccounts = await getAccounts('Other Current Asset');   // Fetch asset accounts
                const expenseAccounts = await getAccounts('Cost of Goods Sold'); // Fetch expense accounts
              const incomeAccounts = await getAccounts('Income', 'SalesOfProductIncome'); 

            
                // Use the first account for each type (if available)
                // const materialIncAccIdNew = incomeAccounts?.length > 0 ? incomeAccounts[0].Id : null;
                let dynamicMaterialAssetAccId = assetAccounts?.length > 0 ? assetAccounts[0].Id : null;
                let dynamicMaterialExpAccId = expenseAccounts?.length > 0 ? expenseAccounts[0].Id : null;
                let dynamicMaterialIncAccId = incomeAccounts?.length > 0 ? incomeAccounts[0].Id : null;
                let asOfDate = moment().format('YYYY-MM-DD');

                if(materialProductData?.incomeGlCode){
                  dynamicMaterialIncAccId =await findAccountByGlCode(materialProductData?.incomeGlCode)
                }
                if(materialProductData?.expenseGlCode){
                  dynamicMaterialExpAccId = await findAccountByGlCode(materialProductData?.expenseGlCode)
                }


        // ✅ Determine QuickBooks Item Type Based on itemType
        let itemTypeMap = {
          1: "Inventory",      // Inventory
          2: "NonInventory",   // Non-Inventory
          3: "Service",        // Service
          4: "Group"           // Bundle (QuickBooks uses "Group" for bundles)
        };
        let qbType = materialProductData?.itemType && itemTypeMap[materialProductData?.itemType] || "Inventory"; 

    let track=true
    if(qbType=="Inventory" || materialProductData?.itemType == 1){
     track=true
    }else{
     track=false
    }

        // Prepare the updated material product data
        const quickBooksMaterialData = {
          ...existingMaterial,
          Name: materialProductData.name + locationInventoryCode,
          Type: qbType,
          TrackQtyOnHand: track,
          UnitPrice: materialProductData?.price,
          Description: materialProductData?.description || '',
          QtyOnHand: materialProductData?.unit_qty || 0,
          Sku: materialProductData?.sku,
          ReorderPoint: materialProductData?.ReorderPoint,
          sparse: true, // Sparse update to ensure only specified fields are updated
          InvStartDate: asOfDate,
          IncomeAccountRef: {
            value: dynamicMaterialIncAccId || materialIncAccId 
            },
            AssetAccountRef: {
              value:dynamicMaterialAssetAccId || materialAssetAccId
            },
            ExpenseAccountRef: {
               value: dynamicMaterialExpAccId || materialExpAccId
            },

        };

           // ✅ Handle Category & Subcategory Assignments Correctly
           if (materialProductData?.qb_sub_category && materialProductData?.qb_sub_category !== "null") {
            quickBooksMaterialData.SubItem = true;
            quickBooksMaterialData.ParentRef = { value: String(materialProductData.qb_sub_category) };
          } else if (materialProductData?.qb_category && materialProductData?.qb_category !== "null") {
            quickBooksMaterialData.SubItem = true; // Required for QuickBooks to recognize the parent-child relationship
            quickBooksMaterialData.ParentRef = { value: String(materialProductData.qb_category) };
          } else {
            // If no category or subcategory, remove ParentRef & SubItem
            delete quickBooksMaterialData?.ParentRef;
            delete quickBooksMaterialData?.SubItem;
          }

        console.log("QuickBooks Material Data for Update:", JSON.stringify(quickBooksMaterialData, null, 2));

        const updateUrl = PRODUCTION
        ? `https://quickbooks.api.intuit.com/v3/company/${realmId}/item?operation=update&minorversion=4`
        : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/item?operation=update&minorversion=4`;
        // Step 3: Make the update request
        const updateResponse = await axios.post(
          updateUrl,
          quickBooksMaterialData,
          {
            headers: {
              Authorization: `Bearer ${token}`,
              'Content-Type': 'application/json',
              Accept: 'application/json',
            },
          }
        );

        if (updateResponse.status === 200) {
          console.log("Material has been successfully updated in QuickBooks:", updateResponse?.data);
          materialQbId = updateResponse?.data?.Item?.Id;
        } else {
          console.error("Failed to update the material in QuickBooks:", updateResponse.data);
        }
      } else {
        console.log(`Material with name '${materialProductData.name}' not found in QuickBooks.`);
      }
    } catch (error) {
      console.error("Error updating material in QuickBooks:", error.response ? error.response.data : error.message);
      const faultType = error.response?.data?.Fault?.type;
      if (faultType === 'AUTHENTICATION') {
        console.log('Access token expired, refreshing token...');
        try {
          // Retry the API call with a new access token
          await postUpdateMaterial(true);
        } catch (refreshError) {
          console.error('Error refreshing token:', refreshError.response?.data || refreshError.message);
        }
      } else if (faultType === 'ValidationFault') {
        console.error("Validation error updating material in QuickBooks:", JSON.stringify(error.response?.data, null, 2));
      } else {
        console.error("Other error updating material in QuickBooks:", error.response?.data || error.message);
      }
    }
  };

  await postUpdateMaterial();
};

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


const addDataToProductTable = async (data) => {
  let currentDate = Math.round(moment().valueOf() / 1000);

  // Loop through each product in the data array
  console.log('addDataToMaterialTable--data1', data)
  for (const product of data) {
    const retail_price = product.retail_price || product.product_retail_price
    const name = product.name || product.product_name
    const quantity = product.quantity || product.product_qty || product.location_quantity
    quickbookId = null;

    const productData = {
      inhouse: 2,
      price: Number(retail_price),
      retail_price: Number(retail_price),
      unit:4,
      added_by: 1,
      name: name,
      timestamp: currentDate,
      isAdditionalProduct: 1,
    };

    const sampleProductDataForQuickbook = {
      name: name,
      unit_qty: quantity || 0,
      cost: Number(retail_price) || 0,
      wholeSalePrice: Number(retail_price) || 0,
      retailPrice: Number(retail_price) || 0,
    };

    let productNameExist = await Product.findProductByName(name);

    if (productNameExist) {
      console.log(productNameExist.dataValues, "Product exists");
      const existingProductId = productNameExist.dataValues.id;

      if (productNameExist.dataValues.syncWithQuickbooks === 0) {
        // Add to QuickBooks if not synced
        try {
          const productResponse = await addProductToQuickBooks(sampleProductDataForQuickbook);
          console.log('productResponse2', productResponse)
          if (productResponse.quickbookId) {
            productData.syncWithQuickbooks = 1;
            productData.quickbookId = productResponse.quickbookId;
          }
        } catch (error) {
          console.log('Error adding to QuickBooks', error);
        }
      } else {
        // Update in QuickBooks
        try {
          await updateProductInQuickBooks(sampleProductDataForQuickbook);
          if (quickbookId) {
            productData.syncWithQuickbooks = 1;
            productData.quickbookId = quickbookId;
          }
        } catch (error) {
          console.log('Error updating in QuickBooks', error);
        }
      }

      // Update existing product in your system
      try {
        let updated = await Product.updateProduct(existingProductId, productData);
        console.log('Product updated', updated);
      } catch (error) {
        console.log('Error updating product in your system', error);
      }
    } else {
      // Add to QuickBooks if product doesn't exist in your system
      try {
        const productResponse = await addProductToQuickBooks(sampleProductDataForQuickbook);
        console.log('productResponse1', productResponse)
        if (productResponse.quickbookId) {
          productData.syncWithQuickbooks = 1;
          productData.quickbookId = productResponse.quickbookId;
        }
      } catch (error) {
        console.log('Error adding to QuickBooks', error);
      }

      // Add new product to your system
      try {
        let result = await Product.addProduct(productData);
        console.log('Product added', result);
      } catch (error) {
        console.log('Error adding product in your system', error);
      }
    }
  }
};

const addDataToServiceTable = async (data) => {
  let currentDate = Math.round(moment().valueOf() / 1000);

  // Loop through each product in the data array
  // console.log('addDataToMaterialTable--data2', data)
  for (const product of data) {
    
    serviceUniqueId = null;

    const productData = {
      inhouse: 2,
      service_type:36,
      markup:0,             // Number(product?.markup || 0),
      cost: Number(product.cost || 0),
      total_cost: Number(product.total_cost || 0),
      added_by: 1,
      name: product.name,
      timestamp: currentDate,
      makeAsTask: product?.makeAsTask,
      makeInstallCard: product?.makeInstallCard,
    };

    const sampleProductDataForQuickbook = {
      name: product.name,
      unit_qty: product.quantity || 0,
      cost: Number(product.total_cost) || 0,
      wholeSalePrice: Number(product.total_cost) || 0,
      retailPrice: Number(product.total_cost) || 0,
    };

    let isServiceNameExist = await Product.findServiceByName(product.name);
    console.log('serviceservice--1-', isServiceNameExist)

    if (isServiceNameExist) {
      console.log(isServiceNameExist.dataValues, "Service exists");
      const existingProductId = isServiceNameExist.dataValues.id;

      if (isServiceNameExist.dataValues.syncWithQuickbooks === 0) {
        // Add to QuickBooks if not synced
        try {
          await addServiceToQuickBooks(sampleProductDataForQuickbook);
          if (serviceUniqueId) {
            productData.syncWithQuickbooks = 1;
            productData.quickbookId = serviceUniqueId;
          }
        } catch (error) {
          console.log('Error adding to QuickBooks', error);
        }
      } else {
        // Update in QuickBooks
        try {
          await updateServiceInQuickBooks(sampleProductDataForQuickbook);
          if (serviceUniqueId) {
            productData.syncWithQuickbooks = 1;
            productData.quickbookId = serviceUniqueId;
          }
        } catch (error) {
          console.log('Error updating in QuickBooks', error);
        }
      }

      // Update existing product in your system
      try {
        let updated = await Product.updateLabourCostServices(existingProductId, productData);
        console.log('Service updated', updated);
        console.log('existingServiceId--1-', updated)
      } catch (error) {
        console.log('Error updating service in your system', error);
      }
    } else {
      // Add to QuickBooks if product doesn't exist in your system
      try {
        await addServiceToQuickBooks(sampleProductDataForQuickbook);
        if (serviceUniqueId) {
          productData.syncWithQuickbooks = 1;
          productData.quickbookId = serviceUniqueId;
        }
      } catch (error) {
        console.log('Error adding to QuickBooks', error);
      }

      // Add new product to your system
      try {
        let result = await Product.addLabourCostServices(productData);
        console.log('Service added', result);
        console.log('existingServiceId--1-', result)
      } catch (error) {
        console.log('Error adding service in your system', error);
      }
    }
  }
};

const addDataToMaterialTable = async (data) => {
  let currentDate = Math.round(moment().valueOf() / 1000);

  // Loop through each product in the data array
  // console.log('addDataToMaterialTable--data3', data)
  for (const product of data) {

    const price = product.price || product.product_price
    const name = product.name || product.product_name
    const newQuantity = product.quantity || product.product_qty || product.location_quantity
    
    quickbookId = null;
    const quantity = Number(newQuantity) || 1;
    const markUpCost = Number(product.markUpCost) || 0;
    const totalCost = markUpCost * quantity;

    const productData = {
      inhouse: 2,
      price: Number(totalCost || 0),
      retail_price: Number(totalCost || 0),
      added_by: 1,
      unit:4,
      name: name,
      timestamp: currentDate,
      isAdditionalProduct: 1,
      meterial:1
    };

    const sampleProductDataForQuickbook = {
      name: name,
      unit_qty: quantity || 0,
      cost: Number(totalCost || 0),
      wholeSalePrice: Number(totalCost || 0),
      retailPrice: Number(totalCost || 0),
      description: '',
      price: price ? price : 0,
      qb_category:  null,
      qb_sub_category: null,
      sku: null,
      ReorderPoint: 0,
      itemType: 1,
      incomeGlCode : null,
      expenseGlCode : null,
    };

    let productNameExist = await Product.findProductByName(name);

    if (productNameExist) {
      console.log(productNameExist.dataValues, "Product exists");
      const existingProductId = productNameExist.dataValues.id;

      if (productNameExist.dataValues.syncWithQuickbooks === 0) {
        // Add to QuickBooks if not synced
        try {
          await addMaterialToQuickBooks(sampleProductDataForQuickbook);
          if (materialQbId) {
            productData.syncWithQuickbooks = 1;
            productData.quickbookId = materialQbId;
          }
        } catch (error) {
          console.log('Error adding to QuickBooks', error);
        }
      } else {
        // Update in QuickBooks
        try {
          await updateMaterialInQuickBooks(sampleProductDataForQuickbook);
          if (materialQbId) {
            productData.syncWithQuickbooks = 1;
            productData.quickbookId = materialQbId;
          }
        } catch (error) {
          console.log('Error updating in QuickBooks', error);
        }
      }

      // Update existing product in your system
      try {
        let updated = await Product.updateProduct(existingProductId, productData);
        console.log('material updated', updated);
      } catch (error) {
        console.log('Error updating material in your system', error);
      }
    } else {
      // Add to QuickBooks if product doesn't exist in your system
      try {
        await addMaterialToQuickBooks(sampleProductDataForQuickbook);
        if (materialQbId) {
          productData.syncWithQuickbooks = 1;
          productData.quickbookId = materialQbId;
        }
      } catch (error) {
        console.log('Error adding to QuickBooks', error);
      }

      // Add new product to your system
      try {
        let result = await Product.addProduct(productData);
        console.log('Material added', result);
      } catch (error) {
        console.log('Error adding material in your system', error);
      }
    }
  }
};


// below function was mandatory

const addRequests = async (req, res) => {
  try {
    const { userId,isClient, isRequestEstimate, noServiceNoTask, taxCategory, selectedEstimators, values, userName, address,allLocationSame, allServiceLocationSame, allRfpServiceLocationSame,addDataToOtherFacilityServices,
      allRfpProductsLocationSame, quotation, rfpProducts, materials, rfpServices,produtFormulaMaterial,isSplitOrder,addDataToOtherFacility,targetedFacilityIds, noChargeReason, salesRepId, collaborators, is_draft, saveAsDraft,salesRepName } = req.body
    
    const productAttachments = values?.productAttachments || {} // Get product attachments from frontend

      let userType = isClient? 3 : req?.body?.userType 
      let mirrorData = [];
      mirrorData.productData = [];
      mirrorData.materialData = [];
      mirrorData.serviceData = [];
      mirrorData.rfpProductData = [];
      mirrorData.rfpServiceData = [];
      mirrorData.orderData = [];

    // 📊 Progress tracking function
    const emitProgress = (step, message, progress = 0) => {
      console.log(`[PROGRESS-MODAL] 📊 Progress: ${step} - ${message} (${progress}%)`);
      
      const progressData = {
        step,
        message,
        progress,
        timestamp: Date.now(),
        userId: userId, // Include user ID for filtering
        domain: DOMAIN // Add domain for filtering (same as chat)
      };
      
      // Emit to working socket server (same as chat)
      console.log(`[PROGRESS-MODAL] 📡 Emitting progress to working socket server...`);
      try {
        const workingSocket = ioClient(REST_API_IO, {
          path: '/socket.io',
          transports: ['websocket', 'polling'],
          secure: true,
          timeout: 5000
        });
        
        workingSocket.on('connect', () => {
          console.log(`[PROGRESS-MODAL] 🔌 Connected to working socket server`);
          console.log(`[PROGRESS-MODAL] 📊 Emitting progress data:`, progressData);
          
          // Try emitting as 'message' event (same as chat) to test if socket server handles it
          workingSocket.emit('message', {
            ...progressData,
            type: 'progress',
            conversationId: `progress_${userId}`,
            senderId: 'system'
          });
          
          // Also try the original request_progress event
          workingSocket.emit('request_progress', progressData);
          
          console.log(`[PROGRESS-MODAL] ✅ Successfully emitted progress to working socket server`);
          
          // Disconnect after emitting
          setTimeout(() => {
            workingSocket.disconnect();
          }, 1000);
        });
        
        workingSocket.on('connect_error', (error) => {
          console.error(`[PROGRESS-MODAL] ❌ Working socket connection error:`, error);
        });
        
      } catch (error) {
        console.error(`[PROGRESS-MODAL] ❌ Error creating working socket connection:`, error);
      }
    };

    // Initial progress
    const orderType = quotation === 1 ? 'estimate' : 'sales order';
    console.log(`[PROGRESS-MODAL] 🚀 Starting ${orderType} creation for user:`, userId);
    emitProgress('start', `Initializing ${orderType} creation...`, 5);
    
    // Test immediate progress emission
    setTimeout(() => {
      console.log(`[PROGRESS-MODAL] 🧪 Testing immediate progress emission...`);
      emitProgress('test', 'Testing progress connection...', 7);
    }, 1000); 

    const additionalProducts = values?.products?.filter(item => item?.isAdditionalProduct == true);
    if (additionalProducts?.length) {
      await addDataToProductTable(additionalProducts);
    }         
    const additionalServices = values?.selectedServices?.filter(item => item?.isAdditionalProduct == true);
    if (additionalServices?.length) {
      await addDataToServiceTable(additionalServices);
    }         
    const additionalMaterials = materials?.filter(item => item?.isAdditionalProduct == true);
    if (additionalMaterials?.length) {
      await addDataToMaterialTable(additionalMaterials);
    }    
    
    emitProgress('data_preparation', `Preparing ${orderType} data...`, 10);
    let currentDate = moment().unix()
    let isNoCharge = (values.project_categories && values.project_categories["id"] === 13) ? true : false;
    const skills = values.skills?.map(row => row.id)
    const workflows = values.workflows?.map(row => row.id)
    let currentTime = moment().unix()
    let submission_deadline = values.start // moment(values.start).unix()
    let participation_ends = values.end // moment(values.end).unix()
    let uploaded = values.avatarUrl;
    let requestdata = {}
    requestdata['name'] = values.name,
    requestdata['description'] = values.description,
    requestdata['po_number'] = values.po_number || null,
    requestdata['markup_type'] = values.markup_type?.id || 2,
    requestdata['services'] = skills,
    requestdata['notes']=values.notes||"",
    requestdata['no_charge_reason'] = noChargeReason || "",
    requestdata['labour_cost_services'] = values.serviceData,
    requestdata['labour_cost'] = values.labour_cost,
    requestdata['quotation']=quotation||0
    requestdata['is_draft'] = (is_draft === true || saveAsDraft === true) ? 1 : 0
    requestdata['new_order'] = (quotation === 0 || !quotation) && !isSplitOrder && (userType == 3 || isClient) ? 1 : 0
    requestdata['catalog_type']=values?.catalog_type?.id||null
    requestdata['show_install_price'] = values.show_install_price == 0 ? 0 : 1;
    // requestdata['workflows'] = workflows,
    requestdata['project_type'] = values.project_type ? values.project_type["id"] : null
    requestdata['sign_agreement'] = values.sign_agreement ? values.sign_agreement.map(item => item.id).join(',') : null,
    requestdata['project_category'] = values.project_categories ? values.project_categories["id"] : null
    requestdata['no_charge_type'] = values.no_charge_type ? values.no_charge_type["id"] : null
    requestdata['community'] = values.community ? values.community["id"] : null
    requestdata['sales_rep_ids'] = salesRepId ? salesRepId : (values.sales_rep_ids ? values.sales_rep_ids : null)
    const collaboratorsStr = collaborators != null
      ? (Array.isArray(collaborators) ? collaborators.filter(Boolean).join(',') : String(collaborators).trim())
      : null
    requestdata['collaborators'] = collaboratorsStr || null
    requestdata['map_service'] = values.map_service ? values.map_service["id"] : null
    requestdata['submission_deadline'] = submission_deadline
    if (selectedEstimators && selectedEstimators?.length > 0) {
      const newEstimatorsId = selectedEstimators?.map(d => d.id)?.join(',') || null;
      requestdata['estimator_ids'] = newEstimatorsId || null;
    }
    if(values?.project_categories?.id){
    // requestdata['isReview'] = values?.project_categories["id"] == 12 ? 2 : 0
    requestdata['isReview'] =  0
    }
    // requestdata['isReview'] = values?.project_categories["id"] == 12 ? 2 : 0
    // requestdata['is_single_card'] = values?.is_single_card == false ? 0 :1;
    if (values.end !== (undefined || null)) {
      requestdata['participation_ends'] = participation_ends
    } else {
      requestdata['participation_ends'] = 0
    }
    requestdata['installation_start_date'] = values.installation_start_date || null
    requestdata['installation_end_date'] = values.installation_end_date || null
    requestdata['start_date'] = null
    if (values.welcome_message == true) {
      requestdata['welcome_message'] = values.welcome_message
      requestdata['subject'] = values.subject,
        requestdata['email_content'] = values.email_content
    } else {
      requestdata['welcome_message'] = false
      requestdata['subject'] = null,
        requestdata['email_content'] = null
    }
    requestdata['type'] = 2

    const hasOtherFacilityProducts =
      Array.isArray(values?.products) &&
      values.products.some(
        (product) =>
          product?.facility_id != null &&
          String(product.facility_id) !== String(FACILITY_ID)
      );
    const hasOtherFacilityServices =
      Array.isArray(values?.selectedServices) &&
      values.selectedServices.some(
        (service) =>
          service?.facility_id != null &&
          String(service.facility_id) !== String(FACILITY_ID)
      );
    if (values.draft == true || requestdata['is_draft'] === 1) {
      requestdata['status'] = values.draft || 8
    } else {
      requestdata['status'] = (isNoCharge && !hasOtherFacilityProducts && !hasOtherFacilityServices) ? 3 : ((values?.client_approve == false && !hasOtherFacilityProducts && !hasOtherFacilityServices) ? 3 : 2);
      requestdata['min_payment_status'] = isNoCharge ? 1 : values?.clients?.billing_type == 0 ? 1 : 0;
      // requestdata['client_approve'] = values?.client_approve == false ? 0 :1;
      requestdata['client_approve'] = isRequestEstimate ? 0 : userType === 3 ? 0 : (values?.client_approve == false ? 0 : 1);
    }
    if(!empty(quotation) && quotation===1){
      requestdata['status'] = 8
      requestdata['is_request_estimate'] = isRequestEstimate ? 1 : 0
      let hasService = false
      if (values.additionalSelectedServices && values.additionalSelectedServices.length > 0) {
        hasService = true
      }
      requestdata['quotation_status'] = (requestdata['is_draft'] === 1) ? 1 : (values?.client_approve == false ? 4 : (isRequestEstimate ? (hasService == true ? 3 : 2) : (userType === 3 ? 2 : 2)))
      // requestdata['client_approve'] = values?.client_approve == false ? 0 :1;
      requestdata['client_approve'] = isRequestEstimate ? 0 : userType === 3 ? 0 : (values?.client_approve == false ? 0 : 1);
      requestdata['send_to_client'] = 0; // userType === 3 ? 1 : 0;
    }

    // let isNoCharge = (values.project_categories && values.project_categories["id"] === 13) ? true : false;
    let negativeAmount = isNoCharge?  -1 : 1

    let totalDiscountForNoCharge = (parseFloat(parseFloat(values.total).toFixed(2)) + parseFloat(parseFloat(values.taxAmount).toFixed(2))).toFixed(2)
    
    requestdata['added_by'] = userId,
    requestdata['created_at'] = currentTime,
    requestdata['timestamp'] = currentTime
    requestdata['noServiceNoTask'] = noServiceNoTask ? 1 : 0
    if (!empty(uploaded)) {
      for (const row of uploaded) {
        if(row?.file_type?.startsWith('image/')){
          requestdata['thumbnail'] = row.url
        }
      }
    }
    requestdata['is_location'] = allLocationSame
    requestdata['is_service_location'] = allServiceLocationSame || 0
    requestdata['is_rfp_service_location'] = allRfpServiceLocationSame || 0
    requestdata['is_rfp_product_location'] = allRfpProductsLocationSame || 0
    requestdata['origin_facility_id'] = FACILITY_ID
    requestdata['is_split_order'] = isSplitOrder ? 1 :0
    const facilityTypeIds =
    Array.isArray(values?.facility_type)
      ? values.facility_type.map(it => it?.id).filter(Boolean).join(',')
      : (values?.facility_type?.id ?? null);

  requestdata['facility_type'] = facilityTypeIds || null;

    let clientDetails =null
    if(userId){
      clientDetails = await Partners.findClientByUserId(userId)
    }

    UniqueEstimateId =null
    doc_number = null
    let clientName = values?.clients?.quickbookCustomerName || userName;
    
    const products = Array.isArray(values?.products) ? values.products : [];
    const productWithInstallPriceData = []

    products.forEach(catalog => {
  // 1. Main product data
  productWithInstallPriceData.push({
    ...catalog, 
  });

  // 2. Install price as additional line
  if (catalog.install_cost && catalog.install_cost > 0) {
    productWithInstallPriceData.push({
      name: 'Install Price',
      taxCode: 'SI026666',
      actual_retail_price: catalog.install_cost,
      quantity: ( catalog?.quantity  || 1)*(catalog?.locationQuantity || 1)
    });
  }
});
    const avalaraData ={
      name: values.name,
      description: values.description,
      clientDetails :values?.clients,
      catalogs : productWithInstallPriceData,   // values?.products, 
      services :values?.additionalSelectedServices,
      materials : materials,
      rfpProducts : rfpProducts,            //.filter(val => val.material ==1),
      rfpServices : rfpServices,
      produtFormulaMaterial:[],  //produtFormulaMaterial,
      discount:isNoCharge? 0 : Number(values.invoice_discount)  ||0,
      isNoCharge : isNoCharge,
      projectData:values?.community
    }
    
    console.log('avalaraData', avalaraData)
    
    emitProgress('avalara_tax', 'Calculating taxes with Avalara...', 14);
    // let addRequestToAvalata = {}
    // if(userType !==3){
    //   addRequestToAvalata = await createAvalaraEstimate(avalaraData)
    // }
//no need  avalara tax from here in QB production  already autocalculating the tax
    let addRequestToAvalata = {};
    // try {
    //   if (
    //     userType !== 3 && empty(isRequestEstimate) &&
    //     (
    //       (values?.products?.length > 0) ||
    //       (values?.additionalSelectedServices?.length > 0) ||
    //       (materials?.length > 0) ||
    //       (rfpServices?.length > 0) ||
    //       (rfpProducts?.length > 0)
    //     )
    //   ) {
    //     addRequestToAvalata = await createAvalaraEstimate(avalaraData);
    //     emitProgress('avalara_success', 'Tax calculation completed successfully!', 18);
    //   } else {
    //     console.warn("⚠️ Skipping Avalara call: No sellable line items or userType === 3.");
    //     emitProgress('avalara_skipped', 'Skipping tax calculation - no sellable items', 22);
    //   }
    // } catch (e) {
    //   console.warn("⚠️ Avalara error skipped:", e.message);
    //   emitProgress('avalara_error', `Tax calculation failed: ${e.message}`, 24);
    // } // comment this line if there cause any issue and uncommented above one and the catalogs : values?.products || [], this line beacuse it is also fixed with that line

    // console.log('addRequestToAvalata', addRequestToAvalata)
    // requestdata['avalara_transaction_id'] = addRequestToAvalata?.avalara_transaction_id || null
    // requestdata['avalara_code'] = Number(addRequestToAvalata?.avalara_code) || null
 
    let avalaraTaxAmount = 0
    // if(requestdata.avalara_code){
    //    avalaraTaxAmount = addRequestToAvalata?.totalTax || 0
    // }else{
    //   avalaraTaxAmount =isNoCharge? 0 : values.taxAmount ||  0
    // }

    // if(values.project_categories.name =='Time & Material'){
      // const additionalProducts = values?.products?.filter(item => item?.isAdditionalProduct == true);
      // if (additionalProducts?.length) {
      //   addDataToProductTable(additionalProducts);
      // }         
      // const additionalServices = values?.selectedServices?.filter(item => item?.isAdditionalProduct == true);
      // if (additionalServices?.length) {
      //   addDataToServiceTable(additionalServices);
      // }         
      // const additionalMaterials = materials?.filter(item => item?.isAdditionalProduct == true);
      // if (additionalMaterials?.length) {
      //   addDataToMaterialTable(additionalMaterials);
      // }         
    // }
    let labourCostServices = await Product.getAllLabourCostServices();
    labourCostServices = Array.isArray(labourCostServices) ? labourCostServices : [];
    if (values?.project_categories?.name === 'Time & Material') {
      values.selectedServices = values.selectedServices || [];
      const has10 = values.selectedServices.some(s => +s.id === 10 || +s.labour_cost_service_id === 10);

      if (!has10) {
        const svc10 = labourCostServices.find(s => +s.id === 10);
        if (svc10) {
          values.selectedServices.push({
            id: 10,
            labour_cost_service_id: 10,
            name: svc10.name,
            quantity: 0,
            unit_cost: Number(svc10.total_cost ?? svc10.cost ?? 0),
            cost: 0,
            total_cost: 0,
            makeAsTask: 0,
            makeInstallCard: 1,
            product_id: null,
            recipe_id: null,
            indexId: Date.now(),
            productIndex: 0,
            addresses: []
          });
        }
      }
    }
    console.log('values.selectedServicessssss',values.selectedServices)
    let productDataFromDB = [];

    const checkAndLogProducts = async (values) => {
      console.log("+++++++++", values);
      
      // Check if values.products exists and is an array
      if (!Array.isArray(values.products)) {
        console.error('Error: values.products is not an array or is undefined.');
        return; // Exit the function if it's not an array
      }
    
      // Loop through each product in the array
      for (const product of values.products) {
        try {
          // Log the entire product object for clarity
          console.log('Checking Product:', product);
    
          // Check if the product exists in the database by its name
          let productNameExist = await Product.findProductByName(product.name);
    
          // If the product exists, store it in the productDataFromDB array
          if (productNameExist) {
            // console.log('Product found in database:', productNameExist.dataValues);
            // Push the product data into productDataFromDB array
            productDataFromDB.push(productNameExist.dataValues);
          } else {
            console.log(`Product with name "${product.name}" not found in the database.`);
          }
        } catch (error) {
          console.error('Error while searching for product:', product.name, error);
        }
      }
    
      // Log the final array of products found in the database
      // console.log('All products found in the database:', productDataFromDB);
    };

    let productsData = [];
    let materialsData = [];
    let servicesData = []
	  let rfpServicesData =[]
	  let rfpProductsData =[]
	  let rfpMaterialData =[]
    
    const resultofdbproduct = await checkAndLogProducts({ products: values.products })
     productsData = (productDataFromDB.length > 0 ? productDataFromDB : values.products || []).filter(item => item.syncWithQuickbooks === 0 || item.syncWithQuickbooks === null);
     materialsData = (materials || []).filter(item => item.syncWithQuickbooks === 0 ||  item.syncWithQuickbooks === null);
    const quantityDiffData = values?.selectedServices?.filter(value => value.quantityDifference !== 0);
    // const servicesData = (quantityDiffData || []).filter(item => item.syncWithQuickbooks === 0 || item.syncWithQuickbooks === null && item.quantityDifference !== 0);
     servicesData = (values?.additionalSelectedServices || []).filter(item => item.syncWithQuickbooks === 0 || item.syncWithQuickbooks === null );

     
    


    const findByIdAndFilter = async (rfpServices) => {
      const rfpServicesData = await Promise.all(
        (rfpServices || []).map(async (service) => {
          const serviceData = await Product.getLabourCostServicesById(service.labour_cost_service_id);
          
       
          if (serviceData[0]?.syncWithQuickbooks === 0 || serviceData[0]?.syncWithQuickbooks === null) {
            return serviceData; // Keep the service if syncWithQuickbooks in serviceData is 0
          }
          return null; // Discard the service if syncWithQuickbooks is not 0
        })
      );
    
      // Filter out null values from the final result
      return rfpServicesData.filter(service => service !== null);
    };

    const findByIdAndFilterProducts = async (rfpProducts) => {
      const rfpProductsData = await Promise.all(
        (rfpProducts || []).map(async (product) => {
          const productData = await Product.getProductById(product.product_id);
          
          // Check if the first productData entry has syncWithQuickbooks as 0 or null
          if (productData[0]?.syncWithQuickbooks === 0 || productData[0]?.syncWithQuickbooks === null) {
            return productData; // Keep the product if syncWithQuickbooks in productData is 0
          }
          return null; // Discard the product if syncWithQuickbooks is not 0
        })
      );
    

      return rfpProductsData.filter(product => product !== null);
    };

    //  rfpServicesData = await findByIdAndFilter(rfpServices);
     rfpServicesData = (rfpServices || []).filter(item => item.syncWithQuickbooks === 0 || item.syncWithQuickbooks === null);
    //  rfpProductsData = await findByIdAndFilterProducts(rfpProducts);
     rfpProductsData = (rfpProducts || []).filter(item => ((item.syncWithQuickbooks === 0 || item.syncWithQuickbooks === null) && item.material == 0));
     rfpMaterialData = (rfpProducts || []).filter(item => ((item.syncWithQuickbooks === 0 || item.syncWithQuickbooks === null) && item.material == 1));

    let customerData = (values?.clients?.syncWithQuickBook === null || values?.clients?.syncWithQuickBook === 0) ? values.clients : null;


         ////////////////////// verify synced products in quickbooks //////////////////////
         const syncedProductsData = values.products.filter(item => item.syncWithQuickbooks === 1);
         for(const product of syncedProductsData){
           const catalogResultFromQb = await validateInventoryItemsInQbForPo(product,"quickbookId");
           if(catalogResultFromQb?.itemData){
             productsData.push(catalogResultFromQb?.itemData);
             const updateData ={
               syncWithQuickbooks: 0,
               quickbookId: null
             }
             const updateProduct = await Product.updateProductByQbId(product.quickbookId,updateData);
           }
         }
    
        const syncedMaterialData = materials.filter(item => item.syncWithQuickbooks === 1);
         for(const material of syncedMaterialData){
           const materialResultFromQb = await validateInventoryItemsInQbForPo(material,"quickbookId");
           if(materialResultFromQb?.itemData){
             materialsData.push(materialResultFromQb?.itemData);
             const updateData ={
               syncWithQuickbooks: 0,
               quickbookId: null
             }
             const updateMaterial = await Product.updateProductByQbId(material.quickbookId,updateData);
           }
         }
    
        const syncedServicesData = values?.additionalSelectedServices.filter(item => item.syncWithQuickbooks === 1);
         for(const service of syncedServicesData){
           const serviceResultFromQb = await validateInventoryItemsInQbForPo(service,"quickbookId");
           if(serviceResultFromQb?.itemData){
             servicesData.push(serviceResultFromQb?.itemData);
             const updateData ={
               syncWithQuickbooks: 0,
               quickbookId: null
             }
             const updateService = await Product.updateServiceByQbId(service.quickbookId,updateData);
           }
         }
    
        const syncedRfpServicesData = rfpServices.filter(item => item.syncWithQuickbooks === 1);
        for(const service of syncedRfpServicesData){
          const serviceResultFromQb = await validateInventoryItemsInQbForPo(service,"quickbookId");
          if(serviceResultFromQb?.itemData){
            rfpServicesData.push(serviceResultFromQb?.itemData);
             const updateData ={
               syncWithQuickbooks: 0,
               quickbookId: null
             }
             const updateService = await Product.updateServiceByQbId(service.quickbookId,updateData);
          }
        }
        
        const syncedRfpProductsData = rfpProducts.filter(item => item.syncWithQuickbooks === 1 && item.material == 0);
         for(const product of syncedRfpProductsData){
          const productResultFromQb = await validateInventoryItemsInQbForPo(product,"quickbookId");
          if(productResultFromQb?.itemData){
            rfpProductsData.push(productResultFromQb?.itemData);
             const updateData ={
               syncWithQuickbooks: 0,
               quickbookId: null
             }
             const updateProduct = await Product.updateProductByQbId(product.quickbookId,updateData);
          }
         }
    
         const syncedRfpMaterialData = rfpProducts.filter(item => item.syncWithQuickbooks === 1 && item.material == 1);
         for(const material of syncedRfpMaterialData){
          const materialResultFromQb = await validateInventoryItemsInQbForPo(material,"quickbookId");
          if(materialResultFromQb?.itemData){
            rfpMaterialData.push(materialResultFromQb?.itemData);
             const updateData ={
               syncWithQuickbooks: 0,
               quickbookId: null
             }
             const updateMaterial = await Product.updateProductByQbId(material.quickbookId,updateData);
          }
         }
    
         if(values.clients?.syncWithQuickBook === 1){
          const validateClient = await validateClientInQb(values.clients.partnerQbId);
          if(validateClient?.removeQbid){
            customerData = values.clients
            const updateData ={
              syncWithQuickBook: 0,
              partnerQbId: null
            }
            const updateClient = await Client.updateVendorByQbId(values.clients.partnerQbId,updateData);
          }
         }
     
         /////////////////////////////////


    const productNames = productsData.map(product => product.name).join(', ');
    const materialNames = materialsData.map(product => product.name).join(', ');
    const servicesNames = servicesData.map(product => product.name).join(', ');
    // const rfpServicesNames = rfpServicesData.flat().map(service => service.name).join(', ');
    const rfpServicesNames = rfpServicesData.map(service => service.name).join(', ');
    // const rfpProductsNames = rfpProductsData.flat().map(service => service.name).join(', ');
    const rfpProductsNames = rfpProductsData.map(service => service.name).join(', ');
    const rfpMaterialNames = rfpMaterialData.map(product => product.name).join(', ');

    const ReasonWithQb = `This ${quotation===1? 'Estimate' : 'Sales Order'} is not synced with QuickBooks because the following are not in QuickBooks.
                ${productsData.length > 0 ? `Catalogs : ${productNames}` : ''}
                ${materialsData.length > 0 ? `Materials : ${materialNames}` : ''}
                ${servicesData.length > 0 ? `Services: ${servicesNames}` : ''}
                ${rfpServicesData.length > 0 ? `Proposal Services : ${rfpServicesNames}` :''}
                ${rfpProductsData.length > 0 ? `Proposal Products : ${rfpProductsNames}` :''}
                ${rfpMaterialData.length > 0 ? `Proposal Materials : ${rfpMaterialNames}` :''}
                ${customerData? `Customer : ${customerData.name}` :''}
                `;

    console.log('ReasonWithQbReasonWithQb',ReasonWithQb);

    const readyToSync = (productsData.length === 0 && materialsData.length === 0 && servicesData.length === 0 && rfpServicesData.length === 0 && rfpProductsData.length === 0 && rfpMaterialData.length === 0 && !customerData);
    console.log('readyToSync',readyToSync);
    ////////////////////For Quickbook///////////////////////////////////////////////////////////
    if (
      // productsData.length === 0 ||
      // materialsData.length === 0 ||
      // servicesData.length === 0 ||
      // rfpServicesData.length === 0 ||
      // rfpProductsData.length === 0 ||
      // !customerData
      readyToSync
    ) {
    emitProgress('quickbooks_sync', 'Syncing with QuickBooks...', 28);
    try {
      
      emitProgress('quickbooks_customer', 'Finding customer in QuickBooks...', 33);
      let custimerId;
      try {
        custimerId =values?.clients?.partnerQbId 
      } catch (error) {
        console.log("Error finding customer in QuickBooks:", error);
        emitProgress('quickbooks_customer_error', 'Failed to find customer in QuickBooks', 35);
        throw error; // Re-throw to trigger the main catch block
      }
      let TotalDiscount = 0

      if (values?.products && values.products.length > 0) {
        emitProgress('quickbooks_products', 'Processing catalogs for QuickBooks...', 38);
      }
      let installPriceQbId = null;
let findInstallPriceQbId = await findItemByName('Install Price');
if(findInstallPriceQbId && findInstallPriceQbId.Id){
  installPriceQbId = findInstallPriceQbId.Id;
}

      let productItems = await Promise.all(
        values?.products?.map(async product => {
          let productId =  product?.quickbookId                         
          let  Quantity = (product.quantity * product.locationQuantity) || 1
          // let productPrice =product.markup_type===1? product.price : product.retail_price
          let unitPrice = product?.actual_retail_price || product?.retail_price || 0
          let productPrice =isNoCharge? 0 : (product?.isAdditionalProduct ? parseFloat(product?.price)?.toFixed(2) || 0 : parseFloat(unitPrice)?.toFixed(2) || 0)
          // TotalDiscount += Number(product.discount)
          if(!productId){
            let productNameExist = await Product.findProductByName(product.name);
            if (productNameExist) {
              productId = productNameExist.dataValues.quickbookId;
            }
          }
  
          const productData =  {
            itemId: productId,
            name: product.name ,
            amount: isNoCharge? 0 : productPrice *  Quantity,
            quantity: Quantity ,
            unitPrice: isNoCharge? 0 : productPrice,
            // unitPrice: product.product_sub_total / Quantity,
          };

          if (product?.install_cost && product?.install_cost > 0) {
            const installPriceData = {
              itemId: installPriceQbId,  // Assuming `installPriceQbId` is defined elsewhere
              name: "Install Price",
              amount: isNoCharge ? 0 : product?.install_cost * product?.install_quantity,
              quantity: product?.install_quantity,
              unitPrice: isNoCharge ? 0 : product?.install_cost,
            };
      
            // Return both rows (product and install price)
            return [productData, installPriceData];
          } else {
            // Return only the base product data if no install cost
            return [productData];
          }

        })
      );
      productItems = productItems.flat();

      if (materials && materials.length > 0) {
        emitProgress('quickbooks_materials', 'Processing materials for QuickBooks...', 39);
      }
      const additionalMaterialItems = await Promise.all(
        materials.map(async material => {
          let materialId = material?.quickbookId      
          let Quantity = material.quantity?Number(material.quantity) : 1

          if(!materialId){
            let productNameExist = await Product.findProductByName(material.name);
            if (productNameExist) {
              materialId = productNameExist.dataValues.quickbookId;
            }
          }
        
          return {
            itemId: materialId,
            name: material.name,
            amount:isNoCharge? 0 : material.markUpCost *  Quantity,
            quantity: Quantity > 0 ? Quantity : 1, // Ensure quantity is greater than zero
            unitPrice:isNoCharge? 0 : material.markUpCost,
          };
        })
      );

      if (values.additionalSelectedServices && values.additionalSelectedServices.length > 0) {
        emitProgress('quickbooks_services', 'Processing services for QuickBooks...', 42);
      }
      const serviceItems = await Promise.all(
        values.additionalSelectedServices
          // .filter(service => service.quantityDifference !== 0)  // Filter out services with quantityDifference == 0
          .map(async service => {
            let serviceId = service?.quickbookId         
            let quantity = service.quantityDifference > 0 ? service.quantityDifference : service.quantity || 1;
            
            if(!serviceId){
              let serviceNameExist = await Product.findServiceByName(service.name);
              if (serviceNameExist) {
                serviceId = serviceNameExist.dataValues.quickbookId;
              }
            }
          
            return {
              itemId: serviceId,
              name: service.name,
              amount: isNoCharge? 0 : service.total_cost * quantity,
              quantity: quantity,  // Ensure quantity is greater than zero
              unitPrice: isNoCharge? 0 : service.total_cost,
            };
          })
      );
      
      if (rfpServices && rfpServices.length > 0) {
        emitProgress('quickbooks_rfp_services', 'Processing RFP services for QuickBooks...', 45);
      }
      const rfpServiceItems = await Promise.all(
        rfpServices.map(async service => {
          let serviceId =service?.quickbookId    

          if(!serviceId){
            let serviceNameExist = await Product.findServiceByName(service.name);
            if (serviceNameExist) {
              serviceId = serviceNameExist.dataValues.quickbookId;
            }
          }
          return {
            itemId: serviceId,
            name: service.name,
            amount: isNoCharge? 0 : service.originalMarkup/100 * service.total_cost + service.total_cost,
            // quantity: service.qty >= 0 ? service.qty : 1, // Ensure quantity is greater than zero
            quantity: 1, // Ensure quantity is greater than zero
            unitPrice:isNoCharge? 0 : service.originalMarkup/100 * service.total_cost + service.total_cost,
          };
        })
      );

      if (rfpProducts && rfpProducts.length > 0) {
        emitProgress('quickbooks_rfp_products', 'Processing RFP products for QuickBooks...', 48);
      }
      const rfpProductItems = await Promise.all(
        rfpProducts.map(async product => {
          let productId =product?.quickbookId             
          let price =isNoCharge? 0 : values.markup_type===1? product.WholesaleMarkup/100 *product.sellingPrice +product.sellingPrice:product.product_markup /100 * product.product_retail_price+ product.product_retail_price
          let quantity =product.quantity * product.location_quantity

          if(!productId){
            let productNameExist = await Product.findProductByName(product.name);
            if (productNameExist) {
              productId = productNameExist.dataValues.quickbookId;
            }
          }
          return {
            itemId: productId,
            name: product.product_name,
            amount:isNoCharge? 0 : price * quantity  ,
            quantity: quantity || 1, // Ensure quantity is greater than zero
            unitPrice:isNoCharge? 0 : price,
          };
        })
      );
      // let pOIds = values?.community?.poNumber?.split(',');
      // let latestPoId = pOIds ? pOIds[pOIds.length - 1] : null; 
      // let projectPoDetails = await Community.getPoDetailsById(latestPoId);
      // console.log('projectPoDetails',projectPoDetails);
      let projectPoNumber = null // projectPoDetails.length > 0 ? projectPoDetails[0].po_number : null;

      const requestData = {
        name: values.name,
        description: values.description,
        notes: values.description,
        customerId: custimerId,
        // items: [...productItems],
        items: [...productItems, ...additionalMaterialItems, ...serviceItems, ...rfpServiceItems, ...rfpProductItems],
        note: 'description note',
        discount:isNoCharge? 0 : Number(values.invoice_discount) + TotalDiscount ||0,
        // discount:isNoCharge? (totalDiscountForNoCharge - (values.taxAmount || 0)) : Number(values.invoice_discount) + TotalDiscount ||0,
        jobType:values?.project_type?.name || "nill",
        taxAmount :avalaraTaxAmount, // isNoCharge? 0 : values.taxAmount ||  0,
        customGrandTotal :isNoCharge? (totalDiscountForNoCharge * negativeAmount) :  0,
        jobClass:values?.project_type?.quickBookId || null,
        projectName:values?.community?.name || null,
        poNumber:values.po_number,
        projectPoNumber :projectPoNumber || null,
        clientData :values?.clients || null,
        ProjectData :values?.community || null,
        salesRepName :salesRepName || values?.sales_rep_names || ""
      };
      requestData['salesRepName'] = salesRepName || values?.sales_rep_names || "";

      if(quotation===1){
        emitProgress('quickbooks_estimate', 'Creating estimate in QuickBooks...', 52);
        try {
          let quickbookEstimateId = await addEstimateToQuickBooks(requestData);
          doc_number = quickbookEstimateId.Estimate.DocNumber || doc_number;
          emitProgress('quickbooks_estimate_success', 'Estimate created successfully in QuickBooks!', 53);
          console.log(quickbookEstimateId,"quatation--------1");
        } catch (error) {
          console.log("Error creating estimate in QuickBooks:", error);
          emitProgress('quickbooks_estimate_error', 'Failed to create estimate in QuickBooks', 54);
          throw error; // Re-throw to trigger the main catch block
        }
      }else {
        emitProgress('quickbooks_sales_order', 'Creating sales order in QuickBooks...', 52);
        try {
          // let quickbookInvoiceId = await addInvoiceToQuickBooks(requestData)
          let quickbookEstimateId = await addEstimateToQuickBooks(requestData);
          doc_number = doc_number;
          emitProgress('quickbooks_sales_order_success', 'Sales order created successfully in QuickBooks!', 53);
        } catch (error) {
          console.log("Error creating sales order in QuickBooks:", error);
          emitProgress('quickbooks_sales_order_error', 'Failed to create sales order in QuickBooks', 54);
          throw error; // Re-throw to trigger the main catch block
        }
      }
    } catch (error) {
      console.log("error while adding data to quickbook",error);
      emitProgress('quickbooks_error', 'QuickBooks sync failed - continuing without sync...', 54);
    }
  }else{
    emitProgress('quickbooks_error', 'QuickBooks sync failed - continuing without sync...', 55);
  }
///////////////////////////////////////////////////////////////////////////

  if(UniqueEstimateId != null){
    requestdata['estimate_id'] =(UniqueEstimateId && doc_number) ? UniqueEstimateId : null
    requestdata['syncWithQuickbooks'] = 1
    requestdata['doc_number'] = doc_number
    requestdata['est_doc_number'] = doc_number
    requestdata['is_production_qb'] = 1
  }
  else if (productsData.length === 0 && materialsData.length === 0 && servicesData.length === 0 && rfpServicesData.length===0 && rfpProductsData.length===0 && !customerData) {
    requestdata['reasonWithQb'] = null;
  }else{
    requestdata['reasonWithQb'] = ReasonWithQb
  }
    console.log('requestdata-------',requestdata)
    emitProgress('saving_request', `Saving ${orderType} to database...`, 57);
    let result = await Request.addRequest(requestdata, noChargeReason)
    let salesOrderId = result;
    let newMirrorData = { ...requestdata };
    try {
      if(isSplitOrder && (addDataToOtherFacility || addDataToOtherFacilityServices)){
        requestdata['origin_facility_request_id'] = result
        if (values?.clients?.billing_type != 0) {
          requestdata['status'] =  0
        }
        requestdata['project_category'] = 15
        requestdata['new_order'] = 1
        // requestdata['for_parent_facility'] = 2
        // let addBuildOrderSalesOrderToCurrentInstallationResult = await addBuildOrderSalesOrderToCurrentInstallation(requestdata,userId,uploaded,values,avalaraTaxAmount,negativeAmount,taxCategory,address,targetedFacilityIds)
        requestdata['for_parent_facility'] = 1
        let addBuildOrderSalesOrderToOtherInstallationResult = await addBuildOrderSalesOrderToOtherInstallation(requestdata,userId,uploaded,values,avalaraTaxAmount,negativeAmount,taxCategory,address,targetedFacilityIds,productAttachments)
      }
    } catch (error) {
      console.log('error while adding build order sales order to other installation',error)
    }


    if(values?.project_categories?.id !== 14 && values?.client_approve == false){
      let routeData = {};
      routeData['name'] = `route ${currentDate}`;
      routeData['assigned_driver'] = null;
      routeData['timestamp'] = currentDate;
      routeData['added_by'] = userId;
      routeData['status'] = 1;
      routeData['assigned_sales_order'] = result;
      routeData['assigned'] = 0;
      const randomBytes = crypto.randomBytes(2).toString('hex');
      const u_id = parseInt(randomBytes, 16) % 90000 + 10000;
      routeData['unique_id'] = 'SWS' + u_id;
      let routeId = await Routes.createRoute(routeData);
    }
    const orderData = {}
    if (result) {
      if (!empty(uploaded)) {
        for (const row of uploaded) {
          let approvedImage = {}
          console.log('values?.project_categories?.id === 1111',requestdata?.project_category)
          if(requestdata?.project_category == 11 ){
            approvedImage.approved = 1
        }else if(row?.file_type?.startsWith('image')){
            approvedImage.approved = 0
          }else{
            approvedImage.approved = 1
          }
          approvedImage.name = row.url;
          approvedImage.file_type = row.file_type;
          approvedImage.extension = row.extension;
          approvedImage.file_size = row.size;
          approvedImage.request_id = result;
          approvedImage.added_by = userId
          approvedImage.timestamp = moment().unix()
          approvedImage.isDesign = userType === 11 ? 1 : 2;
          let upload = await Request.addRequestAttachments(approvedImage)
          // // ✅ Automatically convert PDF to PNG (same as uploadDesigns)
          // const isPdfFile =
          //   approvedImage.file_type === 'application/pdf' ||
          //   approvedImage.file_type === 'pdf' ||
          //   approvedImage.extension === 'pdf';

          // if (isPdfFile && upload) {
          //   try {
          //     await convertPdfToPngHelper(upload);
          //     console.log(`PDF conversion completed for document ID: ${upload}`);
          //   } catch (conversionError) {
          //     console.error(
          //       `Error converting PDF for document ${upload}:`,
          //       conversionError
          //     );
          //   }
          // }

        }
      }

    let data = { 
      assigned_sales_order : result
    }
    let updateRoute = await Routes.updateRoute(data,requestdata.map_service);

    let qbId = requestdata.estimate_id;
    console.log('qbId----',qbId);
    // if(qbId){
     
    //   emitProgress('fetching_avalara_tax_amount', `Fetching Avalara tax amount...`, 58);
    //   console.log("Starting 10-second delay...");
    //   // await delay(15000); // Delay for 15 seconds (10,000 milliseconds)
    //   console.log("10-second delay complete.");
    //   avalaraTaxAmount = await fetchTaxAmountFromEstimateById(qbId);
    //   console.log('avalaraTaxAmount----',avalaraTaxAmount);
    // }

    let totalAmount = ((parseFloat(parseFloat(values.total).toFixed(2)) + parseFloat(parseFloat(avalaraTaxAmount).toFixed(2))) * (negativeAmount || 1)).toFixed(2)
      let orderData = {
        // total: ((parseFloat(parseFloat(values.total).toFixed(2)) + parseFloat(parseFloat(values.taxAmount).toFixed(2))) * (negativeAmount || 1)).toFixed(2),
        // grandTotal: ((parseFloat(parseFloat(values.total).toFixed(2)) + parseFloat(parseFloat(values.taxAmount).toFixed(2))) * (negativeAmount || 1)).toFixed(2),
        // tax_amount: values.taxAmount,
        total: totalAmount,
        grandTotal: totalAmount,
        tax_amount:avalaraTaxAmount,         // values.taxAmount,
        tax_category: taxCategory,
        budget: values.budget,
        extra_discount: values.invoice_discount || 0,
        request_id: result,
        timestamp: currentTime,
        sub_total: !empty(Number(values.sub_total)) ? Number(values.sub_total) : values.total,
        tax: 0,
        status: 1
      };
      console.log('orderData1234-CustomerOrder-----------',orderData);
      mirrorData.orderData = orderData
      let resultData = await CustomerOrder.createCustomerOrder(orderData)

      let requestProduct = {}
      let productsWithoutInstallCost = []; // Track products without install cost
      if (resultData) {
        if (values.products && values.products.length > 0) {
          emitProgress('processing_products', `Processing catalogs and locations for ${orderType}...`, 59);
        }

        for (const row of values.products) {
          let rowQuantity = row.quantity ? row.quantity : 1;
          // let rowDiscount = row.discount ? row.discount : 0
          let rowDiscount = 0
          let rowAdjustment = row?.adjustment ? row?.adjustment : 0
          // let rowAdjustment = row?.adjustment ? row?.adjustment : 0
          let subTotal = row.retail_price * rowQuantity*row.locationQuantity;
          let productTotal = (row.retail_price * rowQuantity*row.locationQuantity) - rowDiscount;

          let existingProductId = null
          if(!row.id){
            let productNameExist = await Product.findProductByName(row.name);
            if (productNameExist) {
              existingProductId = productNameExist.dataValues.id;
            }
          }
          console.log('orderData1234-CustomerOrderproduct-row-----------',row);
          requestProduct['order_id'] = resultData
          requestProduct['is_product_row'] = row?.is_product_row || 0
          requestProduct['product_index'] = row.indexId
          requestProduct['product_id'] = row.id || existingProductId || null,
          requestProduct['product_name'] = row.name || row.product_name,
          requestProduct['product_desc'] = row?.product_desc || null,
          requestProduct['product_notes'] = row.product_notes || null,
          requestProduct['install_notes'] = row.install_notes || null,
          requestProduct['service_notes'] = row.service_notes || null,
          requestProduct['product_price'] = row.price || 0.00,
          requestProduct['product_retail_price'] = row.markup_type == 1 ? row.price : row.retail_price,
          requestProduct['product_markup'] = (row.markup != null && Number.isFinite(Number(row.markup))) ? Number(row.markup) : null,
          requestProduct['product_qty'] = rowQuantity,
          requestProduct['location_quantity'] = row.locationQuantity||1,
          requestProduct['product_sub_total'] = row?.product_sub_total || 0,
          requestProduct['product_tax'] = 0,
          requestProduct['product_total'] = row.product_sub_total || 0,
          requestProduct['workflow'] = row.workflow,
          requestProduct['discount'] = rowDiscount,
          requestProduct['adjustment'] = rowAdjustment,
          requestProduct['timestamp'] = currentTime,
          requestProduct['status'] = row.status
          requestProduct['markAdjType'] =2 // row.markAdjType || 2
          requestProduct['map_service'] = row?.fromMapServices ? 1 : 0
          requestProduct['allLocationSame'] = row?.allLocationSame ? 1 : 0
          requestProduct['isAdditionalProduct'] = row?.isAdditionalProduct ? 1 : 0
          requestProduct['is_single_card'] = row?.is_single_card ? 1 : 0 
          requestProduct['install_cost'] = row?.install_cost || null
          // Ensure install_qty is a valid number, default to 1
          const installQtyValue = (row?.install_quantity !== null && row?.install_quantity !== undefined) 
            ? Number(row.install_quantity) 
            : ((row?.install_qty !== null && row?.install_qty !== undefined) 
              ? Number(row.install_qty) 
              : 1)
          requestProduct['install_qty'] = isNaN(installQtyValue) ? 1 : installQtyValue
          
          // Ensure install_adjustment is a valid number, default to 0
          const installAdjValue = (row?.install_adjustment !== null && row?.install_adjustment !== undefined && row?.install_adjustment !== '') 
            ? Number(row.install_adjustment) 
            : 0
          requestProduct['install_adjustment'] = isNaN(installAdjValue) ? 0 : installAdjValue
          
          // Ensure install_adj_value_type is an integer (1 or 0), default to 1
          const installAdjValueType = (row?.install_adj_value_type !== null && row?.install_adj_value_type !== undefined) 
            ? (row.install_adj_value_type === true || row.install_adj_value_type === 1 || row.install_adj_value_type === '1' ? 1 : 0)
            : 1
          requestProduct['install_adj_value_type'] = installAdjValueType
          requestProduct['installMarkAdjType'] = row?.installMarkAdjType || 2
          requestProduct['facility_id'] = row?.facility_id  || FACILITY_ID
          requestProduct['split_percentage'] =   row?.split_percentage  || 0
          requestProduct['incoming_split_percentage'] = row?.incoming_split_percentage || 0
          requestProduct['expected_formula_price'] = row?.originalExpectedCost || 0

          requestProduct['materialInvId'] = row?.max_price_mi_id || null
          requestProduct['unit_cost'] = row?.actual_retail_price || row?.retail_price || null
          requestProduct['delivery_type_id'] = row?.delivery_installation?.delivery_type_id || null
          requestProduct['installer_facility_id'] = row?.delivery_installation?.installer_facility_id || null
          requestProduct['adj_value_type'] = row?.adj_value_type 
          console.log('requestProductcheck', requestProduct)
          let clonedRequestProduct = { ...requestProduct };  // Using the spread operator to clone the object
          mirrorData.productData.push(clonedRequestProduct);
          let response = await CustomerOrderProduct.createCustomerOrder(requestProduct)
          
          // Track products without install cost
          const installCost = parseFloat(row?.install_cost || 0);
          if (installCost === 0) {
            productsWithoutInstallCost.push(row.name || row.product_name);
          }
          
           requestProduct['install_cost'] = null
           requestProduct['install_qty'] = null
           requestProduct['install_adjustment'] = null
           requestProduct['install_adj_value_type'] = null
           requestProduct['installMarkAdjType'] = null
           requestProduct['facility_id'] = null
           requestProduct['split_percentage'] = null
           requestProduct['incoming_split_percentage'] = null
           requestProduct['expected_formula_price'] = null
           requestProduct['materialInvId'] = null
           requestProduct['unit_cost'] = null
          if (response && address) {
            let result = address?.filter(raw => (raw.productId === row.id && raw.productIndex === row.indexId));

            // let result = address.filter(raw => (
            //   raw.productId === (row.id ?? existingProductId) &&
            //   raw.productIndex === row.indexId
            // ));

            
            let data = {}
            // Match attachments using product_index_id from payload or row.indexId
            const productIndexId = row?.indexId;
            // Get attachments for this product - check both by indexId key and by product_index_id in attachment payload
            let attachments = productAttachments[productIndexId] || [];
            
            // Also check if attachments have product_index_id field and match it
            if (attachments.length === 0 && productIndexId) {
              // Fallback: search all productAttachments for matching product_index_id
              for (const key in productAttachments) {
                const attArray = productAttachments[key] || [];
                const matchingAttachments = attArray.filter(att => 
                  att.product_index_id === productIndexId || att.productIndexId === productIndexId
                );
                if (matchingAttachments.length > 0) {
                  attachments = matchingAttachments;
                  break;
                }
              }
            }
            
            if (result) {
              // Track existing attachments to avoid duplicates within the same batch
              let existingAttachmentUrls = new Set();
              let existingAttachmentIds = new Set();
              
              for (let i = 0; i < result.length; i++) {
                data.order_id = response
                data.product_id = result[i].productId || existingProductId || null
                data.address = result[i].address
                data.city = result[i].city
                data.state = result[i].state
                data.zip = result[i].zip?.trim()
                data.route_location_id = result[i].route_location_id || null
                data.timestamp = moment().unix()
                data.added_by = userId
                let addLocation = await Request.addLocation(data)
                console.log("#############iiAnoosha", i);
                
                // console.log('🔍 [ADD] Processing attachments for product:', {
                //   productIndexId,
                //   attachmentsCount: attachments.length,
                //   addLocation,
                //   response,
                //   allProductAttachmentKeys: Object.keys(productAttachments),
                //   currentProductAttachments: productAttachments[productIndexId]
                // });
                
                // Process product attachments for this location
                if (addLocation && attachments.length > 0 && result) {
                  // Add attachments for this product/location (only if not already exists in this batch)
                  let addedCount = 0;
                  let skippedCount = 0;
                  
                  for (const attachment of attachments) {
                    const attachmentUrl = attachment.url || attachment.name;
                    const attachmentId = attachment.id;
                    
                    console.log('📎 [ADD] Checking attachment:', {
                      id: attachmentId,
                      url: attachmentUrl,
                      file_type: attachment.file_type,
                      productIndexId,
                      product_index_id: attachment.product_index_id,
                      addLocation,
                      alreadyExistsInBatch: existingAttachmentUrls.has(attachmentUrl)
                    });
                    
                    // Check if attachment already exists in this batch (same URL)
                    const existsInBatch = existingAttachmentUrls.has(attachmentUrl);
                    
                    if (existsInBatch) {
                      console.log('⏭️ [ADD] Attachment already exists in batch, skipping:', {
                        id: attachmentId,
                        url: attachmentUrl
                      });
                      skippedCount++;
                      continue;
                    }
                    
                    const attachmentData = {
                      request_id: salesOrderId,
                      name: attachmentUrl,
                      file_type: attachment.file_type,
                      extension: attachment.extension,
                      file_size: attachment.size || attachment.file_size,
                      product_ids: null,
                      location_ids: String(addLocation),
                      approved: 1,
                      approved_by: userId,
                      status: 1,
                      timestamp: moment().unix(),
                      added_by: userId
                    };
                    const attachmentResult = await Request.addRequestAttachments(attachmentData);
                    console.log('✅ [ADD] Attachment added successfully:', attachmentResult);
                    addedCount++;
                    
                    // Automatically convert PDF to PNG if it's a PDF
                    // const isPdfFile = attachmentData.file_type === 'application/pdf' || attachmentData.file_type === 'pdf' || attachmentData.extension === 'pdf';
                    // if (isPdfFile && attachmentResult) {
                    //   try {
                    //     await convertPdfToPngHelper(attachmentResult);
                    //     console.log(`PDF conversion completed for document ID: ${attachmentResult}`);
                    //   } catch (conversionError) {
                    //     console.error(`Error converting PDF for document ${attachmentResult}:`, conversionError);
                    //   }
                    // }
                    
                    // Add to sets to avoid duplicates in same batch
                    existingAttachmentUrls.add(attachmentUrl);
                    if (attachmentResult && attachmentId) {
                      existingAttachmentIds.add(attachmentId);
                    }
                  }
                  
                  console.log('📊 [ADD] Attachment processing summary:', {
                    total: attachments.length,
                    added: addedCount,
                    skipped: skippedCount
                  });
                  
                  // Update isLinkedAll to 1 for this location
                  await Request.updateLocationIsLinkedAll([addLocation]);
                  console.log('✅ [ADD] Updated isLinkedAll for location:', addLocation);
                }
              }
            }
          }
        }
        
        if (rfpProducts && rfpProducts.length > 0) {
          emitProgress('processing_rfp_products', `Processing RFP products for ${orderType}...`, 60);
        }
        for (const row of rfpProducts) {
          let rowQuantity = row.qty ? row.qty : 1;
          let rowDiscount =  0
          row.price=parseFloat((row.product_price+(row.product_price*row.product_markup/100))?.toFixed(2))
          row.retail_price=parseFloat((row.product_price+(row.product_price*row.product_markup/100))?.toFixed(2))
          row.product_sub_total=row.markup_type == 1 ? (row.price*row.quantity*row.location_quantity) : (row.retail_price*row.quantity*row.location_quantity)
          requestProduct['order_id'] = resultData
          requestProduct['product_id'] = row.product_id,
          requestProduct['product_name'] = row.product_name,
          requestProduct['product_notes'] = row?.product_notes || null,
          requestProduct['service_notes'] = row?.service_notes || null,
          requestProduct['product_price'] = row.product_price || 0.00,
          requestProduct['product_retail_price'] = row.markup_type == 1 ? row.price : row.retail_price,
          requestProduct['product_markup'] = (() => { const m = row.markup_type == 1 ? row.WholesaleMarkup : row.RetailMarkup; return (m != null && Number.isFinite(Number(m))) ? Number(m) : null; })(),
          requestProduct['product_qty'] = row.quantity || 1,
          requestProduct['location_quantity'] = row.locationQuantity||1,
          requestProduct['product_sub_total'] = row.product_sub_total,
          requestProduct['product_tax'] = 0,
          requestProduct['product_total'] = row.product_sub_total,
          requestProduct['workflow'] = row.workflow,
          requestProduct['discount'] = rowDiscount||0,
          requestProduct['is_single_card'] = row?.is_single_card ? 1 : 0
          requestProduct['timestamp'] = currentTime,
          requestProduct['status'] = row.status
          requestProduct['adjustment'] = 0,
          requestProduct['rfp'] = 1
          requestProduct['allLocationSame'] = row?.allRfpProductsLocationSame ? 1 : 0
          requestProduct['isAdditionalProduct'] = row?.isAdditionalProduct ? 1 : 0
          requestProduct['material'] = row?.material || 0
          requestProduct['vendor_price'] = (row?.material == 0) ? (row?.vendor_price || 0) : 0
          // 🔍 Ensure install fields are never null for RFP products and log them
          requestProduct['install_cost'] = row?.install_cost || null
          requestProduct['install_qty'] = row?.install_quantity || row?.install_qty || 1
          requestProduct['install_adjustment'] = row?.install_adjustment || 0
          requestProduct['install_adj_value_type'] = row?.install_adj_value_type || 1
          requestProduct['installMarkAdjType'] = row?.installMarkAdjType || 2
          requestProduct['product_markup'] = row?.markup || 0

          console.log('[RFP-PRODUCT] customer_order_products payload:', {
            product_name: requestProduct['product_name'],
            order_id: requestProduct['order_id'],
            install_cost: requestProduct['install_cost'],
            install_qty: requestProduct['install_qty'],
            install_adjustment: requestProduct['install_adjustment'],
            install_adj_value_type: requestProduct['install_adj_value_type'],
          });

          let clonedRequestProduct = { ...requestProduct };  // This clones the object
          mirrorData.productData.push(clonedRequestProduct);
          let response = await CustomerOrderProduct.createCustomerOrder(requestProduct)
          if (response) {

            let result = row.addresses
            let data = {}
            if (result) {
              for (let i = 0; i < result.length; i++) {
                data.order_id = response
                data.product_id =  row.product_id || null
                data.address = result[i].address.trim()
                data.city = result[i].city.trim()
                data.state = result[i].state.trim()
                data.zip = result[i].zip.trim()
                data.timestamp = moment().unix()
                data.added_by = userId
                let addLocation = await Request.addLocation(data)
                console.log("#############iiAnoosha", i);
              }

            }

          }
        }

        if (materials && materials.length > 0) {
          emitProgress('processing_materials', `Processing materials for ${orderType}...`, 65);
        }
        for (const row of materials) {
          let rowQuantity = row.quantity ? row.quantity : 0;
          let rowDiscount =  0
          row.price=row.price||0
          row.retail_price=row.price||0
          // row.product_sub_total=row.price*rowQuantity
          let rowMarkUpCost = row.markUpCost?.toFixed(2) || 0
          row.product_sub_total=rowMarkUpCost *rowQuantity

          let existingProductId = null
          if(!row.id){
            let productNameExist = await Product.findProductByName(row.name);
            if (productNameExist) {
              existingProductId = productNameExist.dataValues.id;
            }
          }
          let requestProductMat = {}
          requestProductMat['order_id'] = resultData
          requestProductMat['product_id'] = row.id || existingProductId || null,
          requestProductMat['product_name'] = row.name,
          requestProductMat['product_notes'] = row?.product_notes || null,
          requestProductMat['material_notes'] = row?.material_notes || null,
          requestProductMat['service_notes'] = row?.service_notes || null,
          requestProductMat['product_price'] = row.price || 0.00,
          requestProductMat['product_retail_price'] =  row.markUpCost 
          requestProductMat['product_markup'] = row.markup || 0,
          // requestProductMat['product_qty'] = values?.project_categories?.name =='Time & Material' ? 0 : row.quantity || 0,
          requestProductMat['product_qty'] = row.quantity || 0,
          // requestProductMat['location_quantity'] = values?.project_categories?.name =='Time & Material' ? 0 : row.quantity,
          requestProductMat['location_quantity'] = row.quantity,
          requestProductMat['product_sub_total'] = row.product_sub_total,
          requestProductMat['product_tax'] = 0,
          requestProductMat['product_total'] = row.product_sub_total,
          requestProductMat['workflow'] = row.workflow||null,
          requestProductMat['discount'] =0,
          requestProductMat['timestamp'] = currentTime,
          requestProductMat['status'] = 1
          requestProductMat['rfp'] = 0
          requestProductMat['stock_type'] =row.stock_type
          requestProductMat['convertion_unit'] = row.stock_type===1?null:row.convertion_unit ?Number(row.convertion_unit):null;
          requestProductMat['adjustment'] = row.adjustment || 0,
          requestProductMat['adj_value_type'] = row.adj_value_type ? 1 : 0,
          requestProduct['markAdjType'] =2 
          requestProductMat['material'] = 1
          requestProductMat['isAdditionalProduct'] = row?.isAdditionalProduct ? 1 : 0
          let clonedRequestProductMat = { ...requestProductMat };  // This clones the object
          mirrorData.materialData.push(clonedRequestProductMat);
          let response = await CustomerOrderProduct.createCustomerOrder(requestProductMat)
       
        }
        
        if (values.selectedServices && values.selectedServices.length > 0) {
          emitProgress('processing_services', `Processing services and locations for ${orderType}...`, 70);
        }
        for (const row of values.selectedServices) {
          let rowQuantity = row.quantity ? Number(row?.quantity || 0) : 0;
            console.log('qtyyyyyyyyyyyyyyyyyyyy',row.quantity,rowQuantity)
          let existingServiceId = null
          if(!row.id){
            let serviceNameExist = await Product.findServiceByName(row.name);
            if (serviceNameExist) {
              existingServiceId = serviceNameExist.dataValues.id;
            }
          }
          console.log('existingServiceId--3-', existingServiceId)
          console.log('rowrowrowrowrow--3-', row)
          let requestProductServ = {};
          requestProductServ['product_index'] = row.productIndex || row.indexId || 0
          requestProductServ['request_id'] = result
          requestProductServ['labour_cost_service_id'] = row.id || existingServiceId || null,
          requestProductServ['recipe_id'] = row.recipe_id || null,
          requestProductServ['product_id'] = row.product_id || null,
          // requestProductServ['qty'] = values?.project_categories?.name =='Time & Material' ? 0 : rowQuantity,
          // requestProductServ['qty'] = (values?.project_categories?.name =='Time & Material' && !row.product_id )? 0 : rowQuantity || 0,
          requestProductServ['qty'] = (values?.project_categories?.name =='Time & Material' && row.id == 10 ) ? 0 : rowQuantity || 0,
          requestProductServ['unit_cost'] = Number(row.unit_cost ?? row.total_cost ?? 0) || 0;
          requestProductServ['markup'] = row?.isAdditionalProduct ? 0 : Number(row.markup || 0),
          requestProductServ['total_cost'] = Number(row.total ?? (requestProductServ['unit_cost'] * requestProductServ['qty']))
          requestProductServ['rfp'] =0
          requestProductServ['map_service'] = row?.fromMapServices ? 1 : 0
          requestProductServ['jobcost_unit_cost'] =  0
          // requestProductServ['jobcost_unit_cost'] = row?.jobcost_unit_cost ? parseFloat(row?.jobcost_unit_cost) : 0
          // requestProductServ['jobcost_quantity'] = values?.project_categories?.name =='Time & Material' ? 0 : parseFloat(rowQuantity)
          requestProductServ['jobcost_quantity'] = (!row?.makeAsTask && !row.recipe_id && row?.labour_cost_service_id !== 10) ? rowQuantity : 0
          requestProductServ['jobcost_total_cost'] = (!row?.makeAsTask && !row.recipe_id && row?.labour_cost_service_id !== 10) ? Number(row?.cost * requestProductServ['qty']) : 0
          // requestProductServ['jobcost_total_cost'] = values?.project_categories?.name =='Time & Material' ? 0 : (row?.jobcost_total_cost ? parseFloat(row?.jobcost_total_cost) : 0)
          // requestProductServ['jobcost_total_cost'] = (values?.project_categories?.name =='Time & Material' && !row.product_id ) ? 0 : (row?.jobcost_total_cost ? parseFloat(row?.jobcost_total_cost) : 0)
          requestProductServ['isAdditionalProduct'] = row?.isAdditionalProduct ? 1 : 0
          requestProductServ['makeAsTask'] = row?.makeAsTask ? 1 : 0
          // requestProductServ['makeInstallCard'] = quotation ?  row?.makeInstallCard ? 1 : 0 : values?.project_categories?.id ==11 ? row?.makeInstallCard ? 1 : 0 : 0
          requestProductServ['makeInstallCard'] = row?.makeInstallCard ? 1 : 0
          requestProductServ['service_notes'] = row?.service_notes || null
          requestProductServ['taken'] = values?.project_categories?.name =='Time & Material' ? 1 : (!row?.makeAsTask && !row.recipe_id && row?.labour_cost_service_id !== 10) ? 1 : 0
          requestProductServ['service_name'] = row?.name || row?.service_name || null
          requestProductServ['service_desc'] = row?.desc || row?.service_desc || row?.description || null
          requestProductServ['is_service_row'] = row?.is_service_row || 0
          requestProductServ['facility_id'] = row?.facility_id || null
          requestProductServ['split_percentage'] = row?.split_percentage || 0
          requestProductServ['incoming_split_percentage'] = row?.incoming_split_percentage || 0
          requestProductServ['delivery_type_id'] = row?.delivery_installation?.delivery_type_id || null
          requestProductServ['installer_facility_id'] = row?.delivery_installation?.installer_facility_id || null
          let clonedRequestProductServ = { ...requestProductServ };  // This clones the object
          mirrorData.serviceData.push(clonedRequestProductServ);
          console.log('requestProductServ--addservice---', requestProductServ)
         let lcsId = row?.id || existingServiceId
          if(values?.project_categories?.id !==11 && lcsId == 10 && quotation !== 1){
            requestProductServ['makeInstallCard'] = 0
          }
          if(values?.project_categories?.id == 12 && lcsId == 10 && quotation !== 1){
            requestProductServ['makeInstallCard'] = 1
          }
          let response = await Request.addRequestServices(requestProductServ)
          const serviceLocations = address?.filter(raw => raw.isService == 1);
          if (response && serviceLocations?.length>0) {
            let addResult = serviceLocations?.filter(raw => (raw.productId === row.id && raw.productIndex === row.indexId));
            let data = {}
            if (addResult) {
              for (let i = 0; i < addResult.length; i++) {
                data.request_id = result
                data.service_id = response
                data.address = addResult[i].address
                data.city = addResult[i].city
                data.state = addResult[i].state
                data.zip = addResult[i].zip.trim()
                data.timestamp = moment().unix()
                data.added_by = userId
                let addLocation = await Request.addServiceLocation(data)
              }
            }
          }
        }
     
        if (rfpServices && rfpServices.length > 0) {
          emitProgress('processing_rfp_services', `Processing RFP services for ${orderType}...`, 73);
        }
        for (const row of rfpServices) {
          let rowQuantity = row.qty ? row.qty : 0;
          let total=row.sellingPrice+(row.sellingPrice*row.originalMarkup/100)*rowQuantity
          requestProduct['request_id'] = result
          requestProduct['labour_cost_service_id'] = row.labour_cost_service_id,
          requestProduct['recipe_id'] =  null,
          requestProduct['product_id'] =  null,
          requestProduct['qty'] = values?.project_categories?.name =='Time & Material' ? 0 : rowQuantity,
          requestProduct['unit_cost'] = row.sellingPrice,
          requestProduct['markup'] = row.originalMarkup || row.markup || 0,
          requestProduct['total_cost'] =total
          requestProduct['rfp'] =1
          requestProduct['jobcost_unit_cost'] = row?.sellingPrice ? parseFloat(row?.sellingPrice) : 0
          requestProduct['jobcost_quantity'] = values?.project_categories?.name =='Time & Material' ? 0 : rowQuantity || 0
          requestProduct['jobcost_total_cost'] = values?.project_categories?.name =='Time & Material' ? 0 : (row?.sellingPrice*rowQuantity)?.toFixed(2) || 0
          requestProduct['makeAsTask'] = row?.makeAsTask_lcs ? 1 : 0
          requestProduct['makeInstallCard'] = row?.makeInstallCard_lcs ? 1 : 0
          requestProduct['service_notes'] = row?.service_notes || null
          requestProduct['service_desc'] = row?.desc || row?.service_desc || row?.description || null
          requestProduct['service_name'] = row?.name || null
          let clonedRequestProduct = { ...requestProduct };  // This clones the object
          mirrorData.serviceData.push(clonedRequestProduct);
          let response = await Request.addRequestServices(requestProduct)
          
          let addresssData = row.addresses
          let data = {}
            if (response) {
              for (let i = 0; i < addresssData?.length; i++) {
                data.request_id = result
                data.service_id = response
                data.address = addresssData[i].address
                data.city = addresssData[i].city
                data.state = addresssData[i].state
                data.zip = addresssData[i].zip.trim()
                data.timestamp = moment().unix()
                data.added_by = userId
                let addLocation = await Request.addServiceLocation(data)
              }
            }
          
        }
        
        // ✅ OPTIMIZATION: Add request partners (critical - must complete before response)
        if (userType != 3) {
          let partnerData = values.clientsData
          if (partnerData && partnerData.length > 0) {
            let client = {}
            let insertedstatus = null
            for (let eachPartner of partnerData) {
              client['request_id'] = result,
              client['last_seen'] = currentDate,
              client['timestamp'] = currentDate,
              client['added_by'] = userId,
              client['partner_id'] = eachPartner.id
              client['accept_status'] = 1;
              client['status'] = 1
              insertedstatus = await RequestPartner.addRequestPartners(client)
            }
            if (insertedstatus) {
              let requestdata = {}
              requestdata['customer_id'] = partnerData[0]['id'];
              requestdata['customer_name'] = partnerData[0]['name'];
              requestdata['customer_phone'] = partnerData[0]['phone'];
              requestdata['customer_address'] = partnerData[0]['address'];
              requestdata['timestamp'] = currentDate;
              let orderId = await CustomerOrder.getCustomerOrderRequestId(result);
              if (empty(orderId.customer_name)) {
                await CustomerOrder.updateCustomerOrder(orderId.id, requestdata);
              }
            }
          }
        } else {
          // ✅ CRITICAL: Add client as partner (must complete before response)
          let clientDetails = await Client.getClientByUserId(userId);
          const currentTime = moment().unix();
          let client = {}
          let insertedstatus = null
          client['request_id'] = result,
          client['last_seen'] = currentTime,
          client['timestamp'] = currentTime,
          client['added_by'] = userId,
          client['partner_id'] = clientDetails.id
          client['accept_status'] = 1;
          client['status'] = 1
          insertedstatus = await RequestPartner.addRequestPartners(client)
          if (insertedstatus) {
            let requestdata = {}
            requestdata['customer_id'] = clientDetails['id'];
            requestdata['customer_name'] = clientDetails['name'];
            requestdata['customer_phone'] = clientDetails['phone'];
            requestdata['customer_address'] = clientDetails['address'];
            requestdata['timestamp'] = currentDate;
            let orderId = await CustomerOrder.getCustomerOrderRequestId(result);
            await CustomerOrder.updateCustomerOrder(orderId.id, requestdata);
          }
        }

          emitProgress('processing_locations', `Processing project locations for the new ${orderType}...`, 84);
          if(values?.project_categories?.id == 12 || values?.project_categories?.id == 13){
            // if project category is time and material
            const newServiceData = await Installation.getServices(result); // result = request_id
            let serviceData = newServiceData?.filter(item => item.makeInstallCard == 1) || [];
            let productData = await Installation.getProducts(resultData) || []; // resultData = order_id
            const productOrServiceData = [...productData,...serviceData]
            let materialOrderData = await CustomerOrderProduct.getMaterialProductByRequest(result,false) || []; // result = request_id
            console.log('productOrServiceData===time and material==final==', productOrServiceData)
            const projectData = values?.community
            if (projectData && productOrServiceData.length <= 0 && materials?.length > 0 && materialOrderData?.length > 0) {
              let usedMaterialOrderIds = new Set(); // To prevent reuse
              for (const row of materials) {
                let existingProductId = row.id;
                if(!existingProductId){
                  let productNameExist = await Product.findProductByName(row.name);
                  if (productNameExist) {
                    existingProductId = productNameExist.dataValues.id;
                  }
                }

                // Find the first unused match from materialOrderData
                const matchingOrder = materialOrderData?.find(
                  entry => entry.product_id === existingProductId && !usedMaterialOrderIds.has(entry.id)
                );

                if (!matchingOrder) continue; // skip if no match found

                const materialOrderId = matchingOrder.id;
                usedMaterialOrderIds.add(materialOrderId); // mark as used
                
                let data = {}
                data.order_id = materialOrderId
                data.product_id = existingProductId || null
                data.address = projectData.address
                data.city = projectData.city
                data.state = projectData.state
                data.zip = projectData.zip?.trim()
                data.route_location_id = null
                data.timestamp = moment().unix()
                data.added_by = userId
                let addLocation = await Request.addLocation(data);
              }
            }
          }

        emitProgress('finalizing', `Finalizing ${orderType} creation...`, 98);
        let reqresult = await Request.getAllRequests(result, userId, userType)

        try {
          if(isSplitOrder && addDataToOtherFacility){
            newMirrorData['origin_facility_request_id'] = result
            newMirrorData['for_parent_facility'] = 2
            newMirrorData['new_order'] = 0
            newMirrorData['project_category'] = values.project_categories ? values.project_categories["id"] : null
            console.log('mirrorDataaaaaa111', mirrorData)
            console.log('mirrorData.productData:', mirrorData.productData);
            console.log('mirrorData.materialData:', mirrorData.materialData);
            // let addMirrorSalesOrderToOtherInstallationResult = await addMirrorOrderSalesOrderToOtherInstallation(newMirrorData,userId,uploaded,values,avalaraTaxAmount,negativeAmount,taxCategory,address,targetedFacilityIds,mirrorData)
          }
        } catch (error) {
          console.log('error while adding build order sales order to other installation',error)
        }
        
        // ✅ OPTIMIZATION: Send response immediately after critical operations
        emitProgress('complete', `${orderType.charAt(0).toUpperCase() + orderType.slice(1)} created successfully!`, 100);
        res.send({ status: 1,result:result ,reqresult, message:empty(quotation)? "Sales Order has been successfully added!.":"Estimate has been successfully added!."  })

        // ✅ OPTIMIZATION: Handle all notifications/emails/SMS/activity logs/tab counts async (non-blocking)
        const handleNotificationsAsync = async () => {
          try {
            const deliveryTypes = new Set();
            (values?.products || []).forEach(row => {
              const id = row?.delivery_installation?.delivery_type_id;
              if (id != null && id !== '') {
                if (id == 1) deliveryTypes.add('Installs');
                else if (id == 2) deliveryTypes.add('Pickup');
                else if (id == 3) deliveryTypes.add('Shipping');
                else deliveryTypes.add('Unknown');
              }
            });
            (values?.selectedServices || []).forEach(row => {
              const id = row?.delivery_installation?.delivery_type_id;
              if (id != null && id !== '') {
                if (id == 1) deliveryTypes.add('Installs');
                else if (id == 2) deliveryTypes.add('Pickup');
                else if (id == 3) deliveryTypes.add('Shipping');
                else deliveryTypes.add('Unknown');
              }
            });
            if (deliveryTypes.size > 0) {
              const typeStr = [...deliveryTypes].join(', ');
              const requestActivity = {
                request_id: result,
                activity: `Delivery type of the ${orderType} set to ${typeStr}.`,
                added_by: userId,
                timestamp: currentTime,
                status: 1
              };
              try {
                const requestRow = await Request.getRequestById(result);
                if (requestRow && requestRow[0]) {
                  requestActivity.partner_id = requestRow[0].customer_id || 0;
                  requestActivity.partner_name = requestRow[0].customer_name || null;
                } else {
                  requestActivity.partner_id = 0;
                }
                await RequestActivity.addRequestUpdate(requestActivity);
              } catch (e) {
                console.log('Error logging delivery type:', e);
              }
            }
            // emitProgress('sending_notifications', `Sending email and SMS notifications for ${orderType}...`, 80);
            // const allUserIds = await Admin.getAllUserIds();
        let adminMangerIds = await Admin.getAllAdminAndManagerUserIds();
        // const request = await Request.getRequestById(result)
        // console.log("request[0]",request[0])
          const selectedClientId = values?.clients?.user_id ||null;
          console.log("clientIddddd" , selectedClientId)
          let allUsersToCheck = [...new Set([userId, selectedClientId, ...adminMangerIds].filter(Boolean))];
          console.log('alluserssssssssarray',allUsersToCheck)
        
            // Admin/Manager email and SMS notifications (parallelized)
            const adminNotificationPromises = allUsersToCheck.map(async (userIdToCheck) => {
              try {
            console.log('useriddddd',userIdToCheck)
            const creator = await Admin.getUserNotificationAndPermissionSettings(userIdToCheck);
            console.log('creatorrrrrrrrr',creator)
                if (!creator) return;
        
        const recipientPhone = creator?.phone || TWILIO_PHONE;
        const Phone_country_code = creator?.phone_country_code || '1';
        console.log('recipientPhoneeee',recipientPhone)
        console.log('Phone_country_codeeeee',Phone_country_code)

        // ✉️ & 📱 Estimate Notification
        if (quotation === 1 && creator?.pcb_new_estimate === 1) {
          // Email Notification
                  const emailPromise = (async () => {
          if (creator.emailEnabled === 1 && creator.email && creator?.pcb_new_estimate_email === 1) {
            let welcome = "Hello " + creator.name + ",";
            let subject = `📝 New Estimate Created: ${values.name}`;
            let email_content = "A new estimate has been created. Please review the details in your software.";

            let message = newEstimateCreatedEmailTemplate({
              user: creator,
              requestName: values.name,
              projectType: values.project_type?.name,
              baseUrl: BASE_URL,
              type: 'estimate',
            });

            message = message.replace('[emailContent]', email_content);
            message = message.replace('[CONTENT]', welcome);
            message = message.replace('[LOGIN_URL]', `${BASE_URL}`);

            const mailOptions = {
              to: creator.email,
              from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
              subject: subject,
              text: subject,
              html: message,
            };

            try {
              await transporter.sendMail(mailOptions);
              console.log(`✅ Estimate email sent to creator: ${creator.email}`);
            } catch (err) {
              console.error(`❌ Error sending estimate email to ${creator.email}:`, err);
            }
          }
                  })();

          // SMS Notification
                  const smsPromise = (async () => {
          if (creator.smsEnabled === 1 && creator.phone && creator?.pcb_new_estimate_sms === 1) {
            const smsContent = 
              `📢 Estimate Created!\n` +
              `📄 Name: "${values.name}"\n` +
              `💼 Type: ${values.project_type?.name || 'N/A'}\n` +
              `🔗 View: ${BASE_URL}\n` +
              `— SignWise`;

            try {
              await sendMessageToPhoneNumber(recipientPhone, smsContent,Phone_country_code);
              console.log("✅ Estimate SMS sent to creator");
            } catch (err) {
              console.error("❌ Error sending estimate SMS to creator:", err);
            }
          }
                  })();

                  await Promise.all([emailPromise, smsPromise]);
        }

        // ✉️ & 📱 Sales Order Notification
        if (quotation !== 1 && creator?.pcb_new_sales_order === 1) {
          console.log("✅ Sales order SMSsssssss");
          // Email Notification
                  const emailPromise = (async () => {
          if (creator.emailEnabled === 1 && creator.email && creator?.pcb_new_sales_order_email === 1) {
            let welcome = "Hello " + creator.name + ",";
            let subject = `📦 New Sales Order Created: ${values.name}`;
            let email_content = "A new sales order has been created. Please review the details in your software.";

            let message = newEstimateCreatedEmailTemplate({
              user: creator,
              requestName: values.name,
              projectType: values.project_type?.name,
              baseUrl: BASE_URL,
              type: 'sales_order',
            });
          console.log("✅ Sales order SMSsssssss 2");
            message = message.replace('[emailContent]', email_content);
            message = message.replace('[CONTENT]', welcome);
            message = message.replace('[LOGIN_URL]', `${BASE_URL}`);

            const mailOptions = {
              to: creator.email,
              from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
              subject: subject,
              text: subject,
              html: message,
            };

            try {
              if(SEND_EMAIL){
                await transporter.sendMail(mailOptions);
                console.log(`✅ Sales order email sent to creator: ${creator.email}`);
                }
            } catch (err) {
              console.error(`❌ Error sending sales order email to ${creator.email}:`, err);
            }
          }
                  })();

          // SMS Notification
                  const smsPromise = (async () => {
          if (creator.smsEnabled === 1 && creator.phone && creator?.pcb_new_sales_order_sms === 1) {
          console.log("✅ Sales order SMSsssssss 3");
            const smsContent = 
              `📢 Sales Order Created!\n` +
              `📄 Name: "${values.name}"\n` +
              `💼 Type: ${values.project_type?.name || 'N/A'}\n` +
              `🔗 View: ${BASE_URL}\n` +
              `— SignWise`;

            try {
              await sendMessageToPhoneNumber(recipientPhone, smsContent,Phone_country_code);
              console.log("✅ Sales order SMS sent to creator");
            } catch (err) {
              console.error("❌ Error sending sales order SMS to creator:", err);
            }
          }
                  })();

                  await Promise.all([emailPromise, smsPromise]);
        }
              } catch (err) {
                console.error(`❌ Error processing user ${userIdToCheck}:`, err);
      }
            });
            await Promise.all(adminNotificationPromises);
       
            // Partner invitation emails and notifications (for userType != 3)
        if (userType != 3) {
          console.log('Email sent  111111')
          let isAutoApprove =  values?.client_approve == false ? true : false
          let autoApprovedMsg = empty(quotation)
            ? '📦✅ A New Sales Order Has Been Created and Approved'
            : '📄✅ A New Estimate Has Been Created and Approved'
          let needApprovalMsg = empty(quotation)
            ? '📦⏳ A New Sales Order Is Awaiting Your Acceptance'
            : '📄⏳ A New Estimate Is Awaiting Your Acceptance'
          let isRequestMsg = isRequestEstimate ? "A New Estimate Has Been Requested" : ""
          let finalMessage = isAutoApprove ? autoApprovedMsg : (isRequestEstimate ? isRequestMsg : needApprovalMsg)
          let partnerData = values.clientsData
          let subject = finalMessage
          let email_content = empty(quotation)?`You are invited to join New sales order by ` + userName:`You are invited to join New estimate by ` + userName;
          if (req.body.values.isCustomMessage) {
            subject = req.body.values.subject
            email_content = req.body.values.emailcontent
          }
          console.log("###########Client--------",partnerData);
              
          if (partnerData && partnerData.length > 0) {
            console.log("###########partnerData--------");
              // let partnerIds = partnerData.map(eachClient => eachClient.id);
              // const requestTeams = await Request.getRequestPartnersByIds(partnerIds)
              // const requestTeamUserIds = await Request.getRequestTeamUserIds(result)
              // const adminIds = await Admin.getAllAdminIds()

                // Partner invitation emails (parallelized)
                const partnerEmailPromises = partnerData.map(async (eachPartner) => {
                let welcome = "Hello " + eachPartner['company_name'] + ","
                sgMail.setApiKey(SENDGRID_API_KEY)
                let message = rfqEmail()
                message = message.replace('[emailContent]', email_content)
                message = message.replace('[CONTENT]', welcome)
                message = message.replace('[LOGIN_URL]', `${BASE_URL}`)

                const mailOptions = {
                    to: eachPartner.email,  
                    from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
                  subject: subject,
                  text: subject,
                  html: message,
                }
                try {
                  if(SEND_EMAIL){
                  await transporter.sendMail(mailOptions);
                  console.log('Email sent',eachPartner.email );
                  }
                } catch (error) {
                  console.error('##########Err', error);
                  }
                });
                await Promise.all(partnerEmailPromises);
              }

              // User notifications and activity logs
              let notification_msg;
          if (!empty(quotation)) {
            // Quotation flow
            notification_msg =
              values?.client_approve === 1 || values?.client_approve === true
                ? `📄 New Estimate <strong>${values.name}</strong> has been added and is awaiting client approval.`
                : `📄 New Estimate <strong> ${values.name}.</strong> has been added.`;
          } else {
            notification_msg = `📦 New Sales Order <strong> ${requestdata['name']}.</strong> has been added.`;
          }
          let notification_url = `official-request/view/${result}`

              await Admin.incrementField(userId, "noti_count", 1);
          let notificationData_user = {
            user_id: userId,
            url: notification_url,
            timestamp: currentDate,
            message: notification_msg,
            status: 1,
            from_id: userId,
          };
              await Admin.addNotification(notificationData_user);

              // Admin notifications (parallelized)
          let adminIds = await Admin.getAllAdminIds();
              const adminNotificationPromises2 = adminIds
                .filter(eachadminIds => eachadminIds != userId)
                .map(async (eachadminIds) => {
                  try {
                    await Admin.incrementField(eachadminIds, 'noti_count', 1)
              let addNotification = {
                user_id: eachadminIds,
                url: notification_url,
                timestamp: moment().unix(),
                message: notification_msg,
                status: 1,
                from_id: userId,
              }
                    await Admin.addNotification(addNotification);
                  } catch (err) {
                    console.error(`❌ Error sending notification to admin ${eachadminIds}:`, err);
            }
                });
              await Promise.all(adminNotificationPromises2);

              // Activity log
          // Add products without install cost to notification message
          if (productsWithoutInstallCost.length > 0) {
            const productList = productsWithoutInstallCost.map(name => `'${name}'`).join(', ');
            notification_msg += ` Products without install price added: ${productList}.`;
          }
          
          let requestActivity = {}
          requestActivity['request_id'] = result;
          requestActivity['activity'] = notification_msg;
          requestActivity['added_by'] = userId
          requestActivity['timestamp'] = currentTime
              await RequestActivity.addRequestUpdate(requestActivity)
          // // Ensure updates appear in request updates feed
          // await RequestUpdates.addRequestUpdates(requestActivity)
            } else {
              // Client notifications (for userType == 3)
          let clientDetails = await Client.getClientByUserId(userId);
          const currentTime = moment().unix();
              
              if (clientDetails) {
            let requestActivity = {}
            requestActivity['request_id'] = result,
              requestActivity['partner_id'] = clientDetails.id,
              requestActivity['partner_name'] = clientDetails.name,
              requestActivity['activity'] = `Client,${clientDetails.name} has initiated the Request`,
              requestActivity['added_by'] = userId,
              requestActivity['timestamp'] = currentTime,
              requestActivity['addestatusd_by'] = 1
                await RequestUpdates.addRequestUpdates(requestActivity)
                
            const request = await Request.getRequestById(result)
            // const requestTeams = await Request.getRequestPartnersByIds([clientDetails.id])
            const requestTeamUserIds = await Request.getRequestTeamUserIds(result)
            const adminIds = await Admin.getAllAdminIds()

            // Notification 
            let notification_msg = !empty(quotation)
              ? `📄 ${clientDetails.name} has initiated a new Estimate <strong>${request[0].name}</strong>.`
              : `📦 ${clientDetails.name} has initiated a new Request <strong>${request[0].name}</strong>.`
            let notification_url = `official-request/view/${result}`

                // User notification
                await Admin.incrementField(userId, "noti_count", 1);
            let notificationUser = {
              user_id: userId,
              url: notification_url,
              timestamp: currentTime,
              message: notification_msg,
              status: 1,
              from_id: userId,
            }
                await Admin.addNotification(notificationUser);

                // Request team notifications (parallelized)
                const teamNotificationPromises = requestTeamUserIds.map(async (request_team_us_id) => {
                  try {
                    await Admin.incrementField(request_team_us_id, 'noti_count', 1)
              let addNotification = {
                user_id: request_team_us_id,
                url: notification_url,
                timestamp: currentTime,
                message: notification_msg,
                status: 1,
                from_id: userId,
              }
                    await Admin.addNotification(addNotification);
                  } catch (err) {
                    console.error(`❌ Error sending notification to team member ${request_team_us_id}:`, err);
                  }
                });
                await Promise.all(teamNotificationPromises);

                // Admin notifications (parallelized)
                const adminNotificationPromises3 = adminIds.map(async (adminids) => {
                  try {
                    await Admin.incrementField(adminids, 'noti_count', 1)
              let addNotification = {
                user_id: adminids,
                url: notification_url,
                timestamp: currentTime,
                message: notification_msg,
                status: 1,
                from_id: userId,
              }
                    await Admin.addNotification(addNotification);
                  } catch (err) {
                    console.error(`❌ Error sending notification to admin ${adminids}:`, err);
            }
                });
                await Promise.all(adminNotificationPromises3);

                // Admin emails (parallelized)
            let welcome = "Hello Admin,"
            let email_content = empty(quotation)?`${clientDetails.name}"</strong> has been initiated new  request <strong>"${request[0].name}"</strong>."`:
            `${clientDetails.name}"</strong> has been initiated new  Estimate <strong>"${request[0].name}"</strong>."`
            let subject = empty(quotation)? " New Request from Client":" New Estimate from Client"
            let message = rfqEmail()
            message = message.replace('[emailContent]', email_content)
            message = message.replace('[CONTENT]', welcome)
            message = message.replace('[LOGIN_URL]', `${BASE_URL}`)
                
            let adminEmails = await Admin.getAllAdminEmails();
                const adminEmailPromises = adminEmails.map(async (row) => {
              console.log("$$$$$$$$$$$$$$$nush", row);
              const mailOptions = {
                    to: row, 
                    from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
                subject: subject,
                text: subject,
                html: message,
              }
              try {
                if(SEND_EMAIL){
                await transporter.sendMail(mailOptions);
                console.log('Email sent');
                }
              } catch (error) {
                console.error('##########Err', error);
                  }
                });
                await Promise.all(adminEmailPromises);
              }
            }

            // Tab count updates
            // emitProgress('updating_tab_counts', `Updating user tab counts...`, 85);
          // const requestId = result
          // const handleTabCountUpdate = async (quotation, requestId, allUsersToCheck, userType, selectedClientId, allUserIds) => {
          //   let ensureUserExists;
          //   let updateOpenCount;
          //   let stagedCountField;
          //     if (quotation === 1) {
          //       stagedCountField = 'staged_count';
          //       ensureUserExists = async (userId) => {
          //         try {
          //         let userOrderCount = await Request.getEstimateTabCountByUserId(userId);
          //         if (!userOrderCount || userOrderCount === "No record found") {
          //           const EstimateData = {
          //             open_count: 0,
          //             approved_count_count: 0,
          //             archive_count: 0,
          //             timestamp: moment().unix(),
          //             user_id: userId,
          //           };
          //           await Request.addEstimateTabCount(EstimateData);
          //           }
          //         } catch (err) {
          //           console.error(`❌ Error ensuring user exists in estimate tab count for ${userId}:`, err);
          //         }
          //       };
          //       updateOpenCount = async (userId) => {
          //         try {
          //         if (request[0]?.client_approve === 0) {
          //           await Request.EstimateTabIncrementField(userId, 'approved_count', 1, 'approved_count_ids', requestId);
          //           } else {
          //           await Request.EstimateTabIncrementField(userId, 'open_count', 1, 'open_count_ids', requestId);
          //           }
          //         } catch (err) {
          //           console.error(`❌ Error updating estimate tab count for ${userId}:`, err);
          //         }
          //       }
          //     } else {
          //         console.log("quotation statussssssssss")
          //         ensureUserExists = async (userId) => {
          //         try {
          //           const userOrderCount = await Request.getSalesOrderByUserId(userId);
          //           console.log("quotation statussssssssss 1",userOrderCount)
          //           if (!userOrderCount || userOrderCount === "No record found") {
          //             const salesOrderData = {
          //               open_count: 0,
          //               approved_count: 0,
          //               in_production_count: 0,
          //               installs_count: 0,
          //               pickup_count: 0,
          //               shipping_count: 0,
          //               invoicing_count: 0,
          //               completed_count: 0,
          //               timestamp: moment().unix(),
          //               user_id: userId,
          //             };
          //             await Request.addSalesOrderTabCount(salesOrderData);
          //           }
          //         } catch (err) {
          //           console.error(`❌ Error ensuring user exists in sales order tab count for ${userId}:`, err);
          //           }
          //         };
              
          //         updateOpenCount = async (userId) => {
          //         try {
          //           console.log("quotation statussssssssss 2")
          //           if (request[0]?.client_approve === 0 && request[0]?.min_payment_status == 1) {
          //             console.log("quotation statussssssssss 3")
          //             await Request.salesOrderIncrementField(userId, 'approved_count', 1, 'approved_count_ids', requestId);
          //           } else {
          //             await Request.salesOrderIncrementField(userId, 'open_count', 1, 'open_count_ids', requestId);
          //           }
          //         } catch (err) {
          //           console.error(`❌ Error updating sales order tab count for ${userId}:`, err);
          //           }
          //         };
          //       }

          //   // Ensure users exist in the respective tab count table
          //   await Promise.all(allUsersToCheck.map(user => ensureUserExists(user)));
          
          //   let usersToUpdate = [...new Set([selectedClientId,userId, ...allUserIds].filter(Boolean))];
          
          //   // Update counts for users
          //   await Promise.all(usersToUpdate.map(user => updateOpenCount(user)));
          
          //     console.log(`Successfully updated ${stagedCountField || 'counts'} for all necessary users.`);
          // };

          // await handleTabCountUpdate(quotation, requestId, allUsersToCheck, userType, selectedClientId, allUserIds);
        } catch (error) {
            console.error('Error in async notifications handler:', error);
          }
        };

        // ✅ OPTIMIZATION: Run non-critical operations asynchronously (fire and forget)
        handleNotificationsAsync().catch(error => {
          console.error('Error in async operations:', error);
        });
      }

      else {
        res.send({ status: 0, message: "Failed to add the request" });
      }
    }

  } catch (error) {
    console.log(error, 'Error in adding request')
    res.send({ message: "Error in adding request"+error });
  }
}

const addRequestDraft = async (req, res) => {
  try {
    req.body.values = req.body.values || {};
    const v = req.body.values;
    v.name = v.name || '';
    v.description = v.description || '';
    v.products = v.products || [];
    v.selectedServices = v.selectedServices || [];
    v.additionalSelectedServices = v.additionalSelectedServices || [];
    v.skills = v.skills || [];
    v.start = v.start != null ? v.start : null;
    v.end = v.end != null ? v.end : null;
    v.total = v.total != null ? v.total : 0;
    v.clientsData = [v.clients] || [];
    v.taxAmount = v.taxAmount != null ? v.taxAmount : 0;
    v.sub_total = v.sub_total != null ? v.sub_total : 0;
    v.labour_cost = v.labour_cost != null ? v.labour_cost : 0;
    v.serviceData = v.serviceData || '';
    v.avatarUrl = v.avatarUrl || [];
    v.markup_type = v.markup_type || { id: 2 };
    v.project_categories = v.project_categories || null;
    v.project_type = v.project_type || null;
    v.sign_agreement = v.sign_agreement || [];
    v.budget = v.budget != null ? v.budget : 0;
    v.invoice_discount = v.invoice_discount != null ? v.invoice_discount : 0;
    req.body.address = req.body.address || [];
    req.body.rfpProducts = req.body.rfpProducts || [];
    req.body.rfpServices = req.body.rfpServices || [];
    req.body.materials = req.body.materials || [];
    req.body.produtFormulaMaterial = req.body.produtFormulaMaterial || [];
    req.body.allLocationSame = req.body.allLocationSame ?? false;
    req.body.allRfpServiceLocationSame = req.body.allRfpServiceLocationSame ?? true;
    req.body.allRfpProductsLocationSame = req.body.allRfpProductsLocationSame ?? false;
    req.body.noServiceNoTask = req.body.noServiceNoTask ?? false;
    req.body.quotation = 1;
    req.body.is_draft = true;
    req.body.saveAsDraft = true;
    return addRequests(req, res);
  } catch (error) {
    console.log(error, 'Error in adding draft request');
    res.send({ status: 0, message: 'Error in adding draft request' });
  }
};

const getRequestByIdNew = async (req, res) => {
  try {
    const { request_id } = req.body;
    let userId = req.body.userId
    let userType = req.body.userType
    let result = await Request.getAllRequests(request_id, userId, userType)
    const isMirrored = (result?.data?.[0]?.for_parent_facility == 2 && result?.data?.[0]?.received_status != 2) ? true : false;
    const targetFacilityId = result?.data?.[0]?.origin_facility_id || null;
    const originRequestId = result?.data?.[0]?.origin_facility_request_id || null;
    const splitStatus = result?.data?.[0]?.status || null;
    const projectCategory = result?.data?.[0]?.project_category || null;

    // if(!isMirrored && originRequestId && targetFacilityId && projectCategory == 15){
    //   result = await Request.getAllRequests(originRequestId, userId, userType)
    // }

    if(isMirrored && originRequestId && targetFacilityId){
      console.log("Inside ismirrored")
      const targetFacility = filteredSignwiseInstalations.find(
        f => f.id === Number(targetFacilityId)
      );
      if (!targetFacility?.url) return null;
      const projectResponse = await axios.post(
        `${targetFacility.url}view-request-new`,
        {
          request_id: originRequestId,
          userId: userId,
          userType: userType
        }
      );
      console.log('for_parent_facility= 1', result?.data?.[0]?.for_parent_facility)
      if(projectResponse?.data){
        const forParentFacility = result?.data?.[0]?.for_parent_facility;
        projectResponse.data.data = projectResponse.data.data.map(item => ({
          ...item,
          real_for_parent_facility: forParentFacility,
          real_origin_facility_request_id: originRequestId,
          real_origin_facility_id: targetFacilityId,
        }));
        return res.send({
        ...projectResponse.data,
        splitStatus
      });
      }else {
        res.send({ status: 0, message: "Error in fetching requets by id from mirrored facility" })
      }
    }

    console.log('result32323', result)
    let routeData = await Route.getRouteDatabySalesOrderId(request_id)
    let checkProjectExist = await Request.checkExistingProject(request_id);
    result = result?.data?.map(item => {
      const formattedDescription = (item.description || '').replace(/\r?\n/g, '\n');
      const formattedNotes = (item.notes || '').replace(/\r?\n/g, '\n');
      console.log('for_parent_facility= 2', result?.data?.[0]?.for_parent_facility)
      return {
        ...item,
        description: formattedDescription,
        description_lines: formattedDescription ? formattedDescription.split('\n') : [],
        notes: formattedNotes,
        notes_lines: formattedNotes ? formattedNotes.split('\n') : [],
        checkProjectExist: checkProjectExist,
        welcome_message: item.welcome_message === 1 ? true : false,
        routeData,
        splitStatus,
      };
    });
    let customerData = await CustomerOrder.getCustomerOrderRequestId(request_id)
    let requestAcceptedPartners = await RequestPartner.requestAwaitingPartners(request_id, 1)
    let partner = await Client.getPartnerByUserId(userId);
    let updates = await RequestUpdates.fetchAllUpdates(5, request_id, null, partner.id, userId, userType)
    // If this estimate originated from a design order, include those updates as well
    if (result && result[0]?.isFromDesignOrder == 1 && result[0]?.design_order_id) {
      const designOrderUpdates = await RequestUpdates.fetchAllDesignOrderUpdates(
        5,
        result[0].design_order_id,
        null,
        partner.id,
        userId,
        userType
      );
      updates = [...designOrderUpdates, ...updates];
    }
    let attachments = await RequestPartner.fetchAllRequestAttachments(request_id);
    let getRequestLocationsByRequestIdWhereIsLinkedAllZero = await Request.getRequestLocationsByRequestIdWhereIsLinkedAllZero(request_id);
    let payments = await RequestPartner.fetchAllRequestPayments(request_id);
    let team_members = await Teams.getAllTeams(true, "", userId, userType);
    let isEstimateJob = false
    if(result[0]?.is_request_estimate == 1 && result[0]?.quotation == 1){
      isEstimateJob = true
    }
    let projectExisting=await Request.getProjectExisting(request_id, isEstimateJob)
    let team=[]
    if(projectExisting){
      team = await Project.getAllProjectMembers(projectExisting.id)
    }

    let jobNoteMembers = [];

    if (projectExisting || result) {
      const jobNoteProjectMembers = await Project.projectMembersForMessaging(request_id);
      const jobNoteDrivers = await Project.DriverForMessaging(request_id);
      const jobNoteInstaller = await Project.InstallersForMessaging(request_id);
      const getAllAdmins = await Project.AdminsForMessaging(userId);
      
      // If this request was converted from a design order, include design order project members
      let designOrderProjectMembers = [];
      if (result[0]?.design_order_id) {
        designOrderProjectMembers = await Request.getAllDesignOrderProjectMembers(result[0].design_order_id);
      }
      
      // const requestAcceptedPartners = await RequestPartner.requestAwaitingPartners(request_id, 1)

      const combined = [
        ...jobNoteProjectMembers,
        ...jobNoteDrivers,
        ...jobNoteInstaller,
        ...getAllAdmins,
        ...designOrderProjectMembers, // Add design order project members
        // ...requestAcceptedPartners
      ];

      jobNoteMembers = Array.from(
        new Map(
          combined
            .filter(user => user?.user_id) // filter out null or undefined user_id
            .map(user => [user.user_id, user]) // remove duplicates by user_id
        ).values()
      );
    }

    if (!empty(result)) {
      res.send({
        status: 1,
        data: result,
        requestAcceptedPartners,
        updates,
        attachments,
        getRequestLocationsByRequestIdWhereIsLinkedAllZero,
        customerData,
        payments,
        team_members,
        team,
        jobNoteMembers,
        splitStatus,
        message: "Data fetched successfully"
      });
    }
    else {
      res.send({ status: 0, message: "Error in fetching request by id" })
    }
  } catch (error) {
    console.log(error, 'error in get request by id')
    res.send({ status: 0, message: 'failed to get request by id' })
  }
}

const getRequestById = async (req, res) => {
  try {
    const { request_id, noMaterials, noProducts } = req.body;
    let userId = req.body.userId
    let userType = req.body.userType
    // let result = await Request.getRequestById(request_id) 
    // let routeData = await Route.getRouteDatabySalesOrderId(request_id)
    // let result = await Request.getAllRequests(request_id, userId, userType)
    let  [routeData, result] = await Promise.all([
      Route.getRouteDatabySalesOrderId(request_id),
      Request.getAllRequests(request_id, userId, userType)
    ]);
   
    const isMirrored = (result?.data?.[0]?.for_parent_facility == 2 && result?.data?.[0]?.received_status != 2) ? true : false;
    const targetFacilityId = result?.data?.[0]?.origin_facility_id || null;
    const originRequestId = result?.data?.[0]?.origin_facility_request_id || null;
    const splitStatus = result?.data?.[0]?.status || null;
    const projectCategory = result?.data?.[0]?.project_category || null;
    const is_split_order = result?.data?.[0]?.is_split_order || null;
    // console.log('splitStatus111', splitStatus)
    let checkProjectExist = await Request.checkExistingProject(request_id);

    // if(!isMirrored && originRequestId && targetFacilityId && projectCategory == 15){
    //   result = await Request.getAllRequests(originRequestId, userId, userType)
    // }

    if(isMirrored && originRequestId && targetFacilityId){
      // console.log("Inside ismirrored 222")
      const targetFacility = filteredSignwiseInstalations.find(
        f => f.id === Number(targetFacilityId)
      );
      if (!targetFacility?.url) return null;
      const projectResponse = await axios.post(
        `${targetFacility.url}view-request`,
        {
          request_id: originRequestId,
          userId: userId,
          userType: userType
        }
      );
      // console.log('for_parent_facility= 3', result?.data?.[0]?.for_parent_facility)
      if(projectResponse?.data){
        const forParentFacility = result?.data?.[0]?.for_parent_facility;
        projectResponse.data.data = projectResponse.data.data.map(item => ({
          ...item,
          real_for_parent_facility: forParentFacility,
          real_origin_facility_request_id: originRequestId,
          real_origin_facility_id: targetFacilityId,
        }));
        return res.send({
        ...projectResponse.data,
        splitStatus
      });
      }else {
        res.send({ status: 0, message: "Error in fetching requets by id from mirrored facility" })
      }
    }
    
    // console.log('getRequestById - result before mapping:', result?.data?.[0]);
    // console.log('getRequestById - design_order_id:', result?.data?.[0]?.design_order_id);
    
    // result = result?.data?.map(item => {
    //   const formattedDescription = (item.description || '').replace(/\r?\n/g, '\n');
    //   const formattedNotes = (item.notes || '').replace(/\r?\n/g, '\n');
    //   // console.log('for_parent_facility= 4', result?.data?.[0]?.for_parent_facility)
    //   return {
    //     ...item,
    //     description: formattedDescription,
    //     description_lines: formattedDescription ? formattedDescription.split('\n') : [],
    //     notes: formattedNotes,
    //     notes_lines: formattedNotes ? formattedNotes.split('\n') : [],
    //     checkProjectExist: checkProjectExist,
    //     welcome_message: item.welcome_message === 1 ? true : false,
    //     routeData,
    //     splitStatus,
    //   };
    // });
    const mapRequestResult = (item) => {
      const formattedDescription = (item.description || '').replace(/\r?\n/g, '\n');
      const formattedNotes = (item.notes || '').replace(/\r?\n/g, '\n');
      return {
        ...item,
        description: formattedDescription,
        description_lines: formattedDescription ? formattedDescription.split('\n') : [],
        notes: formattedNotes,
        notes_lines: formattedNotes ? formattedNotes.split('\n') : [],
        checkProjectExist,
        welcome_message: item.welcome_message === 1,
        routeData,
        splitStatus,
      };
    };
    
    result = result?.data?.map(mapRequestResult);
   
    // console.log("getAllDesignOrderProjectMembersgetAllDesignOrderProjectMembers")
    // let projectMembers = await Request.getAllDesignOrderProjectMembers(result[0]?.design_order_id);
    // let customerData = await CustomerOrder.getCustomerOrderRequestId(request_id)
    // let serviceSkill = await Request.getServices(null)
    // let products = await Product.getAllProducts(false);
    let [projectMembers, customerData, serviceSkill, products] = await Promise.all([
      Request.getAllDesignOrderProjectMembersNew(result[0]?.design_order_id),
      CustomerOrder.getCustomerOrderRequestId(request_id),
      Request.getServices(null),
      noProducts ? Promise.resolve([]) : Product.getAllProducts(false),
    ]);

    // products = products.filter(row => row.inhouse === 1)
    let isEstimateJob = false
    if(result[0]?.is_request_estimate == 1 && result[0]?.quotation == 1){
      isEstimateJob = true
    }
    let projectExisting=await Request.getProjectExisting(request_id, isEstimateJob)
    console.log('projectExisting', projectExisting);
    
    let team=[]
    if(projectExisting){
      team = await Project.getAllProjectMembers(projectExisting.id)
    }
  
    // for (const data of products) {
    //   let result = await Product.getServiceByProduct(data.id);
    //   data.services = result
    // }
    // for (const data of products){
    //   let result = await Product.getMaterialByProduct(data.id)
    //     data.materials = result || []
    //     if (data?.inhouse == 0) {
    //       const price = Number(data?.sub_contract_price) || 0;
    //       const markup = Number(data?.markup) || 0;
    //       data.actual_retail_price = price * (1 + markup / 100);
    //     }
    //     data.facility_id = FACILITY_ID
    // }

    // Perform both asynchronous operations in parallel for each product
    if (!noProducts && Array.isArray(products) && products.length > 0) {
      await Promise.all(
        products.map(async (data) => {
          // Fetch service and material data concurrently for each product
          const [serviceResult, materialResult] = await Promise.all([
            Product.getServiceByProductNew(data.id),
            Product.getMaterialByProductNew(data.id)
          ]);
// console.log("serviceResult", serviceResult);
// console.log("materialResult", materialResult);

          // Assign the fetched results to the respective properties
          data.services = serviceResult;
          data.materials = materialResult || [];

          // Process in-house products
          if (data?.inhouse === 0) {
            const price = Number(data?.sub_contract_price) || 0;
            const markup = Number(data?.markup) || 0;
            data.actual_retail_price = price * (1 + markup / 100);
          }

          // Set facility ID for each product
          data.facility_id = FACILITY_ID;
        })
      );
    }

    let jobNoteMembers = [];

    if (projectExisting || result) {
      // const jobNoteProjectMembers = await Project.projectMembersForMessaging(request_id);
      // const jobNoteDrivers = await Project.DriverForMessaging(request_id);
      // const jobNoteInstaller = await Project.InstallersForMessaging(request_id);
      // const getAllAdmins = await Project.AdminsForMessaging(userId);
      const [jobNoteProjectMembers, jobNoteDrivers, jobNoteInstaller, getAllAdmins] = await Promise.all([
        Project.projectMembersForMessaging(request_id),
        Project.DriverForMessaging(request_id),
        Project.InstallersForMessaging(request_id),
        Project.AdminsForMessagingNew(userId),
      ]);
      // console.log("jobNoteProjectMembers", jobNoteProjectMembers);
      // console.log("jobNoteDrivers", jobNoteDrivers);
      // console.log("jobNoteInstaller", jobNoteInstaller);
      // console.log("getAllAdmins", getAllAdmins);
    
      // If this request was converted from a design order, include design order project members
      let designOrderProjectMembers = [];
      if (result[0]?.design_order_id) {
        designOrderProjectMembers = await Request.getAllDesignOrderProjectMembers(result[0].design_order_id);
      }
    
      // const requestAcceptedPartners = await RequestPartner.requestAwaitingPartners(request_id, 1)

      const combined = [
        ...jobNoteProjectMembers,
        ...jobNoteDrivers,
        ...jobNoteInstaller,
        ...getAllAdmins,
        ...designOrderProjectMembers, // Add design order project members
        // ...requestAcceptedPartners
      ];

      jobNoteMembers = Array.from(
        new Map(
          combined
            .filter(user => user?.user_id) // filter out null or undefined user_id
            .map(user => [user.user_id, user]) // remove duplicates by user_id
        ).values()
      );
    }

    // let requestAwaitingPartners=await RequestPartner.requestAwaitingPartners(request_id,0)
    // let requestRejectedPartners=await RequestPartner.requestAwaitingPartners(request_id,2)
    
    
    // functionalies are commented out to improve performance and add new functionalities in parallel
    // let materials = await Product.getAllMaterials(false);
    // let requestAcceptedPartners = await RequestPartner.requestAwaitingPartners(request_id, 1)
    // let attachments = await RequestPartner.fetchAllRequestAttachments(request_id);
    // let getRequestLocationsByRequestIdWhereIsLinkedAllZero = await Request.getRequestLocationsByRequestIdWhereIsLinkedAllZero(request_id);
    // let payments = await RequestPartner.fetchAllRequestPayments(request_id);
    // let customFieldTemplateId = await Client.getAllFieldTemplatesRfq(request_id)
    // let partner = await Client.getPartnerByUserId(userId);
    // let workflows = await Client.getAllWorkflows();
    // let labourCostServices = await Product.getAllLabourCostServices();
    let requestServices = []
    if(is_split_order == 1 && projectCategory != 15){
      requestServices = await Request.getSplitRequestServices(request_id,false,false,false,result[0]?.project_category)
      console.log("requestServices split",requestServices)
    }else {
      requestServices = await Request.getRequestServices(request_id,false,false,false,result[0]?.project_category)
      console.log("requestServices split",requestServices)
    }
    let [
      materials,
      requestAcceptedPartners,
      attachments,
      getRequestLocationsByRequestIdWhereIsLinkedAllZero,
      payments,
      customFieldTemplateId,
      partner,
      workflows,
      labourCostServices,
      // requestServices
    ] = await Promise.all([
      noMaterials ? Promise.resolve([]) : Product.getAllMaterialsNew(false),
      RequestPartner.requestAwaitingPartners(request_id, 1),  // requestAcceptedPartners
      RequestPartner.fetchAllRequestAttachments(request_id),
      Request.getRequestLocationsByRequestIdWhereIsLinkedAllZero(request_id),
      RequestPartner.fetchAllRequestPayments(request_id),
      Client.getAllFieldTemplatesRfq(request_id),
      Client.getPartnerByUserId(userId),
      Client.getAllWorkflows(),
      Product.getAllLabourCostServices(),
      // Request.getRequestServices(request_id, false, false, false, result[0]?.project_category)
    ]);
    // console.log("designOrderProjectMembers", materials);
    // return res.send({ status: 1, data:materials });
    // for (const data of labourCostServices) {
    //   data.facility_id = FACILITY_ID
    // }
    let updates = await RequestUpdates.fetchAllUpdates(5, request_id, null, partner.id, userId, userType)
    // labourCostServices=labourCostServices.filter(row=>row.inhouse===1) // for inhouse only
    // labourCostServices = labourCostServices.filter(row => row.inhouse === 0)
    // console.log("requestServices",requestServices)
    if (requestServices && requestServices.length > 0) {
      requestServices = requestServices?.map(service => {
        let totalCost = parseFloat(service.service_original_cost || 0);
        if(result[0]?.project_category ==12 && result[0]?.is_split_order == 1 && (service.facility_id != FACILITY_ID && service.facility_id != null)){
            service.service_original_cost = parseFloat(service.unit_cost || 0);
            totalCost = parseFloat(service.unit_cost || 0);
            service.quantity = parseFloat(service.jobcost_quantity || 0) || parseFloat(service.quantity || 0);
        }
        const jobcostQuantity = (result[0]?.project_category ==12 && service?.fixed_service_cost !== 1) ? parseFloat(service.jobcost_quantity || 0) :parseFloat(service.quantity || 0);
        let totalServiceCost = totalCost * jobcostQuantity;

        // if(result[0]?.project_category ==12 && service?.labour_cost_service_id !== 10){ // uncomment if needed
        if(result[0]?.project_category ==12 && (service.facility_id == FACILITY_ID || service.facility_id == null)){
          //  service.quantity = parseFloat(service.jobcost_quantity).toFixed(2)
          service.total_cost =totalServiceCost // parseFloat(service.jobcost_total_cost)
          //  totalServiceCost =parseFloat(Number(service.jobcost_total_cost))
          service.quantity =jobcostQuantity 
        }
        if(result[0]?.project_category == 13){
          service.service_original_cost = service.unit_cost
          service.originalMarkup = service.markup
          }
    
        return {
          ...service,
          total_service_cost: totalServiceCost.toFixed(2),
        };
      });
    }
    // console.log("$$$$$$$$$$$$",customFieldTemplateId);
    // let fieldTemplates = [];
    // let i = 0;
    // for (const row of customFieldTemplateId) {
    //   let field_template_ids = await OnboardWorkFlowFields.getfieldTemplatesbyStageId(row.stage_id);
    //   //  console.log("HIFI", field_template_ids);

    //   if (!empty(field_template_ids)) {
    //     let templateData = await OnboardWorkFlowStages.getTemplateFormDataById(field_template_ids, 0, 0, 1, request_id);
    //     fieldTemplates[i] = templateData;
    //     i++;
    //   }
    // }
    // let fieldTemplatesNew = []
    // let ids = new Set();
    // fieldTemplates.forEach(innerArray => {
    //   let obj = innerArray[0];
    //   if (!ids.has(obj.id)) {
    //     fieldTemplatesNew.push(obj);
    //     ids.add(obj.id);
    //   }
    // });
    // Use Promise.all to parallelize the fetching of field templates
      const fieldTemplates = await Promise.all(
        customFieldTemplateId.map(async (row) => {
          const field_template_ids = await OnboardWorkFlowFields.getfieldTemplatesbyStageId(row.stage_id);
          if (!empty(field_template_ids)) {
            const templateData = await OnboardWorkFlowStages.getTemplateFormDataById(field_template_ids, 0, 0, 1, request_id);
            return templateData;
          }
          return null;
        })
      );

      // Filter out null values if any
      const validFieldTemplates = fieldTemplates.filter(template => template !== null);

      // Use Set to ensure unique field templates based on `id`
      const fieldTemplatesNew = Array.from(
        new Map(validFieldTemplates.flat().map(item => [item[0].id, item[0]]))
        .values()
      );


    let QuestionTemplateId = await Client.getQuestionnairTemplatesRfq(request_id)


    // let QuestionTemplates = []
    // for (const row of QuestionTemplateId) {
    //   let questionnairIds = await OnboardWorkFlowQuestionnaires.getQuestionairebyStageId(row.stage_id);

    //   if (!empty(questionnairIds)) {
    //     let QuestionData = await OnboardWorkFlowStages.get_qns_formData_by_id(questionnairIds, 0, 0, 1, request_id);
    //     QuestionTemplates.push(QuestionData);

    //   }
    // }
    // let QuestionTemplatesNew = []
    // ids = new Set();
    // QuestionTemplates.forEach(innerArray => {
    //   let obj = innerArray[0];
    //   if (!ids.has(obj.id)) {
    //     QuestionTemplatesNew.push(obj);
    //     ids.add(obj.id);
    //   }
    // });
    // let signAgrementData = await Client.getAllSignAgrementsOfRfq(request_id)
    // let allDataEmpty = true;
    // if (!empty(signAgrementData) || !empty(QuestionTemplates) || !empty(fieldTemplates)) {
    //   allDataEmpty = false;
    // }
    // Fetch all QuestionTemplates concurrently
      const QuestionTemplates = await Promise.all(
        QuestionTemplateId.map(async (row) => {
          const questionnairIds = await OnboardWorkFlowQuestionnaires.getQuestionairebyStageId(row.stage_id);

          if (!empty(questionnairIds)) {
            return await OnboardWorkFlowStages.get_qns_formData_by_id(questionnairIds, 0, 0, 1, request_id);
          }
          return null;
        })
      );

      // Filter out null values and flatten the result
      const validQuestionTemplates = QuestionTemplates.filter(template => template !== null).flat();

      // Ensure uniqueness of QuestionTemplates based on the id
      const QuestionTemplatesNew = Array.from(
        new Map(validQuestionTemplates.map(item => [item[0].id, item[0]]))
        .values()
      );

      // Fetch sign agreement data
      const signAgrementData = await Client.getAllSignAgrementsOfRfq(request_id);

      // Check if any of the required data is empty
      const allDataEmpty = empty(signAgrementData) && empty(QuestionTemplatesNew) && empty(fieldTemplates);

      // The value of allDataEmpty will be `false` if any of the data is non-empty

    // let portfolio = await OnboardWorkFlowStages.getPortfoliobypartnerid(0, 1, request_id)
    // console.log("###########",signAgrementData);
    // let requestProposals = await RequestProposals.getAllPropsalsByRequestId(request_id, userId, userType)
    // let customData=await OnboardWorkFlowStages.getRfqData(request_id)
    // let allProposals = await Proposals.getAllAwardedProposals()
    // let team_members = await Teams.getAllTeams(
    //   true,
    //   "",
    //   userId,
    //   userType,
    // );
let [portfolio, requestProposals, allProposals, team_members,currentFacilitySplitManagementSettings,otherInstallationSplitManagementSettings] = await Promise.all([
  OnboardWorkFlowStages.getPortfoliobypartnerid(0, 1, request_id),
  RequestProposals.getAllPropsalsByRequestId(request_id, userId, userType),
  Proposals.getAllAwardedProposals(),
  Teams.getAllTeams(true, "", userId, userType),
  SplitManagementModel.getAllSplitManagementSettings(),
  SplitManagementModel.getAllSplitManagementSettingsFromOtherInstallations()
]);
let [otherInstallationProducts, otherInstallationServices] = await Promise.all([
  is_split_order === 1 
    ? SplitManagementModel.getAllProductsFromMultipleDatabase(true)
    : Promise.resolve([]), // Returns an empty array as a resolved promise when not a split order
  is_split_order === 1 
    ? SplitManagementModel.getAllLabourCostServicesFromOtherInstallations()
    : Promise.resolve([])  // Same here for the services
]);

    // let otherInstallationProducts = [];
    // if(is_split_order == 1){
    //   otherInstallationProducts = await SplitManagementModel.getAllProductsFromMultipleDatabase(true);
    // }else{
    //   otherInstallationProducts = []
    // }
    // const currentFacilitySplitManagementSettings = await SplitManagementModel.getAllSplitManagementSettings();
    // const otherInstallationSplitManagementSettings = await SplitManagementModel.getAllSplitManagementSettingsFromOtherInstallations();
    if (!empty(result)) {
      res.send({
        status: 1, data: result
        , skill: serviceSkill,
        products: products,
        requestAcceptedPartners,
        updates,
        attachments,
        getRequestLocationsByRequestIdWhereIsLinkedAllZero,
        customerData,
        labourCostServices,
        requestServices,
        payments,
        workflows,
        signAgrementData,
        fieldTemplates: fieldTemplatesNew,
        QuestionTemplates: QuestionTemplatesNew,
        allDataEmpty,
        portfolio,
        requestProposals,
        team_members,
        team,
        allProposals,
        materials,
        projectMembers,
        jobNoteMembers,
        otherInstallationProducts,
        otherInstallationServices,
        currentFacilitySplitManagementSettings,
        otherInstallationSplitManagementSettings,
        splitStatus,
        // customData,
        message: "Data fetched successfully"
      });
    }
    else {
      res.send({ status: 0, message: "Error in fetching requets by id" })
    }
  } catch (error) {
    console.log(error, 'error in get request by id')
    res.send({ status: 0, message: 'failed to get request by id' })
  }
}
const getRequestByIdForInvoice = async (req, res) => {
  try {
    const { request_id, noProducts } = req.body;
    const userId = req.body.userId;
    const userType = req.body.userType;
    let [routeData, result] = await Promise.all([
      Route.getRouteDatabySalesOrderId(request_id),
      Request.getAllRequests(request_id, userId, userType),
    ]);

    const isMirrored =
      result?.data?.[0]?.for_parent_facility == 2 &&
      result?.data?.[0]?.received_status != 2;
    const targetFacilityId = result?.data?.[0]?.origin_facility_id || null;
    const originRequestId = result?.data?.[0]?.origin_facility_request_id || null;
    const splitStatus = result?.data?.[0]?.status || null;
    const projectCategory = result?.data?.[0]?.project_category ?? null;
    const is_split_order = result?.data?.[0]?.is_split_order ?? null;

    const checkProjectExist = await Request.checkExistingProject(request_id);

if (isMirrored && originRequestId && targetFacilityId) {
  const targetFacility = filteredSignwiseInstalations.find(
    (f) => f.id === Number(targetFacilityId)
  );
      if (!targetFacility?.url) {
        return res.send({
          status: 0,
          message: "Mirrored facility URL not configured",
        });
      }
      const mirrorPayload = { request_id: originRequestId, userId, userType };
      let projectResponse;
      try {
        projectResponse = await axios.post(
          `${targetFacility.url}view-request-for-invoice`,
          mirrorPayload
        );
        if (projectResponse?.data?.status !== 1) {
          projectResponse = await axios.post(
            `${targetFacility.url}view-request`,
            mirrorPayload
          );
        }
      } catch (err) {
        try {
          projectResponse = await axios.post(
            `${targetFacility.url}view-request`,
            mirrorPayload
          );
        } catch (err2) {
          console.log(err2, 'mirrored view-request fallback failed');
          return res.send({
            status: 0,
            message: 'Error in fetching request from mirrored facility',
          });
        }
      }
      if (projectResponse?.data) {
        const forParentFacility = result?.data?.[0]?.for_parent_facility;
        projectResponse.data.data = projectResponse.data.data.map((item) => ({
          ...item,
          real_for_parent_facility: forParentFacility,
          real_origin_facility_request_id: originRequestId,
          real_origin_facility_id: targetFacilityId,
        }));
        return res.send({
          ...projectResponse.data,
          splitStatus,
        });
      }
      res.send({
        status: 0,
        message: "Error in fetching requets by id from mirrored facility",
      });
      return;
    }


    const mapRequestResult = (item) => {
      const formattedDescription = (item.description || '').replace(/\r?\n/g, '\n');
      const formattedNotes = (item.notes || '').replace(/\r?\n/g, '\n');
      return {
        ...item,
        description: formattedDescription,
        description_lines: formattedDescription ? formattedDescription.split('\n') : [],
        notes: formattedNotes,
        notes_lines: formattedNotes ? formattedNotes.split('\n') : [],
        checkProjectExist,
        welcome_message: item.welcome_message === 1,
        routeData,
        splitStatus,
      };
    };
    
    result = result?.data?.map(mapRequestResult);
   


    let [products] = await Promise.all([      
      noProducts ? Promise.resolve([]) : Product.getAllProducts(false),
    ]);


  

    if (!noProducts && Array.isArray(products) && products.length > 0) {
      await Promise.all(
        products.map(async (data) => {
          // Fetch service and material data concurrently for each product
          const [serviceResult, materialResult] = await Promise.all([
            Product.getServiceByProductNew(data.id),
            Product.getMaterialByProductNew(data.id)
          ]);

          data.services = serviceResult;
          data.materials = materialResult || [];

          if (data?.inhouse === 0) {
            const price = Number(data?.sub_contract_price) || 0;
            const markup = Number(data?.markup) || 0;
            data.actual_retail_price = price * (1 + markup / 100);
          }

          data.facility_id = FACILITY_ID;
        })
      );
    }




    let requestServices = []
    if(is_split_order == 1 && projectCategory != 15){
      requestServices = await Request.getSplitRequestServices(request_id,false,false,false,result[0]?.project_category)
      // console.log("requestServices split",requestServices)
    }else {
      requestServices = await Request.getRequestServices(request_id,false,false,false,result[0]?.project_category)
      // console.log("requestServices split",requestServices)
    }

    if (requestServices && requestServices.length > 0) {
      requestServices = requestServices?.map(service => {
        let totalCost = parseFloat(service.service_original_cost || 0);
        if(result[0]?.project_category ==12 && result[0]?.is_split_order == 1 && (service.facility_id != FACILITY_ID && service.facility_id != null)){
            service.service_original_cost = parseFloat(service.unit_cost || 0);
            totalCost = parseFloat(service.unit_cost || 0);
            service.quantity = parseFloat(service.jobcost_quantity || 0) || parseFloat(service.quantity || 0);
        }
        const jobcostQuantity = (result[0]?.project_category ==12 && service?.fixed_service_cost !== 1) ? parseFloat(service.jobcost_quantity || 0) :parseFloat(service.quantity || 0);
        let totalServiceCost = totalCost * jobcostQuantity;

        if(result[0]?.project_category ==12 && (service.facility_id == FACILITY_ID || service.facility_id == null)){
         
          service.total_cost =totalServiceCost 
     
          service.quantity =jobcostQuantity 
        }
        if(result[0]?.project_category == 13){
          service.service_original_cost = service.unit_cost
          service.originalMarkup = service.markup
          }
    
        return {
          ...service,
          total_service_cost: totalServiceCost.toFixed(2),
        };
      });
    }




  


if (!empty(result)) {
      res.send({
        status: 1, 
        data: result,
        requestServices,
        message: "Data fetched successfully"
      });
    }
    else {
      res.send({ status: 0, message: "Error in fetching requets by id" })
    }
  } catch (error) {
    console.log(error, 'error in get request by id')
    res.send({ status: 0, message: 'failed to get request by id' })
  }
}
const getRequestDataForInstalationById = async (req, res) => {
  try {
    const { request_id, userId, userType, isLogs } = req.body;

    if (isLogs) {
      let [attachments, partner] = await Promise.all([
        RequestPartner.fetchAllRequestAttachments(request_id),
        Client.getPartnerByUserId(userId),
      ]);
      let updates = await RequestUpdates.fetchAllUpdates(5, request_id, null, partner.id, userId, userType);
      res.send({
        status: 1,
        attachments,
        updates,
        message: "Data fetched successfully"
      });
    } else {
      let [routeData, result, attachments, checkProjectExist] = await Promise.all([
        Route.getRouteDatabySalesOrderId(request_id),
        Request.getAllRequests(request_id, userId, userType),
        RequestPartner.fetchAllRequestAttachments(request_id),
        Request.checkExistingProject(request_id),
      ]);
      //   const splitStatus = result?.data?.[0]?.status || null;
      //   const is_split_order = result?.data?.[0]?.is_split_order || null;
      //   const projectCategory = result?.data?.[0]?.project_category || null;
      const { status, is_split_order, project_category } = result.data[0];  // Destructure the values
      const splitStatus = status || null;
      const isSplitOrder = is_split_order || null;
      const projectCategory = project_category || null;

      const mapRequestResult = (item) => {
        const formattedDescription = (item.description || '').replace(/\r?\n/g, '\n');
        const formattedNotes = (item.notes || '').replace(/\r?\n/g, '\n');
        return {
          ...item,
          description: formattedDescription,
          description_lines: formattedDescription ? formattedDescription.split('\n') : [],
          notes: formattedNotes,
          notes_lines: formattedNotes ? formattedNotes.split('\n') : [],
          checkProjectExist,
          welcome_message: item.welcome_message === 1,
          routeData,
          splitStatus,
        };
      };

      result = result?.data?.map(mapRequestResult);
      let requestServices = []
      if (is_split_order == 1 && projectCategory != 15) {
        requestServices = await Request.getSplitRequestServices(request_id, false, false, false, result[0]?.project_category)
        // console.log("requestServices split",requestServices)
      } else {
        requestServices = await Request.getRequestServices(request_id, false, false, false, result[0]?.project_category)
        // console.log("requestServices split",requestServices)
      }

      if (requestServices && requestServices.length > 0) {
        requestServices = requestServices?.map(service => {
          let totalCost = parseFloat(service.service_original_cost || 0);
          if (result[0]?.project_category == 12 && result[0]?.is_split_order == 1 && (service.facility_id != FACILITY_ID && service.facility_id != null)) {
            service.service_original_cost = parseFloat(service.unit_cost || 0);
            totalCost = parseFloat(service.unit_cost || 0);
            service.quantity = parseFloat(service.jobcost_quantity || 0) || parseFloat(service.quantity || 0);
          }
          const jobcostQuantity = (result[0]?.project_category == 12 && service?.fixed_service_cost !== 1) ? parseFloat(service.jobcost_quantity || 0) : parseFloat(service.quantity || 0);
          let totalServiceCost = totalCost * jobcostQuantity;
          if (result[0]?.project_category == 12 && (service.facility_id == FACILITY_ID || service.facility_id == null)) {
            service.total_cost = totalServiceCost
            service.quantity = jobcostQuantity
          }
          if (result[0]?.project_category == 13) {
            service.service_original_cost = service.unit_cost
            service.originalMarkup = service.markup
          }

          return {
            ...service,
            total_service_cost: totalServiceCost.toFixed(2),
          };
        });
      }
      if (result && result.length > 0) {
        res.send({
          status: 1,
          data: result,
          attachments,
          requestServices,
          message: "Data fetched successfully"
        });
      }

      else {
        res.send({ status: 0, message: "Error in fetching requets by id" })
      }
    }
  } catch (error) {
    console.log(error, 'error in get request by id')
    res.send({ status: 0, message: 'failed to get request by id' })
  }
}
const getRequestByIdProducts = async (req, res) => {
  try {
    // const { request_id } = req.body;
    const products = await Product.getAllProductsNew(false);
//     if (Array.isArray(products) && products.length > 0) {
//       await Promise.all(
//         products.map(async (data) => {
//           // Fetch service and material data concurrently for each product
//           const [serviceResult, materialResult] = await Promise.all([
//             Product.getServiceByProductNew(data.id),
//             Product.getMaterialByProductNew(data.id)
//           ]);
// // console.log("serviceResult", serviceResult);
// // console.log("materialResult", materialResult);

//           // Assign the fetched results to the respective properties
//           data.services = serviceResult;
//           data.materials = materialResult || [];

//           // Process in-house products
//           if (data?.inhouse === 0) {
//             const price = Number(data?.sub_contract_price) || 0;
//             const markup = Number(data?.markup) || 0;
//             data.actual_retail_price = price * (1 + markup / 100);
//           }

//           // Set facility ID for each product
//           data.facility_id = FACILITY_ID;
//         })
//       );
//     }
    // const materials = await Product.getAllMaterials(false);
    products.forEach((p) => {
      // Process in-house products
      if (p?.inhouse === 0) {
        const price = Number(p?.sub_contract_price) || 0;
        const markup = Number(p?.markup) || 0;
        p.actual_retail_price = price * (1 + markup / 100);
      }
    
      p.facility_id = FACILITY_ID;
    });
    res.send({ status: 1,products })
  } catch (error) {
    console.log(error, 'error in get request by id products')
    res.send({ status: 0, message: 'failed to get request by id products' })
  }
}
const getRequestByIdMaterials = async (req, res) => {
  try {
    let materials = []
    if (req.body) {
      const { isNoAdditionalProduct, request_id, inHouse } = req.body;
      // const products = await Product.getAllProducts(false);
      let noAdditionalProduct = isNoAdditionalProduct ?? false
      materials = await Product.getAllMaterials(false, noAdditionalProduct, inHouse);
    } else {
      materials = await Product.getAllMaterials(false, false, false);
    }
    res.send({ status: 1, materials })
  } catch (error) {
    console.log(error, 'error in get request by id products')
    res.send({ status: 0, message: 'failed to get request by id products' })
  }
}
const getRequestByIdProductionInnerPage = async (req, res) => {
  try {
    const { request_id } = req.body;
    let userId = req.body.userId
    let userType = req.body.userType
    let routeData = await Route.getRouteDatabySalesOrderId(request_id)
    // Use optimized function for single request fetch instead of getAllRequests
    let result = await Request.getRequestByIdOptimized(request_id, userId, userType)
    let checkProjectExist = await Request.checkExistingProject(request_id);
    
    result = result?.data?.map(item => {
      const formattedDescription = (item.description || '').replace(/\r?\n/g, '\n');
      const formattedNotes = (item.notes || '').replace(/\r?\n/g, '\n');
      return {
        ...item,
        description: formattedDescription,
        description_lines: formattedDescription ? formattedDescription.split('\n') : [],
        notes: formattedNotes,
        notes_lines: formattedNotes ? formattedNotes.split('\n') : [],
        checkProjectExist: checkProjectExist,
        welcome_message: item.welcome_message === 1 ? true : false,
        routeData
      };
    });
    // COMMENTED OUT - Moved to separate endpoint: getOrderDetailsData
    // let projectMembers = await Request.getAllDesignOrderProjectMembers(result[0]?.design_order_id);
    // let customerData = await CustomerOrder.getCustomerOrderRequestId(request_id)
    // COMMENTED OUT - Moved to separate endpoint: getJobNoteMembers
    // let projectExisting=await Request.getProjectExisting(request_id)
    // let jobNoteMembers = [];

    // if (projectExisting || result) {
    //   const jobNoteProjectMembers = await Project.projectMembersForMessaging(request_id);
    //   const jobNoteDrivers = await Project.DriverForMessaging(request_id);
    //   const jobNoteInstaller = await Project.InstallersForMessaging(request_id);
    //   const getAllAdmins = await Project.AdminsForMessaging(userId);
      
    //   let designOrderProjectMembers = [];
    //   if (result[0]?.design_order_id) {
    //     designOrderProjectMembers = await Request.getAllDesignOrderProjectMembers(result[0].design_order_id);
    //   }
      

    //   const combined = [
    //     ...jobNoteProjectMembers,
    //     ...jobNoteDrivers,
    //     ...jobNoteInstaller,
    //     ...getAllAdmins,
    //     ...designOrderProjectMembers,
    //   ];

    //   jobNoteMembers = Array.from(
    //     new Map(
    //       combined
    //         .filter(user => user?.user_id)
    //         .map(user => [user.user_id, user])
    //     ).values()
    //   );
    // }

    // COMMENTED OUT - Moved to separate endpoint: getRequestUpdates
    // let updates = await RequestUpdates.fetchAllUpdates(5, request_id, null, partner.id, userId, userType)
    // COMMENTED OUT - Moved to separate endpoint: getRequestAttachments
    // let attachments = await RequestPartner.fetchAllRequestAttachments(request_id);
    // COMMENTED OUT - Moved to separate endpoint: getOrderDetailsData
    // let customFieldTemplateId = await Client.getAllFieldTemplatesRfq(request_id)
    // let requestServices = await Request.getRequestServices(request_id,false,false,false,result[0]?.project_category)
    // if (requestServices && requestServices.length > 0) {
    //   requestServices = requestServices?.map(service => {
    //     const totalCost = parseFloat(service.service_original_cost || 0);
    //     const jobcostQuantity = (result[0]?.project_category ==12 && service?.fixed_service_cost !== 1) ? parseFloat(service.jobcost_quantity || 0) :parseFloat(service.quantity || 0);
    //     let totalServiceCost = totalCost * jobcostQuantity;

    //       if(result[0]?.project_category ==12){
    //        service.total_cost =totalServiceCost
    //       service.quantity =jobcostQuantity 
    //     }
    //     if(result[0]?.project_category == 13){
    //       service.service_original_cost = service.unit_cost
    //       service.originalMarkup = service.markup
    //       }
    
    //     return {
    //       ...service,
    //       total_service_cost: totalServiceCost.toFixed(2),
    //     };
    //   });
    // }
    // let fieldTemplates = [];
    // let i = 0;
    // for (const row of customFieldTemplateId) {
    //   let field_template_ids = await OnboardWorkFlowFields.getfieldTemplatesbyStageId(row.stage_id);
    //   //  console.log("HIFI", field_template_ids);

    //   if (!empty(field_template_ids)) {
    //     let templateData = await OnboardWorkFlowStages.getTemplateFormDataById(field_template_ids, 0, 0, 1, request_id);
    //     fieldTemplates[i] = templateData;
    //     i++;
    //   }
    // }
    // let fieldTemplatesNew = []
    // let ids = new Set();
    // fieldTemplates.forEach(innerArray => {
    //   let obj = innerArray[0];
    //   if (!ids.has(obj.id)) {
    //     fieldTemplatesNew.push(obj);
    //     ids.add(obj.id);
    //   }
    // });
    // let QuestionTemplateId = await Client.getQuestionnairTemplatesRfq(request_id)


    // let QuestionTemplates = []
    // for (const row of QuestionTemplateId) {
    //   let questionnairIds = await OnboardWorkFlowQuestionnaires.getQuestionairebyStageId(row.stage_id);

    //   if (!empty(questionnairIds)) {
    //     let QuestionData = await OnboardWorkFlowStages.get_qns_formData_by_id(questionnairIds, 0, 0, 1, request_id);
    //     QuestionTemplates.push(QuestionData);

    //   }
    // }
    // let QuestionTemplatesNew = []
    // ids = new Set();
    // QuestionTemplates.forEach(innerArray => {
    //   let obj = innerArray[0];
    //   if (!ids.has(obj.id)) {
    //     QuestionTemplatesNew.push(obj);
    //     ids.add(obj.id);
    //   }
    // });
    // let signAgrementData = await Client.getAllSignAgrementsOfRfq(request_id)
    // let allDataEmpty = true;
    // if (!empty(signAgrementData) || !empty(QuestionTemplates) || !empty(fieldTemplates)) {
    //   allDataEmpty = false;
    // }
    // let portfolio = await OnboardWorkFlowStages.getPortfoliobypartnerid(0, 1, request_id)
    let requestProposals = await RequestProposals.getAllPropsalsByRequestId(request_id, userId, userType)
    if (!empty(result)) {
      res.send({
        status: 1, data: result,
        requestProposals,
        // COMMENTED OUT - Moved to separate endpoint: getRequestUpdates
        // updates,
        // COMMENTED OUT - Moved to separate endpoint: getRequestAttachments
        // attachments,
        // COMMENTED OUT - Moved to separate endpoint: getOrderDetailsData
        // customerData,
        // COMMENTED OUT - Moved to separate endpoint: getRequestServices
        // requestServices,
        // COMMENTED OUT - Moved to separate endpoint: getOrderDetailsData
        // signAgrementData,
        // fieldTemplates: fieldTemplatesNew,
        // QuestionTemplates: QuestionTemplatesNew,
        // allDataEmpty,
        // portfolio,
        // projectMembers,
        // COMMENTED OUT - Moved to separate endpoint: getJobNoteMembers
        // jobNoteMembers,
        // customData,
        message: "Data fetched successfully"
      });
    }
    else {
      res.send({ status: 0, message: "Error in fetching requets by id" })
    }
  } catch (error) {
    console.log(error, 'error in get request by id')
    res.send({ status: 0, message: 'failed to get request by id' })
  }
}

// New controller function for fetching attachments only
const getRequestAttachments = async (req, res) => {
  try {
    const { request_id,isMirrored,targetFacilityId } = req.body;
    
      if (!request_id) {
        return res.send({ status: 0, message: 'Request ID is required' });
      }

      let parentRequestId = null
      if(request_id){
      const getOriginParentRequestId = await Request.getOriginParentRequestId(request_id)
      parentRequestId = getOriginParentRequestId[0]?.origin_facility_request_id
      }
      if(isMirrored && targetFacilityId && parentRequestId){
        const targetFacility = filteredSignwiseInstalations.find(
          f => f.id === Number(targetFacilityId)
        );
        if (!targetFacility?.url) return null;
        const projectResponse = await axios.post(
          `${targetFacility.url}request/get-attachments`,
          {
            request_id:parentRequestId
          }
        );
        if(projectResponse?.data){
          return res.send({ status: 1, attachments: projectResponse?.data?.attachments })
        }else{
          return res.send({ status: 0, message: "Failed to fetch attachments" })
        }
      }

    // Fetch attachments only
    let attachments = await RequestPartner.fetchAllRequestAttachments(request_id);
    
    res.send({
      status: 1,
      attachments,
      message: "Attachments fetched successfully"
    });
  } catch (error) {
    console.log(error, 'error in get request attachments')
    res.send({ status: 0, message: 'failed to get request attachments' })
  }
}

// New controller function for fetching updates only
const getRequestUpdates = async (req, res) => {
  try {
    const { request_id } = req.body;
    let userId = req.body.userId
    let userType = req.body.userType
    
    if (!request_id) {
      return res.send({ status: 0, message: 'Request ID is required' });
    }

    if (!userId || !userType) {
      return res.send({ status: 0, message: 'User ID and User Type are required' });
    }

    // Fetch partner to get partner.id for updates
    let partner = await Client.getPartnerByUserId(userId);
    // Fetch updates (model already merges design/estimate history when applicable)
    let updates = await RequestUpdates.fetchAllUpdates(5, request_id, null, partner.id, userId, userType)
    
    res.send({
      status: 1,
      updates,
      message: "Updates fetched successfully"
    });
  } catch (error) {
    console.log(error, 'error in get request updates')
    res.send({ status: 0, message: 'failed to get request updates' })
  }
}

const getSplitEstimateUpdates = async (req, res) => {
  try {
    const { split_estimate_id } = req.body;
    let userId = req.body.userId
    let userType = req.body.userType
    
    if (!split_estimate_id) {
      return res.send({ status: 0, message: 'Split Estimate ID is required' });
    }

    if (!userId || !userType) {
      return res.send({ status: 0, message: 'User ID and User Type are required' });
    }

    // Fetch partner to get partner.id for updates
    let partner = await Client.getPartnerByUserId(userId);
    // Fetch updates only
    let updates = await RequestUpdates.fetchAllSplitEstimateUpdates(5, split_estimate_id, null, partner.id, userId, userType)
    
    res.send({
      status: 1,
      updates,
      message: "Split Estimate Updates fetched successfully"
    });
  } catch (error) {
    console.log(error, 'error in get split estimate updates')
    res.send({ status: 0, message: 'failed to get split estimate updates' })
  }
}

// New controller function for fetching request services only
const getRequestServices = async (req, res) => {
  try {
    const { request_id } = req.body;
    let userId = req.body.userId
    let userType = req.body.userType
    
    if (!request_id) {
      return res.send({ status: 0, message: 'Request ID is required' });
    }

    // Get request data to fetch project_category
    let result = await Request.getAllRequests(request_id, userId, userType)
    
    if (!result || !result.data || result.data.length === 0) {
      return res.send({ status: 0, message: 'Request not found' });
    }

    // Fetch request services
    let requestServices = await Request.getRequestServices(request_id, false, false, false, result.data[0]?.project_category)
    
    if (requestServices && requestServices.length > 0) {
      requestServices = requestServices?.map(service => {
        const totalCost = parseFloat(service.service_original_cost || 0);
        const jobcostQuantity = (result.data[0]?.project_category == 12 && service?.fixed_service_cost !== 1) 
          ? parseFloat(service.jobcost_quantity || 0) 
          : parseFloat(service.quantity || 0);
        let totalServiceCost = totalCost * jobcostQuantity;

        if(result.data[0]?.project_category == 12){
          service.total_cost = totalServiceCost
          service.quantity = jobcostQuantity 
        }
        if(result.data[0]?.project_category == 13){
          service.service_original_cost = service.unit_cost
          service.originalMarkup = service.markup
        }

        return {
          ...service,
          total_service_cost: totalServiceCost.toFixed(2),
        };
      });
    }
    
    res.send({
      status: 1,
      requestServices: requestServices || [],
      message: "Request services fetched successfully"
    });
  } catch (error) {
    console.log(error, 'error in get request services')
    res.send({ status: 0, message: 'failed to get request services' })
  }
}

// New controller function for fetching order details data (projectMembers, portfolio, fieldTemplates, signAgrementData, QuestionTemplates)
const getOrderDetailsData = async (req, res) => {
  try {
    const { request_id } = req.body;
    let userId = req.body.userId
    let userType = req.body.userType
    
    if (!request_id) {
      return res.send({ status: 0, message: 'Request ID is required' });
    }

    // Get request data to fetch design_order_id
    let result = await Request.getAllRequests(request_id, userId, userType)
    
    if (!result || !result.data || result.data.length === 0) {
      return res.send({ status: 0, message: 'Request not found' });
    }

    // Fetch projectMembers
    let projectMembers = await Request.getAllDesignOrderProjectMembers(result.data[0]?.design_order_id);
    
    // Fetch fieldTemplates
    let customFieldTemplateId = await Client.getAllFieldTemplatesRfq(request_id)
    let fieldTemplates = [];
    let i = 0;
    for (const row of customFieldTemplateId) {
      let field_template_ids = await OnboardWorkFlowFields.getfieldTemplatesbyStageId(row.stage_id);

      if (!empty(field_template_ids)) {
        let templateData = await OnboardWorkFlowStages.getTemplateFormDataById(field_template_ids, 0, 0, 1, request_id);
        fieldTemplates[i] = templateData;
        i++;
      }
    }
    let fieldTemplatesNew = []
    let ids = new Set();
    fieldTemplates.forEach(innerArray => {
      let obj = innerArray[0];
      if (!ids.has(obj.id)) {
        fieldTemplatesNew.push(obj);
        ids.add(obj.id);
      }
    });

    // Fetch QuestionTemplates
    let QuestionTemplateId = await Client.getQuestionnairTemplatesRfq(request_id)
    let QuestionTemplates = []
    for (const row of QuestionTemplateId) {
      let questionnairIds = await OnboardWorkFlowQuestionnaires.getQuestionairebyStageId(row.stage_id);

      if (!empty(questionnairIds)) {
        let QuestionData = await OnboardWorkFlowStages.get_qns_formData_by_id(questionnairIds, 0, 0, 1, request_id);
        QuestionTemplates.push(QuestionData);
      }
    }
    let QuestionTemplatesNew = []
    ids = new Set();
    QuestionTemplates.forEach(innerArray => {
      let obj = innerArray[0];
      if (!ids.has(obj.id)) {
        QuestionTemplatesNew.push(obj);
        ids.add(obj.id);
      }
    });

    // Fetch signAgrementData
    let signAgrementData = await Client.getAllSignAgrementsOfRfq(request_id)
    
    // Calculate allDataEmpty
    let allDataEmpty = true;
    if (!empty(signAgrementData) || !empty(QuestionTemplates) || !empty(fieldTemplates)) {
      allDataEmpty = false;
    }

    // Fetch portfolio
    let portfolio = await OnboardWorkFlowStages.getPortfoliobypartnerid(0, 1, request_id)
    
    // Fetch customerData
    let customerData = await CustomerOrder.getCustomerOrderRequestId(request_id)
    
    res.send({
      status: 1,
      projectMembers,
      portfolio,
      fieldTemplates: fieldTemplatesNew,
      signAgrementData,
      QuestionTemplates: QuestionTemplatesNew,
      allDataEmpty,
      customerData,
      message: "Order details data fetched successfully"
    });
  } catch (error) {
    console.log(error, 'error in get order details data')
    res.send({ status: 0, message: 'failed to get order details data' })
  }
}

const getRequestByIdWithoutTeamAndProducts = async (req, res) => {
  try {
    const { request_id ,isNotSplitOrder} = req.body;
    let userId = req.body.userId
    let userType = req.body.userType
    // let result = await Request.getRequestById(request_id) 
    let routeData = await Route.getRouteDatabySalesOrderId(request_id)
    let result = await Request.getAllRequests(request_id, userId, userType)
    let checkProjectExist = await Request.checkExistingProject(request_id);
    
    console.log('getRequestByIdWithoutTeamAndProducts - result:', result);
    console.log('getRequestByIdWithoutTeamAndProducts - result before mapping:', result?.data?.[0]);
    console.log('getRequestByIdWithoutTeamAndProducts - design_order_id:', result?.data?.[0]?.design_order_id);
    
    result = result?.data?.map(item => {
      const formattedDescription = (item.description || '').replace(/\r?\n/g, '\n');
      const formattedNotes = (item.notes || '').replace(/\r?\n/g, '\n');
      return {
      ...item,
        description: formattedDescription,
        description_lines: formattedDescription ? formattedDescription.split('\n') : [],
        notes: formattedNotes,
        notes_lines: formattedNotes ? formattedNotes.split('\n') : [],
      checkProjectExist: checkProjectExist,
      welcome_message: item.welcome_message === 1 ? true : false,
      routeData
      };
    });
    let projectMembers = await Request.getAllDesignOrderProjectMembers(result[0]?.design_order_id);
    let customerData = await CustomerOrder.getCustomerOrderRequestId(request_id)
    // BACKUP: Removed serviceSkill from here - moved to get-products API
    // let serviceSkill = await Request.getServices(null)
    let isEstimateJob = false
    if(result[0]?.is_request_estimate == 1 && result[0]?.quotation == 1){
      isEstimateJob = true
    }
    let projectExisting=await Request.getProjectExisting(request_id, isEstimateJob)
    let team=[]
    if(projectExisting){
      team = await Project.getAllProjectMembers(projectExisting.id)
    }
    
    // BACKUP: jobNoteMembers logic moved to separate API endpoint for better performance
    // let jobNoteMembers = [];
    // if (projectExisting || result) {
    //   const jobNoteProjectMembers = await Project.projectMembersForMessaging(request_id);
    //   const jobNoteDrivers = await Project.DriverForMessaging(request_id);
    //   const jobNoteInstaller = await Project.InstallersForMessaging(request_id);
    //   const getAllAdmins = await Project.AdminsForMessaging(userId);
    //   
    //   // If this request was converted from a design order, include design order project members
    //   let designOrderProjectMembers = [];
    //   if (result[0]?.design_order_id) {
    //     designOrderProjectMembers = await Request.getAllDesignOrderProjectMembers(result[0].design_order_id);
    //   }
    //   
    //   const combined = [
    //     ...jobNoteProjectMembers,
    //     ...jobNoteDrivers,
    //     ...jobNoteInstaller,
    //     ...getAllAdmins,
    //     ...designOrderProjectMembers, // Add design order project members
    //   ];
    //   jobNoteMembers = Array.from(
    //     new Map(
    //       combined
    //         .filter(user => user?.user_id) // filter out null or undefined user_id
    //         .map(user => [user.user_id, user]) // remove duplicates by user_id
    //     ).values()
    //   );
    // }

    // let materials = await Product.getAllMaterials(true);
    // let requestAwaitingPartners=await RequestPartner.requestAwaitingPartners(request_id,0)
    // let requestRejectedPartners=await RequestPartner.requestAwaitingPartners(request_id,2)
    let requestAcceptedPartners = await RequestPartner.requestAwaitingPartners(request_id, 1)
    let partner = await Client.getPartnerByUserId(userId);
    let updates = await RequestUpdates.fetchAllUpdates(5, request_id, null, partner.id, userId, userType)
    let attachments = await RequestPartner.fetchAllRequestAttachments(request_id);
    let payments = await RequestPartner.fetchAllRequestPayments(request_id);
    // let workflows = await Client.getAllWorkflows();
    let customFieldTemplateId = await Client.getAllFieldTemplatesRfq(request_id)
    // let labourCostServices = await Product.getAllLabourCostServices();
    // labourCostServices=labourCostServices.filter(row=>row.inhouse===1) // for inhouse only
    // labourCostServices = labourCostServices.filter(row => row.inhouse === 0)
    let requestServices = await Request.getRequestServices(request_id,false,false,false,result[0]?.project_category)
    console.log("requestServices",requestServices)
    if (requestServices && requestServices.length > 0) {
      requestServices = requestServices?.map(service => {
        const totalCost = parseFloat(service.service_original_cost || 0);
        const jobcostQuantity = (result[0]?.project_category ==12 && service?.fixed_service_cost !== 1) ? parseFloat(service.jobcost_quantity || 0) :parseFloat(service.quantity || 0);
        let totalServiceCost = totalCost * jobcostQuantity;

        // if(result[0]?.project_category ==12 && service?.labour_cost_service_id !== 10){ // uncomment if needed
          if(result[0]?.project_category ==12){
          //  service.quantity = parseFloat(service.jobcost_quantity).toFixed(2)
           service.total_cost =totalServiceCost // parseFloat(service.jobcost_total_cost)
          //  totalServiceCost =parseFloat(Number(service.jobcost_total_cost))
          service.quantity =jobcostQuantity 
        }
        if(result[0]?.project_category == 13){
          service.service_original_cost = service.unit_cost
          service.originalMarkup = service.markup
          }
    
        return {
          ...service,
          total_service_cost: totalServiceCost.toFixed(2),
        };
      });
    }
    // console.log("$$$$$$$$$$$$",customFieldTemplateId);
    let fieldTemplates = [];
    let i = 0;
    for (const row of customFieldTemplateId) {
      let field_template_ids = await OnboardWorkFlowFields.getfieldTemplatesbyStageId(row.stage_id);
      //  console.log("HIFI", field_template_ids);

      if (!empty(field_template_ids)) {
        let templateData = await OnboardWorkFlowStages.getTemplateFormDataById(field_template_ids, 0, 0, 1, request_id);
        fieldTemplates[i] = templateData;
        i++;
      }
    }
    let fieldTemplatesNew = []
    let ids = new Set();
    fieldTemplates.forEach(innerArray => {
      let obj = innerArray[0];
      if (!ids.has(obj.id)) {
        fieldTemplatesNew.push(obj);
        ids.add(obj.id);
      }
    });
    let QuestionTemplateId = await Client.getQuestionnairTemplatesRfq(request_id)


    let QuestionTemplates = []
    for (const row of QuestionTemplateId) {
      let questionnairIds = await OnboardWorkFlowQuestionnaires.getQuestionairebyStageId(row.stage_id);

      if (!empty(questionnairIds)) {
        let QuestionData = await OnboardWorkFlowStages.get_qns_formData_by_id(questionnairIds, 0, 0, 1, request_id);
        QuestionTemplates.push(QuestionData);

      }
    }
    let QuestionTemplatesNew = []
    ids = new Set();
    QuestionTemplates.forEach(innerArray => {
      let obj = innerArray[0];
      if (!ids.has(obj.id)) {
        QuestionTemplatesNew.push(obj);
        ids.add(obj.id);
      }
    });
    let signAgrementData = await Client.getAllSignAgrementsOfRfq(request_id)
    let allDataEmpty = true;
    if (!empty(signAgrementData) || !empty(QuestionTemplates) || !empty(fieldTemplates)) {
      allDataEmpty = false;
    }
    let portfolio = await OnboardWorkFlowStages.getPortfoliobypartnerid(0, 1, request_id)
    // console.log("###########",signAgrementData);
    let requestProposals = await RequestProposals.getAllPropsalsByRequestId(request_id, userId, userType)
    // let customData=await OnboardWorkFlowStages.getRfqData(request_id)
    // BACKUP: Removed allProposals from here - moved to get-products API
    // let allProposals = await Proposals.getAllAwardedProposals()
    // const otherInstallationProducts = await SplitManagementModel.getAllProductsFromMultipleDatabase(true);
    const currentFacilitySplitManagementSettings = await SplitManagementModel.getAllSplitManagementSettings();
    const otherInstallationSplitManagementSettings = await SplitManagementModel.getAllSplitManagementSettingsFromOtherInstallations();
    // const otherInstallationServices = await SplitManagementModel.getAllLabourCostServicesFromOtherInstallations();
    let otherInstallationProducts = [];
    let otherInstallationServices = [];
    if(isNotSplitOrder){
      otherInstallationProducts = [];
      otherInstallationServices = [];
    }else{
      otherInstallationProducts = await SplitManagementModel.getAllProductsFromMultipleDatabase(true);
      otherInstallationServices = await SplitManagementModel.getAllLabourCostServicesFromOtherInstallations();
    }
    if (!empty(result)) {
      res.send({
        status: 1, data: result
        // BACKUP: Removed skill and allProposals from here - moved to get-products API
        // , skill: serviceSkill,
        , requestAcceptedPartners,
        updates,
        attachments,
        customerData,
        // BACKUP: Removed labourCostServices and materials from here - moved to get-products API
        // labourCostServices,
        requestServices,
        payments,
        // workflows,
        signAgrementData,
        fieldTemplates: fieldTemplatesNew,
        QuestionTemplates: QuestionTemplatesNew,
        allDataEmpty,
        portfolio,
        requestProposals,
        team,
        // allProposals,
        // materials,
        projectMembers,
        // jobNoteMembers, // BACKUP: Moved to separate API endpoint
        // customData,
        otherInstallationProducts,
        otherInstallationServices,
        currentFacilitySplitManagementSettings,
        otherInstallationSplitManagementSettings,
        message: "Data fetched successfully"
      });
    }
    else {
      res.send({ status: 0, message: "Error in fetching requets by id" })
    }
  } catch (error) {
    console.log(error, 'error in get request by id without team and products')
    res.send({ status: 0, message: 'failed to get request by id without team and products' })
  }
}

const getEstimateJobNewData = async (req, res) => {
  try {
    const { request_id } = req.body;
    const userId = req.body.userId;
    const userType = req.body.userType;

    let result = await Request.getAllRequests(request_id, userId, userType);

    result = result?.data?.map(item => {
      const formattedDescription = (item.description || '').replace(/\r?\n/g, '\n');
      const formattedNotes = (item.notes || '').replace(/\r?\n/g, '\n');
      return {
        ...item,
        description: formattedDescription,
        description_lines: formattedDescription ? formattedDescription.split('\n') : [],
        notes: formattedNotes,
        notes_lines: formattedNotes ? formattedNotes.split('\n') : [],
        welcome_message: item.welcome_message === 1 ? true : false,
      };
    });

    if (!empty(result)) {
      res.send({ status: 1, data: result });
    } else {
      res.send({ status: 0, message: "Error in fetching requets by id" });
    }
  } catch (error) {
    console.log(error, 'error in get request by id without team and products');
    res.send({ status: 0, message: 'failed to get request by id without team and products' });
  }
};

// const getProductsForRequest = async (req, res) => {
//   try {
//     const { isNotSplitOrder,newProduct } = req.body;

//     let products = await Product.getAllProducts(false);
//     // products = products.filter(row => row.inhouse === 1)
//     products = products.filter(row => row.inhouse !== 6)
    
//     for (const data of products) {
//       let result = await Product.getServiceByProduct(data.id);
//       data.services = result

//       let result1 = await Product.getMaterialByProduct(data.id)
//       data.materials = result1 || []
//       data.facility_id = FACILITY_ID
//       if (data?.inhouse == 0) {
//         const price = Number(data?.sub_contract_price) || 0;
//         const markup = Number(data?.markup) || 0;
//         data.actual_retail_price = price * (1 + markup / 100);
//       }
//     }
//     // for (const data of products){
//     //   let result = await Product.getMaterialByProduct(data.id)
//     //     data.materials = result || []
//         // data.facility_id = FACILITY_ID
//           // if(data?.inhouse == 0){
//           //   data.actual_retail_price = data?.sub_contract_price
//           // }
//     // }

//     // NEW: Added labourCostServices and materials from view-request-without-team-products
//     let labourCostServices = await Product.getAllLabourCostServices();
//     for (const data of labourCostServices) {
//       data.facility_id = FACILITY_ID
//     }
//     // labourCostServices=labourCostServices.filter(row=>row.inhouse===1) // for inhouse only
//     // labourCostServices = labourCostServices.filter(row => row.inhouse === 0)
//     let materials = await Product.getAllMaterials(false);
    
//     // NEW: Added skill and allProposals from view-request-without-team-products
//     let skill = await Request.getServices(null);
//     let allProposals = await Proposals.getAllAwardedProposals();
//     let otherInstallationProducts = []
//     let otherInstallationServices = []
//     if(isNotSplitOrder){
//       otherInstallationProducts = [];
//       otherInstallationServices = [];
//     }else{
//       otherInstallationProducts = await SplitManagementModel.getAllProductsFromMultipleDatabase(true);
//       otherInstallationServices = await SplitManagementModel.getAllLabourCostServicesFromOtherInstallations();
//     }
//     const currentFacilitySplitManagementSettings = await SplitManagementModel.getAllSplitManagementSettings();
//     const otherInstallationSplitManagementSettings = await SplitManagementModel.getAllSplitManagementSettingsFromOtherInstallations();
//     if (!empty(products)) {
//       res.send({
//         status: 1,
//         products: products,
//         labourCostServices: labourCostServices,
//         materials: materials,
//         skill: skill,
//         allProposals: allProposals,
//         otherInstallationProducts: otherInstallationProducts,
//         otherInstallationServices: otherInstallationServices,
//         currentFacilitySplitManagementSettings,
//         otherInstallationSplitManagementSettings,
//         message: "Products fetched successfully"
//       });
//     }
//     else {
//       res.send({ status: 0, message: "Error in fetching products" })
//     }
//   } catch (error) {
//     console.log(error, 'error in get products for request')
//     res.send({ status: 0, message: 'failed to get products for request' })
//   }
// }
const getProductsForRequest = async (req, res) => {
  try {
    const { isNotSplitOrder, noProduct, ProductOnly,noMaterial } = req.body;

    let products = [];
    let labourCostServices = [];
    let materials = [];
    let skill = [];
    let allProposals = [];
    let currentFacilitySplitManagementSettings = [];
    let otherInstallationSplitManagementSettings = [];
    let otherInstallationProducts = [];
    let otherInstallationServices = [];

    /* =======================================================
       1️⃣ PRODUCT ONLY MODE (🔥 BULK OPTIMIZED)
       ======================================================= */
    if (ProductOnly) {

      products = await Product.getAllProductsNew(false);
      products = products.filter(row => row.inhouse !== 6);

      products.forEach((p) => {
      // Process in-house products
      if (p?.inhouse === 0) {
        const price = Number(p?.sub_contract_price) || 0;
        const markup = Number(p?.markup) || 0;
        p.actual_retail_price = price * (1 + markup / 100);
      }
    
      p.facility_id = FACILITY_ID;
    });

      // if (products.length) {
      //   const productIds = products.map(p => p.id);

      //   const [allServices, allMaterials] = await Promise.all([
      //     Product.getServicesForBulkProductsByIds(productIds),
      //     Product.getMaterialsForBulkProductsByIds(productIds)
      //   ]);

      //   // Create lookup maps
      //   const serviceMap = {};
      //   const materialMap = {};

      //   for (const s of allServices) {
      //     if (!serviceMap[s.product_id]) serviceMap[s.product_id] = [];
      //     serviceMap[s.product_id].push(s);
      //   }

      //   for (const m of allMaterials) {
      //     if (!materialMap[m.product_id]) materialMap[m.product_id] = [];
      //     materialMap[m.product_id].push(m);
      //   }

      //   products = products.map(product => {
      //     product.services = serviceMap[product.id] || [];
      //     product.materials = materialMap[product.id] || [];
      //     product.facility_id = FACILITY_ID;

      //     if (product?.inhouse == 0) {
      //       const price = Number(product?.sub_contract_price) || 0;
      //       const markup = Number(product?.markup) || 0;
      //       product.actual_retail_price = price * (1 + markup / 100);
      //     }

      //     return product;
      //   });
      // }

      return res.send({
        status: 1,
        products,
        message: "Products fetched successfully"
      });
    }

    /* =======================================================
       2️⃣ NON-PRODUCT MODE
       ======================================================= */
    if (noProduct) {

      // [
      //   labourCostServices,
      //   materials,
      //   skill,
      //   allProposals,
      //   currentFacilitySplitManagementSettings,
      //   otherInstallationSplitManagementSettings
      // ] = await Promise.all([
      //   Product.getAllLabourCostServices(),
      //   Product.getAllMaterials(false),
      //   Request.getServices(null),
      //   Proposals.getAllAwardedProposals(),
      //   SplitManagementModel.getAllSplitManagementSettings(),
      //   SplitManagementModel.getAllSplitManagementSettingsFromOtherInstallations()
      // ]);
      const promises = [
        Product.getAllLabourCostServices(),
        Request.getServices(null),
        Proposals.getAllAwardedProposals(),
        SplitManagementModel.getAllSplitManagementSettings(),
        SplitManagementModel.getAllSplitManagementSettingsFromOtherInstallations()
      ];
      
      if (!noMaterial) {
        promises.splice(1, 0, Product.getAllMaterials(false));
      }
      
      const results = await Promise.all(promises);
      
      if (noMaterial) {
        [
          labourCostServices,
          skill,
          allProposals,
          currentFacilitySplitManagementSettings,
          otherInstallationSplitManagementSettings
        ] = results;
      
        materials = [];
      } else {
        [
          labourCostServices,
          materials,
          skill,
          allProposals,
          currentFacilitySplitManagementSettings,
          otherInstallationSplitManagementSettings
        ] = results;
      }
      
      // labourCostServices = labourCostServices.map(service => ({
      //   ...service,
      //   facility_id: FACILITY_ID
      // }));

      if (!isNotSplitOrder) {
        [otherInstallationProducts, otherInstallationServices] =
          await Promise.all([
            SplitManagementModel.getAllProductsFromMultipleDatabase(true),
            SplitManagementModel.getAllLabourCostServicesFromOtherInstallations()
          ]);
      }

      return res.send({
        status: 1,
        products: [],
        labourCostServices,
        materials,
        skill,
        allProposals,
        otherInstallationProducts,
        otherInstallationServices,
        currentFacilitySplitManagementSettings,
        otherInstallationSplitManagementSettings,
        message: "Data fetched successfully"
      });
    }

    /* =======================================================
       3️⃣ DEFAULT MODE (🔥 BULK OPTIMIZED)
       ======================================================= */

    [
      products,
      labourCostServices,
      materials,
      skill,
      allProposals,
      currentFacilitySplitManagementSettings,
      otherInstallationSplitManagementSettings
    ] = await Promise.all([
      Product.getAllProducts(false),
      Product.getAllLabourCostServices(),
      Product.getAllMaterials(false),
      Request.getServices(null),
      Proposals.getAllAwardedProposals(),
      SplitManagementModel.getAllSplitManagementSettings(),
      SplitManagementModel.getAllSplitManagementSettingsFromOtherInstallations()
    ]);

    products = products.filter(row => row.inhouse !== 6);

    if (products.length) {
      const productIds = products.map(p => p.id);

      const [allServices, allMaterials] = await Promise.all([
        Product.getServicesForBulkProductsByIds(productIds),
        Product.getMaterialsForBulkProductsByIds(productIds)
      ]);

      const serviceMap = {};
      const materialMap = {};

      for (const s of allServices) {
        if (!serviceMap[s.product_id]) serviceMap[s.product_id] = [];
        serviceMap[s.product_id].push(s);
      }

      for (const m of allMaterials) {
        if (!materialMap[m.product_id]) materialMap[m.product_id] = [];
        materialMap[m.product_id].push(m);
      }

      products = products.map(product => {
        product.services = serviceMap[product.id] || [];
        product.materials = materialMap[product.id] || [];
        product.facility_id = FACILITY_ID;

        if (product?.inhouse == 0) {
          const price = Number(product?.sub_contract_price) || 0;
          const markup = Number(product?.markup) || 0;
          product.actual_retail_price = price * (1 + markup / 100);
        }

        return product;
      });
    }

    // labourCostServices = labourCostServices.map(service => ({
    //   ...service,
    //   facility_id: FACILITY_ID
    // }));

    if (!isNotSplitOrder) {
      [otherInstallationProducts, otherInstallationServices] =
        await Promise.all([
          SplitManagementModel.getAllProductsFromMultipleDatabase(true),
          SplitManagementModel.getAllLabourCostServicesFromOtherInstallations()
        ]);
    }

    return res.send({
      status: 1,
      products,
      labourCostServices,
      materials,
      skill,
      allProposals,
      otherInstallationProducts,
      otherInstallationServices,
      currentFacilitySplitManagementSettings,
      otherInstallationSplitManagementSettings,
      message: "Products fetched successfully"
    });

  } catch (error) {
    console.error(error, 'error in get products for request');
    return res.send({ status: 0, message: 'failed to get products for request' });
  }
};


const getRequestLabourCostServices = async (req, res) => {
  try {
    let labourCostServices = await Product.getAllLabourCostServices();
    
    res.send({
      status: 1,
      labourCostServices,
      message: "Labour cost services fetched successfully"
    });
  } catch (error) {
    console.log(error, 'error in get request labour cost services')
    res.send({ status: 0, message: 'failed to get request labour cost services' })
  }
}


const editRequestDraft = async (req, res) => {
  try {
    req.body.values = req.body.values || {};
    const v = req.body.values;
    v.name = v.name || '';
    v.description = v.description || '';
    v.products = v.products || [];
    v.selectedServices = v.selectedServices || [];
    v.additionalSelectedServices = v.additionalSelectedServices || [];
    v.services = v.services || '';
    v.start = v.start != null ? v.start : null;
    v.end = v.end != null ? v.end : null;
    v.clientsData = [v.clients] || [];
    v.sign_agreement = v.sign_agreement || [];
    v.total = v.total != null ? v.total : 0;
    v.taxAmount = v.taxAmount != null ? v.taxAmount : 0;
    v.sub_total = v.sub_total != null ? v.sub_total : 0;
    v.labour_cost = v.labour_cost != null ? v.labour_cost : 0;
    v.avatarUrl = v.avatarUrl || [];
    req.body.address = req.body.address || [];
    req.body.rfpProducts = req.body.rfpProducts || [];
    req.body.rfpServices = req.body.rfpServices || [];
    req.body.materials = req.body.materials || [];
    req.body.produtFormulaMaterial = req.body.produtFormulaMaterial || [];
    req.body.quotation = 1;
    req.body.saveAsDraft = true;
    req.body.is_draft = true;
    return editRequest(req, res);
  } catch (error) {
    console.log(error, 'Error in editing draft request');
    res.send({ status: 0, message: 'Error in editing draft request' });
  }
};

const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));

const editRequest = async (req, res) => {
  try {
    UniqueEstimateId =null
    doc_number = null
    const { values, userId, isHavingProductRows,isHavingServiceRows, isUpdateRequestFlow, isFromFinalReview, noServiceNoTask,avalara_transaction_id,avalara_code, saveAsDraft, taxCategory, userType, request_id, address,allLocationSame,allServiceLocationSame,allRfpServiceLocationSame,allRfpProductsLocationSame,quotation, isFromCreateEstimate, selectedEstimators,rfpProducts,rfpServices,materials,proj_status,produtFormulaMaterial,new_order, noChargeReason,all_sales_rep_ids,sales_rep_names,collaborators,salesRepName,isFinalInvoiceEdit } = req.body;
    console.log('all_sales_rep_ids',all_sales_rep_ids)
    const productAttachments = values?.productAttachments || {}; // Get product attachments from frontend
    console.log('🔍 [EDIT] Product attachments received:', {
      request_id,
      productAttachmentsKeys: Object.keys(productAttachments),
      productAttachmentsCount: Object.keys(productAttachments).length
    });
    let mirrorData = [];
    mirrorData.productData = [];
    mirrorData.materialData = [];
    mirrorData.serviceData = [];
    mirrorData.rfpProductData = [];
    mirrorData.rfpServiceData = [];
    mirrorData.orderData = [];
    // 📊 Progress tracking function
    const emitProgress = (step, message, progress = 0) => {
      console.log(`[PROGRESS-MODAL] 📊 Progress: ${step} - ${message} (${progress}%)`);
      const progressData = {
        step,
        message,
        progress,
        timestamp: Date.now(),
        userId: userId, // Include user ID for filtering
        domain: DOMAIN // Add domain for filtering (same as chat)
      };
      
      // Emit to working socket server (same as chat)
      console.log(`[PROGRESS-MODAL] 📡 Emitting progress to working socket server...`);
      try {
        const workingSocket = ioClient(REST_API_IO, {
          path: '/socket.io',
          transports: ['websocket', 'polling'],
          secure: true,
          timeout: 5000
        });
        
        workingSocket.on('connect', () => {
          console.log(`[PROGRESS-MODAL] 🔌 Connected to working socket server`);
          console.log(`[PROGRESS-MODAL] 📊 Emitting progress data:`, progressData);
          workingSocket.emit('message', {
            ...progressData,
            type: 'progress'
          });
          workingSocket.emit('request_progress', progressData);
          console.log(`[PROGRESS-MODAL] ✅ Successfully emitted progress to working socket server`);
          
          // Disconnect after emitting
          setTimeout(() => {
            workingSocket.disconnect();
          }, 1000);
        });
        
        workingSocket.on('connect_error', (error) => {
          console.error(`[PROGRESS-MODAL] ❌ Working socket connection error:`, error);
        });
        
      } catch (error) {
        console.error(`[PROGRESS-MODAL] ❌ Error creating working socket connection:`, error);
      }
    };

    // Initial progress
    const orderType = quotation === 1 ? 'estimate' : 'sales order';
    console.log(`[PROGRESS-MODAL] 🚀 Starting ${orderType} update for user:`, userId);
    emitProgress('start', `Initializing ${orderType} update...`, 5);
    
    emitProgress('data_preparation', `Preparing ${orderType} data...`, 10);
    
    // Process additional products/services/materials
    emitProgress('processing_additional_items', `Processing additional products, services, and materials...`, 15);
    
    let currentDate = moment().unix()
    let uploaded = values.avatarUrl;
    let requestdata = {}
console.log("quotationnnnnnnnnn",quotation)
    const additionalProducts = values?.products?.filter(item => item?.isAdditionalProduct == true);
    if (additionalProducts?.length) {
      await addDataToProductTable(additionalProducts);
    }         
    const additionalServices = values?.selectedServices?.filter(item => item?.isAdditionalProduct == true);
    if (additionalServices?.length) {
      await addDataToServiceTable(additionalServices);
    }         
    const additionalMaterials = materials?.filter(item => item?.isAdditionalProduct == true);
    if (additionalMaterials?.length) {
      await addDataToMaterialTable(additionalMaterials);
    }

    emitProgress('processing_attachments', `Processing attachments and images...`, 20);
    console.log("dataaaaaaaaaaaaaaaaaaaaaaaaa",values.avatarUrl)

    // When request originated from design order (design_order_id set), preserve attachment images by copying to new attachment ids on re-add
    let requestMetaResult = await Request.getAllRequests(request_id, userId, userType);
    let design_order_id = requestMetaResult?.data?.[0]?.design_order_id || null;
    let existingRequestAttachmentImagesMap = {};
    if (design_order_id) {
      const existingAttachmentsForCopy = await Request.getAllRequestAttachments(request_id);
      for (const att of existingAttachmentsForCopy || []) {
        const imgs = await getRequestAttachmentImagesByDocumentId(att.id, 'request');
        if (imgs && imgs.length > 0) existingRequestAttachmentImagesMap[att.id] = imgs;
      }
    }

    // if (!empty(uploaded)) { // i done for my ticket if any issues please find any other solution because this condition will break my ticket
      // Delete images for all attachments before deleting attachments
      const existingAttachments = await Request.getAllRequestAttachments(request_id);
      if (existingAttachments && existingAttachments.length > 0) {
        for (const attachment of existingAttachments) {
          if (design_order_id) {
            // Preserve image files: only delete DB rows so we can reattach same images to new attachment ids
            await deleteRequestAttachmentImagesByDocumentId(attachment.id, 'request');
          } else {
            await deleteRequestAttachmentImagesHelper(attachment.id);
          }
        }
      }
      let deleteAllAtachment = await Request.deleteRequestAttachments(request_id)
      for (const row of uploaded) {
        if(row?.file_type?.startsWith('image/')){
          requestdata['thumbnail'] = row.url
        }
      }
    // }

  

    if (!empty(uploaded)) {
      for (const row of uploaded) {
        let approvedImage = {}
        if(values?.project_category?.id === 11 ){
            approvedImage.approved = 1
        }else if(row?.file_type?.startsWith('image')){
          approvedImage.approved = row.approved || 0
        }else{
          approvedImage.approved = 1
        }
        approvedImage.name =row?.name || row?.url;
        approvedImage.file_type = row.file_type;
        approvedImage.extension = row.extension;
        approvedImage.file_size = row.size;
        approvedImage.request_id = request_id;
        approvedImage.added_by =row.added_by || userId
        approvedImage.approved_by = userId
        approvedImage.reason = row.reason || null
        // approvedImage.approved = row.approved ||0
        approvedImage.location_ids = row.location_ids || null
        approvedImage.timestamp = moment().unix()
        approvedImage.isDesign = userType === 11 ? 1 : 2;
        let upload = await Request.addRequestAttachments(approvedImage)
        // When request originated from design order, copy existing attachment images (edited pages) to new attachment; else convert PDF if needed
        const savedImages = design_order_id && (row.id != null) ? existingRequestAttachmentImagesMap[row.id] : null;
        if (savedImages && savedImages.length > 0) {
          const timestamp = moment().unix();
          for (const img of savedImages) {
            await addRequestAttachmentImage({
              document_id: upload,
              document_source: 'request',
              image_url: img.image_url,
              page_number: img.page_number,
              file_size: img.file_size != null ? img.file_size : 0,
              timestamp: timestamp,
              status: 1
            });
          }
          console.log(`editRequest: copied ${savedImages.length} attachment images to request attachment ${upload} (from design order)`);
        } else {
          // const isPdfFile =
          //   approvedImage.file_type === 'application/pdf' ||
          //   approvedImage.file_type === 'pdf' ||
          //   approvedImage.extension === 'pdf';

          // if (isPdfFile && upload) {
          //   try {
          //     await convertPdfToPngHelper(upload);
          //     console.log(`PDF conversion completed for document ID: ${upload}`);
          //   } catch (conversionError) {
          //     console.error(
          //       `Error converting PDF for document ${upload}:`,
          //       conversionError
          //     );
          //   }
          // }
        }

      }
    }

    emitProgress('processing_attachments_complete', `Attachments processed successfully`, 25);
    
    emitProgress('fetching_attachments', `Fetching existing attachments...`, 26);
    
    let attachments = await RequestPartner.fetchAllRequestAttachments(request_id);
    let movedToProduction = false;
let allCreatedTask = []
    // if (proj_status === 3) {
    //   // deleteing installlations from installation table
    //   //  allCreatedTask = await ProjectTask.getAllProjectTasks(projectId, userType = 1, userId = 1)
    //   await Installation.updateRequestsToInProduction(request_id);
    //   movedToProduction = true; 
    // }
    
  
    let currentTime = moment().unix()
    const partnerData = values.clientsData
    let partnerIds = await RequestPartner.fetchPartners(request_id);
        console.log('partnerIds-----', partnerIds)
        if (partnerData && partnerData.length > 0 && partnerIds.length <= 0) {
      console.log("###########partnerData--------");
      let client = {}
      let insertedstatus = null
      for (let eachPartner of partnerData) {

        client['request_id'] = request_id || result[0]?.id,
          client['last_seen'] = currentDate,
          client['timestamp'] = currentDate,
          client['added_by'] = userId,
          client['partner_id'] = eachPartner.id
        client['accept_status'] = 1;
        client['status'] = 1
        insertedstatus = await RequestPartner.addRequestPartners(client)
        console.log("###########partnerData--------",insertedstatus);
      }
    }
    const skills = values.skills?.map(row => row.id)
    const workflow = values.workflow?.map(row => row.id)
    let submission_deadline = moment(values.start).unix()
    let participation_ends = moment(values.end).unix()
    emitProgress('fetching_request_data', `Fetching existing request data...`, 28);
    
    let result = await Request.getAllRequests(request_id, userId, userType)
    console.log("resultresultresult",result)
    let isNoCharge =  values?.project_category ? (values?.project_category["id"] == 13 ? true : false) : false;
    result = result.data
    let orderId = values.order_id
    if(isFromCreateEstimate){
      requestdata['is_create_estimate'] = isFromCreateEstimate || 1
    }
    requestdata['name'] = values.name,
    requestdata['description'] = values.description,
    requestdata['po_number'] = values.po_number || null,
    requestdata['markup_type'] = values.markup_type.id || 2,
    requestdata['services'] = skills,
    requestdata['workflows'] = workflow,
    requestdata['labour_cost_services'] = values.services,
    requestdata['labour_cost'] = values.labour_cost || 0,
    requestdata['project_type'] = values.project_type && values.project_type["id"]||0
    requestdata['sign_agreement'] = values.sign_agreement ? values.sign_agreement.map(item => item.id).join(',') : null,
    requestdata['project_category'] = values.project_category && values.project_category["id"] || 0;
    requestdata['no_charge_type'] = values.no_charge_type ? values.no_charge_type["id"] : null
    requestdata['community'] =  values.community&&values.community["id"]||0
    requestdata['map_service'] = values.map_service && values.map_service["id"] || 0
    requestdata['submission_deadline'] = values.start
    requestdata['is_location'] = allLocationSame
    requestdata['is_service_location'] = allServiceLocationSame || 0
    requestdata['is_rfp_service_location'] = allRfpServiceLocationSame || 0
    requestdata['is_rfp_product_location'] = allRfpProductsLocationSame || 0
    requestdata['catalog_type']=values?.catalog_type?.id||null
    requestdata['isFromEstimate']=values?.isFromEstimate||0
    requestdata['is_split_order'] = values?.is_split ? 1 : 0
    requestdata['show_install_price'] = values.show_install_price == 0 ? 0 : 1;
    requestdata['bill_to'] = values?.bill_to || null;
    requestdata['ship_to'] = values?.ship_to || null;
    if(values?.project_category?.id){    
    // requestdata['isReview'] = values?.project_category["id"] == 12 ? 2 : 0
    requestdata['isReview'] = (values?.project_category["id"] == 12  && result[0]?.allInstallationCompleted == 1 ) ? 2 : 0
    }
    const facilityTypeIds =
    Array.isArray(values?.facility_type)
      ? values.facility_type.map(it => it?.id).filter(Boolean).join(',')
      : (values?.facility_type?.id ?? null);
  requestdata['facility_type'] = facilityTypeIds || null;

    // requestdata['isReview'] = values?.project_category["id"] == 12 ? 2 : 0
    // requestdata['is_single_card'] = values?.is_single_card == false ? 0 :1;
    requestdata['notes']=values.notes||"",
    console.log('🔍 [EDIT] noChargeReason received:', noChargeReason);
    requestdata['no_charge_reason'] = noChargeReason || "",
    console.log('🔍 [EDIT] requestdata no_charge_reason:', requestdata['no_charge_reason']);
    requestdata['quotation']=quotation||0
    requestdata['is_draft'] = saveAsDraft ? 1 : 0
    requestdata['new_order'] = new_order !== undefined ? new_order : (result[0]?.new_order || 0)
    requestdata['sales_rep_ids'] = all_sales_rep_ids || null
    requestdata['sales_rep_names'] = sales_rep_names || null
    const collaboratorsStr = collaborators != null
    ? (Array.isArray(collaborators) ? collaborators.filter(Boolean).join(',') : String(collaborators).trim())
    : null
  requestdata['collaborators'] = collaboratorsStr || null
    if (selectedEstimators && selectedEstimators?.length > 0) {
      const newEstimatorsId = selectedEstimators?.map(d => d.id)?.join(',') || null;
      requestdata['estimator_ids'] = newEstimatorsId || null;
    }
    if (values.end !== (undefined || null)) {
      requestdata['participation_ends'] = values.end
    } else {
      requestdata['participation_ends'] = 0
    }
    requestdata['installation_start_date'] = values.installation_start_date || null
    requestdata['installation_end_date'] = values.installation_end_date || null
    requestdata['start_date'] = null
    // requestdata['subject']=values.subject,
    // requestdata['email_content']=values.email_content
    if (values.welcome_message == true) {
      requestdata['welcome_message'] = values.welcome_message
      if (values.subject !== (undefined || null)) {
        requestdata['subject'] = values.subject
      } else {
        requestdata['subject'] = null
      }
      if (values.email_content !== (undefined || null)) {
        requestdata['email_content'] = values.email_content
      } else {
        requestdata['email_content'] = null
      }
    } else {
      requestdata['welcome_message'] = false
    }
    requestdata['type'] = 2
    const hasOtherFacilityProducts =
      Array.isArray(values?.products) &&
      values.products.some(
        (product) =>
          product?.facility_id != null &&
          String(product.facility_id) !== String(FACILITY_ID)
      );
    const hasOtherFacilityServices =
      Array.isArray(values?.selectedServices) &&
      values.selectedServices.some(
        (service) =>
          service?.facility_id != null &&
          String(service.facility_id) !== String(FACILITY_ID)
      );
    if (values.draft == true || saveAsDraft) {
      requestdata['status'] = values.draft || 8
      if (saveAsDraft && quotation === 1) requestdata['quotation_status'] = 1
    } else {
      // requestdata['status'] = 2
      requestdata['status'] =  isFinalInvoiceEdit ? 4 : (isNoCharge && !hasOtherFacilityProducts && !hasOtherFacilityServices) ? 3 : ((values?.client_approve == false && !hasOtherFacilityProducts && !hasOtherFacilityServices) ? 3 : 2);

      if(isNoCharge || values?.client_approve == false){
        requestdata['reason'] = null
        requestdata['reject_file'] = null
        requestdata['reject_file_type'] = null  
      }
      // if(isNoCharge || values?.clients?.billing_type  ){
        requestdata['min_payment_status'] = isNoCharge ? 1 : values?.clients?.billing_type == 0 ? 1 : 0;
      // }
      requestdata['client_approve'] = values?.client_approve == false ? 0 :1;
    }
    
    let isRequestEstimate = result[0]?.is_request_estimate;
    if(!empty(quotation) && quotation===1){
      requestdata['status'] = 8
      requestdata['quotation_status'] = saveAsDraft ? 1 : isFromCreateEstimate ? 3 : (isHavingProductRows || isHavingServiceRows || isUpdateRequestFlow) ? 3 : isFromFinalReview ? (values?.client_approve == false ? 4 : 9) : ((userType === 3) ? 2 : (isRequestEstimate ? 3 : 2))
      requestdata['client_approve'] = values?.client_approve == false ? 0 :1;
      requestdata['send_to_client'] = (isHavingProductRows || isHavingServiceRows || isUpdateRequestFlow) ? 0 : isFromFinalReview ? 1 : 0 // userType !== 3 ? 0 : 1;
    }
    requestdata['edited_by'] = userId,
    // requestdata['added_by'] = userId,
    requestdata['timestamp'] = currentTime
    requestdata['noServiceNoTask'] = noServiceNoTask ? 1 : 0
    if(values?.installationStatus == 2){
      requestdata['edit_reason'] = values?.edit_reason  ? values?.edit_reason : null
    }

    // if (!empty(uploaded)) {
    //   let deleteAllAtachment = await Request.deleteRequestAttachments(request_id)
    //   for (const row of uploaded) {
    //     if(row?.file_type?.startsWith('image/')){
    //       requestdata['thumbnail'] = row.url
    //     }
    //   }
    // }
    let quickBooksError = null;

    console.log('resultresultresult',result)
        if (proj_status === 3 && result[0]?.allInstallationCompleted !==1) {
      // deleteing installlations from installation table
      //  allCreatedTask = await ProjectTask.getAllProjectTasks(projectId, userType = 1, userId = 1)
      await Installation.updateRequestsToInProduction(request_id);
      movedToProduction = true; 
    }

    // let isNoCharge =  values?.project_category ? (values?.project_category["id"] == 13 ? true : false) : false;
    let negativeAmount = isNoCharge?  -1 : 1

    let totalDiscountForNoCharge = (parseFloat(parseFloat(values.total).toFixed(2)) + parseFloat(parseFloat(values.taxAmount).toFixed(2))).toFixed(2)

    emitProgress('preparing_avalara_data', `Preparing tax calculation data...`, 30);
    
    let labourCostServices = await Product.getAllLabourCostServices();
    labourCostServices = Array.isArray(labourCostServices) ? labourCostServices : [];
    if (values?.project_category?.name === 'Time & Material') {
      values.selectedServices = values.selectedServices || [];
      const has10 = values.selectedServices.some(s => +s.id === 10 || +s.labour_cost_service_id === 10);

      if (!has10) {
        const svc10 = labourCostServices.find(s => +s.id === 10);
        if (svc10) {
          values.selectedServices.push({
            id: 10,
            labour_cost_service_id: 10,
            name: svc10.name,
            quantity: 0,
            unit_cost: Number(svc10.total_cost ?? svc10.cost ?? 0),
            cost: 0,
            total_cost: 0,
            makeAsTask: 0,
            makeInstallCard: 0,
            product_id: null,
            recipe_id: null,
            indexId: Date.now(),
            productIndex: 0,
            addresses: []
          });
        }
      }
    }
    let productDataFromDB = [];

    const checkAndLogProducts = async (values) => {
      console.log("+++++++++", values);
      
      // Check if values.products exists and is an array
      if (!Array.isArray(values.products)) {
        console.error('Error: values.products is not an array or is undefined.');
        return; // Exit the function if it's not an array
      }
    
      // Loop through each product in the array
      for (const product of values.products) {
        try {
          // Log the entire product object for clarity
          console.log('Checking Product:', product);
    
          let name =product.name? product.name :product.product_name
          let productNameExist = await Product.findProductByName(name);
    
          // If the product exists, store it in the productDataFromDB array
          if (productNameExist) {
            // console.log('Product found in database:', productNameExist.dataValues);
            // Push the product data into productDataFromDB array
            productDataFromDB.push(productNameExist.dataValues);
          } else {
            console.log(`Product with name "${product.name}" not found in the database.`);
          }
        } catch (error) {
          console.error('Error while searching for product:', product.name, error);
        }
      }
    
      // Log the final array of products found in the database
      // console.log('All products found in the database:', productDataFromDB);
    };
    
    const resultofdbproduct = await checkAndLogProducts({ products: values.products })

    let productsData = [];
    let materialsData = [];
    let servicesData = []
	  let rfpServicesData =[]
	  let rfpProductsData =[]
	  let rfpMaterialData =[]
    // const productsData = (productDataFromDB.length > 0 ? productDataFromDB : values.products || []).filter(item => item.syncWithQuickbooks === 0 || item.syncWithQuickbooks === null);
     productsData = (values.products || []).filter(item => item.syncWithQuickbooks === 0 || item.syncWithQuickbooks === null);

 
       
    const findByIdAndFilter = async (rfpServices) => {
      const rfpServicesData = await Promise.all(
        (rfpServices || []).map(async (service) => {
          let serviceData = null;
          if(service?.isAdditionalProduct){
            const name =service.name
            const result = await Product.findServiceByName(name);
            if (!result) return null;
            serviceData = [result.dataValues];
          }else{
            const id =service.labour_cost_service_id?service.labour_cost_service_id :service.id
            serviceData = await Product.getLabourCostServicesById(id);
          }
       
          if (serviceData?.[0]?.syncWithQuickbooks == 0 || serviceData?.[0]?.syncWithQuickbooks == null) {
            return serviceData; // Keep the service if syncWithQuickbooks in serviceData is 0
          }
          return null; // Discard the service if syncWithQuickbooks is not 0
        })
      );
    
      // Filter out null values from the final result
      return rfpServicesData.filter(service => service !== null);
    };

    const findByIdAndFilterProducts = async (rfpProducts) => {
      const rfpProductsData = await Promise.all(
        (rfpProducts || []).map(async (product) => {
          let productData = null
          if(product?.isAdditionalProduct){
            const name = product.name || product.product_name
            const result = await Product.findProductByName(name);
            if (!result) return null;
            productData = [result.dataValues];
          }else{
            productData = await Product.getProductById(product.product_id);
          }
          
          // Check if the first productData entry has syncWithQuickbooks as 0 or null
          if (productData?.[0]?.syncWithQuickbooks === 0 || productData?.[0]?.syncWithQuickbooks === null) {
            return productData; // Keep the product if syncWithQuickbooks in productData is 0
          }
          return null; // Discard the product if syncWithQuickbooks is not 0
        })
      );
    

      return rfpProductsData.filter(product => product !== null);
    };


    const products = Array.isArray(values?.products) ? values.products : [];
    const productWithInstallPriceData = []

    products.forEach(catalog => {
  // 1. Main product data
  productWithInstallPriceData.push({
    ...catalog, 
  });

  // 2. Install price as additional line
  if (catalog.install_cost && catalog.install_cost > 0) {
    productWithInstallPriceData.push({
      name: 'Install Price',
      taxCode: 'SI026666',
      actual_retail_price: catalog.install_cost,
      quantity: ( catalog?.quantity  || 1)*(catalog?.locationQuantity || 1)
    });
  }
});

    
      const avalaraData ={
      name: values.name,
      description: values.description,
      clientDetails :values?.clients,
      catalogs : productWithInstallPriceData,   //values?.products, 
      services :values?.additionalSelectedServices,
      materials : materials,
      rfpProducts : rfpProducts,            //.filter(val => val.material ==1),    // rfpProducts,
      rfpServices : rfpServices,
      produtFormulaMaterial:[], //produtFormulaMaterial,
      discount:isNoCharge? 0 : Number(values.invoice_discount)  ||0,
      isNoCharge : isNoCharge,
       projectData:values?.community
    }
    
    emitProgress('processing_avalara', `Processing tax calculations with Avalara...`, 32);
    
    let avalaraTaxAmount = 0
    // no need avalara calculation anymore because in QB production account there is tax from avalara as a line of item
  //   if(avalara_code && ((values?.selectedServices?.length > 0) || (materials?.length > 0) || (rfpServices?.length > 0) || (rfpProducts?.length > 0))){
  // const updatedRequestToAvalara = await updateAvalaraTransaction(avalaraData,avalara_code);
  //  console.log('✅ Updated Avalara Transaction:', updatedRequestToAvalara);
    
  //  requestdata['avalara_transaction_id'] = updatedRequestToAvalara?.avalara_transaction_id || null
  //  avalaraTaxAmount = updatedRequestToAvalara?.totalTax || 0
  //   }else{

  //     // const addRequestToAvalata = await createAvalaraEstimate(avalaraData)

  //     let addRequestToAvalata = {};
  //   try {
  //     if (
  //       userType !== 3 &&
  //       (
  //          (values?.products?.length > 0) ||
  //         (values?.additionalSelectedServices?.length > 0) ||
  //         (materials?.length > 0) ||
  //         (rfpServices?.length > 0) ||
  //         (rfpProducts?.length > 0)
  //       )
  //     ) {
  //       emitProgress('creating_avalara_estimate', `Creating new Avalara estimate...`, 34);
  //       addRequestToAvalata = await createAvalaraEstimate(avalaraData);
  //       emitProgress('avalara_estimate_created', `Avalara estimate created successfully`, 36);
  //     } else {
  //       emitProgress('skipping_avalara', `Skipping Avalara: No sellable line items`, 34);
  //       console.warn("⚠️ Skipping Avalara call: No sellable line items or userType === 3.");
  //     }
  //   } catch (e) {
  //     emitProgress('avalara_error', `Avalara error: ${e.message}`, 34);
  //     console.warn("⚠️ Avalara error skipped:", e.message);
  //   } // comment this line if there cause any issue and uncommented above one and the catalogs : values?.products || [], this line beacuse it is also fixed with that line

  //   console.log('addRequestToAvalata', addRequestToAvalata)
  //   // return
  //   requestdata['avalara_transaction_id'] = addRequestToAvalata?.avalara_transaction_id || null
  //   requestdata['avalara_code'] = Number(addRequestToAvalata?.avalara_code) || null
 
  //   //  avalaraTaxAmount = addRequestToAvalata?.totalTax || 0
  //    if(requestdata.avalara_code){
  //      avalaraTaxAmount = addRequestToAvalata?.totalTax || 0
  //   }else{
  //     avalaraTaxAmount = isNoCharge? 0 : values.taxAmount ||  0
  //   }
  //   }
    
    emitProgress('preparing_quickbooks_data', `Preparing QuickBooks integration data...`, 38);
    
    let previosProductTotal = 0
    let previosServiceTotal = 0
    let previosJobCostTotal = 0

    if (empty(quotation) && values?.project_category?.id ==  12) {
      // let projectId = await Project.projectIdFromRequestId(request_id)
      // let projectData = await Project.getProjectById(projectId)
     let joCostData = await Request.getJobCostDetailsForMaterials(request_id) || {};

      previosProductTotal = parseFloat(Number(joCostData?.productTotal) || 0).toFixed(2) 
      previosServiceTotal = parseFloat(Number(joCostData?.serviceTotal) || 0).toFixed(2) 
      previosJobCostTotal = parseFloat(Number(previosProductTotal) + Number(previosServiceTotal) || 0).toFixed(2);
    }


// const rfpServicesData = await findByIdAndFilter(rfpServices);
 rfpServicesData = (rfpServices || []).filter(item => item.syncWithQuickbooks === 0 || item.syncWithQuickbooks === null);
// const rfpProductsData = await findByIdAndFilterProducts(rfpProducts);
 rfpProductsData = (rfpProducts || []).filter(item => ((item.syncWithQuickbooks === 0 || item.syncWithQuickbooks === null) && item.material == 0));
 rfpMaterialData = (rfpProducts || []).filter(item => ((item.syncWithQuickbooks === 0 || item.syncWithQuickbooks === null) && item.material == 1));
// const materialsData = await findByIdAndFilterProducts(materials);
 materialsData = (materials || []).filter(item => item.syncWithQuickbooks === 0 || item.syncWithQuickbooks === null);
// const servicesData =await findByIdAndFilter(quantityDiffData);
// const servicesData =await findByIdAndFilter(values?.additionalSelectedServices);
 servicesData =values?.additionalSelectedServices.filter(value => value.quickbookId === null);



    let customerData = (values?.clients?.syncWithQuickBook === null || values?.clients?.syncWithQuickBook ===  0) ? values.clients : null;

     ////////////////////// verify synced products in quickbooks //////////////////////
    const syncedProductsData = values.products.filter(item => item.syncWithQuickbooks === 1);
     for(const product of syncedProductsData){
       const catalogResultFromQb = await validateInventoryItemsInQbForPo(product,"quickbookId");
       if(catalogResultFromQb?.itemData){
         productsData.push(catalogResultFromQb?.itemData);
         const updateData ={
           syncWithQuickbooks: 0,
           quickbookId: null
         }
         const updateProduct = await Product.updateProductByQbId(product.quickbookId,updateData);
       }
     }

    const syncedMaterialData = materials.filter(item => item.syncWithQuickbooks === 1);
     for(const material of syncedMaterialData){
       const materialResultFromQb = await validateInventoryItemsInQbForPo(material,"quickbookId");
       if(materialResultFromQb?.itemData){
         materialsData.push(materialResultFromQb?.itemData);
         const updateData ={
           syncWithQuickbooks: 0,
           quickbookId: null
         }
         const updateMaterial = await Product.updateProductByQbId(material.quickbookId,updateData);
       }
     }

    const syncedServicesData = values?.additionalSelectedServices.filter(item => item.syncWithQuickbooks === 1);
     for(const service of syncedServicesData){
       const serviceResultFromQb = await validateInventoryItemsInQbForPo(service,"quickbookId");
       if(serviceResultFromQb?.itemData){
         servicesData.push(serviceResultFromQb?.itemData);
         const updateData ={
           syncWithQuickbooks: 0,
           quickbookId: null
         }
         const updateService = await Product.updateServiceByQbId(service.quickbookId,updateData);
       }
     }

    const syncedRfpServicesData = rfpServices.filter(item => item.syncWithQuickbooks === 1);
    for(const service of syncedRfpServicesData){
      const serviceResultFromQb = await validateInventoryItemsInQbForPo(service,"quickbookId");
      if(serviceResultFromQb?.itemData){
        rfpServicesData.push(serviceResultFromQb?.itemData);
         const updateData ={
           syncWithQuickbooks: 0,
           quickbookId: null
         }
         const updateService = await Product.updateServiceByQbId(service.quickbookId,updateData);
      }
    }
    
    const syncedRfpProductsData = rfpProducts.filter(item => item.syncWithQuickbooks === 1 && item.material == 0);
     for(const product of syncedRfpProductsData){
      const productResultFromQb = await validateInventoryItemsInQbForPo(product,"quickbookId");
      if(productResultFromQb?.itemData){
        rfpProductsData.push(productResultFromQb?.itemData);
         const updateData ={
           syncWithQuickbooks: 0,
           quickbookId: null
         }
         const updateProduct = await Product.updateProductByQbId(product.quickbookId,updateData);
      }
     }

     const syncedRfpMaterialData = rfpProducts.filter(item => item.syncWithQuickbooks === 1 && item.material == 1);
     for(const material of syncedRfpMaterialData){
      const materialResultFromQb = await validateInventoryItemsInQbForPo(material,"quickbookId");
      if(materialResultFromQb?.itemData){
        rfpMaterialData.push(materialResultFromQb?.itemData);
         const updateData ={
           syncWithQuickbooks: 0,
           quickbookId: null
         }
         const updateMaterial = await Product.updateProductByQbId(material.quickbookId,updateData);
      }
     }

     if(values.clients?.syncWithQuickBook === 1){
      const validateClient = await validateClientInQb(values.clients.partnerQbId);
      if(validateClient?.removeQbid){
        customerData = values.clients
        const updateData ={
          syncWithQuickBook: 0,
          partnerQbId: null
        }
        const updateClient = await Client.updateVendorByQbId(values.clients.partnerQbId,updateData);
      }
     }
 
     /////////////////////////////////
    

    const productNames = productsData.map(product => product.name).join(', ');
    // const materialNames = materialsData.flat().map(product => product.name).join(', ');
    const materialNames = materialsData.map(product => product.name || product.product_name).join(', ');
    // const servicesNames = servicesData.flat().map(product => product.name).join(', ');
    const servicesNames = servicesData.map(service => service.name || service.service_name).join(', ');
    // const rfpServicesNames = rfpServicesData.flat().map(service => service.name).join(', ');
    const rfpServicesNames = rfpServicesData.map(service => service.name || service.service_name).join(', ');
    // const rfpProductsNames = rfpProductsData.flat().map(service => service.name).join(', ');
    const rfpProductsNames = rfpProductsData.map(product => product.name || product.product_name).join(', ');

    const rfpMaterialNames = rfpMaterialData.map(product => product.name || product.product_name).join(', ');

    let ReasonWithQb = `This ${quotation===1? 'Estimate' : 'Sales Order'} is not synced with QuickBooks because the following are not in QuickBooks.
                ${productsData.length > 0 ? `Catalogs : ${productNames}` : ''}
                ${materialsData.length > 0 ? `Materials : ${materialNames}` : ''}
                ${servicesData.length > 0 ? `Services : ${servicesNames}` : ''}
                ${rfpServicesData.length > 0 ? `Proposal Services : ${rfpServicesNames}` :''}
                ${rfpProductsData.length > 0 ? `Proposal Catalogs : ${rfpProductsNames}` :''}
                ${rfpMaterialData.length > 0 ? `Proposal Materials : ${rfpMaterialNames}` :''}
                ${customerData? `Customer : ${customerData.name}` :''}
                `;

            
console.log('dataaaaaaaaaaaaaaaaaa',ReasonWithQb);


const readyToSync = (productsData.length === 0 && materialsData.length === 0 && servicesData.length === 0 && rfpServicesData.length === 0 && rfpProductsData.length === 0 && rfpMaterialData.length === 0 && !customerData);
console.log('readyToSync',readyToSync);
///////////////////////////////////////////////quickbook/////////////////////////
emitProgress('checking_quickbooks_sync', `Checking QuickBooks synchronization requirements...`, 40);

if (
  // productsData.length === 0 ||
  // materialsData.length === 0 ||
  // servicesData.length === 0 ||
  // rfpServicesData.length === 0 ||
  // rfpProductsData.length === 0 ||
  // !customerData
  readyToSync
) {
try {
  emitProgress('processing_quickbooks', `Processing QuickBooks integration...`, 42);
  let clientName = partnerData[0]['quickbookCustomerName']
// let customerId =  0;
let customerId =partnerData[0]?.partnerQbId 
const products = values.products || [];
const additionalMaterials = materials || [];
const selectedServices = values.selectedServices || [];

let installPriceQbId = null;
let findInstallPriceQbId = await findItemByName('Install Price');
if(findInstallPriceQbId && findInstallPriceQbId.Id){
  installPriceQbId = findInstallPriceQbId.Id;
}

let productItems
let TotalDiscount =0
if(values.editReq){
   productItems = await Promise.all(
    products.map(async product => {
      let name =product.name? product.name :product.product_name
      
       let productId =  product?.quickbookId                
      

       let  Quantity = (product.quantity * product.locationQuantity) || 1
       let unitPrice = product?.actual_retail_price || product?.retail_price || 0
      // let price = product.markup_type===2 ? product.retail_price :product.price
      let price = product?.isAdditionalProduct ? parseFloat(product?.price)?.toFixed(2) || 0 : parseFloat(unitPrice)?.toFixed(2) || 0
      // TotalDiscount +=Number(product.discount)
      if(!productId){
        let productNameExist = await Product.findProductByName(name);
        if (productNameExist) {
          productId = productNameExist.dataValues.quickbookId;
        }
      }
       const productData = {
         itemId: productId,
         name: name,
         // amount:product.price * Quantity,
         amount: isNoCharge? 0 : price * Quantity,
         quantity: Quantity,
         unitPrice:isNoCharge? 0 : price,
       };
           // Check if product has an install cost
    if (product?.install_cost && product?.install_cost > 0) {
      const installPriceData = {
        itemId: installPriceQbId,  // Assuming `installPriceQbId` is defined elsewhere
        name: "Install Price",
        amount: isNoCharge ? 0 : product?.install_cost * product?.install_quantity,
        quantity: product?.install_quantity,
        unitPrice: isNoCharge ? 0 : product?.install_cost,
      };

      // Return both rows (product and install price)
      return [productData, installPriceData];
    } else {
      // Return only the base product data if no install cost
      return [productData];
    }

     })
   );
}else{

   productItems = await Promise.all(
       products.map(async product => {
          let productId =product?.quickbookId          
          let name =product.name? product.name :product.product_name
          
          let  Quantity = product.quantity * product.locationQuantity
        //  let price = product.markup_type===2 ? product.retail_price :product.price
         let price = product?.isAdditionalProduct ? parseFloat(product?.price)?.toFixed(2) || 0 : parseFloat(product?.SPMA)?.toFixed(2) || 0
         if(!productId){
          let productNameExist = await Product.findProductByName(name);
          if (productNameExist) {
            productId = productNameExist.dataValues.quickbookId;
          }
        }

         return {
            itemId: productId,
            name: name,
            // amount:product.markup_type===2 ? product.retail_price * Quantity :product.price * Quantity,
            amount: isNoCharge? 0 : price * Quantity,
            // amount:price * Quantity,
            quantity:Quantity,
            unitPrice:isNoCharge? 0 : price,
          };
        })
      );
 
}
productItems = productItems.flat();
console.log('productItemsproductItems',productItems);
   
  
      const additionalMaterialItems = await Promise.all(
        additionalMaterials.map(async material => {
          let name =material.product_name?material.product_name:material.name
          let materialId =material.quickbookId    

          let Quantity = material.quantity ? Number(material.quantity) : 1   
          let amount = (material.markUpCost * Quantity).toFixed(2);
          // console.log("materialData",material);
           if(!materialId){
            let productNameExist = await Product.findProductByName(name);
            if (productNameExist) {
              materialId = productNameExist.dataValues.quickbookId;
            }
          }
          return {
            itemId: materialId,
            name: name,
            amount: isNoCharge? 0 : Number(amount),
            quantity: Quantity > 0 ? Quantity : 1, // Ensure quantity is greater than zero
            unitPrice: isNoCharge? 0 : material.markUpCost,
          };
        })
      );

     
  
      if (values.additionalSelectedServices && values.additionalSelectedServices.length > 0) {
        emitProgress('processing_additional_services', `Processing ${values.additionalSelectedServices.length} additional services for QuickBooks...`, 44);
      }
      
      const serviceItems = await Promise.all(
        // selectedServices
          // .filter(service => service.quantityDifference !== 0)  // Filter out services with quantityDifference == 0
          values.additionalSelectedServices.map(async service => {
            let serviceId =  service?.quickbookId             

            let quantity = Number(service.quantity) || 1;
            const theAmount = service.unit_cost?service.unit_cost :service.total_cost
            const amount = theAmount * service.quantityDifference
            
            if(!serviceId){
              let serviceNameExist = await Product.findServiceByName(service.name);
              if (serviceNameExist) {
                serviceId = serviceNameExist.dataValues.quickbookId;
              }
            }
            
            return {
              itemId: serviceId,
              name: service.name,
              // amount: service.unit_cost * service.quantityDifference,
              amount: isNoCharge? 0 : Number(amount),
              quantity: service.quantityDifference,  // Ensure quantity is greater than zero
              unitPrice: isNoCharge? 0 : service.unit_cost?service.unit_cost :service.total_cost,
            };
          })
      );
      

    
      const rfpServiceItems = await Promise.all(
        rfpServices.map(async service => {
          let serviceId = service?.quickbookId       
          let price= service.originalMarkup/100 * service.unit_cost + service.unit_cost

          if(!serviceId){
            let serviceNameExist = await Product.findServiceByName(service.name);
            if (serviceNameExist) {
              serviceId = serviceNameExist.dataValues.quickbookId;
            }
          }
          return {
            itemId: serviceId,
            name: service.name,
            amount:isNoCharge? 0 : price * service.quantity,
            // quantity: service.qty >= 0 ? service.qty : 1, // Ensure quantity is greater than zero
            quantity: service.quantity, // Ensure quantity is greater than zero
            unitPrice: isNoCharge? 0 : price,
          };
        })
      );



      const rfpProductItems = await Promise.all(
        rfpProducts.map(async product => {
          let productId =product?.quickbookId    

          // let price = values.markup_type===1? product.product_retail_price  :  product.product_markup / 100 * product.product_price+product.price
          let priceofProuduct =   product.product_markup / 100 * product.product_price +product.product_price
        let quantity =product.quantity * product.location_quantity

        if(!productId){
          let productNameExist = await Product.findProductByName(product.name);
          if (productNameExist) {
            productId = productNameExist.dataValues.quickbookId;
          }
        }

          return {
            itemId: productId,
            name: product.product_name,
            amount: isNoCharge? 0 : priceofProuduct * quantity  ,
            quantity: quantity || 1, // Ensure quantity is greater than zero
            unitPrice: isNoCharge? 0 : priceofProuduct,
          };
        })
      );
      let pOIds = values?.community?.poNumber?.split(',');
      let latestPoId = pOIds ? pOIds[pOIds.length - 1] : null; 
      let projectPoDetails = await Community.getPoDetailsById(latestPoId);
      console.log('projectPoDetails',projectPoDetails);
      let projectPoNumber = projectPoDetails.length > 0 ? projectPoDetails[0].po_number : null;
      
  
      const requestData = {
        name: values.name,
        description: values.description,
        notes: values.description,
       customerId: customerId,
        items: [...productItems, ...additionalMaterialItems, ...serviceItems, ...rfpServiceItems, ...rfpProductItems],
        note: 'description note',
        discount:isNoCharge? 0 : Number(values.invoice_discount) + Number(TotalDiscount) || 0,
        jobType:values?.project_type?.name || "nill",
        // taxAmount :values.taxAmount ||  0,
        taxAmount :avalaraTaxAmount,  // isNoCharge? 0 : values.taxAmount ||  0,
        customGrandTotal :isNoCharge? (totalDiscountForNoCharge * negativeAmount) :  0,
        timestamp : currentTime,
        jobClass:values?.project_type?.quickBookId || null,
        projectName:values?.community?.name || null,
        poNumber:values.po_number || null,
        projectPoNumber:projectPoNumber ||null,
        clientData :values?.clients || null,
        ProjectData :values?.community || null,
         salesRepName :salesRepName ||values?.sales_rep_names || "",
         bill_to:values?.bill_to || '',
         ship_to:values?.ship_to || '',
      };

  
    console.log("requestData salesRepName",requestData);
    



      emitProgress('syncing_quickbooks', `Syncing with QuickBooks...`, 45);
      
      if(quotation===1){
        if(values.estimate_id){
          emitProgress('updating_quickbooks_estimate', `Updating QuickBooks estimate...`, 47);
          let quickbookEstimateId =  await editEstimateInQuickBooks(requestData,values.estimate_id)
          doc_number = quickbookEstimateId.Estimate.DocNumber || doc_number;
          emitProgress('quickbooks_estimate_updated', `QuickBooks estimate updated successfully`, 49);
        }else{
          emitProgress('creating_quickbooks_estimate', `Creating new QuickBooks estimate...`, 47);
          let quickbookEstimateId =  await addEstimateToQuickBooks(requestData)
          doc_number = quickbookEstimateId.Estimate.DocNumber || doc_number;
          emitProgress('quickbooks_estimate_created', `QuickBooks estimate created successfully`, 49);
        }
      }else {
        if(values.estimate_id){
        // let quickbookInvoiceId = await editInvoiceInQuickBooks(requestData,values.estimate_id)
        emitProgress('updating_quickbooks_sales_order', `Updating QuickBooks sales order...`, 47);
        let quickbookEstimateId =  await editEstimateInQuickBooks(requestData,values.estimate_id)
        doc_number = doc_number;
        emitProgress('quickbooks_sales_order_updated', `QuickBooks sales order updated successfully`, 49);
      }else{
    // let quickbookInvoiceId = await addInvoiceToQuickBooks(requestData)
    emitProgress('creating_quickbooks_sales_order', `Creating new QuickBooks sales order...`, 47);
    let quickbookEstimateId =  await addEstimateToQuickBooks(requestData)

    doc_number =  doc_number;       //quickbookInvoiceId.Invoice.DocNumber ||
    emitProgress('quickbooks_sales_order_created', `QuickBooks sales order created successfully`, 49);
  }
        
      }

    }catch (quickBooksError) {
      emitProgress('quickbooks_error', `QuickBooks sync failed - continuing without sync...`, 45);
      console.log("Error during QuickBooks integration:", quickBooksError);
      quickBooksError = quickBooksError.message ;
    }
  }else{
    emitProgress('quickbooks_error', `QuickBooks sync failed - continuing without sync...`, 45);
  }
 
// if(UniqueEstimateId != null){
//   requestdata['estimate_id'] = UniqueEstimateId
//   requestdata['syncWithQuickbooks'] = 1
//   requestdata['reasonWithQb'] = null;
// }else{

//   if (productsData.length === 0 && materialsData.length === 0 && servicesData.length === 0 && rfpServicesData.length===0 && rfpProductsData.length===0 && !customerData) {
//     requestdata['reasonWithQb'] = null;
//     ReasonWithQb=null
// } else {
//   requestdata['reasonWithQb'] = ReasonWithQb;
// }
// }
if(doc_number){
  requestdata['doc_number'] = doc_number
  requestdata['est_doc_number'] = doc_number
}
if(UniqueEstimateId != null){
  requestdata['estimate_id'] =(UniqueEstimateId && doc_number) ? UniqueEstimateId : null
  if(doc_number){
    requestdata['doc_number'] = doc_number
     requestdata['est_doc_number'] = doc_number
  }
  requestdata['syncWithQuickbooks'] = 1
  requestdata['is_production_qb'] = 1
  requestdata['reasonWithQb'] = null;
}else{

if (productsData.length === 0 && materialsData.length === 0 && servicesData.length === 0 && rfpServicesData.length===0 && rfpProductsData.length===0 && !customerData) {
    requestdata['reasonWithQb'] = null;
} else {
  requestdata['reasonWithQb'] = ReasonWithQb;
  requestdata['syncWithQuickbooks'] = 0
  requestdata['estimate_id'] = null


}

}

// requestdata['salesRepName'] = salesRepName || values?.sales_rep_names || "";
 /////////////////////////////////////////////////////////////////////////////////////////////
    
    emitProgress('saving_request', `Updating ${orderType} in database...`, 40);
    let updateReqID = await Request.updateRequets(request_id, requestdata);
    console.log('requestdata23233', requestdata)
    let newMirrorData = { ...requestdata };
    
    if (updateReqID) {
      emitProgress('request_saved', `${orderType} updated successfully in database`, 45);
    } else {
      emitProgress('request_save_error', `Failed to update ${orderType} in database`, 45);
      throw new Error('Failed to update request in database');
    }

    // if(updateReqID && !quotation){
    //   if (!empty(uploaded)) {
    //     for (const row of uploaded) {
    //       let approvedImage = {}
    //       approvedImage.name =row?.name || row?.url;
    //       approvedImage.file_type = row.file_type;
    //       approvedImage.extension = row.extension;
    //       approvedImage.file_size = row.size;
    //       approvedImage.request_id = request_id;
    //       approvedImage.added_by =row.added_by || userId
    //       approvedImage.approved_by = userId
    //       approvedImage.approved = row.approved ||0
    //       approvedImage.location_ids = row.location_ids || null
    //       approvedImage.timestamp = moment().unix()
    //       let upload = await Request.addRequestAttachments(approvedImage)
    //     }
    //   }

    // }
    const routeId = await Routes.getRouteDatabyRequestId(request_id);
    if(values?.project_category?.id !== 14 && values?.client_approve === false && routeId == false){
      let routeData = {};
        routeData['name'] = `route ${currentTime}`;
        routeData['assigned_driver'] = null;
        routeData['timestamp'] = currentTime;
        // routeData['edited_by'] = userId;
        routeData['added_by'] = userId;
        routeData['status'] = 1;
        routeData['assigned_sales_order'] = request_id;
        routeData['assigned'] = 0;
        const randomBytes = crypto.randomBytes(2).toString('hex');
        const u_id = parseInt(randomBytes, 16) % 90000 + 10000;
        routeData['unique_id'] = 'SWS' + u_id;
        let createRouteId = await Routes.createRoute(routeData);
        }
    if (result && result[0]?.map_service) {
      let data = { 
        assigned_sales_order : null
      }
      let updateRoute = await Routes.updateRoute(data,result[0]?.map_service);
    }
    let data1 = { 
      assigned_sales_order : request_id
    }
    let updateRoute1 = await Routes.updateRoute(data1,requestdata.map_service);

    let qbId = values.estimate_id || requestdata.estimate_id;
    console.log('qbId----',qbId);
    // if(qbId){
     
    //   emitProgress('fetching_avalara_tax_amount', `Fetching Avalara tax amount...`, 48);
    //   // console.log("Starting 10-second delay...");
    //   // await delay(15000); // Delay for 15 seconds (10,000 milliseconds)
    //   // console.log("10-second delay complete.");
    //   // avalaraTaxAmount = await fetchTaxAmountFromEstimateById(qbId);
    //   // console.log('avalaraTaxAmount----',avalaraTaxAmount);
    // }
    console.log('values.total---',values.total)
    console.log('values.taxAmount---',values.taxAmount)
     let totalAmount =  ((parseFloat(parseFloat(values.total).toFixed(2)) + parseFloat(parseFloat(avalaraTaxAmount).toFixed(2))) * (negativeAmount || 1)).toFixed(2)
    let orderData = {};
    // orderData['total'] = ((parseFloat(parseFloat(values.total).toFixed(2)) + parseFloat(parseFloat(values.taxAmount).toFixed(2))) * (negativeAmount || 1)).toFixed(2),
    // orderData['grandTotal'] = ((parseFloat(parseFloat(values.total).toFixed(2)) + parseFloat(parseFloat(values.taxAmount).toFixed(2))) * (negativeAmount || 1)).toFixed(2),
     orderData['total'] = totalAmount,
    orderData['grandTotal'] = totalAmount,
    orderData['tax_amount'] = avalaraTaxAmount                 //values.taxAmount;
    orderData['tax_category'] = taxCategory;
    orderData['budget'] = values.budget;
    orderData['extra_discount'] = values.invoice_discount || 0;
    orderData['request_id'] = request_id;
    orderData['timestamp'] = currentTime;
    orderData['sub_total'] = values.sub_total;
    orderData['tax'] = 0;
    orderData['status'] = 1;
    orderData['customer_id'] = (partnerData && partnerData?.length > 0) ? partnerData[0]['id'] : null;
    orderData['customer_name'] = (partnerData && partnerData?.length > 0) ? partnerData[0]['name'] : null;
    orderData['customer_phone'] = (partnerData && partnerData?.length > 0) ? partnerData[0]['phone'] : null;
    orderData['customer_address'] = (partnerData && partnerData?.length > 0) ? partnerData[0]['address'] : null;
    mirrorData.orderData = orderData;
    emitProgress('updating_customer_order', `Updating customer order data...`, 50);
    let updatedOrderId = await CustomerOrder.updateCustomerOrder(orderId, orderData);

    emitProgress('processing_order_data', `Processing order data for ${orderType}...`, 52);
    let requestProduct = {}
    let CustomerProductData = await CustomerOrderProduct?.getCustomerOrderProductsbyId(orderId) || [];
    console.log('............CustomerProductData',CustomerProductData)

    const oldProductsForDelivery = (CustomerProductData || []).filter(p => p.material == 0 && p.rfp == 0);
    const deliveryTypeChange = { changed: false, items: [] };
    if (values?.products?.length > 0 && oldProductsForDelivery.length > 0) {
      for (const row of values.products) {
        let productIdForMatch = row?.product_id ?? row?.id;
        if (row?.isAdditionalProduct) {
          const name = row.product_name || row.name;
          const found = await Product.findProductByName(name);
          if (found) productIdForMatch = found.dataValues.id;
        }
        const oldProduct = oldProductsForDelivery.find(p =>
          (p.product_index == row.indexId || p.product_index == row.product_index) &&
          String(p.product_id || '') === String(productIdForMatch || '')
        );
        if (!oldProduct) continue;
        const oldVal = oldProduct.delivery_type_id != null ? String(oldProduct.delivery_type_id) : '';
        const newVal = row?.delivery_installation?.delivery_type_id != null ? String(row.delivery_installation.delivery_type_id) : '';
        if (oldVal !== newVal) {
          deliveryTypeChange.changed = true;
          let deliveryType = 'Unknown';
          if (newVal === '1' || row?.delivery_installation?.delivery_type_id == 1) deliveryType = 'Installs';
          else if (newVal === '2' || row?.delivery_installation?.delivery_type_id == 2) deliveryType = 'Pickup';
          else if (newVal === '3' || row?.delivery_installation?.delivery_type_id == 3) deliveryType = 'Shipping';
          const itemName = row?.product_notes || row?.name || row?.product_name || 'Product';
          deliveryTypeChange.items.push({ name: itemName, deliveryType, isProduct: true });
        }
      }
    }

    // ✅ FIX: Check if request is in production (status === 4) - preserve existing products/services/materials/rfp
    const isInProduction = result[0]?.status === 4;
    let deletedCustomerProduct;
    let existingProductsMap = {}; // Map to track existing products for matching
    let existingMaterialsMap = {}; // Map to track existing materials for matching
    let existingRfpProductsMap = {}; // Map to track existing RFP products for matching
    let processedProductIds = new Set(); // Track which products we've processed
    let processedMaterialIds = new Set(); // Track which materials we've processed
    let processedRfpProductIds = new Set(); // Track which RFP products we've processed

    if (isInProduction) {
      // In production: Don't delete, we'll update existing products/materials/rfp
      console.log('⚠️ Request is in production (status 4) - preserving existing products/services/materials/rfp');
      deletedCustomerProduct = null;

      // Build map of existing products, materials, and RFP products for matching
      // Only include active items (status != 0) in the maps
      if (CustomerProductData?.length > 0) {
        CustomerProductData.forEach(product => {
          // Only process active items (status != 0)
          if (product.status == 0) return;

          // Regular products (not material, not rfp)
          if (product.material == 0 && product.rfp == 0) {
            const key = `${product.product_id || 'null'}_${product.product_index || 0}`;
            if (!existingProductsMap[key]) {
              existingProductsMap[key] = [];
            }
            existingProductsMap[key].push(product);
          }
          // Materials
          else if (product.material == 1 && product.rfp == 0) {
            const key = `${product.product_id || 'null'}_${product.product_index || 0}`;
            if (!existingMaterialsMap[key]) {
              existingMaterialsMap[key] = [];
            }
            existingMaterialsMap[key].push(product);
          }
          // RFP Products
          else if (product.rfp == 1) {
            const key = `${product.product_id || 'null'}_${product.product_index || 0}`;
            if (!existingRfpProductsMap[key]) {
              existingRfpProductsMap[key] = [];
            }
            existingRfpProductsMap[key].push(product);
          }
        });
      }
    } else {
      // Normal flow: delete and recreate
      deletedCustomerProduct = await CustomerOrderProduct.deletedCustomerOrderProduct(orderId);
    }

    let oldProductIdMap = {}; // Store old product ids with corresponding new ones
    let newProductIdMap = {}; // Store new product ids
    let oldLocationIdMap = {}; // Store old product ids with corresponding new ones
    let newLocationIdMap = {}; // Store new product ids
    let updatedAttachments = []; 
    
    //  CustomerProductData?.forEach(product => {
    //  oldProductIdMap[product.product_id] = product.id; 
    //  });

    // ✅ Fix: Store multiple `id`s for the same `product_id`
    CustomerProductData?.forEach(product => {
      if (!oldProductIdMap[product.product_id]) {
          oldProductIdMap[product.product_id] = []; // Initialize as an array
      }
      oldProductIdMap[product.product_id].push(product.id); // Store multiple IDs
    });

   
    CustomerProductData?.forEach(orderProduct => {
      // console.log('locationData---',orderProduct)
      orderProduct?.location?.forEach(product => {
        if (product.id) {
          if (!oldLocationIdMap[product.id]) {
            oldLocationIdMap[product.id] = []; // Initialize as an array
          }
          oldLocationIdMap[product.id].push(product.id); // Store multiple IDs
        }
      })
    });

    // console.log('oldProductIdMap',oldProductIdMap)
    // console.log('oldLocationIdMap',oldLocationIdMap)



    if (CustomerProductData?.length > 0) {
      for (let row of CustomerProductData) {
        let deleteLocation = await Request.deleteLocation(row.id);
      }
    }
    
    // Delete existing attachments ONCE before processing all products to avoid deleting attachments added in previous iterations
    let deleteExistingAttachments = await Request.deleteExistingAttachments(request_id);
    console.log('deleteExistingAttachments',deleteExistingAttachments)
    
    if (values?.products && values.products.length > 0) {
      emitProgress('processing_products', `Processing ${values.products.length} catalogs for ${orderType}...`, 55);
    }
    for (let row of values?.products) {
      // let product_id =  values?.project_category?.name == 'Time & Material' ? null : (row?.product_id || row?.id || null) ;
      let product_id =  row?.product_id || row?.id || null ;
      let locationData = row?.location || null;
      let product_name = row.product_name ? row.product_name : row.name
      let price = row.product_price ? row.product_price : row.price;
      let ret_price = row.product_retail_price ? row.product_retail_price : row.retail_price;
      let markup = row.product_markup ? row.product_markup : row.markup;
      let product_qty = row.quantity ? row.quantity : 1;
      // let subTotal = row.product_sub_total;
      // let productTotal = row.product_sub_total;
      let rowTotal = parseFloat(row.markup_type == 1 ? price : ret_price || 0);
      console.log('rowTotalrowTotal', rowTotal)
      rowTotal = parseFloat(rowTotal.toFixed(2));
    
    let locationQuantity = row.locationQuantity || 1;
    // let discount = parseFloat(row.discount) || 0;
    let adjustment = parseFloat(row.adjustment) || 0;
    let discount = 0;
    let productQty = product_qty;
    
    // Calculate subtotal and product total
    let subTotal = parseFloat(rowTotal + ((rowTotal * adjustment) / 100))?.toFixed(2);
    subTotal = parseFloat(((subTotal * productQty * locationQuantity) - discount).toFixed(2));
    // subTotal = subTotal + ((subTotal * adjustment) / 100);

    let productTotal = parseFloat(rowTotal + ((rowTotal * adjustment) / 100))?.toFixed(2);
    productTotal = parseFloat(parseFloat((productTotal * productQty * locationQuantity).toFixed(2)) - discount).toFixed(2);
    // productTotal = parseFloat(productTotal) + parseFloat((productTotal * adjustment) / 100);

    let existingProductId = null
    if(row?.isAdditionalProduct){
      let productNameExist = await Product.findProductByName(product_name);
      if (productNameExist) {
        existingProductId = productNameExist.dataValues.id;
      }
    }
    
      requestProduct['product_index'] = row.indexId
      requestProduct['order_id'] = orderId;
      requestProduct['is_product_row'] = row?.is_product_row || 0
      requestProduct['product_id'] = row?.isAdditionalProduct ? existingProductId : product_id || null;
      requestProduct['product_name'] = product_name;
      requestProduct['product_desc'] = row?.product_desc || row.description || null,
      requestProduct['product_notes'] = row?.product_notes || null,
      requestProduct['install_notes'] = row?.install_notes || null,
      requestProduct['service_notes'] = row?.service_notes || null,
      requestProduct['product_price'] = price || 0.00;
      requestProduct['product_retail_price'] = row.markup_type == 1 ? price : ret_price;
      requestProduct['product_markup'] = markup;
      requestProduct['product_qty'] = product_qty;
      requestProduct['location_quantity'] = row.locationQuantity||1,
      requestProduct['product_sub_total'] = subTotal;
      requestProduct['product_tax'] = 0;
      requestProduct['workflow'] = row.workflow,
      requestProduct['product_total'] = productTotal;
      requestProduct['discount'] = row.discount || 0;
      requestProduct['adjustment'] = row.adjustment || 0;
      requestProduct['timestamp'] = currentTime;
      requestProduct['status'] = row.status;
      requestProduct['markAdjType'] =2 // row.markAdjType || 2
      requestProduct['map_service'] = row?.fromMapServices ? 1 : 0
      requestProduct['allLocationSame'] = row?.allLocationSame ? 1 : 0
      requestProduct['isAdditionalProduct'] = row?.isAdditionalProduct ? 1 : 0
      requestProduct['is_single_card'] = row?.is_single_card == false ? 0 :1;
      requestProduct['install_cost'] = row?.install_cost || null
      requestProduct['install_qty'] = row?.install_quantity ?? row?.install_qty ?? 1
      requestProduct['install_adjustment'] = row?.install_adjustment || 0
      requestProduct['install_adj_value_type'] = row?.install_adj_value_type
      requestProduct['installMarkAdjType'] = row?.installMarkAdjType || 2
      requestProduct['facility_id'] = row?.facility_id  || FACILITY_ID
      requestProduct['split_percentage'] =   row?.split_percentage  || 0
      requestProduct['incoming_split_percentage'] = row?.incoming_split_percentage || 0
      requestProduct['expected_formula_price'] = row?.originalExpectedCost || 0
      requestProduct['materialInvId'] = row?.max_price_mi_id || null
      requestProduct['unit_cost'] = row?.actual_retail_price || row?.retail_price || null
      requestProduct['delivery_type_id'] = row?.delivery_installation?.delivery_type_id || null
      requestProduct['installer_facility_id'] = row?.delivery_installation?.installer_facility_id || null
      requestProduct['adj_value_type'] = row?.adj_value_type 
      requestProduct['is_existing_catalog'] = row?.is_existing_catalog ? 1 : 0
      console.log('requestProduct--product', requestProduct)
      console.log("dataaaaaaaaaaaaaaaaaaa",rowTotal, productQty, locationQuantity, discount,adjustment,productTotal,subTotal);
    let clonedRequestProduct = { ...requestProduct };  // Using the spread operator to clone the object
    mirrorData.productData.push(clonedRequestProduct);
      // ✅ FIX: When in production, update existing product instead of creating new
      let requestProductResult = null;
      if (isInProduction) {
        const matchKey = `${product_id || 'null'}_${row.indexId || 0}`;
        const existingProducts = existingProductsMap[matchKey] || [];
        const existingProduct = existingProducts.find(p => !processedProductIds.has(p.id));

        if (existingProduct) {
          // Update existing product - preserves customer_order_product_id and task relationships
          console.log(`✅ Updating existing product ID ${existingProduct.id} (preserving tasks)`);
          requestProduct['timestamp'] = existingProduct.timestamp; // Keep original timestamp
          const updated = await CustomerOrderProduct.updateCustomerOrderProduct(requestProduct, existingProduct.id);
          if (updated) {
            requestProductResult = existingProduct.id; // Keep same ID
            processedProductIds.add(existingProduct.id);

            // Update locations for existing product
            if (CustomerProductData?.length > 0) {
              let deleteLocation = await Request.deleteLocation(existingProduct.id);
            }
          }
        }
      }

      // Create new product if not in production or no existing product found
      if (!requestProductResult) {
        requestProductResult = await CustomerOrderProduct.createCustomerOrder(requestProduct);
      }
      requestProduct['install_cost'] =null
      requestProduct['install_qty'] = 0
      requestProduct['install_adjustment'] = 0
      requestProduct['install_adj_value_type'] = 1
      requestProduct['installMarkAdjType'] = 1
      requestProduct['facility_id'] = null
      requestProduct['split_percentage'] = null
      requestProduct['incoming_split_percentage'] = null
      requestProduct['expected_formula_price'] = null
      requestProduct['materialInvId'] =null
      requestProduct['unit_cost'] = null
      if (requestProductResult) {

        let result = address.filter(raw => (raw.productId === row.id && raw.productIndex === row.indexId));

        // let result = address.filter(raw => (
        //   raw.productId === (row.id ?? existingProductId) &&
        //   raw.productIndex === row.indexId
        // ));
        

        let data = {}
        if (result) {
          for (let i = 0; i < result.length; i++) {
            data.order_id = requestProductResult
            data.product_id = row?.isAdditionalProduct ? existingProductId : result[i].productId || null
            // data.product_id = values?.project_category?.name == 'Time & Material' ? null : (result[i].productId || null)
            data.address = result[i].address
            data.city = result[i].city
            data.state = result[i].state
            data.zip = result[i].zip?.trim()
            data.route_location_id = result[i].route_location_id || null
            data.timestamp = moment().unix()
            // data.edited_by = userId
            data.added_by = userId
            let newLocationId = await Request.addLocation(data)
            
            // Process product attachments for this location
            // Match attachments using product_index_id from payload or row.indexId
            const productIndexId = row?.indexId;
            console.log('productIndexIddd add', productIndexId)
            // Get attachments for this product - check both by indexId key and by product_index_id in attachment payload
            let attachments = productAttachments[productIndexId] || [];
            console.log('attachmentsss add', attachments)
            
            // Also check if attachments have product_index_id field and match it
            if (attachments.length === 0 && productIndexId) {
              // Fallback: search all productAttachments for matching product_index_id
              for (const key in productAttachments) {
                const attArray = productAttachments[key] || [];
                const matchingAttachments = attArray.filter(att => 
                  att.product_index_id === productIndexId || att.productIndexId === productIndexId
                );
                if (matchingAttachments.length > 0) {
                  attachments = matchingAttachments;
                  break;
                }
              }
            }
            console.log('attachments++++++++++++++', attachments)
            console.log('productAttachments++++++++++++++ 2', productAttachments)
            console.log('🔍 [EDIT] Processing attachments for product:', {
              productIndexId,
              attachmentsCount: attachments.length,
              newLocationId,
              requestProductResult,
              allProductAttachmentKeys: Object.keys(productAttachments),
              currentProductAttachments: productAttachments[productIndexId]
            });
            
            if (newLocationId && attachments.length > 0) {
              // Get existing attachments for this request to check for duplicates
              // Only check once per product, not per location, to avoid unnecessary queries
              let existingAttachments = [];
              let existingAttachmentUrls = new Set();
              let existingAttachmentIds = new Set();
              
              // Only fetch existing attachments on first location of this product
              if (i === 0) {
                existingAttachments = await Request.getAllRequestAttachments(request_id);
                existingAttachments.forEach(att => {
                  const attData = att.dataValues || att;
                  if (attData.name) {
                    existingAttachmentUrls.add(attData.name);
                  }
                  if (attData.id) {
                    existingAttachmentIds.add(attData.id);
                  }
                });
              }
              
              // Add attachments for this product/location (only if not already exists)
              let addedCount = 0;
              let skippedCount = 0;
              for (const attachment of attachments) {
                const attachmentUrl = attachment.url || attachment.name;
                const attachmentId = attachment.id;
                
                console.log('📎 [EDIT] Checking attachment:', {
                  id: attachmentId,
                  url: attachmentUrl,
                  file_type: attachment.file_type,
                  productIndexId,
                  product_index_id: attachment.product_index_id,
                  newLocationId,
                  alreadyExistsById: attachmentId ? existingAttachmentIds.has(attachmentId) : false,
                  alreadyExistsByUrl: existingAttachmentUrls.has(attachmentUrl)
                });
                
                // Check if attachment already exists by ID (if provided) or by URL/name
                const existsById = attachmentId && existingAttachmentIds.has(attachmentId);
                const existsByUrl = existingAttachmentUrls.has(attachmentUrl);
                
                if (existsById || existsByUrl) {
                  console.log('⏭️ [EDIT] Attachment already exists, skipping:', {
                    id: attachmentId,
                    url: attachmentUrl,
                    existsById,
                    existsByUrl
                  });
                  skippedCount++;
                  continue;
                }
                
                const attachmentData = {
                  request_id: request_id,
                  name: attachmentUrl,
                  file_type: attachment.file_type,
                  extension: attachment.extension,
                  file_size: attachment.size || attachment.file_size,
                  product_ids: null,
                  location_ids: String(newLocationId),
                  approved: 1,
                  approved_by: userId,
                  status: 1,
                  timestamp: moment().unix(),
                  added_by: userId
                };
                const attachmentResult = await Request.addRequestAttachments(attachmentData);
                console.log('✅ [EDIT] Attachment added successfully:', attachmentResult);
                addedCount++;
                
                // Automatically convert PDF to PNG if it's a PDF
                // const isPdfFile = attachmentData.file_type === 'application/pdf' || attachmentData.file_type === 'pdf' || attachmentData.extension === 'pdf';
                // if (isPdfFile && attachmentResult) {
                //   try {
                //     await convertPdfToPngHelper(attachmentResult);
                //     console.log(`PDF conversion completed for document ID: ${attachmentResult}`);
                //   } catch (conversionError) {
                //     console.error(`Error converting PDF for document ${attachmentResult}:`, conversionError);
                //   }
                // }
                
                // Add to sets to avoid duplicates in same batch
                existingAttachmentUrls.add(attachmentUrl);
                if (attachmentResult && attachmentId) {
                  existingAttachmentIds.add(attachmentId);
                }
              }
              
              console.log('📊 [EDIT] Attachment processing summary:', {
                total: attachments.length,
                added: addedCount,
                skipped: skippedCount
              });
              
              // Update isLinkedAll to 1 for this location
              await Request.updateLocationIsLinkedAll([newLocationId]);
              console.log('✅ [EDIT] Updated isLinkedAll for location:', newLocationId);
            }
            
            // Store in mapping for updating request_attachments & project_task
            let oldLocationId = result[i].req_loc_id || null;
            if (oldLocationId) {
              if (!newLocationIdMap[oldLocationId]) {
                newLocationIdMap[oldLocationId] = [];
              }
              newLocationIdMap[oldLocationId].push(newLocationId);
              // console.log(`🔹 Mapped Old Location ID ${oldLocationId} -> New ID ${newLocationId}`);
            }
          }
        }

        // ✅ FIX: When in production and product was updated, map to itself
        if (isInProduction && processedProductIds.has(requestProductResult)) {
          // Product was updated, so map to itself (no change in ID)
          newProductIdMap[requestProductResult] = requestProductResult;
        } else {
          // New product created, use normal mapping logic
          let oldProductIds = oldProductIdMap[product_id] || [];

          if (oldProductIds.length > 0) {
            oldProductIds.forEach(oldProductId => {
                newProductIdMap[oldProductId] = requestProductResult; // ✅ Update immediately
                // console.log(`🔹 Mapped Old Product ID ${oldProductId} -> New ID ${requestProductResult}`);
            });
          } else {
              console.log(`⚠️ No matching Old Product ID found for product_id: ${product_id}`);
          }
        }

        // console.log("🔹 Old Product ID Map:", oldProductIdMap);
        // console.log("🔹 New Product ID Map:", newProductIdMap);
        // console.log("🔹 Old Location ID Map:", oldLocationIdMap);
        // console.log("🔹 New Location ID Map:", newLocationIdMap);
        // console.log("🔹 Attachments Before Update:", attachments);

        // ********** UPDATED PROJECT TASKS BASED ON NEW PRODUCT IDS **********
        for (const oldProductId in newProductIdMap) {
          if (newProductIdMap.hasOwnProperty(oldProductId)) {
            const newProductId = newProductIdMap[oldProductId];

            const newProjectTaskData = {
              customer_order_product_id: newProductId,
            };

            try {
              const updateProjectTask = await ProjectTask.updateProjectTaskByCustomerOrderProductId(newProjectTaskData, oldProductId);
              // console.log(`✅ Updated ProjectTask: Old ID ${oldProductId} -> New ID ${newProductId}`, updateProjectTask);
            } catch (error) {
              console.error(`❌ Error updating ProjectTask for Old ID ${oldProductId}:`, error);
            }
          }
        }
        // ********** UPDATED PROJECT TASKS BASED ON NEW PRODUCT IDS **********
        for (const oldLocationId in newLocationIdMap) {
          if (newLocationIdMap.hasOwnProperty(oldLocationId)) {
            const newLocId = newLocationIdMap[oldLocationId];

            const newProjectTaskData = {
              request_location_id: newLocId,
            };

            try {
              const updateProjectTask = await ProjectTask.updateProjectTaskByRequestLocationId(newProjectTaskData, oldLocationId);
              // console.log(`✅ Updated ProjectTask: Old ID ${oldLocationId} -> New ID ${newLocId}`, updateProjectTask);
            } catch (error) {
              console.error(`❌ Error updating ProjectTask for Old ID ${oldLocationId}:`, error);
            }
          }
        }
        // ********** END OF UPDATED PROJECT TASK LOGIC **********

        // Track assigned product IDs to prevent duplicates
        let assignedProductIds = new Set(); // Keeps track of used product IDs

        // **Update Attachments with New Product IDs**
        attachments?.forEach(attachment => {
          if (attachment.product_ids || attachment.location_ids) {
              let productIdsArray = attachment.product_ids?.split(",").map(id => id.trim()) || [];
              let locationIdsArray = attachment.location_ids?.split(",").map(id => id.trim()) || [];
              let updatedProductIds = [];
              let updatedLocationIds = [];

              productIdsArray.forEach(prodId => {
                  let oldIds = oldProductIdMap[prodId] || [prodId]; // Get mapped old IDs or itself
                  let newIds = oldIds.map(oldId => newProductIdMap[oldId] || oldId); // Convert each old ID to new ID
                  
                  newIds.forEach(newId => {
                      while (assignedProductIds.has(newId)) { // If already assigned, increment
                          newId++; 
                      }
                      assignedProductIds.add(newId); // Mark ID as assigned
                      updatedProductIds.push(newId);
                  });
              });

              locationIdsArray.forEach(oldLocId => {
                let oldIds = oldLocationIdMap[oldLocId] || [oldLocId];
                let newIds = oldIds.flatMap(oldId => newLocationIdMap[oldId] || [oldId]);
          
                newIds.forEach(newId => {
                  if (!updatedLocationIds.includes(newId)) {
                    updatedLocationIds.push(newId); // Store unique new IDs
                  }
                });
              });

              attachment.product_ids = [...new Set(updatedProductIds)].join(","); // Remove duplicates
              attachment.location_ids = [...new Set(updatedLocationIds)].join(","); // Remove duplicates
              updatedAttachments.push(attachment);
              // console.log(`🔹 Final product_ids for attachment ${attachment.id}:`, attachment.product_ids, attachment.location_ids);
          }
        });
      }
    }

    for (let attachment of updatedAttachments) {
      if (attachment.location_ids && attachment.location_ids.length > 0) { 
          await Request.updateRequestAttachmentsById(attachment.id, { product_ids: null, location_ids: attachment.location_ids });
          // console.log(`✅ Successfully updated attachment ${attachment.id} with location_ids: ${attachment?.location_ids}`);
      } else {
          console.warn(`⚠️ Skipping update for attachment ${attachment.id} due to missing location_ids.`);
      }
    }

    if (rfpProducts && rfpProducts.length > 0) {
      emitProgress('processing_rfp_products', `Processing ${rfpProducts.length} RFP products for ${orderType}...`, 60);
    }
    for (const row of rfpProducts) {
      
      let rowQuantity = row.qty ? row.qty : 1;
      let rowDiscount =  0
      
      row.price=parseFloat((row.product_price+(row.product_price*row.product_markup/100))?.toFixed(2))
      row.retail_price=parseFloat((row.product_price+(row.product_price*row.product_markup/100))?.toFixed(2))
      
      row.product_sub_total=row.price*row.quantity*row.location_quantity
      requestProduct['order_id'] = orderId,
      requestProduct['product_id'] = row.product_id,
      requestProduct['product_name'] = row.product_name,
      requestProduct['product_notes'] = row?.product_notes || null,
      requestProduct['install_notes'] = row?.install_notes || null,
      requestProduct['service_notes'] = row?.service_notes || null,
      requestProduct['product_price'] = row.product_price || 0.00,
      requestProduct['product_retail_price'] = row.retail_price,
      requestProduct['product_markup'] =  row.product_markup || row.markup || 0,
      requestProduct['product_qty'] = row.quantity || 1,
      requestProduct['location_quantity'] = row.locationQuantity||1,
      requestProduct['product_sub_total'] = row.product_sub_total,
      requestProduct['product_tax'] = 0,
      requestProduct['product_total'] = row.product_sub_total,
      requestProduct['workflow'] = row.workflow,
      requestProduct['discount'] = rowDiscount||0,
      requestProduct['timestamp'] = currentTime,
      requestProduct['status'] = row.status
      requestProduct['adjustment'] = 0,
      requestProduct['rfp'] = 1
      requestProduct['material'] = row?.material || 0
      requestProduct['is_single_card'] = row?.is_single_card ? 1 : 0
      requestProduct['allLocationSame'] = row?.allRfpProductsLocationSame ? 1 : 0
      requestProduct['vendor_price'] = (row?.material == 0) ? (row?.vendor_price || 0) : 0
      let clonedRequestProduct = { ...requestProduct };  // This clones the object
      mirrorData.productData.push(clonedRequestProduct);
      // ✅ FIX: When in production, update existing RFP product instead of creating new
      let requestRfpProductResult = null;
      if (isInProduction) {
        const matchKey = `${row.product_id || 'null'}_${row.indexId || 0}`;
        const existingRfpProducts = existingRfpProductsMap[matchKey] || [];
        const existingRfpProduct = existingRfpProducts.find(p => !processedRfpProductIds.has(p.id));

        if (existingRfpProduct) {
          // Update existing RFP product - preserves customer_order_product_id and task relationships
          console.log(`✅ Updating existing RFP product ID ${existingRfpProduct.id} (preserving tasks)`);
          requestProduct['timestamp'] = existingRfpProduct.timestamp; // Keep original timestamp
          const updated = await CustomerOrderProduct.updateCustomerOrderProduct(requestProduct, existingRfpProduct.id);
          if (updated) {
            requestRfpProductResult = existingRfpProduct.id; // Keep same ID
            processedRfpProductIds.add(existingRfpProduct.id);

            // Update locations for existing RFP product
            if (CustomerProductData?.length > 0) {
              let deleteLocation = await Request.deleteLocation(existingRfpProduct.id);
            }
          }
        }
      }

      // Create new RFP product if not in production or no existing RFP product found
      if (!requestRfpProductResult) {
        requestRfpProductResult = await CustomerOrderProduct.createCustomerOrder(requestProduct);
      }

      let response = requestRfpProductResult;
      if (response) {

        let result = row.location
        let data = {}
        if (result) {
          for (let i = 0; i < result.length; i++) {
            data.order_id = response
            data.product_id = row.product_id || null
            data.address = result[i].address?.trim()
            data.city = result[i].city?.trim()
            data.state = result[i].state?.trim()
            data.zip = result[i].zip?.trim()
            data.timestamp = moment().unix()
            // data.edited_by = userId
            data.added_by = userId
            let addLocation = await Request.addLocation(data)
          }

        }

      }
    }
    
    if (materials && materials.length > 0) {
      emitProgress('processing_materials', `Processing ${materials.length} materials for ${orderType}...`, 70);
    }
    for (const row of materials) {
      
      let rowQuantity = row.quantity ? row.quantity : 0;
      let rowDiscount =  0
      row.price=row.price||row.product_price||0
      row.retail_price=row.price||0
      // row.product_sub_total=row.price*rowQuantity
      let rowMarkUpCost = row.markUpCost?.toFixed(2) || 0
      row.product_sub_total=rowMarkUpCost *rowQuantity

      let existingProductId = null
      if(row?.isAdditionalProduct){
        let newName = row.name||row.product_name
        let productNameExist = await Product.findProductByName(newName);
        if (productNameExist) {
          existingProductId = productNameExist.dataValues.id;
        }
      }

      requestProduct['order_id'] = orderId
      requestProduct['product_id'] = row?.isAdditionalProduct ? existingProductId : row.product_id || row.id || null,
      requestProduct['product_name'] = row.name||row.product_name,
      requestProduct['product_notes'] = row?.product_notes || null,
      requestProduct['material_notes'] = row?.material_notes || null,
      requestProduct['install_notes'] = row?.install_notes || null,
      requestProduct['service_notes'] = row?.service_notes || null,
      requestProduct['product_price'] = row.price || 0.00,
      requestProduct['product_retail_price'] =  row.markUpCost 
      requestProduct['product_markup'] = row.markup || 0,
      // requestProduct['product_qty'] = values?.project_category?.name =='Time & Material' ? 0 : row.quantity || 0,
      requestProduct['product_qty'] = row.quantity || 0,
      // requestProduct['location_quantity'] = values?.project_category?.name =='Time & Material' ? 0 : row.quantity,
      requestProduct['location_quantity'] = row.quantity,
      requestProduct['product_sub_total'] = row.product_sub_total,
      requestProduct['product_tax'] = 0,
      requestProduct['product_total'] = row.product_sub_total,
      requestProduct['workflow'] = row.workflow||null,
      requestProduct['discount'] =0,
      requestProduct['timestamp'] = currentTime,
      requestProduct['status'] = 1
      requestProduct['adjustment'] = row.adjustment || 0,
      requestProduct['adj_value_type'] = row.adj_value_type ? 1 : 0,
      requestProduct['markAdjType'] =2 
      requestProduct['rfp'] = 0
      requestProduct['material'] = 1
      requestProduct['stock_type'] =row.stock_type 
      requestProduct['convertion_unit'] = row.stock_type===1?null:row.convertion_unit ?Number(row.convertion_unit):null;
      requestProduct['isAdditionalProduct'] = row?.isAdditionalProduct ? 1 : 0

      // Set product_index for materials (needed for matching)
      requestProduct['product_index'] = row.indexId || row.product_index || 0;
      console.log('requestProduct--material', requestProduct)
      let clonedRequestProduct = { ...requestProduct };  // This clones the object
      mirrorData.materialData.push(clonedRequestProduct);
      console.log('Material matching - productId:', row?.isAdditionalProduct ? existingProductId : row.product_id || row.id || null, 'indexId:', row.indexId, 'product_index:', row.product_index);

      // ✅ FIX: When in production, update existing material instead of creating new
      let requestMaterialResult = null;
      if (isInProduction) {
        let existingMaterial = null;

        // First, try to find by customer_order_product.id if row.id exists (most reliable)
        if (row.id && typeof row.id === 'number') {
          const materialById = CustomerProductData.find(p =>
            p.id === row.id &&
            p.material == 1 &&
            p.rfp == 0 &&
            !processedMaterialIds.has(p.id)
          );
          if (materialById) {
            existingMaterial = materialById;
            console.log(`✅ Found existing material by ID: ${row.id}`);
          }
        }

        // If not found by ID, try matching by product_id and product_index
        if (!existingMaterial) {
          const productId = row?.isAdditionalProduct ? existingProductId : row.product_id || null;
          // Use product_index from row (same as what's stored in DB) for matching
          const materialIndex = row.product_index || row.indexId || 0;
          const matchKey = `${productId || 'null'}_${materialIndex}`;
          console.log('Material matchKey:', matchKey, 'productId:', productId, 'materialIndex:', materialIndex);
          console.log('existingMaterialsMap keys:', Object.keys(existingMaterialsMap));
          const existingMaterials = existingMaterialsMap[matchKey] || [];
          console.log('Found existing materials:', existingMaterials.length, 'for key:', matchKey);
          existingMaterial = existingMaterials.find(m => !processedMaterialIds.has(m.id));
        }

        if (existingMaterial) {
          // Update existing material - preserves customer_order_product_id and task relationships
          console.log(`✅ Updating existing material ID ${existingMaterial.id} (preserving tasks)`);
          console.log(`   Existing material: product_id=${existingMaterial.product_id}, product_index=${existingMaterial.product_index}, id=${existingMaterial.id}`);
          requestProduct['timestamp'] = existingMaterial.timestamp; // Keep original timestamp
          const updated = await CustomerOrderProduct.updateCustomerOrderProduct(requestProduct, existingMaterial.id);
          if (updated) {
            requestMaterialResult = existingMaterial.id; // Keep same ID
            processedMaterialIds.add(existingMaterial.id);
            console.log(`   ✅ Material updated successfully, ID: ${existingMaterial.id}`);

            // Update locations for existing material
            if (CustomerProductData?.length > 0) {
              let deleteLocation = await Request.deleteLocation(existingMaterial.id);
            }
          } else {
            console.log(`   ❌ Failed to update material ID ${existingMaterial.id}`);
          }
        } else {
          console.log(`   ⚠️ No existing material found. Creating new material.`);
          console.log(`   Row data: id=${row.id}, product_id=${row.product_id}, product_index=${row.product_index}, indexId=${row.indexId}`);
        }
      }

      // Create new material if not in production or no existing material found
      if (!requestMaterialResult) {
        requestMaterialResult = await CustomerOrderProduct.createCustomerOrder(requestProduct);
      }

      let response = requestMaterialResult;
      console.log('response-materialssssss', response)
    }

    emitProgress('cleaning_old_data', `Cleaning up old service data...`, 76);
        // Fetch old services before deletion for comparison
    let oldServicesData = await Request.getRequestServices(request_id, false, false, false, result[0]?.project_category) || [];
    
    const oldRegularServices = (oldServicesData || []).filter(s => (s.rfp || 0) === 0);
    const selectedServicesList = values?.selectedServices || [];
    for (const row of selectedServicesList) {
      // For matching: DB uses product_index. Frontend may send product_index (from spread),
      // productIndex (product-linked services), or indexId/newIndexId (overwritten for additional services).
      const rowProductIndex = row?.product_index ?? row?.productIndex ?? row?.indexId ?? row?.newIndexId ?? '';
      const rowServiceId = row?.labour_cost_service_id ?? row?.id ?? '';
      // Use ?? not || for product_index/product_id - 0 and null are valid; || treats 0 as falsy and breaks matching
      const oldService = oldRegularServices.find(s =>
        String(s.labour_cost_service_id ?? '') === String(rowServiceId) &&
        String(s.product_index ?? '') === String(rowProductIndex) &&
        String(s.product_id ?? '') === String(row?.product_id ?? '')
      );
      if (!oldService) continue;
      const oldVal = oldService.delivery_type_id != null ? String(oldService.delivery_type_id) : '';
      const newVal = row?.delivery_installation?.delivery_type_id != null ? String(row.delivery_installation.delivery_type_id) : '';
      if (oldVal !== newVal) {
        deliveryTypeChange.changed = true;
        let deliveryType = 'Unknown';
        if (newVal === '1' || row?.delivery_installation?.delivery_type_id == 1) deliveryType = 'Installs';
        else if (newVal === '2' || row?.delivery_installation?.delivery_type_id == 2) deliveryType = 'Pickup';
        else if (newVal === '3' || row?.delivery_installation?.delivery_type_id == 3) deliveryType = 'Shipping';
        const itemName = row?.name || row?.service_name || oldService?.service_name || 'Service';
        deliveryTypeChange.items.push({ name: itemName, deliveryType, isProduct: false });
      }
    }
    if (deliveryTypeChange.changed && deliveryTypeChange.items.length > 0) {
      try {
        const requestRow = await Request.getRequestById(request_id);
        const partnerId = (requestRow && requestRow[0]) ? (requestRow[0].customer_id || 0) : 0;
        const partnerName = (requestRow && requestRow[0]) ? requestRow[0].customer_name || null : null;
        for (const item of deliveryTypeChange.items) {
          const itemLabel = item.isProduct ? 'Product' : 'Service';
          const requestActivity = {
            request_id,
            activity: `${itemLabel} "${item.name}" delivery type changed to ${item.deliveryType}.`,
            added_by: userId,
            timestamp: currentTime,
            status: 1,
            partner_id: partnerId,
            partner_name: partnerName
          };
          await RequestActivity.addRequestUpdate(requestActivity);
        }
      } catch (e) {
        console.log('Error logging delivery type change:', e);
      }
    }
    
    // ✅ FIX: When in production, preserve existing services
    let deleteServices;
    let deleteLocation;
    let existingServicesMap = {}; // Map to track existing services
    let processedServiceIds = new Set(); // Track which services we've processed

    if (isInProduction) {
      // Fetch existing services to match and update
      const existingServices = await Request.getRequestServices(request_id,false,false,false,result[0]?.project_category) || [];
      console.log(`⚠️ Request is in production - found ${existingServices.length} existing services to preserve`);

      // Build map of existing services for matching (includes both regular and RFP services)
      existingServices.forEach(service => {
        // For RFP services, use rfp flag in the key to differentiate
        const rfpFlag = service.rfp == 1 ? 'rfp' : 'regular';
        // NOTE:
        // For recipe-linked services, product_index is not a stable product-line identifier (it's treated as a service index),
        // so matching on it causes duplicate inserts on edit. Match recipe-services by recipe_id + service_id (+ product_id).
        const key = (service?.recipe_id != null && service?.recipe_id !== '')
          ? `${rfpFlag}_${service.labour_cost_service_id || 'null'}_recipe_${service.recipe_id}_${service.product_id || 'null'}`
          : `${rfpFlag}_${service.labour_cost_service_id || 'null'}_${service.product_index || 0}_${service.product_id || 'null'}`;
        if (!existingServicesMap[key]) {
          existingServicesMap[key] = [];
        }
        existingServicesMap[key].push(service);
      });

      // Don't delete services - we'll update them
      deleteServices = null;
      deleteLocation = null;
      let deleteServiceLocation = await Request.deleteServiceLocation(request_id);
    } else {
      // Normal flow: delete all services before recreating
      deleteServices = await Request.deleteRequestServices(request_id);
      deleteLocation = await Request.deleteServiceLocation(request_id);
    }

    emitProgress('fetching_staff_data', `Fetching staff member data for calculations...`, 77);
    
    const getAllTimelogMembers = await ProjectTask.fetchStaffMembersByRequestId(request_id)
    console.log('getAllTimelogMembers', getAllTimelogMembers)

    const installLabour = values.selectedServices?.filter(item => item.labour_cost_service_id == 10);
    console.log('installLabour', installLabour)

    const hourlyRates = getAllTimelogMembers.map(member => member.hourly_rate).filter(rate => rate > 0);
    const totalHourlyCost = hourlyRates.reduce((sum, rate) => sum + rate, 0);
    console.log('totalHourlyCost', totalHourlyCost)
    
    if (values.selectedServices && values.selectedServices.length > 0) {
      emitProgress('processing_services', `Processing ${values.selectedServices.length} services for ${orderType}...`, 75);
    }
    for (const row of values.selectedServices) {
      let rowQuantity = row.quantity ? parseFloat(row.quantity) : 0;

      // const averageHourlyCost = totalHourlyCost / getAllTimelogMembers?.length;
      const averageHourlyCost = getAllTimelogMembers?.length > 0 && totalHourlyCost > 0
        ? totalHourlyCost / getAllTimelogMembers.length
        : (row.cost || 0);
      console.log('averageHourlyCost', averageHourlyCost,rowQuantity )

      let existingServiceId = null
      if(row?.isAdditionalProduct){
        let serviceNameExist = await Product.findServiceByName(row.name);
        if (serviceNameExist) {
          existingServiceId = serviceNameExist.dataValues.id;
        }
      }
      console.log('rowrowrowrowrow--3-', row)
      requestProduct['product_index'] = row.productIndex || row?.indexId || 0 || row.product_index
      requestProduct['request_id'] = request_id
      requestProduct['labour_cost_service_id'] = row?.isAdditionalProduct ? existingServiceId : row.labour_cost_service_id || (row?.is_service_row == 1 ? null : row.id || null ),
      requestProduct['recipe_id'] = row.recipe_id || null,
      requestProduct['product_id'] = row.product_id || null,
      // requestProduct['qty'] = values?.project_category?.name =='Time & Material' ? 0 : rowQuantity,
      // requestProduct['qty'] = (values?.project_category?.name =='Time & Material'  && !row.product_id) ? 0 : rowQuantity,
      requestProduct['qty'] = rowQuantity || 0,
      requestProduct['unit_cost'] = row.unit_cost || row.total_cost,
      requestProduct['markup'] = row.markup || 0,
      requestProduct['total_cost'] = row.total || (requestProduct['unit_cost'] * requestProduct['qty'])
      requestProduct['rfp'] = 0
      requestProduct['is_dos'] = row?.is_dos
      requestProduct['map_service'] = row?.fromMapServices ? 1 : 0
      requestProduct['jobcost_unit_cost'] = (result[0]?.allInstallationCompleted == 1 && row?.labour_cost_service_id == 10 ) ? Number(averageHourlyCost) : 0
      requestProduct['jobcost_quantity'] = result[0]?.project_category == 12 ? rowQuantity : (!row?.makeAsTask && !row.recipe_id && row?.labour_cost_service_id !== 10) ? rowQuantity :  0
      requestProduct['jobcost_total_cost'] = (result[0]?.project_category == 12 && row?.labour_cost_service_id == 10 )? Number(averageHourlyCost * requestProduct['qty']) : (result[0]?.project_category == 12) ? Number(row?.cost * requestProduct['qty']) : (!row?.makeAsTask && !row.recipe_id && row?.labour_cost_service_id !== 10) ? Number(row?.cost * requestProduct['qty']) : 0
      // requestProduct['jobcost_unit_cost'] = 0
      // requestProduct['jobcost_quantity'] = 0
      // requestProduct['jobcost_quantity'] = (values?.project_category?.name =='Time & Material'  && !row.product_id) ? 0 : rowQuantity || 0
      // requestProduct['jobcost_total_cost'] = (values?.project_category?.name =='Time & Material'  && !row.product_id)  ? 0 : (row?.jobcost_total_cost ? parseFloat(row?.jobcost_total_cost) : 0)
      // requestProduct['jobcost_total_cost'] = 0
      // requestProduct['jobcost_total_cost'] = values?.project_category?.name =='Time & Material' ? 0 : (row?.jobcost_total_cost ? parseFloat(row?.jobcost_total_cost) : 0)
      requestProduct['isAdditionalProduct'] = row?.isAdditionalProduct ? 1 : 0
      requestProduct['makeAsTask'] = row?.makeAsTask ? 1 : 0
   // requestProduct['makeInstallCard'] =quotation ?  row?.makeInstallCard ? 1 : 0 : values?.project_category?.id ==11 ? row?.makeInstallCard ? 1 : 0 : 0
      requestProduct['makeInstallCard'] = row?.makeInstallCard ? 1 : 0 
      requestProduct['service_notes'] = row?.service_notes || null
      requestProduct['taken'] = (result[0]?.allInstallationCompleted == 1 || result[0]?.project_category == 12) ? 1 : (!row?.makeAsTask && !row.recipe_id && row?.labour_cost_service_id !== 10) ? 1 : 0
      requestProduct['fixed_service_cost'] = (result[0]?.project_category == 12 && rowQuantity > 0 )? 1 : 0
      requestProduct['facility_id'] = row?.facility_id || null
      requestProduct['split_percentage'] = row?.split_percentage || 0
      requestProduct['incoming_split_percentage'] = row?.incoming_split_percentage || 0
      requestProduct['delivery_type_id'] = row?.delivery_installation?.delivery_type_id || null
      requestProduct['installer_facility_id'] = row?.delivery_installation?.installer_facility_id || null
      requestProduct['service_desc'] = row?.desc || row?.service_desc || row?.description || null
      requestProduct['is_service_row'] = row?.is_service_row || 0
      requestProduct['is_existing_service'] = row?.is_existing_service || 0
      console.log('requestProduct--service', requestProduct)
      let lcsId = row?.labour_cost_service_id || row.id
      if(values?.project_category?.id !==11 &&  lcsId == 10 && quotation !== 1){
        requestProduct['makeInstallCard'] = 0
      }
      if(values?.project_category?.id == 12 && lcsId == 10 && quotation !== 1){
        requestProduct['makeInstallCard'] = 1
      }
      requestProduct['service_name'] = row?.name || row?.service_name || null
      let clonedRequestProduct = { ...requestProduct };  // This clones the object
      // Now, push the cloned object into mirrorData.serviceData
      mirrorData.serviceData.push(clonedRequestProduct);

      // ✅ FIX: When in production, update existing service instead of creating new
      let response = null;
      if (isInProduction) {
        const rfpFlag = requestProduct['rfp'] == 1 ? 'rfp' : 'regular';
        const matchKey = (requestProduct?.recipe_id != null && requestProduct?.recipe_id !== '')
          ? `${rfpFlag}_${requestProduct['labour_cost_service_id'] || 'null'}_recipe_${requestProduct.recipe_id}_${requestProduct['product_id'] || 'null'}`
          : `${rfpFlag}_${requestProduct['labour_cost_service_id'] || 'null'}_${row.productIndex || 0}_${requestProduct['product_id'] || 'null'}`;
        const existingServices = existingServicesMap[matchKey] || [];
        const existingService = existingServices.find(s => !processedServiceIds.has(s.id));

        if (existingService) {
          // Update existing service - preserves service_id and task relationships
          console.log(`✅ Updating existing service ID ${existingService.id} (preserving tasks)`);
          // Do not overwrite job cost fields when updating an existing service
          const {
            jobcost_unit_cost,
            jobcost_quantity,
            jobcost_total_cost,
            taken,
            ...requestProductForUpdate
          } = requestProduct;
          const updated = await Request.updateRequestServices(requestProductForUpdate, existingService.id, request_id);
          if (updated) {
            response = existingService.id; // Keep same ID
            processedServiceIds.add(existingService.id);
            console.log('existingService', response)
            // Update service locations
            
          }
        }
      }

      // Create new service if not in production or no existing service found
      if (!response) {
        response = await Request.addRequestServices(requestProduct);
        // Add newly created service to processedServiceIds to prevent deletion
        if (response && isInProduction) {
          processedServiceIds.add(response);
        }
      }
      const serviceLocations = address?.filter(raw => raw.isService == 1);
      console.log('serviceLocations111', serviceLocations , response)
      if (response && serviceLocations?.length>0) {
        console.log('serviceLocations----',serviceLocations)
        console.log('rowserviceLocations----',row)
        let addResult = serviceLocations?.filter(raw => (raw.productId == row?.isAdditionalProduct ? null : requestProduct.labour_cost_service_id && raw.productIndex === row.indexId && raw.newIndexId === row.newIndexId));
        console.log('addResultserviceLocations----',addResult)
        let data = {}
        if (addResult) {
          for (let i = 0; i < addResult.length; i++) {
            data.request_id = request_id
            data.service_id = response
            data.address = addResult[i].address
            data.city = addResult[i].city
            data.state = addResult[i].state
            data.zip = addResult[i].zip?.trim()
            data.timestamp = moment().unix()
            // data.edited_by = userId
            data.added_by = userId
            let addLocation = await Request.addServiceLocation(data)
          console.log('addLocation222', addLocation)
          }
        }
        if (result[0]?.allInstallationCompleted == 1 && row?.labour_cost_service_id == 10) {
          let requestActivity = {}
          requestActivity['request_id'] = request_id;
          requestActivity['activity'] = quotation 
            ? `Estimate updated. Install labor has been calculated using the average hourly cost: <strong> ${averageHourlyCost}</strong>.`
            : `Sales order updated. Install labor has been calculated using the average hourly cost: <strong> ${averageHourlyCost}</strong>.`;
          requestActivity['added_by'] = userId
          requestActivity['timestamp'] = currentTime
          requestActivity['is_jobcost'] = 1
          console.log('reqActivityIddddddddddddddd', requestActivity)
          let reqActivityId = await RequestActivity.addRequestUpdate(requestActivity)
          console.log('reqActivityId', reqActivityId)
          let requestActivity2 = {
            request_id: request_id,
            activity: `There are no live details available since the install labor was edited from the review.`,
            added_by: userId,
            timestamp: currentTime,
            is_jobcost: 2
          };

        console.log('Request Activity 2:', requestActivity2);
        let reqActivityId2 = await RequestActivity.addRequestUpdate(requestActivity2);
        console.log('Request Activity 2 ID:', reqActivityId2);
        }
      }
    }
    
    if (rfpServices && rfpServices.length > 0) {
      emitProgress('processing_rfp_services', `Processing ${rfpServices.length} RFP services for ${orderType}...`, 80);
    }
    for (const row of rfpServices) {
      let rowQuantity = row.qty ? (row.qty) : (row.quantity||1);
      let total=(row.sellingPrice||row.unit_cost)+((row.sellingPrice|row.unit_cost)*(row.originalMarkup||row.markup)/100)*rowQuantity
      requestProduct['request_id'] = request_id
      requestProduct['labour_cost_service_id'] = row.labour_cost_service_id,
      requestProduct['recipe_id'] =  null,
      requestProduct['product_id'] =  null,
      requestProduct['qty'] = values?.project_category?.name =='Time & Material' ? 0 : rowQuantity,
      requestProduct['unit_cost'] = row.sellingPrice||row.unit_cost,
      requestProduct['markup'] = row.originalMarkup||row.markup,
      requestProduct['total_cost'] =total
      requestProduct['rfp'] =1
      requestProduct['jobcost_unit_cost'] = row?.sellingPrice ? parseFloat(row?.sellingPrice) : 0
      requestProduct['jobcost_quantity'] = values?.project_category?.name =='Time & Material' ? 0 : parseFloat(rowQuantity) || 0
      requestProduct['jobcost_total_cost'] = values?.project_category?.name =='Time & Material' ? 0 : (parseFloat(row?.sellingPrice)*parseFloat(rowQuantity))?.toFixed(2) || 0
      requestProduct['makeAsTask'] = row?.makeAsTask_lcs ? 1 : 0
      requestProduct['makeInstallCard'] = row?.makeInstallCard_lcs ? 1 : 0
      requestProduct['service_notes'] = row?.service_notes || null
      requestProduct['service_name'] = row?.name || null
      let clonedRequestProduct = { ...requestProduct };  // This clones the object
      mirrorData.serviceData.push(clonedRequestProduct);

      // ✅ FIX: When in production, update existing RFP service instead of creating new
      let response = null;
      if (isInProduction) {
        const rfpFlag = 'rfp'; // RFP services
        const productIndex = row.productIndex || row.indexId || 0;
        const matchKey = `${rfpFlag}_${requestProduct['labour_cost_service_id'] || 'null'}_${productIndex}_${requestProduct['product_id'] || 'null'}`;
        const existingServices = existingServicesMap[matchKey] || [];
        const existingService = existingServices.find(s => !processedServiceIds.has(s.id));

        if (existingService) {
          // Update existing RFP service - preserves service_id and task relationships
          console.log(`✅ Updating existing RFP service ID ${existingService.id} (preserving tasks)`);
          const updated = await Request.updateRequestServices(requestProduct, existingService.id, request_id);
          if (updated) {
            response = existingService.id; // Keep same ID
            processedServiceIds.add(existingService.id);

            // Update service locations
            // let deleteServiceLocation = await Request.deleteServiceLocation(request_id);
          }
        }
      }

      // Create new RFP service if not in production or no existing RFP service found
      if (!response) {
        response = await Request.addRequestServices(requestProduct);
        // Add newly created RFP service to processedServiceIds to prevent deletion
        if (response && isInProduction) {
          processedServiceIds.add(response);
        }
      }

      let addresssData = row.location
          let data = {}
            if (response) {
              for (let i = 0; i < addresssData.length; i++) {
                data.request_id = request_id
                data.service_id = response
                data.address = addresssData[i].address
                data.city = addresssData[i].city
                data.state = addresssData[i].state
                data.zip = addresssData[i].zip?.trim()
                data.timestamp = moment().unix()
                // data.edited_by = userId
                data.added_by = userId
                let addLocation = await Request.addServiceLocation(data)
              }
            }
    }
        let newServicesData = await Request.getRequestServices(request_id, false, false, false, result[0]?.project_category) || [];
    
    // ✅ FIX: Delete removed products/services/materials/rfp when in production
    if (isInProduction) {
      emitProgress('deleting_removed_items', `Deleting removed items...`, 79);

      // Delete products that were not processed (removed from order)
      // Only check active items (status != 0) - don't re-process already inactive items
      if (CustomerProductData?.length > 0) {
        for (const existingProduct of CustomerProductData) {
          // Skip items that are already inactive
          if (existingProduct.status == 0) continue;

          // Regular products
          if (existingProduct.material == 0 && existingProduct.rfp == 0) {
            if (!processedProductIds.has(existingProduct.id)) {
              console.log(`⚠️ Product ID ${existingProduct.id} (${existingProduct.product_name}) removed - deleting`);
              // Delete locations first
              await Request.deleteLocation(existingProduct.id);
              // Delete the product
              await CustomerOrderProduct.deleteCustomerOrderProductById(existingProduct.id);
            }
          }
          // Materials
          else if (existingProduct.material == 1 && existingProduct.rfp == 0) {
            if (!processedMaterialIds.has(existingProduct.id)) {
              console.log(`⚠️ Material ID ${existingProduct.id} (${existingProduct.product_name}) removed - deleting`);
              // Delete locations first
              await Request.deleteLocation(existingProduct.id);
              // Delete the material
              await CustomerOrderProduct.deleteCustomerOrderProductById(existingProduct.id);
            }
          }
          // RFP Products
          else if (existingProduct.rfp == 1) {
            if (!processedRfpProductIds.has(existingProduct.id)) {
              console.log(`⚠️ RFP Product ID ${existingProduct.id} (${existingProduct.product_name}) removed - deleting`);
              // Delete locations first
              await Request.deleteLocation(existingProduct.id);
              // Delete the RFP product
              await CustomerOrderProduct.deleteCustomerOrderProductById(existingProduct.id);
            }
          }
        }
      }

      // Delete services that were not processed (removed from order)
      // Get all existing services (including RFP services) and check which ones weren't processed
      const allExistingServices = await Request.getRequestServices(request_id) || [];
      console.log('allExistingServicesssssssssssssssss', allExistingServices)

      // Minimal fix for recipe-linked services:
      // If recipe_id appears multiple times in current products (same recipe product added multiple times),
      // we should keep that many copies of each recipe service (recipe_id + labour_cost_service_id).
      // Otherwise, the cleanup below deletes recipe services incorrectly.
      const recipeInstanceCount = new Map(); // recipe_id -> count in values.products
      const remainingRecipeServiceKeeps = new Map(); // `${recipeId}_${serviceId}` -> remaining keep count
      const payloadProducts = Array.isArray(values?.products) ? values.products : [];
      for (const p of payloadProducts) {
        const rid = p?.recipe_id ?? p?.recipeId ?? p?.recipe?.id ?? null;
        if (rid == null || rid === '') continue;
        const recipeId = Number(rid);
        recipeInstanceCount.set(recipeId, (recipeInstanceCount.get(recipeId) || 0) + 1);
      }

      for (const existingService of allExistingServices) {
        // Skip services that are already inactive
        if (existingService.status == 0) continue;

        // Preserve recipe-linked services by COUNT (do not rely on product_index)
        const svcRecipeId = existingService?.recipe_id ?? existingService?.recipeId ?? null;
        if (svcRecipeId != null && svcRecipeId !== '') {
          const recipeId = Number(svcRecipeId);
          const instances = recipeInstanceCount.get(recipeId) || 0;
          if (instances > 0) {
            const serviceId = Number(existingService?.labour_cost_service_id);
            const key = `${recipeId}_${serviceId}`;
            if (!remainingRecipeServiceKeeps.has(key)) {
              remainingRecipeServiceKeeps.set(key, instances);
            }
            const remaining = remainingRecipeServiceKeeps.get(key) || 0;
            if (remaining > 0) {
              remainingRecipeServiceKeeps.set(key, remaining - 1);
              continue;
            }
            // remaining == 0 => extra copy (belongs to removed product instance) => allow deletion
          }
          // instances == 0 => recipe no longer present in products => allow deletion
        }

        if (existingService.id && !processedServiceIds.has(existingService.id)) {
          console.log(`⚠️ Service ID ${existingService.id} removed - deleting`);
          // Delete service locations for this specific service first
          await Request.deleteServiceLocationByServiceId(existingService.id);
          // Delete the service
          await Request.deleteRequestServicesById(existingService.id);
        }
      }
    }

    console.log('result==++++=========',result[0]?.partners[0]?.id)
    console.log('partnerData==++++=========',partnerData[0]?.user_id)

    // ✅ CRITICAL: Update request partners (must complete before response)
    let partnerUpdateStatus = null;
    let invitationEmailData = null;
    
    if (result[0]?.partners[0]?.id != partnerData[0]?.user_id) {
      if (partnerData && partnerData.length > 0) {
        let client = {}
        for (let eachPartner of partnerData) {
          client['last_seen'] = currentDate,
          client['timestamp'] = currentDate,
          client['added_by'] = userId,
          client['partner_id'] = eachPartner.id
          client['accept_status'] = 1;
          client['status'] = 1
          partnerUpdateStatus = await RequestPartner.updateRequestPartnersByRequestId(request_id, client);
        }
        
        // ✅ Prepare invitation email data for async processing
        if (partnerUpdateStatus) {
          let subject = empty(quotation)?'A new request awaiting your acceptance':'A new estimate awaiting your acceptance';
          let email_content = empty(quotation)?`You are invited to join New request by ` + values?.userName:`You are invited to join estimate by ` + values?.userName;
          if (req.body.values.welcome_message) {
            subject = req.body.values.subject
            email_content = req.body.values.email_content
          }
          invitationEmailData = {
            partnerData,
            subject,
            email_content
          };
        }
      }
    }

    //notification
    emitProgress('generating_notifications', `Generating system notifications...`, 85);
    
    let NewCustomerProductData = await CustomerOrderProduct.getCustomerOrderProductsbyId(orderId);
    console.log('NewCustomerProductData', NewCustomerProductData)

    console.log('CustomerProductDataaa 222', CustomerProductData)
    // if(result[0]?.allInstallationCompleted == 1 && values?.project_category?.id == 12){
    //   const previousMaterials = CustomerProductData?.filter((item)=>item.material == 1)
    //   const newMaterials = NewCustomerProductData?.filter((item)=>item.material == 1)
    //   console.log('newMaterials', newMaterials)
    //   const projectId = result[0]?.isProject
    //   console.log('previousMaterials', previousMaterials)
    //   let jobCostProducts = await JobCost.fetchAllJobCostByProjectId(projectId, userId);
    //   console.log('AlljobCostProducts', jobCostProducts)
    //   const selectedMaterials = jobCostProducts?.filter((item) => (item.rfp == 0 && item.recipe == 0))
    //   if (selectedMaterials?.length > 0) {
    //     for (const element of selectedMaterials) {
    //       const material = await Product.getMaterialById(element?.product_id)
    //       console.log('materiallll', material)
    //       if (material?.length > 0) {
    //         const materialData = material[0];
    //         const finishedProducts = await Product.getAllFinishedProductsByProductId(materialData?.material_id)
    //         const matchingValue = previousMaterials?.find(val => val?.product_id === materialData?.material_id);
    //         let newQty = finishedProducts[0]?.qty + matchingValue?.quantity ;
    //         let totalCost = finishedProducts[0]?.unit_cost * newQty
    //         const data = {
    //           qty: newQty,
    //           total_cost: totalCost
    //         };
    //         const updated = await Product.updateMaterialStock(finishedProducts[0]?.id, data)
    //         console.log('updatedddd', updated)
    //         if (updated) {
    //           console.log('updateddddddddddddddd stock');
    //         } else {
    //           console.log('Failed to update stock');
    //           res.send({ status: 0, message: 'Failed to update stock' });
    //         }
    //       } else {
    //         res.send({ status: 0, message: `Stock is lesser than entered qty`, resultId: material[0].id });
    //         return;
    //       }
    //     }
    //   }

    //   if (selectedMaterials?.filter((item) => item.rfp != 1)?.length > 0) {
    //     for (const element of selectedMaterials) {
    //       const material = await Product.getMaterialById(element?.product_id)
    //       console.log('materiallll', material)
    //       if (material?.length > 0) {
    //         const materialData = material[0];
    //         const matchingNewMaterial = newMaterials?.find(
    //           (nm) => nm.id === element?.customer_order_product_id
    //         );
    //         console.log('matchingNewMaterial', matchingNewMaterial)

    //         // Deduct quantity of new material from existing stock
    //         let newQty = material[0]?.qty - (matchingNewMaterial?.quantity || 0);
    //         let totalCost = material[0]?.unit_cost * newQty
    //         console.log('element?.quantity===', element?.quantity, 'element?.actual_quantity===', element?.actual_quantity)
    //          console.log(`DEDUCTING STOCK -> Material ID: ${materialData.material_id} Deducted Qty: ${matchingNewMaterial?.quantity || 0}, New Qty: ${newQty}, Unit Cost: ${materialData.unit_cost}, Total Cost: ${totalCost}`);
    //           const finishedProducts = await Product.getAllFinishedProductsByProductId(material[0]?.material_id)
    //           console.log('finishedProductssssssssss', finishedProducts)
    //           const newMatchingProduct = finishedProducts.find(fProduct => fProduct.qty >= newQty);
    //           console.log('newMatchingProduct===', newMatchingProduct);

    //           if (newMatchingProduct) {
    //             const data = {
    //               qty: newQty,
    //               total_cost: totalCost
    //             };
    //             console.log('data11111111', data,newMatchingProduct.id)
    //             const updated = await Product.updateMaterialStock(newMatchingProduct.id, data)
    //             console.log('updatedddd', updated)
    //             if (updated) {
    //               console.log('updateddddddddddddddd stock');
    //             } else {
    //               console.log('Failed to update stock');
    //               res.send({ status: 0, message: 'Failed to update stock' });
    //             }
    //           } else {
    //             res.send({ status: 0, message: `Stock is lesser than entered qty`, resultId: material[0].id });
    //             return;
    //           }
    //       }
    //     }
    //   }
    // }
    console.log('result[0]?.allInstallationCompleted==',result[0]?.allInstallationCompleted)
    console.log('values?.project_category?.id == 12==',values?.project_category?.id == 12)
    if (result[0]?.allInstallationCompleted == 1 && values?.project_category?.id == 12) {
      emitProgress('managing_stock', `Managing material stock levels...`, 88);
      
      const previousMaterials = CustomerProductData?.filter(item => item.material == 1);
      console.log('previousMaterials----', previousMaterials)
      const newMaterials = (Array.isArray(NewCustomerProductData) ? NewCustomerProductData : [])?.filter(item => item.material == 1);
      console.log('newMaterials', newMaterials)
      const projectId = result[0]?.isProject;

      const jobCostProducts = await JobCost.fetchAllJobCostByProjectId(projectId, userId);
      const selectedMaterials = jobCostProducts?.filter(item => item.rfp == 0);
      console.log('selectedMaterials', selectedMaterials)

      if (selectedMaterials?.length > 0) {

        // 1️⃣ ADD previous quantities back to finished products
        for (const element of selectedMaterials) {
          console.log('selectedMaterials--element-- add', element)
          const material = await Product.getMaterialById(element?.product_id);
          console.log('selectedMaterials--material--', material)
          if (!material?.length) continue;

          const materialData = material[0];
          const finishedProducts = await Product.getAllFinishedProductsByProductId(materialData.material_id);
          console.log('selectedMaterials--finishedProducts--', finishedProducts)
          if (!finishedProducts?.length) continue;

          // const matchingPrevious = previousMaterials?.find(val => val?.product_id === materialData.material_id);
          const matchingPrevious = previousMaterials?.find(val => 
            val?.product_id === materialData.material_id &&
            ((val?.stock_type === 1 && materialData?.partial === 0) || 
              ((val?.stock_type === 2 && materialData?.partial === 1) && val?.convertion_unit == materialData?.partial_unit ))
          );
          console.log('selectedMaterials--matchingPrevious--', matchingPrevious)
          const addQty = matchingPrevious?.quantity || 0;
          if (addQty === 0) continue;

          let remainingToAdd = addQty;
          let filteredProducts = [];

          if (element?.partial === 1) {
            filteredProducts = finishedProducts.filter(item => item.partial === 1 && element?.partial_unit === item?.partial_unit);
          } else if (element?.partial === 0) {
            filteredProducts = finishedProducts.filter(item => item.partial === 0);
          }
          console.log('filteredProducts---', filteredProducts)

          // Add to finished products one by one
          for (const fProduct of filteredProducts) {
            const newQty = fProduct.qty + remainingToAdd;
            const totalCost = fProduct.unit_cost * newQty;
            const updated = await Product.updateMaterialStock(fProduct.id, { qty: newQty, total_cost: totalCost });
            console.log(`Added previous stock -> Finished Product ID: ${fProduct.id}, Added Qty: ${remainingToAdd}, New Qty: ${newQty}`);
            break; // Once added to one product, exit (assuming previous qty goes to one finished product)
          }
        }

        // 2️⃣ DEDUCT new/current quantities from updated stock
        console.log('ded--newMaterials--', newMaterials)
        for (const element of newMaterials) {
          let deductQty = element.quantity || 0;
          console.log('ded--newMaterials--deductQty--', deductQty)
          if (deductQty === 0) continue;

          const finishedProducts = await Product.getAllFinishedProductsByProductId(element?.product_id);
          console.log('finishedProducts--deduct', finishedProducts)
          if (!finishedProducts?.length) continue;

          let filteredProducts = [];

          if (element?.stock_type === 2) {
            // filteredProducts = finishedProducts.filter(item => item.partial === 1);
            filteredProducts = finishedProducts.filter(item => item.partial === 1 && element?.convertion_unit === item?.partial_unit);
          } else if (element?.stock_type === 1) {
            filteredProducts = finishedProducts.filter(item => item.partial === 0);
          }
          console.log('filteredProducts---debt', filteredProducts)
          // Sort finished products by available qty descending (optional, can use any logic)
          const sortedProducts = filteredProducts.sort((a, b) => b.qty - a.qty);

          for (const fProduct of sortedProducts) {
            if (deductQty <= 0) break;

            const availableQty = fProduct.qty;
            const qtyToDeduct = Math.min(availableQty, deductQty);
            const newQty = availableQty - qtyToDeduct;
            const totalCost = fProduct.unit_cost * newQty;

            const updated = await Product.updateMaterialStock(fProduct.id, { qty: newQty, total_cost: totalCost });
            if (!updated) {
              res.send({ status: 0, message: 'Failed to update stock' });
              return;
            }

            deductQty -= qtyToDeduct;
            console.log(`Deducted new stock -> Finished Product ID: ${fProduct.id}, Deducted Qty: ${qtyToDeduct}, New Qty: ${newQty}`);
          }

          // If after going through all finished products, we still have qty to deduct
          if (deductQty > 0) {
            res.send({ status: 0, message: `Stock is lesser than entered qty` });
            return;
          }
        }
      }
      
      emitProgress('stock_management_complete', `Stock management completed successfully`, 89);
    }


    // ✅ OPTIMIZATION: Prepare notification data for async processing
    let notification_msg = '';
    let notification_url = '';
    let requestDetails = null;

    if (updateReqID && !quotation) {
      notification_url = `official-request/view/${request_id}`
      if (result[0]['name'] != requestdata['name']) {
        notification_msg += `The Sales Order Name changed to <strong> ${requestdata['name']}.</strong> `
      }
      if (result[0]['map_service'] != requestdata['map_service']) {
        if(values?.map_service?.name){
        notification_msg += `The map service changed to <strong> ${values?.map_service['name']}.</strong> `
        }
      }
      if (result[0]['description'] != requestdata['description']) {
        notification_msg += `The Sales Order description updated to <strong> ${requestdata['description']}.</strong> `
      }
      if (result[0]?.partners?.[0]?.id != partnerData[0]?.user_id) {
        notification_msg += `Client has been updated to <strong> ${partnerData[0]?.company_name}.</strong> `
      }
      if (result[0]['project_type'] != requestdata['project_type']) {
        notification_msg += `The Sales Order Project type has been updated to <strong> ${values?.project_type['name']}.</strong> `
      }
      if (result[0]['project_category'] != requestdata['project_category']) {
        notification_msg += `The Sales Order Job category has been updated to <strong> ${values?.project_category['name']}.</strong> `
      }
      if (result[0]['community'] != requestdata['community']) {
        notification_msg += `The Sales Order Project has been updated to <strong> ${values?.community.name}.</strong> `
      }
      if (result[0]['po_number'] != requestdata['po_number']) {
        notification_msg += `The Sales Order PO number has been updated to <strong> ${values?.po_number}.</strong> `
      }
      if (result[0]['notes'] != requestdata['notes']) {
        notification_msg += `The Sales Order Notes has been updated to <strong> ${requestdata['notes']}.</strong> `
      }
      console.log('result[0][sales_rep_ids]', result[0]['sales_rep_ids'])
      console.log('requestdata[sales_rep_ids]', requestdata['sales_rep_ids'])
      if ((result[0]['sales_rep_ids'] || '') != (requestdata['sales_rep_ids'] || '')) {
        notification_msg += `The Sales Order Sales Rep has been updated to <strong> ${requestdata['sales_rep_names']}.</strong> `
      }
      if ((result[0]['collaborators'] || '').trim() !== (requestdata['collaborators'] || '').trim()) {
        const oldIds = (result[0]['collaborators'] || '').split(',').map(s => String(s).trim()).filter(Boolean);
        const newIds = (requestdata['collaborators'] || '').split(',').map(s => String(s).trim()).filter(Boolean);
        const removedIds = oldIds.filter(id => !newIds.includes(id));
        const addedIds = newIds.filter(id => !oldIds.includes(id));
        const allStaff = await Teams.getStaffNamesByIds([...removedIds, ...addedIds]);
        const idToName = Object.fromEntries((allStaff || []).map(s => [String(s.id), s.name]));
        if (removedIds.length) notification_msg += `Collaborators removed: <strong> ${removedIds.map(id => idToName[id] || id).join(', ')}.</strong> `;
        if (addedIds.length) notification_msg += `Collaborators added: <strong> ${addedIds.map(id => idToName[id] || id).join(', ')}.</strong> `;
      }
      if (result[0]['submission_deadline'] != requestdata['submission_deadline']) {
        notification_msg += `The Sales Order Start date has been updated to <strong> ${moment(requestdata['submission_deadline'] * 1000).format('YYYY-MM-DD ')}.</strong> `
      }
      if (result[0]['participation_ends'] != requestdata['participation_ends']) {
        notification_msg += `The Sales Order End date has been updated to <strong> ${moment(requestdata['participation_ends'] * 1000).format('YYYY-MM-DD ')}.</strong> `
      }
      CustomerProductData && CustomerProductData?.forEach(oldItem => {
        const newItem = (Array.isArray(NewCustomerProductData) ? NewCustomerProductData : [])?.find(item => ((item.product_id === oldItem.product_id) && (item.product_index === oldItem.product_index)) );
        const isRfpProduct = (rfpProducts && rfpProducts.some(rp => rp.product_id === oldItem.product_id)) || oldItem.rfp === 1;
        const isRfpMaterial = oldItem.material === 1 && (oldItem.rfp === 1 || (rfpProducts && rfpProducts.some(rp => rp.product_id === oldItem.product_id && rp.material === 1)));
        const itemType = isRfpProduct && oldItem.material !== 1 ? 'Proposal Product' : (isRfpMaterial ? 'Proposal Material' : (oldItem.material === 1 ? 'Material' : 'Product'));
        
        if (!newItem) {
          notification_msg += `${itemType} '${oldItem.product_name}' removed from the Sales order.`;
        } else {
          // Check product/material notes changes
          if ((oldItem.product_notes || '') !== (newItem.product_notes || '')) {
            notification_msg += `Notes for ${itemType.toLowerCase()} '${oldItem.product_name}' have been updated.`;
          }
          if ((oldItem.product_desc || '') !== (newItem.product_desc || '')) {
            notification_msg += `Description for ${itemType.toLowerCase()} '${oldItem.product_name}' has been updated.`;
          }
          if (newItem.quantity !== oldItem.quantity) {
            notification_msg += `Unit Quantity of '${oldItem.product_name}' changed from ${oldItem.quantity} to ${newItem.quantity}.`;
          }
          if (newItem.locationQuantity !== oldItem.locationQuantity) {
            notification_msg += `Quantity of '${oldItem.product_name}' changed from ${oldItem.locationQuantity} to ${newItem.locationQuantity}.`;
          }

          // Check if install cost was deleted
          const oldInstallCost = parseFloat(oldItem.install_cost || 0);
          const newInstallCost = parseFloat(newItem.install_cost || 0);
          if (oldInstallCost > 0 && newInstallCost === 0) {
            notification_msg += `Install cost for ${itemType.toLowerCase()} '${oldItem.product_name}' has been removed.`;
          }
          const oldLocation = oldItem.location?.[0] || {};
          const newLocation = newItem.location?.[0] || {};
          if (
            oldLocation?.added_by !== newLocation?.added_by ||
            oldLocation?.address !== newLocation?.address ||
            oldLocation?.city !== newLocation?.city ||
            oldLocation?.product_id !== newLocation?.product_id ||
            oldLocation?.state !== newLocation?.state ||
            oldLocation?.zip !== newLocation?.zip
          ) {
            notification_msg += `Location for ${itemType.toLowerCase()} '${oldItem.product_name}' changed.`;
          }
        }
      });
      NewCustomerProductData && NewCustomerProductData.forEach(newItem => {
        const oldItem = CustomerProductData.find(item => item.product_id === newItem.product_id);
        const isRfpProduct = (rfpProducts && rfpProducts.some(rp => rp.product_id === newItem.product_id)) || newItem.rfp === 1;
        const isRfpMaterial = newItem.material === 1 && (newItem.rfp === 1 || (rfpProducts && rfpProducts.some(rp => rp.product_id === newItem.product_id && rp.material === 1)));
        const itemType = isRfpProduct && newItem.material !== 1 ? 'Proposal Product' : (isRfpMaterial ? 'Proposal Material' : (newItem.material === 1 ? 'Material' : 'Product'));
        
        if (!oldItem) {
          const newInstallCost = parseFloat(newItem.install_cost || 0);
          if (newInstallCost === 0 && newItem.material !== 1) {
            notification_msg += `New ${itemType.toLowerCase()} '${newItem.product_name}' without install price added to the order.`;
          } else {
            notification_msg += `New ${itemType.toLowerCase()} '${newItem.product_name}' added to the order.`;
          }
        }
      });
      // Service comparison logic
      oldServicesData && oldServicesData?.forEach(oldService => {
        const newService = (Array.isArray(newServicesData) ? newServicesData : [])?.find(service => 
          service.labour_cost_service_id === oldService.labour_cost_service_id && 
          service.product_index === oldService.product_index &&
          service.product_id === oldService.product_id &&
          (service.rfp || 0) === (oldService.rfp || 0)
        );
        const isRfpService = oldService.rfp === 1 || (rfpServices && rfpServices.some(rs => rs.labour_cost_service_id === oldService.labour_cost_service_id));
        const serviceType = isRfpService ? 'Proposal Service' : 'Service';
        
        if (!newService) {
          notification_msg += `${serviceType} '${oldService.name}' removed from the Sales order.`;
        } else {
          // Check service notes changes
          if ((oldService.service_notes || '') !== (newService.service_notes || '')) {
            notification_msg += `Notes for ${serviceType.toLowerCase()} '${oldService.name}' have been updated.`;
          }
          if (parseFloat(newService.quantity || newService.qty || 0) !== parseFloat(oldService.quantity || oldService.qty || 0)) {
            notification_msg += `Quantity of ${serviceType.toLowerCase()} '${oldService.name}' changed from ${oldService.quantity || oldService.qty || 0} to ${newService.quantity || newService.qty || 0}.`;
          }
          const oldServiceLocation = oldService.address ? {
            address: oldService.address,
            city: oldService.city,
            state: oldService.state,
            zip: oldService.zip
          } : {};
          const newServiceLocation = newService.address ? {
            address: newService.address,
            city: newService.city,
            state: newService.state,
            zip: newService.zip
          } : {};
          if (
            oldServiceLocation?.address !== newServiceLocation?.address ||
            oldServiceLocation?.city !== newServiceLocation?.city ||
            oldServiceLocation?.state !== newServiceLocation?.state ||
            oldServiceLocation?.zip !== newServiceLocation?.zip
          ) {
            notification_msg += `Location for ${serviceType.toLowerCase()} '${oldService.name}' changed.`;
          }
        }
      });
      newServicesData && newServicesData.forEach(newService => {
        const oldService = oldServicesData.find(service => 
          service.labour_cost_service_id === newService.labour_cost_service_id && 
          service.product_index === newService.product_index &&
          service.product_id === newService.product_id &&
          (service.rfp || 0) === (newService.rfp || 0)
        );
        const isRfpService = newService.rfp === 1 || (rfpServices && rfpServices.some(rs => rs.labour_cost_service_id === newService.labour_cost_service_id));
        const serviceType = isRfpService ? 'Proposal Service' : 'Service';
        
        if (!oldService) {
          notification_msg += `New ${serviceType.toLowerCase()} '${newService.name}' added to the Sales order.`;
        }
      });
      if (notification_msg && notification_msg.trim() !== '') {
        notification_msg = `✏️ ${notification_msg}`;
      }
      
    }
    
    // ✅ CRITICAL: Fetch request details for response (must complete before response)
    requestDetails = await Request.getRequestById(request_id)

      if(values?.project_category?.id == 12 || values?.project_category?.id == 13){
        // if project category is time and material
        const newServiceData = await Installation.getServices(request_id); // request_id
        let serviceData = newServiceData?.filter(item => item.makeInstallCard == 1) || [];
        let productData = await Installation.getProducts(orderId) || []; // order_id
        const productOrServiceData = [...productData,...serviceData]
        let materialOrderData = await CustomerOrderProduct.getMaterialProductByRequest(request_id,false) || []; // request_id
        console.log('productOrServiceData===time and material==final==', productOrServiceData)
        const projectData = values?.community
        if (projectData && productOrServiceData.length <= 0 && materials?.length > 0 && materialOrderData?.length > 0) {
          let deleteLocation = await Request.deleteMaterialLocationByOrderId(orderId);
          let usedMaterialOrderIds = new Set(); // To prevent reuse
          for (const row of materials) {
            let existingProductId = row?.isAdditionalProduct ? null : row.product_id || row.id || null;
            if(!existingProductId){
              let productNameExist = await Product.findProductByName(row.name);
              if (productNameExist) {
                existingProductId = productNameExist.dataValues.id;
              }
            }

            // Find the first unused match from materialOrderData
            const matchingOrder = materialOrderData?.find(
              entry => entry.product_id === existingProductId && !usedMaterialOrderIds.has(entry.id)
            );

            if (!matchingOrder) continue; // skip if no match found

            const materialOrderId = matchingOrder.id;
            usedMaterialOrderIds.add(materialOrderId); // mark as used


            let data = {}
            data.order_id = materialOrderId
            data.product_id = existingProductId || null
            data.address = projectData.address
            data.city = projectData.city
            data.state = projectData.state
            data.zip = projectData.zip?.trim()
            data.route_location_id = null
            data.timestamp = moment().unix()
            // data.edited_by = userId
            data.added_by = userId
            let addLocation = await Request.addLocation(data);
          }
        }
      }

      // let requestActivity = {}
      // requestActivity['request_id'] = request_id;
      // requestActivity['activity'] = notification_msg;
      // requestActivity['added_by'] = userId
      // requestActivity['timestamp'] = currentTime
      // let reqActivityId = await RequestActivity.addRequestUpdate(requestActivity)
      emitProgress('checking_project_existence', `Checking for existing job data...`, 93);
      
      let checkProjectExist = null
      if(!empty(quotation) && quotation===1){
        checkProjectExist = await Request.checkExistingProject(request_id, isRequestEstimate)
      }else{
        checkProjectExist = await Request.checkExistingProject(request_id)
      }
      console.log('checkProjectExistttttt',checkProjectExist)
      if(checkProjectExist){
        emitProgress('cleaning_project_data', `Cleaning up existing job data...`, 94);
        // NOTE (keep old code as backup):
        // Do NOT delete staff timelogs / payouts when editing the sales order,
        // because it wipes historical tracking data.
        //
        // const timelogDeleted = await ProjectTask.deleteStaffTaskTimelogs(checkProjectExist);
        // const payoutDeleted = await JobCost.deletePayOutByProjectId(checkProjectExist);
      }
      // ✅ CRITICAL: Journal entries (financial data - must complete before response)
        // if(values?.project_category?.id == 12 ) {
      // emitProgress('creating_journal_entries', `Creating journal entries for job cost...`, 95);
          await createJornalEntryForJobCost(request_id,previosJobCostTotal,previosProductTotal,previosServiceTotal)
      // emitProgress('journal_entries_created', `Journal entries created successfully`, 97);
        // }
        
      // Sync build order updates to other installations (only for split orders)
      try {
        if(requestDetails && requestDetails[0] && requestDetails[0].quotation == 0){
          const isSplitOrder = requestDetails[0]?.is_split_order;
          const originFacilityRequestId = requestDetails[0]?.origin_facility_request_id;
          const forParentFacility = requestDetails[0]?.for_parent_facility;
          
          // For main sales order: sync to child build orders in other installations
          // For child build orders: sync to other installations (if needed)
          if(isSplitOrder && forParentFacility !== 1){
            console.log('isSplitOrder and forParentFacility !== 1');
            // This is a split order that needs to be synced
            let targetedFacilityIds = [];
            
            // Get all unique facility_ids from products that are not from current facility
            values.products.forEach(product => {
              if(product.facility_id && product.facility_id !== FACILITY_ID){
                if(!targetedFacilityIds.includes(product.facility_id)){
                  targetedFacilityIds.push(product.facility_id);
                }
              }
            });

            values.selectedServices.forEach(service => {
              if(service.facility_id && service.facility_id !== FACILITY_ID){
                if(!targetedFacilityIds.includes(service.facility_id)){
                  targetedFacilityIds.push(service.facility_id);
                }
              }
            });
            console.log('targetedFacilityIds for update---', targetedFacilityIds);
            console.log('Split Order Details:', {
              isSplitOrder,
              originFacilityRequestId,
              currentRequestId: request_id,
              parentRequestId: originFacilityRequestId || request_id,
              productCount: values.products.length,
              facilitiesWithProducts: targetedFacilityIds,
              servciceCount:values.selectedServices.length
            });
            
            if(targetedFacilityIds.length > 0){
              // Use current request_id as origin_facility_request_id for main sales order
              // For child build orders, use the existing origin_facility_request_id
              const parentRequestId = originFacilityRequestId || request_id;
              
              requestdata['origin_facility_request_id'] = parentRequestId;
              if (values?.clients?.billing_type != 0) {
                requestdata['status'] =  0
              }
              // requestdata['status'] = 3;
              requestdata['project_category'] = 15;
              requestdata['for_parent_facility'] = 1;
              requestdata['added_by'] = userId,
              requestdata['origin_facility_id'] = FACILITY_ID
              requestdata['is_split_order'] = isSplitOrder ? 1 :0
              requestdata['created_at'] = currentTime
              requestdata['new_order'] = 1;
              const isFromEdit = true;
                let existingOtherInstallationRequestDataRes = await SplitManagementController.getOtherInstallationRequestData(parentRequestId, targetedFacilityIds);
               
              // Step 1: Delete existing build orders from other installations
              emitProgress('updating_from_other_installations', `Updating existing build orders from other installations...`, 95);
              let deleteBuildOrderFromOtherInstallationsResult = await deleteBuildOrderFromOtherInstallations(parentRequestId, targetedFacilityIds);
              console.log('deleteBuildOrderFromOtherInstallationsResult---', deleteBuildOrderFromOtherInstallationsResult);
              
              // Step 2: Add updated build orders using existing proven function
              emitProgress('updating_to_other_installations', `updated build orders to other installations...`, 96);
              let addBuildOrderSalesOrderToOtherInstallationResult = await addBuildOrderSalesOrderToOtherInstallation(requestdata, userId, uploaded, values, avalaraTaxAmount, negativeAmount, taxCategory, address, targetedFacilityIds,productAttachments,isFromEdit,existingOtherInstallationRequestDataRes);
              console.log('addBuildOrderSalesOrderToOtherInstallationResult---', addBuildOrderSalesOrderToOtherInstallationResult);
              // if(addBuildOrderSalesOrderToOtherInstallationResult){
              //   newMirrorData['project_category'] = (values.project_category && values.project_category["id"]) || 0;
              //   newMirrorData['for_parent_facility'] = 2;
              //   newMirrorData['added_by'] = userId;
              //   newMirrorData['estimate_id'] = result[0]?.estimate_id || null
              //   newMirrorData['syncWithQuickbooks'] = result[0]?.syncWithQuickbooks || null
              //   newMirrorData['doc_number'] = result[0]?.doc_number || null
              //   newMirrorData['est_doc_number'] = result[0]?.est_doc_number || null
              //   newMirrorData['new_order'] = 0
              //   newMirrorData['origin_facility_request_id'] = parentRequestId;
              //   newMirrorData['origin_facility_id'] = FACILITY_ID
              //   newMirrorData['created_at'] = currentTime

              //   let deleteMirrorOrderFromOtherInstallationsResult = await deleteMirrorOrderFromOtherInstallations(parentRequestId, targetedFacilityIds);
              //   console.log('deletemirrorSalesOrderFromOtherInstallationsResult---', deleteMirrorOrderFromOtherInstallationsResult);

              //   let addMirrorSalesOrderToOtherInstallationResult = await addMirrorOrderSalesOrderToOtherInstallation(newMirrorData,userId,uploaded,values,avalaraTaxAmount,negativeAmount,taxCategory,address,targetedFacilityIds,mirrorData)
              //   console.log('addMirrorSalesOrderToOtherInstallationResult', addMirrorSalesOrderToOtherInstallationResult)
              // }
            }
          }
        }
      } catch (error) {
        console.log('error while syncing build order to other installation', error);
      }
        
    // ✅ OPTIMIZATION: Send response immediately after critical operations
      emitProgress('finalizing_update', `Finalizing ${orderType} update...`, 98);
      emitProgress('complete', `${orderType.charAt(0).toUpperCase() + orderType.slice(1)} updated successfully!`, 100);
      res.send({ status: 1, message: 'Request updated successfully', quickBooksError: quickBooksError ? `Request updated but QuickBooks integration failed: ${quickBooksError}` : null ,qbReason:ReasonWithQb, checkProjectExist, newLocationIdMap, updatedAttachments, movedToProduction })
      
      
    // ✅ OPTIMIZATION: Handle all notifications/emails/SMS/activity logs/tab counts async (non-blocking)
    const handleNotificationsAsync = async () => {
      try {
        // Activity log for sales order changes
        if (updateReqID && !quotation && !empty(notification_msg)) {
          emitProgress('adding_activity_log', `Adding activity log entry...`, 87);
          let requestActivity = {}
          requestActivity['request_id'] = request_id;
          requestActivity['activity'] = notification_msg;
          requestActivity['added_by'] = userId
          requestActivity['timestamp'] = currentTime
          let reqActivityId = await RequestActivity.addRequestUpdate(requestActivity)
          emitProgress('activity_log_added', `Activity log entry added successfully`, 88);
        }

    
    
    // Add update logging for quotations (estimates)
    if (updateReqID && quotation) {
      let notification_msg = `✏️ `
      let notification_url = `official-request/view/${request_id}`
      if (result[0]['name'] != requestdata['name']) {
        notification_msg += `The Estimate Name changed to <strong> ${requestdata['name']}.</strong> `
      }
      if (result[0]['map_service'] != requestdata['map_service']) {
        if(values?.map_service?.name){
        notification_msg += `The map service changed to <strong> ${values?.map_service['name']}.</strong> `
        }
      }
      if (result[0]['description'] != requestdata['description']) {
        notification_msg += `The Estimate description updated to <strong> ${requestdata['description']}.</strong> `
      }
      if (result[0]?.partners?.[0]?.id != partnerData[0]?.user_id) {
        notification_msg += `Client has been updated to <strong> ${partnerData[0]?.company_name}.</strong> `
      }
      if (result[0]['project_type'] != requestdata['project_type']) {
        notification_msg += `The Estimate Project type has been updated to <strong> ${values?.project_type['name']}.</strong> `
      }
      if (result[0]['project_category'] != requestdata['project_category']) {
        notification_msg += `The Estimate Project category has been updated to <strong> ${values?.project_category['name']}.</strong> `
      }
      if (result[0]['community'] != requestdata['community']) {
        notification_msg += `The Estimate Project has been updated to <strong> ${values?.community.name}.</strong> `
      }
      if (result[0]['po_number'] != requestdata['po_number']) {
        notification_msg += `The Estimate PO number has been updated to <strong> ${values?.po_number}.</strong> `
      }
      console.log('............result[0]',result[0])
      console.log('............requestdata',requestdata)
      if (result[0]['submission_deadline'] != requestdata['submission_deadline']) {
        notification_msg += `The Estimate Start date has been updated to <strong> ${moment(requestdata['submission_deadline'] * 1000).format('YYYY-MM-DD ')}.</strong> `
      }
      if (result[0]['notes'] != requestdata['notes']) {
        notification_msg += `The Estimate Notes has been updated to <strong> ${requestdata['notes']}.</strong> `
      }
      console.log('............result[0].participation_ends',result[0].participation_ends)
      console.log('............requestdata.participation_ends',requestdata.participation_ends)

      const resultEndDate = moment(result[0]['participation_ends'] * 1000).format('YYYY-MM-DD');
      const requestEndDate = moment(requestdata['participation_ends'] * 1000).format('YYYY-MM-DD');

      if (resultEndDate !== requestEndDate) {
        notification_msg += `The Estimate Installation date has been updated to <strong> ${requestEndDate}.</strong> `;
      }
      if ((result[0]['collaborators'] || '').trim() !== (requestdata['collaborators'] || '').trim()) {
        const oldIds = (result[0]['collaborators'] || '').split(',').map(s => String(s).trim()).filter(Boolean);
        const newIds = (requestdata['collaborators'] || '').split(',').map(s => String(s).trim()).filter(Boolean);
        const removedIds = oldIds.filter(id => !newIds.includes(id));
        const addedIds = newIds.filter(id => !oldIds.includes(id));
        const allStaff = await Teams.getStaffNamesByIds([...removedIds, ...addedIds]);
        const idToName = Object.fromEntries((allStaff || []).map(s => [String(s.id), s.name]));
        if (removedIds.length) notification_msg += `Collaborators removed: <strong> ${removedIds.map(id => idToName[id] || id).join(', ')}.</strong> `;
        if (addedIds.length) notification_msg += `Collaborators added: <strong> ${addedIds.map(id => idToName[id] || id).join(', ')}.</strong> `;
      }
      CustomerProductData && CustomerProductData?.forEach(oldItem => {
        const newItem = (Array.isArray(NewCustomerProductData) ? NewCustomerProductData : [])?.find(
          item => ((item.product_id === oldItem.product_id) && (item.product_index === oldItem.product_index))
        );
        console.log('............oldItem',oldItem)
        console.log('............newItem',newItem)

        const isRfpProduct = (rfpProducts && rfpProducts.some(rp => rp.product_id === oldItem.product_id)) || oldItem.rfp === 1;
        const isRfpMaterial = oldItem?.material === 1 && (oldItem.rfp === 1 || (rfpProducts && rfpProducts.some(rp => rp.product_id === oldItem.product_id && rp.material === 1)));
        let oldItemType = oldItem?.material === 1 ? 'material' : 'product';
        if (isRfpProduct && oldItem?.material !== 1) {
          oldItemType = 'Proposal Product';
        } else if (isRfpMaterial) {
          oldItemType = 'Proposal Material';
        }
        const oldItemTypeCap = oldItemType.charAt(0).toUpperCase() + oldItemType.slice(1);

        if (!newItem && isRequestEstimate !== 1) {
          notification_msg += `${oldItemTypeCap} '${oldItem.product_name}' removed from the Estimate.`;
        } else {
          if ((oldItem.product_notes || '') !== (newItem.product_notes || '')) {
            notification_msg += `Notes for ${oldItemType.toLowerCase()} '${oldItem.product_name}' have been updated.`;
          }
          if ((oldItem.product_desc || '') !== (newItem.product_desc || '')) {
            notification_msg += `Description for ${oldItemType.toLowerCase()} '${oldItem.product_name}' has been updated.`;
          }
          if (newItem.quantity !== oldItem.quantity) {
            notification_msg += `Unit Quantity of '${oldItem.product_name}' changed from ${oldItem.quantity} to ${newItem.quantity}.`;
          }
          if (newItem.locationQuantity !== oldItem.locationQuantity) {
            notification_msg += `Quantity of '${oldItem.product_name}' changed from ${oldItem.locationQuantity} to ${newItem.locationQuantity}.`;
          }
          if (newItem.adjustment !== oldItem.adjustment) {
            notification_msg += `Marketing Adjustment of '${oldItem.product_name}' changed from ${oldItem.adjustment} to ${newItem.adjustment}.`;
          }
          if (newItem.adj_value_type !== oldItem.adj_value_type) {
            notification_msg += `Marketing Adjustment Type of '${oldItem.product_name}' changed to ${newItem.adj_value_type === 1 ? 'positive' : 'negative'}.`;
          }

          // Check if install cost was deleted
          const oldInstallCost = parseFloat(oldItem.install_cost || 0);
          const newInstallCost = parseFloat(newItem.install_cost || 0);
          if (oldInstallCost > 0 && newInstallCost === 0) {
            notification_msg += `Install cost for ${oldItemType.toLowerCase()} '${oldItem.product_name}' has been removed.`;
          }
          const oldLocation = oldItem.location?.[0] || {};
          const newLocation = newItem.location?.[0] || {};
          if (
            oldLocation?.added_by !== newLocation?.added_by ||
            oldLocation?.address !== newLocation?.address ||
            oldLocation?.city !== newLocation?.city ||
            oldLocation?.product_id !== newLocation?.product_id ||
            oldLocation?.state !== newLocation?.state ||
            oldLocation?.zip !== newLocation?.zip
          ) {
            notification_msg += `Location for ${oldItemType.toLowerCase()} '${oldItem.product_name}' changed.`;
          }
        }
      });
      NewCustomerProductData && NewCustomerProductData.forEach(newItem => {
        const oldItem = CustomerProductData.find(item => item.product_id === newItem.product_id);
        const isRfpProduct = (rfpProducts && rfpProducts.some(rp => rp.product_id === newItem.product_id)) || newItem.rfp === 1;
        const isRfpMaterial = newItem?.material === 1 && (newItem.rfp === 1 || (rfpProducts && rfpProducts.some(rp => rp.product_id === newItem.product_id && rp.material === 1)));
        let newItemType = newItem?.material === 1 ? 'material' : 'product';
        if (isRfpProduct && newItem?.material !== 1) {
          newItemType = 'Proposal Product';
        } else if (isRfpMaterial) {
          newItemType = 'Proposal Material';
        }
        const newItemTypeCap = newItemType.charAt(0).toUpperCase() + newItemType.slice(1);
        if (!oldItem) {
          const newInstallCost = parseFloat(newItem.install_cost || 0);
          if (newInstallCost === 0 && newItem?.material !== 1) {
            notification_msg += `New ${newItemType.toLowerCase()} '${newItem.product_name}' without install price added to the estimate.`;
          } else {
            notification_msg += `New ${newItemType.toLowerCase()} '${newItem.product_name}' added to the estimate.`;
          }
        }
      });
      
      // Service comparison logic for estimates
      oldServicesData && oldServicesData?.forEach(oldService => {
        const newService = (Array.isArray(newServicesData) ? newServicesData : [])?.find(service => 
          service.labour_cost_service_id === oldService.labour_cost_service_id && 
          service.product_index === oldService.product_index &&
          service.product_id === oldService.product_id &&
          (service.rfp || 0) === (oldService.rfp || 0)
        );
        const isRfpService = oldService.rfp === 1 || (rfpServices && rfpServices.some(rs => rs.labour_cost_service_id === oldService.labour_cost_service_id));
        const serviceType = isRfpService ? 'Proposal Service' : 'Service';
        
        if (!newService) {
          notification_msg += `${serviceType} '${oldService.name}' removed from the Estimate.`;
        } else {
          if ((oldService.service_notes || '') !== (newService.service_notes || '')) {
            notification_msg += `Notes for ${serviceType.toLowerCase()} '${oldService.name}' have been updated.`;
          }
          if (parseFloat(newService.quantity || newService.qty || 0) !== parseFloat(oldService.quantity || oldService.qty || 0)) {
            notification_msg += `Quantity of ${serviceType.toLowerCase()} '${oldService.name}' changed from ${oldService.quantity || oldService.qty || 0} to ${newService.quantity || newService.qty || 0}.`;
          }
          const oldServiceLocation = oldService.address ? {
            address: oldService.address,
            city: oldService.city,
            state: oldService.state,
            zip: oldService.zip
          } : {};
          const newServiceLocation = newService.address ? {
            address: newService.address,
            city: newService.city,
            state: newService.state,
            zip: newService.zip
          } : {};
          if (
            oldServiceLocation?.address !== newServiceLocation?.address ||
            oldServiceLocation?.city !== newServiceLocation?.city ||
            oldServiceLocation?.state !== newServiceLocation?.state ||
            oldServiceLocation?.zip !== newServiceLocation?.zip
          ) {
            notification_msg += `Location for ${serviceType.toLowerCase()} '${oldService.name}' changed.`;
          }
        }
      });
      newServicesData && newServicesData.forEach(newService => {
        const oldService = oldServicesData.find(service => 
          service.labour_cost_service_id === newService.labour_cost_service_id && 
          service.product_index === newService.product_index &&
          service.product_id === newService.product_id &&
          (service.rfp || 0) === (newService.rfp || 0)
        );
        const isRfpService = newService.rfp === 1 || (rfpServices && rfpServices.some(rs => rs.labour_cost_service_id === newService.labour_cost_service_id));
        const serviceType = isRfpService ? 'Proposal Service' : 'Service';
        
        if (!oldService) {
          notification_msg += `New ${serviceType.toLowerCase()} '${newService.name}' added to the Estimate.`;
        }
      });
      
      if (!empty(notification_msg)) {
        emitProgress('adding_activity_log', `Adding activity log entry...`, 87);
        let requestActivity = {}
        requestActivity['request_id'] = request_id;
        requestActivity['activity'] = notification_msg;
        requestActivity['added_by'] = userId
        requestActivity['timestamp'] = currentTime
        let reqActivityId = await RequestActivity.addRequestUpdate(requestActivity)
        // Keep updates visible in request updates feed
        await RequestUpdates.addRequestUpdates(requestActivity)
        emitProgress('activity_log_added', `Activity log entry added successfully`, 88);
      }
    }
    
    // ✅ Invitation emails to partners (when client changed) - parallelized
    if (invitationEmailData && invitationEmailData.partnerData && invitationEmailData.partnerData.length > 0) {
      emitProgress('sending_invitation_emails', `Sending invitation emails to clients...`, 82);
      
      const invitationEmailPromises = invitationEmailData.partnerData.map(async (eachPartner) => {
        try {
          let welcome = "Hello " + eachPartner['company_name'] + ","
          sgMail.setApiKey(SENDGRID_API_KEY)
          let message = rfqEmail()
          message = message.replace('[emailContent]', invitationEmailData.email_content)
          message = message.replace('[CONTENT]', welcome)
          message = message.replace('[LOGIN_URL]', `${BASE_URL}`)

          const mailOptions = {
            to: eachPartner.email,
            from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
            subject: invitationEmailData.subject,
            text: invitationEmailData.subject,
            html: message,
          };

          if(SEND_EMAIL){
            await transporter.sendMail(mailOptions);
            console.log(`✅ Invitation email sent to: ${eachPartner.email}`);
            emitProgress('email_sent', `Invitation email sent to ${eachPartner.email}`, 84);
          }
        } catch (error) {
          emitProgress('email_error', `Failed to send invitation email to ${eachPartner.email}: ${error.message}`, 84);
          console.error(`❌ Error sending invitation email to ${eachPartner.email}:`, error);
        }
      });

      await Promise.all(invitationEmailPromises);
    }


    if (!empty(requestDetails?.[0]?.status)) {
      emitProgress('sending_system_notifications', `Sending system notifications to users...`, 90);
      
      let notification_msg = quotation 
        ? `The Estimate <strong> ${requestDetails[0]['name']}.</strong> has been updated`
        : `The Sales Order <strong> ${requestDetails[0]['name']}.</strong> has been updated`
      let notification_url = `official-request/view/${request_id}`

          let requestPartners = await Request.getRequestPartnersByIds([], request_id)
          let requestTeamuserId = await Request.getRequestTeamUserIds(request_id)
          const adminIds = await Admin.getAllAdminIds()

          // Partner notifications (parallelized)
          const partnerNotificationPromises = requestPartners.map(async (eachpartner) => {
            await Admin.incrementField(eachpartner.id, "noti_count", 1);
            await Admin.addNotification({
              user_id: eachpartner.id,
              url: notification_url,
              timestamp: currentTime,
              message: notification_msg,
              status: 1,
              from_id: userId,
            });
          });

          // Team user notifications (parallelized)
          const teamNotificationPromises = requestTeamuserId.map(async (request_team_us_id) => {
            await Admin.incrementField(request_team_us_id, 'noti_count', 1);
            await Admin.addNotification({
              user_id: request_team_us_id,
              url: notification_url,
              timestamp: currentTime,
              message: notification_msg,
              status: 1,
              from_id: userId,
            });
          });

          // Admin notifications (parallelized)
          const adminNotificationPromises = adminIds.map(async (eachadminIds) => {
            await Admin.incrementField(eachadminIds, 'noti_count', 1);
            await Admin.addNotification({
              user_id: eachadminIds,
              url: notification_url,
              timestamp: currentTime,
              message: notification_msg,
              status: 1,
              from_id: userId,
            });
          });

          await Promise.all([...partnerNotificationPromises, ...teamNotificationPromises, ...adminNotificationPromises]);
        }

        // Tab count updates (parallelized)
        // const allUserIds = await Admin.getAllUserIds();
        let adminMangerIds = await Admin.getAllAdminAndManagerUserIds();
        let PartnerUserIds = await RequestPartner.fetchPartnerUserIds(request_id)
        // const usersToUpdate = [...new Set([userId, ...PartnerUserIds, ...allUserIds].filter(Boolean))];
        
    //     const updateEditCounts = async (userId) => {
    //       try {
    //         if (quotation) {
    //           if (values?.client_approve === false) {
    //             await Request.EstimateTabIncrementField(userId, 'open_count', -1, 'open_count_ids', request_id, false);
    //             await Request.EstimateTabIncrementField(userId, 'approved_count', 1, 'approved_count_ids', request_id, true);
    //             console.log("Estimate tab counts updated: Open -1, Approved +1");
    //           } else if (values?.client_approve === true) {
    //             await Request.EstimateTabIncrementField(userId, 'approved_count', -1, 'approved_count_ids', request_id, false);
    //             await Request.EstimateTabIncrementField(userId, 'open_count', 1, 'open_count_ids', request_id, true);
    //             console.log("Estimate tab counts updated: Approved -1, Open +1");
    //           }
    // } else {
    //           if (values?.client_approve === false) {
    //             await Request.salesOrderIncrementField(userId, 'open_count', -1, 'open_count_ids', request_id, false);
    //             await Request.salesOrderIncrementField(userId, 'approved_count', 1, 'approved_count_ids', request_id, true);
    //             console.log("Sales order counts updated: Open -1, Approved +1");
    //           } else if (values?.client_approve === true) {
    //             await Request.salesOrderIncrementField(userId, 'approved_count', -1, 'approved_count_ids', request_id, false);
    //             await Request.salesOrderIncrementField(userId, 'open_count', 1, 'open_count_ids', request_id, true);
    //             console.log("Sales order counts updated: Approved -1, Open +1");
    //           }
    //         }
    //       } catch (err) {
    //         console.error(`❌ Error updating tab counts for user ${userId}:`, err);
    //       }
    //     };
        
    //     console.log("valuessss client",values?.client_approve )
    //     await Promise.all(usersToUpdate.map(userId => updateEditCounts(userId)));
    //     console.log("Request tab counts updated successfully after edit.");

        // Email and SMS notifications (parallelized)
        if(!saveAsDraft) {
          const usersToSendMessage = [...new Set([userId, ...PartnerUserIds, ...adminMangerIds].filter(Boolean))];
          emitProgress('sending_email_sms_notifications', `Sending email and SMS notifications...`, 92);
          
          const emailSmsPromises = usersToSendMessage.map(async (userIdToCheck) => {
            try {
              console.log('useriddddd',userIdToCheck)
              const creator = await Admin.getUserNotificationAndPermissionSettings(userIdToCheck);
              console.log('creatorrrrrrrrr',creator)
              if (!creator) return;

              const recipientPhone = creator?.phone || TWILIO_PHONE;
              const Phone_country_code = creator?.phone_country_code || '1';
              console.log('recipientPhoneeee',recipientPhone)
              console.log('Phone_country_codeeeee',Phone_country_code)

              const emailPromise = (async () => {
                // ✉️ Estimate Notification
                if (quotation === 1 && creator?.pcb_new_estimate === 1 && creator.emailEnabled === 1 && creator.email && creator?.pcb_new_estimate_email === 1) {
                  let welcome = "Hello " + creator.name + ",";
                  let subject = `📝 Estimate Updated: ${values.name}`;
                  let email_content = "Estimate has been updated. Please review the details in your software.";

                  let message = newEstimateCreatedEmailTemplate({
                    user: creator,
                    requestName: values.name,
                    projectType: values.project_type?.name,
                    baseUrl: BASE_URL,
                    type: 'estimate',
                  });

                  message = message.replace('[emailContent]', email_content);
                  message = message.replace('[CONTENT]', welcome);
                  message = message.replace('[LOGIN_URL]', `${BASE_URL}`);

                  const mailOptions = {
                    to: creator.email,
                    from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
                    subject: subject,
                    text: subject,
                    html: message,
                  };

                  try {
                    await transporter.sendMail(mailOptions);
                    console.log(`✅ Estimate email sent to creator: ${creator.email}`);
                  } catch (err) {
                    console.error(`❌ Error sending estimate email to ${creator.email}:`, err);
                  }
                }

                // ✉️ Sales Order Notification
                if (quotation !== 1 && creator?.pcb_new_sales_order === 1 && creator.emailEnabled === 1 && creator.email) {
                  let welcome = "Hello " + creator.name + ",";
                  let subject = `📦 Sales Order Updated: ${values.name}`;
                  let email_content = "A sales order has been updated. Please review the details in your software.";

                  let message = newEstimateCreatedEmailTemplate({
                    user: creator,
                    requestName: values.name,
                    projectType: values.project_type?.name,
                    baseUrl: BASE_URL,
                    type: 'sales_order',
                  });
                  
                  message = message.replace('[emailContent]', email_content);
                  message = message.replace('[CONTENT]', welcome);
                  message = message.replace('[LOGIN_URL]', `${BASE_URL}`);

                  const mailOptions = {
                    to: creator.email,
                    from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
                    subject: subject,
                    text: subject,
                    html: message,
                  };

                  try {
                    await transporter.sendMail(mailOptions);
                    console.log(`✅ Sales order email sent to creator: ${creator.email}`);
                  } catch (err) {
                    console.error(`❌ Error sending sales order email to ${creator.email}:`, err);
                  }
                }
              })();

              const smsPromise = (async () => {
                // 📱 Estimate SMS Notification
                if (quotation === 1 && creator?.pcb_new_estimate === 1 && creator.smsEnabled === 1 && creator.phone && creator?.pcb_new_estimate_sms === 1) {
                  const smsContent = 
                    `📢 Estimate Updated!\n` +
                    `📄 Name: "${values.name}"\n` +
                    `💼 Type: ${values.project_type?.name || 'N/A'}\n` +
                    `🔗 View: ${BASE_URL}\n` +
                    `— SignWise`;

                  try {
                    await sendMessageToPhoneNumber(recipientPhone, smsContent, Phone_country_code);
                    console.log("✅ Estimate SMS sent to creator");
                  } catch (err) {
                    console.error("❌ Error sending estimate SMS to creator:", err);
                  }
                }

                // 📱 Sales Order SMS Notification
                if (quotation !== 1 && creator?.pcb_new_sales_order === 1 && creator.smsEnabled === 1 && creator.phone && creator?.pcb_new_sales_order_sms === 1) {
                  const smsContent = 
                    `📢 Sales Order Updated!\n` +
                    `📄 Name: "${values.name}"\n` +
                    `💼 Type: ${values.project_type?.name || 'N/A'}\n` +
                    `🔗 View: ${BASE_URL}\n` +
                    `— SignWise`;

                  try {
                    await sendMessageToPhoneNumber(recipientPhone, smsContent, Phone_country_code);
                    console.log("✅ Sales order SMS sent to creator");
                  } catch (err) {
                    console.error("❌ Error sending sales order SMS to creator:", err);
                  }
                }
              })();

              await Promise.all([emailPromise, smsPromise]);
            } catch (err) {
              console.error(`❌ Error sending notifications to user ${userIdToCheck}:`, err);
            }
          });

          await Promise.all(emailSmsPromises);
        }

        // Activity log for review update
        if (result[0]?.allInstallationCompleted == 1) {
          let requestActivity = {}
          requestActivity['request_id'] = request_id;
          requestActivity['activity'] = `The sales order review has been updated `;
          requestActivity['added_by'] = userId
          requestActivity['timestamp'] = currentTime
          console.log('reqActivityIddddddddddddddd', requestActivity)
          let reqActivityId = await RequestActivity.addRequestUpdate(requestActivity)
          console.log('reqActivityId', reqActivityId)
        }
      } catch (error) {
        console.error('Error in async notifications handler:', error);
      }
    };

    // ✅ OPTIMIZATION: Run non-critical operations asynchronously (fire and forget)
    handleNotificationsAsync().catch(error => {
      console.error('Error in async operations:', error);
    });
  } catch (error) {
    console.log(error, 'error in get request by id')
    res.send({ status: 0, message: 'failed to get request by id1'+error })
  }
}

const deleteRequestByIdEstimate = async (req, res) => {
  try {
    const { request_id, request, userId} = req.body;
    let oldReqData = await Request.getRequestById(request_id)
    let qbErr = null
    let currentTime = moment().unix()
    
    // Send response immediately after main delete operation
    let result = await Request.deleteRequestById(request_id)
    if (result) {
      res.send({ status: 1, data: result, message: 'Request deleted successfully' })
      console.log('Request deleted successfully');
      
      // Continue with background operations (non-blocking)
      setImmediate(async () => {
        try {
          // Route update
          if (oldReqData && oldReqData[0]?.map_service) {
            let data = { 
              assigned_sales_order : null
            }
            let updateRoute = await Routes.updateRoute(data,oldReqData[0]?.map_service);
          }
          
          // const removeSalesOrderIdFromTab = async (userId, requestId) => {
          //   const record = await  Request.getSalesOrderCountByUser(userId);
          //   if (!record) return;
          
          //   const tabFields = [
          //     'open',
          //     'approved',
          //     'in_production',
          //     'installs',
          //     'pickup',
          //     'shipping',
          //     'invoicing',
          //     'completed'
          //   ];
          
          //   for (let field of tabFields) {
          //     const fieldIds = `${field}_count_ids`;
          //     const countField = `${field}_count`;
          
          //     const ids = record[fieldIds]?.split(',') || [];
          //     if (ids.includes(requestId.toString())) {
          //       await Request.salesOrderIncrementField(userId, countField, -1, fieldIds, requestId, false);
          //       console.log(`Sales Order count updated for user ${userId}, removed ${requestId} from ${field}`);
          //       break; // found and removed, exit loop
          //     }
          //   }
          // };
          
          // Project deletion
          const project_id = await Project.getProjectByRequestId(request_id)
          if (project_id) {
            try {
              const installations = await Installation.getInstallationsByProjectId(project_id);
              const installationDeleted = await Installation.deleteInstallationUsingProjectId(project_id);
              const timelogDeleted = await ProjectTask.deleteStaffTaskTimelogs(project_id);
              const jobDeleted = await Project.deleteProject(project_id);
              console.log('Project and related data deleted')
            } catch (error) {
              console.error('❌ Error during project deletion:', error);
            }
          }
          
          // Notifications
          let notification_msg = `${request ? 'Sales Order' : 'Estimate'} <strong> ${oldReqData[0]?.['name']}.</strong> has been deleted.`
          let notification_url = `official-request/view/${request_id}`
          
          let adminIds = await Admin.getAllAdminIds();
          let adminMangerIds = await Admin.getAllAdminAndManagerUserIds();
          for (let eachadminIds of adminIds) {
            let incrementResult = await Admin.incrementField(eachadminIds, 'noti_count', 1)
            let addNotification = {
              user_id: eachadminIds,
              url: notification_url,
              timestamp: moment().unix(),
              message: notification_msg,
              status: 1,
              from_id: 1,
            }
            const addNotificationResult = await Admin.addNotification(addNotification);
          }
          
          // Request activity
          let requestActivity = {}
          requestActivity['request_id'] = request_id;
          requestActivity['activity'] = notification_msg;
          requestActivity['added_by'] = 1;
          requestActivity['timestamp'] = currentTime
          let reqActivityId = await RequestActivity.addRequestUpdate(requestActivity)
          
          // QuickBooks operations
          try {
            if(request){
              if(oldReqData[0]?.estimate_id && oldReqData[0]?.syncWithQuickbooks){
                let invoice =  deleteInvoiceInQuickBooks(oldReqData[0]?.estimate_id)
              }
            }else{
              if(oldReqData[0]?.estimate_id && oldReqData[0]?.syncWithQuickbooks){
                let estimate =  deleteEstimateInQuickBooks(oldReqData[0]?.estimate_id)
              }
            }
          } catch (error) {
            qbErr=error
            console.log("QuickBooks error",error)
          }

          // Tab count updates
          // const requestStatus = oldReqData[0]?.quotation_status;
          // const requestQuotation = oldReqData[0]?.quotation;

          // const allAdminUserIds = await Admin.getAllUserIds();
          const partnerUserIds = await RequestPartner.fetchPartnerUserIds(request_id);

          // const usersToUpdate = [...new Set([userId, ...partnerUserIds, ...allAdminUserIds].filter(Boolean))];

          // await Promise.all(usersToUpdate.map(async (userId) => {
          //   if (requestQuotation === 1) {
          //     if (requestStatus === 2) {
          //       await Request.EstimateTabIncrementField(userId, 'open_count', -1, 'open_count_ids', request_id, false);
          //       console.log("estimate open count updated")
          //     } else if (requestStatus === 4) {
          //       await Request.EstimateTabIncrementField(userId, 'approved_count', -1, 'approved_count_ids', request_id, false);
          //       console.log("estimate approved count updated")
          //     }
          //   }
          // }));

          // if (oldReqData[0]?.quotation === 0) {
          //   await Promise.all(usersToUpdate.map(async (uid) => {
          //     await removeSalesOrderIdFromTab(uid, request_id);
          //   }));
          // }
          // console.log("Request count updated")
          
          // Email and SMS notifications
          const allUsersToCheck = [...new Set([userId,...partnerUserIds, ...adminMangerIds].filter(Boolean))];
          for (const userIdToCheck of allUsersToCheck) {
            const approver = await Admin.getUserNotificationAndPermissionSettings(userIdToCheck);
            if (!approver) continue;

            const itemType = (oldReqData[0]?.quotation === 1 && oldReqData[0]?.status === 8)  ? 'Estimate' : "Sales Order";
            const itemName = oldReqData[0]?.name;

            if (approver?.emailEnabled === 1  && approver?.email) {
              const subject = `⚠️ ${itemType} Deleted: ${itemName}`;
              const welcome = `Hello ${approver.name},`;
              const email_content = `The ${itemType.toLowerCase()} "${itemName}" has been deleted from the system. Please review your records if necessary.`;

              let message = deletedItemEmailTemplate({
                user: approver,
                itemType: itemType,
                itemName: oldReqData[0]?.name,
                baseUrl: BASE_URL,
              });

              message = message.replace('[emailContent]', email_content);
              message = message.replace('[CONTENT]', welcome);
              message = message.replace('[LOGIN_URL]', `${BASE_URL}`);

              const mailOptions = {
                to: approver.email,
                from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
                subject,
                text: subject,
                html: message,
              };

              try {
                await transporter.sendMail(mailOptions);
                console.log(`✅ Delete email sent to: ${approver.email}`);
              } catch (err) {
                console.error(`❌ Error sending delete email:`, err);
              }
            }

            if (approver?.smsEnabled === 1) {
              const smsContent = 
              `⚠️ ${itemType} Deleted\n` + 
              `📄 ${itemType}: "${itemName}"\n` + 
              `🔗 View: ${BASE_URL}\n` +
              `— SignWise`;

              const recipientPhone = approver.phone || TWILIO_PHONE;
              const Phone_country_code = approver.phone_country_code || '';

              try {
                await sendMessageToPhoneNumber(recipientPhone, smsContent,Phone_country_code);
                console.log(`✅ Delete SMS sent to ${approver.name || 'User'} (${recipientPhone})`);
              } catch (err) {
                console.error(`❌ Failed to send Delete SMS:`, err);
              }
            }
          }
          
          console.log("Background cleanup operations completed")
        } catch (error) {
          console.error("Error in background operations:", error);
        }
      });
    } else {
      res.send({ status: 0, message: 'Request deleted failed' })
    }
  } catch (error) {
    console.log(error, 'error in delete request')
    res.send({ status: 0, message: 'failed to delete request' })
  }
}

async function duplicateEstimate(accessToken, estimateId) {
  const baseUrl = `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}`;
  const headers = {
    Authorization: `Bearer ${accessToken}`,
    Accept: 'application/json',
    'Content-Type': 'application/json'
  };
 
  // 1. Fetch the original estimate
  const original = await axios.get(`${baseUrl}/estimate/${estimateId}`, { headers });
  const estimate = original.data.Estimate;
 
  // 2. Remove fields that should not be reused
  delete estimate.Id;
  delete estimate.SyncToken;
  delete estimate.MetaData;
  delete estimate.DocNumber
 
  // 3. (Optional) remove DocNumber if you want QuickBooks to auto-generate it
  // delete estimate.DocNumber;
 
  // 4. Create the new estimate
  const created = await axios.post(`${baseUrl}/estimate`, estimate, { headers });
 
  // 5. Save new estimate ID and DocNumber to your DB
  const newEstimate = created.data.Estimate;
  const newEstimateId = newEstimate.Id;
  const newDocNumber = newEstimate.DocNumber;
 
  // Save `newEstimateId` and `newDocNumber` to your database here
  console.log('New Estimate Created:', newEstimateId, newDocNumber);
 
  return newEstimate;
}

const duplicateRequest = async (req, res) => {
  try {
    const { userId, request_id, userType, socketId, duplicateAsSalesOrder } = req.body;
    const currentDate = Math.floor(Date.now() / 1000);
    
    // 🚀 PROGRESS TRACKING: Socket progress emission function
    const emitProgress = (step, message, progress) => {
      const progressData = {
        step,
        message,
        progress,
        userId,
        domain: DOMAIN,
        timestamp: currentDate,
        type: 'duplicate_request_progress'
      };
      
      // Emit to working socket server (same as chat)
      console.log(`[DUPLICATE-PROGRESS] 📡 Emitting progress to working socket server...`);
      try {
        const workingSocket = ioClient(REST_API_IO, {
          path: '/socket.io',
          transports: ['websocket', 'polling'],
          secure: true,
          timeout: 5000
        });
        
        workingSocket.on('connect', () => {
          console.log(`[DUPLICATE-PROGRESS] 🔌 Connected to working socket server`);
          console.log(`[DUPLICATE-PROGRESS] 📊 Emitting progress data:`, progressData);
          
          // Try emitting as 'message' event (same as chat) to test if socket server handles it
          workingSocket.emit('message', {
            ...progressData,
            type: 'progress',
            conversationId: `duplicate_progress_${userId}`,
            senderId: 'system'
          });
          
          // Also try the original request_progress event
          workingSocket.emit('request_progress', progressData);
          
          console.log(`[DUPLICATE-PROGRESS] ✅ Successfully emitted progress to working socket server`);
          
          // Disconnect after emitting
          setTimeout(() => {
            workingSocket.disconnect();
          }, 1000);
        });
        
        workingSocket.on('connect_error', (error) => {
          console.error(`[DUPLICATE-PROGRESS] ❌ Working socket connection error:`, error);
        });
        
      } catch (error) {
        console.error(`[DUPLICATE-PROGRESS] ❌ Error creating working socket connection:`, error);
      }
    };

    // Initial progress
    console.log(`[DUPLICATE-PROGRESS] 🚀 Starting sales order duplication for user:`, userId);
    emitProgress('start', `Initializing sales order duplication...`, 5);
    
    let quickBook = null
    let newClientUserId = null
    emitProgress('fetching_request', `Fetching original request data...`, 10);
    const getRequest = await Request.getRequestById(request_id, userId, userType);
    const originalRequest = getRequest[0];

    if (!originalRequest) {
      emitProgress('error', 'Original request not found', 0);
      return res.status(404).send({ status: 0, message: 'Original request not found' });
    }

    if(originalRequest){
      try {
        emitProgress('quickbooks_duplication', `Duplicating QuickBooks estimate...`, 15);
        const estimateId = originalRequest?.estimate_id
        const token = await getNewAccessToken()
        quickBook = await duplicateEstimate(token,estimateId)
      } catch (error) {
        console.log('error',error)
        emitProgress('quickbooks_error', `QuickBooks duplication failed, continuing without QB sync...`, 15);
      }

    }

    // When duplicateAsSalesOrder is true (from Sales Order page), always create Sales Order duplicate
    // so it appears in Sales Order Awaiting Approval tab, not Estimate's New tab
    const isSalesOrderDuplicate = duplicateAsSalesOrder === true;
    const duplicatedRequest = {
      ...originalRequest,
      name: `${originalRequest.name} - ${currentDate}`,
      status: isSalesOrderDuplicate ? 2 : (originalRequest?.quotation === 1 ? 8 : 2),
      quotation_status: isSalesOrderDuplicate ? 3 : ((originalRequest?.quotation === 1 && originalRequest?.is_request_estimate != 1) ? 2 : 3),
      quotation: isSalesOrderDuplicate ? 0 : originalRequest?.quotation,
      client_approve: 0,
      id: undefined,
      cust_order_id: null,
      timestamp: currentDate,
      added_by: userId,
      edited_by:null,
      send_to_client: 0,
      syncWithQuickbooks: quickBook?.DocNumber ? 1 : 0,
      doc_number: quickBook?.DocNumber || null,
      est_doc_number: quickBook?.DocNumber || null,
      estimate_id :quickBook?.Id || null,
      duplicated_from:originalRequest.id,
      installation_acceptance:null,
      is_create_estimate : 0,
      new_order: 0,  // Duplicated sales order appears in Awaiting Approval tab
      hold: 0,  // Duplicated orders appear in Awaiting Approval, not Hold tab
      min_payment_status: 0
    };

    emitProgress('creating_request', `Creating duplicated request...`, 25);
    const newRequestId = await Request.addRequest(duplicatedRequest);

    if (!newRequestId) {
      emitProgress('error', 'Failed to duplicate request', 0);
      return res.send({ status: 0, message: 'Failed to duplicate request' });
    }

    emitProgress('duplicating_partners', `Duplicating request partners...`, 35);
    let oldRequestPartners = await RequestPartner.getRequestPartnerByIds({}, request_id);
    console.log('oldsRequestPartnerssss',oldRequestPartners)
    for (let eachPartner of oldRequestPartners) {
      newClientUserId = eachPartner.id
      let client = {};
      client['request_id'] = newRequestId,
      client['last_seen'] = currentDate,
      client['timestamp'] = currentDate,
      client['added_by'] = userId,
      client['partner_id'] = eachPartner?.partner_id || eachPartner.id
      client['accept_status'] = 1;
      client['status'] = 1
      await RequestPartner.addRequestPartners(client)
    }

    emitProgress('duplicating_customer_order', `Duplicating customer order...`, 45);
    const previousCustomerOrder = await CustomerOrder.getCustomerOrderRequestId(request_id);
    console.log('previousCustomerOrderrr',previousCustomerOrder)
    if (previousCustomerOrder) {
      let duplicatedOrder = {
        ...previousCustomerOrder,
        id: undefined,
        request_id: newRequestId,
      };

      // For request estimates, adjust totals based on existing estimate (type 44) services if present.
      // If no estimate services exist, keep the previous behavior (totals will be recalculated later if needed).
      if (originalRequest?.is_request_estimate == 1) {
        try {
          const previousServices = await Request.getAllServiceByRequest(request_id);
          const estimateServices = Array.isArray(previousServices)
            ? previousServices.filter(s => Number(s.service_type) == 44)
            : [];

          if (estimateServices.length > 0) {
            // Sum of unit_cost * qty for all estimate services
            const estimateSubTotal = estimateServices.reduce((sum, svc) => {
              const qty = Number(svc.qty) || 1;
              const unit = Number(svc.unit_cost) || 0;
              return sum + (unit * qty);
            }, 0);

            // Use existing tax (percentage) and tax_amount (absolute) from previous order.
            // Grand total = subtotal + absolute tax amount (no multiplication).
            const taxAmount = Number(previousCustomerOrder.tax_amount) || 0;

            duplicatedOrder = {
              ...duplicatedOrder,
              sub_total: estimateSubTotal,
              total: estimateSubTotal,
              grandTotal: estimateSubTotal + taxAmount,
              tax: 0,
              tax_amount: taxAmount,
            };
          } else {
            // No estimate services: keep totals zero so they can be set later if needed
            duplicatedOrder = {
              ...duplicatedOrder,
              sub_total: 0,
              total: 0,
              grandTotal: 0,
              tax: 0,
              tax_amount: 0,
            };
          }
        } catch (err) {
          console.error('Error calculating estimate totals for duplicated request:', err);
          // Fallback to zero totals on error
          duplicatedOrder = {
            ...duplicatedOrder,
            sub_total: 0,
            total: 0,
            grandTotal: 0,
            tax: 0,
            tax_amount: 0,
          };
        }
      }

      const newCustomerOrder = await CustomerOrder.createCustomerOrder(duplicatedOrder);
      console.log('newCustomerOrderrrr',newCustomerOrder)

      if (newCustomerOrder) {
        emitProgress('duplicating_products', `Duplicating order products...`, 55);
        const previousOrderProducts = await CustomerOrderProduct.getCustomerOrderProductByOrderId(previousCustomerOrder.id);
        console.log('previousOrderProducts',previousOrderProducts)
         if (Array.isArray(previousOrderProducts) && previousOrderProducts.length > 0) {
          for (const product of previousOrderProducts) {
            let duplicatedProduct;
            
            // For request estimates, only copy name, description, and product_index
            if (originalRequest?.is_request_estimate == 1) {
              duplicatedProduct = {
                ...product,
                id: undefined,
                order_id: newCustomerOrder,
                timestamp: currentDate,
                // Keep only name, description, and product_index
                product_name: product.product_name || product.name || '',
                product_desc: product.product_desc || product.description || '',
                product_index: product.product_index || product.indexId || 0,
                // Set all cost/amount fields to default/zero
                product_price: 0,
                product_retail_price: 0,
                product_qty: 0,
                product_sub_total: 0,
                product_total: 0,
                product_tax: 0,
                unit_cost: 0,
                discount: 0,
                adjustment: 0,
                product_markup: null,
                install_cost: null,
                expected_formula_price: 0,
                location_quantity: 0,
                is_product_row: 1,
              };
            } else {
              // For regular requests, copy everything as before (ensure non-nullable fields have defaults)
              duplicatedProduct = {
                ...product,
                id: undefined,
                order_id: newCustomerOrder,
                timestamp: currentDate,
                adjustment: product.adjustment != null ? product.adjustment : 0,
                adj_value_type: product.adj_value_type != null ? product.adj_value_type : 1,
              };
            }
            
            const createdProduct = await CustomerOrderProduct.createCustomerOrder(duplicatedProduct);
            console.log('createdProduct',createdProduct)
            if(createdProduct){
              emitProgress('duplicating_locations', `Duplicating product locations...`, 65);
              const getPreviousLocations = await Request.getAllLocationProductsById(product?.id)
              console.log('getPreviousLocations',getPreviousLocations)
              if (getPreviousLocations.length > 0) {
                  for (const location of getPreviousLocations) {
                    const duplicateLocation = {
                      ...location,
                      id: undefined,
                      order_id: createdProduct
                    };
                    const newLocation = await Request.addLocation(duplicateLocation);
                    console.log('getnewLocation',newLocation)
                  }
                }
            }
          }
        }
      }
      let getPreviousServices = [];
      if(originalRequest?.is_request_estimate == 1){
        getPreviousServices = await Request.getAllServiceByRequest(request_id)
      }else{
        getPreviousServices = await Request.getAllServiceByRequestId(request_id)
      }
      // const getPreviousServices = await Request.getAllServiceByRequestId(request_id)
      console.log('getPreviousServicessss',getPreviousServices)
      if(Array.isArray(getPreviousServices) && getPreviousServices.length > 0){
        for (const service of getPreviousServices) {
          const duplicatedService = {
            ...service,
            id: undefined,
            request_id: newRequestId,
            timestamp: currentDate,
            is_service_row: 1,  
          };
          const newServiceId = await Request.addRequestServices(duplicatedService);
          console.log('newServiceId',newServiceId)

          if(newServiceId){
            const getAllPreviousServiceLocations = await Request.getAllLocationServiceByRequestId(request_id)
            const getPreviousServiceLocations = getAllPreviousServiceLocations?.filter(loc => loc.service_id === service.id)
            console.log('getPreviousServiceLocations',getPreviousServiceLocations)
            if (getPreviousServiceLocations?.length > 0) {
              for (const location of getPreviousServiceLocations) {
                const duplicateLocation = {
                  ...location,
                  id: undefined,
                  service_id : newServiceId,
                  request_id: newRequestId,
                  timestamp: currentDate,
                };
                const newLocation = await Request.addServiceLocation(duplicateLocation);
                console.log('getnewServiceLocation',newLocation)
              }
            }
          }
        }
      }

      // For request estimates, if there is no existing service_type == 44, add a single 44 service
      const hasEstimateService = Array.isArray(getPreviousServices)
        && getPreviousServices.some(service => Number(service.service_type) == 44);
      console.log('hasEstimateService',hasEstimateService)
        if (originalRequest?.is_request_estimate == 1 && !hasEstimateService && newCustomerOrder) {
       console.log('hasEstimateService 22')
          emitProgress('adding_default_service', `Adding default service for request estimate...`, 60);
        try {
          const allLabourCostServices = await Product.getAllLabourCostServices();
          const serviceType44Services = allLabourCostServices.filter(service => 
            service.service_type == 44 && service.status === 1
          );

          // Add only the first service_type == 44 service
          if (serviceType44Services && serviceType44Services.length > 0) {
            const labourService = serviceType44Services[0]; // Get only the first one
            const serviceData = {
              product_index: 0,
              request_id: newRequestId,
              labour_cost_service_id: labourService.id,
              recipe_id: null,
              product_id: null,
              qty: 1,
              markup: labourService.markup || 0,
              unit_cost: labourService.total_cost || 0,
              total_cost: labourService.total_cost || 0,
              job_cost: 0,
              taken: 0,
              rfp: 0,
              map_service: 0,
              jobcost_unit_cost: 0,
              jobcost_quantity: 0,
              jobcost_total_cost: 0,
              is_dos: 0,
              makeAsTask: labourService.makeAsTask || 0,
              makeInstallCard: labourService.makeInstallCard || 0,
              status: 1,
              timestamp: currentDate,
            };
            const newServiceId = await Request.addRequestServices(serviceData);
            console.log('newServiceId',newServiceId)
            // Update customer order totals with single service total
            if (newServiceId) {
              const serviceTotal = Number(serviceData.total_cost) || 0;
              if (serviceTotal > 0) {
                const updatedOrderData = {
                  sub_total: serviceTotal,
                  total: serviceTotal,
                  grandTotal: serviceTotal,
                  timestamp: currentDate,
                };
                await CustomerOrder.updateCustomerOrder(newCustomerOrder, updatedOrderData);
                console.log('Updated customer order with service total:', updatedOrderData);
              }
            }
          }
        } catch (error) {
          console.error('Error adding default service for request estimate:', error);
        }
      }
    }

    if(newRequestId){
      emitProgress('duplicating_attachments', `Duplicating request attachments...`, 75);
      const oldRequestAttachements = await Request.getAllRequestAttachments(request_id)
      console.log('oldRequestAttachementsssss',oldRequestAttachements)
      if(oldRequestAttachements?.length > 0){
        for (let attachment of oldRequestAttachements) {
          const plainAttachment = attachment.get ? attachment.get({ plain: true }) : attachment;
          console.log('plainAttachmenttt',plainAttachment)
          let duplicateAttachment = {
            ...plainAttachment,
            id:undefined,
            request_id : newRequestId,
            approved:0,
            approved_by:null,
            location_ids:null,
          };
     
        const newAttachments =  await Request.addRequestAttachments(duplicateAttachment)
          // ✅ Convert PDF to PNG after duplicating attachment
          // const isPdfFile =
          //   duplicateAttachment.file_type === 'application/pdf' ||
          //   duplicateAttachment.file_type === 'pdf' ||
          //   duplicateAttachment.extension === 'pdf';

          // if (isPdfFile && newAttachments) {
          //   try {
          //     await convertPdfToPngHelper(newAttachments);
          //     console.log(`PDF conversion completed for duplicated document ID: ${newAttachments}`);
          //   } catch (conversionError) {
          //     console.error(
          //       `Error converting duplicated PDF ${newAttachments}:`,
          //       conversionError
          //     );
          //   }
          // }

        console.log('newAttachmentsssss',newAttachments)
    }
      }
    }

    // if(newRequestId){
    //   emitProgress('updating_tab_counts', `Updating user tab counts...`, 85);
    //   console.log('newClientUserId',newClientUserId)
    //   const allUserIds = await Admin.getAllUserIds();
    //   const usersToUpdate = [...new Set([userId, newClientUserId, ...allUserIds].filter(Boolean))];
    //   console.log('usersToUpdateee',usersToUpdate)
    //   await Promise.all(usersToUpdate.map(async (uid) => {
    //     const userOrderCount = await Request.getSalesOrderByUserId(uid);
    //     if (!userOrderCount || userOrderCount === "No record found") {
    //       const SalesOrderData = {
    //         open_count: 1,
    //         open_count_ids: [newRequestId],
    //         timestamp: currentDate,
    //         user_id: uid,
    //       };
    //       await Request.addSalesOrderTabCount(SalesOrderData);
    //     } else {
    //       await Request.salesOrderIncrementField(uid, "open_count", 1, "open_count_ids", newRequestId, true);
    //     }
    //   }));
    // }

    if(newRequestId){
      emitProgress('creating_notifications', `Creating notifications and activities...`, 95);
      const isEstimate = originalRequest?.quotation === 1;
      let notification_msg = `${isEstimate ? 'Estimate' : 'Sales Order'} has been duplicated`
      let notification_url = `official-request/view/${newRequestId}`

      let adminIds = await Admin.getAllAdminIds();
      if (!adminIds.includes(userId)) {
          adminIds.push(userId);
        }
      console.log('adminIds  1',adminIds)
      for (let eachadminIds of adminIds) {
        if (eachadminIds) {
          let incrementResult = await Admin.incrementField(eachadminIds, 'noti_count', 1)
          let addNotification = {
            user_id: eachadminIds,
            url: notification_url,
            timestamp: moment().unix(),
            message: notification_msg,
            status: 1,
            from_id: userId,
          }
          const addNotificationResult = await Admin.addNotification(addNotification);
        }
      }
      // Add activity/update to NEW request activities
      try {
        let requestActivity = {}
        requestActivity['request_id'] = newRequestId;
        requestActivity['activity'] = notification_msg;
        requestActivity['added_by'] = userId
        requestActivity['timestamp'] = currentDate
        requestActivity['status'] = 1
        console.log('[DUPLICATE-ACTIVITY] 📝 Adding activity to new request:', requestActivity);
        let reqActivityId = await RequestActivity.addRequestUpdate(requestActivity)
        console.log('[DUPLICATE-ACTIVITY] ✅ Activity added to new request successfully:', reqActivityId);
      } catch (error) {
        console.error('[DUPLICATE-ACTIVITY] ❌ Error adding activity to new request:', error);
      }
      
      // Add activity/update to ORIGINAL request indicating it has been duplicated
      try {
        let originalRequestActivity = {}
        originalRequestActivity['request_id'] = request_id;
        originalRequestActivity['activity'] = `This ${isEstimate ? 'estimate' : 'sales order'} has been duplicated`;
        originalRequestActivity['added_by'] = userId
        originalRequestActivity['timestamp'] = currentDate
        originalRequestActivity['status'] = 1
        console.log('[DUPLICATE-ACTIVITY] 📝 Adding activity to original request:', originalRequestActivity);
        let originalReqActivityId = await RequestActivity.addRequestUpdate(originalRequestActivity)
        console.log('[DUPLICATE-ACTIVITY] ✅ Activity added to original request successfully:', originalReqActivityId);
      } catch (error) {
        console.error('[DUPLICATE-ACTIVITY] ❌ Error adding activity to original request:', error);
      }
    }
    const isEstimate = !isSalesOrderDuplicate && originalRequest?.quotation === 1;
    emitProgress('complete', `${isEstimate ? 'Estimate' : 'Sales order'} duplicated successfully!`, 100);
    return res.send({ status: 1,request_id:newRequestId, message: `${isEstimate ? 'Estimate' : 'Sales Order'} duplicated successfully`, newRequestId });
    // emitProgress('complete', `Sales order duplicated successfully!`, 100);
    // return res.send({ status: 1, message: 'Request duplicated successfully', newRequestId });
  } catch (error) {
    console.error('Error in duplicating request:', error);
    emitProgress('error', `Error duplicating sales order: ${error.message}`, 0);
    return res.status(500).send({ status: 0, message: 'Internal server error', error });
  }
};

const sendToClientEstimate =async(req,res)=>{
  try {
    const {request_id, client, client_approve, userId} = req.body
    const data={
      send_to_client : 1,
      quotation_status : client_approve == 0 ? 4 : 9
    }
    let updated = await Request.updateRequets(request_id, data);
    
    // const clientUser = await Client.getClientById(client);
    // const clientUserId = clientUser?.user_id;

    // if (clientUserId) {
    //   await Request.EstimateTabIncrementField(clientUserId, "open_count", 1, "open_count_ids", request_id, true);   
    // } else {
    //   console.warn("Client user ID not found, skipping count update.");
    // }
    if (updated) {
      return res.send({ status: 1 });
    }
    res.send({ status: 0 });

  } catch (error) {
    console.log("error",error)
    res.send({ error:error, message:"server error" })
  }
}

//////////////////////////////////////////quickbook
const deleteEstimateInQuickBooks = async (estimateId) => {
  let token = await getNewAccessToken();

  const postDeleteEstimate = async (useNewToken = true) => {
    if (useNewToken) {
      console.log('Refreshing access token...');
      token = await getNewAccessToken();
    }

    try {
      const apiUrl = PRODUCTION
      ? `https://quickbooks.api.intuit.com/v3/company/${realmId}`
      : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}`;

      // Step 1: Retrieve the estimate by ID to get SyncToken
      const getEstimateResponse = await axios.get(
        // `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/estimate/${estimateId}`,
        `${apiUrl}/estimate/${estimateId}`,
        {
          headers: {
            Authorization: `Bearer ${token}`,
            'Content-Type': 'application/json',
            Accept: 'application/json',
          },
        }
      );

      if (getEstimateResponse.status === 200 && getEstimateResponse.data.Estimate) {
        const estimateData = getEstimateResponse.data.Estimate;

        // Step 2: Send the delete request with SyncToken
        const deleteEstimateResponse = await axios.post(
          // `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/estimate?operation=delete`,
          `${apiUrl}/estimate?operation=delete`,
          {
            Id: estimateData.Id,
            SyncToken: estimateData.SyncToken, // SyncToken is required for deletion
          },
          {
            headers: {
              Authorization: `Bearer ${token}`,
              'Content-Type': 'application/json',
              Accept: 'application/json',
            },
          }
        );

        if (deleteEstimateResponse.status === 200) {
          console.log("Estimate has been successfully deleted in QuickBooks:", deleteEstimateResponse.data);
        } else {
          console.error("Failed to delete the estimate in QuickBooks:", deleteEstimateResponse.data);
        }
      } else {
        console.log(`Estimate with ID '${estimateId}' not found in QuickBooks.`);
      }
    } catch (error) {
      console.error("Error deleting estimate in QuickBooks:", JSON.stringify(error.response?.data.Fault?.Error, null, 2) || error.message);
      const faultType = error.response?.data?.Fault?.type;
      if (faultType === 'AUTHENTICATION') {
        console.log('Access token expired, refreshing token...');
        try {
          // Retry the API call with a new access token
          await postDeleteEstimate(true);
        } catch (refreshError) {
          console.error('Error refreshing token:', refreshError.response?.data || refreshError.message);
        }
      } else {
        console.error("Other error deleting estimate in QuickBooks:", JSON.stringify(error.response?.data.Fault?.Error, null, 2) || error.message);
      }
    }
  };

  await postDeleteEstimate();
};


const deleteInvoiceInQuickBooks = async (invoiceId) => {
  let token = await getNewAccessToken();

  const postDeleteInvoice = async (useNewToken = true) => {
    if (useNewToken) {
      console.log('Refreshing access token...');
      token = await getNewAccessToken();
    }

    try {
      const apiUrl = PRODUCTION
      ? `https://quickbooks.api.intuit.com/v3/company/${realmId}`
      : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}`;

      // Step 1: Retrieve the invoice by ID to get SyncToken
      const getInvoiceResponse = await axios.get(
        // `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/invoice/${invoiceId}`,
        `${apiUrl}/invoice/${invoiceId}`,
        {
          headers: {
            Authorization: `Bearer ${token}`,
            'Content-Type': 'application/json',
            Accept: 'application/json',
          },
        }
      );

      if (getInvoiceResponse.status === 200 && getInvoiceResponse.data.Invoice) {
        const invoiceData = getInvoiceResponse.data.Invoice;

        // Step 2: Send the delete request with SyncToken
        const deleteInvoiceResponse = await axios.post(
          // `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/invoice?operation=delete`,
          `${apiUrl}/invoice?operation=delete`,
          {
            Id: invoiceData.Id,
            SyncToken: invoiceData.SyncToken, // SyncToken is required for deletion
          },
          {
            headers: {
              Authorization: `Bearer ${token}`,
              'Content-Type': 'application/json',
              Accept: 'application/json',
            },
          }
        );

        if (deleteInvoiceResponse.status === 200) {
          console.log("Invoice has been successfully deleted in QuickBooks:", deleteInvoiceResponse.data);
        } else {
          console.error("Failed to delete the invoice in QuickBooks:", deleteInvoiceResponse.data);
        }
      } else {
        console.log(`Invoice with ID '${invoiceId}' not found in QuickBooks.`);
      }
    } catch (error) {
      console.error("Error deleting invoice in QuickBooks:", JSON.stringify(error.response?.data.Fault?.Error, null, 2) || error.message);
      const faultType = error.response?.data?.Fault?.type;
      if (faultType === 'AUTHENTICATION') {
        console.log('Access token expired, refreshing token...');
        try {
          // Retry the API call with a new access token
          await postDeleteInvoice(true);
        } catch (refreshError) {
          console.error('Error refreshing token:', refreshError.response?.data || refreshError.message);
        }
      } else {
        console.error("Other error deleting invoice in QuickBooks:", JSON.stringify(error.response?.data.Fault?.Error, null, 2) || error.message);
      }
    }
  };

  await postDeleteInvoice();
};


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

const deleteRequestById = async (req, res) => {
  try {
    const { request_id, request, userId} = req.body;
    let oldReqData = await Request.getRequestById(request_id)
    let isSplitOrder = oldReqData[0]?.is_split_order;
    const projectCategory = oldReqData[0]?.project_category;
    const parentFacilityId = oldReqData[0]?.for_parent_facility;
    let qbErr = null
    let currentTime = moment().unix()
    if (oldReqData && oldReqData[0]?.map_service) {
      let data = { 
        assigned_sales_order : null
      }
      let updateRoute = await Routes.updateRoute(data,oldReqData[0]?.map_service);
    }
    
    // const removeSalesOrderIdFromTab = async (userId, requestId) => {
    //   const record = await  Request.getSalesOrderCountByUser(userId);
    //   if (!record) return;
    
    //   const tabFields = [
    //     'open',
    //     'approved',
    //     'in_production',
    //     'installs',
    //     'pickup',
    //     'shipping',
    //     'invoicing',
    //     'completed'
    //   ];
    
    //   for (let field of tabFields) {
    //     const fieldIds = `${field}_count_ids`;
    //     const countField = `${field}_count`;
    
    //     const ids = record[fieldIds]?.split(',') || [];
    //     if (ids.includes(requestId.toString())) {
    //       await Request.salesOrderIncrementField(userId, countField, -1, fieldIds, requestId, false);
    //       console.log(`Sales Order count updated for user ${userId}, removed ${requestId} from ${field}`);
    //       break; // found and removed, exit loop
    //     }
    //   }
    // };
    
    if (isSplitOrder && projectCategory != 15) {
      try {
        const facilityTypes = (oldReqData[0]?.facility_type || "")
          .split(",")
          .map(v => v.trim())
          .filter(v => v !== "");
        const targetFacilityId = facilityTypes.filter(id => id !== String(FACILITY_ID));
        let deleteBuildOrderFromOtherInstallationsResult = await deleteBuildOrderFromOtherInstallations(request_id, targetFacilityId);
        console.log('deleteBuildOrderFromOtherInstallationsResult---', deleteBuildOrderFromOtherInstallationsResult);
      } catch (error) {
        console.log("split build order deleting error", error)
      }
    }

    if (isSplitOrder && projectCategory == 15 && parentFacilityId == 1) {
      try {
        const facilityTypes = (oldReqData[0]?.facility_type || "")
          .split(",")
          .map(v => v.trim())
          .filter(v => v !== "");
        const targetFacilityId = facilityTypes.filter(id => id !== String(FACILITY_ID));

        // Use origin request id if this is a build-order copy so that
        // all other facility orders that share the same origin are deleted.
        const originRequestId = oldReqData[0]?.origin_facility_request_id || request_id;

        const deleteBuildOrderFromOtherInstallationsResult =
          await SplitManagementController.BuildOrderFromOtherInstallationsDelete(
            originRequestId,
            targetFacilityId,
            request_id // Pass the build order ID
          );

        console.log(
          'BuildOrderFromOtherInstallationsDeleteResult---',
          deleteBuildOrderFromOtherInstallationsResult
        );
      } catch (error) {
        console.log("BuildOrderFromOtherInstallationsDelete error", error)
      }
    }

    if (isSplitOrder && projectCategory == 15 && parentFacilityId == 2) {
      try {
        const facilityTypes = (oldReqData[0]?.facility_type || "")
          .split(",")
          .map(v => v.trim())
          .filter(v => v !== "");
        const targetFacilityId = facilityTypes.filter(id => id !== String(FACILITY_ID));

        // Use origin request id if this is a build-order copy so that
        // all other facility orders that share the same origin are deleted.
        const originRequestId = oldReqData[0]?.origin_facility_request_id || request_id;
        const originFacilityId = oldReqData[0]?.origin_facility_id ;
        console.log('originRequestId.......',originRequestId);
        console.log('originFacilityId.......',originFacilityId);
        const getParentRequest = await SplitManagementController.getRequestByMirrorBuildOrder(originRequestId,originFacilityId);
        console.log('getParentRequest.......',getParentRequest);
        
        // The parent order ID is stored in origin_facility_request_id of the build order!
        const parentRequestId = getParentRequest?.origin_facility_request_id || originRequestId;
        console.log('parentRequestId.......',parentRequestId);
        const deleteBuildOrderFromOtherInstallationsResult =
          await SplitManagementController.deleteMirrorBuildOrderFromOtherInstallations(
            parentRequestId,
            targetFacilityId,
            request_id // Pass the build order ID
          );

        console.log(
          'BuildOrderFromOtherInstallationsDeleteResult---',
          deleteBuildOrderFromOtherInstallationsResult
        );
        
        // Now also delete the parent order from the current facility since this was requested
        if (parentRequestId && parentRequestId !== request_id) {
           await Request.deleteRequestById(parentRequestId);
           console.log(`Parent order ${parentRequestId} deleted from current facility.`);
        }

      } catch (error) {
        console.log("BuildOrderFromOtherInstallationsDelete error", error)
      }
    }
  
    // ✅ CRITICAL: Delete request and associated project (must complete before response)
    let result = await Request.deleteRequestById(request_id)
    if (result) {
      const project_id = await Project.getProjectByRequestId(request_id)
      if (project_id) {
        try {
          const installations = await Installation.getInstallationsByProjectId(project_id);
          await Installation.deleteInstallationUsingProjectId(project_id);
          await ProjectTask.deleteStaffTaskTimelogs(project_id);
          await Project.deleteProject(project_id);
          console.log('deleteddddddddddddddddd')
        } catch (error) {
          console.error('❌ Error during project deletion:', error);
      }
      }

      // ✅ OPTIMIZATION: Prepare response data early
      let qbErr = null;
      
      // ✅ OPTIMIZATION: Send response immediately after critical operations
      res.send({ status: 1, data: result, message: 'Request deleted successfully', qbErr: qbErr });

      // ✅ OPTIMIZATION: Handle all notifications/emails/SMS/tab counts/QuickBooks async (non-blocking)
      const handleCleanupAsync = async () => {
        try {
          let notification_msg = `${request ? 'Sales Order' : 'Estimate'} <strong> ${oldReqData[0]?.['name']}.</strong> has been deleted.`;
          let notification_url = `official-request/view/${request_id}`;
      
      let adminIds = await Admin.getAllAdminIds();
      let adminMangerIds = await Admin.getAllAdminAndManagerUserIds();
          
          // Admin notifications (parallelized)
          const adminNotificationPromises = adminIds.map(async (eachadminIds) => {
            await Admin.incrementField(eachadminIds, 'noti_count', 1);
            await Admin.addNotification({
          user_id: eachadminIds,
          url: notification_url,
          timestamp: moment().unix(),
          message: notification_msg,
          status: 1,
          from_id: userId || 1,
            });
          });
          await Promise.all(adminNotificationPromises);
          
          // Activity log
          await RequestActivity.addRequestUpdate({
            request_id: request_id,
            activity: notification_msg,
            added_by: userId || 1,
            timestamp: currentTime
          });
          
          // QuickBooks deletion (async, non-blocking)
          try {
            if (request) {
              if (oldReqData[0]?.estimate_id && oldReqData[0]?.syncWithQuickbooks) {
                await deleteInvoiceInQuickBooks(oldReqData[0]?.estimate_id);
          }
            } else {
              if (oldReqData[0]?.estimate_id && oldReqData[0]?.syncWithQuickbooks) {
                await deleteEstimateInQuickBooks(oldReqData[0]?.estimate_id);
      }
            }
      } catch (error) {
            console.log("QuickBooks deletion error", error);
      }

          // Tab count updates
      // const requestStatus = oldReqData[0]?.quotation_status;
      // const requestQuotation = oldReqData[0]?.quotation;

      // const allAdminUserIds = await Admin.getAllUserIds();
      const partnerUserIds = await RequestPartner.fetchPartnerUserIds(request_id);
      // const usersToUpdate = [...new Set([userId, ...partnerUserIds, ...allAdminUserIds].filter(Boolean))];

          // Estimate tab count updates (parallelized)
      //   if (requestQuotation === 1) {
      //       await Promise.all(usersToUpdate.map(async (userId) => {
      //     if (requestStatus === 2) {
      //       await Request.EstimateTabIncrementField(userId, 'open_count', -1, 'open_count_ids', request_id, false);
      //           console.log("estimate open count updated");
      //     } else if (requestStatus === 4) {
      //       await Request.EstimateTabIncrementField(userId, 'approved_count', -1, 'approved_count_ids', request_id, false);
      //           console.log("estimate approved count updated");
      //   }
      // }));
      //     }

          // Sales order tab count updates (parallelized)
      // if (oldReqData[0]?.quotation === 0) {
      //   await Promise.all(usersToUpdate.map(async (uid) => {
      //     await removeSalesOrderIdFromTab(uid, request_id);
      //   }));
      // }
      //     console.log("Request count updated");
          
          // Email and SMS notifications (parallelized)
          const allUsersToCheck = [...new Set([userId, ...partnerUserIds, ...adminMangerIds].filter(Boolean))];
          console.log('allUsersTocheckkkkkkk', allUsersToCheck);
          
          const emailSmsPromises = allUsersToCheck.map(async (userIdToCheck) => {
            try {
            const approver = await Admin.getUserNotificationAndPermissionSettings(userIdToCheck);
              if (!approver) return;

              const itemType = (oldReqData[0]?.quotation === 1 && oldReqData[0]?.status === 8) ? 'Estimate' : "Sales Order";
          const itemName = oldReqData[0]?.name;

              const emailPromise = (async () => {
                if (approver?.emailEnabled === 1 && approver?.email) {
          const subject = `⚠️ ${itemType} Deleted: ${itemName}`;
          const welcome = `Hello ${approver.name},`;
          const email_content = `The ${itemType.toLowerCase()} "${itemName}" has been deleted from the system. Please review your records if necessary.`;

          let message = deletedItemEmailTemplate({
            user: approver,
            itemType: itemType,
            itemName: oldReqData[0]?.name,
            baseUrl: BASE_URL,
          });

          message = message.replace('[emailContent]', email_content);
          message = message.replace('[CONTENT]', welcome);
          message = message.replace('[LOGIN_URL]', `${BASE_URL}`);

          const mailOptions = {
            to: approver.email,
            from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
            subject,
            text: subject,
            html: message,
          };

            await transporter.sendMail(mailOptions);
            console.log(`✅ Delete email sent to: ${approver.email}`);
          }
              })();

              const smsPromise = (async () => {
        if (approver?.smsEnabled === 1) {
          const smsContent = 
          `⚠️ ${itemType} Deleted\n` + 
          `📄 ${itemType}: "${itemName}"\n` + 
          `🔗 View: ${BASE_URL}\n` +
          `— SignWise`;

          const recipientPhone = approver.phone || TWILIO_PHONE;
          const Phone_country_code = approver.phone_country_code || '';

                  await sendMessageToPhoneNumber(recipientPhone, smsContent, Phone_country_code);
            console.log(`✅ Delete SMS sent to ${approver.name || 'User'} (${recipientPhone})`);
                }
              })();

              await Promise.all([emailPromise, smsPromise]);
          } catch (err) {
              console.error(`❌ Error sending notifications to user ${userIdToCheck}:`, err);
          }
          });

          await Promise.all(emailSmsPromises);
        } catch (error) {
          console.error('Error in async cleanup handler:', error);
        }
      };

      // ✅ OPTIMIZATION: Run non-critical operations asynchronously (fire and forget)
      // These will complete in the background without blocking the response
      handleCleanupAsync().catch(error => {
        console.error('Error in async cleanup operations:', error);
      });
    } else {
      res.send({ status: 0, message: 'Request deleted failed',qbErr:qbErr })
    }
  } catch (error) {
    console.log(error, 'error in delete request')
    res.send({ status: 0, message: 'failed to delete request' })
  }
}
const addRequetPartners = async (req, res) => {
  const { request_id, values, userId, userName, userType } = req.body;
  let partnerData = values.clients
  const currentTime = moment().unix();
  let subject = 'A new request awaiting your acceptance';
  let email_content = `You are invited to join New request by ` + userName;
  if (req.body.values.isCustomMessage) {
    subject = req.body.values.subject
    email_content = req.body.values.emailcontent
  }
  if (partnerData && partnerData.length > 0) {

    let client = {}
    let insertedstatus = null
    for (let eachPartner of partnerData) {

      client['request_id'] = request_id,
        client['last_seen'] = currentTime,
        client['timestamp'] = currentTime,
        client['added_by'] = userId,
        client['partner_id'] = eachPartner.id
      client['accept_status'] = 1;
      client['status'] = 1
      insertedstatus = await RequestPartner.addRequestPartners(client)
    }

    if (insertedstatus) {

      let partnerIds = partnerData.map(eachClient => eachClient.id);
      let tempRequestPartnerArr = partnerData.map(eachClient => eachClient.id + ',' + eachClient.name);

      let requestActivity = {}
      for (let eachReqPartner of tempRequestPartnerArr) {

        requestActivity['request_id'] = request_id,
          requestActivity['partner_id'] = eachReqPartner.split(',')[0],
          requestActivity['partner_name'] = eachReqPartner.split(',')[1],
          requestActivity['activity'] = `Client, ${eachReqPartner.split(',')[1]} has been added to the request`,
          requestActivity['added_by'] = userId,
          requestActivity['timestamp'] = currentTime,
          requestActivity['addestatusd_by'] = 1

        let activityResult = await RequestUpdates.addRequestUpdates(requestActivity)


      }
      let requestdata = {}
      requestdata['customer_id'] = partnerData[0]['id'];
      requestdata['customer_name'] = partnerData[0]['name'];
      requestdata['customer_phone'] = partnerData[0]['phone'];
      requestdata['customer_address'] = partnerData[0]['address'];
      let orderId = await CustomerOrder.getCustomerOrderRequestId(request_id);
      console.log("#########################customer name", orderId.customer_name);
      if (empty(orderId.customer_name)) {
        await CustomerOrder.updateCustomerOrder(orderId.id, requestdata);
      }

      const request = await Request.getRequestById(request_id)

      const requestTeams = await Request.getRequestPartnersByIds(partnerIds)

      const requestTeamUserIds = await Request.getRequestTeamUserIds(request_id)
      const adminIds = await Admin.getAllAdminIds()

      if (requestTeams.length > 0) {
        for (const requestTeam of requestTeams) {
          // Notification 
          let notification_msg =
            `Member <strong>"${requestTeam.name}"</strong> has been added to the request <strong>"${request[0].name}"</strong>."`
          let notification_url = `official-request/view/${request_id}`

          // Alert assigned user of updation
          let incrementResult = await Admin.incrementField(requestTeam.id, "noti_count", 1);
          let notificationUser = {
            user_id: requestTeam.id,
            url: notification_url,
            timestamp: currentTime,
            message: notification_msg,
            status: 1,
            from_id: userId,
          }
          const addNotificationResult = await Admin.addNotification(notificationUser);
          for (let request_team_us_id of requestTeamUserIds) {
            // Alert assigned user of updation
            let incrementResult = await Admin.incrementField(request_team_us_id, 'noti_count', 1)
            let addNotification = {
              user_id: request_team_us_id,
              url: notification_url,
              timestamp: currentTime,
              message: notification_msg,
              status: 1,
              from_id: userId,
            }
            const addNotificationResult = await Admin.addNotification(addNotification);
          }
          for (let adminids of adminIds) {
            let incrementResult = await Admin.incrementField(adminids, 'noti_count', 1)
            let addNotification = {
              user_id: adminids,
              url: notification_url,
              timestamp: currentTime,
              message: notification_msg,
              status: 1,
              from_id: userId,
            }
            const addNotificationResult = await Admin.addNotification(addNotification);
          }


        }
      }
      //email send
      // let requestEmailSend=await Request.getAllRequests(request_id)

      for (let eachPartner of partnerData) {

        let welcome = "Hello " + eachPartner['company_name'] + ","

        sgMail.setApiKey(SENDGRID_API_KEY)
        let message = rfqEmail()
        message = message.replace('[emailContent]', email_content)
        message = message.replace('[CONTENT]', welcome)

        message = message.replace('[LOGIN_URL]', `${BASE_URL}`)
        const msg = {
          to: eachPartner.email, // Change to your recipient
          from: 'nushara@skyniche.com', // Change to your verified sender
          subject: subject,
          text: subject,
          html: message,
        }
        // sgMail
        //   .send(msg)
        //   .then(() => {
        //     console.log('Email sent')
        //   })
        //   .catch((error) => {
        //     console.error(error)
        //   })

        const mailOptions = {
          to:  eachPartner.email, 
          from: '"SignWise Solutions" <noreply@signwisesolutions.com>', //'nushara@skyniche.com', // Change to your verified sender
          subject: subject,
          text: subject,
          html: message,
        }
        try {
          if(SEND_EMAIL){
          await transporter.sendMail(mailOptions);
          console.log('Email sent');
          }
        } catch (error) {
          console.error('##########Err', error);
         
        }
      }


      res.send({ status: 1, message: 'Partners added successfully' });
    } else {
      res.send({ status: 0, message: 'Failed to add partners' });
    }
  }
}
const requestSearch = async (req, res) => {
  try {
    let userType = req.body.userType
    let content = req.body.content
    let userId = req.body.userId
    let result = await Request.searchRequests(null, userType, userId, content)
    if (!empty(result)) {
      res.send({ status: 1, message: 'Searched Details', data: result })
    } else {
      res.send({ status: 0, message: 'Error in Searched Details' })
    }
  } catch (error) {
    console.log(error, 'Error in searching requests')
  }
}
const acceptOrRejectRequest = async (req, reply) => {
  try {
    let { requestId, requestPartnerId, acceptStatus, userId } = req.body
    let currentDate = Math.round(moment().valueOf() / 1000);

    let acceptQuery = await RequestPartner.acceptOrRejectRequest(requestPartnerId, { accept_status: acceptStatus });
    if (acceptQuery) {
      let request = await Request.getRequestById(requestId);
      let partner = await RequestPartner.getRequestPartnerById(requestPartnerId);
      if (acceptStatus == 1) {
        let requestdata = {}
        requestdata['customer_id'] = partner['id'];
        requestdata['customer_name'] = partner['name'];
        requestdata['customer_phone'] = partner['phone'];
        requestdata['customer_address'] = partner['address'];
        let orderId = await CustomerOrder.getCustomerOrderRequestId(requestId);
        await CustomerOrder.updateCustomerOrder(orderId.id, requestdata);
        let reqActivityMsg = "Request has been accepted"
        let notificationMsg = `The member ${partner.name} accepted the request ${request.name}.`;
        let notificationUrl = `request/view/${requestId}`;

        // Alert to user of new added product
        if (!empty(partner['user_id'])) {
          const incrementResult_User = await Admin.incrementField(partner.name, "noti_count", 1);


          let notificationData_user = {
            user_id: partner.user_id,
            url: notificationUrl,
            timestamp: currentDate,
            message: notificationMsg,
            status: 1,
            from_id: userId,
          };

          const addNotificationResult_user = await Admin.addNotification(notificationData_user);
        }


        let adminIds = await Admin.getAllAdminIds();
        let notificationData = {};

        for (const adminId of adminIds) {

          if (adminId != userId) {
            const incrementResult = await Admin.incrementField(adminId, "noti_count", 1);

            notificationData = {
              user_id: adminId,
              url: notificationUrl,
              timestamp: currentDate,
              message: notificationMsg,
              status: 1,
              from_id: userId,
            };

            const addNotificationResult = await Admin.addNotification(notificationData);

          }
        }
        let requestActivity = {}
        requestActivity['request_id'] = requestId;
        requestActivity['activity'] = reqActivityMsg
        requestActivity['added_by'] = userId
        requestActivity['timestamp'] = currentDate
        let reqActivityId = await RequestActivity.addRequestUpdate(requestActivity)

      }
      if (acceptStatus == 1) {
        reply.send({ status: 1, message: "You have accepted the request" });
      }
      else {

        let notificationMsg = `The member ${partner.name} rejected the request ${request[0].name}.`;
        let notificationUrl = `request/view/${requestId}`;

        // Alert to user of new added product
        if (!empty(partner['user_id'])) {
          const incrementResult_User = await Admin.incrementField(partner.name, "noti_count", 1);


          let notificationData_user = {
            user_id: partner.user_id,
            url: notificationUrl,
            timestamp: currentDate,
            message: notificationMsg,
            status: 1,
            from_id: userId,
          };

          const addNotificationResult_user = await Admin.addNotification(notificationData_user);
        }


        let adminIds = await Admin.getAllAdminIds();
        let notificationData = {};

        for (const adminId of adminIds) {

          if (adminId != userId) {
            const incrementResult = await Admin.incrementField(adminId, "noti_count", 1);

            notificationData = {
              user_id: adminId,
              url: notificationUrl,
              timestamp: currentDate,
              message: notificationMsg,
              status: 1,
              from_id: userId,
            };

            const addNotificationResult = await Admin.addNotification(notificationData);

          }
        }
        let reqActivityMsg = "Request has been rejected"
        let requestActivity = {}
        requestActivity['request_id'] = requestId;
        requestActivity['activity'] = reqActivityMsg
        requestActivity['added_by'] = userId
        requestActivity['timestamp'] = currentDate
        let reqActivityId = await RequestActivity.addRequestUpdate(requestActivity)
        reply.send({ status: 1, message: "You have rejected the request" });
      }
    }
    if (acceptStatus == 1) {
      reply.send({ status: 0, message: "Failed to accepted the request" });
    }
    else {
      reply.send({ status: 0, message: "Failed to rejected the request", error });
    }
  }
  catch (error) {
    console.log("error : ", error);
  }
}


//////////////////////////////////////Quickbook
const getInvoiceById = async (invoiceId) => {
  try {
    // Fetch the access token
    let token = await getNewAccessToken();

    const apiUrl = PRODUCTION
    ? `https://quickbooks.api.intuit.com/v3/company/${realmId}`
    : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}`;

    // Make the request to fetch the invoice by ID
    const response = await axios.get(
      // `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/invoice/${invoiceId}`,
      `${apiUrl}/invoice/${invoiceId}`,
      {
        headers: {
          Authorization: `Bearer ${token}`,
          'Content-Type': 'application/json',
          Accept: 'application/json'
        }
      }
    );    
    return response.data.Invoice;
  } catch (error) {
    console.error("Error fetching invoice from QuickBooks:", error.response ? error.response.data : error.message);

    // Check if the error is due to authentication and possibly retry fetching the token
    if (error.response && error.response.data.Fault && error.response.data.Fault.type === 'AUTHENTICATION') {
      console.error("Authentication error. Please check your access token.");
    }

    return null;
  }
};



const updateInvoiceStatus = async (invoice, status) => {
  try {
    // Properly await the token retrieval
    let token = await getNewAccessToken();

    const updatedInvoiceData = {
      Id: invoice.Id,
      SyncToken: invoice.SyncToken,
      TxnStatus: status,
      CustomField: [
        {
          DefinitionId: "1", // Replace with your CustomField DefinitionId
          Name: "Accepted",
          Type: "StringType",
          StringValue: status
        }
      ],
      sparse: true
    };

    const apiUrl = PRODUCTION
      ? `https://quickbooks.api.intuit.com/v3/company/${realmId}/invoice`
      : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/invoice`;

    // Make the API call to update the invoice
    const response = await axios.post(
      apiUrl,
      updatedInvoiceData,
      {
        headers: {
          Authorization: `Bearer ${token}`,
          'Content-Type': 'application/json',
          Accept: 'application/json'
        }
      }
    );

    if (response.status === 200) {
      console.log("Invoice status updated successfully:", response.data);
      return response.data;
    } else {
      console.error("Failed to update invoice status in QuickBooks:", response.data);
      return null;
    }
  } catch (error) {
    console.error("Error updating invoice status in QuickBooks:", error.response ? error.response.data : error.message);
    return null;
  }
};

let newInvoiceId = null;
let  newDocNum = null
///////////////////////////////////////////////////////////
const createJornalEntryForFinalInvoice = async ( totalJobCostAmount,serviceDescription,materialDescription,description,materialAmount,serviceAmount,date,customerDetails,projectTypeQuickBookId ) => {
  
  console.log("allamounts",totalJobCostAmount,materialAmount,serviceAmount)
  // let accountsReceivableDebitAccount =await getAccountIdByName("Accounts Receivable (A/R)")
  
  let accountReceivableAccId = PRODUCTION ? "1012000" : "1100"
  let accountsReceivableDebitAccount =await findAccountIdByAcctNum(accountReceivableAccId)
  let revenuematerialCreditAccount =await findAccountIdByAcctNum(4020000)
  let revenueLaborCreditAccount =await findAccountIdByAcctNum(4010000)
  // let accountsReceivableDebitAccount =await getAccountIdByName('Accounts Receivable')
  // let revenuematerialCreditAccount =await getAccountIdByName('Revenue – Materials')
  // let revenueLaborCreditAccount =await getAccountIdByName('Revenue – Labor')

  const journalEntryData = {
  Line: [
    {
      Id: "1",
      Description: description,
      Amount: totalJobCostAmount || 0,
      DetailType: "JournalEntryLineDetail",
      JournalEntryLineDetail: {
        PostingType: "Debit",
        AccountRef: {
          value: accountsReceivableDebitAccount,   //Accounts Receivable (A/R)
          name: "Accounts Receivable"
        },
        DepartmentRef: { value: departmentId },
         Entity: {
      Type: "Customer",
      EntityRef: customerDetails
    }
      }
    },
    {
      Id: "2",
      Description: materialDescription,
      Amount: materialAmount || 0,
      DetailType: "JournalEntryLineDetail",
      JournalEntryLineDetail: {
        PostingType: "Credit",
        AccountRef: {
          value: revenuematerialCreditAccount // 🔁 Revenue – Materials
        },
        DepartmentRef: { value: departmentId },
        ClassRef: { value: projectTypeQuickBookId }
      }
    },
    {
      Id: "3",
      Description: serviceDescription,
      Amount: serviceAmount , 
      DetailType: "JournalEntryLineDetail",
      JournalEntryLineDetail: {
        PostingType: "Credit",
        AccountRef: {
          value: revenueLaborCreditAccount // 🔁 Revenue – Labor
        },
        DepartmentRef: { value: departmentId },
        ClassRef: { value: projectTypeQuickBookId }
      }
    }
  ],
  TxnDate: date,
  PrivateNote: description
};
   
  
      try {
        let token = await getNewAccessToken();
        const quickbooksApiBaseUrl = PRODUCTION
          ? `https://quickbooks.api.intuit.com/v3/company/${realmId}`
          : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}`;
    
        // Make the API request to create the journal entry
        const journalEntryResponse = await axios.post(`${quickbooksApiBaseUrl}/journalentry`, journalEntryData, {
          headers: {
            'Authorization': `Bearer ${token}`,
            'Accept': 'application/json',
            'Content-Type': 'application/json'
          },
          timeout: 10000
        });
    
        // Log the success response
        console.log("Journal Entry created successfully for final invoice:", journalEntryResponse.data);
    
      } catch (err) {
        // Log the error details
        console.error("❌ Error creating Journal Entry for final invoice:", err.response?.data || err.message);
        if (err.response?.data?.Fault?.Error) {
          console.error("QuickBooks Error Details:", err.response.data.Fault.Error);
        } else {
          console.error("Unexpected Error:", err);
        }
      }
}

const convertEstimateToInvoice = async (estimateId,totalJobCostAmount,salesOrderName,productTotal,serviceTotal,projectTypeQuickBookId,paymentTermQuickbookId) => {
  if (!estimateId) {
    throw new Error('Estimate ID is required');
  }

  console.log("estimateId", estimateId);

  try {
    let token = await getNewAccessToken(); // Ensure you await the access token retrieval

    // Determine the API base URL based on the environment
    const quickbooksApiBaseUrl = PRODUCTION
      ? `https://quickbooks.api.intuit.com/v3/company/${realmId}`
      : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}`;

    const url = `${quickbooksApiBaseUrl}/estimate/${estimateId}?minorversion=65`;

    // Fetch the estimate details from QuickBooks
    let estimateResponse;
    try {
      estimateResponse = await axios.get(url, {
        headers: {
          'Authorization': `Bearer ${token}`,
          'Accept': 'application/json'
        },
        timeout: 10000 // Set a timeout as needed
      });
      console.log("Fetched estimate for conversion:", estimateResponse.data);
    } catch (error) {
      if (error.response && error.response.status === 401) {
        console.log('Access token expired, refreshing...');
        token = await getNewAccessToken(); // Refresh token
        estimateResponse = await axios.get(url, {
          headers: {
            'Authorization': `Bearer ${token}`,
            'Accept': 'application/json'
          },
          timeout: 10000 // Set a timeout as needed
        });
        console.log("Fetched estimate after refreshing token:", estimateResponse.data);
      } else {
        console.error('Error fetching estimate:', error.response ? error.response.data : error.message);
        throw error;
      }
    }

    const estimate = estimateResponse.data.Estimate;
    const customerDetails =estimate?.CustomerRef

    if (!estimate) {
      throw new Error('Estimate not found in QuickBooks');
    }

    // Get the inventory start date for the items
    const inventoryStartDate = moment(estimate.InvStartDate); // Assuming InvStartDate is part of the estimate

    // Set the transaction date to today if it's after the inventory start date
    const txnDate = moment().format('YYYY-MM-DD');

    // Prepare invoice data based on the estimate, including discount and tax details
    let invoiceData = {
      "CustomerRef": estimate.CustomerRef,
      "DepartmentRef": estimate.DepartmentRef || departmentId,
      "Line": estimate.Line.map(lineItem => {
        if (lineItem.SalesItemLineDetail && lineItem.SalesItemLineDetail.ItemRef) {
          return {
            "Amount": lineItem.Amount,
            "DetailType": "SalesItemLineDetail",
            "SalesItemLineDetail": {
              "ItemRef": lineItem.SalesItemLineDetail.ItemRef,
              "Qty": lineItem.SalesItemLineDetail.Qty,
              "UnitPrice": lineItem.SalesItemLineDetail.UnitPrice,
              "TaxCodeRef": lineItem.SalesItemLineDetail.TaxCodeRef
            }
          };
        } else if (lineItem.DetailType === 'DiscountLineDetail') {
          return {
            "Amount": lineItem.Amount,
            "DetailType": "DiscountLineDetail",
            "DiscountLineDetail": lineItem.DiscountLineDetail
          };
        } else {
          console.warn('Unhandled line item type:', lineItem);
          return null; // Skip this line item if it's invalid or not supported
        }
      }).filter(item => item !== null), // Filter out null values
      "TxnDate": txnDate, // Set transaction date to the determined date
      "DueDate": moment().add(30, 'days').format('YYYY-MM-DD'), // Example: Net 30 terms
      "PrivateNote": estimate.PrivateNote || "Converted from estimate",
      // Include tax details if available
      "TxnTaxDetail": {
        "TxnTaxCodeRef": estimate.TxnTaxDetail.TxnTaxCodeRef,
        "TotalTax": estimate.TxnTaxDetail.TotalTax,
        "TaxReviewStatus": estimate.TxnTaxDetail.TaxReviewStatus,
        "TaxLine": estimate.TxnTaxDetail.TaxLine 
      },
      "CustomField": estimate.CustomField,
      "ClassRef": estimate.ClassRef,
      "BillEmail":estimate.BillEmail,
      "BillAddr":estimate.BillAddr,
      "ShipAddr":estimate.ShipAddr,
    };

    if (paymentTermQuickbookId) {
      invoiceData.SalesTermRef = {
        value: String(paymentTermQuickbookId)
      };
    }
    // Create the invoice in QuickBooks
    let invoiceResponse;
    try {
      invoiceResponse = await axios.post(`${quickbooksApiBaseUrl}/invoice`, invoiceData, {
        headers: {
          'Authorization': `Bearer ${token}`, // Ensure the latest token is used here
          'Accept': 'application/json',
          'Content-Type': 'application/json'
        },
        timeout: 10000 // Add timeout here as well
      });
      console.log("Invoice creation response:", invoiceResponse.data);
    } catch (error) {
      if (error.response && error.response.status === 401) {
        console.log('Access token expired again, refreshing...');
        token = await getNewAccessToken(); // Refresh token
        invoiceResponse = await axios.post(`${quickbooksApiBaseUrl}/invoice`, invoiceData, {
          headers: {
            'Authorization': `Bearer ${token}`,
            'Accept': 'application/json',
            'Content-Type': 'application/json'
          },
          timeout: 10000 // Add timeout here as well
        });
        console.log("Invoice creation response after refreshing token:", invoiceResponse.data);
      } else {
        console.error('Error creating invoice:', error.response ? error.response.data : error.message);
        throw error;
      }
    }

    const invoice = invoiceResponse.data;
    await markEstimateAsConverted(estimateId); // Mark the estimate as converted
    console.log("New Invoice ID:", invoice.Invoice.Id);
    newInvoiceId = invoice.Invoice.Id;
    newDocNum = invoice.Invoice.DocNumber;



   
try {
  // Prepare Journal Entry for Material Deduction
 

  let date =moment().format('YYYY-MM-DD')
 const halfJobCostAmount = parseFloat((totalJobCostAmount || 0) / 2).toFixed(2); 
 const serviceAmount = parseFloat(serviceTotal || 0).toFixed(2);
 const materialAmount = parseFloat(productTotal || 0).toFixed(2);
  const description = `Final invoice for sales order "${salesOrderName}" has been approved with a total amount of $${totalJobCostAmount}.`;
  const serviceDescription = `Final invoice for sales order "${salesOrderName}" has been approved with a service amount of $${serviceAmount}.`;
  const materialDescription = `Final invoice for sales order "${salesOrderName}" has been approved with a material amount of $${materialAmount}.`;

  // const finalInvResponse = await createJornalEntryForFinalInvoice(totalJobCostAmount,serviceDescription,materialDescription,description,materialAmount,serviceAmount,date,customerDetails,projectTypeQuickBookId)

 
} catch (error) {
  console.error("Error creating Journal Entry for material deduction:", error.response ? error.response.data : error.message);
}
// 🔵 ADDED CODE: End Deduct Material Expenses


    return invoice.Invoice.Id;

  } catch (error) {
    console.error("Error converting estimate to invoice in QuickBooks: ", error.response ? error.response.data : error.message);
    throw error;
  }
};

const markEstimateAsConverted = async (estimateId) => {
  try {
      let token = await getNewAccessToken();
      const quickbooksApiBaseUrl = PRODUCTION
          ? `https://quickbooks.api.intuit.com/v3/company/${realmId}`
          : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}`;

      // Fetch the latest estimate to get the SyncToken and CustomerRef
      const estimateResponse = await axios.get(`${quickbooksApiBaseUrl}/estimate/${estimateId}`, {
          headers: {
              'Authorization': `Bearer ${token}`,
              'Accept': 'application/json'
          },
          timeout: 10000
      });

      const estimate = estimateResponse.data.Estimate;
      console.log("estimatedataaaaaaaaaaaa",estimate)
      const syncToken = estimate.SyncToken;

      // Prepare update data to mark the estimate as converted
      // const updateData = {
      //     "Id": estimateId,
      //     "SyncToken": syncToken,
      //     "TxnStatus": "Converted", 
      //     "PrivateNote": "This estimate has been converted to an invoice.",
      //     "Line": estimate.Line, 
      //     "CustomerRef": estimate.CustomerRef 
      // };
      const updateData = {
        "Id": estimateId,
        "DepartmentRef": estimate.DepartmentRef || departmentId,
        "SyncToken": syncToken,
        "TxnStatus": "Converted", 
        "PrivateNote": "This estimate has been converted to an invoice.",
        "Line": estimate.Line,  // Retaining all lines from the original estimate
        "CustomerRef": estimate.CustomerRef, 
        "CustomField": estimate.CustomField, // Including custom fields
        "DocNumber": estimate.DocNumber, // Including document number
        "TxnDate": estimate.TxnDate, // Including transaction date
        "CurrencyRef": estimate.CurrencyRef, // Including currency reference
        "BillAddr": estimate.BillAddr, // Including billing address
        "ShipFromAddr": estimate.ShipFromAddr, // Including shipping address
        "ClassRef": estimate.ClassRef, // Including class reference
        // "TxnTaxDetail": estimate.TxnTaxDetail, // Including tax details
        "TotalAmt": estimate.TotalAmt, // Including total amount
        "ApplyTaxAfterDiscount": estimate.ApplyTaxAfterDiscount, // Including tax application setting
        "PrintStatus": estimate.PrintStatus, // Including print status
        "EmailStatus": estimate.EmailStatus // Including email status
    };

      const response = await axios.post(`${quickbooksApiBaseUrl}/estimate`, updateData, {
          headers: {
              'Authorization': `Bearer ${token}`,
              'Content-Type': 'application/json',
              'Accept': 'application/json'
          },
          timeout: 10000
      });

      if (response.status === 200) {
          console.log("Estimate successfully marked as converted:", response.data);
      } else {
          console.error("Failed to update estimate status:", response.data);
      }
  } catch (error) {
      console.error("Error updating estimate status:", error.response ? error.response.data : error.message);

      // Log specific validation error details if available
      if (error.response && error.response.data && error.response.data.Fault) {
          console.error("Validation Error Details:", JSON.stringify(error.response.data.Fault.Error, null, 2));
      }
  }
};
// (async () => {
//   await markEstimateAsConverted(10519);
// })()

// let result =convertEstimateToInvoice(8177,100,"dsfsf",40,60)

//////////////////////////////////////////////////////////
async function run() {
  let EstimateData = await Request.getRequestById(358);
  console.log("EstimateData",EstimateData)
}
// run()



const acceptFinalInvoice = async (req, reply) => {
  try {
    const { requestId, requestPartnerId, userId, additionalAmount, grandTotal } = req.body;
    const currentDate = Math.round(moment().valueOf() / 1000);
    let EstimateData = await Request.getRequestById(requestId);
    const estimateId = EstimateData[0]?.estimate_id;
    const orderId = EstimateData[0]?.cust_order_id;
   const salesOrderName = EstimateData[0]?.name
   const paymentTermQuickbookId = EstimateData[0]?.paymentTermQuickbookId
    
    // console.log("EstimateDataEstimateData",EstimateData)
    
    const currentTime = moment().unix();
    let joCostData = await Request.getJobCostDetails(requestId);
    let productTotal = Number(joCostData?.productTotal)
    let serviceTotal = Number(joCostData?.serviceTotal)
    let projectTypeQuickBookId = joCostData?.projectTypeQuickBookId
    let totalJobCostAmount = parseFloat(productTotal + serviceTotal).toFixed(2) || 0
    try {
      if (estimateId) {
        let quickbookInvoiceId = await convertEstimateToInvoice(estimateId,totalJobCostAmount,salesOrderName,productTotal,serviceTotal,projectTypeQuickBookId,paymentTermQuickbookId);

        const estimate = null   // await getInvoiceById(estimateId);
        if (!estimate) {
          console.error("Error fetching invoice from QuickBooks: Estimate not found");
        } else {
          const name = 'additional amount'
          let productId = await findItemIdByName(name);
          const additionalData = {
            itemId: productId,
            name: name,
            amount: parseFloat(additionalAmount)?.toFixed(2) || 0, 
            quantity: 1 ,
            unitPrice: parseFloat(additionalAmount)?.toFixed(2) || 0
          };

          // Fetch existing items from the estimate
          console.log('estimate-----',estimate)
        const existingItems = estimate.Line || [];

        // Convert existing items to match the required format for updating
        const formattedExistingItems = existingItems.map(item => ({
          Amount: item.Amount,
          DetailType: item.DetailType,
          SalesItemLineDetail: item.SalesItemLineDetail,
        }));

        // Combine existing items with the new additional data
        const combinedItems = [
          ...formattedExistingItems, 
          // additionalData ]
          {
            Amount: parseFloat(parseFloat(additionalData.amount).toFixed(2)),
            DetailType: "SalesItemLineDetail",
            SalesItemLineDetail: {
              ItemRef: { value: additionalData.itemId },
              Qty: additionalData.quantity,
              UnitPrice: parseFloat(parseFloat(additionalData.unitPrice).toFixed(2)),
              TaxCodeRef: { value: 'NON' }, // Ensure the additional amount is non-taxable
            },
          },
        ];
          const requestData = {
            items: combinedItems,
          };
          console.log('combinedItems----',combinedItems)
          // let quickbookInvoiceId = await editInvoiceInQuickBooksFinal(requestData,estimateId)
          let quickbookInvoiceId = await convertEstimateToInvoice(estimateId);
          const doc_number = quickbookInvoiceId.Invoice.DocNumber || doc_number;
        }
      } else {
        console.error("Error fetching invoice from QuickBooks: Invalid estimateId 1");
      }
    } catch (quickbooksError) {
      console.error("Error in QuickBooks API: ", quickbooksError);
    }
    let statusUpdate = null
    if(newInvoiceId && newDocNum) {
      
       statusUpdate = await Request.updateRequets(requestId, { installation_acceptance: 1 ,timestamp : currentTime,estimate_id:newInvoiceId,doc_number:newDocNum });
       newInvoiceId = null
       newDocNum = null
      }else{
      
       statusUpdate = await Request.updateRequets(requestId, { installation_acceptance: 1 ,timestamp : currentTime});
    }
    if (statusUpdate) {
      try {
        const facilityTypes = (EstimateData[0]?.facility_type || "")
          .split(",")
          .map(v => v.trim())
          .filter(v => v !== "");
        const targetFacilityId = facilityTypes.filter(id => id !== String(FACILITY_ID));
        const originRequestId = requestId || null;
        const isSplitOrder = EstimateData[0]?.is_split_order || null;
        const project_category = EstimateData[0]?.project_category == 15 ? true : false;
        const installStatus = 5;
        if (isSplitOrder && originRequestId && targetFacilityId && project_category) {
          const updateSplitMirrorStatus = await updateMirrorOrderStatusFromOtherInstallations(originRequestId, targetFacilityId, installStatus);
          console.log('updateSplitMirrorStatus invoicing', updateSplitMirrorStatus)
        }
      } catch (error) {
        console.log('error updateSplitMirrorStatus invoicing', error)
      }
    //   const isSplitOrder = Number(EstimateData[0]?.is_split_order);
    //   const isBuildOrder = Number(EstimateData[0]?.for_parent_facility);
    //   if(isSplitOrder && isBuildOrder){
    //   const parentFacilityId = Number(EstimateData[0]?.origin_facility_id);
    //   const originFacilityRequestId = Number(EstimateData[0]?.origin_facility_request_id);
    //   const customerOrderProductData = await CustomerOrderProduct.getCustomerOrderProductsbyRequestId(requestId);
    //   for(let eachProduct of customerOrderProductData){
    //     const facilityId = Number(eachProduct.facility_id)
    //     let productData ={} 
    //     productData =  await Product.getProductDetailsFromId(eachProduct.product_id)
    //      console.log("productData",productData)
        
    //     const update = await Product.updateCustomerOrderProductTransferStatus(eachProduct.product_id,EstimateData[0]?.id,2)
    //     const newProductData = {
    //       ...productData,
    //       id: null,
    //       special_price: null,
    //       // formula_cost: null,
    //       price: null,
    //       recipe_id: null,
    //       in_stock_no: Number(eachProduct?.location_quantity),
    //       inhouse: 6
    //     };
    //     const transferResult = await transferProductStockToOtherInstallation(newProductData,null,originFacilityRequestId,parentFacilityId,eachProduct.product_id);
    //     console.log('transferResult---', transferResult)
        

    //    }

    // }

      let orderData = {
        grandTotal : parseFloat(grandTotal).toFixed(2),
        addl_amount : parseFloat(additionalAmount).toFixed(2),
      }

      if (EstimateData && EstimateData[0]?.map_service) {
        let data = { 
          assigned_sales_order : null
        }
        let updateRoute = await Routes.updateRoute(data,EstimateData[0]?.map_service);
      }

      let updatedOrderId = await CustomerOrder.updateCustomerOrder(orderId, orderData);
      const request = await Request.getRequestById(requestId);
      const partner = await RequestPartner.getRequestPartnerById(requestPartnerId);
      const partnerName = partner.name;

      let reqActivityMsg, notificationMsg, notificationUrl;

      const userData = await Admin.getUserById(userId);
      const user = userData?.[0]; // safely get first object

      const performedBy = user
        ? `${user.first_name || ''} ${user.last_name || ''}`.trim() || user.username
        : 'Unknown User';

      reqActivityMsg = `${performedBy} accepted the final invoice ${request[0].name}.`;
      // reqActivityMsg = `The client ${partnerName} accepted the final invoice ${request[0].name}.`;
      notificationMsg = reqActivityMsg;
      notificationUrl = `request/view/${requestId}`;

      if (partner.user_id) {
        await Admin.incrementField(partnerName, "noti_count", 1);
        const notificationData_user = {
          user_id: partner.user_id,
          url: notificationUrl,
          timestamp: currentDate,
          message: notificationMsg,
          status: 1,
          from_id: userId,
        };
        await Admin.addNotification(notificationData_user);
      }

      const adminIds = await Admin.getAllAdminIds();
      for (const adminId of adminIds) {
        if (adminId !== userId) {
          await Admin.incrementField(adminId, "noti_count", 1);
          const notificationData = {
            user_id: adminId,
            url: notificationUrl,
            timestamp: currentDate,
            message: notificationMsg,
            status: 1,
            from_id: userId,
          };
          await Admin.addNotification(notificationData);
        }
      }

      const requestActivity = {
        request_id: requestId,
        activity: reqActivityMsg,
        added_by: userId,
        timestamp: currentDate,
      };
      const reqActivityId = await RequestActivity.addRequestUpdate(requestActivity);

      // await RequestUpdates.addRequestUpdates({
      //   request_id: requestId,
      //   activity: `The client ${partnerName} accepted the final invoice ${request[0].name}.`,
      //   partner_id: 0,
      //   added_by: userId,
      //   timestamp: currentDate,
      // });

      // const allUserIds = await Admin.getAllUserIds();
      const projectResult = await Project.getProjectByRequestId(requestId);
      // console.log("projectResult",projectResult)
      const allInstallations = await  Installation.findInstallationsByRequestId(requestId);
      // console.log("allInstallationsssss",allInstallations)
        // const allAdminUserIds = await Admin.getAllAdminUserIds();
        // const selectedClientId = values?.clients?.user_id ||null;
        let PartnerUserIds = await RequestPartner.fetchPartnerUserIds(requestId)
        // const usersToUpdate = [...new Set([userId,...PartnerUserIds, ...allUserIds].filter(Boolean))];

        // const updateEditCounts = async (userId) => {
        //     await Request.salesOrderIncrementField(userId, 'invoicing_count', -1, 'invoicing_count_ids', requestId, false);
        //     await Request.salesOrderIncrementField(userId, 'completed_count', 1, 'completed_count_ids', requestId, true);
        //     await Project.jobTabIncrementField(userId, 'completed_count', 1, 'completed_count_ids', projectResult, true);
        //     console.log("Moved to approved tab: completed-, approved++");
        // };
  
        // await Promise.all(usersToUpdate.map(userId => updateEditCounts(userId)));
        // const updateInstallationCompletedCount = async (installation) => {
        //   console.log("updateInstallationCompletedCount",installation);
        //   await Promise.all(usersToUpdate.map(userId => {
        //     if (installation?.completed_status == 1) {
        //        Installation.installationTabIncrementField(userId, 'completed_count', -1, 'completed_count_ids', installation.id, false);
        //     }else if (installation?.completed_status == 2){
        //       Installation.pickupTabIncrementField(userId, 'completed_count', -1, 'completed_count_ids', installation.id, false);
        //     }else{
        //     Installation.shippingTabIncrementField(userId, 'completed_count', -1, 'completed_count_ids', installation.id, false);
        //     }
        //     console.log("update Installation updated",installation?.id);
        //   }
        // ));
        // console.log("installation count updated");
        // };

        // await Promise.all(allInstallations.map(installation => updateInstallationCompletedCount(installation)));
        
        // const updateInstallationCompletedCount = async (installation) => {
        //   console.log("updateInstallationCompletedCount", installation);
        //   const promises = usersToUpdate.map(async (userId) => {
        //       if (installation?.complete_status === 1) {
        //           await Installation.installationTabIncrementField(userId, 'completed_count', -1, 'completed_count_ids', installation.id, false);
        //           console.log("update installs", installation?.id);
        //         } else if (installation?.complete_status === 2) {
        //           await Installation.pickupTabIncrementField(userId, 'completed_count', -1, 'completed_count_ids', installation.id, false);
        //           console.log("update pickup", installation?.id);
        //         } else {
        //           await Installation.shippingTabIncrementField(userId, 'completed_count', -1, 'completed_count_ids', installation.id, false);
        //           console.log("update shipping", installation?.id);
        //         }
        //       console.log("update Installation updated", installation?.id);
        //   });
      
        //   await Promise.all(promises);
        //   console.log("installation count updated");
        // };

        // if(allInstallations && allInstallations?.length > 0){
        //   await Promise.all(allInstallations.map(installation => updateInstallationCompletedCount(installation)));
        //   console.log("Request tab counts updated successfully after edit.");
        // }

      // Send email and SMS to the approving user if settings allow
        // const approver = await Admin.getUserNotificationAndPermissionSettings(userId);
        let adminMangerIds = await Admin.getAllAdminAndManagerUserIds();
        const allUsersToCheck = [...new Set([userId,...PartnerUserIds, ...adminMangerIds].filter(Boolean))];
        // console.log('allUsersTocheckkkkkkk',allUsersToCheck)
        for (const userIdToCheck of allUsersToCheck) {
            console.log('useriddddd',userIdToCheck)
            const approver = await Admin.getUserNotificationAndPermissionSettings(userIdToCheck);
            console.log('approverrrrrrrrr',approver)
            if (!approver) continue;

            if (approver?.emailEnabled === 1 && approver?.pcb_final_invoice === 1 && approver?.pcb_final_invoice_email === 1 && approver?.email) {
          const subject = `✅ Final Invoice Approved: ${request[0]?.name}`;
          const welcome = `Hello ${approver.name},`;
          const email_content = `You have approved the final invoice for the sales order "${request[0]?.name}". The billing process is now complete.`;

          let message = finalInvoiceApprovedEmailTemplate({
            user: approver,
            requestName: request[0]?.name,
            projectType: projectResult?.project_type || '',
            totalAmount: grandTotal,
            baseUrl: BASE_URL,
          });

          message = message.replace('[emailContent]', email_content);
          message = message.replace('[CONTENT]', welcome);
          message = message.replace('[LOGIN_URL]', `${BASE_URL}`);

          const mailOptions = {
            // to: 'deeraj@skyniche.com',
              // to:'sanjay.pk@skyniche.com',
            to: approver.email,
            from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
            subject,
            text: subject,
            html: message,
          };

          try {
            await transporter.sendMail(mailOptions);
            console.log(`✅ Final invoice email sent to: ${approver.email}`);
          } catch (err) {
            console.error(`❌ Error sending invoice email:`, err);
          }
        }

        if (approver?.pcb_final_invoice === 1 && approver?.pcb_final_invoice_sms === 1 && approver?.smsEnabled === 1 && approver?.phone) {
          const smsContent = 
            `✅ Final Invoice Approved\n` +
            `📄 Job: "${request[0]?.name}"\n` +
            `💰 Total: $${grandTotal}\n` +
            `🔗 View: ${BASE_URL}\n` +
            `— SignWise`;

          const recipientPhone = approver.phone || TWILIO_PHONE;
          const Phone_country_code = approver.phone_country_code || '';

          try {
            // await sendMessageToPhoneNumber('9074906155', smsContent);
            // await sendMessageToPhoneNumber('9947931552', smsContent,'91');
            await sendMessageToPhoneNumber(recipientPhone, smsContent,Phone_country_code);
            console.log(`✅ Final invoice SMS sent to ${approver.name || 'User'} (${recipientPhone})`);
          } catch (err) {
            console.error(`❌ Failed to send invoice SMS:`, err);
          }
        }
      }
      reply.send({ status: 1, message: "You have accepted the final invoice", allInstallations });
    } else {
      reply.send({ status: 0, message: "Failed to accept the final invoice", statusUpdate });
    }
  } catch (error) {
    console.log("acceptFinalInvoice error : ", error);
    reply.send({ status: 500, message: "Server error", error });
  }
}

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

const acceptOrRejectRequestNew = async (req, reply) => {
  try {
    const { requestId, requestPartnerId, acceptStatus, userId, reason ,billing_type,mirroredOrder, rejectFile} = req.body;
    const currentDate = Math.round(moment().valueOf() / 1000);
    const currentTime = moment().unix();


     //////////////////////////for quickbook
  //    let EstimateData = await Request.getRequestById(requestId)
  //    const estimateId = EstimateData[0]?.estimate_id;
  //  console.log("estimateId",estimateId)
  //    const estimate = await getInvoiceById(estimateId)
 
  //    if (!estimate) {
  //      return reply.status(500).send({ error: "Failed to fetch estimate from QuickBooks" });
  //    }
  //    let status = acceptStatus === 1 ? 'Accepted' : 'Rejected';
  //    console.log("acceptStatus",acceptStatus)
      
  //      const updatedEstimate = await updateInvoiceStatus(estimate, status);


  let EstimateData = await Request.getRequestById(requestId);
  const estimateId = EstimateData[0]?.estimate_id;

  // try {
  //   if (estimateId) {
  //     const estimate = await getInvoiceById(estimateId);

  //     if (!estimate) {
  //       console.error("Error fetching invoice from QuickBooks: Estimate not found");
  //     } else {
  //       let status = acceptStatus === 1 ? 'Accepted' : 'Rejected';

  //       const updatedEstimate = await updateInvoiceStatus(estimate, status);
  //     }
  //   } else {
  //     console.error("Error fetching invoice from QuickBooks: Invalid estimateId");
  //   }
  // } catch (quickbooksError) {
  //   console.error("Error in QuickBooks API: ", quickbooksError);
  
  // }
     /////////////////////////////////////////

    let acceptQuery = null
    if(acceptStatus === 4){
      acceptQuery = true
    }else{
      acceptQuery = await RequestPartner.acceptOrRejectRequest(requestPartnerId, { accept_status: acceptStatus });
    }
    let requestData, statusUpdate;
    let checkProjectExist = await Request.checkExistingProject(requestId)
    const newRequestData = await Request.getRequestById(requestId);
    if (acceptQuery) {
      if (acceptStatus === 1) {
        requestData = await Request.getRequestById(requestId);
        let requestStatus = {
          status: 3,
          client_approve: 0,
          reason: null,
          reject_file: null,
          reject_file_type: null,
          timestamp : currentTime
        }
        if(requestData[0]?.total <=0  || billing_type ==0){
          requestStatus['min_payment_status'] = 1
        }
        // if(mirroredOrder){
        //   requestStatus['split_mirror_status'] = 7;
        // }
        statusUpdate = await Request.updateRequets(requestId, requestStatus);
        if(mirroredOrder && newRequestData[0]?.origin_facility_request_id && newRequestData[0]?.origin_facility_id){
          const originFacilityRequestId = newRequestData[0]?.origin_facility_request_id;
          const originFacilityId = newRequestData[0]?.origin_facility_id;
          const facilityTypes = (newRequestData[0]?.facility_type || "")
          .split(",")
          .map(v => v.trim())
          .filter(v => v !== ""); // Remove empty strings
          const targettedFacilityId = facilityTypes.filter(id => id !== String(FACILITY_ID));
          console.log('targettedFacilityIdddd', targettedFacilityId)
          const data = {
            status: 3,
            client_approve: 0,
            timestamp : currentTime
          }
          const updateOtherfacilityStatus = await SplitManagementModel.updateOtherfacilityApproveStatus(requestId,originFacilityRequestId,originFacilityId,targettedFacilityId, data);
          // if(updateOtherfacilityStatus?.approved){
          //   statusUpdate = await Request.updateRequets(requestId, { split_mirror_status : 0, });
          // }
          console.log('updateOtherfacilityStatussssss accept', updateOtherfacilityStatus)
        }
        // statusUpdate = await Request.updateRequets(requestId, { status: 3 });
      }else if (acceptStatus === 4) {
        statusUpdate = await Request.updateRequets(requestId, { 
          status: 2, 
          reason,
          reject_file: rejectFile ? rejectFile?.url : null,
          reject_file_type: rejectFile ? rejectFile?.file_type : null,
          timestamp : currentTime
        });
        if(statusUpdate){
          if (checkProjectExist) {
            const project_id = checkProjectExist
            try {
              const installations = await Installation.getInstallationsByProjectId(project_id);
              const installationDeleted = await Installation.deleteInstallationUsingProjectId(project_id);
              const timelogDeleted = await ProjectTask.deleteStaffTaskTimelogs(project_id);
              const jobDeleted = await Project.deleteProject(project_id);
              console.log('job deleteddddddddddddddddd')
            } catch (error) {
              console.error('❌ Error during job deletion:', error);
            }
          }
        }
      } else {
        statusUpdate = await Request.updateRequets(requestId, { status: 6, reason,timestamp : currentTime,split_mirror_status : 0, });
        if(mirroredOrder && newRequestData[0]?.origin_facility_request_id && newRequestData[0]?.origin_facility_id){
          const originFacilityRequestId = newRequestData[0]?.origin_facility_request_id;
          const originFacilityId = newRequestData[0]?.origin_facility_id;
          const facilityTypes = (newRequestData[0]?.facility_type || "")
          .split(",")
          .map(v => v.trim())
          .filter(v => v !== ""); // Remove empty strings
 
          const targettedFacilityId = facilityTypes.filter(id => id !== String(FACILITY_ID));
          console.log('targettedFacilityIdddd', targettedFacilityId)
          const data = {
            status: 6,
            reason: reason,
            timestamp : currentTime,
            split_mirror_status : 0,
            mirror_approve_status : null,
          }
          const updateOtherfacilityStatus = await SplitManagementModel.updateOtherfacilityRejectStatus(requestId,originFacilityRequestId,originFacilityId,targettedFacilityId, data);
          console.log('updateOtherfacilityStatussssss reject', updateOtherfacilityStatus)
        }
      }

      const request = await Request.getRequestById(requestId);
      const partner = await RequestPartner.getRequestPartnerById(requestPartnerId);
      const partnerName = partner.name;
      let actionUserName = partnerName;
      if (userId) {
        try {
          const userInfo = await Admin.getUserById(userId);
          if (userInfo && userInfo[0]) {
            actionUserName = userInfo[0].first_name 
              ? `${userInfo[0].first_name} ${userInfo[0].last_name || ''}`.trim() 
              : (userInfo[0].company_name || partnerName);
          }
        } catch (error) {
          console.error('Error getting user info for activity log:', error);
          // Fallback to partnerName if error occurs
        }
      }

      let reqActivityMsg, notificationMsg, notificationUrl;

      if (acceptStatus == 1) {
        reqActivityMsg = `The member ${actionUserName} accepted the Invoice ${request[0].name}.`;
        notificationMsg = reqActivityMsg;
        notificationUrl = `request/view/${requestId}`;

      } else if (acceptStatus == 4) {
        reqActivityMsg = `The member ${actionUserName} rejected the sales order ${request[0].name} (Moved to Open)`;
        notificationMsg = reqActivityMsg;
        notificationUrl = `request/view/${requestId}`;
      } else {
        reqActivityMsg = `The member ${actionUserName} rejected the order ${request[0].name}.`;
        notificationMsg = reqActivityMsg;
        notificationUrl = `request/view/${requestId}`;
      }

      if (partner.user_id) {
        await Admin.incrementField(partnerName, "noti_count", 1);
        const notificationData_user = {
          user_id: partner.user_id,
          url: notificationUrl,
          timestamp: currentDate,
          message: notificationMsg,
          status: 1,
          from_id: userId,
        };
        await Admin.addNotification(notificationData_user);
      }

      const adminIds = await Admin.getAllAdminIds();
      for (const adminId of adminIds) {
        if (adminId !== userId) {
          await Admin.incrementField(adminId, "noti_count", 1);
          const notificationData = {
            user_id: adminId,
            url: notificationUrl,
            timestamp: currentDate,
            message: notificationMsg,
            status: 1,
            from_id: userId,
          };
          await Admin.addNotification(notificationData);
        }
      }

      const requestActivity = {
        request_id: requestId,
        activity: reqActivityMsg,
        added_by: userId,
        timestamp: currentDate,
      };
      const reqActivityId = await RequestActivity.addRequestUpdate(requestActivity);

      // await RequestUpdates.addRequestUpdates({
      //   request_id: requestId,
      //   activity: reqActivityMsg,
      //   partner_id: 0,
      //   added_by: userId,
      //   timestamp: currentDate,
      // }); // un comment this line if needed but when i un commented the final invoice accepted message in the job log is coming twise 

      if(request[0]?.project_category !== 14 && acceptStatus == 1){
      let routeData = {};
        routeData['name'] = `route ${currentTime}`;
        routeData['assigned_driver'] = null;
        routeData['timestamp'] = currentTime;
        routeData['added_by'] = userId;
        routeData['status'] = 1;
        routeData['assigned_sales_order'] = requestId;
        routeData['assigned'] = 0;
        const randomBytes = crypto.randomBytes(2).toString('hex');
        const u_id = parseInt(randomBytes, 16) % 90000 + 10000;
        routeData['unique_id'] = 'SWS' + u_id;
          let routeId = await Routes.createRoute(routeData);
        }
        
        // const allUserIds = await Admin.getAllUserIds();
        // const allUserIds = await Admin.getAllAdminUserIds();
        // const selectedClientId = values?.clients?.user_id ||null;
        // let PartnerUserIds = await RequestPartner.fetchPartnerUserIds(requestId)
        // const usersToUpdate = [...new Set([userId,...PartnerUserIds, ...allUserIds].filter(Boolean))];
        // const updateEditCounts = async (userId) => {
        //   if (acceptStatus === 1) {
        //     await Request.salesOrderIncrementField(userId, 'open_count', -1, 'open_count_ids', requestId, false);
        //     await Request.salesOrderIncrementField(userId, 'approved_count', 1, 'approved_count_ids', requestId, true);
        //     console.log("Moved to approved tab: open--, approved++");
        //   } else if (acceptStatus === 4) {
        //     await Request.salesOrderIncrementField(userId, 'open_count', 1, 'open_count_ids', requestId, true);
        //     await Request.salesOrderIncrementField(userId, 'in_production_count', -1, 'in_production_count_ids', requestId, false);
        //     console.log("Rejected request: open--");
        //   } else {
        //     await Request.salesOrderIncrementField(userId, 'open_count', -1, 'open_count_ids', requestId, false);
        //     console.log("Rejected request: open--");
        //   }
        // };
  
        // await Promise.all(usersToUpdate.map(userId => updateEditCounts(userId)));
        // console.log("Request tab counts updated successfully after edit.");

      if (acceptStatus == 1) {
        reply.send({ status: 1, message: "You have accepted the request", checkProjectExist });
      } else {
        reply.send({ status: 2, message: "You have rejected the request", checkProjectExist });
      }

    } else {
      if (acceptStatus == 1) {
        reply.send({ status: 0, message: "Failed to accept the request",checkProjectExist });
      } else {
        reply.send({ status: 0, message: "Failed to reject the request",checkProjectExist });
      }
    }
  } catch (error) {
    console.log("acceptOrRejectRequestNew error : ", error);
    reply.send({ status: 500, message: "Server error", error });
  }
}
const rejectSalesOrderFromJob = async (req, reply) => {
  try {
    const { requestId, requestPartnerId, userId, reason ,billing_type,mirroredOrder, rejectFile} = req.body;
    const currentDate = Math.round(moment().valueOf() / 1000);
    const currentTime = moment().unix();
    let acceptQuery = true
    let requestData, statusUpdate;
    let checkProjectExist = await Request.checkExistingProject(requestId)
    const newRequestData = await Request.getRequestById(requestId);
    if (acceptQuery) {
      statusUpdate = await Request.updateRequets(requestId, { 
        status: 6, 
        reason,
        reject_file: rejectFile ? rejectFile?.url : null,
        reject_file_type: rejectFile ? rejectFile?.file_type : null,
        timestamp : currentTime
      });
      if(statusUpdate){
        if (checkProjectExist) {
          const project_id = checkProjectExist
          try {
            const updatedProjectStatus = await Project.updateProjectById(project_id, { status: 9,change_order:0 });
            console.log('updatedProjectStatusdddddddddddddddd', updatedProjectStatus)
          } catch (error) {
            console.error('❌ Error during job deletion:', error);
          }
        }
      }
    }

    const request = await Request.getRequestById(requestId);
    const partner = await RequestPartner.getRequestPartnerById(requestPartnerId);
    const partnerName = partner?.name;

    let reqActivityMsg, notificationMsg, notificationUrl;

    // Resolve acting user's display name (admin or partner)
    const userData = await Admin.getUserById(userId);
    const userObj = userData?.[0];
    const performedBy = userObj
      ? `${userObj.first_name || ''} ${userObj.last_name || ''}`.trim() || userObj.username
      : null;
    const actorName = performedBy || partnerName || 'User';

    reqActivityMsg = `${actorName} rejected the order ${request[0].name}.`;
    notificationMsg = reqActivityMsg;
    notificationUrl = `request/view/${requestId}`;
    
    if (partner.user_id) {
      await Admin.incrementField(partnerName, "noti_count", 1);
      const notificationData_user = {
        user_id: partner.user_id,
        url: notificationUrl,
        timestamp: currentDate,
        message: notificationMsg,
        status: 1,
        from_id: userId,
      };
      await Admin.addNotification(notificationData_user);
    }

    const adminIds = await Admin.getAllAdminIds();
    for (const adminId of adminIds) {
      if (adminId !== userId) {
        await Admin.incrementField(adminId, "noti_count", 1);
        const notificationData = {
          user_id: adminId,
          url: notificationUrl,
          timestamp: currentDate,
          message: notificationMsg,
          status: 1,
          from_id: userId,
        };
        await Admin.addNotification(notificationData);
      }
    }

    const requestActivity = {
      request_id: requestId,
      activity: reqActivityMsg,
      added_by: userId,
      timestamp: currentDate,
    };
    const reqActivityId = await RequestActivity.addRequestUpdate(requestActivity);
      
    // const allUserIds = await Admin.getAllUserIds();
    // let PartnerUserIds = await RequestPartner.fetchPartnerUserIds(requestId)
    // const usersToUpdate = [...new Set([userId,...PartnerUserIds, ...allUserIds].filter(Boolean))];
    // const updateEditCounts = async (userId) => {
    //   await Request.salesOrderIncrementField(userId, 'open_count', -1, 'open_count_ids', requestId, false);
    //   console.log("Rejected request: open--");
    // };

    // await Promise.all(usersToUpdate.map(userId => updateEditCounts(userId)));
    // console.log("Request tab counts updated successfully after edit.");
    reply.send({ status: 2, message: "You have rejected the request", checkProjectExist });
  } catch (error) {
    console.log("acceptOrRejectRequestNew error : ", error);
    reply.send({ status: 500, message: "Server error", error });
  }
}

////////////////////////////////////// quickbook
const createJournalEntryForJobStarted = async (filteredMaterialDataForQb,jobName) => {
  try {
    if (!filteredMaterialDataForQb?.length) {
      console.warn("No received material data to post.");
      return null;
    }
    console.log('filteredMaterialDataForQb',filteredMaterialDataForQb)

    const token = await getNewAccessToken();
    const lines = [];
    const docNums = new Set();

    let totalDebit = 0;
    let totalCredit = 0;

    filteredMaterialDataForQb.forEach((item) => {
      const amount = parseFloat(item.product_total || 0);
      if (amount <= 0 || isNaN(amount)) return;

      const docNum = item.receive_doc_number || 'N/A';
      docNums.add(docNum);

      const description = `Material "${item?.product_name}" worth $${parseFloat(item?.product_total).toFixed(2)} has been issued to a job "${jobName}".`;


      // Debit: Work In Progress (WIP)
      lines.push({
        Description: description,
        Amount: amount,
        DetailType: "JournalEntryLineDetail",
        JournalEntryLineDetail: {
          PostingType: "Debit",
          AccountRef: {
            value: "1150040005" // Work In Progress (WIP)
          },
          DepartmentRef: { value: departmentId }
        }
      });

      totalDebit += amount;

      // Credit: Inventory Asset
      lines.push({
        Description: description,
        Amount: amount,
        DetailType: "JournalEntryLineDetail",
        JournalEntryLineDetail: {
          PostingType: "Credit",
          AccountRef: {
            value: "182" // Inventory Asset
          },
          DepartmentRef: { value: departmentId }
        }
      });

      totalCredit += amount;
    });

    if (totalDebit !== totalCredit) {
      console.error("❌ Journal Entry is not balanced!");
      return null;
    }

    const journalEntry = {
      Line: lines,
      TxnDate: moment().format('YYYY-MM-DD'),
      PrivateNote: `Auto Journal Entry for Job Start | POs: ${[...docNums].join(', ')}`
    };

    const apiUrl = PRODUCTION
      ? `https://quickbooks.api.intuit.com/v3/company/${realmId}`
      : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}`;

    const response = await axios.post(`${apiUrl}/journalentry`, journalEntry, {
      headers: {
        Authorization: `Bearer ${token}`,
        'Content-Type': 'application/json',
        Accept: 'application/json'
      }
    });

    if (response.status === 200 || response.status === 201) {
      console.log("✅ Journal Entry created successfully:", response.data);
      return response.data;
    } else {
      console.error("❌ QuickBooks API returned error:", response.data);
      return null;
    }

  } catch (error) {
    console.error("Error creating Journal Entry for Job Start:", error.response?.data || error.message);
    throw error;
  }
};

const addJournalEntryToQuickBooks = async ({ amount, requestId, journalEntryValue,description,
        debitAccount,debitAccountId,creditAccount,creditAccountId,projectTypeQuickBookId }) => {
  console.log('amountamountamount',amount,journalEntryValue)
  const token = await getNewAccessToken();
  const apiUrl = PRODUCTION
    ? `https://quickbooks.api.intuit.com/v3/company/${realmId}`
    : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}`;

  const date = moment().format('YYYY-MM-DD');




  const journalEntryData = {
    Line: [
      {
        Id: "1",
        Description: description,
        Amount: amount,
        DetailType: "JournalEntryLineDetail",
        JournalEntryLineDetail: {
          PostingType: "Debit",
          AccountRef: { value: debitAccountId, name: debitAccount },
          DepartmentRef: { value: departmentId },
          ClassRef: { value: projectTypeQuickBookId }
        }
      },
      {
        Id: "2",
        Description: description,
        Amount: amount,
        DetailType: "JournalEntryLineDetail",
        JournalEntryLineDetail: {
          PostingType: "Credit",
          AccountRef: { value: creditAccountId, name: creditAccount },
          DepartmentRef: { value: departmentId },
          ClassRef: { value: projectTypeQuickBookId }
        }
      }
    ],
    TxnDate: date,
    PrivateNote: description
  };

  const response = await axios.post(`${apiUrl}/journalentry`, journalEntryData, {
    headers: {
      Authorization: `Bearer ${token}`,
      Accept: 'application/json',
      'Content-Type': 'application/json'
    }
  });

  console.log('joCostData=== job progress ✅ Journal entry posted:', response.data);
};


const hanldeMaterialJobCost =async(joCostData,productTotal,previosProductTotal,requestId,journalEntryValueMain,total)=>{
  if(Number(productTotal) > Number(previosProductTotal)){
    let amount = parseFloat(productTotal - previosProductTotal).toFixed(2);
    //for 0 - 100% completion of a job
    let  journalEntryValue =1
    const description = `Job "${joCostData.name}" has been updated with an amount of $${amount}.`;


 let debitAccount = "Work In Progress";
 let creditAccount = "Inventory";
 let debitAccountId =await findAccountIdByAcctNum(1014000) 
 let creditAccountId =await findAccountIdByAcctNum(1013000)


    await addJournalEntryToQuickBooks({
      amount,
      requestId,
      journalEntryValue,
      description,
      debitAccount,
      debitAccountId,
      creditAccount,
      creditAccountId,
      projectTypeQuickBookId:joCostData.projectTypeQuickBookId
    });

  }else{
    console.log("Already journal entry created -no change in material job cost");
  }
}

const hanldeServiceJobCost =async(joCostData,serviceTotal,previosServiceTotal,requestId,journalEntryValueMain,total)=>{
  if(Number(serviceTotal) > Number(previosServiceTotal)){
    let amount = parseFloat(serviceTotal - previosServiceTotal).toFixed(2);
    //for 0 - 100% completion of a job
    let  journalEntryValue =1
    const description = `Job "${joCostData.name}" has been updated with an amount of $${amount}.`;
  
 let debitAccount = "Work In Progress";
 let creditAccount = "Split Labor";
 let debitAccountId = await findAccountIdByAcctNum(1014000)
 let creditAccountId =await findAccountIdByAcctNum(5081000)

    await addJournalEntryToQuickBooks({
      amount,
      requestId,
      journalEntryValue,
      description,
      debitAccount,
      debitAccountId,
      creditAccount,
      creditAccountId,
      projectTypeQuickBookId:joCostData.projectTypeQuickBookId
    });


  }else{
    console.log("Already journal entry created -no change in service job cost");
  }
}

const createJornalEntryForJobCost = async(requestId,previosJobCostTotal,previosProductTotal,previosServiceTotal) => {
  try {
    console.log('joCostData=== requestId',requestId)
    let joCostData = await Request.getJobCostDetailsForMaterials(requestId);
    
    console.log('joCostData=== 1 joCostData',joCostData)
    
    const journalEntryValueMain = (joCostData.everyProductTaken === 1 && joCostData.everyServiceTaken === 1) ? 2 : 1;
    console.log('joCostData=== 2 journalEntryValue',journalEntryValueMain)
    const productTotal = parseFloat(Number(joCostData.productTotal) || 0).toFixed(2);
    const serviceTotal = parseFloat(Number(joCostData.serviceTotal) || 0).toFixed(2);
    const total = parseFloat(Number(productTotal) + Number(serviceTotal) || 0).toFixed(2);
    console.log('joCostData=== 3 productTotal',previosProductTotal,productTotal)
    console.log('joCostData=== 4 serviceTotal',previosServiceTotal,serviceTotal)
      
    const materialResponse = await hanldeMaterialJobCost(joCostData,productTotal,previosProductTotal,requestId,journalEntryValueMain,total);
    const serviceResponse = await hanldeServiceJobCost(joCostData,serviceTotal,previosServiceTotal,requestId,journalEntryValueMain,total);

 
  } catch (error) {
    console.log("Error creating journal entry for materials", error);
    
  }
}
//////////////////////////////////////
//if you are changing anything in the acceptOrRejectInvoice function,
// then make sure that is the same changes needed in split funcitonality as well if yes then
//  you need to include the same changes in acceptOrRejectInvoiceSplit funciton as well
const acceptOrRejectInvoice = async (req, reply) => {
  try {
    let { requestId, requestPartnerId, acceptStatus, userId, reason ,movedToProduction,projectCategoryId,installationStatus} = req.body
    const addedByUserId = userId
    const currentTime = moment().unix();

 
    let salesOrderData = await Request.getRequestById(requestId)
    let isSplit = salesOrderData[0]?.is_split_order
    let splitProjectCategoryId = salesOrderData[0]?.project_category
    const facilityTypes = (salesOrderData[0]?.facility_type || "")
    .split(",")
    .map(v => v.trim())
    .filter(v => v !== ""); // Remove empty strings
  
 
    console.log('targetFacility 2',facilityTypes)
    console.log('targetFacility 3',salesOrderData[0]?.facility_type)
    const targettedFacilityId = facilityTypes.filter(id => id !== String(FACILITY_ID));

    // if(isSplit && splitProjectCategoryId !== 15 && targettedFacilityId.length > 0){
    //   for(let i=0;i<targettedFacilityId.length;i++){
    //     const targetFacility = filteredSignwiseInstalations.find(installation => installation.id === parseInt(targettedFacilityId[i]));
    //     const targetUrl =  targetFacility?.url 
        
    //     await axios.post(`${targetUrl}request/convert-sales-order-to-job-split`, {originRequestId:requestId, requestPartnerId, acceptStatus, userId, reason ,movedToProduction,projectCategoryId,for_parent_facility: 1})
    //     .then(response => {
    //       console.log("response split 123456",response)
    //     })
    //     .catch(error => {
    //       console.log("error split",error)
    //     })
    //   }
    // }
    // 📊 Progress tracking function
    const emitProgress = (step, message, progress = 0) => {
      console.log(`[PROGRESS-MODAL] 📊 Progress: ${step} - ${message} (${progress}%)`);
      
      const progressData = {
        step,
        message,
        progress,
        timestamp: Date.now(),
        userId: userId, // Include user ID for filtering
        domain: DOMAIN // Add domain for filtering (same as chat)
      };
      
      // Emit to working socket server (same as chat)
      console.log(`[PROGRESS-MODAL] 📡 Emitting progress to working socket server...`);
      try {
        const workingSocket = ioClient(REST_API_IO, {
          path: '/socket.io',
          transports: ['websocket', 'polling'],
          secure: true,
          timeout: 5000
        });
        
        workingSocket.on('connect', () => {
          console.log(`[PROGRESS-MODAL] 🔌 Connected to working socket server`);
          console.log(`[PROGRESS-MODAL] 📊 Emitting progress data:`, progressData);
          workingSocket.emit('message', {
            ...progressData,
            type: 'progress'
          });
          workingSocket.emit('request_progress', progressData);
          console.log(`[PROGRESS-MODAL] ✅ Successfully emitted progress to working socket server`);
          
          // Disconnect after emitting
          setTimeout(() => {
            workingSocket.disconnect();
          }, 1000);
        });
        
        workingSocket.on('connect_error', (error) => {
          console.error(`[PROGRESS-MODAL] ❌ Working socket connection error:`, error);
        });
        
      } catch (error) {
        console.error(`[PROGRESS-MODAL] ❌ Error creating working socket connection:`, error);
      }
    };

    // Initial progress
    console.log(`[PROGRESS-MODAL] 🚀 Starting sales order to job conversion for user:`, userId);
    emitProgress('start', `Initializing sales order to job conversion...`, 5);
    
    emitProgress('checking_project_existence', `Checking for existing project...`, 10);
    let checkProjectExist = await Request.checkExistingProject(requestId)
    let currentDate = Math.round(moment().valueOf() / 1000);
    let acceptQuery = false
    let directInstalls = false
    emitProgress('updating_partner_status', `Updating client acceptance status...`, 15);
    
    if(requestPartnerId){
        acceptQuery = await RequestPartner.acceptOrRejectRequest(requestPartnerId, { accept_status: acceptStatus });
    }
    acceptQuery = true
    let allCreatedTask = []

    emitProgress('fetching_project_data', `Fetching job and job cost data...`, 20);
    let projectId = await Project.projectIdFromRequestId(requestId)
    let projectData = await Project.getProjectById(projectId)
    let joCostData = await Request.getJobCostDetailsForMaterials(requestId);
 
        const previosProductTotal =0 //parseFloat(Number(joCostData?.productTotal) || 0).toFixed(2) 
        const previosServiceTotal =0 //parseFloat(Number(joCostData?.serviceTotal) || 0).toFixed(2) 
        const previosJobCostTotal =0 // parseFloat(Number(previosProductTotal) + Number(previosServiceTotal) || 0).toFixed(2);
     

    if (acceptStatus === 1) {
      emitProgress('processing_acceptance', `Processing sales order acceptance...`, 25);
      
      let requestData = await Request.getRequestById(requestId)
      console.log('requestData', requestData)
      let reqresult = await Request.getAllRequests(requestId, userId)
      reqresult = reqresult?.data || []
      console.log('reqresulttt', reqresult[0]?.allInstallationCompleted)
      
      emitProgress('preparing_project_data', `Preparing data for job creation...`, 30);
      
      // adding accepted request to job 
      let projectData = {};
        projectData.project_uid = u_id;
        projectData.name = requestData[0]['name'];
        projectData.noServiceNoTask = requestData[0]['noServiceNoTask'];
        projectData.short_code = requestData[0]['name'].substring(0, 3);
        projectData.request_id = requestId;
        projectData.tags = '';
        projectData.start_date = requestData[0]['submission_deadline'] || null;
        projectData.due_date = null;
        // projectData.installation_date = requestData[0]['participation_ends'] || null;
        projectData.installation_date = requestData[0]['participation_ends'] || requestData[0]['installation_end_date'] || null;
        projectData.installation_start_date = requestData[0]['installation_start_date'] || null;
        projectData.installation_end_date = requestData[0]['installation_end_date'] || null;
        projectData.project_category = requestData[0]['project_category'] || 0;
        projectData.community = requestData[0]['community'] || 1;
        projectData.project_type = requestData[0]['project_type'] || 0;
        projectData.website_url = '';
        projectData.state_id = 0;
        projectData.city = 'DefaultCity';
        projectData.cost = requestData[0]['budget'] || null;
        projectData.description = requestData[0]['description'];
        projectData.approved_image = requestData[0]['thumbnail'] || 'https://media.freshbooks.com/wp-content/uploads/2022/03/approve-an-invoice-payment.jpg';
        projectData.cover_image = requestData[0]['thumbnail'] || null;
        projectData.reason = requestData[0]['reason'] || '';
        projectData.added_by = requestData[0]['added_by'];
        projectData.timestamp = moment().unix();
        projectData.status = reqresult[0]?.allInstallationCompleted ? 3 :1;
        projectData.completion = 0;
        projectData.installation_status = 0;
        projectData.timestamp = currentTime
        // console.log('requestData[0]?.job_category_name',requestData[0]?.job_category_name)
        // console.log('projectData',projectData)

      emitProgress('checking_project_name', `Checking job name availability...`, 35);
      
      let projectNameExist = await Project.isProjectNameExist(projectData.name);
      // if (projectNameExist != true) { 
      projectData.thumbnail = 'fileNamewithDate';
      let checkProjectExist = await Request.checkExistingProject(requestId)
      // let allServices = await Product.getAllServicesBasedOnRequest(requestId)
      // // console.log('allServices----',allServices);
      // for (const product of allServices) {
      //   for (const workflow of product?.workflow) {
      //     console.log('--------workflow', workflow)
      //   }
      // }
      // return;
      if (!checkProjectExist) {
        emitProgress('creating_new_project', `Creating new job...`, 40);
       
        let result = await Project.addProject(projectData)

        if (result) {
          emitProgress('project_created', `Job created successfully`, 45);
          
          let projects = await Project.getProjectById(result.id);

          // Add collaborators (if any) from the request to project_members
          try {
            const rawCollaborators = requestData?.[0]?.collaborators || salesOrderData[0]?.collaborators || null;
            if (rawCollaborators) {
              const collaboratorIds = Array.isArray(rawCollaborators)
                ? rawCollaborators.map(c => (typeof c === 'object' ? (c.id ?? c) : c))
                : String(rawCollaborators || '').split(',').map(s => s.trim()).filter(Boolean);

              for (const coll of collaboratorIds) {
                const staffId = coll ? Number(coll) : null;
                if (!staffId) continue;
                const pm = {
                  project_id: result.id,
                  staff_id: staffId,
                  partner_id: null,
                  contact_id: null,
                  lead_id: null,
                  collaboration :1,
                };
                const alreadyExisting = await Project.projectMemberExist(pm);
                if (!alreadyExisting) {
                  await Project.addProjectMember(pm);
                }
              }
            }
          } catch (err) {
            console.log('Error adding collaborators to project members (create):', err);
          }
          let allProducts = await Product.getAllProductsBasedOnRequest(requestId)
          allProducts = allProducts.filter(row=>row.facility_id == FACILITY_ID)
          let allServices = await Product.getAllServicesBasedOnRequest(requestId,false)
          let taskUid = await ProjectTask.getSubTaskUid(result.id);
          let materialData = await Product.getMaterialDataFromRequestId(requestId)
          
          emitProgress('fetching_products_services', `Fetching products and services data...`, 50);

          
          // let filteredMaterialDataForQb = materialData.flat().filter(item => item?.isReceived == 1);
            
          // try {
          //   let response = await createJournalEntryForJobStarted(filteredMaterialDataForQb, projectData?.name)
          // } catch (error) {
          //   console.log("Error in QuickBooks API: ", error);
          // }

          emitProgress('processing_attachments', `Processing job attachments...`, 55);
          
          let allReqAttachments = await Request.getAllRequestAttachments(requestId)
          const urlObjects = [];
          allReqAttachments?.forEach(attachment => {
            if(attachment?.approved === 1){
              const isImage = attachment?.file_type?.startsWith('image/') || attachment?.file_type == 'img';
              const isVideo = attachment?.file_type?.startsWith('video/');
              const isPdf = attachment?.file_type === 'application/pdf';
              let attachmentExtension = null
              if(isImage){
                attachmentExtension = 'image'
              }else if(isVideo){
                attachmentExtension = 'video'
              }else{
                attachmentExtension = 'other'
              }
              urlObjects.push({
                  url: attachment.name,
                  file_type:attachment?.file_type || 'img',
                  extension: attachmentExtension
              });
            }
          });
      
          // console.log("$$$$$$$$$$URL",urlObjects);
          for(let i=0;i<urlObjects.length;i++){
            let data={}
            if(urlObjects[i].url){
                data.userid=userId,
                data.project_id=result.id,
                data.file_url=urlObjects[i].url
                data.file_type=urlObjects[i].file_type
                data.extension=urlObjects[i].extension,
                data.timestamp=moment().unix()
                let inserted=await ProjectTask.addProjectAttachments(data)
            }
          }
       
          emitProgress('processing_workflows', `Processing catalog workflows and skills...`, 60);
          
          let workFlowIDs = ""
          let skillSet = []
          
          for (const product of allProducts) {
            if(product?.workflow[0]?.workflow_id) {

              skillSet.push(await Product.getWorkflowById(product?.workflow[0]?.workflow_id))

            }

            const allSkillIds = skillSet
              ?.flatMap(workflow => workflow?.map(stage => stage?.skill_ids?.split(',')))
            
              ?.flat()
            let uniqueSkillIds = [...new Set(allSkillIds)];
            uniqueSkillIds = uniqueSkillIds?.filter(skillId => skillId !== "" && skillId !== undefined);

           
            // console.log(uniqueSkillIds);
            let uniqueSkillIdsString = uniqueSkillIds?.join(',');
          
          
            uniqueSkillIdsString=uniqueSkillIdsString?.replace(/,$/, '');
            let projectManagers=[]
            if(!empty(uniqueSkillIdsString)){
             projectManagers = await Project.getTeamMembersBasedOnSkills(uniqueSkillIdsString, 14)
            }

            //   reply.send({uniqueSkillIds:uniqueSkillIds,uniqueSkillIdsString:uniqueSkillIdsString,projectManagers:projectManagers})
            let pm = {}
            // console.log(projectManagers, '--------Team based on Skill Task')
            for (const projectManager of projectManagers) {

              pm = {
                project_id: result.id,
                staff_id: !empty(projectManager["id"]) ? projectManager["id"] : NULL,
                partner_id: null,
                contact_id: null,
                lead_id: null,

              };
              let alreadyExisting = await Project.projectMemberExist(pm);

              if (!alreadyExisting) {
                emitProgress('adding_project_members', `Adding job members for ${product.product_name}...`, 61);
                let insertId = await Project.addProjectMember(pm);
                emitProgress('sending_member_notifications', `Sending notifications to job members...`, 62);
                let notificationMsg = `New Job "${projectData.name}" has been added.`;
                let notificationUrl = "Jobs";
                const incrementResult_User = await Admin.incrementField(
                  projectManager.user_id,
                  "noti_count",
                  1
                );
                let notificationData_user = {
                  user_id: projectManager.user_id||1,
                  url: notificationUrl,
                  timestamp: currentDate,
                  message: "has assigned you to the job: " + projectData.name,
                  status: 1,
                  from_id: projectData.added_by,
                };

                const addNotificationResult_user = await Admin.addNotification(notificationData_user);
              }
            }
          }
          emitProgress('creating_product_tasks', `Creating catalog tasks...`, 65);
          
          for (const product of allProducts) {
            emitProgress('creating_task', `Creating task: ${product.product_name}...`, 66);
            const projectTask = {
              name: product.product_name,
              project_id: result.id,
              timestamp: moment().unix(),
              userid: projectData.added_by,
              start_date: projectData.start_date || null,
              due_date: projectData.installation_date ? projectData.installation_date : null,
              task_uid: taskUid,
              complete: 0,
              complete_sync: 1,
              status: 1,
              cover_image: product?.product_design || projects?.cover_image || null,
              product_id: product?.product_id ? parseInt(product?.product_id) : null,
              // cover_image: projects.cover_image || "null",
              priority: 1,
              reporter_id: projectData.added_by,
              description: "",
              project_task_uid: "",
              order_id: product?.product_index,
              customer_order_product_id:product?.id,
              request_location_id:product?.location_id,
            };
            const projectTaskId = await ProjectTask.addProjectTask(projectTask);
            let task_uid = await ProjectTask.getSubTaskUid(projectTaskId);
            if(product?.workflow){
              emitProgress('creating_subtasks', `Creating subtasks for ${product.product_name}...`, 67);
              for (const workflow of product?.workflow) {
              emitProgress('creating_subtask', `Creating subtask: ${workflow.name}...`, 68);
              const subTask = {
                name: workflow.name,
                task_id: projectTaskId,
                skill_ids: workflow?.skill_ids,
                timestamp: moment().unix(),
                userid: projectData?.added_by,
                start_date: projectData.start_date || null,
                due_date: projectData.installation_date ? projectData.installation_date : null,
                task_uid: task_uid,
                complete: 0,
                priority: 1,
                status: 1,
                reporter_id: projectData.added_by,
                description: workflow?.message,
              };

              const projectSubTaskId = await ProjectTask.addProjectSubTask(subTask);
              let projectManagers=[]
              if(!empty(workflow?.skill_ids)){
               projectManagers = await Project.getTeamMembersBasedOnSkills(workflow?.skill_ids, 14)
              }
              
              // console.log(projectManagers, '--------Team based on Skill sub task')
             
              emitProgress('assigning_subtask_members', `Assigning members to subtask: ${workflow.name}...`, 69);
              let notificationMsg = `New Job "${projectData.name}" has been added.`;
              let notificationUrl = "Jobs";
              let pm = {}
              for (const projectManager of projectManagers) {

                pm = {
                  project_id: result.id,
                  subtask_id: projectSubTaskId,
                  staff_id: !empty(projectManager["id"]) ? projectManager["id"] : NULL,
                  partner_id: null,
                  contact_id: null,
                  lead_id: null,

                  };

                let insertId = await ProjectTask.AddMembersToSubTask(pm);
                emitProgress('sending_subtask_notifications', `Sending notifications to subtask members...`, 70);
                let notificationData_user = {
                  user_id: projectManager.user_id||1,
                  url: notificationUrl,
                  timestamp: currentDate,
                  message: "has assigned you to the job: " + projectData.name,
                  status: 1,
                  from_id: projectData.added_by,
                };

                const addNotificationResult_user = await Admin.addNotification(
                  notificationData_user
                );

              }

            }
            }
          }

          emitProgress('processing_services', `Processing services and workflows...`, 70);
          
          if(allServices?.length > 0){
            emitProgress('processing_service_workflows', `Processing ${allServices.length} service workflows...`, 71);
            let skillSet = []
            for (const product of allServices) {
              if(product?.workflow[0]?.workflow_id) {
                skillSet.push(await Product.getWorkflowById(product?.workflow[0]?.workflow_id))
              }

              const allSkillIds = skillSet
                ?.flatMap(workflow => workflow?.map(stage => stage?.skill_ids?.split(',')))
              
                ?.flat()
              let uniqueSkillIds = [...new Set(allSkillIds)];
              uniqueSkillIds = uniqueSkillIds?.filter(skillId => skillId !== "" && skillId !== undefined);

            
              // console.log(uniqueSkillIds);
              let uniqueSkillIdsString = uniqueSkillIds?.join(',');
            
            
              uniqueSkillIdsString=uniqueSkillIdsString?.replace(/,$/, '');
              let projectManagers=[]
              if(!empty(uniqueSkillIdsString)){
                projectManagers = await Project.getTeamMembersBasedOnSkills(uniqueSkillIdsString, 14)
              }
              
              //   reply.send({uniqueSkillIds:uniqueSkillIds,uniqueSkillIdsString:uniqueSkillIdsString,projectManagers:projectManagers})
              let pm = {}
              // console.log(projectManagers, '--------Team based on Skill Task')
              for (const projectManager of projectManagers) {

                pm = {
                  project_id: result.id,
                  staff_id: !empty(projectManager["id"]) ? projectManager["id"] : NULL,
                  partner_id: null,
                  contact_id: null,
                  lead_id: null,

                };
                let alreadyExisting = await Project.projectMemberExist(pm);

                if (!alreadyExisting) {
                  emitProgress('adding_service_members', `Adding service members for ${product.service_name}...`, 72);
                  let insertId = await Project.addProjectMember(pm);
                  emitProgress('sending_service_member_notifications', `Sending notifications to service members...`, 73);
                  let notificationMsg = `New Job "${projectData.name}" has been added.`;
                  let notificationUrl = "Jobs";
                  const incrementResult_User = await Admin.incrementField(
                    projectManager.user_id,
                    "noti_count",
                    1
                  );
                  let notificationData_user = {
                    user_id: projectManager.user_id||1,
                    url: notificationUrl,
                    timestamp: currentDate,
                    message: "has assigned you to the job: " + projectData.name,
                    status: 1,
                    from_id: projectData.added_by,
                  };

                  const addNotificationResult_user = await Admin.addNotification(
                    notificationData_user
                  );
                }
              }
            }
            emitProgress('creating_service_tasks', `Creating service tasks...`, 74);
            for (const product of allServices) {
              emitProgress('creating_service_task', `Creating service task: ${product.service_name}...`, 75);
              const projectTask = {
                name: product.service_name,
                project_id: result.id,
                timestamp: moment().unix(),
                userid: projectData.added_by,
                start_date: projectData.start_date || null,
                due_date: projectData.installation_date ? projectData.installation_date : null,
                task_uid: taskUid,
                complete: 0,
                complete_sync: 1,
                status: 1,
                cover_image: product?.product_design || projects?.cover_image || null,
                product_id: product?.product_id ? parseInt(product?.product_id) : null,
                service_id: product?.labour_cost_service_id ? parseInt(product?.labour_cost_service_id) : null,
                // cover_image: projects.cover_image || "null",
                priority: 1,
                reporter_id: projectData.added_by,
                description: "",
                project_task_uid: "",
                order_id: product?.product_index,
                customer_order_product_id:product?.id

              };

              const projectTaskId = await ProjectTask.addProjectTask(projectTask);
              let task_uid = await ProjectTask.getSubTaskUid(projectTaskId);
              if(product?.workflow){
                  emitProgress('creating_service_subtasks', `Creating service subtasks for ${product.service_name}...`, 76);
                  for (const workflow of product?.workflow) {
                // console.log(workflow, '--------workflow')
                emitProgress('creating_service_subtask', `Creating service subtask: ${workflow.name}...`, 77);
                const subTask = {
                  name: workflow.name,
                  task_id: projectTaskId,
                  skill_ids: workflow?.skill_ids,
                  timestamp: moment().unix(),
                  userid: projectData?.added_by,
                  start_date: projectData.start_date || null,
                  due_date: projectData.installation_date ? projectData.installation_date : null,
                  task_uid: task_uid,
                  complete: 0,
                  priority: 1,
                  status: 1,
                  reporter_id: projectData.added_by,
                  description: workflow?.message,
                };

                const projectSubTaskId = await ProjectTask.addProjectSubTask(subTask);
                let projectManagers=[]
                if(!empty(workflow?.skill_ids)){
                projectManagers = await Project.getTeamMembersBasedOnSkills(workflow?.skill_ids, 14)
                }
                
                // console.log(projectManagers, '--------Team based on Skill sub task')
              
                emitProgress('assigning_service_subtask_members', `Assigning members to service subtask: ${workflow.name}...`, 78);
                let notificationMsg = `New Job "${projectData.name}" has been added.`;
                let notificationUrl = "Jobs";
                let pm = {}
                for (const projectManager of projectManagers) {

                  pm = {
                    project_id: result.id,
                    subtask_id: projectSubTaskId,
                    staff_id: !empty(projectManager["id"]) ? projectManager["id"] : NULL,
                    partner_id: null,
                    contact_id: null,
                    lead_id: null,

                  };

                  let insertId = await ProjectTask.AddMembersToSubTask(pm);
                  emitProgress('sending_service_subtask_notifications', `Sending notifications to service subtask members...`, 79);
                  let notificationData_user = {
                    user_id: projectManager.user_id||1,
                    url: notificationUrl,
                    timestamp: currentDate,
                    message: "has assigned you to the job: " + projectData.name,
                    status: 1,
                    from_id: projectData.added_by,
                  };

                  const addNotificationResult_user = await Admin.addNotification(
                    notificationData_user
                  );

                }

              }
              }
            }
          }
       
          emitProgress('finalizing_tasks', `Finalizing job tasks...`, 75);
          
          let allProjectTask = await ProjectTask.getAllProjectTasks(result.id, userType = 1, userId = 1)
          console.log('allProjectTaskallProjectTask',allProjectTask)
          allCreatedTask = await ProjectTask.getAllProjectTasks(result.id, userType = 1, userId = 1)
          console.log('allCreatedTaskallCreatedTask',allCreatedTask)
          // if(((allProjectTask?.length <= 0 && requestData[0]['noServiceNoTask'] == 1 && !isSplit) || (allCreatedTask?.length <= 0)) && projectCategoryId !== 12 && !isSplit){
          //   // Service-only requests (projectCategoryId = 11) should have status = 1 (pre-production), others = 3 (invoicing)
          //   const projectStatus = projectCategoryId === 11 ? 1 : 3;
          //   const resultStatus = await Project.updateProject(result.id, { status: projectStatus, completion : 100 });
          // }
          if (allCreatedTask?.length <= 0 && projectCategoryId !== 12) {
            console.log(`⚠️ No installation created for project ${result.id}, but tasks exist.`);
            
            await Project.updateProject(result.id, { noServiceNoTask: 0 });
            await Request.updateRequestById(requestId, { noServiceNoTask: 0 });

            // notify
            let notificationMsg = `Sales Order "${projectData.name}" has been moved to 'Invoicing' because all tasks and installtions are off.`;
            let notificationUrl = "official-request";
            // Increment notification count
            const incrementResult_User = await Admin.incrementField(userId, "noti_count", 1);
            let notificationData_user = {
              user_id: userId,
              url: notificationUrl,
              timestamp: currentDate,
              message: notificationMsg,
              status: 1,
              from_id: userId,
            };
            const addNotificationResult_user = await Admin.addNotification(notificationData_user);

            // Alert to all admins of new added project

            let adminIds = await Admin.getAllAdminIds();
            let notificationData = {};
            for (const adminId of adminIds) {
              if (adminId != userId) {
                const incrementResult = await Admin.incrementField(adminId, "noti_count", 1);
                notificationData = {
                    user_id: adminId,
                    url: notificationUrl,
                    timestamp: currentDate,
                    message: notificationMsg,
                    status: 1,
                    from_id: userId,
                };
                const addNotificationResult = await Admin.addNotification(notificationData);
              }
            }
          }
          emitProgress('processing_job_cost', `Processing job cost calculations...`, 80);
          
          /************  Jobcost ******************** */

          emitProgress('fetching_materials', `Fetching materials and product formulas...`, 82);
          
          let allMaterials = await Product.getAllMaterials(true)
          // let allProductsData = await Product.getAllLocations(requestId)
          let allRecipies = []
          let allProductsData = []
          if (requestData[0]?.project_category === 15) {
            allProductsData = await Product.getAllLocations(requestId)
          } else {
            allProductsData = await Product.getNonStockProducts(requestId)
          }
         console.log('allProductsData--------1',allProductsData)
          // console.log('allProductsData----',allProductsData)
          // if(allProductsData && requestData[0]?.job_category_name != 'Time & Material'){
          if(allProductsData){
            allRecipies = await Recipe.getAllReceipesByProjectId(allProductsData,requestId);
          }
          let results = await Product.getFinishedProducts(0,true);
          let newMaterialObjects = [];
          let jobCostFlag=0;
          let notifyJobcost=""
          
          emitProgress('processing_recipes', `Processing recipes and material calculations...`, 84);
          // console.log("#################recipe123", results);
         

          for (const recipeItem of allRecipies) {
            // let results = await Product.getFinishedProducts();
            for (const ingredient of recipeItem?.ingredients) {
              let materialItems = results?.filter((mat) => mat?.material_id === ingredient?.product_id);
              let filteredMaterialItems = materialItems?.filter(mat => 
                (ingredient.stockType === 2 ? mat.partial === 1 : mat.partial === 0)
              );
              let quantityInInitialUnit = 0;
              quantityInInitialUnit = ingredient?.quantity;
              // if(ingredient.stockType === 1 || materialItems[0]?.unit == 4){
              //   quantityInInitialUnit = ingredient?.quantity;
              // }else{
              //   let conversionFactorToInitial = getConversionFactor(ingredient?.convertionUnit, materialItems[0]?.unit, ingredient?.width || 1);
              //   quantityInInitialUnit = ingredient?.convertionQty * conversionFactorToInitial;
              // }
              let quantityNeeded = quantityInInitialUnit * recipeItem?.quantity * recipeItem?.location_quantity;
              // let quantityNeeded = ingredient?.quantity*recipeItem?.quantity*recipeItem?.location_quantity;
              // let totalAvailableQuantity = filteredMaterialItems?.reduce((total, item) => total + item.qty, 0);
              let totalAvailableQuantity = 0;
              if(ingredient.stockType === 1){
                totalAvailableQuantity = filteredMaterialItems?.reduce((total, item) => total + item?.qty, 0);
              }else{
                totalAvailableQuantity = filteredMaterialItems?.filter((item)=>item.partial_unit==ingredient?.convertionUnit)?.reduce((total, item) => total + item?.qty, 0);
              }
              // console.log(`Converted Ingredient Quantity to Initial Unit (${ingredient.convertionUnit} → ${materialItems[0]?.unit}), width : ${ingredient?.width}:`, quantityInInitialUnit);
              // console.log('filteredMaterialItems===', filteredMaterialItems);
              // console.log('ingredient?.quantity===', ingredient?.quantity);
              // console.log('recipeItem?.quantity===', recipeItem?.quantity);
              // console.log('recipeItem?.location_quantity===', recipeItem?.location_quantity);
              // console.log('Total Available Quantity (Before Conversion) ===', totalAvailableQuantity);
              // console.log('Quantity Needed (Before Conversion) ===', quantityNeeded);
              
              // // Get actual width and quantity from material stock
              // let width = ingredient?.width || 1;

              // // Convert quantityNeeded dynamically using width
              // let conversionFactor = getConversionFactor(ingredient?.convertionUnit, materialItems[0]?.unit, width);
              // let convertedQuantityNeeded = quantityNeeded * conversionFactor;

              if (totalAvailableQuantity >= quantityNeeded) {
              // if (totalAvailableQuantity >= convertedQuantityNeeded) {
                // ✅ Sufficient stock available
                // let remainingQuantity = convertedQuantityNeeded;
                let remainingQuantity = quantityNeeded;
                filteredMaterialItems?.forEach((materialItem) => {

                  // Convert unit cost based on unit type
                  let originalUnitCost = Number(materialItem?.unit_cost) || 0;
                  // let convertedUnitCost = getConvertedUnitCost(originalUnitCost, materialItem?.unit, ingredient?.convertionUnit, width);

                  // console.log(`Unit Cost (Before Conversion):`, originalUnitCost);
                  // console.log(`Converted Unit Cost (${materialItems[0]?.unit} → ${ingredient.convertionUnit} at width ${width}):`, convertedUnitCost);
                  // console.log(`totalAvailableQuantity :`, totalAvailableQuantity);
                  // console.log(`convertedQuantityNeeded :`, convertedQuantityNeeded);

                  if (remainingQuantity > 0) {
                    let quantityToPull = Math.min(remainingQuantity, materialItem?.qty);
                    newMaterialObjects.push({
                      id: materialItem?.id,
                      productId: materialItem?.material_id,
                      quantity: quantityToPull,
                      unit_cost: materialItem?.inhouse == 0 ? 0 : originalUnitCost,
                      total_cost: materialItem?.inhouse == 0 ? 0 : parseFloat(quantityToPull) * parseFloat(originalUnitCost),
                      recipe: recipeItem?.id,
                      recipeName: recipeItem?.name,
                      customer_order_product_id: recipeItem?.customer_order_product_id,

                    });
                    results = results?.map(item => item?.id === materialItem?.id ? { ...item, qty: item?.qty - quantityToPull } : item);
                    remainingQuantity -= quantityToPull;
                  }
                });
                // console.log(`Added ${quantityNeeded} units of ${ingredient.name}`);
              } else {
                // Insufficient quantity available in existing packages, pull from additional packages
                console.log(`Insufficient quantity of ${ingredient?.name} available in existing packages.1`);
                console.log(`Pulling additional ${quantityNeeded - totalAvailableQuantity} units from packages...1`);
                // Your logic to pull additional quantities from packages goes here
              
              
                if(empty(notifyJobcost)){
                    notifyJobcost+=`There is insuffient quantity on materials: ${ingredient?.name} (${quantityNeeded}) (${ingredient.stockType === 1 ? 'Finished' : 'Partial'})`
                }
                else{
                   notifyJobcost+=` ,${ingredient?.name} (${quantityNeeded}) (${ingredient.stockType === 1 ? 'Finished' : 'Partial'})`
                }
                jobCostFlag=1
              }
            };
          };
          emitProgress('processing_rfp_products', `Processing RFP products for job cost...`, 86);
          
          let rfpProduct=await CustomerOrderProduct.getRFPProductByRequest(requestId)
        
          // console.log("###############vk1888888888888888888", rfpProduct);
          for(let i=0;i<rfpProduct?.length;i++){
            let jobCostData = {
              product_id: rfpProduct[i]?.product_id,
              project_id: result?.id,
              actual_quantity: rfpProduct[i]?.product_qty*rfpProduct[i]?.location_quantity,
              actual_unit_price:  rfpProduct[i]?.material == 0 ? rfpProduct[i]?.vendor_price : rfpProduct[i]?.product_price,
              actual_total:  rfpProduct[i]?.material == 0 ? rfpProduct[i]?.vendor_price : rfpProduct[i]?.product_price*rfpProduct[i]?.product_qty*rfpProduct[i]?.location_quantity,
              quantity: rfpProduct[i]?.product_qty*rfpProduct[i]?.location_quantity,
              unit_price:  rfpProduct[i]?.material == 0 ? rfpProduct[i]?.vendor_price : rfpProduct[i]?.product_price,
              total:  rfpProduct[i]?.material == 0 ? rfpProduct[i]?.vendor_price : rfpProduct[i]?.product_price*rfpProduct[i]?.product_qty*rfpProduct[i]?.location_quantity,
              // quantity: 0,
              // unit_price:  rfpProduct[i]?.material == 0 ? rfpProduct[i]?.vendor_price : rfpProduct[i]?.product_price,
              // total:  0,
              recipe: 1,
              recipe_name: null,
              timestamp: moment().unix(),
              added_by: userId,
              rfp:1,
              customer_order_product_id: rfpProduct[i]?.id,

            };
            insertedstatus = await JobCost.addJobCost(jobCostData);
            console.log("#####insertedStatus 1", jobCostData);
          }
          emitProgress('updating_material_stock', `Updating material stock levels...`, 88);
          
          for (let i = 0; i < newMaterialObjects?.length; i++) {
            const materialData = await Product.getMaterialById(newMaterialObjects[i].id)
            if (!empty(materialData)) {

              let newQty = materialData[0].qty - newMaterialObjects[i].quantity
              // console.log("#############MaterialData", materialData[0]);
              // console.log("#############newMaterialObjects", newMaterialObjects[i]);
              // console.log("#############newQty1", newQty);
              if (newQty >= 0) {
                let totalCost = newMaterialObjects[i].unit_cost * newQty
                let data = {};
                data['qty'] = newQty
                data['total_cost'] = parseInt(totalCost)
                let jobCostData = {
                  product_id: newMaterialObjects[i].id,
                  project_id: result.id,
                  // quantity: reqresult[0]?.allInstallationCompleted ? newMaterialObjects[i].quantity : 0,
                  quantity: reqresult[0]?.allInstallationCompleted ? newMaterialObjects[i].quantity : 0,
                  unit_price: newMaterialObjects[i].unit_cost,
                  total: reqresult[0]?.allInstallationCompleted ? newMaterialObjects[i].total_cost : 0,
                  // quantity: newMaterialObjects[i].quantity,
                  // unit_price: newMaterialObjects[i].unit_cost,
                  // total: newMaterialObjects[i].total_cost,
                  actual_quantity: newMaterialObjects[i].quantity,
                  actual_unit_price: newMaterialObjects[i].unit_cost,
                  actual_total: newMaterialObjects[i].total_cost,
                  recipe: 1,
                  recipe_name: newMaterialObjects[i].recipeName,
                  timestamp: moment().unix(),
                  added_by: userId,
                 customer_order_product_id: newMaterialObjects[i]?.customer_order_product_id,

                };
                if (jobCostData?.actual_quantity > 0) {
                  insertedstatus = await JobCost.addJobCost(jobCostData);
                  console.log("#####insertedStatus 2", jobCostData);

                  reduceStockResult = await Product.updateMaterialStock(newMaterialObjects[i].id, data)
                  // console.log("#####reduceStockResult", reduceStockResult);

                  // Autofill stock if stock is 0
                  // if(newQty <= 0){
                  //   if(materialData[0]?.autofill == 1){
                  //     const parentFinishedMaterial = await Product.getMaterialById(materialData[0]?.parent_finished_product_id)
                  //     console.log('parentFinishedMaterial----',parentFinishedMaterial)
                  //     if (parentFinishedMaterial.length > 0 && parentFinishedMaterial[0]?.qty > 0) {
                  //       let reducedQty = parentFinishedMaterial[0]?.qty - 1
                  //       let parentTotalCost = parentFinishedMaterial[0]?.unit_cost * reducedQty
                  //       let parentData = {
                  //         qty: reducedQty,
                  //         total_cost: parseInt(parentTotalCost)
                  //       };
                  //       const reduceFinishedProductStock = await Product.updateMaterialStock(materialData[0]?.parent_finished_product_id, parentData)
        
                  //       let addingQty = materialData[0]?.actual_qty
                  //       let totalCost = materialData[0]?.actual_total_cost
                  //       let autoFilldata = {
                  //         qty: addingQty,
                  //         total_cost: totalCost
                  //       };
                  //       console.log('autofillPartialStock--',autoFilldata);
                  //       const autofillPartialStock = await Product.updateMaterialStock(materialData[0]?.id, autoFilldata);
                  //     }
                  //   }
                  // }
                }
              } else {
                console.log(`Stock is lesser than entered qty 1`);
                // Autofill stock if stock is 0
                // if(newQty <= 0){
                //   if(materialData[0]?.autofill == 1){
                //     const parentFinishedMaterial = await Product.getMaterialById(materialData[0]?.parent_finished_product_id)
                //     console.log('parentFinishedMaterial----',parentFinishedMaterial)
                //     if (parentFinishedMaterial.length > 0 && parentFinishedMaterial[0]?.qty > 0) {
                //       let reducedQty = parentFinishedMaterial[0]?.qty - 1
                //       let parentTotalCost = parentFinishedMaterial[0]?.unit_cost * reducedQty
                //       let parentData = {
                //         qty: reducedQty,
                //         total_cost: parseInt(parentTotalCost)
                //       };
                //       const reduceFinishedProductStock = await Product.updateMaterialStock(materialData[0]?.parent_finished_product_id, parentData)
      
                //       let addingQty = materialData[0]?.actual_qty
                //       let totalCost = materialData[0]?.actual_total_cost
                //       let autoFilldata = {
                //         qty: addingQty,
                //         total_cost: totalCost
                //       };
                //       console.log('autofillPartialStock--',autoFilldata);
                //       const autofillPartialStock = await Product.updateMaterialStock(materialData[0]?.id, autoFilldata);
                //     }
                //   }
                // }
                // reply.send({ status: 0, message: `Stock is lesser than entered qty for material (${materialData[0].barcode})`, resultId: materialData[0].id });
              }
            }
          }
          // console.log("#################newMaterialObjects", newMaterialObjects);
            let newMaterialRfqObjects = [];
            let bringStockCatalog = requestData[0].project_category == 15 ? false: true
          let materialRfq=await CustomerOrderProduct.getMaterialProductByRequest(requestId,bringStockCatalog)
          if(requestData[0]['job_category_name'] == 'Time & Material'){
            for(let i=0; i<materialRfq.length; i++){
              let results = await Product.getFinishedProducts(materialRfq[i].product_id,true);
              // console.log('results===== 122',results)
              let notPartial=materialRfq[i].stock_type===1 ? true : false
              let filteredResult=[]
              if(notPartial){
                filteredResult = results?.filter(item=>item.partial == 0 )
              }else{
                filteredResult = results?.filter(item=>((item.partial == 1) && (item.partial_unit == materialRfq[i].convertion_unit)) )
              }
              // console.log('results===== 12',results)
              if(filteredResult?.length > 0){
                let jobCostData = {
                  product_id: filteredResult[0].id,
                  project_id: result.id,
                  quantity: reqresult[0]?.allInstallationCompleted ? materialRfq[i].product_qty : 0,
                  unit_price: materialRfq[i].product_price,
                  total: reqresult[0]?.allInstallationCompleted ? materialRfq[i].product_total : 0,
                  // quantity: materialRfq[i].quantity,
                  // unit_price: materialRfq[i].unit_cost,
                  // total: materialRfq[i].total_cost,
                  actual_quantity: materialRfq[i].product_qty,
                  actual_unit_price: materialRfq[i].product_price,
                  actual_total: materialRfq[i].product_total,
                  recipe: 0,
                  recipe_name: '',
                  timestamp: moment().unix(),
                  added_by: userId,
                  customer_order_product_id: materialRfq[i]?.id,
                };
                insertedstatus = await JobCost.addJobCost(jobCostData);
                console.log("#####insertedStatus 3", jobCostData);
              } else {
                console.log(`Insufficient quantity of ${materialRfq[i].product_name} available in existing packages.21`);
                if(empty(notifyJobcost)){
                  notifyJobcost+=`There is insuffient quantity on materials: ${materialRfq[i].product_name} (Finished)`
                }
                else{
                  notifyJobcost+=` ,${materialRfq[i].product_name} (Finished)`
                }
                jobCostFlag=1
              }
            }
          }else{
            for(let i=0;i<materialRfq.length; i++){
              let quantityNeeded = materialRfq[i].product_qty;
              // let results = await Product.getFinishedProducts();
              let notPartial=materialRfq[i].stock_type===1 ? true : false
              console.log('notPartial', notPartial)
              let filteredResult=[]
              if(notPartial){
                filteredResult = results?.filter(item=>item.partial == 0 )
              }else{
                filteredResult = results?.filter(item=>((item.partial == 1) && (item.partial_unit == materialRfq[i].convertion_unit)) )
              }
              let materialItems = filteredResult.filter((mat) => mat.material_id === materialRfq[i].product_id);
              let totalAvailableQuantity = materialItems.reduce((total, item) => total + item.qty, 0);

              
              if (totalAvailableQuantity >= quantityNeeded) {
                // Sufficient quantity available in existing packages
                let remainingQuantity = quantityNeeded;
                // console.log('materialRfq---',materialRfq)
                // console.log('materialItems---',materialItems)
                materialItems.forEach((materialItem) => {
                  if (remainingQuantity > 0) {
                    let quantityToPull = Math.min(remainingQuantity, materialItem.qty);
                    newMaterialRfqObjects.push({
                      id: materialItem.id,
                      productId: materialItem.material_id,
                      quantity: quantityToPull,
                      unit_cost: materialItem?.inhouse == 0 ? 0 : materialItem.unit_cost,
                      total_cost: materialItem?.inhouse == 0 ? 0 : parseFloat(quantityToPull) * parseFloat(materialItem.unit_cost),
                      recipe:0,
                      recipeName: null,
                    });
                    filteredResult = filteredResult.map(item => item.id === materialItem.id ? { ...item, qty: item.qty - quantityToPull } : item);
                    remainingQuantity -= quantityToPull;
                  }
                });
              
              } else {
                // Insufficient quantity available in existing packages, pull from additional packages
                console.log(`Insufficient quantity of ${materialRfq[i].product_name} available in existing packages.22`);
                console.log(`Pulling additional ${quantityNeeded - totalAvailableQuantity} units from packages...2`);
                // Your logic to pull additional quantities from packages goes here
              
              
                if(empty(notifyJobcost)){
                    notifyJobcost+=`There is insuffient quantity on materials: ${materialRfq[i].product_name} (${quantityNeeded}) (Finished)`
                }
                else{
                  notifyJobcost+=` ,${materialRfq[i].product_name} (${quantityNeeded}) (Finished)`
                }
                jobCostFlag=1
              }
            }
            // console.log("#################newMaterialObjects22222222222",newMaterialRfqObjects);
            for (let i = 0; i < newMaterialRfqObjects.length; i++) {
              const materialData = await Product.getMaterialById(newMaterialRfqObjects[i].id)
              if (!empty(materialData)) {
                let newQty = materialData[0].qty - newMaterialRfqObjects[i].quantity
                // console.log("#############MaterialData", materialData[0]);
                // console.log("#############newMaterialObjects", newMaterialRfqObjects[i]);
                // console.log("#############newQty2", newQty);
                if (newQty >= 0) {
                  let totalCost = newMaterialRfqObjects[i].unit_cost * newQty
                  let data = {};
                  data['qty'] = newQty
                  data['total_cost'] = parseInt(totalCost)
                  let jobCostData = {
                    product_id: newMaterialRfqObjects[i].id,
                    project_id: result.id,
                    quantity: reqresult[0]?.allInstallationCompleted ? newMaterialRfqObjects[i].quantity : 0,
                    unit_price: newMaterialRfqObjects[i].unit_cost,
                    total: reqresult[0]?.allInstallationCompleted ? newMaterialRfqObjects[i].total_cost : 0,
                    // quantity: newMaterialRfqObjects[i].quantity,
                    // unit_price: newMaterialRfqObjects[i].unit_cost,
                    // total: newMaterialRfqObjects[i].total_cost,
                    actual_quantity: newMaterialRfqObjects[i].quantity,
                    actual_unit_price: newMaterialRfqObjects[i].unit_cost,
                    actual_total: newMaterialRfqObjects[i].total_cost,
                    recipe: 0,
                    recipe_name: newMaterialRfqObjects[i].recipeName,
                    timestamp: moment().unix(),
                    added_by: userId,
                    // customer_order_product_id: newMaterialRfqObjects[i]?.customer_order_product_id,
                  };
                  if (jobCostData?.actual_quantity > 0) {
                    insertedstatus = await JobCost.addJobCost(jobCostData);
                    console.log("#####insertedStatus 4", jobCostData);

                    reduceStockResult = await Product.updateMaterialStock(newMaterialRfqObjects[i].id, data)
                    // console.log("#####reduceStockResult", reduceStockResult);
                  }
                } else {
                  console.log(`Stock is lesser than entered qty 2`);
                  // reply.send({ status: 0, message: `Stock is lesser than entered qty for ${materialData[0].name} (${materialData[0].barcode})`, resultId: materialData[0].id });
                }
              }
            }
          }

          if(requestData[0]['job_category_name'] != 'Time & Material'){
            for (let i = 0; i < newMaterialObjects?.length; i++) {
              const materialData = await Product.getMaterialById(newMaterialObjects[i].id)
              if (!empty(materialData)) {
                let newQty = materialData[0].qty + newMaterialObjects[i].quantity
                // console.log("#############MaterialData", materialData[0]);
                // console.log("#############newMaterialObjects", newMaterialObjects[i]);
                // console.log("#############newQty1", newQty);
                if (newQty >= 0) {
                  let totalCost = newMaterialObjects[i].unit_cost * newQty
                  let data = {};
                  data['qty'] = newQty
                  data['total_cost'] = parseInt(totalCost)
                  const reduceStockResultNew = await Product.updateMaterialStock(newMaterialObjects[i].id, data)
                } else {
                  console.log(`Stock is lesser than entered qty 3`);
                  // reply.send({ status: 0, message: `Stock is lesser than entered qty for material (${materialData[0].barcode})`, resultId: materialData[0].id });
                }
              }
            }
  
            for (let i = 0; i < newMaterialRfqObjects?.length; i++) {
              const materialData = await Product.getMaterialById(newMaterialRfqObjects[i].id)
              if (!empty(materialData)) {
                let newQty = materialData[0].qty + newMaterialRfqObjects[i].quantity
                // console.log("#############MaterialData", materialData[0]);
                // console.log("#############newMaterialObjects", newMaterialRfqObjects[i]);
                // console.log("#############newQty2", newQty);
                if (newQty >= 0) {
                  let totalCost = newMaterialRfqObjects[i].unit_cost * newQty
                  let data = {};
                  data['qty'] = newQty
                  data['total_cost'] = parseInt(totalCost)
                  const reduceStockResultNew = await Product.updateMaterialStock(newMaterialRfqObjects[i].id, data)
                  }
              } else {
                  console.log(`Stock is lesser than entered qty 4`);
                  // reply.send({ status: 0, message: `Stock is lesser than entered qty for ${materialData[0].name} (${materialData[0].barcode})`, resultId: materialData[0].id });
              }
            }
          }
       
          emitProgress('sending_notifications', `Sending job creation notifications...`, 80);
          
          let notificationMsg = `New Job "${projectData.name}" has been added.`;
          let notificationUrl = "Jobs";
          
          emitProgress('sending_user_notifications', `Sending notifications to job creator...`, 81);
          const incrementResult_User = await Admin.incrementField(
            projectData.added_by,
            "noti_count",
            1
          );
          let notificationData_user = {
            user_id: projectData.added_by,
            url: notificationUrl,
            timestamp: currentDate,
            message: notificationMsg,
            status: 1,
            from_id: projectData.added_by,
          };
          const addNotificationResult_user = await Admin.addNotification( notificationData_user );

          emitProgress('sending_admin_notifications', `Sending notifications to administrators...`, 82);
          // Alert to all admins of new added project
          let adminIds = await Admin.getAllAdminIds();
          let notificationData = {};
          for (const adminId of adminIds) {
            if (adminId != projectData.added_by) {
              const incrementResult = await Admin.incrementField(
                adminId,
                "noti_count",
                1
              );

              notificationData = {
                user_id: adminId,
                url: notificationUrl,
                timestamp: currentDate,
                message: notificationMsg,
                status: 1,
                from_id: projectData.added_by,
              };

              const addNotificationResult = await Admin.addNotification(
                notificationData
              );

            }
          }


          // }
          /////

          emitProgress('updating_request_status', `Updating status of sales order to Inproduction...`, 83);
          
          const statusUpdate = await Request.updateRequets(requestId, { status: 4,timestamp : currentTime });
          if (jobCostFlag === 1) {
            const resultStatus = await Project.updateProject(result.id, { jobcost: 1, notifyJobcost });
          }
          
          if (
            isSplit &&
            splitProjectCategoryId == 15 &&
            salesOrderData[0]?.for_parent_facility == 1 &&
            salesOrderData[0]?.origin_facility_id
          ) {
            try {
              const targetFacilityId = salesOrderData[0].origin_facility_id;
              const updateOriginFacilityStatus =
                await SplitManagementModel.updateOriginFacilityRequestStatus(
                  requestId,        // originRequestId in target DB
                  targetFacilityId, // which installation DB to update
                  {
                    status: 4,
                    split_mirror_status: 8,
                    timestamp: currentTime
                  }
                );
              console.log('updateOriginFacilityRequestStatus (build order)', updateOriginFacilityStatus);
            } catch (err) {
              console.error('Error updating origin facility request status for build-order split:', err);
            }
          }

          emitProgress('job_creation_complete', `Job created successfully!`, 90);
        }
      }
      else {
        emitProgress('updating_existing_project', `Updating existing job...`, 40);
        
        console.log('gggggggggggggggggg',installationStatus)
        if(installationStatus == 2){
          projectData.change_order = 0
          directInstalls = installationStatus == 2 ? true : directInstalls
        }else if (projectData && projectData.status === 1 ) {
          projectData.change_order = 1
        }
        console.log('gggggggggggggggggg',projectData)

        let result = await Project.updateProject(checkProjectExist, projectData)
        let statusUpdate = await Request.updateRequets(requestId, { status: 4 ,timestamp : currentTime });
        let projects = await Project.getProjectById(checkProjectExist);
        let allProducts = await Product.getAllProductsBasedOnRequest(requestId)
        allProducts = allProducts.filter(row=>row.facility_id == FACILITY_ID)
        console.log('allProductssssssss', allProducts)
        let allServices = await Product.getAllServicesBasedOnRequest(requestId,false);
        let taskUid = await ProjectTask.getSubTaskUid(checkProjectExist);
        let materialData = await Product.getMaterialDataFromRequestId(requestId)
        
        emitProgress('processing_existing_project_data', `Processing existing job data...`, 50);

        // Add collaborators (if any) from the request to project_members for existing project
        try {
          const rawCollaborators = requestData?.[0]?.collaborators || salesOrderData[0]?.collaborators || null;
          if (rawCollaborators) {
            const collaboratorIds = Array.isArray(rawCollaborators)
              ? rawCollaborators.map(c => (typeof c === 'object' ? (c.id ?? c) : c))
              : String(rawCollaborators || '').split(',').map(s => s.trim()).filter(Boolean);

            for (const coll of collaboratorIds) {
              const staffId = coll ? Number(coll) : null;
              if (!staffId) continue;
              const pm = {
                project_id: checkProjectExist,
                staff_id: staffId,
                partner_id: null,
                contact_id: null,
                lead_id: null,
              };
              const alreadyExisting = await Project.projectMemberExist(pm);
              if (!alreadyExisting) {
                await Project.addProjectMember(pm);
              }
            }
          }
        } catch (err) {
          console.log('Error adding collaborators to project members (update):', err);
        }

          
          // let filteredMaterialDataForQb = materialData.flat().filter(item => item?.isReceived == 1);
            
          // try {
          //   let response = await createJournalEntryForJobStarted(filteredMaterialDataForQb, projectData?.name)
          // } catch (error) {
          //   console.log("Error in QuickBooks API: ", error);
          // }


        emitProgress('updating_attachments', `Updating job attachments...`, 55);
        
        let allReqAttachments = await Request.getAllRequestAttachments(requestId)

        const attachmentDeleted = await ProjectTask.deleteProjectAttachments(checkProjectExist)
        
        const urlObjects = [];
        allReqAttachments?.forEach(attachment => {
          if(attachment?.approved === 1){
            const isImage = attachment?.file_type?.startsWith('image/') || attachment?.file_type == 'img';
            const isVideo = attachment?.file_type?.startsWith('video/');
            const isPdf = attachment?.file_type === 'application/pdf';
            let attachmentExtension = null
            if(isImage){
              attachmentExtension = 'image'
            }else if(isVideo){
              attachmentExtension = 'video'
            }else{
              attachmentExtension = 'other'
            }
            urlObjects.push({
                url: attachment.name,
                file_type:attachment?.file_type || 'img',
                extension: attachmentExtension
            });
          }
        });
    
        // console.log("$$$$$$$$$$URL",urlObjects);
        for(let i=0; i<urlObjects.length; i++){
          let data={}
          if(urlObjects[i]?.url){
              data.userid=userId,
              data.project_id=checkProjectExist,
              data.file_url=urlObjects[i].url
              data.file_type=urlObjects[i].file_type
              data.extension=urlObjects[i].extension,
              data.timestamp=moment().unix()
              let inserted=await ProjectTask.addProjectAttachments(data)
          }
        }

        emitProgress('updating_team_members', `Updating job team members...`, 60);
        
        // NOTE (keep old code as backup):
        // We should NOT wipe existing members on existing project update.
        // Only add missing members below using Project.projectMemberExist(pm).
        //
        // let deletedProjectMembers = await Project.deleteAllProjectMember(checkProjectExist);
        // console.log('$$$$$$$$$$$deletedProjectMembers', deletedProjectMembers);
        let workFlowIDs = ""
        let skillSet = []
        // reply.send({allProducts:allProducts})
        for (const product of allProducts) {
          if (product?.workflow[0]){
            if (product.workflow[0]?.workflow_id) {
              skillSet.push(await Product.getWorkflowById(product.workflow[0]?.workflow_id))
            }
          }

          const allSkillIds = skillSet
            .flatMap(workflow => workflow.map(stage => stage.skill_ids?.split(',')))
            .flat()
          let uniqueSkillIds = [...new Set(allSkillIds)];
          uniqueSkillIds = uniqueSkillIds?.filter(skillId => skillId !== "" && skillId !== undefined);
          let uniqueSkillIdsString = uniqueSkillIds?.join(',');
          // uniqueSkillIdsString = uniqueSkillIdsString?.slice(0, -1);
          uniqueSkillIdsString=uniqueSkillIdsString.replace(/,$/, '');
          let projectManagers=[]
          if(!empty(uniqueSkillIdsString)){
           projectManagers = await Project.getTeamMembersBasedOnSkills(uniqueSkillIdsString, 14)
          }
         
          let pm = {}
          for (const projectManager of projectManagers) {

            pm = {
              project_id: checkProjectExist,
              staff_id: !empty(projectManager["id"]) ? projectManager["id"] : NULL,
              partner_id: null,
              contact_id: null,
              lead_id: null,

            };
            let alreadyExisting = await Project.projectMemberExist(pm);

            if (!alreadyExisting) {
              emitProgress('adding_existing_project_members', `Adding existing job members for ${product.product_name}...`, 61);
              let insertId = await Project.addProjectMember(pm);
              emitProgress('sending_existing_member_notifications', `Sending notifications to existing job members...`, 62);
              let notificationMsg = `New Job "${projectData.name}" has been added.`;
              let notificationUrl = "Jobs";
              const incrementResult_User = await Admin.incrementField(
                projectManager.user_id,
                "noti_count",
                1
              );
              let notificationData_user = {
                user_id: projectManager.user_id,
                url: notificationUrl,
                timestamp: currentDate,
                message: "has assigned you to the job: " + projectData.name,
                status: 1,
                from_id: projectData.added_by,
              };

              const addNotificationResult_user = await Admin.addNotification(
                notificationData_user
              );
            }

          }

        }
        let allProjectTask = await ProjectTask.getAllProjectTasks(checkProjectExist, userType = 1, userId = 1)

        // // Loop through each task
        // const promises = allProjectTask.map(async task => {
        //   if (task.from_product == 1) {
        //     const taskNameExists = allProducts.some(product => ((product.product_name.includes(task.name)) && (product.product_index == task.order_id) && (product.location_id == task.request_location_id)));

        //     if (!taskNameExists) {
        //       await ProjectTask.updateProjectTask({ status: 0 }, task.ID);
        //       await ProjectTask.updateProjectSubTaskByTaskId({ status: 0 }, task.ID);
        //     }
        //   }
        // });

        // await Promise.all(promises);

        // ✅ Cleanup removed product/service tasks (only if NOT manually added)
        // If a product/service was removed from the Sales Order, delete its linked task + subtasks + subtask members + attachments.
        // Never touch manually added tasks (is_manually_added == 1).
        try {
          // Simplified cleanup:
          // In `project_tasks`, `customer_order_product_id` is the `customer_order_products.id`
          // (for both product rows and service rows).
          const currentCopIds = new Set([
            ...(allProducts || []).map(p => Number(p?.id)).filter(Boolean),
            ...(allServices || []).map(s => Number(s?.id)).filter(Boolean),
          ]);

          const deletePromises = (allProjectTask || []).map(async (task) => {
            const taskId = task?.ID || task?.id;
            if (!taskId) return;

            // Never delete manually added tasks
            if (Number(task?.is_manually_added) === 1) return;

            const isLinkedTask = (task?.product_id != null) || (task?.service_id != null);
            if (!isLinkedTask) return;

            // If task is not tied to a customer_order_product_id, skip deletion (safer than guessing).
            const copId = task?.customer_order_product_id != null ? Number(task.customer_order_product_id) : null;
            if (!copId) return;

            // Keep tasks that still exist in the current Sales Order payload.
            if (currentCopIds.has(copId)) return;

            // Otherwise, this linked product/service was removed → delete task + subtasks/members + attachments.
            await ProjectTask.deleteProjectSubTasksAndMembers(taskId);
            await ProjectTask.deleteProjectTaskAttachmentByTaskId(taskId);
            await ProjectTask.deleteProjectTask(taskId);
          });

          await Promise.all(deletePromises);

          // Old fallback version (backup):
          // - matched by service/product signature if `customer_order_product_id` was missing.
          // Keep this commented in case you ever find old rows without customer_order_product_id.
        } catch (error) {
          console.log("Task cleanup error (existing project):", error);
        }

        // NOTE (keep old code as backup):
        // Do NOT delete and recreate tasks/subtasks for existing project — it resets completion/status.
        // We only create tasks/subtasks when a NEW product/service is added.
        //
        // const filteredTasks = allProjectTask?.filter(item => item.product_id != null && item.service_id == null);
        // const deletedTasks = await ProjectTask.deleteProjectTaskByProjectId(checkProjectExist);
        // if(filteredTasks?.length > 0){
        //   for (const task of filteredTasks){
        //     const deletedSubTaskAndMembers = await ProjectTask.deleteProjectSubTasksAndMembers(task.ID);
        //     const deletedTaskAttachments= await ProjectTask.deleteProjectTaskAttachmentByTaskId(task.ID);
        //   }
        // }

        // console.log('allProductsallProductsallProducts', allProducts);
        emitProgress('creating_existing_tasks', `Creating existing job tasks...`, 63);
        for (const product of allProducts) {
          emitProgress('creating_existing_task', `Creating existing task: ${product.product_name}...`, 64);
          // If task already exists for this product line, do not recreate it (preserve progress/status/subtasks).
          const existingProductTaskId = await Request.checkExistingTask(product.product_name, checkProjectExist, product.product_index);
          if (existingProductTaskId) continue;

          const projectTask = {
            name: product.product_name,
            project_id: checkProjectExist,
            timestamp: moment().unix(),
            userid: projectData.added_by,
            start_date: projectData.start_date || null,
            due_date: projectData.installation_date ? projectData.installation_date : null,
            task_uid: taskUid,
            complete:reqresult[0]?.allInstallationCompleted ? 100 : 0,
            complete_sync: 1,
            status: reqresult[0]?.allInstallationCompleted ? 3 : 1,
            cover_image: product.product_design || projects.cover_image || null,
            product_id: product?.product_id ? parseInt(product?.product_id) : null,
            // cover_image: projects.cover_image || "null",
            priority: 1,
            reporter_id: projectData.added_by,
            description: "",
            project_task_uid: "",
            order_id: product.product_index,
            customer_order_product_id:product?.id,
            request_location_id:product?.location_id,
          };

          // let checkTaskExisting = await Request.checkExistingTask(product.product_name, checkProjectExist, product.product_index);

          // if (!checkTaskExisting) {

            const projectTaskId = await ProjectTask.addProjectTask(projectTask);
            let task_uid = await ProjectTask.getSubTaskUid(projectTaskId);
            if(product?.inhouse === 1 && product?.isTrafficCatalog !== 1){
              emitProgress('creating_existing_subtasks', `Creating existing subtasks for ${product.product_name}...`, 65);
              for (const workflow of product.workflow) {
                emitProgress('creating_existing_subtask', `Creating existing subtask: ${workflow.name}...`, 66);
                const subTask = {
                  name: workflow.name,
                  task_id: projectTaskId,
                  skill_ids: workflow.skill_ids,
                  timestamp: moment().unix(),
                  userid: projectData.added_by,
                  start_date: projectData.start_date || null,
                  due_date: projectData.installation_date ? projectData.installation_date : null,
                  task_uid: task_uid,
                  complete: reqresult[0]?.allInstallationCompleted ? 100 :0,
                  priority: 1,
                  status: reqresult[0]?.allInstallationCompleted ? 3 : 1,
                  reporter_id: projectData.added_by,
                  description: workflow.message,
                };
                // let checkSubTaskExisting=await Request.checkExistingSubTask(workflow.name,projectTaskId);
  
                // if(!checkSubTaskExisting){
                const projectSubTaskId = await ProjectTask.addProjectSubTask(subTask);
                let projectManagers=[]
                if(!empty(workflow.skill_ids)){
                 projectManagers = await Project.getTeamMembersBasedOnSkills(workflow.skill_ids, 14)
                }
                
                emitProgress('assigning_existing_subtask_members', `Assigning members to existing subtask: ${workflow.name}...`, 67);
                let notificationMsg = `New Job "${projectData.name}" has been added.`;
                let notificationUrl = "Jobs";
                let pm = {}
                for (const projectManager of projectManagers) {
  
                  pm = {
                    project_id: checkProjectExist,
                    subtask_id: projectSubTaskId,
                    staff_id: !empty(projectManager["id"]) ? projectManager["id"] : NULL,
                    partner_id: null,
                    contact_id: null,
                    lead_id: null,
  
                  };
  
                  let insertId = await ProjectTask.AddMembersToSubTask(pm);
                  emitProgress('sending_existing_subtask_notifications', `Sending notifications to existing subtask members...`, 68);
                  let notificationData_user = {
                    user_id: projectManager.user_id,
                    url: notificationUrl,
                    timestamp: currentDate,
                    message: "has assigned you to the job: " + projectData.name,
                    status: 1,
                    from_id: projectData.added_by,
                  };
  
                  const addNotificationResult_user = await Admin.addNotification(
                    notificationData_user
                  );
                }
              }
            }
        }

        if(allServices?.length > 0){
          emitProgress('processing_existing_services', `Processing existing services...`, 69);
          let skillSet = []
          for (const product of allServices) {
            if (product?.workflow[0]){
              if (product.workflow[0]?.workflow_id) {
                skillSet.push(await Product.getWorkflowById(product.workflow[0]?.workflow_id))
              }
            }
  
            const allSkillIds = skillSet
              .flatMap(workflow => workflow.map(stage => stage.skill_ids?.split(',')))
              .flat()
            let uniqueSkillIds = [...new Set(allSkillIds)];
            uniqueSkillIds = uniqueSkillIds?.filter(skillId => skillId !== "" && skillId !== undefined);
            let uniqueSkillIdsString = uniqueSkillIds?.join(',');
            // uniqueSkillIdsString = uniqueSkillIdsString?.slice(0, -1);
            uniqueSkillIdsString=uniqueSkillIdsString.replace(/,$/, '');
            let projectManagers=[]
            if(!empty(uniqueSkillIdsString)){
             projectManagers = await Project.getTeamMembersBasedOnSkills(uniqueSkillIdsString, 14)
            }
           
            // let deleted = await Project.deleteAllProjectMember(checkProjectExist);
            let pm = {}
            for (const projectManager of projectManagers) {
  
              pm = {
                project_id: checkProjectExist,
                staff_id: !empty(projectManager["id"]) ? projectManager["id"] : NULL,
                partner_id: null,
                contact_id: null,
                lead_id: null,
              };
              let alreadyExisting = await Project.projectMemberExist(pm);
  
              if (!alreadyExisting) {
                emitProgress('adding_existing_service_members', `Adding existing service members for ${product.service_name}...`, 70);
                let insertId = await Project.addProjectMember(pm);
                emitProgress('sending_existing_service_notifications', `Sending notifications to existing service members...`, 71);
                let notificationMsg = `New Job "${projectData.name}" has been added.`;
                let notificationUrl = "Jobs";
                const incrementResult_User = await Admin.incrementField(
                  projectManager.user_id,
                  "noti_count",
                  1
                );
                let notificationData_user = {
                  user_id: projectManager?.user_id || userId,
                  url: notificationUrl,
                  timestamp: currentDate,
                  message: "has assigned you to the job: " + projectData.name,
                  status: 1,
                  from_id: projectData.added_by,
                };
  
                const addNotificationResult_user = await Admin.addNotification(
                  notificationData_user
                );
              }
  
            }
  
          }
          let allProjectTask = await ProjectTask.getAllProjectTasks(checkProjectExist, userType = 1, userId = 1)
  
          // NOTE (keep old code as backup):
          // Do NOT inactivate or recreate service tasks on existing project update — preserve existing progress.
          //
          // const promises = allProjectTask.map(async task => {
          //   if (task.from_product == 1 && task.service_id != null) {
          //     const taskNameExists = allServices.some(product => ((product.service_name.includes(task.name)) && (product.product_index == task.order_id)));
          //     if (!taskNameExists) {
          //       await ProjectTask.updateProjectTask({ status: 0 }, task.ID);
          //       await ProjectTask.updateProjectSubTaskByTaskId({ status: 0 }, task.ID);
          //     }
          //   }
          // });
          // await Promise.all(promises);
  
          emitProgress('creating_existing_service_tasks', `Creating existing service tasks...`, 72);
          for (const product of allServices) {
            emitProgress('creating_existing_service_task', `Creating existing service task: ${product.service_name}...`, 73);
            const projectTask = {
              name: product.service_name,
              project_id: checkProjectExist,
              timestamp: moment().unix(),
              userid: projectData.added_by,
              start_date: projectData.start_date || null,
              due_date: projectData.installation_date ? projectData.installation_date : null,
              task_uid: taskUid,
              complete: reqresult[0]?.allInstallationCompleted ? 100 :0,
              complete_sync: 1,
              status: reqresult[0]?.allInstallationCompleted ? 3 : 1,
              cover_image: product.product_design || projects.cover_image || null,
              product_id: product?.product_id ? parseInt(product?.product_id) : null,
              service_id: product?.labour_cost_service_id ? parseInt(product?.labour_cost_service_id) : null,
              // cover_image: projects.cover_image || "null",
              priority: 1,
              reporter_id: projectData.added_by,
              description: "",
              project_task_uid: "",
              order_id: product.product_index,
              customer_order_product_id:product?.id

            };
  
  
            let checkTaskExisting = await Request.checkExistingTask(product.service_name, checkProjectExist, product.product_index);
  
            if (!checkTaskExisting) {
              emitProgress('creating_existing_service_subtasks', `Creating existing service subtasks for ${product.service_name}...`, 74);
              const projectTaskId = await ProjectTask.addProjectTask(projectTask);
              let task_uid = await ProjectTask.getSubTaskUid(projectTaskId);
              for (const workflow of product.workflow) {
                emitProgress('creating_existing_service_subtask', `Creating existing service subtask: ${workflow.name}...`, 75);
                const subTask = {
                  name: workflow.name,
                  task_id: projectTaskId,
                  skill_ids: workflow.skill_ids,
                  timestamp: moment().unix(),
                  userid: projectData.added_by,
                  start_date: projectData.start_date || null,
                  due_date: projectData.installation_date ? projectData.installation_date : null,
                  task_uid: task_uid,
                  complete: reqresult[0]?.allInstallationCompleted ? 100 :0,
                  priority: 1,
                  status: reqresult[0]?.allInstallationCompleted ? 3 : 1,
                  reporter_id: projectData.added_by,
                  description: workflow.message,
                };
                // let checkSubTaskExisting=await Request.checkExistingSubTask(workflow.name,projectTaskId);
  
                // if(!checkSubTaskExisting){
                const projectSubTaskId = await ProjectTask.addProjectSubTask(subTask);
                let projectManagers=[]
                if(!empty(workflow.skill_ids)){
                 projectManagers = await Project.getTeamMembersBasedOnSkills(workflow.skill_ids, 14)
                }
                
                emitProgress('assigning_existing_service_subtask_members', `Assigning members to existing service subtask: ${workflow.name}...`, 76);
                let notificationMsg = `New Job "${projectData.name}" has been added.`;
                let notificationUrl = "Jobs";
                let pm = {}
                for (const projectManager of projectManagers) {
  
                  pm = {
                    project_id: checkProjectExist,
                    subtask_id: projectSubTaskId,
                    staff_id: !empty(projectManager["id"]) ? projectManager["id"] : NULL,
                    partner_id: null,
                    contact_id: null,
                    lead_id: null,
  
                  };
  
                  let insertId = await ProjectTask.AddMembersToSubTask(pm);
                  emitProgress('sending_existing_service_subtask_notifications', `Sending notifications to existing service subtask members...`, 77);
                  let notificationData_user = {
                    user_id: projectManager?.user_id || userId,
                    url: notificationUrl,
                    timestamp: currentDate,
                    message: "has assigned you to the job: " + projectData.name,
                    status: 1,
                    from_id: projectData.added_by,
                  };
  
                  const addNotificationResult_user = await Admin.addNotification(
                    notificationData_user
                  );
  
                }
  
                // }
  
              }
            }
            else {
              emitProgress('updating_existing_service_subtasks', `Updating existing service subtasks for ${product.service_name}...`, 78);
              for (const workflow of product.workflow) {
                let task_uid = await ProjectTask.getSubTaskUid(checkTaskExisting);
  
                const subTask = {
                  name: workflow.name,
                  task_id: checkTaskExisting,
                  skill_ids: workflow.skill_ids,
                  timestamp: moment().unix(),
                  userid: projectData.added_by,
                  start_date: projectData.start_date || null,
                  due_date: projectData.installation_date ? projectData.installation_date : null,
                  task_uid: task_uid,
                  complete: 0,
                  priority: 1,
                  status: 1,
                  reporter_id: projectData.added_by,
                  description: workflow.message,
                };
                let checkSubTaskExisting = await Request.checkExistingSubTask(workflow.name, checkTaskExisting);
  
                if (!checkSubTaskExisting) {
                  emitProgress('creating_additional_service_subtask', `Creating additional service subtask: ${workflow.name}...`, 79);
                  const projectSubTaskId = await ProjectTask.addProjectSubTask(subTask);
                  let projectManagers=[]
                  if(!empty(workflow.skill_ids)){
                   projectManagers = await Project.getTeamMembersBasedOnSkills(workflow.skill_ids, 14)
                  }
                  
                  emitProgress('assigning_additional_service_subtask_members', `Assigning members to additional service subtask: ${workflow.name}...`, 80);
                  let notificationMsg = `New Job "${projectData.name}" has been added.`;
                  let notificationUrl = "Jobs";
                  let pm = {}
                  for (const projectManager of projectManagers) {
  
                    pm = {
                      project_id: checkProjectExist,
                      subtask_id: projectSubTaskId,
                      staff_id: !empty(projectManager["id"]) ? projectManager["id"] : NULL,
                      partner_id: null,
                      contact_id: null,
                      lead_id: null,
  
                    };
  
                    let insertId = await ProjectTask.AddMembersToSubTask(pm);
                    emitProgress('sending_additional_service_subtask_notifications', `Sending notifications to additional service subtask members...`, 81);
                    let notificationData_user = {
                      user_id: projectManager.user_id,
                      url: notificationUrl,
                      timestamp: currentDate,
                      message: "has assigned you to the job: " + projectData.name,
                      status: 1,
                      from_id: projectData.added_by,
                    };
  
                    const addNotificationResult_user = await Admin.addNotification(
                      notificationData_user
                    );
  
                  }
  
                  // }
  
                }
              }
            }
            //  pending subtasks update-----------------
          }
        }

        let allTasks = await ProjectTask.getAllProjectTasks(checkProjectExist, userType = 1, userId = 1);
        allCreatedTask = await ProjectTask.getAllProjectTasks(checkProjectExist, userType = 1, userId = 1)
        
        if(((allTasks?.length <= 0 && requestData[0]['noServiceNoTask'] == 1) || (allCreatedTask?.length <= 0)) && projectCategoryId !== 12){
          // if (movedToProduction) {
          //   const resultStatus = await Project.updateProject(checkProjectExist, { status: 1, completion: 100 });

          // } else {
          //   const resultStatus = await Project.updateProject(checkProjectExist, { status: 3, completion: 100 });

          // } // uncomment this if any issues occure when edit from installs 
          const resultStatus = await Project.updateProject(checkProjectExist, { status: 3, completion: 100 });
          directInstalls = true
        }
        if (allCreatedTask?.length <= 0 && projectCategoryId !== 12) {
          console.log(`⚠️ No installation created for project ${checkProjectExist}, but tasks exist.`);
          
          await Project.updateProject(checkProjectExist, { noServiceNoTask: 0 });
          await Request.updateRequestById(requestId, { noServiceNoTask: 0 });

          // notify
          let notificationMsg = `Sales Order "${projectData.name}" has been moved to 'Invoicing' because all tasks and installtions are off.`;
          let notificationUrl = "official-request";
          // Increment notification count
          const incrementResult_User = await Admin.incrementField(userId, "noti_count", 1);
          let notificationData_user = {
            user_id: userId,
            url: notificationUrl,
            timestamp: currentDate,
            message: notificationMsg,
            status: 1,
            from_id: userId,
          };
          const addNotificationResult_user = await Admin.addNotification(notificationData_user);

          // Alert to all admins of new added project

          let adminIds = await Admin.getAllAdminIds();
          let notificationData = {};
          for (const adminId of adminIds) {
            if (adminId != userId) {
              const incrementResult = await Admin.incrementField(adminId, "noti_count", 1);
              notificationData = {
                  user_id: adminId,
                  url: notificationUrl,
                  timestamp: currentDate,
                  message: notificationMsg,
                  status: 1,
                  from_id: userId,
              };
              const addNotificationResult = await Admin.addNotification(notificationData);
            }
          }
        }

        emitProgress('processing_existing_job_cost', `Processing existing job cost calculations...`, 80);
        
        /************  Jobcost ******************** */
        let flag = 0
        let recipe = 1
        let allMaterials = await Product.getAllMaterials(true)
        let allProductsData = []
        console.log('requestData[0]?.project_category',requestData[0]?.project_category)
        if (requestData[0]?.project_category === 15) {
          allProductsData = await Product.getAllLocations(requestId)
        }else{
          allProductsData = await Product.getNonStockProducts(requestId)
        }
        console.log('allProductsData--------1',allProductsData)
        let allRecipies = []
        if(allProductsData) { //} && requestData[0]?.job_category_name != 'Time & Material'){
          allRecipies = await Recipe.getAllReceipesByProjectId(allProductsData,requestId);
        }
        let selectedMaterials = await JobCost.fetchAllJobCostByProjectId(checkProjectExist, userId = 1, recipe);
        let reduceStockResult, deleted;
        if (selectedMaterials.length > 0) {
          for (const element of selectedMaterials) {

            const material = await Product.getMaterialById(element.product_id)

            if (!empty(material)) {
              //const matchingValue = newMaterialObjects?.find(val => val?.id === material[0]?.id);
              // let quantity = Math.max(element?.quantity || 0, element?.actual_quantity || 0);
              // let newQty = material[0]?.qty + quantity
              let newQty = material[0]?.qty + (element.quantity || 0)
              let totalCost = material[0]?.unit_cost * newQty
              // console.log('newQtynewQty---1',newQty)
              // if (matchingValue ? newQty >= matchingValue.quantity : true) {
              if (newQty >= parseInt(material[0].qty)) {
                const data = {
                  qty: newQty,
                  total_cost: totalCost
                };
                const updated = await Product.updateMaterialStock(element.product_id, data)
                if (updated) {
                  deleted = await JobCost.deleteJobCost(element.id, recipe);
                } else {
                  console.log('Failed to update stock');
                  reply.send({ status: 0, message: 'Failed to update stock' });
                }
              } else {
                console.log(`Stock is lesser than entered qty 5`);
                // reply.send({ status: 0, message: `Stock is lesser than entered qty for ${material[0]?.name} (${material[0].barcode})`, resultId: material[0].id });
                // return;
              }
              // } else {
                // console.log(`Stock is lesser than entered qty`);
              //   reply.send({ status: 3, message: `Stock is lesser than entered qty` });
              // }

            }
          }
          flag = 1
        }
        
        let results = await Product.getFinishedProducts(0,true);
        // console.log('results111122333', results)
        let newMaterialObjects = [];
       
        let jobCost=0;
        let notifyJobcost=""
       
        for (const recipeItem of allRecipies) {
          // let results = await Product.getFinishedProducts();
          for (const ingredient of recipeItem.ingredients) {
            let materialItems = results?.filter((mat) => mat.material_id === ingredient.product_id);
            let filteredMaterialItems = materialItems?.filter(mat => 
              (ingredient.stockType === 2 ? mat.partial === 1 : mat.partial === 0)
            );
            let quantityInInitialUnit = 0;
            quantityInInitialUnit = ingredient?.quantity;
            // if(ingredient.stockType === 1 || materialItems[0]?.unit == 4){
            //   quantityInInitialUnit = ingredient?.quantity;
            // }else{
            //   let conversionFactorToInitial = getConversionFactor(ingredient?.convertionUnit, materialItems[0]?.unit, ingredient?.width || 1);
            //   quantityInInitialUnit = ingredient?.convertionQty * conversionFactorToInitial;
            // }
            let quantityNeeded = quantityInInitialUnit * recipeItem?.quantity * recipeItem?.location_quantity;
            // let quantityNeeded = ingredient?.quantity*recipeItem?.quantity*recipeItem?.location_quantity;
            // let totalAvailableQuantity = filteredMaterialItems?.reduce((total, item) => total + item.qty, 0);
            let totalAvailableQuantity = 0;
            if(ingredient.stockType === 1){
              totalAvailableQuantity = filteredMaterialItems?.reduce((total, item) => total + item?.qty, 0);
            }else{
              totalAvailableQuantity = filteredMaterialItems?.filter((item)=>item.partial_unit==ingredient?.convertionUnit)?.reduce((total, item) => total + item?.qty, 0);
            }
            // console.log(`Converted Ingredient Quantity to Initial Unit (${ingredient.convertionUnit} → ${materialItems[0]?.unit}), width : ${ingredient?.width}:`, quantityInInitialUnit);
            // console.log('filteredMaterialItems===', filteredMaterialItems);
            // console.log('ingredient?.quantity===', ingredient?.quantity);
            // console.log('recipeItem?.quantity===', recipeItem?.quantity);
            // console.log('recipeItem?.location_quantity===', recipeItem?.location_quantity);
            // console.log('Total Available Quantity (Before Conversion) ===', totalAvailableQuantity);
            // console.log('Quantity Needed (Before Conversion) ===', quantityNeeded);
            
            // Get actual width and quantity from material stock
            // let width = ingredient?.width || 1;

            // // Convert quantityNeeded dynamically using width
            // let conversionFactor = getConversionFactor(ingredient?.convertionUnit, materialItems[0]?.unit, width);
            // let convertedQuantityNeeded = quantityNeeded * conversionFactor;

            if (totalAvailableQuantity >= quantityNeeded) {
            // if (totalAvailableQuantity >= convertedQuantityNeeded) {
              // ✅ Sufficient stock available
              // let remainingQuantity = convertedQuantityNeeded;
              let remainingQuantity = quantityNeeded;
              filteredMaterialItems?.forEach((materialItem) => {
                // Convert unit cost based on unit type
                let originalUnitCost = Number(materialItem?.unit_cost) || 0;
                // let convertedUnitCost = getConvertedUnitCost(originalUnitCost, materialItem?.unit, ingredient?.convertionUnit, width);

                // console.log(`Unit Cost (Before Conversion):`, originalUnitCost);
                // console.log(`Converted Unit Cost (${materialItems[0]?.unit} → ${ingredient.convertionUnit} at width ${width}):`, convertedUnitCost);
                // console.log(`totalAvailableQuantity :`, totalAvailableQuantity);
                // console.log(`convertedQuantityNeeded :`, convertedQuantityNeeded);
                if (remainingQuantity > 0) {
                  let quantityToPull = Math.min(remainingQuantity, materialItem.qty);
                  newMaterialObjects.push({
                    id: materialItem.id,
                    productId: materialItem.material_id,
                    quantity: quantityToPull,
                    unit_cost: materialItem?.inhouse == 0 ? 0 : originalUnitCost,
                    total_cost: materialItem?.inhouse == 0 ? 0 : parseFloat(quantityToPull) * parseFloat(originalUnitCost),
                    recipe: recipeItem.id,
                    recipeName: recipeItem.name,
                    customer_order_product_id: recipeItem?.customer_order_product_id,
                  });
                  results = results.map(item => item.id === materialItem.id ? { ...item, qty: item.qty - quantityToPull } : item);
                  remainingQuantity -= quantityToPull;
                }
              });
              console.log(`Added ${quantityNeeded} units of ${ingredient.name}`);
            } else {
              // Insufficient quantity available in existing packages, pull from additional packages
              console.log(`Insufficient quantity of ${ingredient.name} available in existing packages.3`);
              console.log(`Pulling additional ${quantityNeeded - totalAvailableQuantity} units from packages...3`);
              // Your logic to pull additional quantities from packages goes here
              // notifyJobcost+=` Insufficient quantity of ${ingredient.name} available in existing packages.`
              if(empty(notifyJobcost)){
                  notifyJobcost+=`There is insuffient quantity on materials: ${ingredient.name} (${quantityNeeded}) (${ingredient.stockType === 1 ? 'Finished' : 'Partial'})`
              }
              else{
                 notifyJobcost+=`, ${ingredient.name} (${quantityNeeded}) (${ingredient.stockType === 1 ? 'Finished' : 'Partial'})`
              }
              jobCost=1
            }
          };
        };
        
        if ((flag === 1 && deleted) || (flag === 0)) {
          for (let i = 0; i < newMaterialObjects?.length; i++) {
            const materialData = await Product.getMaterialById(newMaterialObjects[i].id)
            if (materialData) {
         
              let newQty = materialData[0].qty - newMaterialObjects[i].quantity
              // console.log('newQtynewQty---2',newQty)
              if (newQty >= 0) {
                let totalCost = newMaterialObjects[i].unit_cost * newQty
                let data = {};
                data['qty'] = newQty
                data['total_cost'] = parseInt(totalCost)
                let jobCostData = {
                  product_id: newMaterialObjects[i].id,
                  project_id: checkProjectExist,
                  quantity: reqresult[0]?.allInstallationCompleted ? newMaterialObjects[i].quantity : 0,
                  unit_price: newMaterialObjects[i].unit_cost,
                  total: reqresult[0]?.allInstallationCompleted ? newMaterialObjects[i].total_cost : 0,
                  // quantity: newMaterialObjects[i].quantity,
                  // unit_price: newMaterialObjects[i].unit_cost,
                  // total: newMaterialObjects[i].total_cost,
                  actual_quantity: newMaterialObjects[i].quantity,
                  actual_unit_price: newMaterialObjects[i].unit_cost,
                  actual_total: newMaterialObjects[i].total_cost,
                  recipe: 1,
                  recipe_name: newMaterialObjects[i].recipeName,
                  timestamp: moment().unix(),
                  added_by: userId,
                  customer_order_product_id: newMaterialObjects[i]?.customer_order_product_id,
                };
                if (jobCostData?.actual_quantity > 0) {
                  insertedstatus = await JobCost.addJobCost(jobCostData);
                  console.log("#####insertedStatus 5", jobCostData);

                  reduceStockResult = await Product.updateMaterialStock(newMaterialObjects[i].id, data);
                  // Autofill stock if stock is 0
                  // if(newQty <= 0){
                  //   if(materialData[0]?.autofill == 1){
                  //     const parentFinishedMaterial = await Product.getMaterialById(materialData[0]?.parent_finished_product_id)
                  //     console.log('parentFinishedMaterial----',parentFinishedMaterial)
                  //     if (parentFinishedMaterial.length > 0 && parentFinishedMaterial[0]?.qty > 0) {
                  //       let reducedQty = parentFinishedMaterial[0]?.qty - 1
                  //       let parentTotalCost = parentFinishedMaterial[0]?.unit_cost * reducedQty
                  //       let parentData = {
                  //         qty: reducedQty,
                  //         total_cost: parseInt(parentTotalCost)
                  //       };
                  //       const reduceFinishedProductStock = await Product.updateMaterialStock(materialData[0]?.parent_finished_product_id, parentData)
        
                  //       let addingQty = materialData[0]?.actual_qty
                  //       let totalCost = materialData[0]?.actual_total_cost
                  //       let autoFilldata = {
                  //         qty: addingQty,
                  //         total_cost: totalCost
                  //       };
                  //       console.log('autofillPartialStock--',autoFilldata);
                  //       const autofillPartialStock = await Product.updateMaterialStock(materialData[0]?.id, autoFilldata);
                  //     }
                  //   }
                  // }

                }
              } else {
                console.log(`Stock is lesser than entered qty 6`);
                // Autofill stock if stock is 0
                // if(newQty <= 0){
                //   if(materialData[0]?.autofill == 1){
                //     const parentFinishedMaterial = await Product.getMaterialById(materialData[0]?.parent_finished_product_id)
                //     console.log('parentFinishedMaterial----',parentFinishedMaterial)
                //     if (parentFinishedMaterial.length > 0 && parentFinishedMaterial[0]?.qty > 0) {
                //       let reducedQty = parentFinishedMaterial[0]?.qty - 1
                //       let parentTotalCost = parentFinishedMaterial[0]?.unit_cost * reducedQty
                //       let parentData = {
                //         qty: reducedQty,
                //         total_cost: parseInt(parentTotalCost)
                //       };
                //       const reduceFinishedProductStock = await Product.updateMaterialStock(materialData[0]?.parent_finished_product_id, parentData)
      
                //       let addingQty = materialData[0]?.actual_qty
                //       let totalCost = materialData[0]?.actual_total_cost
                //       let autoFilldata = {
                //         qty: addingQty,
                //         total_cost: totalCost
                //       };
                //       console.log('autofillPartialStock--',autoFilldata);
                //       const autofillPartialStock = await Product.updateMaterialStock(materialData[0]?.id, autoFilldata);
                //     }
                //   }
                // }
              }
            }
          }
        }
        let deleteRfpJobcost=await JobCost.deleteRfpJobcost(checkProjectExist)
        let rfpProduct=await CustomerOrderProduct.getRFPProductByRequest(requestId)
        

        for(let i=0;i<rfpProduct?.length;i++){
          let jobCostData = {
            product_id: rfpProduct[i].product_id,
            project_id: checkProjectExist,
            // quantity: 0,
            // unit_price: rfpProduct[i]?.material == 0 ? rfpProduct[i]?.vendor_price : rfpProduct[i].product_price,
            // total: 0,
            quantity: rfpProduct[i].product_qty*rfpProduct[i].location_quantity,
            unit_price:  rfpProduct[i]?.material == 0 ? rfpProduct[i]?.vendor_price : rfpProduct[i].product_price,
            total:  rfpProduct[i]?.material == 0 ? rfpProduct[i]?.vendor_price : rfpProduct[i].product_price*rfpProduct[i].product_qty*rfpProduct[i].location_quantity,
            actual_quantity: rfpProduct[i].product_qty*rfpProduct[i].location_quantity,
            actual_unit_price:  rfpProduct[i]?.material == 0 ? rfpProduct[i]?.vendor_price : rfpProduct[i].product_price,
            actual_total:  rfpProduct[i]?.material == 0 ? rfpProduct[i]?.vendor_price : rfpProduct[i].product_price*rfpProduct[i].product_qty*rfpProduct[i].location_quantity,
            recipe: 1,
            recipe_name: null,
            timestamp: moment().unix(),
            added_by: userId,
            rfp:1,
            customer_order_product_id: rfpProduct[i].id,
          };
          insertedstatus = await JobCost.addJobCost(jobCostData);
          console.log("#####insertedStatus 6", jobCostData);
        }

        let newMaterialRfqObjects = [];
        let bringStockCatalog = requestData?.[0]?.project_category == 15 ? false: true
          let materialRfq=await CustomerOrderProduct.getMaterialProductByRequest(requestId,bringStockCatalog)
          console.log("#################newMaterialObjects2222222222212", materialRfq);
          if(requestData[0]['job_category_name'] == 'Time & Material'){
            const deleted = await JobCost.deleteMaterialJobcost(checkProjectExist);
            for(let i=0; i<materialRfq.length; i++){
              let results = await Product.getFinishedProducts(materialRfq[i].product_id,true);
                // console.log('#################newMaterialObjects22222222222121', results)
                let notPartial=materialRfq[i].stock_type===1 ? true : false
                let filteredResult=[]
              if(notPartial){
                filteredResult = results?.filter(item=>item.partial == 0 )
              }else{
                filteredResult = results?.filter(item=>((item.partial == 1) && (item.partial_unit == materialRfq[i].convertion_unit)) )
              }
              if(filteredResult?.length > 0){
                console.log('results=====',results[0])
                let jobCostData = {
                  product_id: filteredResult[0].id,
                  project_id: checkProjectExist,
                  // quantity: reqresult[0]?.allInstallationCompleted ? materialRfq[i].product_qty : 0,
                  quantity: reqresult[0]?.project_category == 12 ? materialRfq[i].product_qty : 0,
                  unit_price: materialRfq[i].product_price,
                  // total: reqresult[0]?.allInstallationCompleted ? ((materialRfq[i].product_price || 0) * (materialRfq[i].product_qty || 0)) : 0,
                  total: reqresult[0]?.project_category == 12? ((materialRfq[i].product_price || 0) * (materialRfq[i].product_qty || 0)) : 0,
                  // quantity: materialRfq[i].quantity,
                  // unit_price: materialRfq[i].unit_cost,
                  // total: materialRfq[i].total_cost,
                  actual_quantity: materialRfq[i].product_qty,
                  actual_unit_price: materialRfq[i].product_price,
                  actual_total: materialRfq[i].product_total,
                  recipe: 0,
                  recipe_name: '',
                  timestamp: moment().unix(),
                  added_by: userId,
                  customer_order_product_id: materialRfq[i].id,
                };
                insertedstatus = await JobCost.addJobCost(jobCostData);
                console.log("#####insertedStatus 7", jobCostData);
              } else {
                console.log(`Insufficient quantity of ${materialRfq[i].product_name} available in existing packages.23`);
                if(empty(notifyJobcost)){
                  notifyJobcost+=`There is insuffient quantity on materials: ${materialRfq[i].product_name} (Finished)`
                }
                else{
                  notifyJobcost+=` ,${materialRfq[i].product_name} (Finished)`
                }
                jobCostFlag=1
              }
            }
          }else{
            for(let i=0;i<materialRfq.length; i++){
              // let results = await Product.getFinishedProducts();
               let notPartial=materialRfq[i].stock_type===1 ? true : false
              //  console.log('materialItem22222222222s', notPartial)
              //  console.log('materialItem22222222222s', results)
              if(notPartial){
                filteredResult = results?.filter(item=>item.partial == 0 )
              }else{
                filteredResult = results?.filter(item=>((item.partial == 1) && (item.partial_unit == materialRfq[i].convertion_unit)) )
              }
              // console.log('results===== 12',results)
              let quantityNeeded = materialRfq[i].product_qty;
              let materialItems = filteredResult.filter((mat) => mat.material_id === materialRfq[i].product_id);
              // console.log('materialItem22222222222s', materialItems)
              // console.log('materialItem22222222222s', quantityNeeded)
              let totalAvailableQuantity = materialItems.reduce((total, item) => total + item.qty, 0);

              if (totalAvailableQuantity >= quantityNeeded) {
                // Sufficient quantity available in existing packages
                let remainingQuantity = quantityNeeded;
                // console.log('materialRfq---',materialRfq)
                // console.log('materialItems---',materialItems)
                materialItems.forEach((materialItem) => {
                  if (remainingQuantity > 0) {
                    let quantityToPull = Math.min(remainingQuantity, materialItem.qty);
                    newMaterialRfqObjects.push({
                      id: materialItem.id,
                      productId: materialItem.material_id,
                      quantity: quantityToPull,
                      unit_cost: materialItem?.inhouse == 0 ? 0 : materialItem.unit_cost,
                      total_cost: materialItem?.inhouse == 0 ? 0 : parseFloat(quantityToPull) * parseFloat(materialItem.unit_cost),
                      recipe:0,
                      recipeName: null,
                    });
                    filteredResult = filteredResult.map(item => item.id === materialItem.id ? { ...item, qty: item.qty - quantityToPull } : item);
                    remainingQuantity -= quantityToPull;
                  }
                });
              
              } else {
                // Insufficient quantity available in existing packages, pull from additional packages
                console.log(`Insufficient quantity of ${materialRfq[i].product_name} available in existing packages.4`);
                console.log(`Pulling additional ${quantityNeeded - totalAvailableQuantity} units from packages...4`);
                // Your logic to pull additional quantities from packages goes here
              
              
                if(empty(notifyJobcost)){
                    notifyJobcost+=`There is insuffient quantity on materials: ${materialRfq[i].product_name} (${quantityNeeded}) (Finished)`
                }
                else{
                  notifyJobcost+=` ,${materialRfq[i].product_name} (${quantityNeeded}) (Finished)`
                }
                
              
                jobCost=1
                jobCostFlag=1
              }
            }
            for (let i = 0; i < newMaterialRfqObjects.length; i++) {
              const materialData = await Product.getMaterialById(newMaterialRfqObjects[i].id)
              if (!empty(materialData)) {
                let newQty = materialData[0].qty - newMaterialRfqObjects[i].quantity
                // console.log('newQtynewQty---3',newQty)
                if (newQty >= 0) {
                  let totalCost = newMaterialRfqObjects[i].unit_cost * newQty
                  let data = {};
                  data['qty'] = newQty
                  data['total_cost'] = parseInt(totalCost)
                  let jobCostData = {
                    product_id: newMaterialRfqObjects[i].id,
                    project_id: checkProjectExist,
                    quantity: reqresult[0]?.allInstallationCompleted ? newMaterialRfqObjects[i].quantity : 0,
                    unit_price: newMaterialRfqObjects[i].unit_cost,
                    total: reqresult[0]?.allInstallationCompleted ? newMaterialRfqObjects[i].total_cost : 0,
                    // quantity: newMaterialRfqObjects[i].quantity,
                    // unit_price: newMaterialRfqObjects[i].unit_cost,
                    // total: newMaterialRfqObjects[i].total_cost,
                    actual_quantity: newMaterialRfqObjects[i].quantity,
                    actual_unit_price: newMaterialRfqObjects[i].unit_cost,
                    actual_total: newMaterialRfqObjects[i].total_cost,
                    recipe: 0,
                    recipe_name: newMaterialRfqObjects[i].recipeName,
                    timestamp: moment().unix(),
                    added_by: userId,
                    // customer_order_product_id: newMaterialRfqObjects[i]?.customer_order_product_id,
                  };
                  if (jobCostData?.actual_quantity > 0) {
                    insertedstatus = await JobCost.addJobCost(jobCostData);
                    console.log("#####insertedStatus 8", jobCostData);


                    reduceStockResult = await Product.updateMaterialStock(newMaterialRfqObjects[i].id, data)

                  }
                } else {
                  console.log(`Stock is lesser than entered qty 7`);
                  
                  // reply.send({ status: 0, message: `Stock is lesser than entered qty for ${materialData[0].name} (${materialData[0].barcode})`, resultId: materialData[0].id });
                }
              }
            }
          }

          if(requestData[0]['job_category_name'] != 'Time & Material'){
            if ((flag === 1 && deleted) || (flag === 0)) {
              for (let i = 0; i < newMaterialObjects?.length; i++) {
                const materialData = await Product.getMaterialById(newMaterialObjects[i].id)
                if (materialData) {
                  let newQty = materialData[0].qty + newMaterialObjects[i].quantity
                  // console.log('newQtynewQty---4',newQty)
                  if (newQty >= 0) {
                    let totalCost = newMaterialObjects[i].unit_cost * newQty
                    let data = {};
                    data['qty'] = newQty
                    data['total_cost'] = parseInt(totalCost)
                    const reduceStockResultNew = await Product.updateMaterialStock(newMaterialObjects[i].id, data);
                  } else {
                    console.log(`Stock is lesser than entered qty 8`);
                  }
                }
              }
            }
            for (let i = 0; i < newMaterialRfqObjects.length; i++) {
              const materialData = await Product.getMaterialById(newMaterialRfqObjects[i].id)
              if (!empty(materialData)) {
                let newQty = materialData[0].qty + newMaterialRfqObjects[i].quantity
                console.log('newQtynewQty---5',newQty)
                if (newQty >= 0) {
                  let totalCost = newMaterialRfqObjects[i].unit_cost * newQty
                  let data = {};
                  data['qty'] = newQty
                  data['total_cost'] = parseInt(totalCost)
                  reduceStockResult = await Product.updateMaterialStock(newMaterialRfqObjects[i].id, data)
                } else {
                  console.log(`Stock is lesser than entered qty 9`);
                }
              }
            }
          }

        let notificationMsg = ` Job "${projectData.name}" has been updated.`;
        let notificationUrl = "Jobs";
        const incrementResult_User = await Admin.incrementField(
          projectData.added_by,
          "noti_count",
          1
        );
        let notificationData_user = {
          user_id: projectData.added_by,
          url: notificationUrl,
          timestamp: currentDate,
          message: notificationMsg,
          status: 1,
          from_id: projectData.added_by,
        };
        const addNotificationResult_user = await Admin.addNotification(
          notificationData_user
        );

        // Alert to all admins of new added project
        let adminIds = await Admin.getAllAdminIds();
        let notificationData = {};
        for (const adminId of adminIds) {
          if (adminId != projectData.added_by) {
            const incrementResult = await Admin.incrementField(
              adminId,
              "noti_count",
              1
            );

            notificationData = {
              user_id: adminId,
              url: notificationUrl,
              timestamp: currentDate,
              message: notificationMsg,
              status: 1,
              from_id: projectData.added_by,
            };

            const addNotificationResult = await Admin.addNotification(
              notificationData
            );

          }
        }
        emitProgress('finalizing_existing_project', `Finalizing existing job updates...`, 82);
        
        let allTask = await ProjectTask.getAllProjectTasks(checkProjectExist, userType = 1)
        let firstTaskStatus = allTask[0]?.status

        const sumCompletionTask = allTask?.reduce((acc, task) => acc + task?.complete, 0);
        const averageCompletionTask = sumCompletionTask / allTask?.length;
        const tasksWithStatusCompletedTasks = allTask?.every(task => task.status === firstTaskStatus);
        let projectTaskData = {};
        projectTaskData.completion = averageCompletionTask
        if(installationStatus == 2){
          projectTaskData.status = 3
        }else if (tasksWithStatusCompletedTasks) {
          // projectTaskData.status = firstTaskStatus
          projectTaskData.status = 1
        }
        else {
          projectTaskData.status = 1
          // projectTaskData.status = 2
        }
        if(jobCost===1){
         
          projectTaskData.jobcost=1
          projectTaskData.notifyJobcost=notifyJobcost
          
        }
        else{
          projectTaskData.jobcost=0
          projectTaskData.notifyJobcost=notifyJobcost
        }
        if(reqresult[0]?.allInstallationCompleted){
          projectTaskData.status = 3
        }
        let resultStatus = await Project.updateProject(checkProjectExist, projectTaskData);
        console.log('resultStatus', resultStatus)
        console.log('projectTaskData 1', projectTaskData)
        
        emitProgress('project_update_complete', `Job updated successfully!`, 90);
      }
    }
    else {
      emitProgress('processing_rejection', `Processing sales order rejection...`, 50);
      
      let statusUpdate = await Request.updateRequets(requestId, { status: 6, reason ,timestamp : currentTime });
      
      emitProgress('rejection_complete', `Sales order rejected successfully`, 90);
    }
    // ✅ OPTIMIZATION: Prepare response data early
    let responseData = {
      status: acceptStatus == 1 ? 1 : 2,
      message: acceptStatus == 1 ? "You have accepted the request" : "You have  rejected the request",
      allCreatedTask,
      directInstalls
    };
    
    // Determine response case
    if (acceptQuery) {
      responseData.case = 1;
    } else {
      responseData.case = 2;
    }

    // ✅ OPTIMIZATION: Handle QuickBooks async (non-blocking)
    const handleQuickBooksAsync = async () => {
      if (acceptStatus == 1) {
        try {
          // Fetch current project data to ensure we have the latest
          const currentProjectId = await Project.projectIdFromRequestId(requestId);
          if (currentProjectId) {
            const currentProjectData = await Project.getProjectById(currentProjectId);
            if (currentProjectData && currentProjectData.project_category !== 12) {
              await createJornalEntryForJobCost(requestId, previosJobCostTotal, previosProductTotal, previosServiceTotal);
            }
          }
        } catch (error) {
          console.log("error in quickbook", error);
        }
      }
    };

    // ✅ OPTIMIZATION: Handle all notifications/emails/SMS async (non-blocking)
    const handleNotificationsAsync = async () => {
      if (!acceptQuery) return;
      
      try {
      emitProgress('processing_activity_logs', `Processing activity logs and notifications...`, 94);
      
      // let request = await Request.getRequestById(requestId);
      const request = salesOrderData
      let partner = await RequestPartner.getRequestPartnerById(requestPartnerId);

        if (acceptStatus == 1) {
          let reqActivityMsg = `🔄 Sales Order "${request[0].name}" has been converted to Job.`;
        let notificationMsg = `The Sales Order '${request[0].name}' has been converted to Job.`;
        let notificationUrl = `request/view/${requestId}`;

        emitProgress('sending_partner_notifications', `Sending notifications to partner...`, 95);
        
          // Partner notification
        if (!empty(partner['user_id'])) {
          const incrementResult_User = await Admin.incrementField(partner.name, "noti_count", 1);
          let notificationData_user = {
            user_id: partner.user_id,
            url: notificationUrl,
            timestamp: currentDate,
            message: notificationMsg,
            status: 1,
            from_id: userId,
          };
            await Admin.addNotification(notificationData_user);
        }

        emitProgress('updating_tab_counts', `Updating user tab counts...`, 96);
        
        // let projectId = await Project.getProjectByRequestId(requestId);
          // let requestData = await Request.getRequestById(requestId);
        // const allUserIds = await Admin.getAllUserIds();
          let PartnerUserIds = await RequestPartner.fetchPartnerUserIds(requestId);
          // const usersToUpdate = [...new Set([userId, ...PartnerUserIds, ...allUserIds].filter(Boolean))];
          
        // const updateEditCounts = async (userId) => {
        //   const jobsCount = await Project.getJobsTabByUserId(userId);
        //   if (!jobsCount || jobsCount === "No record found") {
        //     const jobTabData = {
        //       user_id: userId,
        //       hold_count: 0,
        //       pre_production_count: 0,
        //       production_count: 0,
        //       installs_count: 0,
        //       pickup_count: 0,
        //       shipping_count: 0,
        //       completed_count: 0,
        //       timestamp: moment().unix()
        //     };
        //     await Project.addJobsTabCount(jobTabData);
        //   }

        //     if (allCreatedTask?.length <= 0) {
        //     await Request.salesOrderIncrementField(userId, 'approved_count', -1, 'approved_count_ids', requestId, false);
        //     await Request.salesOrderIncrementField(userId, 'installs_count', 1, 'installs_count_ids', requestId, true);
        //     await Project.jobTabIncrementField(userId, 'installs_count', 1, 'installs_count_ids', projectId, true);
        //     } else {
        //     await Request.salesOrderIncrementField(userId, 'approved_count', -1, 'approved_count_ids', requestId, false);
        //     await Request.salesOrderIncrementField(userId, 'in_production_count', 1, 'in_production_count_ids', requestId, true);
        //     await Project.jobTabIncrementField(userId, 'pre_production_count', 1, 'pre_production_count_ids', projectId, true);
        //   }
        //   };
  
          // Parallelize tab count updates
        // await Promise.all(usersToUpdate.map(userId => updateEditCounts(userId)));

        emitProgress('sending_admin_notifications', `Sending notifications to administrators...`, 97);

        let adminIds = await Admin.getAllAdminIds();
          const adminNotificationPromises = adminIds
            .filter(adminId => adminId != userId)
            .map(async (adminId) => {
              await Admin.incrementField(adminId, "noti_count", 1);
              await Admin.addNotification({
              user_id: adminId,
              url: notificationUrl,
              timestamp: currentDate,
              message: notificationMsg,
              status: 1,
              from_id: userId,
              });
            });

          await Promise.all(adminNotificationPromises);

        emitProgress('adding_activity_log', `Adding activity log entry...`, 98);
          await RequestActivity.addRequestUpdate({
            request_id: requestId,
            activity: reqActivityMsg,
            added_by: addedByUserId || userId,
            timestamp: currentDate
          });
        
        emitProgress('sending_email_sms_notifications', `Sending email and SMS notifications...`, 99);
        
        let adminMangerIds = await Admin.getAllAdminAndManagerUserIds();
          const usersToSendMessage = [...new Set([userId, ...PartnerUserIds, ...adminMangerIds].filter(Boolean))];

          // Parallelize email/SMS sending
          const emailSmsPromises = usersToSendMessage.map(async (userIdToCheck) => {
            try {
            const creator = await Admin.getUserNotificationAndPermissionSettings(userIdToCheck);
              if (!creator) return;

              const emailPromise = (async () => {
                if (creator.emailEnabled === 1 && creator.email && creator?.pcb_new_jobs === 1 && creator?.pcb_new_jobs_email === 1) {
            const welcome = `Hello ${creator.name},`;
            const subject = checkProjectExist ? 
                    `🛠️  Job Updated (Converted from Sales Order): ${request[0].name}` :
            `🛠️ New Job Created (Converted from Sales Order): ${request[0].name}`;
            const email_content = checkProjectExist ?
                    `A job has been updated by converting a sales order. Please check the Jobs section in your software.` :
                    `A new job has been created by converting a sales order. Please check the Jobs section in your software.`;

            let message = newJobCreatedEmailTemplate({
              user: creator,
              requestName: request[0].name,
              projectType: request[0].project_type,
              total: request[0].total || 0,
              baseUrl: BASE_URL,
              type: 'job',
              convertion: true,
            });

            message = message.replace('[emailContent]', email_content);
            message = message.replace('[CONTENT]', welcome);
            message = message.replace('[LOGIN_URL]', `${BASE_URL}`);

            const mailOptions = {
              to: creator.email,
              from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
              subject,
              text: subject,
              html: message,
            };

              await transporter.sendMail(mailOptions);
              console.log(`✅ Job email sent to: ${creator.email}`);
                }
              })();

              const smsPromise = (async () => {
                if (creator?.smsEnabled === 1 && creator?.phone && creator?.pcb_new_jobs === 1 && creator?.pcb_new_jobs_sms === 1) {
            const smsContent =
                    (checkProjectExist ? `🛠️ Job Updated (Converted from Sales Order) !\n` : `🛠️ New Job Created (Converted from Sales Order) !\n`) +
              `📄 Name: "${request[0].name}"\n` +
              `💰 Total: ₹${request[0].total || 0}\n` +
              `🔗 View: ${BASE_URL}\n` +
              `— SignWise`;
  
            const recipientPhone = creator.phone || TWILIO_PHONE;
            const Phone_country_code = creator.phone_country_code || '';
  
                  await sendMessageToPhoneNumber(recipientPhone, smsContent, Phone_country_code);
              console.log(`✅ SMS sent to ${creator.name || 'User'} (${recipientPhone}) for Job "${request[0].name}"`);
                }
              })();

              await Promise.all([emailPromise, smsPromise]);
            } catch (err) {
              console.error(`❌ Error sending notifications to user ${userIdToCheck}:`, err);
            }
          });

          await Promise.all(emailSmsPromises);
        } else {
        emitProgress('processing_rejection_notifications', `Processing rejection notifications...`, 95);

          let reqActivityMsg = `The member ${partner.name} rejected the request ${request[0].name}.`;
        let notificationMsg = `The member ${partner.name} rejected the request ${request[0].name}.`;
        let notificationUrl = `request/view/${requestId}`;

        emitProgress('sending_rejection_notifications', `Sending rejection notifications to partner...`, 97);
        
        if (!empty(partner['user_id'])) {
            await Admin.incrementField(partner.name, "noti_count", 1);
            await Admin.addNotification({
            user_id: partner.user_id,
            url: notificationUrl,
            timestamp: currentDate,
            message: notificationMsg,
            status: 1,
            from_id: userId,
            });
        }

        emitProgress('sending_rejection_admin_notifications', `Sending rejection notifications to administrators...`, 98);

        let adminIds = await Admin.getAllAdminIds();
          const adminNotificationPromises = adminIds
            .filter(adminId => adminId != userId)
            .map(async (adminId) => {
              await Admin.incrementField(adminId, "noti_count", 1);
              await Admin.addNotification({
              user_id: adminId,
              url: notificationUrl,
              timestamp: currentDate,
              message: notificationMsg,
              status: 1,
              from_id: userId,
              });
            });

          await Promise.all(adminNotificationPromises);
        
        emitProgress('adding_rejection_activity_log', `Adding rejection activity log entry...`, 99);
          await RequestActivity.addRequestUpdate({
            request_id: requestId,
            activity: reqActivityMsg,
            added_by: userId,
            timestamp: currentDate
          });

        emitProgress('rejection_notifications_complete', `Rejection notifications completed successfully!`, 99);
        }
        } catch (error) {
        console.error('Error in async notifications handler:', error);
      }
    };

    // ✅ OPTIMIZATION: Handle split facility operations async (non-blocking)
    const handleSplitFacilitiesAsync = async () => {
      if (isSplit && targettedFacilityId.length > 0) {
        const splitPromises = targettedFacilityId.map(async (facilityId) => {
        try {
            const targetFacility = filteredSignwiseInstalations.find(installation => installation.id === parseInt(facilityId));
            const targetUrl = targetFacility?.url;
            
            if (targetUrl) {
              await axios.post(`${targetUrl}request/convert-sales-order-to-job-split`, {
                originRequestId: requestId,
                requestPartnerId,
                acceptStatus,
                userId,
                reason,
                movedToProduction,
                projectCategoryId,
                for_parent_facility: 2
              });
              console.log("mirror response split", facilityId);
        }
        } catch (error) {
            console.log("mirror error split", error);
          }
        });
        
        await Promise.all(splitPromises);
      }
    };

    // ✅ OPTIMIZATION: Send response immediately after critical operations
    emitProgress('complete', `Sales order conversion completed!`, 100);
    reply.send(responseData);

    // ✅ OPTIMIZATION: Run non-critical operations asynchronously (fire and forget)
    // These will complete in the background without blocking the response
    Promise.all([
      handleQuickBooksAsync(),
      handleNotificationsAsync(),
      // handleSplitFacilitiesAsync()
    ]).catch(error => {
      console.error('Error in async operations:', error);
    });
  }
  catch (error) {
    console.log("error : ", error);
    reply.send({ status: 500, message: "Server error", error });
  }
}

const createEstimateJob = async (req, reply) => {
  try {
    let { requestId, userId, requestPartnerId, isUpdateRequestFlow } = req.body;
    isUpdateRequestFlow = Boolean(isUpdateRequestFlow);
    const addedByUserId = userId;
    const currentTime = moment().unix();

    let estimateData = await Request.getRequestById(requestId);
    const salesOrderData = estimateData;
    let isRequestEstimate = estimateData[0]?.is_request_estimate;

    // 📊 Progress tracking function
    const emitProgress = (step, message, progress = 0) => {
      console.log(
        `[PROGRESS-MODAL] 📊 Progress: ${step} - ${message} (${progress}%)`
      );

      const progressData = {
        step,
        message,
        progress,
        timestamp: Date.now(),
        userId: userId, // Include user ID for filtering
        domain: DOMAIN, // Add domain for filtering (same as chat)
      };

      // Emit to working socket server (same as chat)
      console.log(
        `[PROGRESS-MODAL] 📡 Emitting progress to working socket server...`
      );
      try {
        const workingSocket = ioClient(REST_API_IO, {
          path: "/socket.io",
          transports: ["websocket", "polling"],
          secure: true,
          timeout: 5000,
        });

        workingSocket.on("connect", () => {
          console.log(`[PROGRESS-MODAL] 🔌 Connected to working socket server`);
          console.log(
            `[PROGRESS-MODAL] 📊 Emitting progress data:`,
            progressData
          );
          workingSocket.emit("message", {
            ...progressData,
            type: "progress",
          });
          workingSocket.emit("request_progress", progressData);
          console.log(
            `[PROGRESS-MODAL] ✅ Successfully emitted progress to working socket server`
          );

          // Disconnect after emitting
          setTimeout(() => {
            workingSocket.disconnect();
          }, 1000);
        });

        workingSocket.on("connect_error", (error) => {
          console.error(
            `[PROGRESS-MODAL] ❌ Working socket connection error:`,
            error
          );
        });
      } catch (error) {
        console.error(
          `[PROGRESS-MODAL] ❌ Error creating working socket connection:`,
          error
        );
      }
    };

    // Initial progress
    console.log(
      `[PROGRESS-MODAL] 🚀 Starting estimate job conversion for user:`,
      userId
    );
    emitProgress("start", `Initializing estimate job conversion...`, 5);

    let currentDate = Math.round(moment().valueOf() / 1000);

    emitProgress("processing_acceptance", `Processing estimate...`, 25);

    // let requestData = await Request.getRequestById(requestId);
    let requestData = estimateData;
    emitProgress(
      "preparing_project_data",
      `Preparing data for job creation...`,
      30
    );

    let projectData = {};
    projectData.project_uid = u_id;
    projectData.is_estimate_job = 1;
    projectData.name = requestData[0]["name"];
    projectData.noServiceNoTask = requestData[0]["noServiceNoTask"] || 0;
    projectData.short_code = requestData[0]["name"].substring(0, 3);
    projectData.request_id = requestId;
    projectData.tags = "";
    projectData.start_date = requestData[0]["submission_deadline"] || null;
    projectData.due_date = null;
    projectData.installation_date = 
      requestData[0]["participation_ends"] ||
      requestData[0]["installation_end_date"] ||
      null;
    projectData.installation_start_date =
      requestData[0]["installation_start_date"] || null;
    projectData.installation_end_date =
      requestData[0]["installation_end_date"] || null;
    projectData.project_category = requestData[0]["project_category"] || 0;
    projectData.community = requestData[0]["community"] || 1;
    projectData.project_type = requestData[0]["project_type"] || 0;
    projectData.website_url = "";
    projectData.state_id = 0;
    projectData.city = "DefaultCity";
    projectData.cost = requestData[0]["budget"] || null;
    projectData.description = requestData[0]["description"];
    projectData.approved_image =
      requestData[0]["thumbnail"] ||
      "https://media.freshbooks.com/wp-content/uploads/2022/03/approve-an-invoice-payment.jpg";
    projectData.cover_image = requestData[0]["thumbnail"] || null;
    projectData.reason = requestData[0]["reason"] || "";
    projectData.added_by = requestData[0]["added_by"];
    projectData.timestamp = moment().unix();
    projectData.status = 1;
    projectData.completion = 0;
    projectData.installation_status = 0;
    projectData.timestamp = currentTime;
    projectData.thumbnail = "fileNamewithDate";
    let checkProjectExist = await Request.checkExistingProject(requestId, true);
    console.log("checkProjectExist", checkProjectExist);
    // let checkProjectExist = await Request.checkExistingEstimateProject(requestId);

    if (!checkProjectExist) {
      console.log("createEstimateJob--creating_new job");
      emitProgress("creating_new_project", `Creating new job...`, 40);

      let result = await Project.addProject(projectData);
      // let result = await Project.addEstimateProject(projectData);

      if (result) {
        emitProgress("project_created", `Job created successfully`, 45);

        let projects = await Project.getProjectById(result.id);
        let allServices = await Product.getAllServicesBasedOnRequest(
          requestId,
          false
        );
        let taskUid = await ProjectTask.getSubTaskUid(result.id);

        emitProgress(
          "processing_attachments",
          `Processing job attachments...`,
          55
        );

        let allReqAttachments = await Request.getAllRequestAttachments(
          requestId
        );
        const urlObjects = [];
        allReqAttachments?.forEach((attachment) => {
          if (attachment?.approved === 1) {
            const isImage =
              attachment?.file_type?.startsWith("image/") ||
              attachment?.file_type == "img";
            const isVideo = attachment?.file_type?.startsWith("video/");
            const isPdf = attachment?.file_type === "application/pdf";
            let attachmentExtension = null;
            if (isImage) {
              attachmentExtension = "image";
            } else if (isVideo) {
              attachmentExtension = "video";
            } else {
              attachmentExtension = "other";
            }
            urlObjects.push({
              url: attachment.name,
              file_type: attachment?.file_type || "img",
              extension: attachmentExtension,
            });
          }
        });

        for (let i = 0; i < urlObjects.length; i++) {
          let data = {};
          if (urlObjects[i].url) {
            (data.userid = userId),
              (data.project_id = result.id),
              (data.file_url = urlObjects[i].url);
            data.file_type = urlObjects[i].file_type;
            (data.extension = urlObjects[i].extension),
              (data.timestamp = moment().unix());
            let inserted = await ProjectTask.addProjectAttachments(data);
          }
        }

        emitProgress(
          "processing_workflows",
          `Processing catalog workflows and skills...`,
          60
        );

        let workFlowIDs = "";
        let skillSet = [];

        emitProgress(
          "processing_services",
          `Processing services and workflows...`,
          70
        );

        if (allServices?.length > 0) {
          emitProgress(
            "processing_service_workflows",
            `Processing ${allServices.length} service workflows...`,
            71
          );
          let skillSet = [];
          for (const product of allServices) {
            if (product?.workflow[0]?.workflow_id) {
              skillSet.push(
                await Product.getWorkflowById(product?.workflow[0]?.workflow_id)
              );
            }

            const allSkillIds = skillSet
              ?.flatMap((workflow) =>
                workflow?.map((stage) => stage?.skill_ids?.split(","))
              )
              ?.flat();
            let uniqueSkillIds = [...new Set(allSkillIds)];
            uniqueSkillIds = uniqueSkillIds?.filter(
              (skillId) => skillId !== "" && skillId !== undefined
            );

            let uniqueSkillIdsString = uniqueSkillIds?.join(",");

            uniqueSkillIdsString = uniqueSkillIdsString?.replace(/,$/, "");
            let projectManagers = [];
            if (!empty(uniqueSkillIdsString)) {
              projectManagers = await Project.getTeamMembersBasedOnSkills(
                uniqueSkillIdsString,
                14
              );
            }

            projectManagers = await Project.getTeamMembersBasedOnSkills(
                uniqueSkillIdsString,
                14
              );

            let pm = {};
            let estimators = estimateData[0]?.estimator_ids || '';
            let estimatorArray = await Project.getEstimators(estimators)

            for (const projectManager of estimatorArray) {
              let estimatorId = projectManager.id;
              if (estimatorId) {
                pm = {
                  project_id: result.id,
                  staff_id: estimatorId || null,
                  partner_id: null,
                  contact_id: null,
                  lead_id: null,
                };
                let insertId = await Project.addProjectMember(pm);
              }
            }
          }
          emitProgress(
            "creating_service_tasks",
            `Creating service tasks...`,
            74
          );
          for (const product of allServices) {
            emitProgress(
              "creating_service_task",
              `Creating service task: ${product.service_name}...`,
              75
            );
            const projectTask = {
              name: product.service_name,
              project_id: result.id,
              timestamp: moment().unix(),
              userid: projectData.added_by,
              start_date: projectData.start_date || null,
              due_date: projectData.installation_date
                ? projectData.installation_date
                : null,
              task_uid: taskUid,
              complete: 0,
              complete_sync: 1,
              status: 1,
              cover_image:
                product?.product_design || projects?.cover_image || null,
              product_id: product?.product_id
                ? parseInt(product?.product_id)
                : null,
              service_id: product?.labour_cost_service_id
                ? parseInt(product?.labour_cost_service_id)
                : null,
              priority: 1,
              reporter_id: projectData.added_by,
              description: "",
              project_task_uid: "",
              order_id: product?.product_index,
              customer_order_product_id: product?.id,
            };

            const projectTaskId = await ProjectTask.addProjectTask(projectTask);
            let task_uid = await ProjectTask.getSubTaskUid(projectTaskId);
            if (product?.workflow) {
              emitProgress(
                "creating_service_subtasks",
                `Creating service subtasks for ${product.service_name}...`,
                76
              );
              for (const workflow of product?.workflow) {
                emitProgress(
                  "creating_service_subtask",
                  `Creating service subtask: ${workflow.name}...`,
                  77
                );
                const subTask = {
                  name: workflow.name,
                  task_id: projectTaskId,
                  skill_ids: workflow?.skill_ids,
                  timestamp: moment().unix(),
                  userid: projectData?.added_by,
                  start_date: projectData.start_date || null,
                  due_date: projectData.installation_date
                    ? projectData.installation_date
                    : null,
                  task_uid: task_uid,
                  complete: 0,
                  priority: 1,
                  status: 1,
                  reporter_id: projectData.added_by,
                  description: workflow?.message,
                };

                const projectSubTaskId = await ProjectTask.addProjectSubTask(
                  subTask
                );
                let projectManagers = [];
                if (!empty(workflow?.skill_ids)) {
                  projectManagers = await Project.getTeamMembersBasedOnSkills(
                    workflow?.skill_ids,
                    14
                  );
                }

                emitProgress(
                  "assigning_service_subtask_members",
                  `Assigning members to service subtask: ${workflow.name}...`,
                  78
                );
                let notificationMsg = `New Job "${projectData.name}" has been added.`;
                let notificationUrl = "Jobs";
                let pm = {};
                let estimators = estimateData[0]?.estimator_ids || '';
                let estimatorArray = await Project.getEstimators(estimators)
                for (const projectManager of estimatorArray) {
                  let estimatorId = projectManager.id;
                  if (estimatorId) {
                    pm = {
                      project_id: result.id,
                      subtask_id: projectSubTaskId,
                      staff_id: estimatorId || null,
                      partner_id: null,
                      contact_id: null,
                      lead_id: null,
                    };

                    let insertId = await ProjectTask.AddMembersToSubTask(pm);
                    emitProgress(
                      "sending_service_subtask_notifications",
                      `Sending notifications to service subtask members...`,
                      79
                    );
                    let notificationData_user = {
                      user_id: projectManager.user_id || 1,
                      url: notificationUrl,
                      timestamp: currentDate,
                      message:
                        "Assigned you to the estimate job: " + projectData.name,
                      status: 1,
                      from_id: projectData.added_by,
                    };

                    const addNotificationResult_user =
                      await Admin.addNotification(notificationData_user);
                  }
                }
              }
            }
          }
        }

        emitProgress("finalizing_tasks", `Finalizing job tasks...`, 75);

        let allProjectTask = await ProjectTask.getAllProjectTasks(
          result.id,
          (userType = 1),
          (userId = 1)
        );
        console.log("allProjectTaskallProjectTask", allProjectTask);
        let allCreatedTask = await ProjectTask.getAllProjectTasks(
          result.id,
          (userType = 1),
          (userId = 1)
        );

        emitProgress(
          "sending_notifications",
          `Sending job creation notifications...`,
          80
        );

        let notificationMsg = `New Estimate Job "${projectData.name}" has been created.`;
        let notificationUrl = "Jobs";

        emitProgress(
          "sending_user_notifications",
          `Sending notifications to job creator...`,
          81
        );
        const incrementResult_User = await Admin.incrementField(
          projectData.added_by,
          "noti_count",
          1
        );
        let notificationData_user = {
          user_id: projectData.added_by,
          url: notificationUrl,
          timestamp: currentDate,
          message: notificationMsg,
          status: 1,
          from_id: projectData.added_by,
        };
        const addNotificationResult_user = await Admin.addNotification(
          notificationData_user
        );

        emitProgress(
          "sending_admin_notifications",
          `Sending notifications to administrators...`,
          82
        );
        // Alert to all admins of new added project
        let adminIds = await Admin.getAllAdminIds();
        let notificationData = {};
        for (const adminId of adminIds) {
          if (adminId != projectData.added_by) {
            const incrementResult = await Admin.incrementField(
              adminId,
              "noti_count",
              1
            );

            notificationData = {
              user_id: adminId,
              url: notificationUrl,
              timestamp: currentDate,
              message: notificationMsg,
              status: 1,
              from_id: projectData.added_by,
            };

            const addNotificationResult = await Admin.addNotification(
              notificationData
            );
          }
        }

        emitProgress(
          "job_creation_complete",
          `Estimate Job created successfully!`,
          90
        );
      }
    } else {
      console.log("createEstimateJob--Updating existing job");
      emitProgress("updating_existing_project", `Updating existing job...`, 40);

      let result = await Project.updateProject(checkProjectExist, projectData);
      let statusUpdate = await Request.updateRequets(requestId, {
        timestamp: currentTime,
      });
      let projects = await Project.getProjectById(checkProjectExist);

      let allServices = await Product.getAllServicesBasedOnRequest(
        requestId,
        false
      );
      let taskUid = await ProjectTask.getSubTaskUid(checkProjectExist);

      emitProgress(
        "processing_existing_project_data",
        `Processing existing job data...`,
        50
      );

      emitProgress("updating_attachments", `Updating job attachments...`, 55);

      let allReqAttachments = await Request.getAllRequestAttachments(requestId);

      const attachmentDeleted = await ProjectTask.deleteProjectAttachments(
        checkProjectExist
      );

      const urlObjects = [];
      allReqAttachments?.forEach((attachment) => {
        if (attachment?.approved === 1) {
          const isImage =
            attachment?.file_type?.startsWith("image/") ||
            attachment?.file_type == "img";
          const isVideo = attachment?.file_type?.startsWith("video/");
          const isPdf = attachment?.file_type === "application/pdf";
          let attachmentExtension = null;
          if (isImage) {
            attachmentExtension = "image";
          } else if (isVideo) {
            attachmentExtension = "video";
          } else {
            attachmentExtension = "other";
          }
          urlObjects.push({
            url: attachment.name,
            file_type: attachment?.file_type || "img",
            extension: attachmentExtension,
          });
        }
      });

      for (let i = 0; i < urlObjects.length; i++) {
        let data = {};
        if (urlObjects[i]?.url) {
          (data.userid = userId),
            (data.project_id = checkProjectExist),
            (data.file_url = urlObjects[i].url);
          data.file_type = urlObjects[i].file_type;
          (data.extension = urlObjects[i].extension),
            (data.timestamp = moment().unix());
          let inserted = await ProjectTask.addProjectAttachments(data);
        }
      }
         if (isUpdateRequestFlow && (!allServices || allServices.length <= 0)) {
        // Update Request fallback: keep existing task tree, just reset to New.
        await ProjectTask.updateProjectTaskByProjectId(
          { status: 1, complete: 0 },
          checkProjectExist
        );
        await ProjectTask.updateProjectSubTaskByProjectId(
          { status: 1, complete: 0 },
          checkProjectExist
        );
      } else {

      emitProgress("updating_team_members", `Updating job team members...`, 60);

      let deletedProjectMembers = await Project.deleteAllProjectMember(
        checkProjectExist
      );

      let workFlowIDs = "";
      let skillSet = [];

      let allProjectTask = await ProjectTask.getAllProjectTasks(
        checkProjectExist,
        (userType = 1),
        (userId = 1)
      );

      const filteredTasks = allProjectTask?.filter(
        (item) => item.product_id != null && item.service_id == null
      );
      const deletedTasks = await ProjectTask.deleteProjectTaskByProjectId(
        checkProjectExist
      );
      if (filteredTasks?.length > 0) {
        for (const task of filteredTasks) {
          const deletedSubTaskAndMembers =
            await ProjectTask.deleteProjectSubTasksAndMembers(task.ID);
          const deletedTaskAttachments =
            await ProjectTask.deleteProjectTaskAttachmentByTaskId(task.ID);
        }
      }

      // console.log('allProductsallProductsallProducts', allProducts);
      emitProgress(
        "creating_existing_tasks",
        `Creating existing job tasks...`,
        63
      );

      if (allServices?.length > 0) {
        emitProgress(
          "processing_existing_services",
          `Processing existing services...`,
          69
        );
        let skillSet = [];
        for (const product of allServices) {
          if (product?.workflow[0]) {
            if (product.workflow[0]?.workflow_id) {
              skillSet.push(
                await Product.getWorkflowById(product.workflow[0]?.workflow_id)
              );
            }
          }

          const allSkillIds = skillSet
            .flatMap((workflow) =>
              workflow.map((stage) => stage.skill_ids?.split(","))
            )
            .flat();
          let uniqueSkillIds = [...new Set(allSkillIds)];
          uniqueSkillIds = uniqueSkillIds?.filter(
            (skillId) => skillId !== "" && skillId !== undefined
          );
          let uniqueSkillIdsString = uniqueSkillIds?.join(",");
          uniqueSkillIdsString = uniqueSkillIdsString.replace(/,$/, "");

          let pm = {};
          let estimators = estimateData[0]?.estimator_ids || '';
          let estimatorArray = await Project.getEstimators(estimators)
          for (const projectManager of estimatorArray) {
            let estimatorId = projectManager.id;
            if (estimatorId) {
              pm = {
                project_id: checkProjectExist,
                staff_id: estimatorId || null,
                partner_id: null,
                contact_id: null,
                lead_id: null,
              };
              let alreadyExisting = await Project.projectMemberExist(pm);

              if (!alreadyExisting) {
                emitProgress(
                  "adding_existing_service_members",
                  `Adding existing service members for ${product.service_name}...`,
                  70
                );
                let insertId = await Project.addProjectMember(pm);
                emitProgress(
                  "sending_existing_service_notifications",
                  `Sending notifications to existing service members...`,
                  71
                );
                let notificationMsg = `New  Job "${projectData.name}" has been added.`;
                let notificationUrl = "Jobs";
                const incrementResult_User = await Admin.incrementField(
                  projectManager.user_id,
                  "noti_count",
                  1
                );
                let notificationData_user = {
                  user_id: projectManager?.user_id || userId,
                  url: notificationUrl,
                  timestamp: currentDate,
                  message:
                    "Assigned you to the estimate job: " + projectData.name,
                  status: 1,
                  from_id: projectData.added_by,
                };

                const addNotificationResult_user = await Admin.addNotification(
                  notificationData_user
                );
              }
            }
          }
        }
        let allProjectTask = await ProjectTask.getAllProjectTasks(
          checkProjectExist,
          (userType = 1),
          (userId = 1)
        );

        // Loop through each task
        const promises = allProjectTask.map(async (task) => {
          if (task.from_product == 1 && task.service_id != null) {
            const taskNameExists = allServices.some(
              (product) =>
                product.service_name.includes(task.name) &&
                product.product_index == task.order_id
            );

            if (!taskNameExists) {
              await ProjectTask.updateProjectTask({ status: 0 }, task.ID);
              await ProjectTask.updateProjectSubTaskByTaskId(
                { status: 0 },
                task.ID
              );
            }
          }
        });

        await Promise.all(promises);

        emitProgress(
          "creating_existing_service_tasks",
          `Creating existing service tasks...`,
          72
        );
        for (const product of allServices) {
          emitProgress(
            "creating_existing_service_task",
            `Creating existing service task: ${product.service_name}...`,
            73
          );
          const projectTask = {
            name: product.service_name,
            project_id: checkProjectExist,
            timestamp: moment().unix(),
            userid: projectData.added_by,
            start_date: projectData.start_date || null,
            due_date: projectData.installation_date
              ? projectData.installation_date
              : null,
            task_uid: taskUid,
            complete: 0,
            complete_sync: 1,
            status: 1,
            cover_image: product.product_design || projects.cover_image || null,
            product_id: product?.product_id
              ? parseInt(product?.product_id)
              : null,
            service_id: product?.labour_cost_service_id
              ? parseInt(product?.labour_cost_service_id)
              : null,
            // cover_image: projects.cover_image || "null",
            priority: 1,
            reporter_id: projectData.added_by,
            description: "",
            project_task_uid: "",
            order_id: product.product_index,
            customer_order_product_id: product?.id,
          };

          let checkTaskExisting = await Request.checkExistingTask(
            product.service_name,
            checkProjectExist,
            product.product_index
          );
          if (!checkTaskExisting) {
            emitProgress(
              "creating_existing_service_subtasks",
              `Creating existing service subtasks for ${product.service_name}...`,
              74
            );
            const projectTaskId = await ProjectTask.addProjectTask(projectTask);
            let task_uid = await ProjectTask.getSubTaskUid(projectTaskId);
            for (const workflow of product.workflow) {
              emitProgress(
                "creating_existing_service_subtask",
                `Creating existing service subtask: ${workflow.name}...`,
                75
              );
              const subTask = {
                name: workflow.name,
                task_id: projectTaskId,
                skill_ids: workflow.skill_ids,
                timestamp: moment().unix(),
                userid: projectData.added_by,
                start_date: projectData.start_date || null,
                due_date: projectData.installation_date
                  ? projectData.installation_date
                  : null,
                task_uid: task_uid,
                complete: 0,
                priority: 1,
                status: 1,
                reporter_id: projectData.added_by,
                description: workflow.message,
              };

              const projectSubTaskId = await ProjectTask.addProjectSubTask(
                subTask
              );
              let projectManagers = [];
              if (!empty(workflow.skill_ids)) {
                projectManagers = await Project.getTeamMembersBasedOnSkills(
                  workflow.skill_ids,
                  14
                );
              }

              emitProgress(
                "assigning_existing_service_subtask_members",
                `Assigning members to existing service subtask: ${workflow.name}...`,
                76
              );
              let notificationMsg = `New Job "${projectData.name}" has been added.`;
              let notificationUrl = "Jobs";
              let pm = {};
              let estimators = estimateData[0]?.estimator_ids || '';
              let estimatorArray = await Project.getEstimators(estimators)
              for (const projectManager of estimatorArray) {
                let estimatorId = projectManager.id;
                if (estimatorId) {
                  pm = {
                    project_id: checkProjectExist,
                    subtask_id: projectSubTaskId,
                    staff_id: estimatorId || null,
                    partner_id: null,
                    contact_id: null,
                    lead_id: null,
                  };

                  let insertId = await ProjectTask.AddMembersToSubTask(pm);
                  emitProgress(
                    "sending_existing_service_subtask_notifications",
                    `Sending notifications to existing service subtask members...`,
                    77
                  );
                  let notificationData_user = {
                    user_id: projectManager?.user_id || userId,
                    url: notificationUrl,
                    timestamp: currentDate,
                    message:
                      "Assigned you to the estimate job: " + projectData.name,
                    status: 1,
                    from_id: projectData.added_by,
                  };

                  const addNotificationResult_user = await Admin.addNotification(
                    notificationData_user
                  );
                }
              }
            }
          } else {
            emitProgress(
              "updating_existing_service_subtasks",
              `Updating existing service subtasks for ${product.service_name}...`,
              78
            );
            for (const workflow of product.workflow) {
              let task_uid = await ProjectTask.getSubTaskUid(checkTaskExisting);

              const subTask = {
                name: workflow.name,
                task_id: checkTaskExisting,
                skill_ids: workflow.skill_ids,
                timestamp: moment().unix(),
                userid: projectData.added_by,
                start_date: projectData.start_date || null,
                due_date: projectData.installation_date
                  ? projectData.installation_date
                  : null,
                task_uid: task_uid,
                complete: 0,
                priority: 1,
                status: 1,
                reporter_id: projectData.added_by,
                description: workflow.message,
              };
              let checkSubTaskExisting = await Request.checkExistingSubTask(
                workflow.name,
                checkTaskExisting
              );

              if (!checkSubTaskExisting) {
                emitProgress(
                  "creating_additional_service_subtask",
                  `Creating additional service subtask: ${workflow.name}...`,
                  79
                );
                const projectSubTaskId = await ProjectTask.addProjectSubTask(
                  subTask
                );
                let projectManagers = [];
                if (!empty(workflow.skill_ids)) {
                  projectManagers = await Project.getTeamMembersBasedOnSkills(
                    workflow.skill_ids,
                    14
                  );
                }

                emitProgress(
                  "assigning_additional_service_subtask_members",
                  `Assigning members to additional service subtask: ${workflow.name}...`,
                  80
                );
                let notificationMsg = `New Job "${projectData.name}" has been added.`;
                let notificationUrl = "Jobs";
                let pm = {};
                let estimators = estimateData[0]?.estimator_ids || '';
                let estimatorArray = await Project.getEstimators(estimators)
                for (const projectManager of estimatorArray) {
                  let estimatorId = projectManager.id;
                  if (estimatorId) {
                    pm = {
                      project_id: checkProjectExist,
                      subtask_id: projectSubTaskId,
                      staff_id: estimatorId || null,
                      partner_id: null,
                      contact_id: null,
                      lead_id: null,
                    };

                    let insertId = await ProjectTask.AddMembersToSubTask(pm);
                    emitProgress(
                      "sending_additional_service_subtask_notifications",
                      `Sending notifications to additional service subtask members...`,
                      81
                    );
                    let notificationData_user = {
                      user_id: projectManager.user_id,
                      url: notificationUrl,
                      timestamp: currentDate,
                      message:
                        "Assigned you to the estimate job: " + projectData.name,
                      status: 1,
                      from_id: projectData.added_by,
                    };

                    const addNotificationResult_user =
                      await Admin.addNotification(notificationData_user);
                  }
                }
              }
            }
          }
        }
        }
      }

      if (isUpdateRequestFlow) {
        // Reset time tracking to make the estimate job behave like a fresh "New" job.
        await ProjectTask.deleteStaffTaskTimelogs(checkProjectExist);
        await ProjectTask.deleteStopwatchByJobId(checkProjectExist);
      }

      allCreatedTask = await ProjectTask.getAllProjectTasks(
        checkProjectExist,
        (userType = 1),
        (userId = 1)
      );

      emitProgress(
        "processing_existing_job_cost",
        `Processing existing job cost calculations...`,
        80
      );

      let jobCost = 0;
      let notifyJobcost = "";

      let notificationMsg = `Estimate Job "${projectData.name}" has been updated.`;
      let notificationUrl = "Jobs";
      const incrementResult_User = await Admin.incrementField(
        projectData.added_by,
        "noti_count",
        1
      );
      let notificationData_user = {
        user_id: projectData.added_by,
        url: notificationUrl,
        timestamp: currentDate,
        message: notificationMsg,
        status: 1,
        from_id: projectData.added_by,
      };
      const addNotificationResult_user = await Admin.addNotification(
        notificationData_user
      );

      // Alert to all admins of new added project
      let adminIds = await Admin.getAllAdminIds();
      let notificationData = {};
      for (const adminId of adminIds) {
        if (adminId != projectData.added_by) {
          const incrementResult = await Admin.incrementField(
            adminId,
            "noti_count",
            1
          );

          notificationData = {
            user_id: adminId,
            url: notificationUrl,
            timestamp: currentDate,
            message: notificationMsg,
            status: 1,
            from_id: projectData.added_by,
          };

          const addNotificationResult = await Admin.addNotification(
            notificationData
          );
        }
      }
      emitProgress(
        "finalizing_existing_project",
        `Finalizing existing job updates...`,
        82
      );

      let allTask = await ProjectTask.getAllProjectTasks(
        checkProjectExist,
        (userType = 1)
      );
      const sumCompletionTask = allTask?.reduce(
        (acc, task) => acc + (task?.complete || 0),
        0
      ) || 0;
      const averageCompletionTask = allTask?.length
        ? sumCompletionTask / allTask.length
        : 0;
      let projectTaskData = {};
      projectTaskData.completion = isUpdateRequestFlow ? 0 : averageCompletionTask;
      if (isUpdateRequestFlow) {
        projectTaskData.status = 1;
      } else {
        let firstTaskStatus = allTask[0]?.status;
        const tasksWithStatusCompletedTasks = allTask?.every(
          (task) => task.status === firstTaskStatus
        );
        if (tasksWithStatusCompletedTasks) {
          projectTaskData.status = firstTaskStatus;
        } else {
          projectTaskData.status = 2;
        }
      }
      if (jobCost === 1) {
        projectTaskData.jobcost = 1;
        projectTaskData.notifyJobcost = notifyJobcost;
      } else {
        projectTaskData.jobcost = 0;
        projectTaskData.notifyJobcost = notifyJobcost;
      }
      let resultStatus = await Project.updateProject(
        checkProjectExist,
        projectTaskData
      );
      emitProgress("project_update_complete", `Job updated successfully!`, 90);
    }

    emitProgress(
      "processing_activity_logs",
      `Processing activity logs and notifications...`,
      94
    );

    // let request = await Request.getRequestById(requestId);
    let request = estimateData;
    let partner = await RequestPartner.getRequestPartnerById(requestPartnerId);

    let reqActivityMsg;
    if (checkProjectExist) {
      reqActivityMsg = `🔄 Estimate Job "${request[0].name}" has been updated.`;
    } else {
      reqActivityMsg = `Estimate Job "${request[0].name}" has been created.`;
    }
    let notificationMsg = `The Sales Order '${request[0].name}' has been converted to Job.`;
    let notificationUrl = `request/view/${requestId}`;

    emitProgress(
      "sending_partner_notifications",
      `Sending notifications to partner...`,
      95
    );

    // // Alert to user of new added product
    // if (!empty(partner["user_id"])) {
    //   const incrementResult_User = await Admin.incrementField(
    //     partner.name,
    //     "noti_count",
    //     1
    //   );

    //   let notificationData_user = {
    //     user_id: partner.user_id,
    //     url: notificationUrl,
    //     timestamp: currentDate,
    //     message: notificationMsg,
    //     status: 1,
    //     from_id: userId,
    //   };

    //   const addNotificationResult_user = await Admin.addNotification(
    //     notificationData_user
    //   );
    // }

    // emitProgress(
    //   "sending_admin_notifications",
    //   `Sending notifications to administrators...`,
    //   97
    // );

    // let adminIds = await Admin.getAllAdminIds();
    // let notificationData = {};

    // for (const adminId of adminIds) {
    //   if (adminId != userId) {
    //     const incrementResult = await Admin.incrementField(
    //       adminId,
    //       "noti_count",
    //       1
    //     );
    //     notificationData = {
    //       user_id: adminId,
    //       url: notificationUrl,
    //       timestamp: currentDate,
    //       message: notificationMsg,
    //       status: 1,
    //       from_id: userId,
    //     };
    //     const addNotificationResult = await Admin.addNotification(
    //       notificationData
    //     );
    //   }
    // }
    // emitProgress("adding_activity_log", `Adding activity log entry...`, 98);
    // let requestActivity = {};
    // requestActivity["request_id"] = requestId;
    // requestActivity["activity"] = reqActivityMsg;
    // requestActivity["added_by"] = addedByUserId || userId;
    // requestActivity["timestamp"] = currentDate;
    // let reqActivityId = await RequestActivity.addRequestUpdate(requestActivity);

    emitProgress("complete", `Estimate job creatied successfully!`, 100);
    reply.send({
      status: 1,
      message: !checkProjectExist
        ? "Estimate job created successfully"
        : "Estimate job updated successfully",
    });
    // Sending notifications asynchronously after the response
    const handleAsyncNotifications = async () => {
      try {
        // Emit progress (optional) for partner notifications
        // emitProgress("sending_partner_notifications", `Sending notifications to partner...`, 95);

        // Alert to user of new added product
        if (partner["user_id"]) {
          // Increment user notification count
          const incrementResult_User = await Admin.incrementField(
            partner.name,
            "noti_count",
            1
          );

          // Prepare notification data
          let notificationData_user = {
            user_id: partner.user_id,
            url: notificationUrl,
            timestamp: currentDate,
            message: notificationMsg,
            status: 1,
            from_id: userId,
          };

          // Add user notification
          const addNotificationResult_user = await Admin.addNotification(notificationData_user);
        }

        // Emit progress for admin notifications
        // emitProgress("sending_admin_notifications", `Sending notifications to administrators...`, 97);

        // Get admin IDs and send notifications
        let adminIds = await Admin.getAllAdminIds();
        for (const adminId of adminIds) {
          if (adminId != userId) {
            // Increment admin notification count
            const incrementResult = await Admin.incrementField(adminId, "noti_count", 1);

            // Prepare notification data for admin
            let notificationData = {
              user_id: adminId,
              url: notificationUrl,
              timestamp: currentDate,
              message: notificationMsg,
              status: 1,
              from_id: userId,
            };

            // Add admin notification
            const addNotificationResult = await Admin.addNotification(notificationData);
          }
        }

        // Emit progress for activity log
        // emitProgress("adding_activity_log", `Adding activity log entry...`, 98);

        // Add activity log entry
        let requestActivity = {
          request_id: requestId,
          activity: reqActivityMsg,
          added_by: addedByUserId || userId,
          timestamp: currentDate,
        };
        let reqActivityId = await RequestActivity.addRequestUpdate(requestActivity);
      } catch (error) {
        console.error("Error in async notifications:", error);
      }
    };
    // Trigger the asynchronous notification and logging process
    handleAsyncNotifications().catch(error => {
      console.error("Error in handling async notifications:", error);
    });
  } catch (error) {
    console.log("error : ", error);
    reply.send({ status: 500, message: "Server error", error });
  }
};

const acceptOrRejectInvoiceSplit = async (req, reply) => {
  try {
    let { originRequestId, requestPartnerId, acceptStatus, userId, reason ,movedToProduction,projectCategoryId,for_parent_facility} = req.body
    console.log('for_parent_facilityyy', for_parent_facility)
    const addedByUserId = userId
    const currentTime = moment().unix();

    let salesOrderData = await Request.getRequestByOriginRequestId(originRequestId,for_parent_facility)
    let requestId = salesOrderData[0]?.id
    // 📊 Progress tracking function
    const emitProgress = (step, message, progress = 0) => {
      console.log(`[PROGRESS-MODAL] 📊 Progress: ${step} - ${message} (${progress}%)`);
      
      const progressData = {
        step,
        message,
        progress,
        timestamp: Date.now(),
        userId: userId, // Include user ID for filtering
        domain: DOMAIN // Add domain for filtering (same as chat)
      };
      
      // Emit to working socket server (same as chat)
      console.log(`[PROGRESS-MODAL] 📡 Emitting progress to working socket server...`);
      try {
        const workingSocket = ioClient(REST_API_IO, {
          path: '/socket.io',
          transports: ['websocket', 'polling'],
          secure: true,
          timeout: 5000
        });
        
        workingSocket.on('connect', () => {
          console.log(`[PROGRESS-MODAL] 🔌 Connected to working socket server`);
          console.log(`[PROGRESS-MODAL] 📊 Emitting progress data:`, progressData);
          workingSocket.emit('message', {
            ...progressData,
            type: 'progress'
          });
          workingSocket.emit('request_progress', progressData);
          console.log(`[PROGRESS-MODAL] ✅ Successfully emitted progress to working socket server`);
          
          // Disconnect after emitting
          setTimeout(() => {
            workingSocket.disconnect();
          }, 1000);
        });
        
        workingSocket.on('connect_error', (error) => {
          console.error(`[PROGRESS-MODAL] ❌ Working socket connection error:`, error);
        });
        
      } catch (error) {
        console.error(`[PROGRESS-MODAL] ❌ Error creating working socket connection:`, error);
      }
    };

    // Initial progress
    console.log(`[PROGRESS-MODAL] 🚀 Starting sales order to job conversion for user:`, userId);
    emitProgress('start', `Initializing sales order to job conversion...`, 5);
    
    emitProgress('checking_project_existence', `Checking for existing project...`, 10);
    let checkProjectExist = await Request.checkExistingProject(requestId)
    let currentDate = Math.round(moment().valueOf() / 1000);
    let acceptQuery = false
    let directInstalls = false
    emitProgress('updating_partner_status', `Updating client acceptance status...`, 15);
    
    if(requestPartnerId){
        acceptQuery = await RequestPartner.acceptOrRejectRequest(requestPartnerId, { accept_status: acceptStatus });
    }
    acceptQuery = true
    let allCreatedTask = []

    emitProgress('fetching_project_data', `Fetching job and job cost data...`, 20);
    let projectId = await Project.projectIdFromRequestId(requestId)
    let projectData = await Project.getProjectById(projectId)
    let joCostData = await Request.getJobCostDetailsForMaterials(requestId);
 
        const previosProductTotal =0 //parseFloat(Number(joCostData?.productTotal) || 0).toFixed(2) 
        const previosServiceTotal =0 //parseFloat(Number(joCostData?.serviceTotal) || 0).toFixed(2) 
        const previosJobCostTotal =0 // parseFloat(Number(previosProductTotal) + Number(previosServiceTotal) || 0).toFixed(2);
     

    if (acceptStatus === 1) {
      emitProgress('processing_acceptance', `Processing sales order acceptance...`, 25);
      
      let requestData = await Request.getRequestById(requestId)
      console.log('requestData', requestData)
      let reqresult = await Request.getAllRequests(requestId, userId)
      reqresult = reqresult?.data || []
      console.log('reqresulttt', reqresult[0]?.allInstallationCompleted)
      
      emitProgress('preparing_project_data', `Preparing data for job creation...`, 30);
      
      // adding accepted request to job 
      let projectData = {};
        projectData.project_uid = u_id;
        projectData.name = requestData[0]['name'];
        projectData.noServiceNoTask = requestData[0]['noServiceNoTask'];
        projectData.short_code = requestData[0]['name'].substring(0, 3);
        projectData.request_id = requestId;
        projectData.tags = '';
        projectData.start_date = requestData[0]['submission_deadline'] || null;
        projectData.due_date = null;
        // projectData.installation_date = requestData[0]['participation_ends'] || null;
        projectData.installation_date = requestData[0]['participation_ends'] || requestData[0]['installation_end_date'] || null;
        projectData.installation_start_date = requestData[0]['installation_start_date'] || null;
        projectData.installation_end_date = requestData[0]['installation_end_date'] || null;
        projectData.project_category = requestData[0]['project_category'] || 0;
        projectData.community = requestData[0]['community'] || 1;
        projectData.project_type = requestData[0]['project_type'] || 0;
        projectData.website_url = '';
        projectData.state_id = 0;
        projectData.city = 'DefaultCity';
        projectData.cost = requestData[0]['budget'] || null;
        projectData.description = requestData[0]['description'];
        projectData.approved_image = requestData[0]['thumbnail'] || 'https://media.freshbooks.com/wp-content/uploads/2022/03/approve-an-invoice-payment.jpg';
        projectData.cover_image = requestData[0]['thumbnail'] || null;
        projectData.reason = requestData[0]['reason'] || '';
        projectData.added_by = requestData[0]['added_by'];
        projectData.timestamp = moment().unix();
        projectData.status = reqresult[0]?.allInstallationCompleted ? 3 :1;
        projectData.completion = 0;
        projectData.installation_status = 0;
        projectData.timestamp = currentTime
        // console.log('requestData[0]?.job_category_name',requestData[0]?.job_category_name)
        // console.log('projectData',projectData)

      emitProgress('checking_project_name', `Checking job name availability...`, 35);
      
      let projectNameExist = await Project.isProjectNameExist(projectData.name);
      // if (projectNameExist != true) { 
      projectData.thumbnail = 'fileNamewithDate';
      let checkProjectExist = await Request.checkExistingProject(requestId)
      // let allServices = await Product.getAllServicesBasedOnRequest(requestId)
      // // console.log('allServices----',allServices);
      // for (const product of allServices) {
      //   for (const workflow of product?.workflow) {
      //     console.log('--------workflow', workflow)
      //   }
      // }
      // return;
      if (!checkProjectExist) {
        emitProgress('creating_new_project', `Creating new job...`, 40);
       
        let result = await Project.addProject(projectData)

        if (result) {
          emitProgress('project_created', `Job created successfully`, 45);
          
          let projects = await Project.getProjectById(result.id);
          let allProducts = await Product.getAllProductsBasedOnRequest(requestId)
          let allServices = await Product.getAllServicesBasedOnRequest(requestId,false)
          let taskUid = await ProjectTask.getSubTaskUid(result.id);
          let materialData = await Product.getMaterialDataFromRequestId(requestId)
          
          emitProgress('fetching_products_services', `Fetching products and services data...`, 50);

          
          // let filteredMaterialDataForQb = materialData.flat().filter(item => item?.isReceived == 1);
            
          // try {
          //   let response = await createJournalEntryForJobStarted(filteredMaterialDataForQb, projectData?.name)
          // } catch (error) {
          //   console.log("Error in QuickBooks API: ", error);
          // }

          emitProgress('processing_attachments', `Processing job attachments...`, 55);
          
          let allReqAttachments = await Request.getAllRequestAttachments(requestId)
          const urlObjects = [];
          allReqAttachments?.forEach(attachment => {
            if(attachment?.approved === 1){
              const isImage = attachment?.file_type?.startsWith('image/') || attachment?.file_type == 'img';
              const isVideo = attachment?.file_type?.startsWith('video/');
              const isPdf = attachment?.file_type === 'application/pdf';
              let attachmentExtension = null
              if(isImage){
                attachmentExtension = 'image'
              }else if(isVideo){
                attachmentExtension = 'video'
              }else{
                attachmentExtension = 'other'
              }
              urlObjects.push({
                  url: attachment.name,
                  file_type:attachment?.file_type || 'img',
                  extension: attachmentExtension
              });
            }
          });
      
          // console.log("$$$$$$$$$$URL",urlObjects);
          for(let i=0;i<urlObjects.length;i++){
            let data={}
            if(urlObjects[i].url){
                data.userid=userId,
                data.project_id=result.id,
                data.file_url=urlObjects[i].url
                data.file_type=urlObjects[i].file_type
                data.extension=urlObjects[i].extension,
                data.timestamp=moment().unix()
                let inserted=await ProjectTask.addProjectAttachments(data)
            }
          }
       
          emitProgress('processing_workflows', `Processing catalog workflows and skills...`, 60);
          
          let workFlowIDs = ""
          let skillSet = []
          
          for (const product of allProducts) {
            if(product?.workflow[0]?.workflow_id) {

              skillSet.push(await Product.getWorkflowById(product?.workflow[0]?.workflow_id))

            }

            const allSkillIds = skillSet
              ?.flatMap(workflow => workflow?.map(stage => stage?.skill_ids?.split(',')))
            
              ?.flat()
            let uniqueSkillIds = [...new Set(allSkillIds)];
            uniqueSkillIds = uniqueSkillIds?.filter(skillId => skillId !== "" && skillId !== undefined);

           
            // console.log(uniqueSkillIds);
            let uniqueSkillIdsString = uniqueSkillIds?.join(',');
          
          
            uniqueSkillIdsString=uniqueSkillIdsString?.replace(/,$/, '');
            let projectManagers=[]
            if(!empty(uniqueSkillIdsString)){
             projectManagers = await Project.getTeamMembersBasedOnSkills(uniqueSkillIdsString, 14)
            }

            //   reply.send({uniqueSkillIds:uniqueSkillIds,uniqueSkillIdsString:uniqueSkillIdsString,projectManagers:projectManagers})
            let pm = {}
            // console.log(projectManagers, '--------Team based on Skill Task')
            for (const projectManager of projectManagers) {

              pm = {
                project_id: result.id,
                staff_id: !empty(projectManager["id"]) ? projectManager["id"] : NULL,
                partner_id: null,
                contact_id: null,
                lead_id: null,

              };
              let alreadyExisting = await Project.projectMemberExist(pm);

              if (!alreadyExisting) {
                emitProgress('adding_project_members', `Adding job members for ${product.product_name}...`, 61);
                let insertId = await Project.addProjectMember(pm);
                emitProgress('sending_member_notifications', `Sending notifications to job members...`, 62);
                let notificationMsg = `New Job "${projectData.name}" has been added.`;
                let notificationUrl = "Jobs";
                const incrementResult_User = await Admin.incrementField(
                  projectManager.user_id,
                  "noti_count",
                  1
                );
                let notificationData_user = {
                  user_id: projectManager.user_id||1,
                  url: notificationUrl,
                  timestamp: currentDate,
                  message: "has assigned you to the job: " + projectData.name,
                  status: 1,
                  from_id: projectData.added_by,
                };

                const addNotificationResult_user = await Admin.addNotification(notificationData_user);
              }
            }
          }
          emitProgress('creating_product_tasks', `Creating catalog tasks...`, 65);
          
          for (const product of allProducts) {
            if (product.facility_id !== FACILITY_ID) continue;
            emitProgress('creating_task', `Creating task: ${product.product_name}...`, 66);
            const projectTask = {
              name: product.product_name,
              project_id: result.id,
              timestamp: moment().unix(),
              userid: projectData.added_by,
              start_date: projectData.start_date || null,
              due_date: projectData.installation_date ? projectData.installation_date : null,
              task_uid: taskUid,
              complete: 0,
              complete_sync: 1,
              status: 1,
              cover_image: product?.product_design || projects?.cover_image || null,
              product_id: product?.product_id ? parseInt(product?.product_id) : null,
              // cover_image: projects.cover_image || "null",
              priority: 1,
              reporter_id: projectData.added_by,
              description: "",
              project_task_uid: "",
              order_id: product?.product_index,
              customer_order_product_id:product?.id,
              request_location_id:product?.location_id,
            };
            const projectTaskId = await ProjectTask.addProjectTask(projectTask);
            let task_uid = await ProjectTask.getSubTaskUid(projectTaskId);
            if(product?.workflow){
              emitProgress('creating_subtasks', `Creating subtasks for ${product.product_name}...`, 67);
              for (const workflow of product?.workflow) {
              emitProgress('creating_subtask', `Creating subtask: ${workflow.name}...`, 68);
              const subTask = {
                name: workflow.name,
                task_id: projectTaskId,
                skill_ids: workflow?.skill_ids,
                timestamp: moment().unix(),
                userid: projectData?.added_by,
                start_date: projectData.start_date || null,
                due_date: projectData.installation_date ? projectData.installation_date : null,
                task_uid: task_uid,
                complete: 0,
                priority: 1,
                status: 1,
                reporter_id: projectData.added_by,
                description: workflow?.message,
              };

              const projectSubTaskId = await ProjectTask.addProjectSubTask(subTask);
              let projectManagers=[]
              if(!empty(workflow?.skill_ids)){
               projectManagers = await Project.getTeamMembersBasedOnSkills(workflow?.skill_ids, 14)
              }
              
              // console.log(projectManagers, '--------Team based on Skill sub task')
             
              emitProgress('assigning_subtask_members', `Assigning members to subtask: ${workflow.name}...`, 69);
              let notificationMsg = `New Job "${projectData.name}" has been added.`;
              let notificationUrl = "Jobs";
              let pm = {}
              for (const projectManager of projectManagers) {

                pm = {
                  project_id: result.id,
                  subtask_id: projectSubTaskId,
                  staff_id: !empty(projectManager["id"]) ? projectManager["id"] : NULL,
                  partner_id: null,
                  contact_id: null,
                  lead_id: null,

                  };

                let insertId = await ProjectTask.AddMembersToSubTask(pm);
                emitProgress('sending_subtask_notifications', `Sending notifications to subtask members...`, 70);
                let notificationData_user = {
                  user_id: projectManager.user_id||1,
                  url: notificationUrl,
                  timestamp: currentDate,
                  message: "has assigned you to the job: " + projectData.name,
                  status: 1,
                  from_id: projectData.added_by,
                };

                const addNotificationResult_user = await Admin.addNotification(
                  notificationData_user
                );

              }

            }
            }
          }

          emitProgress('processing_services', `Processing services and workflows...`, 70);
          
          if(allServices?.length > 0){
            emitProgress('processing_service_workflows', `Processing ${allServices.length} service workflows...`, 71);
            let skillSet = []
            for (const product of allServices) {
              if(product?.workflow[0]?.workflow_id) {
                skillSet.push(await Product.getWorkflowById(product?.workflow[0]?.workflow_id))
              }

              const allSkillIds = skillSet
                ?.flatMap(workflow => workflow?.map(stage => stage?.skill_ids?.split(',')))
              
                ?.flat()
              let uniqueSkillIds = [...new Set(allSkillIds)];
              uniqueSkillIds = uniqueSkillIds?.filter(skillId => skillId !== "" && skillId !== undefined);

            
              // console.log(uniqueSkillIds);
              let uniqueSkillIdsString = uniqueSkillIds?.join(',');
            
            
              uniqueSkillIdsString=uniqueSkillIdsString?.replace(/,$/, '');
              let projectManagers=[]
              if(!empty(uniqueSkillIdsString)){
                projectManagers = await Project.getTeamMembersBasedOnSkills(uniqueSkillIdsString, 14)
              }
              
              //   reply.send({uniqueSkillIds:uniqueSkillIds,uniqueSkillIdsString:uniqueSkillIdsString,projectManagers:projectManagers})
              let pm = {}
              // console.log(projectManagers, '--------Team based on Skill Task')
              for (const projectManager of projectManagers) {

                pm = {
                  project_id: result.id,
                  staff_id: !empty(projectManager["id"]) ? projectManager["id"] : NULL,
                  partner_id: null,
                  contact_id: null,
                  lead_id: null,

                };
                let alreadyExisting = await Project.projectMemberExist(pm);

                if (!alreadyExisting) {
                  emitProgress('adding_service_members', `Adding service members for ${product.service_name}...`, 72);
                  let insertId = await Project.addProjectMember(pm);
                  emitProgress('sending_service_member_notifications', `Sending notifications to service members...`, 73);
                  let notificationMsg = `New Job "${projectData.name}" has been added.`;
                  let notificationUrl = "Jobs";
                  const incrementResult_User = await Admin.incrementField(
                    projectManager.user_id,
                    "noti_count",
                    1
                  );
                  let notificationData_user = {
                    user_id: projectManager.user_id||1,
                    url: notificationUrl,
                    timestamp: currentDate,
                    message: "has assigned you to the job: " + projectData.name,
                    status: 1,
                    from_id: projectData.added_by,
                  };

                  const addNotificationResult_user = await Admin.addNotification(
                    notificationData_user
                  );
                }
              }
            }
            emitProgress('creating_service_tasks', `Creating service tasks...`, 74);
            for (const product of allServices) {
              if(requestData[0]?.project_category !==11 && product?.labour_cost_service_id == 10) continue
              emitProgress('creating_service_task', `Creating service task: ${product.service_name}...`, 75);
              const projectTask = {
                name: product.service_name,
                project_id: result.id,
                timestamp: moment().unix(),
                userid: projectData.added_by,
                start_date: projectData.start_date || null,
                due_date: projectData.installation_date ? projectData.installation_date : null,
                task_uid: taskUid,
                complete: 0,
                complete_sync: 1,
                status: 1,
                cover_image: product?.product_design || projects?.cover_image || null,
                product_id: product?.product_id ? parseInt(product?.product_id) : null,
                service_id: product?.labour_cost_service_id ? parseInt(product?.labour_cost_service_id) : null,
                // cover_image: projects.cover_image || "null",
                priority: 1,
                reporter_id: projectData.added_by,
                description: "",
                project_task_uid: "",
                order_id: product?.product_index,
                customer_order_product_id:product?.id

              };

              const projectTaskId = await ProjectTask.addProjectTask(projectTask);
              let task_uid = await ProjectTask.getSubTaskUid(projectTaskId);
              if(product?.workflow){
                  emitProgress('creating_service_subtasks', `Creating service subtasks for ${product.service_name}...`, 76);
                  for (const workflow of product?.workflow) {
                // console.log(workflow, '--------workflow')
                emitProgress('creating_service_subtask', `Creating service subtask: ${workflow.name}...`, 77);
                const subTask = {
                  name: workflow.name,
                  task_id: projectTaskId,
                  skill_ids: workflow?.skill_ids,
                  timestamp: moment().unix(),
                  userid: projectData?.added_by,
                  start_date: projectData.start_date || null,
                  due_date: projectData.installation_date ? projectData.installation_date : null,
                  task_uid: task_uid,
                  complete: 0,
                  priority: 1,
                  status: 1,
                  reporter_id: projectData.added_by,
                  description: workflow?.message,
                };

                const projectSubTaskId = await ProjectTask.addProjectSubTask(subTask);
                let projectManagers=[]
                if(!empty(workflow?.skill_ids)){
                projectManagers = await Project.getTeamMembersBasedOnSkills(workflow?.skill_ids, 14)
                }
                
                // console.log(projectManagers, '--------Team based on Skill sub task')
              
                emitProgress('assigning_service_subtask_members', `Assigning members to service subtask: ${workflow.name}...`, 78);
                let notificationMsg = `New Job "${projectData.name}" has been added.`;
                let notificationUrl = "Jobs";
                let pm = {}
                for (const projectManager of projectManagers) {

                  pm = {
                    project_id: result.id,
                    subtask_id: projectSubTaskId,
                    staff_id: !empty(projectManager["id"]) ? projectManager["id"] : NULL,
                    partner_id: null,
                    contact_id: null,
                    lead_id: null,

                  };

                  let insertId = await ProjectTask.AddMembersToSubTask(pm);
                  emitProgress('sending_service_subtask_notifications', `Sending notifications to service subtask members...`, 79);
                  let notificationData_user = {
                    user_id: projectManager.user_id||1,
                    url: notificationUrl,
                    timestamp: currentDate,
                    message: "has assigned you to the job: " + projectData.name,
                    status: 1,
                    from_id: projectData.added_by,
                  };

                  const addNotificationResult_user = await Admin.addNotification(
                    notificationData_user
                  );

                }

              }
              }
            }
          }
       
          emitProgress('finalizing_tasks', `Finalizing job tasks...`, 75);
          
          let allProjectTask = await ProjectTask.getAllProjectTasks(result.id, userType = 1, userId = 1)
          console.log('allProjectTaskallProjectTask',allProjectTask)
          allCreatedTask = await ProjectTask.getAllProjectTasks(result.id, userType = 1, userId = 1)
          console.log('allCreatedTaskallCreatedTask',allCreatedTask)
          // if(((allProjectTask?.length <= 0 && requestData[0]['noServiceNoTask'] == 1) || (allCreatedTask?.length <= 0)) && projectCategoryId !== 12){
          //   // Service-only requests (projectCategoryId = 11) should have status = 1 (pre-production), others = 3 (invoicing)
          //   const projectStatus = projectCategoryId === 11 ? 1 : 3;
          //   const resultStatus = await Project.updateProject(result.id, { status: projectStatus, completion : 100 });
          // }
          if (allCreatedTask?.length <= 0 && projectCategoryId !== 12) {
            console.log(`⚠️ No installation created for project ${result.id}, but tasks exist.`);
            
            await Project.updateProject(result.id, { noServiceNoTask: 0 });
            await Request.updateRequestById(requestId, { noServiceNoTask: 0 });

            // notify
            let notificationMsg = `Sales Order "${projectData.name}" has been moved to 'Invoicing' because all tasks and installtions are off.`;
            let notificationUrl = "official-request";
            // Increment notification count
            const incrementResult_User = await Admin.incrementField(userId, "noti_count", 1);
            let notificationData_user = {
              user_id: userId,
              url: notificationUrl,
              timestamp: currentDate,
              message: notificationMsg,
              status: 1,
              from_id: userId,
            };
            const addNotificationResult_user = await Admin.addNotification(notificationData_user);

            // Alert to all admins of new added project

            let adminIds = await Admin.getAllAdminIds();
            let notificationData = {};
            for (const adminId of adminIds) {
              if (adminId != userId) {
                const incrementResult = await Admin.incrementField(adminId, "noti_count", 1);
                notificationData = {
                    user_id: adminId,
                    url: notificationUrl,
                    timestamp: currentDate,
                    message: notificationMsg,
                    status: 1,
                    from_id: userId,
                };
                const addNotificationResult = await Admin.addNotification(notificationData);
              }
            }
          }
          emitProgress('processing_job_cost', `Processing job cost calculations...`, 80);
          
          /************  Jobcost ******************** */

          emitProgress('fetching_materials', `Fetching materials and product formulas...`, 82);
          
          let allMaterials = await Product.getAllMaterials(true)
          // let allProductsData = await Product.getAllLocations(requestId)
          let allRecipies = []
          let allProductsData = []
          if (requestData[0]?.project_category === 15) {
            allProductsData = await Product.getAllLocations(requestId)
          } else {
            allProductsData = await Product.getNonStockProducts(requestId)
          }
         console.log('allProductsData--------1',allProductsData)
          // console.log('allProductsData----',allProductsData)
          // if(allProductsData && requestData[0]?.job_category_name != 'Time & Material'){
          if(allProductsData){
            allRecipies = await Recipe.getAllReceipesByProjectId(allProductsData,requestId);
          }
          let results = await Product.getFinishedProducts(0,true);
          let newMaterialObjects = [];
          let jobCostFlag=0;
          let notifyJobcost=""
          
          emitProgress('processing_recipes', `Processing recipes and material calculations...`, 84);
          // console.log("#################recipe123", results);
         

          for (const recipeItem of allRecipies) {
            // let results = await Product.getFinishedProducts();
            for (const ingredient of recipeItem?.ingredients) {
              let materialItems = results?.filter((mat) => mat?.material_id === ingredient?.product_id);
              let filteredMaterialItems = materialItems?.filter(mat => 
                (ingredient.stockType === 2 ? mat.partial === 1 : mat.partial === 0)
              );
              let quantityInInitialUnit = 0;
              quantityInInitialUnit = ingredient?.quantity;
              // if(ingredient.stockType === 1 || materialItems[0]?.unit == 4){
              //   quantityInInitialUnit = ingredient?.quantity;
              // }else{
              //   let conversionFactorToInitial = getConversionFactor(ingredient?.convertionUnit, materialItems[0]?.unit, ingredient?.width || 1);
              //   quantityInInitialUnit = ingredient?.convertionQty * conversionFactorToInitial;
              // }
              let quantityNeeded = quantityInInitialUnit * recipeItem?.quantity * recipeItem?.location_quantity;
              // let quantityNeeded = ingredient?.quantity*recipeItem?.quantity*recipeItem?.location_quantity;
              // let totalAvailableQuantity = filteredMaterialItems?.reduce((total, item) => total + item.qty, 0);
              let totalAvailableQuantity = 0;
              if(ingredient.stockType === 1){
                totalAvailableQuantity = filteredMaterialItems?.reduce((total, item) => total + item?.qty, 0);
              }else{
                totalAvailableQuantity = filteredMaterialItems?.filter((item)=>item.partial_unit==ingredient?.convertionUnit)?.reduce((total, item) => total + item?.qty, 0);
              }
              // console.log(`Converted Ingredient Quantity to Initial Unit (${ingredient.convertionUnit} → ${materialItems[0]?.unit}), width : ${ingredient?.width}:`, quantityInInitialUnit);
              // console.log('filteredMaterialItems===', filteredMaterialItems);
              // console.log('ingredient?.quantity===', ingredient?.quantity);
              // console.log('recipeItem?.quantity===', recipeItem?.quantity);
              // console.log('recipeItem?.location_quantity===', recipeItem?.location_quantity);
              // console.log('Total Available Quantity (Before Conversion) ===', totalAvailableQuantity);
              // console.log('Quantity Needed (Before Conversion) ===', quantityNeeded);
              
              // // Get actual width and quantity from material stock
              // let width = ingredient?.width || 1;

              // // Convert quantityNeeded dynamically using width
              // let conversionFactor = getConversionFactor(ingredient?.convertionUnit, materialItems[0]?.unit, width);
              // let convertedQuantityNeeded = quantityNeeded * conversionFactor;

              if (totalAvailableQuantity >= quantityNeeded) {
              // if (totalAvailableQuantity >= convertedQuantityNeeded) {
                // ✅ Sufficient stock available
                // let remainingQuantity = convertedQuantityNeeded;
                let remainingQuantity = quantityNeeded;
                filteredMaterialItems?.forEach((materialItem) => {

                  // Convert unit cost based on unit type
                  let originalUnitCost = Number(materialItem?.unit_cost) || 0;
                  // let convertedUnitCost = getConvertedUnitCost(originalUnitCost, materialItem?.unit, ingredient?.convertionUnit, width);

                  // console.log(`Unit Cost (Before Conversion):`, originalUnitCost);
                  // console.log(`Converted Unit Cost (${materialItems[0]?.unit} → ${ingredient.convertionUnit} at width ${width}):`, convertedUnitCost);
                  // console.log(`totalAvailableQuantity :`, totalAvailableQuantity);
                  // console.log(`convertedQuantityNeeded :`, convertedQuantityNeeded);

                  if (remainingQuantity > 0) {
                    let quantityToPull = Math.min(remainingQuantity, materialItem?.qty);
                    newMaterialObjects.push({
                      id: materialItem?.id,
                      productId: materialItem?.material_id,
                      quantity: quantityToPull,
                      unit_cost: materialItem?.inhouse == 0 ? 0 : originalUnitCost,
                      total_cost: materialItem?.inhouse == 0 ? 0 : parseFloat(quantityToPull) * parseFloat(originalUnitCost),
                      recipe: recipeItem?.id,
                      recipeName: recipeItem?.name,
                      customer_order_product_id: recipeItem?.customer_order_product_id,

                    });
                    results = results?.map(item => item?.id === materialItem?.id ? { ...item, qty: item?.qty - quantityToPull } : item);
                    remainingQuantity -= quantityToPull;
                  }
                });
                // console.log(`Added ${quantityNeeded} units of ${ingredient.name}`);
              } else {
                // Insufficient quantity available in existing packages, pull from additional packages
                console.log(`Insufficient quantity of ${ingredient?.name} available in existing packages.1`);
                console.log(`Pulling additional ${quantityNeeded - totalAvailableQuantity} units from packages...1`);
                // Your logic to pull additional quantities from packages goes here
              
              
                if(empty(notifyJobcost)){
                    notifyJobcost+=`There is insuffient quantity on materials: ${ingredient?.name} (${quantityNeeded}) (${ingredient.stockType === 1 ? 'Finished' : 'Partial'})`
                }
                else{
                   notifyJobcost+=` ,${ingredient?.name} (${quantityNeeded}) (${ingredient.stockType === 1 ? 'Finished' : 'Partial'})`
                }
                jobCostFlag=1
              }
            };
          };
          emitProgress('processing_rfp_products', `Processing RFP products for job cost...`, 86);
          
          let rfpProduct=await CustomerOrderProduct.getRFPProductByRequest(requestId)
        
          // console.log("###############vk1888888888888888888", rfpProduct);
          for(let i=0;i<rfpProduct?.length;i++){
            let jobCostData = {
              product_id: rfpProduct[i]?.product_id,
              project_id: result?.id,
              actual_quantity: rfpProduct[i]?.product_qty*rfpProduct[i]?.location_quantity,
              actual_unit_price:  rfpProduct[i]?.material == 0 ? rfpProduct[i]?.vendor_price : rfpProduct[i]?.product_price,
              actual_total:  rfpProduct[i]?.material == 0 ? rfpProduct[i]?.vendor_price : rfpProduct[i]?.product_price*rfpProduct[i]?.product_qty*rfpProduct[i]?.location_quantity,
              quantity: rfpProduct[i]?.product_qty*rfpProduct[i]?.location_quantity,
              unit_price:  rfpProduct[i]?.material == 0 ? rfpProduct[i]?.vendor_price : rfpProduct[i]?.product_price,
              total:  rfpProduct[i]?.material == 0 ? rfpProduct[i]?.vendor_price : rfpProduct[i]?.product_price*rfpProduct[i]?.product_qty*rfpProduct[i]?.location_quantity,
              // quantity: 0,
              // unit_price:  rfpProduct[i]?.material == 0 ? rfpProduct[i]?.vendor_price : rfpProduct[i]?.product_price,
              // total:  0,
              recipe: 1,
              recipe_name: null,
              timestamp: moment().unix(),
              added_by: userId,
              rfp:1,
              customer_order_product_id: rfpProduct[i]?.id,

            };
            insertedstatus = await JobCost.addJobCost(jobCostData);
            console.log("#####insertedStatus 1", jobCostData);
          }
          emitProgress('updating_material_stock', `Updating material stock levels...`, 88);
          
          for (let i = 0; i < newMaterialObjects?.length; i++) {
            const materialData = await Product.getMaterialById(newMaterialObjects[i].id)
            if (!empty(materialData)) {

              let newQty = materialData[0].qty - newMaterialObjects[i].quantity
              // console.log("#############MaterialData", materialData[0]);
              // console.log("#############newMaterialObjects", newMaterialObjects[i]);
              // console.log("#############newQty1", newQty);
              if (newQty >= 0) {
                let totalCost = newMaterialObjects[i].unit_cost * newQty
                let data = {};
                data['qty'] = newQty
                data['total_cost'] = parseInt(totalCost)
                let jobCostData = {
                  product_id: newMaterialObjects[i].id,
                  project_id: result.id,
                  // quantity: reqresult[0]?.allInstallationCompleted ? newMaterialObjects[i].quantity : 0,
                  quantity: reqresult[0]?.allInstallationCompleted ? newMaterialObjects[i].quantity : 0,
                  unit_price: newMaterialObjects[i].unit_cost,
                  total: reqresult[0]?.allInstallationCompleted ? newMaterialObjects[i].total_cost : 0,
                  // quantity: newMaterialObjects[i].quantity,
                  // unit_price: newMaterialObjects[i].unit_cost,
                  // total: newMaterialObjects[i].total_cost,
                  actual_quantity: newMaterialObjects[i].quantity,
                  actual_unit_price: newMaterialObjects[i].unit_cost,
                  actual_total: newMaterialObjects[i].total_cost,
                  recipe: 1,
                  recipe_name: newMaterialObjects[i].recipeName,
                  timestamp: moment().unix(),
                  added_by: userId,
                 customer_order_product_id: newMaterialObjects[i]?.customer_order_product_id,

                };
                if (jobCostData?.actual_quantity > 0) {
                  insertedstatus = await JobCost.addJobCost(jobCostData);
                  console.log("#####insertedStatus 2", jobCostData);

                  reduceStockResult = await Product.updateMaterialStock(newMaterialObjects[i].id, data)
                  // console.log("#####reduceStockResult", reduceStockResult);

                  // Autofill stock if stock is 0
                  // if(newQty <= 0){
                  //   if(materialData[0]?.autofill == 1){
                  //     const parentFinishedMaterial = await Product.getMaterialById(materialData[0]?.parent_finished_product_id)
                  //     console.log('parentFinishedMaterial----',parentFinishedMaterial)
                  //     if (parentFinishedMaterial.length > 0 && parentFinishedMaterial[0]?.qty > 0) {
                  //       let reducedQty = parentFinishedMaterial[0]?.qty - 1
                  //       let parentTotalCost = parentFinishedMaterial[0]?.unit_cost * reducedQty
                  //       let parentData = {
                  //         qty: reducedQty,
                  //         total_cost: parseInt(parentTotalCost)
                  //       };
                  //       const reduceFinishedProductStock = await Product.updateMaterialStock(materialData[0]?.parent_finished_product_id, parentData)
        
                  //       let addingQty = materialData[0]?.actual_qty
                  //       let totalCost = materialData[0]?.actual_total_cost
                  //       let autoFilldata = {
                  //         qty: addingQty,
                  //         total_cost: totalCost
                  //       };
                  //       console.log('autofillPartialStock--',autoFilldata);
                  //       const autofillPartialStock = await Product.updateMaterialStock(materialData[0]?.id, autoFilldata);
                  //     }
                  //   }
                  // }
                }
              } else {
                console.log(`Stock is lesser than entered qty 1`);
                // Autofill stock if stock is 0
                // if(newQty <= 0){
                //   if(materialData[0]?.autofill == 1){
                //     const parentFinishedMaterial = await Product.getMaterialById(materialData[0]?.parent_finished_product_id)
                //     console.log('parentFinishedMaterial----',parentFinishedMaterial)
                //     if (parentFinishedMaterial.length > 0 && parentFinishedMaterial[0]?.qty > 0) {
                //       let reducedQty = parentFinishedMaterial[0]?.qty - 1
                //       let parentTotalCost = parentFinishedMaterial[0]?.unit_cost * reducedQty
                //       let parentData = {
                //         qty: reducedQty,
                //         total_cost: parseInt(parentTotalCost)
                //       };
                //       const reduceFinishedProductStock = await Product.updateMaterialStock(materialData[0]?.parent_finished_product_id, parentData)
      
                //       let addingQty = materialData[0]?.actual_qty
                //       let totalCost = materialData[0]?.actual_total_cost
                //       let autoFilldata = {
                //         qty: addingQty,
                //         total_cost: totalCost
                //       };
                //       console.log('autofillPartialStock--',autoFilldata);
                //       const autofillPartialStock = await Product.updateMaterialStock(materialData[0]?.id, autoFilldata);
                //     }
                //   }
                // }
                // reply.send({ status: 0, message: `Stock is lesser than entered qty for material (${materialData[0].barcode})`, resultId: materialData[0].id });
              }
            }
          }
          // console.log("#################newMaterialObjects", newMaterialObjects);
            let newMaterialRfqObjects = [];
            let bringStockCatalog = requestData[0].project_category == 15 ? false: true
          let materialRfq=await CustomerOrderProduct.getMaterialProductByRequest(requestId,bringStockCatalog)
          if(requestData[0]['job_category_name'] == 'Time & Material'){
            for(let i=0; i<materialRfq.length; i++){
              let results = await Product.getFinishedProducts(materialRfq[i].product_id,true);
              // console.log('results===== 122',results)
              let notPartial=materialRfq[i].stock_type===1 ? true : false
              let filteredResult=[]
              if(notPartial){
                filteredResult = results?.filter(item=>item.partial == 0 )
              }else{
                filteredResult = results?.filter(item=>((item.partial == 1) && (item.partial_unit == materialRfq[i].convertion_unit)) )
              }
              // console.log('results===== 12',results)
              if(filteredResult?.length > 0){
                let jobCostData = {
                  product_id: filteredResult[0].id,
                  project_id: result.id,
                  quantity: reqresult[0]?.allInstallationCompleted ? materialRfq[i].product_qty : 0,
                  unit_price: materialRfq[i].product_price,
                  total: reqresult[0]?.allInstallationCompleted ? materialRfq[i].product_total : 0,
                  // quantity: materialRfq[i].quantity,
                  // unit_price: materialRfq[i].unit_cost,
                  // total: materialRfq[i].total_cost,
                  actual_quantity: materialRfq[i].product_qty,
                  actual_unit_price: materialRfq[i].product_price,
                  actual_total: materialRfq[i].product_total,
                  recipe: 0,
                  recipe_name: '',
                  timestamp: moment().unix(),
                  added_by: userId,
                  customer_order_product_id: materialRfq[i]?.id,
                };
                insertedstatus = await JobCost.addJobCost(jobCostData);
                console.log("#####insertedStatus 3", jobCostData);
              } else {
                console.log(`Insufficient quantity of ${materialRfq[i].product_name} available in existing packages.21`);
                if(empty(notifyJobcost)){
                  notifyJobcost+=`There is insuffient quantity on materials: ${materialRfq[i].product_name} (Finished)`
                }
                else{
                  notifyJobcost+=` ,${materialRfq[i].product_name} (Finished)`
                }
                jobCostFlag=1
              }
            }
          }else{
            for(let i=0;i<materialRfq.length; i++){
              let quantityNeeded = materialRfq[i].product_qty;
              // let results = await Product.getFinishedProducts();
              let notPartial=materialRfq[i].stock_type===1 ? true : false
              console.log('notPartial', notPartial)
              let filteredResult=[]
              if(notPartial){
                filteredResult = results?.filter(item=>item.partial == 0 )
              }else{
                filteredResult = results?.filter(item=>((item.partial == 1) && (item.partial_unit == materialRfq[i].convertion_unit)) )
              }
              let materialItems = filteredResult.filter((mat) => mat.material_id === materialRfq[i].product_id);
              let totalAvailableQuantity = materialItems.reduce((total, item) => total + item.qty, 0);

              
              if (totalAvailableQuantity >= quantityNeeded) {
                // Sufficient quantity available in existing packages
                let remainingQuantity = quantityNeeded;
                // console.log('materialRfq---',materialRfq)
                // console.log('materialItems---',materialItems)
                materialItems.forEach((materialItem) => {
                  if (remainingQuantity > 0) {
                    let quantityToPull = Math.min(remainingQuantity, materialItem.qty);
                    newMaterialRfqObjects.push({
                      id: materialItem.id,
                      productId: materialItem.material_id,
                      quantity: quantityToPull,
                      unit_cost: materialItem?.inhouse == 0 ? 0 : materialItem.unit_cost,
                      total_cost: materialItem?.inhouse == 0 ? 0 : parseFloat(quantityToPull) * parseFloat(materialItem.unit_cost),
                      recipe:0,
                      recipeName: null,
                    });
                    filteredResult = filteredResult.map(item => item.id === materialItem.id ? { ...item, qty: item.qty - quantityToPull } : item);
                    remainingQuantity -= quantityToPull;
                  }
                });
              
              } else {
                // Insufficient quantity available in existing packages, pull from additional packages
                console.log(`Insufficient quantity of ${materialRfq[i].product_name} available in existing packages.22`);
                console.log(`Pulling additional ${quantityNeeded - totalAvailableQuantity} units from packages...2`);
                // Your logic to pull additional quantities from packages goes here
              
              
                if(empty(notifyJobcost)){
                    notifyJobcost+=`There is insuffient quantity on materials: ${materialRfq[i].product_name} (${quantityNeeded}) (Finished)`
                }
                else{
                  notifyJobcost+=` ,${materialRfq[i].product_name} (${quantityNeeded}) (Finished)`
                }
                jobCostFlag=1
              }
            }
            // console.log("#################newMaterialObjects22222222222",newMaterialRfqObjects);
            for (let i = 0; i < newMaterialRfqObjects.length; i++) {
              const materialData = await Product.getMaterialById(newMaterialRfqObjects[i].id)
              if (!empty(materialData)) {
                let newQty = materialData[0].qty - newMaterialRfqObjects[i].quantity
                // console.log("#############MaterialData", materialData[0]);
                // console.log("#############newMaterialObjects", newMaterialRfqObjects[i]);
                // console.log("#############newQty2", newQty);
                if (newQty >= 0) {
                  let totalCost = newMaterialRfqObjects[i].unit_cost * newQty
                  let data = {};
                  data['qty'] = newQty
                  data['total_cost'] = parseInt(totalCost)
                  let jobCostData = {
                    product_id: newMaterialRfqObjects[i].id,
                    project_id: result.id,
                    quantity: reqresult[0]?.allInstallationCompleted ? newMaterialRfqObjects[i].quantity : 0,
                    unit_price: newMaterialRfqObjects[i].unit_cost,
                    total: reqresult[0]?.allInstallationCompleted ? newMaterialRfqObjects[i].total_cost : 0,
                    // quantity: newMaterialRfqObjects[i].quantity,
                    // unit_price: newMaterialRfqObjects[i].unit_cost,
                    // total: newMaterialRfqObjects[i].total_cost,
                    actual_quantity: newMaterialRfqObjects[i].quantity,
                    actual_unit_price: newMaterialRfqObjects[i].unit_cost,
                    actual_total: newMaterialRfqObjects[i].total_cost,
                    recipe: 0,
                    recipe_name: newMaterialRfqObjects[i].recipeName,
                    timestamp: moment().unix(),
                    added_by: userId,
                    // customer_order_product_id: newMaterialRfqObjects[i]?.customer_order_product_id,
                  };
                  if (jobCostData?.actual_quantity > 0) {
                    insertedstatus = await JobCost.addJobCost(jobCostData);
                    console.log("#####insertedStatus 4", jobCostData);

                    reduceStockResult = await Product.updateMaterialStock(newMaterialRfqObjects[i].id, data)
                    // console.log("#####reduceStockResult", reduceStockResult);
                  }
                } else {
                  console.log(`Stock is lesser than entered qty 2`);
                  // reply.send({ status: 0, message: `Stock is lesser than entered qty for ${materialData[0].name} (${materialData[0].barcode})`, resultId: materialData[0].id });
                }
              }
            }
          }

          if(requestData[0]['job_category_name'] != 'Time & Material'){
            for (let i = 0; i < newMaterialObjects?.length; i++) {
              const materialData = await Product.getMaterialById(newMaterialObjects[i].id)
              if (!empty(materialData)) {
                let newQty = materialData[0].qty + newMaterialObjects[i].quantity
                // console.log("#############MaterialData", materialData[0]);
                // console.log("#############newMaterialObjects", newMaterialObjects[i]);
                // console.log("#############newQty1", newQty);
                if (newQty >= 0) {
                  let totalCost = newMaterialObjects[i].unit_cost * newQty
                  let data = {};
                  data['qty'] = newQty
                  data['total_cost'] = parseInt(totalCost)
                  const reduceStockResultNew = await Product.updateMaterialStock(newMaterialObjects[i].id, data)
                } else {
                  console.log(`Stock is lesser than entered qty 3`);
                  // reply.send({ status: 0, message: `Stock is lesser than entered qty for material (${materialData[0].barcode})`, resultId: materialData[0].id });
                }
              }
            }
  
            for (let i = 0; i < newMaterialRfqObjects?.length; i++) {
              const materialData = await Product.getMaterialById(newMaterialRfqObjects[i].id)
              if (!empty(materialData)) {
                let newQty = materialData[0].qty + newMaterialRfqObjects[i].quantity
                // console.log("#############MaterialData", materialData[0]);
                // console.log("#############newMaterialObjects", newMaterialRfqObjects[i]);
                // console.log("#############newQty2", newQty);
                if (newQty >= 0) {
                  let totalCost = newMaterialRfqObjects[i].unit_cost * newQty
                  let data = {};
                  data['qty'] = newQty
                  data['total_cost'] = parseInt(totalCost)
                  const reduceStockResultNew = await Product.updateMaterialStock(newMaterialRfqObjects[i].id, data)
                  }
              } else {
                  console.log(`Stock is lesser than entered qty 4`);
                  // reply.send({ status: 0, message: `Stock is lesser than entered qty for ${materialData[0].name} (${materialData[0].barcode})`, resultId: materialData[0].id });
              }
            }
          }
       
          emitProgress('sending_notifications', `Sending job creation notifications...`, 80);
          
          let notificationMsg = `New Job "${projectData.name}" has been added.`;
          let notificationUrl = "Jobs";
          
          emitProgress('sending_user_notifications', `Sending notifications to job creator...`, 81);
          const incrementResult_User = await Admin.incrementField(
            projectData.added_by,
            "noti_count",
            1
          );
          let notificationData_user = {
            user_id: projectData.added_by,
            url: notificationUrl,
            timestamp: currentDate,
            message: notificationMsg,
            status: 1,
            from_id: projectData.added_by,
          };
          const addNotificationResult_user = await Admin.addNotification( notificationData_user );

          emitProgress('sending_admin_notifications', `Sending notifications to administrators...`, 82);
          // Alert to all admins of new added project
          let adminIds = await Admin.getAllAdminIds();
          let notificationData = {};
          for (const adminId of adminIds) {
            if (adminId != projectData.added_by) {
              const incrementResult = await Admin.incrementField(
                adminId,
                "noti_count",
                1
              );

              notificationData = {
                user_id: adminId,
                url: notificationUrl,
                timestamp: currentDate,
                message: notificationMsg,
                status: 1,
                from_id: projectData.added_by,
              };

              const addNotificationResult = await Admin.addNotification(
                notificationData
              );

            }
          }


          // }
          /////

          emitProgress('updating_request_status', `Updating status of sales order to Inproduction...`, 83);
          
          const statusUpdate = await Request.updateRequets(requestId, { status: 4,timestamp : currentTime });
          if (jobCostFlag === 1) {
            const resultStatus = await Project.updateProject(result.id, { jobcost: 1, notifyJobcost });
          }
          
          emitProgress('job_creation_complete', `Job created successfully!`, 90);
        }
      }
      else {
        emitProgress('updating_existing_project', `Updating existing job...`, 40);
        
        console.log('gggggggggggggggggg',projectData)
        if (projectData && projectData.status === 1) {
          projectData.change_order = 1
        }
        let result = await Project.updateProject(checkProjectExist, projectData)
        let statusUpdate = await Request.updateRequets(requestId, { status: 4 ,timestamp : currentTime });
        let projects = await Project.getProjectById(checkProjectExist);
        let allProducts = await Product.getAllProductsBasedOnRequest(requestId)
        console.log('allProductssssssss', allProducts)
        let allServices = await Product.getAllServicesBasedOnRequest(requestId,false);
        let taskUid = await ProjectTask.getSubTaskUid(checkProjectExist);
        let materialData = await Product.getMaterialDataFromRequestId(requestId)
        
        emitProgress('processing_existing_project_data', `Processing existing job data...`, 50);

          
          // let filteredMaterialDataForQb = materialData.flat().filter(item => item?.isReceived == 1);
            
          // try {
          //   let response = await createJournalEntryForJobStarted(filteredMaterialDataForQb, projectData?.name)
          // } catch (error) {
          //   console.log("Error in QuickBooks API: ", error);
          // }


        emitProgress('updating_attachments', `Updating job attachments...`, 55);
        
        let allReqAttachments = await Request.getAllRequestAttachments(requestId)

        const attachmentDeleted = await ProjectTask.deleteProjectAttachments(checkProjectExist)
        
        const urlObjects = [];
        allReqAttachments?.forEach(attachment => {
          if(attachment?.approved === 1){
            const isImage = attachment?.file_type?.startsWith('image/') || attachment?.file_type == 'img';
            const isVideo = attachment?.file_type?.startsWith('video/');
            const isPdf = attachment?.file_type === 'application/pdf';
            let attachmentExtension = null
            if(isImage){
              attachmentExtension = 'image'
            }else if(isVideo){
              attachmentExtension = 'video'
            }else{
              attachmentExtension = 'other'
            }
            urlObjects.push({
                url: attachment.name,
                file_type:attachment?.file_type || 'img',
                extension: attachmentExtension
            });
          }
        });
    
        // console.log("$$$$$$$$$$URL",urlObjects);
        for(let i=0; i<urlObjects.length; i++){
          let data={}
          if(urlObjects[i]?.url){
              data.userid=userId,
              data.project_id=checkProjectExist,
              data.file_url=urlObjects[i].url
              data.file_type=urlObjects[i].file_type
              data.extension=urlObjects[i].extension,
              data.timestamp=moment().unix()
              let inserted=await ProjectTask.addProjectAttachments(data)
          }
        }

        emitProgress('updating_team_members', `Updating job team members...`, 60);
        
        let deletedProjectMembers = await Project.deleteAllProjectMember(checkProjectExist);
        console.log('$$$$$$$$$$$deletedProjectMembers', deletedProjectMembers);
        let workFlowIDs = ""
        let skillSet = []
        // reply.send({allProducts:allProducts})
        for (const product of allProducts) {
          if (product?.workflow[0]){
            if (product.workflow[0]?.workflow_id) {
              skillSet.push(await Product.getWorkflowById(product.workflow[0]?.workflow_id))
            }
          }

          const allSkillIds = skillSet
            .flatMap(workflow => workflow.map(stage => stage.skill_ids?.split(',')))
            .flat()
          let uniqueSkillIds = [...new Set(allSkillIds)];
          uniqueSkillIds = uniqueSkillIds?.filter(skillId => skillId !== "" && skillId !== undefined);
          let uniqueSkillIdsString = uniqueSkillIds?.join(',');
          // uniqueSkillIdsString = uniqueSkillIdsString?.slice(0, -1);
          uniqueSkillIdsString=uniqueSkillIdsString.replace(/,$/, '');
          let projectManagers=[]
          if(!empty(uniqueSkillIdsString)){
           projectManagers = await Project.getTeamMembersBasedOnSkills(uniqueSkillIdsString, 14)
          }
         
          let pm = {}
          for (const projectManager of projectManagers) {

            pm = {
              project_id: checkProjectExist,
              staff_id: !empty(projectManager["id"]) ? projectManager["id"] : NULL,
              partner_id: null,
              contact_id: null,
              lead_id: null,

            };
            let alreadyExisting = await Project.projectMemberExist(pm);

            if (!alreadyExisting) {
              emitProgress('adding_existing_project_members', `Adding existing job members for ${product.product_name}...`, 61);
              let insertId = await Project.addProjectMember(pm);
              emitProgress('sending_existing_member_notifications', `Sending notifications to existing job members...`, 62);
              let notificationMsg = `New Job "${projectData.name}" has been added.`;
              let notificationUrl = "Jobs";
              const incrementResult_User = await Admin.incrementField(
                projectManager.user_id,
                "noti_count",
                1
              );
              let notificationData_user = {
                user_id: projectManager.user_id,
                url: notificationUrl,
                timestamp: currentDate,
                message: "has assigned you to the job: " + projectData.name,
                status: 1,
                from_id: projectData.added_by,
              };

              const addNotificationResult_user = await Admin.addNotification(
                notificationData_user
              );
            }

          }

        }
        let allProjectTask = await ProjectTask.getAllProjectTasks(checkProjectExist, userType = 1, userId = 1)

        // // Loop through each task
        // const promises = allProjectTask.map(async task => {
        //   if (task.from_product == 1) {
        //     const taskNameExists = allProducts.some(product => ((product.product_name.includes(task.name)) && (product.product_index == task.order_id) && (product.location_id == task.request_location_id)));

        //     if (!taskNameExists) {
        //       await ProjectTask.updateProjectTask({ status: 0 }, task.ID);
        //       await ProjectTask.updateProjectSubTaskByTaskId({ status: 0 }, task.ID);
        //     }
        //   }
        // });

        // await Promise.all(promises);

        const filteredTasks = allProjectTask?.filter(item => item.product_id != null && item.service_id == null);
        const deletedTasks = await ProjectTask.deleteProjectTaskByProjectId(checkProjectExist);
        if(filteredTasks?.length > 0){
          for (const task of filteredTasks){
            const deletedSubTaskAndMembers = await ProjectTask.deleteProjectSubTasksAndMembers(task.ID);
            const deletedTaskAttachments= await ProjectTask.deleteProjectTaskAttachmentByTaskId(task.ID);
          }
        }

        // console.log('allProductsallProductsallProducts', allProducts);
        emitProgress('creating_existing_tasks', `Creating existing job tasks...`, 63);
        for (const product of allProducts) {
          emitProgress('creating_existing_task', `Creating existing task: ${product.product_name}...`, 64);
          const projectTask = {
            name: product.product_name,
            project_id: checkProjectExist,
            timestamp: moment().unix(),
            userid: projectData.added_by,
            start_date: projectData.start_date || null,
            due_date: projectData.installation_date ? projectData.installation_date : null,
            task_uid: taskUid,
            complete:reqresult[0]?.allInstallationCompleted ? 100 : 0,
            complete_sync: 1,
            status: reqresult[0]?.allInstallationCompleted ? 3 : 1,
            cover_image: product.product_design || projects.cover_image || null,
            product_id: product?.product_id ? parseInt(product?.product_id) : null,
            // cover_image: projects.cover_image || "null",
            priority: 1,
            reporter_id: projectData.added_by,
            description: "",
            project_task_uid: "",
            order_id: product.product_index,
            customer_order_product_id:product?.id,
            request_location_id:product?.location_id,
          };

          // let checkTaskExisting = await Request.checkExistingTask(product.product_name, checkProjectExist, product.product_index);

          // if (!checkTaskExisting) {

            const projectTaskId = await ProjectTask.addProjectTask(projectTask);
            let task_uid = await ProjectTask.getSubTaskUid(projectTaskId);
            if(product?.inhouse === 1 && product?.isTrafficCatalog !== 1 || product?.inhouse !== 1){
              emitProgress('creating_existing_subtasks', `Creating existing subtasks for ${product.product_name}...`, 65);
              for (const workflow of product.workflow) {
                emitProgress('creating_existing_subtask', `Creating existing subtask: ${workflow.name}...`, 66);
                const subTask = {
                  name: workflow.name,
                  task_id: projectTaskId,
                  skill_ids: workflow.skill_ids,
                  timestamp: moment().unix(),
                  userid: projectData.added_by,
                  start_date: projectData.start_date || null,
                  due_date: projectData.installation_date ? projectData.installation_date : null,
                  task_uid: task_uid,
                  complete: reqresult[0]?.allInstallationCompleted ? 100 :0,
                  priority: 1,
                  status: reqresult[0]?.allInstallationCompleted ? 3 : 1,
                  reporter_id: projectData.added_by,
                  description: workflow.message,
                };
                // let checkSubTaskExisting=await Request.checkExistingSubTask(workflow.name,projectTaskId);
  
                // if(!checkSubTaskExisting){
                const projectSubTaskId = await ProjectTask.addProjectSubTask(subTask);
                let projectManagers=[]
                if(!empty(workflow.skill_ids)){
                 projectManagers = await Project.getTeamMembersBasedOnSkills(workflow.skill_ids, 14)
                }
                
                emitProgress('assigning_existing_subtask_members', `Assigning members to existing subtask: ${workflow.name}...`, 67);
                let notificationMsg = `New Job "${projectData.name}" has been added.`;
                let notificationUrl = "Jobs";
                let pm = {}
                for (const projectManager of projectManagers) {
  
                  pm = {
                    project_id: checkProjectExist,
                    subtask_id: projectSubTaskId,
                    staff_id: !empty(projectManager["id"]) ? projectManager["id"] : NULL,
                    partner_id: null,
                    contact_id: null,
                    lead_id: null,
  
                  };
  
                  let insertId = await ProjectTask.AddMembersToSubTask(pm);
                  emitProgress('sending_existing_subtask_notifications', `Sending notifications to existing subtask members...`, 68);
                  let notificationData_user = {
                    user_id: projectManager.user_id,
                    url: notificationUrl,
                    timestamp: currentDate,
                    message: "has assigned you to the job: " + projectData.name,
                    status: 1,
                    from_id: projectData.added_by,
                  };
  
                  const addNotificationResult_user = await Admin.addNotification(
                    notificationData_user
                  );
                }
              }
            }
        }

        if(allServices?.length > 0){
          emitProgress('processing_existing_services', `Processing existing services...`, 69);
          let skillSet = []
          for (const product of allServices) {
            if (product?.workflow[0]){
              if (product.workflow[0]?.workflow_id) {
                skillSet.push(await Product.getWorkflowById(product.workflow[0]?.workflow_id))
              }
            }
  
            const allSkillIds = skillSet
              .flatMap(workflow => workflow.map(stage => stage.skill_ids?.split(',')))
              .flat()
            let uniqueSkillIds = [...new Set(allSkillIds)];
            uniqueSkillIds = uniqueSkillIds?.filter(skillId => skillId !== "" && skillId !== undefined);
            let uniqueSkillIdsString = uniqueSkillIds?.join(',');
            // uniqueSkillIdsString = uniqueSkillIdsString?.slice(0, -1);
            uniqueSkillIdsString=uniqueSkillIdsString.replace(/,$/, '');
            let projectManagers=[]
            if(!empty(uniqueSkillIdsString)){
             projectManagers = await Project.getTeamMembersBasedOnSkills(uniqueSkillIdsString, 14)
            }
           
            // let deleted = await Project.deleteAllProjectMember(checkProjectExist);
            let pm = {}
            for (const projectManager of projectManagers) {
  
              pm = {
                project_id: checkProjectExist,
                staff_id: !empty(projectManager["id"]) ? projectManager["id"] : NULL,
                partner_id: null,
                contact_id: null,
                lead_id: null,
              };
              let alreadyExisting = await Project.projectMemberExist(pm);
  
              if (!alreadyExisting) {
                emitProgress('adding_existing_service_members', `Adding existing service members for ${product.service_name}...`, 70);
                let insertId = await Project.addProjectMember(pm);
                emitProgress('sending_existing_service_notifications', `Sending notifications to existing service members...`, 71);
                let notificationMsg = `New Job "${projectData.name}" has been added.`;
                let notificationUrl = "Jobs";
                const incrementResult_User = await Admin.incrementField(
                  projectManager.user_id,
                  "noti_count",
                  1
                );
                let notificationData_user = {
                  user_id: projectManager?.user_id || userId,
                  url: notificationUrl,
                  timestamp: currentDate,
                  message: "has assigned you to the job: " + projectData.name,
                  status: 1,
                  from_id: projectData.added_by,
                };
  
                const addNotificationResult_user = await Admin.addNotification(
                  notificationData_user
                );
              }
  
            }
  
          }
          let allProjectTask = await ProjectTask.getAllProjectTasks(checkProjectExist, userType = 1, userId = 1)
  
          // Loop through each task
          const promises = allProjectTask.map(async task => {
            if (task.from_product == 1 && task.service_id != null) {
              const taskNameExists = allServices.some(product => ((product.service_name.includes(task.name)) && (product.product_index == task.order_id)));
  
              if (!taskNameExists) {
                await ProjectTask.updateProjectTask({ status: 0 }, task.ID);
                await ProjectTask.updateProjectSubTaskByTaskId({ status: 0 }, task.ID);
              }
            }
          });
  
          await Promise.all(promises);
  
          emitProgress('creating_existing_service_tasks', `Creating existing service tasks...`, 72);
          for (const product of allServices) {
            if(requestData[0]?.project_category !==11 && product?.labour_cost_service_id == 10) continue
            emitProgress('creating_existing_service_task', `Creating existing service task: ${product.service_name}...`, 73);
            const projectTask = {
              name: product.service_name,
              project_id: checkProjectExist,
              timestamp: moment().unix(),
              userid: projectData.added_by,
              start_date: projectData.start_date || null,
              due_date: projectData.installation_date ? projectData.installation_date : null,
              task_uid: taskUid,
              complete: reqresult[0]?.allInstallationCompleted ? 100 :0,
              complete_sync: 1,
              status: reqresult[0]?.allInstallationCompleted ? 3 : 1,
              cover_image: product.product_design || projects.cover_image || null,
              product_id: product?.product_id ? parseInt(product?.product_id) : null,
              service_id: product?.labour_cost_service_id ? parseInt(product?.labour_cost_service_id) : null,
              // cover_image: projects.cover_image || "null",
              priority: 1,
              reporter_id: projectData.added_by,
              description: "",
              project_task_uid: "",
              order_id: product.product_index,
              customer_order_product_id:product?.id

            };
  
  
            let checkTaskExisting = await Request.checkExistingTask(product.service_name, checkProjectExist, product.product_index);
  
            if (!checkTaskExisting) {
              emitProgress('creating_existing_service_subtasks', `Creating existing service subtasks for ${product.service_name}...`, 74);
              const projectTaskId = await ProjectTask.addProjectTask(projectTask);
              let task_uid = await ProjectTask.getSubTaskUid(projectTaskId);
              for (const workflow of product.workflow) {
                emitProgress('creating_existing_service_subtask', `Creating existing service subtask: ${workflow.name}...`, 75);
                const subTask = {
                  name: workflow.name,
                  task_id: projectTaskId,
                  skill_ids: workflow.skill_ids,
                  timestamp: moment().unix(),
                  userid: projectData.added_by,
                  start_date: projectData.start_date || null,
                  due_date: projectData.installation_date ? projectData.installation_date : null,
                  task_uid: task_uid,
                  complete: reqresult[0]?.allInstallationCompleted ? 100 :0,
                  priority: 1,
                  status: reqresult[0]?.allInstallationCompleted ? 3 : 1,
                  reporter_id: projectData.added_by,
                  description: workflow.message,
                };
                // let checkSubTaskExisting=await Request.checkExistingSubTask(workflow.name,projectTaskId);
  
                // if(!checkSubTaskExisting){
                const projectSubTaskId = await ProjectTask.addProjectSubTask(subTask);
                let projectManagers=[]
                if(!empty(workflow.skill_ids)){
                 projectManagers = await Project.getTeamMembersBasedOnSkills(workflow.skill_ids, 14)
                }
                
                emitProgress('assigning_existing_service_subtask_members', `Assigning members to existing service subtask: ${workflow.name}...`, 76);
                let notificationMsg = `New Job "${projectData.name}" has been added.`;
                let notificationUrl = "Jobs";
                let pm = {}
                for (const projectManager of projectManagers) {
  
                  pm = {
                    project_id: checkProjectExist,
                    subtask_id: projectSubTaskId,
                    staff_id: !empty(projectManager["id"]) ? projectManager["id"] : NULL,
                    partner_id: null,
                    contact_id: null,
                    lead_id: null,
  
                  };
  
                  let insertId = await ProjectTask.AddMembersToSubTask(pm);
                  emitProgress('sending_existing_service_subtask_notifications', `Sending notifications to existing service subtask members...`, 77);
                  let notificationData_user = {
                    user_id: projectManager?.user_id || userId,
                    url: notificationUrl,
                    timestamp: currentDate,
                    message: "has assigned you to the job: " + projectData.name,
                    status: 1,
                    from_id: projectData.added_by,
                  };
  
                  const addNotificationResult_user = await Admin.addNotification(
                    notificationData_user
                  );
  
                }
  
                // }
  
              }
            }
            else {
              emitProgress('updating_existing_service_subtasks', `Updating existing service subtasks for ${product.service_name}...`, 78);
              for (const workflow of product.workflow) {
                let task_uid = await ProjectTask.getSubTaskUid(checkTaskExisting);
  
                const subTask = {
                  name: workflow.name,
                  task_id: checkTaskExisting,
                  skill_ids: workflow.skill_ids,
                  timestamp: moment().unix(),
                  userid: projectData.added_by,
                  start_date: projectData.start_date || null,
                  due_date: projectData.installation_date ? projectData.installation_date : null,
                  task_uid: task_uid,
                  complete: 0,
                  priority: 1,
                  status: 1,
                  reporter_id: projectData.added_by,
                  description: workflow.message,
                };
                let checkSubTaskExisting = await Request.checkExistingSubTask(workflow.name, checkTaskExisting);
  
                if (!checkSubTaskExisting) {
                  emitProgress('creating_additional_service_subtask', `Creating additional service subtask: ${workflow.name}...`, 79);
                  const projectSubTaskId = await ProjectTask.addProjectSubTask(subTask);
                  let projectManagers=[]
                  if(!empty(workflow.skill_ids)){
                   projectManagers = await Project.getTeamMembersBasedOnSkills(workflow.skill_ids, 14)
                  }
                  
                  emitProgress('assigning_additional_service_subtask_members', `Assigning members to additional service subtask: ${workflow.name}...`, 80);
                  let notificationMsg = `New Job "${projectData.name}" has been added.`;
                  let notificationUrl = "Jobs";
                  let pm = {}
                  for (const projectManager of projectManagers) {
  
                    pm = {
                      project_id: checkProjectExist,
                      subtask_id: projectSubTaskId,
                      staff_id: !empty(projectManager["id"]) ? projectManager["id"] : NULL,
                      partner_id: null,
                      contact_id: null,
                      lead_id: null,
  
                    };
  
                    let insertId = await ProjectTask.AddMembersToSubTask(pm);
                    emitProgress('sending_additional_service_subtask_notifications', `Sending notifications to additional service subtask members...`, 81);
                    let notificationData_user = {
                      user_id: projectManager.user_id,
                      url: notificationUrl,
                      timestamp: currentDate,
                      message: "has assigned you to the job: " + projectData.name,
                      status: 1,
                      from_id: projectData.added_by,
                    };
  
                    const addNotificationResult_user = await Admin.addNotification(
                      notificationData_user
                    );
  
                  }
  
                  // }
  
                }
              }
            }
            //  pending subtasks update-----------------
          }
        }

        let allTasks = await ProjectTask.getAllProjectTasks(checkProjectExist, userType = 1, userId = 1);
        allCreatedTask = await ProjectTask.getAllProjectTasks(checkProjectExist, userType = 1, userId = 1)
        
        if(((allTasks?.length <= 0 && requestData[0]['noServiceNoTask'] == 1) || (allCreatedTask?.length <= 0)) && projectCategoryId !== 12){
          // if (movedToProduction) {
          //   const resultStatus = await Project.updateProject(checkProjectExist, { status: 1, completion: 100 });

          // } else {
          //   const resultStatus = await Project.updateProject(checkProjectExist, { status: 3, completion: 100 });

          // } // uncomment this if any issues occure when edit from installs 
          const resultStatus = await Project.updateProject(checkProjectExist, { status: 3, completion: 100 });
          directInstalls = true
        }
        if (allCreatedTask?.length <= 0 && projectCategoryId !== 12) {
          console.log(`⚠️ No installation created for project ${checkProjectExist}, but tasks exist.`);
          
          await Project.updateProject(checkProjectExist, { noServiceNoTask: 0 });
          await Request.updateRequestById(requestId, { noServiceNoTask: 0 });

          // notify
          let notificationMsg = `Sales Order "${projectData.name}" has been moved to 'Invoicing' because all tasks and installtions are off.`;
          let notificationUrl = "official-request";
          // Increment notification count
          const incrementResult_User = await Admin.incrementField(userId, "noti_count", 1);
          let notificationData_user = {
            user_id: userId,
            url: notificationUrl,
            timestamp: currentDate,
            message: notificationMsg,
            status: 1,
            from_id: userId,
          };
          const addNotificationResult_user = await Admin.addNotification(notificationData_user);

          // Alert to all admins of new added project

          let adminIds = await Admin.getAllAdminIds();
          let notificationData = {};
          for (const adminId of adminIds) {
            if (adminId != userId) {
              const incrementResult = await Admin.incrementField(adminId, "noti_count", 1);
              notificationData = {
                  user_id: adminId,
                  url: notificationUrl,
                  timestamp: currentDate,
                  message: notificationMsg,
                  status: 1,
                  from_id: userId,
              };
              const addNotificationResult = await Admin.addNotification(notificationData);
            }
          }
        }

        emitProgress('processing_existing_job_cost', `Processing existing job cost calculations...`, 80);
        
        /************  Jobcost ******************** */
        let flag = 0
        let recipe = 1
        let allMaterials = await Product.getAllMaterials(true)
        let allProductsData = []
        console.log('requestData[0]?.project_category',requestData[0]?.project_category)
        if (requestData[0]?.project_category === 15) {
          allProductsData = await Product.getAllLocations(requestId)
        }else{
          allProductsData = await Product.getNonStockProducts(requestId)
        }
        console.log('allProductsData--------1',allProductsData)
        let allRecipies = []
        if(allProductsData) { //} && requestData[0]?.job_category_name != 'Time & Material'){
          allRecipies = await Recipe.getAllReceipesByProjectId(allProductsData,requestId);
        }
        let selectedMaterials = await JobCost.fetchAllJobCostByProjectId(checkProjectExist, userId = 1, recipe);
        let reduceStockResult, deleted;
        if (selectedMaterials.length > 0) {
          for (const element of selectedMaterials) {

            const material = await Product.getMaterialById(element.product_id)

            if (!empty(material)) {
              //const matchingValue = newMaterialObjects?.find(val => val?.id === material[0]?.id);
              // let quantity = Math.max(element?.quantity || 0, element?.actual_quantity || 0);
              // let newQty = material[0]?.qty + quantity
              let newQty = material[0]?.qty + (element.quantity || 0)
              let totalCost = material[0]?.unit_cost * newQty
              // console.log('newQtynewQty---1',newQty)
              // if (matchingValue ? newQty >= matchingValue.quantity : true) {
              if (newQty >= parseInt(material[0].qty)) {
                const data = {
                  qty: newQty,
                  total_cost: totalCost
                };
                const updated = await Product.updateMaterialStock(element.product_id, data)
                if (updated) {
                  deleted = await JobCost.deleteJobCost(element.id, recipe);
                } else {
                  console.log('Failed to update stock');
                  reply.send({ status: 0, message: 'Failed to update stock' });
                }
              } else {
                console.log(`Stock is lesser than entered qty 5`);
                // reply.send({ status: 0, message: `Stock is lesser than entered qty for ${material[0]?.name} (${material[0].barcode})`, resultId: material[0].id });
                // return;
              }
              // } else {
                // console.log(`Stock is lesser than entered qty`);
              //   reply.send({ status: 3, message: `Stock is lesser than entered qty` });
              // }

            }
          }
          flag = 1
        }
        
        let results = await Product.getFinishedProducts(0,true);
        // console.log('results111122333', results)
        let newMaterialObjects = [];
       
        let jobCost=0;
        let notifyJobcost=""
       
        for (const recipeItem of allRecipies) {
          // let results = await Product.getFinishedProducts();
          for (const ingredient of recipeItem.ingredients) {
            let materialItems = results?.filter((mat) => mat.material_id === ingredient.product_id);
            let filteredMaterialItems = materialItems?.filter(mat => 
              (ingredient.stockType === 2 ? mat.partial === 1 : mat.partial === 0)
            );
            let quantityInInitialUnit = 0;
            quantityInInitialUnit = ingredient?.quantity;
            // if(ingredient.stockType === 1 || materialItems[0]?.unit == 4){
            //   quantityInInitialUnit = ingredient?.quantity;
            // }else{
            //   let conversionFactorToInitial = getConversionFactor(ingredient?.convertionUnit, materialItems[0]?.unit, ingredient?.width || 1);
            //   quantityInInitialUnit = ingredient?.convertionQty * conversionFactorToInitial;
            // }
            let quantityNeeded = quantityInInitialUnit * recipeItem?.quantity * recipeItem?.location_quantity;
            // let quantityNeeded = ingredient?.quantity*recipeItem?.quantity*recipeItem?.location_quantity;
            // let totalAvailableQuantity = filteredMaterialItems?.reduce((total, item) => total + item.qty, 0);
            let totalAvailableQuantity = 0;
            if(ingredient.stockType === 1){
              totalAvailableQuantity = filteredMaterialItems?.reduce((total, item) => total + item?.qty, 0);
            }else{
              totalAvailableQuantity = filteredMaterialItems?.filter((item)=>item.partial_unit==ingredient?.convertionUnit)?.reduce((total, item) => total + item?.qty, 0);
            }
            // console.log(`Converted Ingredient Quantity to Initial Unit (${ingredient.convertionUnit} → ${materialItems[0]?.unit}), width : ${ingredient?.width}:`, quantityInInitialUnit);
            // console.log('filteredMaterialItems===', filteredMaterialItems);
            // console.log('ingredient?.quantity===', ingredient?.quantity);
            // console.log('recipeItem?.quantity===', recipeItem?.quantity);
            // console.log('recipeItem?.location_quantity===', recipeItem?.location_quantity);
            // console.log('Total Available Quantity (Before Conversion) ===', totalAvailableQuantity);
            // console.log('Quantity Needed (Before Conversion) ===', quantityNeeded);
            
            // Get actual width and quantity from material stock
            // let width = ingredient?.width || 1;

            // // Convert quantityNeeded dynamically using width
            // let conversionFactor = getConversionFactor(ingredient?.convertionUnit, materialItems[0]?.unit, width);
            // let convertedQuantityNeeded = quantityNeeded * conversionFactor;

            if (totalAvailableQuantity >= quantityNeeded) {
            // if (totalAvailableQuantity >= convertedQuantityNeeded) {
              // ✅ Sufficient stock available
              // let remainingQuantity = convertedQuantityNeeded;
              let remainingQuantity = quantityNeeded;
              filteredMaterialItems?.forEach((materialItem) => {
                // Convert unit cost based on unit type
                let originalUnitCost = Number(materialItem?.unit_cost) || 0;
                // let convertedUnitCost = getConvertedUnitCost(originalUnitCost, materialItem?.unit, ingredient?.convertionUnit, width);

                // console.log(`Unit Cost (Before Conversion):`, originalUnitCost);
                // console.log(`Converted Unit Cost (${materialItems[0]?.unit} → ${ingredient.convertionUnit} at width ${width}):`, convertedUnitCost);
                // console.log(`totalAvailableQuantity :`, totalAvailableQuantity);
                // console.log(`convertedQuantityNeeded :`, convertedQuantityNeeded);
                if (remainingQuantity > 0) {
                  let quantityToPull = Math.min(remainingQuantity, materialItem.qty);
                  newMaterialObjects.push({
                    id: materialItem.id,
                    productId: materialItem.material_id,
                    quantity: quantityToPull,
                    unit_cost: materialItem?.inhouse == 0 ? 0 : originalUnitCost,
                    total_cost: materialItem?.inhouse == 0 ? 0 : parseFloat(quantityToPull) * parseFloat(originalUnitCost),
                    recipe: recipeItem.id,
                    recipeName: recipeItem.name,
                    customer_order_product_id: recipeItem?.customer_order_product_id,
                  });
                  results = results.map(item => item.id === materialItem.id ? { ...item, qty: item.qty - quantityToPull } : item);
                  remainingQuantity -= quantityToPull;
                }
              });
              console.log(`Added ${quantityNeeded} units of ${ingredient.name}`);
            } else {
              // Insufficient quantity available in existing packages, pull from additional packages
              console.log(`Insufficient quantity of ${ingredient.name} available in existing packages.3`);
              console.log(`Pulling additional ${quantityNeeded - totalAvailableQuantity} units from packages...3`);
              // Your logic to pull additional quantities from packages goes here
              // notifyJobcost+=` Insufficient quantity of ${ingredient.name} available in existing packages.`
              if(empty(notifyJobcost)){
                  notifyJobcost+=`There is insuffient quantity on materials: ${ingredient.name} (${quantityNeeded}) (${ingredient.stockType === 1 ? 'Finished' : 'Partial'})`
              }
              else{
                 notifyJobcost+=`, ${ingredient.name} (${quantityNeeded}) (${ingredient.stockType === 1 ? 'Finished' : 'Partial'})`
              }
              jobCost=1
            }
          };
        };
        
        if ((flag === 1 && deleted) || (flag === 0)) {
          for (let i = 0; i < newMaterialObjects?.length; i++) {
            const materialData = await Product.getMaterialById(newMaterialObjects[i].id)
            if (materialData) {
         
              let newQty = materialData[0].qty - newMaterialObjects[i].quantity
              // console.log('newQtynewQty---2',newQty)
              if (newQty >= 0) {
                let totalCost = newMaterialObjects[i].unit_cost * newQty
                let data = {};
                data['qty'] = newQty
                data['total_cost'] = parseInt(totalCost)
                let jobCostData = {
                  product_id: newMaterialObjects[i].id,
                  project_id: checkProjectExist,
                  quantity: reqresult[0]?.allInstallationCompleted ? newMaterialObjects[i].quantity : 0,
                  unit_price: newMaterialObjects[i].unit_cost,
                  total: reqresult[0]?.allInstallationCompleted ? newMaterialObjects[i].total_cost : 0,
                  // quantity: newMaterialObjects[i].quantity,
                  // unit_price: newMaterialObjects[i].unit_cost,
                  // total: newMaterialObjects[i].total_cost,
                  actual_quantity: newMaterialObjects[i].quantity,
                  actual_unit_price: newMaterialObjects[i].unit_cost,
                  actual_total: newMaterialObjects[i].total_cost,
                  recipe: 1,
                  recipe_name: newMaterialObjects[i].recipeName,
                  timestamp: moment().unix(),
                  added_by: userId,
                  customer_order_product_id: newMaterialObjects[i]?.customer_order_product_id,
                };
                if (jobCostData?.actual_quantity > 0) {
                  insertedstatus = await JobCost.addJobCost(jobCostData);
                  console.log("#####insertedStatus 5", jobCostData);

                  reduceStockResult = await Product.updateMaterialStock(newMaterialObjects[i].id, data);
                  // Autofill stock if stock is 0
                  // if(newQty <= 0){
                  //   if(materialData[0]?.autofill == 1){
                  //     const parentFinishedMaterial = await Product.getMaterialById(materialData[0]?.parent_finished_product_id)
                  //     console.log('parentFinishedMaterial----',parentFinishedMaterial)
                  //     if (parentFinishedMaterial.length > 0 && parentFinishedMaterial[0]?.qty > 0) {
                  //       let reducedQty = parentFinishedMaterial[0]?.qty - 1
                  //       let parentTotalCost = parentFinishedMaterial[0]?.unit_cost * reducedQty
                  //       let parentData = {
                  //         qty: reducedQty,
                  //         total_cost: parseInt(parentTotalCost)
                  //       };
                  //       const reduceFinishedProductStock = await Product.updateMaterialStock(materialData[0]?.parent_finished_product_id, parentData)
        
                  //       let addingQty = materialData[0]?.actual_qty
                  //       let totalCost = materialData[0]?.actual_total_cost
                  //       let autoFilldata = {
                  //         qty: addingQty,
                  //         total_cost: totalCost
                  //       };
                  //       console.log('autofillPartialStock--',autoFilldata);
                  //       const autofillPartialStock = await Product.updateMaterialStock(materialData[0]?.id, autoFilldata);
                  //     }
                  //   }
                  // }

                }
              } else {
                console.log(`Stock is lesser than entered qty 6`);
                // Autofill stock if stock is 0
                // if(newQty <= 0){
                //   if(materialData[0]?.autofill == 1){
                //     const parentFinishedMaterial = await Product.getMaterialById(materialData[0]?.parent_finished_product_id)
                //     console.log('parentFinishedMaterial----',parentFinishedMaterial)
                //     if (parentFinishedMaterial.length > 0 && parentFinishedMaterial[0]?.qty > 0) {
                //       let reducedQty = parentFinishedMaterial[0]?.qty - 1
                //       let parentTotalCost = parentFinishedMaterial[0]?.unit_cost * reducedQty
                //       let parentData = {
                //         qty: reducedQty,
                //         total_cost: parseInt(parentTotalCost)
                //       };
                //       const reduceFinishedProductStock = await Product.updateMaterialStock(materialData[0]?.parent_finished_product_id, parentData)
      
                //       let addingQty = materialData[0]?.actual_qty
                //       let totalCost = materialData[0]?.actual_total_cost
                //       let autoFilldata = {
                //         qty: addingQty,
                //         total_cost: totalCost
                //       };
                //       console.log('autofillPartialStock--',autoFilldata);
                //       const autofillPartialStock = await Product.updateMaterialStock(materialData[0]?.id, autoFilldata);
                //     }
                //   }
                // }
              }
            }
          }
        }
        let deleteRfpJobcost=await JobCost.deleteRfpJobcost(checkProjectExist)
        let rfpProduct=await CustomerOrderProduct.getRFPProductByRequest(requestId)
        

        for(let i=0;i<rfpProduct?.length;i++){
          let jobCostData = {
            product_id: rfpProduct[i].product_id,
            project_id: checkProjectExist,
            // quantity: 0,
            // unit_price: rfpProduct[i]?.material == 0 ? rfpProduct[i]?.vendor_price : rfpProduct[i].product_price,
            // total: 0,
            quantity: rfpProduct[i].product_qty*rfpProduct[i].location_quantity,
            unit_price:  rfpProduct[i]?.material == 0 ? rfpProduct[i]?.vendor_price : rfpProduct[i].product_price,
            total:  rfpProduct[i]?.material == 0 ? rfpProduct[i]?.vendor_price : rfpProduct[i].product_price*rfpProduct[i].product_qty*rfpProduct[i].location_quantity,
            actual_quantity: rfpProduct[i].product_qty*rfpProduct[i].location_quantity,
            actual_unit_price:  rfpProduct[i]?.material == 0 ? rfpProduct[i]?.vendor_price : rfpProduct[i].product_price,
            actual_total:  rfpProduct[i]?.material == 0 ? rfpProduct[i]?.vendor_price : rfpProduct[i].product_price*rfpProduct[i].product_qty*rfpProduct[i].location_quantity,
            recipe: 1,
            recipe_name: null,
            timestamp: moment().unix(),
            added_by: userId,
            rfp:1,
            customer_order_product_id: rfpProduct[i].id,
          };
          insertedstatus = await JobCost.addJobCost(jobCostData);
          console.log("#####insertedStatus 6", jobCostData);
        }

        let newMaterialRfqObjects = [];
        let bringStockCatalog = requestData?.[0]?.project_category == 15 ? false: true
          let materialRfq=await CustomerOrderProduct.getMaterialProductByRequest(requestId,bringStockCatalog)
          console.log("#################newMaterialObjects2222222222212", materialRfq);
          if(requestData[0]['job_category_name'] == 'Time & Material'){
            const deleted = await JobCost.deleteMaterialJobcost(checkProjectExist);
            for(let i=0; i<materialRfq.length; i++){
              let results = await Product.getFinishedProducts(materialRfq[i].product_id,true);
                // console.log('#################newMaterialObjects22222222222121', results)
                let notPartial=materialRfq[i].stock_type===1 ? true : false
                let filteredResult=[]
              if(notPartial){
                filteredResult = results?.filter(item=>item.partial == 0 )
              }else{
                filteredResult = results?.filter(item=>((item.partial == 1) && (item.partial_unit == materialRfq[i].convertion_unit)) )
              }
              if(filteredResult?.length > 0){
                console.log('results=====',results[0])
                let jobCostData = {
                  product_id: filteredResult[0].id,
                  project_id: checkProjectExist,
                  // quantity: reqresult[0]?.allInstallationCompleted ? materialRfq[i].product_qty : 0,
                  quantity: reqresult[0]?.project_category == 12 ? materialRfq[i].product_qty : 0,
                  unit_price: materialRfq[i].product_price,
                  // total: reqresult[0]?.allInstallationCompleted ? ((materialRfq[i].product_price || 0) * (materialRfq[i].product_qty || 0)) : 0,
                  total: reqresult[0]?.project_category == 12? ((materialRfq[i].product_price || 0) * (materialRfq[i].product_qty || 0)) : 0,
                  // quantity: materialRfq[i].quantity,
                  // unit_price: materialRfq[i].unit_cost,
                  // total: materialRfq[i].total_cost,
                  actual_quantity: materialRfq[i].product_qty,
                  actual_unit_price: materialRfq[i].product_price,
                  actual_total: materialRfq[i].product_total,
                  recipe: 0,
                  recipe_name: '',
                  timestamp: moment().unix(),
                  added_by: userId,
                  customer_order_product_id: materialRfq[i].id,
                };
                insertedstatus = await JobCost.addJobCost(jobCostData);
                console.log("#####insertedStatus 7", jobCostData);
              } else {
                console.log(`Insufficient quantity of ${materialRfq[i].product_name} available in existing packages.23`);
                if(empty(notifyJobcost)){
                  notifyJobcost+=`There is insuffient quantity on materials: ${materialRfq[i].product_name} (Finished)`
                }
                else{
                  notifyJobcost+=` ,${materialRfq[i].product_name} (Finished)`
                }
                jobCostFlag=1
              }
            }
          }else{
            for(let i=0;i<materialRfq.length; i++){
              // let results = await Product.getFinishedProducts();
               let notPartial=materialRfq[i].stock_type===1 ? true : false
              //  console.log('materialItem22222222222s', notPartial)
              //  console.log('materialItem22222222222s', results)
              if(notPartial){
                filteredResult = results?.filter(item=>item.partial == 0 )
              }else{
                filteredResult = results?.filter(item=>((item.partial == 1) && (item.partial_unit == materialRfq[i].convertion_unit)) )
              }
              // console.log('results===== 12',results)
              let quantityNeeded = materialRfq[i].product_qty;
              let materialItems = filteredResult.filter((mat) => mat.material_id === materialRfq[i].product_id);
              // console.log('materialItem22222222222s', materialItems)
              // console.log('materialItem22222222222s', quantityNeeded)
              let totalAvailableQuantity = materialItems.reduce((total, item) => total + item.qty, 0);

              if (totalAvailableQuantity >= quantityNeeded) {
                // Sufficient quantity available in existing packages
                let remainingQuantity = quantityNeeded;
                // console.log('materialRfq---',materialRfq)
                // console.log('materialItems---',materialItems)
                materialItems.forEach((materialItem) => {
                  if (remainingQuantity > 0) {
                    let quantityToPull = Math.min(remainingQuantity, materialItem.qty);
                    newMaterialRfqObjects.push({
                      id: materialItem.id,
                      productId: materialItem.material_id,
                      quantity: quantityToPull,
                      unit_cost: materialItem?.inhouse == 0 ? 0 : materialItem.unit_cost,
                      total_cost: materialItem?.inhouse == 0 ? 0 : parseFloat(quantityToPull) * parseFloat(materialItem.unit_cost),
                      recipe:0,
                      recipeName: null,
                    });
                    filteredResult = filteredResult.map(item => item.id === materialItem.id ? { ...item, qty: item.qty - quantityToPull } : item);
                    remainingQuantity -= quantityToPull;
                  }
                });
              
              } else {
                // Insufficient quantity available in existing packages, pull from additional packages
                console.log(`Insufficient quantity of ${materialRfq[i].product_name} available in existing packages.4`);
                console.log(`Pulling additional ${quantityNeeded - totalAvailableQuantity} units from packages...4`);
                // Your logic to pull additional quantities from packages goes here
              
              
                if(empty(notifyJobcost)){
                    notifyJobcost+=`There is insuffient quantity on materials: ${materialRfq[i].product_name} (${quantityNeeded}) (Finished)`
                }
                else{
                  notifyJobcost+=` ,${materialRfq[i].product_name} (${quantityNeeded}) (Finished)`
                }
                
              
                jobCost=1
                jobCostFlag=1
              }
            }
            for (let i = 0; i < newMaterialRfqObjects.length; i++) {
              const materialData = await Product.getMaterialById(newMaterialRfqObjects[i].id)
              if (!empty(materialData)) {
                let newQty = materialData[0].qty - newMaterialRfqObjects[i].quantity
                // console.log('newQtynewQty---3',newQty)
                if (newQty >= 0) {
                  let totalCost = newMaterialRfqObjects[i].unit_cost * newQty
                  let data = {};
                  data['qty'] = newQty
                  data['total_cost'] = parseInt(totalCost)
                  let jobCostData = {
                    product_id: newMaterialRfqObjects[i].id,
                    project_id: checkProjectExist,
                    quantity: reqresult[0]?.allInstallationCompleted ? newMaterialRfqObjects[i].quantity : 0,
                    unit_price: newMaterialRfqObjects[i].unit_cost,
                    total: reqresult[0]?.allInstallationCompleted ? newMaterialRfqObjects[i].total_cost : 0,
                    // quantity: newMaterialRfqObjects[i].quantity,
                    // unit_price: newMaterialRfqObjects[i].unit_cost,
                    // total: newMaterialRfqObjects[i].total_cost,
                    actual_quantity: newMaterialRfqObjects[i].quantity,
                    actual_unit_price: newMaterialRfqObjects[i].unit_cost,
                    actual_total: newMaterialRfqObjects[i].total_cost,
                    recipe: 0,
                    recipe_name: newMaterialRfqObjects[i].recipeName,
                    timestamp: moment().unix(),
                    added_by: userId,
                    // customer_order_product_id: newMaterialRfqObjects[i]?.customer_order_product_id,
                  };
                  if (jobCostData?.actual_quantity > 0) {
                    insertedstatus = await JobCost.addJobCost(jobCostData);
                    console.log("#####insertedStatus 8", jobCostData);


                    reduceStockResult = await Product.updateMaterialStock(newMaterialRfqObjects[i].id, data)

                  }
                } else {
                  console.log(`Stock is lesser than entered qty 7`);
                  
                  // reply.send({ status: 0, message: `Stock is lesser than entered qty for ${materialData[0].name} (${materialData[0].barcode})`, resultId: materialData[0].id });
                }
              }
            }
          }

          if(requestData[0]['job_category_name'] != 'Time & Material'){
            if ((flag === 1 && deleted) || (flag === 0)) {
              for (let i = 0; i < newMaterialObjects?.length; i++) {
                const materialData = await Product.getMaterialById(newMaterialObjects[i].id)
                if (materialData) {
                  let newQty = materialData[0].qty + newMaterialObjects[i].quantity
                  // console.log('newQtynewQty---4',newQty)
                  if (newQty >= 0) {
                    let totalCost = newMaterialObjects[i].unit_cost * newQty
                    let data = {};
                    data['qty'] = newQty
                    data['total_cost'] = parseInt(totalCost)
                    const reduceStockResultNew = await Product.updateMaterialStock(newMaterialObjects[i].id, data);
                  } else {
                    console.log(`Stock is lesser than entered qty 8`);
                  }
                }
              }
            }
            for (let i = 0; i < newMaterialRfqObjects.length; i++) {
              const materialData = await Product.getMaterialById(newMaterialRfqObjects[i].id)
              if (!empty(materialData)) {
                let newQty = materialData[0].qty + newMaterialRfqObjects[i].quantity
                console.log('newQtynewQty---5',newQty)
                if (newQty >= 0) {
                  let totalCost = newMaterialRfqObjects[i].unit_cost * newQty
                  let data = {};
                  data['qty'] = newQty
                  data['total_cost'] = parseInt(totalCost)
                  reduceStockResult = await Product.updateMaterialStock(newMaterialRfqObjects[i].id, data)
                } else {
                  console.log(`Stock is lesser than entered qty 9`);
                }
              }
            }
          }

        let notificationMsg = ` Job "${projectData.name}" has been updated.`;
        let notificationUrl = "Jobs";
        const incrementResult_User = await Admin.incrementField(
          projectData.added_by,
          "noti_count",
          1
        );
        let notificationData_user = {
          user_id: projectData.added_by,
          url: notificationUrl,
          timestamp: currentDate,
          message: notificationMsg,
          status: 1,
          from_id: projectData.added_by,
        };
        const addNotificationResult_user = await Admin.addNotification(
          notificationData_user
        );

        // Alert to all admins of new added project
        let adminIds = await Admin.getAllAdminIds();
        let notificationData = {};
        for (const adminId of adminIds) {
          if (adminId != projectData.added_by) {
            const incrementResult = await Admin.incrementField(
              adminId,
              "noti_count",
              1
            );

            notificationData = {
              user_id: adminId,
              url: notificationUrl,
              timestamp: currentDate,
              message: notificationMsg,
              status: 1,
              from_id: projectData.added_by,
            };

            const addNotificationResult = await Admin.addNotification(
              notificationData
            );

          }
        }
        emitProgress('finalizing_existing_project', `Finalizing existing job updates...`, 82);
        
        let allTask = await ProjectTask.getAllProjectTasks(checkProjectExist, userType = 1)
        let firstTaskStatus = allTask[0]?.status

        const sumCompletionTask = allTask?.reduce((acc, task) => acc + task?.complete, 0);
        const averageCompletionTask = sumCompletionTask / allTask?.length;
        const tasksWithStatusCompletedTasks = allTask?.every(task => task.status === firstTaskStatus);
        let projectTaskData = {};
        projectTaskData.completion = averageCompletionTask
        if (tasksWithStatusCompletedTasks) {
          projectTaskData.status = firstTaskStatus
        }
        else {
          projectTaskData.status = 2
        }
        if(jobCost===1){
         
          projectTaskData.jobcost=1
          projectTaskData.notifyJobcost=notifyJobcost
          
        }
        else{
          projectTaskData.jobcost=0
          projectTaskData.notifyJobcost=notifyJobcost
        }
        if(reqresult[0]?.allInstallationCompleted){
          projectTaskData.status = 3
        }
        let resultStatus = await Project.updateProject(checkProjectExist, projectTaskData);
        console.log('projectTaskData 1', projectTaskData)
        
        emitProgress('project_update_complete', `Job updated successfully!`, 90);
      }
    }
    else {
      emitProgress('processing_rejection', `Processing sales order rejection...`, 50);
      
      let statusUpdate = await Request.updateRequets(requestId, { status: 6, reason ,timestamp : currentTime });
      
      emitProgress('rejection_complete', `Sales order rejected successfully`, 90);
    }
    if (acceptQuery) {
      emitProgress('processing_activity_logs', `Processing activity logs and notifications...`, 94);
      
      let request = await Request.getRequestById(requestId);
      let partner = await RequestPartner.getRequestPartnerById(requestPartnerId);
      if (acceptStatus == 1) {

        // let reqActivityMsg = `The member ${partner.name} accepted the Invoice ${request[0].name}.`
        let reqActivityMsg = `🔄 Sales Order "${request[0].name}" has been converted to Job.`
        let notificationMsg = `The Sales Order '${request[0].name}' has been converted to Job.`;
        let notificationUrl = `request/view/${requestId}`;

        emitProgress('sending_partner_notifications', `Sending notifications to partner...`, 95);
        
        // Alert to user of new added product
        if (!empty(partner['user_id'])) {
          const incrementResult_User = await Admin.incrementField(partner.name, "noti_count", 1);


          let notificationData_user = {
            user_id: partner.user_id,
            url: notificationUrl,
            timestamp: currentDate,
            message: notificationMsg,
            status: 1,
            from_id: userId,
          };

          const addNotificationResult_user = await Admin.addNotification(notificationData_user);
        }

        emitProgress('updating_tab_counts', `Updating user tab counts...`, 96);
        
        let projectId = await Project.getProjectByRequestId(requestId);
        console.log("projectttttttt id",projectId)
        // let requestData = await Request.getRequestById(requestId)
        // const allUserIds = await Admin.getAllUserIds();
        // const allUserIds = await Admin.getAllAdminUserIds();
        // const selectedClientId = values?.clients?.user_id ||null;
        let PartnerUserIds = await RequestPartner.fetchPartnerUserIds(requestId)
        // const usersToUpdate = [...new Set([userId,...PartnerUserIds, ...allUserIds].filter(Boolean))];
      //   const updateEditCounts = async (userId) => {

      //     const jobsCount = await Project.getJobsTabByUserId(userId);
      //     if (!jobsCount || jobsCount === "No record found") {
      //       const jobTabData = {
      //         user_id: userId,
      //         hold_count: 0,
      //         pre_production_count: 0,
      //         production_count: 0,
      //         installs_count: 0,
      //         pickup_count: 0,
      //         shipping_count: 0,
      //         completed_count: 0,
      //         timestamp: moment().unix()
      //       };
      //       await Project.addJobsTabCount(jobTabData);
      //     }

      //     if(allCreatedTask?.length <= 0){
      //     // if(requestData[0]?.job_category_name == 'Service only'){
      //       await Request.salesOrderIncrementField(userId, 'approved_count', -1, 'approved_count_ids', requestId, false);
      //       await Request.salesOrderIncrementField(userId, 'installs_count', 1, 'installs_count_ids', requestId, true);
      //       await Project.jobTabIncrementField(userId, 'installs_count', 1, 'installs_count_ids', projectId, true);
      //       console.log("moved to installs tab counts updated");
      //     }else{
      //       await Request.salesOrderIncrementField(userId, 'approved_count', -1, 'approved_count_ids', requestId, false);
      //       await Request.salesOrderIncrementField(userId, 'in_production_count', 1, 'in_production_count_ids', requestId, true);
      //       await Project.jobTabIncrementField(userId, 'pre_production_count', 1, 'pre_production_count_ids', projectId, true);
      //       console.log("Sales order tab counts updated");            
      //     }
      // }
  
        // Apply updates
        // await Promise.all(usersToUpdate.map(userId => updateEditCounts(userId)));
        // console.log("Request tab counts updated successfully after edit.");

        emitProgress('sending_admin_notifications', `Sending notifications to administrators...`, 97);

        let adminIds = await Admin.getAllAdminIds();
        let notificationData = {};

        for (const adminId of adminIds) {

          if (adminId != userId) {
            const incrementResult = await Admin.incrementField(adminId, "noti_count", 1);

            notificationData = {
              user_id: adminId,
              url: notificationUrl,
              timestamp: currentDate,
              message: notificationMsg,
              status: 1,
              from_id: userId,
            };

            const addNotificationResult = await Admin.addNotification(notificationData);

          }
        }
        emitProgress('adding_activity_log', `Adding activity log entry...`, 98);

        let requestActivity = {}
        requestActivity['request_id'] = requestId;
        requestActivity['activity'] = reqActivityMsg
        requestActivity['added_by'] = addedByUserId ||userId
        requestActivity['timestamp'] = currentDate
        let reqActivityId = await RequestActivity.addRequestUpdate(requestActivity)
        
        emitProgress('sending_email_sms_notifications', `Sending email and SMS notifications...`, 99);
        
        let adminMangerIds = await Admin.getAllAdminAndManagerUserIds();
        const usersToSendMessage = [...new Set([userId,...PartnerUserIds, ...adminMangerIds].filter(Boolean))];

        for (const userIdToCheck of usersToSendMessage) {
            console.log('useriddddd',userIdToCheck)
            const creator = await Admin.getUserNotificationAndPermissionSettings(userIdToCheck);
            console.log('creatorrrrrrrrr',creator)
            if (!creator) continue;
        // const creator = await Admin.getUserNotificationAndPermissionSettings(userId);

        if (creator.emailEnabled === 1 && creator.email) {
          if (creator?.pcb_new_jobs === 1 && creator?.pcb_new_jobs_email === 1) {
            const welcome = `Hello ${creator.name},`;
            const subject = checkProjectExist ? 
            `🛠️  Job Updated (Converted from Sales Order): ${request[0].name}`:
            `🛠️ New Job Created (Converted from Sales Order): ${request[0].name}`;
            const email_content = checkProjectExist ?
            `A job has been updated by converting a sales order. Please check the Jobs section in your software.`:
            `A new job has been created by converting a sales order. Please check the Jobs section in your software.`

            let message = newJobCreatedEmailTemplate({
              user: creator,
              requestName: request[0].name,
              projectType: request[0].project_type,
              total: request[0].total || 0,
              baseUrl: BASE_URL,
              type: 'job',
              convertion: true,
            });

            message = message.replace('[emailContent]', email_content);
            message = message.replace('[CONTENT]', welcome);
            message = message.replace('[LOGIN_URL]', `${BASE_URL}`);

            const mailOptions = {
              // to: 'deeraj@skyniche.com',
              // to: 'sanjay.pk@skyniche.com',
              to: creator.email,
              from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
              subject,
              text: subject,
              html: message,
            };

            try {
              await transporter.sendMail(mailOptions);
              console.log(`✅ Job email sent to: ${creator.email}`);
            } catch (err) {
              console.error(`❌ Error sending job email:`, err);
            }
          }
        }
        if (creator?.smsEnabled === 1 && creator?.phone){
          if (creator?.pcb_new_jobs === 1 && creator?.pcb_new_jobs_sms === 1) {
            const smsContent =
              (checkProjectExist
            ? `🛠️ Job Updated (Converted from Sales Order) !\n`
            : `🛠️ New Job Created (Converted from Sales Order) !\n`) +
              `📄 Name: "${request[0].name}"\n` +
              `💰 Total: ₹${request[0].total || 0}\n` +
              `🔗 View: ${BASE_URL}\n` +
              `— SignWise`;
  
            const recipientPhone = creator.phone || TWILIO_PHONE;
            const Phone_country_code = creator.phone_country_code || '';
  
            try {
              // await sendMessageToPhoneNumber('9074906155', smsContent);
              // await sendMessageToPhoneNumber('9947931552', smsContent,'91');
              await sendMessageToPhoneNumber(recipientPhone, smsContent,Phone_country_code);
              console.log(`✅ SMS sent to ${creator.name || 'User'} (${recipientPhone}) for Job "${request[0].name}"`);
            } catch (err) {
              console.error(`❌ Failed to send Job SMS to ${creator.name || 'User'} (${recipientPhone}):`, err);
            }
          }
        }
      }
      }
      else {
        emitProgress('processing_rejection_notifications', `Processing rejection notifications...`, 95);

        let reqActivityMsg = `The member ${partner.name} rejected the request ${request[0].name}.`
        let notificationMsg = `The member ${partner.name} rejected the request ${request[0].name}.`;
        let notificationUrl = `request/view/${requestId}`;

        emitProgress('sending_rejection_notifications', `Sending rejection notifications to partner...`, 97);
        
        // Alert to user of new added product
        if (!empty(partner['user_id'])) {
          const incrementResult_User = await Admin.incrementField(partner.name, "noti_count", 1);


          let notificationData_user = {
            user_id: partner.user_id,
            url: notificationUrl,
            timestamp: currentDate,
            message: notificationMsg,
            status: 1,
            from_id: userId,
          };

          const addNotificationResult_user = await Admin.addNotification(notificationData_user);
        }

        emitProgress('sending_rejection_admin_notifications', `Sending rejection notifications to administrators...`, 98);

        let adminIds = await Admin.getAllAdminIds();
        let notificationData = {};

        for (const adminId of adminIds) {

          if (adminId != userId) {
            const incrementResult = await Admin.incrementField(adminId, "noti_count", 1);

            notificationData = {
              user_id: adminId,
              url: notificationUrl,
              timestamp: currentDate,
              message: notificationMsg,
              status: 1,
              from_id: userId,
            };

            const addNotificationResult = await Admin.addNotification(notificationData);

          }
        }
        
        emitProgress('adding_rejection_activity_log', `Adding rejection activity log entry...`, 99);
        
        let requestActivity = {}
        requestActivity['request_id'] = requestId;
        requestActivity['activity'] = reqActivityMsg
        requestActivity['added_by'] = userId
        requestActivity['timestamp'] = currentDate
        let reqActivityId = await RequestActivity.addRequestUpdate(requestActivity)

        emitProgress('rejection_notifications_complete', `Rejection notifications completed successfully!`, 99);

      }
     
      if (acceptStatus == 1) {
        try {
          // let requestData = await Request.getRequestById(requestId)

        //   let projectId = await Project.projectIdFromRequestId(requestId)
        //  let projectData = await Project.getProjectById(projectId)
        // let joCostData = await Request.getJobCostDetailsForMaterials(requestId);
 
        // const previosProductTotal =(projectData?.project_category == 12) ? 0 : parseFloat(Number(joCostData?.productTotal) || 0).toFixed(2) 
        // const previosServiceTotal =(projectData?.project_category == 12) ? 0 : parseFloat(Number(joCostData?.serviceTotal) || 0).toFixed(2) 
        // const previosJobCostTotal = parseFloat(Number(previosProductTotal) + Number(previosServiceTotal) || 0).toFixed(2);
     
       if(projectData?.project_category !== 12) {
         await createJornalEntryForJobCost(requestId,previosJobCostTotal,previosProductTotal,previosServiceTotal)
        }
          
        } catch (error) {
          console.log("error in quickbook",error)
        }
        reply.send({ status: 1, message: "You have accepted the request",case:1, allCreatedTask ,directInstalls});
      } else {
        // Final completion message for rejection
        emitProgress('complete', `Sales order rejection completed successfully!`, 100);
        reply.send({ status: 2, message: "You have  rejected the request", allCreatedTask ,directInstalls});
      }
    }

    else {
      if (acceptStatus == 1) {
        try {
          // let requestData = await Request.getRequestById(requestId)

        //   let projectId = await Project.projectIdFromRequestId(requestId)
        //  let projectData = await Project.getProjectById(projectId)
        //  console.log('projectData', projectData)
        // let joCostData = await Request.getJobCostDetailsForMaterials(requestId);
 
        // const previosProductTotal =(projectData?.project_category == 12) ? 0 : parseFloat(Number(joCostData?.productTotal) || 0).toFixed(2) 
        // const previosServiceTotal =(projectData?.project_category == 12 ) ? 0 : parseFloat(Number(joCostData?.serviceTotal) || 0).toFixed(2) 
        // const previosJobCostTotal = parseFloat(Number(previosProductTotal) + Number(previosServiceTotal) || 0).toFixed(2);
     
        if(projectData?.project_category !== 12) {
        await createJornalEntryForJobCost(requestId,previosJobCostTotal,previosProductTotal,previosServiceTotal)
        }
        } catch (error) {
          console.log("error in quickbook",error)
        }
              
      // Final completion message after all notifications
      emitProgress('complete', `Sales order to job conversion completed successfully!`, 100);
        reply.send({ status: 1, message: "You have accepted the request",case:2, allCreatedTask,directInstalls });
      } else {
        reply.send({ status: 2, message: "You have  rejected the request", allCreatedTask,directInstalls });
      }
    }
  }
  catch (error) {
    console.log("error : ", error);
    reply.send({ status: 500, message: "Server error", error });
  }
}

//////////////////////////////////////Quickbook quickbid accept/reject
const getEstimateById = async (estimateId) => {
  try {
    // Fetch the access token
    let token = await getNewAccessToken();

    const apiUrl = PRODUCTION
    ? `https://quickbooks.api.intuit.com/v3/company/${realmId}/estimate/${estimateId}`
    : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/estimate/${estimateId}`;


    // Make the request to fetch the estimate by ID
    const response = await axios.get(
      apiUrl,
      {
        headers: {
          Authorization: `Bearer ${token}`,
          'Content-Type': 'application/json',
          Accept: 'application/json'
        }
      }
    );

    return response.data.Estimate;
  } catch (error) {
    console.error("Error fetching estimate from QuickBooks:", error.response ? error.response.data : error.message);

    // Check if the error is due to authentication and possibly retry fetching the token
    if (error.response && error.response.data.fault && error.response.data.fault.type === 'AUTHENTICATION') {
      console.error("Authentication error. Please check your access token.");
    }

    return null;
  }
};


const updateEstimateStatus = async (estimate, status) => {
  try {
    // Properly await the token retrieval
    let token = await getNewAccessToken();

    const updatedEstimateData = {
      Id: estimate.Id,
      SyncToken: estimate.SyncToken,
      TxnStatus: status,
      CustomField: [
        {
          DefinitionId: "1", // Replace with your CustomField DefinitionId
          Name: "Accepted",
          Type: "StringType",
          StringValue: status
        }
      ],
      sparse: true
    };

    const apiUrl = PRODUCTION
    ? `https://quickbooks.api.intuit.com/v3/company/${realmId}/estimate`
    : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/estimate`;


    // Make the API call to update the estimate
    const response = await axios.post(
      apiUrl,
      updatedEstimateData,
      {
        headers: {
          Authorization: `Bearer ${token}`,
          'Content-Type': 'application/json',
          Accept: 'application/json'
        }
      }
    );

    if (response.status === 200) {
      console.log("Estimate status updated successfully:", response.data);
      return response.data;
    } else {
      console.error("Failed to update estimate status in QuickBooks:", response.data);
      return null;
    }
  } catch (error) {
    console.error("Error updating estimate status in QuickBooks:", error.response ? error.response.data : error.message);
    return null;
  }
};



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

const acceptOrRejectQuotation = async (req, reply) => {
  try {
    console.log("dataaaaaaaaaaaaaaaaaaaa",req.body)
    let { requestId, requestPartnerId, acceptStatus, userId, reason } = req.body

    //////////////////////////for quickbook
//     let EstimateData = await Request.getRequestById(requestId)
//     const estimateId = EstimateData[0].estimate_id;
//   console.log("estimateId",estimateId)
//     const estimate = await getEstimateById(estimateId)

//     if (!estimate) {
//       return reply.status(500).send({ error: "Failed to fetch estimate from QuickBooks" });
//     }
//     let status = acceptStatus === 1 ? 'Accepted' : 'Rejected';
// console.log("acceptStatus",acceptStatus)
//       // Update the estimate status in QuickBooks
//       const updatedEstimate = await updateEstimateStatus(estimate, status);


try {
  let EstimateData = await Request.getRequestById(requestId);
  const estimateId = EstimateData[0]?.estimate_id;
  console.log("estimateId", estimateId);

  if (estimateId) {
    const estimate = await getEstimateById(estimateId);

    if (!estimate) {
      console.error("Failed to fetch estimate from QuickBooks.");
    } else {
      let status = acceptStatus === 1 ? 'Accepted' : 'Rejected';
      console.log("acceptStatus", acceptStatus);
      // Update the estimate status in QuickBooks
      await updateEstimateStatus(estimate, status);
    }
  } else {
    console.error("Invalid estimateId in QuickBooks.");
  }
} catch (quickbooksError) {
  console.error("Error in QuickBooks API: ", quickbooksError);
  // Log the error and continue with the rest of the process
}

    /////////////////////////////////////////
    let currentDate = Math.round(moment().valueOf() / 1000);

    if (acceptStatus === 3) {
      // HOLD: set hold=1 on request, reason required
      if (!reason || !String(reason).trim()) {
        return reply.send({ status: 0, message: "Reason is required to put this Estimate on Hold" });
      }
      let statusUpdate = await Request.updateRequets(requestId, { hold: 1, hold_reason: reason.trim() });
      if (statusUpdate) {
        reply.send({ status: 1, message: "Estimate has been moved to Hold" });
      } else {
        reply.send({ status: 0, message: "Failed to move Estimate to Hold" });
      }
      return;
    }

    let acceptQuery = await RequestPartner.acceptOrRejectRequest(requestPartnerId, { accept_status: acceptStatus });

    if (acceptStatus === 1) {
      console.log("acceptStatus",acceptStatus)
      let requestData = await Request.getRequestById(requestId)

           let statusUpdate = await Request.updateRequets(requestId, { quotation_status: 4,client_approve:0});
           console.log("statusUpdate.",statusUpdate)
    
      }
    
    
    else {
      let statusUpdate = await Request.updateRequets(requestId, { quotation_status: 6, reason });

    }
    if (acceptQuery) {
      let request = await Request.getRequestById(requestId);
      let partner = await RequestPartner.getRequestPartnerById(requestPartnerId);
      
      // Get user name if userId = 1, otherwise use partner name
      let displayName = partner.name;
      if (userId === 1) {
        const userData = await Admin.getUserById(userId);
        const user = userData?.[0];
        if (user) {
          displayName = `${user.first_name || ''} ${user.last_name || ''}`.trim() || user.username || 'User';
        }
      }
      
      if (acceptStatus == 1) {

        let reqActivityMsg = `The member ${displayName} accepted the Estimate ${request[0].name}.`
        let notificationMsg = `The member ${displayName} accepted the Estimate ${request[0].name}.`;
        let notificationUrl = `request/view/${requestId}`;

        // Alert to user of new added product
        if (!empty(partner['user_id'])) {
          const incrementResult_User = await Admin.incrementField(partner.name, "noti_count", 1);


          let notificationData_user = {
            user_id: partner.user_id,
            url: notificationUrl,
            timestamp: currentDate,
            message: notificationMsg,
            status: 1,
            from_id: userId,
          };

          const addNotificationResult_user = await Admin.addNotification(notificationData_user);
        }


        let adminIds = await Admin.getAllAdminIds();
        let notificationData = {};

        for (const adminId of adminIds) {

          if (adminId != userId) {
            const incrementResult = await Admin.incrementField(adminId, "noti_count", 1);

            notificationData = {
              user_id: adminId,
              url: notificationUrl,
              timestamp: currentDate,
              message: notificationMsg,
              status: 1,
              from_id: userId,
            };

            const addNotificationResult = await Admin.addNotification(notificationData);

          }
        }
        let requestActivity = {}
        requestActivity['request_id'] = requestId;
        requestActivity['activity'] = reqActivityMsg
        requestActivity['added_by'] = userId
        requestActivity['timestamp'] = currentDate
        let reqActivityId = await RequestActivity.addRequestUpdate(requestActivity)

      }
      else {

        let reqActivityMsg = `The member ${displayName} rejected the Estimate ${request[0].name}.`
        let notificationMsg = `The member ${displayName} rejected the Estimate ${request[0].name}.`;
        let notificationUrl = `request/view/${requestId}`;

        // Alert to user of new added product
        if (!empty(partner['user_id'])) {
          const incrementResult_User = await Admin.incrementField(partner.name, "noti_count", 1);


          let notificationData_user = {
            user_id: partner.user_id,
            url: notificationUrl,
            timestamp: currentDate,
            message: notificationMsg,
            status: 1,
            from_id: userId,
          };

          const addNotificationResult_user = await Admin.addNotification(notificationData_user);
        }


        let adminIds = await Admin.getAllAdminIds();
        let notificationData = {};

        for (const adminId of adminIds) {

          if (adminId != userId) {
            const incrementResult = await Admin.incrementField(adminId, "noti_count", 1);

            notificationData = {
              user_id: adminId,
              url: notificationUrl,
              timestamp: currentDate,
              message: notificationMsg,
              status: 1,
              from_id: userId,
            };

            const addNotificationResult = await Admin.addNotification(notificationData);

          }
        }
        let requestActivity = {}
        requestActivity['request_id'] = requestId;
        requestActivity['activity'] = reqActivityMsg
        requestActivity['added_by'] = userId
        requestActivity['timestamp'] = currentDate
        let reqActivityId = await RequestActivity.addRequestUpdate(requestActivity)

      }

      // const allUserIds = await Admin.getAllAdminUserIds();
      // const allUserIds = await Admin.getAllUserIds();
      // const requestData = await Request.getRequestById(requestId);
      // console.log("requestDataaaaaaaa",requestData[0])
      // let allUsersToCheck = [...new Set([userId, ...allUserIds].filter(Boolean))];
    
      // if (acceptStatus === 1) {
      //   await Promise.all(
      //     allUsersToCheck.map(async (user) => {
      //       await Request.EstimateTabIncrementField(user, 'open_count', -1, 'open_count_ids', requestId,false);
      //       await Request.EstimateTabIncrementField(user, 'approved_count', 1, 'approved_count_ids', requestId,true);
      //     })
      //   );
      // } else {
      //   // Rejected: Decrease open_count
      //   await Promise.all(
      //     allUsersToCheck.map(async (user) => {
      //       await Request.EstimateTabIncrementField(user, 'open_count', -1, 'open_count_ids', requestId,false);
      //     })
      //   );
      // }

      // console.log(`Successfully updated counts based on Estimate ${acceptStatus === 1 ? 'Acceptance' : 'Rejection'}.`);



      if (acceptStatus == 1) {
        reply.send({ status: 1, message: "You have accepted the Estimate" });
      }

      else {
        reply.send({ status: 2, message: "You have  rejected the Estimate" });
      }
    }

    else {
      if (acceptStatus == 1) {
        reply.send({ status: 1, message: "You have accepted the Estimate" });
      }

      else {
        reply.send({ status: 2, message: "You have  rejected the Estimate" });
      }
    }

  }
  catch (error) {
    console.log("error : ", error);
    reply.send({ status: 0, message: "Failed to rejected the Estimate", error });
  }
}

const updateEstimateHoldStatus = async (req, reply) => {
  try {
    let { request_id, userId, reason = '' } = req.body;
    const request = await Request.getRequestById(request_id);
    if (!request || !request[0]) {
      return reply.send({ status: 0, message: "Estimate not found" });
    }
    const currentHold = request[0]?.hold;
    let updateData = {};
    if (currentHold === 0) {
      // Move to Hold - reason required
      if (!reason || !String(reason).trim()) {
        return reply.send({ status: 0, message: "Reason is required to put this Estimate on Hold" });
      }
      updateData = { hold: 1, hold_reason: reason.trim() };
    } else {
      // Release from Hold
      updateData = { hold: 0, hold_reason: null };
    }
    const statusUpdate = await Request.updateRequets(request_id, updateData);
    if (!statusUpdate) {
      return reply.send({ status: 0, message: "Failed to update Estimate hold status" });
    }

    const currentDate = moment().unix();
    const notificationMsg = currentHold === 0
      ? `The estimate "${request[0]?.name}" has been moved to Hold`
      : `The estimate "${request[0]?.name}" has been removed from Hold`;
    const message = currentHold === 0
      ? "Estimate has been moved to Hold"
      : "Estimate has been removed from Hold";

    try {
      await Project.updateEstimateJobHoldByRequestId(request_id, updateData.hold);
    } catch (projectErr) {
      console.log("Error syncing estimate job hold (request updated successfully):", projectErr);
    }

    try {
      await RequestActivity.addRequestUpdate({
        request_id: request_id,
        activity: notificationMsg,
        added_by: userId,
        timestamp: currentDate
      });

      let notificationUrl = "quick-bid/view/" + request_id;
      let notificationData = {};
      await Admin.incrementField(userId, "noti_count", 1);
      let notificationData_user = {
        user_id: userId,
        url: notificationUrl,
        timestamp: currentDate,
        message: notificationMsg,
        status: 1,
        from_id: userId,
      };
      await Admin.addNotification(notificationData_user);
      let adminIds = await Admin.getAllAdminIds();

      for (const adminId of adminIds) {
        if (adminId != userId) {
          await Admin.incrementField(adminId, "noti_count", 1);
          notificationData = {
            user_id: adminId,
            url: notificationUrl,
            timestamp: currentDate,
            message: notificationMsg,
            status: 1,
            from_id: userId,
          };
          await Admin.addNotification(notificationData);
        }
      }
    } catch (notifErr) {
      console.log("Error adding activity/notifications (request updated successfully):", notifErr);
    }

    return reply.send({ status: 1, message });
  } catch (error) {
    console.log("Error in updateEstimateHoldStatus:", error);
    reply.send({ status: 0, message: "Failed to update Estimate hold status" });
  }
};

const reopenRequest = async (req, reply) => {
  try {
    let { requestId, userId } = req.body
    let currentDate = moment().unix();
    let partnerIds = await RequestPartner.fetchPartners(requestId);


    if (!empty(partnerIds)) {
      for (let i = 0; i < partnerIds.length; i++) {
        let id = partnerIds[i];
        await RequestPartner.acceptOrRejectRequest(id, { accept_status: 1 });
      }
      let statusUpdate = await Request.updateRequets(requestId, { status: 2 });
      let request = await Request.getRequestById(requestId);
      let requestPartners = await RequestPartner.getRequestPartnerByIds({}, requestId)
      console.log("#########", request);
      let reqActivityMsg = "Request has been Reopened"
      let notificationMsg = `The request ${request[0].name} has been reopened.`;
      let notificationUrl = `request/view/${requestId}`;

      // Alert to user of new added product

      const incrementResult_User = await Admin.incrementField(userId, "noti_count", 1);


      let notificationData_user = {
        user_id: userId,
        url: notificationUrl,
        timestamp: currentDate,
        message: notificationMsg,
        status: 1,
        from_id: userId,
      };

      const addNotificationResult_user = await Admin.addNotification(notificationData_user);
      let notificationData = {};
      if (requestPartners.length > 0) {
        for (const reqId of requestPartners) {
          console.log("$$$$$$$$$$$$$", reqId);
          const incrementResult = await Admin.incrementField(reqId['id'], "noti_count", 1);

          notificationData = {
            user_id: reqId['id'],
            url: notificationUrl,
            timestamp: currentDate,
            message: notificationMsg,
            status: 1,
            from_id: userId,
          };

          const addNotificationResult = await Admin.addNotification(notificationData);
        }
      }

      let adminIds = await Admin.getAllAdminIds();



      for (const adminId of adminIds) {

        if (adminId != userId) {

          const incrementResult = await Admin.incrementField(adminId, "noti_count", 1);

          notificationData = {
            user_id: adminId,
            url: notificationUrl,
            timestamp: currentDate,
            message: notificationMsg,
            status: 1,
            from_id: userId,
          };

          const addNotificationResult = await Admin.addNotification(notificationData);

        }
      }
      console.log("$$$$$$$$$$$$$");
      let requestActivity = {}
      requestActivity['request_id'] = requestId;
      requestActivity['activity'] = reqActivityMsg
      requestActivity['added_by'] = userId
      requestActivity['timestamp'] = currentDate
      requestActivity['status'] = 1
      let reqActivityId = await RequestActivity.addRequestUpdate(requestActivity)
      reply.send({ status: 1, message: "Request reopened successfully" });

    }
    else {
      reply.send({ status: 0, message: "Failed to  reopen request" });
    }
  }
  catch (error) {
    console.log(error, 'Error in adding request update ')
  }
}
const reopenRequestJob = async (req, reply) => {
  try {
    let { requestId,jobId, userId } = req.body;
    let currentDate = moment().unix();

    // Fetch partner IDs once instead of twice
    let partnerIds = await RequestPartner.fetchPartners(requestId);
    
    if (partnerIds && partnerIds.length > 0) {
      // Update the request status and fetch updated data in parallel
      const [statusUpdate, requestPartners,jobUpdate] = await Promise.all([
        Request.updateRequets(requestId, { status: 4,reason:"",reject_file:null,reject_file_type:null }),
        RequestPartner.getRequestPartnerByIds({}, requestId),
        Project.updateProjectById(jobId, { status: 1 }),
      ]);
      // let requestPartners = await RequestPartner.getRequestPartnerByIds({}, requestId)
      // console.log("#########", request);

      const reqActivityMsg = "Job and sales order has been Reopened";
      const notificationMsg = `The Job and sales order has been reopened.`;
      const notificationUrl = `request/view/${requestId}`;

      // Send the response immediately after updating the request status
      reply.send({ status: 1, message: "Job and sales order reopened successfully" });

      // Notification helper function
      const sendNotification = async (userId, message) => {
        const incrementResult = await Admin.incrementField(userId, "noti_count", 1);
        const notificationData = {
          user_id: userId,
          url: notificationUrl,
          timestamp: currentDate,
          message: message,
          status: 1,
          from_id: userId,
        };
        return Admin.addNotification(notificationData);
      };

      // Increment user notification count and send notification
      await sendNotification(userId, notificationMsg);

      // If there are request partners, send notifications to them in parallel
      if (requestPartners.length > 0) {
        const partnerNotificationPromises = requestPartners.map(reqId => 
          sendNotification(reqId['id'], notificationMsg)
        );
        await Promise.all(partnerNotificationPromises);
      }

      // Get all admin IDs and send notifications in parallel
      const adminIds = await Admin.getAllAdminIds();
      const adminNotificationPromises = adminIds
        .filter(adminId => adminId !== userId)
        .map(adminId => sendNotification(adminId, notificationMsg));

      await Promise.all(adminNotificationPromises);

      // Log request activity
      const requestActivity = {
        request_id: requestId,
        activity: reqActivityMsg,
        added_by: userId,
        timestamp: currentDate,
        status: 1
      };
      await RequestActivity.addRequestUpdate(requestActivity);

    } else {
      reply.send({ status: 0, message: "Failed to reopen request" });
    }
  } catch (error) {
    console.log(error, 'Error in adding request update');
    reply.send({ status: 0, message: 'An error occurred' });
  }
};


const reopenQuotation = async (req, reply) => {
  try {
    let { requestId, userId } = req.body
    let currentDate = moment().unix();
    let partnerIds = await RequestPartner.fetchPartners(requestId);


    if (!empty(partnerIds)) {
      for (let i = 0; i < partnerIds.length; i++) {
        let id = partnerIds[i];
        await RequestPartner.acceptOrRejectRequest(id, { accept_status: 1 });
      }
      let request = await Request.getRequestById(requestId);
      let newStatus = request[0]?.is_request_estimate == 1 ? 3 : 2
      let statusUpdate = await Request.updateRequets(requestId, { quotation_status: newStatus, send_to_client:0, is_create_estimate:0, reason:"" });
      if(request[0]?.is_request_estimate == 1){
        await Project.updateEstimateJobProjectByRequestId(requestId, { status: 1, completion: 0 });
      }
      let requestPartners = await RequestPartner.getRequestPartnerByIds({}, requestId)
      console.log("#########", request);
      let reqActivityMsg = "Estimate has been Reopened"
      let notificationMsg = `The estimate ${request[0].name} has been reopened.`;
      let notificationUrl = `request/view/${requestId}`;

      // Alert to user of new added product

      const incrementResult_User = await Admin.incrementField(userId, "noti_count", 1);


      let notificationData_user = {
        user_id: userId,
        url: notificationUrl,
        timestamp: currentDate,
        message: notificationMsg,
        status: 1,
        from_id: userId,
      };

      const addNotificationResult_user = await Admin.addNotification(notificationData_user);
      let notificationData = {};
      if (requestPartners.length > 0) {
        for (const reqId of requestPartners) {
          console.log("$$$$$$$$$$$$$", reqId);
          const incrementResult = await Admin.incrementField(reqId['id'], "noti_count", 1);

          notificationData = {
            user_id: reqId['id'],
            url: notificationUrl,
            timestamp: currentDate,
            message: notificationMsg,
            status: 1,
            from_id: userId,
          };

          const addNotificationResult = await Admin.addNotification(notificationData);
        }
      }

      let adminIds = await Admin.getAllAdminIds();



      for (const adminId of adminIds) {

        if (adminId != userId) {

          const incrementResult = await Admin.incrementField(adminId, "noti_count", 1);

          notificationData = {
            user_id: adminId,
            url: notificationUrl,
            timestamp: currentDate,
            message: notificationMsg,
            status: 1,
            from_id: userId,
          };

          const addNotificationResult = await Admin.addNotification(notificationData);

        }
      }
      console.log("$$$$$$$$$$$$$");
      let requestActivity = {}
      requestActivity['request_id'] = requestId;
      requestActivity['activity'] = reqActivityMsg
      requestActivity['added_by'] = userId
      requestActivity['timestamp'] = currentDate
      requestActivity['status'] = 1
      let reqActivityId = await RequestActivity.addRequestUpdate(requestActivity)
      reply.send({ status: 1, message: "Estimate reopened successfully" });

    }
    else {
      reply.send({ status: 0, message: "Failed to  reopen estimate" });
    }
  }
  catch (error) {
    console.log(error, 'Error in adding request update ')
  }
}
///////////////////////////Quickbook fun to convert estimate to invoice////////////////////////////////////////////




const convertEstimateToSalesOrder = async (estimateId) => {
  if (!estimateId) {
    throw new Error('Estimate ID is required');
  }

  console.log("estimateId", estimateId);

  try {
    let token = await getNewAccessToken(); // Ensure you await the access token retrieval

    // Determine the API base URL based on the environment
    const quickbooksApiBaseUrl = PRODUCTION
      ? `https://quickbooks.api.intuit.com/v3/company/${realmId}`
      : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}`;

    const url = `${quickbooksApiBaseUrl}/estimate/${estimateId}?minorversion=65`;

    // Fetch the estimate details from QuickBooks
    let estimateResponse;
    try {
      estimateResponse = await axios.get(url, {
        headers: {
          'Authorization': `Bearer ${token}`,
          'Accept': 'application/json'
        },
        timeout: 10000 // Set a timeout as needed
      });
      console.log("Fetched estimate for conversion:", estimateResponse.data);
    } catch (error) {
      if (error.response && error.response.status === 401) {
        console.log('Access token expired, refreshing...');
        token = await getNewAccessToken(); // Refresh token
        estimateResponse = await axios.get(url, {
          headers: {
            'Authorization': `Bearer ${token}`,
            'Accept': 'application/json'
          },
          timeout: 10000 // Set a timeout as needed
        });
        console.log("Fetched estimate after refreshing token:", estimateResponse.data);
      } else {
        console.error('Error fetching estimate:', error.response ? error.response.data : error.message);
        throw error;
      }
    }

    const estimate = estimateResponse.data.Estimate;

    if (!estimate) {
      throw new Error('Estimate not found in QuickBooks');
    }

    // Get the inventory start date for the items
    const inventoryStartDate = moment(estimate.InvStartDate); // Assuming InvStartDate is part of the estimate

    // Set the transaction date to today if it's after the inventory start date
    const txnDate = moment().format('YYYY-MM-DD');

    // Prepare sales order data based on the estimate, including discount and tax details
    let salesOrderData = {
      "CustomerRef": estimate.CustomerRef,
      "Line": estimate.Line.map(lineItem => {
        if (lineItem.SalesItemLineDetail && lineItem.SalesItemLineDetail.ItemRef) {
          return {
            "Amount": lineItem.Amount,
            "DetailType": "SalesItemLineDetail",
            "SalesItemLineDetail": {
              "ItemRef": lineItem.SalesItemLineDetail.ItemRef,
              "Qty": lineItem.SalesItemLineDetail.Qty,
              "UnitPrice": lineItem.SalesItemLineDetail.UnitPrice,
              "TaxCodeRef": lineItem.SalesItemLineDetail.TaxCodeRef
            }
          };
        } else if (lineItem.DetailType === 'DiscountLineDetail') {
          return {
            "Amount": lineItem.Amount,
            "DetailType": "DiscountLineDetail",
            "DiscountLineDetail": lineItem.DiscountLineDetail
          };
        } else {
          console.warn('Unhandled line item type:', lineItem);
          return null; // Skip this line item if it's invalid or not supported
        }
      }).filter(item => item !== null), // Filter out null values
      "TxnDate": txnDate, // Set transaction date to the determined date
      "DueDate": moment().add(30, 'days').format('YYYY-MM-DD'), // Example: Net 30 terms
      "PrivateNote": estimate.PrivateNote || "Converted from estimate",
      // Include tax details if available
      "TxnTaxDetail": {
        "TxnTaxCodeRef": estimate.TxnTaxDetail.TxnTaxCodeRef,
        "TotalTax": estimate.TxnTaxDetail.TotalTax,
        "TaxReviewStatus": estimate.TxnTaxDetail.TaxReviewStatus,
        "TaxLine": estimate.TxnTaxDetail.TaxLine 
      }
    };

    // Create the sales order in QuickBooks
    let salesOrderResponse;
    try {
      salesOrderResponse = await axios.post(`${quickbooksApiBaseUrl}/salesorder`, salesOrderData, {
        headers: {
          'Authorization': `Bearer ${token}`, // Ensure the latest token is used here
          'Accept': 'application/json',
          'Content-Type': 'application/json'
        },
        timeout: 10000 // Add timeout here as well
      });
      console.log("Sales order creation response:", salesOrderResponse.data);
    } catch (error) {
      if (error.response && error.response.status === 401) {
        console.log('Access token expired again, refreshing...');
        token = await getNewAccessToken(); // Refresh token
        salesOrderResponse = await axios.post(`${quickbooksApiBaseUrl}/salesorder`, salesOrderData, {
          headers: {
            'Authorization': `Bearer ${token}`,
            'Accept': 'application/json',
            'Content-Type': 'application/json'
          },
          timeout: 10000 // Add timeout here as well
        });
        console.log("Sales order creation response after refreshing token:", salesOrderResponse.data);
      } else {
        console.error('Error creating Sales order:', error.response ? error.response.data : error.message);
        throw error;
      }
    }

    const salesOrder = salesOrderResponse.data;
    await markEstimateAsConverted(estimateId); // Mark the estimate as converted
    console.log("New Sales order ID:", salesOrder.SalesOrder.Id);
    newInvoiceId = salesOrder.SalesOrder.Id;
    newDocNum = salesOrder.SalesOrder.DocNumber;
    return salesOrder.SalesOrder.Id;

  } catch (error) {
    console.error("Error converting estimate to Sales order in QuickBooks: ", error.response ? error.response.data : error.message);
    throw error;
  }
};



const markSalesOrderAsConverted = async (salesOrderId) => {
  try {
    let token = await getNewAccessToken();
    const quickbooksApiBaseUrl = PRODUCTION
      ? `https://quickbooks.api.intuit.com/v3/company/${realmId}`
      : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}`;

    // Fetch the latest sales order to get the SyncToken and CustomerRef
    const salesOrderResponse = await axios.get(`${quickbooksApiBaseUrl}/salesorder/${salesOrderId}`, {
      headers: {
        'Authorization': `Bearer ${token}`,
        'Accept': 'application/json'
      },
      timeout: 10000
    });

    const salesOrder = salesOrderResponse.data.SalesOrder;
    const syncToken = salesOrder.SyncToken;

    const updateData = {
      "Id": salesOrderId,
      "SyncToken": syncToken,
      "TxnStatus": "Converted",
      "PrivateNote": "This sales order has been converted to an invoice.",
      "Line": salesOrder.Line,
      "CustomerRef": salesOrder.CustomerRef
    };

    const response = await axios.post(`${quickbooksApiBaseUrl}/salesorder`, updateData, {
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json',
        'Accept': 'application/json'
      },
      timeout: 10000 // Add timeout here as well
    });

    if (response.status === 200) {
      console.log("Sales order successfully marked as converted:", response.data);
    } else {
      console.error("Failed to update sales order status:", response.data);
    }
  } catch (error) {
    console.error("Error updating sales order status:", error.response ? error.response.data : error.message);

    // Log specific validation error details if available
    if (error.response && error.response.data && error.response.data.Fault) {
      console.error("Validation Error Details:", JSON.stringify(error.response.data.Fault.Error, null, 2));
    }
  }
};




////////////////////////////////////////////////////////////////////////
const convertQuotation = async (req, reply) => {
  try {
    let { requestId, userId } = req.body
    let currentDate = moment().unix();
    
    // 📊 Progress tracking function
    const emitProgress = (step, message, progress = 0) => {
      console.log(`[PROGRESS-MODAL] 📊 Progress: ${step} - ${message} (${progress}%)`);
      
      const progressData = {
        step,
        message,
        progress,
        timestamp: Date.now(),
        userId: userId, // Include user ID for filtering
        domain: DOMAIN // Add domain for filtering (same as chat)
      };
      
      // Emit to working socket server (same as chat)
      console.log(`[PROGRESS-MODAL] 📡 Emitting progress to working socket server...`);
      try {
        const workingSocket = ioClient(REST_API_IO, {
          path: '/socket.io',
          transports: ['websocket', 'polling'],
          secure: true,
          timeout: 5000
        });
        
        workingSocket.on('connect', () => {
          console.log(`[PROGRESS-MODAL] 🔌 Connected to working socket server`);
          console.log(`[PROGRESS-MODAL] 📊 Emitting progress data:`, progressData);
          workingSocket.emit('message', {
            ...progressData,
            type: 'progress'
          });
          workingSocket.emit('request_progress', progressData);
          console.log(`[PROGRESS-MODAL] ✅ Successfully emitted progress to working socket server`);
          
          // Disconnect after emitting
          setTimeout(() => {
            workingSocket.disconnect();
          }, 1000);
        });
        
        workingSocket.on('connect_error', (error) => {
          console.error(`[PROGRESS-MODAL] ❌ Working socket connection error:`, error);
        });
        
      } catch (error) {
        console.error(`[PROGRESS-MODAL] ❌ Error creating working socket connection:`, error);
      }
    };

    // Initial progress
    console.log(`[PROGRESS-MODAL] 🚀 Starting estimate conversion for user:`, userId);
    emitProgress('start', `Initializing estimate conversion...`, 5);
    
    emitProgress('fetching_request_data', `Fetching estimate data...`, 10);
    
    emitProgress('fetching_partner_data', `Fetching client data...`, 12);
    let partnerIds = await RequestPartner.fetchPartners(requestId);
    console.log('partnerIds----', partnerIds)
    let PartnerUserIds = await RequestPartner.fetchPartnerUserIds(requestId)
    
    newInvoiceId =null
    newDocNum = null
    let request = await Request.getRequestById(requestId);
    console.log("request-----------------",request);
    
    // emitProgress('checking_quickbooks_integration', `Checking QuickBooks integration...`, 15);
    
    try {
      if (request && request[0].estimate_id) {
        // emitProgress('converting_quickbooks_estimate', `Converting QuickBooks estimate to invoice...`, 20);
        // let newInvoiceId = await convertEstimateToInvoice(request[0].estimate_id);
        console.log("New Invoice ID:", newInvoiceId,newDocNum,);
        // emitProgress('quickbooks_conversion_complete', `QuickBooks conversion completed`, 25);
      } else {
        // emitProgress('no_quickbooks_integration', `No QuickBooks integration found`, 20);
        console.log("Estimate ID not found in request data");
      }
    } catch (error) {
      // emitProgress('quickbooks_error', `QuickBooks conversion error: ${error.message}`, 20);
      console.log("error",error)
    }

   
    if (!empty(partnerIds)) {
      emitProgress('updating_partner_status', `Updating client acceptance status...`, 30);
      
      for (let i = 0; i < partnerIds.length; i++) {
        let id = partnerIds[i];
        await RequestPartner.acceptOrRejectRequest(id, { accept_status: 1 });
      }
      
      emitProgress('updating_request_status', `Converting estimate to sales order...`, 35);
      
      // ✅ CRITICAL: Update request status (must complete before response)
      if(newInvoiceId && newDocNum) {
        await Request.updateRequets(requestId, { status: 2, estimate_id: newInvoiceId, doc_number: newDocNum, isFromEstimate: 1 });
        emitProgress('request_status_updated', `Estimate converted to sales order successfully`, 40);
      } else {
        await Request.updateRequets(requestId, { status: 2, timestamp: currentDate, isFromEstimate: 1 });
        emitProgress('request_status_updated', `Estimate converted to sales order successfully`, 40);
      }

      // ✅ OPTIMIZATION: Send response immediately after critical operations
      emitProgress('conversion_complete', `Estimate conversion completed successfully!`, 100);
      reply.send({ status: 1, message: "Estimate converted to Sales Order successfully" });

      // ✅ OPTIMIZATION: Handle all notifications/emails/SMS/tab counts async (non-blocking)
      const handleNotificationsAsync = async () => {
        try {
      emitProgress('generating_notifications', `Generating system notifications...`, 45);
      
      // let request = await Request.getRequestById(requestId);
          let requestPartners = await RequestPartner.getRequestPartnerByIds({}, requestId);
      console.log("#########", request);
          let reqActivityMsg = `🔄 Estimate "${request[0].name}" has been converted to Sales Order`;
      let notificationMsg = `The estimate ${request[0].name} has been converted to Sales Order.`;
      let notificationUrl = `request/view/${requestId}`;

      emitProgress('sending_user_notifications', `Sending notifications to users...`, 50);

          // User notification
          await Admin.incrementField(userId, "noti_count", 1);
          await Admin.addNotification({
        user_id: userId,
        url: notificationUrl,
        timestamp: currentDate,
        message: notificationMsg,
        status: 1,
        from_id: userId,
          });

          // Partner notifications (parallelized)
      if (requestPartners.length > 0) {
            const partnerNotificationPromises = requestPartners.map(async (reqId) => {
              await Admin.incrementField(reqId['id'], "noti_count", 1);
              await Admin.addNotification({
            user_id: reqId['id'],
            url: notificationUrl,
            timestamp: currentDate,
            message: notificationMsg,
            status: 1,
            from_id: userId,
              });
            });
            await Promise.all(partnerNotificationPromises);
      }

      emitProgress('sending_admin_notifications', `Sending notifications to administrators...`, 55);
      
          // Admin notifications (parallelized)
      let adminIds = await Admin.getAllAdminIds();
          const adminNotificationPromises = adminIds
            .filter(adminId => adminId != userId)
            .map(async (adminId) => {
              await Admin.incrementField(adminId, "noti_count", 1);
              await Admin.addNotification({
            user_id: adminId,
            url: notificationUrl,
            timestamp: currentDate,
            message: notificationMsg,
            status: 1,
            from_id: userId,
              });
            });
          await Promise.all(adminNotificationPromises);
      
      emitProgress('adding_activity_log', `Adding activity log entry...`, 60);
          await RequestActivity.addRequestUpdate({
            request_id: requestId,
            activity: reqActivityMsg,
            added_by: userId,
            timestamp: currentDate,
            status: 1
          });
      
      emitProgress('activity_log_added', `Activity log entry added successfully`, 65);

      let adminMangerIds = await Admin.getAllAdminAndManagerUserIds();
      emitProgress('sending_email_sms_notifications', `Sending email and SMS notifications...`, 70);
      
          let allUsersToSendMessage = [...new Set([userId, ...PartnerUserIds, ...adminMangerIds].filter(Boolean))];
          console.log('allUsersToSendMessage', allUsersToSendMessage);

          // Parallelize email/SMS sending
          const emailSmsPromises = allUsersToSendMessage.map(async (userIdToCheck) => {
            try {
            const creator = await Admin.getUserNotificationAndPermissionSettings(userIdToCheck);
              if (!creator) return;

              const emailPromise = (async () => {
                if (creator.emailEnabled === 1 && creator.email && creator?.pcb_new_sales_order === 1 && creator?.pcb_new_sales_order_email === 1) {
          emitProgress('sending_email', `Sending email notification to ${creator.name}...`, 75);
          const welcome = `Hello ${creator.name},`;
          const subject = `📦 New Sales Order Created ( Estimate Converted ): ${request[0].name}`;
          const email_content = `A new sales order has been created. Please review the details in your software.`;

          let message = newEstimateCreatedEmailTemplate({
            user: creator,
            requestName: request[0].name,
            projectType: request[0].project_type,
            total: request[0].total || 0,
            baseUrl: BASE_URL,
            type: 'sales_order',
            convertion: true,
          });

          message = message.replace('[emailContent]', email_content);
          message = message.replace('[CONTENT]', welcome);
          message = message.replace('[LOGIN_URL]', `${BASE_URL}`);

          const mailOptions = {
            to: creator.email,
            from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
            subject,
            text: subject,
            html: message,
          };

            await transporter.sendMail(mailOptions);
            console.log(`✅ Sales order email sent to: ${creator.email}`);
          }
              })();

              const smsPromise = (async () => {
                if (creator?.smsEnabled === 1 && creator?.phone && creator?.pcb_new_sales_order === 1 && creator?.pcb_new_sales_order_sms === 1) {
          emitProgress('sending_sms', `Sending SMS notification to ${creator.name}...`, 80);
          const smsContent =
            `📦 New Sales Order Created ( Estimate Converted )!\n` +
            `📄 Name: "${request[0].name}"\n` +
            `💰 Total: ₹${request[0].total || 0}\n` +
            `🔗 View: ${BASE_URL}\n` +
            `— SignWise`;
  
          const recipientPhone = creator.phone || TWILIO_PHONE;
          const Phone_country_code = creator.phone_country_code || '';
  
                  await sendMessageToPhoneNumber(recipientPhone, smsContent, Phone_country_code);
            console.log(`✅ SMS sent to ${creator.name || 'Unknown'} (${recipientPhone}) for Sales Order "${request[0].name}"`);
                }
              })();

              await Promise.all([emailPromise, smsPromise]);
          } catch (err) {
              console.error(`❌ Error sending notifications to user ${userIdToCheck}:`, err);
          }
          });

          await Promise.all(emailSmsPromises);

      emitProgress('preparing_tab_updates', `Preparing user tab count updates...`, 85);
      
      // const allUserIds = await Admin.getAllUserIds();
          // console.log("partnerssssID", PartnerUserIds);
          // let allUsersToCheck = [...new Set([userId, ...PartnerUserIds, ...allUserIds].filter(Boolean))];
    
      // const updateEstimateCounts = async (userId) => {
      //   const userOrderCount = await Request.getSalesOrderByUserId(userId);
      //   if (!userOrderCount || userOrderCount === "No record found") {
      //     const salesOrderData = {
      //       open_count: 0,
      //       approved_count: 0,
      //       in_production_count: 0,
      //       installs_count: 0,
      //       pickup_count: 0,
      //       shipping_count: 0,
      //       invoicing_count: 0,
      //       completed_count: 0,
      //       timestamp: moment().unix(),
      //       user_id: userId,
      //     };
      //     await Request.addSalesOrderTabCount(salesOrderData);
      //   }
      
      //   await Request.EstimateTabIncrementField(userId, 'approved_count', -1, 'approved_count_ids', requestId, false);
      //   await Request.EstimateTabIncrementField(userId, 'archive_count', 1, 'archive_count_ids', requestId, true);
      //   await Request.salesOrderIncrementField(userId, 'open_count', 1, 'open_count_ids', requestId, true);
      // };

        // emitProgress('updating_tab_counts', `Updating user tab counts...`, 85);
        // await Promise.all(allUsersToCheck.map(userId => updateEstimateCounts(userId)));
        //   console.log("successfully added");
        } catch (error) {
          console.error('Error in async notifications handler:', error);
        }
      };

      // ✅ OPTIMIZATION: Run non-critical operations asynchronously (fire and forget)
      // These will complete in the background without blocking the response
      handleNotificationsAsync().catch(error => {
        console.error('Error in async operations:', error);
      });

    }
    else {
      reply.send({ status: 0, message: "Failed to  convert estimate" });
    }
  }
  catch (error) {
    console.log(error, 'Error in convertQuotation');
    reply.send({ status: 500, message: "Server error", error });
  }
}
const getAllClientRequestId = async (req, reply) => {
  let { requestId } = req.body
  let data = await Request.getAllClientRequestId(requestId)
  if (!empty(data)) {
    reply.send({ status: 1, data });
  }
  else {
    reply.send({ status: 0, message: "No Active Client" });
  }
}

const uploadDesigns = async (req, reply) => {
  let { requestId, userId, uploaded, type, size, extension, socketId } = req.body

  // 🚀 PROGRESS TRACKING: Socket progress emission function
  const emitProgress = (step, message, progress) => {
    const progressData = {
      step,
      message,
      progress,
      userId,
      domain: DOMAIN,
      timestamp: moment().unix(),
      type: 'upload_designs_progress'
    };
    
    // Emit to working socket server (same as chat)
    console.log(`[UPLOAD-DESIGNS-PROGRESS] 📡 Emitting progress to working socket server...`);
    try {
      const workingSocket = ioClient(REST_API_IO, {
        path: '/socket.io',
        transports: ['websocket', 'polling'],
        secure: true,
        timeout: 5000
      });
      
      workingSocket.on('connect', () => {
        console.log(`[UPLOAD-DESIGNS-PROGRESS] 🔌 Connected to working socket server`);
        console.log(`[UPLOAD-DESIGNS-PROGRESS] 📊 Emitting progress data:`, progressData);
        
        // Try emitting as 'message' event (same as chat) to test if socket server handles it
        workingSocket.emit('message', {
          ...progressData,
          type: 'progress',
          conversationId: `upload_designs_progress_${userId}`,
          senderId: 'system'
        });
        
        // Also try the original request_progress event
        workingSocket.emit('request_progress', progressData);
        
        console.log(`[UPLOAD-DESIGNS-PROGRESS] ✅ Successfully emitted progress to working socket server`);
        
        // Disconnect after emitting
        setTimeout(() => {
          workingSocket.disconnect();
        }, 1000);
      });
      
      workingSocket.on('connect_error', (error) => {
        console.error(`[UPLOAD-DESIGNS-PROGRESS] ❌ Working socket connection error:`, error);
      });
      
    } catch (error) {
      console.error(`[UPLOAD-DESIGNS-PROGRESS] ❌ Error creating working socket connection:`, error);
    }
  };

  // Initial progress
  console.log(`[UPLOAD-DESIGNS-PROGRESS] 🚀 Starting design upload for user:`, userId);
  emitProgress('start', `Initializing design upload...`, 5);

  let uploadedArray = []
  const requests = await Request.getRequestById(requestId)
  console.log('requestsssssss',requests)
  if (!empty(uploaded)) {
    emitProgress('processing_uploads', `Processing uploaded files...`, 15);
    
    for (const row of uploaded) {
      let approvedImage = {}
      approvedImage.name = row;
      approvedImage.extension = extension;
      approvedImage.file_size = size;
      approvedImage.file_type = type;
      approvedImage.request_id = requestId;
      approvedImage.added_by = userId
      if(requests[0].project_category === 11){
          approvedImage.approved = 1
      }else if(type.startsWith('image')){
        approvedImage.approved = 0
      }else{
        approvedImage.approved = 1
      }
      approvedImage.timestamp = moment().unix()
      
      emitProgress('adding_attachments', `Adding request attachments...`, 30);
      
      let upload = await Request.addRequestAttachments(approvedImage)

      // Automatically convert PDF to PNG if it's a PDF
      // const isPdfFile = type === 'application/pdf' || type === 'pdf' || extension === 'pdf';
      // if (isPdfFile && upload) {
      //   try {
      //     emitProgress('converting_pdf', `Converting PDF to images...`, 35);
      //     await convertPdfToPngHelper(upload);
      //     console.log(`PDF conversion completed for document ID: ${upload}`);
      //   } catch (conversionError) {
      //     console.error(`Error converting PDF for document ${upload}:`, conversionError);
      //     // Continue even if conversion fails
      //   }
      // }

      emitProgress('checking_project_existence', `Checking project existence...`, 45);
      let isRequestEstimate = false
      if(requests[0]?.is_request_estimate == 1 && requests[0]?.quotation == 1){
        isRequestEstimate = true
      }
      let checkProjectExist = await Request.checkExistingProject(requestId, isRequestEstimate)
      if (checkProjectExist) {
        emitProgress('processing_project_attachments', `Processing project attachments...`, 60);
        
        const urlObjects = [];
        if(approvedImage?.approved === 1){
          const isImage = approvedImage?.file_type?.startsWith('image/') || approvedImage?.file_type == 'img';
          const isVideo = approvedImage?.file_type?.startsWith('video/');
          const isPdf = approvedImage?.file_type === 'application/pdf';
          let attachmentExtension = null
          if(isImage){
            attachmentExtension = 'image'
          }else if(isVideo){
            attachmentExtension = 'video'
          }else{
            attachmentExtension = 'other'
          }
          urlObjects.push({
              url: approvedImage.name,
              file_type: approvedImage.file_type || 'img',
              extension: attachmentExtension
          });
          for(let i=0; i<urlObjects?.length; i++){
            let data={}
            if(urlObjects[i]?.url){
                data.userid=userId,
                data.project_id=checkProjectExist,
                data.file_url=urlObjects[i].url
                data.extension=urlObjects[i].extension,
                data.file_type=urlObjects[i].file_type,
                data.timestamp=moment().unix()
                let inserted=await ProjectTask.addProjectAttachments(data)
            }
          }
        }
      }
      if (upload) {
        uploadedArray.push(upload)
      }
    }
    if(type.startsWith('image/')){
      emitProgress('updating_thumbnail', `Updating request thumbnail...`, 70);
      
      let requestdata = {}
      requestdata['thumbnail'] = uploaded[0]
      let updateRfq = await Request.updateRequets(requestId, requestdata)
    }
    
    emitProgress('processing_notifications', `Processing notifications...`, 80);
    
    let notification_msg = `New Design  has been uploaded`
    let notification_url = `official-request/view/${requestId}`

    let adminIds = await Admin.getAllAdminIds();
    for (let eachadminIds of adminIds) {
      if (eachadminIds != userId) {
        let incrementResult = await Admin.incrementField(eachadminIds, 'noti_count', 1)
        let addNotification = {
          user_id: eachadminIds,
          url: notification_url,
          timestamp: moment().unix(),
          message: notification_msg,
          status: 1,
          from_id: userId,
        }
        const addNotificationResult = await Admin.addNotification(addNotification);
      }
    }
    
    emitProgress('adding_request_activity', `Adding request activity...`, 90);
    
    let requestActivity = {}
    requestActivity['request_id'] = requestId;
    requestActivity['activity'] = notification_msg;
    requestActivity['added_by'] = userId
    requestActivity['timestamp'] = moment().unix()
    let reqActivityId = await RequestActivity.addRequestUpdate(requestActivity)
    
    emitProgress('complete', `Design uploaded successfully!`, 100);
    reply.send({ status: 1, message: "New design/document added!.", data: uploadedArray })

  }
  else {
    emitProgress('error', `Failed to upload design`, 0);
    reply.send({ status: 0, message: "Failed to add Designs!." })
  }
}
const updateDesigns = async (req, reply) => {
  let { requestId, userId, socketId, ...data } = req.body

  // 🚀 PROGRESS TRACKING: Socket progress emission function
  const emitProgress = (step, message, progress) => {
    const progressData = {
      step,
      message,
      progress,
      userId,
      domain: DOMAIN,
      timestamp: moment().unix(),
      type: 'update_designs_progress'
    };
    
    // Emit to working socket server (same as chat)
    console.log(`[UPDATE-DESIGNS-PROGRESS] 📡 Emitting progress to working socket server...`);
    try {
      const workingSocket = ioClient(REST_API_IO, {
        path: '/socket.io',
        transports: ['websocket', 'polling'],
        secure: true,
        timeout: 5000
      });
      
      workingSocket.on('connect', () => {
        console.log(`[UPDATE-DESIGNS-PROGRESS] 🔌 Connected to working socket server`);
        console.log(`[UPDATE-DESIGNS-PROGRESS] 📊 Emitting progress data:`, progressData);
        
        // Try emitting as 'message' event (same as chat) to test if socket server handles it
        workingSocket.emit('message', {
          ...progressData,
          type: 'progress',
          conversationId: `update_designs_progress_${userId}`,
          senderId: 'system'
        });
        
        // Also try the original request_progress event
        workingSocket.emit('request_progress', progressData);
        
        console.log(`[UPDATE-DESIGNS-PROGRESS] ✅ Successfully emitted progress to working socket server`);
        
        // Disconnect after emitting
        setTimeout(() => {
          workingSocket.disconnect();
        }, 1000);
      });
      
      workingSocket.on('connect_error', (error) => {
        console.error(`[UPDATE-DESIGNS-PROGRESS] ❌ Working socket connection error:`, error);
      });
      
    } catch (error) {
      console.error(`[UPDATE-DESIGNS-PROGRESS] ❌ Error creating working socket connection:`, error);
    }
  };

  // Determine operation type and set appropriate messages
  let operationType = "update";
  let operationMessage = "Initializing design update...";
  
  if (data?.status === 0) {
    operationType = "delete";
    operationMessage = "Initializing design deletion...";
  } else if (data.approved === 0 && !data.product_id) {
    operationType = "reject";
    operationMessage = "Initializing design rejection...";
  } else if (data.approved === 1) {
    operationType = "approve";
    operationMessage = "Initializing design approval...";
  } else if (data.product_id) {
    operationType = "link";
    operationMessage = "Initializing design linking...";
  }

  // Initial progress
  console.log(`[UPDATE-DESIGNS-PROGRESS] 🚀 Starting design ${operationType} for user:`, userId);
  emitProgress('start', operationMessage, 5);

  let uploadedArray = []
  if (!empty(data)) {
    emitProgress('updating_attachments', `${operationType === 'delete' ? 'Removing' : operationType === 'reject' ? 'Rejecting' : operationType === 'approve' ? 'Approving' : operationType === 'link' ? 'Linking' : 'Updating'} request attachments...`, 15);
    
    // let data={}
    // data.name=row;
    // data.request_id=requestId;
    // data.added_by=userId
    // data.timestamp=moment().unix()
    
    // Handle status change to 0 (soft delete) - update image status to 0
    if (data.status === 0 && data.id) {
      await updateRequestAttachmentImagesStatusByDocumentId(data.id, 0, 'request');
      console.log(`✅ Updated image status to 0 for document ID: ${data.id}`);
    }
    // If file is being replaced (name field is updated), delete old images first
    else if (data.id && data.name) {
      const existingAttachment = await Request.getRequestAttachmentById(data.id);
      if (existingAttachment && existingAttachment.name !== data.name) {
        // File is being replaced, delete old images
        await deleteRequestAttachmentImagesHelper(data.id, 'request');
      }
    }
    
    let upload = await Request.updateRequestAttachments(requestId, data)
    let attachment = await Request.getRequestAttachmentById(data?.id)
    
    // If this is a PDF and was just added/updated, convert it
    // if (attachment && (attachment.file_type === 'application/pdf' || attachment.file_type === 'pdf' || attachment.extension === 'pdf')) {
    //   const existingImages = await getRequestAttachmentImagesByDocumentId(attachment.id, 'request');
    //   if (!existingImages || existingImages.length === 0) {
    //     // No images exist, convert PDF to PNG
    //     try {
    //       emitProgress('converting_pdf', `Converting PDF to images...`, 35);
    //       await convertPdfToPngHelper(attachment.id);
    //     } catch (conversionError) {
    //       console.error(`Error converting PDF for document ${attachment.id}:`, conversionError);
    //     }
    //   }
    // }

    emitProgress('fetching_request_data', `Fetching request data...`, 30);

    let existingRequestData = await Request.FindReqDataById(requestId)
    let reqData={
      status:3
    }
    if(data.approved == 1 && existingRequestData.quotation !==1 && existingRequestData.status !==1 && existingRequestData.status !==2 && existingRequestData.status !==3){
      emitProgress('updating_request_status', `Updating request status...`, 40);
      
      let updateRequest = await Request.updateRequestById(requestId,reqData)
      console.log('updateRequest',updateRequest)
    }

    emitProgress('checking_project_existence', `Checking project existence...`, 50);
    let isRequestEstimate = false
    if(existingRequestData.is_request_estimate === 1 && existingRequestData.quotation == 1){
      isRequestEstimate = true
    }
    let checkProjectExist = await Request.checkExistingProject(requestId, isRequestEstimate)
    if (checkProjectExist) {
      emitProgress('processing_project_attachments', `Processing project attachments...`, 60);
      
      const urlObjects = [];
      if(data?.approved === 1){
        const isImage = attachment?.file_type?.startsWith('image/') || attachment?.file_type == 'img';
        const isVideo = attachment?.file_type?.startsWith('video/');
        const isPdf = attachment?.file_type === 'application/pdf';
        let attachmentExtension = null
        if(isImage){
          attachmentExtension = 'image'
        }else if(isVideo){
          attachmentExtension = 'video'
        }else{
          attachmentExtension = 'other'
        }
        urlObjects.push({
            url: attachment.name,
            file_type: attachment.file_type || 'img',
            extension: attachmentExtension
        });
        for(let i=0; i<urlObjects?.length; i++){
          let data={}
          if(urlObjects[i]?.url){
              data.userid=userId,
              data.project_id=checkProjectExist,
              data.file_url=urlObjects[i].url
              data.file_type=urlObjects[i].file_type,
              data.extension=urlObjects[i].extension,
              data.timestamp=moment().unix()
              let inserted=await ProjectTask.addProjectAttachments(data)
          }
        }
      }else if(data?.approved === 0){
        const attachmentDeleted = await ProjectTask.deleteProjectAttachmentsByURL(attachment?.name, checkProjectExist)
      }
    }

    emitProgress('processing_notifications', `Processing notifications...`, 75);

    let notification_msg = `Design has been ${operationType === 'approve' ? `linked with catalog "${data[0]?.product_name}" and approved` :
      operationType === 'reject' ? 'Design has been rejected' :
        operationType === 'link' ? `Design has been linked with catalog "${data[0]?.product_name}"` :
          operationType === 'delete' ? 'Design has been deleted' : 'Design has been updated'}`;

    let notification_url = `official-request/view/${requestId}`

    let adminIds = await Admin.getAllAdminIds();
    for (let eachadminIds of adminIds) {
      if (eachadminIds != userId) {
        let incrementResult = await Admin.incrementField(eachadminIds, 'noti_count', 1)
        let addNotification = {
          user_id: eachadminIds,
          url: notification_url,
          timestamp: moment().unix(),
          message: notification_msg,
          status: 1,
          from_id: userId,
        }
        const addNotificationResult = await Admin.addNotification(addNotification);
      }
    }
    
    emitProgress('adding_request_activity', `Adding request activity...`, 90);
    
    let requestActivity = {}
    requestActivity['request_id'] = requestId;
    requestActivity['activity'] = notification_msg;
    requestActivity['added_by'] = userId
    requestActivity['timestamp'] = moment().unix()
    let reqActivityId = await RequestActivity.addRequestUpdate(requestActivity)
    
    emitProgress('complete', `Design ${operationType === 'delete' ? 'deleted' : operationType === 'reject' ? 'rejected' : operationType === 'approve' ? 'approved' : operationType === 'link' ? 'linked' : 'updated'} successfully!`, 100);
    reply.send({ status: 1, message: "Design succesfully updated!." })

  }
  else {
    emitProgress('error', `Failed to ${operationType} design`, 0);
    reply.send({ status: 0, message: "Failed to update Design!." })
  }
}


//////////////////////////////////////////////Quickbook payment

const applyPaymentToQuickBooks = async (invoiceId, amount) => {
  try {
    const token = await getNewAccessToken();
    // const invoiceUrl = `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/invoice/${invoiceId}?minorversion=65`;

    const apiUrl = PRODUCTION
    ? `https://quickbooks.api.intuit.com/v3/company/${realmId}`
    : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}`;

    const invoiceUrl = `${apiUrl}/invoice/${invoiceId}?minorversion=65`;
  

    // Fetch the latest invoice from QuickBooks
    const invoiceResponse = await axios.get(invoiceUrl, {
      headers: {
        Authorization: `Bearer ${token}`,
        Accept: 'application/json'
      }
    });

    const invoice = invoiceResponse.data.Invoice;
    const remainingBalance = invoice.Balance;

    console.log("Current invoice balance:", remainingBalance);

    if (amount > remainingBalance) {
      throw new Error("Payment amount exceeds the remaining balance.");
    }

    // Prepare the payment data for QuickBooks
    const paymentData = {
      CustomerRef: invoice.CustomerRef,
      TotalAmt: amount,
      DepartmentRef: { value: departmentId },
      Line: [{
        Amount: amount,
        LinkedTxn: [{
          TxnId: invoiceId,
          TxnType: "Invoice"
        }]
      }]
    };

    // Apply the payment to the invoice in QuickBooks
    const paymentResponse = await axios.post(
      // `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/payment`,
      `${apiUrl}/payment`,
      paymentData,
      {
        headers: {
          Authorization: `Bearer ${token}`,
          'Content-Type': 'application/json',
          Accept: 'application/json'
        }
      }
    );

    return paymentResponse.data;

  } catch (error) {
    console.error("Error applying payment to QuickBooks:", error.response ? error.response.data : error.message);
    throw error;
  }
};


///////////////////////////////////////////////////
const addRequestPayments = async (req, reply) => {
  try {
    let { userId, amount, title, requestId } = req.body.paymentSubmit;
    let currentDate = moment().unix()
    let data = {}
    data.request_id = requestId
    data.amount = amount
    data.title = title
    data.added_by = userId
    data.timestamp = moment().unix();
    data.paymentMode = req.body?.paymentType
    let request = await Request.getRequestById(requestId);
    let total_paid_amount =await Request.findTotalPaidAmount(requestId)
    let percentage =request[0]?.billing_type
    let total_amount = request[0]?.total
  
    let minimum_amount = parseFloat((total_amount * percentage) /100).toFixed(2)
    console.log("addRequestPayments===minimum_amount", minimum_amount)
    console.log("addRequestPayments===total_paid_amount", total_paid_amount)
    console.log("addRequestPayments===amount", amount)
    if((Number(total_paid_amount || 0) + Number(amount || 0)) >= minimum_amount) {
      console.log("addRequestPayments===minimum_amount reached")
      let data={
        min_payment_status :1
      }
      let result = await Request.editRequestTable(requestId,data)

      if(request[0]?.is_split_order == 1 && request[0]?.status == 2 && request[0]?.project_category != 15){
        console.log("addRequestPayments===updateRequestInCurrentFacility")
        let updateRequestInCurrentFacility = await Request.updateRequestByOriginFacilityId(request[0]?.origin_facility_id, requestId, {
          min_payment_status :1,
          status : 2
        })
        const facilityTypes = (request[0]?.facility_type || "")
          .split(",")
          .map(v => v.trim())
          .filter(v => v !== "");
        const targetedFacilities = facilityTypes.filter(id => id !== String(FACILITY_ID));
        // const targetedFacilities = otherFacilities?.map(facility => facility.id)

        let updateRequestForOtherFacility = await SplitManagementController.updateBuildOrderToOtherInstallation(targetedFacilities, requestId, {
          min_payment_status :1,
          status : 2
        })
      }
    }
    
    // /////////////////////////////////////quickbook
    // const request = await Request.getRequestById(requestId);
    // const invoiceId = request[0].estimate_id;


    // const paymentResponse = await applyPaymentToQuickBooks(invoiceId, amount);

    // console.log("Payment applied to QuickBooks invoice:", paymentResponse);
    
    try {
        
      const invoiceId = request[0].estimate_id;

      const paymentResponse = await applyPaymentToQuickBooks(invoiceId, amount);

      console.log("Payment applied to QuickBooks invoice:", paymentResponse);
    } catch (quickBooksError) {
      console.log("Error applying payment to QuickBooks:", quickBooksError);
    }

    ///////////////////////////////////////
    let addPayment = await Request.addRequestPayment(data);
    const request_id = requestId;
    // if (addPayment) {

    //   let notificationMsg = `New payment "${title}:$${amount}" has been marked as received.`;
    //   let notificationUrl = "official-request/view/${request_id}";
    //   const incrementResult_User = await Admin.incrementField(
    //     userId,
    //     "noti_count",
    //     1
    //   );

    //   let notificationData_user = {
    //     user_id: userId,
    //     url: notificationUrl,
    //     timestamp: currentDate,
    //     message: notificationMsg,
    //     status: 1,
    //     from_id: userId,
    //   };
    //   const addNotificationResult_user = await Admin.addNotification(
    //     notificationData_user
    //   );
    //   let adminIds = await Admin.getAllAdminIds();
    //   let notificationData = {};

    //   for (const adminId of adminIds) {

    //     if (adminId != userId) {
    //       const incrementResult = await Admin.incrementField(
    //         adminId,
    //         "noti_count",
    //         1
    //       );

    //       notificationData = {
    //         user_id: adminId,
    //         url: notificationUrl,
    //         timestamp: currentDate,
    //         message: notificationMsg,
    //         status: 1,
    //         from_id: userId,
    //       };

    //       const addNotificationResult = await Admin.addNotification(
    //         notificationData
    //       );

    //     }
    //   }
    //   let requestActivity = {}
    //   requestActivity['request_id'] = requestId;
    //   requestActivity['activity'] = notificationMsg;
    //   requestActivity['added_by'] = userId
    //   requestActivity['timestamp'] = currentDate
    //   let reqActivityId = await RequestActivity.addRequestUpdate(requestActivity)

    // //   const allUserIds = await Admin.getAllUserIds();
    // // const PartnerUserIds = await RequestPartner.fetchPartnerUserIds(requestId);
    // // const usersToUpdate = [...new Set([userId, ...PartnerUserIds, ...allUserIds].filter(Boolean))];

    // // const updateEditCounts = async (userId) => {
    // //   await Request.salesOrderIncrementField(userId, 'open_count', -1, 'open_count_ids', requestId, false);
    // //   await Request.salesOrderIncrementField(userId, 'approved_count', 1, 'approved_count_ids', requestId, true);
    // //   console.log(`Counts updated for user ${userId}: open -1, approved +1`);
    // // };

    // // await Promise.all(usersToUpdate.map(userId => updateEditCounts(userId)));

    //   reply.send({ status: 1, message: "New payment added successfully" })
    // }
    if (addPayment) {
      // Response to be sent first
      reply.send({ status: 1, message: "New payment added successfully" });
    
      // Async function to handle notifications
      const handleAsyncNotifications = async () => {
        try {
          // Define missing fields based on your original code context
          let notificationMsg = `New payment "${title}:$${amount}" has been marked as received.`;
          let notificationUrl = `official-request/view/${request_id}`;
          const currentDate = moment().unix(); // Assuming `currentDate` needs to be defined
      
          // Notify the user
          const incrementResult_User = await Admin.incrementField(
            userId,
            "noti_count",
            1
          );
      
          let notificationData_user = {
            user_id: userId,
            url: notificationUrl,
            timestamp: currentDate,
            message: notificationMsg,
            status: 1,
            from_id: userId,
          };
      
          const addNotificationResult_user = await Admin.addNotification(
            notificationData_user
          );
      
          // Notify all admins
          let adminIds = await Admin.getAllAdminIds();
          let notificationData = {};
      
          for (const adminId of adminIds) {
            if (adminId != userId) {
              const incrementResult = await Admin.incrementField(
                adminId,
                "noti_count",
                1
              );
      
              notificationData = {
                user_id: adminId,
                url: notificationUrl,
                timestamp: currentDate,
                message: notificationMsg,
                status: 1,
                from_id: userId,
              };
      
              const addNotificationResult = await Admin.addNotification(
                notificationData
              );
            }
          }
      
          // Adding request activity log
          let requestActivity = {};
          requestActivity['request_id'] = requestId; // Assuming requestId is defined
          requestActivity['activity'] = notificationMsg;
          requestActivity['added_by'] = userId;
          requestActivity['timestamp'] = currentDate;
      
          let reqActivityId = await RequestActivity.addRequestUpdate(requestActivity);
        } catch (error) {
          console.error("Error in async notifications:", error);
        }
      };      
    
      // Execute the notifications asynchronously (after sending the response)
      handleAsyncNotifications().catch(error => {
        console.error("Error in handling async notifications:", error);
      });
    }    
    else {
      reply.send({ status: 0, message: "Failed to add New Payment" })
    }
  }
  catch (error) {
    console.log("Error in addRequestPayments:" + error);
  }
}

const editRequestPayments = async (req, reply) => {
  let { userId, amount, title, requestId, id } = req.body.paymentSubmit;
  let currentDate = moment().unix()
  let data = {}
  data.request_id = requestId
  data.amount = amount
  data.title = title
  data.updated_by = userId
  data.timestamp = moment().unix();
  console.log("Edit$$$$$$$$$$$", data);
  let addPayment = await Request.updateRequestPayment(data, id);
  if (addPayment) {
    let notificationMsg = `New payment "${title}:$${amount}" has been updated.`;
    let notificationUrl = "official-request/view/${request_id}";
    const incrementResult_User = await Admin.incrementField(
      userId,
      "noti_count",
      1
    );



    let notificationData_user = {
      user_id: userId,
      url: notificationUrl,
      timestamp: currentDate,
      message: notificationMsg,
      status: 1,
      from_id: userId,
    };
    const addNotificationResult_user = await Admin.addNotification(
      notificationData_user
    );
    let adminIds = await Admin.getAllAdminIds();
    let notificationData = {};

    for (const adminId of adminIds) {

      if (adminId != userId) {
        const incrementResult = await Admin.incrementField(
          adminId,
          "noti_count",
          1
        );

        notificationData = {
          user_id: adminId,
          url: notificationUrl,
          timestamp: currentDate,
          message: notificationMsg,
          status: 1,
          from_id: userId,
        };

        const addNotificationResult = await Admin.addNotification(
          notificationData
        );

      }
    }
    let requestActivity = {}
    requestActivity['request_id'] = requestId;
    requestActivity['activity'] = notificationMsg;
    requestActivity['added_by'] = userId
    requestActivity['timestamp'] = currentDate
    let reqActivityId = await RequestActivity.addRequestUpdate(requestActivity)

    // const allUserIds = await Admin.getAllUserIds();
    // const PartnerUserIds = await RequestPartner.fetchPartnerUserIds(requestId);
    // const usersToUpdate = [...new Set([userId, ...PartnerUserIds, ...allUserIds].filter(Boolean))];

    // const updateEditCounts = async (userId) => {
    //   await Request.salesOrderIncrementField(userId, 'open_count', -1, 'open_count_ids', requestId, false);
    //   await Request.salesOrderIncrementField(userId, 'approved_count', 1, 'approved_count_ids', requestId, true);
    //   console.log(`Counts updated for user ${userId}: open -1, approved +1`);
    // };

    // await Promise.all(usersToUpdate.map(userId => updateEditCounts(userId)));

    reply.send({ status: 1, message: "Paymenyt updated successfully" })
  }
  else {
    reply.send({ status: 0, message: "Failed to update Payment" })
  }

}
const fetchAllPayments = async (req, reply) => {
  const { request_id } = req.body;

  let payments = await RequestPartner.fetchAllRequestPayments(request_id);
  reply.send({
    payments,
    message: "Data fetched successfully"
  });
}
const deletePayment = async (req, res) => {
  const { id } = req.body;

  let result = await Request.deletePayment(id)
  if (result) {
    res.send({ status: 1, data: result, message: 'Payment deleted successfully' })
  } else {
    res.send({ status: 0, message: 'Failed to delete Payment' })
  }
}
const addClient = async (req, res) => {
  const { request_id, values, userId, userName, userType } = req.body;
  let clientDetails = await Client.getClientById(userId);

  const currentTime = moment().unix();
  let subject = 'A new request initiated by client';
  let email_content = `A new request initiated by client` + userName;
  let client = {}
  let insertedstatus = null
  client['request_id'] = request_id,
    client['last_seen'] = currentTime,
    client['timestamp'] = currentTime,
    client['added_by'] = userId,
    client['partner_id'] = clientDetails.id
  client['accept_status'] = 1;
  client['status'] = 1
  insertedstatus = await RequestPartner.addRequestPartners(client)
  if (insertedstatus) {

    let requestActivity = {}

    requestActivity['request_id'] = request_id,
      requestActivity['partner_id'] = eachReqPartner.split(',')[0],
      requestActivity['partner_name'] = eachReqPartner.split(',')[1],
      requestActivity['activity'] = `Client,${client.name} has initiated the Request`,
      requestActivity['added_by'] = userId,
      requestActivity['timestamp'] = currentTime,
      requestActivity['addestatusd_by'] = 1
    let activityResult = await RequestUpdates.addRequestUpdates(requestActivity)
    let requestdata = {}
    requestdata['customer_id'] = clientDetails['id'];
    requestdata['customer_name'] = clientDetails['name'];
    requestdata['customer_phone'] = clientDetails['phone'];
    requestdata['customer_address'] = clientDetails['address'];
    let orderId = await CustomerOrder.getCustomerOrderRequestId(request_id);
    await CustomerOrder.updateCustomerOrder(orderId.id, requestdata);
    const request = await Request.getRequestById(request_id)
    const requestTeams = await Request.getRequestPartnersByIds(partnerIds)
    const requestTeamUserIds = await Request.getRequestTeamUserIds(request_id)
    const adminIds = await Admin.getAllAdminIds()



    // Notification 
    let notification_msg =
      `${client.name}"</strong> has been initiated new  request <strong>"${request[0].name}"</strong>."`
    let notification_url = `official-request/view/${request_id}`

    // Alert assigned user of updation
    let incrementResult = await Admin.incrementField(requestTeam.id, "noti_count", 1);
    let notificationUser = {
      user_id: requestTeam.id,
      url: notification_url,
      timestamp: currentTime,
      message: notification_msg,
      status: 1,
      from_id: userId,
    }
    const addNotificationResult = await Admin.addNotification(notificationUser);
    for (let request_team_us_id of requestTeamUserIds) {
      // Alert assigned user of updation
      let incrementResult = await Admin.incrementField(request_team_us_id, 'noti_count', 1)
      let addNotification = {
        user_id: request_team_us_id,
        url: notification_url,
        timestamp: currentTime,
        message: notification_msg,
        status: 1,
        from_id: userId,
      }
      const addNotificationResult = await Admin.addNotification(addNotification);
    }
    for (let adminids of adminIds) {
      let incrementResult = await Admin.incrementField(adminids, 'noti_count', 1)
      let addNotification = {
        user_id: adminids,
        url: notification_url,
        timestamp: currentTime,
        message: notification_msg,
        status: 1,
        from_id: userId,
      }
      const addNotificationResult = await Admin.addNotification(addNotification);
    }


    let message = rfqEmail()
    message = message.replace('[emailContent]', email_content)
    message = message.replace('[CONTENT]', welcome)
    message = message.replace('[NAME]', eachPartner['username'])
    message = message.replace('[PASSWORD]', values.password)
    message = message.replace('[LOGIN_URL]', `${BASE_URL}`)
    let adminEmails = await Admin.getAllAdminEmails();
    for (const row of adminEmails) {
      console.log("$$$$$$$$$$$$$$$nush", row);
      const msg = {
        to: row, // Change to your recipient
        from: 'nushara@skyniche.com', // Change to your verified sender
        subject: adminSubject,
        text: adminSubject,
        html: adminMessage,
      }
      // sgMail
      //   .send(msg)
      //   .then(() => {
      //     console.log('Email sent')
      //   })
      //   .catch((error) => {
      //     console.error(error)
      //   })

      const mailOptions = {
        to:  row, 
        from: '"SignWise Solutions" <noreply@signwisesolutions.com>', //'nushara@skyniche.com', // Change to your verified sender
        subject: adminSubject,
        text: adminSubject,
        html: adminMessage,
      }
      try {
        if(SEND_EMAIL){
        await transporter.sendMail(mailOptions);
        console.log('Email sent');
        }
      } catch (error) {
        console.error('##########Err', error);
       
      }
    }

    res.send({ status: 1, message: 'Partners added successfully' });
  } else {
    res.send({ status: 0, message: 'Failed to add partners' });
  }

}

const getRequestByCommunityId = async (req, res) => {
  try {
    const { communityId, userId, userType, hasPermission } = req.body;
    let clientDetails = await Client.getPartnerByUserId(userId)
    console.log("################clientDetails",clientDetails);
    let result = await Request.getRequestByCommunityId(communityId, userId, userType, hasPermission,clientDetails.id)
    if (result) {
      res.send({ status: 1, data: result, message: "Data fetched successfully" });
    }
    else {
      res.send({ status: 0, message: "Error in fetching requets by id" })
    }
  } catch (error) {
    console.log(error, 'error in get request by community id')
    res.send({ status: 0, message: 'failed to get request by community id', error: error })
  }
}

const getCompletedInstallsByCommunityId = async (req, res) => {
  try {
    // const { communityId, userId, userType, hasPermission } = req.body;
    const userId = req.body.userId
    let page = req.body.page
    let rowsPerPage = req.body.rowsPerPage
    const userType = req.body.userType
    const hasPermission = req.body.hasPermission || false
    let sort = req.body.sort
    let fromDate = req?.body?.from
    let toDate = req?.body?.to
    let dueData = req?.body?.dueData;
    let search = req?.body?.search;
    let communityId = req?.body?.communityId
    let filterValue = req?.body?.filterValue
    let filterClient = req?.body?.filterClient
    let filterProjectType = req?.body?.filterProjectType
    let clientDetails = await Client.getPartnerByUserId(userId)
    console.log("################clientDetails",clientDetails);
    let result = await Request.getCompletedInstallsByCommunityId(communityId, userId, userType, hasPermission,clientDetails.id,page ,rowsPerPage,sort,fromDate,toDate,dueData,search,filterValue,filterClient,filterProjectType)
    let filteredInstalls = await Request.getCompletedInstallsByCommunityIdForFilter(communityId ,userId, userType, hasPermission, sort, fromDate, toDate,filterClient)
    let clients = await Client.getAllClientsDataFilters();
    console.log("filterrrrrrr" , filteredInstalls)
    // ORIGINAL CODE - BACKUP (N+1 Query Problem)
    // for (const data of result.data) {
    //   let attachment = await Request.getAllRequestAttachmentsForCards(data.id, data.isFromDesignOrder);
    //   data.attachment = attachment || [];
    // }

    // OPTIMIZED VERSION - All attachments are now fetched within the optimized model
    if (result && filteredInstalls) {
      let totalCount = result.totalCount || 0;
      let data = result.data || [];
      res.send({ status: 1, data: data,totalCount:totalCount,filteredInstalls:filteredInstalls ,clients:clients, message: "Data fetched successfully" });
    }
    else {
      res.send({ status: 0, message: "Error in fetching requets by id" })
    }
  } catch (error) {
    console.log(error, 'error in get request by community id')
    res.send({ status: 0, message: 'failed to get request by community id', error: error })
  }
}

const getProjectByCommunityId = async (req, res) => {
  try {
    const { communityId, userId, userType, hasPermission } = req.body;
    let clientDetails = await Client.getPartnerByUserId(userId)
    let result = await Request.getProjectByCommunityId(communityId, userId, userType, hasPermission,clientDetails.id)
    if (result) {
      res.send({ status: 1, data: result, message: "Data fetched successfully" });
    }
    else {
      res.send({ status: 0, message: "Error in fetching projects by id" })
    }
  } catch (error) {
    console.log(error, 'error in get projects by community id1')
    res.send({ status: 0, message: 'failed to get projects by community id', error: error })
  }
}

// const getCompleteDesignByCommunityId = async (req, res) => {
//   try {
//     // const { communityId, userId, userType, hasPermission } = req.body;
//     let userId = req.body.userId
//     let userType = req.body.userType
//     let page = req.body.page
//     let search = req.body.search
//     let rowPerpage = req.body.rowsPerPage || 6
//     let sort = req.body.sort
//     let fromDate = req.body.from
//     let toDate = req.body.to
//     let dueData = req.body.dueData
//     let communityId = req.body.communityId
//     let hasPermission = req.body.hasPermission || false;
//     let clientDetails = await Client.getPartnerByUserId(userId)
//     // let designs = await Request.getCompleteDesignByCommunityId(communityId, userId, userType,clientDetails.id)
//     let result = await Request.getCompleteDesignOrdersByCommunityId(communityId, userId, userType, hasPermission,clientDetails.id,page, rowPerpage, search,sort,fromDate,toDate,dueData)
//     let results = result
//     // let totalCount = await Request.getTotalCompleteDesignOrdersByCommunityId(communityId, userId, userType,clientDetails.id)
    
//     for (const row of results) {
//       let attachment = await Request.getAllDesignOrderAttachments(row.id)
//       row.attachment=attachment || []
//     }
    
//     if (result) {
//       res.send({ status: 1, data: result, message: "Data fetched successfully" });
//     }
//     else {
//       res.send({ status: 0, message: "Error in fetching projects by id" })
//     }
//   } catch (error) {
//     console.log(error, 'error in get projects by community id1')
//     res.send({ status: 0, message: 'failed to get projects by community id', error: error })
//   }
// }
const getCompleteDesignByCommunityId = async (req, res) => {
  try {
    let userId = req.body.userId;
    let userType = req.body.userType;
    let page = req.body.page;
    let search = req.body.search;
    let rowPerpage = req.body.rowsPerPage || 6;
    let sort = req.body.sort;
    let fromDate = req.body.from;
    let toDate = req.body.to;
    let dueData = req.body.dueData;
    let communityId = req.body.communityId;
    let hasPermission = req.body.hasPermission || false;
    let filterClient = req.body.filterClient
    let clientDetails = await Client.getPartnerByUserId(userId);
    console.log("#################CommunityyyyyyyyyyClient", clientDetails);
    let result = await Request.getCompleteDesignOrdersByCommunityId(
      communityId, userId, userType, hasPermission, clientDetails.id, page, rowPerpage, search, sort, fromDate, toDate, dueData,filterClient
    );
    let clients = await Client.getAllClientsDataFilters();
    if (!result || result.length === 0) {
      return res.send({ status: 0, message: "No design orders found" });
    }

    let totalCount = result[result.length - 1]?.totalCount || 0;
    result.pop();

    for (const row of result) {
      let attachment = await Request.getAllDesignOrderAttachments(row.id);
      row.attachment = attachment || [];
      let reference_attachments = await Request.getAllDesignOrderReferenceAttachments(row.id);
      row.reference_attachment = reference_attachments || [];
    }
    res.send({
      status: 1,
      data: result,
      clients:clients,
      totalCount: totalCount,
      message: "Data fetched successfully"
    });

  } catch (error) {
    console.error("Error in get projects by community id:", error);
    res.send({
      status: 0,
      message: "Failed to get projects by community id",
      error: error
    });
  }
};


const getJobByClientId = async (req, res) => {
  try {
    const { clientId, userId, userType, id } = req.body;
    let result = await Request.getJobsByClientId(clientId, userId, userType, id)
    if (result) {
      res.send({ status: 1, data: result, message: "Data fetched successfully" });
    }
    else {
      res.send({ status: 0, message: "Error in fetching projects by id" })
    }
  } catch (error) {
    console.log(error, 'error in get projects by community id2')
    res.send({ status: 0, message: 'failed to get projects by community id', error: error })
  }
}

const getProjectByClientId = async (req, res) => {
  try {
    const { clientId, userId, userType } = req.body;
    let clientDetails = await Client.getPartnerByUserId(clientId)
    let result = await Request.getProjectByClientId(clientId, userId, userType, clientDetails.id)
    if (result) {
      res.send({ status: 1, data: result, message: "Data fetched successfully" });
    }
    else {
      res.send({ status: 0, message: "Error in fetching projects by id" })
    }
  } catch (error) {
    console.log(error, 'error in get projects by community id3')
    res.send({ status: 0, message: 'failed to get projects by community id', error: error })
  }
}

const updateProductsAddress = async (request, res) => {
  console.log("Received Data in Backend:", request.body);
  try {
    let currentDate = Math.round(moment().valueOf() / 1000);
    let order_id = request.body?.order_id || null;
    let location_id = request.body?.location_id || null;
    let existingData;
    if(location_id){
      existingData = await Request.getLocationProductsByLocId(location_id);
    }else{
      existingData = await Request.getLocationProductsById(order_id);
    }

    if (!existingData) {
      return res.send({ status: 0, message: "Product not found" });
    }
    let installationData = {
      address: request.body.address ?? existingData.address,
      city: request.body.city ?? existingData.city,
      state: request.body.state ?? existingData.state,
      zip: request.body.zip ?? existingData.zip,
      lat: request.body.lat ?? existingData.lat,
      lng: request.body.lng ?? existingData.lng,
      starting_point: request.body?.starting_point ? 1 : null,
      ending_point: request.body?.ending_point ? 1 : null,
      color: request.body.color,
    };
    let updateLoc = null;
    if(location_id){
      updateLoc=await Request.updateLocation(installationData, location_id)
    }else{
      updateLoc=await Request.updateLocationProducts(installationData, order_id)
    }
    
    if (updateLoc) {
      res.send({ status: 1, data: updateLoc, message: "Route updated successfully" });
    }
    else {
      res.send({ status: 0, message: "Error in updating product route" })
    }
  } catch (error) {
    console.log("Server error", error);
    res.status(500).send({ message: "Server error" + error });
  }
};

const updateServiceAddress = async (request, res) => {
  try {
    let currentDate = Math.round(moment().valueOf() / 1000);
    let service_id = request.body.service_id;

    let existingData = await Request.getRouteServiceLocationById(service_id);
    if (!existingData) {
      return res.send({ status: 0, message: "Service not found" });
    }

    let installationData = {
      address: request.body.address ?? existingData.address,
      city: request.body.city ?? existingData.city,
      state: request.body.state ?? existingData.state,
      zip: request.body.zip ?? existingData.zip,
      lat: request.body.lat ?? existingData.lat,
      lng: request.body.lng ?? existingData.lng,
      starting_point: request.body?.starting_point ? 1 : null,
      ending_point: request.body?.ending_point ? 1 : null,
      color: request.body.color,
    };

       updateLoc=await Request.updateRouteServiceLocation(installationData, service_id)
       if (updateLoc) {
        res.send({ status: 1, data: updateLoc, message: "Route updated successfully" });
      }
      else {
        res.send({ status: 0, message: "Error in updating product route" })
      }
  } catch (error) {
    console.log("Server error", error);
    res.status(500).send({ message: "Server error" + error });
  }
};

const saveQuickbidOffReqOrder = async (req, res) => {
  try {
    const { reorderedItems } = req.body;
    console.log("updatedCardsupdatedCardsupdatedCards",reorderedItems)
    await Request.updateCardOrder(reorderedItems);
    res.status(200).send({ status: 1, message: 'Order saved' });
  } catch (error) {
    console.log('saveReq error : ',error)
    res.status(500).send({ status: 500, message: 'Error saving order', error:error });
  }
}

const updateDesignOrderCardOrder = async (req, res) => {
  try {
    const { reorderedItems } = req.body;
    await Request.updateDesignOrderCardOrder(reorderedItems);
    res.status(200).send({ status: 1, message: 'Order saved' });
  } catch (error) {
    console.log('saveReq error : ',error)
    res.status(500).send({ status: 500, message: 'Error saving order', error:error });
  }
}

const getAllDesignOrders = async (req, res) => {
  try {
    let userId = req.body.userId
    let userType = req.body.userType
    let page = req.body.page
    let search = req.body.search
    let rowPerpage = req.body.rowsPerPage
    let where=req.body.where
    let sort = req.body.sort
    let fromDate = req.body.from
    let toDate = req.body.to
    let dueData = req.body.dueData
    let filterClient = req.body.filterClient
    let filterProject = req.body.filterProject
    let isClient =req.body.isClient
    let data = await Request.getAllDesignOrders(null, userId, userType,isClient, page, rowPerpage, search,where,sort,fromDate,toDate,dueData,filterClient,filterProject);
    let clients = await Client.getAllClientsDataFilters();
    
    // Fetch communities for project filter
    let community = [];
    if(userType != 3){
      community = await Community.getAllCommunities(false, null, null, null, userType);
    }else{
      let clientDetails = await Client.getPartnerByUserId(userId);
      if(clientDetails){
        community = await Community.getAllCommunitiesByClient(false, clientDetails?.id);
      }
    }
    
console.log('datadatadatadata',data)
let result = data.data
    
    for (const row of result) {    
      let attachment = await Request.getAllDesignOrderAttachments(row.id)
      console.log('rowrowrowrow', row?.status)
      console.log('attachmentattachment', attachment)
      row.attachment=row?.status == 2 ? (attachment?.filter(imageItem=>(imageItem?.file_type?.startsWith("image") || imageItem?.file_type === "img")) || []) : []
      let reference_attachments = await Request.getAllDesignOrderReferenceAttachments(row.id)
      row.reference_attachment=row?.status == 2 ? (reference_attachments?.filter(imageItem=>(imageItem?.file_type?.startsWith("image") || imageItem?.file_type === "img")) || []) : []
    }
    console.log('allCount===4',data)
      res.send({
      status: 1,
      data: data,
      clients:clients,
      community: community,
      message: 'Design Orders fetched successful'
    })
  } catch (error) {
    console.log(error)
    res.send({ status: 0, message: 'Failed to fetch design orders' });
  }
}
const getAllDesignOrdersCount = async (req, res) => {
  try {
    let userId = req.body.userId
    let userType = req.body.userType
    let page = req.body.page
    let search = req.body.search
    let rowPerpage = req.body.rowsPerPage
    let where=req.body.where
    let sort = req.body.sort
    let fromDate = req.body.from
    let toDate = req.body.to
    let dueData = req.body.dueData
    let filterClient = req.body.filterClient
    let filterProject = req.body.filterProject
    
    let data = await Request.getAllDesignOrdersCount(null, userId, userType, page, rowPerpage, search,where,sort,fromDate,toDate,dueData,filterClient,filterProject);
    console.log('allCount===4',data)
      res.send({
      status: 1,
      data: data,
      message: 'Design Orders fetched successful'
    })
  } catch (error) {
    console.log(error)
    res.send({ status: 0, message: 'Failed to fetch design orders' });
  }
}

const getAllDesignOrdersTabCount = async (req, res) => {
  try {
    let userId = req.body.userId
    // let userType = req.body.userType
    // let page = req.body.page
    // let search = req.body.search
    // let rowPerpage = req.body.rowsPerPage
    // let where=req.body.where
    // let sort = req.body.sort
    // let fromDate = req.body.from
    // let toDate = req.body.to
    // let dueData = req.body.dueData
    
    let data = await Request.getDesignCountByUser(userId);
      res.send({
      status: 1,
      data: data,
      message: 'Design Orders fetched successful'
    })
  } catch (error) {
    console.log(error)
    res.send({ status: 0, message: 'Failed to fetch design orders' });
  }
}

const addDesignOrder = async (req, res) => {
  try {
    const { userId, userType,isClient, values, userName,selectedServices,totalCost,designers, socketId,collaborators } = req.body
    console.log('designers------------+++++++++++', designers)
    const collaboratorsStr = collaborators != null
    ? (Array.isArray(collaborators) ? collaborators.filter(Boolean).join(',') : String(collaborators).trim())
    : null
    // 🚀 PROGRESS TRACKING: Socket progress emission function
    const emitProgress = (step, message, progress) => {
      const progressData = {
        step,
        message,
        progress,
        userId,
        domain: DOMAIN,
        timestamp: moment().unix(),
        type: 'add_design_order_progress'
      };
      
      // Emit to working socket server (same as chat)
      console.log(`[ADD-DESIGN-PROGRESS] 📡 Emitting progress to working socket server...`);
      try {
        const workingSocket = ioClient(REST_API_IO, {
          path: '/socket.io',
          transports: ['websocket', 'polling'],
          secure: true,
          timeout: 5000
        });
        
        workingSocket.on('connect', () => {
          console.log(`[ADD-DESIGN-PROGRESS] 🔌 Connected to working socket server`);
          console.log(`[ADD-DESIGN-PROGRESS] 📊 Emitting progress data:`, progressData);
          
          // Try emitting as 'message' event (same as chat) to test if socket server handles it
          workingSocket.emit('message', {
            ...progressData,
            type: 'progress',
            conversationId: `add_design_progress_${userId}`,
            senderId: 'system'
          });
          
          // Also try the original request_progress event
          workingSocket.emit('request_progress', progressData);
          
          console.log(`[ADD-DESIGN-PROGRESS] ✅ Successfully emitted progress to working socket server`);
          
          // Disconnect after emitting
          setTimeout(() => {
            workingSocket.disconnect();
          }, 1000);
        });
        
        workingSocket.on('connect_error', (error) => {
          console.error(`[ADD-DESIGN-PROGRESS] ❌ Working socket connection error:`, error);
        });
        
      } catch (error) {
        console.error(`[ADD-DESIGN-PROGRESS] ❌ Error creating working socket connection:`, error);
      }
    };

    // Initial progress
    console.log(`[ADD-DESIGN-PROGRESS] 🚀 Starting design order creation for user:`, userId);
    emitProgress('start', `Initializing design order creation...`, 5);
    
    let profileData = await Settings.getCompanyProfileDetails()
    console.log('selectedServicesselectedServices',selectedServices)
    let currentDate = moment().unix();
    let start_date = values.start;
    let due_date = values.end;
    let uploaded = values.avatarUrl;
    let uploadedReferenceFiles = values.referenceFiles;
    let designOrderData = {}
    designOrderData['name'] = values.name;
    designOrderData['designer_email_sent'] = values.designer_email_sent;
    designOrderData['designer_sms_sent'] = values.designer_sms_sent;
    designOrderData['total_cost'] = totalCost;
    designOrderData['description'] = values.description;
    if(userType == 3 || isClient){
      let clientDetails = await Client.getClientByUserId(userId);
      designOrderData['client'] = clientDetails.id
    }else{
      designOrderData['client'] = values.clients.id
    }
    designOrderData['community'] = values?.community?.id||null;
    designOrderData['notes'] = values.notes || "";
    if (values.start !== (undefined || null)) {
      designOrderData['start_date'] = start_date
    } else {
      designOrderData['start_date'] = 0
    }
    if (values.end !== (undefined || null)) {
      designOrderData['due_date'] = due_date
    } else {
      designOrderData['due_date'] = 0
    }
    if (values.welcome_message == true) {
      designOrderData['welcome_message'] = values.welcome_message
      designOrderData['subject'] = values.subject,
      designOrderData['email_content'] = values.email_content
    } else {
      designOrderData['welcome_message'] = false
      designOrderData['subject'] = null,
      designOrderData['email_content'] = null
    }
    if (values.draft == true) {
      designOrderData['status'] = 4
    } else {
      // designOrderData['status'] = values?.client_approve == false ? 2 : 1
      designOrderData['status'] =  1
      designOrderData['client_approve'] = values?.client_approve == false ? 0 : 1
    }
    designOrderData['added_by'] = userId
    designOrderData['timestamp'] = currentDate
    designOrderData['created_at'] = currentDate
    // designOrderData['is_from_client'] = (userType == 3 || isClient) ? 1 : 0
    designOrderData['is_in_queue'] = (userType == 3 || isClient) ? 0 : 1
    designOrderData['collaborators'] = collaboratorsStr || null
    console.log('designOrderDatadesignOrderData',designOrderData)
    emitProgress('creating_design_order', `Creating design order...`, 25);
    let result = await Request.addDesignOrders(designOrderData)
    if (result) {
      if (!empty(uploadedReferenceFiles)) {
        emitProgress('uploading_reference_files', `Uploading reference attachments...`, 40);
        for (const row of uploadedReferenceFiles) {
          let approvedImage = {}
          approvedImage.name = row.url;
          approvedImage.file_type = row.file_type;
          approvedImage.extension = row.extension;
          approvedImage.file_size = row.size;
          approvedImage.design_order_id = result;
          approvedImage.isDesign = userType === 11 ? 1 : 2;
          approvedImage.isNew = userType === 11 ? 1 : 2;
          approvedImage.added_by = userId
          approvedImage.timestamp = moment().unix()
          approvedImage.is_reference = 1
          let upload = await Request.addDesignOrderAttachments(approvedImage)
          // const isPdfRef =
          //   approvedImage.file_type === 'application/pdf' ||
          //   approvedImage.file_type === 'pdf' ||
          //   approvedImage.extension === 'pdf';
          // if (isPdfRef && upload) {
          //   try {
          //     await convertDesignOrderPdfToPngHelper(upload);
          //   } catch (conversionError) {
          //     console.error(`Error converting PDF for design order attachment ${upload}:`, conversionError);
          //   }
          // }
        }
      }
      if (!empty(uploaded)) {
        emitProgress('uploading_attachments', `Uploading design order attachments...`, 55);
        for (const row of uploaded) {
          let approvedImage = {}
          approvedImage.name = row.url;
          approvedImage.file_type = row.file_type;
          approvedImage.extension = row.extension;
          approvedImage.file_size = row.size;
          approvedImage.design_order_id = result;
          approvedImage.isDesign = userType === 11 ? 1 : 2;
          approvedImage.isNew = userType === 11 ? 1 : 2;
          approvedImage.added_by = userId
          approvedImage.timestamp = moment().unix()
          let upload = await Request.addDesignOrderAttachments(approvedImage)
          // const isPdfDesign =
          //   approvedImage.file_type === 'application/pdf' ||
          //   approvedImage.file_type === 'pdf' ||
          //   approvedImage.extension === 'pdf';
          // if (isPdfDesign && upload) {
          //   try {
          //     await convertDesignOrderPdfToPngHelper(upload);
          //   } catch (conversionError) {
          //     console.error(`Error converting PDF for design order attachment ${upload}:`, conversionError);
          //   }
          // }
        }
        
        // If this is the first attachment, set design_order_job_status = 2 (in progress)
        // if (uploaded?.length > 0) {
        //   const updateData = {
        //     design_order_job_status: 2
        //   };
        //   await Request.updateDesignOrders(result, updateData);
        //   console.log('Updated design_order_job_status to 2 for first attachment in addDesignOrder');
        // }
      }
      // if(selectedServices && selectedServices.length > 0) {
      //   for (const row of selectedServices) {
      //     const serviceData={}
      //     serviceData.labour_cost_service_id=row.id,
      //     serviceData.quantity=row.quantity,
      //     serviceData.total=row.total,
      //     serviceData.design_order_id=result

      //     let serviceDataResult = await Request.addDesignOrderServices(serviceData)



      //   }

      // }


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

      if(selectedServices?.length > 0){
        emitProgress('processing_services', `Processing design services...`, 75);
        for (const product of selectedServices) {
       let total =parseFloat((product?.quantity || 1) * product.total_cost).toFixed(2)
          const serviceData={}
          serviceData.labour_cost_service_id=product.id,
          serviceData.quantity=product?.quantity || 1,
          serviceData.unit_cost=product.total_cost,
          serviceData.total=total,
          serviceData.design_order_id=result

          

          let serviceDataResult = await Request.addDesignOrderServices(serviceData)

          let skillSet = []

          if(product?.workflow){
            
            let workflowData =await Product.getWorkflowById(product?.workflow)
            skillSet.push(workflowData)

            const allSkillIds = skillSet
                ?.flatMap(workflow => workflow?.map(stage => stage?.skill_ids?.split(',')))?.flat()

            let uniqueSkillIds = [...new Set(allSkillIds)];
                uniqueSkillIds = uniqueSkillIds?.filter(skillId => skillId !== "" && skillId !== undefined);

                console.log(uniqueSkillIds);
                let uniqueSkillIdsString = uniqueSkillIds?.join(',');

                uniqueSkillIdsString=uniqueSkillIdsString?.replace(/,$/, '');
                let projectManagers=[]
                if(!empty(uniqueSkillIdsString)){
                  projectManagers = await Project.getTeamMembersBasedOnSkills(uniqueSkillIdsString, 14)
                }
                let pm = {}
                // for (const projectManager of projectManagers) {
                  for (const designer of designers) {
                  pm = {
                    design_order_id: result,
                    // staff_id: !empty(projectManager["id"]) ? projectManager["id"] : NULL,
                    staff_id: !empty(designer["id"]) ? designer["id"] : NULL,
                    partner_id: null,
                    contact_id: null,
                    lead_id: null,
                  };
                  let alreadyExisting = await Request.projectMemberExist(pm);
                  if(!alreadyExisting){
                  let insertId = await Request.addDesignOrdersProjectMember(pm);
                  }
                  
                }


              for (const workflow of workflowData) {
              const subTask = {
                design_order_id:result,
                name: workflow.name,
                task_id: serviceDataResult,
                skill_ids: workflow?.skill_ids,
                timestamp: moment().unix(),
                userid: userId,
                start_date: start_date || null,
                due_date: due_date || null,
                task_uid: 1,
                complete:  0,
                priority: 1,
                status:  1,
                reporter_id:userId,
                description: workflow?.message,
              };

              const projectSubTaskId = await Request.addDesignOrdersSubTask(subTask);
              console.log('projectSubTaskId',projectSubTaskId)
              // for (const projectManager of projectManagers) {
                for (const designer of designers) {
                let pm = {
                  subtask_id: projectSubTaskId,
                  // staff_id: !empty(projectManager["id"]) ? projectManager["id"] : NULL,
                  staff_id: !empty(designer["id"]) ? designer["id"] : NULL,
                  partner_id: null,
                  contact_id: null,
                  lead_id: null,
                };
                let alreadyExisting = await Request.projectSubtaskMemberExist(pm, projectSubTaskId);

                if(!alreadyExisting){
                let sub_Task_mem = await Request.addDesignOrdersProjectSubtaskMember(pm);
                }
              }
            }
          }
        }
      }

      const designOrderProducts = values?.products || values?.productRows || []
      const validProducts = designOrderProducts.filter((product) => {
        const name = product?.product_name || product?.name || ''
        return String(name).trim() !== ''
      })
      if (validProducts.length > 0) {
        emitProgress('processing_products', `Processing design products...`, 80);
        for (const [index, product] of validProducts.entries()) {
          const productIndex = Number(
            product?.product_index ?? product?.indexId ?? index + 1
          ) || index + 1;
          const productData = {
            design_order_id: result,
            product_index: productIndex,
            name: (product?.product_name || product?.name || '').trim(),
            description: product?.product_desc || product?.description || '',
            status: 1,
          }
          const productId = await Request.addDesignOrderProducts(productData)
          console.log('productId',productId)
        }
      }
      
      // ✅ OPTIMIZATION: Send response immediately after critical operations
      emitProgress('complete', `Design order created successfully!`, 100);
      res.send({ status: 1, data: { id: result }, message:"Design Order has been successfully added!." })

      // ✅ OPTIMIZATION: Handle all notifications/emails/SMS/activity logs/tab counts async (non-blocking)
  //     const handleNotificationsAsync = async () => {
  //       try {
  //     let adminMangerIds = await Admin.getAllAdminAndManagerUserIds();
  //       let allAdminToCheck = [...new Set([userId,...adminMangerIds].filter(Boolean))];
  //         console.log('alluserssssssssarray',allAdminToCheck)
        
  //         // emitProgress('sending_notifications', `Sending notifications to team members...`, 65);
  //       // const creator = await Admin.getUserNotificationAndPermissionSettings(userId); // assumes this function returns user's email/sms info
        
  //         for (const userIdToCheck of allAdminToCheck) {
  //           console.log('useriddddd',userIdToCheck)
  //           const creator = await Admin.getUserNotificationAndPermissionSettings(userIdToCheck);
  //           console.log('creatorrrrrrrrr',creator)
  //           if (!creator) continue;
        
  //       const recipientPhone = creator?.phone || TWILIO_PHONE;
  //       const Phone_country_code = creator?.phone_country_code || '1';
  //       console.log('recipientPhoneeee',recipientPhone)
  //       console.log('Phone_country_codeeeee',Phone_country_code)

  //       if (creator.pcb_new_design_jobs === 1 || creator.view_design_permission === 1) {
  //         console.log("✅ Sales order SMSsssssss");
  //         // Email Notification
  //         if (
  //           creator.emailEnabled === 1 &&
  //           creator.email &&
  //           creator.pcb_new_design_jobs_email === 1 &&
  //           creator.pcb_new_design_jobs === 1
  //         ) {
  //           let welcome = "Hello " + creator.name + ",";
  //           let subject = `📦 New Design Order Created: ${values.name}`;
  //           let email_content = "A new design order has been created. Please review the details in your software.";

  //           let message = designersEmail({
  //           // designer:creator,
  //           designer: designers,
  //           designOrderName: designOrderData['name'],
  //           email_content,
  //           welcome,
  //           baseUrl: BASE_URL,
  //           profileData: profileData[0],
  //         });
  //         console.log("✅ Design order SMSsssssss 2");
  //           message = message.replace('[emailContent]', email_content);
  //           message = message.replace('[CONTENT]', welcome);
  //           message = message.replace('[LOGIN_URL]', `${BASE_URL}`);

  //           const mailOptions = {
  //             // to: 'deeraj@skyniche.com',
  //             to: creator.email,
  //             from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
  //             subject: subject,
  //             text: subject,
  //             html: message,
  //           };

  //           try {
  //             await transporter.sendMail(mailOptions);
  //             console.log(`✅ design order email sent to creator1: ${creator.email}`);
  //           } catch (err) {
  //             console.error(`❌ Error sending design order email to ${creator.email}:`, err);
  //           }
  //         }
  //         // SMS Notification
  //         if (
  //           creator.smsEnabled === 1 &&
  //           creator.phone &&
  //           creator.pcb_new_design_jobs === 1 &&
  //           creator.pcb_new_design_jobs_sms === 1
  //         ) {
  //         console.log("✅ design order SMSsssssss 3");
  //           const smsContent = `🎨 Hello ${creator?.name},\n\n📢 A new Design Order "${designOrderData['name']}" has been Created\n\n🔗 Login: ${BASE_URL}\n\n– Team SignWise`;

  //           try {
  //             // await sendMessageToPhoneNumber('9074906155', smsContent);
  //             await sendMessageToPhoneNumber(recipientPhone, smsContent,Phone_country_code);
  //             // await sendMessageToPhoneNumber('9074906155', smsContent,'91');
  //             console.log("✅ Design order SMS sent to creator");
  //           } catch (err) {
  //             console.error("❌ Error sending Design order SMS to creator:", err);
  //           }
  //         }
  //       }
  //     }

  //     const sentToViewDesign = new Set();
  //     for (const designer of designers) {
  //       console.log('designerdesigner',designers)
  //       if (
  //         designer.email &&
  //         designer.emailEnabled === 1 &&
  //         // designer.view_design_permission === 1 && designer.view_design_permission_email === 1 &&
  //         designer.emailEnabled === 1 &&
  //         designer.pcb_new_design_jobs === 1 &&
  //         designer.pcb_new_design_jobs_email === 1 &&
  //         !sentToViewDesign.has(designer.email)
  //       ) {
  //         const subject = `New Design Order Assigned - ${designOrderData['name']}`;
  //         const email_content = `A new design order titled "${designOrderData['name']}" has been assigned to you. Please log in to view details.`;
  //         const welcome = `Hello ${designer.name},`;

  //         let message = designersEmail({
  //           designer: designers,
  //           // designer,
  //           designOrderName: designOrderData['name'],
  //           email_content,
  //           welcome,
  //           baseUrl: BASE_URL,
  //           profileData: profileData[0],
  //         });

  //         message = message.replace('[emailContent]', email_content);
  //         message = message.replace('[CONTENT]', welcome);
  //         message = message.replace('[LOGIN_URL]', `${BASE_URL}`);

  //         const mailOptions = {
  //           // to: 'deeraj@skyniche.com',
  //           to: designer.email,
  //           from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
  //           subject,
  //           text: subject,
  //           html: message,
  //         };

  //         try {
  //           await transporter.sendMail(mailOptions);
  //           console.log(`✅ ViewPermission Email sent to2: ${designer.email}`);
  //           sentToViewDesign.add(designer.email);
  //         } catch (err) {
  //           console.error(`❌ Error sending ViewPermission email to ${designer.email}:`, err);
  //         }
  //       }
  //     }

  //       const sentTo = new Set();
  //       if (designers?.some(d => d.email && d.emailEnabled === 1 && d.pcb_new_design_jobs === 1 && d.pcb_new_design_jobs_email === 1)) {
  //       for (const designer of designers) {
  //         if (
  //           designer.email &&
  //           designer.emailEnabled === 1 &&
  //           designer.pcb_new_design_jobs === 1 &&
  //           designer.pcb_new_design_jobs_email === 1 &&
  //           !sentTo.has(designer.email)
  //         ) {
  //           let welcome = "Hello " + designer.name + ",";
  //           let subject = `New Design Job Added for You - ${designOrderData['name']}`;
  //           let email_content = `You’ve been assigned a new design job. Please check your design order list for more details.`;

  //           let message = designersEmail({
  //             designer,
  //             designOrderName: designOrderData['name'],
  //             email_content,
  //             welcome,
  //             baseUrl: BASE_URL,
  //             profileData: profileData[0],
  //           });

  //           message = message.replace('[emailContent]', email_content);
  //           message = message.replace('[CONTENT]', welcome);
  //           message = message.replace('[LOGIN_URL]', `${BASE_URL}`);

  //           const mailOptions = {
  //             // to: 'deeraj@skyniche.com',
  //             to: designer.email,
  //             from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
  //             subject: subject,
  //             text: subject,
  //             html: message,
  //           };

  //           try {
  //             await transporter.sendMail(mailOptions);
  //             console.log(`✅ Email sent to designer3: ${designer.email}`);
  //             sentTo.add(designer.email);
  //           } catch (err) {
  //             console.error(`❌ Error sending email to ${designer.email}:`, err);
  //         }
  //       }
  //     }
  //   }

  //   const sentPhonesViewDesign = new Set();
  //   for (const designer of designers) {
  //     if (
  //       designer.phone &&
  //       designer.smsEnabled === 1 &&
  //       // designer.view_design_permission === 1 &&
  //       // designer.view_design_permission_sms === 1 &&
  //       designer.pcb_new_design_jobs === 1 &&
  //       designer.pcb_new_design_jobs_sms === 1 &&
  //       !sentPhonesViewDesign.has(designer.phone)
  //     ) {
  //       const smsContent = `🎨 Hello ${designer.name},\n\n📢 A new Design Order "${designOrderData['name']}" has been assigned to you.\n\n🔗 Login: ${BASE_URL}\n\n– Team SignWise`;

  //       try {
  //         // await sendMessageToPhoneNumber('9074906155', smsContent,'91');
  //         await sendMessageToPhoneNumber(designer.phone, smsContent,designer.phone_country_code);
  //         console.log(`✅ ViewPermission SMS sent to: ${designer.phone}`);
  //         sentPhonesViewDesign.add(designer.phone);
  //       } catch (err) {
  //         console.error(`❌ Error sending ViewPermission SMS to ${designer.phone}:`, err.message);
  //       }
  //     }
  //   }

  //   const sentPhones = new Set();
  //   if (designers?.some(d => d.phone && d.smsEnabled === 1 && d.pcb_new_design_jobs === 1 && d.pcb_new_design_jobs_sms === 1)) {
  //   for (const designer of designers) {
  //     if (
  //       designer.phone &&
  //       designer.smsEnabled === 1 &&
  //       designer.pcb_new_design_jobs === 1 &&
  //       designer.pcb_new_design_jobs_sms === 1 &&
  //       !sentPhones.has(designer.phone)
  //     ) {
  //       const smsContent = `🎨 Hello ${designer.name},\n\n✨ A new Design Job "${designOrderData['name']}" has been added for you.\n\n🔗 Login here: ${BASE_URL}\n\n🚀 – Team SignWise`;

  //       try {
  //         // await sendMessageToPhoneNumber('9074906155', smsContent,'91');
  //         await sendMessageToPhoneNumber(designer.phone, smsContent,designer.phone_country_code);
  //         console.log(`✅ SMS sent to designer: ${designer.phone}`);
  //         sentPhones.add(designer.phone);
  //       } catch (err) {
  //         console.error(`❌ Error sending SMS to ${designer.phone}:`, err.message);
  //       }
  //     }
  //   }
  // }

  //     //////////////////////////////////////////////////////////////
  //     if (userType != 3 && !isClient) {
  //       console.log('userType',userType)
  //       let partnerData = values.clientsData
  //       let isAutoApprove =  values?.client_approve == false ? true : false
  //        let autoApprovedMsg=isAutoApprove?'A New Design Order Has Been Created and Approved':'A New Design Order Has Been Awaiting your Acceptance'
  //       // let subject = 'A new request awaiting your acceptance';
  //       let subject = autoApprovedMsg
  //       let email_content = `You are invited to join New Design Order by ` + userName;
  //       if (req.body.values.isCustomMessage) {
  //         subject = req.body.values.subject
  //         email_content = req.body.values.emailcontent
  //       }
  //       if (partnerData && partnerData.length > 0) {
  //         for (let eachPartner of partnerData) {
  //           let welcome = "Hello " + eachPartner['company_name'] + ","
  //           sgMail.setApiKey(SENDGRID_API_KEY)
  //           let message = rfqEmail()
  //           message = message.replace('[emailContent]', email_content)
  //           message = message.replace('[CONTENT]', welcome)

  //           message = message.replace('[LOGIN_URL]', `${BASE_URL}`)
  //           const mailOptions = {
  //             to:  eachPartner.email , 
  //             from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
  //             subject: subject,
  //             text: subject,
  //             html: message,
  //           }
  //           try {
  //             if(SEND_EMAIL){
  //             await transporter.sendMail(mailOptions);
  //             console.log('Email sent 1');
  //             }
  //           } catch (error) {
  //             console.error('##########Err', error);
  //           }
  //         }
  //       }
  //       let notification_msg = `🎨 New Design Order <strong>"${designOrderData['name']}"</strong> has been added .`
  //       let notification_url = `design-order/view/${result}`
  //       const incrementResult_User = await Admin.incrementField(userId,"noti_count",1);
  //       let notificationData_user = {
  //         user_id: userId,
  //         url: notification_url,
  //         timestamp: currentDate,
  //         message: notification_msg,
  //         status: 1,
  //         from_id: userId,
  //       };
  //       const addNotificationResult_user = await Admin.addNotification( notificationData_user );
  //       let adminIds = await Admin.getAllAdminIds();
  //       for (let eachadminIds of adminIds) {
  //         if (eachadminIds != userId) {
  //           let incrementResult = await Admin.incrementField(eachadminIds, 'noti_count', 1)
  //           let addNotification = {
  //             user_id: eachadminIds,
  //             url: notification_url,
  //             timestamp: moment().unix(),
  //             message: notification_msg,
  //             status: 1,
  //             from_id: userId,
  //           }
  //           const addNotificationResult = await Admin.addNotification(addNotification);
  //         }
  //       }
  //       let orderActivity = {}
  //       orderActivity['design_order_id'] = result;
  //       orderActivity['activity'] = notification_msg;
  //       orderActivity['added_by'] = userId
  //       orderActivity['timestamp'] = currentDate
  //       let reqActivityId = await RequestActivity.addDesignOrderUpdate(orderActivity);
  //     } else {
  //       let clientDetails = await Client.getClientByUserId(userId);
  //       const currentDate = moment().unix();
  //       // let subject = 'A new design order initiated by client';
  //       // let email_content = `A new design order initiated by client` + userName;
  //       let requestActivity = {}
  //       requestActivity['design_order_id'] = result,
  //       requestActivity['partner_id'] = clientDetails.id,
  //       requestActivity['partner_name'] = clientDetails.name,
  //       requestActivity['activity'] = `👤 Client,"${clientDetails.name}" has initiated the design order`,
  //       requestActivity['added_by'] = userId,
  //       requestActivity['timestamp'] = currentDate,
  //       requestActivity['status'] = 1
  //       let activityResult = await RequestActivity.addDesignOrderUpdate(requestActivity)
  //       const request = await Request.getRequestById(result)
  //       const adminIds = await Admin.getAllAdminIds()
  //       // Notification 
  //       let notification_msg =`${clientDetails.name}"</strong> has been initiated new design order <strong>"${designOrderData['name']}"</strong>."`
  //       let notification_url = `design-order/view/${result}`

  //       // Alert assigned user of updation
  //       let incrementResult = await Admin.incrementField(userId, "noti_count", 1);
  //       let notificationUser = {
  //         user_id: userId,
  //         url: notification_url,
  //         timestamp: currentDate,
  //         message: notification_msg,
  //         status: 1,
  //         from_id: userId,
  //       }
  //       const addNotificationResult = await Admin.addNotification(notificationUser);
  //       for (let adminids of adminIds) {
  //         let incrementResult = await Admin.incrementField(adminids, 'noti_count', 1)
  //         let addNotification = {
  //           user_id: adminids,
  //           url: notification_url,
  //           timestamp: currentDate,
  //           message: notification_msg,
  //           status: 1,
  //           from_id: userId,
  //         }
  //         const addNotificationResult = await Admin.addNotification(addNotification);
  //       }

  //       let welcome = "Hello Admin,"
  //       let email_content = `${clientDetails.name}"</strong> has been initiated new design order <strong>"${designOrderData['name']}"</strong>."`
  //       let subject = " New Design Order from Client"
  //       let message = rfqEmail()
  //       message = message.replace('[emailContent]', email_content)
  //       message = message.replace('[CONTENT]', welcome)

  //       message = message.replace('[LOGIN_URL]', `${BASE_URL}`)
  //       let adminEmails = await Admin.getAllAdminEmails();
  //       for (const row of adminEmails) {
  //         const mailOptions = {
  //           to:  row, 
  //           from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
  //           subject: subject,
  //           text: subject,
  //           html: message,
  //         }
  //         try {
  //           if(SEND_EMAIL){
  //           await transporter.sendMail(mailOptions);
  //           console.log('Email sent');
  //           }
  //         } catch (error) {
  //           console.error('##########Err', error);
  //         }
  //       }
  //     }
  //   //   const allUserIds = await Admin.getAllUserIds();
  //   //   const selectedClientId = values?.clients?.user_id || null;
  //   //   let allUsersToCheck = [...new Set([userId, selectedClientId, ...allUserIds].filter(Boolean))];

  //   //   // emitProgress('updating_tab_counts', `Updating user tab counts...`, 85);
  //   //   const ensureUserExists = async (userId) => {
  //   //       let userOrderCount = await Request.getDesignOrderCountByUserId(userId);
          
  //   //       if (!userOrderCount || userOrderCount === "No record found") {
  //   //           const designOrderData = {
  //   //               user_id: userId,
  //   //               open_count: 0, 
  //   //               approved_count: 0,
  //   //               rejected_count: 0,
  //   //               hold_count: 0,
  //   //               timestamp: currentDate,
  //   //               open_count_ids:result
  //   //           };
  //   //           await Request.addDesignOrderCount(designOrderData);
  //   //       }
  //   //   };

  //   //   await Promise.all(allUsersToCheck.map(user => ensureUserExists(user)));

  //   //   let usersToUpdate = (userType == 3 || isClient) 
  //   //       ? [...new Set([userId, selectedClientId, ...allUserIds].filter(Boolean))]
  //   //       : [...new Set([selectedClientId, ...allUserIds].filter(Boolean))];

  //   //   // const updateOpenCount = async (userId) => {
  //   //   //     await Request.designCountIncrementField(userId, 'open_count', 1);
  //   //   // };
  //   //   const updateOpenCount = async (userId,increase = true) => {
  //   //     await Request.designCountIncrementField(userId, 'open_count', 1, 'open_count_ids', result, increase);
  //   //     if (userType !== 3 && !isClient) {
  //   //       await Request.designCountIncrementField(userId, 'new_count', 1, 'new_count_ids', result, increase);
  //   //     }
  //   // };

  //   //   await Promise.all(usersToUpdate.map(user => updateOpenCount(user)));

  //   //   console.log("Successfully updated open_count for all necessary users.");
  //       } catch (error) {
  //         console.error('Error in async notifications handler:', error);
  //       }
  //     };
    // new function for optimization
      const handleNotificationsAsync = async () => {
        try {
          let adminMangerIds = await Admin.getAllAdminAndManagerUserIds();
          let allAdminToCheck = [...new Set([userId, ...adminMangerIds].filter(Boolean))];
          // console.log('alluserssssssssarray', allAdminToCheck);
      
          // Emit progress (optional)
          // emitProgress('sending_notifications', `Sending notifications to team members...`, 65);
      
          // Parallelize Admin/Manager email and SMS notifications
          const adminIds = await Admin.getAllAdminIds();
          const adminNotificationPromises = allAdminToCheck.map(async (userIdToCheck) => {
            const creator = await Admin.getUserNotificationAndPermissionSettings(userIdToCheck);
            if (!creator) return;
      
            const recipientPhone = creator?.phone || TWILIO_PHONE;
            const Phone_country_code = creator?.phone_country_code || '1';
            // console.log('recipientPhoneeee', recipientPhone);
            // console.log('Phone_country_codeeeee', Phone_country_code);
      
            // Handle Sales Order or Design Order Notifications
            if (creator.pcb_new_design_jobs === 1 || creator.view_design_permission === 1) {
              // Email Notification for Design Orders
              const emailPromise = (async () => {
                if (creator.emailEnabled === 1 && creator.email && creator.pcb_new_design_jobs_email === 1 && creator.pcb_new_design_jobs === 1) {
                  let welcome = "Hello " + creator.name + ",";
                  let subject = `📦 New Design Order Created: ${values.name}`;
                  let email_content = "A new design order has been created. Please review the details in your software.";
      
                  let message = designersEmail({
                    designer: designers,
                    designOrderName: designOrderData['name'],
                    email_content,
                    welcome,
                    baseUrl: BASE_URL,
                    profileData: profileData[0],
                  });
                  message = message.replace('[emailContent]', email_content);
                  message = message.replace('[CONTENT]', welcome);
                  message = message.replace('[LOGIN_URL]', `${BASE_URL}`);
      
                  const mailOptions = {
                    to: creator.email,
                    from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
                    subject: subject,
                    text: subject,
                    html: message,
                  };
      
                  try {
                    await transporter.sendMail(mailOptions);
                    console.log(`✅ Design order email sent to creator: ${creator.email}`);
                  } catch (err) {
                    console.error(`❌ Error sending design order email to ${creator.email}:`, err);
                  }
                }
              })();
      
              // SMS Notification for Design Orders
              const smsPromise = (async () => {
                if (creator.smsEnabled === 1 && creator.phone && creator.pcb_new_design_jobs_sms === 1) {
                  const smsContent = `🎨 Hello ${creator?.name},\n\n📢 A new Design Order "${designOrderData['name']}" has been Created\n\n🔗 Login: ${BASE_URL}\n\n– Team SignWise`;
      
                  try {
                    await sendMessageToPhoneNumber(recipientPhone, smsContent, Phone_country_code);
                    console.log("✅ Design order SMS sent to creator");
                  } catch (err) {
                    console.error("❌ Error sending Design order SMS to creator:", err);
                  }
                }
              })();
      
              // Wait for both email and SMS to be sent
              await Promise.all([emailPromise, smsPromise]);
            }
          });
      
          // Wait for all admin notifications to be processed
          await Promise.all(adminNotificationPromises);
      
          // Handle designer notifications (Emails)
          const sentToViewDesign = new Set();
          const designerEmailPromises = designers.map(async (designer) => {
            if (
              designer.email &&
              designer.emailEnabled === 1 &&
              designer.pcb_new_design_jobs === 1 &&
              designer.pcb_new_design_jobs_email === 1 &&
              !sentToViewDesign.has(designer.email)
            ) {
              const subject = `New Design Order Assigned - ${designOrderData['name']}`;
              const email_content = `A new design order titled "${designOrderData['name']}" has been assigned to you. Please log in to view details.`;
              const welcome = `Hello ${designer.name},`;
      
              let message = designersEmail({
                designer: designers,
                designOrderName: designOrderData['name'],
                email_content,
                welcome,
                baseUrl: BASE_URL,
                profileData: profileData[0],
              });
      
              message = message.replace('[emailContent]', email_content);
              message = message.replace('[CONTENT]', welcome);
              message = message.replace('[LOGIN_URL]', `${BASE_URL}`);
      
              const mailOptions = {
                to: designer.email,
                from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
                subject: subject,
                text: subject,
                html: message,
              };
      
              try {
                await transporter.sendMail(mailOptions);
                // console.log(`✅ ViewPermission Email sent to: ${designer.email}`);
                sentToViewDesign.add(designer.email);
              } catch (err) {
                console.error(`❌ Error sending ViewPermission email to ${designer.email}:`, err);
              }
            }
          });
      
          // Wait for all designer email notifications to be sent
          await Promise.all(designerEmailPromises);
      
          // Handle SMS notifications for designers
          const sentPhonesViewDesign = new Set();
          const designerSMSPromises = designers.map(async (designer) => {
            if (
              designer.phone &&
              designer.smsEnabled === 1 &&
              designer.pcb_new_design_jobs === 1 &&
              designer.pcb_new_design_jobs_sms === 1 &&
              !sentPhonesViewDesign.has(designer.phone)
            ) {
              const smsContent = `🎨 Hello ${designer.name},\n\n📢 A new Design Order "${designOrderData['name']}" has been assigned to you.\n\n🔗 Login: ${BASE_URL}\n\n– Team SignWise`;
      
              try {
                await sendMessageToPhoneNumber(designer.phone, smsContent, designer.phone_country_code);
                // console.log(`✅ ViewPermission SMS sent to: ${designer.phone}`);
                sentPhonesViewDesign.add(designer.phone);
              } catch (err) {
                console.error(`❌ Error sending ViewPermission SMS to ${designer.phone}:`, err.message);
              }
            }
          });
      
          // Wait for all designer SMS notifications to be sent
          await Promise.all(designerSMSPromises);
      
          // Handle Partner invitations (parallelized)
          if (userType !== 3) {
            let isAutoApprove = values?.client_approve === false ? true : false;
            let autoApprovedMsg = isAutoApprove ? 'A New Design Order Has Been Created and Approved' : 'A New Design Order Has Been Awaiting your Acceptance';
            let subject = autoApprovedMsg;
            let email_content = `You are invited to join New Design Order by ` + userName;
            if (req.body.values.isCustomMessage) {
              subject = req.body.values.subject;
              email_content = req.body.values.emailcontent;
            }
              const partnerData = values?.clientsData
              const partnerEmailPromises = partnerData?.map(async (eachPartner) => {
              let welcome = "Hello " + eachPartner['company_name'] + ",";
              sgMail.setApiKey(SENDGRID_API_KEY);
              let message = rfqEmail();
              message = message.replace('[emailContent]', email_content);
              message = message.replace('[CONTENT]', welcome);
              message = message.replace('[LOGIN_URL]', `${BASE_URL}`);
      
              const mailOptions = {
                to: eachPartner.email,
                from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
                subject: subject,
                text: subject,
                html: message,
              };
      
              try {
                if (SEND_EMAIL) {
                  await transporter.sendMail(mailOptions);
                  // console.log('Email sent to partner:', eachPartner.email);
                }
              } catch (error) {
                console.error('Error sending email to partner:', error);
              }
            });
      
            // Wait for all partner invitation emails to be sent
            await Promise.all(partnerEmailPromises);
          }
      
          // Handle Admin notifications (parallelized)
          let notification_msg = `🎨 New Design Order <strong>"${designOrderData['name']}"</strong> has been added.`;
          let notification_url = `design-order/view/${result}`;
      
          await Admin.incrementField(userId, "noti_count", 1);
          let notificationData_user = {
            user_id: userId,
            url: notification_url,
            timestamp: currentDate,
            message: notification_msg,
            status: 1,
            from_id: userId,
          };
          await Admin.addNotification(notificationData_user);
      
          const adminNotificationPromises2 = adminIds
            .filter(eachadminIds => eachadminIds != userId)
            .map(async (eachadminIds) => {
              await Admin.incrementField(eachadminIds, 'noti_count', 1);
              let addNotification = {
                user_id: eachadminIds,
                url: notification_url,
                timestamp: moment().unix(),
                message: notification_msg,
                status: 1,
                from_id: userId,
              };
              await Admin.addNotification(addNotification);
            });
      
          // Wait for all admin notifications to be sent
          await Promise.all(adminNotificationPromises2);
      
          // Activity log
          let orderActivity = {
            design_order_id: result,
            activity: notification_msg,
            added_by: userId,
            timestamp: currentDate,
          };
          await RequestActivity.addDesignOrderUpdate(orderActivity);
      
        } catch (error) {
          console.error('Error in async notifications handler:', error);
        }
      };  

      // ✅ OPTIMIZATION: Run non-critical operations asynchronously (fire and forget)
      handleNotificationsAsync().catch(error => {
        console.error('Error in async operations:', error);
      });
      
    } else {
      emitProgress('error', 'Failed to create design order', 0);
      res.send({ status: 0, message: "Failed to create design order" });
    }
  } catch (error) {
    console.log(error, 'Error creating design order')
    // emitProgress('error', `Error creating design order: ${error.message}`, 0);
    res.send({ message: "Error creating design order "+error });
  }
}

const deleteDesignOrderDesign = async (req,res)=>{
  try {
    const {id, userId} = req.body
    const moment = require('moment');
    let currentDate = moment().unix();
    
      if (id) {
        // Get design details before deletion
        const sequelize = require('../config/dbconnection');
        const designQuery = await sequelize.query(`
          SELECT doa.*, do.name as design_order_name, 
          CONCAT(u.first_name, ' ', u.last_name) as added_by_name
          FROM design_order_attachments doa
          LEFT JOIN design_orders do ON do.id = doa.design_order_id
          LEFT JOIN users u ON u.id = doa.added_by
          WHERE doa.id = ${id}
        `);
        
        const designDetails = designQuery[0] && designQuery[0].length > 0 ? designQuery[0][0] : null;
        
        const deleteDsign = await Request.deleteDesignOrderAttachment(id)
        if(deleteDsign){
          // Add update entry for Job Log
          if (designDetails && userId) {
            const userData = await Admin.getUserById(userId);
            const userName = userData && userData[0] ? `${userData[0].first_name} ${userData[0].last_name}` : 'Unknown User';
            const designName = designDetails.name ? designDetails.name.split('/').pop() : 'Design';
            const activityMsg = `<strong>${userName}</strong> deleted design "<strong>${designName}</strong>" from the design order "<strong>${designDetails.design_order_name}</strong>".`;
            
            let requestActivity = {
              design_order_id: designDetails.design_order_id,
              activity: activityMsg,
              added_by: userId,
              timestamp: currentDate
            };
            await RequestActivity.addDesignOrderUpdate(requestActivity);
          }
          
          res.send({ status: 1, message:"Design deleted successfully." })
        }else{
          res.send({ status: 0, message:"Failed to Delete the Design." })
        }
      }
  } catch (error) {
     console.error("deleteDesignOrderAttachment error:", error);
    return res.status(500).send({ status: 0, message: "Failed to delete design", error: error.toString() });
  }
}

const getWorkflowMembers = async (req, res) => {
  try {
    const workflowIds = req.body.workflow;

    if (!workflowIds || workflowIds.length === 0) {
      return res.status(400).send({ status: 0, message: "Workflow ID(s) are required" });
    }

    const workflowIdArray = Array.isArray(workflowIds) ? workflowIds : [workflowIds];

    let allSkillIds = [];

    for (const workflowId of workflowIdArray) {
      const workflowData = await Product.getWorkflowById(workflowId);

      if (!empty(workflowData)) {
        const skillIds = workflowData
          .flatMap(stage => stage?.skill_ids?.split(','))
          .filter(skillId => skillId !== "" && skillId !== undefined);

        allSkillIds.push(...skillIds);
      }
    }

    const uniqueSkillIds = [...new Set(allSkillIds)];
    const uniqueSkillIdsString = uniqueSkillIds.join(',');

    let projectManagers = [];

    if (!empty(uniqueSkillIdsString)) {
      projectManagers = await Project.getDesignTeamMembersBasedOnSkills(uniqueSkillIdsString);
    }

    return res.send({
      status: 1,
      projectManagers,
      message: "Project managers fetched successfully",
    });
  } catch (error) {
    console.error("getWorkflowMembers error:", error);
    return res.status(500).send({ status: 0, message: "Failed to fetch workflow members", error: error.toString() });
  }
};

const getJobNoteMembers = async (req, res) => {
  try {
    const { request_id } = req.body;
    let userId = req.body.userId;
    let userType = req.body.userType;

    // Check if request exists
    let result = await Request.getRequestById(request_id, userId, userType);
    let projectExisting = await Project.getProjectByRequestId(request_id);

    let jobNoteMembers = [];

    if (projectExisting || result) {
      const jobNoteProjectMembers = await Project.projectMembersForMessaging(request_id);
      const jobNoteDrivers = await Project.DriverForMessaging(request_id);
      const jobNoteInstaller = await Project.InstallersForMessaging(request_id);
      const getAllAdmins = await Project.AdminsForMessaging(userId);
      
      // If this request was converted from a design order, include design order project members
      let designOrderProjectMembers = [];
      if (result[0]?.design_order_id) {
        designOrderProjectMembers = await Request.getAllDesignOrderProjectMembers(result[0].design_order_id);
      }
      
      const combined = [
        ...jobNoteProjectMembers,
        ...jobNoteDrivers,
        ...jobNoteInstaller,
        ...getAllAdmins,
        ...designOrderProjectMembers,
      ];

      jobNoteMembers = Array.from(
        new Map(
          combined
            .filter(user => user?.user_id) // filter out null or undefined user_id
            .map(user => [user.user_id, user]) // remove duplicates by user_id
        ).values()
      );
    }

    res.send({
      status: 1,
      jobNoteMembers,
      message: "Job note members fetched successfully"
    });
  } catch (error) {
    console.log(error, 'error in get job note members');
    res.send({ status: 0, message: 'failed to get job note members' });
  }
};

const getJobCostNotesDetails = async (req, res) => {
  try {
    const { request_id } = req.body;
    let userId = req.body.userId;
    let userType = req.body.userType;

    // Get request details
    let result = await Request.getAllRequests(request_id, userId, userType);
    let projectExisting = await Project.getProjectByRequestId(request_id);

    // Map result data
    let requestDetails = null;
    if (result?.data && result.data.length > 0) {
      requestDetails = result.data[0];
    }

    // Get job note members
    let jobNoteMembers = [];

    if (projectExisting || result) {
      const jobNoteProjectMembers = await Project.projectMembersForMessaging(request_id);
      const jobNoteDrivers = await Project.DriverForMessaging(request_id);
      const jobNoteInstaller = await Project.InstallersForMessaging(request_id);
      const getAllAdmins = await Project.AdminsForMessaging(userId);
      
      // If this request was converted from a design order, include design order project members
      let designOrderProjectMembers = [];
      if (requestDetails?.design_order_id) {
        designOrderProjectMembers = await Request.getAllDesignOrderProjectMembers(requestDetails.design_order_id);
      }
      
      const combined = [
        ...jobNoteProjectMembers,
        ...jobNoteDrivers,
        ...jobNoteInstaller,
        ...getAllAdmins,
        ...designOrderProjectMembers,
      ];

      jobNoteMembers = Array.from(
        new Map(
          combined
            .filter(user => user?.user_id) // filter out null or undefined user_id
            .map(user => [user.user_id, user]) // remove duplicates by user_id
        ).values()
      );
    }

    res.send({
      status: 1,
      jobNoteMembers,
      data: requestDetails ? [requestDetails] : [],
      message: "Job cost notes details fetched successfully"
    });
  } catch (error) {
    console.log(error, 'error in get job cost notes details');
    res.send({ status: 0, message: 'failed to get job cost notes details' });
  }
};

const getDesignOrderById = async (req, res) => {
  try {
    const { design_order_id,isClient } = req.body;
    let userId = req.body.userId
    let userType = req.body.userType
    let result = await Request.getAllDesignOrders(design_order_id, userId, userType,isClient);
    result = result?.data?.map(item => {
      const formattedDescription = (item.description || '').replace(/\r?\n/g, '\n');
      const formattedNotes = (item.notes || '').replace(/\r?\n/g, '\n');
      return {
      ...item,
        description: formattedDescription,
        description_lines: formattedDescription ? formattedDescription.split('\n') : [],
        notes: formattedNotes,
        notes_lines: formattedNotes ? formattedNotes.split('\n') : [],
      welcome_message: item.welcome_message === 1 ? true : false
      };
    });
    // let customerData = await CustomerOrder.getCustomerOrderRequestId(design_order_id);
    let partner = await Client.getPartnerByUserId(userId);
    let customerData = await Client.getClientById(result[0].client);
    
    // Fetch design order updates
    let designOrderUpdates = await RequestUpdates.fetchAllDesignOrderUpdates(5, design_order_id, null, partner.id, userId, userType);
    
    // Check if design order has a request_id (converted from estimate) and fetch those updates too
    let allUpdates = [];
    if (result[0]?.request_id) {
      try {
        let estimateUpdates = await RequestUpdates.fetchAllUpdates(5, result[0].request_id, null, partner.id, userId, userType);
        // Merge and sort in chronological order (newest first), removing duplicates by id
        const mergedMap = new Map();
        [...designOrderUpdates, ...estimateUpdates].forEach(update => {
          if (!mergedMap.has(update.id)) {
            mergedMap.set(update.id, update);
          }
        });
        allUpdates = Array.from(mergedMap.values()).sort((a, b) => {
          const aKey = Number(a?.raw_timestamp ?? a?.timestamp ?? a?.id ?? 0);
          const bKey = Number(b?.raw_timestamp ?? b?.timestamp ?? b?.id ?? 0);
          return bKey - aKey;
        });
        console.log('Merged design order and estimate updates:', allUpdates.length);
      } catch (error) {
        console.log('Error fetching estimate updates for design order:', error);
        allUpdates = designOrderUpdates;
      }
    } else {
      allUpdates = designOrderUpdates;
    }
    
    let attachments = await Request.fetchAllDesignOrderAttachments(design_order_id);
    let reference_attachments = await Request.fetchAllDesignOrderReferenceAttachments(design_order_id);
    const enrichAttachmentConvertedImages = async (attachment) => {
      const docId = attachment.id ?? attachment.ID;
      const isPdf =
        (attachment.file_type === 'application/pdf' || attachment.file_type === 'pdf' || attachment.extension === 'pdf');
      if (isPdf && docId) {
        try {
          attachment.converted_images = await getRequestAttachmentImagesByDocumentId(docId, 'design_order');
        } catch (e) {
          console.error(`Error fetching converted images for design order attachment ${docId}:`, e);
          attachment.converted_images = [];
        }
      } else {
        attachment.converted_images = [];
      }
    };
    for (const att of attachments) await enrichAttachmentConvertedImages(att);
    for (const att of reference_attachments) await enrichAttachmentConvertedImages(att);
    let community = await Community.getAllCommunitiesByClient(false,result[0].client);
    let servicesData = await Request.getDesignOrderById(design_order_id);
    let productsData = await Request.getDesignOrderProductsByDesignOrderId(design_order_id);
    let rejected_attachments = await Request.getDesignOrderRejectedAttachments(design_order_id);

    // let projectMembers = await Request.getAllDesignOrderProjectMembers(design_order_id);
    let designersOnly = await Request.getAllDesignOrderProjectMembers(design_order_id);
    
    const getAllAdmins = await Project.AdminsForMessaging(userId);
    
    // Get only the first admin (not all admins)
    const allAdmins = getAllAdmins.length > 0 ? getAllAdmins : [];
    
    // Combine project members with one admin and remove duplicates for job notes
    const combined = [
      // ...projectMembers,
      ...designersOnly,
      ...allAdmins
    ];
    
    // projectMembers = Array.from(
      let projectMembers = Array.from(
      new Map(
        combined
          .filter(user => user?.user_id) // filter out null or undefined user_id
          .map(user => [user.user_id, user]) // remove duplicates by user_id
      ).values()
    );
    
    // Get job note members for design orders (same as project members with admins)
    let jobNoteMembers = projectMembers || [];
    
    if (!empty(result)) {
      res.send({
        status: 1, 
        data: result,
        updates: allUpdates,
        attachments,
        reference_attachments,
        customerData,
        community,
        servicesData,
        productsData,
        projectMembers: designersOnly,
        jobNoteMembers,
        rejected_attachments,
        message: "Data fetched successfully"
      });
    }
    else {
      res.send({ status: 0, message: "Error in fetching design order by id" })
    }
  } catch (error) {
    console.log(error, 'error in get design order by id')
    res.send({ status: 0, message: 'failed to get design order by id' })
  }
}

const duplicateDesignOrder = async (req, res) => {
  try {
    const { design_order_id, userId } = req.body;

    if (empty(design_order_id)) {
      return res.send({ status: 0, message: 'design_order_id is required' });
    }

    const sourceDesignOrderData = await Request.getDesignOrderByIdNew(design_order_id);
    if (!sourceDesignOrderData || sourceDesignOrderData.length === 0) {
      return res.send({ status: 0, message: 'Design order not found' });
    }

    const sourceOrder = sourceDesignOrderData[0]?.dataValues || sourceDesignOrderData[0];
    const currentDate = moment().unix();
    const creatorId = userId || sourceOrder?.added_by;

    const designOrderData = {
      name: sourceOrder?.name,
      designer_email_sent: sourceOrder?.designer_email_sent,
      designer_sms_sent: sourceOrder?.designer_sms_sent,
      total_cost: sourceOrder?.total_cost,
      description: sourceOrder?.description,
      client: sourceOrder?.client,
      community: sourceOrder?.community || null,
      notes: sourceOrder?.notes || "",
      start_date: sourceOrder?.start_date || 0,
      due_date: sourceOrder?.due_date || 0,
      requested_due_date: sourceOrder?.requested_due_date || null,
      welcome_message: sourceOrder?.welcome_message,
      subject: sourceOrder?.subject || null,
      email_content: sourceOrder?.email_content || null,
      status: 1,
      client_approve: sourceOrder?.client_approve,
      added_by: creatorId,
      edited_by: creatorId,
      timestamp: currentDate,
      created_at: currentDate,
      is_in_queue: sourceOrder?.is_in_queue,
      is_from_client: sourceOrder?.is_from_client,
      collaborators: sourceOrder?.collaborators || null,
      estimate_id: sourceOrder?.estimate_id,
      syncWithQuickbooks: sourceOrder?.syncWithQuickbooks,
      reasonWithQb: sourceOrder?.reasonWithQb,
      card_order: sourceOrder?.card_order,
      completion: 1,
      design_order_job_status: 1,
      reason: sourceOrder?.reason,
      reject_type: sourceOrder?.reject_type,
      reject_file: sourceOrder?.reject_file,
      reject_file_type: sourceOrder?.reject_file_type,
      hold_reason: sourceOrder?.hold_reason,
      hold: sourceOrder?.hold,
      send_client_app_req: sourceOrder?.send_client_app_req,
      working_designer_id: sourceOrder?.working_designer_id,
      request_id: sourceOrder?.request_id,
      old_design_order_id: sourceOrder?.id,
    };

    const duplicatedDesignOrderId = await Request.addDesignOrders(designOrderData);

    if (!duplicatedDesignOrderId) {
      return res.send({ status: 0, message: 'Failed to duplicate design order' });
    }

    const [attachments, referenceAttachments, services, products, projectMembers, subtasks] = await Promise.all([
      Request.getAllDesignOrderAttachments(design_order_id),
      Request.getAllDesignOrderReferenceAttachments(design_order_id),
      Request.getDesignOrderById(design_order_id),
      Request.getDesignOrderProductsByDesignOrderId(design_order_id),
      Request.getAllDesignOrderProjectMembers(design_order_id),
      Request.getDesignOrdersSubTaskByDesignOrder(design_order_id),
    ]);
    await RequestActivity.addDesignOrderUpdate({
      design_order_id: design_order_id,
      activity: ` This Design order "${sourceOrder?.name}" has been reopened`,
      added_by: creatorId,
      timestamp: currentDate,
      status: 1,
    });

    const allAttachments = [...(attachments || []), ...(referenceAttachments || [])];
    for (const row of allAttachments) {
      const attachmentName = row?.name || row?.file_url;
      if (empty(attachmentName)) continue;
      const attachmentData = {
        name: attachmentName,
        file_type: row?.file_type || 'img',
        extension: row?.extension || 'img',
        file_size: row?.file_size || 2,
        design_order_id: duplicatedDesignOrderId,
        isDesign: row?.isDesign || 1,
        isNew: row?.isNew || 1,
        added_by: creatorId,
        timestamp: currentDate,
        approved: row?.approved,
        approved_by: row?.approved_by,
        reason: row?.reason,
        status: row?.status || 1,
        isChat: row?.isChat || 0,
        is_reference: row?.is_reference || 0,
      };
      const uploadResult = await Request.addDesignOrderAttachments(attachmentData);
      if (!uploadResult || Number.isNaN(Number(uploadResult))) {
        console.log('duplicateDesignOrder: failed to copy attachment', attachmentData);
      }
    }

    const serviceIdMap = new Map();
    for (const row of services || []) {
      const serviceData = {
        labour_cost_service_id: row?.labour_cost_service_id,
        quantity: row?.quantity,
        total: row?.total,
        unit_cost: row?.unit_cost,
        taken: row?.taken,
        job_cost: row?.job_cost,
        jobcost_quantity: row?.jobcost_quantity,
        jobcost_total_cost: row?.jobcost_total_cost,
        complete: row?.complete,
        status: row?.status,
        design_order_id: duplicatedDesignOrderId,
      };
      const newServiceId = await Request.addDesignOrderServices(serviceData);
      serviceIdMap.set(row?.id, newServiceId);
    }

    for (const row of products || []) {
      const productData = {
        design_order_id: duplicatedDesignOrderId,
        product_index: row?.product_index,
        name: row?.name,
        description: row?.description || '',
        status: row?.status || 1,
      };
      await Request.addDesignOrderProducts(productData);
    }

    for (const row of projectMembers || []) {
      if (empty(row?.staff_id)) continue;
      const memberData = {
        design_order_id: duplicatedDesignOrderId,
        staff_id: row?.staff_id,
        partner_id: row?.partner_id || null,
        contact_id: row?.contact_id || null,
        lead_id: row?.lead_id || null,
      };
      const alreadyExisting = await Request.projectMemberExist(memberData);
      if (!alreadyExisting) {
        await Request.addDesignOrdersProjectMember(memberData);
      }
    }

    const subtaskIdMap = new Map();
    for (const row of subtasks || []) {
      const subTaskData = {
        design_order_id: duplicatedDesignOrderId,
        name: row?.name,
        task_id: serviceIdMap.get(row?.task_id) || row?.task_id,
        skill_ids: row?.skill_ids,
        timestamp: currentDate,
        userid: creatorId || row?.userid,
        start_date: row?.start_date || null,
        due_date: row?.due_date || null,
        task_uid: row?.task_uid || 1,
        complete: row?.complete || 0,
        priority: row?.priority || 1,
        status: row?.status || 1,
        reporter_id: row?.reporter_id || creatorId || 1,
        description: row?.description || '',
        complete_sync: row?.complete_sync || 1,
      };
      const newSubTaskId = await Request.addDesignOrdersSubTask(subTaskData);
      subtaskIdMap.set(row?.id, newSubTaskId);
    }

    for (const row of subtasks || []) {
      const taskSubTasks = await Request.getAllDesignOrderProjectSubTaskBytaskId(row?.task_id, 1, creatorId || 1);
      const matchedSubTask = (taskSubTasks || []).find((st) => Number(st?.id) === Number(row?.id));
      const teamMembers = matchedSubTask?.teamMembers || [];
      const mappedSubTaskId = subtaskIdMap.get(row?.id);
      if (!mappedSubTaskId) continue;

      for (const member of teamMembers) {
        if (empty(member?.staff_id)) continue;
        const pm = {
          subtask_id: mappedSubTaskId,
          staff_id: member?.staff_id,
          partner_id: member?.partner_id || null,
          contact_id: member?.contact_id || null,
          lead_id: member?.lead_id || null,
        };
        const alreadyExisting = await Request.projectSubtaskMemberExist(pm, mappedSubTaskId);
        if (!alreadyExisting) {
          await Request.addDesignOrdersProjectSubtaskMember(pm);
        }
      }
    }

    res.send({
      status: 1,
      data: {
        id: duplicatedDesignOrderId,
        old_design_order_id: design_order_id,
      },
      message: 'Design order duplicated successfully',
    });
  } catch (error) {
    console.log(error, 'error in duplicate design order');
    res.send({ status: 0, message: 'failed to duplicate design order' });
  }
}

const mergeDuplicatedDesignOrderToSalesOrder = async (req, res) => {
  try {
    const { duplicated_design_order_id, userId } = req.body;
    if (empty(duplicated_design_order_id)) {
      return res.send({ status: 0, message: 'duplicated_design_order_id is required' });
    }

    const duplicatedDesignOrderData = await Request.getDesignOrderByIdNew(duplicated_design_order_id);
    if (!duplicatedDesignOrderData || duplicatedDesignOrderData.length === 0) {
      return res.send({ status: 0, message: 'Duplicated design order not found' });
    }

    const duplicatedDesignOrder = duplicatedDesignOrderData[0]?.dataValues || duplicatedDesignOrderData[0];
    if (empty(duplicatedDesignOrder?.old_design_order_id)) {
      return res.send({ status: 0, message: 'old_design_order_id not found for duplicated design order' });
    }

    const existingSalesOrderRequest = await Request.getSalesOrderRequestByDesignOrderId(
      duplicatedDesignOrder.old_design_order_id
    );
    const existingEstimateRequest = !existingSalesOrderRequest?.id
      ? await Request.getEstimateRequestByDesignOrderId(duplicatedDesignOrder.old_design_order_id)
      : null;
    const existingRequest = existingSalesOrderRequest?.id ? existingSalesOrderRequest : existingEstimateRequest;
    const mergeTargetLabel = existingSalesOrderRequest?.id ? 'Sales Order' : 'Estimate';
    const mergeTargetLabelLower = existingSalesOrderRequest?.id ? 'sales order' : 'estimate';

    if (!existingRequest?.id) {
      return res.send({ status: 0, message: 'Existing sales order/estimate request not found for old design order id' });
    }

    const requestId = existingRequest.id;
    const currentDate = moment().unix();

    const [selectedServices, uploaded] = await Promise.all([
      Request.getDesignOrderServicesByDesignOrderId(duplicated_design_order_id),
      Request.fetchAllDesignOrderAttachmentsForConvert(duplicated_design_order_id),
    ]);

    const requestUpdateData = {
      name: duplicatedDesignOrder?.name || existingRequest?.name,
      description: duplicatedDesignOrder?.description || existingRequest?.description,
      notes: duplicatedDesignOrder?.notes || "",
      labour_cost: duplicatedDesignOrder?.total_cost || 0,
      community: duplicatedDesignOrder?.community || null,
      submission_deadline: duplicatedDesignOrder?.start_date || 0,
      participation_ends: duplicatedDesignOrder?.due_date || 0,
      welcome_message: duplicatedDesignOrder?.welcome_message ? 1 : 0,
      subject: duplicatedDesignOrder?.welcome_message ? duplicatedDesignOrder?.subject : null,
      email_content: duplicatedDesignOrder?.welcome_message ? duplicatedDesignOrder?.email_content : null,
      collaborators: duplicatedDesignOrder?.collaborators || null,
      timestamp: currentDate,
    };

    const requestUpdated = await Request.updateRequets(requestId, requestUpdateData);
    if (!requestUpdated) {
      return res.send({ status: 0, message: `Failed to update existing ${mergeTargetLabelLower} request` });
    }

    await Request.deleteRequestAttachments(requestId);
    for (const row of uploaded || []) {
      const approvedImage = {};
      if (row?.file_type?.startsWith('image')) {
        approvedImage.approved = 0;
      } else {
        approvedImage.approved = 1;
      }
      approvedImage.name = row.name || row.url || row.file_url;
      approvedImage.file_type = row.file_type || 'img';
      approvedImage.extension = row.extension || 'img';
      approvedImage.file_size = row.file_size || row.size || 2;
      approvedImage.request_id = requestId;
      approvedImage.added_by = userId || duplicatedDesignOrder?.added_by || null;
      approvedImage.timestamp = moment().unix();
      await Request.addRequestAttachments(approvedImage);
    }

    const labourCostServiceIds = (selectedServices || []).map(service => service.labour_cost_service_id);
    for (const serviceId of labourCostServiceIds) {
      await Request.deleteRequestServicesByLabourCostServiceId(requestId, serviceId);
    }

    for (const row of selectedServices || []) {
      const requestProduct = {};
      let rowQuantity = row.quantity ? row.quantity : 1;
      requestProduct['product_index'] = 0;
      requestProduct['request_id'] = requestId;
      requestProduct['labour_cost_service_id'] = row.labour_cost_service_id;
      requestProduct['recipe_id'] = null;
      requestProduct['product_id'] = null;
      requestProduct['qty'] = rowQuantity;
      requestProduct['unit_cost'] = row.unit_cost || row.total;
      requestProduct['markup'] = row?.markup || 0;
      requestProduct['total_cost'] = row.total;
      requestProduct['rfp'] = 0;
      requestProduct['map_service'] = 0;
      requestProduct['jobcost_unit_cost'] = 0;
      requestProduct['jobcost_quantity'] = row?.jobcost_quantity ? row?.jobcost_quantity : 0;
      requestProduct['jobcost_total_cost'] = row?.jobcost_total_cost ? row?.jobcost_total_cost : 0;
      requestProduct['is_dos'] = 1;
      requestProduct['makeAsTask'] = row?.makeAsTask ? 1 : 0;
      requestProduct['makeInstallCard'] = row?.makeInstallCard ? 1 : 0;
      await Request.addRequestServices(requestProduct);
    }

    const customerOrderData = await CustomerOrder.getCustomerOrderRequestId(requestId);
    if (customerOrderData?.id) {
      const serviceTotal = Array.isArray(selectedServices)
        ? selectedServices.reduce((sum, row) => sum + (Number(row?.total) || 0), 0)
        : 0;
      const existingTax = Number(customerOrderData?.tax_amount ?? customerOrderData?.tax ?? 0) || 0;
      await CustomerOrder.updateCustomerOrder(customerOrderData.id, {
        sub_total: serviceTotal,
        total: serviceTotal + existingTax,
        grandTotal: serviceTotal + existingTax,
        timestamp: currentDate,
      });
    }

    await Request.updateDesignOrders(duplicated_design_order_id, {
      request_id: requestId,
      status: 3,
      timestamp: currentDate,
      edited_by: userId || duplicatedDesignOrder?.added_by || null,
    });
    // await RequestActivity.addDesignOrderUpdate({
    //   design_order_id: duplicated_design_order_id,
    //   activity: `🔄 ${mergeTargetLabel} has been merged with duplicated design order "${duplicatedDesignOrder?.name || ''}"`,
    //   added_by: userId || duplicatedDesignOrder?.added_by || null,
    //   timestamp: currentDate,
    //   status: 1,
    // });

    await RequestActivity.addRequestUpdate({
      request_id: requestId,
      activity: `🔄 ${mergeTargetLabel} has been merged with reopened design order "${duplicatedDesignOrder?.name || ''}"`,
      added_by: userId || duplicatedDesignOrder?.added_by || null,
      timestamp: currentDate,
      status: 1,
    });

    return res.send({
      status: 1,
      request_id: requestId,
      message: `Duplicated design order changes merged to existing ${mergeTargetLabelLower} successfully`,
    });
  } catch (error) {
    console.log(error, 'error in merge duplicated design order to sales order');
    return res.send({ status: 0, message: 'failed to merge duplicated design order to sales order' });
  }
}

const mergeDuplicatedDesignOrderToRequestEstimate = async (req, res) => {
  try {
    const { duplicated_design_order_id, userId } = req.body;
    if (empty(duplicated_design_order_id)) {
      return res.send({ status: 0, message: 'duplicated_design_order_id is required' });
    }

    const duplicatedDesignOrderData = await Request.getDesignOrderByIdNew(duplicated_design_order_id);
    if (!duplicatedDesignOrderData || duplicatedDesignOrderData.length === 0) {
      return res.send({ status: 0, message: 'Duplicated design order not found' });
    }

    const duplicatedDesignOrder = duplicatedDesignOrderData[0]?.dataValues || duplicatedDesignOrderData[0];
    if (empty(duplicatedDesignOrder?.old_design_order_id)) {
      return res.send({ status: 0, message: 'old_design_order_id not found for duplicated design order' });
    }

    const existingRequestEstimate = await Request.getRequestEstimateByDesignOrderId(
      duplicatedDesignOrder.old_design_order_id
    );
    if (!existingRequestEstimate?.id) {
      return res.send({ status: 0, message: 'Existing request estimate not found for old design order id' });
    }

    const requestId = existingRequestEstimate.id;
    const currentDate = moment().unix();
    const estimateServiceId = 44;

    const [designOrderProducts, uploaded, labourCostServices] = await Promise.all([
      Request.getDesignOrderProductsByDesignOrderId(duplicated_design_order_id),
      Request.fetchAllDesignOrderAttachmentsForConvert(duplicated_design_order_id),
      Product.getAllLabourCostServices(),
    ]);

    const estimateService = labourCostServices?.find((service) => Number(service?.service_type) === estimateServiceId);
    const estimateServiceUnitCost = Number(estimateService?.total_cost || estimateService?.cost || 0);
    const estimateServiceTotal = estimateServiceUnitCost;

    const requestUpdateData = {
      name: duplicatedDesignOrder?.name || existingRequestEstimate?.name,
      description: duplicatedDesignOrder?.description || existingRequestEstimate?.description,
      notes: duplicatedDesignOrder?.notes || "",
      labour_cost: duplicatedDesignOrder?.total_cost || 0,
      community: duplicatedDesignOrder?.community || null,
      submission_deadline: duplicatedDesignOrder?.start_date || 0,
      participation_ends: duplicatedDesignOrder?.due_date || 0,
      welcome_message: duplicatedDesignOrder?.welcome_message ? 1 : 0,
      subject: duplicatedDesignOrder?.welcome_message ? duplicatedDesignOrder?.subject : null,
      email_content: duplicatedDesignOrder?.welcome_message ? duplicatedDesignOrder?.email_content : null,
      collaborators: duplicatedDesignOrder?.collaborators || null,
      quotation: 1,
      is_request_estimate: 1,
      timestamp: currentDate,
    };

    const requestUpdated = await Request.updateRequets(requestId, requestUpdateData);
    if (!requestUpdated) {
      return res.send({ status: 0, message: 'Failed to update existing request estimate' });
    }

    await Request.deleteRequestAttachments(requestId);
    for (const row of uploaded || []) {
      const approvedImage = {};
      if (row?.file_type?.startsWith('image')) {
        approvedImage.approved = 0;
      } else {
        approvedImage.approved = 1;
      }
      approvedImage.name = row.name || row.url || row.file_url;
      approvedImage.file_type = row.file_type || 'img';
      approvedImage.extension = row.extension || 'img';
      approvedImage.file_size = row.file_size || row.size || 2;
      approvedImage.request_id = requestId;
      approvedImage.added_by = userId || duplicatedDesignOrder?.added_by || null;
      approvedImage.timestamp = moment().unix();
      await Request.addRequestAttachments(approvedImage);
    }

    await Request.deleteRequestServices(requestId);
    if (estimateService?.id) {
      const requestProduct = {
        product_index: 0,
        service_name: estimateService?.name || 'Estimate Service',
        request_id: requestId,
        labour_cost_service_id: estimateService.id,
        recipe_id: null,
        product_id: null,
        qty: 1,
        unit_cost: estimateServiceUnitCost,
        markup: Number(estimateService?.markup || 0),
        total_cost: estimateServiceTotal,
        rfp: 0,
        map_service: 0,
        jobcost_unit_cost: 0,
        jobcost_quantity: 0,
        jobcost_total_cost: 0,
        is_dos: 0,
        makeAsTask: 1,
        makeInstallCard: 0,
      };
      await Request.addRequestServices(requestProduct);
    }

    const customerOrderData = await CustomerOrder.getCustomerOrderRequestId(requestId);
    if (customerOrderData?.id) {
      const existingTax = Number(customerOrderData?.tax_amount ?? customerOrderData?.tax ?? 0) || 0;
      const customerData = duplicatedDesignOrder?.client ? await Client.getClientById(duplicatedDesignOrder.client) : null;

      await CustomerOrder.updateCustomerOrder(customerOrderData.id, {
        sub_total: estimateServiceTotal,
        total: estimateServiceTotal + existingTax,
        grandTotal: estimateServiceTotal + existingTax,
        customer_id: customerData?.id || customerOrderData?.customer_id || null,
        customer_name: customerData?.name || customerOrderData?.customer_name || null,
        customer_phone: customerData?.phone || customerOrderData?.customer_phone || null,
        customer_address: customerData?.address || customerOrderData?.customer_address || null,
        timestamp: currentDate,
      });

      await CustomerOrderProduct.deletedCustomerOrderProduct(customerOrderData.id);
      for (const product of designOrderProducts || []) {
        const requestProduct = {
          order_id: customerOrderData.id,
          is_product_row: 1,
          product_index: product?.product_index || 0,
          product_id: null,
          product_name: product?.name || '',
          product_desc: product?.description || null,
          product_notes: null,
          install_notes: null,
          product_price: 0.00,
          product_retail_price: 0.00,
          product_markup: 0,
          product_qty: 1,
          location_quantity: 1,
          product_sub_total: 0,
          product_tax: 0,
          product_total: 0,
          discount: 0,
          adjustment: 0,
          adj_value_type: 1,
          install_qty: 1,
          install_adjustment: 0,
          install_adj_value_type: 1,
          timestamp: currentDate,
          status: 1,
          rfp: 0,
          material: 0,
          map_service: 0,
          isAdditionalProduct: 0,
          is_single_card: 0,
          jobcost_quantity: 0,
          jobcost_total_cost: 0,
          taken: 0
        };
        await CustomerOrderProduct.createCustomerOrder(requestProduct);
      }
    }

    await Request.updateDesignOrders(duplicated_design_order_id, {
      request_id: requestId,
      status: 3,
      timestamp: currentDate,
      edited_by: userId || duplicatedDesignOrder?.added_by || null,
    });

    // await RequestActivity.addDesignOrderUpdate({
    //   design_order_id: duplicated_design_order_id,
    //   activity: `🔄 Request Estimate has been merged with duplicated design order "${duplicatedDesignOrder?.name || ''}"`,
    //   added_by: userId || duplicatedDesignOrder?.added_by || null,
    //   timestamp: currentDate,
    //   status: 1,
    // });

    await RequestActivity.addRequestUpdate({
      request_id: requestId,
      activity: `🔄 Request Estimate has been merged with reopened design order "${duplicatedDesignOrder?.name || ''}"`,
      added_by: userId || duplicatedDesignOrder?.added_by || null,
      timestamp: currentDate,
      status: 1,
    });

    return res.send({
      status: 1,
      request_id: requestId,
      message: 'Duplicated design order changes merged to existing request estimate successfully',
    });
  } catch (error) {
    console.log(error, 'error in merge duplicated design order to request estimate');
    return res.send({ status: 0, message: 'failed to merge duplicated design order to request estimate' });
  }
}

const deleteDesignOrderById = async (req, res) => {
  try {
    const { design_order_id,client,userId } = req.body;
    let qbErr = null
    let designOrder = await Request.getDesignOrderByIdNew(design_order_id);
    console.log("design orderssss>", designOrder);

    if (!designOrder || designOrder.length === 0) {
      return res.send({ status: 0, message: 'Design Order not found', qbErr: qbErr });
    }
    
    let previousStatus = designOrder[0]?.dataValues?.status;
    console.log("previousStatus orderssss>", previousStatus);
    const clientUser = await Client.getClientById(client);
    const clientUserId = clientUser?.user_id;

    // const allUserIds = await Admin.getAllUserIds();
    // let usersToUpdate = [...new Set([clientUserId, ...allUserIds].filter(Boolean))];

    let result = await Request.deleteDesignOrderById(design_order_id)
    if (result) {
      // const updateCounts = async (userId) => {
      //    if (designOrder[0]?.dataValues?.hold === 1) {
      //     await Request.designCountIncrementField(userId, "hold_count", -1, "hold_count_ids", design_order_id, false);
      //     await Request.designCountIncrementField(userId, "job_hold_count", -1, "job_hold_count_ids", design_order_id, false);
      //     console.log("hold_count updated")
      //   }
      //   else if (previousStatus === 1) {
      //     await Request.designCountIncrementField(userId, "open_count", -1, "open_count_ids", design_order_id, false);
      //   } else if (previousStatus === 2) {
      //     await Request.designCountIncrementField(userId, "approved_count", -1, "approved_count_ids", design_order_id, false);
      //   } else if (previousStatus === 6) {
      //     await Request.designCountIncrementField(userId, "rejected_count", -1, "rejected_ids", design_order_id, false);
      //   }
      //   if (designOrder[0]?.dataValues?.design_order_job_status === 1) {
      //     await Request.designCountIncrementField(userId, "new_count", -1, "new_count_ids", design_order_id, false);
      //   console.log("newcount updated")
      //   } else if (designOrder[0]?.dataValues?.design_order_job_status === 2) {
      //     await Request.designCountIncrementField(userId, "in_progress_count", -1, "in_progress_count_ids", design_order_id, false);
      //     console.log("inprogress updated")
      //   }else if (designOrder[0]?.dataValues?.design_order_job_status === 3) {
      //     await Request.designCountIncrementField(userId, "completed_count", -1, "completed_count_ids", design_order_id, false);
      //     console.log("completed_count updated")
      //   }
      // };
      // await Promise.all(usersToUpdate.map(user => updateCounts(user)));
      // console.log("Successfully removed deleted design order from counts.");

      // ✅ OPTIMIZATION: Send response immediately after critical operations
      res.send({
        status: 1,
        data: result,
        message: 'Order deleted successfully',
        qbErr: qbErr
      });

      // ✅ OPTIMIZATION: Handle all notifications/emails/SMS/activity logs async (non-blocking)
      const handleNotificationsAsync = async () => {
        try {
      let currentDate = moment().unix();
      let notification_msg = `🗑️ Design Order <strong>"${designOrder[0]?.dataValues?.name}"</strong> has been deleted.`;
          let notification_url = `design-order/view/${design_order_id}`;

          // Add Request Activity
      let requestActivity = {};
      requestActivity['design_order_id'] = design_order_id;
      requestActivity['activity'] = notification_msg;
      requestActivity['added_by'] = userId;
      requestActivity['timestamp'] = currentDate;
          await RequestActivity.addDesignOrderUpdate(requestActivity);
      console.log("Request activity added for deleted design order");
      
      // Notification for the user who deleted
      if (userId) {
            await Admin.incrementField(userId, "noti_count", 1);
        let notificationData_user = {
          user_id: userId,
          url: notification_url,
          timestamp: currentDate,
          message: notification_msg,
          status: 1,
          from_id: userId,
        };
            await Admin.addNotification(notificationData_user);
      }

          // Notifications for all admins (parallelized)
      let adminIds = await Admin.getAllAdminIds();
          const adminNotificationPromises = adminIds
            .filter(eachadminIds => eachadminIds != userId)
            .map(async (eachadminIds) => {
              try {
                await Admin.incrementField(eachadminIds, 'noti_count', 1);
          let addNotification = {
            user_id: eachadminIds,
            url: notification_url,
            timestamp: currentDate,
            message: notification_msg,
            status: 1,
            from_id: userId,
          };
                await Admin.addNotification(addNotification);
              } catch (err) {
                console.error(`❌ Error sending notification to admin ${eachadminIds}:`, err);
        }
            });
          await Promise.all(adminNotificationPromises);
      console.log("User notifications added for deleted design order");

          // Admin/Manager email and SMS notifications (parallelized)
      let adminMangerIds = await Admin.getAllAdminAndManagerUserIds();
      console.log('adminMangerIds', adminMangerIds);
      let allAdminToCheck = [...new Set(adminMangerIds.filter(Boolean))];
      const profileData = await Settings.getCompanyProfileDetails();
      console.log('alluserssssssssarray', allAdminToCheck);

          const adminEmailSmsPromises = allAdminToCheck.map(async (userIdToCheck) => {
        try {
          console.log('useriddddd', userIdToCheck);
          const creator = await Admin.getUserNotificationAndPermissionSettings(userIdToCheck);
          console.log('creatorrrrrrrrr', creator);

              if (!creator) return;

          const recipientPhone = creator?.phone || TWILIO_PHONE;
          const Phone_country_code = creator?.phone_country_code || '1';
          console.log('recipientPhoneeee', recipientPhone);
          console.log('Phone_country_codeeeee', Phone_country_code);

          console.log("✅ Sales order SMSsssssss");

          // Email Notification
              const emailPromise = (async () => {
          if (
            creator.emailEnabled === 1 &&
            creator.email &&
            creator.pcb_new_design_jobs === 1 && 
            creator.pcb_new_design_jobs_email === 1
          ) {
            let welcome = "Hello " + creator.name + ",";
            let subject = `📦 Design Order Deleted: ${designOrder[0]?.dataValues?.name}`;
            let email_content = "Design order has been deleted. Please review the details in your software.";

            let message = designersEmail({
              designer: creator,
                    designOrderName: designOrder[0]?.dataValues?.name,
              email_content,
              welcome,
              baseUrl: BASE_URL,
              profileData: profileData[0],
            });

            message = message.replace('[emailContent]', email_content);
            message = message.replace('[CONTENT]', welcome);
            message = message.replace('[LOGIN_URL]', `${BASE_URL}`);

            const mailOptions = {
              to: creator.email,
              from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
              subject: subject,
              text: subject,
              html: message,
            };

            try {
              await transporter.sendMail(mailOptions);
              console.log(`✅ design order email sent to creator4: ${creator.email}`);
            } catch (err) {
              console.error(`❌ Error sending design order email to ${creator.email}:`, err);
            }
          }
              })();

          // SMS Notification
              const smsPromise = (async () => {
          if (
            creator.smsEnabled === 1 &&
            creator.phone &&
            creator.pcb_new_design_jobs === 1 && 
            creator.pcb_new_design_jobs_sms === 1
          ) {
            const smsContent = `🎨 Hello ${creator.name},\n\n📢 Design Order "${designOrder[0]?.dataValues?.name}" has been Deleted.\n\n🔗 Login: ${BASE_URL}\n\n– Team SignWise`;

            try {
              await sendMessageToPhoneNumber(recipientPhone, smsContent, Phone_country_code);
              console.log("✅ Design order SMS sent to creator");
            } catch (err) {
              console.error("❌ Error sending Design order SMS to creator:", err);
            }
          }
              })();

              await Promise.all([emailPromise, smsPromise]);
        } catch (err) {
          console.error(`❌ Error processing user ${userIdToCheck}:`, err);
        }
          });
          await Promise.all(adminEmailSmsPromises);
        } catch (error) {
          console.error('Error in async notifications handler:', error);
        }
      };

      // ✅ OPTIMIZATION: Run non-critical operations asynchronously (fire and forget)
      handleNotificationsAsync().catch(error => {
        console.error('Error in async operations:', error);
      });

    } else {
      res.send({ status: 0, message: 'Order delete failed',qbErr:qbErr })
    }
  } catch (error) {
    console.log(error, 'error in delete order')
    res.send({ status: 0, message: 'failed to delete order' ,qbErr:qbErr})
  }
}

const updateDesignOrder = async (req, res) => {
  try {
    UniqueEstimateId =null
    doc_number = null //attach
    const { values, userId, userType,isClient, design_order_id ,selectedServices,totalCost,designers, isClientEdited, socketId, quantity ,collaborators } = req.body;
    const collaboratorsStr = collaborators != null
    ? (Array.isArray(collaborators) ? collaborators.filter(Boolean).join(',') : String(collaborators).trim())
    : null
    // 🚀 PROGRESS TRACKING: Socket progress emission function
    const emitProgress = (step, message, progress) => {
      const progressData = {
        step,
        message,
        progress,
        userId,
        domain: DOMAIN,
        timestamp: moment().unix(),
        type: 'update_design_order_progress'
      };
      
      // Emit to working socket server (same as chat)
      console.log(`[UPDATE-DESIGN-PROGRESS] 📡 Emitting progress to working socket server...`);
      try {
        const workingSocket = ioClient(REST_API_IO, {
          path: '/socket.io',
          transports: ['websocket', 'polling'],
          secure: true,
          timeout: 5000
        });
        
        workingSocket.on('connect', () => {
          console.log(`[UPDATE-DESIGN-PROGRESS] 🔌 Connected to working socket server`);
          console.log(`[UPDATE-DESIGN-PROGRESS] 📊 Emitting progress data:`, progressData);
          
          // Try emitting as 'message' event (same as chat) to test if socket server handles it
          workingSocket.emit('message', {
            ...progressData,
            type: 'progress',
            conversationId: `update_design_progress_${userId}`,
            senderId: 'system'
          });
          
          // Also try the original request_progress event
          workingSocket.emit('request_progress', progressData);
          
          console.log(`[UPDATE-DESIGN-PROGRESS] ✅ Successfully emitted progress to working socket server`);
          
          // Disconnect after emitting
          setTimeout(() => {
            workingSocket.disconnect();
          }, 1000);
        });
        
        workingSocket.on('connect_error', (error) => {
          console.error(`[UPDATE-DESIGN-PROGRESS] ❌ Working socket connection error:`, error);
        });
        
      } catch (error) {
        console.error(`[UPDATE-DESIGN-PROGRESS] ❌ Error creating working socket connection:`, error);
      }
    };

    // Initial progress
    console.log(`[UPDATE-DESIGN-PROGRESS] 🚀 Starting design order update for user:`, userId);
    emitProgress('start', `Initializing design order update...`, 5);
    
    let currentTime = moment().unix()
    const partnerData = values.clientsData
    let submission_deadline = moment(values.start).unix()
    let participation_ends = moment(values.end).unix()
    let requestdata = {}
    emitProgress('fetching_design_order', `Fetching design order data...`, 10);
    let result = await Request.getAllDesignOrders(design_order_id, userId, userType,isClient)
    let previousServiceData = await Request.getDesignOrderById(design_order_id)
    const prevDesignerEmailSent = previousServiceData?.designer_email_sent || 0;
    const prevDesignerSMSSent = previousServiceData?.designer_sms_sent || 0;
    const profileData = await Settings.getCompanyProfileDetails();

    const previousDesigners = await Request.getDesignOrderDesigners(design_order_id);
    console.log('previousDesigners', previousDesigners)
    const newDesignerIds = designers.map(d => d.id);
    console.log('newDesignerIds', newDesignerIds)
    const oldDesignerIds = previousDesigners.map(d => d.id);
    console.log('oldDesignerIds', oldDesignerIds)
    const previousProducts = await Request.getDesignOrderProductsByDesignOrderId(design_order_id) || [];

    const newlyAdded = newDesignerIds.filter(id => !oldDesignerIds.includes(id));
    console.log('newlyAdded', newlyAdded)
    
    const alreadyExisting = oldDesignerIds.filter(id => newDesignerIds.includes(id));
    console.log('alreadyExisting', alreadyExisting)

    const designersChanged =
      newlyAdded.length > 0 ||
      oldDesignerIds.length !== newDesignerIds.length;

      emitProgress('checking_designer_changes', `Checking designer changes...`, 20);
      // ✅ OPTIMIZATION: Prepare designer notification data for async processing
      const eligibleEmailDesigners = designers.filter(
        (d) => d.emailEnabled === 1 && d.pcb_new_design_jobs === 1 && d.pcb_new_design_jobs_email === 1
      );
      console.log('eligibleEmailDesigners', eligibleEmailDesigners)

      const eligibleViewDesignEmailDesigners = designers.filter(
        d => d.emailEnabled === 1 && d.view_design_permission === 1 && d.pcb_new_design_jobs_sms !== 1
      );

    // ✅ OPTIMIZATION: Calculate removed designer IDs (needed for async processing)
    const removedDesignerIds = oldDesignerIds.filter(id => !newDesignerIds.includes(id));
      console.log('removedDesignerIds', removedDesignerIds);

    // ✅ OPTIMIZATION: All designer notifications (emails/SMS for newly added, existing, and removed designers) 
    // are now handled asynchronously after the response is sent (see handleNotificationsAsync function below)

    // --- Log designers update in design_order_updates ---
    // const addedDesignerNames = newlyAdded
    //   .map(id => {
    //     const d = designers.find(des => des.id === id);
    //     if (!d) return null;
    //     return d.name || (d.first_name ? `${d.first_name} ${d.last_name || ''}`.trim() : d.company_name) || d.id?.toString() || null;
    //   })
    //   .filter(Boolean);

    // // Try to resolve removed designer names from user records for reliable display
    // const removedDesignerDetailsAll = removedDesignerIds.length > 0
    //   ? await Request.getUsersByIds(removedDesignerIds)
    //   : [];
    // const removedNameMap = new Map();
    // (removedDesignerDetailsAll || []).forEach(d => {
    //   const nm =
    //     d?.name ||
    //     (d?.first_name ? `${d.first_name} ${d.last_name || ''}`.trim() : d?.company_name) ||
    //     null;
    //   if (nm) removedNameMap.set(d.id, nm);
    // });

    // const removedDesignerNames = removedDesignerIds
    //   .map(id => {
    //     const resolved = removedNameMap.get(id);
    //     if (resolved) return resolved;
    //     const d = previousDesigners.find(prev => prev.id === id);
    //     if (!d) return null;
    //     const name =
    //       d.name ||
    //       (d.first_name ? `${d.first_name} ${d.last_name || ''}`.trim() : d.company_name);
    //     return name || null;
    //   })
    //   .filter(Boolean);

    // if (addedDesignerNames.length > 0 || removedDesignerNames.length > 0) {
    //   const userData = await Admin.getUserById(userId);
    //   const userName = userData && userData[0]
    //     ? (userData[0].first_name ? `${userData[0].first_name} ${userData[0].last_name || ''}`.trim() : userData[0].company_name || 'Unknown User')
    //     : 'Unknown User';

    //   const changeParts = [];
    //   if (addedDesignerNames.length > 0) changeParts.push(`added: "${addedDesignerNames.join('", "')}"`);
    //   if (removedDesignerNames.length > 0) changeParts.push(`removed: "${removedDesignerNames.join('", "')}"`);

    //   const designerActivity = `👥 Designers updated (${changeParts.join(' | ')}) by "${userName}"`;

    //   const designerUpdateData = {
    //     design_order_id: design_order_id,
    //     activity: designerActivity,
    //     added_by: userId,
    //     timestamp: moment().unix(),
    //     status: 1
    //   };

    //   await RequestActivity.addDesignOrderUpdate(designerUpdateData);
    // }

    console.log("previousServiceData", previousServiceData)
    result = result.data
    requestdata['name'] = values.name,
    requestdata['client'] = values?.clients?.id,
    requestdata['total_cost'] = totalCost;
    requestdata['community'] = values.community.id||null;
    requestdata['designer_email_sent'] = values.designer_email_sent;
    requestdata['designer_sms_sent'] = values.designer_sms_sent;
    requestdata['description'] = values.description,
    requestdata['start_date'] = values.start
    requestdata['notes']=values.notes||""
    // requestdata['send_client_app_req']= isClientEdited ? 0 : result[0]?.send_client_app_req
    requestdata['send_client_app_req']= 0
    let uploaded = values.avatarUrl;
    let uploadedReferenceFiles = values.referenceFiles;

    // Capture previous attachments for change tracking (designs and references)
    const previousDesignAttachments = await Request.fetchAllDesignOrderAttachments(design_order_id) || [];
    const previousReferenceAttachments = await Request.fetchAllDesignOrderReferenceAttachments(design_order_id) || [];
    const prevDesignNames = previousDesignAttachments.map(att => att.name).filter(Boolean);
    const prevReferenceNames = previousReferenceAttachments.map(att => att.name).filter(Boolean);

    // console.log('uploadeduploaded',uploaded)
    // return
    if (values.end !== (undefined || null)) {
      requestdata['due_date'] = values.end
    } else {
      requestdata['due_date'] = 0
    }
    if (values.welcome_message == true) {
      requestdata['welcome_message'] = values.welcome_message
      if (values.subject !== (undefined || null)) {
        requestdata['subject'] = values.subject
      } else {
        requestdata['subject'] = null
      }
      if (values.email_content !== (undefined || null)) {
        requestdata['email_content'] = values.email_content
      } else {
        requestdata['email_content'] = null
      }
    } else {
      requestdata['welcome_message'] = false
      requestdata['subject'] = null
      requestdata['email_content'] = null
    }
    if (values.draft == true) {
      requestdata['status'] = 4
    } else if (result[0]?.status == 6 && result[0]?.send_client_app_req == 1) {
      requestdata['status'] = 1
      requestdata['design_order_job_status'] = 3
      requestdata['send_client_app_req'] = 1
      requestdata['client_approve'] = values?.client_approve == false ? 0 : 1
      requestdata['reject_type'] = null
      // requestdata['requested_due_date'] = null
    } 
    else if (result[0]?.status == 6 && result[0]?.send_client_app_req == 0) {
      requestdata['status'] = 1
      requestdata['design_order_job_status'] = 3
      requestdata['send_client_app_req'] = 0
      requestdata['client_approve'] = values?.client_approve == false ? 0 : 1
      requestdata['reject_type'] = null
      // requestdata['requested_due_date'] = null
    } 
    else {
      // requestdata['status'] = values?.client_approve == false ? 2 :1
      requestdata['status'] = 1
      // requestdata['design_order_job_status'] = 1
      requestdata['client_approve'] = values?.client_approve == false ? 0 : 1
    }
    // requestdata['added_by'] = userId,
    requestdata['edited_by'] = userId,  
    requestdata['timestamp'] = currentTime
    // requestdata['is_from_client'] = (userType == 3 || isClient) ? 1 : 0
    requestdata['collaborators'] = collaboratorsStr || null
    requestdata['is_in_queue'] = (userType == 3 || isClient) ? 0 : 1
    requestdata['reason'] = null
    requestdata['reject_file'] = null
    requestdata['reject_file_type'] = null
    let updateReqID = await Request.updateDesignOrders(design_order_id, requestdata);
    
    // Get userType from users table to validate
    const userData = await Admin.getUserById(userId);
    const actualUserType = userData && userData[0] ? userData[0].user_type : null;
    
    // Update design_order_job_status based on userType
    if(design_order_id){
      const fundDesignOrderById = await Request.getDesignOrderByIdNew(design_order_id)
      const designOrder = fundDesignOrderById[0];
      let currentJobStatus = designOrder?.dataValues?.design_order_job_status;

      if(actualUserType === 11){
        // Designer userType 11: Set job status to 2 and save working_designer_id from status 1, 3, or 4
        if(currentJobStatus === 1 || currentJobStatus === 3){
          const updateData = {
            design_order_job_status: 2,
            working_designer_id: userId
          }
          await Request.updateDesignOrders(design_order_id, updateData);
          console.log(`Updated design_order_job_status from ${currentJobStatus} to 2 for userType 11 in updateDesignOrder`);
        } else if(currentJobStatus === 4){
          // If rejected (status 4), clear working_designer_id
          const updateData = {
            working_designer_id: null
          }
          await Request.updateDesignOrders(design_order_id, updateData);
          console.log('Cleared working_designer_id for rejected design order (status 4) in updateDesignOrder');
        }
      } else {
        // Other users: Set job status to 1 and clear working_designer_id
        const updateData = {
          // design_order_job_status: 1,
          working_designer_id: null
        }
        await Request.updateDesignOrders(design_order_id, updateData);
        console.log('Updated design_order_job_status to 1 for non-designer user in updateDesignOrder');
      }
    }
    
    // ✅ OPTIMIZATION: Prepare partner invitation email data for async processing
    let partnerInvitationEmailData = null;
    if (result[0]?.client != partnerData[0]?.id) {
      let subject = 'A new design order awaiting your acceptance';
      let email_content = `You are invited to join a new design order by ` + values?.userName;
      if (req.body.values.welcome_message) {
        subject = req.body.values.subject
        email_content = req.body.values.email_content
      }
      if (partnerData && partnerData.length > 0) {
        partnerInvitationEmailData = {
          partnerData,
          subject,
          email_content,
          BASE_URL,
          SEND_EMAIL,
          transporter
        };
      }
    }

    if (updateReqID) {
      emitProgress('updating_design_order', `Updating design order...`, 30);
      let allSubtasks = await Request.getDesignOrdersSubTaskByDesignOrder(design_order_id);
      let deleteDesignOrderServices = await Request.deleteDesignOrderServices(design_order_id)
      let deleteDesignOrderSubTask = await Request.deleteDesignOrdersSubTask(design_order_id)
      let deleteDesignOrderMembers = await Request.deleteDesignOrdersProjectMember(design_order_id);
      let deleteDesignOrderProducts = await Request.deleteDesignOrderProductsByDesignOrderId(design_order_id);
      for(const subtask of allSubtasks){
        let sub_Task_mem = await Request.deleteDesignOrdersProjectSubtaskMember(subtask.id);
      }

      if(selectedServices?.length > 0){
        emitProgress('updating_services', `Updating design services...`, 50);
        for (const product of selectedServices) {
          const serviceData={}
          serviceData.labour_cost_service_id=product.id,
          serviceData.quantity=product?.quantity || 1,
          serviceData.unit_cost=product.total_cost,
          serviceData.total=(parseFloat(product?.quantity) || 1) * (parseFloat(product.total_cost) || 0),
          serviceData.design_order_id=design_order_id

          let serviceDataResult = await Request.addDesignOrderServices(serviceData,design_order_id)

          if(product?.workflow){
            let skillSet = []
            let workflowData =await Product.getWorkflowById(product?.workflow)
            skillSet.push(workflowData)
            const allSkillIds = skillSet?.flatMap(workflow => workflow?.map(stage => stage?.skill_ids?.split(',')))?.flat()
            let uniqueSkillIds = [...new Set(allSkillIds)];
            uniqueSkillIds = uniqueSkillIds?.filter(skillId => skillId !== "" && skillId !== undefined);
            let uniqueSkillIdsString = uniqueSkillIds?.join(',');
            uniqueSkillIdsString=uniqueSkillIdsString?.replace(/,$/, '');
            let projectManagers=[]
            if(!empty(uniqueSkillIdsString)){
              projectManagers = await Project.getTeamMembersBasedOnSkills(uniqueSkillIdsString, 14)
            }
            let pm = {}
            // for (const projectManager of projectManagers) {
              for (const designer of designers) {
              pm = {
                design_order_id: design_order_id,
                // staff_id: !empty(projectManager["id"]) ? projectManager["id"] : NULL,
                staff_id: !empty(designer["id"]) ? designer["id"] : NULL,
                partner_id: null,
                contact_id: null,
                lead_id: null,
              };
              let alreadyExisting = await Request.projectMemberExist(pm);
              if (!alreadyExisting) {
                let insertId = await Request.addDesignOrdersProjectMember(pm);
              }
              // let insertId = await Request.addDesignOrdersProjectMember(pm);
            }
            for (const workflow of workflowData) {
              console.log(workflow, '--------workflow')
              const subTask = {
                name: workflow.name,
                design_order_id : design_order_id,
                task_id: serviceDataResult,
                skill_ids: workflow?.skill_ids,
                timestamp: moment().unix(),
                userid: userId,
                start_date: values.start || null,
                due_date: values.end || null,
                task_uid: 1,
                complete:  0,
                priority: 1,
                status:  1,
                reporter_id:userId,
                description: workflow?.message,
              };
              console.log('subTask===',subTask)
              const projectSubTaskId = await Request.addDesignOrdersSubTask(subTask);
              console.log('projectSubTaskId===',projectSubTaskId)
              // for (const projectManager of projectManagers) {
                for (const designer of designers) {
                let pm = {
                  subtask_id: projectSubTaskId,
                  // staff_id: !empty(projectManager["id"]) ? projectManager["id"] : NULL,
                  staff_id: !empty(designer["id"]) ? designer["id"] : NULL,
                  partner_id: null,
                  contact_id: null,
                  lead_id: null,
                };
                let alreadyExisting = await Request.projectSubtaskMemberExist(pm, projectSubTaskId);

                if(!alreadyExisting){
                  let sub_Task_mem = await Request.addDesignOrdersProjectSubtaskMember(pm);
                }
              }
            }
          }
        }
      }

      const designOrderProducts = values?.products || values?.productRows || []
      const validProducts = designOrderProducts.filter((product) => {
        const name = product?.product_name || product?.name || ''
        return String(name).trim() !== ''
      })
      if (validProducts.length > 0) {
        emitProgress('updating_products', `Updating design products...`, 60);
        for (const [index, product] of validProducts.entries()) {
          const productIndex = Number(
            product?.product_index ?? product?.indexId ?? index + 1
          ) || index + 1;
          const productData = {
            design_order_id,
            product_index: productIndex,
            name: (product?.product_name || product?.name || '').trim(),
            description: product?.product_desc || product?.description || '',
            status: 1,
          }
          await Request.addDesignOrderProducts(productData)
        }
      }

      let deleteDesignOrder = await Request.deleteDesignOrderAttachments(design_order_id);
      if (!empty(uploadedReferenceFiles)) {
        emitProgress('updating_reference_attachments', `Updating reference attachments...`, 60);
        for (const row of uploadedReferenceFiles) {
          let approvedImage = {}
          approvedImage.name = row.url;
          approvedImage.file_type = row.file_type;
          approvedImage.extension = row.extension;
          approvedImage.file_size = row.size;
          approvedImage.design_order_id = design_order_id;
          // approvedImage.added_by = userId
          approvedImage.added_by =row?.added_by || userId
          approvedImage.approved=row.approved || 0,
          approvedImage.approved_by=row.approved_by_id || null,
          approvedImage.status = row?.status || 1,
          approvedImage.isChat =row.isChat || 0,
          approvedImage.isDesign =row.isDesign,
          approvedImage.isNew =row.isNew || 1,
          approvedImage.reason =row.reason || null,
          approvedImage.timestamp = moment().unix()
          approvedImage.is_reference = 1
          let upload = await Request.addDesignOrderAttachments(approvedImage)
          // const isPdfRef =
          //   approvedImage.file_type === 'application/pdf' ||
          //   approvedImage.file_type === 'pdf' ||
          //   approvedImage.extension === 'pdf';
          // if (isPdfRef && upload) {
          //   try {
          //     await convertDesignOrderPdfToPngHelper(upload);
          //   } catch (conversionError) {
          //     console.error(`Error converting PDF for design order attachment ${upload}:`, conversionError);
          //   }
          // }
        }
      }
      if (!empty(uploaded)) {
        emitProgress('updating_attachments', `Updating design order attachments...`, 70);
        for (const row of uploaded) {
          console.log('row555555', row)
          let approvedImage = {}
          approvedImage.name = row.url;
          approvedImage.file_type = row.file_type;
          approvedImage.extension = row.extension;
          approvedImage.file_size = row.size;
          approvedImage.design_order_id = design_order_id;
          // approvedImage.added_by = userId
          
          approvedImage.added_by =row?.added_by || userId
          approvedImage.approved=row.approved || 0,
          approvedImage.approved_by=row.approved_by_id || null,
          approvedImage.status = row?.status || 1,
          approvedImage.isChat =row.isChat || 0,
          approvedImage.isDesign =row.isDesign,
          approvedImage.isNew =row.isNew || 1,
          approvedImage.reason =row.reason || null,

          approvedImage.timestamp = moment().unix()
          let upload = await Request.addDesignOrderAttachments(approvedImage)
          // const isPdfDesign =
          //   approvedImage.file_type === 'application/pdf' ||
          //   approvedImage.file_type === 'pdf' ||
          //   approvedImage.extension === 'pdf';
          // if (isPdfDesign && upload) {
          //   try {
          //     await convertDesignOrderPdfToPngHelper(upload);
          //   } catch (conversionError) {
          //     console.error(`Error converting PDF for design order attachment ${upload}:`, conversionError);
          //   }
          // }
        }
        
        // If this is the first attachment, set design_order_job_status = 2 (in progress)
        // if (uploaded?.length > 0) {
        //   const designOrder = await Request.getDesignOrderByIdNew(design_order_id);
        //   const currentJobStatus = designOrder?.[0]?.dataValues?.design_order_job_status;
        //   // Only update if current status is 1 (new)
        //   if (currentJobStatus == 1) {
        //     const updateData = {
        //       design_order_job_status: 2
        //     };
        //     await Request.updateDesignOrders(design_order_id, updateData);
        //     console.log('Updated design_order_job_status to 2 for first attachment in updateDesignOrder');
        //   }
        // }
      }
      
      // ✅ OPTIMIZATION: Send response immediately after critical operations
      emitProgress('finalizing_update', `Finalizing design order update...`, 95);
      emitProgress('complete', `Design order updated successfully!`, 100);
      res.send({ status: 1, message: 'Design Order updated successfully' });

      // ✅ OPTIMIZATION: Prepare notification data for async processing

      // Compare both id and name for product updates
      const prevProducts = previousProducts.map(product => ({
        id: product.id,
        name: product.name.trim(),
        product_desc: product?.product_desc || product?.description || '',
      }));

      const newProducts = (values?.products || values?.productRows || []).map(product => ({
        id: product?.product_id,
        name: (product?.product_name || product?.name || '').trim(),
        product_desc: product?.product_desc || product?.description || '',
      }));
      console.log('prevProducts===',prevProducts)
      console.log('newProducts===',newProducts)

      // Initialize arrays to track the changes
      const addedProducts = newProducts.filter(product => !prevProducts.some(p => p.id === product.id));
      const removedProducts = prevProducts.filter(product => !newProducts.some(p => p.id === product.id));
      const updatedProducts = newProducts.filter(product =>
        prevProducts.some(p =>
          p.id === product.id &&
          (p.name !== product.name || p.product_desc !== product.product_desc)
        )
      );

      // Initialize the notification message
      let notification_msg = `✏️ The Design Order has been updated. `;
      let changeDetails = [];

     // Collect granular changes for products
      if (addedProducts.length > 0) {
        changeDetails.push(`Added products: "${addedProducts.map(p => p.name).join('", "')}"`);
      }
      if (removedProducts.length > 0) {
        changeDetails.push(`Removed products: "${removedProducts.map(p => p.name).join('", "')}"`);
      }
      // Handle updates and show if name or description changed
if (updatedProducts.length > 0) {
  const updatedProductDetails = updatedProducts.map(p => {
    const prevProduct = prevProducts.find(prev => prev.id === p.id);
    const nameChanged = prevProduct && prevProduct.name !== p.name;
    const descChanged = prevProduct && prevProduct.product_desc !== p.product_desc;
    
    let updateMessage = `Updated product: "${p.name}"`;

    if (nameChanged) {
      updateMessage += ` (Name changed from "${prevProduct.name}" to "${p.name}")`;
    }

    if (descChanged) {
      updateMessage += ` (Description changed from "${prevProduct.product_desc}" to "${p.product_desc}")`;
    }

    return updateMessage;
  }).join(' | ');

  changeDetails.push(updatedProductDetails);
}
      // Client change
      const previousClientId = result?.[0]?.client;
      const previousClientName = result?.[0]?.customer_name || result?.[0]?.client_name || '';
      const newClientId = partnerData?.[0]?.id;
      const newClientName = partnerData?.[0]?.name || previousClientName;
      if (previousClientId !== newClientId && newClientId) {
        changeDetails.push(`Client changed to <strong>"${newClientName}"</strong>`);
      }
      // Project / community change
      const previousProjectName = result?.[0]?.community_name || '';
      const newProjectName = values?.community?.name || previousProjectName;
      const previousProjectId = result?.[0]?.community || result?.[0]?.project || null;
      const newProjectId = values?.community?.id || null;
      if (previousProjectId !== newProjectId && newProjectId) {
        changeDetails.push(`Project changed to <strong>"${newProjectName}"</strong>`);
      }
      // Name change
      if (result[0]['name'] != requestdata['name']) {
        changeDetails.push(`Design Order name changed to <strong>"${requestdata['name']}"</strong>`);
      }
      // Description change
      if (result[0]['description'] != requestdata['description']) {
        changeDetails.push(`Description updated to <strong>${requestdata['description']}</strong>`);
      }
      // Start date change
      if (result[0]['start_date'] != requestdata['start_date']) {
        changeDetails.push(`Start date updated to <strong>${moment(requestdata['start_date'] * 1000).format('YYYY-MM-DD')}</strong>`);
      }
      // End date change
      if (result[0]['due_date'] != requestdata['due_date']) {
        changeDetails.push(`End date updated to <strong>${moment(requestdata['due_date'] * 1000).format('YYYY-MM-DD')}</strong>`);
      }

      // Designers change (added/removed) into same message
      const addedDesignerNames = newlyAdded
        .map(id => {
          const d = designers.find(des => des.id === id);
          if (!d) return null;
          return d.name || (d.first_name ? `${d.first_name} ${d.last_name || ''}`.trim() : d.company_name) || null;
        })
        .filter(Boolean);

      const removedDesignerDetailsAll = removedDesignerIds.length > 0
        ? await Request.getUsersByIds(removedDesignerIds)
        : [];
      const removedNameMap = new Map();
      (removedDesignerDetailsAll || []).forEach(d => {
        const nm =
          d?.name ||
          (d?.first_name ? `${d.first_name} ${d.last_name || ''}`.trim() : d?.company_name) ||
          null;
        if (nm) removedNameMap.set(d.id, nm);
      });

      const removedDesignerNames = removedDesignerIds
        .map(id => {
          const resolved = removedNameMap.get(id);
          if (resolved) return resolved;
          const d = previousDesigners.find(prev => prev.id === id);
          if (!d) return null;
          const name =
            d.name ||
            (d.first_name ? `${d.first_name} ${d.last_name || ''}`.trim() : d.company_name);
          return name || null;
        })
        .filter(Boolean);

      if (addedDesignerNames.length > 0 || removedDesignerNames.length > 0) {
        const parts = [];
        if (addedDesignerNames.length > 0) parts.push(`added designers: "${addedDesignerNames.join('", "')}"`);
        if (removedDesignerNames.length > 0) parts.push(`removed designers: "${removedDesignerNames.join('", "')}"`);
        changeDetails.push(`Designers updated (${parts.join(' | ')})`);
      }

      // Designs (attachments) change
      const newDesignNames = (uploaded || []).map(row => row.url).filter(Boolean);
      const addedDesignFiles = newDesignNames.filter(n => !prevDesignNames.includes(n));
      const removedDesignFiles = prevDesignNames.filter(n => !newDesignNames.includes(n));
      if (addedDesignFiles.length > 0 || removedDesignFiles.length > 0) {
        const parts = [];
        if (addedDesignFiles.length > 0) parts.push(`${addedDesignFiles.length} design${addedDesignFiles.length > 1 ? 's' : ''} added`);
        if (removedDesignFiles.length > 0) parts.push(`${removedDesignFiles.length} design${removedDesignFiles.length > 1 ? 's' : ''} removed`);
        changeDetails.push(`Design files updated (${parts.join(' | ')})`);
      }

      // Reference files change
      const newReferenceNames = (uploadedReferenceFiles || []).map(row => row.url).filter(Boolean);
      const addedRefFiles = newReferenceNames.filter(n => !prevReferenceNames.includes(n));
      const removedRefFiles = prevReferenceNames.filter(n => !newReferenceNames.includes(n));
      if (addedRefFiles.length > 0 || removedRefFiles.length > 0) {
        const parts = [];
        if (addedRefFiles.length > 0) parts.push(`${addedRefFiles.length} reference${addedRefFiles.length > 1 ? 's' : ''} added`);
        if (removedRefFiles.length > 0) parts.push(`${removedRefFiles.length} reference${removedRefFiles.length > 1 ? 's' : ''} removed`);
        changeDetails.push(`Reference files updated (${parts.join(' | ')})`);
      }

      // Total cost change
      if (result[0]['total_cost'] != totalCost) {
        changeDetails.push(`Total cost updated to <strong>${totalCost}</strong>`);
      }
      if (result[0]['quantity'] != quantity) {
        changeDetails.push(`Hours updated to <strong>${quantity}</strong>`);
      }

      // Notes change
      if ((result[0]['notes'] || '') != (requestdata['notes'] || '')) {
        changeDetails.push(`Notes updated to <strong>${requestdata['notes'] || ''}</strong>`);
      }

      if (changeDetails.length > 0) {
        notification_msg += ` Changes: ` + changeDetails.join(' | ');
      }

      // ✅ OPTIMIZATION: Handle all notifications/emails/SMS/activity logs/tab counts async (non-blocking)
      const handleNotificationsAsync = async () => {
        try {
          // Partner invitation emails (if client changed)
          if (partnerInvitationEmailData) {
            emitProgress('sending_invitation_emails', `Sending invitation emails to clients...`, 82);
            const { partnerData, subject, email_content, BASE_URL, SEND_EMAIL, transporter } = partnerInvitationEmailData;
            const invitationEmailPromises = partnerData.map(async (eachPartner) => {
              let welcome = "Hello " + eachPartner['company_name'] + ","
              sgMail.setApiKey(SENDGRID_API_KEY)
              let message = rfqEmail()
              message = message.replace('[emailContent]', email_content)
              message = message.replace('[CONTENT]', welcome)
              message = message.replace('[LOGIN_URL]', `${BASE_URL}`)
              const mailOptions = {
                to: eachPartner.email, 
                from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
                subject: subject,
                text: subject,
                html: message,
              }
              try {
                if(SEND_EMAIL){
                  await transporter.sendMail(mailOptions);
                  console.log('Email sent');
                  emitProgress('email_sent', `Invitation email sent to ${eachPartner.email}`, 84);
                }
              } catch (error) {
                emitProgress('email_error', `Failed to send email to ${eachPartner.email}: ${error.message}`, 84);
                console.error('##########Err', error);
              }
            });
            await Promise.all(invitationEmailPromises);
          }

          // Designer notifications (emails and SMS)
          if (designersChanged && eligibleEmailDesigners.length > 0 && prevDesignerEmailSent === 0) {
            await sendDesignersUpdateEmails(
              newlyAdded,
              alreadyExisting,
              design_order_id,
              values,
              userId,
              eligibleEmailDesigners,
              designers,
            );
          }

          if (designersChanged && eligibleViewDesignEmailDesigners.length > 0 && prevDesignerEmailSent === 0) {
            await sendViewPermissionDesignersUpdateEmails(
              newlyAdded,
              alreadyExisting,
              design_order_id,
              values,
              userId,
              eligibleViewDesignEmailDesigners,
              designers,
            );
          }

          // Designer SMS notifications (parallelized)
          if (newlyAdded.length > 0 && prevDesignerSMSSent === 0) {
            const smsPromises = [];
            
            // SMS to newly added designers (view_design_permission)
            for (let id of newlyAdded) {
              const designer = designers.find(d => d.id === id);
              if (!designer || !designer.phone || designer.smsEnabled !== 1 || designer.view_design_permission !== 1 || designer.pcb_new_design_jobs_email === 1) continue;

              const smsContent = `🎨 Hello ${designer.name},\n\n✨ You've been assigned a new Design Job: ${values.name}.\n\n🔗 Login: ${BASE_URL}\n\n– Team SignWise`;
              smsPromises.push(
                sendMessageToPhoneNumber(designer.phone, smsContent, designer.phone_country_code)
                  .then(() => console.log(`✅ SMS sent (view_design_permission) to new designer: ${designer.phone}`))
                  .catch(err => console.error(`❌ SMS error (view_design_permission) for new designer ${designer.phone}:`, err.message))
              );
            }

            // SMS to newly added designers (pcb_new_design_jobs)
            for (let id of newlyAdded) {
              const designer = designers.find(d => d.id === id);
              if (!designer || !designer.phone || designer.smsEnabled !== 1 || designer.pcb_new_design_jobs !== 1 || designer.pcb_new_design_jobs_sms !== 1) continue;

              const smsContent = `🎨 Hello ${designer.name},\n\n✨ You've been assigned a new Design Job: ${values.name}.\n\n🔗 Login: ${BASE_URL}\n\n– Team SignWise`;
              smsPromises.push(
                sendMessageToPhoneNumber(designer.phone, smsContent, designer.phone_country_code)
                  .then(() => console.log(`✅ SMS sent to new designer: ${designer.phone}`))
                  .catch(err => console.error(`❌ Error sending SMS to new designer ${designer.phone}:`, err.message))
              );
            }

            // SMS to existing designers about additions (view_design_permission)
            for (let id of alreadyExisting) {
              const designer = designers.find(d => d.id === id);
              if (!designer || !designer.phone || designer.smsEnabled !== 1 || designer.view_design_permission !== 1 || designer.pcb_new_design_jobs_email === 1) continue;

              const newDesignerNames = newlyAdded.map(id => {
                const d = designers.find(d => d.id === id);
                return d ? d.name : null;
              }).filter(Boolean).join(', ');

              const smsContent = `🔔 Hello ${designer.name},\n\nNew designer(s): ${newDesignerNames} added to Design Job "${values.name}".\n\n📥 Check details: ${BASE_URL}`;
              smsPromises.push(
                sendMessageToPhoneNumber(designer.phone, smsContent, designer.phone_country_code)
                  .then(() => console.log(`✅ SMS sent (view_design_permission) to existing designer about additions: ${designer.phone}`))
                  .catch(err => console.error(`❌ SMS error (view_design_permission) for existing designer ${designer.phone}:`, err.message))
              );
            }

            // SMS to existing designers about additions (pcb_new_design_jobs)
            for (let id of alreadyExisting) {
              const designer = designers.find(d => d.id === id);
              if (!designer || !designer.phone || designer.smsEnabled !== 1 || designer.pcb_new_design_jobs !== 1 || designer.pcb_new_design_jobs_sms !== 1) continue;
               
              const newDesignerNames = newlyAdded.map(id => {
                const d = designers.find(d => d.id === id);
                return d ? d.name : null;
              }).filter(Boolean).join(', ');

              const smsContent = `🔔 Hello ${designer.name},\n\nNew designer(s): ${newDesignerNames} added to Design Job "${values.name}".\n\n📥 Check details: ${BASE_URL}`;
              smsPromises.push(
                sendMessageToPhoneNumber(designer.phone, smsContent, designer.phone_country_code)
                  .then(() => console.log(`✅ SMS sent to existing designer about new additions: ${designer.phone}`))
                  .catch(err => console.error(`❌ Error sending SMS to existing designer ${designer.phone}:`, err.message))
              );
            }

            await Promise.all(smsPromises);
          }

          // Removed designer emails
          if (prevDesignerEmailSent === 0 && removedDesignerIds.length > 0) {
            const allDesignerDetails = await Request.getUsersByIds([...removedDesignerIds, ...previousDesigners.map(d => d.id)]);

            const removedDesignerDetails = allDesignerDetails.filter(
              d => removedDesignerIds.includes(d.id) && d.emailEnabled === 1 && d.pcb_new_design_jobs === 1 && d.pcb_new_design_jobs_email === 1
            );

            const removedDesignerNameDetails = allDesignerDetails.filter(
              d => removedDesignerIds.includes(d.id)
            );

            const remainingDesignerDetails = allDesignerDetails.filter(
              d => !removedDesignerIds.includes(d.id) && d.emailEnabled === 1 && d.pcb_new_design_jobs === 1 && d.pcb_new_design_jobs_email === 1
            );

            if (removedDesignerNameDetails.length > 0) {
              await sendRemovedDesignersEmail(
                removedDesignerDetails,
                remainingDesignerDetails,
                design_order_id,
                values.name,
                BASE_URL,
                userId,
                removedDesignerNameDetails
              );
            }

            const removedDesignersV2 = oldDesignerIds.filter(id => !newDesignerIds.includes(id));
            if (removedDesignersV2.length > 0) {
              const allDesignerDetailsV2 = await Request.getUsersByIds([
                ...removedDesignersV2,
                ...previousDesigners.map(d => d.id)
              ]);

              const removedDesignerDetailsV2 = allDesignerDetailsV2.filter(
                d =>
                  removedDesignersV2.includes(d.id) &&
                  d.email &&
                  d.emailEnabled === 1 &&
                  d.view_design_permission === 1 &&
                  d.pcb_new_design_jobs_sms !== 1
              );

              const removedDesignerNameDetailsV2 = allDesignerDetailsV2.filter(
                d => removedDesignersV2.includes(d.id)
              );

              const remainingDesignerDetailsV2 = allDesignerDetailsV2.filter(
                d =>
                  !removedDesignersV2.includes(d.id) &&
                  d.email &&
                  d.emailEnabled === 1 &&
                  d.view_design_permission === 1 &&
                  d.pcb_new_design_jobs_sms !== 1
              );

              if (removedDesignerNameDetailsV2.length > 0) {
                await sendRemovedDesignersViewPermissionEmail(
                  removedDesignerDetailsV2,
                  remainingDesignerDetailsV2,
                  design_order_id,
                  values.name,
                  BASE_URL,
                  userId,
                  removedDesignerNameDetailsV2
                );
              }
            }
          }

          // Removed designer SMS notifications (parallelized)
          if (removedDesignerIds.length > 0 && prevDesignerSMSSent === 0) {
            const removedViewDesigners = await Request.getUsersByIds(removedDesignerIds);
            const remainingViewDesigners = await Request.getUsersByIds(
              previousDesigners.filter(d => !removedDesignerIds.includes(d.id)).map(d => d.id)
            );

            const removedSmsPromises = [];

            for (const designer of removedViewDesigners) {
              if (!designer || !designer.phone || designer.smsEnabled !== 1 || designer.view_design_permission !== 1 || designer.pcb_new_design_jobs_email === 1) continue;

              const smsContent = `⚠️ Notice: You have been removed from the Design Job "${values.name}".\n\n🔗 Login: ${BASE_URL}`;
              removedSmsPromises.push(
                sendMessageToPhoneNumber(designer.phone, smsContent, designer.phone_country_code)
                  .then(() => console.log(`✅ SMS sent (view_design_permission) to removed designer: ${designer.phone}`))
                  .catch(err => console.error(`❌ SMS error (view_design_permission) for removed designer ${designer.phone}:`, err.message))
              );
            }

            for (const designer of remainingViewDesigners) {
              if (!designer || !designer.phone || designer.smsEnabled !== 1 || designer.view_design_permission !== 1 || designer.pcb_new_design_jobs_email === 1) continue;

              const removedNames = removedViewDesigners.map(d => d.name).join(', ');
              const smsContent = `🔄 Update: ${removedNames} removed from Design Job "${values.name}".\n\n🔗 Login: ${BASE_URL}`;
              removedSmsPromises.push(
                sendMessageToPhoneNumber(designer.phone, smsContent, designer.phone_country_code)
                  .then(() => console.log(`✅ SMS sent (view_design_permission) to remaining designer: ${designer.phone}`))
                  .catch(err => console.error(`❌ SMS error (view_design_permission) for remaining designer ${designer.phone}:`, err.message))
              );
            }

            const removedDesignerDetails = await Request.getUsersByIds(removedDesignerIds);
            const remainingDesignerDetails = await Request.getUsersByIds(previousDesigners.filter(d => !removedDesignerIds.includes(d.id)).map(d => d.id));

            for (const designer of removedDesignerDetails) {
              if (!designer || !designer.phone || designer.smsEnabled !== 1 || designer.pcb_new_design_jobs !== 1 || designer.pcb_new_design_jobs_sms !== 1) continue;

              const smsContent = `⚠️ Notice: You have been removed from the Design Job "${values.name}".\n\n🔗 Login: ${BASE_URL}`;
              removedSmsPromises.push(
                sendMessageToPhoneNumber(designer.phone, smsContent, designer.phone_country_code)
                  .then(() => console.log(`✅ SMS sent to removed designer: ${designer.phone}`))
                  .catch(err => console.error(`❌ Error sending SMS to removed designer ${designer.phone}:`, err.message))
              );
            }

            for (const designer of remainingDesignerDetails) {
              if (!designer || !designer.phone || designer.smsEnabled !== 1 || designer.pcb_new_design_jobs !== 1 || designer.pcb_new_design_jobs_sms !== 1) continue;

              const removedNames = removedDesignerDetails.map(d => d.name).join(', ');
              const smsContent = `🔄 Update: ${removedNames} removed from Design Job "${values.name}".\n\n🔗 Login: ${BASE_URL}`;
              removedSmsPromises.push(
                sendMessageToPhoneNumber(designer.phone, smsContent, designer.phone_country_code)
                  .then(() => console.log(`✅ SMS sent to existing designer about removals: ${designer.phone}`))
                  .catch(err => console.error(`❌ Error sending SMS to existing designer ${designer.phone}:`, err.message))
              );
            }

            await Promise.all(removedSmsPromises);
          }

          // Activity log
      if (!empty(notification_msg)) {
        let requestActivity = {}
        requestActivity['design_order_id'] = design_order_id;
        requestActivity['activity'] = notification_msg;
            requestActivity['added_by'] = userId
        requestActivity['timestamp'] = currentTime
            await RequestActivity.addDesignOrderUpdate(requestActivity);
      }

          // Admin/Manager email and SMS notifications (parallelized)
      let adminMangerIds = await Admin.getAllAdminAndManagerUserIds();
        let allAdminToCheck = [...new Set([userId,...adminMangerIds].filter(Boolean))];
          console.log('alluserssssssssarray',allAdminToCheck)
        
          const adminNotificationPromises = allAdminToCheck.map(async (userIdToCheck) => {
            try {
            const creator = await Admin.getUserNotificationAndPermissionSettings(userIdToCheck);
            console.log('creatorrrrrrrrr',creator)
              if (!creator) return;
        
            const recipientPhone = creator?.phone || TWILIO_PHONE;
            const Phone_country_code = creator?.phone_country_code || '1';
            console.log('recipientPhoneeee',recipientPhone)
            console.log('Phone_country_codeeeee',Phone_country_code)

            if (creator.pcb_new_design_jobs === 1 || creator.view_design_permission === 1) {
              console.log("✅ Sales order SMSsssssss");
              // Email Notification
                const emailPromise = (async () => {
              if (
                creator.emailEnabled === 1 &&
                creator.email &&
                creator.pcb_new_design_jobs === 1 && 
                creator.pcb_new_design_jobs_email === 1
              ) {
                let welcome = "Hello " + creator.name + ",";
                let subject = `📦 New Design Order Updated: ${values.name}`;
                let email_content = " Design order has been updated. Please review the details in your software.";

                let message = designersEmail({
                designer:creator,
                designOrderName:result[0]?.name,
                email_content,
                welcome,
                baseUrl: BASE_URL,
                profileData: profileData[0],
              });
              console.log("✅ Design order SMSsssssss 2");
                message = message.replace('[emailContent]', email_content);
                message = message.replace('[CONTENT]', welcome);
                message = message.replace('[LOGIN_URL]', `${BASE_URL}`);

                const mailOptions = {
                  to: creator.email,
                  from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
                  subject: subject,
                  text: subject,
                  html: message,
                };

                try {
                  await transporter.sendMail(mailOptions);
                  console.log(`✅ design order email sent to creator5: ${creator.email}`);
                } catch (err) {
                  console.error(`❌ Error sending design order email to ${creator.email}:`, err);
                }
              }
                })();

              // SMS Notification
                const smsPromise = (async () => {
              if (
                creator?.smsEnabled === 1 &&
                creator?.phone &&
                creator.pcb_new_design_jobs === 1 && 
                creator.pcb_new_design_jobs_sms === 1
              ) {
              console.log("✅ design order SMSsssssss 3");
                const smsContent = `🎨 Hello ${creator?.name},\n\n📢   Design Order "${result[0]?.name}" has been updated.\n\n🔗 Login: ${BASE_URL}\n\n– Team SignWise`;

                try {
                  await sendMessageToPhoneNumber(recipientPhone, smsContent,Phone_country_code);
                  console.log("✅ Design order SMS sent to creator");
                } catch (err) {
                  console.error("❌ Error sending Design order SMS to creator:", err);
                }
              }
                })();

                await Promise.all([emailPromise, smsPromise]);
            }
            } catch (err) {
              console.error(`❌ Error sending notifications to user ${userIdToCheck}:`, err);
          }
          });
          await Promise.all(adminNotificationPromises);

          // Partner and Admin notifications
    // let requestDetails = await Request.getAllDesignOrders(design_order_id, userId, userType,isClient);
    // requestDetails = requestDetails?.data
    let requestDetails = result?.data;
    console.log('requestDetails-requestDetails==',requestDetails)
    if (!empty(requestDetails[0].status)) {
            let notification_msg_partner = `The Design Order <strong>"${requestDetails[0]['name']}".</strong> has been updated`
            let notification_url_partner = `design-order/view/${design_order_id}`

      let requestPartners = await Request.getDesignOrderPartnersByIds([], design_order_id)

            // Partner notifications (parallelized)
            const partnerNotificationPromises = requestPartners.map(async (eachpartner) => {
              try {
                await Admin.incrementField(eachpartner.id, "noti_count", 1);
        let notificationUser = {
          user_id: eachpartner.id,
                  url: notification_url_partner,
          timestamp: currentTime,
                  message: notification_msg_partner,
          status: 1,
          from_id: userId,
        }
                await Admin.addNotification(notificationUser);
              } catch (err) {
                console.error(`❌ Error sending notification to partner ${eachpartner.id}:`, err);
      }
            });
            await Promise.all(partnerNotificationPromises);

            // Admin notifications (parallelized)
      const adminIds = await Admin.getAllAdminIds()
            const adminNotificationPromises2 = adminIds.map(async (eachadminIds) => {
              try {
                await Admin.incrementField(eachadminIds, 'noti_count', 1)
        let addNotification = {
          user_id: eachadminIds,
                  url: notification_url_partner,
          timestamp: currentTime,
                  message: notification_msg_partner,
          status: 1,
          from_id: userId,
        }
                await Admin.addNotification(addNotification);
              } catch (err) {
                console.error(`❌ Error sending notification to admin ${eachadminIds}:`, err);
      }
            });
            await Promise.all(adminNotificationPromises2);

            // Tab count updates (parallelized)
    //   if (!previousServiceData || previousServiceData.length === 0 || updateReqID) {
    //     emitProgress('updating_tab_counts', `Updating user tab counts...`, 85);
    //     const allUserIds = await Admin.getAllUserIds();
    //   const selectedClientId = result[0]?.client|| null;
    //   const clientUserId = await Admin.getPartnersUserIdById(selectedClientId)
    //   console.log("clientUserId",clientUserId)
    //   let allUsersToUpdate = [...new Set([userId, clientUserId, ...allUserIds].filter(Boolean))];

    //           const updateOpenCount = async (userIdToUpdate) => {
    //             try {
    //               await Request.designCountIncrementField(userIdToUpdate, 'open_count', 1, 'open_count_ids', design_order_id, true);
    //         if(updateReqID){
    //                 await Request.designCountIncrementField(userIdToUpdate, 'new_count', 1, 'new_count_ids', design_order_id, true);
    //                 console.log("design_order open updated inprogress count" , userIdToUpdate , design_order_id)
    //         }
    //               console.log("design_order_job_status new updated inprogress count" , userIdToUpdate , design_order_id)
    //             } catch (err) {
    //               console.error(`❌ Error updating tab count for user ${userIdToUpdate}:`, err);
    //             }
    // };
    // await Promise.all(allUsersToUpdate.map(user => updateOpenCount(user)));
    // console.log("count updated successfully")
    //   }
          }
        } catch (error) {
          console.error('Error in async notifications handler:', error);
        }
      };

      // ✅ OPTIMIZATION: Run non-critical operations asynchronously (fire and forget)
      handleNotificationsAsync().catch(error => {
        console.error('Error in async operations:', error);
      });
    } else {
      emitProgress('error', 'Failed to update design order', 0);
      res.send({ message: 'failed to edit design order' })
    }
  } catch (error) {
    console.log(error, 'error in get Design Order by id')
    emitProgress('error', `Error updating design order: ${error.message}`, 0);
    res.send({ status: 0, message: 'failed to get Design Order by id'+error })
  }
}
// const updateDesignOrder = async (req, res) => {
//   try {
//     UniqueEstimateId =null
//     doc_number = null //attach
//     const { values, userId, userType,isClient, design_order_id ,selectedServices,totalCost,designers, isClientEdited, socketId } = req.body;
    
//     // 🚀 PROGRESS TRACKING: Socket progress emission function
//     const emitProgress = (step, message, progress) => {
//       const progressData = {
//         step,
//         message,
//         progress,
//         userId,
//         domain: DOMAIN,
//         timestamp: moment().unix(),
//         type: 'update_design_order_progress'
//       };
      
//       // Emit to working socket server (same as chat)
//       console.log(`[UPDATE-DESIGN-PROGRESS] 📡 Emitting progress to working socket server...`);
//       try {
//         const workingSocket = ioClient(REST_API_IO, {
//           path: '/socket.io',
//           transports: ['websocket', 'polling'],
//           secure: true,
//           timeout: 5000
//         });
        
//         workingSocket.on('connect', () => {
//           console.log(`[UPDATE-DESIGN-PROGRESS] 🔌 Connected to working socket server`);
//           console.log(`[UPDATE-DESIGN-PROGRESS] 📊 Emitting progress data:`, progressData);
          
//           // Try emitting as 'message' event (same as chat) to test if socket server handles it
//           workingSocket.emit('message', {
//             ...progressData,
//             type: 'progress',
//             conversationId: `update_design_progress_${userId}`,
//             senderId: 'system'
//           });
          
//           // Also try the original request_progress event
//           workingSocket.emit('request_progress', progressData);
          
//           console.log(`[UPDATE-DESIGN-PROGRESS] ✅ Successfully emitted progress to working socket server`);
          
//           // Disconnect after emitting
//           setTimeout(() => {
//             workingSocket.disconnect();
//           }, 1000);
//         });
        
//         workingSocket.on('connect_error', (error) => {
//           console.error(`[UPDATE-DESIGN-PROGRESS] ❌ Working socket connection error:`, error);
//         });
        
//       } catch (error) {
//         console.error(`[UPDATE-DESIGN-PROGRESS] ❌ Error creating working socket connection:`, error);
//       }
//     };

//     // Initial progress
//     console.log(`[UPDATE-DESIGN-PROGRESS] 🚀 Starting design order update for user:`, userId);
//     emitProgress('start', `Initializing design order update...`, 5);
    
//     let currentTime = moment().unix()
//     const partnerData = values.clientsData
//     let submission_deadline = moment(values.start).unix()
//     let participation_ends = moment(values.end).unix()
//     let requestdata = {}
//     emitProgress('fetching_design_order', `Fetching design order data...`, 10);
//     let result = await Request.getAllDesignOrders(design_order_id, userId, userType,isClient)
//     let previousServiceData = await Request.getDesignOrderById(design_order_id)
//     const prevDesignerEmailSent = previousServiceData?.designer_email_sent || 0;
//     const prevDesignerSMSSent = previousServiceData?.designer_sms_sent || 0;
//     const profileData = await Settings.getCompanyProfileDetails();

//     const previousDesigners = await Request.getDesignOrderDesigners(design_order_id);
//     console.log('previousDesigners', previousDesigners)
//     const newDesignerIds = designers.map(d => d.id);
//     console.log('newDesignerIds', newDesignerIds)
//     const oldDesignerIds = previousDesigners.map(d => d.id);
//     console.log('oldDesignerIds', oldDesignerIds)

//     const newlyAdded = newDesignerIds.filter(id => !oldDesignerIds.includes(id));
//     console.log('newlyAdded', newlyAdded)
    
//     const alreadyExisting = oldDesignerIds.filter(id => newDesignerIds.includes(id));
//     console.log('alreadyExisting', alreadyExisting)

//     const designersChanged =
//       newlyAdded.length > 0 ||
//       oldDesignerIds.length !== newDesignerIds.length;

//       emitProgress('checking_designer_changes', `Checking designer changes...`, 20);
//       const eligibleEmailDesigners = designers.filter(
//         (d) => d.emailEnabled === 1 && d.pcb_new_design_jobs === 1 && d.pcb_new_design_jobs_email === 1
//       );
//       console.log('eligibleEmailDesigners', eligibleEmailDesigners)

//       if (designersChanged && eligibleEmailDesigners.length > 0 && prevDesignerEmailSent === 0) {
//         await sendDesignersUpdateEmails(
//           newlyAdded,
//           alreadyExisting,
//           design_order_id,
//           values,
//           userId,
//           eligibleEmailDesigners,
//           designers,
//         );
//       }

//       const eligibleViewDesignEmailDesigners = designers.filter(
//         d => d.emailEnabled === 1 && d.view_design_permission === 1 && d.pcb_new_design_jobs_sms !== 1
//       );

//       if (designersChanged && eligibleViewDesignEmailDesigners.length > 0 && prevDesignerEmailSent === 0) {
//         await sendViewPermissionDesignersUpdateEmails(
//           newlyAdded,
//           alreadyExisting,
//           design_order_id,
//           values,
//           userId,
//           eligibleViewDesignEmailDesigners,
//           designers,
//         );
//       }

//       if (newlyAdded.length > 0 && prevDesignerSMSSent === 0) {
//         for (let id of newlyAdded) {
//           const designer = designers.find(d => d.id === id);
//           if (!designer || !designer.phone || designer.smsEnabled !== 1 || designer.view_design_permission !== 1 || designer.pcb_new_design_jobs_email === 1) continue;

//           const smsContent = `🎨 Hello ${designer.name},\n\n✨ You’ve been assigned a new Design Job: ${values.name}.\n\n🔗 Login: ${BASE_URL}\n\n– Team SignWise`;

//           try {
//             // await sendMessageToPhoneNumber('9074906155', smsContent);
//             await sendMessageToPhoneNumber(designer.phone, smsContent,designer.phone_country_code);
//             console.log(`✅ SMS sent (view_design_permission) to new designer: ${designer.phone}`);
//           } catch (err) {
//             console.error(`❌ SMS error (view_design_permission) for new designer ${designer.phone}:`, err.message);
//           }
//         }
//       }

//     if (newlyAdded.length > 0 && prevDesignerSMSSent === 0) {
//       for (let id of newlyAdded) { // 🔄 Use `newlyAdded` instead of `newDesignerIds`
//         const designer = designers.find(d => d.id === id);
//         if (!designer || !designer.phone || designer.smsEnabled !== 1 || designer.pcb_new_design_jobs !== 1 || designer.pcb_new_design_jobs_sms !== 1) continue;

//         const smsContent = `🎨 Hello ${designer.name},\n\n✨ You’ve been assigned a new Design Job: ${values.name}.\n\n🔗 Login: ${BASE_URL}\n\n– Team SignWise`;

//         try {
//           // await sendMessageToPhoneNumber('9074906155', smsContent);
//           await sendMessageToPhoneNumber(designer.phone, smsContent,designer.phone_country_code);
//           // await sendMessageToPhoneNumber('9074906155', smsContent,'91');
//           console.log(`✅ SMS sent to new designer: ${designer.phone}`);
//         } catch (err) {
//           console.error(`❌ Error sending SMS to new designer ${designer.phone}:`, err.message);
//         }
//       }
//     }

//     if (newlyAdded.length > 0 && prevDesignerSMSSent === 0) {
//       for (let id of alreadyExisting) {
//         const designer = designers.find(d => d.id === id);
//         if (!designer || !designer.phone || designer.smsEnabled !== 1 || designer.view_design_permission !== 1 || designer.pcb_new_design_jobs_email === 1) continue;

//         const newDesignerNames = newlyAdded.map(id => {
//           const d = designers.find(d => d.id === id);
//           return d ? d.name : null;
//         }).filter(Boolean).join(', ');

//         const smsContent = `🔔 Hello ${designer.name},\n\nNew designer(s): ${newDesignerNames} added to Design Job "${values.name}".\n\n📥 Check details: ${BASE_URL}`;

//         try {
//           // await sendMessageToPhoneNumber('9074906155', smsContent);
//           await sendMessageToPhoneNumber(designer.phone, smsContent,designer.phone_country_code);
//           console.log(`✅ SMS sent (view_design_permission) to existing designer about additions: ${designer.phone}`);
//         } catch (err) {
//           console.error(`❌ SMS error (view_design_permission) for existing designer ${designer.phone}:`, err.message);
//         }
//       }
//     }

//     if (newlyAdded.length > 0 && prevDesignerSMSSent === 0) {
//       for (let id of alreadyExisting) {
//         const designer = designers.find(d => d.id === id);
//         if (!designer || !designer.phone || designer.smsEnabled !== 1 || designer.pcb_new_design_jobs !== 1 || designer.pcb_new_design_jobs_sms !== 1) continue;
         
//         const newDesignerNames = newlyAdded.map(id => {
//           const d = designers.find(d => d.id === id);
//           return d ? d.name : null;
//         }).filter(Boolean).join(', ');

//         const smsContent = `🔔 Hello ${designer.name},\n\nNew designer(s): ${newDesignerNames} added to Design Job "${values.name}".\n\n📥 Check details: ${BASE_URL}`;

//         try {
//           // await sendMessageToPhoneNumber('9074906155', smsContent);
//           await sendMessageToPhoneNumber(designer.phone, smsContent,designer.phone_country_code);
//           // await sendMessageToPhoneNumber('9074906155', smsContent,'91');
//           console.log(`✅ SMS sent to existing designer about new additions: ${designer.phone}`);
//         } catch (err) {
//           console.error(`❌ Error sending SMS to existing designer ${designer.phone}:`, err.message);
//         }
//       }
//     }

//     const removedDesignerIds = oldDesignerIds.filter(id => !newDesignerIds.includes(id));
//       console.log('removedDesignerIds', removedDesignerIds);

//       if (prevDesignerEmailSent === 0 && removedDesignerIds.length > 0) {
//         const allDesignerDetails = await Request.getUsersByIds([...removedDesignerIds, ...previousDesigners.map(d => d.id)]);

//         const removedDesignerDetails = allDesignerDetails.filter(
//           d => removedDesignerIds.includes(d.id) && d.emailEnabled === 1 && d.pcb_new_design_jobs === 1 && d.pcb_new_design_jobs_email === 1
//         );

//         const removedDesignerNameDetails = allDesignerDetails.filter(
//           d => removedDesignerIds.includes(d.id)
//         );

//         console.log('filtered removedDesignerDetails', removedDesignerDetails);

//         const remainingDesignerDetails = allDesignerDetails.filter(
//           d => !removedDesignerIds.includes(d.id) && d.emailEnabled === 1 && d.pcb_new_design_jobs === 1 && d.pcb_new_design_jobs_email === 1
//         );
//         console.log('filtered remainingDesignerDetails', remainingDesignerDetails);

//         if (removedDesignerNameDetails.length > 0) {
//           await sendRemovedDesignersEmail(
//             removedDesignerDetails,
//             remainingDesignerDetails,
//             design_order_id,
//             values.name,
//             BASE_URL,
//             userId,
//             removedDesignerNameDetails
//           );
//         }
//       }

//       const removedDesignersV2 = oldDesignerIds.filter(id => !newDesignerIds.includes(id));
//       console.log('removedDesignersV2', removedDesignersV2);

//       if (prevDesignerEmailSent === 0 && removedDesignersV2.length > 0) {
//         const allDesignerDetailsV2 = await Request.getUsersByIds([
//           ...removedDesignersV2,
//           ...previousDesigners.map(d => d.id)
//         ]);

//         const removedDesignerDetailsV2 = allDesignerDetailsV2.filter(
//           d =>
//             removedDesignersV2.includes(d.id) &&
//             d.email &&
//             d.emailEnabled === 1 &&
//             d.view_design_permission === 1 &&
//             d.pcb_new_design_jobs_sms !== 1
//         );

//         const removedDesignerNameDetailsV2 = allDesignerDetailsV2.filter(
//           d => removedDesignersV2.includes(d.id)
//         );

//         console.log('filtered removedDesignerDetails V2', removedDesignerDetailsV2);

//         const remainingDesignerDetailsV2 = allDesignerDetailsV2.filter(
//           d =>
//             !removedDesignersV2.includes(d.id) &&
//             d.email &&
//             d.emailEnabled === 1 &&
//             d.view_design_permission === 1 &&
//             d.pcb_new_design_jobs_sms !== 1
//         );

//         console.log('filtered remainingDesignerDetails V2', remainingDesignerDetailsV2);

//         if (removedDesignerNameDetailsV2.length > 0) {
//           await sendRemovedDesignersViewPermissionEmail(
//             removedDesignerDetailsV2,
//             remainingDesignerDetailsV2,
//             design_order_id,
//             values.name,
//             BASE_URL,
//             userId,
//             removedDesignerNameDetailsV2
//           );
//         }
//       }

//       if (removedDesignerIds.length > 0 && prevDesignerSMSSent === 0) {
//         const removedViewDesigners = await Request.getUsersByIds(removedDesignerIds);
//         const remainingViewDesigners = await Request.getUsersByIds(
//           previousDesigners.filter(d => !removedDesignerIds.includes(d.id)).map(d => d.id)
//         );

//         for (const designer of removedViewDesigners) {
//           if (!designer || !designer.phone || designer.smsEnabled !== 1 || designer.view_design_permission !== 1 || designer.pcb_new_design_jobs_email === 1) continue;

//           const smsContent = `⚠️ Notice: You have been removed from the Design Job "${values.name}".\n\n🔗 Login: ${BASE_URL}`;

//           try {
//             // await sendMessageToPhoneNumber('9074906155', smsContent);
//             await sendMessageToPhoneNumber(designer.phone, smsContent,designer.phone_country_code);
//             console.log(`✅ SMS sent (view_design_permission) to removed designer: ${designer.phone}`);
//           } catch (err) {
//             console.error(`❌ SMS error (view_design_permission) for removed designer ${designer.phone}:`, err.message);
//           }
//         }

//         for (const designer of remainingViewDesigners) {
//           if (!designer || !designer.phone || designer.smsEnabled !== 1 || designer.view_design_permission !== 1 || designer.pcb_new_design_jobs_email === 1) continue;

//           const removedNames = removedViewDesigners.map(d => d.name).join(', ');
//           const smsContent = `🔄 Update: ${removedNames} removed from Design Job "${values.name}".\n\n🔗 Login: ${BASE_URL}`;

//           try {
//             // await sendMessageToPhoneNumber('9074906155', smsContent);
//             await sendMessageToPhoneNumber(designer.phone, smsContent,designer.phone_country_code);
//             console.log(`✅ SMS sent (view_design_permission) to remaining designer: ${designer.phone}`);
//           } catch (err) {
//             console.error(`❌ SMS error (view_design_permission) for remaining designer ${designer.phone}:`, err.message);
//           }
//         }
//       }

//     if (removedDesignerIds.length > 0 && prevDesignerSMSSent === 0) {

//       const removedDesignerDetails = await Request.getUsersByIds(removedDesignerIds);
//       console.log('removedDesignerDetails', removedDesignerDetails)

//       const remainingDesignerDetails = await Request.getUsersByIds(previousDesigners.filter(d => !removedDesignerIds.includes(d.id)).map(d => d.id));
//       console.log('remainingDesignerDetails', remainingDesignerDetails)

//       for (const designer of removedDesignerDetails) {
//         if (!designer || !designer.phone || designer.smsEnabled !== 1 || designer.pcb_new_design_jobs !== 1 || designer.pcb_new_design_jobs_sms !== 1) continue;

//         const smsContent = `⚠️ Notice: You have been removed from the Design Job "${values.name}".\n\n🔗 Login: ${BASE_URL}`;

//         try {
//           // await sendMessageToPhoneNumber('9074906155', smsContent);
//           await sendMessageToPhoneNumber(designer.phone, smsContent,designer.phone_country_code);
//           // await sendMessageToPhoneNumber('9074906155', smsContent,'91');
//           console.log(`✅ SMS sent to removed designer: ${designer.phone}`);
//         } catch (err) {
//           console.error(`❌ Error sending SMS to removed designer ${designer.phone}:`, err.message);
//         }
//       }

//       for (const designer of remainingDesignerDetails) {
//         if (!designer || !designer.phone || designer.smsEnabled !== 1 || designer.pcb_new_design_jobs !== 1 || designer.pcb_new_design_jobs_sms !== 1) continue;

//         const removedNames = removedDesignerDetails.map(d => d.name).join(', ');
//         const smsContent = `🔄 Update: ${removedNames} removed from Design Job "${values.name}".\n\n🔗 Login: ${BASE_URL}`;

//         try {
//           // await sendMessageToPhoneNumber('9074906155', smsContent);
//           await sendMessageToPhoneNumber(designer.phone, smsContent,designer.phone_country_code);
//           // await sendMessageToPhoneNumber('9074906155', smsContent,'91');
//           console.log(`✅ SMS sent to existing designer about removals: ${designer.phone}`);
//         } catch (err) {
//           console.error(`❌ Error sending SMS to existing designer ${designer.phone}:`, err.message);
//         }
//       }
//     }

//     // --- Log designers update in design_order_updates ---
//     // const addedDesignerNames = newlyAdded
//     //   .map(id => {
//     //     const d = designers.find(des => des.id === id);
//     //     if (!d) return null;
//     //     return d.name || (d.first_name ? `${d.first_name} ${d.last_name || ''}`.trim() : d.company_name) || d.id?.toString() || null;
//     //   })
//     //   .filter(Boolean);

//     // // Try to resolve removed designer names from user records for reliable display
//     // const removedDesignerDetailsAll = removedDesignerIds.length > 0
//     //   ? await Request.getUsersByIds(removedDesignerIds)
//     //   : [];
//     // const removedNameMap = new Map();
//     // (removedDesignerDetailsAll || []).forEach(d => {
//     //   const nm =
//     //     d?.name ||
//     //     (d?.first_name ? `${d.first_name} ${d.last_name || ''}`.trim() : d?.company_name) ||
//     //     null;
//     //   if (nm) removedNameMap.set(d.id, nm);
//     // });

//     // const removedDesignerNames = removedDesignerIds
//     //   .map(id => {
//     //     const resolved = removedNameMap.get(id);
//     //     if (resolved) return resolved;
//     //     const d = previousDesigners.find(prev => prev.id === id);
//     //     if (!d) return null;
//     //     const name =
//     //       d.name ||
//     //       (d.first_name ? `${d.first_name} ${d.last_name || ''}`.trim() : d.company_name);
//     //     return name || null;
//     //   })
//     //   .filter(Boolean);

//     // if (addedDesignerNames.length > 0 || removedDesignerNames.length > 0) {
//     //   const userData = await Admin.getUserById(userId);
//     //   const userName = userData && userData[0]
//     //     ? (userData[0].first_name ? `${userData[0].first_name} ${userData[0].last_name || ''}`.trim() : userData[0].company_name || 'Unknown User')
//     //     : 'Unknown User';

//     //   const changeParts = [];
//     //   if (addedDesignerNames.length > 0) changeParts.push(`added: "${addedDesignerNames.join('", "')}"`);
//     //   if (removedDesignerNames.length > 0) changeParts.push(`removed: "${removedDesignerNames.join('", "')}"`);

//     //   const designerActivity = `👥 Designers updated (${changeParts.join(' | ')}) by "${userName}"`;

//     //   const designerUpdateData = {
//     //     design_order_id: design_order_id,
//     //     activity: designerActivity,
//     //     added_by: userId,
//     //     timestamp: moment().unix(),
//     //     status: 1
//     //   };

//     //   await RequestActivity.addDesignOrderUpdate(designerUpdateData);
//     // }

//     console.log("previousServiceData", previousServiceData)
//     result = result.data
//     requestdata['name'] = values.name,
//     requestdata['client'] = values?.clients?.id,
//     requestdata['total_cost'] = totalCost;
//     requestdata['community'] = values.community.id||null;
//     requestdata['designer_email_sent'] = values.designer_email_sent;
//     requestdata['designer_sms_sent'] = values.designer_sms_sent;
//     requestdata['description'] = values.description,
//     requestdata['start_date'] = values.start
//     requestdata['notes']=values.notes||""
//     // requestdata['send_client_app_req']= isClientEdited ? 0 : result[0]?.send_client_app_req
//     requestdata['send_client_app_req']= 0
//     let uploaded = values.avatarUrl;
//     let uploadedReferenceFiles = values.referenceFiles;

//     // Capture previous attachments for change tracking (designs and references)
//     const previousDesignAttachments = await Request.fetchAllDesignOrderAttachments(design_order_id) || [];
//     const previousReferenceAttachments = await Request.fetchAllDesignOrderReferenceAttachments(design_order_id) || [];
//     const prevDesignNames = previousDesignAttachments.map(att => att.name).filter(Boolean);
//     const prevReferenceNames = previousReferenceAttachments.map(att => att.name).filter(Boolean);

//     // console.log('uploadeduploaded',uploaded)
//     // return
//     if (values.end !== (undefined || null)) {
//       requestdata['due_date'] = values.end
//     } else {
//       requestdata['due_date'] = 0
//     }
//     if (values.welcome_message == true) {
//       requestdata['welcome_message'] = values.welcome_message
//       if (values.subject !== (undefined || null)) {
//         requestdata['subject'] = values.subject
//       } else {
//         requestdata['subject'] = null
//       }
//       if (values.email_content !== (undefined || null)) {
//         requestdata['email_content'] = values.email_content
//       } else {
//         requestdata['email_content'] = null
//       }
//     } else {
//       requestdata['welcome_message'] = false
//       requestdata['subject'] = null
//       requestdata['email_content'] = null
//     }
//     if (values.draft == true) {
//       requestdata['status'] = 4
//     } else if (result[0]?.status == 6) {
//       requestdata['status'] = 6
//     } else {
//       // requestdata['status'] = values?.client_approve == false ? 2 :1
//       requestdata['status'] = 1
//       // requestdata['design_order_job_status'] = 1
//       requestdata['client_approve'] = values?.client_approve == false ? 0 : 1
//     }
//     // requestdata['added_by'] = userId,
//     requestdata['edited_by'] = userId,  
//     requestdata['timestamp'] = currentTime
//     // requestdata['is_from_client'] = (userType == 3 || isClient) ? 1 : 0
//     requestdata['is_in_queue'] = (userType == 3 || isClient) ? 0 : 1
//     requestdata['reason'] = null
//     requestdata['reject_file'] = null
//     requestdata['reject_file_type'] = null
//     let updateReqID = await Request.updateDesignOrders(design_order_id, requestdata);
    
//     // Get userType from users table to validate
//     const userData = await Admin.getUserById(userId);
//     const actualUserType = userData && userData[0] ? userData[0].user_type : null;
    
//     // Update design_order_job_status based on userType
//     if(design_order_id){
//       const fundDesignOrderById = await Request.getDesignOrderByIdNew(design_order_id)
//       const designOrder = fundDesignOrderById[0];
//       let currentJobStatus = designOrder?.dataValues?.design_order_job_status;

//       if(actualUserType === 11){
//         // Designer userType 11: Set job status to 2 and save working_designer_id from status 1, 3, or 4
//         if(currentJobStatus === 1 || currentJobStatus === 3){
//           const updateData = {
//             design_order_job_status: 2,
//             working_designer_id: userId
//           }
//           await Request.updateDesignOrders(design_order_id, updateData);
//           console.log(`Updated design_order_job_status from ${currentJobStatus} to 2 for userType 11 in updateDesignOrder`);
//         } else if(currentJobStatus === 4){
//           // If rejected (status 4), clear working_designer_id
//           const updateData = {
//             working_designer_id: null
//           }
//           await Request.updateDesignOrders(design_order_id, updateData);
//           console.log('Cleared working_designer_id for rejected design order (status 4) in updateDesignOrder');
//         }
//       } else {
//         // Other users: Set job status to 1 and clear working_designer_id
//         const updateData = {
//           // design_order_job_status: 1,
//           working_designer_id: null
//         }
//         await Request.updateDesignOrders(design_order_id, updateData);
//         console.log('Updated design_order_job_status to 1 for non-designer user in updateDesignOrder');
//       }
//     }
    
//     if (result[0]?.client != partnerData[0]?.id) {
//       let subject = 'A new design order awaiting your acceptance';
//       let email_content = `You are invited to join a new design order by ` + values?.userName;
//       if (req.body.values.welcome_message) {
//         subject = req.body.values.subject
//         email_content = req.body.values.email_content
//       }
//       if (partnerData && partnerData.length > 0) {
//         let client = {}
//         let insertedstatus = null
//         for (let eachPartner of partnerData) {
//           let welcome = "Hello " + eachPartner['company_name'] + ","
//           sgMail.setApiKey(SENDGRID_API_KEY)
//           let message = rfqEmail()
//           message = message.replace('[emailContent]', email_content)
//           message = message.replace('[CONTENT]', welcome)
//           message = message.replace('[LOGIN_URL]', `${BASE_URL}`)
//           const mailOptions = {
//             to:  eachPartner.email, 
//             from: '"SignWise Solutions" <noreply@signwisesolutions.com>', // Change to your verified sender
//             subject: subject,
//             text: subject,
//             html: message,
//           }
//           try {
//             if(SEND_EMAIL){
//             await transporter.sendMail(mailOptions);
//             console.log('Email sent');
//             }
//           } catch (error) {
//             console.error('##########Err', error);
//           }
//         }
//       }
//     }

//     if (updateReqID) {
//       emitProgress('updating_design_order', `Updating design order...`, 30);
//       let allSubtasks = await Request.getDesignOrdersSubTaskByDesignOrder(design_order_id);
//       let deleteDesignOrderServices = await Request.deleteDesignOrderServices(design_order_id)
//       let deleteDesignOrderSubTask = await Request.deleteDesignOrdersSubTask(design_order_id)
//       let deleteDesignOrderMembers = await Request.deleteDesignOrdersProjectMember(design_order_id);
//       for(const subtask of allSubtasks){
//         let sub_Task_mem = await Request.deleteDesignOrdersProjectSubtaskMember(subtask.id);
//       }

//       if(selectedServices?.length > 0){
//         emitProgress('updating_services', `Updating design services...`, 50);
//         for (const product of selectedServices) {
//           const serviceData={}
//           serviceData.labour_cost_service_id=product.id,
//           serviceData.quantity=product?.quantity || 1,
//           serviceData.unit_cost=product.total_cost,
//           serviceData.total=(parseFloat(product?.quantity) || 1) * (parseFloat(product.total_cost) || 0),
//           serviceData.design_order_id=design_order_id

//           let serviceDataResult = await Request.addDesignOrderServices(serviceData,design_order_id)

//           if(product?.workflow){
//             let skillSet = []
//             let workflowData =await Product.getWorkflowById(product?.workflow)
//             skillSet.push(workflowData)
//             const allSkillIds = skillSet?.flatMap(workflow => workflow?.map(stage => stage?.skill_ids?.split(',')))?.flat()
//             let uniqueSkillIds = [...new Set(allSkillIds)];
//             uniqueSkillIds = uniqueSkillIds?.filter(skillId => skillId !== "" && skillId !== undefined);
//             let uniqueSkillIdsString = uniqueSkillIds?.join(',');
//             uniqueSkillIdsString=uniqueSkillIdsString?.replace(/,$/, '');
//             let projectManagers=[]
//             if(!empty(uniqueSkillIdsString)){
//               projectManagers = await Project.getTeamMembersBasedOnSkills(uniqueSkillIdsString, 14)
//             }
//             let pm = {}
//             // for (const projectManager of projectManagers) {
//               for (const designer of designers) {
//               pm = {
//                 design_order_id: design_order_id,
//                 // staff_id: !empty(projectManager["id"]) ? projectManager["id"] : NULL,
//                 staff_id: !empty(designer["id"]) ? designer["id"] : NULL,
//                 partner_id: null,
//                 contact_id: null,
//                 lead_id: null,
//               };
//               let alreadyExisting = await Request.projectMemberExist(pm);
//               if (!alreadyExisting) {
//                 let insertId = await Request.addDesignOrdersProjectMember(pm);
//               }
//               // let insertId = await Request.addDesignOrdersProjectMember(pm);
//             }
//             for (const workflow of workflowData) {
//               console.log(workflow, '--------workflow')
//               const subTask = {
//                 name: workflow.name,
//                 design_order_id : design_order_id,
//                 task_id: serviceDataResult,
//                 skill_ids: workflow?.skill_ids,
//                 timestamp: moment().unix(),
//                 userid: userId,
//                 start_date: values.start || null,
//                 due_date: values.end || null,
//                 task_uid: 1,
//                 complete:  0,
//                 priority: 1,
//                 status:  1,
//                 reporter_id:userId,
//                 description: workflow?.message,
//               };
//               console.log('subTask===',subTask)
//               const projectSubTaskId = await Request.addDesignOrdersSubTask(subTask);
//               console.log('projectSubTaskId===',projectSubTaskId)
//               // for (const projectManager of projectManagers) {
//                 for (const designer of designers) {
//                 let pm = {
//                   subtask_id: projectSubTaskId,
//                   // staff_id: !empty(projectManager["id"]) ? projectManager["id"] : NULL,
//                   staff_id: !empty(designer["id"]) ? designer["id"] : NULL,
//                   partner_id: null,
//                   contact_id: null,
//                   lead_id: null,
//                 };
//                 let alreadyExisting = await Request.projectSubtaskMemberExist(pm, projectSubTaskId);

//                 if(!alreadyExisting){
//                   let sub_Task_mem = await Request.addDesignOrdersProjectSubtaskMember(pm);
//                 }
//               }
//             }
//           }
//         }
//       }

//       let deleteDesignOrder = await Request.deleteDesignOrderAttachments(design_order_id);
//       if (!empty(uploadedReferenceFiles)) {
//         emitProgress('updating_reference_attachments', `Updating reference attachments...`, 60);
//         for (const row of uploadedReferenceFiles) {
//           let approvedImage = {}
//           approvedImage.name = row.url;
//           approvedImage.file_type = row.file_type;
//           approvedImage.extension = row.extension;
//           approvedImage.file_size = row.size;
//           approvedImage.design_order_id = design_order_id;
//           // approvedImage.added_by = userId
//           approvedImage.added_by =row?.added_by || userId
//           approvedImage.approved=row.approved || 0,
//           approvedImage.approved_by=row.approved_by_id || null,
//           approvedImage.status = row?.status || 1,
//           approvedImage.isChat =row.isChat || 0,
//           approvedImage.isDesign =row.isDesign,
//           approvedImage.isNew =row.isNew || 1,
//           approvedImage.reason =row.reason || null,
//           approvedImage.timestamp = moment().unix()
//           approvedImage.is_reference = 1
//           let upload = await Request.addDesignOrderAttachments(approvedImage)
//         }
//       }
//       if (!empty(uploaded)) {
//         emitProgress('updating_attachments', `Updating design order attachments...`, 70);
//         for (const row of uploaded) {
//           console.log('row555555', row)
//           let approvedImage = {}
//           approvedImage.name = row.url;
//           approvedImage.file_type = row.file_type;
//           approvedImage.extension = row.extension;
//           approvedImage.file_size = row.size;
//           approvedImage.design_order_id = design_order_id;
//           // approvedImage.added_by = userId
          
//           approvedImage.added_by =row?.added_by || userId
//           approvedImage.approved=row.approved || 0,
//           approvedImage.approved_by=row.approved_by_id || null,
//           approvedImage.status = row?.status || 1,
//           approvedImage.isChat =row.isChat || 0,
//           approvedImage.isDesign =row.isDesign,
//           approvedImage.isNew =row.isNew || 1,
//           approvedImage.reason =row.reason || null,

//           approvedImage.timestamp = moment().unix()
//           let upload = await Request.addDesignOrderAttachments(approvedImage)
//         }
        
//         // If this is the first attachment, set design_order_job_status = 2 (in progress)
//         // if (uploaded?.length > 0) {
//         //   const designOrder = await Request.getDesignOrderByIdNew(design_order_id);
//         //   const currentJobStatus = designOrder?.[0]?.dataValues?.design_order_job_status;
//         //   // Only update if current status is 1 (new)
//         //   if (currentJobStatus == 1) {
//         //     const updateData = {
//         //       design_order_job_status: 2
//         //     };
//         //     await Request.updateDesignOrders(design_order_id, updateData);
//         //     console.log('Updated design_order_job_status to 2 for first attachment in updateDesignOrder');
//         //   }
//         // }
//       }
//       let notification_msg = `✏️ The Design Order has been updated. `
//       let notification_url = `design-order/view/${design_order_id}`

//       // Collect granular changes
//       const changeDetails = [];
//       // Client change
//       const previousClientId = result?.[0]?.client;
//       const previousClientName = result?.[0]?.customer_name || result?.[0]?.client_name || '';
//       const newClientId = partnerData?.[0]?.id;
//       const newClientName = partnerData?.[0]?.name || previousClientName;
//       if (previousClientId !== newClientId && newClientId) {
//         changeDetails.push(`Client changed to <strong>"${newClientName}"</strong>`);
//       }
//       // Project / community change
//       const previousProjectName = result?.[0]?.community_name || '';
//       const newProjectName = values?.community?.name || previousProjectName;
//       const previousProjectId = result?.[0]?.community || result?.[0]?.project || null;
//       const newProjectId = values?.community?.id || null;
//       if (previousProjectId !== newProjectId && newProjectId) {
//         changeDetails.push(`Project changed to <strong>"${newProjectName}"</strong>`);
//       }
//       // Name change
//       if (result[0]['name'] != requestdata['name']) {
//         changeDetails.push(`Design Order name changed to <strong>"${requestdata['name']}"</strong>`);
//       }
//       // Description change
//       if (result[0]['description'] != requestdata['description']) {
//         changeDetails.push(`Description updated to <strong>${requestdata['description']}</strong>`);
//       }
//       // Start date change
//       if (result[0]['start_date'] != requestdata['start_date']) {
//         changeDetails.push(`Start date updated to <strong>${moment(requestdata['start_date'] * 1000).format('YYYY-MM-DD')}</strong>`);
//       }
//       // End date change
//       if (result[0]['due_date'] != requestdata['due_date']) {
//         changeDetails.push(`End date updated to <strong>${moment(requestdata['due_date'] * 1000).format('YYYY-MM-DD')}</strong>`);
//       }

//       // Designers change (added/removed) into same message
//       const addedDesignerNames = newlyAdded
//         .map(id => {
//           const d = designers.find(des => des.id === id);
//           if (!d) return null;
//           return d.name || (d.first_name ? `${d.first_name} ${d.last_name || ''}`.trim() : d.company_name) || null;
//         })
//         .filter(Boolean);

//       const removedDesignerDetailsAll = removedDesignerIds.length > 0
//         ? await Request.getUsersByIds(removedDesignerIds)
//         : [];
//       const removedNameMap = new Map();
//       (removedDesignerDetailsAll || []).forEach(d => {
//         const nm =
//           d?.name ||
//           (d?.first_name ? `${d.first_name} ${d.last_name || ''}`.trim() : d?.company_name) ||
//           null;
//         if (nm) removedNameMap.set(d.id, nm);
//       });

//       const removedDesignerNames = removedDesignerIds
//         .map(id => {
//           const resolved = removedNameMap.get(id);
//           if (resolved) return resolved;
//           const d = previousDesigners.find(prev => prev.id === id);
//           if (!d) return null;
//           const name =
//             d.name ||
//             (d.first_name ? `${d.first_name} ${d.last_name || ''}`.trim() : d.company_name);
//           return name || null;
//         })
//         .filter(Boolean);

//       if (addedDesignerNames.length > 0 || removedDesignerNames.length > 0) {
//         const parts = [];
//         if (addedDesignerNames.length > 0) parts.push(`added designers: "${addedDesignerNames.join('", "')}"`);
//         if (removedDesignerNames.length > 0) parts.push(`removed designers: "${removedDesignerNames.join('", "')}"`);
//         changeDetails.push(`Designers updated (${parts.join(' | ')})`);
//       }

//       // Designs (attachments) change
//       const newDesignNames = (uploaded || []).map(row => row.url).filter(Boolean);
//       const addedDesignFiles = newDesignNames.filter(n => !prevDesignNames.includes(n));
//       const removedDesignFiles = prevDesignNames.filter(n => !newDesignNames.includes(n));
//       if (addedDesignFiles.length > 0 || removedDesignFiles.length > 0) {
//         const parts = [];
//         if (addedDesignFiles.length > 0) parts.push(`${addedDesignFiles.length} design${addedDesignFiles.length > 1 ? 's' : ''} added`);
//         if (removedDesignFiles.length > 0) parts.push(`${removedDesignFiles.length} design${removedDesignFiles.length > 1 ? 's' : ''} removed`);
//         changeDetails.push(`Design files updated (${parts.join(' | ')})`);
//       }

//       // Reference files change
//       const newReferenceNames = (uploadedReferenceFiles || []).map(row => row.url).filter(Boolean);
//       const addedRefFiles = newReferenceNames.filter(n => !prevReferenceNames.includes(n));
//       const removedRefFiles = prevReferenceNames.filter(n => !newReferenceNames.includes(n));
//       if (addedRefFiles.length > 0 || removedRefFiles.length > 0) {
//         const parts = [];
//         if (addedRefFiles.length > 0) parts.push(`${addedRefFiles.length} reference${addedRefFiles.length > 1 ? 's' : ''} added`);
//         if (removedRefFiles.length > 0) parts.push(`${removedRefFiles.length} reference${removedRefFiles.length > 1 ? 's' : ''} removed`);
//         changeDetails.push(`Reference files updated (${parts.join(' | ')})`);
//       }

//       // Total cost change
//       if (result[0]['total_cost'] != totalCost) {
//         changeDetails.push(`Total cost updated to <strong>${totalCost}</strong>`);
//       }
//       // Notes change
//       if ((result[0]['notes'] || '') != (requestdata['notes'] || '')) {
//         changeDetails.push(`Notes updated to <strong>${requestdata['notes'] || ''}</strong>`);
//       }

//       if (changeDetails.length > 0) {
//         notification_msg += ` Changes: ` + changeDetails.join(' | ');
//       }

//       if (!empty(notification_msg)) {
//         let requestActivity = {}
//         requestActivity['design_order_id'] = design_order_id;
//         requestActivity['activity'] = notification_msg;
//         // requestActivity['edited_by'] = userId
//         requestActivity['added_by'] = userId // ⚠ Uncomment this line if any breaks happen because of this 
//         requestActivity['timestamp'] = currentTime
//         let reqActivityId = await RequestActivity.addDesignOrderUpdate(requestActivity);
//       }
//       let adminMangerIds = await Admin.getAllAdminAndManagerUserIds();
//         let allAdminToCheck = [...new Set([userId,...adminMangerIds].filter(Boolean))];
//           console.log('alluserssssssssarray',allAdminToCheck)
//         // const creator = await Admin.getUserNotificationAndPermissionSettings(userId); // assumes this function returns user's email/sms info
        
//           for (const userIdToCheck of allAdminToCheck) {
//             console.log('useriddddd',userIdToCheck)
//             const creator = await Admin.getUserNotificationAndPermissionSettings(userIdToCheck);
//             console.log('creatorrrrrrrrr',creator)
//             if (!creator) continue;
        
//             const recipientPhone = creator?.phone || TWILIO_PHONE;
//             const Phone_country_code = creator?.phone_country_code || '1';
//             console.log('recipientPhoneeee',recipientPhone)
//             console.log('Phone_country_codeeeee',Phone_country_code)

//             if (creator.pcb_new_design_jobs === 1 || creator.view_design_permission === 1) {
//               console.log("✅ Sales order SMSsssssss");
//               // Email Notification
//               if (
//                 creator.emailEnabled === 1 &&
//                 creator.email &&
//                 creator.pcb_new_design_jobs === 1 && 
//                 creator.pcb_new_design_jobs_email === 1
//               ) {
//                 let welcome = "Hello " + creator.name + ",";
//                 let subject = `📦 New Design Order Updated: ${values.name}`;
//                 let email_content = " Design order has been updated. Please review the details in your software.";

//                 let message = designersEmail({
//                 designer:creator,
//                 designOrderName:result[0]?.name,
//                 email_content,
//                 welcome,
//                 baseUrl: BASE_URL,
//                 profileData: profileData[0],
//               });
//               console.log("✅ Design order SMSsssssss 2");
//                 message = message.replace('[emailContent]', email_content);
//                 message = message.replace('[CONTENT]', welcome);
//                 message = message.replace('[LOGIN_URL]', `${BASE_URL}`);

//                 const mailOptions = {
//                   // to: 'sanjay.pk@skyniche.com',
//                   to: creator.email,
//                   from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
//                   subject: subject,
//                   text: subject,
//                   html: message,
//                 };

//                 try {
//                   await transporter.sendMail(mailOptions);
//                   console.log(`✅ design order email sent to creator5: ${creator.email}`);
//                 } catch (err) {
//                   console.error(`❌ Error sending design order email to ${creator.email}:`, err);
//                 }
//               }

//               // SMS Notification
//               if (
//                 creator?.smsEnabled === 1 &&
//                 creator?.phone &&
//                 creator.pcb_new_design_jobs === 1 && 
//                 creator.pcb_new_design_jobs_sms === 1
//               ) {
//               console.log("✅ design order SMSsssssss 3");
//                 const smsContent = `🎨 Hello ${creator?.name},\n\n📢   Design Order "${result[0]?.name}" has been updated.\n\n🔗 Login: ${BASE_URL}\n\n– Team SignWise`;

//                 try {
//                   // await sendMessageToPhoneNumber('9074906155', smsContent);
//                   await sendMessageToPhoneNumber(recipientPhone, smsContent,Phone_country_code);
//                   // await sendMessageToPhoneNumber('9074906155', smsContent,'91');
//                   // await sendMessageToPhoneNumber('9947931552', smsContent,'91');
//                   console.log("✅ Design order SMS sent to creator");
//                 } catch (err) {
//                   console.error("❌ Error sending Design order SMS to creator:", err);
//                 }
//               }
//             }
//           }
//     }
//     let requestDetails = await Request.getAllDesignOrders(design_order_id, userId, userType,isClient);
//     requestDetails = requestDetails?.data
//     console.log('requestDetails-requestDetails==',requestDetails)
//     if (!empty(requestDetails[0].status)) {
//       let notification_msg = `The Design Order <strong>"${requestDetails[0]['name']}".</strong> has been updated`
//       let notification_url = `design-order/view/${design_order_id}`

//       let requestPartners = await Request.getDesignOrderPartnersByIds([], design_order_id)

//       for (let eachpartner of requestPartners) {
//         //alert 
//         let incrementResult = await Admin.incrementField(eachpartner.id, "noti_count", 1);
//         let notificationUser = {
//           user_id: eachpartner.id,
//           url: notification_url,
//           timestamp: currentTime,
//           message: notification_msg,
//           status: 1,
//           from_id: userId,
//         }
//         const addNotificationResult = await Admin.addNotification(notificationUser);
//       }

//       const adminIds = await Admin.getAllAdminIds()
//       for (let eachadminIds of adminIds) {
//         let incrementResult = await Admin.incrementField(eachadminIds, 'noti_count', 1)
//         let addNotification = {
//           user_id: eachadminIds,
//           url: notification_url,
//           timestamp: currentTime,
//           message: notification_msg,
//           status: 1,
//           from_id: userId,
//         }
//         const addNotificationResult = await Admin.addNotification(addNotification);
//       }

//       if (!previousServiceData || previousServiceData.length === 0 || updateReqID) {
//         emitProgress('updating_tab_counts', `Updating user tab counts...`, 85);
//         const allUserIds = await Admin.getAllUserIds();
//       const selectedClientId = result[0]?.client|| null;
//       const clientUserId = await Admin.getPartnersUserIdById(selectedClientId)
//       console.log("clientUserId",clientUserId)
//       let allUsersToUpdate = [...new Set([userId, clientUserId, ...allUserIds].filter(Boolean))];

//       const updateOpenCount = async (userId,increase = true) => {
//             await Request.designCountIncrementField(userId, 'open_count', 1, 'open_count_ids', design_order_id, true);
//             if(updateReqID){
//               await Request.designCountIncrementField(userId, 'new_count', 1, 'new_count_ids', design_order_id, true);
//               console.log("design_order open updated inprogress count" , userId , design_order_id)
//             }
//             console.log("design_order_job_status new updated inprogress count" , userId , design_order_id)
//     };
//     await Promise.all(allUsersToUpdate.map(user => updateOpenCount(user)));
//     console.log("count updated successfully")
//       }

//       emitProgress('complete', `Design order updated successfully!`, 100);
//       res.send({ status: 1, message: 'Design Order updated successfully' })
      
//     } else {
//       emitProgress('error', 'Failed to update design order', 0);
//       res.send({ message: 'failed to edit design order',requestDetails })
//     }
//   } catch (error) {
//     console.log(error, 'error in get Design Order by id')
//     emitProgress('error', `Error updating design order: ${error.message}`, 0);
//     res.send({ status: 0, message: 'failed to get Design Order by id'+error })
//   }
// }
const sendDesignersUpdateEmails = async (
  newlyAdded, // 👈 correct now
  existingDesignerIds,
  design_order_id,
  values,
  userId,
  eligibleEmailDesigners,
  designers
) => {
  console.log('newlyAdded===', newlyAdded)
  const subjectNew = `🎨 New Design Job Assigned: ${values.name}`;
  const subjectExisting = `👥 Team Update: New Designer(s) Added to ${values.name}`;

  const newlyAddedDesigners = designers?.filter(
    d => newlyAdded.includes(d.id) // && d.email && d.emailEnabled === 1 && d.pcb_new_design_jobs === 1
  );
  const newlyAddedNames = newlyAddedDesigners.map(d => d.name).join(', ');
  const profileData = (await Settings.getCompanyProfileDetails())[0];

  // --- Send to new designers ---
  for (let id of newlyAdded) {
    const designer = designers?.find(d => d.id === id);
    console.log('designer', designer)
    if (!designer || !designer.email || designer.emailEnabled !== 1 || designer.pcb_new_design_jobs !== 1 || designer.pcb_new_design_jobs_email !== 1) continue;

    const welcome = `Hello ${designer.name},`;
    const email_content = `🎨 You’ve been assigned a new Design Job: <strong>${values.name}</strong>.<br><br>🔗 <a href="${BASE_URL}">Login to your software</a> to get started.`;

    const message = designersEmail({
      designer,
      designOrderName: values.name,
      email_content,
      welcome,
      baseUrl: BASE_URL,
      profileData
    });

    const mailOptions = {
      // to: 'deeraj@skyniche.com',
      to: designer.email,
      from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
      subject: subjectNew,
      html: message
    };

    try {
      await transporter.sendMail(mailOptions);
      console.log(`✅ Email sent to new designer6: ${designer.email}`);
    } catch (error) {
      console.error(`❌ Failed to send email to new designer ${designer.email}:`, error);
    }
  }

  // --- Send to existing designers about new additions ---
  if (newlyAdded.length > 0) {
    for (let id of existingDesignerIds) {
      const designer = designers?.find(d => d.id === id);
      if (!designer || !designer.email || designer.emailEnabled !== 1 || designer.pcb_new_design_jobs !== 1 || designer.pcb_new_design_jobs_email !== 1) continue;

      const email_content = `🔄 Update: New designer(s) <strong>${newlyAddedNames}</strong> have been added to your Design Job: <strong>${values.name}</strong>.<br><br>🔗 <a href="${BASE_URL}">Login</a> to view details.`;

      const message = existingDesignerEmail({
        designer,
        designOrderName: values.name,
        baseUrl: BASE_URL,
        design_order_id,
        newlyAddedNames,
        profileData,
        email_content
      });

      const mailOptions = {
        // to: 'deeraj@skyniche.com',
        to: designer.email,
        from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
        subject: subjectExisting,
        html: message
      };

      try {
        await transporter.sendMail(mailOptions);
        console.log(`✅ Email sent to existing designer: ${designer.email}`);
      } catch (error) {
        console.error(`❌ Failed to send email to existing designer ${designer.email}:`, error);
      }
    }
  }
};

const sendViewPermissionDesignersUpdateEmails = async (
  newlyAdded,
  existingDesignerIds,
  design_order_id,
  values,
  userId,
  eligibleEmailDesigners,
  designers
) => {
  const subjectNew = `🎨 New Design Access Granted: ${values.name}`;
  const subjectExisting = `👥 Design Team Updated: New Access Granted for ${values.name}`;

  const newlyAddedDesigners = designers?.filter(
    d => newlyAdded.includes(d.id)
  );

  const newlyAddedNames = newlyAddedDesigners.map(d => d.name).join(', ');
  const profileData = (await Settings.getCompanyProfileDetails())[0];

  // --- Notify newly added designers (view_permission === 1)
  for (let id of newlyAdded) {
    const designer = designers?.find(d => d.id === id);
    if (!designer || !designer.email || designer.emailEnabled !== 1 || designer.view_design_permission !== 1) continue;

    const welcome = `Hello ${designer.name},`;
    const email_content = `🎨 You’ve been granted access to a new Design Job: <strong>${values.name}</strong>.<br><br>🔗 <a href="${BASE_URL}">Login to your software</a> to view the details.`;

    const message = designersEmail({
      designer,
      designOrderName: values.name,
      email_content,
      welcome,
      baseUrl: BASE_URL,
      profileData
    });

    const mailOptions = {
      // to: 'deeraj@skyniche.com',
      to: designer.email,
      from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
      subject: subjectNew,
      html: message
    };

    try {
      await transporter.sendMail(mailOptions);
      console.log(`✅ View-permission email sent to new designer7: ${designer.email}`);
    } catch (error) {
      console.error(`❌ Failed to send view-permission email to new designer ${designer.email}:`, error);
    }
  }

  // --- Notify existing designers about the newly added ones
  if (newlyAdded.length > 0) {
    for (let id of existingDesignerIds) {
      const designer = designers?.find(d => d.id === id);
      if (!designer || !designer.email || designer.emailEnabled !== 1 || designer.view_design_permission !== 1 || designer.pcb_new_design_jobs_sms === 1) continue;

      const email_content = `🔄 Update: New designer(s) <strong>${newlyAddedNames}</strong> now have access to your Design Job: <strong>${values.name}</strong>.<br><br>🔗 <a href="${BASE_URL}">Login</a> to view updates.`;

      const message = existingDesignerEmail({
        designer,
        designOrderName: values.name,
        baseUrl: BASE_URL,
        design_order_id,
        newlyAddedNames,
        profileData,
        email_content
      });

      const mailOptions = {
        // to: 'deeraj@skyniche.com',
        to: designer.email,
        from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
        subject: subjectExisting,
        html: message
      };

      try {
        await transporter.sendMail(mailOptions);
        console.log(`✅ View-permission update email sent to existing designer: ${designer.email}`);
      } catch (error) {
        console.error(`❌ Failed to send view-permission update email to ${designer.email}:`, error);
      }
    }
  }
};

const sendRemovedDesignersEmail = async (
  removedDesignerDetails,
  remainingDesignerDetails,
  design_order_id,
  designOrderName,
  baseUrl,
  userId,
  removedDesignerNameDetails
) => {
  const subjectRemoved = `🚫 Removed from Design Order: ${designOrderName}`;
  const subjectExisting = `👥 Team Update: Designer(s) Removed from ${designOrderName}`;
  const profileData = await Settings.getCompanyProfileDetails();

  const eligibleRemainingDesigners = remainingDesignerDetails;

  const removedNames = removedDesignerNameDetails.map(d => d.name).join(', ');

  // ✅ Send to removed designers
  for (let designer of removedDesignerDetails) {
    if (designer.pcb_new_design_jobs_sms === 1) continue;
    const message = removedDesignerEmail({
      designer,
      designOrderName,
      baseUrl,
      design_order_id,
      profileData: profileData[0],
    });

    const mailOptions = {
      // to: 'deeraj@skyniche.com',
      to: designer.email,
      from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
      subject: subjectRemoved,
      html: message,
    };

    try {
      await transporter.sendMail(mailOptions);
      console.log(`✅ Email sent to removed designer: ${designer.email}`);
    } catch (error) {
      console.error(`❌ Failed to send email to removed designer ${designer.email}:`, error);
    }
  }

  // ✅ Send to remaining designers about the removal
  for (let designer of eligibleRemainingDesigners) {
    if (designer.pcb_new_design_jobs_sms === 1) continue;
    const message = remainingDesignerInfoEmail({
      designer,
      designOrderName,
      baseUrl,
      design_order_id,
      removedNames,
      profileData: profileData[0],
    });

    const mailOptions = {
      // to: 'deeraj@skyniche.com',
      to: designer.email,
      from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
      subject: subjectExisting,
      html: message,
    };

    try {
      await transporter.sendMail(mailOptions);
      console.log(`✅ Email sent to existing designer about removal: ${designer.email}`);
    } catch (error) {
      console.error(`❌ Failed to send email to existing designer ${designer.email}:`, error);
    }
  }
};

const sendRemovedDesignersViewPermissionEmail = async (
  removedDesignerDetails,
  remainingDesignerDetails,
  design_order_id,
  designOrderName,
  baseUrl,
  userId,
  removedDesignerNameDetails
) => {
  const subjectRemoved = `🚫 Access Removed: Design Order "${designOrderName}"`;
  const subjectExisting = `📢 Update: Designer(s) Access Revoked from "${designOrderName}"`;
  const profileData = await Settings.getCompanyProfileDetails();

  const eligibleRemainingDesigners = remainingDesignerDetails;
  const removedNames = removedDesignerNameDetails.map(d => d.name).join(', ');

  // ✅ Notify removed designers (view permission based)
  for (let designer of removedDesignerDetails) {
    if (designer.pcb_new_design_jobs_sms === 1) continue;
    const message = removedDesignerEmail({
      designer,
      designOrderName,
      baseUrl,
      design_order_id,
      profileData: profileData[0],
    });

    const mailOptions = {
      // to: 'deeraj@skyniche.com',
      to: designer.email,
      from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
      subject: subjectRemoved,
      html: message,
    };

    try {
      await transporter.sendMail(mailOptions);
      console.log(`✅ View-permission removal email sent to: ${designer.email}`);
    } catch (error) {
      console.error(`❌ Failed to send view-permission removal email to ${designer.email}:`, error);
    }
  }

  // ✅ Notify remaining designers about who was removed
  for (let designer of eligibleRemainingDesigners) {
    if (designer.pcb_new_design_jobs_sms === 1) continue;
    const message = remainingDesignerInfoEmail({
      designer,
      designOrderName,
      baseUrl,
      design_order_id,
      removedNames,
      profileData: profileData[0],
    });

    const mailOptions = {
      // to: 'deeraj@skyniche.com',
      to: designer.email,
      from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
      subject: subjectExisting,
      html: message,
    };

    try {
      await transporter.sendMail(mailOptions);
      console.log(`✅ View-permission update email sent to: ${designer.email}`);
    } catch (error) {
      console.error(`❌ Failed to send view-permission update email to ${designer.email}:`, error);
    }
  }
};

const updateDesignOrderDueDate = async (req, res) => {
  try {
    const { due_date, design_order_id, added_by } = req.body;
    let orderData = {}
    if (due_date !== (undefined || null)) {
      orderData['due_date'] = due_date
    } else {
      orderData['due_date'] = 0
    }
    orderData['timestamp'] = currentDate;
    let updatedData = await Request.updateDesignOrders(design_order_id, orderData);
    if (updatedData) {
      const currentTime = moment().unix();
      if(orderData?.due_date){
        const newDueDate = moment(orderData?.due_date * 1000).format('MM-DD-YYYY')
        let notification_msg = `📅 The Design Order Due Date has been updated to <strong> ${newDueDate}.</strong> `
        if (!empty(notification_msg)) {
          let requestActivity = {}
          requestActivity['design_order_id'] = design_order_id;
          requestActivity['activity'] = notification_msg;
          requestActivity['added_by'] = added_by
          requestActivity['timestamp'] = currentTime
          let reqActivityId = await RequestActivity.addDesignOrderUpdate(requestActivity);
        }
      }
      res.send({ status: 1, data: updatedData, message: 'Design Order updated successfully' })
    } else {
      res.send({ status: 0, message: 'Failed to update design order' })
    }
  } catch (error) {
    console.log(error, 'error updating Design Order by id')
    res.send({ status: 0, message: 'Failed to update design order'+error })
  }
}

const updateDesignOrderRequestedDueDate = async (req, res) => {
  try {
    const { requested_due_date, design_order_id, added_by } = req.body;
    const currentDate = Math.round(moment().valueOf() / 1000);
    let orderData = {}
    if (requested_due_date !== (undefined || null)) {
      orderData['requested_due_date'] = requested_due_date
    } else {
      orderData['requested_due_date'] = 0
    }
    orderData['timestamp'] = currentDate;
    let updatedData = await Request.updateDesignOrders(design_order_id, orderData);
    if (updatedData) {
      const currentTime = moment().unix();
      if(orderData?.requested_due_date){
        const newRequestedDueDate = moment(orderData?.requested_due_date * 1000).format('MM-DD-YYYY')
        let notification_msg = `📅 The Design Order Requested Due Date has been updated to <strong> ${newRequestedDueDate}.</strong> `
        if (!empty(notification_msg)) {
          let requestActivity = {}
          requestActivity['design_order_id'] = design_order_id;
          requestActivity['activity'] = notification_msg;
          requestActivity['added_by'] = added_by
          requestActivity['timestamp'] = currentTime
          let reqActivityId = await RequestActivity.addDesignOrderUpdate(requestActivity);
        }
      }
      res.send({ status: 1, data: updatedData, message: 'Design Order Requested Due Date updated successfully' })
    } else {
      res.send({ status: 0, message: 'Failed to update design order requested due date' })
    }
  } catch (error) {
    console.log(error, 'error updating Design Order Requested Due Date by id')
    res.send({ status: 0, message: 'Failed to update design order requested due date'+error })
  }
}

const acceptOrRejectDesignOrder = async (req, reply) => {
  try {
    const { design_order_id, acceptStatus, newStatus, userId,userType, reason, rejectFiles, rejectType ,requestedDueDate} = req.body;
    const currentDate = Math.round(moment().valueOf() / 1000);

    // let allDesigns = await Request.fetchAllDesignOrderAttachments(design_order_id);
    // const allSentDesignIds = allDesigns?.filter(des=>des.isDesign == 1 && des.approved != 1 && des.status == 2)?.map(item => item.id);

    let isClient = await Request.checkIfUserIsClient(userType);;
    let requestData = await Request.getAllDesignOrders(design_order_id, userId,userType, isClient);;
    let statusUpdate;
    if (acceptStatus === 1) {
      statusUpdate = await Request.updateDesignOrders(design_order_id, { status: newStatus, timestamp : currentDate });

      const assignedDesigners = await Request.getDesignOrderDesigners(design_order_id);

        if (assignedDesigners?.length > 0) {
          const allDesigners = await Request.getUsersByIds(assignedDesigners.map(d => d.id));
        
          const eligibleDesigners = allDesigners.filter(
            d => d.email && d.emailEnabled === 1 && d.approve_design_permission === 1 && d.approve_design_permission_email === 1
          );
        
         if (eligibleDesigners.length > 0) {
            const requestInfo = requestData.data?.[0];
            const designOrderName = requestInfo?.name;
            const baseUrl = BASE_URL;
            let profileData = await Settings.getCompanyProfileDetails();
          
            for (const designer of eligibleDesigners) {
              const emailContent = yourDesignAcceptedEmailTemplate({
                designer,
                designOrderName,
                baseUrl,
                design_order_id,
                profileData: profileData[0],
              });
            
              const mailOptions = {
                // to: 'deeraj@skyniche.com',
                to: designer.email,
                from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
                subject: `🎉 Design Order Design Accepted: ${designOrderName}`,
                html: emailContent,
              };
            
              try {
                await transporter.sendMail(mailOptions);
                console.log(`✅ Acceptance email sent to designer: ${designer.email}`);
              } catch (err) {
                console.error(`❌ Failed to send acceptance email to ${designer.email}:`, err);
              } 
            }
          }

          const smsEligibleDesigners = allDesigners.filter(
            d => d.phone && d.smsEnabled === 1 && d.approve_design_permission === 1 && d.approve_design_permission_sms === 1
          );
          
          if (smsEligibleDesigners.length > 0) {
            const requestInfo = requestData.data?.[0];
            const designOrderName = requestInfo?.name;
            for (const designer of smsEligibleDesigners) {
              try {
                const smsContent = `🎨 Hello ${designer.name},\n\n✅ Your design for the order "${designOrderName}" has been *approved* by the client.\n\n📂 View it here:\n${BASE_URL}\n\n– Team SignWise`;
                // await sendMessageToPhoneNumber('9074906155', smsContent);
                await sendMessageToPhoneNumber(designer.phone, smsContent,designer.phone_country_code);
                console.log(`📩 SMS sent to designer: ${designer.phone}`);
              } catch (smsErr) {
                console.error(`❌ Failed to send SMS to ${designer.phone}:`, smsErr);
              }
            }
          }

        } 
        
        // // Update selected designs to isDesign = 1
      // const designData = { approved: 1, approved_by: userId, reason: null }
      // await Request.updateMultipleDesignOrderAttachments(allSentDesignIds,designData)
    } else { // reject design order
      const designOrderData = {
        status: newStatus, 
        design_order_job_status: 4, 
        reason : reason, 
        reject_type: rejectType || null,
        requested_due_date: requestedDueDate || null,
        // reject_file: rejectFile ? rejectFile?.url : null,
        // reject_file_type: rejectFile ? rejectFile?.file_type : null,
        timestamp : currentDate, 
        working_designer_id: null
      }
      const rejectedFilesData = rejectFiles.map(file => ({
        design_order_id: design_order_id,
        file_url: file.url,
        file_type: file.file_type,
        file_name: file.original_name || file.url.split('/').pop(),
        timestamp: currentDate
      }));      
      if (rejectedFilesData.length > 0) {
        await Request.addDesignOrderRejectedFiles(rejectedFilesData);
      }      
      console.log("rejectedFilesData",rejectedFilesData);
      statusUpdate = await Request.updateDesignOrders(design_order_id, designOrderData);
      const assignedDesigners = await Request.getDesignOrderDesigners(design_order_id);

      if (assignedDesigners?.length > 0) {
        const allDesigners = await Request.getUsersByIds(assignedDesigners.map(d => d.id));
      
        const eligibleRejectDesigners = allDesigners.filter(
          d => d.email && d.emailEnabled === 1 && d.reject_design_permission === 1 && d.reject_design_permission_email === 1
        );
      
        if (eligibleRejectDesigners.length > 0) {
          const requestInfo = requestData.data?.[0];
          const designOrderName = requestInfo?.name;
          const baseUrl = BASE_URL;
          let profileData = await Settings.getCompanyProfileDetails();
        
          for (const designer of eligibleRejectDesigners) {
            const emailContent = yourDesignRejectedEmailTemplate({ 
              designer,
              designOrderName,
              baseUrl,
              design_order_id,
              profileData: profileData[0],
              reason,
              rejectFiles
            });
          
            const mailOptions = {
              // to: 'deeraj@skyniche.com',
              to: designer.email,
              from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
              subject: `❌ Design Order Rejected: ${designOrderName}`,
              html: emailContent,
            };
          
            try {
              await transporter.sendMail(mailOptions);
              console.log(`📪 Rejection email sent to designer: ${designer.email}`);
            } catch (err) {
              console.error(`❌ Failed to send rejection email to ${designer.email}:`, err);
            }
          }
        }

        const smsEligibleRejectDesigners = allDesigners.filter(
          d => d.phone && d.smsEnabled === 1 && d.reject_design_permission === 1 && d.reject_design_permission_sms === 1
        );

        if (smsEligibleRejectDesigners.length > 0) {
          const requestInfo = requestData.data?.[0];
          const designOrderName = requestInfo?.name;
          for (const designer of smsEligibleRejectDesigners) {
            try {
              const smsContent = `🎨 Hello ${designer.name},\n\n❌ Your design for the order "${designOrderName}" was *rejected* by the client.\n\n📄 Reason: ${reason || 'No reason provided.'}\n\n🔍 View here:\n${BASE_URL}\n\n– Team SignWise`;
              // await sendMessageToPhoneNumber('9074906155', smsContent);
              await sendMessageToPhoneNumber(designer.phone, smsContent,designer.phone_country_code);
              console.log(`📩 Rejection SMS sent to designer: ${designer.phone}`);
            } catch (smsErr) {
              console.error(`❌ Failed to send rejection SMS to ${designer.phone}:`, smsErr);
            }
          }
        }

      }

      // // Update unselected designs to isDesign = 2
      // const designDataUnselected = { approved: 0, approved_by: null, status:0, reason: reason };
      // if (allSentDesignIds?.length > 0) {
      //   await Request.updateMultipleDesignOrderAttachments(allSentDesignIds, designDataUnselected);
      // }

      if(statusUpdate){
        let allTasks = await Request.getAllDesignOrderTasks(design_order_id,userId,1)
        for(const task of allTasks){
          let project = {};
          project.status = 4
          let result = await Request.updateDesignOrderService(project,task.id);
          let allSubtask = await Request.getAllDesignOrderProjectSubTaskBytaskId(task.id,1);
          for(const subTask of allSubtask){
            let projectTask = {}
            projectTask['status'] = 4;
            let updated = await Request.updateDesignOrdersSubTask(projectTask, subTask.id);
          }
        }
      }
    }
    requestData = requestData.data[0]
    const partnerName = requestData.customer_name;

    // Get the actual user who performed the action (admin or client)
    let actionUserName = partnerName; // Default to client name
    if (userId) {
      try {
        const userInfo = await Admin.getUserById(userId);
        if (userInfo && userInfo[0]) {
          actionUserName = userInfo[0].first_name 
            ? `${userInfo[0].first_name} ${userInfo[0].last_name || ''}`.trim() 
            : (userInfo[0].company_name || partnerName);
        }
      } catch (error) {
        console.error('Error getting user info for activity log:', error);
        // Fallback to partnerName if error occurs
      }
    }

    let reqActivityMsg, notificationMsg, notificationUrl;

    if (acceptStatus == 1) {
      reqActivityMsg = `✅ The member "${actionUserName}" accepted the design order "${requestData.name}".`;
      notificationMsg = reqActivityMsg;
      notificationUrl = `design-order/view/${design_order_id}`;

    } else {
      reqActivityMsg = `❌ The member "${actionUserName}" rejected the design order "${requestData.name}".`;
      notificationMsg = reqActivityMsg;
      notificationUrl = `design-order/view/${design_order_id}`;
    }

    if (userId) {
      await Admin.incrementField(userId, "noti_count", 1);
      const notificationData_user = {
        user_id: userId,
        url: notificationUrl,
        timestamp: currentDate,
        message: notificationMsg,
        status: 1,
        from_id: userId,
      };
      await Admin.addNotification(notificationData_user);
    }

    const adminIds = await Admin.getAllAdminIds();
    for (const adminId of adminIds) {
      if (adminId !== userId) {
        await Admin.incrementField(adminId, "noti_count", 1);
        const notificationData = {
          user_id: adminId,
          url: notificationUrl,
          timestamp: currentDate,
          message: notificationMsg,
          status: 1,
          from_id: userId,
        };
        await Admin.addNotification(notificationData);
      }
    }

    const requestActivity = {
      design_order_id: design_order_id,
      activity: reqActivityMsg,
      added_by: userId,
      timestamp: currentDate,
    };
    const reqActivityId = await RequestActivity.addDesignOrderUpdate(requestActivity);

    // const allUserIds = await Admin.getAllUserIds();

    // let countField = acceptStatus === 1 ? "approved_count" : "rejected_count";

    // let usersToUpdate = [...new Set([userId, ...allUserIds].filter(Boolean))];

    // const updateCounts = async (userId) => {
    //   await Request.designCountIncrementField(userId, "open_count", -1);
    //   await Request.designCountIncrementField(userId, countField, 1);
    // };
    // await Promise.all(usersToUpdate.map(user => updateCounts(user)));

    // console.log("✅ Successfully updated open_count and status counts.");
    // const countField = acceptStatus === 1 ? "approved_count" : "rejected_count";
    // const countFieldIds = acceptStatus === 1 ? "approved_count_ids" : "rejected_ids";

    // let usersToUpdate = [...new Set([userId, ...allUserIds].filter(Boolean))];

    // const updateCounts = async (userId) => {
    //   await Request.designCountIncrementField(userId, "open_count", -1, "open_count_ids", design_order_id, false);

    //   await Request.designCountIncrementField(userId, countField, 1, countFieldIds, design_order_id, true);
    // };

    // await Promise.all(usersToUpdate.map(user => updateCounts(user)));

    // console.log("✅ Successfully updated open_count and status counts.");


    if (acceptStatus == 1) {
      reply.send({ status: 1, message: "You have accepted the design order" });
    } else {
      reply.send({ status: 2, message: "You have rejected the design order" });
    }
  } catch (error) {
    console.error("acceptOrRejectDesignOrder error: ", error);
    reply.send({
      status: 500,
      message: "Server error",
      error: error.message || "Unknown error",
    });
  }  
}

// const reduceDesignOrderCount = async (req, reply) => {
//   try {
//     const { design_order_id, userId } = req.body;

//     const userCount = await Request.getDesignOrderCountByUserId(userId);
//     if (!userCount) {
//       return reply.status(400).json({ status: 0, message: "User record not found" });
//     }

//     const countFields = {
//       open_count: "open_count_ids",
//       approved_count: "approved_count_ids",
//       rejected_count: "rejected_ids",
//       hold_count: "hold_count_ids",
//     };

//     let updatedField = null;
//     let updatedCountField = null;

//     for (const [countField, idsField] of Object.entries(countFields)) {
//       let idsArray = userCount[idsField] ? userCount[idsField].split(",") : [];

//       if (idsArray.includes(design_order_id.toString())) {
//         await Request.designCountIncrementField(userId, countField, -1, idsField, design_order_id, false);
//         updatedField = idsField;
//         updatedCountField = countField;
//         break;
//       }
//     }

//     if (!updatedField) {
//       return reply.status(400).json({ status: 0, message: "Design order ID not found in any count list" });
//     }

//     console.log(`Successfully updated count for ${updatedCountField}`);

//     reply.send({
//       status: 1,
//       message: `Count updated successfully. Removed from ${updatedField}`,
//       updatedCountField,
//     });
//   } catch (error) {
//     console.error("Error reducing design order count:", error);
//     reply.send({ status: 500, message: "Server error", error });
//   }
// };

const reduceDesignOrderCount = async (req, reply) => {
  try {
    const { design_order_id, userId } = req.body;
    console.log(`Successfully design_order_id`,design_order_id,userId);
    const userCount = await Request.getDesignOrderCountByUserData(userId);
    if (!userCount) {
      return reply.status(400).send({ status: 0, message: "User record not found" });
    }
    console.log(`Successfully userCount`,userCount);
    const countFields = {
      open_count: "open_count_ids",
      approved_count: "approved_count_ids",
      rejected_count: "rejected_ids",
      hold_count: "hold_count_ids",
    };

    let updatedField = null;
    let updatedCountField = null;

    for (const [countField, idsField] of Object.entries(countFields)) {
      let idsArray = userCount[idsField] ? userCount[idsField].split(",") : [];

      if (idsArray.includes(design_order_id.toString())) {
        await Request.designCountIncrementField(userId, countField, -1, idsField, design_order_id, false);
        updatedField = idsField;
        updatedCountField = countField;
        break;
      }
    }

    if (!updatedField) {
      return reply.status(400).send({ status: 0, message: "Design order ID not found in any count list" });
    }

    console.log(`✅ Successfully updated count for ${updatedCountField}`);

    reply.send({
      status: 1,
      message: `Count updated successfully. Removed from ${updatedField}`,
      updatedCountField,
    });
  } catch (error) {
    console.error("Error reducing design order count:", error);
    reply.status(500).send({ status: 0, message: "Server error", error });
  }
};

const reduceDesignOrderJobsCount = async (req, reply) => {
  try {
    const { design_order_id, userId } = req.body;
    console.log(`Successfully design_order_id`,design_order_id,userId);
    const userCount = await Request.getDesignOrderCountByUserData(userId);
    if (!userCount) {
      return reply.status(400).send({ status: 0, message: "User record not found" });
    }
    console.log(`Successfully userCount`,userCount);
    const countFields = {
      new_count: "new_count_ids",
      in_progress_count: "in_progress_count_ids",
      completed_count: "completed_count_ids",
      job_hold_count: "job_hold_count_ids",
    };

    let updatedField = null;
    let updatedCountField = null;

    for (const [countField, idsField] of Object.entries(countFields)) {
      let idsArray = userCount[idsField] ? userCount[idsField].split(",") : [];

      if (idsArray.includes(design_order_id.toString())) {
        await Request.designCountIncrementField(userId, countField, -1, idsField, design_order_id, false);
        updatedField = idsField;
        updatedCountField = countField;
        break;
      }
    }

    if (!updatedField) {
      return reply.status(400).send({ status: 0, message: "Design order ID not found in any count list" });
    }

    console.log(`✅ Successfully updated count for ${updatedCountField}`);

    reply.send({
      status: 1,
      message: `Count updated successfully. Removed from ${updatedField}`,
      updatedCountField,
    });
  } catch (error) {
    console.error("Error reducing design order count:", error);
    reply.status(500).send({ status: 0, message: "Server error", error });
  }
};


const convertDesignToSalesOrder = async (req, reply) => {
  try {
    const { design_order_id, userId,userType, userName, salesOrder,isDesignOrder, socketId } = req.body;
    let currentDate = moment().unix();

    // 📊 Progress tracking function
    const emitProgress = (step, message, progress = 0) => {
      console.log(`[CONVERSION-PROGRESS] 📊 Progress: ${step} - ${message} (${progress}%)`);
      
      const progressData = {
        socketId: socketId,
        userId: userId,
        step: step,
        message: message,
        progress: progress,
        timestamp: moment().unix(),
        domain: DOMAIN
      };
      
      // Emit to working socket server
      console.log(`[CONVERSION-PROGRESS] 📡 Emitting progress to working socket server...`);
      try {
        const workingSocket = ioClient(REST_API_IO, {
          path: '/socket.io',
          transports: ['websocket', 'polling'],
          secure: true,
          timeout: 5000
        });
        
        workingSocket.on('connect', () => {
          console.log(`[CONVERSION-PROGRESS] 🔌 Connected to working socket server`);
          console.log(`[CONVERSION-PROGRESS] 📊 Emitting progress data:`, progressData);
          
          // Emit progress data
          workingSocket.emit('request_progress', progressData);
          
          console.log(`[CONVERSION-PROGRESS] ✅ Successfully emitted progress to working socket server`);
          
          // Disconnect after emitting
          setTimeout(() => {
            workingSocket.disconnect();
          }, 1000);
        });
        
        workingSocket.on('connect_error', (error) => {
          console.error(`[CONVERSION-PROGRESS] ❌ Working socket connection error:`, error);
        });
        
      } catch (error) {
        console.error(`[CONVERSION-PROGRESS] ❌ Error creating working socket connection:`, error);
      }
    };

    // Initial progress
    const conversionType = salesOrder ? 'Sales Order' : 'Estimate';
    console.log(`[CONVERSION-PROGRESS] 🚀 Starting design order conversion to ${conversionType} for user:`, userId);
    emitProgress('start', `Initializing design order conversion to ${conversionType}...`, 5);

    let requestData = await Request.getAllDesignOrders(design_order_id, userId,userType);
    const selectedServices = await Request.getDesignOrderServicesByDesignOrderId(design_order_id);
    let statusUpdate = await Request.updateDesignOrders(design_order_id, { status: 3, timestamp: currentDate });
    let uploaded = await Request.fetchAllDesignOrderAttachmentsForConvert(design_order_id);

    emitProgress('data_fetch', 'Fetching design order data and services...', 15);

    requestData = requestData.data[0]
    let isAlreadyConverted = requestData?.request_id && requestData.request_id !== null;
    let existingRequestId = requestData?.request_id;
    let partnerIds = requestData?.client;
    let customerDatas = await Client.getClientById(partnerIds);
    let values = requestData;
    console.log('requestData===',requestData)
    console.log('selectedServices===',selectedServices)
    values['clientData'] = customerDatas
    let submission_deadline = requestData.start_date
    let participation_ends = requestData.due_date
    let requestdata = {}
    requestdata['name'] = values.name,
    requestdata['description'] = values.description,
    requestdata['markup_type'] = 2,
    requestdata['services'] = null,
    requestdata['notes']=values.notes||"",
    requestdata['labour_cost_services'] = null,
    requestdata['labour_cost'] = requestData?.total_cost || 0,
    requestdata['quotation']=salesOrder ? 0 : 1
    if(!isAlreadyConverted){
      requestdata['project_type'] = null
    }
    requestdata['project_category'] = null
    requestdata['design_order_id'] = design_order_id || null
    requestdata['community'] = values?.community ? values?.community : null;
    requestdata['submission_deadline'] = submission_deadline
    if (participation_ends !== (undefined || null)) {
      requestdata['participation_ends'] = participation_ends
    } else {
      requestdata['participation_ends'] = 0
    }
    requestdata['start_date'] = null
    if (values.welcome_message == true) {
      requestdata['welcome_message'] = values.welcome_message
      requestdata['subject'] = values.subject,
      requestdata['email_content'] = values.email_content
    } else {
      requestdata['welcome_message'] = false
      requestdata['subject'] = null,
      requestdata['email_content'] = null
    }
    requestdata['type'] = 2
    requestdata['status'] = salesOrder ? 2 : 8
    requestdata['quotation_status'] = 2
    requestdata['new_order'] = salesOrder ? 1 : 0
    console.log('designorder-clientData',values?.clientData)
    requestdata['min_payment_status'] = values?.clientData?.billing_type == 0 ? 1 : 0;
    requestdata['added_by'] = userId,
    requestdata['timestamp'] = currentDate
    requestdata['created_at'] = requestData?.created_at || currentDate
    requestdata['isFromDesignOrder'] = 1
    if (!empty(uploaded)) {
      for (const row of uploaded) {
        if(row?.file_type?.startsWith('image/')){
          requestdata['thumbnail'] = row.url
        }
      }
    }
    requestdata['is_location'] = 0
    requestdata['is_service_location'] = 0
    requestdata['is_rfp_service_location'] = 0
    requestdata['is_rfp_product_location'] = 0
    let taxAmountData = 0;
    let taxCategory = null;
    let projectData = null
    if (values?.community) {
      try {
        const taxData = await Community.getCommunityById(values.community);
        projectData = taxData
        console.log('taxData===',taxData)
        if (taxData?.tax_category && taxData?.tax_type !== undefined && taxData?.tax_value !== undefined) {
          taxCategory = taxData.tax_category;
          // taxAmountData = taxData.tax_type === 1
          //   ? ((requestData?.total_cost || 0) * (taxData.tax_value || 0)) / 100  // Percentage Tax
          //   : (taxData.tax_value || 0);  // Fixed Amount Tax
        }
      } catch (error) {
        console.error("Error fetching tax data:", error);
      }
    }
    let servicesData =[]

     servicesData = selectedServices?.filter(item => item.syncWithQuickbooks === 0 || item.syncWithQuickbooks === null);

     const synceServicesData = selectedServices?.filter(item => item.syncWithQuickbooks === 1);
     for(const service of synceServicesData){
       const serviceResultFromQb = await validateInventoryItemsInQbForPo(service,"quickbookId");
       if(serviceResultFromQb?.itemData){
         servicesData.push(serviceResultFromQb?.itemData);
         const updateData ={
           syncWithQuickbooks: 0,
           quickbookId: null
         }
         const updateService = await Product.updateServiceByQbId(service.quickbookId,updateData);
       }
     }

    const servicesNames = servicesData?.map(product => product.name).join(', ');


    const customerData = (values?.clientData?.syncWithQuickBook === null || values?.clientData?.syncWithQuickBook === 0) ? values.clientData : null;
    if(values?.clientData?.syncWithQuickBook === 1 && values?.clientData?.partnerQbId){
      const customerResultFromQb = await validateClientInQb(values?.clientData?.partnerQbId);
      if(customerResultFromQb?.removeQbid){
        customerData = values.clientData 
        const updateData ={
          syncWithQuickBook: 0,
          partnerQbId: null
        }
        const updateClient = await Client.updateVendorByQbId(values?.clientData?.partnerQbId,updateData);
      }
    }

    UniqueEstimateId =null
    doc_number = null
    let clientName = values?.clientData?.quickbookCustomerName;
    console.log("values===",values);
    console.log("values=clientName==",clientName);
    
    const ReasonWithQb = `This ${salesOrder ? 'Sales Order' : 'Estimate'} is not synced with QuickBooks because the following are not in QuickBooks.
                ${customerData? `Customer : ${customerData.name}` :''}
                ${servicesData.length > 0 ? `Services: ${servicesNames}` : ''}
                `;

    ////////////////////For Quickbook///////////////////////////////////////////////////////////
    console.log("values=customerData==",customerData);
    console.log("values=ReasonWithQb==",ReasonWithQb);
    const serviceItems = await Promise.all(
      selectedServices
        .map(async service => {
          let serviceId =service?.quickbookId 
          console.log('serviceId==',serviceId)
          console.log('service==',service)
          let quantity = service.quantity || 1;
          return {
            itemId: serviceId,
            name: service.name,
            amount: (Number(service.unit_cost) || 0) * quantity,
            quantity: quantity,
            unitPrice: Number(service.unit_cost) || 0,
          };
        })
    );
    const readToSyncWithQb = (!customerData && servicesData.length === 0);
    if (!customerData && !isAlreadyConverted && readToSyncWithQb) {
      try {
        emitProgress('quickbooks_sync', 'Syncing with QuickBooks...', 25);
        let customerId =values?.clientData?.partnerQbId 
        const requestData = {
          name: values.name,
          description: values.description,
          notes: values.notes || values.description,
          items:[...serviceItems],
          customerId: customerId,
          note: 'description note',
          discount: 0,
          jobType:"nil",
          taxAmount : taxAmountData || 0,
          clientData :values?.clientData || null,
          ProjectData :projectData || null,
          projectName:projectData?.name || null,
        };
        if(salesOrder){
          emitProgress('quickbooks_sales_order', 'Creating sales order in QuickBooks...', 30);
          // let quickbookInvoiceId = await addInvoiceToQuickBooks(requestData)
          let quickbookEstimateId =  await addEstimateToQuickBooks(requestData)
          emitProgress('quickbooks_sales_order_success', 'Sales order created successfully in QuickBooks!', 35);
          doc_number = quickbookInvoiceId?.Invoice?.DocNumber || doc_number;
        }else {
          emitProgress('quickbooks_estimate', 'Creating estimate in QuickBooks...', 30);
          let quickbookEstimateId =  await addEstimateToQuickBooks(requestData)
          emitProgress('quickbooks_estimate_success', 'Estimate created successfully in QuickBooks!', 35);
          doc_number = quickbookEstimateId?.Estimate?.DocNumber || doc_number;
        }
      } catch (error) {
        console.log("error while adding data to quickbook",error);
        emitProgress('quickbooks_error', 'QuickBooks sync failed - continuing without sync...', 35);
      }
    }else{
      emitProgress('quickbooks_error', 'QuickBooks sync failed - continuing without sync...', 35);
    }
    if(UniqueEstimateId != null){
      requestdata['estimate_id'] =(UniqueEstimateId && doc_number) ? UniqueEstimateId : null
      requestdata['syncWithQuickbooks'] = 1
      requestdata['is_production_qb'] = 1
      requestdata['doc_number'] = doc_number
      requestdata['est_doc_number'] = doc_number
    }else if (selectedServices?.length === 0 && !customerData ) {
      requestdata['reasonWithQb'] = 'To sync with QuickBooks, please add at least one service.';
    }else{
      requestdata['reasonWithQb'] = ReasonWithQb
    }
    console.log('requestdata===123',requestdata)
    // Preserve collaborators from the design order (serialize to CSV when needed)
    if (values?.collaborators) {
      if (Array.isArray(values.collaborators)) {
        requestdata['collaborators'] = values.collaborators
          .map((c) => (typeof c === 'object' && c?.id != null ? c.id : c))
          .filter(Boolean)
          .join(',');
      } else {
        requestdata['collaborators'] = String(values.collaborators || '');
      }
    }

    emitProgress('saving_request', `Saving ${conversionType} to database...`, 40);
    // let result = await Request.addRequest(requestdata)
    let result;
    if (isAlreadyConverted) {
      // Update existing request instead of creating new one
      result = await Request.updateRequets(existingRequestId, requestdata);
      result = existingRequestId; // Set result to existing request ID for consistency
      const deleteAttachments = await Request.deleteRequestAttachments(existingRequestId);
      console.log('Updated existing request:', existingRequestId);
    } else {
      // Create new request
      result = await Request.addRequest(requestdata);
      console.log('Created new request:', result);
    }
    if (result) {
      emitProgress('processing_messages', 'Processing messages and attachments...', 50);
      const requestMessagesImageData = await ClientChat.getRequestMessageImagesBydesignOrderId(design_order_id)
      let updateData = {
        request_id : result,
        from_status : 2,
      }
      let messagesUpdated = await ClientChat.updateRequestMessage(updateData, design_order_id)

      // Update job notes with new request_id
      const updateJobNoteData = {
        request_id: result,
        from_status: 1
      }
      await ClientChat.updateJobNoteMessage(updateJobNoteData, design_order_id)

      for(const imageData of requestMessagesImageData){
        const requestAttachmentData={
          request_id : result
        }
        let upload = await Request.updateRequestAttachmentsDeisgnOrderId(imageData?.image_id,design_order_id,requestAttachmentData)
      }
      if (!empty(uploaded)) {
        emitProgress('processing_attachments', 'Processing design order attachments...', 55);
        for (const row of uploaded) {
          let approvedImage = {}
          if(row?.file_type?.startsWith('image')){
            approvedImage.approved = 0
          }else{
            approvedImage.approved = 1
          }
          approvedImage.name = row.name || row.url;
          approvedImage.file_type = row.file_type;
          approvedImage.extension = row.extension;
          approvedImage.file_size = row.file_size || row.size;
          approvedImage.request_id = result;
          approvedImage.added_by = userId
          approvedImage.timestamp = moment().unix()
          let upload = await Request.addRequestAttachments(approvedImage)
          // ✅ Copy images from design_order (including edited pages); only convert PDF when no images exist
          const sourceDoAttachmentId = row.id ?? row.ID;
          const existingImages = sourceDoAttachmentId
            ? await getRequestAttachmentImagesByDocumentId(sourceDoAttachmentId, 'design_order')
            : [];
          if (existingImages && existingImages.length > 0) {
            await copyAttachmentImagesToNewDocument(sourceDoAttachmentId, 'design_order', upload, 'request');
          } else {
            // const isPdfFile =
            //   approvedImage.file_type === 'application/pdf' ||
            //   approvedImage.file_type === 'pdf' ||
            //   approvedImage.extension === 'pdf';
            // if (isPdfFile && upload) {
            //   try {
            //     await convertPdfToPngHelper(upload);
            //     console.log(`PDF conversion completed for document ID: ${upload}`);
            //   } catch (conversionError) {
            //     console.error(
            //       `Error converting PDF for document ${upload}:`,
            //       conversionError
            //     );
            //   }
            // }
          }

        }
      }
      let qbId = requestdata.estimate_id;
      console.log('qbId----',qbId);
      // if(qbId){
      //   emitProgress('fetching_avalara_tax_amount', `Fetching Avalara tax amount...`, 56);
      //   console.log("Starting 10-second delay...");
      //   await delay(15000); // Delay for 15 seconds (10,000 milliseconds)
      //   console.log("10-second delay complete.");
      //   taxAmountData = await fetchTaxAmountFromEstimateById(qbId);
      //   console.log('avalaraTaxAmount----',taxAmountData);
      // }

      let orderData = {
        total: (requestData?.total_cost || 0) + (taxAmountData || 0),
        grandTotal: (requestData?.total_cost || 0) + (taxAmountData || 0),
        tax_amount: taxAmountData || 0,
        tax_category: taxCategory || null,
        budget: 0,
        extra_discount: 0,
        request_id: result,
        timestamp: currentDate,
        sub_total: requestData?.total_cost || 0,
        tax: taxAmountData || 0,
        status: 1
      };
      emitProgress('creating_order', 'Creating customer order...', 60);
      // let resultData = await CustomerOrder.createCustomerOrder(orderData);
      let resultData;
      if (!isAlreadyConverted) {
        // Only create customer order for new conversions
        resultData = await CustomerOrder.createCustomerOrder(orderData);
        console.log('Created new customer order:', resultData);
      } else {
        // For updates, get existing customer order and add current design services total
        resultData = await CustomerOrder.getCustomerOrderRequestId(result);
        console.log('Using existing customer order before service total update:', resultData);

        if (resultData) {
          // Calculate total of current design order services
          const currentDesignServicesTotal = Array.isArray(selectedServices)
            ? selectedServices.reduce((sum, row) => {
                const rowTotal = Number(row?.total) || 0;
                return sum + rowTotal;
              }, 0)
            : 0;
          console.log('currentDesignServicesTotal===',currentDesignServicesTotal)
          // Safely read existing totals
          const existingSubTotal = Number(resultData.sub_total) || 0;
          const existingTotal = Number(resultData.total) || 0;
          const existingGrandTotal = Number(resultData.grandTotal || resultData.grand_total) || existingTotal;
              console.log('existingSubTotal===',existingSubTotal)
          const updatedOrderTotals = {
            sub_total: existingSubTotal + currentDesignServicesTotal,
            total: existingTotal + currentDesignServicesTotal,
            grandTotal: existingGrandTotal + currentDesignServicesTotal,
            timestamp: currentDate,
          };

          // Prefer id if present, otherwise fall back to resultData.request_id
          const existingOrderId = resultData.id || resultData.request_id;
          if (existingOrderId) {
            await CustomerOrder.updateCustomerOrder(existingOrderId, updatedOrderTotals);
            console.log('Updated existing customer order with new design services total:', updatedOrderTotals);
          }
        }
      }
      
      emitProgress('processing_services', 'Processing services and products...', 65);
      
      // Declare variables in outer scope for async function access
      let partnerData = null;
      let insertedstatus = null;
      let partnerIds = null;
      let subject = null;
      let email_content = null;
      
      // ✅ CRITICAL: Add request services (must complete before response)
      if (resultData) {
        // for (const row of selectedServices) {
        //   let requestProduct={};
        //   let rowQuantity = row.quantity ? row.quantity : 1;
        //   requestProduct['product_index'] = 0
        //   requestProduct['request_id'] = result
        //   requestProduct['labour_cost_service_id'] = row.labour_cost_service_id,
        //   requestProduct['recipe_id'] = null,
        //   requestProduct['product_id'] = null,
        //   requestProduct['qty'] = rowQuantity,
        //   requestProduct['unit_cost'] = row.unit_cost || row.total,
        //   requestProduct['markup'] = row?.markup || 0,
        //   requestProduct['total_cost'] = row.total
        //   requestProduct['rfp'] = 0
        //   requestProduct['map_service'] = 0
        //   requestProduct['jobcost_unit_cost'] = 0 //row.unit_cost
        //   requestProduct['jobcost_quantity'] = row?.jobcost_quantity ? row?.jobcost_quantity : 0
        //   requestProduct['jobcost_total_cost'] = row?.jobcost_total_cost ? row?.jobcost_total_cost : 0
        //   requestProduct['is_dos'] = 1
        //   requestProduct['makeAsTask'] = row?.makeAsTask ? 1 : 0
        //   requestProduct['makeInstallCard'] = row?.makeInstallCard ? 1 : 0
        //   let response = await Request.addRequestServices(requestProduct)
        // }
        if (!isAlreadyConverted) {
          // Add services for new conversions
          for (const row of selectedServices) {
            let requestProduct={};
            let rowQuantity = row.quantity ? row.quantity : 1;
            requestProduct['product_index'] = 0
            requestProduct['request_id'] = result
            requestProduct['labour_cost_service_id'] = row.labour_cost_service_id,
            requestProduct['recipe_id'] = null,
            requestProduct['product_id'] = null,
            requestProduct['qty'] = rowQuantity,
            requestProduct['unit_cost'] = row.unit_cost || row.total,
            requestProduct['markup'] = row?.markup || 0,
            requestProduct['total_cost'] = row.total
            requestProduct['rfp'] = 0
            requestProduct['map_service'] = 0
            requestProduct['jobcost_unit_cost'] = 0 //row.unit_cost
            requestProduct['jobcost_quantity'] = row?.jobcost_quantity ? row?.jobcost_quantity : 0
            requestProduct['jobcost_total_cost'] = row?.jobcost_total_cost ? row?.jobcost_total_cost : 0
            requestProduct['is_dos'] = 1
            requestProduct['makeAsTask'] = row?.makeAsTask ? 1 : 0
            requestProduct['makeInstallCard'] = row?.makeInstallCard ? 1 : 0
            let response = await Request.addRequestServices(requestProduct)
          }
          console.log('Added services to new request');
        } else {
          // For updates: Delete existing services that match design order services and add new ones
          console.log('Updating services for already converted design order');
          
          // Get all labour_cost_service_id values from selectedServices
          const labourCostServiceIds = selectedServices.map(service => service.labour_cost_service_id);
          console.log('Design order service IDs to delete:', labourCostServiceIds);
          
          // Delete existing services that match the design order services
          for (const serviceId of labourCostServiceIds) {
            const deleteResult = await Request.deleteRequestServicesByLabourCostServiceId(result, serviceId);
            console.log(`Deleted existing service with labour_cost_service_id: ${serviceId}`, deleteResult);
          }
          
          // Add new services from design order
          for (const row of selectedServices) {
            let requestProduct={};
            let rowQuantity = row.quantity ? row.quantity : 1;
            requestProduct['product_index'] = 0
            requestProduct['request_id'] = result
            requestProduct['labour_cost_service_id'] = row.labour_cost_service_id,
            requestProduct['recipe_id'] = null,
            requestProduct['product_id'] = null,
            requestProduct['qty'] = rowQuantity,
            requestProduct['unit_cost'] = row.unit_cost || row.total,
            requestProduct['markup'] = row?.markup || 0,
            requestProduct['total_cost'] = row.total
            requestProduct['rfp'] = 0
            requestProduct['map_service'] = 0
            requestProduct['jobcost_unit_cost'] = 0 //row.unit_cost
            requestProduct['jobcost_quantity'] = row?.jobcost_quantity ? row?.jobcost_quantity : 0
            requestProduct['jobcost_total_cost'] = row?.jobcost_total_cost ? row?.jobcost_total_cost : 0
            requestProduct['is_dos'] = 1
            requestProduct['makeAsTask'] = row?.makeAsTask ? 1 : 0
            requestProduct['makeInstallCard'] = row?.makeInstallCard ? 1 : 0
            let response = await Request.addRequestServices(requestProduct)
          }
          console.log('Updated services for existing request');
        }

        // ✅ CRITICAL: Add request partners (must complete before response)
        // Only add partners for new conversions (to avoid duplicates)
        if (!isAlreadyConverted) {
        partnerData = [values.clientData]
        console.log('partnerData===',partnerData)
        subject = `A new ${salesOrder ? 'sales order' : 'estimate'} awaiting your acceptance`;
        email_content = `You are invited to join new ${salesOrder ? 'sales order' : 'estimate'} by ` + userName;
        if (values.welcome_message) {
          subject = values.subject
          email_content = values.email_content
        }
        if (partnerData && partnerData.length > 0) {
          let client = {}
          for (let eachPartner of partnerData) {
            client['request_id'] = result,
            client['last_seen'] = currentDate,
            client['timestamp'] = currentDate,
            client['added_by'] = userId,
            client['partner_id'] = eachPartner.id
            client['accept_status'] = 1;
            client['status'] = 1
            insertedstatus = await RequestPartner.addRequestPartners(client);
          }
          if (insertedstatus) {
            console.log('partnerData===',partnerData)
            partnerIds = partnerData.map(eachClient => eachClient.id);
            let tempRequestPartnerArr = partnerData.map(eachClient => eachClient.id + ',' + eachClient.name);

            let requestdata = {}
            requestdata['customer_id'] = partnerData[0]['id'];
            requestdata['customer_name'] = partnerData[0]['name'];
            requestdata['customer_phone'] = partnerData[0]['phone'];
            requestdata['customer_address'] = partnerData[0]['address'];
            requestdata['timestamp'] = currentDate;
            let orderId = await CustomerOrder.getCustomerOrderRequestId(result);
            if (empty(orderId.customer_name)) {
              await CustomerOrder.updateCustomerOrder(orderId.id, requestdata);
            }
          }
        }
        }
      }
      
      // ✅ OPTIMIZATION: Send response immediately after critical operations
      emitProgress('completing_conversion', `Conversion to ${conversionType} completed successfully!`, 100);
      reply.status(200).send({ 
        status: 1, 
        message: `Design Order converted to ${conversionType} successfully`, 
        request_id: result 
      });

      // ✅ OPTIMIZATION: Handle all notifications/emails/SMS/tab counts async (non-blocking)
      // Only run if resultData exists (matching original behavior)
      if (resultData) {
        const handleNotificationsAsync = async () => {
          try {
          // Admin/Manager email and SMS notifications (parallelized)
      let adminMangerIds = await Admin.getAllAdminAndManagerUserIds();
          let allAdminToCheck = [...new Set([userId, ...adminMangerIds].filter(Boolean))];
          console.log('alluserssssssssarray',allAdminToCheck)
          const isQuotation = requestdata.quotation === 1;

          const emailSmsPromises = allAdminToCheck.map(async (userIdToCheck) => {
            try {
              console.log('useriddddd',userIdToCheck)
      const creator = await Admin.getUserNotificationAndPermissionSettings(userIdToCheck);
              if (!creator) return;

              const emailPromise = (async () => {
       if (creator.emailEnabled === 1 && creator.email) {
        if (isQuotation && creator?.pcb_new_estimate === 1 && creator?.pcb_new_estimate_email === 1) {
          const welcome = `Hello ${creator.name},`;
          const subject = `📝 New Estimate Created ( Design Order Converted ): ${requestdata.name}`;
          const email_content = `A new estimate has been created. Please review the details in your software.`;

          let message = newEstimateCreatedEmailTemplate({
            user: creator,
            requestName: requestdata.name,
            projectType: requestdata.project_type,
            total: orderData.total,
            baseUrl: BASE_URL,
            type: 'estimate',
            convertion: true,
          });

          message = message.replace('[emailContent]', email_content);
          message = message.replace('[CONTENT]', welcome);
          message = message.replace('[LOGIN_URL]', `${BASE_URL}`);

          const mailOptions = {
            to: creator.email,
            from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
            subject,
            text: subject,
            html: message,
          };

          try {
            await transporter.sendMail(mailOptions);
            console.log(`✅ Estimate email sent to: ${creator.email}`);
          } catch (err) {
            console.error(`❌ Error sending estimate email:`, err);
          }
        }

        if (!isQuotation && creator?.pcb_new_sales_order === 1 && creator?.pcb_new_sales_order_email === 1) {
          const welcome = `Hello ${creator.name},`;
                    const subject = `📦 New Sales Order Created ( Design Order Converted ): ${requestdata.name}`;
          const email_content = `A new sales order has been created. Please review the details in your software.`;

          let message = newEstimateCreatedEmailTemplate({
            user: creator,
            requestName: requestdata.name,
            projectType: requestdata.project_type,
            total: orderData.total,
            baseUrl: BASE_URL,
            type: 'sales_order',
            convertion: true,
          });

          message = message.replace('[emailContent]', email_content);
          message = message.replace('[CONTENT]', welcome);
          message = message.replace('[LOGIN_URL]', `${BASE_URL}`);

          const mailOptions = {
            to: creator.email,
            from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
            subject,
            text: subject,
            html: message,
          };

          try {
            await transporter.sendMail(mailOptions);
            console.log(`✅ Sales order email sent to: ${creator.email}`);
          } catch (err) {
            console.error(`❌ Error sending sales order email:`, err);
          }
        }
      }
              })();

              const smsPromise = (async () => {
      if (isQuotation && creator?.pcb_new_estimate === 1 && creator?.pcb_new_estimate_sms === 1) {
        const smsContent = 
          `📝 New Estimate Created ( Design Order Converted )!\n` +
          `📄 Name: "${requestdata.name}"\n` +
          `💰 Total: ₹${orderData.total}\n` +
          `🔗 View: ${BASE_URL}\n` +
          `— SignWise`;
            
        const recipientPhone = creator.phone || TWILIO_PHONE;
        const Phone_country_code = creator.phone_country_code || '';
        try {
                    await sendMessageToPhoneNumber(recipientPhone, smsContent, Phone_country_code);
          console.log(`✅ SMS sent to ${creator.name || 'Unknown'} (${recipientPhone}) for Estimate "${requestdata.name}"`);
        } catch (err) {
          console.error(`❌ Failed to send Estimate SMS to ${creator.name || 'Unknown'} (${recipientPhone}):`, err);
        }
      }
      
      if (!isQuotation && creator?.pcb_new_sales_order === 1 && creator?.pcb_new_sales_order_sms === 1) {
        const smsContent = 
                    `📦 New Sales Order Created ( Design Order Converted )!\n` +
          `📄 Name: "${requestdata.name}"\n` +
          `💰 Total: ₹${orderData.total}\n` +
          `🔗 View: ${BASE_URL}\n` +
          `— SignWise`;
      
        const recipientPhone = creator.phone || TWILIO_PHONE;
        const Phone_country_code = creator.phone_country_code || '';
        try {
                    await sendMessageToPhoneNumber(recipientPhone, smsContent, Phone_country_code);
          console.log(`✅ SMS sent to ${creator.name || 'Unknown'} (${recipientPhone}) for Sales Order "${requestdata.name}"`);
        } catch (err) {
          console.error(`❌ Failed to send Sales Order SMS to ${creator.name || 'Unknown'} (${recipientPhone}):`, err);
        }
      }
              })();

              await Promise.all([emailPromise, smsPromise]);
            } catch (err) {
              console.error(`❌ Error sending notifications to user ${userIdToCheck}:`, err);
            }
          });

          await Promise.all(emailSmsPromises);

          // Partner emails (parallelized)
          if (partnerData && partnerData.length > 0 && insertedstatus) {
            const partnerEmailPromises = partnerData.map(async (eachPartner) => {
              try {
              let welcome = "Hello " + eachPartner['company_name'] + ","
              sgMail.setApiKey(SENDGRID_API_KEY)
              let message = rfqEmail()
              message = message.replace('[emailContent]', email_content)
              message = message.replace('[CONTENT]', welcome)
              message = message.replace('[LOGIN_URL]', `${BASE_URL}`)

              const mailOptions = {
                  to: eachPartner.email, 
                  from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
                subject: subject,
                text: subject,
                html: message,
              }
              try {
                if(SEND_EMAIL){
                await transporter.sendMail(mailOptions);
                    console.log('✅ Partner email sent to:', eachPartner.email);
                }
              } catch (error) {
                  console.error('❌ Error sending partner email:', error);
                }
              } catch (error) {
                console.error('❌ Error processing partner email:', error);
              }
            });
            await Promise.all(partnerEmailPromises);
          }

          // System notifications (parallelized)
        if (!empty(partnerIds)) {
            let requestId = result;
          let request = await Request.getRequestById(requestId);
          let requestPartners = await RequestPartner.getRequestPartnerByIds({}, requestId)
          let reqActivityMsg = `🔄 Design Order "${request[0].name}" has been converted to ${salesOrder ? 'Sales Order' : 'Estimate'}`
          let notificationMsg = `The design order ${request[0].name} has been converted to ${salesOrder ? 'Sales Order' : 'Estimate'}.`;
            let notificationUrl = salesOrder ? `request/view/${requestId}` : `quick-bid/view/${requestId}`;

            // User notification
            await Admin.incrementField(userId, "noti_count", 1);
            await Admin.addNotification({
            user_id: userId,
            url: notificationUrl,
            timestamp: currentDate,
            message: notificationMsg,
            status: 1,
            from_id: userId,
            });

            // Partner notifications (parallelized)
          if (requestPartners.length > 0) {
              const partnerNotificationPromises = requestPartners.map(async (reqId) => {
                await Admin.incrementField(reqId['id'], "noti_count", 1);
                await Admin.addNotification({
                user_id: reqId['id'],
                url: notificationUrl,
                timestamp: currentDate,
                message: notificationMsg,
                status: 1,
                from_id: userId,
                });
              });
              await Promise.all(partnerNotificationPromises);
            }

            // Admin notifications (parallelized)
          let adminIds = await Admin.getAllAdminIds();
            const adminNotificationPromises = adminIds
              .filter(adminId => adminId !== userId)
              .map(async (adminId) => {
                await Admin.incrementField(adminId, "noti_count", 1);
                await Admin.addNotification({
                user_id: adminId,
                url: notificationUrl,
                timestamp: currentDate,
                message: notificationMsg,
                status: 1,
                from_id: userId,
                });
              });
            await Promise.all(adminNotificationPromises);

            // Activity log
            await RequestActivity.addRequestUpdate({
              request_id: requestId,
              activity: reqActivityMsg,
              added_by: userId,
              timestamp: currentDate,
              status: 1
            });
          }

          // Tab count updates (parallelized)
      // if (requestData?.status == 2) {
      //     console.log(`Removing design_order_id: ${design_order_id} from approved_count and approved_count_ids`);
    
      //     const allUserIds = await Admin.getAllUserIds();
      //       let usersToUpdate = [...new Set([userId, customerDatas?.user_id, ...allUserIds].filter(Boolean))];
    
      //     // const updateCounts = async (userId) => {
      //     //     try {
      //     //   if(!salesOrder){
      //     //     console.log(`Adding estimate`);
      //     //     await Request.EstimateTabIncrementField(userId, "open_count", 1, "open_count_ids", result, true);
      //     //   }else{
      //     //     console.log(`Adding sales order`);
      //     //     await Request.salesOrderIncrementField(userId, "open_count", 1, "open_count_ids", result, true);
      //     //   }
                
      //     //       // Update approved_count (with error handling)
      //     //       try {
      //     //     await Request.designCountIncrementField(userId, "approved_count", -1, "approved_count_ids", design_order_id, false);
      //     //       } catch (err) {
      //     //         console.warn(`⚠️ Failed to update approved_count for user ${userId}:`, err.message);
      //     //       }
                
      //     //       // Update job status specific counts (with error handling)
      //     //     if(values?.design_order_job_status == 1){
      //     //         try {
      //     //       await Request.designCountIncrementField(userId, "new_count", -1, "new_count_ids", design_order_id, false);
      //     //     console.log("design_order_job_status new updated new_count")
      //     //         } catch (err) {
      //     //           console.warn(`⚠️ Design order ID not found in new_count_ids for user ${userId}:`, err.message);
      //     //         }
      //     //     }else if(values?.design_order_job_status == 2){
      //     //         try {
      //     //       await Request.designCountIncrementField(userId, "in_progress_count", -1, "in_progress_count_ids", design_order_id, false);
      //     //       console.log("design_order_job_status new updated in_progress_count")
      //     //         } catch (err) {
      //     //           console.warn(`⚠️ Design order ID not found in in_progress_count_ids for user ${userId}:`, err.message);
      //     //         }
      //     //     }else if (values?.design_order_job_status == 3){
      //     //         try {
      //     //       await Request.designCountIncrementField(userId, "completed_count", -1, "completed_count_ids", design_order_id, false);
      //     //       console.log("design_order_job_status new updated completed_count")
      //     //         } catch (err) {
      //     //           console.warn(`⚠️ Design order ID not found in completed_count_ids for user ${userId}:`, err.message);
      //     //         }
      //     //       }
      //     //     } catch (err) {
      //     //       console.error(`❌ Error updating counts for user ${userId}:`, err.message);
      //     //     }
      //     //   };

      //       // Initialize tab counts if needed (parallelized)
      //     // if(!salesOrder){
      //     // await Promise.all(usersToUpdate.map(async (uid) => {
      //     //   const userOrderCount = await Request.getEstimateTabCountByUserId(uid);
      //     //   if (!userOrderCount || userOrderCount === "No record found") {
      //     //     const EstimateData = {
      //     //       open_count: 0,
      //     //       approved_count: 0,
      //     //       archive_count: 0,
      //     //       timestamp: moment().unix(),
      //     //       user_id: uid,
      //     //     };
      //     //     await Request.addEstimateTabCount(EstimateData);
      //     //   }
      //     //     }));
      //     //   } else {
      //     //   await Promise.all(usersToUpdate.map(async (uid) => {
      //     //     const userOrderCount = await Request.getSalesOrderByUserId(uid);
      //     //     if (!userOrderCount || userOrderCount === "No record found") {
      //     //       const salesOrderData = {
      //     //           open_count: 0,
      //     //           approved_count: 0,
      //     //           in_production_count: 0,
      //     //           installs_count: 0,
      //     //           pickup_count: 0,
      //     //           shipping_count: 0,
      //     //           invoicing_count: 0,
      //     //           completed_count: 0,
      //     //           timestamp: moment().unix(),
      //     //           user_id: uid,
      //     //       };
      //     //       await Request.addSalesOrderTabCount(salesOrderData);
      //     //     }
      //     //     }));
      //     // }
    
      //     // await Promise.all(usersToUpdate.map(user => updateCounts(user)));
      //     //   console.log("✅ Successfully updated approved_count and approved_count_ids after conversion.");
      //     }
        } catch (error) {
          console.error('Error in async notifications handler:', error);
        }
      };

      // ✅ OPTIMIZATION: Run non-critical operations asynchronously (fire and forget)
      handleNotificationsAsync().catch(error => {
        console.error('Error in async operations:', error);
      });
      }
    } else {
      emitProgress('conversion_failed', 'Failed to create request in database', 100);
      reply.status(500).send({ 
        status: 0, 
        message: "Failed to convert Design Order - Database error" 
      });
    }
  } catch (error) {
    console.log(error, 'Error in converting design order');
    emitProgress('conversion_error', `Conversion failed: ${error.message}`, 100);
    reply.status(500).send({ 
      status: 0, 
      message: "Failed to convert Design Order", 
      error: error.message 
    });
  }
}

const convertDesignToRequestEstimate = async (req, reply) => {
  try {
    const { design_order_id, userId, userType, userName, socketId } = req.body;
    let currentDate = moment().unix();

    // 📊 Progress tracking function
    const emitProgress = (step, message, progress = 0) => {
      console.log(`[CONVERSION-PROGRESS] 📊 Progress: ${step} - ${message} (${progress}%)`);

      const progressData = {
        socketId: socketId,
        userId: userId,
        step: step,
        message: message,
        progress: progress,
        timestamp: moment().unix(),
        domain: DOMAIN
      };

      // Emit to working socket server
      console.log(`[CONVERSION-PROGRESS] 📡 Emitting progress to working socket server...`);
      try {
        const workingSocket = ioClient(REST_API_IO, {
          path: '/socket.io',
          transports: ['websocket', 'polling'],
          secure: true,
          timeout: 5000
        });

        workingSocket.on('connect', () => {
          console.log(`[CONVERSION-PROGRESS] 🔌 Connected to working socket server`);
          console.log(`[CONVERSION-PROGRESS] 📊 Emitting progress data:`, progressData);

          // Emit progress data
          workingSocket.emit('request_progress', progressData);

          console.log(`[CONVERSION-PROGRESS] ✅ Successfully emitted progress to working socket server`);

          // Disconnect after emitting
          setTimeout(() => {
            workingSocket.disconnect();
          }, 1000);
        });

        workingSocket.on('connect_error', (error) => {
          console.error(`[CONVERSION-PROGRESS] ❌ Working socket connection error:`, error);
        });

      } catch (error) {
        console.error(`[CONVERSION-PROGRESS] ❌ Error creating working socket connection:`, error);
      }
    };

    // Initial progress
    console.log(`[CONVERSION-PROGRESS] 🚀 Starting design order conversion to Request Estimate for user:`, userId);
    emitProgress('start', `Initializing design order conversion to Request Estimate...`, 5);

    let requestData = await Request.getAllDesignOrders(design_order_id, userId, userType);
    const designOrderProducts = await Request.getDesignOrderProductsByDesignOrderId(design_order_id);
    let statusUpdate = await Request.updateDesignOrders(design_order_id, { status: 3, timestamp: currentDate });
    let uploaded = await Request.fetchAllDesignOrderAttachmentsForConvert(design_order_id);

    emitProgress('data_fetch', 'Fetching design order data and estimate service...', 15);

    requestData = requestData.data[0]

    // let estimateData
    // if(requestData?.request_id){
    //   estimateData = await Request.getRequestById(requestData?.request_id);
    // }
    // const isRequestEstimate = Array.isArray(estimateData) && estimateData[0]?.is_request_estimate === 1;
    // let isAlreadyConverted = requestData?.request_id && requestData.request_id !== null && !isRequestEstimate;
    let isAlreadyConverted = false;
    console.log('isAlreadyConverted===',isAlreadyConverted)
    let existingRequestId = requestData?.request_id;
    let partnerIds = requestData?.client;
    let customerDatas = await Client.getClientById(partnerIds);
    let values = requestData;

    values['clientData'] = customerDatas
    let submission_deadline = requestData.start_date
    let participation_ends = requestData.due_date
    let requestdata = {}
    requestdata['name'] = values.name,
    requestdata['description'] = values.description,
    requestdata['markup_type'] = 2,
    requestdata['services'] = null,
    requestdata['notes']=values.notes||"",
    requestdata['labour_cost_services'] = null,
    requestdata['labour_cost'] = requestData?.total_cost || 0,
    requestdata['quotation'] = 1
    requestdata['is_request_estimate'] = 1
    requestdata['project_type'] = null
    requestdata['project_category'] = null
    requestdata['design_order_id'] = design_order_id || null
    requestdata['community'] = values?.community ? values?.community : null;
    requestdata['submission_deadline'] = submission_deadline
    if (participation_ends !== (undefined || null)) {
      requestdata['participation_ends'] = participation_ends
    } else {
      requestdata['participation_ends'] = 0
    }
    requestdata['start_date'] = null
    if (values.welcome_message == true) {
      requestdata['welcome_message'] = values.welcome_message
      requestdata['subject'] = values.subject,
      requestdata['email_content'] = values.email_content
    } else {
      requestdata['welcome_message'] = false
      requestdata['subject'] = null,
      requestdata['email_content'] = null
    }
    requestdata['type'] = 2
    requestdata['status'] = 8
    requestdata['new_order'] = 0
    requestdata['min_payment_status'] = values?.clientData?.billing_type == 0 ? 1 : 0;
    requestdata['added_by'] = userId,
    requestdata['timestamp'] = currentDate
    requestdata['created_at'] = requestData?.created_at || currentDate
    requestdata['isFromDesignOrder'] = 1
    requestdata['client_approve'] = 0
    requestdata['quotation_status'] = 3
    if (!empty(uploaded)) {
      for (const row of uploaded) {
        if(row?.file_type?.startsWith('image/')){
          requestdata['thumbnail'] = row.url
        }
      }
    }
    requestdata['is_location'] = 0
    requestdata['is_service_location'] = 0
    requestdata['is_rfp_service_location'] = 0
    requestdata['is_rfp_product_location'] = 0
    let taxAmountData = 0;
    let taxCategory = null;
    let projectData = null
    if (values?.community) {
      try {
        const taxData = await Community.getCommunityById(values.community);
        projectData = taxData
        if (taxData?.tax_category && taxData?.tax_type !== undefined && taxData?.tax_value !== undefined) {
          taxCategory = taxData.tax_category;
        }
      } catch (error) {
        console.error("Error fetching tax data:", error);
      }
    }

    const estimateServiceId = 44
    const labourCostServices = await Product.getAllLabourCostServices();
    const estimateService = labourCostServices?.find((service) => Number(service?.service_type) === estimateServiceId);
    const selectedServices = [{
      labour_cost_service_id: estimateService?.id,
      name: estimateService?.name || 'Estimate Service',
      unit_cost: estimateService?.cost,
      total_cost: estimateService?.total_cost,
      markup: estimateService?.markup,
      service_type : estimateServiceId,
      quantity: 1,
      quickbookId: estimateService?.quickbookId,
      syncWithQuickbooks: estimateService?.syncWithQuickbooks
    }];
    const estimateOnlyServices = selectedServices.filter(
      (service) =>
        service?.service_type === estimateServiceId
    );
console.log('estimateOnlyServices',estimateOnlyServices)
    let servicesData =[]
          servicesData = estimateOnlyServices?.filter(item => item.syncWithQuickbooks === 0 || item.syncWithQuickbooks === null);

    const synceServicesData = estimateOnlyServices?.filter(item => item.syncWithQuickbooks === 1);      
    for(const service of synceServicesData){
      const serviceResultFromQb = await validateInventoryItemsInQbForPo(service,"quickbookId");
      if(serviceResultFromQb?.itemData){
        servicesData.push(serviceResultFromQb?.itemData);
        const updateData ={
          syncWithQuickbooks: 0,
          quickbookId: null
        }
        const updateService = await Product.updateServiceByQbId(service.quickbookId,updateData);
      }
    }
    const servicesNames = servicesData?.map(product => product.name).join(', ');
    let customerData = (values?.clientData?.syncWithQuickBook === null || values?.clientData?.syncWithQuickBook === 0) ? values.clientData : null;

    if(values?.clientData?.syncWithQuickBook === 1 && values?.clientData?.partnerQbId){
      const customerResultFromQb = await validateClientInQb(values?.clientData?.partnerQbId);
      if(customerResultFromQb?.removeQbid){
        customerData = values.clientData 
      }
      const updateData ={
        syncWithQuickBook: 0,
        partnerQbId: null
      }
      const updateCustomer = await Client.updateVendorByQbId(values?.clientData?.partnerQbId,updateData);
    }

    UniqueEstimateId =null
    doc_number = null
    let clientName = values?.clientData?.quickbookCustomerName;
   
    const ReasonWithQb = `This Estimate is not synced with QuickBooks because the following are not in QuickBooks.
                ${customerData? `Customer : ${customerData.name}` :''}
                ${servicesData.length > 0 ? `Services : ${servicesNames}` : ''}
                `;
const readToSyncWithQb = (!customerData && servicesData.length === 0);
    ////////////////////For Quickbook///////////////////////////////////////////////////////////
    const serviceItems = await Promise.all(
      estimateOnlyServices
        .map(async service => {
          let serviceId =service?.quickbookId 
          let quantity = service.quantity || 1;
          return {
            itemId: serviceId,
            name: service.name,
            amount: (Number(service.unit_cost) || 0) * quantity,
            quantity: quantity,
            unitPrice: Number(service.unit_cost) || 0,
          };
        })
    );

    if (!customerData) {
      try {
        emitProgress('quickbooks_sync', 'Syncing with QuickBooks...', 25);
        let customerId = values?.clientData?.partnerQbId;
        const requestData = {
          name: values.name,
          description: values.description,
          notes: values.notes || values.description,
          items:[...serviceItems],
          customerId: customerId,
          note: 'description note',
          discount: 0,
          jobType:"nil",
          taxAmount : taxAmountData || 0,
          clientData :values?.clientData || null,
          ProjectData :projectData || null,
          projectName:projectData?.name || null,
        };
        if(readToSyncWithQb){
        emitProgress('quickbooks_estimate', 'Creating estimate in QuickBooks...', 30);
        let quickbookEstimateId =  await addEstimateToQuickBooks(requestData)
        emitProgress('quickbooks_estimate_success', 'Estimate created successfully in QuickBooks!', 35);
        doc_number = quickbookEstimateId?.Estimate?.DocNumber || doc_number;
        }else{
          emitProgress('quickbooks_error', 'QuickBooks sync failed - continuing without sync...', 35);
        }
      } catch (error) {
        console.log("error while adding data to quickbook",error);
        emitProgress('quickbooks_error', 'QuickBooks sync failed - continuing without sync...', 35);
      }
    }
    if(UniqueEstimateId != null){
      requestdata['estimate_id'] =(UniqueEstimateId && doc_number) ? UniqueEstimateId : null
      requestdata['syncWithQuickbooks'] = 1
      requestdata['is_production_qb'] = 1
      requestdata['doc_number'] = doc_number
      requestdata['est_doc_number'] = doc_number
    }else if (selectedServices?.length === 0 && !customerData ) {
      requestdata['reasonWithQb'] = 'To sync with QuickBooks, please add at least one service.';
    }else{
      requestdata['reasonWithQb'] = ReasonWithQb
    }

    const estimators = await Project.getAllEstimateTeamMembers();
    const estimatorIds = (estimators || []).map((estimator) => estimator.id).filter(Boolean);
    requestdata['estimator_ids'] = estimatorIds.length > 0 ? estimatorIds.join(',') : null;

    emitProgress('saving_request', `Saving Request Estimate to database...`, 40);
    let result;
    if (isAlreadyConverted) {
      result = await Request.updateRequets(existingRequestId, requestdata);
      result = existingRequestId;
      const deleteAttachments = await Request.deleteRequestAttachments(existingRequestId);
      console.log('Updated existing request:', existingRequestId);
    } else {
      result = await Request.addRequest(requestdata)
      console.log('Created new request:', result);
    }
    if (result) {
      emitProgress('processing_messages', 'Processing messages and attachments...', 50);
      const requestMessagesImageData = await ClientChat.getRequestMessageImagesBydesignOrderId(design_order_id)
      let updateData = {
        request_id : result,
        from_status : 2,
      }
      let messagesUpdated = await ClientChat.updateRequestMessage(updateData, design_order_id)

      // Update job notes with new request_id
      const updateJobNoteData = {
        request_id: result,
        from_status: 1
      }
      await ClientChat.updateJobNoteMessage(updateJobNoteData, design_order_id)

      for(const imageData of requestMessagesImageData){
        const requestAttachmentData={
          request_id : result
        }
        let upload = await Request.updateRequestAttachmentsDeisgnOrderId(imageData?.image_id,design_order_id,requestAttachmentData)
      }
      if (!empty(uploaded)) {
        emitProgress('processing_attachments', 'Processing design order attachments...', 55);
        for (const row of uploaded) {
          let approvedImage = {}
          if(row?.file_type?.startsWith('image')){
            approvedImage.approved = 0
          }else{
            approvedImage.approved = 1
          }
          approvedImage.name = row.name || row.url;
          approvedImage.file_type = row.file_type;
          approvedImage.extension = row.extension;
          approvedImage.file_size = row.file_size || row.size;
          approvedImage.request_id = result;
          approvedImage.added_by = userId
          approvedImage.timestamp = moment().unix()
          let upload = await Request.addRequestAttachments(approvedImage)
          // ✅ Copy images from design_order (including edited pages); only convert PDF when no images exist
          const sourceDoAttachmentId = row.id ?? row.ID;
          const existingImages = sourceDoAttachmentId
            ? await getRequestAttachmentImagesByDocumentId(sourceDoAttachmentId, 'design_order')
            : [];
          if (existingImages && existingImages.length > 0) {
            await copyAttachmentImagesToNewDocument(sourceDoAttachmentId, 'design_order', upload, 'request');
          } else {
            // const isPdfFile =
            //   approvedImage.file_type === 'application/pdf' ||
            //   approvedImage.file_type === 'pdf' ||
            //   approvedImage.extension === 'pdf';
            // if (isPdfFile && upload) {
            //   try {
            //     await convertPdfToPngHelper(upload);
            //     console.log(`PDF conversion completed for document ID: ${upload}`);
            //   } catch (conversionError) {
            //     console.error(
            //       `Error converting PDF for document ${upload}:`,
            //       conversionError
            //     );
            //   }
            // }
          }

        }
      }

      const estimateServicesTotal = estimateOnlyServices.reduce(
        (sum, service) => sum + (Number(service.total_cost || 0) * Number(service.quantity || 1)),
        0
      );

      let qbId = requestdata.estimate_id;
      console.log('qbId----',qbId);
      // if(qbId){
      //   emitProgress('fetching_avalara_tax_amount', `Fetching Avalara tax amount...`, 56);
      //   console.log("Starting 10-second delay...");
      //   await delay(15000); // Delay for 15 seconds (10,000 milliseconds)
      //   console.log("10-second delay complete.");
      //   taxAmountData = await fetchTaxAmountFromEstimateById(qbId);
      //   console.log('avalaraTaxAmount----',taxAmountData);
      // }

      let orderData = {
        total: estimateServicesTotal + (taxAmountData || 0),
        grandTotal: estimateServicesTotal + (taxAmountData || 0),
        tax_amount: taxAmountData || 0,
        tax_category: taxCategory || null,
        budget: 0,
        extra_discount: 0,
        request_id: result,
        timestamp: currentDate,
        sub_total: estimateServicesTotal,
        tax: taxAmountData || 0,
        status: 1,
        customer_id: values?.clientData?.id || null,
        customer_name: values?.clientData?.name || null,
        customer_phone: values?.clientData?.phone || null,
        customer_address: values?.clientData?.address || null
      };
      emitProgress('creating_order', 'Creating customer order...', 60);
      let resultData;
      if (!isAlreadyConverted) {
        resultData = await CustomerOrder.createCustomerOrder(orderData);
        console.log('Created new customer order:', resultData);
      } else {
        resultData = await CustomerOrder.getCustomerOrderRequestId(result);
        console.log('Using existing customer order:', resultData);
      }

      emitProgress('processing_services', 'Processing estimate service and products...', 65);

      // ✅ CRITICAL: Add request services (estimate service only)
      if (resultData) {
        if (isAlreadyConverted) {
          const labourCostServiceIds = estimateOnlyServices.map(service => service.labour_cost_service_id);
          for (const serviceId of labourCostServiceIds) {
            const deleteResult = await Request.deleteRequestServicesByLabourCostServiceId(result, serviceId);
            console.log(`Deleted existing service with labour_cost_service_id: ${serviceId}`, deleteResult);
          }
        }

        for (const row of estimateOnlyServices) {
          let requestProduct={};
          let rowQuantity = row.quantity ? row.quantity : 1;
          requestProduct['product_index'] = 0
          requestProduct['service_name'] = row?.name
          requestProduct['request_id'] = result
          requestProduct['labour_cost_service_id'] = row.labour_cost_service_id,
          requestProduct['recipe_id'] = null,
          requestProduct['product_id'] = null,
          requestProduct['qty'] = rowQuantity,
          requestProduct['unit_cost'] = row.total_cost || row?.unit_cost || 0,
          requestProduct['markup'] = row?.markup || 0,
          requestProduct['total_cost'] = (Number(row.total_cost || row?.unit_cost || 0) * rowQuantity)
          requestProduct['rfp'] = 0
          requestProduct['map_service'] = 0
          requestProduct['jobcost_unit_cost'] = 0
          requestProduct['jobcost_quantity'] = 0
          requestProduct['jobcost_total_cost'] = 0
          requestProduct['is_dos'] = 0
          requestProduct['makeAsTask'] = 1
          requestProduct['makeInstallCard'] = 0
          let response = await Request.addRequestServices(requestProduct)
        }

        const orderId = typeof resultData === 'object' ? resultData?.id : resultData;
        if (isAlreadyConverted && orderId) {
          await CustomerOrderProduct.deletedCustomerOrderProduct(orderId);
        }

        if (designOrderProducts?.length > 0) {
          for (const product of designOrderProducts) {
            const requestProduct = {
              order_id: orderId,
              is_product_row: 1,
              product_index: product?.product_index || 0,
              product_id: null,
              product_name: product?.name || '',
              product_desc: product?.description || null,
              product_notes: null,
              install_notes: null,
              product_price: 0.00,
              product_retail_price: 0.00,
              product_markup: 0,
              product_qty: 1,
              location_quantity: 1,
              product_sub_total: 0,
              product_tax: 0,
              product_total: 0,
              discount: 0,
              adjustment: 0,
              adj_value_type: 1,
              install_qty: 1,
              install_adjustment: 0,
              install_adj_value_type: 1,
              timestamp: currentDate,
              status: 1,
              rfp: 0,
              material: 0,
              map_service: 0,
              isAdditionalProduct: 0,
              is_single_card: 0,
              jobcost_quantity: 0,
              jobcost_total_cost: 0,
              taken: 0
            };
            await CustomerOrderProduct.createCustomerOrder(requestProduct);
          }
        }
      }

      // ✅ Add request partners only on first conversion
      let requestPartnerId = null
      if (!isAlreadyConverted) {
        if (userType != 3) {
          const partnerData = values?.clientData ? [values.clientData] : []
          if (partnerData.length > 0) {
            for (let eachPartner of partnerData) {
              const client = {
                request_id: result,
                last_seen: currentDate,
                timestamp: currentDate,
                added_by: userId,
                partner_id: eachPartner.id,
                accept_status: 1,
                status: 1,
              }
              await RequestPartner.addRequestPartners(client)
            }
          }
        } else {
          const clientDetails = await Client.getClientByUserId(userId);
          if (clientDetails?.id) {
            const client = {
              request_id: result,
              last_seen: currentDate,
              timestamp: currentDate,
              added_by: userId,
              partner_id: clientDetails.id,
              accept_status: 1,
              status: 1,
            }
            await RequestPartner.addRequestPartners(client)
          }
        }
        const partnerIds = await RequestPartner.fetchPartners(result);
        requestPartnerId = partnerIds?.[0] || null;
      console.log('requestPartnerId1',requestPartnerId)
      } else {
        const partnerIds = await RequestPartner.fetchPartners(result);
        requestPartnerId = partnerIds?.[0] || null;
        console.log('requestPartnerId2',requestPartnerId)
      }

      // ✅ OPTIMIZATION: Send response immediately after critical operations
      emitProgress('completing_conversion', `Conversion to Request Estimate completed successfully!`, 100);
      reply.status(200).send({ 
        status: 1, 
        message: `Design Order converted to Request Estimate successfully`, 
        request_id: result,
        partner_id: requestPartnerId
      });

      // ✅ OPTIMIZATION: Handle all notifications/emails/SMS/tab counts async (non-blocking)
      if (resultData) {
        const handleNotificationsAsync = async () => {
          try {
            let adminMangerIds = await Admin.getAllAdminAndManagerUserIds();
            let allAdminToCheck = [...new Set([userId, ...adminMangerIds].filter(Boolean))];
            const isQuotation = true;

            const emailSmsPromises = allAdminToCheck.map(async (userIdToCheck) => {
              try {
                const creator = await Admin.getUserNotificationAndPermissionSettings(userIdToCheck);
                if (!creator) return;

                const emailPromise = (async () => {
                  if (creator.emailEnabled === 1 && creator.email) {
                    if (isQuotation && creator?.pcb_new_estimate === 1 && creator?.pcb_new_estimate_email === 1) {
                      const welcome = `Hello ${creator.name},`;
                      const subject = `📝 New Estimate Created ( Design Order Converted ): ${requestdata.name}`;
                      const email_content = `A new estimate has been created. Please review the details in your software.`;

                      let message = newEstimateCreatedEmailTemplate({
                        user: creator,
                        requestName: requestdata.name,
                        projectType: requestdata.project_type,
                        total: orderData.total,
                        baseUrl: BASE_URL,
                        type: 'estimate',
                        convertion: true,
                      });

                      message = message.replace('[emailContent]', email_content);
                      message = message.replace('[CONTENT]', welcome);
                      message = message.replace('[LOGIN_URL]', `${BASE_URL}`);

                      const mailOptions = {
                        to: creator.email,
                        from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
                        subject,
                        text: subject,
                        html: message,
                      };

                      try {
                        await transporter.sendMail(mailOptions);
                        console.log(`✅ Estimate email sent to: ${creator.email}`);
                      } catch (err) {
                        console.error(`❌ Error sending estimate email:`, err);
                      }
                    }
                  }
                })();

                const smsPromise = (async () => {
                  if (isQuotation && creator?.pcb_new_estimate === 1 && creator?.pcb_new_estimate_sms === 1) {
                    const smsContent = 
                      `📝 New Estimate Created ( Design Order Converted )!\n` +
                      `📄 Name: "${requestdata.name}"\n` +
                      `💰 Total: ₹${orderData.total}\n` +
                      `🔗 View: ${BASE_URL}\n` +
                      `— SignWise`;

                    const recipientPhone = creator.phone || TWILIO_PHONE;
                    const Phone_country_code = creator.phone_country_code || '';
                    try {
                      await sendMessageToPhoneNumber(recipientPhone, smsContent, Phone_country_code);
                      console.log(`✅ SMS sent to ${creator.name || 'Unknown'} (${recipientPhone}) for Estimate "${requestdata.name}"`);
                    } catch (err) {
                      console.error(`❌ Failed to send Estimate SMS to ${creator.name || 'Unknown'} (${recipientPhone}):`, err);
                    }
                  }
                })();

                await Promise.all([emailPromise, smsPromise]);
              } catch (err) {
                console.error(`❌ Error sending notifications to user ${userIdToCheck}:`, err);
              }
            });

            await Promise.all(emailSmsPromises);

            if (!empty(partnerIds)) {
              let requestId = result;
              let request = await Request.getRequestById(requestId);
              let requestPartners = await RequestPartner.getRequestPartnerByIds({}, requestId)
              let reqActivityMsg = `🔄 Design Order "${request[0].name}" has been converted to ${request[0].is_request_estimate == 1 ? 'Request Estimate' : 'Estimate'}`
              let notificationMsg = `The design order ${request[0].name} has been converted to ${request[0].is_request_estimate == 1 ? 'Request Estimate' : 'Estimate'}.`;
              let notificationUrl = `quick-bid/view/${requestId}`;

              await Admin.incrementField(userId, "noti_count", 1);
              await Admin.addNotification({
                user_id: userId,
                url: notificationUrl,
                timestamp: currentDate,
                message: notificationMsg,
                status: 1,
                from_id: userId,
              });

              if (requestPartners.length > 0) {
                const partnerNotificationPromises = requestPartners.map(async (reqId) => {
                  await Admin.incrementField(reqId['id'], "noti_count", 1);
                  await Admin.addNotification({
                    user_id: reqId['id'],
                    url: notificationUrl,
                    timestamp: currentDate,
                    message: notificationMsg,
                    status: 1,
                    from_id: userId,
                  });
                });
                await Promise.all(partnerNotificationPromises);
              }

              let adminIds = await Admin.getAllAdminIds();
              const adminNotificationPromises = adminIds
                .filter(adminId => adminId !== userId)
                .map(async (adminId) => {
                  await Admin.incrementField(adminId, "noti_count", 1);
                  await Admin.addNotification({
                    user_id: adminId,
                    url: notificationUrl,
                    timestamp: currentDate,
                    message: notificationMsg,
                    status: 1,
                    from_id: userId,
                  });
                });
              await Promise.all(adminNotificationPromises);

              await RequestActivity.addRequestUpdate({
                request_id: requestId,
                activity: reqActivityMsg,
                added_by: userId,
                timestamp: currentDate,
                status: 1
              });
            }
          } catch (error) {
            console.error('Error in async notifications handler:', error);
          }
        };

        handleNotificationsAsync().catch(error => {
          console.error('Error in async operations:', error);
        });
      }
    } else {
      emitProgress('conversion_failed', 'Failed to create request in database', 100);
      reply.status(500).send({ 
        status: 0, 
        message: "Failed to convert Design Order - Database error" 
      });
    }
  } catch (error) {
    console.log(error, 'Error in converting design order');
    emitProgress('conversion_error', `Conversion failed: ${error.message}`, 100);
    reply.status(500).send({ 
      status: 0, 
      message: "Failed to convert Design Order", 
      error: error.message 
    });
  }
}

const convertDesignToSplitEstimate = async (req, reply) => {
  try {
    const { design_order_id, userId, userType, userName, socketId ,currentFacilityId} = req.body;
    let currentDate = moment().unix();

    // 📊 Progress tracking function
    const emitProgress = (step, message, progress = 0) => {
      console.log(`[SPLIT-ESTIMATE-CONVERSION] 📊 Progress: ${step} - ${message} (${progress}%)`);
      
      const progressData = {
        socketId: socketId,
        userId: userId,
        step: step,
        message: message,
        progress: progress,
        timestamp: moment().unix(),
        domain: DOMAIN
      };
      
      // Emit to working socket server
      console.log(`[SPLIT-ESTIMATE-CONVERSION] 📡 Emitting progress to working socket server...`);
      try {
        const workingSocket = ioClient(REST_API_IO, {
          path: '/socket.io',
          transports: ['websocket', 'polling'],
          secure: true,
          timeout: 5000
        });
        
        workingSocket.on('connect', () => {
          console.log(`[SPLIT-ESTIMATE-CONVERSION] 🔌 Connected to working socket server`);
          console.log(`[SPLIT-ESTIMATE-CONVERSION] 📊 Emitting progress data:`, progressData);
          
          // Emit progress data
          workingSocket.emit('request_progress', progressData);
          
          console.log(`[SPLIT-ESTIMATE-CONVERSION] ✅ Successfully emitted progress to working socket server`);
          
          // Disconnect after emitting
          setTimeout(() => {
            workingSocket.disconnect();
          }, 1000);
        });
        
        workingSocket.on('connect_error', (error) => {
          console.error(`[SPLIT-ESTIMATE-CONVERSION] ❌ Working socket connection error:`, error);
        });
        
      } catch (error) {
        console.error(`[SPLIT-ESTIMATE-CONVERSION] ❌ Error creating working socket connection:`, error);
      }
    };

    // Initial progress
    console.log(`[SPLIT-ESTIMATE-CONVERSION] 🚀 Starting design order conversion to Split Estimate for user:`, userId);
    emitProgress('start', 'Initializing design order conversion to Split Estimate...', 5);

    // Fetch design order data
    let requestData = await Request.getAllDesignOrders(design_order_id, userId, userType);
    const selectedServices = await Request.getDesignOrderServicesByDesignOrderId(design_order_id);
    let uploaded = await Request.fetchAllDesignOrderAttachmentsForConvert(design_order_id);

    emitProgress('data_fetch', 'Fetching design order data and attachments...', 15);

    requestData = requestData.data[0];
    let partnerIds = requestData?.client;
    let customerDatas = await Client.getClientById(partnerIds);
    let values = requestData;
    
    console.log('requestData===', requestData);
    console.log('selectedServices===', selectedServices);
    console.log('uploaded attachments===', uploaded);

    values['clientData'] = customerDatas;

    // Filter approved attachments
    const approvedAttachments = uploaded?.filter(attachment => attachment.isDesign === 1) || [];
    console.log('approvedAttachments===', approvedAttachments);

    emitProgress('creating_split_estimate', 'Creating split estimate...', 25);

    // Create split estimate data
    const uniqueId = Number(`${Date.now()}${Math.floor(100 + Math.random() * 900)}`);
    const splitEstimateData = {
      id: uniqueId,
      name: values.name,
      description: values.description || '',
      origin_facility_id: currentFacilityId || null, // Will be set from current facility
      to_facility_id: null, // Will be set from current facility
      status: 1,
      created_at: currentDate,
      timestamp: currentDate,
      added_by: userId,
      total_cost: 0,
      reason: null,
      isFromDesignOrder: design_order_id // Store the design order ID
    };

    // Add split estimate to database
    const splitEstimateId = await SplitEstimate.addSplitEstimate(splitEstimateData);
    let splitEstimateUpdatesData = {
      split_estimate_id: splitEstimateId,
      partner_id: customerDatas?.id || 0,
      partner_name: customerDatas?.name || '',
      activity: `Split Estimate '${values?.name}' has been converted from Design Order.`,
      added_by: userId,
      timestamp: currentDate,
    }
    await RequestUpdates.addSplitEstimateUpdates(splitEstimateUpdatesData);
    console.log('splitEstimateId===', splitEstimateId);
    if (!splitEstimateId) {
      emitProgress('error', 'Failed to create split estimate', 100);
      return reply.status(500).send({ 
        status: 0, 
        message: "Failed to create split estimate" 
      });
    }

    emitProgress('processing_attachments', 'Processing approved attachments...', 40);

    // Add approved attachments to split estimate
    if (approvedAttachments.length > 0) {
      for (const attachment of approvedAttachments) {
        const attachmentData = {
          url: attachment.name || attachment.url,
          file_type: attachment.file_type,
          split_estimate_id: splitEstimateId,
          status: 1,
          timestamp: currentDate
        };
        console.log('attachmentData===', attachmentData);
        const attachmentResult = await SplitEstimate.addSplitEstimateAttachments(attachmentData);
        console.log('attachmentResult===', attachmentResult);
        const newAttachmentId = attachmentResult?.id ?? attachmentResult?.dataValues?.id;
        if (newAttachmentId) {
          // When converting from design order: carry existing edited PDF images from design order attachment instead of re-converting
          const designOrderAttachmentId = attachment.id ?? attachment.ID;
          const existingImages = designOrderAttachmentId
            ? await getRequestAttachmentImagesByDocumentId(designOrderAttachmentId, 'design_order')
            : [];
          if (existingImages && existingImages.length > 0) {
            const timestamp = moment().unix();
            for (const img of existingImages) {
              await addRequestAttachmentImage({
                document_id: newAttachmentId,
                document_source: 'split_estimate',
                image_url: img.image_url,
                page_number: img.page_number,
                file_size: img.file_size != null ? img.file_size : 0,
                timestamp: timestamp,
                status: 1
              });
            }
            console.log(`[SPLIT-ESTIMATE-CONVERSION] Copied ${existingImages.length} edited PDF images to split estimate attachment ${newAttachmentId} (from design order attachment ${designOrderAttachmentId})`);
          } else {
            // No existing images: convert PDF to PNG (including existing PDFs added before conversion in design order)
            // const isPdfFile =
            //   (attachment.file_type === 'application/pdf' || attachment.file_type === 'pdf') ||
            //   (attachment.extension && String(attachment.extension).toLowerCase() === 'pdf') ||
            //   (attachment.name && String(attachment.name).toLowerCase().endsWith('.pdf')) ||
            //   (attachment.url && String(attachment.url).toLowerCase().endsWith('.pdf'));
            // if (isPdfFile) {
            //   try {
            //     await convertPdfToPngHelper(newAttachmentId, SplitEstimate.getSplitEstimateAttachmentByIdForConversion, 'split_estimate');
            //     console.log(`[SPLIT-ESTIMATE-CONVERSION] PDF conversion completed for split estimate attachment ID: ${newAttachmentId}`);
            //   } catch (conversionError) {
            //     console.error(`[SPLIT-ESTIMATE-CONVERSION] Error converting PDF for split estimate attachment ${newAttachmentId}:`, conversionError);
            //   }
            // }
          }
        }
      }
    }

    emitProgress('processing_services', 'Processing design services...', 50);

    // Add design services to split estimate services
    if (selectedServices && selectedServices.length > 0) {
      console.log(`Adding ${selectedServices.length} design services to split estimate ${splitEstimateId}`);
      
      for (const service of selectedServices) {
        try {
          const serviceData = {
            labour_cost_service_id: service.labour_cost_service_id,
            name: service.name,
            quantity: service.quantity || 0,
            total: service.total || 0,
            unit_cost: service.unit_cost || 0,
            taken: service.taken || 0,
            job_cost: service.job_cost || 0,
            jobcost_quantity: service.jobcost_quantity || 0.00,
            jobcost_total_cost: service.jobcost_total_cost || 0.00,
            split_estimate_id: splitEstimateId,
            complete: service.complete || 0,
            status: service.status || 1
          };
          
          console.log('serviceData===', serviceData);
          const serviceResult = await SplitEstimate.addSplitEstimateServices(serviceData);
          let splitEstimateServicesUpdatesData = {
            split_estimate_id: splitEstimateId,
            partner_id: customerDatas?.id || 0,
            partner_name: customerDatas?.name || '',
            activity: `Split Estimate "${values?.name}" has been created`,
            added_by: userId,
            timestamp: currentDate,
          }
          await RequestUpdates.addSplitEstimateUpdates(splitEstimateServicesUpdatesData);
          console.log('serviceResult===', serviceResult);
          
        } catch (serviceError) {
          console.log(`Error adding service to split estimate:`, serviceError);
          // Continue with other services even if one fails
        }
      }
    }

    emitProgress('updating_design_order', 'Updating design order status...', 70);

    // ✅ CRITICAL: Update design order status to completed (status: 3)
    let statusUpdate = await Request.updateDesignOrders(design_order_id, { 
      status: 3, 
      timestamp: currentDate 
    });

    // ✅ OPTIMIZATION: Send response immediately after critical operations
    emitProgress('complete', 'Split estimate conversion completed successfully!', 100);
    reply.status(200).send({ 
      status: 1, 
      message: "Design Order converted to Split Estimate successfully", 
      split_estimate_id: splitEstimateId 
    });

    // ✅ OPTIMIZATION: Handle all notifications/emails/SMS async (non-blocking)
    const handleNotificationsAsync = async () => {
      try {
    emitProgress('sending_notifications', 'Sending notifications...', 85);

        // Send notifications to admins and managers (parallelized)
    let adminMangerIds = await Admin.getAllAdminAndManagerUserIds();
    let allAdminToCheck = [...new Set([userId, ...adminMangerIds].filter(Boolean))];
    
        const emailSmsPromises = allAdminToCheck.map(async (userIdToCheck) => {
          try {
      const creator = await Admin.getUserNotificationAndPermissionSettings(userIdToCheck);
            if (!creator) return;

            const emailPromise = (async () => {
      if (creator.emailEnabled === 1 && creator.email) {
        const welcome = `Hello ${creator.name},`;
        const subject = `📊 New Split Estimate Created (Design Order Converted): ${values.name}`;
        const email_content = `A new split estimate has been created from a design order. Please review the details in your software.`;

        const mailOptions = {
          to: creator.email,
          from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
          subject,
          text: subject,
          html: `<p>${email_content}</p><p>Split Estimate ID: ${splitEstimateId}</p>`,
        };

        try {
          await transporter.sendMail(mailOptions);
          console.log(`✅ Split estimate email sent to: ${creator.email}`);
        } catch (err) {
          console.error(`❌ Error sending split estimate email:`, err);
        }
      }
            })();

            const smsPromise = (async () => {
      if (creator.smsEnabled === 1 && creator.phone) {
        const smsContent = 
          `📊 New Split Estimate Created!\n` +
          `📄 Name: "${values.name}"\n` +
          `🆔 ID: ${splitEstimateId}\n` +
          `🔗 View: ${BASE_URL}\n` +
          `— SignWise`;
          
        const recipientPhone = creator.phone || TWILIO_PHONE;
        const Phone_country_code = creator.phone_country_code || '';
        try {
          await sendMessageToPhoneNumber(recipientPhone, smsContent, Phone_country_code);
          console.log(`✅ SMS sent to ${creator.name || 'Unknown'} (${recipientPhone}) for Split Estimate "${values.name}"`);
        } catch (err) {
          console.error(`❌ Failed to send Split Estimate SMS to ${creator.name || 'Unknown'} (${recipientPhone}):`, err);
        }
      }
            })();

            await Promise.all([emailPromise, smsPromise]);
          } catch (err) {
            console.error(`❌ Error sending notifications to user ${userIdToCheck}:`, err);
          }
        });

        await Promise.all(emailSmsPromises);
      } catch (error) {
        console.error('Error in async notifications handler:', error);
      }
    };

    // ✅ OPTIMIZATION: Run non-critical operations asynchronously (fire and forget)
    handleNotificationsAsync().catch(error => {
      console.error('Error in async operations:', error);
    });

  } catch (error) {
    console.log(error, 'Error in converting design order to split estimate');
    emitProgress('conversion_error', `Conversion failed: ${error.message}`, 100);
    reply.status(500).send({ 
      status: 0, 
      message: "Failed to convert Design Order to Split Estimate", 
      error: error.message 
    });
  }
}

const fetchLatestInvoiceData = async(invoiceId)=>{
  let token = await getNewAccessToken()

  const url = PRODUCTION
  ? `https://quickbooks.api.intuit.com/v3/company/${realmId}/invoice/${invoiceId}?minorversion=65`
  : `https://sandbox-quickbooks.api.intuit.com/v3/company/${realmId}/invoice/${invoiceId}?minorversion=65`;
  try {
    const response = await axios.get(url, {
      headers: {
        Authorization: `Bearer ${token}`,
        Accept: 'application/json'
      },
      timeout: 10000 
    });
    // console.log("Fetched latest estimate:", response.data);
    return response.data;
  } catch (error) {
    if (error.response) {
      // Server responded with a status other than 2xx
      console.error("Error Response Data:", JSON.stringify(error.response.data, null, 2));
      console.error("Error Response Status:", error.response.status);
      console.error("Error Response Headers:", JSON.stringify(error.response.headers, null, 2));
    } else if (error.request) {
      console.error("No response received:", error.request);
    } else {
      console.error("Error Message:", error.message);
    }
  }
}

// const updatePaymentFromQb = async(req,res)=>{
//   try {
//     const {userId,allId,company_name,username,user_role}=req.body
//     let id =349
    
//     let currentDate = moment().unix()
//     let date = moment().format('MM-DD-YYYY');
//     let result =await Request.findRequestAndPaymentById(id)

//     if(result){
//       let invoiceId = result.estimate_id
//       let totalReqAmount = Number(result.totalCustomerOrders)
//       let totalPaidAmount = parseFloat(result.totalPaymentAmount).toFixed(2)
//       let balanceReqAmount = parseFloat(totalReqAmount - totalPaidAmount).toFixed(2)
      
//      let latestInvoiceData = await fetchLatestInvoiceData(invoiceId)
//      let qbBalanceAmount = latestInvoiceData?.Invoice?.Balance
//     //  let qbPaidAmount =(totalReqAmount-qbBalanceAmount) - totalPaidAmount
//     const qbPaidAmount = parseFloat((totalReqAmount - qbBalanceAmount) - totalPaidAmount).toFixed(2);

//      let percentageOfTotalAmount = ((qbPaidAmount / totalReqAmount) * 100).toFixed(2);
//      console.log("latestInvoiceDatalatestInvoiceData",balanceReqAmount,qbBalanceAmount,qbPaidAmount,totalPaidAmount)
   
//      let request = await Request.getRequestById(id);
//     console.log("resultresultresult result",result)
//     console.log("resultresultresult request",request)


//      let message = `
//      <p>Dear ${username},</p>
   
//      <p>We are pleased to inform you that a payment deposit has been made towards the project. Below are the details of the transaction:</p>
     
//      <table style="border-collapse: collapse; width: 100%; margin-top: 10px;">
//        <tr>
//          <td style="padding: 8px; border: 1px solid #ddd;"><strong>Client Name:</strong></td>
//          <td style="padding: 8px; border: 1px solid #ddd;">${request[0].customer_name}</td>
//        </tr>
//        <tr>
//          <td style="padding: 8px; border: 1px solid #ddd;"><strong>Invoice Number:</strong></td>
//          <td style="padding: 8px; border: 1px solid #ddd;">#${result?.doc_number}</td>
//        </tr>
//        <tr>
//          <td style="padding: 8px; border: 1px solid #ddd;"><strong>Project Name:</strong></td>
//          <td style="padding: 8px; border: 1px solid #ddd;">${result.name}</td>
//        </tr>
//        <tr>
//          <td style="padding: 8px; border: 1px solid #ddd;"><strong>Deposit Amount:</strong></td>
//          <td style="padding: 8px; border: 1px solid #ddd;">$ ${qbPaidAmount}</td>
//        </tr>
//        <tr>
//          <td style="padding: 8px; border: 1px solid #ddd;"><strong>Percentage of Total Amount:</strong></td>
//          <td style="padding: 8px; border: 1px solid #ddd;">${percentageOfTotalAmount}%</td>
//        </tr>
//        <tr>
//          <td style="padding: 8px; border: 1px solid #ddd;"><strong>Remaining Balance:</strong></td>
//          <td style="padding: 8px; border: 1px solid #ddd;">${balanceReqAmount}</td>
//        </tr>
//        <tr>
//          <td style="padding: 8px; border: 1px solid #ddd;"><strong>Payment Date:</strong></td>
//          <td style="padding: 8px; border: 1px solid #ddd;">${date}</td>
//        </tr>
//      </table>
   
//      <p style="margin-top: 20px;">Kindly verify the payment details and update the project records accordingly. If you have any questions or need further assistance, feel free to reach out.</p>
   
//      <p>Best regards,<br>
//      ${username}<br>
//      ${user_role}<br>
//      ${company_name}</p>
//      `;
//      let subject = 'Payment Deposit Notification for Project Invoice';

//      const mailOptions = {
//       to:  'ramdharvish@skyniche.com' , 
//       from: '"SignWise Solutions" <noreply@signwisesolutions.com>', 
//       subject: subject,
//       text: subject,
//       html: message,
//     }
  
//     try {
//       await transporter.sendMail(mailOptions);
//       console.log('Email sent');
      
//     } catch (error) {
//       console.error('##########Err', error);
     
//     }

//      if(balanceReqAmount !== qbBalanceAmount){
//       let data = {}
//       data.request_id = id
//       data.amount = qbPaidAmount
     
//       data.added_by = userId
//       data.timestamp = moment().unix();
//       data.paymentMode = "cash"
      
//       let notificationMsg = `A deposite of ${data.amount} has been made on invoice number #${result?.doc_number}`;
//       let notificationUrl = "official-request/view/${request_id}";
//       const incrementResult_User = await Admin.incrementField(
//         userId,
//         "noti_count",
//         1
//       );

//       let notificationData_user = {
//         user_id: userId,
//         url: notificationUrl,
//         timestamp: currentDate,
//         message: notificationMsg,
//         status: 1,
//         from_id: userId,
//       };
      

//       if(qbBalanceAmount == 0 &&(balanceReqAmount ==totalReqAmount)){
//         data.title = "Full payment"
//         let addPayment = await Request.addRequestPayment(data);

//         let reqData={
//           min_payment_status :1
//        }
//          let result = await Request.editRequestTable(id,reqData)

//          const addNotificationResult_user = await Admin.addNotification(
//           notificationData_user
//         );

//       }else{
//         data.title = "Partial payment"
//         let addPayment = await Request.addRequestPayment(data);
      
//       /////update req table min_payment_status
//       let request = await Request.getRequestById(id);
//       let total_paid_amount =await Request.findTotalPaidAmount(id)
//       let percentage =request[0]?.billing_type
//       let total_amount = request[0]?.total
   
//       let minimum_amount = parseFloat((total_amount * percentage) /100).toFixed(2)
  
//     if((Number(total_paid_amount || 0) + Number(data.amount || 0))  >=minimum_amount) {
//        let reqData={
//        min_payment_status :1
//     }
//       let result = await Request.editRequestTable(id,reqData)
//     }
//     //////////////////

//     const addNotificationResult_user = await Admin.addNotification(
//       notificationData_user
//     );

//       }
//      }else{
//       res.send({message:"all are up to date"})
//      }
    
//     }else{
//       console.log('not getting data')
//     }
//   } catch (error) {
//     res.send({ status: 500, message: "Server error", error })
//   }
// }

const updatePaymentFromQb = async (req, res) => {
  try {
    const { userId, allId,admin_email,company_name,username,user_role } = req.body;
    // const { userId,admin_email,company_name,username,user_role } = req.body;  //for testing single req

  // let allId=[561]  //for testing single req

    // Validate input
    if (!userId || !Array.isArray(allId)) {
      return res.status(400).send({
        status: 400,
        message: "Invalid input. 'userId' and 'allId' are required.",
      });
    }

    console.log('Received allId:', allId);

    // Process each ID
    const results = await Promise.all(
      allId.map((id) => processSingleRequest(id, userId,admin_email,company_name,username,user_role))
    );
    
    // Categorize results
    const successResults = results.filter((result) => result.status === 'success');
    const upToDateResults = results.filter((result) => result.status === 'up_to_date');
    const errorResults = results.filter((result) => result.status === 'error');

    // Send response
    return res.status(200).send({
      status: 200,
      message: 'Processing completed for all IDs.',
      details: {
        total: allId.length,
        success: successResults.length,
        up_to_date: upToDateResults.length,
        errors: errorResults.length,
        results, // Include detailed results
      },
    });
  } catch (error) {
    console.error('Server Error:', error);
    return res.status(500).send({
      status: 500,
      message: 'Server error',
      error: error.message || 'An error occurred',
    });
  }
};


const processSingleRequest = async (id, userId, admin_email,company_name,username,user_role) => {
  try {
    const currentDate = moment().unix();
    let date = moment().format('MM-DD-YYYY');
    const result = await Request.findRequestAndPaymentById(id);

    if (!result) {
      return { id, status: 'not_found', message: 'No data found for the given ID.' };
    }

    const invoiceId = result.estimate_id;
    const totalReqAmount = Number(result.totalCustomerOrders);
    const totalPaidAmount = parseFloat(result.totalPaymentAmount).toFixed(2);
    const balanceReqAmount = parseFloat(totalReqAmount - totalPaidAmount).toFixed(2);

    const latestInvoiceData = await fetchLatestInvoiceData(invoiceId);
    console.log('latestInvoiceData',latestInvoiceData)
    const qbBalanceAmount = parseFloat(latestInvoiceData?.Invoice?.Balance).toFixed(2);
    const qbPaidAmount = parseFloat((totalReqAmount - qbBalanceAmount) - totalPaidAmount).toFixed(2);
    let percentageOfbalanceAmt = parseFloat((qbBalanceAmount / totalReqAmount) * 100).toFixed(2);
    let percentageOfTotalAmount = parseFloat(100 - Number(percentageOfbalanceAmt)).toFixed(2)
console.log('percentageOfTotalAmountpercentageOfTotalAmount',percentageOfTotalAmount)
    console.log(
      `Processing ID ${id}: balanceReqAmount=${balanceReqAmount}, qbBalanceAmount=${qbBalanceAmount}, qbPaidAmount=${qbPaidAmount}, totalPaidAmount=${totalPaidAmount}`
    );

    //email template
    const request = await Request.getRequestById(id);

    let subject = 'Payment Deposit Notification for Project Invoice';

    let message = `
    <p>Dear ${username},</p>
  
    <p>We are pleased to inform you that a payment deposit has been made towards the project. Below are the details of the transaction:</p>
    
    <table style="border-collapse: collapse; width: 100%; margin-top: 10px;">
      <tr>
        <td style="padding: 8px; border: 1px solid #ddd;"><strong>Client Name:</strong></td>
        <td style="padding: 8px; border: 1px solid #ddd;">${request[0].customer_name}</td>
      </tr>
      <tr>
        <td style="padding: 8px; border: 1px solid #ddd;"><strong>Invoice Number:</strong></td>
        <td style="padding: 8px; border: 1px solid #ddd;">#${result?.doc_number}</td>
      </tr>
      <tr>
        <td style="padding: 8px; border: 1px solid #ddd;"><strong>Project Name:</strong></td>
        <td style="padding: 8px; border: 1px solid #ddd;">${result.name}</td>
      </tr>
      <tr>
        <td style="padding: 8px; border: 1px solid #ddd;"><strong>Deposit Amount:</strong></td>
        <td style="padding: 8px; border: 1px solid #ddd;">$ ${qbPaidAmount}</td>
      </tr>
      <tr>
        <td style="padding: 8px; border: 1px solid #ddd;"><strong>Percentage of Total Amount:</strong></td>
        <td style="padding: 8px; border: 1px solid #ddd;">${percentageOfTotalAmount}%</td>
      </tr>
      <tr>
        <td style="padding: 8px; border: 1px solid #ddd;"><strong>Remaining Balance:</strong></td>
        <td style="padding: 8px; border: 1px solid #ddd;">${balanceReqAmount}</td>
      </tr>
      <tr>
        <td style="padding: 8px; border: 1px solid #ddd;"><strong>Payment Date:</strong></td>
        <td style="padding: 8px; border: 1px solid #ddd;">${date}</td>
      </tr>
    </table>
  
    <p style="margin-top: 20px;">Kindly verify the payment details and update the project records accordingly. If you have any questions or need further assistance, feel free to reach out.</p>
  
    <p>Best regards,<br>
    ${username}<br>
    ${user_role}<br>
    ${company_name}</p>
    `;

         const mailOptions = {
      to:  admin_email ,             //'ramdharvish@skyniche.com' , 
      from: '"SignWise Solutions" <noreply@signwisesolutions.com>', 
      subject: subject,
      text: subject,
      html: message,
    }
  
 

    //&& qbPaidAmount below if  if need
    if ((balanceReqAmount !== qbBalanceAmount) &&result.syncWithQuickbooks==1 && totalReqAmount >=0 && latestInvoiceData) {
      const data = {
        request_id: id,
        amount: qbPaidAmount,
        added_by: userId,
        timestamp: currentDate,
        paymentMode: 'cash',
        source:2
      };

      const notificationMsg = `A deposit of ${data.amount} has been made on invoice number #${result?.doc_number}`;
      const notificationUrl = `official-request/view/${id}`;

      const notificationData_user = {
        user_id: userId,
        url: notificationUrl,
        timestamp: currentDate,
        message: notificationMsg,
        status: 1,
        from_id: userId,
      };

      if (qbBalanceAmount === 0 && (balanceReqAmount === totalReqAmount) && qbPaidAmount > 0) {
        data.title = 'Full payment';
        await Request.addRequestPayment(data);

        const reqData = { min_payment_status: 1 };
        await Request.editRequestTable(id, reqData);

        await Admin.addNotification(notificationData_user);

        try {
          if(SEND_EMAIL){
          await transporter.sendMail(mailOptions);
          console.log('Email sent');
          }
        } catch (error) {
          console.error('##########Err', error);
         
        }
  
        return { id, status: 'success', message: 'Payment updated successfully.' };

      } else {
        // if(qbPaidAmount > 0 && qbBalanceAmount > 0){
          if(qbPaidAmount > 0 ){

        
        data.title = 'Deposit'; // 'Partial payment';
        await Request.addRequestPayment(data);

        const request = await Request.getRequestById(id);
        const total_paid_amount = await Request.findTotalPaidAmount(id);
        const percentage = request[0]?.billing_type;
        const total_amount = request[0]?.total;

        const minimum_amount = parseFloat((total_amount * percentage) / 100).toFixed(2);
        // console.log('minimum_amountminimum_amount',total_paid_amount,minimum_amount,data.amount)

        // if (Number(total_paid_amount || 0) + Number(data.amount || 0) >= minimum_amount) {
          if (Number(total_paid_amount || 0)  >= minimum_amount) {
          const reqData = { min_payment_status: 1 };
          await Request.editRequestTable(id, reqData);
        }

        await Admin.addNotification(notificationData_user);

        try {
          if(SEND_EMAIL){
          await transporter.sendMail(mailOptions);
          console.log('Email sent');
          }
        } catch (error) {
          console.error('##########Err', error);
         
        }
  
        return { id, status: 'success', message: 'Payment updated successfully.' };
      }

     
    return { id, status: 'up_to_date', message: 'All are up to date.' };
    
    }


    } else {
      return { id, status: 'up_to_date', message: 'All are up to date.' };
    }
  } catch (error) {
    console.error(`Error processing ID ${id}:`, error);
    return { id, status: 'error', message: error.message || 'An error occurred.' };
  }
};


const checksendMain =async()=>{
  let subject = 'Payment Deposit Notification for Project Invoice';

  let message = `
  <p>Dear [Admin Name],</p>

  <p>We are pleased to inform you that a payment deposit has been made towards the project. Below are the details of the transaction:</p>
  
  <table style="border-collapse: collapse; width: 100%; margin-top: 10px;">
    <tr>
      <td style="padding: 8px; border: 1px solid #ddd;"><strong>Client Name:</strong></td>
      <td style="padding: 8px; border: 1px solid #ddd;">[Client Name]</td>
    </tr>
    <tr>
      <td style="padding: 8px; border: 1px solid #ddd;"><strong>Invoice Number:</strong></td>
      <td style="padding: 8px; border: 1px solid #ddd;">[Invoice Number]</td>
    </tr>
    <tr>
      <td style="padding: 8px; border: 1px solid #ddd;"><strong>Project Name:</strong></td>
      <td style="padding: 8px; border: 1px solid #ddd;">[Project Name]</td>
    </tr>
    <tr>
      <td style="padding: 8px; border: 1px solid #ddd;"><strong>Deposit Amount:</strong></td>
      <td style="padding: 8px; border: 1px solid #ddd;">$500</td>
    </tr>
    <tr>
      <td style="padding: 8px; border: 1px solid #ddd;"><strong>Percentage of Total Amount:</strong></td>
      <td style="padding: 8px; border: 1px solid #ddd;">50%</td>
    </tr>
    <tr>
      <td style="padding: 8px; border: 1px solid #ddd;"><strong>Remaining Balance:</strong></td>
      <td style="padding: 8px; border: 1px solid #ddd;">[Remaining Amount, if available]</td>
    </tr>
    <tr>
      <td style="padding: 8px; border: 1px solid #ddd;"><strong>Payment Date:</strong></td>
      <td style="padding: 8px; border: 1px solid #ddd;">[Date of Payment]</td>
    </tr>
  </table>

  <p style="margin-top: 20px;">Kindly verify the payment details and update the project records accordingly. If you have any questions or need further assistance, feel free to reach out.</p>

  <p>Best regards,<br>
  [Your Name]<br>
  [Your Position]<br>
  [Your Company Name]</p>
  `;

  const mailOptions = {
    to:  'ramdharvish@skyniche.com' , 
    from: '"SignWise Solutions" <noreply@signwisesolutions.com>', 
    subject: subject,
    text: subject,
    html: message,
  }

  try {
    if(SEND_EMAIL){
    await transporter.sendMail(mailOptions);
    console.log('Email sent');
    }
  } catch (error) {
    console.error('##########Err', error);
   
  }
}
// checksendMain()


const updateDesignOrderJobStatus = async(req,res)=>{
  try {
    const {userId,projectId,status} = req.body
    let projects = await Project.getDesignOrderProjectById(projectId);
    const completion = status?.id == 3 ? 100 : status?.id == 2 ? 50 : 0
    const newStatus = status?.id || 1
    let currentTime = moment().unix()
    let data ={}
    if(projects?.design_order_job_status == 4){
      data.status = 1
      data.send_client_app_req = 0
    }
    if(newStatus === 4){
      data.status = 6
    }
    data.design_order_job_status = newStatus
    data.completion = completion
    data.timestamp = currentTime

    if(newStatus == 1){
      data.is_in_queue = 1
    }else{
      data.is_in_queue = 0
    }
    
    // Set working_designer_id based on status and userType
    if(newStatus == 1){
      data.working_designer_id = null
    } else if(newStatus == 4){ 
      data.working_designer_id = null
    }  else {
      // Get userType to check if it's a designer
      const userData = await Admin.getUserById(userId);
      const actualUserType = userData && userData[0] ? userData[0].user_type : null;
      
      if(actualUserType === 11){
        data.working_designer_id = userId
      }
    }
    let updateJobStatus = await Request.updateDesignOrderJobStatus(data,projectId);

    // if(updateJobStatus){
    //   // Add log entry to design_order_updates table
    //   try {
    //     // Get user information for the activity log
    //     const userInfo = await Admin.getUserById(userId);
    //     const userName = userInfo && userInfo[0] 
    //       ? (userInfo[0].first_name ? `${userInfo[0].first_name} ${userInfo[0].last_name || ''}`.trim() : userInfo[0].company_name || 'Unknown User')
    //       : 'Unknown User';
        
    //     // Get status label based on newStatus (matching frontend display labels)
    //     let statusLabel = '';
    //     switch(newStatus) {
    //       case 1:
    //         // Status 1 with is_in_queue = 1 shows "In Queue", with is_in_queue = 0 shows "New"
    //         // Since we set is_in_queue = 1 when newStatus == 1, use "In Queue"
    //         statusLabel = 'In Queue';
    //         break;
    //       case 2:
    //         statusLabel = 'In Design';
    //         break;
    //       case 3:
    //         statusLabel = 'Design Job Completed';
    //         break;
    //       case 4:
    //         statusLabel = 'Sent to Client';
    //         break;
    //       default:
    //         statusLabel = `Status ${newStatus}`;
    //     }
        
    //     // Create activity message
    //     const activityMessage = `🔄 Design Order status changed to "${statusLabel}" by "${userName}"`;
        
    //     // Add log entry
    //     const designOrderUpdateData = {
    //       design_order_id: projectId,
    //       activity: activityMessage,
    //       added_by: userId,
    //       timestamp: currentTime,
    //       status: 1
    //     };
        
    //     await RequestActivity.addDesignOrderUpdate(designOrderUpdateData);
    //   } catch (logError) {
    //     console.error('Error adding design order status update log:', logError);
    //     // Don't fail the entire operation if logging fails
    //   }
    //   let allTasks = await Request.getAllDesignOrderTasks(projectId,userId,userType=1)
    //   for(const task of allTasks){
    //     let project = {};
    //     project.complete = completion
    //     project.status = newStatus
    //     let result = await Request.updateDesignOrderService(project,task.id);
    //     let allSubtask = await Request.getAllDesignOrderProjectSubTaskBytaskId(task.id,userType=1);
    //     for(const subTask of allSubtask){
    //       let projectTask = {}
    //       projectTask['status'] = newStatus;
    //       let updated = await Request.updateDesignOrdersSubTask(projectTask, subTask.id);
    //     }
    //   }
    //   if(newStatus == 3){
    //     // Send email and SMS notifications for design order completion
    //     let adminMangerIds = await Admin.getAllAdminAndManagerUserIds();
        
    //     // Get design order project members (designers)
    //     let projectMembers = await Request.getAllDesignOrderProjectMembers(projectId);
    //     let designerUserIds = [];
    //     if (projectMembers && projectMembers.length > 0) {
    //       designerUserIds = projectMembers.map(member => {
    //         // Get user_id from the project member data
    //         return member.user_id || member.userid;
    //       }).filter(Boolean);
    //     }
        
    //     // Combine all user IDs and remove duplicates
    //     let allAdminToCheck = [...new Set([userId, ...adminMangerIds, ...designerUserIds].filter(Boolean))];
    //     console.log('alluserssssssssarray', allAdminToCheck);

    //     // Get profile data for email template
    //     let profileData = await Settings.getCompanyProfileDetails();

    //     for (const userIdToCheck of allAdminToCheck) {
    //       try {
    //         console.log('useriddddd', userIdToCheck);
    //         const creator = await Admin.getUserNotificationAndPermissionSettings(userIdToCheck);
    //         console.log('creatorrrrrrrrr', creator);

    //         if (!creator) {
    //           console.log(`Skipping user ${userIdToCheck} - missing creator data or email settings`);
    //           continue;
    //         }
            
    //         console.log('creator.email---', creator.email,creator.emailEnabled, creator?.pcb_completed_design_jobs, creator?.pcb_completed_design_jobs_email);
    //         // Email Notification
    //         if (creator.emailEnabled === 1 && creator.email && creator?.pcb_completed_design_jobs === 1 && creator?.pcb_completed_design_jobs_email === 1) {
    //           const subject = `🎨 Design Job Completed - New Designs Ready for Approval: ${projects?.name}`;
    //           const content = `
    //           Hello ${creator.name},<br/><br/>
    //           The design job "<strong>${projects?.name}</strong>" has been completed successfully.<br/>
    //           The new designs ready to be sent for approval. 
    //           Please review the details in your software.<br/><br/>
    //         `;            
    //           let message = designOrderCompletionEmail({
    //             subject: subject,
    //             content: content,
    //             baseUrl: BASE_URL,
    //             profileData: profileData[0]
    //           });

    //           const mailOptions = {
    //             to: creator.email,
    //             // to: 'sanjay.pk@skyniche.com',
    //             from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
    //             subject: subject,
    //             text: subject,
    //             html: message,
    //           };

    //           try {
    //             await transporter.sendMail(mailOptions);
    //             console.log(`✅ Design order completion email sent to: ${creator.email}`);
    //           } catch (err) {
    //             console.error(`❌ Error sending design order completion email:`, err);
    //           }
    //         }
    //         console.log('creator.smsEnabled---', creator.smsEnabled, creator.phone, creator?.pcb_completed_design_jobs, creator?.pcb_completed_design_jobs_sms);
    //         // SMS Notification
    //         if (creator.smsEnabled === 1 && creator.phone && creator?.pcb_completed_design_jobs === 1 && creator?.pcb_completed_design_jobs_sms === 1) {
    //           const smsContent = 
    //             `🎨 Design Job Completed - New designs ready to be sent for approval:\n` +
    //             `📄 Name: "${projects?.name}"\n` +
    //             `🔗 View: ${BASE_URL}\n` +
    //             `— SignWise`;
                
    //           const recipientPhone = creator.phone || TWILIO_PHONE;
    //           const Phone_country_code = creator.phone_country_code || '';
    //           try {
    //             await sendMessageToPhoneNumber(recipientPhone, smsContent, Phone_country_code);
    //             // await sendMessageToPhoneNumber('9947931552', smsContent,'91');
    //             console.log(`✅ Design job completion SMS sent to ${creator.name || 'Unknown'} (${recipientPhone})`);
    //           } catch (err) {
    //             console.error(`❌ Failed to send design job completion SMS to ${creator.name || 'Unknown'} (${recipientPhone}):`, err);
    //           }
    //         }
    //       } catch (error) {
    //         console.error(`Error processing notifications for user ${userIdToCheck}:`, error);
    //       }
    //     }
    //   }
    //   console.log('projects===',projects)
    //   let notificationMsg = `Status updated for the Design Order "${projects?.name}"`
    //   let notificationUrl = "design-order-job/view-project/" + projectId;
    //   let notificationData = {};
    //   const currentDate = moment().unix();
    //   const incrementResult_User = await Admin.incrementField(userId, "noti_count", 1);
    //   let notificationData_user = {
    //     user_id: userId,
    //     url: notificationUrl,
    //     timestamp: currentDate,
    //     message: notificationMsg,
    //     status: 1,
    //     from_id: userId,
    //   };
    //   const addNotificationResult_user = await Admin.addNotification(notificationData_user);
    //   let adminIds = await Admin.getAllAdminIds();
    //   for (const adminId of adminIds) {
    //     if (adminId != userId) {
    //       const incrementResult = await Admin.incrementField(adminId, "noti_count", 1);
    //       notificationData = {
    //         user_id: adminId,
    //         url: notificationUrl,
    //         timestamp: currentDate,
    //         message: notificationMsg,
    //         status: 1,
    //         from_id: userId,
    //       };
    //       const addNotificationResult = await Admin.addNotification(notificationData);
    //     }
    //   }
    //   // const allUserIds = await Admin.getAllUserIds();
    //   // const selectedClientId = projects?.client|| null;
    //   // const clientUserId = await Admin.getPartnersUserIdById(selectedClientId)
    //   // console.log("clientUserId",clientUserId)
    //   // let allUsersToUpdate = [...new Set([userId, clientUserId, ...allUserIds].filter(Boolean))];

    // //   const updateOpenCount = async (userId,increase = true) => {
    // //     if(projects?.design_order_job_status == 1){
    // //       console.log("previous status neww")
    // //       if (newStatus == 2) {
    // //         await Request.designCountIncrementField(userId, 'new_count', -1, 'new_count_ids', projectId, false);
    // //         await Request.designCountIncrementField(userId, 'in_progress_count', 1, 'in_progress_count_ids', projectId, true);
    // //       console.log("design_order_job_status new count to progress from new count")
    // //       }
    // //       if (newStatus == 3) {
    // //         await Request.designCountIncrementField(userId, 'new_count', -1, 'new_count_ids', projectId, false);
    // //         await Request.designCountIncrementField(userId, 'completed_count', 1, 'completed_count_ids', projectId, true);
    // //         console.log("design_order_job_status new count to completed from new count")
    // //       }
    // //     }else if (projects?.design_order_job_status == 2){
    // //       console.log("previous status in progress")
    // //       if (newStatus == 3) {
    // //         await Request.designCountIncrementField(userId, 'in_progress_count', -1, 'in_progress_count_ids', projectId, false);
    // //         await Request.designCountIncrementField(userId, 'completed_count', 1, 'completed_count_ids', projectId, true);
    // //         console.log("design_order_job_status in progress to  completed count from in progress")
    // //       }else if (newStatus == 1) {
    // //         await Request.designCountIncrementField(userId, 'in_progress_count', -1, 'in_progress_count_ids', projectId, false);
    // //         await Request.designCountIncrementField(userId, 'new_count', 1, 'new_count_ids', projectId, true);
    // //         console.log("design_order_job_status in  progress to new count from in progress")
    // //       }
    // //     }else if (projects?.design_order_job_status == 3){
    // //       console.log("previous status in completed")
    // //       if (newStatus == 2) {
    // //         await Request.designCountIncrementField(userId, 'completed_count', -1, 'completed_count_ids', projectId, false);
    // //         await Request.designCountIncrementField(userId, 'in_progress_count', 1, 'in_progress_count_ids', projectId, true);
    // //         console.log("design_order_job_status in  completed count to  progress from completed")
    // //       }else if (newStatus == 1) {
    // //         await Request.designCountIncrementField(userId, 'completed_count', -1, 'completed_count_ids', projectId, false);
    // //         await Request.designCountIncrementField(userId, 'new_count', 1, 'new_count_ids', projectId, true);
    // //         console.log("design_order_job_status in  completed count to new from completed")
    // //       }
    // //     }
    // // };

    // // await Promise.all(allUsersToUpdate.map(user => updateOpenCount(user)));
    // // console.log("count updated successfully")
    //   res.send({ status:1, message:notificationMsg })
    // }else{
    //   res.send({ status:0, message:"Failed to update status of design order job" })
    // }
    if (updateJobStatus) {
      // Add log entry to design_order_updates table
      try {
        const userInfo = await Admin.getUserById(userId);
        const userName = userInfo && userInfo[0]
          ? (userInfo[0].first_name ? `${userInfo[0].first_name} ${userInfo[0].last_name || ''}`.trim() : userInfo[0].company_name || 'Unknown User')
          : 'Unknown User';
    
        let statusLabel = '';
        switch (newStatus) {
          case 1:
            statusLabel = 'In Queue';
            break;
          case 2:
            statusLabel = 'In Design';
            break;
          case 3:
            statusLabel = 'Design Job Completed';
            break;
          case 4:
            statusLabel = 'Rejected';
            break;
          default:
            statusLabel = `Status ${newStatus}`;
        }
    
        const activityMessage = `🔄 Design Order status changed to "${statusLabel}" by "${userName}"`;
    
        const designOrderUpdateData = {
          design_order_id: projectId,
          activity: activityMessage,
          added_by: userId,
          timestamp: currentTime,
          status: 1
        };
    
        await RequestActivity.addDesignOrderUpdate(designOrderUpdateData);
      } catch (logError) {
        console.error('Error adding design order status update log:', logError);
        // Don't fail the entire operation if logging fails
      }
    
      // Process all tasks and subtasks asynchronously
      let allTasks = await Request.getAllDesignOrderTasks(projectId, userId, userType = 1);
      for (const task of allTasks) {
        let project = {};
        project.complete = completion;
        project.status = newStatus;
        let result = await Request.updateDesignOrderService(project, task.id);
        let allSubtask = await Request.getAllDesignOrderProjectSubTaskBytaskId(task.id, userType = 1);
        for (const subTask of allSubtask) {
          let projectTask = { 'status': newStatus };
          let updated = await Request.updateDesignOrdersSubTask(projectTask, subTask.id);
        }
      }
      // Send the response immediately after processing the design order job status update
      res.send({ status: 1, message: "Design order status updated successfully" });
    
      // If newStatus is 3 (Design Job Completed), handle notifications asynchronously
      if (newStatus == 3) {
    
        // Async function to handle notifications
        const handleNotificationsAsync = async () => {
          try {
            let adminMangerIds = await Admin.getAllAdminAndManagerUserIds();
    
            let projectMembers = await Request.getAllDesignOrderProjectMembers(projectId);
            let designerUserIds = [];
            if (projectMembers && projectMembers.length > 0) {
              designerUserIds = projectMembers.map(member => member.user_id || member.userid).filter(Boolean);
            }
    
            let allAdminToCheck = [...new Set([userId, ...adminMangerIds, ...designerUserIds].filter(Boolean))];
    
            let profileData = await Settings.getCompanyProfileDetails();
    
            // Handle notifications for each admin/manager
            const adminNotificationPromises = allAdminToCheck.map(async (userIdToCheck) => {
              const creator = await Admin.getUserNotificationAndPermissionSettings(userIdToCheck);
              if (!creator) return;
    
              const recipientPhone = creator?.phone || TWILIO_PHONE;
              const Phone_country_code = creator?.phone_country_code || '';
    
              // Email Notification
              if (creator.emailEnabled === 1 && creator.email && creator?.pcb_completed_design_jobs === 1 && creator?.pcb_completed_design_jobs_email === 1) {
                const subject = `🎨 Design Job Completed - New Designs Ready for Approval: ${projects?.name}`;
                const content = `
                  Hello ${creator.name},<br/><br/>
                  The design job "<strong>${projects?.name}</strong>" has been completed successfully.<br/>
                  The new designs ready to be sent for approval. 
                  Please review the details in your software.<br/><br/>
                `;
                let message = designOrderCompletionEmail({
                  subject: subject,
                  content: content,
                  baseUrl: BASE_URL,
                  profileData: profileData[0]
                });
    
                const mailOptions = {
                  to: creator.email,
                  from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
                  subject: subject,
                  text: subject,
                  html: message,
                };
    
                await transporter.sendMail(mailOptions);
              }
    
              // SMS Notification
              if (creator.smsEnabled === 1 && creator.phone && creator?.pcb_completed_design_jobs === 1 && creator?.pcb_completed_design_jobs_sms === 1) {
                const smsContent =
                  `🎨 Design Job Completed - New designs ready to be sent for approval:\n` +
                  `📄 Name: "${projects?.name}"\n` +
                  `🔗 View: ${BASE_URL}\n` +
                  `— SignWise`;
    
                await sendMessageToPhoneNumber(recipientPhone, smsContent, Phone_country_code);
              }
            });
    
            // Wait for all admin notifications to be processed
            await Promise.all(adminNotificationPromises);
    
            // Add notifications for user and admins
            let notificationMsg = `Status updated for the Design Order "${projects?.name}"`;
            let notificationUrl = "design-order-job/view-project/" + projectId;
            let currentDate = moment().unix();
    
            // Increment notification count for the user
            await Admin.incrementField(userId, "noti_count", 1);
            let notificationData_user = {
              user_id: userId,
              url: notificationUrl,
              timestamp: currentDate,
              message: notificationMsg,
              status: 1,
              from_id: userId,
            };
            await Admin.addNotification(notificationData_user);
    
            // Increment notification count for all admin users
            let adminIds = await Admin.getAllAdminIds();
            const adminNotificationPromises2 = adminIds
              .filter(adminId => adminId !== userId)
              .map(async (adminId) => {
                await Admin.incrementField(adminId, "noti_count", 1);
                let notificationData = {
                  user_id: adminId,
                  url: notificationUrl,
                  timestamp: currentDate,
                  message: notificationMsg,
                  status: 1,
                  from_id: userId,
                };
                await Admin.addNotification(notificationData);
              });
    
            // Wait for all admin notifications to be processed
            await Promise.all(adminNotificationPromises2);
    
          } catch (error) {
            console.error("Error in async notifications:", error);
          }
        };
    
        // Execute the notifications asynchronously
        handleNotificationsAsync().catch(error => {
          console.error('Error in async operations:', error);
        });
      }
    }
     else {
      res.send({ status: 0, message: "Failed to update design order job status" });
    }
    
  } catch (error) {
    console.log("error",error)
    res.send({ error:error, message:"server error" })
  }
}


const sendApprovalReqDesignOrder =async(req,res)=>{
  try {
    const {design_order_id,client, selectedDesigns, userId} = req.body

    let allDesigns = await Request.fetchAllDesignOrderAttachments(design_order_id);
    // const allDesignIds = allDesigns?.filter(des=>des.status != 0 && des.approved != 1)?.map(item => item.id);
    // const selectedSet = new Set(selectedDesigns?.map(Number));
    // const unselectedDesigns = allDesignIds?.filter(id => !selectedSet.has(id));

    const allDesignIds = allDesigns.map(item => item.id);
    const selectedSet = new Set(selectedDesigns.map(Number));
    const unselectedDesigns = allDesignIds.filter(id => !selectedSet.has(id));

    console.log('selectedDesigns', selectedDesigns)
    const data={
      send_client_app_req : 1,
      design_order_job_status : 3
    }
    let updated = await Request.updateDesignOrders(design_order_id, data);
    const clientUser = await Client.getClientById(client);
    const clientUserId = clientUser?.user_id;

    // Update selected designs to isDesign = 1
    // const designData = { isDesign : 1, status: 2 }
    const designData = { isDesign : 1, approved: 1, approved_by: clientUserId }
    await Request.updateMultipleDesignOrderAttachments(selectedDesigns,designData)

    // Update unselected designs to isDesign = 2
    // const designDataUnselected = { isDesign: 2 };
    const designDataUnselected = { isDesign: 2, approved: 0, approved_by: null  };
    if (unselectedDesigns.length > 0) {
      // await Request.updateMultipleDesignOrderAttachments(unselectedDesigns, designDataUnselected);
    }

    let updateDesignOrderAttachmnets = await Request.updateDesignOrderAttachmentsByDesignOrderId(design_order_id, {isNew :0})

    // if (clientUserId) {
    //   await Request.designCountIncrementField(clientUserId, "open_count", 1, "open_count_ids", design_order_id, true);   
    // } else {
    //   console.warn("Client user ID not found, skipping count update.");
    // }
    if (updated) {
      // Add log entry to design_order_updates table
      try {
        const currentTime = moment().unix();
        // Get user information for the activity log
        const userInfo = await Admin.getUserById(userId);
        const userName = userInfo && userInfo[0] 
          ? (userInfo[0].first_name ? `${userInfo[0].first_name} ${userInfo[0].last_name || ''}`.trim() : userInfo[0].company_name || 'Unknown User')
          : 'Unknown User';
        
        // Get design order name
        const designOrderData = await Request.getDesignOrderByIdNew(design_order_id);
        const designOrderName = designOrderData && designOrderData[0] ? designOrderData[0].dataValues?.name : 'Design Order';
        
        // Create activity message
        const activityMessage = `📤 Designs sent for client approval for Design Order "${designOrderName}" by "${userName}"`;
        
        // Add log entry
        const designOrderUpdateData = {
          design_order_id: design_order_id,
          activity: activityMessage,
          added_by: userId,
          timestamp: currentTime,
          status: 1
        };
        
        await RequestActivity.addDesignOrderUpdate(designOrderUpdateData);
      } catch (logError) {
        console.error('Error adding design order send approval log:', logError);
        // Don't fail the entire operation if logging fails
      }
      return res.send({ status: 1 });
    }
    res.send({ status: 0 });

  } catch (error) {
    console.log("error",error)
    res.send({ error:error, message:"server error" })
  }
}

const updateMultiDesigns = async (req, reply) => {
  let { requestId, userId, data, designId, approved, socketId } = req.body

  // Determine operation type based on the data and approved status
  let operationType = 'update'; // default
  if (approved === true) {
    operationType = 'approve';
  } else if (approved === false && data && data.length > 0) {
    operationType = 'reject';
  } else if (data && data.length > 0) {
    operationType = 'link';
  }

  // 🚀 PROGRESS TRACKING: Socket progress emission function
  const emitProgress = (step, message, progress) => {
    const progressData = {
      step,
      message,
      progress,
      userId,
      domain: DOMAIN,
      timestamp: moment().unix(),
      type: 'update_multi_designs_progress'
    };
    
    // Emit to working socket server (same as chat)
    console.log(`[UPDATE-MULTI-DESIGNS-PROGRESS] 📡 Emitting progress to working socket server...`);
    try {
      const workingSocket = ioClient(REST_API_IO, {
        path: '/socket.io',
        transports: ['websocket', 'polling'],
        secure: true,
        timeout: 5000
      });
      
      workingSocket.on('connect', () => {
        console.log(`[UPDATE-MULTI-DESIGNS-PROGRESS] 🔌 Connected to working socket server`);
        console.log(`[UPDATE-MULTI-DESIGNS-PROGRESS] 📊 Emitting progress data:`, progressData);
        
        // Try emitting as 'message' event (same as chat) to test if socket server handles it
        workingSocket.emit('message', {
          ...progressData,
          type: 'progress',
          conversationId: `update_multi_designs_progress_${userId}`,
          senderId: 'system'
        });
        
        // Also try the original request_progress event
        workingSocket.emit('request_progress', progressData);
        
        console.log(`[UPDATE-MULTI-DESIGNS-PROGRESS] ✅ Successfully emitted progress to working socket server`);
        
        // Disconnect after emitting
        setTimeout(() => {
          workingSocket.disconnect();
        }, 1000);
      });
      
      workingSocket.on('connect_error', (error) => {
        console.error(`[UPDATE-MULTI-DESIGNS-PROGRESS] ❌ Working socket connection error:`, error);
      });
      
    } catch (error) {
      console.error(`[UPDATE-MULTI-DESIGNS-PROGRESS] ❌ Error creating working socket connection:`, error);
    }
  };

  // Initial progress
  console.log(`[UPDATE-MULTI-DESIGNS-PROGRESS] 🚀 Starting multi-design update for user:`, userId);
  emitProgress('start', `Initializing design update...`, 5);

  // console.log('datadatadatadata',data)
  // return
  let attachmentId = designId || data[0].id
  const allProductIds = null; // data.map(item => item.product_id).join(',');
  // Filter out null/undefined values and convert to strings, then join
  const allLocationIds = data && data.length > 0 
    ? data.map(item => item.product_id).filter(id => id != null && id !== '').join(',')
    : '';

  console.log('operationType:', operationType);
  console.log('data:', data);
  console.log('allLocationIds:', allLocationIds);

  emitProgress('fetching_request_data', `Fetching request data...`, 15);

  let existingRequestData = await Request.FindReqDataById(requestId)
  // console.log('existingRequestData',existingRequestData)

  
 
  let uploadedArray = []
  if (!empty(data) || !empty(attachmentId)) {
    emitProgress('updating_attachments', `Updating request attachments...`, 25);
    
    let attachData ={
      id:attachmentId,
      requestId :requestId,
      product_ids :allProductIds,
      location_ids :allLocationIds,
      approved:approved ? 1 :0,
      approved_by:approved ? userId : null
    }
    let upload = await Request.updateRequestAttachments(requestId, attachData)
    // let attachment = await Request.getRequestAttachmentById(data?.id)

    // Update isLinkedAll column when linking
    if (operationType === 'approve') {
      console.log('Link operation detected');
      console.log('allLocationIds:', allLocationIds);
      // Extract product IDs from data array and update isLinkedAll
      const productIds = data && data.length > 0 
        ? data.map(item => item.product_id).filter(id => id != null && id !== '' && id !== undefined)
        : [];
      
      console.log('productIds array:', productIds);
      
      if (productIds.length > 0) {
        console.log('Updating isLinkedAll for product IDs:', productIds);
        await Request.updateLocationIsLinkedAll(productIds);
      } else {
        console.log('No valid product IDs found to update isLinkedAll');
      }
    }

    emitProgress('updating_request_status', `Updating request status...`, 40);

    let reqData={
      status:3
    }
    if(approved && existingRequestData.quotation !==1 && existingRequestData.status !==1 && existingRequestData.status !==2 && existingRequestData.status !==3){
      let updateRequest = await Request.updateRequestById(requestId,reqData)
      console.log('updateRequest',updateRequest)
    }

    emitProgress('processing_notifications', `Processing notifications...`, 60);

    let notification_msg = `Design has been ${operationType === 'approve' ? `linked with catalog "${data[0]?.product_name}" and approved` :
      operationType === 'reject' ? 'Design has been rejected' :
        operationType === 'link' ? `Design has been linked with catalog "${data[0]?.product_name}"` :
          operationType === 'delete' ? 'Design has been deleted' : 'Design has been updated'}`;
    let notification_url = `official-request/view/${requestId}`

    let adminIds = await Admin.getAllAdminIds();
    for (let eachadminIds of adminIds) {
      if (eachadminIds != userId) {
        let incrementResult = await Admin.incrementField(eachadminIds, 'noti_count', 1)
        let addNotification = {
          user_id: eachadminIds,
          url: notification_url,
          timestamp: moment().unix(),
          message: notification_msg,
          status: 1,
          from_id: userId,
        }
        const addNotificationResult = await Admin.addNotification(addNotification);
      }
    }
    
    emitProgress('adding_request_activity', `Adding request activity...`, 80);
    
    let requestActivity = {}
    requestActivity['request_id'] = requestId;
    requestActivity['activity'] = notification_msg;
    requestActivity['added_by'] = userId
    requestActivity['timestamp'] = moment().unix()
    let reqActivityId = await RequestActivity.addRequestUpdate(requestActivity)
    
    emitProgress('complete', `Design ${operationType === 'delete' ? 'deleted' : operationType === 'reject' ? 'rejected' : operationType === 'approve' ? 'approved' : operationType === 'link' ? 'linked' : 'updated'} successfully!`, 100);
    reply.send({ status: 1, message: "Design succesfully updated!." })

  }
  else {
    emitProgress('error', `Failed to ${operationType} design`, 0);
    reply.send({ status: 0, message: "Failed to update Design!." })
  }
}

const unlinkMultiDesigns = async (req, reply) => {
  let { requestId, userId, data, designId, socketId } = req.body

  // 🚀 PROGRESS TRACKING: Socket progress emission function
  const emitProgress = (step, message, progress) => {
    const progressData = {
      step,
      message,
      progress,
      userId,
      domain: DOMAIN,
      timestamp: moment().unix(),
      type: 'unlink_multi_designs_progress'
    };
    
    // Emit to working socket server (same as chat)
    console.log(`[UNLINK-MULTI-DESIGNS-PROGRESS] 📡 Emitting progress to working socket server...`);
    try {
      const workingSocket = ioClient(REST_API_IO, {
        path: '/socket.io',
        transports: ['websocket', 'polling'],
        secure: true,
        timeout: 5000
      });
      
      workingSocket.on('connect', () => {
        console.log(`[UNLINK-MULTI-DESIGNS-PROGRESS] 🔌 Connected to working socket server`);
        console.log(`[UNLINK-MULTI-DESIGNS-PROGRESS] 📊 Emitting progress data:`, progressData);
        
        workingSocket.emit('message', {
          ...progressData,
          type: 'progress',
          conversationId: `unlink_multi_designs_progress_${userId}`,
          senderId: 'system'
        });
        
        workingSocket.emit('request_progress', progressData);
        
        console.log(`[UNLINK-MULTI-DESIGNS-PROGRESS] ✅ Successfully emitted progress to working socket server`);
        
        setTimeout(() => {
          workingSocket.disconnect();
        }, 1000);
      });
      
      workingSocket.on('connect_error', (error) => {
        console.error(`[UNLINK-MULTI-DESIGNS-PROGRESS] ❌ Working socket connection error:`, error);
      });
      
    } catch (error) {
      console.error(`[UNLINK-MULTI-DESIGNS-PROGRESS] ❌ Error creating working socket connection:`, error);
    }
  };

  // Initial progress
  console.log(`[UNLINK-MULTI-DESIGNS-PROGRESS] 🚀 Starting multi-design unlink for user:`, userId);
  emitProgress('start', `Initializing design unlink...`, 5);

  let attachmentId = designId || data[0]?.id
  const locationIdsToUnlink = data && data.length > 0 
    ? data.map(item => item.product_id).filter(id => id != null && id !== '').map(id => String(id))
    : [];

  console.log('locationIdsToUnlink:', locationIdsToUnlink);

  emitProgress('fetching_request_data', `Fetching request data...`, 15);

  let existingRequestData = await Request.FindReqDataById(requestId)

  let uploadedArray = []
  if (!empty(data) || !empty(attachmentId)) {
    emitProgress('updating_attachments', `Unlinking request attachments...`, 25);
    
    // Get current attachment data
    let currentAttachment = await Request.getRequestAttachmentById(attachmentId)
    if (!currentAttachment) {
      emitProgress('error', `Attachment not found`, 0);
      return reply.send({ status: 0, message: "Attachment not found!." })
    }

    let currentLocationIds = currentAttachment.location_ids 
      ? currentAttachment.location_ids.split(',').map(id => id.trim())
      : [];

    // Remove the location IDs that need to be unlinked
    let remainingLocationIds = currentLocationIds.filter(id => !locationIdsToUnlink.includes(String(id)));

    // Determine if all catalogs are being unlinked
    const allCatalogsUnlinked = remainingLocationIds.length === 0;

    let attachData = {
      id: attachmentId,
      requestId: requestId,
      location_ids: allCatalogsUnlinked ? null : remainingLocationIds.join(','),
      approved: allCatalogsUnlinked ? 0 : currentAttachment.approved,
      approved_by: allCatalogsUnlinked ? null : currentAttachment.approved_by
    }

    let upload = await Request.updateRequestAttachments(requestId, attachData)

    // Update isLinkedAll for affected locations
    emitProgress('updating_location_status', `Updating location link status...`, 35);
    if (locationIdsToUnlink && locationIdsToUnlink.length > 0) {
      for (const locationId of locationIdsToUnlink) {
        try {
          await Request.updateLocationIsLinkedAllByLocationId(parseInt(locationId), requestId);
        } catch (error) {
          console.error(`Error updating isLinkedAll for location ${locationId}:`, error);
        }
      }
    }

    emitProgress('updating_request_status', `Updating request status...`, 40);

    let reqData = {
      status: 3
    }
    if (!allCatalogsUnlinked && existingRequestData.quotation !== 1 && existingRequestData.status !== 1 && existingRequestData.status !== 2 && existingRequestData.status !== 3) {
      let updateRequest = await Request.updateRequestById(requestId, reqData)
      console.log('updateRequest', updateRequest)
    }

    emitProgress('processing_notifications', `Processing notifications...`, 60);

    let notification_msg = `Design has been unlinked`
    let notification_url = `official-request/view/${requestId}`

    let adminIds = await Admin.getAllAdminIds();
    for (let eachadminIds of adminIds) {
      if (eachadminIds != userId) {
        let incrementResult = await Admin.incrementField(eachadminIds, 'noti_count', 1)
        let addNotification = {
          user_id: eachadminIds,
          url: notification_url,
          timestamp: moment().unix(),
          message: notification_msg,
          status: 1,
          from_id: userId,
        }
        const addNotificationResult = await Admin.addNotification(addNotification);
      }
    }
    
    emitProgress('adding_request_activity', `Adding request activity...`, 80);
    
    let requestActivity = {}
    requestActivity['request_id'] = requestId;
    requestActivity['activity'] = notification_msg;
    requestActivity['added_by'] = userId
    requestActivity['timestamp'] = moment().unix()
    let reqActivityId = await RequestActivity.addRequestUpdate(requestActivity)
    
    emitProgress('complete', `Design unlinked successfully!`, 100);
    reply.send({ status: 1, message: "Design successfully unlinked!." })

  }
  else {
    emitProgress('error', `Failed to unlink design`, 0);
    reply.send({ status: 0, message: "Failed to unlink Design!." })
  }
}

const getAllEstimateTabCount = async (req, res) => {
  try {
    let userId = req.body.userId
    
    let data = await Request.getEstimateCountByUser(userId);
      res.send({
      status: 1,
      data: data,
      message: 'Estimate count fetched successful'
    })
  } catch (error) {
    console.log(error)
    res.send({ status: 0, message: 'Failed to fetch estimate tab counts' });
  }
}

const reduceEstimateTabCount = async (req, reply) => {
  try {
    const { estimate_id, userId } = req.body;
    console.log(`Successfully estimate_id`,estimate_id,userId);
    const userCount = await Request.getEstimateTabCountByUserData(userId);
    if (!userCount) {
      return reply.status(400).send({ status: 0, message: "User record not found" });
    }
    const countFields = {
      open_count: "open_count_ids",
      approved_count: "approved_count_ids",
      archive_count: "archive_count_ids",
    };

    let updatedField = null;
    let updatedCountField = null;

    for (const [countField, idsField] of Object.entries(countFields)) {
      let idsArray = userCount[idsField] ? userCount[idsField].split(",") : [];

      if (idsArray.includes(estimate_id.toString())) {
        await Request.EstimateTabIncrementField(userId, countField, -1, idsField, estimate_id, false);
        updatedField = idsField;
        updatedCountField = countField;
        break;
      }
    }

    if (!updatedField) {
      return reply.status(400).send({ status: 0, message: "Estimate ID not found in any count list" });
    }

    console.log(`Successfully updated count for ${updatedCountField}`);

    reply.send({
      status: 1,
      message: `Count updated successfully. Removed from ${updatedField}`,
      updatedCountField,
    });
  } catch (error) {
    console.error("Error reducing Estimate count:", error);
    reply.status(500).send({ status: 0, message: "Server error", error });
  }
};

const getAllSalesOrderTabCount = async (req, res) => {
  try {
    let userId = req.body.userId
    
    let data = await Request.getSalesOrderCountByUser(userId);
      res.send({
      status: 1,
      data: data,
      message: 'Sales order count fetched successful'
    })
  } catch (error) {
    console.log(error)
    res.send({ status: 0, message: 'Failed to fetch Sales order tab counts' });
  }
}

const reduceSalesOrderTabCount = async (req, reply) => {
  try {
    const { request_id, userId } = req.body;
    console.log(`Successfully sales order`,request_id,userId);
    const userCount = await Request.getSalesOrderByUserData(userId);
    if (!userCount) {
      return reply.status(400).send({ status: 0, message: "User record not found" });
    }
    const countFields = {
      open_count: "open_count_ids",
      approved_count: "approved_count_ids",
      in_production_count: "in_production_count_ids",
      installs_count: "installs_count_ids",
      pickup_count: "pickup_count_ids",
      shipping_count: "shipping_count_ids",
      invoicing_count: "invoicing_count_ids",
      completed_count: "completed_count_ids",
    };

    let updatedField = null;
    let updatedCountField = null;

    for (const [countField, idsField] of Object.entries(countFields)) {
      let idsArray = userCount[idsField] ? userCount[idsField].split(",") : [];

      if (idsArray.includes(request_id.toString())) {
        await Request.salesOrderIncrementField(userId, countField, -1, idsField, request_id, false);
        updatedField = idsField;
        updatedCountField = countField;
        break;
      }
    }

    if (!updatedField) {
      return reply.status(400).send({ status: 0, message: "Sales order ID not found in any count list" });
    }

    console.log(`Successfully updated count for ${updatedCountField}`);

    reply.send({
      status: 1,
      message: `Count updated successfully. Removed from ${updatedField}`,
      updatedCountField,
    });
  } catch (error) {
    console.error("Error reducing Sales order count:", error);
    reply.status(500).send({ status: 0, message: "Server error", error });
  }
};

const changeNewOrderStatusSalesOrderTab = async (req, res) => {
  try {
    const { request_id } = req.body;
    const updateRequest = await Request.chageNewSalesOrderStatus(request_id);
    if(updateRequest){
      res.send({ status: 1, message: "Sales order total updated successfully!" });
    }else{
      res.send({ status: 0, message: "Sales order total not updated" });
    }
  } catch (error) {
    console.log(error)
    res.send({ status: 0, message: 'server error' });
  }
}

const acceptReviewOrder = async (req, res) => {
  try {
    const { requestId, userId } = req.body;
    let requestData = await Request.getRequestById(requestId);
    
    const updateRequest = await Request.updateRequestById(requestId,{isReview : 1})
    
    if(updateRequest){
    let requestPartners = await RequestPartner.getRequestPartnerByIds({}, requestId)
      let reqActivityMsg = "Sales Order has been approved after review and moved to invoicing"
      let notificationMsg = `The Sales Order ${requestData[0].name} approved after review.`;
      let notificationUrl = `request/view/${requestId}`;

      const currentDate = moment().unix();  
       let notificationData_user = { 
        user_id: userId,
        url: notificationUrl,
        timestamp: currentDate,
        message: notificationMsg,
        status: 1,
        from_id: userId,
      };
	  const addNotificationResult_user = await Admin.addNotification(notificationData_user);

     let notificationData = {};
      if (requestPartners.length > 0) {
        for (const reqId of requestPartners) {
          console.log("$$$$$$$$$$$$$", reqId);
          const incrementResult = await Admin.incrementField(reqId['id'], "noti_count", 1);

          notificationData = {
            user_id: reqId['id'],
            url: notificationUrl,
            timestamp: currentDate,
            message: notificationMsg,
            status: 1,
            from_id: userId,
          };

          const addNotificationResult = await Admin.addNotification(notificationData);
        }
      }

      	 let requestActivity = {}
      requestActivity['request_id'] = requestId;
      requestActivity['activity'] = reqActivityMsg
      requestActivity['added_by'] = userId
      requestActivity['timestamp'] = currentDate
      requestActivity['status'] = 1
      let reqActivityId = await RequestActivity.addRequestUpdate(requestActivity)

      res.send({ status: 1, message: "Sales Order approved successfully!" });
    }else{
      res.send({ status: 0, message: "Sales Order not approved!" });

    }

  } catch (error) {
     console.log(error)
    res.send({ status: 0, message: 'server error' });
  }
}

const updateReviewInvoiceTotal = async (req, res) => {
  try {
    const { requestId, orderId, total, userId } = req.body;
    const updateOrder = await CustomerOrder.updateCustomerOrder(orderId, {total : total, grandTotal : total});
    
    if(updateOrder){
      let reqActivityMsg = "Sales Order total has been updated"
      const currentDate = moment().unix();  
      let requestActivity = {}
      requestActivity['request_id'] = requestId;
      requestActivity['activity'] = reqActivityMsg
      requestActivity['added_by'] = userId
      requestActivity['timestamp'] = currentDate
      requestActivity['status'] = 1
      let reqActivityId = await RequestActivity.addRequestUpdate(requestActivity)

      res.send({ status: 1, message: "Sales order total updated successfully!" });
    }else{
      res.send({ status: 0, message: "Sales order total not updated" });
    }
  } catch (error) {
    console.log(error)
    res.send({ status: 0, message: 'server error' });
  }
}

const addRequestService = async (req, res) => {
  try {
    const { serviceData, userId, requestId, projectId, orderId,projectCategory } = req.body;
    
    console.log('Received request data:', { serviceData, userId, requestId }); // Debug log
    
    if (!serviceData || !userId || !requestId) {
      return res.send({ status: 0, message: 'Service data, user ID, and request ID are required' });
    }

    // Map the service data to match the database schema requirements
    let serviceToAdd = {
      request_id: requestId,
      service_name: serviceData?.name || null,
      labour_cost_service_id: serviceData.id, // Default to 1 if not provided
      qty: serviceData.expected_hours || 0, // Default to 1 if not provided
      unit_cost: serviceData.total_cost || 0, // Use cost as unit_cost
      total_cost: (serviceData.total_cost || 0) * (serviceData.expected_hours || 0), // Calculate total cost
      job_cost: 0, // Set as job cost service
      taken: projectCategory ? projectCategory == 12 ? 1 : 0 : 0, // Default value
      rfp: 0, // Default value
      map_service: 0, // Default value
      jobcost_unit_cost: 0, // Use cost as job cost unit cost
      // jobcost_quantity: projectCategory ? projectCategory == 12 ? serviceData.expected_hours : 0 : 0, // Use qty as job cost quantity
      // jobcost_total_cost:projectCategory ? projectCategory == 12 ? (serviceData.cost || 0) * (serviceData.expected_hours || 0) : 0 : 0, // Calculate job cost total
      jobcost_quantity: 0,
      jobcost_total_cost: 0,
      is_dos: 0, // Default value
      isAdditionalProduct: 0, // Default value
      makeAsTask: serviceData?.makeAsTask || 0, // Default value
      // makeInstallCard: 0, // Default value
      makeInstallCard:serviceData?.makeInstallCard || 0,
      service_notes: serviceData?.service_notes || null, // Add service notes
      status: 1,
      timestamp: moment().unix(),
      from_job:1,
      is_addition_service: serviceData.is_addition_service || 0,
      fixed_service_cost : serviceData?.expected_hours > 0 ? 1 : 0,
      facility_id : FACILITY_ID,
      delivery_type_id: null,
      installer_facility_id: null,
    };

    // Set delivery_type_id and installer_facility_id from existing non-recipe services if any
    const existingServices = await Recipe.fetchAllServicesByRequest(requestId) || [];
    const nonRecipeServices = existingServices.filter(s => s.recipe_id == null);
    const refService = nonRecipeServices.find(s => s.installer_facility_id != null || s.delivery_type_id != null);
    if (refService) {
      serviceToAdd.delivery_type_id = refService.delivery_type_id ?? null;
      serviceToAdd.installer_facility_id = refService.installer_facility_id ?? null;
    }

    console.log('Service data being sent to database:', serviceToAdd); // Debug log
    
    const result = await Request.addRequestServices(serviceToAdd);
    
    if (result) {
      // Log the activity
      console.log('serviceData.............', serviceData);
      let reqActivityMsg = `Request service "${serviceData?.service_name || serviceData?.name}" has been added`;
      const currentDate = moment().unix();  
      let requestActivity = {
        request_id: requestId,
        activity: reqActivityMsg,
        added_by: userId,
        timestamp: currentDate,
        status: 1
      };
      
      try {
        await RequestActivity.addRequestUpdate(requestActivity);
      } catch (logError) {
        console.log('Error logging activity:', logError);
        // Don't fail the main operation if logging fails
      }
  
      if (projectCategory == 12 && serviceData?.makeAsTask == 1) {
        try {
          let checkProjectExist = await Request.checkExistingProject(requestId);
          
          if (checkProjectExist) {
            // Get project data
            let projects = await Project.getProjectById(checkProjectExist);
            let requestData = await Request.getRequestById(requestId);
            console.log('🔍 projects:', projects);
            
            if (projects && requestData && requestData.length > 0) {
              console.log('✅ Project and request data found! Getting service workflow...');
              // Get service data with workflow information
              let serviceWithWorkflow = await Product.getServiceWithWorkflowById(serviceData.id);
              console.log('🔍 serviceWithWorkflow:', serviceWithWorkflow);
              let serviceWorkflow = [];
              
              if (serviceWithWorkflow && serviceWithWorkflow.workflow) {
                console.log('🔍 Service has workflow ID:', serviceWithWorkflow.workflow);
                serviceWorkflow = await Product.getWorkflowById(serviceWithWorkflow.workflow);
                console.log('🔍 serviceWorkflow:', serviceWorkflow);
              } else {
                console.log('ℹ️ No workflow found for service - will create task only');
              }
              
              const projectTask = {
                name: serviceWithWorkflow?.service_name || serviceData.service_name || serviceData.name || 'Service Task',
                project_id: checkProjectExist,
                timestamp: moment().unix(),
                userid: userId,
                start_date: projects.start_date || null,
                due_date: projects.installation_date || null,
                task_uid: await ProjectTask.getSubTaskUid(checkProjectExist),
                complete: 0,
                complete_sync: 1,
                status: 1,
                cover_image: null,
                product_id: null,
                service_id: serviceData.id,
                priority: 1,
                reporter_id: userId,
                description: "",
                project_task_uid: "",
                order_id: null,
                customer_order_product_id: result
              };

              console.log('🔍 projectTask data:', projectTask);
              const projectTaskId = await ProjectTask.addProjectTask(projectTask);
              console.log('🔍 projectTaskId created:', projectTaskId);
                
              if (projectTaskId) {
                console.log('✅ Main task created!');
                
                // Only create subtasks if workflow exists (like acceptOrRejectInvoice)
                if (serviceWorkflow && serviceWorkflow.length > 0) {
                  console.log('✅ Workflow found! Creating subtasks from workflow...');
                  // Create subtasks from workflow
                  for (const workflow of serviceWorkflow) {
                    console.log('🔍 Processing workflow stage:', workflow);
                    const subTask = {
                      name: workflow.name,
                      task_id: projectTaskId,
                      skill_ids: workflow?.skill_ids,
                      timestamp: moment().unix(),
                      userid: userId,
                      start_date: projects.start_date || null,
                      due_date: projects.installation_date || null,
                      task_uid: await ProjectTask.getSubTaskUid(projectTaskId),
                      complete: 0,
                      priority: 1,
                      status: 1,
                      reporter_id: userId,
                      description: workflow?.message || "",
                    };

                    console.log('🔍 subTask data:', subTask);
                    const projectSubTaskId = await ProjectTask.addProjectSubTask(subTask);
                    console.log('🔍 projectSubTaskId created:', projectSubTaskId);
                    
                    // Assign team members based on skills
                    if (projectSubTaskId && !empty(workflow?.skill_ids)) {
                      console.log('🔍 Assigning team members for skill_ids:', workflow?.skill_ids);
                      let projectManagers = await Project.getTeamMembersBasedOnSkills(workflow?.skill_ids, 14);
                      console.log('🔍 Found projectManagers:', projectManagers);
                      
                      for (const projectManager of projectManagers) {
                        let pm = {
                          project_id: checkProjectExist,
                          subtask_id: projectSubTaskId,
                          staff_id: !empty(projectManager["id"]) ? projectManager["id"] : null,
                          partner_id: null,
                          contact_id: null,
                          lead_id: null,
                        };

                        console.log('🔍 Adding member to subtask:', pm);
                        await ProjectTask.AddMembersToSubTask(pm);
                        
                        // Send notification to assigned team member
                        let notificationMsg = `New subtask "${workflow.name}" has been assigned to you in project "${projects.name}".`;
                        let notificationUrl = "Jobs";
                        
                        const incrementResult = await Admin.incrementField(
                          projectManager.user_id || 1,
                          "noti_count",
                          1
                        );

                        let notificationData = {
                          user_id: projectManager.user_id || 1,
                          url: notificationUrl,
                          timestamp: currentDate,
                          message: notificationMsg,
                          status: 1,
                          from_id: userId,
                        };

                        await Admin.addNotification(notificationData);
                        console.log('✅ Notification sent to user:', projectManager.user_id);
                      }
                    } else {
                      console.log('❌ No skill_ids or projectSubTaskId not created');
                    }
                  }
                  
                  // Add project members based on all subtask skills (like acceptOrRejectInvoice)
                  console.log('🔍 Adding project members based on all subtask skills...');
                  
                  // Collect all skill IDs from all workflow stages
                  let allSkillIds = serviceWorkflow
                    ?.map(workflow => workflow?.skill_ids?.split(','))
                    ?.flat()
                    ?.filter(skillId => skillId !== "" && skillId !== undefined);
                  
                  let uniqueSkillIds = [...new Set(allSkillIds)];
                  let uniqueSkillIdsString = uniqueSkillIds?.join(',');
                  uniqueSkillIdsString = uniqueSkillIdsString?.replace(/,$/, '');
                  
                  console.log('🔍 uniqueSkillIds for project members:', uniqueSkillIds);
                  console.log('🔍 uniqueSkillIdsString for project members:', uniqueSkillIdsString);
                  
                  let projectManagers = [];
                  if (!empty(uniqueSkillIdsString)) {
                    projectManagers = await Project.getTeamMembersBasedOnSkills(uniqueSkillIdsString, 14);
                    console.log('🔍 Found projectManagers for project:', projectManagers);
                    
                    // Add project members (like acceptOrRejectInvoice)
                    for (const projectManager of projectManagers) {
                      let pm = {
                        project_id: checkProjectExist,
                        staff_id: !empty(projectManager["id"]) ? projectManager["id"] : null,
                        partner_id: null,
                        contact_id: null,
                        lead_id: null,
                      };
                      
                      let alreadyExisting = await Project.projectMemberExist(pm);
                      console.log('🔍 Project member already exists:', alreadyExisting);
                      
                      if (!alreadyExisting) {
                        let insertId = await Project.addProjectMember(pm);
                        console.log('✅ Added project member:', insertId);
                      }
                    }
                  } else {
                    console.log('ℹ️ No skill IDs found for project member assignment');
                  }
                  
                  console.log(`✅ SUCCESS: Tasks and subtasks created for service: ${serviceWithWorkflow?.service_name || serviceData.service_name || serviceData.name}`);
                } else {
                  console.log('ℹ️ No workflow found - task created without subtasks');
                  console.log(`✅ SUCCESS: Task created for service: ${serviceWithWorkflow?.service_name || serviceData.service_name || serviceData.name}`);
                }
              } else {
                console.log('❌ Failed to create main task');
              }
            } else {
              console.log('❌ Project or request data not found');
            }
          } else {
            console.log('❌ No project exists for this request');
          }
        } catch (taskError) {
          console.log('❌ Error creating tasks/subtasks:', taskError);
          // Don't fail the main operation if task creation fails
        }
      } else {
        console.log('❌ Conditions not met for task creation');
        console.log('❌ projectCategory == 12:', projectCategory == 12);
        console.log('❌ serviceData?.makeAsTask == 1:', serviceData?.makeAsTask == 1);
      }

      let projects = await Project.getProjectById(projectId);
      let allProducts =[]
      if(projects?.project_category === 15){
        allProducts = await Product.getAllLocations(orderId)
      }else{
        allProducts = await Product.getNonStockProducts(orderId)
      }

      const allProductIds = allProducts?.split(',').map(id => parseInt(id, 10));

      let allServices = await Recipe.fetchAllServicesByRequest(orderId)
      const filteredServices = allServices.filter(service => {
        // If product_id is not null or undefined, include only if it's in allProductIds
        if (service.product_id != null) {
          return allProductIds.includes(service.product_id);
        }
        // If product_id is null or undefined, include by default
        return true;
      });

      res.send({ status: 1, message: "Service added successfully!", serviceId: result, services:filteredServices });
    } else {
      res.send({ status: 0, message: "Failed to add service" });
    }
  } catch (error) {
    console.log('Error adding request service:', error);
    res.send({ status: 0, message: 'Server error occurred while adding service' });
  }
}

const deleteRequestServiceById = async (req, res) => {
  try {
    const { serviceId, userId, from_job, is_recipe_group, request_id, projectId, orderId } = req.body;
    const serviceData = await Request.getRequestServiceById(serviceId);
    console.log("serviceData2.............", serviceData);
    if (!serviceId) {
      return res.send({ status: 0, message: 'Service ID is required' });
    }
    let projects = await Project.getProjectById(projectId);
    // Update the service status to 0 (deleted) instead of physically deleting
    let result = null;
    if(from_job === 1){
      result = await Request.deleteRequestServicesById(serviceId);
    }else if(is_recipe_group){
      result = await Request.updateProductRequestServiceStatus(request_id);
    }else{
      result = await Request.updateRequestServiceStatus(serviceId);
    }
    if(projects?.project_category == 12){
      result = await Request.deleteRequestServicesById(serviceId);
      const ProjectTaskDelete = await ProjectTask.deleteProjectTaskByServiceId(serviceId);
      const result1 = await ProjectTask.deleteStaffTaskTimelogsByServiceId(serviceId, projectId);
      console.log("result1",result1);
    }
    if (result) {
      // Log the activity
      let reqActivityMsg = `Request service "${serviceData?.service_name || serviceData?.name}" has been deleted`;
      const currentDate = moment().unix();  
      let requestActivity = {
        request_id: request_id, // Use the request_id parameter
        activity: reqActivityMsg,
        added_by: userId,
        timestamp: currentDate,
        status: 1
      };
      
      try {
        await RequestActivity.addRequestUpdate(requestActivity);
      } catch (logError) {
        console.log('Error logging activity:', logError);
        // Don't fail the main operation if logging fails
      }

      let allProducts =[]
      if(projects?.project_category === 15){
        allProducts = await Product.getAllLocations(orderId)
      }else{
        allProducts = await Product.getNonStockProducts(orderId)
      }

      const allProductIds = allProducts?.split(',').map(id => parseInt(id, 10));


      let allServices = await Recipe.fetchAllServicesByRequest(orderId)
      const filteredServices = allServices.filter(service => {
        // If product_id is not null or undefined, include only if it's in allProductIds
        if (service.product_id != null) {
          return allProductIds.includes(service.product_id);
        }
        // If product_id is null or undefined, include by default
        return true;
      });
      res.send({ status: 1, message: "Service deleted successfully!", services:filteredServices });
    } else {
      res.send({ status: 0, message: "Service not found or already deleted" });
    }
  } catch (error) {
    console.log('Error deleting request service:', error);
    res.send({ status: 0, message: 'Server error occurred while deleting service' });
  }
}

const sendInvoicePdfToEmail = async (req, reply) => {
  try {
    const nodemailer = require('nodemailer');
    const fs = require('fs');
    const path = require('path');
    const moment = require('moment');
    const axios = require('axios');
    const { invoiceTable } = require('../constants/emailTemplates');
    const Settings = require("../models/settings");
    const CustomerOrder = require('../models/customerOrder');
    const CustomerOrderProduct = require('../models/customerOrderProduct');
    const Requests = require('../models/request');

    const {
      request_id,
      userId,
      userType,
      recipientEmail,
      ccEmails = [],
      subject,
      message = '',
      invoiceData,
    } = req.body;

    if (!request_id) {
      return reply.send({ status: 0, message: 'Request ID is required' });
    }
    if (!recipientEmail || !recipientEmail.trim()) {
      return reply.send({ status: 0, message: 'Recipient email is required' });
    }
    if (!subject || !subject.trim()) {
      return reply.send({ status: 0, message: 'Subject is required' });
    }

    const timestamp = moment().unix();
    const tempDir = path.join(__dirname, '../uploads/temp');
    
    if (!fs.existsSync(tempDir)) {
      fs.mkdirSync(tempDir, { recursive: true });
    }

    // Get order data
    let orderData = await CustomerOrder.getCustomerOrderRequestId(request_id);
    if (!orderData || !orderData.id) {
      return reply.send({ status: 0, message: 'Order not found' });
    }

    let order_id = orderData.id;

    // Get products and services data (similar to sendMail function)
    const UNITS = [
      { id: 1, name: 'Square feet', short_name: 'Sq ft', unit_id: 1 },
      { id: 2, name: 'Linear feet', short_name: 'Lin ft', unit_id: 1 },
      { id: 3, name: 'Square inches', short_name: 'Sq in', unit_id: 1 },
      { id: 4, name: 'Unit', short_name: 'Unit', unit_id: 2 },
      { id: 5, name: 'Gallons', short_name: 'Gal', unit_id: 3 },
      { id: 6, name: 'Quarts', short_name: 'Qt', unit_id: 3 },
      { id: 7, name: 'Milliliter', short_name: 'ml', unit_id: 3 },
      { id: 8, name: 'Liter', short_name: 'L', unit_id: 3 },
      { id: 9, name: 'Pint', short_name: 'pt', unit_id: 3 },
      { id: 10, name: 'Half Pint', short_name: 'half-pt', unit_id: 3 },
      { id: 11, name: 'Ounce', short_name: 'oz', unit_id: 3 },
      { id: 12, name: 'Kilogram', short_name: 'kg', unit_id: 4 },
      { id: 13, name: 'Gram', short_name: 'g', unit_id: 4 },
      { id: 14, name: 'Milligram', short_name: 'mg', unit_id: 4 },
      { id: 15, name: 'Each', short_name: 'EA', unit_id: 2 },
    ];

    if (!order_id) {
      return reply.send({ status: 0, message: 'Order ID not found' });
    }

    let results = await CustomerOrderProduct.getCustomerOrderProductsbyId(order_id);
    let allProductServices = [];
    if (results && results.length > 0) {
      for (const product of results) {
        let services = await Product.getServiceByProduct(product.product_id);
        let resultArray = [];

        for (let serviceData of services) {
          let subResult = await Requests.getRequestServices(orderData?.request_id, serviceData.id, product.product_id, product.product_index);
          const newSubResult = subResult?.map(service => ({
            ...service,
            qtyData: product.quantity,
            locationQuantity: product.locationQuantity || 1
          }));
          if (newSubResult && newSubResult.length > 0) {
            resultArray.push(newSubResult);
          }
        }

        product.services = resultArray.flatMap(service => service);

        product.services.forEach(service => {
          const newQty = parseFloat((service.prQty * product.quantity * product.locationQuantity)?.toFixed(2));
          const quantityDifference = service.qty - newQty;
          if (newQty !== service.qty) {
            allProductServices.push({
              ...service,
              quantityDifference: quantityDifference,
              total_cost: service.unit_cost * quantityDifference
            });
          }
        });
      }
      orderData['cust_product'] = results;
    } else {
      orderData['cust_product'] = [];
    }
    orderData['allProductServices'] = allProductServices;

    const unitMapping = UNITS.reduce((acc, unit) => {
      acc[unit.id] = unit.short_name;
      return acc;
    }, {});

    orderData.cust_product.forEach(product => {
      const unitToUse = product?.partial_unit ? product?.partial_unit : product?.unit;
      if (unitToUse) {
        product.unitData = unitMapping[unitToUse] || product.unit;
      }
    });

    let result = await Requests.getAllRequests(orderData.requestId, 1, 1);
    orderData.labourCost = result.data[0].labour_cost;

    let requestServices = await Requests.getRequestServices(orderData.requestId);
    const newRequestServices = requestServices.filter(ord => ord.recipe_id === null);
    orderData.requestServices = newRequestServices;

    let profileData = await Settings.getCompanyProfileDetails();
    orderData.companyProfile = profileData[0];

    // Generate HTML invoice
    const invoiceHtml = invoiceTable(orderData);

    // Create PDF from HTML using a simple approach
    // Since no PDF library is available, we'll use html-pdf or similar
    // For now, let's use a simple method - save HTML and convert
    const htmlFilePath = path.join(tempDir, `invoice_${timestamp}.html`);
    fs.writeFileSync(htmlFilePath, invoiceHtml);

    // For PDF generation, try to use available libraries
    // Since user wants to use existing methods, we'll try puppeteer or html-pdf
    let pdfFilePath = null;

    try {
      // Try to use puppeteer if available
      let puppeteer;
      try {
        puppeteer = require('puppeteer');
        if (puppeteer) {
          const browser = await puppeteer.launch({ 
            headless: true,
            args: ['--no-sandbox', '--disable-setuid-sandbox']
          });
          const page = await browser.newPage();
          await page.setContent(invoiceHtml, { waitUntil: 'networkidle0' });
          const pdfBuffer = await page.pdf({
            format: 'A3',
            printBackground: true,
            margin: {
              top: '0.5in',
              right: '0.5in',
              bottom: '0.5in',
              left: '0.5in'
            }
          });
          await browser.close();
          pdfFilePath = path.join(tempDir, `invoice_${timestamp}.pdf`);
          fs.writeFileSync(pdfFilePath, pdfBuffer);
        }
      } catch (e) {
        // Puppeteer not available, try html-pdf
        try {
          const pdf = require('html-pdf');
          if (pdf) {
            pdfFilePath = path.join(tempDir, `invoice_${timestamp}.pdf`);
            await new Promise((resolve, reject) => {
              pdf.create(invoiceHtml, {
                format: 'A3',
                border: {
                  top: '0.5in',
                  right: '0.5in',
                  bottom: '0.5in',
                  left: '0.5in'
                }
              }).toFile(pdfFilePath, (err, res) => {
                if (err) {
                  reject(err);
                } else {
                  resolve(res);
                }
              });
            });
          }
        } catch (e2) {
          // No PDF library available, use HTML as attachment
          console.log('No PDF library available, using HTML as attachment');
          pdfFilePath = htmlFilePath;
        }
      }
    } catch (pdfError) {
      console.error('Error generating PDF:', pdfError);
      // If PDF generation fails, use HTML
      pdfFilePath = htmlFilePath;
    }

    if (!pdfFilePath) {
      // Fallback: use HTML file
      pdfFilePath = htmlFilePath;
    }

    const attachments = [];
    if (pdfFilePath && fs.existsSync(pdfFilePath)) {
      const fileName = `Invoice_${orderData.requestName || orderData.requestId}_${moment().format('YYYY-MM-DD')}.${pdfFilePath.endsWith('.html') ? 'html' : 'pdf'}`;
      attachments.push({
        filename: fileName,
        path: pdfFilePath,
        contentType: pdfFilePath.endsWith('.html') ? 'text/html' : 'application/pdf',
      });
    }

    if (attachments.length === 0) {
      return reply.send({
        status: 0,
        message: 'Failed to generate invoice PDF. Please check server configuration.'
      });
    }

    // Escape HTML for email
    function escapeHtml(text) {
      if (!text) return '';
      return text
        .replace(/&/g, '&amp;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#039;');
    }

    const escapedMessage = escapeHtml(message).replace(/\n/g, '<br>');
    const escapedOrderName = escapeHtml(orderData.requestName || `Order ${orderData.requestId}`);

    const emailHtml = `
      <!DOCTYPE html>
      <html>
      <head>
        <meta charset="utf-8">
        <style>
          body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
          .container { max-width: 600px; margin: 0 auto; padding: 20px; }
          .message { margin-top: 20px; white-space: pre-wrap; }
        </style>
      </head>
      <body>
        <div class="container">
          <h2>Invoice - ${escapedOrderName}</h2>
          ${escapedMessage ? `<div class="message">${escapedMessage}</div>` : ''}
          <p style="margin-top: 20px; color: #666;">
            Please find the invoice PDF attached to this email.
          </p>
        </div>
      </body>
      </html>
    `;

    // Validate and format recipient email
    const recipientEmailFormatted = recipientEmail.trim();

    // Filter and validate CC emails
    let ccEmailsFormatted = [];
    if (Array.isArray(ccEmails) && ccEmails.length > 0) {
      ccEmailsFormatted = ccEmails
        .map(email => typeof email === 'string' ? email.trim() : '')
        .filter(email => email && email.length > 0);
    }

    // Create transporter
    const transporter = nodemailer.createTransport({
      host: 'mail.signwisesolutions.com',
      port: 465,
      secure: true,
      auth: {
        user: 'noreply@signwisesolutions.com',
        pass: 'signwise@2024',
      },
      connectionTimeout: 20000,
      greetingTimeout: 20000,
      socketTimeout: 20000,
      tls: {
        rejectUnauthorized: false
      }
    });

    // Build mailOptions
    const mailOptions = {
      from: '"SignWise Solutions" <noreply@signwisesolutions.com>',
      to: recipientEmailFormatted,
      subject: subject.trim(),
      text: subject.trim(),
      html: emailHtml,
      attachments: attachments,
    };

    // Only add CC if there are CC emails
    if (ccEmailsFormatted.length > 0) {
      mailOptions.cc = ccEmailsFormatted;
    }

    console.log('📧 Preparing to send invoice email:', {
      to: recipientEmailFormatted,
      cc: ccEmailsFormatted.length > 0 ? ccEmailsFormatted : 'none',
      subject: subject.trim(),
      attachmentsCount: attachments.length
    });

    let emailStatus = 1;
    let errorMessage = null;

    // Send email
    try {
      const info = await transporter.sendMail(mailOptions);
      console.log(`✅ Invoice email sent successfully to: ${recipientEmailFormatted}`);
      console.log('📧 Email info:', {
        messageId: info.messageId,
        response: info.response,
        accepted: info.accepted,
        rejected: info.rejected
      });
      if (ccEmailsFormatted.length > 0) {
        console.log(`✅ CC emails: ${ccEmailsFormatted.join(', ')}`);
      }
    } catch (error) {
      console.error(`❌ Error sending invoice email to ${recipientEmailFormatted}:`, error);
      emailStatus = 0;
      errorMessage = error.message || 'Failed to send email';
    }

    // Cleanup temporary files
    try {
      if (fs.existsSync(htmlFilePath)) {
        fs.unlinkSync(htmlFilePath);
      }
      if (pdfFilePath && pdfFilePath !== htmlFilePath && fs.existsSync(pdfFilePath)) {
        fs.unlinkSync(pdfFilePath);
      }
    } catch (cleanupError) {
      console.error('Error cleaning up files:', cleanupError.message);
    }

    if (emailStatus === 1) {
      return reply.send({
        status: 1,
        message: 'Invoice email sent successfully'
      });
    } else {
      return reply.send({
        status: 0,
        message: errorMessage || 'Failed to send invoice email'
      });
    }

  } catch (error) {
    console.error('Error in sendInvoicePdfToEmail:', error);
    return reply.send({
      status: 0,
      message: 'An error occurred while sending the invoice email: ' + error.message
    });
  }
};

const approveSplitOrderInvoiceRequest = async (req, res) => {
  try {
    const { requestId, originRequestId } = req.body;
    const requestData = await Request.getRequestById(requestId);
    if(requestData[0]){

      const facilityTypes = (requestData[0]?.facility_type || "")
          .split(",")
          .map(v => v.trim())
          .filter(v => v !== "");
        const targetFacilityId = facilityTypes.filter(id => id !== String(FACILITY_ID));
      let updateRequestForOtherFacility = await SplitManagementController.updateBuildOrderToOtherInstallation(targetFacilityId, originRequestId, {
        min_payment_status :1
      })
      return res.send({ status: 1, message: 'Split order invoice approved successfully' });
    }
  } catch (error) {
    console.error('Error in approveSplitOrderInvoiceRequest:', error);
    res.send({ status: 0, message: 'Server error occurred while approving split order invoice' });
  }
}

const updateReceivedStatus = async (req, res) => {
  try {
    const { requestId } = req.body;

    if (!requestId) {
      return res.send({ status: 0, message: 'Request ID is required' });
    }

    const newRequestData = await Request.getRequestById(requestId);

    const getAllCustomerOrderProducts = await CustomerOrderProduct.getCustomerOrderProductsbyRequestId(requestId);
    const allProductIds = getAllCustomerOrderProducts?.map(product => product.product_id);
    console.log('allProductIds ===', allProductIds)

    // Update products where transfered_product_id matches the product IDs
    if (allProductIds && allProductIds.length > 0) {
      const updatedProductCount = await Product.updateProductsByTransferedProductIds(allProductIds);
    }

    const updateResult = await Request.updateRequets(requestId, { received_status: 2,split_mirror_status : 0 });

    const originFacilityRequestId = newRequestData[0]?.origin_facility_request_id;
    const originFacilityId = newRequestData[0]?.origin_facility_id;
    if(originFacilityRequestId && originFacilityId && updateResult){
      const data = {
        received_status: 0,
        split_mirror_status: 0
      }
      const updateOtherfacilityStatus = await SplitManagementModel.updateOriginBuildOrderRequestStatus(originFacilityRequestId,originFacilityId,data);
      console.log('updateOtherfacilityStatus ===', updateOtherfacilityStatus)
    }


    if (updateResult) {
      res.send({ status: 1, message: 'Received status updated successfully' });
    } else {
      res.send({ status: 0, message: 'Failed to update received status' });
    }
  } catch (error) {
    console.error('Error updating received status:', error);
    res.send({ status: 0, message: 'Server error occurred' });
  }
}

/**
 * Auto-save and manage individual product rows for Request Estimate
 * Supports create, update, and delete operations for product rows
 */
const updateProductRowEstimate = async (req, res) => {
  try {
    const { requestId, productRow, action, userId } = req.body;

    if (!requestId || !productRow || !action) {
      return res.status(400).send({ 
        status: 0, 
        message: 'Missing required parameters: requestId, productRow, action.' 
      });
    }

    // Get the corresponding order_id from customer_orders
    const customerOrder = await CustomerOrder.getCustomerOrderRequestId(requestId);
    if (!customerOrder || !customerOrder.id) {
      return res.status(404).send({ 
        status: 0, 
        message: 'Customer Order not found for the given Request ID.' 
      });
    }
    const orderId = customerOrder.id;

    let response = null;
    let message = '';
    const currentTime = moment().unix();

    switch (action) {
      case 'create':
        // Ensure that a temporary frontend indexId is provided
        if (productRow.indexId === undefined || productRow.indexId === null) {
          return res.status(400).send({ 
            status: 0, 
            message: 'Missing temporary frontend indexId for new product row.' 
          });
        }
        
        // Prepare data for insertion (only relevant fields for initial creation)
        const newProductData = {
          order_id: orderId,
          product_index: productRow.indexId,
          product_name: productRow.product_name || '',
          product_desc: productRow.product_desc || '',
          is_product_row: 1,
          status: 1,
          timestamp: currentTime,
          product_id: null,
          product_price: 0,
          product_retail_price: 0,
          product_markup: 0,
          product_qty: 1,
          location_quantity: 1,
          product_sub_total: 0,
          product_tax: 0,
          product_total: 0,
          discount: 0,
          adjustment: 0,
          rfp: 0,
          material: 0,
          allLocationSame: 1,
          isAdditionalProduct: 0,
          is_single_card: 0,
          install_cost: null,
          install_qty: 1,
          install_adjustment: 0,
          install_adj_value_type: 1,
          installMarkAdjType: 2,
          facility_id: null,
          split_percentage: 0,
          incoming_split_percentage: 0,
          expected_formula_price: 0,
          materialInvId: null,
          unit_cost: 0,
          delivery_type_id: null,
          installer_facility_id: null,
          adj_value_type: 1
        };
        
        const newProductId = await CustomerOrderProduct.createCustomerOrder(newProductData);
        if (newProductId) {
          message = 'Product row created successfully.';
          response = { backendId: newProductId, indexId: productRow.indexId };
          // Prepare the activity message
          const notification_msg = `Product "${productRow.product_name}" has been added .`;

          // Add an entry to the RequestActivity table after successfully adding the product row
          let requestActivity = {
            request_id: requestId,
            activity: notification_msg,
            added_by: userId,
            timestamp: currentTime,
            status: 1
          };

          let reqActivityId = await RequestActivity.addRequestUpdate(requestActivity);
        } else {
          throw new Error('Failed to create product row in database.');
        }
        break;

      case 'update':
        if (!productRow.backendId) {
          return res.status(400).send({ 
            status: 0, 
            message: 'Missing backendId for product row update.' 
          });
        }
        const oldProductDetails = await CustomerOrderProduct.getProductDetailsById(productRow.backendId);

        const updateFields = {
          product_name: productRow.product_name,
          product_desc: productRow.product_desc,
          timestamp: currentTime
        };
        const updateResult = await CustomerOrderProduct.updateCustomerOrderProduct(updateFields, productRow.backendId);
        if (updateResult && updateResult[0] > 0) {
          message = 'Product row updated successfully.';
          response = { backendId: productRow.backendId, indexId: productRow.indexId };
          // Prepare the activity message for update
          let notification_msg = '';

           // Only log the update if there is a change in the name or description
        if (oldProductDetails.product_name !== productRow.product_name || oldProductDetails.product_desc !== productRow.product_desc) {
          // If the name changed, include it in the message
          if (oldProductDetails.product_name !== productRow.product_name) {
              notification_msg = `Product "${oldProductDetails.product_name}" has been updated to "${productRow.product_name}"`;
          }

          // If the description changed, append it to the message
          if (oldProductDetails.product_desc !== productRow.product_desc) {
              if (notification_msg) {
                  notification_msg += `${productRow.product_name}'s Description changed from "${oldProductDetails.product_desc}" to "${productRow.product_desc}"`;
              } else {
                  notification_msg = `Product ${productRow.product_name}'s Description changed from "${oldProductDetails.product_desc}" to "${productRow.product_desc}"`;
              }
          }
          notification_msg += '.';
         
          // Prepare the requestActivity object
          let requestActivity = {
            request_id: requestId,
            activity: notification_msg,
            added_by: userId,
            timestamp: currentTime,
            status: 1
          };
          // Add the activity to the RequestActivity table
          let reqActivityId = await RequestActivity.addRequestUpdate(requestActivity);
        }
        } else {
          throw new Error('Failed to update product row in database or row not found.');
        }
        break;

      case 'delete':
        if (!productRow.backendId) {
          return res.status(400).send({ 
            status: 0, 
            message: 'Missing backendId for product row deletion.' 
          });
        }
        const productDetails = await CustomerOrderProduct.getProductDetailsById(productRow.backendId);
        // Hard delete - permanently remove the product row from database
        const deleteResult = await CustomerOrderProduct.deleteCustomerOrderProductById(
          productRow.backendId
        );
        if (deleteResult) {
          message = 'Product row deleted successfully.';
          response = { backendId: productRow.backendId };
          // Prepare the activity message for delete
          const notification_msg = `Product "${productDetails.product_name}" has been deleted .`;

          // Prepare the requestActivity object
          let requestActivity = {
            request_id: requestId,
            activity: notification_msg,
            added_by: userId,
            timestamp: currentTime,
            status: 1
          };

    // Add the activity to the RequestActivity table
    let reqActivityId = await RequestActivity.addRequestUpdate(requestActivity);
        } else {
          throw new Error('Failed to delete product row in database or row not found.');
        }
        break;

      default:
        return res.status(400).send({ 
          status: 0, 
          message: 'Invalid action specified. Use: create, update, or delete.' 
        });
    }

    res.send({ status: 1, message, data: response });
  } catch (error) {
    console.error('Error in updateProductRowEstimate:', error);
    res.status(500).send({ 
      status: 0, 
      message: 'Server error during product row operation.', 
      error: error.message 
    });
  }
};

const updateServiceRowEstimate = async (req, res) => {
  try {
    const { requestId, productRow, action, userId } = req.body;

    if (!requestId || !productRow || !action) {
      return res.status(400).send({ 
        status: 0, 
        message: 'Missing required parameters: requestId, productRow, action.' 
      });
    }

    // Get the corresponding order_id from customer_orders
    const customerOrder = await CustomerOrder.getCustomerOrderRequestId(requestId);
    if (!customerOrder || !customerOrder.id) {
      return res.status(404).send({ 
        status: 0, 
        message: 'Customer Order not found for the given Request ID.' 
      });
    }
    const orderId = customerOrder.id;

    let response = null;
    let message = '';
    const currentTime = moment().unix();

    switch (action) {
      case 'create':
        // Ensure that a temporary frontend indexId is provided
        if (productRow.indexId === undefined || productRow.indexId === null) {
          return res.status(400).send({ 
            status: 0, 
            message: 'Missing temporary frontend indexId for new service row.' 
          });
        }
        
        // Prepare data for insertion (only relevant fields for initial creation)
        const serviceData = {
          product_index: productRow?.indexId,
          service_name: productRow?.service_name || '',
          service_desc: productRow?.service_desc || '',
          is_service_row: 1,
          request_id: requestId,
          labour_cost_service_id: null,
          recipe_id: null,
          product_id: null,
          qty: 1,
          markup: 0,
          unit_cost: 0,
          total_cost: 0,
          job_cost: 0,
          taken: 0,
          rfp: 0,
          map_service: 0,
          jobcost_unit_cost: 0,
          jobcost_quantity: 0,
          jobcost_total_cost: 0,
          is_dos: 0,
          makeAsTask: 0,
          makeInstallCard: 0,
          status: 1,
          timestamp: currentDate,
          facility_id: productRow?.facility_id || null,
        };
        const newServiceId = await Request.addRequestServices(serviceData);
        console.log('newServiceId ===', newServiceId)
        if (newServiceId) {
          message = 'Service row created successfully.';
          response = { backendId: newServiceId, indexId: productRow.indexId };
        } else {
          throw new Error('Failed to create service row in database.');
        }
        break;

      case 'update':
        if (!productRow.backendId) {
          return res.status(400).send({ 
            status: 0, 
            message: 'Missing backendId for service row update.' 
          });
        }
        const updateFields = {
          service_name: productRow.service_name,
          service_desc: productRow.service_desc,
          timestamp: currentTime
        };
        const updateResult = await Request.updateRequestServices(updateFields, productRow?.backendId, requestId);
        console.log('updateResult ===', updateResult)
        if (updateResult) {
          message = 'Service row updated successfully.';
          response = { backendId: productRow.backendId, indexId: productRow.indexId };
        } else {
          throw new Error('Failed to update service row in database or row not found.');
        }
        break;

      case 'delete':
        if (!productRow.backendId) {
          return res.status(400).send({ 
            status: 0, 
            message: 'Missing backendId for service row deletion.' 
          });
        }
        // Hard delete - permanently remove the product row from database
        const deleteResult = await Request.deleteRequestServicesById(
          productRow?.backendId
        );
        console.log('deleteResult ===', deleteResult)
        if (deleteResult) {
          message = 'Service row deleted successfully.';
          response = { backendId: productRow.backendId };
        } else {
          throw new Error('Failed to delete service row in database or row not found.');
        }
        break;

      default:
        return res.status(400).send({ 
          status: 0, 
          message: 'Invalid action specified. Use: create, update, or delete.' 
        });
    }

    res.send({ status: 1, message, data: response });
  } catch (error) {
    console.error('Error in update service Row Estimate:', error);
    res.status(500).send({ 
      status: 0, 
      message: 'Server error during product row operation.', 
      error: error.message 
    });
  }
};

/**
 * Delete auto-created Draft Estimate (Request Estimate only)
 * Also deletes all its product rows
 */
const deleteDraftEstimate = async (req, reply) => {
  try {
    const { requestId, userId, isRequestEstimate } = req.body;

    if (!requestId) {
      return reply.send({
        status: 0,
        message: "Request ID is required"
      });
    }

    // 1️⃣ Get Request
    const requestData = await Request.getRequestById(requestId);

    if (!requestData || !requestData[0]) {
      return reply.send({
        status: 0,
        message: "Request not found"
      });
    }

    const request = requestData[0];

    // 2️⃣ Safety check — Only delete Draft Estimate
    // Adjust conditions according to your DB fields
    if (
      request.quotation != 1 ||
      request.is_request_estimate != 1 ||
      request.quotation_status != 2 ||   // 👈 allow draft only
      request.estimate_id != null        // prevent converted ones
    ) {
      return reply.send({
        status: 0,
        message: "Only draft estimate can be deleted"
      });
    }

    // 3️⃣ Get related customer order
    const customerOrder = await CustomerOrder.getCustomerOrderRequestId(requestId);

    if (customerOrder && customerOrder.id) {
      const orderId = customerOrder.id;

      // 4️⃣ Delete all product rows for this order
      await CustomerOrderProduct.deleteAllCustomerOrderProductsByOrderId(orderId);

      // 5️⃣ Delete customer order
      await CustomerOrder.deleteCustomerOrderById(orderId);

      // delete services from order
      await Request.deleteRequestServicesWithRequestId(requestId);
    }

    // 6️⃣ Delete request itself
    await Request.destroyRequestById(requestId);

    return reply.send({
      status: 1,
      message: "Draft estimate deleted successfully"
    });

  } catch (error) {
    console.error("Error in deleteDraftEstimate:", error);
    return reply.send({
      status: 0,
      message: "Server error while deleting draft estimate"
    });
  }
};

const convertEstimateToDesignOrder = async (req, reply) => {
  try {
    let { requestId, userId, socketId } = req.body;
    let currentDate = moment().unix();

    // 📊 Progress tracking function
    const emitProgress = (step, message, progress = 0) => {
      console.log(`[CONVERSION-PROGRESS] 📊 Progress: ${step} - ${message} (${progress}%)`);

      const progressData = {
        socketId: socketId,
        userId: userId,
        step: step,
        message: message,
        progress: progress,
        timestamp: moment().unix(),
        domain: DOMAIN
      };

      // Emit to working socket server
      console.log(`[CONVERSION-PROGRESS] 📡 Emitting progress to working socket server...`);
      try {
        const workingSocket = ioClient(REST_API_IO, {
          path: '/socket.io',
          transports: ['websocket', 'polling'],
          secure: true,
          timeout: 5000
        });

        workingSocket.on('connect', () => {
          console.log(`[CONVERSION-PROGRESS] 🔌 Connected to working socket server`);
          console.log(`[CONVERSION-PROGRESS] 📊 Emitting progress data:`, progressData);

          // Emit progress data
          workingSocket.emit('request_progress', progressData);

          console.log(`[CONVERSION-PROGRESS] ✅ Successfully emitted progress to working socket server`);

          // Disconnect after emitting
          setTimeout(() => {
            workingSocket.disconnect();
          }, 1000);
        });

        workingSocket.on('connect_error', (error) => {
          console.error(`[CONVERSION-PROGRESS] ❌ Working socket connection error:`, error);
        });

      } catch (error) {
        console.error(`[CONVERSION-PROGRESS] ❌ Error creating working socket connection:`, error);
      }
    };

    // Initial progress
    console.log(`[CONVERSION-PROGRESS] 🚀 Starting estimate conversion to Design Order for user:`, userId);
    emitProgress('start', 'Initializing estimate conversion to Design Order...', 5);
    
    // Get request details
    emitProgress('fetching_request', 'Fetching estimate details...', 15);
    let request = await Request.getRequestById(requestId);
    if (!request || request.length === 0) {
      emitProgress('request_not_found', 'Request not found', 100);
      return reply.send({ status: 0, message: "Request not found" });
    }

    let requestData = request[0];
    console.log('requestData ===', requestData)
    // Create design order data from request
    emitProgress('preparing_data', 'Preparing design order data...', 25);
    let designOrderData = {
      name: requestData?.name || 'Design Order from Estimate',
      description: requestData?.description || '',
      client: requestData?.customer_id || null,
      community: requestData?.community || null,
      total_cost: requestData?.total || 0,
      added_by: userId,
      request_id: requestId,
      start_date: requestData?.start_date || currentDate || null,
      created_at: currentDate,
      due_date: requestData?.due_date || currentDate || null,
      notes: requestData?.notes || '',
      status: 1, // Active status
      completion: 1,
      design_order_job_status: 1,
      client_approve: 0,
      designer_email_sent: 0,
      designer_sms_sent: 0,
      hold: 0,
      send_client_app_req: 0,
      edited_by: null,
      working_designer_id: null,
      timestamp: currentDate
    };

    // Create or update the design order (if already converted before)
    emitProgress('creating_design_order', 'Creating design order...', 35);
    let designOrderId = requestData?.design_order_id || null;
    if (designOrderId) {
      await Request.updateDesignOrders(designOrderId, designOrderData);
      // Delete existing design order products when updating
      emitProgress('deleting_existing_products', 'Deleting existing design order products...', 37);
      await Request.deleteDesignOrderProductsByDesignOrderId(designOrderId);
    } else {
      designOrderId = await Request.addDesignOrders(designOrderData);
    }
    
    if (designOrderId) {
      // Get services from the request where labour_cost_service_id == 33
      if (!requestData?.design_order_id) {
        emitProgress('processing_services', 'Processing services...', 45);
        let requestServices = await Request.getAllServiceByRequest(requestId);
        if (requestServices && requestServices.length > 0) {
          for (const service of requestServices) {
            if (service.service_type == 33) {
              let serviceData = {
                labour_cost_service_id: service.labour_cost_service_id,
                quantity: service.qty || 1,
                unit_cost: service.unit_cost || service.originalCost || 0,
                total: service.total || (service.qty * (service.unit_cost || service.originalCost || 0)),
                design_order_id: designOrderId,
                taken: 0,
                job_cost: 0,
                jobcost_quantity: 0,
                jobcost_total_cost: 0,
                complete: 0,
                status: 1
              };
              await Request.addDesignOrderServices(serviceData);
            }
          }
        }
      }

      // Add non-material products from customer order into design order products
      emitProgress('processing_products', 'Processing products from customer order...', 50);
      const customerOrderProducts = await CustomerOrderProduct.getCustomerOrderProductsbyRequestId(requestId);
      if (customerOrderProducts && customerOrderProducts.length > 0) {
        const nonMaterialProducts = customerOrderProducts.filter(
          (product) => Number(product.material) === 0
        );

        if (nonMaterialProducts?.length > 0) {
          // Get existing design order products to continue product_index sequence
          const existingDesignOrderProducts = await Request.getDesignOrderProductsByDesignOrderId(designOrderId);
          let startIndex = 1;

          if (existingDesignOrderProducts && existingDesignOrderProducts.length > 0) {
            const maxExistingIndex = existingDesignOrderProducts.reduce((max, prod) => {
              const idx = Number(prod.product_index) || 0;
              return idx > max ? idx : max;
            }, 0);
            startIndex = maxExistingIndex + 1;
          }

          for (const product of nonMaterialProducts) {
            const productData = {
              design_order_id: designOrderId,
              product_index: startIndex,
              name: (product.product_name || product.name || '').trim(),
              description: product.product_desc || product.description || '',
              status: 1,
            };
            const result = await Request.addDesignOrderProducts(productData);
            console.log('result====design order products===',result)
            startIndex += 1;
          }
        }
      }

      // Get attachments from the request
      emitProgress('processing_attachments', 'Processing attachments...', 55);
      if (requestData?.design_order_id) {
        await Request.deleteDesignOrderAttachments(designOrderId);
      }
      let requestAttachments = await Request.getAllRequestAttachments(requestId);
      if (requestAttachments && requestAttachments.length > 0) {
        for (const attachment of requestAttachments) {
          let attachmentData = {
            name: attachment.name,
            design_order_id: designOrderId,
            extension: attachment?.extension || 'img',
            file_size: attachment?.file_size || 0,
            file_type: attachment?.file_type || 'img',
            approved: 0,
            approved_by: null,
            added_by: userId,
            reason: null,
            timestamp: currentDate,
            isChat: 0,
            status: 1,
            isDesign: attachment?.isDesign || 2,
            isNew: 1,
            is_reference: 0
          };
          const newDoAttachmentId = await Request.addDesignOrderAttachments(attachmentData);
          // ✅ Copy images from request (including edited pages); only convert PDF when no images exist
          const existingImages = await getRequestAttachmentImagesByDocumentId(attachment.id, 'request');
          if (existingImages && existingImages.length > 0) {
            await copyAttachmentImagesToNewDocument(attachment.id, 'request', newDoAttachmentId, 'design_order');
          } else {
            // const isPdf =
            //   (attachment.file_type === 'application/pdf' || attachment.file_type === 'pdf' || (attachment.extension || '').toLowerCase() === 'pdf');
            // if (isPdf && newDoAttachmentId) {
            //   try {
            //     await convertDesignOrderPdfToPngHelper(newDoAttachmentId);
            //     console.log(`PDF conversion completed for design order attachment ID: ${newDoAttachmentId}`);
            //   } catch (conversionError) {
            //     console.error(`Error converting PDF for design order attachment ${newDoAttachmentId}:`, conversionError);
            //   }
            // }
          }
        }
      }

      // Update request status to 0 (archived/converted)
      emitProgress('updating_request', 'Updating request status...', 65);
      await Request.updateRequets(requestId, {
        status: 0,
        timestamp: currentDate,
        isFromDesignOrder: 1,
        design_order_id: designOrderId
      });

      // Add request activity
      emitProgress('adding_activities', 'Adding activity logs...', 75);
      let requestActivity = {
        request_id: requestId,
        activity: "Estimate has been converted to Design Order",
        added_by: userId,
        timestamp: currentDate,
        status: 1
      };
      await RequestActivity.addRequestUpdate(requestActivity);

        let requestActivity1 = {}
          requestActivity1['design_order_id'] = designOrderId;
          requestActivity1['activity'] = `🔄 ${requestData.is_request_estimate == 1 ? 'Request Estimate' : 'Estimate'} "${requestData.name}" has been converted to Design Order`;
          requestActivity1['added_by'] = userId;
          requestActivity1['timestamp'] = currentDate
        let reqActivityId = await RequestActivity.addDesignOrderUpdate(requestActivity1);

      // Send notifications
      emitProgress('sending_notifications', 'Sending notifications...', 85);
      let notificationMsg = `The estimate "${requestData.name}" has been converted to Design Order.`;
      let notificationUrl = `design-order/view/${designOrderId}`;

      // Notify the user who converted
      const incrementResult_User = await Admin.incrementField(userId, "noti_count", 1);
      let notificationData_user = {
        user_id: userId,
        url: notificationUrl,
        timestamp: currentDate,
        message: notificationMsg,
        status: 1,
        from_id: userId,
      };
      await Admin.addNotification(notificationData_user);

      // Notify admins
      let adminIds = await Admin.getAllAdminIds();
      for (const adminId of adminIds) {
        if (adminId != userId) {
          const incrementResult = await Admin.incrementField(adminId, "noti_count", 1);
          let notificationData = {
            user_id: adminId,
            url: notificationUrl,
            timestamp: currentDate,
            message: notificationMsg,
            status: 1,
            from_id: userId,
          };
          await Admin.addNotification(notificationData);
        }
      }

      emitProgress('conversion_complete', 'Estimate converted to Design Order successfully!', 100);
      reply.send({ status: 1, message: "Estimate converted to Design Order successfully", designOrderId: designOrderId });
    } else {
      emitProgress('creation_failed', 'Failed to create Design Order', 100);
      reply.send({ status: 0, message: "Failed to create Design Order" });
    }
  } catch (error) {
    console.log(error, 'Error in converting estimate to design order');
    emitProgress('conversion_error', `Conversion failed: ${error.message}`, 100);
    reply.send({ status: 0, message: "Error in converting estimate to design order" });
  }
}

const revertRequestStatus = async (req, reply) => {
  try {
    const { requestId, userId, targetStatus, targetQuotationStatus, targetProjectStatus, targetInstallationStatus, installationId } = req.body;
    const currentTime = moment().unix();

    if (!requestId || !userId) {
      return reply.send({ status: 0, message: "Request ID and User ID are required." });
    }

    // Handle installation-only status revert (e.g., Completed → Installing, or Installing/Completed → Staged).
    // This does NOT touch the request or project status.
    if (typeof targetInstallationStatus !== 'undefined') {
      const ts = Number(targetInstallationStatus);

      // Only 1 (Staged) and 2 (Installing) are valid targets.
      if (ts !== 1 && ts !== 2) {
        return reply.send({ status: 0, message: `Invalid target installation status ${ts}. Only 1 (Staged) or 2 (Installing) are allowed.` });
      }

      let activityLabel = ts === 2 ? 'Installing' : 'Staged';

      if (installationId) {
        if (ts === 2) {
          // Completed (4) → Installing (2): status change only — preserve assignees, route, and timelogs.
          await Installation.updateInstallationStatus(
            { status: 2, timestamp: currentTime },
            installationId
          );
        } else {
          // → Staged (1): clear assignees only — keep route_id so the next assignment
          // can find the existing route and correctly set status 2.
          await Installation.updateInstallationStatus(
            {
              status: 1, // staged
              team_id: null,
              driver_id: null,
              driver_is_installer: 1,
              timestamp: currentTime,
            },
            installationId
          );

          // Delete staff clock-in/timelogs for this installation.
          const sequelizeLocal = require('../config/dbconnection');
          await sequelizeLocal.query(
            `DELETE FROM staff_task_timelogs WHERE installation_id = :installationId`,
            { replacements: { installationId: Number(installationId) }, type: sequelizeLocal.QueryTypes.DELETE }
          );
        }
      } else if (requestId) {
        // Handle sales order level revert where no specific installationId is provided
        const project_id = await Request.checkExistingProject(requestId);
        if (!project_id) {
          return reply.send({ status: 0, message: "Project not found for this request." });
        }

        const sequelizeLocal = require('../config/dbconnection');
        const installations = await Installation.getInstallationsByProjectId(project_id);
        const idsToStage = [];

        for (const inst of (installations || [])) {
          const instId = Number(inst?.id);
          if (!instId) continue;

          const instStatus = Number(inst?.status);
          // Only process Completed (4) installations, OR Installing (2) if we're reverting back to Staged (1)
          if (instStatus !== 4 && !(instStatus === 2 && ts === 1)) continue; 

          const completeStatus = Number(inst?.complete_status);
          const desiredInstallationStatus = (ts === 2 && [2, 3].includes(completeStatus)) ? 1 : ts;

          const updatePayload = {
            status: desiredInstallationStatus,
            complete_status: 1, // move back to "Installation" flow in UI
            timestamp: currentTime,
          };

          if (desiredInstallationStatus === 1) {
            updatePayload.team_id = null;
            updatePayload.driver_id = null;
            updatePayload.driver_is_installer = 1;
            idsToStage.push(instId);
          }

          await Installation.updateInstallationStatus(updatePayload, instId);
        }

        const uniqueIdsToStage = [...new Set(idsToStage)];
        if (uniqueIdsToStage.length > 0) {
          await sequelizeLocal.query(
            `DELETE FROM staff_task_timelogs
             WHERE installation_id IN (${uniqueIdsToStage.join(',')})`,
            { type: sequelizeLocal.QueryTypes.DELETE }
          );
        }
      }

      const currentRequestData = await Request.getRequestById(requestId);
      const currentRequest = currentRequestData?.[0];

      if (currentRequest) {
        await RequestActivity.addRequestUpdate({
          request_id: requestId,
          activity: `Installation status reverted to ${activityLabel} for "${currentRequest.name}".`,
          added_by: userId,
          timestamp: currentTime,
          status: 1,
        });
      }

      return reply.send({ status: 1, message: `Installation status reverted to ${activityLabel} successfully.` });
    }

    // Helpers for installation-stage revert/cleanup (only used in this endpoint).
    const sequelize = require('../config/dbconnection');

    const revertInstallingInstallationsToStaged = async (projectId) => {
      const installations = await Installation.getInstallationsByProjectId(projectId);
      const installingInstallationIds = (installations || [])
        // Also include already-staged installations (1) so assignees/timelogs are cleared
        // when we revert a job back to production/pre-production.
        .filter((inst) => {
          const st = Number(inst?.status);
          return st === 1 || st === 2; // 1 => staged, 2 => installing
        })
        .map((inst) => Number(inst?.id))
        .filter(Boolean);

      if (installingInstallationIds.length === 0) return;

      // Clear assignees and move back to staged (status: 1).
      // Do NOT clear route_id — the route record still exists so keeping the link
      // lets the assign endpoint find it and correctly set status 2 on re-assignment.
      for (const instId of installingInstallationIds) {
        await Installation.updateInstallationStatus(
          {
            status: 1, // staged
            team_id: null,
            driver_id: null,
            driver_is_installer: 1,
            timestamp: currentTime,
          },
          instId
        );
      }

      // Delete staff clock-in/timelogs for these installations.
      await sequelize.query(
        `DELETE FROM staff_task_timelogs
         WHERE installation_id IN (${installingInstallationIds.join(',')})`,
        { type: sequelize.QueryTypes.DELETE }
      );
    };

    // When the whole request is already "all installations completed",
    // we should revert completed installations/pickups back to staged/installing
    // (instead of deleting installation records).
    const revertCompletedInstallationsToStagedOrInstalling = async (projectId, targetProjectStatus) => {
      const installations = await Installation.getInstallationsByProjectId(projectId);
      const idsToStage = [];

      for (const inst of (installations || [])) {
        const instId = Number(inst?.id);
        if (!instId) continue;

        const instStatus = Number(inst?.status);
        // allInstallationCompleted is based on completed installations; keep this revert narrow
        // to avoid impacting installations already in staged/installing states.
        if (instStatus !== 4) continue;

        // installation.complete_status is used by the UI to differentiate Install / Pick up / Shipping.
        const completeStatus = Number(inst?.complete_status);

        // UI behavior: when current installation is pickup/shipping, reverting to "Installing" is not allowed.
        // So for those cases we revert to staged only.
        let desiredInstallationStatus;
        if (Number(targetProjectStatus) === 1) {
          desiredInstallationStatus = 1; // staged
        } else {
          // targetProjectStatus === 2
          desiredInstallationStatus = [2, 3].includes(completeStatus) ? 1 : 2;
        }

        const updatePayload = {
          status: desiredInstallationStatus,
          complete_status: 1, // move back to "Installation" flow in UI
          timestamp: currentTime,
        };

        if (desiredInstallationStatus === 1) {
          // staged: clear assignees + let next assignment re-pick route
          updatePayload.team_id = null;
          updatePayload.driver_id = null;
          updatePayload.driver_is_installer = 1;
          idsToStage.push(instId);
        }

        await Installation.updateInstallationStatus(updatePayload, instId);
      }

      // staged cleanup: remove clock-in/timelogs for these installations
      const uniqueIdsToStage = [...new Set(idsToStage)];
      if (uniqueIdsToStage.length === 0) return;

      await sequelize.query(
        `DELETE FROM staff_task_timelogs
         WHERE installation_id IN (${uniqueIdsToStage.join(',')})`,
        { type: sequelize.QueryTypes.DELETE }
      );
    };

    const deleteOtherInstallationsByRequestId = async (currentProjectId) => {
      const [otherProjectsResult] = await sequelize.query(
        `SELECT id
         FROM projects
         WHERE request_id = :requestId
           AND status != 0
           AND id != :currentProjectId`,
        {
          replacements: { requestId, currentProjectId },
          type: sequelize.QueryTypes.SELECT,
        }
      );

      const otherProjectIds = (otherProjectsResult || []).map((row) => row.id).filter(Boolean);
      for (const pid of otherProjectIds) {
        await Installation.deleteInstallationUsingProjectId(pid);
        await ProjectTask.deleteStaffTaskTimelogs(pid);
      }
    };

    const currentRequestData = await Request.getRequestById(requestId);
    if (!currentRequestData || currentRequestData.length === 0) {
      return reply.send({ status: 0, message: "Request not found." });
    }

    const currentRequest = currentRequestData[0];
    const currentStatus = currentRequest.status;
    const isQuotation = currentRequest.quotation === 1 || currentRequest.is_request_estimate === 1;
    const allInstallationCompleted =
      Number(
        currentRequest?.allInstallationCompleted ??
          // tolerate older/typo field names if present
          currentRequest?.allInstallationsCompleted ??
          currentRequest?.allInstallionsCmpleted ??
          0
      ) === 1;

    let newStatus = currentStatus;
    let newQuotationStatus = currentRequest.quotation_status;
    let statusChanged = false;

    if (isQuotation) {
      // Allowed backwards flow for quotation_status:
      // 9 (Approved) -> 4 or 2
      // 4 / 6 / 3 -> 2
      if (typeof targetQuotationStatus !== "undefined") {
        const qs = Number(targetQuotationStatus);
        const currentQS = Number(currentRequest.quotation_status);

        let allowedTargets = [];
        if (currentQS === 9) {
          allowedTargets = [4, 2];
        } else if (currentQS === 4 || currentQS === 6 || currentQS === 3) {
          allowedTargets = [2];
        }

        if (!allowedTargets.includes(qs)) {
          return reply.send({
            status: 0,
            message: `Invalid target quotation_status ${qs} for current quotation_status ${currentQS}.`,
          });
        }

        newQuotationStatus = qs;
        newStatus = 8;
        statusChanged = true;
      } else {
        switch (currentRequest.quotation_status) {
          case 4: // Awaiting Client Approval
          case 6: // Rejected
          case 3: // Internal estimate state
            newQuotationStatus = 2; // New/Open
            newStatus = 8; // Estimate main status
            statusChanged = true;
            break;
          case 9: // Approved by Client
            newQuotationStatus = 4; // Back to Awaiting Client Approval
            newStatus = 8;
            statusChanged = true;
            break;
          default:
            console.log(`No explicit previous status defined for quotation_status: ${currentRequest.quotation_status}`);
        }
      }
    } else {
      // Sales order main status backwards flow:
      // 4 (In Production) -> 3 or 2
      // 3 (Approved/Invoicing) -> 2
      // 6 (Rejected) -> 2
      // 0 (Archived) -> 2
      if (typeof targetStatus !== "undefined") {
        const ts = Number(targetStatus);
        const cs = Number(currentStatus);

        let allowedTargets = [];
        if (cs === 4) {
          allowedTargets = [3, 2];
        } else if (cs === 3 || cs === 6 || cs === 0) {
          allowedTargets = [2];
        }

        if (!allowedTargets.includes(ts)) {
          return reply.send({
            status: 0,
            message: `Invalid target status ${ts} for current status ${cs}.`,
          });
        }

        newStatus = ts;
        statusChanged = true;
      } else {
        switch (currentStatus) {
          case 4: // In Production
            newStatus = 3; // Approved/Invoicing
            statusChanged = true;
            break;
          case 3: // Approved/Invoicing
            newStatus = 2; // Open
            statusChanged = true;
            break;
          case 6: // Rejected
            newStatus = 2; // Open
            statusChanged = true;
            break;
          case 0: // Archived
            newStatus = 2; // Open
            statusChanged = true;
            break;
          default:
            console.log(`No explicit previous status defined for status: ${currentStatus}`);
        }
      }
    }

    if (!statusChanged) {
      return reply.send({ status: 1, message: "No change in status needed or no defined previous status." });
    }

    if (!isQuotation && currentStatus === 4 && typeof targetProjectStatus !== "undefined") {
      const project_id = await Request.checkExistingProject(requestId);
      if (!project_id) {
        return reply.send({ status: 0, message: "Project not found for this request." });
      }

      const projectData = await Project.getProjectById(project_id);
      const currentProjectStatus = Array.isArray(projectData) ? projectData[0]?.status : projectData?.status;

      // If current project status is 1 (Production), do NOT do project-only change.
      // Let the normal revert flow run, which will revert request status and delete the project.
      if (currentProjectStatus !== 1) {
        const ps = Number(targetProjectStatus);

        // If job is currently in Installation (status 3) and we are reverting back
        // to Production (2) or Pre-Production (1), revert installations to staged
        // (clear assignees + delete staff clock-ins) and remove installations from other
        // projects under the same request_id.
        if (currentProjectStatus === 3 && (ps === 1 || ps === 2)) {
          try {
            if (allInstallationCompleted) {
              await revertCompletedInstallationsToStagedOrInstalling(project_id, ps);
            } else {
              await revertInstallingInstallationsToStaged(project_id); // clears assignees + deletes timelogs
              await Installation.deleteInstallationUsingProjectId(project_id); // delete the installation records
            }
            await deleteOtherInstallationsByRequestId(project_id);
          } catch (installCleanupError) {
            console.error(
              'Error during installation revert/cleanup after project status revert:',
              installCleanupError
            );
          }
        }

        await Project.updateProjectById(project_id, { status: ps });
        return reply.send({ status: 1, message: "Project status updated successfully." });
      }
      // else: fall through to normal revert logic below
    }

    const updateFields = { status: newStatus, timestamp: currentTime };
    if (isQuotation) {
      updateFields.quotation_status = newQuotationStatus;
    }

    const updateResult = await Request.updateRequets(requestId, updateFields);

    if (!updateResult) {
      return reply.send({ status: 0, message: "Failed to update request status in database." });
    }

    // If request was In Production and we reverted it, handle associated job/project
    if (!isQuotation && currentStatus === 4 && newStatus !== 4) {
      try {
        const project_id = await Request.checkExistingProject(requestId);
        if (project_id) {
          const projectData = await Project.getProjectById(project_id);
          const currentProjectStatus = Array.isArray(projectData)
            ? projectData[0]?.status
            : projectData?.status;

          if (currentProjectStatus === 3) {
            // Revert to staged (clears assignees + timelogs) then delete installation records.
            if (allInstallationCompleted) {
              await revertCompletedInstallationsToStagedOrInstalling(project_id, 1);
            } else {
              await revertInstallingInstallationsToStaged(project_id);
              await Installation.deleteInstallationUsingProjectId(project_id);
            }
            // Delete installations from other projects that share the same request_id.
            await deleteOtherInstallationsByRequestId(project_id);
          } else {
            const installations = await Installation.getInstallationsByProjectId(project_id);
            const installationDeleted = await Installation.deleteInstallationUsingProjectId(project_id);
            const timelogDeleted = await ProjectTask.deleteStaffTaskTimelogs(project_id);
            const jobDeleted = await Project.deleteProject(project_id);
            console.log('Project and related data deleted after reverting from production');

            // Also remove installations from other projects under the same request_id.
            await deleteOtherInstallationsByRequestId(project_id);
          }
        }
      } catch (cleanupError) {
        console.error('Error during project cleanup after revert:', cleanupError);
      }
    }

    const statusType = isQuotation ? "Estimate" : "Sales Order";
    const oldStatusName = isQuotation ? `quotation_status ${currentRequest.quotation_status}` : `status ${currentStatus}`;
    const newStatusName = isQuotation ? `quotation_status ${newQuotationStatus}` : `status ${newStatus}`;
    const activityMsg = `${statusType} "${currentRequest.name}" status reverted from ${oldStatusName} to ${newStatusName}.`;

    await RequestActivity.addRequestUpdate({
      request_id: requestId,
      activity: activityMsg,
      added_by: userId,
      timestamp: currentTime,
      status: 1,
    });

    const notificationUrl = isQuotation ? `quick-bid/view/${requestId}` : `official-request/view/${requestId}`;

    const adminIds = await Admin.getAllAdminIds();
    const creatorId = currentRequest.added_by;
    const relevantUserIds = [...new Set([userId, creatorId, ...adminIds].filter(Boolean))];

    for (const id of relevantUserIds) {
      await Admin.incrementField(id, "noti_count", 1);
      await Admin.addNotification({
        user_id: id,
        url: notificationUrl,
        timestamp: currentTime,
        message: activityMsg,
        status: 1,
        from_id: userId,
      });
    }

    return reply.send({ status: 1, message: `${statusType} status reverted successfully.` });
  } catch (error) {
    console.error("Error in revertRequestStatus:", error);
    return reply.send({ status: 0, message: "Server error while reverting request status.", error: error.message });
  }
};

const revertRequestToProduction = async (req, reply) => {
  try {
    const { requestId, userId } = req.body;
    const currentTime = moment().unix();

    if (!requestId || !userId) {
      return reply.send({ status: 0, message: "Request ID and User ID are required." });
    }

    const currentRequestData = await Request.getRequestById(requestId);
    if (!currentRequestData || currentRequestData.length === 0) {
      return reply.send({ status: 0, message: "Request not found for the provided ID." });
    }

    const projectId = await Request.checkExistingProject(requestId);
    if (!projectId) {
      return reply.send({ status: 0, message: "Project not found for this request." });
    }

    const currentRequest = currentRequestData[0];
    const currentStatus = currentRequest.status;
    const allInstallationCompleted =
      Number(
        currentRequest?.allInstallationCompleted ??
          currentRequest?.allInstallationsCompleted ??
          currentRequest?.allInstallionsCmpleted ??
          0
      ) === 1;
    const sequelize = require('../config/dbconnection');
    const projectData = await Project.getProjectById(projectId);
    const currentProjectStatus = Array.isArray(projectData) ? projectData[0]?.status : projectData?.status;

    const updateResult = await Request.updateRequets(requestId, { status: 4, timestamp: currentTime });
    if (!updateResult) {
      return reply.send({ status: 0, message: "Failed to update request status in database." });
    }
    await Project.updateProjectById(projectId, { status: 1, change_order: 1 });

    if (currentProjectStatus === 3) {
      try {
        await Installation.deleteInstallationUsingProjectId(projectId);
        await ProjectTask.deleteStaffTaskTimelogs(projectId);
      } catch (cleanupError) {
        console.error('Error deleting installations for request during production revert:', cleanupError);
      }

      try {
        const [otherProjectsResult] = await sequelize.query(
          `SELECT id
           FROM projects
           WHERE request_id = :requestId
             AND status != 0
             AND id != :projectId`,
          {
            replacements: { requestId, projectId },
            type: sequelize.QueryTypes.SELECT,
          }
        );

        const otherProjectIds = (otherProjectsResult || []).map((row) => row.id).filter(Boolean);
        for (const otherProjectId of otherProjectIds) {
          await Installation.deleteInstallationUsingProjectId(otherProjectId);
          await ProjectTask.deleteStaffTaskTimelogs(otherProjectId);
        }
      } catch (otherCleanupError) {
        console.error('Error deleting installations from sibling projects during production revert:', otherCleanupError);
      }
    }

    await RequestActivity.addRequestUpdate({
      request_id: requestId,
      activity: `Sales order "${currentRequest.name}" reverted to production with change order enabled.`,
      added_by: userId,
      timestamp: currentTime,
      status: 1,
    });

    const notificationUrl = `official-request/view/${requestId}`;
    const adminIds = await Admin.getAllAdminIds();
    const creatorId = currentRequest.added_by;
    const relevantUserIds = [...new Set([userId, creatorId, ...adminIds].filter(Boolean))];

    for (const id of relevantUserIds) {
      await Admin.incrementField(id, "noti_count", 1);
      await Admin.addNotification({
        user_id: id,
        url: notificationUrl,
        timestamp: currentTime,
        message: `Sales order "${currentRequest.name}" reverted to production with job status 1.`,
        status: 1,
        from_id: userId,
      });
    }

    return reply.send({ status: 1, message: "Request reverted to production successfully." });
  } catch (error) {
    console.error("Error in revertRequestToProduction:", error);
    return reply.send({
      status: 0,
      message: "Server error while reverting the request to production.",
      error: error.message,
    });
  }
};

const addEmail = async (req, res) => {
  try {
    const { email } = req.body;
    const newEmail = await Request.addEmail({ email });
    res.send({ status: 1, message: "Email added successfully", email: newEmail });
  } catch (error) {
    console.log(error, 'Error in adding email');
    res.send({ status: 0, message: "Error in adding email" });
  }
}

const getEmails = async (req, res) => {
  try {
    const emails = await Request.getEmails();
    res.send({ status: 1, message: "Emails fetched successfully", emails: emails });
  } catch (error) {
    console.log(error, 'Error in fetching emails');
    res.send({ status: 0, message: "Error in fetching emails" });
  }
}

// Helper function to delete request attachment images (from DB and filesystem)
const deleteRequestAttachmentImagesHelper = async (documentId, documentSource = 'request') => {
  try {
    if (!documentId) {
      console.error('deleteRequestAttachmentImagesHelper: Document ID is required');
      return { success: false, message: 'Document ID is required' };
    }

    // Get all images for this document
    const images = await getRequestAttachmentImagesByDocumentId(documentId, documentSource);
    
    if (!images || images.length === 0) {
      console.log(`No images found for document ID: ${documentId}`);
      return { success: true, message: 'No images to delete', deletedCount: 0 };
    }

    const uploadsDir = path.join(__dirname, '../uploads');
    let deletedCount = 0;
    let deletedFiles = [];

    // Delete image files from filesystem
    for (const image of images) {
      try {
        const imagePath = path.join(uploadsDir, image.image_url);
        
        if (fs.existsSync(imagePath)) {
          fs.unlinkSync(imagePath);
          deletedFiles.push(imagePath);
          deletedCount++;
          console.log(`✅ Deleted image file: ${imagePath}`);
        } else {
          console.warn(`⚠️ Image file not found: ${imagePath}`);
        }
      } catch (fileError) {
        console.error(`❌ Error deleting image file for image ID ${image.id}:`, fileError.message);
      }
    }

    // Delete images from database
    const deletedDbCount = await deleteRequestAttachmentImagesByDocumentId(documentId, documentSource);
    console.log(`✅ Deleted ${deletedDbCount} image record(s) from database for document ID: ${documentId}`);

    return {
      success: true,
      message: `Successfully deleted ${deletedCount} image file(s) and ${deletedDbCount} database record(s)`,
      deletedCount: deletedCount,
      deletedFiles: deletedFiles
    };

  } catch (error) {
    console.error('Error in deleteRequestAttachmentImagesHelper:', error);
    return {
      success: false,
      message: 'An error occurred while deleting images: ' + error.message
    };
  }
};

// Helper function to convert PDF to PNG automatically
// getDocFn: optional; defaults to request attachment getter; use getDesignOrderAttachmentByIdForConversion for design order attachments
// documentSource: 'request' | 'design_order' - stored in request_attachment_images to separate same document_id from different tables
const convertPdfToPngHelper = async (documentId, getDocFn = getRequestAttachmentByIdForConversion, documentSource = 'request') => {
  try {
    if (!documentId) {
      console.error('convertPdfToPngHelper: Document ID is required');
      return { success: false, message: 'Document ID is required' };
    }

    // Get document details
    const doc = await getDocFn(documentId);
    if (!doc) {
      console.error('convertPdfToPngHelper: Document not found');
      return { success: false, message: 'Document not found' };
    }

    console.log('convertPdfToPngHelper: Loaded document for conversion', {
      documentId,
      documentSource,
      file_type: doc.file_type,
      extension: doc.extension,
      name: doc.name,
      file_url: doc.file_url,
      url: doc.url
    });

    // Prefer explicit URL fields first (e.g. Supabase URLs for project files), then fall back to name
    const pdfUrl = doc.file_url || doc.url || doc.name;
    if (!pdfUrl || typeof pdfUrl !== 'string' || !pdfUrl.trim()) {
      console.error(`convertPdfToPngHelper: Invalid or empty PDF URL for document ${documentId}`, { pdfUrl });
      return { success: false, message: 'PDF URL is missing or invalid' };
    }

    const fileType = String(doc.file_type || '').toLowerCase();
    const extension = String(doc.extension || '').toLowerCase();
    const nameLower = String((doc.name || '')).toLowerCase();
    const urlLower = String((doc.file_url || doc.url || '')).toLowerCase();

    const isPdf =
      fileType === 'application/pdf' ||
      fileType === 'pdf' ||
      fileType === 'other' ||
      extension === 'pdf' ||
      nameLower.endsWith('.pdf') ||
      urlLower.endsWith('.pdf');

    console.log('convertPdfToPngHelper: PDF detection result', {
      documentId,
      documentSource,
      pdfUrl,
      fileType,
      extension,
      nameLower,
      urlLower,
      isPdf
    });

    if (!isPdf) {
      return { success: false, message: 'File is not a PDF' };
    }

    // Create folder structure
    const uploadsDir = path.join(__dirname, '../uploads');
    const imagesDir = path.join(uploadsDir, 'request-attachments', 'images');
    const tempDir = path.join(uploadsDir, 'temp');

    if (!fs.existsSync(imagesDir)) {
      fs.mkdirSync(imagesDir, { recursive: true });
    }
    if (!fs.existsSync(tempDir)) {
      fs.mkdirSync(tempDir, { recursive: true });
    }

    const timestamp = moment().unix();
    const pdfBaseName = `pdf_${documentId}_${timestamp}`;
    let pdfPath;
    let isRemoteUrl = false;

    // 🌍 HANDLE SUPABASE OR REMOTE URL
    if (pdfUrl.startsWith('http://') || pdfUrl.startsWith('https://')) {
      isRemoteUrl = true;
      console.log('🌍 Downloading PDF from remote URL:', pdfUrl);

      const response = await axios.get(pdfUrl, {
        responseType: 'arraybuffer',
        timeout: 60000,
        headers: {
          'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
          'Accept': 'application/pdf,*/*'
        },
        validateStatus: (status) => status >= 200 && status < 300
      });

      pdfPath = path.join(tempDir, `${pdfBaseName}.pdf`);
      fs.writeFileSync(pdfPath, response.data);
      console.log('✅ Remote PDF downloaded');
    } else {
      // 💻 HANDLE LOCAL FILE
      console.log('💻 Using local PDF path:', pdfUrl);

      const possibleLocalPath = path.isAbsolute(pdfUrl)
        ? pdfUrl
        : path.join(__dirname, '../uploads', pdfUrl);

      if (!fs.existsSync(possibleLocalPath)) {
        throw new Error(`Local PDF file not found at ${possibleLocalPath}`);
      }

      pdfPath = possibleLocalPath;
      console.log('✅ Local PDF found');
    }

    // Load PDF
    const pdfBytes = fs.readFileSync(pdfPath);
    const pdfDoc = await PDFDocument.load(pdfBytes);
    const totalPages = pdfDoc.getPageCount();
    console.log(`📄 PDF contains ${totalPages} pages`);

    const sofficePath = '/usr/bin/soffice';
    if (!fs.existsSync(sofficePath)) {
      const msg = `LibreOffice "soffice" binary not found at path: ${sofficePath}`;
      console.error(`❌ convertPdfToPngHelper (document ${documentId}): ${msg}`);
      return {
        success: false,
        message: msg
      };
    }
    const uploadedImages = [];

    for (let i = 0; i < totalPages; i++) {
      const pageNumber = i + 1;
      console.log(`Processing page ${pageNumber}...`);

      // Create single page PDF
      const newPdf = await PDFDocument.create();
      const [copiedPage] = await newPdf.copyPages(pdfDoc, [i]);
      newPdf.addPage(copiedPage);

      const singlePagePdfPath = path.join(
        tempDir,
        `${pdfBaseName}_page_${pageNumber}.pdf`
      );

      const singlePagePdfBytes = await newPdf.save();
      fs.writeFileSync(singlePagePdfPath, singlePagePdfBytes);

      // Convert to PNG (stderr suppressed to hide harmless "javaldx - java may not function" warning; no Java required for PDF→PNG)
      try {
        execSync(
          `${sofficePath} --headless --convert-to png --outdir "${imagesDir}" "${singlePagePdfPath}"`,
          { stdio: ['pipe', 'pipe', 'pipe'] }
        );
      } catch (convErr) {
        console.error(`❌ convertPdfToPngHelper: soffice conversion failed for document ${documentId}, page ${pageNumber}:`, convErr.message || convErr);
        throw convErr;
      }

      // Find generated PNG
      const files = fs.readdirSync(imagesDir);
      const generatedPng = files.find(
        file =>
          file.startsWith(`${pdfBaseName}_page_${pageNumber}`) &&
          file.endsWith('.png')
      );

      if (generatedPng) {
        const sourcePngPath = path.join(imagesDir, generatedPng);
        const finalFileName = `${documentId}_page_${pageNumber}_${timestamp}.png`;
        const finalPngPath = path.join(imagesDir, finalFileName);

        if (sourcePngPath !== finalPngPath) {
          if (fs.existsSync(finalPngPath)) {
            fs.unlinkSync(finalPngPath);
          }
          fs.renameSync(sourcePngPath, finalPngPath);
        }

        const fileSize = fs.statSync(finalPngPath).size;

        const relativePath = path
          .join('uploads', 'request-attachments', 'images', finalFileName)
          .replace(/\\/g, '/');

        const imageData = {
          document_id: documentId,
          document_source: documentSource,
          image_url: relativePath,
          page_number: pageNumber,
          file_size: fileSize,
          timestamp: moment().unix(),
          status: 1
        };

        const savedImage = await addRequestAttachmentImage(imageData);

        uploadedImages.push({
          id: savedImage.id,
          page_number: pageNumber,
          image_url: relativePath,
          file_size: fileSize
        });

        console.log(`✅ Page ${pageNumber} saved`);
      }

      // Cleanup single page file
      if (fs.existsSync(singlePagePdfPath)) {
        fs.unlinkSync(singlePagePdfPath);
      }
    }

    // Cleanup only downloaded temp file
    if (isRemoteUrl && fs.existsSync(pdfPath)) {
      fs.unlinkSync(pdfPath);
    }

    if (uploadedImages.length === 0) {
      return { success: false, message: 'No pages converted' };
    }

    return {
      success: true,
      message: `Converted ${uploadedImages.length} page(s) successfully`,
      data: uploadedImages
    };

  } catch (error) {
    const isHttpError = error.response != null;
    const status = error.response && error.response.status;
    const msg = isHttpError
      ? `Download failed: ${status} ${error.response.statusText || ''}`.trim()
      : error.message;
    if (isHttpError) {
      console.error(`❌ convertPdfToPngHelper (document ${documentId}): ${msg}`);
    } else {
      console.error('❌ Error in convertPdfToPngHelper:', error.message);
    }
    return {
      success: false,
      message: 'Error converting PDF: ' + msg
    };
  }
};

const convertDesignOrderPdfToPngHelper = (documentId) =>
  convertPdfToPngHelper(documentId, getDesignOrderAttachmentByIdForConversion, 'design_order');

// Copy existing PDF-converted images from one document to another (e.g. design_order -> request on convert)
const copyAttachmentImagesToNewDocument = async (sourceDocumentId, sourceDocumentSource, targetDocumentId, targetDocumentSource) => {
  if (!sourceDocumentId || !targetDocumentId) return;
  try {
    const images = await getRequestAttachmentImagesByDocumentId(sourceDocumentId, sourceDocumentSource);
    if (!images || images.length === 0) return;
    const timestamp = moment().unix();
    for (const img of images) {
      await addRequestAttachmentImage({
        document_id: targetDocumentId,
        document_source: targetDocumentSource,
        image_url: img.image_url,
        page_number: img.page_number,
        file_size: img.file_size != null ? img.file_size : 0,
        timestamp: timestamp,
        status: 1
      });
    }
    console.log(`Copied ${images.length} attachment images from ${sourceDocumentSource}:${sourceDocumentId} to ${targetDocumentSource}:${targetDocumentId}`);
  } catch (err) {
    console.error('copyAttachmentImagesToNewDocument error:', err);
  }
};

// Controller function to get attachment images
const getAttachmentImages = async (req, reply) => {
  try {
    const { attachmentId, documentSource = 'request' } = req.body;

    if (!attachmentId) {
      return reply.send({
        status: 0,
        message: 'Attachment ID is required'
      });
    }

    const images = await getRequestAttachmentImagesByDocumentId(attachmentId, documentSource);

    return reply.send({
      status: 1,
      images: images || [],
      message: 'Images fetched successfully'
    });

  } catch (error) {
    console.error('Error in getAttachmentImages:', error);
    return reply.send({
      status: 0,
      message: 'An error occurred while fetching images: ' + error.message
    });
  }
};

// Controller function to save edited PDF image
const saveEditedPdfImage = async (req, reply) => {
  try {
    const { attachmentId, requestId, editedImageBase64, imageId, documentSource = 'request' } = req.body;

    if (!attachmentId || !requestId || !editedImageBase64) {
      return reply.send({
        status: 0,
        message: 'Missing required fields: attachmentId, requestId, and editedImageBase64 are required'
      });
    }

    // Get all images for this attachment
    const images = await getRequestAttachmentImagesByDocumentId(attachmentId, documentSource);
    
    if (!images || images.length === 0) {
      return reply.send({
        status: 0,
        message: 'No images found for this attachment. Please ensure the PDF has been converted to images first.'
      });
    }

    // Use the specified imageId if provided, otherwise use the first image
    let targetImage;
    if (imageId) {
      targetImage = images.find(img => img.id === imageId);
      if (!targetImage) {
        return reply.send({
          status: 0,
          message: `Image with ID ${imageId} not found for this attachment.`
        });
      }
    } else {
      // Fallback to first image if no imageId provided
      targetImage = images[0];
    }

    const uploadsDir = path.join(__dirname, '../uploads');
    // image_url in DB may be "uploads/request-attachments/images/..." — resolve relative to uploadsDir (no double "uploads")
    let imageUrlForPath = (targetImage.image_url || '').replace(/^uploads[/\\]/, '');
    const imagePath = path.join(uploadsDir, imageUrlForPath);

    // Convert base64 to buffer
    const base64Data = editedImageBase64.replace(/^data:image\/png;base64,/, '');
    const imageBuffer = Buffer.from(base64Data, 'base64');

    // Ensure directory exists
    const imageDir = path.dirname(imagePath);
    if (!fs.existsSync(imageDir)) {
      fs.mkdirSync(imageDir, { recursive: true });
    }

    // Save the edited image, replacing the existing file
    if (fs.existsSync(imagePath)) {
      fs.unlinkSync(imagePath);
    }
    fs.writeFileSync(imagePath, imageBuffer);

    // Get new file size
    const fileSize = fs.statSync(imagePath).size;

    // Update the image record with new file size and timestamp
    await RequestAttachmentImages.update(
      {
        file_size: fileSize,
        timestamp: moment().unix()
      },
      {
        where: { id: targetImage.id }
      }
    );

    console.log(`✅ Successfully saved edited PDF image for attachment ID: ${attachmentId}, image ID: ${targetImage.id}`);

    return reply.send({
      status: 1,
      message: 'Edited PDF image saved successfully',
      imageUrl: targetImage.image_url,
      imageId: targetImage.id
    });

  } catch (error) {
    console.error('Error in saveEditedPdfImage:', error);
    return reply.send({
      status: 0,
      message: 'An error occurred while saving the edited PDF image: ' + error.message
    });
  }
};

// Controller function to save original edited PDF file
const saveEditedPdfFile = async (req, reply) => {
  try {
    const { attachmentId, requestId, documentSource = 'request', supabaseUrl, fileSize } = req.body;
    console.log('📝 [Backend] saveEditedPdfFile received:', { attachmentId, requestId, documentSource, supabaseUrl, fileSize });

    if (!attachmentId || !supabaseUrl) {
      return reply.send({
        status: 0,
        message: 'Missing required fields: attachmentId and supabaseUrl are required'
      });
    }

    // Get the attachment record
    const attachment = await getEditedPdfAttachmentBySource(attachmentId, documentSource);

    if (!attachment) {
      return reply.send({
        status: 0,
        message: 'Attachment not found'
      });
    }

    // Update the attachment record with new Supabase URL, file size and timestamp
    const now = moment().unix();
    const updateResult = await updateEditedPdfAttachmentBySource({
      documentId: attachmentId,
      documentSource,
      supabaseUrl,
      fileSize,
      existingFileSize: attachment.file_size,
      timestamp: now
    });
    if (!updateResult) {
      return reply.send({
        status: 0,
        message: `Unsupported documentSource: ${documentSource}`
      });
    }

    // No longer converting PDF to image as requested by user
    // Deleting old images if they exist to keep the system clean, but no new reconversion
    console.log(`🧹 Deleting old images for attachment ${attachmentId} (conversion skipped)...`);
    try {
      await deleteRequestAttachmentImagesByDocumentId(attachmentId, documentSource);
    } catch (delError) {
      console.error('Error deleting old images:', delError);
    }

    console.log(`✅ Successfully updated attachment ID: ${attachmentId} with Supabase URL: ${supabaseUrl}`);

    return reply.send({
      status: 1,
      message: 'Edited PDF file updated successfully',
      name: supabaseUrl
    });

  } catch (error) {
    console.error('Error in saveEditedPdfFile:', error);
    return reply.send({
      status: 0,
      message: 'An error occurred while updating the edited PDF: ' + error.message
    });
  }
};

const getAllOverViewCount = async (req, res) => {
  try {
    let userId = req.body.userId
    let userType = req.body.userType
    let page = req.body.page
    let search = req.body.search
    let rowPerpage = req.body.rowsPerPage
    // let where = req.body.where
    const queryParams = {
      userId,
      userType,
      page,
      rowPerpage,
      search,
      sort: req.body.sort,
      fromDate: req.body.from,
      toDate: req.body.to,
      dueData: req.body.dueData,
      filterCommunity: req.body.filterCommunity,
      filterClient: req.body?.filterClient,
      filterProjectType: req.body?.filterProjectType,
      filterUser: req.body?.filterUser,
      filterProjectCategory: req.body?.filterProjectCategory
    }

    const overViewWhereConditions = [
      {
        id:1,
        key: 'newSalesOrder',
        where: ` r.new_order = 1 AND r.quotation = 0 AND r.hold=0 AND r.is_split_order = 0 `
      },
      {
        id:2,
        key: 'reviewOrders',
        where: `((proj.status = 3 AND total_inst_counts.total_installation_count IS NULL AND 
          r.noServiceNoTask = 0 AND r.status=4) OR (i.status IS NOT NULL AND 
          (COALESCE(inst_counts.installation_count, 0) >= COALESCE(total_inst_counts.total_installation_count, 0))
          AND (COALESCE(total_inst_counts.total_installation_count, 0) > 0)))
          AND r.installation_acceptance IS NULL AND r.isReview =2
          AND r.is_split_order = 0 `
      },
      {
        id:3,
        key: 'salesOrderAwaitingApproval',
        where: ` ((r.status=2) OR (r.status=3 AND r.min_payment_status=0) OR (r.split_mirror_status = 7 AND r.status = 3)) AND r.hold=0 AND r.new_order = 0 AND r.is_split_order = 0 `
      },
      {
        id:4,
        key: 'inProductionSalesOrders',
        where: `((r.status=4 and (i.status IS NULL OR i.status = 0) and proj.status <> 3 AND r.hold=0 AND r.hold=0 AND r.new_order = 0) OR (r.for_parent_facility = 2 AND r.status = 4 AND r.received_status = 0)) AND (r.split_mirror_status = 0 OR r.split_mirror_status = 6 OR r.split_mirror_status = 8) AND r.is_split_order = 0 `
      },
      {
        id:5,
        key: 'salesOrderInstallation',
        where: `((r.for_parent_facility = 2 
          AND r.status = 4
          AND r.split_mirror_status = 1)
          OR (i.status IS NOT NULL AND i.status != 5 AND i.complete_status = 1 AND i.status != 0 AND r.status != 3 AND
          (COALESCE(inst_counts.installation_count, 0) < COALESCE(total_inst_counts.total_installation_count, 0))
          AND (COALESCE(total_inst_counts.total_installation_count, 0) > 0))
          OR (r.status = 4
          AND r.received_status = 1
          AND r.split_mirror_status = 1))
          AND r.is_split_order = 0 `
      },
      {
      id:6,
      key:'salesOrderPickup',
      where:` ((
          r.for_parent_facility = 2 
          AND r.status = 4
          AND r.split_mirror_status = 2
        )
          OR 
        (
        i.status IS NOT NULL AND i.status != 5 AND i.complete_status = 2 AND 
          (COALESCE(inst_counts.installation_count, 0) < COALESCE(total_inst_counts.total_installation_count, 0))
            AND (COALESCE(total_inst_counts.total_installation_count, 0) > 0)
      )
      OR
      (
        r.status = 4
        AND r.received_status = 1
        AND r.split_mirror_status = 2
      )
      ) AND r.is_split_order = 0 `
      },
      {
      id:7,
      key:'salesOrderShipping',
      where:`((
              r.for_parent_facility = 2 
              AND r.status = 4
              AND r.split_mirror_status = 3
            )
              OR 
            (
            i.status IS NOT NULL AND i.status != 5 AND i.complete_status = 3 AND 
              (COALESCE(inst_counts.installation_count, 0) < COALESCE(total_inst_counts.total_installation_count, 0))
                AND (COALESCE(total_inst_counts.total_installation_count, 0) > 0)
            )
            OR
            (
              r.status = 4
              AND r.received_status = 1
              AND r.split_mirror_status = 3
            )
            ) AND r.is_split_order = 0
            `
      },
      {
        id:8,
        key:'returnedOrders',
        where:` i.status IS NOT NULL AND i.status = 5 AND i.complete_status IN (1,2,3) AND i.status != 0 AND r.status != 3 AND
             (COALESCE(inst_counts.installation_count, 0) < COALESCE(total_inst_counts.total_installation_count, 0))
              AND (COALESCE(total_inst_counts.total_installation_count, 0) > 0)
              AND r.is_split_order = 0
            `
      },
      {
        id:9,
        key:'rejectedOrders',
        where:` r.status = 6 AND r.hold=0 AND r.is_split_order = 0 `
      },
      {
        id:10,
        key:'completedSalesOrders',
        where:`  ((r.for_parent_facility = 2 AND r.status = 4 AND r.split_mirror_status = 5) OR
          ((proj.status = 3 AND total_inst_counts.total_installation_count IS NULL AND
            r.noServiceNoTask = 0 AND r.status=4) OR (i.status IS NOT NULL AND
          (COALESCE(inst_counts.installation_count, 0) >= COALESCE(total_inst_counts.total_installation_count, 0))
            AND (COALESCE(total_inst_counts.total_installation_count, 0) > 0)))
          AND r.installation_acceptance IS NOT NULL
          AND NOT (r.installation_acceptance = 1 AND r.timestamp <= UNIX_TIMESTAMP() - (30 * 24 * 60 * 60)))
           AND r.is_split_order = 0 `
      },
      {
        id:11,
        key:'holdSalesOrders',
        where:` r.hold=1 AND r.is_split_order = 0 `
      },
      {
        id:12,
        key:'archiveOrders',
        where:` r.installation_acceptance IS NOT NULL AND r.installation_acceptance = 1 AND r.timestamp <= UNIX_TIMESTAMP() - (30 * 24 * 60 * 60) AND r.quotation = 0 AND r.is_split_order = 0 `
      },
      {
        id:13,
        key:'approvedSalesOrder',
        where:` (r.status=3 AND r.min_payment_status =1 AND r.hold=0 AND r.new_order = 0 AND r.split_mirror_status = 0) AND r.is_split_order = 0 `
      },
      {
        id:14,
        key:'finalInvoice',
        where:` ((r.for_parent_facility = 2 AND r.status = 4 AND r.split_mirror_status = 4) OR
        ((proj.status = 3 AND total_inst_counts.total_installation_count IS NULL AND 
            r.noServiceNoTask = 0 AND r.status=4) OR (i.status IS NOT NULL AND 
        (COALESCE(inst_counts.installation_count, 0) >= COALESCE(total_inst_counts.total_installation_count, 0))
            AND (COALESCE(total_inst_counts.total_installation_count, 0) > 0)))
        AND r.installation_acceptance IS NULL AND r.isReview !=2)
        AND r.is_split_order = 0 `
      },
    ]
    const allSalesOrderCount = await Request.getAllSalesOrderTabCountsForOverview(null, queryParams.userId, queryParams.userType, queryParams.page, queryParams.rowPerpage, queryParams.search, null, queryParams.sort, queryParams.fromDate, queryParams.toDate, queryParams.filterCommunity, queryParams.dueData, queryParams.filterClient, queryParams.filterProjectType, queryParams.filterUser, queryParams.filterProjectCategory)
    console.log('allSalesOrderCount', allSalesOrderCount)
    // const allCounts = await getOverviewCountByWhere(queryParams, overViewWhereConditions)
    // let salesOrderResponse = {
    //   newSalesOrder: allCounts?.newSalesOrder?.totalCount,
    //   reviewOrders: allCounts?.reviewOrders?.totalCount,
    //   salesOrderAwaitingApproval: allCounts.salesOrderAwaitingApproval.totalCount,
    //   inProductionSalesOrders: allCounts?.inProductionSalesOrders?.totalCount,
    //   salesOrderInstallation: allCounts?.salesOrderInstallation?.totalCount,
    //   salesOrderPickup: allCounts?.salesOrderPickup?.totalCount,
    //   salesOrderShipping: allCounts?.salesOrderShipping?.totalCount,
    //   returnedOrders: allCounts?.returnedOrders?.totalCount,
    //   rejectedOrders: allCounts?.rejectedOrders?.totalCount,
    //   completedSalesOrders: allCounts?.completedSalesOrders?.totalCount,
    //   holdSalesOrders: allCounts?.holdSalesOrders?.totalCount,
    //   archiveOrders: allCounts?.archiveOrders?.totalCount,
    //   approvedSalesOrder: allCounts?.approvedSalesOrder?.totalCount,
    //   finalInvoice: allCounts?.finalInvoice?.totalCount,
    // }

    const estimateOverViewWhereConditions = [
      {
        id:1,
        key:'newEstimates',
        where:` r.status = 8 AND r.quotation_status = 2 AND r.hold = 0 `
      },
      {
        id:2,
        key:'inQueueEstimates',
        where:` (r.status = 8 and r.quotation_status = 3 and est_prj.status = 1 and r.hold = 0)  `
      },
      {
        id:3,
        key:'inprogressEstimates',
        where:` (r.status = 8 and r.quotation_status = 3 and est_prj.status = 2 and r.hold = 0) `
      },
      {
        id:4,
        key:'reviewSendForApprovalEstimates',
        where:` (r.status = 8 and r.quotation_status = 3 and est_prj.status = 3 and r.hold = 0) `
      },
      {
        id:5,
        key:'awaitingApproval',
        where:` (r.status = 8 and r.quotation_status = 9 and r.hold = 0) `
      },
      {
        id:6,
        key:"approvedEstimates",
        where:` (r.status = 8 and r.quotation_status = 4 and r.hold = 0)  `
      },
      {
        id:7,
        key:"rejectedEstimates",
        where:` (r.quotation = 1 and r.quotation_status = 6 and r.hold = 0) `
      },
      {
        id:8,
        key:"holdEstimates",
        where:` (r.status = 8 and r.hold = 1)  `
      },
      {
        id:9,
        key:"draftEstimates",
        where:` (r.status = 8 and r.quotation_status = 1 and r.hold = 0)  `
      }
    ]
   const allEstimateCount = await Request.getAllQuotationsCountForOverviewOptimized(null, queryParams.userId, queryParams.userType, queryParams.page, queryParams.rowPerpage, queryParams.search, null, queryParams.sort, queryParams.fromDate, queryParams.toDate, queryParams.filterClient, queryParams.filterProjectType, queryParams.filterUser, queryParams.dueData, queryParams.filterCommunity, null)
    // const estimateCounts = await getEstimateOverViewCountByWhere(queryParams, estimateOverViewWhereConditions)
    // let estimateResponse ={
    //   newEstimates: estimateCounts?.newEstimates?.totalCount,
    //   inQueueEstimates: estimateCounts?.inQueueEstimates?.totalCount,
    //   inprogressEstimates: estimateCounts?.inprogressEstimates?.totalCount,
    //   reviewSendForApprovalEstimates: estimateCounts?.reviewSendForApprovalEstimates?.totalCount,
    //   awaitingApproval: estimateCounts?.awaitingApproval?.totalCount,
    //   approvedEstimates: estimateCounts?.approvedEstimates?.totalCount,
    //   rejectedEstimates: estimateCounts?.rejectedEstimates?.totalCount,
    //   holdEstimates: estimateCounts?.holdEstimates?.totalCount,
    //   draftEstimates: estimateCounts?.draftEstimates?.totalCount,
    // }

    return res.send({
      status: 1,
      salesOrderCounts: allSalesOrderCount,
      estimateCounts: allEstimateCount,
      
    })
  } catch (error) {
    console.error('Error in getAllOverViewCount:', error)
    return res.send({
      status: 0,
      message: 'An error occurred while getting the overview count: ' + error.message
    })
  }
}

const getDesignProductionOverViewCount = async (req, res) => {
  try {
    let userId = req.body.userId
    let userType = req.body.userType
    let page = req.body.page
    let search = req.body.search
    let rowPerpage = req.body.rowsPerPage
    const queryParams = {
      userId,
      userType,
      page,
      rowPerpage,
      search,
      sort: req.body.sort,
      fromDate: req.body.from,
      toDate: req.body.to,
      dueData: req.body.dueData,
      filterCommunity: req.body.filterCommunity,
      filterClient: req.body?.filterClient,
      filterProjectType: req.body?.filterProjectType,
      filterUser: req.body?.filterUser,
      filterProjectCategory: req.body?.filterProjectCategory
    }

    const designOverViewWhereConditions = [
      {
        id: 1,
        key: 'newDesigns',
        where: ` d.status = 1 AND d.hold = 0 AND d.is_in_queue = 0 AND d.design_order_job_status = 1 AND d.send_client_app_req=0`
      },
      {
        id: 2,
        key: 'inQueueDesigns',
        where: ` d.status = 1 AND d.hold = 0 AND d.is_in_queue = 1 AND d.design_order_job_status = 1 AND d.send_client_app_req=0`
      },
      {
        id: 3,
        key: 'inDesignOrders',
        where: ` d.status = 1 AND d.hold = 0 AND d.design_order_job_status = 2 AND d.send_client_app_req=0`
      },
      {
        id: 4,
        key: 'designJobCompleted',
        where: ` d.design_order_job_status = 3 AND d.send_client_app_req=0 AND d.status !=0 AND d.hold = 0`
      },
      {
        id: 5,
        key: 'designsAwaitingApproval',
        where: ` d.status=1 AND d.hold=0 AND d.send_client_app_req=1`
      },
      {
        id: 6,
        key: 'designsOnHold',
        where: ` d.hold=1 AND d.status !=0`
      }
    ]

    const productionTabs = [
      { key: 'jobsHold', tab: 'Hold', useCompletedInstallations: false },
      { key: 'changeOrder', tab: 'Change Order', useCompletedInstallations: false },
      { key: 'preProductionJobs', tab: 'Pre-Production', useCompletedInstallations: false },
      { key: 'productionJobs', tab: 'Production', useCompletedInstallations: false },
      { key: 'jobInstalls', tab: 'Installs', useCompletedInstallations: true },
      { key: 'jobPickup', tab: 'Pickup', useCompletedInstallations: true },
      { key: 'jobShipping', tab: 'Shipping', useCompletedInstallations: true },
      { key: 'tmReview', tab: 'T & M Review', useCompletedInstallations: true },
      { key: 'costCard', tab: 'Cost Card', useCompletedInstallations: true },
      { key: 'completedJobs', tab: 'Completed', useCompletedInstallations: true },
    ]

    const [designCounts, designJobCounts, designBaseCounts, productionCounts] = await Promise.all([
      getDesignOverViewCountByWhere(queryParams, designOverViewWhereConditions),
      Request.getAllDesignOrderJobCount(queryParams.userType, queryParams.userId),
      Request.getAllDesignOrdersCount(
        null,
        queryParams.userId,
        queryParams.userType,
        null,
        null,
        null,
        '',
        null,
        null,
        null,
        false,
        null,
        null
      ),
      getProductionOverViewCountByTabs(
        queryParams,
        productionTabs,
        req.body?.hasJobPermission
      ),
    ])

    const designResponse = {
      newDesignJobs: designJobCounts?.newCount || 0,
      completedDesignJobs: designJobCounts?.completedCount || 0,
      approvedDesigns: designBaseCounts?.acceptedCount || 0,
      rejectedDesigns: designBaseCounts?.rejectedCount || 0,
      newDesigns: designCounts?.newDesigns?.totalCount || 0,
      inQueueDesigns: designCounts?.inQueueDesigns?.totalCount || 0,
      inDesignOrders: designCounts?.inDesignOrders?.totalCount || 0,
      designJobCompleted: designCounts?.designJobCompleted?.totalCount || 0,
      designsAwaitingApproval: designCounts?.designsAwaitingApproval?.totalCount || 0,
      designsOnHold: designCounts?.designsOnHold?.totalCount || 0,
    }

    return res.send({
      status: 1,
      designCounts: designResponse,
      productionCounts
    })
  } catch (error) {
    console.error('Error in getDesignProductionOverViewCount:', error)
    return res.send({
      status: 0,
      message: 'An error occurred while getting the design/production overview count: ' + error.message
    })
  }
}

const getOverviewCountByWhere = async (queryParams, whereConfigList = []) => {
  const counts = {}

  for (const config of whereConfigList) {
    const { key, where } = config
    counts[key] = await Request.getAllRequestsTotalCount(
      null,
      queryParams.userId,
      queryParams.userType,
      queryParams.page,
      queryParams.rowPerpage,
      queryParams.search,
      where,
      queryParams.sort,
      queryParams.fromDate,
      queryParams.toDate,
      queryParams.filterCommunity,
      queryParams.dueData,
      queryParams.filterClient,
      queryParams.filterProjectType,
      queryParams.filterUser,
      queryParams.filterProjectCategory
    )
  }

  return counts
}

const getEstimateOverViewCountByWhere = async (queryParams, whereConfigList = []) => {
  const counts = {}

  for (const config of whereConfigList) {
    const { key, where } = config
   
    counts[key] = await Request.getAllQuotationsCountForOverview(null, queryParams.userId, queryParams.userType, queryParams.page, queryParams.rowPerpage, queryParams.search,where,queryParams.sort, queryParams.fromDate, queryParams.toDate ,queryParams.filterClient, queryParams.filterProjectType, queryParams.filterUser, queryParams.dueData, queryParams.filterCommunity,null)
  }

  return counts
}

const getDesignOverViewCountByWhere = async (queryParams, whereConfigList = []) => {
  return getCountsInBatches(whereConfigList, 3, async ({ key, where }) => {
    const countData = await Request.getAllDesignOrdersCount(
      null,
      queryParams.userId,
      queryParams.userType,
      null,
      null,
      null,
      where,
      null,
      null,
      null,
      false,
      null,
      null
    )
    return { key, value: countData }
  })
}

const getProductionOverViewCountByTabs = async (
  queryParams,
  tabConfigList = [],
  hasJobPermission = false
) => {
  return getCountsInBatches(tabConfigList, 3, async ({ key, tab, useCompletedInstallations }) => {
    const totalCount = useCompletedInstallations
      ? await Project.getTotalCompletedInstallationsCount(
          hasJobPermission,
          true,
          queryParams.userType,
          queryParams.userId,
          queryParams.sort || 'new',
          false,
          queryParams.fromDate,
          queryParams.toDate,
          queryParams.dueData,
          'ALL',
          tab,
          '',
          [],
          [],
          [],
          [],
          [],
          [],
          2
        )
      : await Project.getTotalProjectsCount(
          hasJobPermission,
          true,
          queryParams.userType,
          queryParams.userId,
          null,
          queryParams.sort || 'new',
          queryParams.fromDate,
          queryParams.toDate,
          queryParams.dueData,
          'ALL',
          tab,
          '',
          [],
          [],
          [],
          [],
          [],
          [],
          2
        )

    return { key, value: totalCount || 0 }
  })
}

const getCountsInBatches = async (items = [], batchSize = 3, worker) => {
  const counts = {}
  for (let i = 0; i < items.length; i += batchSize) {
    const chunk = items.slice(i, i + batchSize)
    const results = await Promise.all(chunk.map((item) => worker(item)))
    results.forEach(({ key, value }) => {
      counts[key] = value
    })
  }
  return counts
}



module.exports = {
  getAllRequests,
  getAllRequestsForAdd,
  getAllRequestFilters,
  getAllQuotationFilters,
  getAllRequestsNew,
  getAllRequestsTotalCount,
  addRequests,
  addRequestDraft,
  getRequestByIdNew,
  getRequestById,
  getRequestByIdForInvoice,
  getRequestDataForInstalationById,
  updateDesignOrderRequestedDueDate,
  getRequestByIdProducts,
  getRequestByIdMaterials,
  getRequestByIdProductionInnerPage,
  getRequestAttachments,
  getRequestUpdates,
  getSplitEstimateUpdates,
  getRequestServices,
  getOrderDetailsData,
  editRequest,
  editRequestDraft,
  deleteRequestById,
  deleteRequestByIdEstimate,
  addRequetPartners,
  requestSearch,
  acceptOrRejectRequest,
  acceptOrRejectRequestNew,
  acceptOrRejectInvoice,
  createEstimateJob,
  reopenRequest,
  getAllClientRequestId,
  uploadDesigns,
  addRequestPayments,
  fetchAllPayments,
  deletePayment,
  editRequestPayments,
  updateDesigns,
  getRequestByCommunityId,
  getProjectByCommunityId,
  getProjectByClientId,
  getJobByClientId,
  getCommunityByClient,
  getAllQuotations,
  getAllQuotationsAdd,
  getAllQuotationsCount,
  acceptOrRejectQuotation,
  updateEstimateHoldStatus,
  reopenQuotation,
  reopenRequestJob,
  convertQuotation,
  acceptFinalInvoice,
  saveQuickbidOffReqOrder,
  addDesignOrder,
  getAllDesignOrdersCount,
  getAllDesignOrders,
  getDesignOrderById,
  duplicateDesignOrder,
  mergeDuplicatedDesignOrderToSalesOrder,
  mergeDuplicatedDesignOrderToRequestEstimate,
  deleteDesignOrderById,
  updateDesignOrderCardOrder,
  updateDesignOrder,
  updateDesignOrderDueDate,
  acceptOrRejectDesignOrder,
  convertDesignToSalesOrder,
  convertDesignToRequestEstimate,
  convertDesignToSplitEstimate,
  updatePaymentFromQb,
  updateDesignOrderJobStatus,
  getAllQuotationsCards,
  getCompletedInstallsByCommunityId,
  getCompleteDesignByCommunityId,
  updateProductsAddress,
  updateServiceAddress,
  sendApprovalReqDesignOrder,
  updateMultiDesigns,
  unlinkMultiDesigns,
  getAllDesignOrdersTabCount,
  reduceDesignOrderCount,
  getAllEstimateTabCount,
  reduceEstimateTabCount,
  getAllSalesOrderTabCount,
  reduceSalesOrderTabCount,
  reduceDesignOrderJobsCount,
  getWorkflowMembers,
  getJobNoteMembers,
  getJobCostNotesDetails,
  sendToClientEstimate,
  duplicateRequest,
  deleteDesignOrderDesign,
  updateSalesOrderHoldStatus,
  acceptReviewOrder,
  updateReviewInvoiceTotal,
  addRequestService,
  deleteRequestServiceById,
  getRequestByIdWithoutTeamAndProducts,
  getProductsForRequest,
  getRequestLabourCostServices,
  getCommunitiesForClientAutocomplete,
  acceptOrRejectInvoiceSplit,
  changeNewOrderStatusSalesOrderTab,
  sendInvoicePdfToEmail,
  saveEditedPdfImage,
  getAttachmentImages,
  approveSplitOrderInvoiceRequest,
  updateReceivedStatus,
  rejectSalesOrderFromJob,
  updateProductRowEstimate,
  convertEstimateToDesignOrder,
  updateServiceRowEstimate,
  deleteDraftEstimate,
  revertRequestStatus,
  revertRequestToProduction,
  addEmail,
  getEmails,
  saveEditedPdfFile,
  convertPdfToPngHelper,
  convertDesignOrderPdfToPngHelper,
  getAllOverViewCount,
  getDesignProductionOverViewCount,
  getEstimateJobNewData
}