+ Start a Discussion
DavisTMDavisTM 

Apex trigger to copy a value from a custom field to a standard field

Hi,
I need some help... 

I have an idea, but am not sure how to accomplish it using an Apex trigger... 

User Action: Create a new Lead
Step #1: Enter the lead's first name, last name, email, etc.
Step #2: Select the comapny name from a custom lookup field
Step #3: Save the new lead.

Here's the issue, Company is a standard required field when creating a new Lead. I would like two things to happen during this action. The first is to check the Company field, and if it is blank, check if there is a value in the custom lookup field. If there in a value in the custom lookup field, then copy the value to the Company field. 

Can this be done using an Apex trigger? If so, can you show me how?

Thanks.
Best Answer chosen by DavisTM
Christan G 4Christan G 4
I apologize again for the late response. Please view my answers below:

1. I re-written the code more generically so that you can replace the API values with the values from your org. Please review the code below for reference: 

In Developer Console, go to File, hover over new and select Apex Class. Name the Apex Class, "LeadTriggerHelper" and click Ok. Afterwards, copy and paste this code:
 
public class LeadTriggerHelper {

    public static void updateCompanyName(List <Lead> leadList) {
        
        //Store all look up object records obtained via SOQL into a List.
        List <API_of_LookUpObject> lookUpObjList = [SELECT API_FIELDS FROM API_of_LookUpObject];

        //Replace All the API References in the statements above and below with the APIs 
        //from your org.
        //Replace API_FIELDS with all the fields you want returned in the query with a comma 
        //in-between each of them
        //Example: [SELECT Id, Name FROM Account]; 
        
//Create a Map to store the ID and Look Up Objects. Pass the list created above into it.
Map <Id, API_of_LookUpObject> lookUpObjMap = new Map <ID, API_of_LookUpObject>(lookUpObjList);
        
        //Iterate through Leadlist using for Loop
        for (Lead singLd : leadlist) {
            
            if (singLd.compName__c == null) {
                
                singLd.adderror('You must select a Look Up Record');

            }
                else if (singLd.compName__c != null) {    
                     
                singLd.Company = lookUpObjMap.get(singLd.compName__c).API_FIELD;
/* 1 - Replace API_FIELD in the statement above with the field you want from the Lookup Object.

    2 - Please note that the data type for this field has to be either a string or ID or else an error will occur.  
  
   3 - Also make sure that this API Name is also stated in the SOQL statement mentioned above after the word SELECT or else an error will occur
  */
            }
        } 
    }
}
After you make the API changes and save it, go to Developer Console, go to File and Hover over new and select Apex Trigger. Name it LeadTrigger and select Lead as the sObject.

Copy and paste the code below:
trigger LeadTrigger on Lead (before insert, before update) {

    LeadTriggerHelper.updateCompanyName(Trigger.new);
    
}

As a best practice, one should always put the code of a trigger within a separate Apex Class and just reference the method in the Apex Trigger. The code will be easier to manage just in case you decide to add more trigger methods in the future.

2 - I am not sure what you are referring to when you state user types. Lets say a user selects one of the look up records in the Look up field. Would you like for the standard Company field to state "Existing Company" rather than having it be replaced with the name of the custom company record? If that is what you meant, then yes that is possible. Minor updates would have to be made to the code.

3 - Only updates and insertions of a new or existing lead record will cause this trigger to execute. Thus, if the "+" is shown on lead records and a user clicks it to perform some type of change on the record, then this trigger will execute. However, if the "+" is shown only on the custom object, then it should not trigger a change to the lead records when used. To better understand, is the "+" a button? and where does it usually appear? A sample screenshot would be helpful to get a better understanding.

4 - That is correct. If a user does not have editing access, then they should not be able to utilize the "+" sign. Profiles of those you want to restrict access to should be read only. Use permission sets to give certain individuals access to it.

If you have any more questions or concerns, please feel free to reach out to me. I'm am more than happy to help!

