const moment = require('moment')
const RequestProposals = require('../models/requestProposals')
const Admin = require('../models/admin')
const Proposals = require('../models/proposal')
const { empty } = require('../Helpers/Common')
const Request = require('../models/request')
const CustomerOrder = require("../models/customerOrder")
const CustomerOrderProduct = require("../models/customerOrderProduct")
const { PRODUCTION, getNewAccessToken, realmId, AVALARA_ACCOUNT_ID, AVALARA_LICENSE_KEY, AVALARA_BASE_URL, departmentId, locationInventoryCode } = require('../quickbook/quickbook')
const { default: axios } = require('axios')
const { validateInventoryItemsInQbForPo, validateVendorInQb } = require('./qbController')
const Partners = require('../models/client')
const Product = require('../models/product')




const addProposal = async (req, res) => {

  const { title,
    description,
    attachment,
    budget,
    request_id,
    userId,
    taxCategory,
    values
  } = req.body
  let currentTime = moment().unix()
  let uploadedfile = values.files_upload;

  //  let data={title:title,description:description,attachment_url:attachment.url,attachment_type:attachment.type,budget:budget,request_id:request_id,

  // timestamp:moment().unix(),added_by:userId,vendor_id:userId}
  let totalBudget =(values.total && values.total > 0) ? values.total + (values.taxAmount || 0) : 0

  let data = { title: values.name,
    description: (values.description || '').replace(/\r?\n/g, '\n'),
    description_lines: (values.description || '').replace(/\r?\n/g, '\n').split('\n'),
    attachment_url: "", attachment_type: "", budget: totalBudget, timestamp: moment().unix(), request_id: request_id, added_by: userId, vendor_id: userId,tax:(values.taxAmount || 0) }
  let result = await RequestProposals.addProposal(data)
  console.log("$$$$$$$$$$$$$$$$$$$$$$Result", result);
  let orderData = {}
  orderData['total'] = (values.total && values.total > 0) ? values.total + (values.taxAmount || 0) : 0,
  orderData['tax_category'] = taxCategory || null
  orderData['tax_amount'] = values.taxAmount || 0
    orderData['budget'] = (values.budget && values.budget > 0) ? values.budget + (values.taxAmount || 0) : 0,
    orderData['extra_discount'] = values.invoice_discount || 0,
    orderData['request_id'] = result,
    orderData['timestamp'] = currentTime,
    orderData["sub_total"] = values.total || 0;
    orderData["tariff"] = values.tariff || 0;
    orderData["shipping_cost"] = values.shipping_cost || 0;
  orderData["tax"] = 0;
  orderData['status'] = 1
  let resultData = await RequestProposals.createCustomerOrder(orderData)
  console.log("$$$$$$$$$$$$$$$$$$$$$$ResultData", resultData,uploadedfile);
  let requestProduct = {}
  if (resultData ) {

    if(uploadedfile.length > 0){

    
      let approvedImage = {}
                        approvedImage.name = uploadedfile.url;
                        approvedImage.extension = uploadedfile.type;
                        approvedImage.added_by = userId
                        approvedImage.proposal_id = request_id
                        approvedImage.timestamp = moment().unix()
                        let upload = await Proposals.updateRequestProposalAttachmentsByProposalId(request_id,approvedImage)
                        console.log('bshdfbgisdbgisfigb1',upload,request_id)
    }
    
    for (const row of values.products) {
      console.log('row dsfdfd-------', row);
      let rowQuantity = row.quantity ? row.quantity : 1;
      let rowDiscount = row.discount ? row.discount : 0
      row.product_sub_total = row.sellingPrice || 0 * rowQuantity
      requestProduct['order_id'] = resultData
      requestProduct['product_id'] = row.id,
        requestProduct['product_name'] = row.name,
        requestProduct['product_price'] = row.sellingPrice,
        requestProduct['product_retail_price'] = row.sellingPrice,
        requestProduct['product_markup'] = row.markup,
        requestProduct['product_qty'] = rowQuantity,
        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,
        requestProduct['timestamp'] = currentTime,
        requestProduct['status'] = row.status

      let response = await RequestProposals.createCustomerOrderProduct(requestProduct)

    }

    for (const row of values.materials) {
      console.log('row dsfdfd------- materials', row);
      let rowQuantity = row.quantity ? row.quantity : 1;
      let rowDiscount = row.discount ? row.discount : 0
      row.product_sub_total = row.sellingPrice || 0 * rowQuantity
      requestProduct['order_id'] = resultData
      requestProduct['product_id'] = row.id,
        requestProduct['product_name'] = row.name,
        requestProduct['product_price'] = row.sellingPrice,
        requestProduct['product_retail_price'] = row.sellingPrice,
        requestProduct['product_markup'] = row.markup,
        requestProduct['product_qty'] = rowQuantity,
        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,
        requestProduct['timestamp'] = currentTime,
        requestProduct['status'] = row.status
        requestProduct['material'] = 1
      let response = await RequestProposals.createCustomerOrderProduct(requestProduct)

    }

    console.log("selected services", values.selectedServices);
    for (const row of values.selectedServices) {
      let rowQuantity = row.quantity ? row.quantity : 1;
      requestProduct['request_id'] = result
      requestProduct['labour_cost_service_id'] = row.labour_cost_service_id,
      requestProduct['recipe_id'] = row.recipe_id || null,
      requestProduct['product_id'] = row.product_id || null,
      requestProduct['qty'] = rowQuantity,
      requestProduct['unit_cost'] = row.sellingPrice || 0
      requestProduct['markup'] = row.markup,
      requestProduct['total_cost'] = row.total || (requestProduct['unit_cost'] * requestProduct['qty'])

      requestProduct['jobcost_unit_cost'] = row?.sellingPrice ? row?.sellingPrice : 0
      requestProduct['jobcost_quantity'] = rowQuantity
      requestProduct['jobcost_total_cost'] = (requestProduct['unit_cost'] * requestProduct['qty']) || 0
      let response = await RequestProposals.addRequestServices(requestProduct)
    }


  }
  if (result) {
    let notificationMsg = `New Proposal  has been added.`;
    let notificationUrl = "official-request";


    const incrementResult_User = await Admin.incrementField(
      userId,
      "noti_count",
      1
    );

    let notificationData_user = {
      user_id: userId,
      url: notificationUrl,
      timestamp: moment().unix(),
      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: moment().unix(),
          message: notificationMsg,
          status: 1,
          from_id: userId,
        };

        const addNotificationResult = await Admin.addNotification(
          notificationData
        );

      }
    }
    res.send({
      message: "Proposal added successfully",
      proposalId: result,
      status: 1
    })
  }
  else {
    res.send({
      message: "Proposal not added",
      status: 0
    })
    console.log("Proposal not added");
    return;
  }
}
const updateProposal = async (req, res) => {

  const { title,
    description,
    attachment,
    budget,
    proposalId,
    taxCategory,
    request_id,
    userId,
    values,
  } = req.body
  let currentTime = moment().unix()

  let uploadedfile = values.files_upload;
  
  // let data={title:title,description:description,attachment_url:attachment.url,attachment_type:attachment.type,budget:budget}
   let totalBudget =(values.total && values.total > 0) ? values.total + (values.taxAmount || 0) : 0
  const formattedDescription = (values.description || '').replace(/\r?\n/g, '\n');
  let data = { title: values.name, description: formattedDescription, description_lines: formattedDescription.split('\n'), attachment_url: "", attachment_type: "", budget: totalBudget }
  console.log("#############ProposalID", proposalId);
  let result = await RequestProposals.updateProposal(proposalId, data)
  let orderId = await Proposals.getCustomerOrderRequestId(request_id, userId);
  console.log("$$$$$$$$$$$$$$$$$$$$$$orderId", orderId);
  let orderData = {}
  orderData['total'] = (values.total && values.total > 0) ? values.total + (values.taxAmount || 0) : 0,
  orderData['tax_category'] = taxCategory || null
    orderData['tax_amount'] = values.taxAmount || 0
    orderData['budget'] = totalBudget,
    orderData['extra_discount'] = values.invoice_discount || 0,
    orderData['request_id'] = proposalId,
    orderData['timestamp'] = currentTime,
    orderData["sub_total"] = values.total || 0;
  orderData["tax"] = 0;
  orderData['status'] = 1
  let resultData = await RequestProposals.updateCustomerOrder(orderId.proposalOrder, orderData)
  console.log("$$$$$$$$$$$$$$$$$$$$$$ResultData", resultData);

  if(resultData && uploadedfile.length > 0){
   
   
  let approvedImage = {}
  approvedImage.name = uploadedfile.url;
  approvedImage.extension = uploadedfile.type;
  approvedImage.added_by = userId
  approvedImage.proposal_id = request_id
  approvedImage.timestamp = moment().unix()
  let upload = await Proposals.updateRequestProposalAttachmentsByProposalId(request_id,approvedImage)
  console.log('bshdfbgisdbgisfigb1',upload,request_id)
  }

  let requestProduct = {}
  if (resultData) {
    let CustomerProductData = await Proposals.getVendorOrderProductsbyId(orderId.proposalOrder);
    console.log("#################Old order", CustomerProductData);
    let deletedCustomerProduct = await RequestProposals.deletedCustomerOrderProduct(orderId.proposalOrder);
    for (const row of values.products) {
      console.log('row dsfdfd-------', row);
      let rowQuantity = row.quantity ? row.quantity : 1;
      let rowDiscount = row.discount ? row.discount : 0
      row.product_sub_total = row.sellingPrice || 0 * rowQuantity
      requestProduct['order_id'] = orderId.proposalOrder
      requestProduct['product_id'] = row.id,
        requestProduct['product_name'] = row.name,
        requestProduct['product_price'] = row.sellingPrice,
        requestProduct['product_retail_price'] = row.sellingPrice,
        requestProduct['product_markup'] = row.markup,
        requestProduct['product_qty'] = rowQuantity,
        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,
        requestProduct['timestamp'] = currentTime,
        requestProduct['status'] = row.status

      let response = await RequestProposals.createCustomerOrderProduct(requestProduct)

    }
    console.log("selected services1", values.selectedServices);
    let deleteServices = await RequestProposals.deleteRequestServices(proposalId)
    let requestPr={}
    for (const row of values.selectedServices) {
      let rowQuantity = row.quantity ? row.quantity : 1;
      requestPr['request_id'] = proposalId
      requestPr['labour_cost_service_id'] = row.labour_cost_service_id,
        requestPr['recipe_id'] = row.recipe_id || null,
        requestPr['product_id'] = row.product_id || null,
        requestPr['qty'] = rowQuantity,
        requestPr['unit_cost'] = row.sellingPrice || 0
      requestPr['markup'] = row.markup !=='Infinity'? row.markup : 0 || 0,
        requestPr['total_cost'] = row.total || (requestPr['unit_cost'] * requestPr['qty'])

        requestPr['jobcost_unit_cost'] = row?.sellingPrice ? row?.sellingPrice : 0
        requestPr['jobcost_quantity'] = rowQuantity
        requestPr['jobcost_total_cost'] = (requestPr['unit_cost'] * requestPr['qty']) || 0
      let response = await RequestProposals.addRequestServices(requestPr)
    }


  }
  if (result) {
    let notificationMsg = `Proposal has been updated.`;
    let notificationUrl = "official-request";
    const incrementResult_User = await Admin.incrementField(
      userId,
      "noti_count",
      1
    );

    let notificationData_user = {
      user_id: userId,
      url: notificationUrl,
      timestamp: moment().unix(),
      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: moment().unix(),
          message: notificationMsg,
          status: 1,
          from_id: userId,
        };

        const addNotificationResult = await Admin.addNotification(
          notificationData
        );

      }
    }
    res.send({
      message: "Proposal updated successfully",
      proposalId: result,
      status: 1
    })
  }
  else {
    res.send({
      message: "Proposal not updated",
      status: 1
    })
    console.log("Proposal not updated");
    return;
  }
}
const deleteProposal = async (request, reply) => {
  try {
    let { id, status } = request.body;
    let currentDate = Math.round(moment().valueOf() / 1000);
    if (id) {
      let proposalData = await RequestProposals.getProposalById(id);

      let deleted = await RequestProposals.updateProposal(id, { status: 0 });

      if (deleted) {
        // // Notification
        let notificationMsg = `The proposal "${proposalData[0].title}" has been deleted.`;
        let notificationUrl = "official-requests";

        let adminIds = await Admin.getAllAdminIds();
        let notificationData = {};

        for (const adminId of adminIds) {

          const incrementResult = await Admin.incrementField(
            adminId,
            "noti_count",
            1
          );

          notificationData = {
            user_id: adminId,
            url: notificationUrl,
            timestamp: currentDate,
            message: notificationMsg,
            status: 1,
            from_id: proposalData[0].added_by,
          };
          // console.log("notificationData------------", notificationData);
          const addNotificationResult = await Admin.addNotification(
            notificationData
          );
          // console.log("addNotificationResult-------", addNotificationResult);
        }
        reply.send({
          status: 1,
          message: `The proposal "${proposalData[0].title}" has been deleted successfully!`,
        });
      } else {
        reply.send({ status: 0, message: `Unable to delete the proposal!` });
      }


    } else {
      console.log("Error................");
      reply.send({ status: 500, message: "Error!" });
    }
  } catch (error) {
    reply.status(500).send({ message: "Server error" + error });
  }
}

