function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
sgkmillssgkmills 

Force Territory Rules to run

I have written a C# application that forces territory rules to run on a scheduled time period by running the salesforce generated URL.  So I am trying to write a trigger that runs the territory rules when an Opportunity is either created or updated!  The problem is that I don't see a way to force the territory rules to run! 

Does any one have a practical solution?  BTW, I tried to update the account associated with the opportunity.  If I do it via Salesforce, the account page layout forces the territory rules to run.  This is due to the checkbox 'Run territory Assignment Rules on save by default' on the account page layout.  But, it seems this doesn't work via the API.

Any information on this matter would be greatly appreciated!
Best Answer chosen by Admin (Salesforce Developers) 
sgkmillssgkmills

I am a consultant for a company and I write the code for their Salesforce system, so legally they own all the code.  Therefore, I cannot post the code in its entirety. I did get permission to post snippets which should be clear enough to show you how to use the trigger/asynchronous callout/RESTful webservice approach that I did.

 

Below is the trigger, this gets called each time an Opportunity is updated (after update).  The 'if' statement (o.TerritoryId == null) will make sure the webservice only gets called if the Territory isn't set yet.  The line that calls the RESTful webservice is highlighted in red.  TerritoryRulesUpdate is an Apex class that has a method 'updateAccount' that does and asynchronous callout to the RESTful webservice (written in C#, see below).  I will post snippets for the TerritoryRulesUpdate class and the 'updateAccount' method below.

 

Trigger

 

trigger TR_Trigger on Opportunity (after update) {
for (Opportunity o: Trigger.new) {
if (o.TerritoryId== null) {
// make the asynchronous web service callout
TerritoryRulesUpdate.updateAccount(o.AccountId, o.ID, o.Opportunity_Number__c);

}
}
}

 

TerritoryRulesUpdate Class

public virtual class TerritoryRulesUpdate {


public TerritoryRulesUpdate() {}

// Code omitted, but all the initialization of the HTTPRequest and HTTPResponse classes are done
// above and setting up variables to hold the endpoint. (The endpoint is the URL to call the
// Webservice that was written in c#)

@future (callout=true)
public static void updateAccount(String ID, String OppID, String SFNo) {

TerritoryRulesUpdate t = new TerritoryRulesUpdate();
// After the class is instantiated, we execute the RESTful webservice using Salesforce's
// HTTPClasses. We process the response (HTTPResponse) from the webservice.
// something with the response if I receive a CalloutException.

// Below is the code that actually does the callout with t.execute(req). All these
// variables (req, res) were initialized above, but I had to omit how they were initialized.

try {
TerritoryRulesUpdate.res = t.execute(req);
resBody = t.getResponseBody(res, req);

} catch(System.CalloutException e) {
}
}
}

 

RESTful webservice written in C#

 

