6

I've been working on an Apex class to do our Opportunity territory assignments based on the example here. I needed to modify it to support our rules that included checking the User Role on the Opportunity's Owner. So I modified the SOQL query that fetches the Opportunities to look like this:

Select Id, Owner.UserRole.Name, OwnerId, Territory2Id from Opportunity where Id IN :opportunityIds

This works fine when using my test class. In other words all ifs based on Owner.UserRole.Name get called in the test class and all system.debug tracing shows the correct vaules for Owner.UserRole.Name in the logs. However when I try to add it to the Territory Settings Page I get the following error:

Your settings can't be saved due to an error. An unexpected error occurred. Please include this ErrorId if you contact support: 616121970-108362 (1590199882)

If I change my query to use a formula field on the Opportunity object that pulls the same information the error on the Territory Settings page goes away.

You'll find below a trimmed down version of my code and test class that reproduces the problem. Any ideas why I'm getting this error?

Apex Class

Note - I am aware that in the code below my if clause on opp.Owner.UserRole.Name is rather pointless. The actual clause is more complex I just wanted to provide a simplified version of the code that reproduces the error.

global class TempOppTerrAssignment implements TerritoryMgmt.OpportunityTerritory2AssignmentFilter 
{ 

    /**
     * No-arg constructor.
     */ 
     global TempOppTerrAssignment() {}

 /**
  * 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, Owner.UserRole.Name, OwnerId, Territory2Id from Opportunity where Id IN :opportunityIds];
        Set<Id> ownerIds = new Set<Id>();
        // Create set of ownerIds
        for(Opportunity opp:opportunities){
            if(opp.Owner.UserRole.Name == 'CRE'){
                system.debug('User role is CRE');
                ownerIds.add(opp.OwnerId);
            }
            else{
                system.debug('User role is not CRE');
                ownerIds.add(opp.OwnerId);
            }
        }

        Map<Id,UserTerritory2Association> accountMaxPriorityTerritory = getOwnerTerritory(activeModelId, ownerIds);

        // For each opportunity, assign the territory that matches the owner.  If there isn't a match assign null.
        for(Opportunity opp: opportunities){
           UserTerritory2Association uta = accountMaxPriorityTerritory.get(opp.OwnerId);

          if((uta != null) && (uta.territory2Id != opp.Territory2Id)){
               OppIdTerritoryIdResult.put(opp.Id, uta.territory2Id);
           }else{
               OppIdTerritoryIdResult.put(opp.Id, null);
           }
        }
    }
    return OppIdTerritoryIdResult;
}

/**
  * Query assigned territoryIds in active model for given ownerIds.
  * Create a map of ownerId to UserTerritory2Association.
  */
 private Map<Id,UserTerritory2Association> getOwnerTerritory(Id activeModelId, Set<Id> ownerIds){
    Map<Id,UserTerritory2Association> ownerTerritory = new Map<Id,UserTerritory2Association>();
     for(UserTerritory2Association uta:[SELECT Territory2Id, Territory2.DeveloperName, UserId FROM UserTerritory2Association WHERE UserId IN :ownerIds AND Territory2.Territory2ModelId = :activeModelId]){
        ownerTerritory.put(uta.UserId, uta);
    }
    return ownerTerritory;
}


/**
 * 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;
}
}

Text Class

@isTest
public class TempOppTerrAssignmentTest {

@testSetup
static void setup() 
{
    List<UserRole> roles = new List<UserRole>();
    List<User> userList = new List<User>();


    Profile SalesCRE = [SELECT Name FROM Profile WHERE Name = 'Sales CSR'];
    Profile SalesCE = [SELECT Name FROM Profile WHERE Name = 'Sales Client Executive'];
    Profile adminProfile = [SELECT Name FROM Profile WHERE Name = 'Sales Operations Administrator'];

    UserRole roleCRE = new UserRole(DeveloperName = 'CRE1', Name = 'CRE');
    UserRole roleCE = new UserRole(DeveloperName = 'Client_Executive1', Name = 'Client Executive');
    roles.add(roleCRE);
    roles.add(roleCE);

    insert roles;

    User ownerUser = new User();

    ownerUser.Username = 'testuser@coveo.com';
    ownerUser.put('Email','testuser@coveo.com');

    ownerUser.FirstName = 'Test';
    ownerUser.LastName = 'Owner';

    //creating a user with a role to add the test data to get around the portal account owner must have a role error when creating community errors
    String ownerNickname = ((ownerUser.FirstName != null && ownerUser.FirstName.length() > 0) ? ownerUser.FirstName.substring(0,1) : '' ) + ownerUser.LastName.substring(0,1);
    ownerNickname += String.valueOf(Crypto.getRandomInteger()).substring(1,7);
    ownerUser.put('CommunityNickname', ownerNickname);
    ownerUser.put('Alias', ownerUser.FirstName.substring(0,1) + ownerUser.LastName.substring(0,4));
    ownerUser.put('TimeZoneSidKey', 'GMT');
    ownerUser.put('LocaleSidKey', 'en_US');
    ownerUser.put('EmailEncodingKey', 'UTF-8');
    ownerUser.put('LanguageLocaleKey', 'en_US');
    ownerUser.put('ProfileId', adminProfile.Id);
    ownerUser.UserRoleId = roles[1].Id;

    insert ownerUser;

    User ownerCEUser = new User();

    ownerCEUser.Username = 'salesce@coveo.com';
    ownerCEUser.put('Email','salesce@coveo.com');

    ownerCEUser.FirstName = 'Sales CE';
    ownerCEUser.LastName = 'Owner';

    //creating a user with a role to add the test data to get around the portal account owner must have a role error when creating community errors
    ownerNickname = ((ownerCEUser.FirstName != null && ownerCEUser.FirstName.length() > 0) ? ownerCEUser.FirstName.substring(0,1) : '' ) + ownerCEUser.LastName.substring(0,1);
    ownerNickname += String.valueOf(Crypto.getRandomInteger()).substring(1,7);
    ownerCEUser.put('CommunityNickname', ownerNickname);
    ownerCEUser.put('Alias', ownerCEUser.FirstName.substring(0,1) + ownerCEUser.LastName.substring(0,4));
    ownerCEUser.put('TimeZoneSidKey', 'GMT');
    ownerCEUser.put('LocaleSidKey', 'en_US');
    ownerCEUser.put('EmailEncodingKey', 'UTF-8');
    ownerCEUser.put('LanguageLocaleKey', 'en_US');
    ownerCEUser.put('ProfileId', SalesCE.Id);
    ownerCEUser.UserRoleId = roleCE.Id;
    userList.add(ownerCEUser);

    User ownerCREUser = new User();

    ownerCREUser.Username = 'salescrs@coveo.com';
    ownerCREUser.put('Email','salescrs@coveo.com');

    ownerCREUser.FirstName = 'Sales CRE';
    ownerCREUser.LastName = 'Owner';

    //creating a user with a role to add the test data to get around the portal account owner must have a role error when creating community errors
    ownerNickname = ((ownerCREUser.FirstName != null && ownerCREUser.FirstName.length() > 0) ? ownerCREUser.FirstName.substring(0,1) : '' ) + ownerCREUser.LastName.substring(0,1);
    ownerNickname += String.valueOf(Crypto.getRandomInteger()).substring(1,7);
    ownerCREUser.put('CommunityNickname', ownerNickname);
    ownerCREUser.put('Alias', ownerCREUser.FirstName.substring(0,1) + ownerCREUser.LastName.substring(0,4));
    ownerCREUser.put('TimeZoneSidKey', 'GMT');
    ownerCREUser.put('LocaleSidKey', 'en_US');
    ownerCREUser.put('EmailEncodingKey', 'UTF-8');
    ownerCREUser.put('LanguageLocaleKey', 'en_US');
    ownerCREUser.put('ProfileId', SalesCRE.Id);
    ownerCREUser.UserRoleId = roleCRE.Id;
    userList.add(ownerCREUser);

    insert userList;
    System.runAs(ownerUser)
    {
        List<Territory2> territories = [SELECT DeveloperName, Name, Territory2TypeId FROM Territory2];
        List<Opportunity> oppys = new List<Opportunity>();
        List<Account> accountList = new List<Account>();
        List<UserTerritory2Association> userTerritoryAssociationList = new List<UserTerritory2Association>();

        Account account1 = new Account(Name = 'testAccount');
        accountList.add(account1);

        Account account2 = new Account(Name = 'testAccount2', Account_Executive__c = userList[0].Id);
        accountList.add(account2);

        insert accountList;

        Opportunity opp = new Opportunity(
            Name = 'Opportunity Other',
            CloseDate = System.today().addDays(10),
            StageName = 'Prospecting',
            OwnerId = userList[0].Id,
            Deal_Type__c = 'Coveo for Sitecore',
            AccountId = accountList[0].Id
        );
        oppys.add(opp);

        Opportunity opp2 = new Opportunity(
            Name = 'Opportunity CRE',
            CloseDate = System.today().addDays(10),
            StageName = 'Prospecting',
            OwnerId = userList[1].Id,
            AccountId = accountList[1].Id
        );
        oppys.add(opp2);

        insert oppys;

        Territory2 terryOther;
        Territory2 terryCRE;

        for(Territory2 terry : territories)
        {
            if (terry.Name.contains('CRE') && terryCRE == null)
            {
                terryCRE = terry;
            }
            else if (terry.Name.contains('Other') && terryOther == null)
            {
                terryOther = terry;
            }
        }
        UserTerritory2Association objUserTerritory2Association2Other = new UserTerritory2Association(Territory2Id= terryOther.Id, UserId= userList[0].Id, RoleInTerritory2='Sales / Marketing Administrator');
        userTerritoryAssociationList.add(objUserTerritory2Association2Other);

        UserTerritory2Association objUserTerritory2Association2CRE = new UserTerritory2Association(Territory2Id= terryCRE.Id, UserId= userList[1].Id, RoleInTerritory2='Sales / Marketing Administrator');
        userTerritoryAssociationList.add(objUserTerritory2Association2CRE);

        insert userTerritoryAssociationList;
    }
}

@isTest
static void unitTest() 
{
    List<Opportunity> opportunityList = [Select Id From Opportunity];
    List<Id> opportunityId = new List<Id>();
    for(Opportunity opp:opportunityList)
    {
        opportunityId.add(opp.Id);
    }
    TempOppTerrAssignment filter = new TempOppTerrAssignment();

    Test.startTest();
        Map<Id, Id> resultMap = filter.getOpportunityTerritory2Assignments(opportunityId);
    Test.stopTest();

    System.assertEquals(2, resultMap.size());
}
}
  • 2
    It appears you are already familiar with what a gack is. Have you opened a case with support? This error means something went wrong on the Salesforce end (not necessarily anything wrong with your code). – Adrian Larson Mar 16 '18 at 15:27
  • Opening a support case is my next step. I just wanted to check with the community first to make sure that there wasn't an error that I did on my side before going to support with it. – Jonathan Miller Mar 16 '18 at 15:34
  • 1
    I guess the most obvious thing is just that OwnerId is polymorphic, and referencing parent fields from a name pointing polymorphic lookup wasn't always supported. What API Version are you using? – Adrian Larson Mar 16 '18 at 15:36
  • I'm using version 42. – Jonathan Miller Mar 16 '18 at 15:39
  • Yeah nothing obvious. Definitely open a case. – Adrian Larson Mar 16 '18 at 15:41

Your Answer

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

Browse other questions tagged or ask your own question.