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
SFDC Dev KalluSFDC Dev Kallu 

Populate Account field with a field from user profile using triggers (Trigger newbie)

Hi guys,

This is the first trigger i am trying. I wanted to create a workflow or something by which i could populate a custom field in Account with some profile info from a field from the User Info page. It seems there is no way to get the user info for the person who created the record using workflow and triggers are the only choice.

So i have been reading up on it and trying to come up with a solution. I will post my attempts here, hopefully you guys can help me so that i can archive my goal and also learn about triggers.

trigger updateProfile on Account (after update)
{
      Account.Profile__c = User.Division;
}

Here i am trying to get the Division name for the user and input it into the custom field called Profile in the Account object. This is not working for me, can someone tell me what i am doing wrong?

Thanks!
SFDC Dev KalluSFDC Dev Kallu
My second attempt,

Code:
trigger updateProfile on Account (after insert)
{
    for (Account accs : Trigger.new) {
        User u = [select id, Profile.Name from User where id = :Userinfo.getUserId()];
        accs.Profile__c = u.Profile.Name;
        }
}

 
Gives no errors but nothing gets updated...i'll keep trying i guess...
dkorba2k5dkorba2k5
Change it to a before insert or update trigger instead.  You're updating the field but not pushing the data back in.  If you make it a before trigger instead of an after trigger then that should work.


Message Edited by dkorba2k5 on 12-08-2008 11:52 PM
SFDC Dev KalluSFDC Dev Kallu
Thanks for the reply dkorba2k5,

I tried to change it to before insert and this is the error i got...

Error: Invalid Data.
Review all error messages below to correct your data.
Apex trigger updateProfile caused an unexpected exception, contact your administrator: updateProfile: execution of BeforeInsert caused by: System.QueryException: List has no rows for assignment to SObject: Trigger.updateProfile: line 4, column 18

Any idea what's wrong here...i am trying to query the user records so i dont know why it says no rows when there are rows...

Thanks,
KalluMama
 

JimRaeJimRae
The code change you suggested is not bulk safe.  If a batch update of accounts occurs, the SOQL query for the user will fire with every record in the loop. You would be better served to have the trigger like this:

Code:
trigger updateProfile on Account (before insert)
{
    //query once for profile name string
    String profilename = [select id, Profile.Name from User where id = :Userinfo.getUserId()].Profile.Name;
    
    //loop for all accounts being inserted
    for (Account accs : Trigger.new) {
        accs.Profile__c = profilename;
    }
}

 
Also, be aware that this would only place a value on the creation of the account, not on any update, including a change in ownership.  If that is a concern, a more advanced set of logic might be needed.
Wingsrul91Wingsrul91

So what would this advanced set of logic be?

 

I am trying to do pretty much the exact same thing but need it to update a field after the owner is updated, not just when the record is created.

 

I need to associate various information in a case record that has to do with the record owner, such as office and region.  The office and region information is created in the user profile, so it needs to be pulled from there.

 

We tried to create the relationship using the "lastmodifiedby" field but that doesnt work when someone else changes the record owner.

 

Any help would be greatly appreciated. 

JimRaeJimRae
The more advanced logic would depend on your specific use case First, you would want to have this be an insert and update trigger.  second, you would need to do some testing to see what changed on an update, and if that would cause you to change your need for a profile.  Might do a check to see if the trigger.old value of the ownerid field is different than that of the trigger.new value, and if so, do the update then.
Wingsrul91Wingsrul91

This is what needs to happen:

We have a case owner field that can either be a agent (user) or a queue.  For every salesforce account we have, there is associated agent information such as the office and region they reside in.  Now, any user can update the record owner to a user or a queue.  But when the case record is owned by a user, with a salesforce login, the record needs to import the related user information such as the office they are in.  The users office is edited in the user profile page when their salesforce account is created.  There just needs to be a field in the case record page layout that can display information related to the case owner as long as it is a user, not a queue.

Is there anyway to do this other than a trigger?

