For Zoho Services only:


I'm actually part of something bigger at Ascent Business Solutions recognized as the top Zoho Premium Solutions Partner in the United Kingdom.

Ascent Business Solutions offer support for smaller technical fixes and projects for larger developments, such as migrating to a ZohoCRM.  A team rather than a one-man-band is always available to ensure seamless progress and address any concerns. You'll find our competitive support rates with flexible, no-expiration bundles at https://ascentbusiness.co.uk/zoho-services/uk-zoho-support.  For larger projects, talk to our experts and receive dedicated support from our hands-on project consultants at https://ascentbusiness.co.uk/zoho-services/zoho-crm-implementation.

The team I manage specializes in coding API integrations between Zoho and third-party finance/commerce suites such as Xero, Shopify, WooCommerce, and eBay; to name but a few.  Our passion lies in creating innovative solutions where others have fallen short as well as working with new businesses, new sectors, and new ideas.  Our success is measured by the growth and ROI we deliver for clients, such as transforming a garden shed hobby into a 250k monthly turnover operation or generating a +60% return in just three days after launch through online payments and a streamlined e-commerce solution, replacing a paper-based system.

If you're looking for a partner who can help you drive growth and success, we'd love to work with you.  You can reach out to us on 0121 392 8140 (UK) or info@ascentbusiness.co.uk.  You can also visit our website at https://ascentbusiness.co.uk.
Zoho CRM: JS Widget: Generic Script to pass the Record ID to a CRM function

Zoho CRM: JS Widget: Generic Script to pass the Record ID to a CRM function

What?
There's documentation out there but as this took me a couple of days to simply install a JS widget hosted by Zoho and pass the record ID via a button to it, I'm adding it here in case I need to refer to it in future.

Why?
We have an Accounts module which holds all the companies we deal with in our Zoho CRM. I want our sales team to be able to click on a button off the CRM account record which will call an API returning all the credit information about a company. Unfortunately, if you do this simply using Zoho Deluge in a button, there are no new lines/carriage returns or styling that you can apply, which pops up as a message box to the sales person; but all on one line and not that readable. Using a JS Widget, we can popup a window over the Zoho CRM which displays a more clear HTML & CSS styled message box.

How?
There are links to how to create a widget in detail which I'll link to at the bottom of this page. What follows are the quick steps to create a widget.html file and how to get whatever the Zoho Deluge CRM function returns.

Create a JS Widget on your device (MacOS)
These steps are the minimal amount of information I need to quickly set up a JS widget:
  1. Open a MacOS Terminal
    1. sudo npm install -g zoho-extension-toolkit (this also updates existing install)
    2. sudo zet init
    3. select Zoho CRM
    4. enter a unique Project Name
    5. cd to the new project name
    6. To give permissions to write sudo chmod -R 777 .
    7. open the newly created widget.html file in your code editor
    8. add the code as per the below generic script
    9. when done, save 'widget.html'
    10. then return to terminal and type zet validate
    11. and then type zet pack
    12. this will create a zip file in a dist folder
  2. Login to ZohoCRM as a system administrator
    1. go to Setup > Customization > Modules and Fields > Accounts [or whichever module the button needs to be on] > Buttons > Create New Button
    2. Give it a name, Define the action as Open a Widget, Select Page as In Record, select position as Details then click on Configured Widget - Choose
      1. Click on New Widget, give it a name and set hosting to Zoho
      2. Click on File Upload and upload the zip file that was just created
      3. the Index Page is /widget.html
      4. Hit Save
    3. then find the one you just created in the list and click on Install
    4. Select the profile(s) that will have access to this button and click on Save
  3. Go to a CRM account record and test the button works.
  4. Go back to your Zoho CRM function and simply include HTML and CSS in the output of the function... done.

