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 Creator: Download uploaded file and attach to Sales Order in Zoho Books

What?
This is an article to document how to use Zoho Deluge to download a file that was uploaded into a Zoho Creator form and then to attach it to a Sales Order in Zoho Books.

Why?
Because it took me so long to find out how to do this even after reading the official documentation and going through the online discussion forums to build this solution. As of May 2020, this is how I do it.

How?
So the trick is, go over the official documentation, but don't take it as gospel. You only really need the syntax for attaching a document to a Sales Order in Books and the documentation leaves certain bits out. Just getting the syntax right and using the . setParamName is key.

Let's make it even more interesting: I'm going to use a subform with the file upload field type so our record can have more than 1 file attached to it.

the CRM.attachFile command
Okay as a first example, there is a problem here, you asked for Books and this next bit shows how to attach it to a CRM record. This works in the scenario where you have disabled the transaction modules in CRM (so Sales Orders, Invoices, Purchase Orders) and integrated your Zoho CRM with your Zoho Books. Why? Because then Zoho Books will create 3 custom modules in your CRM under the tab "Zoho Finance".

There is a lag/delay in the synchronization of Zoho Books to Zoho CRM and vice-versa. Annoyingly the attachment does not appear to be included in any sync... This method would be enough if the action was instantaneous and the attachments were also attached to the Sales Order in Books... but it isn't and doesn't. It only attaches the file(s) to the CRM record. See my "Additional" note further below in this article on how to sync the attachments to the CRM record, however for now, check the logic of this as this is known to work on the CRM record:
copyraw
// assuming I have a creator form called "myForm"
// that the ID of the record is the value of the variable "myRecordID"
// that a subform exists in the form called "Attachments"
// that a field exists in the subform called "myUpload"
// that the API name for the CRM Sales Orders module is "CustomModule5004"
// that v_CrmSoID is the matched ID of the Sales Order in CRM

r_CreatorForm = myForm[ID == myRecordID];
if(!isnull(r_CreatorForm.Attachments))
{
    for each  row in r_CreatorForm.Attachments
    {
        r_AttachFile = zoho.crm.attachFile("CustomModule5004",v_CrmSoID, row.myUpload);
    }
}

// r_AttachFile should yield SUCCESS and if you check your CRM record it will have these under "attachments"
  1.  // assuming I have a creator form called "myForm" 
  2.  // that the ID of the record is the value of the variable "myRecordID" 
  3.  // that a subform exists in the form called "Attachments" 
  4.  // that a field exists in the subform called "myUpload" 
  5.  // that the API name for the CRM Sales Orders module is "CustomModule5004" 
  6.  // that v_CrmSoID is the matched ID of the Sales Order in CRM 
  7.   
  8.  r_CreatorForm = myForm[ID == myRecordID]
  9.  if(!isnull(r_CreatorForm.Attachments)) 
  10.  { 
  11.      for each  row in r_CreatorForm.Attachments 
  12.      { 
  13.          r_AttachFile = zoho.crm.attachFile("CustomModule5004",v_CrmSoID, row.myUpload)
  14.      } 
  15.  } 
  16.   
  17.  // r_AttachFile should yield SUCCESS and if you check your CRM record it will have these under "attachments" 

Setup a Zoho Oauth Connection
So that we can use invokeUrl and attempt the REST via API (see what I did there?), we need a Zoho Oauth2 connection in Zoho Books.
  1. In Zoho Creator, go to Setup (3 horizontal lines in top left if in edit mode » pops up the side bar to go to Home or Setup)
  2. Under "Extensions", click on "Connections"
  3. Click on "Add Connection"
  4. Click on "Zoho Oauth"
  5. Give the connection an easy name (will be lowercased) but descriptive. For this test I will call it "myconnector".
  6. Ensure that as a minimum, the scope ZohoBooks.salesorders.CREATE is ticked.
  7. Click on "Authorize"
  8. Read the notice saying Creator would like access to bla bla bla and click on "Accept"

