0

I am in the process of implementing the territories within Salesforce and was following the instructions and sample code from the help documents here: https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_interface_TerritoryMgmt_OpportunityTerritory2AssignmentFilter.htm

I have setup the apex class which is as follows:

    /*** Apex version of the default logic.
If opportunity's assigned account is assigned to
*  Case 1: 0 territories in active model
*            then set territory2Id = null
*  Case 2: 1 territory in active model
*            then set territory2Id = account's territory2Id
*  Case 3: 2 or more territories in active model
*            then set territory2Id = account's territory2Id that is of highest priority.
*            But if multiple territories have same highest priority, then set territory2Id = null 
*/
global class OppTerrAssignDefaultLogicFilter implements TerritoryMgmt.OpportunityTerritory2AssignmentFilter { 
    /**
     * No-arg constructor.
     */ 
     global OppTerrAssignDefaultLogicFilter() {}

     /**
      * Get mapping of opportunity to territory2Id. The incoming list of opportunityIds contains only those with IsExcludedFromTerritory2Filter=false.
      * If territory2Id = null in result map, clear the opportunity.territory2Id if set.
      * If opportunity is not present in result map, its territory2Id remains intact.
      */
    global Map<Id,Id> getOpportunityTerritory2Assignments(List<Id> opportunityIds) { 
        Map<Id, Id> OppIdTerritoryIdResult = new Map<Id, Id>();

        // Get the active territory model Id
        Id activeModelId = getActiveModelId();

        if(activeModelId != null){
            List<Opportunity> opportunities =
              [Select Id, AccountId, Territory2Id from Opportunity where Id IN :opportunityIds];
            Set<Id> accountIds = new Set<Id>();
            // Create set of parent accountIds
            for(Opportunity opp:opportunities){
                if(opp.AccountId != null){
                    accountIds.add(opp.AccountId);
                    }
                }

                Map<Id,Territory2Priority> accountMaxPriorityTerritory = getAccountMaxPriorityTerritory(activeModelId, accountIds);

            // For each opportunity, assign the highest priority territory if there is no conflict, else assign null.
            for(Opportunity opp: opportunities){
               Territory2Priority tp = accountMaxPriorityTerritory.get(opp.AccountId);
               // Assign highest priority territory if there is only 1.
              if((tp != null) && (tp.moreTerritoriesAtPriority == false) && (tp.territory2Id != opp.Territory2Id)){
                   OppIdTerritoryIdResult.put(opp.Id, tp.territory2Id);
               }else{
                   OppIdTerritoryIdResult.put(opp.Id, null);
               }
            }
        }
        return OppIdTerritoryIdResult;
    }
    
    /**
      * Query assigned territoryIds in active model for given accountIds.
      * Create a map of accountId to max priority territory.
      */
     private Map<Id,Territory2Priority> getAccountMaxPriorityTerritory(Id activeModelId, Set<Id> accountIds){
        Map<Id,Territory2Priority> accountMaxPriorityTerritory = new Map<Id,Territory2Priority>();
        for(ObjectTerritory2Association ota:[Select ObjectId, Territory2Id, Territory2.Territory2Type.Priority from ObjectTerritory2Association where objectId IN :accountIds and Territory2.Territory2ModelId = :activeModelId]){
            Territory2Priority tp = accountMaxPriorityTerritory.get(ota.ObjectId);

            if((tp == null) || (ota.Territory2.Territory2Type.Priority > tp.priority)){
                // If this is the first territory examined for account or it has greater priority than current highest priority territory, then set this as new highest priority territory.
                tp = new Territory2Priority(ota.Territory2Id,ota.Territory2.Territory2Type.priority,false);
            }else if(ota.Territory2.Territory2Type.priority == tp.priority){
                // The priority of current highest territory is same as this, so set moreTerritoriesAtPriority to indicate multiple highest priority territories seen so far.
                tp.moreTerritoriesAtPriority = true;
            }
            
            accountMaxPriorityTerritory.put(ota.ObjectId, tp);
        }
        return accountMaxPriorityTerritory;
    }


    /**
     * Get the Id of the Active Territory Model.
     * If none exists, return null.
     */
    private Id getActiveModelId() {
        List<Territory2Model> models = [Select Id from Territory2Model where State = 'Active'];
        Id activeModelId = null;
        if(models.size() == 1){
            activeModelId = models.get(0).Id;
        }

        return activeModelId;
    }

    /**
    * Helper class to help capture territory2Id, its priority, and whether there are more territories with same priority assigned to the account.
    */
    @testVisible
    private class Territory2Priority {
        public Id territory2Id { get; set; }
        public Integer priority { get; set; }
        public Boolean moreTerritoriesAtPriority { get; set; }

        @testVisible
        Territory2Priority(Id territory2Id, Integer priority, Boolean moreTerritoriesAtPriority){
            this.territory2Id = territory2Id;
            this.priority = priority;
            this.moreTerritoriesAtPriority = moreTerritoriesAtPriority;
        }
    }
}

