+ Start a Discussion
Zhenyu ZhangZhenyu Zhang 

Salesforce trigger for multi picklists

Hi Masters,

I am new to Salesforce.

At the moment, I am trying to create a trigger.
In the code, there is one picklist in my lead, and then there is another exact same picklist in my task. When ther is log a call or new task coming in. The task's picklist value will be default to same as the lead's picklist value automacally.

However, the trigger that I created is not running. Is there anything wrong with what I did? Or I just made it too complicated?

Here is my trigger,
Trigger PrePopulateTaskPromoCode on Task (before insert) {
        
    List<Lead> leadsToUpdate = new List<Lead>();
    List<Task> tasks = new List<Task>();
    
    for (Task t : trigger.new) {
        if (String.valueOf(t.WhoId).substring(0,3) == '00Q') {
            Lead leadToLookup = [Select promo_code__c FROM Lead WHERE Id = :t.WhoId];
            String promoCode = leadToLookup.Promo_Code__c;
            t.Promo_Code__c = promoCode;
            tasks.add(t);
  
        }
    }
   // update tasks;
}


 
Best Answer chosen by Zhenyu Zhang
CyberJusCyberJus
In Salesforce there are governor limits that restrict how your code can run. One of those limits sets a maximum number of SOQL statements that can be run in a single transaction. Therefore you never want to execute SOQL within a loop because you never know how many records you may be processing in a trigger. Is is referred to as "bulkifying" your code. 

So for your code you want to loop through the trigger through the trigger items once, get all the ids that may be lead Ids, then run a query that will return a map to the Id, then loop again and pull items out of the map and apply it. 
 
Trigger PrePopulateTaskPromoCode on Task (before insert) {
         
    Set<Id> leadIds = new Set<Id>();
    for (Task t : trigger.new) {
        if (String.valueOf(t.WhoId).substring(0,3) == '00Q') {
            leadIds.add(t.WhoId);    
        }
    } 

    if (!leadIds.isEmpty()) { 

        Map<Id, Lead> leadMap = new Map<Id, Lead>([Select Id, promo_code__c FROM Lead WHERE Id in :leadIds])

         for (Task t : trigger.new) {
            if (String.valueOf(t.WhoId).substring(0,3) == '00Q') {
                 t.Promo_Code__c = leadMap.get(t.WhoId).Promo_Code__c;
            }
         } 
    }
 
}

Now, what do you mean it is "not running" I am sure the trigger is running, but is the value not set on your task after the record is created? 
 

All Answers

SlashApex (Luis Luciani)SlashApex (Luis Luciani)
Hi Zhenyu,

The trigger is probably running, but its not really doing anything. I made some edits and this should accomplish what you need.
 
Trigger PrePopulateTaskPromoCode on Task (before insert)
{
    //Get all the related leads up front. One query instead of several. 
    //Putting the results in a map for easy retrieval.
    Set<Id> leadIds = new Set<Id>();
    for (Task t : trigger.new)
    {
        if (String.valueOf(t.WhoId).substring(0,3) == '00Q')
            leadIds.add(t.WhoId);
    }

    if(leadIds.size() > 0)
    {
        Map<Id, Lead> relatedLeads = new Map<id, Lead>([SELECT id, promo_code__c FROM Lead WHERE id IN :leadIds]);
        
        for (Task t : trigger.new)
        {
            //Set the Task.Promo_Code__c field = to the Lead.Promo_Code__c if the task is related to a Lead
            if (!realatedLeads.containsKey(t.WhoId))
            {
                Lead leadToLookup = relatedLeads.get(t.WhoId);
                t.Promo_Code__c = leadToLookup.Promo_Code__c;  
            }
        }
    }
}

Try it out and let me know. I did not test this, so there might be a few compilation errors.
CyberJusCyberJus
In Salesforce there are governor limits that restrict how your code can run. One of those limits sets a maximum number of SOQL statements that can be run in a single transaction. Therefore you never want to execute SOQL within a loop because you never know how many records you may be processing in a trigger. Is is referred to as "bulkifying" your code. 

So for your code you want to loop through the trigger through the trigger items once, get all the ids that may be lead Ids, then run a query that will return a map to the Id, then loop again and pull items out of the map and apply it. 
 
Trigger PrePopulateTaskPromoCode on Task (before insert) {
         
    Set<Id> leadIds = new Set<Id>();
    for (Task t : trigger.new) {
        if (String.valueOf(t.WhoId).substring(0,3) == '00Q') {
            leadIds.add(t.WhoId);    
        }
    } 

    if (!leadIds.isEmpty()) { 

        Map<Id, Lead> leadMap = new Map<Id, Lead>([Select Id, promo_code__c FROM Lead WHERE Id in :leadIds])

         for (Task t : trigger.new) {
            if (String.valueOf(t.WhoId).substring(0,3) == '00Q') {
                 t.Promo_Code__c = leadMap.get(t.WhoId).Promo_Code__c;
            }
         } 
    }
 
}

Now, what do you mean it is "not running" I am sure the trigger is running, but is the value not set on your task after the record is created? 
 
This was selected as the best answer
Zhenyu ZhangZhenyu Zhang
Sorry about that my expression, I mean my trigger was not accomplishing anything. My task picklist was always empty. I think you are right, my original thought was every single time there is a new transaction coming in, the trigger will fire a query to lead for the value.
Zhenyu ZhangZhenyu Zhang
Thanks @Luis Luciani very much for the answer. It does what I want, SOQL is a bit more complicated than what I imagined. Both of the answera are correct, but I am gonna set @CyberJus 's as the best answer, his explaination gave me another answer.
Zhenyu ZhangZhenyu Zhang
@Luis Luciani, @CyberJus, My code coverage is not 100%...it is only 83%...does it affect the deployment?
SlashApex (Luis Luciani)SlashApex (Luis Luciani)
Hi Zhenyu,

83% is enough to deploy. The minimum is 75%. You should always try to strive for 100% though.

Looking at the code, I don't see how you could be at 83%. What lines are not covered?
CyberJusCyberJus
Do you mean 83% total or just for this trigger? Did you write a test class? All triggers need to have at least some test. If another test creates a task, it will at least partially cover this trigger. 
Zhenyu ZhangZhenyu Zhang
@Luis Luciani, @CyberJus,  Bingo, I was hoping to have that value populated as soon as user “Log a call” (Not after creating the call”). But this trigger seem to auto populate the it after the call has been created. Thank you guys, you are awesome. Learned a lot.