Get the file URL (ie. the PermaLink)
So if you've been googling how to download a file that was uploaded into a Creator form, you may have come across forums that are over 10 years old; URLs that were so custom that they didn't apply to you because of the app owner, name, view link name, etc. The fastest way I know how to get to it is to publish the report and right-click on the attachment to see what the link should be:
  1. Ensure a report exists with your form and that one of the fields/columns viewable is the file upload or attachment field
  2. In Zoho Creator, in the "Edit" mode, go to "Settings"
  3. Under "Users and Control" click on "Publish"
  4. On the same row as your report, click on "Get embed Code" (ensure this report is published)
  5. Get the Permalink and copy and paste this into a new browser window/tab
  6. Right-click on one of the file upload/attachments and copy the link
  7. Return to your code and customize the permalink so it looks the same but appends the file name
See the following as an example of what you should end up with:
copyraw
//
// attach to ZohoBooks (for EU portal)
v_CreatorDownloadBase = "https://creator.zoho.eu/file";
v_AppOwnerName = zoho.adminuser;
v_AppLinkName = zoho.appname;
v_ViewLinkName = "myForm_View";         // put your own report name
v_CreatorRecordID = v_CreatorQuoteID;   // this is the creator record id that has the attachment
v_SubFormName = "mySubform";            // put here the link name of your subform
v_FieldName = "myField";                // put here the link name of the field in that subform
v_PermalinkCode = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL";
v_FileInternalName = row.Upload_File;   // looping through rows here but it wants the internal name here eg. "1234567890123_temp.pdf"
//
// build the URL
l_BuildUrl = List:String();
l_BuildUrl.add(v_CreatorDownloadBase);
l_BuildUrl.add(v_AppOwnerName);
l_BuildUrl.add(v_AppLinkName);
l_BuildUrl.add(v_ViewLinkName);
l_BuildUrl.add(v_CreatorRecordID);
l_BuildUrl.add(v_SubFormName + "." + v_FieldName);
l_BuildUrl.add("download");
l_BuildUrl.add(v_PermalinkCode);
v_DownloadUrl = l_BuildUrl.toString("/");
v_FileDownloadUrl = v_DownloadUrl + "?filepath=/" + v_FileInternalName;
//
// yields something like
// https://creator.zoho.eu/file/myadmin/myapp/myForm_View/123456789012345678/mySubform.myField/download/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL?filepath=/1234567890123_temp.pdf
  1.  // 
  2.  // attach to ZohoBooks (for EU portal) 
  3.  v_CreatorDownloadBase = "https://creator.zoho.eu/file"
  4.  v_AppOwnerName = zoho.adminuser
  5.  v_AppLinkName = zoho.appname
  6.  v_ViewLinkName = "myForm_View";         // put your own report name 
  7.  v_CreatorRecordID = v_CreatorQuoteID;   // this is the creator record id that has the attachment 
  8.  v_SubFormName = "mySubform";            // put here the link name of your subform 
  9.  v_FieldName = "myField";                // put here the link name of the field in that subform 
  10.  v_PermalinkCode = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL"
  11.  v_FileInternalName = row.Upload_File;   // looping through rows here but it wants the internal name here eg. "1234567890123_temp.pdf" 
  12.  // 
  13.  // build the URL 
  14.  l_BuildUrl = List:String()
  15.  l_BuildUrl.add(v_CreatorDownloadBase)
  16.  l_BuildUrl.add(v_AppOwnerName)
  17.  l_BuildUrl.add(v_AppLinkName)
  18.  l_BuildUrl.add(v_ViewLinkName)
  19.  l_BuildUrl.add(v_CreatorRecordID)
  20.  l_BuildUrl.add(v_SubFormName + "." + v_FieldName)
  21.  l_BuildUrl.add("download")
  22.  l_BuildUrl.add(v_PermalinkCode)
  23.  v_DownloadUrl = l_BuildUrl.toString("/")
  24.  v_FileDownloadUrl = v_DownloadUrl + "?filepath=/" + v_FileInternalName; 
  25.  // 
  26.  // yields something like 
  27.  // https://creator.zoho.eu/file/myadmin/myapp/myForm_View/123456789012345678/mySubform.myField/download/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL?filepath=/1234567890123_temp.pdf 

