For Zoho Services only:


I'm actually part of a bigger team at Ascent Business Solutions where we have support technicians and project consultants. Support is for smaller technical fixes but this can include developments, reports or integrations; depending on the size of the task. Projects are for more time-consuming developments such as revamps of the Zoho Suite of apps or on-site training. The advantage of a team is that if I am out-of-office for a day or so, there is always someone at Ascent Business Solutions who can deal with any queries/issues you may have.

Our support rates can be found and purchased at http://ascentbusiness.co.uk/zoho-support-2. A support bundle doesn't have an expiry date. So whether we can do what you want within the bundle and a year later need further support, if there are minutes left on the bundle then there is no additional charge.

Our project rates for bigger developments can be found at http://ascentbusiness.co.uk/crm-solutions/zoho-crm-packages-prices and will involve a dedicated project consultant along with developers who will hold your hand through the development process.

If you want help building a solution for one of the Zoho Apps in the Zoho Suite, contact us on 0121 293 8140 (UK) or by email at info@ascentbusiness.co.uk. You can also visit our website at http://ascentbusiness.co.uk.

I regularly build and specialize in 2-way API integrations for Xero, Shopify and eBay.

Zoho Inventory & eBay Picture Services: UploadSiteHostedPictures

What?
This is an article documenting the process of publishing a Zoho Inventory image to eBay's Picture Hosting Services (EPS).

Why?
Because I couldn't find any other article in the whole world wide web that had a working solution. My use-case here is that I have built an eBay integration between Zoho Inventory and... eBay. Out-of-the-box, and at-time-of-print, Zoho Inventory only syncs eBay one way, in other words, listings on eBay get downloaded to Zoho Inventory but not vice-versa. Then there's an added delay of 4 hours. Then item specifics in eBay's listings don't get parsed into custom fields in Zoho Inventory... and the list goes on. So in my job, we tend to create a few custom functions which will download any item from eBay to Zoho Inventory and vice-versa. Easy to make the function to push an item to an eBay listing (see my other articles for this). But we also want to push an image from Zoho Inventory to eBay.

How?
Again, there is a caveat in that the subscription you have or your client has, needs to be Zoho One. Because we're going to use Zoho Creator as a middleware. Just to add to the pain, I'm going to make my function in Zoho CRM:

Overview
  1. Create a REST API CRM function that can return the ebay URL of the image.
  2. Done

High Level Overview
  1. Create a REST API CRM function
  2. Get Zoho CRM to create a Zoho Creator record by passing it the Inventory ID
  3. Get Zoho Creator to accept this ID, download the file, and upload it to a "File Upload" field
  4. Get Zoho Creator to generate a published / public URL of the file
  5. Get Zoho CRM to scan Creator for the public URL and retrieve this
  6. Get Zoho CRM to generate an XML request to upload the photo to eBay's Hosted Picture Services
  7. Get Zoho CRM to parse the eBay response and return the eBay hosted URL

