This is a quick bit of code for my reference so that I don't have to keep finding a function that gives me the syntax of how to either create or update a record using invokeUrl.
Why?
Because my designers keep including custom fields in their transactional modules line items (quotes, sales orders, invoices, purchase orders). The only way to update these are by using the API and deluge function invokeURL rather than the usual shortcode of zoho.crm.updateRecord() or zoho.crm.createRecord().
How?
This is somewhere in the official documentation but here's the bits that I need:
Updating a record with PUT (note that id needs passed for each row otherwise you will end up with duplicate line items):
/* ******************************************************************************* Function: void automation.fn_Quotes_OnEdit(int p_QuoteID) Label: Fn - Quotes - On Edit Trigger: Used in a workflow when a quote is modified Purpose: Actions when a quote is modified Inputs: int p_QuoteID - the ID of the CRM Quote Record Outputs: void Date Created: 2024-12-10 (Joel Lipman) - Initial release Date Modified: ??? - ??? More Information: Any information that may help ******************************************************************************* */ // // initialize m_UpdateQuote = Map(); // // get quote details v_Endpoint = "https://www.zohoapis.com/crm/v7/Quotes/" + p_QuoteID; r_QuoteDetails = invokeurl [ url :v_Endpoint type :GET connection:"zcrm" ]; l_RecordData = ifnull(r_QuoteDetails.get("data"),List()); for each m_RecordData in l_RecordData { // // apply changes to any of the line items l_NewLineItems = List(); if(!isNull(m_RecordData.get("id"))) { for each m_LineItem in m_RecordData.get("Quoted_Items") { m_NewLineItem = Map(); m_NewLineItem.put("id",m_LineItem.get("id")); m_NewLineItem.put("Description",m_LineItem.get("Description")); m_NewLineItem.put("Discount",m_LineItem.get("Discount")); m_NewLineItem.put("CUSTOM_FIELD_1",m_LineItem.get("CUSTOM_FIELD_1")); m_NewLineItem.put("CUSTOM_FIELD_2",m_LineItem.get("CUSTOM_FIELD_2")); m_NewLineItem.put("Product_Name",m_LineItem.get("Product_Name")); m_NewLineItem.put("Quantity",m_LineItem.get("Quantity")); m_NewLineItem.put("List_Price",m_LineItem.get("List_Price")); l_NewLineItems.add(m_NewLineItem); } } } // // if there is reason to update it then let's update CRM API v7 style if(l_NewLineItems.size() > 0) { // // build request to send to CRM v7 // m_RecordData should have value from last iteration m_UpdateQuote.put("Subject",m_RecordData.get("Subject")); m_UpdateQuote.put("Quoted_Items",l_NewLineItems); l_RecordsToSend = List(); l_RecordsToSend.add(m_UpdateQuote); m_Data = Map(); m_Data.put("data",l_RecordsToSend); // // this is REST API so by default all triggers run. Use an empty list to stop or prevent these from triggering. m_Data.put("trigger",List()); v_Endpoint = "https://www.zohoapis.com/crm/v7/Quotes/" + p_QuoteID; r_QuoteDetails = invokeurl [ url :v_Endpoint type :PUT parameters:m_Data.toString() connection:"zcrm" ]; info r_QuoteDetails; }
- /* *******************************************************************************
- Function: void automation.fn_Quotes_OnEdit(int p_QuoteID)
- Label: Fn - Quotes - On Edit
- Trigger: Used in a workflow when a quote is modified
- Purpose: Actions when a quote is modified
- Inputs: int p_QuoteID - the ID of the CRM Quote Record
- Outputs: void
- Date Created: 2024-12-10 (Joel Lipman)
- - Initial release
- Date Modified: ???
- - ???
- More Information:
- Any information that may help
- ******************************************************************************* */
- //
- // initialize
- m_UpdateQuote = Map();
- //
- // get quote details
- v_Endpoint = "https://www.zohoapis.com/crm/v7/Quotes/" + p_QuoteID;
- r_QuoteDetails = invokeUrl
- [
- url :v_Endpoint
- type :GET
- connection:"zcrm"
- ];
- l_RecordData = ifnull(r_QuoteDetails.get("data"),List());
- for each m_RecordData in l_RecordData
- {
- //
- // apply changes to any of the line items
- l_NewLineItems = List();
- if(!isNull(m_RecordData.get("id")))
- {
- for each m_LineItem in m_RecordData.get("Quoted_Items")
- {
- m_NewLineItem = Map();
- m_NewLineItem.put("id",m_LineItem.get("id"));
- m_NewLineItem.put("Description",m_LineItem.get("Description"));
- m_NewLineItem.put("Discount",m_LineItem.get("Discount"));
- m_NewLineItem.put("CUSTOM_FIELD_1",m_LineItem.get("CUSTOM_FIELD_1"));
- m_NewLineItem.put("CUSTOM_FIELD_2",m_LineItem.get("CUSTOM_FIELD_2"));
- m_NewLineItem.put("Product_Name",m_LineItem.get("Product_Name"));
- m_NewLineItem.put("Quantity",m_LineItem.get("Quantity"));
- m_NewLineItem.put("List_Price",m_LineItem.get("List_Price"));
- l_NewLineItems.add(m_NewLineItem);
- }
- }
- }
- //
- // if there is reason to update it then let's update CRM API v7 style
- if(l_NewLineItems.size() > 0)
- {
- //
- // build request to send to CRM v7
- // m_RecordData should have value from last iteration
- m_UpdateQuote.put("Subject",m_RecordData.get("Subject"));
- m_UpdateQuote.put("Quoted_Items",l_NewLineItems);
- l_RecordsToSend = List();
- l_RecordsToSend.add(m_UpdateQuote);
- m_Data = Map();
- m_Data.put("data",l_RecordsToSend);
- //
- // this is REST API so by default all triggers run. Use an empty list to stop or prevent these from triggering.
- m_Data.put("trigger",List());
- v_Endpoint = "https://www.zohoapis.com/crm/v7/Quotes/" + p_QuoteID;
- r_QuoteDetails = invokeUrl
- [
- url :v_Endpoint
- type :PUT
- parameters:m_Data.toString()
- connection:"zcrm"
- ];
- info r_QuoteDetails;
- }
Creating a record
Pretty much the same as above but instead of the method/type PUT you will use POST with an endpoint of https://www.zohoapis.com/crm/v7/Quotes.
Showing Off
Just a note here for myself where using invokeAPI over invokeURL:
// // retrieve record details r_SoDetails = invokeapi [ service :zohocrm path :"/crm/v7/Sales_Orders/" + p_SalesOrderID type :GET connection:"zcrm" ]; l_SoDetails = ifnull(r_SoDetails.get("data"),List()); m_SoDetails = if(l_SoDetails.size() > 0,l_SoDetails.get(0),Map()); info "Sales Order Record Details: "; info m_SoDetails;
- //
- // retrieve record details
- r_SoDetails = invokeapi
- [
- service :zohocrm
- path :"/crm/v7/Sales_Orders/" + p_SalesOrderID
- type :GET
- connection:"zcrm"
- ];
- l_SoDetails = ifnull(r_SoDetails.get("data"),List());
- m_SoDetails = if(l_SoDetails.size() > 0,l_SoDetails.get(0),Map());
- info "Sales Order Record Details: ";
- info m_SoDetails;
<variable> = invokeapi [ service: <service> path: <path> type: <request_type> parameters: <expression> connection: <connection_name> ];
- <variable> = invokeapi
- [
- service: <service>
- path: <path>
- type: <request_type>
- parameters: <expression>
- connection: <connection_name>
- ];
Additional Note(s):
The trigger input can be workflow, approval, blueprint, pathfinder, or orchestration(for the Journey Builder feature). If "trigger" is not mentioned, the automation actions related to the API will get executed. Enter the trigger value as [] to not execute the workflows. [1]
- To remove or delete a line item when using invokeURL and the PUT method, submit the line item again with its ID as well as a key called "_delete" as per the following snippet:
copyraw
// // initialize m_UpdateQuote = Map(); // // get quote details v_Endpoint = "https://www.zohoapis.com/crm/v7/Quotes/" + p_QuoteID; r_QuoteDetails = invokeurl [ url :v_Endpoint type :GET connection:"zcrm" ]; l_RecordData = ifnull(r_QuoteDetails.get("data"),List()); for each m_RecordData in l_RecordData { // // apply changes to any of the line items l_NewLineItems = List(); if(!isNull(m_RecordData.get("id"))) { for each m_LineItem in m_RecordData.get("Quoted_Items") { if(!isNull(m_LineItem.get("id")) && !m_LineItem.get("Optional")) { // add here line items that will be added or updated m_NewLineItem = Map(); m_NewLineItem.put("id",m_LineItem.get("id")); m_NewLineItem.put("Description",m_LineItem.get("Description")); m_NewLineItem.put("Discount",m_LineItem.get("Discount")); m_NewLineItem.put("CUSTOM_FIELD_1",m_LineItem.get("CUSTOM_FIELD_1")); m_NewLineItem.put("CUSTOM_FIELD_2",m_LineItem.get("CUSTOM_FIELD_2")); m_NewLineItem.put("Product_Name",m_LineItem.get("Product_Name")); m_NewLineItem.put("Quantity",m_LineItem.get("Quantity")); m_NewLineItem.put("List_Price",m_LineItem.get("List_Price")); l_NewLineItems.add(m_NewLineItem); } else { // add here line items to delete / remove / omit m_NewLineItem = Map(); m_NewLineItem.put("id",m_LineItem.get("id")); m_NewLineItem.put("_delete",null); l_NewLineItems.add(m_NewLineItem); } } } } // // if there is reason to update it then let's update CRM API v7 style if(l_NewLineItems.size() > 0) { // m_UpdateQuote.put("Subject",m_RecordData.get("Subject")); m_UpdateQuote.put("Quoted_Items",l_NewLineItems); l_RecordsToSend = List(); l_RecordsToSend.add(m_UpdateQuote); m_Data = Map(); m_Data.put("data",l_RecordsToSend); // // this is REST API so by default all triggers run. Use an list to specify any triggers on update. Use an empty list to stop any. m_Data.put("trigger",{"workflow","approval"}); v_Endpoint = "https://www.zohoapis.com/crm/v7/Quotes/" + p_QuoteID; r_QuoteDetails = invokeurl [ url :v_Endpoint type :PUT parameters:m_Data.toString() connection:"zcrm" ]; info r_QuoteDetails; }
- //
- // initialize
- m_UpdateQuote = Map();
- //
- // get quote details
- v_Endpoint = "https://www.zohoapis.com/crm/v7/Quotes/" + p_QuoteID;
- r_QuoteDetails = invokeUrl
- [
- url :v_Endpoint
- type :GET
- connection:"zcrm"
- ];
- l_RecordData = ifnull(r_QuoteDetails.get("data"),List());
- for each m_RecordData in l_RecordData
- {
- //
- // apply changes to any of the line items
- l_NewLineItems = List();
- if(!isNull(m_RecordData.get("id")))
- {
- for each m_LineItem in m_RecordData.get("Quoted_Items")
- {
- if(!isNull(m_LineItem.get("id")) && !m_LineItem.get("Optional"))
- {
- // add here line items that will be added or updated
- m_NewLineItem = Map();
- m_NewLineItem.put("id",m_LineItem.get("id"));
- m_NewLineItem.put("Description",m_LineItem.get("Description"));
- m_NewLineItem.put("Discount",m_LineItem.get("Discount"));
- m_NewLineItem.put("CUSTOM_FIELD_1",m_LineItem.get("CUSTOM_FIELD_1"));
- m_NewLineItem.put("CUSTOM_FIELD_2",m_LineItem.get("CUSTOM_FIELD_2"));
- m_NewLineItem.put("Product_Name",m_LineItem.get("Product_Name"));
- m_NewLineItem.put("Quantity",m_LineItem.get("Quantity"));
- m_NewLineItem.put("List_Price",m_LineItem.get("List_Price"));
- l_NewLineItems.add(m_NewLineItem);
- }
- else
- {
- // add here line items to delete / remove / omit
- m_NewLineItem = Map();
- m_NewLineItem.put("id",m_LineItem.get("id"));
- m_NewLineItem.put("_delete",null);
- l_NewLineItems.add(m_NewLineItem);
- }
- }
- }
- }
- //
- // if there is reason to update it then let's update CRM API v7 style
- if(l_NewLineItems.size() > 0)
- {
- //
- m_UpdateQuote.put("Subject",m_RecordData.get("Subject"));
- m_UpdateQuote.put("Quoted_Items",l_NewLineItems);
- l_RecordsToSend = List();
- l_RecordsToSend.add(m_UpdateQuote);
- m_Data = Map();
- m_Data.put("data",l_RecordsToSend);
- //
- // this is REST API so by default all triggers run. Use an list to specify any triggers on update. Use an empty list to stop any.
- m_Data.put("trigger",{"workflow","approval"});
- v_Endpoint = "https://www.zohoapis.com/crm/v7/Quotes/" + p_QuoteID;
- r_QuoteDetails = invokeUrl
- [
- url :v_Endpoint
- type :PUT
- parameters:m_Data.toString()
- connection:"zcrm"
- ];
- info r_QuoteDetails;
- }
Source(s):
- Zoho Deluge: invokeAPI Task
- Zoho CRM: Insert or Update Records (Upsert) - not used in above example but here if I want to use it one day.
- Zoho CRM: Update Subform Data again, not used above but probably worth looking as an alternative.
- [1] Zoho CRM: Insert Records
- Zoho CRM: Remove product from a quote or clear product details.