The full code with attachment to CRM and Zoho Books
Note that for the example below, my Creator form is called "Quote", my subform is called "Attachments" and the file upload field within that is called "Upload_File". The respective report is called "Quote_View".
copyraw
string Internal.Attachment_Testing(int p_QuoteID, int p_SoID)
{
	v_CreatorQuoteID = ifnull(p_QuoteID,0);
	v_BooksSoID = ifnull(p_SoID,0);
	//
	// config
	v_BooksOrgID = "1234567890";  // put here your client's own Books Organization ID
	v_CrmSoModule = "CustomModule5004";   // ensure this is the corresponding module API name in CRM
	v_BooksAPIBase = "https://books.zoho.eu/api/v3";
	//
	// init
	v_OutputMessage = "";
	v_CrmSoRef = "";
	v_CrmSoID = 0;
	//
	// get books so name (sales order number)
	r_BooksSoDetails = zoho.books.getRecordsByID("Salesorders",v_BooksOrgID,v_BooksSoID.toString());
	for each  r_SoBooks in r_BooksSoDetails
	{
		if(!isnull(r_SoBooks.get("salesorder_number")))
		{
			v_CrmSoRef = r_SoBooks.get("salesorder_number").toString();
		}
	}
	//
	// retrieve crm id
	if(v_CrmSoRef != "")
	{
		r_SearchResults = zoho.crm.searchRecords(v_CrmSoModule,"(Name:equals:" + v_CrmSoRef + ")");
		for each  r_Result in r_SearchResults
		{
			v_CrmSoID = r_Result.get("id").toLong();
		}
	}
	//
	// loop through attachments and associate to crm record
	if(v_CrmSoID != 0)
	{
		r_CreatorQuoteDetails = Quote[ID == v_CreatorQuoteID];
		if(!isnull(r_CreatorQuoteDetails.Attachments))
		{
			// loop through subform
			for each  row in r_CreatorQuoteDetails.Attachments
			{
				//
				// attach to ZohoCRM
				r_AttachCRM = zoho.crm.attachFile(v_CrmSoModule,v_CrmSoID,row.Upload_File);
				 //
				// attach to ZohoBooks (for EU portal)
				v_CreatorDownloadBase = "https://creator.zoho.eu/file";
				v_AppOwnerName = zoho.adminuser;
				v_AppLinkName = zoho.appname;
				v_ViewLinkName = "Quote_View";         // put your own report name
				v_CreatorRecordID = v_CreatorQuoteID;   // this is the creator record id that has the attachment
				v_SubFormName = "Attachments";            // put here the link name of your subform
				v_FieldName = "Upload_Field";                // put here the link name of the field in that subform
				v_PermalinkCode = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL";
				v_FileInternalName = row.Upload_File;   // looping through rows here but it wants the internal name here eg. "1234567890123_temp.pdf"
				//
				// build the URL
				l_BuildUrl = List:String();
				l_BuildUrl.add(v_CreatorDownloadBase);
				l_BuildUrl.add(v_AppOwnerName);
				l_BuildUrl.add(v_AppLinkName);
				l_BuildUrl.add(v_ViewLinkName);
				l_BuildUrl.add(v_CreatorRecordID);
				l_BuildUrl.add(v_SubFormName + "." + v_FieldName);
				l_BuildUrl.add("download");
				l_BuildUrl.add(v_PermalinkCode);
				v_DownloadUrl = l_BuildUrl.toString("/");
				v_FileDownloadUrl = v_DownloadUrl + "?filepath=/" + v_FileInternalName;
				//
				// download the file into cache
				r_FileDownload = invokeurl
				[
				     url :v_FileDownloadUrl
				     type :GET
				];
				//
				// Important: use setParamName on the response
				r_FileDownload.setParamName("attachment");
				//
				// evaluate endpoint of Books API
				v_AttachUrl = v_BooksAPIBase + "/salesorders/" + v_BooksSoID + "/attachment?organization_id=" + v_BooksOrgID;
				//
				// send the file
				r_AttachBooks = invokeurl
				[
				     url :v_AttachUrl
				     type :POST
				     files:r_FileDownload
				     connection:"myconnector"
				];
				v_OutputMessage = v_OutputMessage + "File: " + v_FileInternalName.toString() + " CRM Response: " + r_AttachCRM.toString() + " Books Response: " + r_AttachBooks.toString();
			}
		}
	}
	return v_OutputMessage;
}

