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  For larger projects, check our bespoke pricing structure and receive dedicated support from our hands-on project consultants and developers at

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  You can also visit our website at

Zoho Books/Inventory: Trigger a workflow when an invoice has been paid

An article on something that has taken me several days to get working: Get Zoho Books or Zoho Inventory that when an invoice is marked as paid, update 2 custom fields with the Payment Method, and the Payment Date.

This was requested by a customer and the problem happened in that the workflow would simply not trigger when the invoice was paid.  The customer had added 3 custom fields: Payment Method, Payment Date, and Last Four Digits.  They needed these because sometimes their customers needed to know what card they had paid with (if paid by card).  The customer wanted payment method and payment date to appear on the invoice.

This sounds rather straightforward, write a function that given an invoice, checks the customer payments and updates the custom fields.  Executing the function this worked as expected.  Now put it on a workflow where the settings were:

  • When an invoice is "Created or Edited"
  • Execute the workflow when "When any field is updated"
  • Filter the triggers when Status is "Paid"
  • Just once or every time is "Everytime"
  • Actions is a custom function called "fn_invoice_updatepaymentmethod"
But this wouldn't work despite being as per the documentation (see sources below).

Here's the code for our function; note it is within ZohoBooks and not ZohoInventory (moved) and the connection "zbooks" has a fullaccess scope:
// initialize
v_PaymentMode = "";
v_PaymentDate = null;
v_Last4Digits = "";
r_UpdateInvoice = Map();
// evaluate
v_InvoiceID = invoice.get("invoice_id");
v_CustomerID = invoice.get("customer_id");
v_BooksOrgID = organization.get("organization_id");
v_InvoiceRef = invoice.get("invoice_number");
v_BalanceDue = ifnull(invoice.get("balance"),0.0);
// search payments
m_SearchCriteria = Map();
// fetch all payments against this customer in order of payment reference (oldest to most recent)
r_SearchResults = zoho.books.getRecords("customerpayments",v_BooksOrgID,m_SearchCriteria,"zbooks");
	l_SearchResults = r_SearchResults.get("customerpayments");
	for each  r_Result in l_SearchResults
			// invalid searches will return 200 results non-matching, so let's double-check
				// retrieve payment mode, amount and date
				v_PaymentMode = ifnull(r_Result.get("payment_mode_formatted"),"");
				v_PaymentDate = r_Result.get("date");
				v_Last4Digits = ifnull(r_Result.get("last_four_digits"),"");
	// if fully paid and payment date is not null
	if(v_BalanceDue.toDecimal() == 0.0 && v_PaymentDate != null)
		m_UpdateInvoice = Map();
		// copy existing custom fields (not required but just in case)
		l_CustomFields = invoice.get("custom_fields");
		l_NewCustomFields = List();
		for each  m_CustomField in l_CustomFields
			// exclude existing custom field payment mode and date
			l_DontCopyFields = {"cf_payment_method","cf_payment_date","cf_last_4_digits"};
		// add custom field payment mode
		m_MiniCustomField = Map();
		// add custom field payment date
		m_MiniCustomField = Map();
		// add custom field last 4 digits
		if(v_Last4Digits != "")
			m_MiniCustomField = Map();
		r_UpdateInvoice = zoho.books.updateRecord("invoices",v_BooksOrgID,v_InvoiceID,m_UpdateInvoice,"zbooks");
		info r_UpdateInvoice;