/////////////////////////////////////////qb

const findItemIdByName = async (name) => {
  try {
    console.log(name,"productname")
    const token = await getNewAccessToken(); // Await the token retrieval

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

    const response = await axiosRetry(() => axios.get(
      apiUrl,
      {
        headers: {
          'Authorization': `Bearer ${token}`,
          'Content-Type': 'application/text'
        },
        params: {
          query: `SELECT * FROM Item WHERE Name = '${name}'`
        }
      }
    ));

    const items = response.data.QueryResponse.Item;
    console.log(items, "itemsssssssssssssssssss");

    if (items && items.length > 0) {
      return items[0].Id;
    } else {
      throw new Error(`Item with name ${name} not found`);
    }
  } catch (err) {
    console.error('Error finding item id by name:', name, err.response ? err.response.data : err.message);
    throw err;
  }
};

const findVendorIdByName = async (name) => {
  try {
    // Ensure valid access token
    let token = await getNewAccessToken();

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

    const response = await axios.get(
      apiUrl,
      {
        headers: {
          'Authorization': `Bearer ${token}`,
          'Content-Type': 'application/text'
        },
        params: {
          query: `SELECT * FROM Vendor WHERE DisplayName = '${name}'`
        }
      }
    );

    const vendors = response.data.QueryResponse.Vendor;
    console.log(vendors, "Vendors retrieved");

    if (vendors && vendors.length > 0) {
      return vendors[0].Id;
    } else {
      throw new Error(`Vendor with name ${name} not found`);
    }
  } catch (err) {
    console.error('Error finding vendor by name:', err.response ? err.response.data : err.message);
    throw err;
  }
};