// output message should be each file name and response to attaching the file to the CRM and Books record
  1.  string Internal.Attachment_Testing(int p_QuoteID, int p_SoID) 
  2.  { 
  3.      v_CreatorQuoteID = ifnull(p_QuoteID,0)
  4.      v_BooksSoID = ifnull(p_SoID,0)
  5.      // 
  6.      // config 
  7.      v_BooksOrgID = "1234567890";  // put here your client's own Books Organization ID 
  8.      v_CrmSoModule = "CustomModule5004";   // ensure this is the corresponding module API name in CRM 
  9.      v_BooksAPIBase = "https://books.zoho.eu/api/v3"
  10.      // 
  11.      // init 
  12.      v_OutputMessage = ""
  13.      v_CrmSoRef = ""
  14.      v_CrmSoID = 0
  15.      // 
  16.      // get books so name (sales order number) 
  17.      r_BooksSoDetails = zoho.books.getRecordsByID("Salesorders",v_BooksOrgID,v_BooksSoID.toString())
  18.      for each  r_SoBooks in r_BooksSoDetails 
  19.      { 
  20.          if(!isnull(r_SoBooks.get("salesorder_number"))) 
  21.          { 
  22.              v_CrmSoRef = r_SoBooks.get("salesorder_number").toString()
  23.          } 
  24.      } 
  25.      // 
  26.      // retrieve crm id 
  27.      if(v_CrmSoRef != "") 
  28.      { 
  29.          r_SearchResults = zoho.crm.searchRecords(v_CrmSoModule,"(Name:equals:" + v_CrmSoRef + ")")
  30.          for each  r_Result in r_SearchResults 
  31.          { 
  32.              v_CrmSoID = r_Result.get("id").toLong()
  33.          } 
  34.      } 
  35.      // 
  36.      // loop through attachments and associate to crm record 
  37.      if(v_CrmSoID != 0) 
  38.      { 
  39.          r_CreatorQuoteDetails = Quote[ID == v_CreatorQuoteID]
  40.          if(!isnull(r_CreatorQuoteDetails.Attachments)) 
  41.          { 
  42.              // loop through subform 
  43.              for each  row in r_CreatorQuoteDetails.Attachments 
  44.              { 
  45.                  // 
  46.                  // attach to ZohoCRM 
  47.                  r_AttachCRM = zoho.crm.attachFile(v_CrmSoModule,v_CrmSoID,row.Upload_File)
  48.                   // 
  49.                  // attach to ZohoBooks (for EU portal) 
  50.                  v_CreatorDownloadBase = "https://creator.zoho.eu/file"
  51.                  v_AppOwnerName = zoho.adminuser
  52.                  v_AppLinkName = zoho.appname
  53.                  v_ViewLinkName = "Quote_View";         // put your own report name 
  54.                  v_CreatorRecordID = v_CreatorQuoteID;   // this is the creator record id that has the attachment 
  55.                  v_SubFormName = "Attachments";            // put here the link name of your subform 
  56.                  v_FieldName = "Upload_Field";                // put here the link name of the field in that subform 
  57.                  v_PermalinkCode = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL"
  58.                  v_FileInternalName = row.Upload_File;   // looping through rows here but it wants the internal name here eg. "1234567890123_temp.pdf" 
  59.                  // 
  60.                  // build the URL 
  61.                  l_BuildUrl = List:String()
  62.                  l_BuildUrl.add(v_CreatorDownloadBase)
  63.                  l_BuildUrl.add(v_AppOwnerName)
  64.                  l_BuildUrl.add(v_AppLinkName)
  65.                  l_BuildUrl.add(v_ViewLinkName)
  66.                  l_BuildUrl.add(v_CreatorRecordID)
  67.                  l_BuildUrl.add(v_SubFormName + "." + v_FieldName)
  68.                  l_BuildUrl.add("download")
  69.                  l_BuildUrl.add(v_PermalinkCode)
  70.                  v_DownloadUrl = l_BuildUrl.toString("/")
  71.                  v_FileDownloadUrl = v_DownloadUrl + "?filepath=/" + v_FileInternalName; 
  72.                  // 
  73.                  // download the file into cache 
  74.                  r_FileDownload = invokeUrl 
  75.                  [ 
  76.                       url :v_FileDownloadUrl 
  77.                       type :GET 
  78.                  ]
  79.                  // 
  80.                  // Important: use setParamName on the response 
  81.                  r_FileDownload.setParamName("attachment")
  82.                  // 
  83.                  // evaluate endpoint of Books API 
  84.                  v_AttachUrl = v_BooksAPIBase + "/salesorders/" + v_BooksSoID + "/attachment?organization_id=" + v_BooksOrgID; 
  85.                  // 
  86.                  // send the file 
  87.                  r_AttachBooks = invokeUrl 
  88.                  [ 
  89.                       url :v_AttachUrl 
  90.                       type :POST 
  91.                       files:r_FileDownload 
  92.                       connection:"myconnector" 
  93.                  ]
  94.                  v_OutputMessage = v_OutputMessage + "file: " + v_FileInternalName.toString() + " CRM Response: " + r_AttachCRM.toString() + " Books Response: " + r_AttachBooks.toString()
  95.              } 
  96.          } 
  97.      } 
  98.      return v_OutputMessage; 
  99.  } 
  100.   
  101.  // output message should be each file name and response to attaching the file to the CRM and Books record 