Detailed Step-by-step
  1. Add Zoho Creator to your Zoho One
    1. Login to Zoho One
    2. Go to Settings (cog in top right)
    3. Click on Applications i the sidebar
    4. Click on the Add Application button
    5. Select Creator by clicking the Add button below it
    6. Link it if applicable to your super admin account
  2. Create an app, a form and publish it in Zoho Creator
    1. Create an app by clicking on Create App
    2. then go into the app and Create a form, I'm calling mine Inventory Photo
    3. Add 3 fields to this form
      • Zoho Inventory ID which is a single-line/text field
      • Creator File which is a file upload field
      • Public URL which is a URL field
    4. Publish it by going to Settings > Publish > Publish Component > and select the REPORT (eg. "Inventory Photo Report"). Note the embed code, specifically the long alphanumeric string.
  3. Create a connection from Zoho Creator to Zoho Inventory
    1. 3 lines icon in the top-left of your Creator (in edit this application mode)
    2. Then go to Setup > Connections > Add Connection
    3. Select Zoho OAuth and specify the scope(s) ZohoInventory.items.READ (add some more if you're unsure...)
    4. Give it a connection name and note down the connection link name. I'm calling mine joel_zoho
    5. Authorize it, etc.
  4. Make a workflow which does the clever bit in Zoho Creator:
    1. Go to Workflow > Form Workflows > New Workflow
    2. Choose the form > Run when Created or Edited > Trigger on Successful form submission > and name it OnSubmit > click on Create Workflow
    3. Select you want to run some Deluge Script and plug in the following code
      copyraw
      if(!isnull(input.Zoho_Inventory_ID))
      {
      	//
      	// specify your own Inventory/Books organization ID
      	v_BooksOrgID = 20221234567;
      	//
      	// determine URL of file to download (note the DC here is EU but yours might be COM)
      	v_Url = "https://inventory.zoho.eu/api/v1/items/" + input.Zoho_Inventory_ID + "/image?organization_id=" + v_BooksOrgID + "";
      	r_File = invokeurl
      	[
      		url :v_Url
      		type :GET
      		connection:"joel_zoho"
      	];
      	r_File.setParamName("file");
      	//
      	// store this file in the field "Creator File"
      	input.Creator_File = r_File;
      	//
      	// use publish key of the report
      	v_PublishKey = "AAAAABBBBBCCCCDDDDEEEFFFGGGGHHHIIIJJJKKLLLMMMNNOOOPPPQQWRRRSSTTUUVVWWXXYYZZ122345567890";
      	//
      	// determine the public URL of the file on this record
      	v_PublicUrl = "https://creatorapp.zohopublic.eu/file" + zoho.appuri + "Inventory_Photo_Report/" + input.ID + "/Creator_File/download/" + v_PublishKey + "?filepath=/" + input.Creator_File;
      	//
      	// add it to the public URL field
      	input.Public_URL = v_PublicUrl;
      }
      1.  if(!isnull(input.Zoho_Inventory_ID)) 
      2.  { 
      3.      // 
      4.      // specify your own Inventory/Books organization ID 
      5.      v_BooksOrgID = 20221234567
      6.      // 
      7.      // determine URL of file to download (note the DC here is EU but yours might be COM) 
      8.      v_Url = "https://inventory.zoho.eu/api/v1/items/" + input.Zoho_Inventory_ID + "/image?organization_id=" + v_BooksOrgID + ""
      9.      r_File = invokeUrl 
      10.      [ 
      11.          url :v_Url 
      12.          type :GET 
      13.          connection:"joel_zoho" 
      14.      ]
      15.      r_File.setParamName("file")
      16.      // 
      17.      // store this file in the field "Creator File" 
      18.      input.Creator_File = r_File; 
      19.      // 
      20.      // use publish key of the report 
      21.      v_PublishKey = "AAAAABBBBBCCCCDDDDEEEFFFGGGGHHHIIIJJJKKLLLMMMNNOOOPPPQQWRRRSSTTUUVVWWXXYYZZ122345567890"
      22.      // 
      23.      // determine the public URL of the file on this record 
      24.      v_PublicUrl = "https://creatorapp.zohopublic.eu/file" + zoho.appuri + "Inventory_Photo_Report/" + input.ID + "/Creator_File/download/" + v_PublishKey + "?filepath=/" + input.Creator_File; 
      25.      // 
      26.      // add it to the public URL field 
      27.      input.Public_URL = v_PublicUrl; 
      28.  } 
  5. Create a workflow to tidy up the records daily in Zoho Creator
    1. Go to Workflow > Schedules > New Workflow
    2. Set the Start date and time to tomorrow's date at 2:00am
    3. Run Daily
    4. Name the workflow Daily Delete Record
    5. Click on Create Workflow
    6. Give it the following code:
      copyraw
      for each  r_ApplicableRecord in Inventory_Photo[Added_Time <= zoho.currenttime.subDay(1)]
      {
      	delete from Inventory_Photo[ID == r_ApplicableRecord.ID];
      }
      1.  for each  r_ApplicableRecord in Inventory_Photo[Added_Time <= zoho.currenttime.subDay(1)] 
      2.  { 
      3.      delete from Inventory_Photo[ID == r_ApplicableRecord.ID]
      4.  } 
  6. Create a CRM connection to Zoho Creator
    1. Login to ZohoCRM
    2. Go to Setup > Developer Space > Connections
    3. Create Connection
    4. Give it the scope(s): ZohoCreator.form.CREATE and ZohoCreator.report.READ (more if this doesn't work properly)
    5. Give it a name, I'm calling mine joel_creator
    6. Authorize it, etc.
  7. Create a REST API CRM Function
    1. Login to ZohoCRM
    2. Go to Setup > Functions > New Function
    3. Give it a name and such, I'm doing the following
      • Function Name: fn_eBay_UploadPhoto
      • Display Name: Fn - eBay - Upload Photo
      • Description: What it says on the tin
      • Category: Standalone
      • Click on Create
      • Give it the parameter/argument p_InventoryitemID as an Integer/Long/Number datatype
    4. As the first part of your code, enter the following as code (you'll need to speify your own App Owner and App Name):
      copyraw
      //
      // ************************************************** CREATOR **************************************************
      v_ItemId = ifnull(p_InventoryItemID,0);
      v_PublicUrl = "";
      //
      // send Zoho Creator the inventory ID
      v_AppOwner = "JoelGoHappy";
      v_AppName = "ZohoInventory_eBayPictureServices";
      v_FormName = "Inventory_Photo";
      v_ReportName = "Inventory_Photo_Report";
      m_Blank = Map();
      m_CreatorParams = Map();
      m_CreatorParams.put("Zoho_Inventory_ID",v_ItemId);
      r_Creator = zoho.creator.createRecord(v_AppOwner,v_AppName,v_FormName,m_CreatorParams,m_Blank,"joel_creator");
      if(!isnull(r_Creator.get("data")))
      {
      	if(!isnull(r_Creator.get("data").get("ID")))
      	{
      		r_CreatorDetails = zoho.creator.getRecordById(v_AppOwner,v_AppName,v_ReportName,r_Creator.get("data").get("ID").toLong(),"joel_creator");
      		if(!isnull(r_CreatorDetails.get("data")))
      		{
      			if(!isnull(r_CreatorDetails.get("data").get("Public_URL").get("url")))
      			{
      				v_PublicUrl = r_CreatorDetails.get("data").get("Public_URL").get("url");
      			}
      		}
      	}
      }
      info r_Creator;
      1.  // 
      2.  // ************************************************** CREATOR ************************************************** 
      3.  v_ItemId = ifnull(p_InventoryItemID,0)
      4.  v_PublicUrl = ""
      5.  // 
      6.  // send Zoho Creator the inventory ID 
      7.  v_AppOwner = "JoelGoHappy"
      8.  v_AppName = "ZohoInventory_eBayPictureServices"
      9.  v_FormName = "Inventory_Photo"
      10.  v_ReportName = "Inventory_Photo_Report"
      11.  m_Blank = Map()
      12.  m_CreatorParams = Map()
      13.  m_CreatorParams.put("Zoho_Inventory_ID",v_ItemId)
      14.  r_Creator = zoho.creator.createRecord(v_AppOwner,v_AppName,v_FormName,m_CreatorParams,m_Blank,"joel_creator")
      15.  if(!isnull(r_Creator.get("data"))) 
      16.  { 
      17.      if(!isnull(r_Creator.get("data").get("ID"))) 
      18.      { 
      19.          r_CreatorDetails = zoho.creator.getRecordById(v_AppOwner,v_AppName,v_ReportName,r_Creator.get("data").get("ID").toLong(),"joel_creator")
      20.          if(!isnull(r_CreatorDetails.get("data"))) 
      21.          { 
      22.              if(!isnull(r_CreatorDetails.get("data").get("Public_URL").get("url"))) 
      23.              { 
      24.                  v_PublicUrl = r_CreatorDetails.get("data").get("Public_URL").get("url")
      25.              } 
      26.          } 
      27.      } 
      28.  } 
      29.  info r_Creator; 
    5. And here's the 2nd part of the function in CRM
      copyraw
      //
      // ************************************************** EBAY **************************************************
      // init
      v_EbayImageURL = "";
      //
      // eval
      v_Endpoint = "https://api.ebay.com/ws/api.dll";
      v_ApiCall = "UploadSiteHostedPictures";
      //
      // a CRM record that holds my API connection details
      v_IntegrationRecordID = 123456789012345678;
      r_CrmRecord = zoho.crm.getRecordById("Integrations",v_IntegrationRecordID);
      v_TradingAPIVersion = r_CrmRecord.get("Compatibility_Version");
      //
      // Another REST API CRM function which generates an eBay Access Token for me
      r_Response = getUrl("https://www.zohoapis.eu/crm/v2/functions/fn_ebay_getaccesstoken/actions/execute?auth_type=apikey&zapikey=1003.<REST_OF_YOUR_ZAPIKEY>");
      if(!isnull(r_Response.toMap().get("details")))
      {
      	if(!isnull(r_Response.toMap().get("details").get("output")))
      	{
      		v_AccessToken = r_Response.toMap().get("details").get("output");
      	}
      }
      info v_AccessToken;
      //
      // build header
      m_Headers = Map();
      m_Headers.put("X-EBAY-API-SITEID",3);
      m_Headers.put("X-EBAY-API-COMPATIBILITY-LEVEL",v_TradingAPIVersion);
      m_Headers.put("X-EBAY-API-CALL-NAME",v_ApiCall);
      m_Headers.put("X-EBAY-API-IAF-TOKEN",v_AccessToken);
      //
      // build params
      m_Params = Map();
      m_Params.put("WarningLevel","High");
      m_Params.put("ErrorLanguage","en_GB");
      m_Params.put("ExternalPictureURL",v_PublicUrl);
      m_Params.put("PictureName","OhBeautifulMe");
      m_Params.put("PictureSet","Supersize");
      //
      // convert to xml and replace root nodes
      x_Params = m_Params.toXML();
      x_Params = x_Params.toString().replaceFirst("<root>","<?xml version=\"1.0\" encoding=\"utf-8\"?><" + v_ApiCall + "Request xmlns=\"urn:ebay:apis:eBLBaseComponents\">");
      x_Params = x_Params.toString().replaceFirst("</root>","</" + v_ApiCall + "Request>");
      info x_Params;
      //
      // send the request XML as a string
      r_ResponseXML = invokeurl
      [
      	url :v_Endpoint
      	type :POST
      	parameters:x_Params
      	headers:m_Headers
      ];
      info r_ResponseXML;
      //
      // parse response and retrieve the ebay URL of the hosted image
      if(r_ResponseXML.containsIgnoreCase("<Ack>Success</Ack>") || r_ResponseXML.containsIgnoreCase("<Ack>Warning</Ack>"))
      {
      	v_BiggestHeight = 0;
      	x_RespondedXML = r_ResponseXML.replaceFirst("xmlns=\"urn:ebay:apis:eBLBaseComponents\"","",true);
      	x_PictureSets = x_RespondedXML.executeXPath("/"+v_ApiCall+"Response/SiteHostedPictureDetails/*").toXmlList();
      	for each  r_PictureSet in x_PictureSets
      	{
      		if(r_PictureSet.contains("PictureSetMember"))
      		{
      			// get image that is the biggest/tallest
      			v_ThisHeight = r_PictureSet.executeXPath("/PictureSetMember/PictureHeight/text()").toLong();
      			if(v_ThisHeight > v_BiggestHeight)
      			{
      				v_BiggestHeight = v_ThisHeight;
      				v_EbayImageURL = r_PictureSet.executeXPath("/PictureSetMember/MemberURL/text()");
      			}
      		}
      	}
      }
      return v_EbayImageURL;
      1.  // 
      2.  // ************************************************** EBAY ************************************************** 
      3.  // init 
      4.  v_EbayImageURL = ""
      5.  // 
      6.  // eval 
      7.  v_Endpoint = "https://api.ebay.com/ws/api.dll"
      8.  v_ApiCall = "UploadSiteHostedPictures"
      9.  // 
      10.  // a CRM record that holds my API connection details 
      11.  v_IntegrationRecordID = 123456789012345678
      12.  r_CrmRecord = zoho.crm.getRecordById("Integrations",v_IntegrationRecordID)
      13.  v_TradingAPIVersion = r_CrmRecord.get("Compatibility_Version")
      14.  // 
      15.  // Another REST API CRM function which generates an eBay Access Token for me 
      16.  r_Response = getUrl("https://www.zohoapis.eu/crm/v2/functions/fn_ebay_getaccesstoken/actions/execute?auth_type=apikey&zapikey=1003.<REST_OF_YOUR_ZAPIKEY>")
      17.  if(!isnull(r_Response.toMap().get("details"))) 
      18.  { 
      19.      if(!isnull(r_Response.toMap().get("details").get("output"))) 
      20.      { 
      21.          v_AccessToken = r_Response.toMap().get("details").get("output")
      22.      } 
      23.  } 
      24.  info v_AccessToken; 
      25.  // 
      26.  // build header 
      27.  m_Headers = Map()
      28.  m_Headers.put("X-EBAY-API-SITEID",3)
      29.  m_Headers.put("X-EBAY-API-COMPATIBILITY-LEVEL",v_TradingAPIVersion)
      30.  m_Headers.put("X-EBAY-API-CALL-NAME",v_ApiCall)
      31.  m_Headers.put("X-EBAY-API-IAF-TOKEN",v_AccessToken)
      32.  // 
      33.  // build params 
      34.  m_Params = Map()
      35.  m_Params.put("WarningLevel","High")
      36.  m_Params.put("ErrorLanguage","en_GB")
      37.  m_Params.put("ExternalPictureURL",v_PublicUrl)
      38.  m_Params.put("PictureName","OhBeautifulMe")
      39.  m_Params.put("PictureSet","Supersize")
      40.  // 
      41.  // convert to xml and replace root nodes 
      42.  x_Params = m_Params.toXML()
      43.  x_Params = x_Params.toString().replaceFirst("<root>","<?xml version=\"1.0\" encoding=\"utf-8\"?><" + v_ApiCall + "Request xmlns=\"urn:ebay:apis:eBLBaseComponents\">")
      44.  x_Params = x_Params.toString().replaceFirst("</root>","</" + v_ApiCall + "Request>")
      45.  info x_Params; 
      46.  // 
      47.  // send the request XML as a string 
      48.  r_ResponseXML = invokeUrl 
      49.  [ 
      50.      url :v_Endpoint 
      51.      type :POST 
      52.      parameters:x_Params 
      53.      headers:m_Headers 
      54.  ]
      55.  info r_ResponseXML; 
      56.  // 
      57.  // parse response and retrieve the ebay URL of the hosted image 
      58.  if(r_ResponseXML.containsIgnoreCase("<Ack>Success</Ack>") || r_ResponseXML.containsIgnoreCase("<Ack>Warning</Ack>")) 
      59.  { 
      60.      v_BiggestHeight = 0
      61.      x_RespondedXML = r_ResponseXML.replaceFirst("xmlns=\"urn:ebay:apis:eBLBaseComponents\"","",true)
      62.      x_PictureSets = x_RespondedXML.executeXPath("/"+v_ApiCall+"Response/SiteHostedPictureDetails/*").toXmlList()
      63.      for each  r_PictureSet in x_PictureSets 
      64.      { 
      65.          if(r_PictureSet.contains("PictureSetMember")) 
      66.          { 
      67.              // get image that is the biggest/tallest 
      68.              v_ThisHeight = r_PictureSet.executeXPath("/PictureSetMember/PictureHeight/text()").toLong()
      69.              if(v_ThisHeight > v_BiggestHeight) 
      70.              { 
      71.                  v_BiggestHeight = v_ThisHeight; 
      72.                  v_EbayImageURL = r_PictureSet.executeXPath("/PictureSetMember/MemberURL/text()")
      73.              } 
      74.          } 
      75.      } 
      76.  } 
      77.  return v_EbayImageURL; 
    6. Save the CRM function
    7. Hover your mouse cursor over it and click on the ellipsis/3 dots
    8. Select the REST API
    9. Enable REST API
    10. Note the REST API function and you can now call it from wherever.
  8. Done

Additional Caveat(s)
  • This currently does one image from Zoho Inventory. I'll need to add to the code to retrieve the other photos from Zoho Inventory
  • Still to run a few tests but if I'm right in my guesses, if you edit the listing in eBay's interface, it may get confused as to where the photo came from and will error when displaying it. I think it will be fine.

Source(s)
Category: Zoho :: Article: 811

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: The information on this website is provided without warranty and any content is merely the opinion of the author. Please try to test in development environments prior to adapting them to your production environments. The articles are written in good faith and, at the time of print, are working examples used 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

Related Articles

Joes Revolver Map

Accreditation

Badge - Certified Zoho Creator Associate
Badge - Certified Zoho Creator Associate

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 - Valid till 8 May 2022 bc1qjtp4l4ra452wzvuk9a45yfj82zkahsyy2z379y
© 2022 Joel Lipman .com. All Rights Reserved.