For Zoho services only


I'm currently part of a wider delivery team at Ascent Business Solutions, recognised as a leading Zoho Premium Solutions Partner in the United Kingdom.

Ascent Business Solutions support organisations with everything from targeted technical fixes through to full Zoho CRM implementations and long-term platform adoption. Working as a team rather than a one-person consultancy allows projects to move forward consistently, with access to the right skills at each stage.

The team I manage specialises in API integrations between Zoho and third-party finance and commerce platforms such as Xero, Shopify, WooCommerce, and eBay. Much of our work involves solving integration challenges that fall outside standard documentation, supporting new ideas, new sectors, and evolving business models.

Success is measured through practical outcomes and return on investment, ranging from scaling small operations into high-turnover businesses to delivering rapid gains through online payments, automation, and streamlined digital workflows.

If you are looking for structured Zoho expertise backed by an established consultancy, you can contact Ascent Business Solutions on 0121 392 8140 (UK), email info@ascentbusiness.co.uk, or visit https://www.ascentbusiness.co.uk.
Zoho CRM: Update Opportunity Type as New or Existing Business

Zoho CRM: Update Opportunity Type as New or Existing Business

What?
A quick article on how to get Zoho CRM to update the deal or opportunity type field with "New Business" or "Existing Business" depending on whether this won opportunity is the second time for this account.

Why?
Mainly for reporting purposes. We want ZohoCRM to report on what is repeat business and what is new business. As part of a process, you could get your staff to update the opportunity "Type" to "Existing Business" or "New Business" at the time they update the stage to "Closed Won". If you'd rather that staff don't have to remember to do this, then this snippet of code is for you.

How?
Note that here, the term "opportunity" is synonymous with the "deal". At time of print, the current workflow system won't let you do this without a bit of code. To be specific, we only want the opportunity type to be updated to "Existing Business" if the opportunity is the second "Won" opportunity against this account.

To do this, we will need a specific workflow that picks up the change in status at the sales order level and where the sales order has been completed (fulfilled or invoiced). This will then see if any other opportunities were won against this account and then update the opportunity related to this sales order.

the Workflow
  1. Login to Zoho CRM as an administrator
  2. Go to Setup > Automation > Workflow Rules > Create Rule
  3. Set "Record Type" to "Sales Orders" and "Rule Name" to something like "Sales Orders - On Complete"
  4. Execute this workflow based on "Record Action" > "Edit" > "Specific Field(s) Get Modified" > tick the "Repeat every time" > Criteria is "When Status is modified to the value ..." then list all complete statuses of a sales order.
  5. Condition 1 is that the status must match one of the complete statuses of the sales order.
  6. Under "Instant action", select "Function" > "Configure Function" > "Write your own"
  7. Give it a displayed name such as "Fn - Sales Orders - On Complete"
  8. Give it a function name, I'm calling mine: fn_SalesOrders_OnComplete
  9. Paste in the function below
  10. In the rightmost bar, you will see a pair of tabs one which has (x) and the tooltip "Arguments" click on this
    1. Type p_SoID in the first field
    2. In the second field type the hash/pound sign # and select "Sales Orders" then "Sales Order Id"

