+ Start a Discussion
mgodseymgodsey 

Trigger to update multi-select picklist

I'm writing a trigger to update a multi-select picklist. However, I'm having trouble adding more than one value to the field.

 

Here's the scenario - we have a number of fields on the record that may or may not be null. They are also all associated with different reasons, which make up the values of the ChangeTag__c multi-select picklist field. If the fields are NOT null, then the corresponding reason needs to be selected in ChangeTag__c.

 

One record can have any given number of reasons. Below is a portion of the code that I wrote:

 

//(4) update Change Tag field based on new field values
for(integer i=0; i<ChangeRequestUpdateList.size();i++){
if(dd.RFP_Template__c!=null){dd.ChangeTag__c='Terms & Conditions';}
if(dd.Legal_Terms__c!=null){dd.ChangeTag__c='Terms & Conditions';}
if(dd.Ad_Server__c!=null){dd.ChangeTag__c='Ad Server Change';}
if(dd.ReTargeting__c!=null){dd.ChangeTag__c='ReTargeting Change';}
}
update ChangeRequestUpdateList;
}
}

 

The issue is that if there are multiple fields that aren't null, only one reason shows up in ChangeTag__c. For example, if RFP_Templace__c!=null and Ad_Server__c!=null, the only value that gets updated to ChangeTag__c is 'Ad Server Change.' Any ideas on how to write the code so that both 'Terms & 'Conditions' and 'Ad Server Change' are selected? There are way more fields and reasons than what is included above, so it would be nearly impossible to write out every combination.

 

Thank you so much!

 

Best Answer chosen by Admin (Salesforce Developers) 
SamuelDeRyckeSamuelDeRycke

I'm stunned your post has 19 views and no one replied yet. The start of your trigger is excellent. But there are some things you need to stay focused on when programming (not specific to apex really).

 

//(3) get Change Request Fields
    List < Deal_Desk_Form__c > ChangeRequestUpdateList = [SELECT Id,  * a LOT of fields  * 
                                                        FROM Deal_Desk_Form__c
                                                        WHERE Id in : ChangeRequestIDs];

Map < ID, Deal_Desk_Form__c > ddMap = new Map < Id, Deal_Desk_Form__c > (); // is there a reason why you're using a map as collection type ? you're only colleccting your objects to do a bulk update, a list collection will do.
for (Deal_Desk_Form__c dd: ChangeRequestUpdateList) { //iterating your result records good, you're not using the changeRequestupdateList for anything else so you could use an optimized for-sql statement here.
ddMap.put(dd.Id, dd); //(4) update Change Tag field based on new field values for (integer i = 0; i < ChangeRequestUpdateList.size(); i++) { //why are you doing ANOTHER iteration over the objects you're already iterating over ? If you're using the structure of example code, analyse which parts you need to use, and which are redundant. Iterating inside an iteration is something we should always try to avoid. If you'd have 20 records that need to be updated, you'd process every record 20 times!
dd.ChangeTag__c = ' '; if (dd.RFP_Template__c != null || dd.Legal_Terms__c != null) { dd.ChangeTag__c += ';Terms & Conditions '; } if (dd.Ad_Server__c != null) { dd.ChangeTag__c += ';Ad Server Change '; } if (dd.ReTargeting__c != null) { dd.ChangeTag__c += ';ReTargeting Change '; } update ChangeRequestUpdateList; //never do dml (insert,update,delete) statements in an iteration ! Mostly for resource/efficiency purposes, this is why this will hit governor limits, they enforce us to code efficiently. } }

 

//List<Deal_Desk_Form__c> ChangeRequestUpdateList = [soql statement];
//Map<ID,Deal_Desk_Form__c> ddMap = new Map<Id,Deal_Desk_Form__c<();

List<Deal_Desk_Form__c> lstDDF = new List<Deal_Desk_Form__c>();
for (Deal_Desk_Form__c dd:[SELECT Id, * a LOT of fields * FROM Deal_Desk_Form__c WHERE Id in : ChangeRequestIDs]) {
//ddMap.put(dd.Id,dd);
//(4) update Change Tag field based on new field values
//for(integer i = 0; i<ChangeRequestUpdateList.size(); i++){
dd.ChangeTag__c = ' ';
if (dd.RFP_Template__c != null || dd.Legal_Terms__c != null) {
dd.ChangeTag__c += ';Terms & Conditions ';
} if (dd.Ad_Server__c != null) {
dd.ChangeTag__c += ';Ad Server Change ';
} if (dd.ReTargeting__c != null) {
dd.ChangeTag__c += ';ReTargeting Change ';
}
lstDDF.add(dd);
}
update lstDDF; //outside the iteration
//}

 

 

 

.

All Answers

mgodseymgodsey

Update - I was able to set it up so that it adds the values to the picklist, but no matter what it also always adds "null". For example, it will say "Terms & Conditions, Ad Server Change, ReTargeting Change, null." Any ideas way? Any help is greatly appreciated!

 

Updated Code:

//(4) update Change Tag field based on new field values
    for(integer i=0; i<ChangeRequestUpdateList.size();i++){                                                
        if(dd.RFP_Template__c!=null||dd.Legal_Terms__c!=null){dd.ChangeTag__c += ';Terms & Conditions ';}
        if(dd.Ad_Server__c!=null){dd.ChangeTag__c +=';Ad Server Change ';}
        if(dd.ReTargeting__c!=null){dd.ChangeTag__c +=';ReTargeting Change ';}
       
update ChangeRequestUpdateList;
}
}
}

 