const findItemByName = async (itemName) => {
  try {
    let token = await getNewAccessToken();

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

    const response = await axios.get(apiUrl, {
      headers: {
        Authorization: `Bearer ${token}`,
        'Content-Type': 'application/text',
      },
      params: {
        query: `SELECT * FROM Item WHERE Name = '${itemName}'`,
      },
    });

    const items = response.data.QueryResponse.Item;

    if (items && items.length > 0) {
      return items[0]; // Return the first matching item
    } else {
      throw new Error(`Item with name ${itemName} not found in QuickBooks.`);
    }
  } catch (error) {
    console.error('Error finding item by name:', error.response ? error.response.data : error.message);
    throw error;
  }
};

const findItemById = async (itemId) => {
  try {
    let token = await getNewAccessToken();

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

    const response = await axios.get(apiUrl, {
      headers: {
        Authorization: `Bearer ${token}`,
        'Content-Type': 'application/text',
      },
      params: {
        query: `SELECT * FROM Item WHERE Id = '${itemId}'`,
      },
    });

    const items = response.data.QueryResponse.Item;

    if (items && items.length > 0) {
      return items[0]; // Return the first matching item
    } else {
      throw new Error(`Item with ID ${itemId} not found in QuickBooks.`);
    }
  } catch (error) {
    console.error('Error finding item by ID:', error.response ? error.response.data : error.message);
    throw error;
  }
};


let docNum=null
let quickbookId=null
const createPurchaseOrder = async (values) => {
  try {
      // Ensure that vendor information is already available in the values
      let vendorName = (values.vendorQbName + locationInventoryCode).trim();
      const vendorId =values?.partnerQbId ? values?.partnerQbId : await findVendorIdByName(vendorName)
      console.log('propsalDatapropsalData venodr id',vendorId)
      if (!vendorId) {
          console.error(`Vendor with name ${values.vendorQbName} not found in QuickBooks.`);
     }

      // Check for missing QuickBooks IDs in products and services
      // const missingProducts = values.rfpProducts.filter(product => !product.syncWithQuickbooks || !product.quickbookId);
      // const missingServices = values.rfpServices.filter(service => !service.syncWithQuickbooks || !service.quickbookId);

      // Create the reason message if any item is missing
      // const ReasonWithQb = `
      //     This Official Request is not synced with QuickBooks because the following are not in QuickBooks:
      //     ${missingProducts.length > 0 ? `Products: ${missingProducts.map(p => p.product_name).join(', ')}` : ''}
      //     ${missingServices.length > 0 ? `RFP Services: ${missingServices.map(s => s.labour_cost_service_name).join(', ')}` : ''}
      // `.trim();

      // // If there are missing items, log the reason and return
      // if (missingProducts.length > 0 || missingServices.length > 0) {
      //     console.log("ReasonWithQb:", ReasonWithQb);
      //     return ReasonWithQb; // Return or handle this message as needed
      // }

      // Map products to QuickBooks format using pre-fetched data
      const productItems = values.rfpProducts.map(product => {
          const quantity = product.product_qty * product.location_quantity;
          const productPrice = parseFloat(product.product_price)?.toFixed(2) || 0;

          return {
              itemId: product.quickbookId,
              name: product.product_name,
              amount: productPrice * quantity,
              quantity: quantity,
              unitPrice: productPrice,
          };
      });

      // Map RFP services to QuickBooks format using pre-fetched data
      const rfpServiceItems = values.rfpServices.map(service => ({
          itemId: service.quickbookId,
          name: service.labour_cost_service_name,
          amount: service.unit_cost * service.qty,
          quantity: service.qty,
          unitPrice: service.unit_cost,
      }));

      let qbCustomTaxId = await findItemByName('Tax')
      const taxData =[{
        itemId: qbCustomTaxId.Id,
        name:'tax',
        amount: values.tax_amount || 0 ,
        quantity: 1,
        unitPrice:values.tax_amount || 0,
      }]

      // Prepare request data for the purchase order
      const requestData = {
          vendorId: vendorId,
          items: [
              ...productItems,
              ...rfpServiceItems,
              ...taxData
          ],
          note: values.description || "Purchase order description"
      };
        console.log('productItems----',productItems)
        console.log('rfpServiceItems----',rfpServiceItems)
        console.log('taxData----',taxData)
      // Send the purchase order data to QuickBooks
      let quickbookPurchaseOrderId = await addPurchaseOrderToQuickBooks(requestData);
      console.log(quickbookPurchaseOrderId, "Purchase order created");

  } catch (error) {
      console.error("Error in creating purchase order:", error);
  }
};