the Code
copyraw
void automation.fn_SalesOrders_OnComplete(Int p_SoID)
{
/* *******************************************************************************
	Function:       void automation.fn_SalesOrders_OnComplete(int p_SoID)
	Label:          Fn - Sales Orders - On Complete
	Trigger:        Workflow when a sales order is completed
	Purpose:		To update the opportunity type to existing or new business
	Inputs:         int p_SoID (the sales order zoho id)
	Outputs:        -

	Date Created:   2026-02-17 (Joel Lipman)
					- Initial release
					- Counts opps against this account and updates the field for this opp
	Date Modified:	???
					- ???

	More Information:
					Any information that may help

	******************************************************************************* */
	//
	// initialize
	v_ThisDealID = "0";
	v_ThisAccountID = "0";
	v_ThisContactID = "0";
	v_CountWonOpps = 0;
	//
	// add in what sales orders you consider "closed won"
	l_CompletedSOStatuses = List();
	l_CompletedSOStatuses.add("Partially Fulfilled");
	l_CompletedSOStatuses.add("Fulfilled");
	l_CompletedSOStatuses.add("Partially Invoiced");
	l_CompletedSOStatuses.add("Invoiced");
	//
	// get sales order record
	r_SoDetails = zoho.crm.getRecordById("Sales_Orders",p_SoID);
	//
	// check status of sales order is a completed one (can be done in workflow but this is a double-check)
	if(l_CompletedSOStatuses.containsIgnoreCase(r_SoDetails.get("Status")))
	{
		if(!isNull(r_SoDetails.get("Deal_Name")))
		{
			v_ThisDealID = r_SoDetails.get("Deal_Name").get("id");
		}
		if(!isNull(r_SoDetails.get("Account_Name")))
		{
			v_ThisAccountID = r_SoDetails.get("Account_Name").get("id");
		}
		if(!isNull(r_SoDetails.get("Contact_Name")))
		{
			v_ThisContactID = r_SoDetails.get("Contact_Name").get("id");
		}
		info v_ThisContactID;
		//
		// set ordered criteria
		m_SortCriteria = Map();
		m_SortCriteria.put("sort_by","id");
		m_SortCriteria.put("sort_order","desc");
		m_SortCriteria.put("fields","id,Status,Deal_Name");
		//
		// count opportunities against this account (B2B)
		r_SearchAccountDeals = zoho.crm.getRelatedRecords("Deals","Accounts",v_ThisAccountID,1,200,m_SortCriteria);
		l_SearchAccountDeals = ifnull(r_SearchAccountDeals,List());
		if(l_SearchAccountDeals.size() > 0)
		{
			for each  m_RelatedDeal in l_SearchAccountDeals
			{
				v_RelatedDealStage = ifnull(m_RelatedDeal.get("Stage"),"");
				v_RelatedDealId = ifnull(m_RelatedDeal.get("id"),"");
				//
				if(v_RelatedDealStage.containsIgnoreCase("Closed Won") && v_RelatedDealId != v_ThisDealID)
				{
					v_CountWonOpps = v_CountWonOpps + 1;
				}
			}
		}
		//
		// account field was blank?
		// count opportunities against this contact (B2C)
		if(v_CountWonOpps == 0 && v_ThisAccountID == "0")
		{
			r_SearchContactDeals = zoho.crm.getRelatedRecords("Deals","Contacts",v_ThisContactID,1,200,m_SortCriteria);
			l_SearchContactDeals = ifnull(r_SearchContactDeals,List());
			if(l_SearchContactDeals.size() > 0)
			{
				for each  m_RelatedDeal2 in l_SearchContactDeals
				{
					v_RelatedDealStage2 = ifnull(m_RelatedDeal2.get("Stage"),"");
					v_RelatedDealId2 = ifnull(m_RelatedDeal2.get("id"),"");
					//
					if(v_RelatedDealStage2.containsIgnoreCase("Closed Won") && v_RelatedDealId2 != v_ThisDealID)
					{
						v_CountWonOpps = v_CountWonOpps + 1;
					}
				}
			}
		}
		//
		// update the opp
		if(v_CountWonOpps > 0)
		{
			m_UpateOpp = Map();
			m_UpateOpp.put("Type","Existing Business");
			r_UpdateOpp = zoho.crm.updateRecord("Deals",v_ThisDealID,m_UpateOpp);
			info r_UpdateOpp;
		}
		else 
		{
			info "This is the first complete sales order so the opportunity type here is still 'New Business'.";
		}
	}
	else
	{
		info "This Sales Order is not considered complete so it will not update the opportunity.";
	}
}
  1.  void automation.fn_SalesOrders_OnComplete(Int p_SoID) 
  2.  { 
  3.  /* ******************************************************************************* 
  4.      Function:       void automation.fn_SalesOrders_OnComplete(int p_SoID) 
  5.      Label:          Fn - Sales Orders - On Complete 
  6.      Trigger:        Workflow when a sales order is completed 
  7.      Purpose:        To update the opportunity type to existing or new business 
  8.      Inputs:         int p_SoID (the sales order zoho id) 
  9.      Outputs:        - 
  10.   
  11.      Date Created:   2026-02-17 (Joel Lipman) 
  12.                      - Initial release 
  13.                      - Counts opps against this account and updates the field for this opp 
  14.      Date Modified:    ??? 
  15.                      - ??? 
  16.   
  17.      More Information: 
  18.                      Any information that may help 
  19.   
  20.      ******************************************************************************* */ 
  21.      // 
  22.      // initialize 
  23.      v_ThisDealID = "0"
  24.      v_ThisAccountID = "0"
  25.      v_ThisContactID = "0"
  26.      v_CountWonOpps = 0
  27.      // 
  28.      // add in what sales orders you consider "closed won" 
  29.      l_CompletedSOStatuses = List()
  30.      l_CompletedSOStatuses.add("Partially Fulfilled")
  31.      l_CompletedSOStatuses.add("Fulfilled")
  32.      l_CompletedSOStatuses.add("Partially Invoiced")
  33.      l_CompletedSOStatuses.add("Invoiced")
  34.      // 
  35.      // get sales order record 
  36.      r_SoDetails = zoho.crm.getRecordById("Sales_Orders",p_SoID)
  37.      // 
  38.      // check status of sales order is a completed one (can be done in workflow but this is a double-check) 
  39.      if(l_CompletedSOStatuses.containsIgnoreCase(r_SoDetails.get("Status"))) 
  40.      { 
  41.          if(!isNull(r_SoDetails.get("Deal_Name"))) 
  42.          { 
  43.              v_ThisDealID = r_SoDetails.get("Deal_Name").get("id")
  44.          } 
  45.          if(!isNull(r_SoDetails.get("Account_Name"))) 
  46.          { 
  47.              v_ThisAccountID = r_SoDetails.get("Account_Name").get("id")
  48.          } 
  49.          if(!isNull(r_SoDetails.get("Contact_Name"))) 
  50.          { 
  51.              v_ThisContactID = r_SoDetails.get("Contact_Name").get("id")
  52.          } 
  53.          info v_ThisContactID; 
  54.          // 
  55.          // set ordered criteria 
  56.          m_SortCriteria = Map()
  57.          m_SortCriteria.put("sort_by","id")
  58.          m_SortCriteria.put("sort_order","desc")
  59.          m_SortCriteria.put("fields","id,Status,Deal_Name")
  60.          // 
  61.          // count opportunities against this account (B2B) 
  62.          r_SearchAccountDeals = zoho.crm.getRelatedRecords("Deals","Accounts",v_ThisAccountID,1,200,m_SortCriteria)
  63.          l_SearchAccountDeals = ifnull(r_SearchAccountDeals,List())
  64.          if(l_SearchAccountDeals.size() > 0) 
  65.          { 
  66.              for each  m_RelatedDeal in l_SearchAccountDeals 
  67.              { 
  68.                  v_RelatedDealStage = ifnull(m_RelatedDeal.get("Stage"),"")
  69.                  v_RelatedDealId = ifnull(m_RelatedDeal.get("id"),"")
  70.                  // 
  71.                  if(v_RelatedDealStage.containsIgnoreCase("Closed Won") && v_RelatedDealId != v_ThisDealID) 
  72.                  { 
  73.                      v_CountWonOpps = v_CountWonOpps + 1
  74.                  } 
  75.              } 
  76.          } 
  77.          // 
  78.          // account field was blank? 
  79.          // count opportunities against this contact (B2C) 
  80.          if(v_CountWonOpps == 0 && v_ThisAccountID == "0") 
  81.          { 
  82.              r_SearchContactDeals = zoho.crm.getRelatedRecords("Deals","Contacts",v_ThisContactID,1,200,m_SortCriteria)
  83.              l_SearchContactDeals = ifnull(r_SearchContactDeals,List())
  84.              if(l_SearchContactDeals.size() > 0) 
  85.              { 
  86.                  for each  m_RelatedDeal2 in l_SearchContactDeals 
  87.                  { 
  88.                      v_RelatedDealStage2 = ifnull(m_RelatedDeal2.get("Stage"),"")
  89.                      v_RelatedDealId2 = ifnull(m_RelatedDeal2.get("id"),"")
  90.                      // 
  91.                      if(v_RelatedDealStage2.containsIgnoreCase("Closed Won") && v_RelatedDealId2 != v_ThisDealID) 
  92.                      { 
  93.                          v_CountWonOpps = v_CountWonOpps + 1
  94.                      } 
  95.                  } 
  96.              } 
  97.          } 
  98.          // 
  99.          // update the opp 
  100.          if(v_CountWonOpps > 0) 
  101.          { 
  102.              m_UpateOpp = Map()
  103.              m_UpateOpp.put("Type","Existing Business")
  104.              r_UpdateOpp = zoho.crm.updateRecord("Deals",v_ThisDealID,m_UpateOpp)
  105.              info r_UpdateOpp; 
  106.          } 
  107.          else 
  108.          { 
  109.              info "This is the first complete sales order so the opportunity type here is still 'New Business'."
  110.          } 
  111.      } 
  112.      else 
  113.      { 
  114.          info "This Sales Order is not considered complete so it will not update the opportunity."
  115.      } 
  116.  } 

Additional Notes:
  • This is for complete sales orders. If you need to trigger this at the invoice level, then you will need to change the workflow to trigger when the invoice reaches a certain status and modify the code above to reflect this.


Category: Zoho CRM :: Article: 1723

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