Apex Triggers
Apex Triggers
Apex Triggers
==============
Triggers are the Custom Apex Code, which will execute the Business Logic
automatically based on certain events.
By using Triggers, we can Perform all the DML operations (INSERT, UPDATE, DELETE,
UNDELETE, UPSERT, MERGE) on all object records at a time. And we can retrieve the
records from the objects by using SOQL Queries.
Note:
Triggers can be fired on both "Before and After" operations on the specified
object.
Note:
Each Trigger should be associated with an "Object". And an object can have
one or more Triggers.
Syntax:
Trigger <TriggerName> ON <ObjectName>(<Trigger Events>)
{
// Write the Business Logic..
}
Ex:
Trigger AccountsTrigger ON Account(<Trigger Events>)
{
// Write the Business Logic..
}
Trigger Events:
===============
By using Trigger Events, we can indicate when the Trigger Should get fired. A
Trigger can be get fired on Multiple Events.
1. Before Insert:
This event will fire the Trigger, Before inserting a New Record
inside the object.
Note:
By using this event, we can implement the Custom
Validations, Duplication Elimination Processes, Check the Object Level Permissions,
Field Level Permissions,...etc.
Ex:
Trigger AccountsTrigger ON Account(Before Insert)
{
// Write the Validations Code,...etc.
}
2. Before Update:
By using this Event, we can fire the Trigger on Before Updating
an existing record in the object.
Note:
By using this Event, we can implement the "Custom
Validations, De-Duplication Processes, Check the Record Level Permissions, Check
the Object Level Permissions and Check the Field Level Permissions",..etc.
Ex:
Trigger AccountsTrigger ON Account(Before Update)
{
// Write the Validations Code,...etc.
}
3. Before Delete:
This event will fire the Trigger on "Before Deleting the Record
from the object".
Note:
By using this event, we can implement the business logic to
verify the Record Level Permissions, Object Level Permissions, To Remove the
Related Childs in the Lookup Relationships, and To Prevent the Deletion of Childs
in Standard Functionalities,..etc.
Ex:
Trigger AccountsTrigger ON Account(Before Delete)
{
// Write the Business Logic,...etc.
}
4. After Insert:
This event will fire the Trigger on "After Inserting" a Record
inside the object.
Note:
This event will be used to implement the "Sending the Email
Alerts to Users, To Share the Records to the Users, To Assign the Records to the
Users and To Update the Rollup Summary Field Values,...etc.".
Ex:
Trigger AccountsTrigger ON Account(After Insert)
{
// Write the Business Logic,...etc.
}
5. After Update:
This Event will fire the Trigger "After Updating an Existing
Record" in the object.
Note:
By using this Event we can implement "Sending the Email
Notifications to the Users, Updating the Rollup Summary Field Values, We can
implement the Custom Sharing Mechanism, implement the Custom Assignment
Rules,...etc.
Ex:
Trigger AccountsTrigger ON Account(After Update)
{
// Write the Validations Code,...etc.
}
6. After Delete:
This event will fire the Trigger "After Deleting an existing
Record in the object".
Note:
By using this event we can Update the Rollup Summary field
values.
Ex:
Trigger AccountsTrigger ON Account(After Delete)
{
// Write the Business Logic..,...etc.
}
7. After UnDelete:
This event will fire the Trigger "Once a Deleted Record has been
Re-Stored back to the actual object".
Note:
By using this event, we can implement the "Sending Email
Alerts, Updating Rollup Summary Field Values, Firing Sharing Rules, Firing
Assignment Rules,...etc."
Ex:
Trigger AccountsTrigger ON Account(After UnDelete)
{
// Write the Business Logic.,...etc.
}
Ex:
Trigger AccountsTrigger ON Account(Before Insert, After Insert, After
Update,
After Delete)
{
// Write the Validations Code,...etc.
}
Note:
All the Triggers information will get resides inside the "ApexTrigger"
object. Each Trigger Code will get stored inside a file with the extension ".apxt".
1. Boolean Trigger.IsInsert:
It returns TRUE, when the Trigger has been fired because of an insert
operation. Else it return FALSE.
2. Boolean Trigger.IsUpdate:
It returns TRUE, if the Trigger has been fired because of an update
operation. Else it returns FALSE.
3. Boolean Trigger.IsDelete:
It returns TRUE, if the Trigger has been fired because of a Delete
Operation. Else it returns FALSE.
4. Boolean Trigger.IsUnDelete:
It return TRUE, If the Trigger has been fired because of an "UnDelete"
operation. Else it returns FALSE.
5. Boolean Trigger.IsBefore:
It returns TRUE, If the Trigger is about to perform the operation. Else
it returns FALSE.
6. Boolean Trigger.IsAfter:
It returns TRUE, if the Trigger has been done with the operation. Else
it returns FALSE.
7. Boolean Trigger.IsExecuting:
It returns TRUE, if the Trigger is currently performing the operation.
Else it returns FALSE.
(i.e. Current Context is a Trigger. Not a VF Page, Not a WebService
Class, and Not an ExecuteAnonymous Call).
8. Integer Trigger.Size:
It returns the Number of Records has been included in the invocation.
Which includes both "Old and New Context Records".
9. Trigger.OperationType:
It returns the Event Name, because of which the Trigger has been fired.
Ex:
BEFORE_INERT AFTER_INSERT
BEFORE_UPDATE AFTER_UPDATE
BEFORE_DELETE AFTER_DELETE
AFTER_UNDELETE
Ex:
Trigger AccountsTrigger ON Account(Before Insert, After Update, Before
Delete)
{
Switch ON Trigger.OperationType
{
When BEFORE_INSERT
{
// Write the Business Logic..
}
When AFTER_UPDATE
{
// Write the Business Logic..
}
When BEFORE_DELETE
{
// Write the Business Logic..
}
}
}
(OR)
Trigger Code:
-------------
Trigger LeadTrigger ON Lead(Before Insert, Before Update, After Insert)
{
if( (Trigger.isInsert || Trigger.isUpdate) && Trigger.isBefore )
{
// Write the Validation Code..
}
(i.e. All the Newly Inserting Records will get placed inside the
Trigger.New for the Validations).
Syntax:
List<SObjectDataType> Trigger.New;
Note:
Trigger.New will be available in "Insert and Update" operations.
It will not be available in the Delete operation.
11. Trigger.Old:
Trigger.Old is Context Variable, which contains the Previous
context records in the form of "List Collection".
Syntax:
List<SObjectDataType> Trigger.Old;
Note:
Trigger.Old will be available in "Update and Delete"
operations. It will not be available in the "Insert" operation.
12. Trigger.OldMap:
Trigger.OldMap is Context Variable, which contains the Previous
context records in the form of "Map Collection".
Where
Key --> Record Id
Value --> Complete Record
Syntax:
Map<ID, SObjectDataType> Trigger.OldMap;
Note:
Trigger.OldMap will be available in "Update and Delete"
operations. It will not be available in the "Insert" operation.
13. Trigger.NewMap
Trigger.NewMap is Context Variable, which contains the Current
context records in the form of "Map Collection".
Where
Key --> Record Id
Value --> Complete Record
Syntax:
Map<ID, SObjectDataType> Trigger.NewMap;
Note:
Trigger.NewMap will be available in "Insert (After Insert)
and Update" operations. It will not be available in the "Delete" operation.
Trigger Bulkification:
======================
Ex:
Trigger AccountsTrigger ON Account(Before Insert)
{
if(Trigger.IsBefore && Trigger.isInsert)
{
for(Account acc : Trigger.New)
{
// Write the Validations Code..
}
}
}
Setup --> Build --> Develop --> Apex Triggers --> New.
UseCase:
========
Create a Trigger on Account Object, to make sure "Account Industry, Fax and
WebSite Fields are Required".
UseCase:
========
Create a Trigger on Lead Object, to Auto Populate the Annual Revenue based on
the Industry Name as below.
}
When 'Finance'
{
ldRecord.AnnualRevenue = 7600000;
}
When 'Insurance'
{
ldRecord.AnnualRevenue = 4500000;
}
When 'Manufacturing'
{
ldRecord.AnnualRevenue = 8400000;
}
When 'Consulting'
{
ldRecord.AnnualRevenue = 3200000;
}
When 'Education'
{
ldRecord.AnnualRevenue = 7400000;
}
When 'Energy'
{
ldRecord.AnnualRevenue = 9400000;
}
}
}
}
}
Note:
Upon firing the Trigger, If the Record contains both "Validation Rule and
Before Trigger" on the same field. Then it will fire the "Before Trigger First".
And then it will fire the Validation Rule.
UseCase:
========
Create a Trigger on the Account Object, to Prevent the Deletion of an Active
Account Record.
Assignments:
============
1. Create a Trigger on the Position Object, to make sure the "Minimum Pay
Field and Contact Number Fields are Required".
2. Create a Trigger on the Lead object, to MakeSure the Lead Address should
be Mandatory. (i.e. Street, City, State, PostalCode, Country)
3. Create a Trigger on the Case Object, to make sure each Case Record should
be Associated with an "Account and Contact".
4. Create a Trigger on the Contact Object, to make sure each Contact Record
should be associated with an "Account".
UseCase:
========
Create a Trigger on the Hiring Manager Object, to maintain the Uniqueness of
the Records based on the "Hiring Manager Name and Contact Number combination".
ObjectName: Hiring_Manager__C
Event Name : Before Insert, Before Update
contact_number__C =: hrRecord.Contact_Number__c];
if(recordsCount > 0)
{
hrRecord.AddError('Duplicate Record Found with the
Same Details. Record Cannot be Acceptable.');
}
}
}
}
UseCase:
========
Create a Trigger to Prevent the Deletion of the Related Contact Records, upon
removing the Account Record from the Object.
if(! lstContacts.isEmpty())
{
for(Contact con : lstContacts)
{
con.AccountId = null;
}
update lstContacts;
}
}
}
Assignment:
===========
1. Create a Trigger on the Hiring Manager Object, to Remove all the Related
Position
Records upon removing the Hiring Manager Record from the object.
UseCase:
========
Create a Trigger to Synchronize the Account Record Changes into the Related
Contact Record fields as below.
from Contact
if(! lstContacts.isEmpty())
{
for(Contact con : lstContacts)
{
/*
con.Phone = Trigger.NewMap.Get(con.AccountId).phone;
con.Fax = Trigger.NewMap.Get(con.AccountId).Fax;
con.MailingStreet =
Trigger.NewMap.Get(con.AccountId).BillingStreet;
con.MailingCity =
Trigger.NewMap.Get(con.AccountId).BillingCity;
con.MailingState =
Trigger.NewMap.Get(con.AccountId).BillingState;
con.MailingPostalCode =
Trigger.NewMap.Get(con.AccountId).BillingPostalCode;
con.MailingCountry =
Trigger.NewMap.Get(con.AccountId).BillingCountry;
*/
con.Phone = acc.Phone;
con.Fax = acc.Fax;
con.MailingCity = acc.BillingCity;
con.MailingState = acc.BillingState;
con.MailingStreet = acc.BillingStreet;
con.MailingPostalCode = acc.BillingPostalCode;
con.MailingCountry = acc.BillingCountry;
update lstContacts;
}
}
}
Custom Settings:
================
Storage Limitation:
-------------------
Organization Wide : Max. of 10 MB Storage.
Salesforce Licenses : 1 MB
2 Salesforce Licenses : 2 MB
5 Salesforce Licenses : 5 MB
10 Salesforce Licenses : 10 MB
15 Salesforce Licenses : 10 MB
/*
<CustomSettingName>.GetAll()
<CustomSettingName>.GetInstance()
<CustomSettingName>.GetValues()
*/
// Get All the Records from the CustomSetting.
Map<String, RegionDetails__c> regDetails = RegionDetails__c.GetAll();
Campaigns
Marketing Team --> Campaigns <-- Leads
|
|
--> Sales Team.
Contact:MailingPostalCode
Lead:Country --> Convert ---> Account:BillingCountry,
Contact:MailingCountry
Lead:Company --> Convert ---> Account:Name, Opportunity:Name
...
3. ConvertedContactID:
|
--> Contains the Contact Record ID has been generated due
to the Lead
Conversion.
4. ConvertedOpportunityID:
|
--> Contains the Opportunity Record ID has been generated
due to the Lead
Conversion.
Database.LeadConvert Class:
===========================
In Salesforce, we have to convert the lead records seperately one by one. We don't
have such options, to convert bulk lead records at a time.
Ex:
Database.LeadConvert lconvert = new Database.LeadConvert();
Ex: To Hold the Multiple Lead Records to be get Converted, We have to use "List"
Collection.
Methods:
--------
Database.LeadConvert class provides a set of methods, which are used to describe
the conversion settings.
Ex:
lconvert.SetLeadId('00Q23423423');
2. SetSendNotificationEmail(<Boolean>):
It is used to indicate to send the Email Notification to Lead Record owner,
regarding the Lead Conversion.
Ex:
lconvert.SetSendNotificationEmail(true);
3. SetDoNotCreateOpportunity(<Boolean>):
By using this method, we can prevent the generation of an opportunity record,
upon lead conversion.
TRUE --> Opportunity will not created.
FALSE --> Opportunity record will get created.
Ex:
lconvert.SetDoNotCreateOpportunity(false);
4. SetConvertedStatus(string):
This method is used to indicate the status to be assigned to the LEad, once
it get converted.
All the "LeadStatus" values exist in "LeadStatus" object, We can query the
values as below.
Ex:
lconvert.SetConvertedStatus(LeadStatusValue);
5. Database.ConvertLead(List<ID>):
This method is used to Convert the specified lead record id's.
We can track the Lead Conversion status of each lead record by using
"Database.LeadConvertResult[]" class.
Ex:
Database.LeadConvertResult[] results =
Database.ConvertLead(List<LeadID>);
UseCase:
========
Create a Trigger to Auto-Convert the Lead Records as the Customers upon
Changing the Lead Status as "Closed - Converted".
Pre-Requisite:
Create a CheckBox Field with the name "Do Not Create Opportunity" in
the Lead Object.
lConvert.setLeadId(ldRecord.Id);
lConvert.setDoNotCreateOpportunity(ldRecord.Do_Not_Create_Opportunity__c);
lConvert.setSendNotificationEmail(true);
lConvert.setConvertedStatus(lStatus.MasterLabel);
if(! lstConvert.isEmpty())
{
Database.LeadConvertResult[] result =
Database.convertLead(lstConvert, false);
}
}
}
// Create a Collection to Hold all the Lead Records, which needs to be get
Converted.
List<Database.LeadConvert> leadsToConvert = new
List<Database.LeadConvert>();
ldConvert.setLeadId(ldRecord.Id);
ldConvert.setSendNotificationEmail(true);
ldConvert.setDoNotCreateOpportunity(ldRecord.Do_Not_Create_Opportunity__c);
ldConvert.setConvertedStatus(lStatus.MasterLabel);
UseCase:
========
Create the Triggers to Synchronize the Records between the Hiring Manager and
Recruiter Objects.
for(Hiring_Manager__C hr : Trigger.New)
{
// Check weather the HR Record has been modified.
if( (Trigger.OldMap.Get(hr.Id).Name != hr.Name) ||
(Trigger.oldMap.Get(hr.Id).Location__C != hr.Location__c) ||
(Trigger.OldMap.Get(hr.id).Contact_Number__C !=
hr.Contact_Number__c) ||
(Trigger.oldMap.Get(hr.id).Email_id__C != hr.Email_ID__c) )
{
hrRecordsUpdated.Put(hr.Id, hr);
}
}
if(! hrRecordsUpdated.isEmpty())
{
HiringManagerSyncHandler.AfterUpdate(hrRecordsUpdated);
}
}
}
for(Hiring_Manager__C hr : lstHRRecords)
{
// Create a Recruiter Record..
Recruiter__C rec = new Recruiter__C();
rec.Name = hr.Name;
rec.Location_Name__c = hr.Location__c;
rec.Contact_Number__c = hr.Contact_Number__c;
rec.Email_Address__c = hr.Email_ID__c;
rec.HiringManagerID__c = hr.Id;
/*
* @ Method Name : AfterUpdate
* @ Description : This Method will Synchronize the Related Recruiter Records
based on the latest
values of Hiring Manager Records.
* @ Parameters:
* @Param Name : mapHRRecords @Type : Map<ID, Hiring_Manager__C>
* @ Returns : N/A
* @ Throws : N/A
* @ References : SyncHiringManagerRecordsTrigger
* @ Version : 1.0
*/
Public static void AfterUpdate(Map<ID, Hiring_Manager__C> mapHRRecords, Boolean
isActive)
{
List<Recruiter__C> lstRecruiters = [Select id, name, location_name__c,
email_address__C,
contact_number__C,
hiringmanagerid__C
from Recruiter__C
Where hiringmanagerid__C
IN : mapHRRecords.KeySet()];
if(! lstRecruiters.isEmpty())
{
List<Recruiter__C> recruitersToUpdate = new List<Recruiter__C>();
rec.Name = hr.Name;
rec.Location_Name__c = hr.Location__c;
rec.Email_Address__c = hr.Email_ID__c;
rec.Contact_Number__c = hr.Contact_Number__c;
recruitersToUpdate.Add(rec);
}
if(! recruitersToUpdate.isEmpty())
{
Update recruitersToUpdate;
}
}
}
}
3. Always use try and catch block in the trigger to handle Exceptions.
5. Do not use DML operations inside the for loop. Add them to the list and
update/insert/delete the list out side the for loop.
7. Write the Proper Test Classes for the Trigger. And maintain minimum 1% of code
coverage for each trigger. And Overall organization wide, we have to maintain 75%
of code coverage while moving the code from sandbox to production environment.
Note:
Recursive Triggers can be avoided by using the "Static Boolean" variables.
Do not execute the trigger on all the update. Only execute the trigger on
specific update.
Here the trigger will get executed on update of stagename field only and will
not trigger on other field's update
opptyIds.add(opp.Id);
Error Code:
2. The new record field values are loaded from the request and overwrite the old
values
4. System validation occurs, such as verifying that all required fields have a non-
null value, and running any user-defined validation rules (VALIDATIONS)
10. If there are workflow field updates, the record is updated again.
11. If the record was updated with workflow field updates, before and after
triggers fire one more time (and only one more time) along with Standard Validation
Rules. (Note: In this case, Custom validation rules will not fire.)
Assignment:
===========
1. Implement the Rollup Summary Functionality between Account and Case
Object.