const addPurchaseOrderToQuickBooks = async (purchaseOrderData) => {
  console.log("Attempting to add purchase order to QuickBooks...");
  let token = await getNewAccessToken();

  const postPurchaseOrder = 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 purchaseOrderData.items) { // Iterate over the `items` array
        // const itemDetails = await findItemByName(item.name);
        const itemDetails = await findItemById(item.itemId);
        item.inventoryStartDate = itemDetails.InventoryStartDate;
      }

      // Set the transaction date
      const txnDate = moment().format('YYYY-MM-DD');

      // Prepare the QuickBooks purchase order data
      const quickBooksPurchaseOrderData = {
        VendorRef: { value: purchaseOrderData.vendorId },
        TxnDate: txnDate,
        DepartmentRef: { value: departmentId },
        Line: [
          ...purchaseOrderData.items.map(item => ({
            Amount: item.amount,
            DetailType: "ItemBasedExpenseLineDetail",
            ItemBasedExpenseLineDetail: {
              ItemRef: { value: item.itemId },
              Qty: item.quantity,
              UnitPrice: item.unitPrice
            }
          }))
        ],
        PrivateNote: purchaseOrderData.note
      };

      console.log("QuickBooks Purchase Order Data:", JSON.stringify(quickBooksPurchaseOrderData, 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(
        `${apiUrl}/purchaseorder`,
        quickBooksPurchaseOrderData,
        {
          headers: {
            Authorization: `Bearer ${token}`,
            'Content-Type': 'application/json',
            Accept: 'application/json'
          },
          timeout: 30000 // 30 seconds
        }
      );

      if (response.status === 200 || response.status === 201) {
        console.log("Purchase order has been successfully added to QuickBooks:", response.data);
        console.log("Unique Purchase Order ID:", response.data.PurchaseOrder.Id);
        docNum= response.data.PurchaseOrder.DocNumber;
        quickbookId = response.data.PurchaseOrder.Id;
        return response.data;
      } else {
        console.error("Failed to add the purchase order 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 postPurchaseOrder(useNewToken, attempt + 1); // Retry
      }

      // Handle authentication error
      // console.error("Error adding purchase order to QuickBooks:", error.response ? error.response.data : error.message);
      if (error.response) {
        console.error(
          "QuickBooks Full Error:\n",
          JSON.stringify(error.response.data, null, 2)
        );
      } else {
        console.error("Error:", error.message);
      }
      const faultType = error.response?.data?.Fault?.type;
      if (faultType === 'AUTHENTICATION') {
        console.log('Access token expired, refreshing token...');
        return postPurchaseOrder(true); // Retry with new access token
      } else {
        throw error; // Rethrow error if not related to DNS or authentication
      }
    }
  };

  return postPurchaseOrder();
};

async function createAvalaraProposalSync(proposalData, attempt = 0) {
  const MAX_RETRIES = 5;

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

  const shipTo = {
    line1: proposalData.vendorAddress || '',
    city: proposalData.vendorCity || '',
    region: proposalData.vendorState || '',
    postalCode: proposalData.vendor_zip_code || '',
    country: 'US'
  };

  //   const shipTo = {
  //   line1: "kuzhalmannam " || '',
  //   city: 'kuzhalmannam' || '',
  //   region: "palakkad" || '',
  //   postalCode: "678702" || '',
  //   country: 'US'
  // };

  const lines = [];

  function pushLine(name, qty, unit, taxCode) {
    const amount = parseFloat(unit * qty).toFixed(2);
    lines.push({
      number: String(lines.length + 1),
      quantity: qty,
      amount,
      itemCode: String(name).slice(0, 50),
      description: name,
      taxCode: taxCode || 'P0000000',
      discounted: false
    });
  }

  // RFP Products
  // (proposalData.rfpProducts.filter(val =>val.isMaterial == 1 ) || []).forEach(rp => {
  (proposalData.rfpProducts || []).forEach(rp => {
    const qty = (rp.product_qty || 1) * (rp.location_quantity || 1);
    const price =  Number(rp.product_price || 0);
    pushLine(rp.product_name, qty, price, rp.taxCode || null);
  });

  // RFP Services
  (proposalData.rfpServices || []).forEach(rs => {
    const qty = Number(rs.qty || 1);
    const price = Number(rs.unit_cost || 0);
    pushLine(rs.labour_cost_service_name, qty, price, rs.taxCode || null);
  });

  if (!lines.length) {
    throw new Error('No valid line items found in proposal.');
  }

  const payload = {
    type: 'SalesInvoice',
    commit: false,
    code: Date.now(),
    date: moment().format('YYYY-MM-DD'),
    customerCode: String(proposalData.id || 'UNKNOWN'),
    companyCode: 'DEFAULT',
    addresses: { shipFrom, shipTo },
    lines
  };

  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('✅ Avalara Proposal Sync Response', 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(res => setTimeout(res, wait));
      return createAvalaraProposalSync(proposalData, attempt + 1);
    }

    console.error('❌ Avalara Proposal Sync Failed', err.message);
    return {};
  }
}


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