SamuelDeRyckeSamuelDeRycke

Just a guess, I assume that when no value is picked, the default string value of the multipicklist may be null. You're using +=, which may add to that null, instead of replace the default null value.

 

You could use = or += depneding on conditionally checking the ChangeTag__c value.

 

 

Mind posting some more of your code ? I don't understand why you're doing an iteration but operating on objects outside your iteration scope.

 

 

 

mgodseymgodsey

Yes, you're exactly right - it's because the default string value of hte multipicklist is null. Below is the rest of the code. It's now working, but I'm new to apex so there may be a better strategy for achieving the same result? All of the updates are happening on the same object though.

 

trigger ChangeRequestTagCPM on Deal_Desk_Form__c (after update) {

//(1) define variable for list of all ChangeRequestIDs (2) that are approved
List<Id> ChangeRequestIDs = new List<ID>();
      for(Deal_Desk_Form__c dd: trigger.new){
      if(dd.approvedParentDealDesk__c!=null && dd.Approval_Status__c=='Approved'&& dd.ChangeTag__c==null){
          ChangeRequestIDs.add(dd.id);
          }
 }
//(3) get Change Request Fields
List<Deal_Desk_Form__c> ChangeRequestUpdateList=[SELECT Id,Ad_Server__c, QC_Ad_Serving_Expense__c, QC_Ad_Verification_Expense__c,
                                            Budget_Allocation__c, Notes__c, Advertiser_Budget_1__c, Advertiser_Budget_2__c, Advertiser_Budget_3__c,ChangeTag__c,
                                            Click_Attribution__c, Click_Window__c, Content_Exclusions__c, Model_1_CPM__c, Model_2_CPM__c, Model_3_CPM__c,
                                            Advertiser_Data_Budget_1__c, Advertiser_Data_Budget_2__c, Advertiser_Data_Budget_3__c, 
                                            Model_1_Data_CPM__c, Model_2_Data_CPM__c, Model_3_Data_CPM__c, Data_Passback__c, DOW__c,
                                            Day_Parting__c, FCap_perday__c, Geo_Targeting__c, Geo_Targeting_Criteria__c,Media_Type_1__c, Media_Type_2__c, Media_Type_3__c,
                                            Model_1_Name__c, Model_2_Name__c, Model_3_Name__c, Model_1_Attributes__c, Model_2_Attributes__c, Model_3_Attributes__c,
                                            Primary_Key_Success_Metric__c, Secondary_Key_Success_Metric__c, ReTargeting__c, Survey__c, QC_Survey_Expense__c, RFP_Template__c,
                                            Legal_Terms__c, Value_Add_Impressions_Requested__c, Value_add_impressions_or__c, View_Attribution__c,View_Window__c 
                                                FROM Deal_Desk_Form__c
                                                WHERE Id in: ChangeRequestIDs];
                                                
Map<ID,Deal_Desk_Form__c> ddMap = new Map<Id,Deal_Desk_Form__c>();
    for(Deal_Desk_Form__c dd:ChangeRequestUpdateList){
    ddMap.put(dd.Id,dd);
    
//(4) update Change Tag field based on new field values
    for(integer i=0; i<ChangeRequestUpdateList.size();i++){
        dd.ChangeTag__c=' ';                                                
        if(dd.RFP_Template__c!=null||dd.Legal_Terms__c!=null){dd.ChangeTag__c += ';Terms & Conditions ';}
        if(dd.Ad_Server__c!=null){dd.ChangeTag__c +=';Ad Server Change ';}
        if(dd.ReTargeting__c!=null){dd.ChangeTag__c +=';ReTargeting Change ';}
        
        
        update ChangeRequestUpdateList;
}
}
}

 