A successful response to the Zoho Books attachment (per file) should be something similar to the following:
copyraw
{
  "code": 0,
  "message": "Your file has been attached to the sales order.",
  "documents": [
    {
      "document_id": "12345678901234567",
      "file_name": "dummy.pdf",
      "file_type": "pdf",
      "file_size": 13264,
      "file_size_formatted": "13 KB"
    }
  ]
}
  1.  { 
  2.    "code": 0, 
  3.    "message": "Your file has been attached to the sales order.", 
  4.    "documents": [ 
  5.      { 
  6.        "document_id": "12345678901234567", 
  7.        "file_name": "dummy.pdf", 
  8.        "file_type": "pdf", 
  9.        "file_size": 13264, 
  10.        "file_size_formatted": "13 KB" 
  11.      } 
  12.    ] 
  13.  } 

Additional: Attach to Zoho Books Sales Order and Attach to Zoho CRM Zoho Finance Sales Order
If you want the attachments that you've attached the Zoho Books Sales Order to sync over to the respective CRM Sales Order (now under Zoho Finance), you can't do this in one workflow because of the delay/lag in that searchRecords will not find the corresponding CRM sales order record. Instead, do the above workflow to attach the files to the Sales Order in Zoho Books, then in your Creator app, do the following for a delayed secondary workflow:
  1. Add a single-line field to your Creator record called Zoho Books Sales Order Ref
  2. Add a decision box field to your Creator record called Attached Files in CRM
  3. Setup a workflow that when SO Ref changes, the tick box is unticked
    1. Form Workflows > New Workflow
    2. Select your form
    3. Run when Created or Edited
    4. When to trigger: User input of a field
    5. Choose Field: ZohoBooks Sales Order Ref
    6. Name it whatever: eg. OnUserInput_ZBSoRef
    7. Put the following Deluge code:
      copyraw
      input.Attached_Files_in_CRM = false;
      1.  input.Attached_Files_in_CRM = false
  4. Setup a scheduled task in Creator
    1. Schedules > New Workflow
    2. Choose a Date Field
    3. Start Date: select form and choose Modified_Time
    4. Run this process on condition "Attached Files in CRM equals false"
    5. Execute Workflow "After 2 Minutes"
    6. Repeat Interval = Once
    7. Name the workflow > Create Workflow
    8. Give it the code
      copyraw
      //
      // **********************************************************
      // attach to CRM record
      // Note: Because record cannot be retrieved with search in 1 workflow, run this 2 minutes after Modified_Time
      // retrieve crm id
      v_CrmSoID=0;
      v_RecordID = ifnull(input.ID,0);
      if(v_RecordID != 0)
      {
      	r_Record = Quote[ID == v_RecordID];
      	v_SoRef = ifnull(r_Record.ZohoBooks_Sales_Order_Ref,"");
      	if(!isnull(r_Record.Attachments))
      	{
      		for each  r_Attachment in r_Record.Attachments
      		{
      			// retrieve crm id
      			if(v_SoRef != "")
      			{
      				r_SearchResults = zoho.crm.searchRecords("CustomModule5004","(Name:equals:" + v_SoRef + ")",1,1);
      				for each  r_Result in r_SearchResults
      				{
      					v_CrmSoID = r_Result.get("id").toLong();
      				}
      			}
      			// attach to CRM sales order
      			if(v_CrmSoID != 0)
      			{
      				r_AttachCRM = zoho.crm.attachFile("CustomModule5004",v_CrmSoID,r_Attachment.Upload_File);
      			}
      		}
      	}
      	r_Record.Attached_Files_in_CRM=true;
      }
      1.  // 
      2.  // ********************************************************** 
      3.  // attach to CRM record 
      4.  // Note: Because record cannot be retrieved with search in 1 workflow, run this 2 minutes after Modified_Time 
      5.  // retrieve crm id 
      6.  v_CrmSoID=0
      7.  v_RecordID = ifnull(input.ID,0)
      8.  if(v_RecordID != 0) 
      9.  { 
      10.      r_Record = Quote[ID == v_RecordID]
      11.      v_SoRef = ifnull(r_Record.ZohoBooks_Sales_Order_Ref,"")
      12.      if(!isnull(r_Record.Attachments)) 
      13.      { 
      14.          for each  r_Attachment in r_Record.Attachments 
      15.          { 
      16.              // retrieve crm id 
      17.              if(v_SoRef != "") 
      18.              { 
      19.                  r_SearchResults = zoho.crm.searchRecords("CustomModule5004","(Name:equals:" + v_SoRef + ")",1,1)
      20.                  for each  r_Result in r_SearchResults 
      21.                  { 
      22.                      v_CrmSoID = r_Result.get("id").toLong()
      23.                  } 
      24.              } 
      25.              // attach to CRM sales order 
      26.              if(v_CrmSoID != 0) 
      27.              { 
      28.                  r_AttachCRM = zoho.crm.attachFile("CustomModule5004",v_CrmSoID,r_Attachment.Upload_File)
      29.              } 
      30.          } 
      31.      } 
      32.      r_Record.Attached_Files_in_CRM=true
      33.  } 