const updateProposalStatus = async (req, res) => {
  const { status, id, requestId, userId,paymentType } = req.body
 docNum=null
 quickbookId=null
let syncWithQuickbooks=0
let ReasonWithQb =''

  const propsalData = await RequestProposals.findProposalById(id)
  console.log('propsalDatapropsalDatapropsalData',propsalData)
  let avalraResponse = {}
//   if (
//   propsalData.rfpServices.length > 0 &&
//   propsalData.rfpProducts.length > 0 
//   // propsalData.rfpProducts.some(val => val.isMaterial === 1)
// ) {
//   // Do something here if both conditions are met
//   avalraResponse = await createAvalaraProposalSync(propsalData) || {}
// }
  
  
  let previosBudget = Number(propsalData.budget)
  let budgetWithoutTax = (previosBudget - Number(propsalData.tax_amount))
 
  let vendorData = propsalData.vendorSyncWithQb !=1? propsalData?.name:""
  let productDataWithoutQb =[]
  let serviceDataWithoutQb =[]
  let materialDataWithoutQb =[]

  if(propsalData.rfpProducts.length > 0){
    productDataWithoutQb = propsalData.rfpProducts.filter(product => product.syncWithQuickbooks !=1 && product?.isMaterial == 0)

    const syncedProductData = propsalData.rfpProducts.filter(product => product.syncWithQuickbooks ==1 && product?.isMaterial == 0)
    for(const product of syncedProductData){
      const productResultFromQb = await validateInventoryItemsInQbForPo(product,"quickbookId");
      if(productResultFromQb?.itemData){
        productDataWithoutQb.push(productResultFromQb?.itemData);
        const updateData ={
          syncWithQuickbooks: 0,
          quickbookId: null
        }
        const updateProduct = await Product.updateProductByQbId(product.quickbookId,updateData);
      }
    }
  }
  if(propsalData.rfpServices.length > 0){
    serviceDataWithoutQb = propsalData.rfpServices.filter(service => service.syncWithQuickbooks !=1)

    const syncedServiceData = propsalData.rfpServices.filter(service => service.syncWithQuickbooks ==1)
    for(const service of syncedServiceData){
      const serviceResultFromQb = await validateInventoryItemsInQbForPo(service,"quickbookId");
      if(serviceResultFromQb?.itemData){
        serviceDataWithoutQb.push(serviceResultFromQb?.itemData);
        const updateData ={
          syncWithQuickbooks: 0,
          quickbookId: null
        }
        const updateService = await Product.updateServiceByQbId(service.quickbookId,updateData);
      }
    }
  }
  if(propsalData.rfpProducts.length > 0){
    materialDataWithoutQb = propsalData.rfpProducts.filter(product => product.syncWithQuickbooks !=1 && product?.isMaterial == 1)

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

  if(propsalData.vendorSyncWithQb ==1 && propsalData.partnerQbId){
    const vendorResultFromQb = await validateVendorInQb(propsalData.partnerQbId);
    if(vendorResultFromQb?.removeQbid){
      vendorData = propsalData.name
      const updateData ={
        syncWithQuickbooks: 0,
        partnerQbId: null
      }
      const updateVendor = await Partners.updateVendorByQbId(propsalData.partnerQbId,updateData);
    }
  }

  // let productNames = propsalData.rfpProducts.filter(product => product.syncWithQuickbooks !=1 && product?.isMaterial == 0)
  // .map(product => product.product_name).join(', ');
  // const serviceNames = propsalData.rfpServices
  // .filter(service => service.syncWithQuickbooks !=1)
  // .map(service => service.labour_cost_service_name).join(', ');
  // let materialNames = propsalData.rfpProducts.filter(product => product.syncWithQuickbooks !=1 && product?.isMaterial == 1)
  // .map(product => product.product_name).join(', ');
  let productNames = productDataWithoutQb.map(product => product.product_name).join(', ');
  const serviceNames = serviceDataWithoutQb.map(service => service.labour_cost_service_name).join(', ');
  let materialNames = materialDataWithoutQb.map(product => product.product_name).join(', ');



  console.log('vendorDatavendorData',vendorData)

  if(vendorData || productNames ||serviceNames || materialNames){
     ReasonWithQb = `This Proposal is not synced with QuickBooks because the following are not in QuickBooks:
  ${productNames.length > 0 ? `Catalogs : ${productNames}` : ''}
  ${serviceNames.length > 0 ? `Services : ${serviceNames}` : ''}
  ${materialNames.length > 0 ? `Materials : ${materialNames}` : ''}
  ${vendorData ? `Vendor : ${vendorData}` : ''}`;

  console.log('ReasonWithQb:', ReasonWithQb);
  }
const readToSyncWithQB =(!vendorData && productDataWithoutQb.length ==0 && serviceDataWithoutQb.length ==0 && materialDataWithoutQb.length ==0)
  try {

  if(readToSyncWithQB){
   console.log("start RFP to QB",propsalData)

  const response = await createPurchaseOrder(propsalData)

  if (docNum && quickbookId) {
    ReasonWithQb="Successfully Synced With Quickbooks"
    let proposalData ={doc_number:docNum ,rfpQbId : quickbookId,syncWithQuickbooks:1,reasonWithQb:ReasonWithQb}
    // data.doc_number = docNum
    // data.quickbookId = quickbookId
   console.log("completedddddddd",proposalData)
  

    let result = await Proposals.updateProposalById(requestId , proposalData)
  }

  }else{

    let proposalData ={syncWithQuickbooks:0,reasonWithQb:ReasonWithQb}


    let result = await Proposals.updateProposalById(requestId , proposalData)

  }

}catch (error) {
    console.log("error",error)
}
  let data = { 
    status: status,
    // avalara_transaction_id: avalraResponse.avalara_transaction_id,
    // avalara_code: avalraResponse.avalara_code || null,
    // avalara_code: avalraResponse.avalara_code || null,
  }
  // if(avalraResponse.avalara_code){
  //  data.tax  = parseFloat(avalraResponse.totalTax || 0).toFixed(2)
  //  data.budget = parseFloat(Number(budgetWithoutTax) + Number(avalraResponse.totalTax))
  // }


  let result = await RequestProposals.updateProposalStatus(id, data, requestId)
  if (result) {
    let notificationMsg = `Proposal has been awarded.`
    let notificationUrl = "official-request";
    const incrementResult_User = await Admin.incrementField(
      userId,
      "noti_count",
      1
    );

    let notificationData_user = {
      user_id: userId,
      url: notificationUrl,
      timestamp: moment().unix(),
      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: moment().unix(),
          message: notificationMsg,
          status: 1,
          from_id: userId,
        };

        const addNotificationResult = await Admin.addNotification(
          notificationData
        );

      }
    }
    res.send({
      message: "Proposal awarded successfully",
      proposalId: result,
      status: 1
    })
  }
  else {
    res.send({
      message: "Proposal not updated",
      status: 1
    })
    console.log("Proposal not updated");
    return;
  }
}
const updateProposalStatusPayment = async (req, res) => {
  const { status, id, requestId, userId,paymentType } = req.body
 
  let data = { status: status ,paymentMode:paymentType}
  console.log('data+++++++++++++++++', data)


  let result = await RequestProposals.updateProposalStatusPayment(id, data, requestId)
  if (result) {
    let notificationMsg = `Proposal payment has been done.`
    let notificationUrl = "official-request";
    const incrementResult_User = await Admin.incrementField(
      userId,
      "noti_count",
      1
    );

    let notificationData_user = {
      user_id: userId,
      url: notificationUrl,
      timestamp: moment().unix(),
      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: moment().unix(),
          message: notificationMsg,
          status: 1,
          from_id: userId,
        };

        const addNotificationResult = await Admin.addNotification(
          notificationData
        );

      }
    }
    res.send({
      message: "Proposal awarded successfully",
      proposalId: result,
      status: 1
    })
  }
  else {
    res.send({
      message: "Proposal not updated",
      status: 1
    })
    console.log("Proposal not updated");
    return;
  }
}