I had found a test class here for this:

@isTest
private class OppTerrAssignDefaultLogicFilterTest {
    
    @isTest
    private static void testActiveModel() {
        // This method is just for code coverage. You can't activate a territory model from code.
        OppTerrAssignDefaultLogicFilter filter = new OppTerrAssignDefaultLogicFilter();
        Id modelId = filter.ActiveModelId;
    }
    
    @isTest
    private static void testOppTerritory() {
        Territory2 terr = new Territory2();
        Territory2 terr2 = new Territory2();
        OppTerrAssignDefaultLogicFilter filter = new OppTerrAssignDefaultLogicFilter();
        
        System.runAs(new User(Id = UserInfo.getUserId())) {
            Territory2Model tm = new Territory2Model(Name = 'test', DeveloperName ='test');
            insert tm;
            
            filter.ActiveModelId = tm.Id; //set the active model Id since it can't be queried
            
            Territory2Type tt = [Select Id from Territory2Type limit 1];
            
            terr = new Territory2(Name = 'Test Territory', DeveloperName = 'TestTerritory', Territory2ModelId = tm.Id, Territory2TypeId = tt.Id);
            insert terr;
            
            terr2 = new Territory2(Name = 'Test Territory2', DeveloperName = 'TestTerritory2', Territory2ModelId = tm.Id, Territory2TypeId = tt.Id);
            insert terr2;
        }
        
        Account a = new Account(Name = 'Test Account');
        insert a;
        
        Contact testContact = new Contact(
                FirstName = 'First',
                LastName = 'Last',
                Email = 'test@test.com',
                AccountId = a.Id
        );
        insert testContact;        
        
        ObjectTerritory2Association ota = new ObjectTerritory2Association(AssociationCause  = 'Territory2Manual', ObjectId = a.Id, Territory2Id = terr.Id);
        insert ota;
        
        Opportunity opp = new Opportunity(
                Name = 'Test Opp',
                AccountId = a.Id,
                //RecordTypeId = String.isNotBlank(oppRecTypeId) ? oppRecTypeId : null,
                StageName = 'Prospecting',
                OpportunityContact__c = testContact.Id,
                Type = 'Systems',
                CloseDate = System.today(),
                Market_Segment__c = 'General Industry',
                End_User_Type__c = 'Agricultural'
        );
        insert opp;   
        
                
        Map<Id, Id> resultMap = filter.getOpportunityTerritory2Assignments(new List<Id>{opp.Id});
        
        System.assertEquals(terr.Id, resultMap.get(opp.Id));
        
        ObjectTerritory2Association ota2 = new ObjectTerritory2Association(AssociationCause  = 'Territory2Manual', ObjectId = a.Id, Territory2Id = terr2.Id);
        insert ota2;
        
        resultMap = filter.getOpportunityTerritory2Assignments(new List<Id>{opp.Id});
        
        System.assertEquals(null, resultMap.get(opp.Id), 'No territory should be assinged as 2 have the same priority');
    }
    
}

I am getting an error at lines 8 and 20 on the test class that states: Variable does not exist: ActiveModelId

Can anyone advise as to how to resolve?

Many Thanks!

1
  • Your existing territory model, type, and territory records are visible in unit tests (regardless of SeeAllData), so you don't need to create a dummy territory structure. Your unit test will use your existing active territory structure. Jan 12 at 21:39
0

Ah, such is the danger of copy/pasting code. When debugging code, it often helps to start at the line where you got your error and start working backwards.

  • So, filter.activeModelId gives you "variable does not exist". That could mean that filter isn't a variable, or activeModelId isn't a variable
  • We see that filter is defined as a variable in your test class (OppTerrAssignDefaultLogicFilter filter = new OppTerrAssignDefaultLogicFilter();), so the issue isn't there
  • Looking through OppTerrAssignDefaultLogicFilter, there is no activeModelId class variable (it is defined locally in 2 methods, but they can't be used externally), so this is the source of your error

I've no experience with the Territory management feature, so I can't say for certain if the "You can't activate a territory model from code." assertion made in a comment in the test class is accurate or not, but if it is then you'll need to modify the OppTerrAssignDefaultLogicFilter class that you got from Salesforce's example to make it testable.

At the very least, that would involve creating an @testVisible or public class variable (static or not, either would be an option) to hold a Territory2Model Id of your choosing. In other words, you need to be able to inject an appropriate Id into this classm, from your test class.

You'd also need to modify getActiveModelId() to use that new class variable in a test context (check Test.isRunningTest(), or if your class variable can only be set when running a test, check if your variable is set or empty/null).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.