Other timewasting activities but good code
Putting this code here on this article as it was the path I initially went down.

Send mail attachment: creator file upload field value as attached file
You might have tested using the trusty sendmail function and then got an error when adding the "attachments" parameter. As a reminder, content-type in the sendmail is not supported in Zoho Creator and nor is List when submitted as a file attachment.
copyraw
sendmail
[
	from: zoho.adminuserid
	to: "This email address is being protected from spambots. You need JavaScript enabled to view it."
	subject: "Testing an attachment"
	message: "This is a test"
	attachments: file: input.my_upload
];
// success!
  1.  sendmail 
  2.  [ 
  3.      from: zoho.adminuserid 
  4.      to: "This email address is being protected from spambots. You need JavaScript enabled to view it." 
  5.      subject: "Testing an attachment" 
  6.      message: "This is a test" 
  7.      attachments: file: input.my_upload 
  8.  ]
  9.  // success! 

Send mail attachment: creator report view as pdf
copyraw
sendmail
[
	from: zoho.adminuserid
	to: "This email address is being protected from spambots. You need JavaScript enabled to view it."
	subject: "Testing an attachment"
	message: "This is a test"
	attachments: view: myFormReport[ID == myRecordID] as PDF
];
// pretty cool: sends a report from Creator as a PDF attached to an email.
  1.  sendmail 
  2.  [ 
  3.      from: zoho.adminuserid 
  4.      to: "This email address is being protected from spambots. You need JavaScript enabled to view it." 
  5.      subject: "Testing an attachment" 
  6.      message: "This is a test" 
  7.      attachments: view: myFormReport[ID == myRecordID] as PDF 
  8.  ]
  9.  // pretty cool: sends a report from Creator as a PDF attached to an email. 

