Salesforce coding lessons for the 99%
Finally, Apex tutorials for point-and-click admins! Written by a self-taught Google engineer.
  • Beginner Tutorials
    • Apex
    • Certifications
    • Career Info
    • Technical Architect
    • Visualforce
    • Videos
  • Apex Academy
  • Success Stories
  • About Me
  • Misc
    • Mailbag
    • Challenges
    • Links
    • Login to my Org
Follow @dvdkliuor SUBSCRIBE!

Quiz Answers – Chapter 6

March 18, 2014

Preface: this post is part of the Advanced Apex Concepts series.
View Chapter 6 quiz questions without the answers!

Chapter 6 Questions:

1. You need to write a trigger that references formula fields on an object. These formula fields often change when a record is created/edited and saved. Should you use a before or after trigger?

Use a before trigger. Formula fields will already be updated – only use after triggers if you need the ID of record created in the trigger!

2. You’re writing a trigger on Opportunities that reassigns the owner of newly created opps. Should you use a before or after trigger?

Use a before trigger. You don’t need the ID of any opp in the trigger! You’re simply only changing its owner.

3. You need to write a trigger that automatically creates Contact Roles for every newly created Opportunity. Should you use a before or after trigger?

Use an after trigger. You’ll need the ID of the newly created opp(s) to assign to the Opportunity Contact Role.

4. True or false. Every trigger you write should have at least one System.debug statement.

True.

5. Is it better to have too many System.debug statements or too few?

Too many! You’ll thank yourself later.

6. True or false. You can see the output of System.debug in both Sandbox and Production orgs.

True. This is why it’s better to have too many debug logs. If you ever need to debug a problem you’re having in production, you won’t need to deploy code with more debug logs just to see the results.

7. Which happens when you use Trigger.oldMap in an insert trigger?
  a. The value returned is always null
  b. You receive an error

b. Trigger.oldMap is not available on insert triggers because it’s impossible to have “old” values on a new record! You can use Trigger.isInsert to make sure you don’t get this error on combined insert and update triggers.

8. You’re creating a trigger that reduces the inventory of a Stock object every time a Product is purchased. Why is it or is it not necessary to use Trigger.oldMap?

Without checking for old vs. new values, your trigger will run every time a “purchased” opp is edited, even after it is already purchased. For example, let’s say a “Closed” status on the Product means it is purchased. Without making sure the old value is not “Closed” and the new value is “Closed”, every field update after a Product is Closed will run your trigger!

9. An email you’re writing needs to include multiple different variables depending on the associated Contact. Create a “body” variable for an email message that will say this for every contact in your trigger (things inside <<this>> are fields):
Dear <<Contact’s First Name>>,

Every day I get lost in your <<Contact’s eye color>> eyes. Your <<Contact’s hair color>> hair smells like <<Roses, if the “Likes Roses” field is checked. Otherwise, Dandelions>>. I want to buy you <<Contact’s favorite ice cream flavor>> ice cream that is organic. I will pick you up after you’re off work at <<Contact’s Account name>>. I will not take no for an answer.

Love,
<<Your first name>>


String body = ‘Dear ‘ + myContact.FirstName + ‘,<br /><br />’;
body += ‘Every day I get lost in your ‘ + myContact.Eye_Color__c + ‘ eyes. ‘;
body += ‘Your ‘ + myContact.Hair_Color__c + ‘ smells like ‘;
if (myContact.Likes_Roses__c) {
  body += ‘roses. ‘;
} else {
  body += ‘dandelions. ‘;
}
body += ‘I want to buy you ‘ + myContact.Favorite_Ice_Cream_Flavor__c + ‘ ice cream that is organic. ‘;
body += ‘I will pick you up after you’re off work at ‘ + myContact.Account.Name + ‘. ‘;
body += ‘I will not take no for an answer.<br /><br />’;
body += ‘Love,<br />’;
body += UserInfo.getFirstName();

Note that <br /> is a line break and that UserInfo gets the information of the person who triggered the trigger.

10. Your manager would like to edit some of your trigger’s variables without having to write new code. You have the option of saving these variables either in a Custom Setting or in a custom object. What are the advantages and disadvantages of each option?