All Answers

Abdul KhatriAbdul Khatri
Is Custom Lookup Field is coming from any other object or you just referecing it a lookup text field with Data type Text. If that is the case then this will only work in classic. In Lightning you may have issues.

Also what if that lookup is also blank or that will never happen. If that never happens than you can remove the check below. Also replace the <CustomLookupField> with the actual field. This may help you some understanding.
 
trigger LeadUpdateCompanyTrigger on Lead (before insert) {
    
    for(Lead lead : trigger.new) {
        
        if lead.<CustomLookupField> != null
	        lead.Company = lead.<CustomLookupField>
            
		//still get an issue here if the custom lookup field is blank so what should be done than            
    }

}

 
DavisTMDavisTM
Many Thanks Abdul...
 
If the user didnt select a value from the custom lookup field, then he/she has to type a value in the comapmy field. The company field is a standard, required field. This may be the reason why there is an error. Maybe a prompt telling the user they have to select a value from the lookup field.



 
Abdul KhatriAbdul Khatri
So are we good. Was my code helpful?
Andrew GAndrew G
A slight variation that may help
trigger LeadUpdateCompanyTrigger on Lead (before insert) {
    
    for(Lead lead : trigger.new) {
        if (lead.Company == null && lead.<CustomLookupField> != null){
            lead.Company = lead.<CustomLookupField>;
        } else if (lead.Company == null && lead.<CustomLookupField> == null) {
            lead.addError('Require either a Company or a customlookupfield');
        }
    }
}

HTH
Andrew
DavisTMDavisTM
Andrew,

Very clean code... I get the following error when I attempt to create the save the new Lead record;

These required fields must be completed: Company
DavisTMDavisTM
@Abdul Khatri,

I made the following changes to your code, and got the same error; "These required fields must be completed: Company"
 
trigger LeadUpdateCompanyTrigger on Lead (before insert) {
     
    for(Lead lead : trigger.new) {

        if (lead.CompName__c != null){

            lead.Company = lead.CompName__c;
             
        //still get an issue here if the custom lookup field is blank so what should be done than           
    }
  }
}

The value selected in the lookup field isn't being copied into the company field when I try to save the new lead record.
DavisTMDavisTM
is is possible the company name isnt getting populated because the company name in the custom lookup field is an ID and not the actual name? How would we get the name instead of the ID?
DavisTMDavisTM
Would the following work?
 
trigger UpdateLeadCompany on Lead (before insert) {

Set<Id> AN = New Set<Id>();

For (Lead LeadComp : Trigger.New) 
{
IF(LeadComp.compName__c != null) 
{
AN.add(LeadComp.compName__c);
 }
}

Map<Id, Agency__c> y = [SELECT Name, compName__c FROM Agency__c WHERE Id IN :AN];
For(Lead CompNameUpdate : Trigger.New)
{
Company a = y get(CompNameUpdate.compName__c);
IF(a != null)
{
CompNameUpdate = a.compName__c;
  }
 }
}

 
Andrew GAndrew G
AAAHHHHH - beginner error by me. 

The error is caused by the Order of Execution considerations:
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_triggers_order_of_execution.htm

Compulsory fields are assessed prior to the firing of the Before Insert triggers.  Steps 2 v 4.  

Not sure how to side-step that one.

Regards
Andrew
DavisTMDavisTM
Andrew,
So, this means I cant accomplish this action with an Apex Trigger, correct?
Andrew GAndrew G
Ok, apparently you can remove the requirement for Company if Person Accounts are enabled.
https://help.salesforce.com/articleView?id=000325733&type=1&mode=1

Where person accounts are not enabled, then an idea has been subbed to SF.  May need an additional vote.
https://success.salesforce.com/ideaView?id=08730000000km09AAA

Regards
Andrew
Andrew GAndrew G
Hi Davis, 
Yes.  The limitation / restriction / nature of the Order of Execution prevents you from solving this issue by use of a trigger.