What we're here for
The following script is what I use to pass the record ID to a CRM function called fn_Accounts_CreditSafe. It was partly written by referring to documentation and using CoPilot or OpenAI ChatGPT for parsing the return value of the function (note that I have SCRIPT within the BODY tags and not in the HEAD):
copyraw
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
  </head>
  <body>
    <script src="https://live.zwidgets.com/js-sdk/1.1/ZohoEmbededAppSDK.min.js"></script>
    <pre>  Loading... Please Wait... </pre>
    <script>
      console.clear();

      ZOHO.embeddedApp.on("PageLoad", function(r_Data) {

        // for debugging
        console.log(r_Data);

        // get id and module on page (if this is a button off a record details view)
        var v_EntityID = r_Data.EntityId, v_EntityModule = r_Data.Entity;

        // get id and module on page (depending on trigger)
        // var v_EntityID = r_Data.EntityId[0], v_EntityModule = r_Data.Entity;

        // Define the function name to be called (don't include the namespace)
        var v_FuncName = "fn_Accounts_GetCreditSafe";

        // Prepare the parameters for the function call
        var m_Params = {
          "arguments": JSON.stringify({
            "p_AccountID": v_EntityID
          })
        };

        ZOHO.CRM.FUNCTIONS.execute( v_FuncName, m_Params )
        .then( function( r_SubData ){

          console.log("Raw response:", r_SubData);

          // Check if the response contains the expected structure
          let output = r_SubData?.details?.output;

          // If the output is JSON text, parse it into a real object
          let parsedOutput;

          try {

            // Attempt to parse the output as JSON
            parsedOutput = JSON.parse(output);

          } catch (e) {

            // If parse fails, just fall back to plain text
            parsedOutput = output;

          }

          // Log the parsed output for debugging
          console.log("Parsed output:", parsedOutput);

          // If parsedOutput is undefined or null, set a default message
          if(parsedOutput === undefined || parsedOutput === null) {

             let errorOutput = r_SubData?.message || "Response from function was unparseable";
            parsedOutput = "<pre style='padding:30px;'>Error: " + errorOutput + "</pre>";
            document.writeln(parsedOutput);

          } else {

            // If parsedOutput is an object, you can now use normal dot notation:
            if (parsedOutput && typeof parsedOutput === "object") {

              // If the output is an object, you can access its properties
              document.writeln(parsedOutput.details.output || "<pre style='padding:30px;'>Error: No parsed output found</pre>");

            } else {

              // If the output is not an object, just write it directly
              document.writeln(parsedOutput);

            }
          }

        })
        .catch( function( r_Error ){
          document.write(JSON.stringify(r_Error));
        });

      });

      ZOHO.embeddedApp.init();

    </script>

  </body>
</html>
  1.  <!DOCTYPE html> 
  2.  <html> 
  3.    <head> 
  4.      <meta charset="UTF-8"> 
  5.    </head> 
  6.    <body> 
  7.      <script src="https://live.zwidgets.com/js-sdk/1.1/ZohoEmbededAppSDK.min.js"></script> 
  8.      <pre>  Loading... Please Wait... </pre> 
  9.      <script> 
  10.        console.clear()
  11.   
  12.        zoho.embeddedApp.on("PageLoad", function(r_Data) { 
  13.   
  14.          // for debugging 
  15.          console.log(r_Data)
  16.   
  17.          // get id and module on page (if this is a button off a record details view) 
  18.          var v_EntityID = r_Data.EntityId, v_EntityModule = r_Data.Entity; 
  19.   
  20.          // get id and module on page (depending on trigger) 
  21.          // var v_EntityID = r_Data.EntityId[0], v_EntityModule = r_Data.Entity; 
  22.   
  23.          // Define the function name to be called (don't include the namespace) 
  24.          var v_FuncName = "fn_Accounts_GetCreditSafe"
  25.   
  26.          // Prepare the parameters for the function call 
  27.          var m_Params = { 
  28.            "arguments": JSON.stringify({ 
  29.              "p_AccountID": v_EntityID 
  30.            }) 
  31.          }
  32.   
  33.          zoho.crm.FUNCTIONS.execute( v_FuncName, m_Params ) 
  34.          .then( function( r_SubData ){ 
  35.   
  36.            console.log("Raw response:", r_SubData)
  37.   
  38.            // Check if the response contains the expected structure 
  39.            let output = r_SubData?.details?.output; 
  40.   
  41.            // If the output is JSON text, parse it into a real object 
  42.            let parsedOutput; 
  43.   
  44.            try { 
  45.   
  46.              // Attempt to parse the output as JSON 
  47.              parsedOutput = JSON.parse(output)
  48.   
  49.            } catch (e) { 
  50.   
  51.              // If parse fails, just fall back to plain text 
  52.              parsedOutput = output; 
  53.   
  54.            } 
  55.   
  56.            // Log the parsed output for debugging 
  57.            console.log("Parsed output:", parsedOutput)
  58.   
  59.            // If parsedOutput is undefined or null, set a default message 
  60.            if(parsedOutput === undefined || parsedOutput === null) { 
  61.   
  62.               let errorOutput = r_SubData?.message || "Response from function was unparseable"
  63.              parsedOutput = "<pre style='padding:30px;'>Error: " + errorOutput + "</pre>"
  64.              document.writeln(parsedOutput)
  65.   
  66.            } else { 
  67.   
  68.              // If parsedOutput is an object, you can now use normal dot notation: 
  69.              if (parsedOutput && typeof parsedOutput === "object") { 
  70.   
  71.                // If the output is an object, you can access its properties 
  72.                document.writeln(parsedOutput.details.output || "<pre style='padding:30px;'>Error: No parsed output found</pre>")
  73.   
  74.              } else { 
  75.   
  76.                // If the output is not an object, just write it directly 
  77.                document.writeln(parsedOutput)
  78.   
  79.              } 
  80.            } 
  81.   
  82.          }) 
  83.          .catch( function( r_Error ){ 
  84.            document.write(JSON.stringify(r_Error))
  85.          })
  86.   
  87.        })
  88.   
  89.        zoho.embeddedApp.init()
  90.   
  91.      </script> 
  92.   
  93.    </body> 
  94.  </html> 