The code below is the main part of the webservice.  Before this method is called, you will need to have the webservice login to Salesforce and create the binding for the webservice.  There are many examples of how to login to Salesforce via .NET, but here is a link: Salesforce using API Example.  The main part of the code is setting up the AssignmentRuleHeader and then updating the Account. We setup a field called 'Run Rules' that we set to true, but the AssignmentRuleHeader is what forces the territory Rules to update.  This is explained in the following link: AssignmentRuleHeader.  After that, all you have to do is send the update to the account back upto Salesforce.

 

 

       private void updateAcct(String AcctId)
{
            ArrayList arrAccts_t = new ArrayList();

//The results will be placed in qr
RunTerritoryRules.sforce.QueryResult qr = null;
/*We are going to increase our return batch size to 250 items
Setting is a recommendation only, different batch sizes may
be returned depending on data, to keep performance optimized.
*/
binding.QueryOptionsValue = new RunTerritoryRules.sforce.QueryOptions();
binding.QueryOptionsValue.batchSize = 250;
binding.QueryOptionsValue.batchSizeSpecified = true;

String qry = "Select Id, Name, Run_Rules__c FROM Account where Id='" + AcctId + "' ";

try
{
qr = binding.query(qry);
bool done = false;

if (qr.size > 0)
{
                    // Create the assignment rule header and add it to the proxy binding
                    AssignmentRuleHeader arh = new AssignmentRuleHeader();
                    arh.useDefaultRule = true;
                    binding.AssignmentRuleHeaderValue = arh;

}

while (!done)
{
for (int i = 0; i < qr.records.Length; i++)
{
RunTerritoryRules.sforce.Account acct = (RunTerritoryRules.sforce.Account)qr.records[i];
                            acct.Run_Rules__c = true;
                            acct.Run_Rules__cSpecified = true;
 arrAccts_t.Add(acct);
recCount++;
}
if (qr.done)
{
done = true;
}
else
{
qr = binding.queryMore(qr.queryLocator);
}
}
}

 

 

Jeff Douglas wrote an article that assisted me.  It shows how to call a RESTful Web Service as a callout in Salesforce.  The link is: RESTful Web Service Callout using POST with Salesforce.com. I also got some good information from this link on Salesforce's documentation on the HTTPResponse Class.  Finally, when it comes to testing the class and trigger, this Salesforce documentation gave me the best approach: Apex Code Test Methods

 

Hopefully, this gets you to a solution for your problem.  Sorry I couldn't give the complete code, but I think I addressed all the main areas of the code.  This is definitely not the only solution, but it is the one that works for us and has been implemented for a bout a year and has worked just fine.  Regarding the asynchronous callout, it happens immediately since only one Account record is being updated at a time.

 

 

All Answers

A_SmithA_Smith
Hi sgkmills,

You shouldn't need to run the rules everyday.  They are designed to be maintained as the data is updated.  A few points that might help:

- Territory rules are not evaluated when creating/updating an opportunity.  Only modifications to an account trigger territory rule evaluation.
- You must set the API header "useDefaultRule" to true in order to trigger the rules engine on an account insert/update.  See the link below for more information:
http://www.salesforce.com/us/developer/docs/api/index_CSH.htm#sforce_api_header_assignmentruleheader.htm#topic-title

Thanks,
Andrew
samdsamd

Hi Andrew

 

We are using the Informatica SFDC connector to update Accounts in SFDC via the API.  Is there any way that we can configure this so that updates performed by the Informatica SFDC connector will cause territory rules to be run?

 

Many thanks

Sam

trublu1trublu1

Hi Andrew and Sam,

 

We are using CastIron to UPSERT Accounts to SFDC via the API.  Is there any way that we can configure this so that upserts performed by the CastIron will trigger the territory rules to be run?

 

Many thanks

JBZJBZ

I"m having the same exact issue SAMD. We're updating Accounts via Informatica, however the TM rules are not re-evaluating the Accounts on update. We've gone front to back on the documentation and even attempted to write trigger on Accounts to solve it, however, you cannot access the AssignmentRuleHeader properties via DML.

 

Anyone out there have a way to trigger assignment rule updates when changes are not made via the UI?

sgkmillssgkmills
I accomplished this by writing a web service and then I wrote a trigger that gets called when the opportunity is updated. The trigger makes a callout to the webservice. The webservice updates the account and that makes for the territory rules run.
JBZJBZ

Sounds viable. Can you post your trigger and webservice code?

samdsamd

Hi

 

I'd also be very interested to see your code - please post it if possible!

 

Thanks

Sam

sgkmillssgkmills

I am a consultant for a company and I write the code for their Salesforce system, so legally they own all the code.  Therefore, I cannot post the code in its entirety. I did get permission to post snippets which should be clear enough to show you how to use the trigger/asynchronous callout/RESTful webservice approach that I did.

 

Below is the trigger, this gets called each time an Opportunity is updated (after update).  The 'if' statement (o.TerritoryId == null) will make sure the webservice only gets called if the Territory isn't set yet.  The line that calls the RESTful webservice is highlighted in red.  TerritoryRulesUpdate is an Apex class that has a method 'updateAccount' that does and asynchronous callout to the RESTful webservice (written in C#, see below).  I will post snippets for the TerritoryRulesUpdate class and the 'updateAccount' method below.

 

Trigger

 

trigger TR_Trigger on Opportunity (after update) {
for (Opportunity o: Trigger.new) {
if (o.TerritoryId== null) {
// make the asynchronous web service callout
TerritoryRulesUpdate.updateAccount(o.AccountId, o.ID, o.Opportunity_Number__c);

}
}
}

 

TerritoryRulesUpdate Class

public virtual class TerritoryRulesUpdate {


public TerritoryRulesUpdate() {}

// Code omitted, but all the initialization of the HTTPRequest and HTTPResponse classes are done
// above and setting up variables to hold the endpoint. (The endpoint is the URL to call the
// Webservice that was written in c#)

@future (callout=true)
public static void updateAccount(String ID, String OppID, String SFNo) {

TerritoryRulesUpdate t = new TerritoryRulesUpdate();
// After the class is instantiated, we execute the RESTful webservice using Salesforce's
// HTTPClasses. We process the response (HTTPResponse) from the webservice.
// something with the response if I receive a CalloutException.

// Below is the code that actually does the callout with t.execute(req). All these
// variables (req, res) were initialized above, but I had to omit how they were initialized.

try {
TerritoryRulesUpdate.res = t.execute(req);
resBody = t.getResponseBody(res, req);

} catch(System.CalloutException e) {
}
}
}

 

RESTful webservice written in C#

 

The code below is the main part of the webservice.  Before this method is called, you will need to have the webservice login to Salesforce and create the binding for the webservice.  There are many examples of how to login to Salesforce via .NET, but here is a link: Salesforce using API Example.  The main part of the code is setting up the AssignmentRuleHeader and then updating the Account. We setup a field called 'Run Rules' that we set to true, but the AssignmentRuleHeader is what forces the territory Rules to update.  This is explained in the following link: AssignmentRuleHeader.  After that, all you have to do is send the update to the account back upto Salesforce.

 

 

       private void updateAcct(String AcctId)
{
            ArrayList arrAccts_t = new ArrayList();

//The results will be placed in qr
RunTerritoryRules.sforce.QueryResult qr = null;
/*We are going to increase our return batch size to 250 items
Setting is a recommendation only, different batch sizes may
be returned depending on data, to keep performance optimized.
*/
binding.QueryOptionsValue = new RunTerritoryRules.sforce.QueryOptions();
binding.QueryOptionsValue.batchSize = 250;
binding.QueryOptionsValue.batchSizeSpecified = true;

String qry = "Select Id, Name, Run_Rules__c FROM Account where Id='" + AcctId + "' ";

try
{
qr = binding.query(qry);
bool done = false;

if (qr.size > 0)
{
                    // Create the assignment rule header and add it to the proxy binding
                    AssignmentRuleHeader arh = new AssignmentRuleHeader();
                    arh.useDefaultRule = true;
                    binding.AssignmentRuleHeaderValue = arh;

}

while (!done)
{
for (int i = 0; i < qr.records.Length; i++)
{
RunTerritoryRules.sforce.Account acct = (RunTerritoryRules.sforce.Account)qr.records[i];
                            acct.Run_Rules__c = true;
                            acct.Run_Rules__cSpecified = true;
 arrAccts_t.Add(acct);
recCount++;
}
if (qr.done)
{
done = true;
}
else
{
qr = binding.queryMore(qr.queryLocator);
}
}
}

 

 

Jeff Douglas wrote an article that assisted me.  It shows how to call a RESTful Web Service as a callout in Salesforce.  The link is: RESTful Web Service Callout using POST with Salesforce.com. I also got some good information from this link on Salesforce's documentation on the HTTPResponse Class.  Finally, when it comes to testing the class and trigger, this Salesforce documentation gave me the best approach: Apex Code Test Methods

 

Hopefully, this gets you to a solution for your problem.  Sorry I couldn't give the complete code, but I think I addressed all the main areas of the code.  This is definitely not the only solution, but it is the one that works for us and has been implemented for a bout a year and has worked just fine.  Regarding the asynchronous callout, it happens immediately since only one Account record is being updated at a time.

 

 

This was selected as the best answer
samdsamd

Perfect, thanks very much for your comprehensive response!  I was hoping to find a solution that didn't involve a webservice being called from another server (which is contrary to the idea of having everything "in the cloud").  I'm currently looking into whether we can create a trigger that does a REST callout back to SFDC to cause the territory assignment rule to run. I'll update this thread if I have any success.

sgkmillssgkmills

I wanted to keep all code within Salesforce, but I don't believe you can invoke an Apex webservice from within Apex.  I read somewhere you must invoke it from an external application.  I guess because of recursive issues!

ShaminaShamina

Hello,

 

I have a similar requirement to run territory assignment rules from Apex. But the Salesforce Apex guide indicates that Database.DMLOptions does not apply for territory management rules so I am currently investigating how to force the rules to run from REST webservices in PHP. I have a few questions regarding the replies in this post. 

 

@ sgkmills :

Concerning this section of your code sample :

 AssignmentRuleHeader arh = new AssignmentRuleHeader();
arh.useDefaultRule = true;
 binding.AssignmentRuleHeaderValue = arh;

 

Can you please tell me how do you have access to the class AssignmentRuleHeader in REST web services?  According to the online documentation for AssignmentRuleHeader , this class is available as a SOAP header and I cannot find the equivalent for REST web services.

 

@samd :

Have you been able to come up with a solution on your end ?

 

Thanking you

 

sgkmillssgkmills

I am using Salesforce'a  SOAP API.  I am using the API to access the AssignmentRuleHeader.  Then I encapsulate everthing into a REST web service.  I then call the webservice from within a trigger in Salesforce.

 

From your statement, it looks as if you want to use the Salesforce's REST API to do the above.  I don't know if the AssignmentRuleHeader is available in that API.