const convertRFPToRFQ = async (req, res) => {

  let { request_id, userId, id } = req.body
  let requestProposals = await RequestProposals.getAllPropsalsByRequestId(request_id, 1, 1)
  requestProposals = requestProposals.filter(row => row.status === 2)
  console.log("Request Proposal", requestProposals);
  let result = await Proposals.getAllProposals(request_id, userId, 3)
  result = result.data
  console.log("Request Proposal result", result);
  let orderData = await Proposals.getCustomerOrderRequestId(request_id, userId)
  let order_id = orderData.id

  if (!empty(order_id)) {
    let results
    if (!empty(orderData.proposalId)) {
      console.log("############Hifiiiiiiiii");
      results = await Proposals.getVendorOrderProductsbyId(orderData.proposalOrder)
    }
    else {
      results = await Proposals.getCustomerOrderProductsbyId(order_id)
    }


    if (!empty(results)) {
      for (const product of results) {


        product.services = [];
      }

      orderData['cust_product'] = results;

    } else {

      orderData['cust_product'] = [];

    }

  }

  let request = {
    name: result[0].name,
    description: result[0].description,
    added_by: result[0].added_by,
    lavour_cost_services: result[0].lavour_cost_services,
    notes: result[0].notes,
    labour_cost: result[0].labour_cost,
    markup_type: result[0].markup_type,
    workflows: result[0].workflows,
    status: 2,
    timestamp: moment().unix(),
    submission_deadline: 0,
    participation_ends: 0,
    type: 2,

  }
  let requestData = await Request.addRequest(request)
  let requestOrder = {
    sub_total: orderData.sub_total,
    tax: orderData.tax,
    total: orderData.total,
    budget: orderData.budget,
    request_id: requestData,
    timestamp: moment().unix(),
    status: 1,
  }
  let resultData = await CustomerOrder.createCustomerOrder(requestOrder)
  let productData = {}
  for (let i = 0; i < orderData['cust_product'].length; i++) {

    productData.product_price = orderData['cust_product'][i]['product_price']
    productData.product_name = orderData['cust_product'][i]['product_name']
    productData.product_markup = orderData['cust_product'][i]['product_markup']
    productData.product_id = orderData['cust_product'][i]['product_id']
    productData.workflow = orderData['cust_product'][i]['workflow']
    productData.unit_qty = orderData['cust_product'][i]['unit_qty']
    productData.unit = orderData['cust_product'][i]['unit']
    productData.product_qty = orderData['cust_product'][i]['quantity']
    productData.product_total = orderData['cust_product'][i]['product_total']
    productData.product_sub_total = orderData['cust_product'][i]['product_sub_total']
    productData.product_retail_price = orderData['cust_product'][i]['product_retail_price']
    productData.product_tax = orderData['cust_product'][i]['product_tax']
    productData.discount = orderData['cust_product'][i]['discount']
    productData.timestamp = moment().unix(),
      productData.order_id = resultData,

      productData.status = 1

    let response = await CustomerOrderProduct.createCustomerOrder(productData)
  }
  requestServices = await Proposals.getVendorServices(result[0].proposalId)
  console.log("######RRRRRRRRR");
  let services = {}
  for (let i = 0; i < requestServices.length; i++) {
    services = {
      request_id:requestData,
      labour_cost_service_id: requestServices[i].labour_cost_service_id,
      recipe_id: null,
      product_id: requestServices[i].product_id,
      qty: requestServices[i].qty,
      unit_cost: requestServices[i].unit_cost,
      total_cost: requestServices[i].unit_cost+(requestServices[i].unit_cost*requestServices[i].markup/100),
      markup: requestServices[i].markup,

      jobcost_unit_cost : requestServices[i].unit_cost || 0,
      jobcost_quantity : requestServices[i].qty,
      jobcost_total_cost : (requestServices[i].unit_cost * requestServices[i].qty) || 0,
    }
    let response = await Request.addRequestServices(services)
  }
  return requestServices
}

