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?
2. You’re writing a trigger on Opportunities that reassigns the owner of newly created opps. Should you use a before or after trigger?
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?
4. True or false. Every trigger you write should have at least one System.debug statement.
5. Is it better to have too many System.debug statements or too few?
6. True or false. You can see the output of System.debug in both Sandbox and Production orgs.
7. Which happens when you use Trigger.oldMap in an insert trigger?
a. The value returned is always null
b. You receive an error
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?
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>>
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?
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 } }
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);
}
}
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);
}
You’re treating myContact like a list but it’s just a Contact!
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?
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……
Why did you decide to use after instead of before? =)
“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
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’
Thank you Mayank! Why hasn’t anyone told me sooner *hmph*!!!!
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’);
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!
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?
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
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.
Definitely not a requirement!
Just trying to stress the importance of it =) It is better to over debug than under debug!
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.
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
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
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 :) )
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
Aahh sweeet :-)
Thanks again for clarifying Dev … so many ways to look at the code !!!
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.’);
}
}
Nitin – looks amazing! Passes the eyeball test for sure! Great job!
Thanks David,
So my check box concept to turn the email on/off was correct :)
Yup!!!!
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 ?
You should be fine =)
If not, I blame my site commenting system for stripping out some of your code since it looks like HTML.
Ohh I guess when posting comment on your page … the content was deleted.
the format I was talking about is
List mails = new List ();
Ok thanks for the confirmation. Yes the site is surely deleting the content
List “Messaging.SingleEmailMessage” mails = new List “Messaging.SingleEmailMessage” ();
You do have a “mails” variable in your code!