JimRaeJimRae

I would say a trigger would be the best choice. If you try to use a formula field, you are more likely to capture the data from the user doing the editing, not necessarily the owner.

 

Here is a simple version, that I think would do what you are describing, you will need to tweak the names of your fields in the user record and in the case record to match accordingly.

 

 

trigger caseOwnerdetail on Case (before insert, before update){ Set<ID> caseOwnerIDs = new Set<ID>(); for(case c:trigger.new){

//get just the users if(c.ownerid.substring(0,3)=='005'){ caseOwnerIDs.add(c.ownerid); } } Map <ID, User> owners = new Map<ID,User>{}; for(user u :[select id,name,region__c,office__c from user where id in:caseOwnerIDs]){ owners.put(u.id,u); } for(case c1:trigger.new){ c1.agentregion__c = owners.get(c1.ownerid).region__c; c1.agentoffice__c = owners.get(c1.ownerid).office__c; } }

 

 

 

Wingsrul91Wingsrul91

Thank you very much for you help!

It is still throwing an error:

 

Error: Compile Error: Method does not exist or incorrect signature: [Id].substring(Integer, Integer) at line 5 column 11

 

I erased the region__c field because its not necessary, I just need it to pull the office__c because I can create formula field based on the office name to display the region.

 

Thank you again for your help! 

JimRaeJimRae

OK,

Try this version.  Should be enough to get you started.

 

 

trigger caseOwnerdetail on Case (before insert, before update){ Set<ID> caseOwnerIDs = new Set<ID>(); for(case c:trigger.new){ //get just the users if(String.valueOf(c.ownerid).substring(0,3)=='005'){ caseOwnerIDs.add(c.ownerid); } } Map <ID, User> owners = new Map<ID,User>{}; for(user u :[select id,name,region__c,office__c from user where id in : caseOwnerIDs]){ owners.put(u.id,u); } for(case c1:trigger.new) { c1.agentregion__c = owners.get(c1.ownerid).region__c; c1.agentoffice__c = owners.get(c1.ownerid).office__c; } }

 

 

 

Wingsrul91Wingsrul91

Thank you very much Jim!  You have been such a great help.  Your code with my modification works in our sandbox.

Is there a quick and easy unit test that I can write in the Eclipse Force.com explorer just so I can deploy the trigger?

Is there another way to deploy triggers?  Since this trigger is pulling one-dimensional data (just a value of a picklist field on a user profile) you would hope that you would not have to write a complex test method in order to validate deployment. 

Any additional help would be greatly appreciated.

I have began by creating a Test Class but do not know how to proceed. 

 

private class TriggerTest {

static testMethod void runPositiveTestCases() {
// TO DO: implement unit test
}
}

 

 

 

JimRaeJimRae

Here is a great article on writing test methods.

 

http://wiki.apexdevnet.com/index.php/An_Introduction_to_Apex_Code_Test_Methods

 

Basically, you need to cause your trigger to fire by simulating the trigger condition on the object you are testing, then add enough conditions to cover all of the code.

 

In your case, you have a case trigger, with before insert and before update conditions, so you would need to try at least one insert case test and one update case test.

 

Use system.assert to check that your trigger has done what it was supposed to do.

I would use the system.runas   to create and insert your case object as a different user (than yourself), then once it is inserted, read the resulting record back out into a test case object, and then test the fields that are supposed to get updated to make sure they did.

 

@isTest private class TriggerTest { static testMethod void runPositiveTestCases() { String testcaseid=''; Case testcase = new Case(); //add all required field values here testcase.name=''; testcase.type=''; //get one user of proper type, use the where clause to filter user u=[select id,region__c,office__c from user where xx LIMIT 1 ] system.runas(u){ try{ insert testcase; testcaseid=testcase.id; //if created correctly }catch(DMLException e){ system.assert(false, 'Error occurred inserting test record:'+e.getDMLMessage(0)); } } Case checkcase = [select id,agentregion__c,agentoffice__c from case where id=:testcaseid]; //validate that the trigger updated the case correctly. system.assertequals(u.region__c,checkcase.agentregion__c); system.assertequals(u.office__c,checkcase.agentoffice__c); } }

 

 

 

Wingsrul91Wingsrul91

So this is what I have:

@isTest private class TriggerTest { static testMethod void runPositiveTestCases() { String testcaseid=''; Case testcase = new Case(); testcase.status='new'; testcase.origin='web'; testcase.description='test'; testcase.owner='00570000000sIeO'; testcase.owner_office_new__c='firmwide'; //get one user of proper type, use the where clause to filter user u=[select id,office__c from user where u LIMIT 1 ] system.runas(u){ try{ insert testcase; testcaseid=testcase.id; //if created correctly }catch(DMLException e){ system.assert(false, 'Error occurred inserting test record:'+e.getDMLMessage(0)); } } Case checkcase = [select id,Owner_office_new__c from case where id=:testcaseid]; //validate that the trigger updated the case correctly. system.assertequals(u.office__c,checkcase.Owner_Office_new__c); } }

I have created the required fields and inputed a value for each.

The trigger is based on the owner field and the owner_office_new__c field and in the above case, the owner office should be 'firmwide'

 

I am having difficulty with the following line:

 user u=[select id,office__c from user where u LIMIT 1 ]
I am not quite sure I understand what you mean by using the 'where' as a filter.

 

Thanks again!

JimRaeJimRae

Looks good.  My only thoughts would be, don't set the value for owner, wouldn't that be the creator, by default?  That is the purpose of using the runas, to simulate a user executing the new case insertion.  Also, don't put your office location in when you insert, if that is what the trigger is supposed to do.

 

The where clause in SOQL is how you filter results. Your goal will be to select a representitive user to run your test as.  You don't want to hard code the value for the user, as that isn't always portable between dev and production.

So, if you wanted to pick your test user that has a profile of "Sales User" for example, your SOQL would look like this:

 

user u=[select id,profileid, office__c from user where profile.name='Sales User' LIMIT 1 ]

 

 

 

Wingsrul91Wingsrul91

Now, I am receiving a Save error: unexpected token: system.runas.

Here is my code:

 

@isTest private class TriggerTest { static testMethod void runPositiveTestCases() { String testcaseid=''; Case testcase = new Case(); testcase.status='new'; testcase.origin='web'; testcase.description='test'; //get one user of proper type, use the where clause to filter user u=[select id,office__c from user where profile.name='Standard Gensler Agent' LIMIT 1 ] system.runas(u){ try{ insert testcase; testcaseid=testcase.id; //if created correctly }catch(DMLException e){ system.assert(false, 'Error occurred inserting test record:'+e.getDMLMessage(0)); } } Case checkcase = [select id,Owner_office_new__c from case where id=:testcaseid]; //validate that the trigger updated the case correctly. system.assertequals(u.office__c,checkcase.Owner_Office_new__c); } }

 Thoughts?

 

 

Wingsrul91Wingsrul91

Now, I am receiving a Save error: unexpected token: system.runas.

Here is my code:

 

@isTest
private class TriggerTest {

static testMethod void runPositiveTestCases() {
String testcaseid='';
Case testcase = new Case();
testcase.status='new';
testcase.origin='web';
testcase.description='test';
//get one user of proper type, use the where clause to filter
user u=[select id,office__c from user where profile.name='Standard Gensler Agent' LIMIT 1 ]
system.runas(u){
try{
insert testcase;
testcaseid=testcase.id; //if created correctly
}catch(DMLException e){
system.assert(false, 'Error occurred inserting test record:'+e.getDMLMessage(0));
}
}
Case checkcase = [select id,Owner_office_new__c from case where id=:testcaseid];

//validate that the trigger updated the case correctly.
system.assertequals(u.office__c,checkcase.Owner_Office_new__c);

}
}

 Thoughts?

 

 

JimRaeJimRae

You are missing a ; at the end of your user select line.

 

 

 

@isTest private class TriggerTest { static testMethod void runPositiveTestCases() { String testcaseid=''; Case testcase = new Case(); testcase.status='new'; testcase.origin='web'; testcase.description='test'; //get one user of proper type, use the where clause to filter user u=[select id,office__c from user where profile.name='Standard Gensler Agent' LIMIT 1 ]; system.runas(u){ try{ insert testcase; testcaseid=testcase.id; //if created correctly }catch(DMLException e){ system.assert(false, 'Error occurred inserting test record:'+e.getDMLMessage(0)); } } Case checkcase = [select id,Owner_office_new__c from case where id=:testcaseid]; //validate that the trigger updated the case correctly. system.assertequals(u.office__c,checkcase.Owner_Office_new__c); } }

 

 

 

Wingsrul91Wingsrul91

Here is a new issue:

 

The trigger works when the incident is changed to user, it successfully updates the owner office field.  But now a user cannot reassign the incident to a queue, it throws an exception:

 

Here is the current trigger:

trigger OwnerOffice on Case (before insert, before update) { Set<ID> caseOwnerIDs = new Set<ID>(); for(case c:trigger.new){ //get just the users if(String.valueOf(c.ownerid).substring(0,3)=='005'){ caseOwnerIDs.add(c.ownerid); } } Map <ID, User> owners = new Map<ID,User>{}; for(user u :[select id,name,office__c from user where id in : caseOwnerIDs]){ owners.put(u.id,u); } for(case c1:trigger.new) { c1.Owner_Office_new__c = owners.get(c1.ownerid).office__c; } }

All the queues begin with the ID: 00G

 

Thanks for your help.

JimRaeJimRae

You can use the same filter again to limit the updates to only cases that are not queue owned.

 

 

trigger OwnerOffice on Case (before insert, before update) { Set<ID> caseOwnerIDs = new Set<ID>(); for(case c:trigger.new){ //get just the users if(String.valueOf(c.ownerid).substring(0,3)=='005'){ caseOwnerIDs.add(c.ownerid); } } Map <ID, User> owners = new Map<ID,User>{}; for(user u :[select id,name,office__c from user where id in : caseOwnerIDs]){ owners.put(u.id,u); } for(case c1:trigger.new) { if(String.valueOf(c.ownerid).substring(0,3)=='005'){ c1.Owner_Office_new__c = owners.get(c1.ownerid).office__c; } } }

 

 

 

Wingsrul91Wingsrul91

Thank you very much, it works!  Here is the final code:

trigger OwnerOffice on Case (before insert, before update) { Set<ID> caseOwnerIDs = new Set<ID>(); for(case c:trigger.new){ //get just the users if(String.valueOf(c.ownerid).substring(0,3)=='005'){ caseOwnerIDs.add(c.ownerid); } } Map <ID, User> owners = new Map<ID,User>{}; for(user u :[select id,name,office__c from user where id in : caseOwnerIDs]){ owners.put(u.id,u); } for(case c1:trigger.new) { if(String.valueOf(c1.ownerid).substring(0,3)!='00G'){ c1.Owner_Offce_new__c = owners.get(c1.ownerid).office__c; } } }

 

It works great, throws no exceptions or anything.  I really appreciate your help!

One thing though.  When you change the case from a user to a queue, it keeps the previous users office.  Is there anyway to make it so that the owner office field on the case page layout equals null when going from a user to a queue?

JimRaeJimRae

That is a fairly simple change.

 

trigger OwnerOffice on Case (before insert, before update) { Set<ID> caseOwnerIDs = new Set<ID>(); for(case c:trigger.new){ //get just the users if(String.valueOf(c.ownerid).substring(0,3)=='005'){ caseOwnerIDs.add(c.ownerid); } } Map <ID, User> owners = new Map<ID,User>{}; for(user u :[select id,name,office__c from user where id in : caseOwnerIDs]){ owners.put(u.id,u); } for(case c1:trigger.new) { if(String.valueOf(c1.ownerid).substring(0,3)!='00G'){ c1.Owner_Offce_new__c = owners.get(c1.ownerid).office__c; }else{ c1.Owner_Offce_new__c = null;// might have to use '' instead of null } } }

 

 

 

Wingsrul91Wingsrul91

Now in the test class it is throwing an error.  Here is my test class:

@isTest private class TriggerTest { static testMethod void runPositiveTestCases() { String testcaseid=''; Case testcase = new Case(); testcase.status='new'; testcase.origin='web'; testcase.description='test'; //get one user of proper type, use the where clause to filter user u=[select id,office__c from user where user.name='adam' LIMIT 1 ]; system.runas(u){ try{ insert testcase; testcaseid=testcase.id; //if created correctly }catch(DMLException e){ system.assert(false, 'Error occurred inserting test record:'+e.getDMLMessage(0)); } } Case checkcase = [select id,Owner_office_new__c from case where id=:testcaseid]; //validate that the trigger updated the case correctly. system.assertequals(u.office__c,checkcase.Owner_Office_new__c); } }

 

The error is a system.exception: assertion failed: expected: firmwide (owner office), actual: null

 

How do I write in that null is an ok value?

Wingsrul91Wingsrul91

nevermind, I got it.

 

I just set the system assert to NOT equals

JimRaeJimRae

That is an odd error for your test case.  If your user (name=adam) is a valid user, then the trigger should be putting in an office name in the field.

That validation should work as written.

As I see the process of the testcase and trigger, the following steps occur:

 

TEST: create a case

TEST: insert the case as user u (name=adam)

TRG: Add the case owners ID to the  caseOwnersId Set

TRG: Query the User table for all users in the caseOwnersID set and get the office__c field

TRG: for each case (only one in the test) loop, and if the owner is not a queue (not a 00G) update the  Owner_office_new__c field

TRG: if it is a queue (ownerid is a 00G) the set the Owner_Office_new__c to null

TEST: SOQL query to extract the newly created test case

TEST: Make sure that the Owner_Office_new__c field equals the Office__c field of the test user (the assert)

 

If the assert is failing then either something is wrong with the trigger, or with the test user or with the test Case that is created.

 

 

Collaborative eCollaborative e

First, thank you for this thread. Your collaboration has a great deal of learning within it.

 

Second, I'm trying to do what I think is a similar thing but wanted your thoughts:

 

I need a custom field (lookup to a user) on the account record to change the Account Owner to the person who is chosen in my lookup. Would I do that with a similar trigger to what you built here?

 

Many thanks

 

Kevin Richardson

JimRaeJimRae

Why wouldn't you just use the Change Owner functionality already on the account record?  It has a built in User lookup.

 

Collaborative eCollaborative e
I need to create a field that my portal users can use to choose whom should own an account that would automatically update the standard Account Owner Field.
JimRaeJimRae

Makes sense.  Will your portal users have permission to change the owner?  Remember, the trigger will run as that user.

 

If so, the trigger would be fairly easy, depending on how many rules you wanted to implement.

 

A basic version would look like this:

 

 

trigger UpdateAccountOwner on Account (before update){ for(Account a:Trigger.New){ if(mycustOwner__c<>null){ //make sure the new owner field not blank a.ownerid=mycustOwner__c; } } }

 

 Other considerations you need to contemplate include, do you always want this to fire?  In the case of the sample, the trigger would fire no matter who updated the account, and no matter why or what the updated.

You might consider: 

  • Only firing when a portal user does the update 
  • Only firing on certain account record types
  • Adding logic not to change when the owner stays the same
  • Adding logic to block certain user roles from being assigned as owner.


Hope that gets you started.

 

 

Collaborative eCollaborative e
Can you give portal users access to change the owner? I'm not sure that's possible. If so does this shut down the idea all together?