// send ourselves an email as evidence that the workflow was triggered and ran this function
	from :zoho.adminuserid
	to :"This email address is being protected from spambots. You need JavaScript enabled to view it."
	subject :"Client Test: ZB: Payment Received for Invoice " + v_InvoiceRef
	message :"Workflow was executed and function ran:Balance: " + v_BalanceDue + "Customer Payments: " + r_SearchResults + "Invoice Update" + r_UpdateInvoice
  1.  // 
  2.  // initialize 
  3.  v_PaymentMode = ""
  4.  v_PaymentDate = null
  5.  v_Last4Digits = ""
  6.  r_UpdateInvoice = Map()
  7.  // 
  8.  // evaluate 
  9.  v_InvoiceID = invoice.get("invoice_id")
  10.  v_CustomerID = invoice.get("customer_id")
  11.  v_BooksOrgID = organization.get("organization_id")
  12.  v_InvoiceRef = invoice.get("invoice_number")
  13.  v_BalanceDue = ifnull(invoice.get("balance"),0.0)
  14.  // 
  15.  // search payments 
  16.  m_SearchCriteria = Map()
  17.  m_SearchCriteria.put("customer_id",v_CustomerID)
  18.  m_SearchCriteria.put("sort_order","A")
  19.  m_SearchCriteria.put("sort_by","payment_number")
  20.  // 
  21.  // fetch all payments against this customer in order of payment reference (oldest to most recent) 
  22.  r_SearchResults = zoho.books.getRecords("customerpayments",v_BooksOrgID,m_SearchCriteria,"zbooks")
  23.  if(!isnull(r_SearchResults.get("customerpayments"))) 
  24.  { 
  25.      l_SearchResults = r_SearchResults.get("customerpayments")
  26.      for each  r_Result in l_SearchResults 
  27.      { 
  28.          if(!isnull(r_Result.get("payment_id"))) 
  29.          { 
  30.              // 
  31.              // invalid searches will return 200 results non-matching, so let's double-check 
  32.              if(r_Result.get("invoice_numbers").containsIgnoreCase(v_InvoiceRef)) 
  33.              { 
  34.                  // retrieve payment mode, amount and date 
  35.                  v_PaymentMode = ifnull(r_Result.get("payment_mode_formatted"),"")
  36.                  v_PaymentDate = r_Result.get("date")
  37.                  v_Last4Digits = ifnull(r_Result.get("last_four_digits"),"")
  38.              } 
  39.          } 
  40.      } 
  41.      // 
  42.      // if fully paid and payment date is not null 
  43.      if(v_BalanceDue.toDecimal() == 0.0 && v_PaymentDate != null) 
  44.      { 
  45.          m_UpdateInvoice = Map()
  46.          // 
  47.          // copy existing custom fields (not required but just in case) 
  48.          l_CustomFields = invoice.get("custom_fields")
  49.          l_NewCustomFields = List()
  50.          for each  m_CustomField in l_CustomFields 
  51.          { 
  52.              // exclude existing custom field payment mode and date 
  53.              l_DontCopyFields = {"cf_payment_method","cf_payment_date","cf_last_4_digits"}
  54.              if(!l_DontCopyFields.contains(m_CustomField.get("api_name"))) 
  55.              { 
  56.                  l_NewCustomFields.add(m_CustomField)
  57.              } 
  58.          } 
  59.          // add custom field payment mode 
  60.          m_MiniCustomField = Map()
  61.          m_MiniCustomField.put("api_name","cf_payment_method")
  62.          m_MiniCustomField.put("value",v_PaymentMode)
  63.          l_NewCustomFields.add(m_MiniCustomField)
  64.          // 
  65.          // add custom field payment date 
  66.          m_MiniCustomField = Map()
  67.          m_MiniCustomField.put("api_name","cf_payment_date")
  68.          m_MiniCustomField.put("value",v_PaymentDate)
  69.          l_NewCustomFields.add(m_MiniCustomField)
  70.          // 
  71.          // add custom field last 4 digits 
  72.          if(v_Last4Digits != "") 
  73.          { 
  74.              m_MiniCustomField = Map()
  75.              m_MiniCustomField.put("api_name","cf_last_4_digits")
  76.              m_MiniCustomField.put("value",v_Last4Digits)
  77.              l_NewCustomFields.add(m_MiniCustomField)
  78.          } 
  79.          // 
  80.          m_UpdateInvoice.put("custom_fields",l_NewCustomFields)
  81.          r_UpdateInvoice = zoho.books.updateRecord("invoices",v_BooksOrgID,v_InvoiceID,m_UpdateInvoice,"zbooks")
  82.          info r_UpdateInvoice; 
  83.      } 
  84.  } 
  85.  // 
  86.  // send ourselves an email as evidence that the workflow was triggered and ran this function 
  87.  sendmail 
  88.  [ 
  89.      from :zoho.adminuserid 
  90.      to :"This email address is being protected from spambots. You need JavaScript enabled to view it." 
  91.      subject :"Client Test: ZB: Payment Received for Invoice " + v_InvoiceRef 
  92.      message :"Workflow was executed and function ran:Balance: " + v_BalanceDue + "Customer Payments: " + r_SearchResults + "Invoice Update" + r_UpdateInvoice 
  93.  ] 

The trick is all in the workflow settings. This worked for us but may not work for you or may be already working for you without this issue. By changing the workflow to execute not "When any field is upddated" but "When any selected field is updated" and then selecting the fields "Status", "Balance", and "Notes". (not sure which one fixed it but I now add all three):
Zoho Books - Trigger Workflow When An Invoice Is Paid - Filter Triggers

  • Workflow did not trigger and did not receive even the email included in my custom function.

Debug Method(s):
  • Go to Settings > Automations > Custom Functions > Select the function > Execute the function against a paid invoice - check the fields updated.
  • Go to Sales > Invoices > Select an invoice > Click on "Comments & History"
  • Go into Settings > Automations > Workflow Logs > Custom Functions: check if/when they occurred and in relation to what invoice.

Category: Zoho :: Article: 819

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

Related Articles

Joes Revolver Map


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:

Donate to Joel Lipman via PayPal

Donate to Joel Lipman with Bitcoin bc1qf6elrdxc968h0k673l2djc9wrpazhqtxw8qqp4

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