const acceptOrRejectInvoice = async (req, reply) => {
  try {
    console.log(req.body)
    let { requestId, requestPartnerId, acceptStatus, userId, reason } = req.body

    let currentDate = Math.round(moment().valueOf() / 1000);

    let acceptQuery = await RequestPartner.acceptOrRejectRequest(requestPartnerId, { accept_status: acceptStatus });

    if (acceptStatus === 1) {
      let requestData = await Request.getRequestById(requestId)
      console.log('requestData', requestData);
      // adding accepted request to job 
      let projectData = {};
        projectData.project_uid = u_id,
        projectData.name = requestData[0]['name'],
        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,
        // Use installation_start_date as installation_date when participation_ends is 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.installation_status = 0,

        console.log("$$$$$$$$$$$$$$$$$$$ projectData : ", projectData);

      let projectNameExist = await Project.isProjectNameExist(projectData.name);
      // if (projectNameExist != true) { 
      projectData.thumbnail = 'fileNamewithDate';
      let checkProjectExist = await Request.checkExistingProject(requestId)
      // console.log("###################REW", checkProjectExist);
      if (!checkProjectExist) {
       
        let result = await Project.addProject(projectData)


        if (result) {
          let projects = await Project.getProjectById(result.id);
          let allProducts = await Product.getAllProductsBasedOnRequest(requestId)
          let taskUid = await ProjectTask.getSubTaskUid(result.id);
       
          let workFlowIDs = ""
          let skillSet = []
          
          for (const product of allProducts) {
            {

              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) {
                let insertId = await Project.addProjectMember(pm);
                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
                );
              }

            }

          }

          for (const product of allProducts) {
            
            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,
            };

            const projectTaskId = await ProjectTask.addProjectTask(projectTask);
            let task_uid = await ProjectTask.getSubTaskUid(projectTaskId);
            for (const workflow of product.workflow) {
              console.log(workflow, '--------workflow')
              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')
             
              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);
                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
                );

              }

            }
          }
       
          /************  Jobcost ******************** */

          let allMaterials = await Product.getAllMaterials(true)
          let allProductsData = await Product.getAllLocations(requestId)
          let allRecipies = await Recipe.getAllReceipesByProjectId(allProductsData,requestId);
          let results = await Product.getFinishedProducts();
          let newMaterialObjects = [];
          let jobCostFlag=0;
          let notifyJobcost=""
          // console.log("#################recipe123", allRecipies);
         
          allRecipies.forEach((recipeItem) => {
            recipeItem.ingredients.forEach((ingredient) => {
              let quantityNeeded = ingredient.quantity*recipeItem.quantity*recipeItem.location_quantity;
              let materialItems = results.filter((mat) => mat.material_id === ingredient.product_id);
              let totalAvailableQuantity = materialItems.reduce((total, item) => total + item.qty, 0);

              if (totalAvailableQuantity >= quantityNeeded) {
                // Sufficient quantity available in existing packages
                let remainingQuantity = quantityNeeded;
                materialItems.forEach((materialItem) => {
                  if (remainingQuantity > 0) {
                    let quantityToPull = Math.min(remainingQuantity, materialItem.qty);
                    newMaterialObjects.push({
                      id: materialItem.id,
                      productId: materialItem.material_id,
                      quantity: quantityToPull,
                      unit_cost: materialItem.unit_cost,
                      total_cost: parseFloat(quantityToPull) * parseFloat(materialItem.unit_cost),
                      recipe: recipeItem.id,
                      recipeName: recipeItem.name,
                    });
                    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}`
                }
                else{
                   notifyJobcost+=` ,${ingredient.name}`
                }
                
              
                
                jobCostFlag=1
              }
            });
          });
          console.log("###############vk1888888888888888888");
          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,
              quantity: rfpProduct[i].product_qty*rfpProduct[i].location_quantity,
              unit_price:  rfpProduct[i].product_price,
              total:  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
            };
            insertedstatus = await JobCost.addJobCost(jobCostData);
          }
          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: newMaterialObjects[i].quantity,
                  unit_price: newMaterialObjects[i].unit_cost,
                  total: newMaterialObjects[i].total_cost,
                  recipe: 1,
                  recipe_name: newMaterialObjects[i].recipeName,
                  timestamp: moment().unix(),
                  added_by: userId,
                };
                if (jobCostData?.quantity > 0) {
                  insertedstatus = await JobCost.addJobCost(jobCostData);
                  // console.log("#####insertedStatus", insertedstatus);

                  reduceStockResult = await Product.updateMaterialStock(newMaterialObjects[i].id, data)
                  // console.log("#####reduceStockResult", reduceStockResult);
                }
              } else {
                console.log(`Stock is lesser than entered qty`);
                // reply.send({ status: 0, message: `Stock is lesser than entered qty for ${materialData[0].name} (${materialData[0].barcode})`, resultId: materialData[0].id });
              }
            }
          }
          console.log("#################newMaterialObjects", newMaterialObjects);
            console.log("#################newMaterialObjects22222222222");
            let newMaterialRfqObjects = [];
          let materialRfq=await CustomerOrderProduct.getMaterialProductByRequest(requestId)
          for(let i=0;i<materialRfq.length; i++){
            let quantityNeeded = materialRfq[i].product_qty;
            let materialItems = results.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;
              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.unit_cost,
                    total_cost: parseFloat(quantityToPull) * parseFloat(materialItem.unit_cost),
                    recipe:1,
                    recipeName: null,
                  });
                  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.2`);
              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}`
              }
              else{
                 notifyJobcost+=` ,${materialRfq[i].product_name}`
              }
              
            
              
              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: newMaterialRfqObjects[i].quantity,
                  unit_price: newMaterialRfqObjects[i].unit_cost,
                  total: newMaterialRfqObjects[i].total_cost,
                  recipe: 1,
                  recipe_name: newMaterialRfqObjects[i].recipeName,
                  timestamp: moment().unix(),
                  added_by: userId,
                };
                if (jobCostData?.quantity > 0) {
                  insertedstatus = await JobCost.addJobCost(jobCostData);
                  // console.log("#####insertedStatus", insertedstatus);

                  reduceStockResult = await Product.updateMaterialStock(newMaterialRfqObjects[i].id, data)
                  // console.log("#####reduceStockResult", reduceStockResult);
                }
              } else {
                console.log(`Stock is lesser than entered qty`);
                // reply.send({ status: 0, message: `Stock is lesser than entered qty for ${materialData[0].name} (${materialData[0].barcode})`, resultId: materialData[0].id });
              }
            }
          }
       
          let notificationMsg = `New Job "${projectData.name}" has been added.`;
          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
              );

            }
          }


          // }
          /////

          const statusUpdate = await Request.updateRequets(requestId, { status: 4 });
          if (jobCostFlag === 1) {
            const resultStatus = await Project.updateProject(result.id, { jobcost: 1, notifyJobcost });
          }
        }
      }
      else {
        console.log("Enter to edit-----------------------")
        let result = await Project.updateProject(checkProjectExist, projectData)
        let statusUpdate = await Request.updateRequets(requestId, { status: 4 });
        let projects = await Project.getProjectById(checkProjectExist);
        let allProducts = await Product.getAllProductsBasedOnRequest(requestId)
        let taskUid = await ProjectTask.getSubTaskUid(checkProjectExist);
        // console.log("###################allProducts", allProducts);
        let workFlowIDs = ""
        let skillSet = []
        // reply.send({allProducts:allProducts})
        for (const product of allProducts) {
          {
            if (product.workflow[0]) {
              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) {
              let insertId = await Project.addProjectMember(pm);
              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)
        console.log("############### allProjectTask", allProjectTask);
        // 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)));
            // console.log("############### taskNameExists", taskNameExists);
            if (!taskNameExists) {
              await ProjectTask.updateProjectTask({ status: 0 }, task.ID);
              await ProjectTask.updateProjectSubTaskByTaskId({ status: 0 }, task.ID);
            }
          }
        });

        await Promise.all(promises);



        for (const product of allProducts) {

          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: 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
          };


          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);
            for (const workflow of product.workflow) {
              // console.log(workflow, '--------workflow')
              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,
              };
              // 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)
              }
              
              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);
                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
                );

              }

              // }

            }
          }
          else {
            for (const workflow of product.workflow) {
              let task_uid = await ProjectTask.getSubTaskUid(checkTaskExisting);
              console.log(workflow, '--------workflow')
              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) {
                const projectSubTaskId = await ProjectTask.addProjectSubTask(subTask);
                let projectManagers=[]
                if(!empty(workflow.skill_ids)){
                 projectManagers = await Project.getTeamMembersBasedOnSkills(workflow.skill_ids, 14)
                }
                
                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);
                  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-----------------
        }
        /************  Jobcost ******************** */
        let flag = 0
        let recipe = 1
        let allMaterials = await Product.getAllMaterials(true)
        let allProductsData = await Product.getAllLocations(requestId)
        let 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)
            console.log('material---', material);
            if (!empty(material)) {
              //const matchingValue = newMaterialObjects?.find(val => val?.id === material[0]?.id);
              let newQty = material[0]?.qty + element.quantity
              let totalCost = material[0]?.unit_cost * newQty
              console.log("#############material[0]", material[0]);
              console.log("#############element", element);
              console.log("#############newQty3", 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`);
                // 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();
        let newMaterialObjects = [];
       
        let jobCost=0;
        let notifyJobcost=""
       
        allRecipies.forEach((recipeItem) => {
          recipeItem.ingredients.forEach((ingredient) => {
            let quantityNeeded = ingredient.quantity*recipeItem.quantity*recipeItem.location_quantity;
            let materialItems = results.filter((mat) => mat.material_id === ingredient.product_id);
            let totalAvailableQuantity = materialItems.reduce((total, item) => total + item.qty, 0);

            if (totalAvailableQuantity >= quantityNeeded) {
              // Sufficient quantity available in existing packages
              let remainingQuantity = quantityNeeded;
              materialItems.forEach((materialItem) => {
                if (remainingQuantity > 0) {
                  let quantityToPull = Math.min(remainingQuantity, materialItem.qty);
                  newMaterialObjects.push({
                    id: materialItem.id,
                    productId: materialItem.material_id,
                    quantity: quantityToPull,
                    unit_cost: materialItem.unit_cost,
                    total_cost: parseFloat(quantityToPull) * parseFloat(materialItem.unit_cost),
                    recipe: recipeItem.id,
                    recipeName: recipeItem.name,
                  });
                  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}`
              }
              else{
                 notifyJobcost+=` ,${ingredient.name}`
              }
              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("#############MaterialData", materialData[0]);
              console.log("#############newMaterialRfqObjects", newMaterialRfqObjects[i]);
              console.log("#############newQty4", 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: newMaterialObjects[i].quantity,
                  unit_price: newMaterialObjects[i].unit_cost,
                  total: newMaterialObjects[i].total_cost,
                  recipe: 1,
                  recipe_name: newMaterialObjects[i].recipeName,
                  timestamp: moment().unix(),
                  added_by: userId,
                };
                if (jobCostData?.quantity > 0) {
                  insertedstatus = await JobCost.addJobCost(jobCostData);
                  console.log("#####jobCostData", jobCostData);

                  console.log("#####insertedStatus", insertedstatus);

                  reduceStockResult = await Product.updateMaterialStock(newMaterialObjects[i].id, data)
                  console.log("#####reduceStockResult", reduceStockResult);
                }
              } else {
                console.log(`Stock is lesser than entered qty`);
                // reply.send({ status: 0, message: `Stock is lesser than entered qty for ${materialData[0].name} (${materialData[0].barcode})`, resultId: materialData[0].id });
              }
            }
          }
        }
        let deleteRfpJobcost=await JobCost.deleteRfpJobcost(checkProjectExist)
        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: checkProjectExist,
            quantity: rfpProduct[i].product_qty*rfpProduct[i].location_quantity,
            unit_price:  rfpProduct[i].product_price,
            total:  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
          };
          insertedstatus = await JobCost.addJobCost(jobCostData);
        }
        console.log("#################newMaterialObjects22222222222");
        let newMaterialRfqObjects = [];
          let materialRfq=await CustomerOrderProduct.getMaterialProductByRequest(requestId)
      for(let i=0;i<materialRfq.length; i++){
        let quantityNeeded = materialRfq[i].product_qty;
        let materialItems = results.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;
          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.unit_cost,
                total_cost: parseFloat(quantityToPull) * parseFloat(materialItem.unit_cost),
                recipe:1,
                recipeName: null,
              });
              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}`
          }
          else{
             notifyJobcost+=` ,${materialRfq[i].product_name}`
          }
          
        
          jobCost=1
          jobCostFlag=1
        }
      }
      console.log("#################newMaterialObjects22222222222",newMaterialRfqObjects);
      console.log("#################notifyJobcost",notifyJobcost);
      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("#############newMaterialRfqObjects", newMaterialRfqObjects[i]);
          console.log("#############newQty5", 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: newMaterialRfqObjects[i].quantity,
              unit_price: newMaterialRfqObjects[i].unit_cost,
              total: newMaterialRfqObjects[i].total_cost,
              recipe: 1,
              recipe_name: newMaterialRfqObjects[i].recipeName,
              timestamp: moment().unix(),
              added_by: userId,
            };
            if (jobCostData?.quantity > 0) {
              insertedstatus = await JobCost.addJobCost(jobCostData);
              // console.log("#####insertedStatus", insertedstatus);

              reduceStockResult = await Product.updateMaterialStock(newMaterialRfqObjects[i].id, data)
              // console.log("#####reduceStockResult", reduceStockResult);
            }
          } else {
            console.log(`Stock is lesser than entered qty`);
            // reply.send({ status: 0, message: `Stock is lesser than entered qty for ${materialData[0].name} (${materialData[0].barcode})`, resultId: materialData[0].id });
          }
        }
      }
   

        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
            );

          }
        }
        let allTask = await ProjectTask.getAllProjectTasks(checkProjectExist, userType = 1)
        let firstTaskStatus = allTask[0].status
        console.log("#############AllTask");
        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
        }
        let resultStatus = await Project.updateProject(checkProjectExist, projectTaskData);
      }
    }
    else {
      let statusUpdate = await Request.updateRequets(requestId, { status: 6, reason });

    }
    if (acceptQuery) {
      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 notificationMsg = `The member ${partner.name} accepted the Invoice ${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 ${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}`;

        // 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 {
        reply.send({ status: 2, message: "You have  rejected the request" });
      }
    }

    else {
      if (acceptStatus == 1) {
        reply.send({ status: 1, message: "You have accepted the request" });
      } else {
        reply.send({ atus: 2, message: "You have  rejected the request" });
      }
    }

  }
  catch (error) {
    console.log("error : ", error);
    reply.send({ status: 500, message: "Server error", error });
  }
}

module.exports = { addProposal, updateProposal, deleteProposal, updateProposalStatus,updateProposalStatusPayment, convertRFPToRFQ }