[Note to self]
Retrieving the record itself (not needed for this task):
copyraw
ZOHO.embeddedApp.on("PageLoad", function(r_Data) {
        // get id and module on page
        var v_EntityID = r_Data.EntityId, v_EntityModule = r_Data.Entity;

        // get the record details
        ZOHO.CRM.API.getRecord({ Entity: v_EntityModule, RecordID: v_EntityID })
        .then(function(response) {
            console.log("Record data:");
            console.log(response);
        }).catch(function(error) {
            console.log("Error:");
            console.log(error);
        });
});
ZOHO.embeddedApp.init();
  1.  zoho.embeddedApp.on("PageLoad", function(r_Data) { 
  2.          // get id and module on page 
  3.          var v_EntityID = r_Data.EntityId, v_EntityModule = r_Data.Entity; 
  4.   
  5.          // get the record details 
  6.          zoho.crm.API.getRecord({ Entity: v_EntityModule, RecordID: v_EntityID }) 
  7.          .then(function(response) { 
  8.              console.log("Record data:")
  9.              console.log(response)
  10.          }).catch(function(error) { 
  11.              console.log("Error:")
  12.              console.log(error)
  13.          })
  14.  })
  15.  zoho.embeddedApp.init()

[Optional]
What follows is the code for the function fn_Accounts_CreditSafe... obviously I can't give you the real Zoho Deluge function here as it would use my credentials but what you need to know is that it is passed the crmAPIRequest (string) variable and that the function itself had to have API Key and OAuth enabled (hover over function in CRM setup and select REST API, then tick both to the "on" position). The returned output from this function should be the HTML that will be displayed in your JS Widget:
copyraw
string standalone.fn_Accounts_GetCreditSafe(string crmAPIRequest)
{
/* *******************************************************************************
	Function:       string standalone.fn_Accounts_GetCreditSafe(string crmAPIRequest)
	Label:          Fn - Accounts - Get Credit Safe Details
	Trigger:        On-Demand / Widget
	Purpose:		Given an account ID returns the credit safe details from CreditSafe Connect API. This function is used by a JS Widget.
	Inputs:         string crmAPIRequest (webhook request from Zoho CRM)
	Outputs:        -

	Date Created:   2023-08-15 (Ascent Business - Joel Lipman)
					- Initial release
					- Testing auth and connection
	Date Modified:	2023-08-15 (Ascent Business - Joel Lipman)
					- Separated function from button so that is can be used by Widget
	******************************************************************************* */
	//
	// initialize
	v_AccountID = "";
	v_Output = "Error: Could not retrieve given CRM Account ID";
	//
	// ****************************** GENERIC CODE BELOW ******************************
	//
	// retrieving the parameters of the request
	m_RequestParams = crmAPIRequest.toMap().get("params");
	//
	// retrieving the set of values from 'arguments' passed in the javascript code
	m_Arguments = ifnull(m_RequestParams.get("arguments"), Map());
	//
	if(!isNull(m_Arguments.get("p_AccountID")))
	{
		v_AccountID = m_Arguments.get("p_AccountID").toString();
		v_Output = "Info: Recognized CRM Account ID: " + v_AccountID;
		//
		r_Account = zoho.crm.getRecordById("Accounts",v_AccountID.toLong());
		if(!isNull(r_Account.get("id")))
		{
			v_Output = "Info: Retrieved Record of CRM Account ID: " + v_AccountID;
		}
	}
	//
	// format output
	v_OutputHtml = "<h1 style='color:green'>Hullo World!</h1><p>"+v_Output+"</p>";
	return v_OutputHtml;
}
  1.  string standalone.fn_Accounts_GetCreditSafe(string crmAPIRequest) 
  2.  { 
  3.  /* ******************************************************************************* 
  4.      Function:       string standalone.fn_Accounts_GetCreditSafe(string crmAPIRequest) 
  5.      Label:          Fn - Accounts - Get Credit Safe Details 
  6.      Trigger:        On-Demand / Widget 
  7.      Purpose:        Given an account ID returns the credit safe details from CreditSafe Connect API. This function is used by a JS Widget. 
  8.      Inputs:         string crmAPIRequest (webhook request from Zoho CRM) 
  9.      Outputs:        - 
  10.   
  11.      Date Created:   2023-08-15 (Ascent Business - Joel Lipman) 
  12.                      - Initial release 
  13.                      - Testing auth and connection 
  14.      Date Modified:    2023-08-15 (Ascent Business - Joel Lipman) 
  15.                      - Separated function from button so that is can be used by Widget 
  16.      ******************************************************************************* */ 
  17.      // 
  18.      // initialize 
  19.      v_AccountID = ""
  20.      v_Output = "Error: Could not retrieve given CRM Account ID"
  21.      // 
  22.      // ****************************** GENERIC CODE BELOW ****************************** 
  23.      // 
  24.      // retrieving the parameters of the request 
  25.      m_RequestParams = crmAPIRequest.toMap().get("params")
  26.      // 
  27.      // retrieving the set of values from 'arguments' passed in the javascript code 
  28.      m_Arguments = ifnull(m_RequestParams.get("arguments"), Map())
  29.      // 
  30.      if(!isNull(m_Arguments.get("p_AccountID"))) 
  31.      { 
  32.          v_AccountID = m_Arguments.get("p_AccountID").toString()
  33.          v_Output = "Info: Recognized CRM Account ID: " + v_AccountID; 
  34.          // 
  35.          r_Account = zoho.crm.getRecordById("Accounts",v_AccountID.toLong())
  36.          if(!isNull(r_Account.get("id"))) 
  37.          { 
  38.              v_Output = "Info: Retrieved Record of CRM Account ID: " + v_AccountID; 
  39.          } 
  40.      } 
  41.      // 
  42.      // format output 
  43.      v_OutputHtml = "<h1 style='color:green'>Hullo World!</h1><p>"+v_Output+"</p>"
  44.      return v_OutputHtml; 
  45.  } 