Custom settings are easier to use however they are less scalable. If you just need to store a few properties of your trigger and you don’t anticipate making additional fields on your Custom Setting after deploying your code (in the foreseeable future), you generally want to use a Custom Setting. Going this route means you won’t need to use any SOQL to access your settings. Custom objects give you the flexibility of having multiple records, which can potentially save your from making many custom fields and having a messy Custom Setting! Custom objects also let you run reports on the settings, add validation rules/workflows, etc etc. If you’re on the fence, go with a Custom Setting.

Chapter 6 Practice Trigger:
Write a trigger that:
– Sends a “Farewell” email to Leads
– Only sends this email when the Lead’s “Rating” changes to “Cold”
– Can be turned on and off without writing any code
– Has at least five System.debug statements
– Don’t forget the test class!

trigger Farewell on Lead (before insert, before update) {
  // Grab your Custom Setting values
  Bye_Settings__c settings = Bye_Settings__c.getInstance('bye');
  Boolean triggerIsOff     = settings.Turn_Off_Trigger__c;
  
  // Make sure trigger is on
  if (!triggerIsOff) {
    System.debug('Farewell trigger is on!');
    
    // Create master email list
    List<Messaging.SingleEmailMessage> mails = 
    new List<Messaging.SingleEmailMessage>();
    
    // Loop through all leads in trigger
    for (Lead myLead : Trigger.new) {
      if (myLead.Email != null) {
        if (myLead.Rating == 'Cold') {

          Boolean sendEmail = false;

          // Inserted leads have no "old" value
          if (Trigger.isInsert) {
            sendEmail = true;
            System.debug(myLead.Email + ' is new and cold.');
          // Check that updated leads were changed to 'Cold'
          } else {
            if (Trigger.oldMap.get(myLead.Id).Rating != 'Cold') {
              sendEmail = true;
              System.debug(myLead.Email + ' is now cold.');
            }
          }
    
          // Create the email
          if (sendEmail) {
            System.debug('Creating email for ' + myLead.Email);
            Messaging.SingleEmailMessage mail = 
            new Messaging.SingleEmailMessage();
            List<String> sendTo = new List<String>();
            sendTo.add(myLead.Email);
            mail.setToAddresses(sendTo);
            mail.setReplyTo(UserInfo.getUserEmail());
            mail.setSenderDisplayName(UserInfo.getName());
            mail.setSubject('Farewell, my love.');
            String body = 'Life is too short.';
            mail.setHtmlBody(body);
            mails.add(mail);
          }
        }
      }
    }
    Messaging.sendEmail(mails);
    System.debug(mails.size() + ' emails sent!');
  } else {
    System.debug('Farewell trigger is off.');
  }
}
@isTest
public class TestFarewell {
  static testMethod void testFarewell() {
    // Create custom settings
    Bye_Settings__c settings     = new Bye_Settings__c();
    settings.Name                = 'bye';
    settings.Turn_Off_Trigger__c = false;
    insert settings;
    
    // Create a cold lead
    Lead lead = new Lead();
    lead.Company  = 'McIlhenny';
    lead.LastName = 'Tabasco';
    lead.Email    = 'moretabasco@gmail.com';
    lead.Rating   = 'Cold';
    insert lead;
    
    // Make lead hot!
    lead.Rating = 'Hot';
    update lead;
    
    // Make lead cold again to send email!
    lead.Rating = 'Cold';
    update lead;

    // We'd test with more records, but
    // Developer orgs have low email limits
    
    // We can't assert that leads received emails!
    // We can test a lot more, but this is fine

  }
}
30 Comments
bilabongster
July 13, 2017 @ 11:14 am

I get Null pointer exception on this code:

trigger LeadFarewell on Lead (before update)
{
Lead_Farewell__c settings = Lead_Farewell__c.getInstance(‘Farewell Email Active?’);
boolean Farewell_Email_Active = settings.Farewell_Email_Active__c;

if (Farewell_Email_Active = true){
List Mails = new List ();

for (Lead xLead: Trigger.new)

{
Lead oldxLead = Trigger.oldmap.get(xLead.id);
boolean LeadOldvalue = oldxLead.Rating.equals(‘Cold’);
boolean LeadNewValue = xLead.Rating.equals(‘Cold’);

if (xLead.email!= NULL && LeadNewvalue!=LeadOldValue)
{
if (LeadNewValue = true)
{
Messaging.SingleEmailMessage Mail = New Messaging.SingleEmailMessage();
List sendTo = new List();
sendTo.add(xLead.Email);
mail.setToAddresses(sendTo);
mail.setReplyTo(‘bilabongster@gmail.com’);
mail.setSenderDisplayName(‘bilabongster’);

mail.setSubject(‘Bye Bye’);
String body = ‘Dear ‘ + xLead.FirstName + ‘, ‘;
body += ‘Sorry to see you go’;

mails.add(mail);

}
}
}
Messaging.sendEmail(mails);
}
}

Reply
Monty
November 17, 2016 @ 2:24 am

Hi David,

I solved the question 9 in the following manner however getting one error at Line: 3, Column: 29
Expression must be a list type: Contact.

Can you help me out with this? I did a lot of brainstorming but i failed in finding the answers.

Also, the logs are not getting generated.

for (Contact myContact : [SELECT Id, Name FROM Contact WHERE Name = ‘Alice’ Limit 1])
{
String body = ‘Dear ‘ + myContact[0].FirstName + ‘,’;

body += ‘Every day I get lost in your ‘ + myContact[0].EyeColor__c + ‘eyes’ +’.’;
if(myContact[0].Likes_Roses__c == true)
{
body+= ‘Your’ +myContact[0].HairColor__c +’hair smells like Roses’ + ‘.’ ;
}
else
{
body+= ‘Your’ +myContact[0].HairColor__c +’hair smells like Dandelions’ + ‘.’ ;
}

body += ‘I want to buy you’ +myContact[0].Favt_Ice_Cream_Color__c + ‘icecream that is organic.’;
body += ‘I will pick you up after you are off work at ‘ +myContact[0].Account.Name + ‘.’;
body += ‘I will not take no for an answer.’ + ”;

body += ‘Love’ + ‘,’;

body += +UserInfo.getFirstName();
System.debug(‘Body’ + body);
}

Reply
    David Liu
    November 17, 2016 @ 8:38 pm

    You’re treating myContact like a list but it’s just a Contact!

    Reply
evan
December 26, 2015 @ 10:34 am

Error Message System.DmlException: Insert failed. First exception on row 0; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, farewell: execution of BeforeInsert

caused by: System.InvalidParameterValueException: Invalid SetupOwner for Custom Settings: Trigger Settings

Trigger.farewell: line 3, column 1: []
Stack Trace Class.testFarewell.testFarewell: line 16, column 1

Can anyone tell me why the “Insert failed”. and maybe shed some light on the other errors?

Reply
Sumanth
August 22, 2015 @ 3:20 am

Hi david i was trying this trigger challenge with ‘after update ‘ trigger…and it worked but you have used a before update trigger…Pls comment on this……

Reply
    David Liu
    August 22, 2015 @ 11:24 pm

    Why did you decide to use after instead of before? =)

    Reply
radha
February 28, 2015 @ 1:32 am

“Custom objects give you the flexibility of having multiple records, which can potentially save your from making many custom fields and having a messy Custom Setting! ”
can u please explain the above line
even with custom setting we are creating records as it is also a object
thanks in advance

Reply
Mayank Srivastava
February 21, 2015 @ 4:59 pm

Some typos:
Answer to question 7: “You can use Trigger.isInsert to make sure you’re don’t get this error on…”
Correction: you’re should be replaced with ‘you’

Answer to question 8: “Without checking for old vs. new values, your trigger will run anything a “purchased” opp is edited..”
Correction: ‘anything’ should be replaced with ‘every time’

Reply
    David Liu
    February 21, 2015 @ 11:43 pm

    Thank you Mayank! Why hasn’t anyone told me sooner *hmph*!!!!

    Reply
Mayank Srivastava
February 19, 2015 @ 10:53 am

Thanks for such easy-to-understand tutorials David. I was just sitting at my desk and wondering that how much time would it possibly have taken me to dig for sources , go through them and possess the knowledge that I have today. Your tutorials have sure saved me a lot of time and effort! My firm paid a hefty amount to a consultant last year to implement something of this sort and I was able to write it all by myself today; thanks to your site. It’s a great feeling when your actual code and test code both run in the first attempt, yay! =)
My only concern is that even though I can think of and write the logic myself, I am copying pasting code snippets from your tutorials since remembering their syntax is tough for me at this point. Is this not good practice and should I begin memorizing the syntax. Example snippets that I had to copy paste:

List mails = new List();
Lead oldLead = Trigger.oldMap.get(myLead.Id);
Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
mail.setReplyTo(‘support@clearstructure.com’);
mail.setSenderDisplayName(‘Clearstructure Financial Technology’);

Reply
    David Liu
    February 19, 2015 @ 9:14 pm

    LOL if it makes you feel any better I copy at least 50% of my code, if not more. This is very common in the programming industry for any language, since it’s too hard to remember everything other than the high level details.

    Someone once told me “Only 1 piece of code was ever written from scratch” – that was the first lines of code ever written!

    Keep up the good stuff Mayank!

    Reply
Faraz Mohammad Khan
February 16, 2015 @ 10:58 pm

Hi David,

I have a question. We are running this trigger before update. But lets say we get an exception while updating the record and the update didn’t complete. Will the email still go out?
If so, will it be better to use after update when sending this email?

Reply
    David Liu
    February 17, 2015 @ 7:19 pm

    An error in either scenario will fail the update!

    Two ways around this:
    1. Use try/catch
    2. Use a Database class method to continue the updates even if one of the records fails

    Reply
Bikram Bhuyan
January 6, 2015 @ 2:48 pm

Hi David,

Thanks a lot for this wonderful tutorials. I have learnt a lot from your tutorial after reading each chapter and taking down the quiz.

I have a question on the answer to point 4.

4. True or false. Every trigger you write should have at least one System.debug statement.
True.

Is it mandatory to have at least one System.debug statement in a trigger or it is advisable to have it. Could you please clarify this?

As per me the answer should be False.

Thanks,
Bikram.

Reply
    David Liu
    January 6, 2015 @ 6:07 pm

    Definitely not a requirement!

    Just trying to stress the importance of it =) It is better to over debug than under debug!

    Reply
Geoff
October 28, 2014 @ 5:44 pm

Two part question here, both around same line of code

Is there a best practice for using nested if statements vs just linking them all together in something like:
if(coldLead.Rating == ‘Cold’ && oldLead.Rating != ‘Cold’ && coldLead.Email !=NULL) ? //(oldLead is Trigger.oldMap value)
Or does it just depend on how many different scenarios you are working with and what is easier?
Are there risks to doing it one way vs the other?

Also, what is the logic for sometimes comparing the Trigger.oldMap to the current value as boolean instead of just comparing them as is? I’ve seen you do this a few times but I don’t understand the reason why, to me you are just adding extra lines.

Reply
    David Liu
    October 28, 2014 @ 6:28 pm

    Hey Geoff,

    Whichever iteration of IF statements looks easier to digest is the best way =) Generally I don’t like putting too many && or || on the same line!

    I compare oldMap using booleans honestly to reduce the number of characters on a single line of code. I only do that for this site since multi-line code might be confusing to newbies =) Otherwise you can do it all within the IF statement!

    Hope this answers your questions!
    David

    Reply
      Geoff
      October 28, 2014 @ 6:44 pm

      Thanks! For some reason I have a mental block thinking that nested ifs will cause issues and keep shying away from them. Understanding that it’s SOQL inside the loop and not if statements that causes issues really helps. I really liked this piece of code that you wrote for the practice trigger, it is very easy to read and I like the sendEmail variable usage

      Reply
Nitin
June 24, 2014 @ 4:21 am

After reading your code I realized that my code can be improved :)

But after checking for all the possibilities I found ‘sendEmail’ variable redundant in the Farewell Trigger. Because of the below mentioned scenarios

1. The code will reach till ‘sendEmail’ variable because the rating is ‘Cold’ – means we have to anyhow send an email
2. Even if the Lead is new and cold – we have to send an email
3. Also irrespective of previous rating current rating is ‘Cold’ – we have to send an email.
4. If we remove this ‘sendEmail’ variable the code will still run as expected.

Please correct me if I am wrong Dev. (I am not trying to correct your code here :) )

Reply
    David Liu
    June 24, 2014 @ 8:14 pm

    No worries – always good to get deep into the code!

    Try this scenario:
    – Lead has a “Cold” rating
    – Lead name is changed from “David” to “Nitin” (trigger is run as a result of the update)

    Here’s how the trigger runs:
    1. Lead currently has a “Cold” rating, so it progresses past the first rating check
    2. It’s an update trigger so it skips the insert part and moves to the else statement
    3. Old value of the lead is “Cold”, so it skips a majority of the else statement and the sendEmail variable remains false
    4. Email does not get sent

    Reply
      Anonymous
      June 24, 2014 @ 9:38 pm

      Aahh sweeet :-)

      Thanks again for clarifying Dev … so many ways to look at the code !!!

      Reply
Nitin
June 23, 2014 @ 7:31 am

Hey David,

I must say its the awesome practice trigger that covered almost every aspect of this chapter.

I just wrote this one but seems slightly different from your advanced trigger. Please check and let me know is this okay.

Also please suggest if there are areas of improvement ? I have written this in a hurry due to time boundation that’s why didn’t gave much time on it to improve upon (my bad) I know I am asking for much but at least I will feel motivated if you can have a compiler eye on this one.

Thanks

==============================================================================

trigger SendFairwelMail on Lead (before update) {

Send_Fairwell_Mail__c check = Send_Fairwell_Mail__c.getInstance(‘sfm’);

//Send_Mail__C is checkbox that returns true or false
if (check.Send_Mail__c) {

System.debug(‘Mail sending is Enabled : ‘ + check.Send_Mail__c);

List mails = new List();

for ( Lead newStatus : Trigger.new ) {

if ( newStatus.Email != null) {

//START : code to check if the status is changed to Cold//
Lead oldStatus = Trigger.oldMap.get(newStatus.Id);

Boolean oldStat = oldStatus.Rating.equals(‘Cold’);
System.debug(‘Lead Old Status is ‘ + oldStatus.Rating);

Boolean newStat = newStatus.Rating.equals(‘Cold’);
System.debug(‘Lead New Status is ‘ + newStatus.Rating);

//END : code to check if the status is changed to Cold//

if ( !oldStat && newStat ) {

Messaging.SingleEmailMessage sendFairwell = new Messaging.SingleEmailMessage();

List sendTo = new List();
sendTo.add(newStatus.Email);
sendFairwell.setToAddresses(sendTo);

sendFairwell.setReplyTo(‘David@Liu.com’);
sendFairwell.setSenderDisplayName(‘David liu’);

List ccTo = new List();
ccTo.add(‘sfdc99@salesforce.com’);
sendFairwell.setCcAddresses(ccTo);

sendFairwell.setSubject(‘Farewell’);
String body = ‘Dear ‘ + newStatus.Name + ‘, ‘;
body += ‘Good Bye !! ‘;
body += ‘Regards, ‘;
body += UserInfo.getFirstName();
sendFairwell.setHtmlBody(body);

mails.add(sendFairwell);

}
}
}
Messaging.sendEmail(mails);
System.debug(‘mail sent’);
} else {
System.debug(‘Farewell Email trigger is off.’);
}
}

Reply
    David Liu
    June 23, 2014 @ 10:53 pm

    Nitin – looks amazing! Passes the eyeball test for sure! Great job!

    Reply
      Nitin
      June 23, 2014 @ 10:59 pm

      Thanks David,

      So my check box concept to turn the email on/off was correct :)

      Reply
        David Liu
        June 23, 2014 @ 11:00 pm

        Yup!!!!

        Reply
      Nitin
      June 23, 2014 @ 11:16 pm

      One more doubt I would like to clear here David.. in a hurry to write the trigger, I forgot to type define the email List (List mails = new List();) as per below standard.

      List mails = new List();

      And I am calling Messaging method as per line ‘Messaging.sendEmail(mails);’ on ‘mails’ List. It didn’t gave any compiler error while saving but will it pose any issue in actual run ?

      Reply
        David Liu
        June 23, 2014 @ 11:18 pm

        You should be fine =)

        If not, I blame my site commenting system for stripping out some of your code since it looks like HTML.

        Reply
        Nitin
        June 23, 2014 @ 11:18 pm

        Ohh I guess when posting comment on your page … the content was deleted.

        the format I was talking about is

        List mails = new List ();

        Reply
          Nitin
          June 23, 2014 @ 11:22 pm

          Ok thanks for the confirmation. Yes the site is surely deleting the content

          List “Messaging.SingleEmailMessage” mails = new List “Messaging.SingleEmailMessage” ();

          Reply
            David Liu
            June 23, 2014 @ 11:27 pm

            You do have a “mails” variable in your code!

            Reply

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *


*

*

Theme: Simple Style by Fimply