SamuelDeRyckeSamuelDeRycke

I'm stunned your post has 19 views and no one replied yet. The start of your trigger is excellent. But there are some things you need to stay focused on when programming (not specific to apex really).

 

//(3) get Change Request Fields
    List < Deal_Desk_Form__c > ChangeRequestUpdateList = [SELECT Id,  * a LOT of fields  * 
                                                        FROM Deal_Desk_Form__c
                                                        WHERE Id in : ChangeRequestIDs];

Map < ID, Deal_Desk_Form__c > ddMap = new Map < Id, Deal_Desk_Form__c > (); // is there a reason why you're using a map as collection type ? you're only colleccting your objects to do a bulk update, a list collection will do.
for (Deal_Desk_Form__c dd: ChangeRequestUpdateList) { //iterating your result records good, you're not using the changeRequestupdateList for anything else so you could use an optimized for-sql statement here.
ddMap.put(dd.Id, dd); //(4) update Change Tag field based on new field values for (integer i = 0; i < ChangeRequestUpdateList.size(); i++) { //why are you doing ANOTHER iteration over the objects you're already iterating over ? If you're using the structure of example code, analyse which parts you need to use, and which are redundant. Iterating inside an iteration is something we should always try to avoid. If you'd have 20 records that need to be updated, you'd process every record 20 times!
dd.ChangeTag__c = ' '; if (dd.RFP_Template__c != null || dd.Legal_Terms__c != null) { dd.ChangeTag__c += ';Terms & Conditions '; } if (dd.Ad_Server__c != null) { dd.ChangeTag__c += ';Ad Server Change '; } if (dd.ReTargeting__c != null) { dd.ChangeTag__c += ';ReTargeting Change '; } update ChangeRequestUpdateList; //never do dml (insert,update,delete) statements in an iteration ! Mostly for resource/efficiency purposes, this is why this will hit governor limits, they enforce us to code efficiently. } }

 

//List<Deal_Desk_Form__c> ChangeRequestUpdateList = [soql statement];
//Map<ID,Deal_Desk_Form__c> ddMap = new Map<Id,Deal_Desk_Form__c<();

List<Deal_Desk_Form__c> lstDDF = new List<Deal_Desk_Form__c>();
for (Deal_Desk_Form__c dd:[SELECT Id, * a LOT of fields * FROM Deal_Desk_Form__c WHERE Id in : ChangeRequestIDs]) {
//ddMap.put(dd.Id,dd);
//(4) update Change Tag field based on new field values
//for(integer i = 0; i<ChangeRequestUpdateList.size(); i++){
dd.ChangeTag__c = ' ';
if (dd.RFP_Template__c != null || dd.Legal_Terms__c != null) {
dd.ChangeTag__c += ';Terms & Conditions ';
} if (dd.Ad_Server__c != null) {
dd.ChangeTag__c += ';Ad Server Change ';
} if (dd.ReTargeting__c != null) {
dd.ChangeTag__c += ';ReTargeting Change ';
}
lstDDF.add(dd);
}
update lstDDF; //outside the iteration
//}

 

 

 

.

This was selected as the best answer
mgodseymgodsey

Hi Sdry - Thank you SO much for your comments. As someone is just learning Apex (with no prior coding experience), I really appreciate the guidance. 

 

I tried following your advice below, but I'm getting an error message. When I try to save, it says "Compile Error: unexpected toekn: 'List' at line 10 column 0."  Line 10 is where my second list (1st DDF) begins - I'm assuming I entered it correctly, but do you see where I went wrong?

 

trigger ChangeRequestTagCPMv2 on Deal_Desk_Form__c (after update) {

//(1) define variable for list of all ChangeRequestIDs (2) that are approved
List<Id> ChangeRequestIDs = new List<ID>();
      for(Deal_Desk_Form__c dd: trigger.new){
      if(dd.approvedParentDealDesk__c!=null && dd.Approval_Status__c=='Approved'&& dd.ChangeTag__c==null){
          ChangeRequestIDs.add(dd.id);
          }
}          
List<Deal_Desk_Form__c> 1stDDF = new List<Deal_Desk_Form__c>();
for(Deal_Desk_Form__c dd: [SELECT Id,Ad_Server__c, QC_Ad_Serving_Expense__c, QC_Ad_Verification_Expense__c,
                                            Budget_Allocation__c, Notes__c, Advertiser_Budget_1__c, Advertiser_Budget_2__c, Advertiser_Budget_3__c,ChangeTag__c,
                                            Click_Attribution__c, Click_Window__c, Content_Exclusions__c, Model_1_CPM__c, Model_2_CPM__c, Model_3_CPM__c,
                                            Advertiser_Data_Budget_1__c, Advertiser_Data_Budget_2__c, Advertiser_Data_Budget_3__c, 
                                            Model_1_Data_CPM__c, Model_2_Data_CPM__c, Model_3_Data_CPM__c, Data_Passback__c, DOW__c,
                                            Day_Parting__c, FCap_perday__c, Geo_Targeting__c, Geo_Targeting_Criteria__c,Media_Type_1__c, Media_Type_2__c, Media_Type_3__c,
                                            Model_1_Name__c, Model_2_Name__c, Model_3_Name__c, Model_1_Attributes__c, Model_2_Attributes__c, Model_3_Attributes__c,
                                            Primary_Key_Success_Metric__c, Secondary_Key_Success_Metric__c, ReTargeting__c, Survey__c, QC_Survey_Expense__c, RFP_Template__c,
                                            Legal_Terms__c, Value_Add_Impressions_Requested__c, Value_add_impressions_or__c, View_Attribution__c,View_Window__c 
                                                FROM Deal_Desk_Form__c
                                                WHERE Id in: ChangeRequestIDs]) {
                                                                                             
        if(dd.RFP_Template__c!=null||dd.Legal_Terms__c!=null){dd.ChangeTag__c += ';Terms & Conditions ';}
        if(dd.Ad_Server__c!=null){dd.ChangeTag__c +=';Ad Server Change ';}
        if(dd.ReTargeting__c!=null){dd.ChangeTag__c +=';ReTargeting Change ';}
        if(dd.Geo_Targeting__c!=null || dd.Geo_Targeting_Criteria__c!=null){dd.ChangeTag__c +=';Geo Targeting Change';}
        dd.ChangeTag__c = dd.ChangeTag__c.replaceAll(';?null;?','');
        }
        1stDDF.add(dd);
}
update 1stDDF;

 

SamuelDeRyckeSamuelDeRycke

Makst 1stDDF  firstDDF. Can't start a variable/method/class/property with a numeric caracter.

 

 

mgodseymgodsey

Hi Sdry - Thank you so much for your help. I had to add in a few brackets to make it save properly, but I believe I still kept the dml statement outside of the iteration. The bracket after the update command closes the first bracket in the trigger, not the list iteration.

 

Below is the final code - if you could let me know if there are still any issues, it would be much appreciated!! I'm really trying to become more efficient and learn to write solid code, and this has been very helpful.

 

trigger ChangeRequestTagCPMv2 on Deal_Desk_Form__c (after update) {

//(1) define variable for list of all ChangeRequestIDs (2) that are approved
List<Id> ChangeRequestIDs = new List<ID>();
      for(Deal_Desk_Form__c dd: trigger.new){
      if(dd.approvedParentDealDesk__c!=null && dd.Approval_Status__c=='Approved'&& dd.ChangeTag__c==null){
          ChangeRequestIDs.add(dd.id);
          }
}  
//(3) get ids and fields for approved change request forms       
List<Deal_Desk_Form__c> approvedchangerequests = new List<Deal_Desk_Form__c>();
for(Deal_Desk_Form__c dd: [SELECT Id,Ad_Server__c, QC_Ad_Serving_Expense__c, QC_Ad_Verification_Expense__c,
                                            Budget_Allocation__c, Notes__c, Advertiser_Budget_1__c, Advertiser_Budget_2__c, Advertiser_Budget_3__c,ChangeTag__c,
                                            Click_Attribution__c, Click_Window__c, Content_Exclusions__c, Model_1_CPM__c, Model_2_CPM__c, Model_3_CPM__c,
                                            Advertiser_Data_Budget_1__c, Advertiser_Data_Budget_2__c, Advertiser_Data_Budget_3__c,
                                            Model_1_Data_CPM__c, Model_2_Data_CPM__c, Model_3_Data_CPM__c, Data_Passback__c, DOW__c,
                                            Day_Parting__c, FCap_perday__c, Geo_Targeting__c, Geo_Targeting_Criteria__c,Media_Type_1__c, Media_Type_2__c, Media_Type_3__c,
                                            Model_1_Name__c, Model_2_Name__c, Model_3_Name__c, Model_1_Attributes__c, Model_2_Attributes__c, Model_3_Attributes__c,
                                            Primary_Key_Success_Metric__c, Secondary_Key_Success_Metric__c, ReTargeting__c, Survey__c, QC_Survey_Expense__c, RFP_Template__c,
                                            Legal_Terms__c, Value_Add_Impressions_Requested__c, Value_add_impressions_or__c, View_Attribution__c,View_Window__c
                                                FROM Deal_Desk_Form__c
                                                WHERE Id in: ChangeRequestIDs]) {
                                                
//(4) select Change Tag reasons based on fields completed on change request form                                                                                            
        if(dd.RFP_Template__c!=null||dd.Legal_Terms__c!=null){dd.ChangeTag__c += ';Terms & Conditions ';}
        if(dd.Ad_Server__c!=null){dd.ChangeTag__c +=';Ad Server Change ';}
        if(dd.ReTargeting__c!=null){dd.ChangeTag__c +=';ReTargeting Change ';}
        if(dd.Geo_Targeting__c!=null || dd.Geo_Targeting_Criteria__c!=null){dd.ChangeTag__c +=';Geo Targeting Change';}
        if(dd.Click_Attribution__c!=null || dd.Click_Window__c!=null || dd.View_Window__c!=null || dd.View_Attribution__c !=null)
            {dd.ChangeTag__c +='; Attribution Change';}
        if(dd.QC_Ad_Serving_Expense__c!=null || dd.QC_Ad_Verification_Expense__c!=null || dd.Survey__c!=null || dd.QC_Survey_Expense__c!=null || 
           dd.Value_Add_Impressions_Requested__c!=null ||dd.Value_add_impressions_or__c!=null){dd.ChangeTag__c += ';Fee Absorption Change';}
        if(dd.Content_Exclusions__c!=null){dd.ChangeTag__c+=';Brand Safety Change ';} 
        if(dd.FCap_perday__c!=null || dd.Day_Parting__c!=null ||dd.DOW__c!=null){dd.ChangeTag__c +=';Delivery Restrictions Change ';}
        if(dd.Budget_Allocation__c!=null || dd.Notes__c!=null){dd.ChangeTag__c +=';Budget Segments Change';}
        if(dd.Model_1_Attributes__c!=null || dd.Model_2_Attributes__c!=null || dd.Model_3_Attributes__c!=null){dd.ChangeTag__c +='; Campaign Attributes Change';}
        if(dd.Media_Type_1__c!=null || dd.Media_Type_2__c!=null || dd.Media_Type_3__c!=null){dd.ChangeTag__c +=';Creative Type Change';}
        if(dd.Model_1_CPM__c!=null || dd.Model_2_CPM__c!=null || dd.Model_3_CPM__c!=null){dd.ChangeTag__c +=';CPM Rate Change';}
        if(dd.Advertiser_Budget_1__c > dd.approvedCampaignBudget1__c){dd.ChangeTag__c +=';Budget Change - Incremental';}
        dd.ChangeTag__c = dd.ChangeTag__c.replaceAll(';?null;?','');
        
        approvedchangerequests.add(dd);
        
//(5) update Change Tag field on approved change request forms
}
update approvedchangerequests;
}

 

 

SamuelDeRyckeSamuelDeRycke

It looks good to me. However, to garantee the code runs as expected, and will continue to do so in the future (or that we are alerted if not) the force.com platform demands us to write test code for our triggers. You will need 75%of your code covered by test codes in production to add anything else.

 

If you haven't done so before, these are worth reading, and a good start off point.

http://wiki.developerforce.com/page/An_Introduction_to_Apex_Code_Test_Methods

http://www.salesforce.com/us/developer/docs/apexcode/index_Left.htm#CSHID=apex_testing_intro.htm|StartTopic=Content%2Fapex_testing_intro.htm|SkinName=webhelp

 

 

 

mgodseymgodsey

Thank you so much!