Source(s)

Category: Zoho :: Article: 908

Add comment

Your rating:

Submit

Credit where Credit is Due:


Feel free to copy, redistribute and share this information. All that we ask is that you attribute credit and possibly even a link back to this website as it really helps in our search engine rankings.

Disclaimer: Please note that the information provided on this website is intended for informational purposes only and does not represent a warranty. The opinions expressed are those of the author only. We recommend testing any solutions in a development environment before implementing them in production. The articles are based on our good faith efforts and were current at the time of writing, reflecting our practical experience in a commercial setting.

Thank you for visiting and, as always, we hope this website was of some use to you!

Kind Regards,

Joel Lipman
www.joellipman.com

Accreditation

Badge - Zoho Creator Certified Developer Associate
Badge - Zoho Deluge Certified Developer
Badge - Certified Zoho CRM Developer

Donate & Support

If you like my content, and would like to support this sharing site, feel free to donate using a method below:

Paypal:
Donate to Joel Lipman via PayPal

Bitcoin:
Donate to Joel Lipman with Bitcoin bc1qf6elrdxc968h0k673l2djc9wrpazhqtxw8qqp4

Ethereum:
Donate to Joel Lipman with Ethereum 0xb038962F3809b425D661EF5D22294Cf45E02FebF

Please publish modules in offcanvas position.