By default, the Company is a required field and required to be on the layout, so you can't even remove it and create your own custom field.
You can't even set a default value. If that was the case, you would test for the default value with the trigger.

So the only way around I could think of would be ..... not a good solution ... but advised the User to put a default value (such as "new") and then test for that with the trigger.  But I can see that giving rise to poor data quality, which is what I think you are trying to improve/resolve with the trigger in the first place. 

Regards
Andrew
Andrew GAndrew G
Note, if the User were to put "new" in the Company field, it will satisfy the compulsory field test done by Salesforce.  
If we look at the order of execution, the trigger would fire, we could then put a Custom Validation on the Object, which test for "new" also.  
But, users would figure out that they could put "n", which means the SF validation succeeds.  If the custom validation is IF(Company = 'new', ......) throw error, then the user can bypass the validation and arise to poor data quality.

You could try something like the above, and then do user Training to emphasis the value of good data and how it helps them.  But we are now relying on human action to give good data, not validation of by use of code/formula/system. (if you know what I mean).

Perhaps, add a validation on the Company field that it has a length greater than 3 IF(LEN(Company) <3, throw error) .  I think i've rarely seen companies with less than 3 letters in the name.  

There's my thoughts.
Regards
Andrew
Christan G 4Christan G 4
Due to Salesforce's order of execution when a record is saved, I don't think you'll be able to bypass not having something within the Company field due to it being a required field. Before triggers are processed after system validation rules are executed. I assume that Salesforce has a default validation rule to ensures that all required fields have a value.

Unfortunately, in this case, I think the only way to bypass this issue would be to create your own custom Lead object. By doing this, you'll be able to create your own Company field and not make it required. This would resolve the issue you are having after creating the trigger.

In regards to your question, the ID of the company would populate. If, however, you wanted to get another field value from that record, you'll need the following: List, SOQL, Map <ID, LookUpObject>

Below is a code example:
public class LeadTriggerHelper {

    public static void updateCompanyName(List <Lead> leadList) {
        
        //Store all look up object records obtained via SOQL into a List.
        List <BDT__c> bdtList = [SELECT Id, Template_Name_BDT__c FROM BDT__c];
        
       //Create a Map to store the ID and Look Up Objects. Pass the list created above into it.
        Map <Id, BDT__c> bdtMap = new Map <ID, BDT__c>(bdtList);
        
        //Iterate through Trigger.new using for Loop
        for (Lead singLd : leadlist) {
            
            if (singLd.Company != null && singLd.BDT_Name__c != null) {
                
          //Here is where we'll use the Map we created.
          //We pass the ID of the Lookup object which we retrieve from singLd.BDT_Name__c.     
          //BDT_Name__c is the look up field on the lead record.
          //The Map uses the ID we pass to get the associate Look Up record.
          //From here we can access other values in the look up record which in this case is 
          //Template_Name_BDT__c.
          //Please note that IDs and Strings can populate in a text field (Company). 
          //Template_Name_BDT__c is a string.
          //If Template_Name_BDT__c was an integer or another incompatible datatype, an 
          //error would occur. To resolve this error, you would have to cast it into a string.
          //Cast Example 
          //(String)bdtMap.get(singLd.BDT_Name__c).Template_Name_BDT__c;
                     
          singLd.Company = bdtMap.get(singLd.BDT_Name__c).Template_Name_BDT__c;
                     
            }
        }
    }
}

If you have any questions, please feel free to comment. I hope my answer helped!

 
Andrew GAndrew G
Hi Davis.

The idea by Christan has merit.  Creating your own Custom Lead object would circumvent the issue you are experiencing.   I would just offer counsel to consider that path carefully, as if you create your own Custom Lead object, you would need to replicate all the other OOB functions related to Leads.  For example, the Conversion process, which creates the Account/Contact & possibly Opportunity,  would need to be rewritten.  Any OOB reports related to Leads would need to be recreated.

