A super quick article on how to download an attachment from CRM and upload to a "file upload" field in Creator.
Why?
My use case here is that we are creating a Creator app that will show the attachments on a CRM contact record (and be clickable). The complication here is that the user can use the Creator form to add a new attachment that will eventually be uploaded back to CRM.
How?
At time of print, things have moved on and based on the Zoho official forums, this used to be a daunting task. Not anymore at least from what I've seen.
You could try adding a subform to Creator but have it as a blank form rather than a bidirectional form as in this example, the parent record doesn't yet exist... The cheat here is that we actually want the user to be able to click and download/open the attachments on the CRM record from within the Creator app. As for the add a new attachment, well that's just functionality.
Here's my Creator form (well the part for the attachments):
- An attachments section to hold a notes field.
- A notes field called "Note_Attachments" (this will display a HTML table with click-to-download files)
- A blank subform callled "Attachments" (links to a form called "Documents")
Here's the Creator form to hold the Attachments:
- Parent Record (a single line that will be a unique ref to prevent downloading the same record again and again)
- Document Name (a single line that will be the name of the file including its extension)
- Document (the file upload field)
- Downloaded (a picklist with the options "Downloaded" and "Pending Upload")
Here's the code for the parent form to download the attachments and upload them to the Creator "Document" form:
// // get attachments v_NoteAttachments = "<table class='jl-table-info jl-table-attachments'>"; m_SortCriteria = Map(); m_SortCriteria.put("sort_by","Created_Time"); m_SortCriteria.put("sort_order","desc"); l_Attachments = zoho.crm.getRelatedRecords("Attachments","Contacts",v_CrmContactID,1,200,m_SortCriteria); for each r_Attachment in l_Attachments { if(r_Attachment.get("id") != null) { v_AttachmentEndpoint = "https://www.zohoapis.com/crm/v2/Contacts/" + v_CrmContactID + "/Attachments/" + r_Attachment.get("id"); r_Download = invokeurl [ url :v_AttachmentEndpoint type :GET connection:"ab_crm" ]; r_Download.setParamName("file"); r_CheckIfDocumentAlreadyExists = Document[Parent_Record == v_CrmContactID.toString() && Document_Name == r_Attachment.get("File_Name")]; if(r_CheckIfDocumentAlreadyExists.count() > 0) { r_NewDocumentRecord = Document[ID == r_CheckIfDocumentAlreadyExists.ID]; v_DateAdded = r_NewDocumentRecord.Added_Time.toString("E, d MMM yyyy HH:mm"); } else { r_Document = insert into Document [ Added_User=zoho.loginuser Document_Name=r_Attachment.get("File_Name") Document_File=r_Download Downloaded="Downloaded" Parent_Record=v_CrmContactID.toString() ]; r_NewDocumentRecord = Document[ID == r_Document]; v_DateAdded = r_NewDocumentRecord.Added_Time.toString("E, d MMM yyyy HH:mm"); } v_LinkToDownload = "<a href='https://creator.zoho.com/file" + zoho.appuri + "All_Documents/" + r_NewDocumentRecord.ID + "/Document_File/download?filepath=/" + r_NewDocumentRecord.Document_File + "'>" + r_NewDocumentRecord.Document_Name + "</a>"; v_NoteAttachments = v_NoteAttachments + "<tr><td>" + v_DateAdded + "</td><td style='font-weight:700;'>" + v_LinkToDownload + "</td></tr>"; } } v_NoteAttachments = v_NoteAttachments + "</table>"; input.Note_Attachments = v_NoteAttachments;
- //
- // get attachments
- v_NoteAttachments = "<table class='jl-table-info jl-table-attachments'>";
- m_SortCriteria = Map();
- m_SortCriteria.put("sort_by","Created_Time");
- m_SortCriteria.put("sort_order","desc");
- l_Attachments = zoho.crm.getRelatedRecords("Attachments","Contacts",v_CrmContactID,1,200,m_SortCriteria);
- for each r_Attachment in l_Attachments
- {
- if(r_Attachment.get("id") != null)
- {
- v_AttachmentEndpoint = "https://www.zohoapis.com/crm/v2/Contacts/" + v_CrmContactID + "/Attachments/" + r_Attachment.get("id");
- r_Download = invokeUrl
- [
- url :v_AttachmentEndpoint
- type :GET
- connection:"ab_crm"
- ];
- r_Download.setParamName("file");
- r_CheckIfDocumentAlreadyExists = Document[Parent_Record == v_CrmContactID.toString() && Document_Name == r_Attachment.get("File_Name")];
- if(r_CheckIfDocumentAlreadyExists.count() > 0)
- {
- r_NewDocumentRecord = Document[ID == r_CheckIfDocumentAlreadyExists.ID];
- v_DateAdded = r_NewDocumentRecord.Added_Time.toString("E, d MMM yyyy HH:mm");
- }
- else
- {
- r_Document = insert into Document
- [
- Added_User=zoho.loginuser
- Document_Name=r_Attachment.get("File_Name")
- Document_File=r_Download
- Downloaded="Downloaded"
- Parent_Record=v_CrmContactID.toString()
- ];
- r_NewDocumentRecord = Document[ID == r_Document];
- v_DateAdded = r_NewDocumentRecord.Added_Time.toString("E, d MMM yyyy HH:mm");
- }
- v_LinkToDownload = "<a href='https://creator.zoho.com/file" + zoho.appuri + "All_Documents/" + r_NewDocumentRecord.ID + "/Document_File/download?filepath=/" + r_NewDocumentRecord.Document_File + "'>" + r_NewDocumentRecord.Document_Name + "</a>";
- v_NoteAttachments = v_NoteAttachments + "<tr><td>" + v_DateAdded + "</td><td style='font-weight:700;'>" + v_LinkToDownload + "</td></tr>";
- }
- }
- v_NoteAttachments = v_NoteAttachments + "</table>";
- input.Note_Attachments = v_NoteAttachments;
Here's the code at the end of the parent form to hide the columns I don't need the user to see (I'll populate these by code):
hide Attachments.Parent_Record; hide Attachments.Document_Name; hide Attachments.Downloaded;
- hide Attachments.Parent_Record;
- hide Attachments.Document_Name;
- hide Attachments.Downloaded;
Here's the code I use when a "Addition of a row" workflow is triggered on that subform. This makes sure I don't download it again and create a new Creator record to hold the same file but in a new record, as well as include the marker to check when uploading the files back to CRM:
row.Parent_Record=input.CRM_Contact; row.Downloaded="Pending Upload";
- row.Parent_Record=input.CRM_Contact;
- row.Downloaded="Pending Upload";
Here's the code I use when uploading a file to the newly added row in that subform (on user input). This sets the document name for display later:
if(!isnull(row.Document_File)) { v_FileDetected = row.Document_File; if(v_FileDetected.contains("_")) { v_SubstringStart = v_FileDetected.indexOf("_"); v_FilenameDetected = v_FileDetected.subString(v_SubstringStart + 1); v_FilenameFormatted = v_FilenameDetected.replaceAll("_"," "); row.Document_Name=v_FilenameFormatted; } }
- if(!isnull(row.Document_File))
- {
- v_FileDetected = row.Document_File;
- if(v_FileDetected.contains("_"))
- {
- v_SubstringStart = v_FileDetected.indexOf("_");
- v_FilenameDetected = v_FileDetected.subString(v_SubstringStart + 1);
- v_FilenameFormatted = v_FilenameDetected.replaceAll("_"," ");
- row.Document_Name=v_FilenameFormatted;
- }
- }
And finally, with a bit of CSS, this is what the end product looks like: