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
ManderMander 

Request Account Ownership

I'm really tired of getting requests to change the account ownership and would like to write some sort of Apex trigger to do the update.  I can't find an example of anyone ever doing this and I'm new to Apex code.  Will this work and if so, do you have something I can start with?

 

Ideally, it would work almost like the Request Update button with some additional adjustments.   The user would click the button and it would check to see of the current owner is active.  If not, the ownership will change to the current user.  If the owner is active, it will send an email requesting ownership and the current owner can either approve or deny.  If approved the ownership would change, if denied, a rejection email would be sent back to the requester.  

 

Am I dreaming to big?  I'd really appreciate any help.

Best Answer chosen by Admin (Salesforce Developers) 
sfdcfoxsfdcfox

Your script could be modified to accommodate that. There's very few reasons why ownership can't be transferred (and most of which wouldn't apply in this scenario), but you can certainly display any resulting error message back to the user.

All Answers

sfdcfoxsfdcfox
Too big? I don't think so. Pretty ambitious for a first project, but I don't see why it can't be accomplished. I don't have anything like this at the moment, but I can give you a few pointers:

* Don't use an action attribute on the page. Make sure the user has a chance to confirm that they actually want to attempt to gain access to the account. This is actually listed as a Security requirement. It's just generally a good idea.

* You can send the email through Apex Code, so make sure you look for that in the documentation.

* You can also write an "email service", so when the user replies with an approve or deny message, you can handle the request appropriately.

* You can also just provide a link to the page where they can approve the request (or not).

* I'd probably recommend a custom object to store these requests, so you can maintain a history. It also makes it easier to write the code that would be used in some rare circumstances, such as determining which request to approve if there are multiple, etc.

That's about all I have at the moment. A well-qualified developer could whip this up in about 4-8 development hours. If you have at least a basic grasp of programming and web design, it shouldn't take more than approximately 16-24 hours of effort. This is assuming about 8-12 hours for getting up to speed on the syntax and such; it took me about this long to grasp over 75% of the concepts of Apex Code and Visualforce (but I was excited, too, because the technology beats the pants off of S-Controls). As an advantage, some of the techniques you'll learn aren't exactly at the novice coding level (such as learning about Interfaces, etc).

Just have fun with it, and feel free to call on us if you get stuck.
ManderMander

I think I've nearly got this figured out, but I'm running into a couple of errors.  Here's my trigger so far:

 

trigger RequestOwnership on Account (before update) {

    for(Account a : trigger.new ) {
        if(a.Owner.IsActive = FALSE && a.Request_Ownership__c == TRUE)
            {  a.ownerId = system.Userinfo.getUserId();
                a.Request_Ownership__c = FALSE; }
        
        if(a.Ownerid == '00580000001hSQb' && a.Request_Ownership__c == TRUE)
            {  a.ownerId = system.Userinfo.getUserId();
                a.Request_Ownership__c = FALSE; }        
  
    }
}

 

If I use a.Owner.IsActive = False, I get the results that I want until I try to change the account owner using the normal change link.  Then I get an error saying:  

 

Error: Apex trigger RequestOwnership caused an unexpected exception, contact your administrator: RequestOwnership: execution of BeforeUpdate caused by: System.NullPointerException: Attempt to de-reference a null object: Trigger.RequestOwnership: line 4, column 1

 

If I use a.Owner.IsActive == False, it makes the ownership change even if the current owner is active, which is exactly what I don't want.

 

Any help would be great.

 

sfdcfoxsfdcfox
trigger RequestOwnership on Account (before update) {
  map<id,user> users = new map<id,user>();
  for(account record:trigger.new)
    users.put(account.ownerid,null);
  users.putall([select id,isactive from user where id in :users.keyset()]);
  for(account record:trigger.new)
    if( record.request_ownership__c && (!users.get(record.ownerid).isactive || record.ownerid == '00580000001hSQb')) {
     record.ownerid = userinfo.getuserid();
  }
}

Problems:

 

1) You can't go through a relationship without querying for it. It just doesn't work (see the first part).

2) You used = instead of == (assignment versus equals comparison). Also, you don't need to compare to true, and false is just !true.

3) You can combine logic with && and || to make even more complex logic so that the you can eliminate duplicate code. Use parenthesis to make sure the logic is interpreted correctly.

4) You really shouldn't use a hard-coded ID. Query for the user ID and use that variable instead. This will likely save you problems in the future. 

ManderMander

Thank you!  I made a couple of changes so I could keep some of the informtion straight in my head and I was getting an error I'm still really not sure about, but it's gone and it works.

 

Here's my final code.

trigger RequestOwnership on Account (before update) {

Map<Id, User> ownerMap = new Map<Id, User>();
for (Account a: Trigger.new)
{ ownerMap.put(a.OwnerID, null);}
{ ownerMap.putall([SELECT Id, IsActive FROM User WHERE Id IN :ownerMap.keySet()]);
 

      for(Account a : trigger.new ) {
        if( a.request_ownership__c &&  (!ownerMap.get(a.OwnerId).IsActive || a.OwnerId == '00580000001hSQb'))
            {  a.ownerId = system.Userinfo.getUserId();
                a.Request_Ownership__c = FALSE; }
            {a.Request_Ownership__c = FALSE; }

    }
}
}

 I hardcoded the one ID because it's a default user for accounts created by our API, not a real person.

 

I also created a custom button to check that box for the user so all they have to do is click that button.

 

If only it could send a message when a change can't be made.  I know I'll get a hundred questions about why they hit the button but didn't get ownership of the account.  Still, it's worth it.  Thanks again!

sfdcfoxsfdcfox

Your script could be modified to accommodate that. There's very few reasons why ownership can't be transferred (and most of which wouldn't apply in this scenario), but you can certainly display any resulting error message back to the user.

This was selected as the best answer
ManderMander

sfdcfox, I'm sorry, but I'm having an issue deploying the trigger.  My test code gave me 77% coverage, but when I deployed the query was too big.  I've made a change, but now I can't get coverage.  Can you take a quick look and help me figure out what it should be?

 

@isTest(SeeAllData=true)
Private class RequestOwnershipTest{
 
static testmethod void myTest(){

 Profile pf = [Select Id from Profile where Name = 'System Administrator'];

        User u = new User();
        u.FirstName = 'Test';
        u.LastName = 'User3';
        u.Email = 'testuser3@test123456789.com';
        u.CompanyName = 'test3.com';
        u.Title = 'Test User3';
        u.Username = 'testuser3@test123456789.com';
        u.Alias = 'testusr3';
        u.IsActive = False;
        u.CommunityNickname = 'Test User3';
        u.TimeZoneSidKey = 'America/Mexico_City';
        u.LocaleSidKey = 'en_US';
        u.EmailEncodingKey = 'ISO-8859-1';
        u.ProfileId = pf.Id;
        u.LanguageLocaleKey = 'en_US';
        insert u;

        User u2 = new User();
        u2.FirstName = 'Test2';
        u2.LastName = 'User';
        u2.Email = 'test2user@test123456789.com';
        u2.CompanyName = 'test2.com';
        u2.Title = 'Test2 User';
        u2.Username = 'test2user@test123456789.com';
        u2.Alias = 'test2usr';
        u2.IsActive = True;
        u2.CommunityNickname = 'Test2 User';
        u2.TimeZoneSidKey = 'America/Mexico_City';
        u2.LocaleSidKey = 'en_US';
        u2.EmailEncodingKey = 'ISO-8859-1';
        u2.ProfileId = pf.Id;
        u2.LanguageLocaleKey = 'en_US';
        insert u2;

    system.runAs(u2){
            Account a = new Account();
            a.Name = 'ABC';
            a.OwnerId = u.id;
            a.Type = 'Customer';
            a.CurrencyIsoCode = 'USD';
            insert a;
            
            Contact c = new Contact();
            c.Account = a;
            c.FirstName = 'Rollup';
            c.LastName = 'Test';
            c.CurrencyIsoCode = 'USD';
            c.Email = 'test@test.com';          
            insert c;
 
            List<Account> lst = [Select OwnerID from Account where Request_Ownership__c = true];
            System.assertEquals(a.ownerid,u.id);
} 

}
}

 I think it's because of Where Request_Ownership__c = True, but I don't know what else to use to limit the query.

 

Thank you so much for being willing to help me out with all of my questions.

Mander

ManderMander

Never mind.  I figured it out.  I'm not sure if it's right, but I got 100% coverage and was able to deploy.  Everything works well in the live org so I think we're good.  Thanks again, sfdcfox.

 

@isTest(SeeAllData=true)
Private class RequestOwnershipTest{
 
static testmethod void myTest(){

 Profile pf = [Select Id from Profile where Name = 'System Administrator'];

        User u = new User();
        u.FirstName = 'Test';
        u.LastName = 'User3';
        u.Email = 'testuser3@test123456789.com';
        u.CompanyName = 'test3.com';
        u.Title = 'Test User3';
        u.Username = 'testuser3@test123456789.com';
        u.Alias = 'testusr3';
        u.IsActive = False;
        u.CommunityNickname = 'Test User3';
        u.TimeZoneSidKey = 'America/Mexico_City';
        u.LocaleSidKey = 'en_US';
        u.EmailEncodingKey = 'ISO-8859-1';
        u.ProfileId = pf.Id;
        u.LanguageLocaleKey = 'en_US';
        insert u;

        User u2 = new User();
        u2.FirstName = 'Test2';
        u2.LastName = 'User';
        u2.Email = 'test2user@test123456789.com';
        u2.CompanyName = 'test2.com';
        u2.Title = 'Test2 User';
        u2.Username = 'test2user@test123456789.com';
        u2.Alias = 'test2usr';
        u2.IsActive = True;
        u2.CommunityNickname = 'Test2 User';
        u2.TimeZoneSidKey = 'America/Mexico_City';
        u2.LocaleSidKey = 'en_US';
        u2.EmailEncodingKey = 'ISO-8859-1';
        u2.ProfileId = pf.Id;
        u2.LanguageLocaleKey = 'en_US';
        insert u2;

            Account a = new Account();
            a.Name = 'ABC';
            a.OwnerId = u.id;
            a.Type = 'Customer';
            a.CurrencyIsoCode = 'USD';
            insert a;
            
    system.runAs(u2){
    
             List<Account> lst = [Select OwnerID, Request_Ownership__c from Account where OwnerId != :u2.id and Request_Ownership__c=true LIMIT 5000];
             Update a;
            a.Request_Ownership__c =true;
            System.assertEquals(a.ownerid,u.id);