I'm not saying don't do a custom Lead object, just keep in mind that if you go down that path, other customisation will be required later.  

Christin's idea does give rise to another idea - create a custom LeadInput Object, which on save creates the Lead object in the background.  There would still be customisation, but you could still leverage the OOB functions for the Lead.  It would mean having an Object who's only purpose is input, as I would recommend deleting the entries after the Lead is succesfully created.  Or alternatively, (and i'm happy to be corrected by others who know Visualforce & Controllers better), perhaps have the custom LeadInput with a custom controller that validates the input and then creates the Lead record without saving the LeadInput object.  

You have some decisions to make.

Hope the above has helped.

And thanks to Christan for the alternate view on a solution.

Regards
Andrew
Christan G 4Christan G 4
Great Points Andrew! As for your idea regarding a Lead Input object, I don't think creating one would be necessary since there are a lot of simpler alternatives. One can simply create a screen flow to fulfill this requirement. Currently, I am in the process of completing the Lightning Flow Trailhead module and in the third lesson, the practice exercise achieves something very similar to what you mentioned using a Flow. The use of Process Builders and Flows is game changing when it comes to what one can do through declarative automation tools today. Most of the use cases of Apex Triggers can now be achieved through a combination of these few lightning features. GUIs are slowly taken over lol. With that being said, I suggest all to do the Lightning Flow Trailhead module when possible.
DavisTMDavisTM
Hi Christian,
I did the trailmix. It's amazing what can be accomplished with Flow & Process Builder. The challenge her is the field is a standard and required field. This field type seems to pose some challenges. Apprently in the next release, Flows will have the capability to update these field types. I will review the module you referred to. Maybe I am forgetting something...

Andrew,
Many thanks for your recommendations and input! I learned a lot from this interaction. I don't really want to go the route of creating a new object and doing cross object relationships, etc. I think its too much effort and complexity for this scenario. Your statement;
"But I can see that giving rise to poor data quality, which is what I think you are trying to improve/resolve with the trigger in the first place." is true. This is my goal.

Christian,
I see how your solution would work if the field was type was different, or I created a custom Lead Object. I don't want to create a custom object and have to maintain the complexities which will will follow....

I have some thinking to do, and decisions to make...
Christan G 4Christan G 4
No Problem! I am glad that I was able to provide some insight. A great tool that helps improve data quality is a Validation Rule. I was actually trying to see if there was a way to apply this to your scenario as potential workaround.
Christan G 4Christan G 4
Omg I think I found a solution after experimenting more with the Apex Trigger. Assuming that a user would have to always look up a record, you can make it a requirement for them to select a record via the look up field when creating a new Lead. The user could type in anything that they want in the company field. When the record is being saved, the system will overwrite whatever the user typed in the Company name with the look up record Name. Here is the code I have written for it. I think this is the closest you are going to get to what you want to achieve. It literally just requires the user to type an anything for the company name when creating the record. It will always get overwritten.
public class LeadTriggerHelper {

    public static void updateCompanyName(List <Lead> leadList) {
        
        //Store all look up object records obtained via SOQL into a List.
        List <BDT__c> bdtList = [SELECT Id, Template_Name_BDT__c FROM BDT__c];
        
        //Create a Map to store the ID and Look Up Objects. Pass the list created above into it.
        Map <Id, BDT__c> bdtMap = new Map <ID, BDT__c>(bdtList);
        
        //Iterate through Trigger.new using for Loop
        for (Lead singLd : leadlist) {
            
            if (singLd.BDT_Name__c == null) {
                
                singLd.adderror('You must select a Look Up Record');
            }
                else if (singLd.BDT_Name__c != null) {    
                     
                singLd.Company = bdtMap.get(singLd.BDT_Name__c).Template_Name_BDT__c;
                     
            }
  
        }

    }
    
}

 
DavisTMDavisTM
Christian,
I am as excited as you are!! A few questions before I give it a try...
  1. The two fields on the lead object which I am working with are the custom field, i.e. compName__c and the standard field, i.e Company. Where would I place these in the code above?
  2. Is it possible to restrict the entry in the user types in the Company field to "Existing Company"?
  3. The lookup field, i.e. compName, allows the addition of a new company by clicking the "+" sign. Will the code affect this in any way?
  4. Am I correct in stating the following?;  If a user does not have write acess on the custom lookup field, i.e. compName__c, they will not be able to create a new company by clicking the "+" sign; It will not be an option.
Christan G 4Christan G 4
Hey Davis! I just saw your comment. I am happy you are excited! I am currently traveling home. I will reply once I get inside in about 2 hours. So sorry for the late reply.
DavisTMDavisTM
Christian,
I'll keep an eye out for your response... 
Christan G 4Christan G 4
I apologize again for the late response. Please view my answers below:

1. I re-written the code more generically so that you can replace the API values with the values from your org. Please review the code below for reference: 

In Developer Console, go to File, hover over new and select Apex Class. Name the Apex Class, "LeadTriggerHelper" and click Ok. Afterwards, copy and paste this code:
 
public class LeadTriggerHelper {

    public static void updateCompanyName(List <Lead> leadList) {
        
        //Store all look up object records obtained via SOQL into a List.
        List <API_of_LookUpObject> lookUpObjList = [SELECT API_FIELDS FROM API_of_LookUpObject];

        //Replace All the API References in the statements above and below with the APIs 
        //from your org.
        //Replace API_FIELDS with all the fields you want returned in the query with a comma 
        //in-between each of them
        //Example: [SELECT Id, Name FROM Account]; 
        
//Create a Map to store the ID and Look Up Objects. Pass the list created above into it.
Map <Id, API_of_LookUpObject> lookUpObjMap = new Map <ID, API_of_LookUpObject>(lookUpObjList);
        
        //Iterate through Leadlist using for Loop
        for (Lead singLd : leadlist) {
            
            if (singLd.compName__c == null) {
                
                singLd.adderror('You must select a Look Up Record');

            }
                else if (singLd.compName__c != null) {    
                     
                singLd.Company = lookUpObjMap.get(singLd.compName__c).API_FIELD;
/* 1 - Replace API_FIELD in the statement above with the field you want from the Lookup Object.

    2 - Please note that the data type for this field has to be either a string or ID or else an error will occur.  
  
   3 - Also make sure that this API Name is also stated in the SOQL statement mentioned above after the word SELECT or else an error will occur
  */
            }
        } 
    }
}
After you make the API changes and save it, go to Developer Console, go to File and Hover over new and select Apex Trigger. Name it LeadTrigger and select Lead as the sObject.

Copy and paste the code below:
trigger LeadTrigger on Lead (before insert, before update) {

    LeadTriggerHelper.updateCompanyName(Trigger.new);
    
}

As a best practice, one should always put the code of a trigger within a separate Apex Class and just reference the method in the Apex Trigger. The code will be easier to manage just in case you decide to add more trigger methods in the future.

2 - I am not sure what you are referring to when you state user types. Lets say a user selects one of the look up records in the Look up field. Would you like for the standard Company field to state "Existing Company" rather than having it be replaced with the name of the custom company record? If that is what you meant, then yes that is possible. Minor updates would have to be made to the code.

3 - Only updates and insertions of a new or existing lead record will cause this trigger to execute. Thus, if the "+" is shown on lead records and a user clicks it to perform some type of change on the record, then this trigger will execute. However, if the "+" is shown only on the custom object, then it should not trigger a change to the lead records when used. To better understand, is the "+" a button? and where does it usually appear? A sample screenshot would be helpful to get a better understanding.

4 - That is correct. If a user does not have editing access, then they should not be able to utilize the "+" sign. Profiles of those you want to restrict access to should be read only. Use permission sets to give certain individuals access to it.

If you have any more questions or concerns, please feel free to reach out to me. I'm am more than happy to help!
This was selected as the best answer
DavisTMDavisTM
Hi Christian, Please disregard the "user types' statement.

In regards to question #2: In short, disregard.

In regrads to question #3: I only need this to value from the lookup field to replace the value in the Company field before insert. As a result, should I remove before update, OR is before update required for this to work?
DavisTMDavisTM
One more note in regards to #3, the "+" sign shows up on the custom lookup field after the use clicks in the area to search for a company name. I can grant access as needed.
DavisTMDavisTM
Christian,
This is my edit of the general code you provided. I am not certain its 100% correct. Perhaps you could do a quick review to confirm?
public class LeadTriggerHelper {

    		public static void updateCompanyName(List <Lead> leadList) {

//Store all look up object records obtained via SOQL into a List.

        	List Account lookUpObjList = [SELECT Id, Name FROM Account];

//Replace All the API References in the statements above and below with the APIs from your org.

//Replace API_FIELDS with all the fields you want returned in the query with a comma in-between each of them

//Example: [SELECT Id, Name FROM Account];

//Create a Map to store the ID and Look Up Objects. Pass the list created above into it.

		Map <Id, compName__c> lookUpObjMap = new Map <Id, Account>(lookUpObjList);
       
//Iterate through Leadlist using for Loop

        	for (Lead singLd : leadlist) {

           		if (singLd.compName__c == null) {
                 
                	singLd.adderror('You must select a Look Up Record');
            	}
               	 	else if (singLd.compName__c != null) {   
                      
                	singLd.Company = lookUpObjMap.get(singLd.compName__c).Name;

/* 1 - Replace API_FIELD in the statement above with the field you want from the Lookup Object.

   2 - Please note that the data type for this field has to be either a string or ID or else an error will occur.

   3 - Also make sure that this API Name is also stated in the SOQL statement mentioned above after the word SELECT or else an error will occur */
            }
        }
    }
}


 
Christan G 4Christan G 4
Hi David! 

In regards to your code, please make the following changes:

Line 07: Please < > around Account after List. (It should look like this: List <Account> lookUpObjList = [SELECT Id, Name FROM Account];)
Line 17: Replace <Id, compName__c> with <Id, Account>

Besides that, everything else looks good to go.

Regarding, #3: You can remove the "before update" phrase from the Apex. Please note though by doing this, this trigger will only execute once which will be before the record is saved to the database rather than each time that record is update.

One thing to keep in mind for future. Lets say after the lead is created, the name of the account changes at some point in the future. When the name of the Account changes, please note that lead record related to that account will still showcase the old name and not the new updated name. If you would like for this to occurr, we would have to create a separate trigger for this on the Account object to update the related lead record. If you don't think this scenario will ever occur, then you can disregard this comment. I just wanted to make sure that you were aware of this possible scenario.
DavisTMDavisTM
Christan!... It works!!
The scenario you describe may occur. So, I will need to add a separate trigger on the Account object. If the Company name changes before the lead is converted, then the update will occur; I like the way you think... 
Christan G 4Christan G 4
Hi Davis! I am so glad that I was able to resolve your issue! If you plan to deploy this code to your Production org, please note that you will have to write a test class for it. All Apex code must have a test coverage of at least 75% before it can be deployed to a Production org. 

I can write a test Apex class if you like. I can also write a trigger for the future scenario I mentioned as well if you think it is needed. Enjoy!
DavisTMDavisTM
Christan,
I would greatly appreciate the test class and the trigger for the future scenario. I assume the future scenario will need a test class as well... Is there a way I can direct message you?
Christan G 4Christan G 4
Awesome! I should have the test class and trigger done by this Friday the latest. In terms of direct messaging, I would be happy to. I am just not sure what would the best way in terms of communication without portraying too much sensitive data on here. I am guessing e-mail unless you have another suggestion. I wish this site had a direct message feature. Hopefully, Salesforce updates it soon.