When attaching a file in CRM unlike in Books, you need the following line of code if invoking and attaching (NB: only does 1 attachment):
copyraw
v_SoID = salesorder.get("salesorder_id");
v_SoRef = salesorder.get("salesorder_number");
v_BooksOrgID = organization.get("organization_id");
v_Url = "https://books.zoho.eu/api/v3/salesorders/" + v_SoID + "/attachment?organization_id=" + v_BooksOrgID;
l_Attachments = invokeurl
[
	url :v_Url
	type :GET
	connection:"abbooks"
];
r_SearchResults = zoho.crm.searchRecords("CustomModule5004","(Name:equals:" + v_SoRef + ")",1,1);
for each  r_Result in r_SearchResults
{
	v_CrmSoID = r_Result.get("id");
	l_Attachments.setParamName("file");
	// v1
	// l_Attachments.setParamName("attachment");
	// r_AttachCRM = zoho.crm.attachFile("CustomModule5004",v_CrmSoID,l_Attachments);
	// v2
	v_AttachUrl = "https://www.zohoapis.eu/crm/v2/CustomModule5004/" + v_CrmSoID + "/Attachments";
	r_AttachCRM = invokeurl
	[
		url :v_AttachUrl
		type :POST
		files:l_Attachments
		connection:"mycrmconnector"
	];
	info r_AttachCRM;
}
  1.  v_SoID = salesorder.get("salesorder_id")
  2.  v_SoRef = salesorder.get("salesorder_number")
  3.  v_BooksOrgID = organization.get("organization_id")
  4.  v_Url = "https://books.zoho.eu/api/v3/salesorders/" + v_SoID + "/attachment?organization_id=" + v_BooksOrgID; 
  5.  l_Attachments = invokeUrl 
  6.  [ 
  7.      url :v_Url 
  8.      type :GET 
  9.      connection:"abbooks" 
  10.  ]
  11.  r_SearchResults = zoho.crm.searchRecords("CustomModule5004","(Name:equals:" + v_SoRef + ")",1,1)
  12.  for each  r_Result in r_SearchResults 
  13.  { 
  14.      v_CrmSoID = r_Result.get("id")
  15.      l_Attachments.setParamName("file")
  16.      // v1 
  17.      // l_Attachments.setParamName("attachment")
  18.      // r_AttachCRM = zoho.crm.attachFile("CustomModule5004",v_CrmSoID,l_Attachments)
  19.      // v2 
  20.      v_AttachUrl = "https://www.zohoapis.eu/crm/v2/CustomModule5004/" + v_CrmSoID + "/Attachments"
  21.      r_AttachCRM = invokeUrl 
  22.      [ 
  23.          url :v_AttachUrl 
  24.          type :POST 
  25.          files:l_Attachments 
  26.          connection:"mycrmconnector" 
  27.      ]
  28.      info r_AttachCRM; 
  29.  } 

Encountered Error(s):
  • {"code":57,"message":"You are not authorized to perform this operation"}: If you connect via a Zoho OAuth connection, check that the portal (Top Level Domain: TLD) is correct [EU for Europe // COM for USA] so books.zoho.eu for Europe and books.zoho.com for US.
  • {"code":2,"message":"The request passed is not valid."}: The parameters you are sending are not in the right format.
  • {"code":15,"message":"Please ensure that the attachment has less than 100 characters."}: The attachment parameter is populated with the file content instead of the internal file name.
  • {"code":2,"message":"Invalid value passed for doc"}: The doc parameter contains special characters.
  • Improper Statement   Error might be due to missing ';' at end of the line or incomplete expression This could be a missing semi-colon somewhere in your code OR that you have included either "content-type" in your invokeUrl (not supported by Creator: use headers instead) or submitted a List of files under "files" (also not supported via Creator).
  • Error due to - 'Error at line : -1, Error due to - 'Internal Exception'': Problem with your parameter format, ensure these are passed as a string (eg m_Params.toString()).
  • {"code":33003,"message":"Receipt not attached."}: So close, are you sending attachment as a parameter with a value in it? Don't. The full code example above will work.
  • Currently this only appears to accept Microsoft Word Documents and Adobe PDF files! Applicable to Zoho Books. If the user attaches text files (TXT) these will only attach to the Sales Order in the CRM record but not to the Sales Order in Zoho Books..

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

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

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 bc1qf6elrdxc968h0k673l2djc9wrpazhqtxw8qqp4

Ethereum:
Donate to Joel Lipman with Ethereum 0xb038962F3809b425D661EF5D22294Cf45E02FebF
© 2024 Joel Lipman .com. All Rights Reserved.